You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by ta...@apache.org on 2016/09/12 20:12:02 UTC
[1/7] qpid-jms git commit: QPIDJMS-207 Adds support for Asynchronous
JMS 2.0 sends.
Repository: qpid-jms
Updated Branches:
refs/heads/master 6553cfd5b -> 6e442f4c6
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/3a03663b/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/failover/FailoverIntegrationTest.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/failover/FailoverIntegrationTest.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/failover/FailoverIntegrationTest.java
index 8eaf707..d6dc443 100644
--- a/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/failover/FailoverIntegrationTest.java
+++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/failover/FailoverIntegrationTest.java
@@ -43,11 +43,14 @@ import javax.jms.MessageProducer;
import javax.jms.Queue;
import javax.jms.QueueBrowser;
import javax.jms.Session;
+import javax.jms.TextMessage;
import javax.jms.Topic;
+import org.apache.qpid.jms.JmsCompletionListener;
import org.apache.qpid.jms.JmsConnection;
import org.apache.qpid.jms.JmsConnectionFactory;
import org.apache.qpid.jms.JmsDefaultConnectionListener;
+import org.apache.qpid.jms.JmsMessageProducer;
import org.apache.qpid.jms.JmsOperationTimedOutException;
import org.apache.qpid.jms.JmsSendTimedOutException;
import org.apache.qpid.jms.policy.JmsDefaultPrefetchPolicy;
@@ -56,6 +59,7 @@ import org.apache.qpid.jms.test.testpeer.TestAmqpPeer;
import org.apache.qpid.jms.test.testpeer.basictypes.AmqpError;
import org.apache.qpid.jms.test.testpeer.basictypes.TerminusDurability;
import org.apache.qpid.jms.test.testpeer.describedtypes.Accepted;
+import org.apache.qpid.jms.test.testpeer.describedtypes.Rejected;
import org.apache.qpid.jms.test.testpeer.describedtypes.sections.AmqpValueDescribedType;
import org.apache.qpid.jms.test.testpeer.matchers.SourceMatcher;
import org.apache.qpid.jms.test.testpeer.matchers.sections.MessageAnnotationsSectionMatcher;
@@ -1034,6 +1038,153 @@ public class FailoverIntegrationTest extends QpidJmsTestCase {
}
}
+ @Test(timeout = 20000)
+ public void testFailoverPassthroughOfCompletedAsyncSend() throws Exception {
+ try (TestAmqpPeer testPeer = new TestAmqpPeer();) {
+ final Connection connection = establishAnonymousConnecton(
+ "failover.reconnectDelay=2000&failover.maxReconnectAttempts=5", testPeer);
+
+ testPeer.expectSaslAnonymousConnect();
+ testPeer.expectBegin();
+ testPeer.expectBegin();
+ testPeer.expectSenderAttach();
+
+ Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ Queue queue = session.createQueue("myQueue");
+
+ // TODO Can change to plain MessageProducer when JMS 2.0 API dependency is added.
+ JmsMessageProducer producer = (JmsMessageProducer) session.createProducer(queue);
+
+ // Create and transfer a new message
+ String text = "myMessage";
+ testPeer.expectTransfer(new TransferPayloadCompositeMatcher());
+ testPeer.expectClose();
+
+ TextMessage message = session.createTextMessage(text);
+ TestJmsCompletionListener listener = new TestJmsCompletionListener();
+
+ producer.send(message, listener);
+
+ assertTrue("Did not get async callback", listener.awaitCompletion(2000, TimeUnit.SECONDS));
+ assertNull(listener.exception);
+ assertNotNull(listener.message);
+ assertTrue(listener.message instanceof TextMessage);
+
+ connection.close();
+
+ testPeer.waitForAllHandlersToComplete(1000);
+ }
+ }
+
+ @Test(timeout = 20000)
+ public void testFalioverPassthroughOfRejectedAsyncCompletionSend() throws Exception {
+ try (TestAmqpPeer testPeer = new TestAmqpPeer();) {
+ final JmsConnection connection = establishAnonymousConnecton(
+ "failover.reconnectDelay=2000&failover.maxReconnectAttempts=5", testPeer);
+
+ testPeer.expectSaslAnonymousConnect();
+ testPeer.expectBegin();
+ testPeer.expectBegin();
+ testPeer.expectSenderAttach();
+
+ Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ Queue queue = session.createQueue("myQueue");
+
+ // TODO Can change to plain MessageProducer when JMS 2.0 API dependency is added.
+ JmsMessageProducer producer = (JmsMessageProducer) session.createProducer(queue);
+
+ Message message = session.createTextMessage("content");
+ testPeer.expectTransfer(new TransferPayloadCompositeMatcher(), nullValue(), false, new Rejected(), true);
+
+ assertNull("Should not yet have a JMSDestination", message.getJMSDestination());
+
+ TestJmsCompletionListener listener = new TestJmsCompletionListener();
+ try {
+ producer.send(message, listener);
+ } catch (JMSException e) {
+ LOG.warn("Caught unexpected error: {}", e.getMessage());
+ fail("No expected exception for this send.");
+ }
+
+ assertTrue("Did not get async callback", listener.awaitCompletion(2000, TimeUnit.SECONDS));
+ assertNotNull(listener.exception);
+ assertNotNull(listener.message);
+ assertTrue(listener.message instanceof TextMessage);
+
+ testPeer.expectTransfer(new TransferPayloadCompositeMatcher());
+ testPeer.expectClose();
+
+ listener = new TestJmsCompletionListener();
+ try {
+ producer.send(message, listener);
+ } catch (JMSException e) {
+ LOG.warn("Caught unexpected error: {}", e.getMessage());
+ fail("No expected exception for this send.");
+ }
+
+ assertTrue("Did not get async callback", listener.awaitCompletion(2000, TimeUnit.SECONDS));
+ assertNull(listener.exception);
+ assertNotNull(listener.message);
+ assertTrue(listener.message instanceof TextMessage);
+
+ connection.close();
+
+ testPeer.waitForAllHandlersToComplete(2000);
+ }
+ }
+
+ @Test(timeout = 20000)
+ public void testFailoverConnectionLossFailsWaitingAsyncCompletionSends() throws Exception {
+ try (TestAmqpPeer testPeer = new TestAmqpPeer();) {
+ final JmsConnection connection = establishAnonymousConnecton(
+ "failover.reconnectDelay=2000&failover.maxReconnectAttempts=60",
+ testPeer);
+
+ testPeer.expectSaslAnonymousConnect();
+ testPeer.expectBegin();
+ testPeer.expectBegin();
+ testPeer.expectSenderAttach();
+
+ Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ Queue queue = session.createQueue("myQueue");
+
+ // TODO Can change to plain MessageProducer when JMS 2.0 API dependency is added.
+ JmsMessageProducer producer = (JmsMessageProducer) session.createProducer(queue);
+
+ final int MSG_COUNT = 5;
+
+ Message message = session.createTextMessage("content");
+ for (int i = 0; i < MSG_COUNT; ++i) {
+ testPeer.expectTransferButDoNotRespond(new TransferPayloadCompositeMatcher());
+ }
+
+ // Accept one which shouldn't complete until after the others have failed.
+ testPeer.expectTransfer(new TransferPayloadCompositeMatcher(), nullValue(), false, new Accepted(), true);
+ testPeer.dropAfterLastHandler();
+
+ TestJmsCompletionListener listener = new TestJmsCompletionListener(MSG_COUNT + 1);
+ try {
+ for (int i = 0; i < MSG_COUNT; ++i) {
+ producer.send(message, listener);
+ }
+
+ producer.send(message, listener);
+ } catch (JMSException e) {
+ LOG.warn("Caught unexpected error: {}", e.getMessage());
+ fail("No expected exception for this send.");
+ }
+
+ assertTrue("Did not get async callback", listener.awaitCompletion(2000, TimeUnit.SECONDS));
+ assertEquals(MSG_COUNT, listener.errorCount);
+ assertEquals(1, listener.successCount);
+ assertNotNull(listener.exception);
+ assertNotNull(listener.message);
+ assertTrue(listener.message instanceof TextMessage);
+
+ connection.close();
+ }
+ }
+
private JmsConnection establishAnonymousConnecton(TestAmqpPeer... peers) throws JMSException {
return establishAnonymousConnecton(null, null, peers);
}
@@ -1076,4 +1227,47 @@ public class FailoverIntegrationTest extends QpidJmsTestCase {
private String createPeerURI(TestAmqpPeer peer, String params) {
return "amqp://localhost:" + peer.getServerPort() + (params != null ? "?" + params : "");
}
+
+ private class TestJmsCompletionListener implements JmsCompletionListener {
+
+ private final CountDownLatch completed;
+
+ public volatile int successCount;
+ public volatile int errorCount;
+
+ public volatile Message message;
+ public volatile Exception exception;
+
+ public TestJmsCompletionListener() {
+ this(1);
+ }
+
+ public TestJmsCompletionListener(int expected) {
+ this.completed = new CountDownLatch(expected);
+ }
+
+ public boolean awaitCompletion(long timeout, TimeUnit units) throws InterruptedException {
+ return completed.await(timeout, units);
+ }
+
+ @Override
+ public void onCompletion(Message message) {
+ LOG.info("JmsCompletionListener onCompletion called with message: {}", message);
+ this.message = message;
+ this.successCount++;
+
+ completed.countDown();
+ }
+
+ @Override
+ public void onException(Message message, Exception exception) {
+ LOG.info("JmsCompletionListener onException called with message: {} error {}", message, exception);
+
+ this.message = message;
+ this.exception = exception;
+ this.errorCount++;
+
+ completed.countDown();
+ }
+ }
}
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/3a03663b/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/mock/MockProvider.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/mock/MockProvider.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/mock/MockProvider.java
index 210ce2c..35d305c 100644
--- a/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/mock/MockProvider.java
+++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/mock/MockProvider.java
@@ -274,7 +274,17 @@ public class MockProvider implements Provider {
try {
checkClosed();
stats.recordSendCall();
+
request.onSuccess();
+ if (envelope.isCompletionRequired()) {
+ if (configuration.isDelayCompletionCalls()) {
+ context.recordPendingCompletion(MockProvider.this, envelope);
+ } else {
+ if (listener != null) {
+ listener.onCompletedMessageSend(envelope);
+ }
+ }
+ }
} catch (Exception error) {
request.onFailure(error);
}
@@ -422,7 +432,6 @@ public class MockProvider implements Provider {
});
}
-
/**
* Switch state to closed without sending any notifications
*/
@@ -489,7 +498,6 @@ public class MockProvider implements Provider {
//----- Implementation details -------------------------------------------//
-
private void checkClosed() throws ProviderClosedException {
if (closed.get()) {
throw new ProviderClosedException("This Provider is already closed");
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/3a03663b/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/mock/MockProviderConfiguration.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/mock/MockProviderConfiguration.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/mock/MockProviderConfiguration.java
index 7c78fff..d8c9019 100644
--- a/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/mock/MockProviderConfiguration.java
+++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/mock/MockProviderConfiguration.java
@@ -25,6 +25,8 @@ public class MockProviderConfiguration {
private boolean failOnStart;
private boolean failOnClose;
+ private boolean delayCompletionCalls;
+
public boolean isFailOnConnect() {
return failOnConnect;
}
@@ -48,4 +50,12 @@ public class MockProviderConfiguration {
public void setFailOnClose(boolean value) {
this.failOnClose = value;
}
+
+ public boolean isDelayCompletionCalls() {
+ return delayCompletionCalls;
+ }
+
+ public void setDelayCompletionCalls(boolean delayCompletionCalls) {
+ this.delayCompletionCalls = delayCompletionCalls;
+ }
}
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/3a03663b/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/mock/MockRemotePeer.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/mock/MockRemotePeer.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/mock/MockRemotePeer.java
index 99fbfbc..a964282 100644
--- a/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/mock/MockRemotePeer.java
+++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/mock/MockRemotePeer.java
@@ -18,10 +18,17 @@ package org.apache.qpid.jms.provider.mock;
import java.io.IOException;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
+import javax.jms.Destination;
+import javax.jms.JMSException;
+import javax.jms.Message;
+
+import org.apache.qpid.jms.message.JmsOutboundMessageDispatch;
import org.apache.qpid.jms.meta.JmsResource;
/**
@@ -42,6 +49,9 @@ public class MockRemotePeer {
private ResourceLifecycleFilter stopFilter;
private ResourceLifecycleFilter destroyFilter;
+ private final Map<Destination, List<PendingCompletion>> pendingCompletions =
+ new ConcurrentHashMap<Destination, List<PendingCompletion>>();
+
public void connect(MockProvider provider) throws IOException {
if (offline) {
throw new IOException();
@@ -146,4 +156,117 @@ public class MockRemotePeer {
public void setResourceDestroyFilter(ResourceLifecycleFilter filter) {
destroyFilter = filter;
}
+
+ //----- Controls handling of Message Send Completions --------------------//
+
+ public void recordPendingCompletion(MockProvider provider, JmsOutboundMessageDispatch envelope) {
+ Destination destination = envelope.getDestination();
+ if (!pendingCompletions.containsKey(destination)) {
+ pendingCompletions.put(destination, new ArrayList<PendingCompletion>());
+ }
+
+ pendingCompletions.get(destination).add(new PendingCompletion(provider, envelope));
+ }
+
+ public void completeAllPendingSends(Destination destination) {
+ if (pendingCompletions.containsKey(destination)) {
+
+ for (List<PendingCompletion> pendingSends : pendingCompletions.values()) {
+ for (PendingCompletion pending : pendingSends) {
+ pending.provider.getProviderListener().onCompletedMessageSend(pending.envelope);
+ }
+ }
+
+ pendingCompletions.remove(destination);
+ }
+ }
+
+ public void failAllPendingSends(Destination destination, Exception error) {
+ if (pendingCompletions.containsKey(destination)) {
+
+ for (List<PendingCompletion> pendingSends : pendingCompletions.values()) {
+ for (PendingCompletion pending : pendingSends) {
+ pending.provider.getProviderListener().onFailedMessageSend(pending.envelope, error);
+ }
+ }
+
+ pendingCompletions.remove(destination);
+ }
+ }
+
+ public void completePendingSend(Message message) throws JMSException {
+ List<PendingCompletion> pendingSends = pendingCompletions.get(message.getJMSDestination());
+ Iterator<PendingCompletion> iterator = pendingSends.iterator();
+ while (iterator.hasNext()) {
+ PendingCompletion pending = iterator.next();
+ if (pending.envelope.getMessage().getJMSMessageID().equals(message.getJMSMessageID())) {
+ pending.provider.getProviderListener().onCompletedMessageSend(pending.envelope);
+ iterator.remove();
+ }
+ }
+ }
+
+ public void completePendingSend(JmsOutboundMessageDispatch envelope) throws JMSException {
+ List<PendingCompletion> pendingSends = pendingCompletions.get(envelope.getDestination());
+ Iterator<PendingCompletion> iterator = pendingSends.iterator();
+ while (iterator.hasNext()) {
+ PendingCompletion pending = iterator.next();
+ if (pending.envelope.getMessage().getJMSMessageID().equals(envelope.getMessage().getJMSMessageID())) {
+ pending.provider.getProviderListener().onCompletedMessageSend(pending.envelope);
+ iterator.remove();
+ }
+ }
+ }
+
+ public void failPendingSend(Message message, Exception error) throws JMSException {
+ List<PendingCompletion> pendingSends = pendingCompletions.get(message.getJMSDestination());
+ Iterator<PendingCompletion> iterator = pendingSends.iterator();
+ while (iterator.hasNext()) {
+ PendingCompletion pending = iterator.next();
+ if (pending.envelope.getMessage().getJMSMessageID().equals(message.getJMSMessageID())) {
+ pending.provider.getProviderListener().onFailedMessageSend(pending.envelope, error);
+ iterator.remove();
+ }
+ }
+ }
+
+ public void failPendingSend(JmsOutboundMessageDispatch envelope, Exception error) throws JMSException {
+ List<PendingCompletion> pendingSends = pendingCompletions.get(envelope.getDestination());
+ Iterator<PendingCompletion> iterator = pendingSends.iterator();
+ while (iterator.hasNext()) {
+ PendingCompletion pending = iterator.next();
+ if (pending.envelope.getMessage().getJMSMessageID().equals(envelope.getMessage().getJMSMessageID())) {
+ pending.provider.getProviderListener().onFailedMessageSend(pending.envelope, error);
+ iterator.remove();
+ }
+ }
+ }
+
+ public List<JmsOutboundMessageDispatch> getPendingCompletions(Destination destination) {
+ List<JmsOutboundMessageDispatch> result = null;
+
+ if (pendingCompletions.containsKey(destination)) {
+ result = new ArrayList<JmsOutboundMessageDispatch>();
+ List<PendingCompletion> pendingMessages = pendingCompletions.get(destination);
+ for (PendingCompletion pending : pendingMessages) {
+ result.add(pending.envelope);
+ }
+ } else {
+ result = Collections.emptyList();
+ }
+
+ return result;
+ }
+
+ private class PendingCompletion {
+
+ public final MockProvider provider;
+ public final JmsOutboundMessageDispatch envelope;
+
+ public PendingCompletion(MockProvider provider, JmsOutboundMessageDispatch envelope) {
+ this.provider = provider;
+ this.envelope = envelope;
+ }
+
+ }
}
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org
[5/7] qpid-jms git commit: QPIDJMS-207 Adds dependency on JMS 2.0 API
and initial implementation.
Posted by ta...@apache.org.
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/0c39522c/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/JmsMessagePropertyIntercepter.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/JmsMessagePropertyIntercepter.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/JmsMessagePropertyIntercepter.java
index 418d85c..bb2eb0b 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/JmsMessagePropertyIntercepter.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/JmsMessagePropertyIntercepter.java
@@ -16,6 +16,8 @@
*/
package org.apache.qpid.jms.message;
+import static org.apache.qpid.jms.message.JmsMessagePropertySupport.checkPropertyNameIsValid;
+import static org.apache.qpid.jms.message.JmsMessagePropertySupport.checkValidObject;
import static org.apache.qpid.jms.message.JmsMessageSupport.JMSX_DELIVERY_COUNT;
import static org.apache.qpid.jms.message.JmsMessageSupport.JMSX_GROUPID;
import static org.apache.qpid.jms.message.JmsMessageSupport.JMSX_GROUPSEQ;
@@ -42,7 +44,6 @@ import javax.jms.DeliveryMode;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
-import javax.jms.MessageFormatException;
import org.apache.qpid.jms.exceptions.JmsExceptionSupport;
import org.apache.qpid.jms.util.TypeConversionSupport;
@@ -808,74 +809,4 @@ public class JmsMessagePropertyIntercepter {
return names;
}
-
- //----- Property Validation Methods --------------------------------------//
-
- private static void checkPropertyNameIsValid(String propertyName, boolean validateNames) throws IllegalArgumentException {
- if (propertyName == null) {
- throw new IllegalArgumentException("Property name must not be null");
- } else if (propertyName.length() == 0) {
- throw new IllegalArgumentException("Property name must not be the empty string");
- }
-
- if (validateNames) {
- checkIdentifierLetterAndDigitRequirements(propertyName);
- checkIdentifierIsntNullTrueFalse(propertyName);
- checkIdentifierIsntLogicOperator(propertyName);
- }
- }
-
- private static void checkIdentifierIsntLogicOperator(String identifier) {
- // Identifiers cannot be NOT, AND, OR, BETWEEN, LIKE, IN, IS, or ESCAPE.
- if ("NOT".equals(identifier) || "AND".equals(identifier) || "OR".equals(identifier) ||
- "BETWEEN".equals(identifier) || "LIKE".equals(identifier) || "IN".equals(identifier) ||
- "IS".equals(identifier) || "ESCAPE".equals(identifier)) {
-
- throw new IllegalArgumentException("Identifier not allowed in JMS: '" + identifier + "'");
- }
- }
-
- private static void checkIdentifierIsntNullTrueFalse(String identifier) {
- // Identifiers cannot be the names NULL, TRUE, and FALSE.
- if ("NULL".equals(identifier) || "TRUE".equals(identifier) || "FALSE".equals(identifier)) {
- throw new IllegalArgumentException("Identifier not allowed in JMS: '" + identifier + "'");
- }
- }
-
- private static void checkIdentifierLetterAndDigitRequirements(String identifier) {
- // An identifier is an unlimited-length sequence of letters and digits, the first of
- // which must be a letter. A letter is any character for which the method
- // Character.isJavaLetter returns true. This includes '_' and '$'. A letter or digit
- // is any character for which the method Character.isJavaLetterOrDigit returns true.
- char startChar = identifier.charAt(0);
- if (!(Character.isJavaIdentifierStart(startChar))) {
- throw new IllegalArgumentException("Identifier does not begin with a valid JMS identifier start character: '" + identifier + "' ");
- }
-
- // JMS part character
- int length = identifier.length();
- for (int i = 1; i < length; i++) {
- char ch = identifier.charAt(i);
- if (!(Character.isJavaIdentifierPart(ch))) {
- throw new IllegalArgumentException("Identifier contains invalid JMS identifier character '" + ch + "': '" + identifier + "' ");
- }
- }
- }
-
- private static void checkValidObject(Object value) throws MessageFormatException {
- boolean valid = value instanceof Boolean ||
- value instanceof Byte ||
- value instanceof Short ||
- value instanceof Integer ||
- value instanceof Long ||
- value instanceof Float ||
- value instanceof Double ||
- value instanceof Character ||
- value instanceof String ||
- value == null;
-
- if (!valid) {
- throw new MessageFormatException("Only objectified primitive objects and String types are allowed but was: " + value + " type: " + value.getClass());
- }
- }
}
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/0c39522c/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/JmsMessagePropertySupport.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/JmsMessagePropertySupport.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/JmsMessagePropertySupport.java
new file mode 100644
index 0000000..2c3576c
--- /dev/null
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/JmsMessagePropertySupport.java
@@ -0,0 +1,124 @@
+/*
+ * 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.jms.message;
+
+import javax.jms.JMSException;
+import javax.jms.MessageFormatException;
+
+import org.apache.qpid.jms.util.TypeConversionSupport;
+
+/**
+ * Provides methods for use when working with JMS Message Properties and their values.
+ */
+public class JmsMessagePropertySupport {
+
+ //----- Conversions Validation for Message Properties --------------------//
+
+ @SuppressWarnings("unchecked")
+ public static <T> T convertPropertyTo(String name, Object value, Class<T> target) throws JMSException {
+ if (value == null) {
+ if (Boolean.class.equals(target)) {
+ return (T) Boolean.FALSE;
+ } else if (Float.class.equals(target) || Double.class.equals(target)) {
+ throw new NullPointerException("property " + name + " was null");
+ } else if (Number.class.isAssignableFrom(target)) {
+ throw new NumberFormatException("property " + name + " was null");
+ } else {
+ return null;
+ }
+ }
+
+ T rc = (T) TypeConversionSupport.convert(value, target);
+ if (rc == null) {
+ throw new MessageFormatException("Property " + name + " was a " + value.getClass().getName() + " and cannot be read as a " + target.getName());
+ }
+
+ return rc;
+ }
+
+ //----- Property Name Validation Methods ---------------------------------//
+
+ public static void checkPropertyNameIsValid(String propertyName, boolean validateNames) throws IllegalArgumentException {
+ if (propertyName == null) {
+ throw new IllegalArgumentException("Property name must not be null");
+ } else if (propertyName.length() == 0) {
+ throw new IllegalArgumentException("Property name must not be the empty string");
+ }
+
+ if (validateNames) {
+ checkIdentifierLetterAndDigitRequirements(propertyName);
+ checkIdentifierIsntNullTrueFalse(propertyName);
+ checkIdentifierIsntLogicOperator(propertyName);
+ }
+ }
+
+ public static void checkIdentifierIsntLogicOperator(String identifier) {
+ // Identifiers cannot be NOT, AND, OR, BETWEEN, LIKE, IN, IS, or ESCAPE.
+ if ("NOT".equals(identifier) || "AND".equals(identifier) || "OR".equals(identifier) ||
+ "BETWEEN".equals(identifier) || "LIKE".equals(identifier) || "IN".equals(identifier) ||
+ "IS".equals(identifier) || "ESCAPE".equals(identifier)) {
+
+ throw new IllegalArgumentException("Identifier not allowed in JMS: '" + identifier + "'");
+ }
+ }
+
+ public static void checkIdentifierIsntNullTrueFalse(String identifier) {
+ // Identifiers cannot be the names NULL, TRUE, and FALSE.
+ if ("NULL".equals(identifier) || "TRUE".equals(identifier) || "FALSE".equals(identifier)) {
+ throw new IllegalArgumentException("Identifier not allowed in JMS: '" + identifier + "'");
+ }
+ }
+
+ public static void checkIdentifierLetterAndDigitRequirements(String identifier) {
+ // An identifier is an unlimited-length sequence of letters and digits, the first of
+ // which must be a letter. A letter is any character for which the method
+ // Character.isJavaLetter returns true. This includes '_' and '$'. A letter or digit
+ // is any character for which the method Character.isJavaLetterOrDigit returns true.
+ char startChar = identifier.charAt(0);
+ if (!(Character.isJavaIdentifierStart(startChar))) {
+ throw new IllegalArgumentException("Identifier does not begin with a valid JMS identifier start character: '" + identifier + "' ");
+ }
+
+ // JMS part character
+ int length = identifier.length();
+ for (int i = 1; i < length; i++) {
+ char ch = identifier.charAt(i);
+ if (!(Character.isJavaIdentifierPart(ch))) {
+ throw new IllegalArgumentException("Identifier contains invalid JMS identifier character '" + ch + "': '" + identifier + "' ");
+ }
+ }
+ }
+
+ //----- Property Type Validation Methods ---------------------------------//
+
+ public static void checkValidObject(Object value) throws MessageFormatException {
+ boolean valid = value instanceof Boolean ||
+ value instanceof Byte ||
+ value instanceof Short ||
+ value instanceof Integer ||
+ value instanceof Long ||
+ value instanceof Float ||
+ value instanceof Double ||
+ value instanceof Character ||
+ value instanceof String ||
+ value == null;
+
+ if (!valid) {
+ throw new MessageFormatException("Only objectified primitive objects and String types are allowed but was: " + value + " type: " + value.getClass());
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/0c39522c/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/JmsObjectMessage.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/JmsObjectMessage.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/JmsObjectMessage.java
index 902719b..e8e0d22 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/JmsObjectMessage.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/JmsObjectMessage.java
@@ -60,6 +60,25 @@ public class JmsObjectMessage extends JmsMessage implements ObjectMessage {
}
@Override
+ public boolean isBodyAssignableTo(@SuppressWarnings("rawtypes") Class target) throws JMSException {
+ if (!facade.hasBody()) {
+ return true;
+ }
+
+ return Serializable.class == target || Object.class == target || target.isInstance(getObject());
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ protected <T> T doGetBody(Class<T> asType) throws JMSException {
+ try {
+ return (T) getObject();
+ } catch (JMSException e) {
+ throw new MessageFormatException("Failed to read Object: " + e.getMessage());
+ }
+ }
+
+ @Override
public String toString() {
return "JmsObjectMessageFacade { " + facade.toString() + " }";
}
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/0c39522c/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/JmsStreamMessage.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/JmsStreamMessage.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/JmsStreamMessage.java
index b334249..c9765f3 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/JmsStreamMessage.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/JmsStreamMessage.java
@@ -475,6 +475,11 @@ public class JmsStreamMessage extends JmsMessage implements StreamMessage {
return "JmsStreamMessage { " + facade.toString() + " }";
}
+ @Override
+ public boolean isBodyAssignableTo(@SuppressWarnings("rawtypes") Class target) throws JMSException {
+ return false;
+ }
+
private void checkBytesInFlight() throws MessageFormatException {
if (remainingBytes != NO_BYTES_IN_FLIGHT) {
throw new MessageFormatException(
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/0c39522c/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/JmsTextMessage.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/JmsTextMessage.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/JmsTextMessage.java
index 69aefe8..9647b57 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/JmsTextMessage.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/JmsTextMessage.java
@@ -22,6 +22,7 @@ import javax.jms.TextMessage;
import org.apache.qpid.jms.message.facade.JmsTextMessageFacade;
+@SuppressWarnings("unchecked")
public class JmsTextMessage extends JmsMessage implements TextMessage {
private final JmsTextMessageFacade facade;
@@ -57,4 +58,14 @@ public class JmsTextMessage extends JmsMessage implements TextMessage {
public String toString() {
return "JmsTextMessage { " + facade.toString() + " }";
}
+
+ @Override
+ public boolean isBodyAssignableTo(@SuppressWarnings("rawtypes") Class target) throws JMSException {
+ return facade.hasBody() ? target.isAssignableFrom(String.class) : true;
+ }
+
+ @Override
+ protected <T> T doGetBody(Class<T> asType) throws JMSException {
+ return (T) getText();
+ }
}
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/0c39522c/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/facade/JmsBytesMessageFacade.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/facade/JmsBytesMessageFacade.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/facade/JmsBytesMessageFacade.java
index 7321a8e..73117b9 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/facade/JmsBytesMessageFacade.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/facade/JmsBytesMessageFacade.java
@@ -86,4 +86,10 @@ public interface JmsBytesMessageFacade extends JmsMessageFacade {
* @return the number of bytes contained in the body of the message.
*/
int getBodyLength();
+
+ /**
+ * @return a copy of the bytes contained in the body of the message.
+ */
+ byte[] copyBody();
+
}
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/0c39522c/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/facade/JmsMessageFacade.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/facade/JmsMessageFacade.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/facade/JmsMessageFacade.java
index 1741251..d5616f5 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/facade/JmsMessageFacade.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/facade/JmsMessageFacade.java
@@ -315,6 +315,25 @@ public interface JmsMessageFacade {
void setExpiration(long expiration);
/**
+ * Returns the set delivery time for this message.
+ *
+ * The value should be returned as an absolute time given in GMT time.
+ *
+ * @return the earliest time that the message should be made available for delivery.
+ */
+ long getDeliveryTime();
+
+ /**
+ * Sets an desired delivery time on this message.
+ *
+ * The delivery time will be given as an absolute time in GMT time.
+ *
+ * @param deliveryTime
+ * the earliest time that the message should be made available for delivery.
+ */
+ void setDeliveryTime(long deliveryTime);
+
+ /**
* Gets the Destination value that was assigned to this message at the time it was
* sent.
*
@@ -429,4 +448,11 @@ public interface JmsMessageFacade {
*/
void setProviderMessageIdObject(Object messageId);
+ /**
+ * Returns true if the underlying message has a body, false if the body is empty.
+ *
+ * @return true if the underlying message has a body, false if the body is empty.
+ */
+ boolean hasBody();
+
}
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/0c39522c/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/message/AmqpJmsBytesMessageFacade.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/message/AmqpJmsBytesMessageFacade.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/message/AmqpJmsBytesMessageFacade.java
index 369dfa9..6b63fb7 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/message/AmqpJmsBytesMessageFacade.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/message/AmqpJmsBytesMessageFacade.java
@@ -19,10 +19,6 @@ package org.apache.qpid.jms.provider.amqp.message;
import static org.apache.qpid.jms.provider.amqp.message.AmqpMessageSupport.JMS_BYTES_MESSAGE;
import static org.apache.qpid.jms.provider.amqp.message.AmqpMessageSupport.JMS_MSG_TYPE;
import static org.apache.qpid.jms.provider.amqp.message.AmqpMessageSupport.OCTET_STREAM_CONTENT_TYPE;
-import io.netty.buffer.ByteBuf;
-import io.netty.buffer.ByteBufInputStream;
-import io.netty.buffer.ByteBufOutputStream;
-import io.netty.buffer.Unpooled;
import java.io.IOException;
import java.io.InputStream;
@@ -40,6 +36,11 @@ import org.apache.qpid.proton.amqp.messaging.Data;
import org.apache.qpid.proton.amqp.messaging.Section;
import org.apache.qpid.proton.message.Message;
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.ByteBufInputStream;
+import io.netty.buffer.ByteBufOutputStream;
+import io.netty.buffer.Unpooled;
+
/**
* A JmsBytesMessageFacade that wraps around Proton AMQP Message instances to provide
* access to the underlying bytes contained in the message.
@@ -216,6 +217,21 @@ public class AmqpJmsBytesMessageFacade extends AmqpJmsMessageFacade implements J
}
@Override
+ public boolean hasBody() {
+ return getBinaryFromBody().getLength() != 0;
+ }
+
+ @Override
+ public byte[] copyBody() {
+ Binary content = getBinaryFromBody();
+ byte[] result = new byte[content.getLength()];
+
+ System.arraycopy(content.getArray(), content.getArrayOffset(), result, 0, content.getLength());
+
+ return result;
+ }
+
+ @Override
public void onSend(long producerTtl) throws JMSException {
super.onSend(producerTtl);
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/0c39522c/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/message/AmqpJmsMapMessageFacade.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/message/AmqpJmsMapMessageFacade.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/message/AmqpJmsMapMessageFacade.java
index dc2cf0b..fe242f7 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/message/AmqpJmsMapMessageFacade.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/message/AmqpJmsMapMessageFacade.java
@@ -141,6 +141,11 @@ public class AmqpJmsMapMessageFacade extends AmqpJmsMessageFacade implements Jms
messageBodyMap.clear();
}
+ @Override
+ public boolean hasBody() {
+ return !messageBodyMap.isEmpty();
+ }
+
private void initializeEmptyBody() {
// Using LinkedHashMap because AMQP map equality considers order,
// so we should behave in as predictable a manner as possible
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/0c39522c/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/message/AmqpJmsMessageFacade.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/message/AmqpJmsMessageFacade.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/message/AmqpJmsMessageFacade.java
index f98dccc..82f63e0 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/message/AmqpJmsMessageFacade.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/message/AmqpJmsMessageFacade.java
@@ -553,6 +553,18 @@ public class AmqpJmsMessageFacade implements JmsMessageFacade {
}
}
+ @Override
+ public long getDeliveryTime() {
+ // TODO Auto-generated method stub
+ return 0;
+ }
+
+ @Override
+ public void setDeliveryTime(long deliveryTime) {
+ // TODO Auto-generated method stub
+
+ }
+
/**
* Sets a value which will be used to override any ttl value that may otherwise be set
* based on the expiration value when sending the underlying AMQP message. A value of 0
@@ -702,6 +714,11 @@ public class AmqpJmsMessageFacade implements JmsMessageFacade {
}
}
+ @Override
+ public boolean hasBody() {
+ return message.getBody() == null;
+ }
+
/**
* @return the true AMQP Message instance wrapped by this Facade.
*/
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/0c39522c/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/message/AmqpJmsObjectMessageFacade.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/message/AmqpJmsObjectMessageFacade.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/message/AmqpJmsObjectMessageFacade.java
index fabefed..f4b541f 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/message/AmqpJmsObjectMessageFacade.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/message/AmqpJmsObjectMessageFacade.java
@@ -125,6 +125,11 @@ public class AmqpJmsObjectMessageFacade extends AmqpJmsMessageFacade implements
}
@Override
+ public boolean hasBody() {
+ return delegate.hasBody();
+ }
+
+ @Override
public void onSend(long producerTtl) throws JMSException {
super.onSend(producerTtl);
delegate.onSend();
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/0c39522c/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/message/AmqpJmsStreamMessageFacade.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/message/AmqpJmsStreamMessageFacade.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/message/AmqpJmsStreamMessageFacade.java
index d63d4a3..64f1fbd 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/message/AmqpJmsStreamMessageFacade.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/message/AmqpJmsStreamMessageFacade.java
@@ -161,6 +161,11 @@ public class AmqpJmsStreamMessageFacade extends AmqpJmsMessageFacade implements
position = 0;
}
+ @Override
+ public boolean hasBody() {
+ return !list.isEmpty();
+ }
+
private List<Object> initializeEmptyBodyList(boolean useSequenceBody) {
List<Object> emptyList = new ArrayList<Object>();
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/0c39522c/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/message/AmqpJmsTextMessageFacade.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/message/AmqpJmsTextMessageFacade.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/message/AmqpJmsTextMessageFacade.java
index e3106e6..44ed9e6 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/message/AmqpJmsTextMessageFacade.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/message/AmqpJmsTextMessageFacade.java
@@ -135,6 +135,15 @@ public class AmqpJmsTextMessageFacade extends AmqpJmsMessageFacade implements Jm
setText(null);
}
+ @Override
+ public boolean hasBody() {
+ try {
+ return getText() != null;
+ } catch (JMSException e) {
+ return false;
+ }
+ }
+
Charset getCharset() {
return charset;
}
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/0c39522c/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/message/AmqpObjectTypeDelegate.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/message/AmqpObjectTypeDelegate.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/message/AmqpObjectTypeDelegate.java
index 7657343..3c1fa12 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/message/AmqpObjectTypeDelegate.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/message/AmqpObjectTypeDelegate.java
@@ -64,4 +64,7 @@ public interface AmqpObjectTypeDelegate {
void copyInto(AmqpObjectTypeDelegate copy) throws Exception;
boolean isAmqpTypeEncoded();
+
+ boolean hasBody();
+
}
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/0c39522c/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/message/AmqpSerializedObjectDelegate.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/message/AmqpSerializedObjectDelegate.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/message/AmqpSerializedObjectDelegate.java
index 618d123..ec73ba9 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/message/AmqpSerializedObjectDelegate.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/message/AmqpSerializedObjectDelegate.java
@@ -194,4 +194,13 @@ public class AmqpSerializedObjectDelegate implements AmqpObjectTypeDelegate, Tru
return true;
}
}
+
+ @Override
+ public boolean hasBody() {
+ try {
+ return getObject() != null;
+ } catch (Exception e) {
+ return false;
+ }
+ }
}
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/0c39522c/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/message/AmqpTypedObjectDelegate.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/message/AmqpTypedObjectDelegate.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/message/AmqpTypedObjectDelegate.java
index cc1038f..1296eaa 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/message/AmqpTypedObjectDelegate.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/message/AmqpTypedObjectDelegate.java
@@ -168,6 +168,15 @@ public class AmqpTypedObjectDelegate implements AmqpObjectTypeDelegate {
return true;
}
+ @Override
+ public boolean hasBody() {
+ try {
+ return getObject() != null;
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
//----- Internal implementation ------------------------------------------//
private boolean isSupportedAmqpValueObjectType(Serializable serializable) {
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/0c39522c/qpid-jms-client/src/test/java/org/apache/qpid/jms/JmsConnectionTest.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/JmsConnectionTest.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/JmsConnectionTest.java
index 3f5b84b..4928faa 100644
--- a/qpid-jms-client/src/test/java/org/apache/qpid/jms/JmsConnectionTest.java
+++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/JmsConnectionTest.java
@@ -266,8 +266,14 @@ public class JmsConnectionTest {
}
@Test(timeout=30000, expected=JMSException.class)
- public void testCreateDurableConnectionConsumer() throws Exception {
+ public void testCreateSharedConnectionConsumer() throws Exception {
connection = new JmsConnection("ID:TEST:1", provider, clientIdGenerator);
- connection.createDurableConnectionConsumer(new JmsTopic(), "id", "", null, 1);
+ connection.createSharedConnectionConsumer(new JmsTopic(), "id", "", null, 1);
+ }
+
+ @Test(timeout=30000, expected=JMSException.class)
+ public void testCreateSharedDurableConnectionConsumer() throws Exception {
+ connection = new JmsConnection("ID:TEST:1", provider, clientIdGenerator);
+ connection.createSharedDurableConnectionConsumer(new JmsTopic(), "id", "", null, 1);
}
}
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/0c39522c/qpid-jms-client/src/test/java/org/apache/qpid/jms/JmsConnectionTestSupport.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/JmsConnectionTestSupport.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/JmsConnectionTestSupport.java
index 0f4a9ce..3986f8b 100644
--- a/qpid-jms-client/src/test/java/org/apache/qpid/jms/JmsConnectionTestSupport.java
+++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/JmsConnectionTestSupport.java
@@ -18,6 +18,8 @@ package org.apache.qpid.jms;
import java.net.URI;
+import javax.jms.JMSContext;
+
import org.apache.qpid.jms.provider.Provider;
import org.apache.qpid.jms.provider.ProviderListener;
import org.apache.qpid.jms.provider.mock.MockProviderFactory;
@@ -51,6 +53,13 @@ public class JmsConnectionTestSupport extends QpidJmsTestCase {
});
}
+ protected JmsContext createJMSContextToMockProvider() throws Exception {
+ JmsConnection connection = new JmsConnection("ID:TEST:1", createMockProvider(), clientIdGenerator);
+ JmsContext context = new JmsContext(connection, JMSContext.AUTO_ACKNOWLEDGE);
+
+ return context;
+ }
+
protected JmsConnection createConnectionToMockProvider() throws Exception {
return new JmsConnection("ID:TEST:1", createMockProvider(), clientIdGenerator);
}
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/0c39522c/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/ConnectionIntegrationTest.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/ConnectionIntegrationTest.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/ConnectionIntegrationTest.java
index a3a6ce2..2e559ac 100644
--- a/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/ConnectionIntegrationTest.java
+++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/ConnectionIntegrationTest.java
@@ -106,6 +106,30 @@ public class ConnectionIntegrationTest extends QpidJmsTestCase {
}
@Test(timeout = 20000)
+ public void testCreateAutoAckSessionByDefault() throws Exception {
+ try (TestAmqpPeer testPeer = new TestAmqpPeer();) {
+ Connection connection = testFixture.establishConnecton(testPeer);
+ testPeer.expectBegin();
+ Session session = connection.createSession();
+ assertNotNull("Session should not be null", session);
+ testPeer.expectClose();
+ connection.close();
+ }
+ }
+
+ @Test(timeout = 20000)
+ public void testCreateAutoAckSessionUsingAckModeOnlyMethod() throws Exception {
+ try (TestAmqpPeer testPeer = new TestAmqpPeer();) {
+ Connection connection = testFixture.establishConnecton(testPeer);
+ testPeer.expectBegin();
+ Session session = connection.createSession(Session.AUTO_ACKNOWLEDGE);
+ assertNotNull("Session should not be null", session);
+ testPeer.expectClose();
+ connection.close();
+ }
+ }
+
+ @Test(timeout = 20000)
public void testCreateTransactedSession() throws Exception {
try (TestAmqpPeer testPeer = new TestAmqpPeer();) {
Connection connection = testFixture.establishConnecton(testPeer);
@@ -132,6 +156,32 @@ public class ConnectionIntegrationTest extends QpidJmsTestCase {
}
@Test(timeout = 20000)
+ public void testCreateTransactedSessionUsingAckModeOnlyMethod() throws Exception {
+ try (TestAmqpPeer testPeer = new TestAmqpPeer();) {
+ Connection connection = testFixture.establishConnecton(testPeer);
+
+ testPeer.expectBegin();
+ // Expect the session, with an immediate link to the transaction coordinator
+ // using a target with the expected capabilities only.
+ CoordinatorMatcher txCoordinatorMatcher = new CoordinatorMatcher();
+ txCoordinatorMatcher.withCapabilities(arrayContaining(TxnCapability.LOCAL_TXN));
+ testPeer.expectSenderAttach(txCoordinatorMatcher, false, false);
+
+ // First expect an unsettled 'declare' transfer to the txn coordinator, and
+ // reply with a declared disposition state containing the txnId.
+ Binary txnId = new Binary(new byte[]{ (byte) 1, (byte) 2, (byte) 3, (byte) 4});
+ testPeer.expectDeclare(txnId);
+ testPeer.expectDischarge(txnId, true);
+ testPeer.expectClose();
+
+ Session session = connection.createSession(Session.SESSION_TRANSACTED);
+ assertNotNull("Session should not be null", session);
+
+ connection.close();
+ }
+ }
+
+ @Test(timeout = 20000)
public void testCreateTransactedSessionFailsWhenNoDetachResponseSent() throws Exception {
try (TestAmqpPeer testPeer = new TestAmqpPeer();) {
Connection connection = testFixture.establishConnecton(testPeer);
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/0c39522c/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/IntegrationTestFixture.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/IntegrationTestFixture.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/IntegrationTestFixture.java
index c0a3cbc..7c33fc1 100644
--- a/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/IntegrationTestFixture.java
+++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/IntegrationTestFixture.java
@@ -24,6 +24,7 @@ import java.util.Map;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
+import javax.jms.JMSContext;
import javax.jms.JMSException;
import org.apache.qpid.jms.JmsConnectionFactory;
@@ -61,22 +62,80 @@ public class IntegrationTestFixture {
// Each connection creates a session for managing temporary destinations etc
testPeer.expectBegin();
- String scheme = ssl ? "amqps" : "amqp";
- final String baseURI = scheme + "://localhost:" + testPeer.getServerPort();
- String remoteURI = baseURI;
- if (optionsString != null) {
- remoteURI = baseURI + optionsString;
- }
+ String remoteURI = buildURI(testPeer, ssl, optionsString);
ConnectionFactory factory = new JmsConnectionFactory(remoteURI);
Connection connection = factory.createConnection("guest", "guest");
- if(setClientId) {
+ if (setClientId) {
// Set a clientId to provoke the actual AMQP connection process to occur.
connection.setClientID("clientName");
}
assertNull(testPeer.getThrowable());
+
return connection;
}
+
+ JMSContext createJMSContext(TestAmqpPeer testPeer) throws JMSException {
+ return createJMSContext(testPeer, null, null, null);
+ }
+
+ JMSContext createJMSContext(TestAmqpPeer testPeer, int sessionMode) throws JMSException {
+ return createJMSContext(testPeer, false, null, null, null, true, sessionMode);
+ }
+
+ JMSContext createJMSContext(TestAmqpPeer testPeer, String optionsString) throws JMSException {
+ return createJMSContext(testPeer, optionsString, null, null);
+ }
+
+ JMSContext createJMSContext(TestAmqpPeer testPeer, Symbol[] serverCapabilities) throws JMSException {
+ return createJMSContext(testPeer, null, serverCapabilities, null);
+ }
+
+ JMSContext createJMSContext(TestAmqpPeer testPeer, Symbol[] serverCapabilities, Map<Symbol, Object> serverProperties) throws JMSException {
+ return createJMSContext(testPeer, null, serverCapabilities, serverProperties);
+ }
+
+ JMSContext createJMSContext(TestAmqpPeer testPeer, String optionsString, Symbol[] serverCapabilities, Map<Symbol, Object> serverProperties) throws JMSException {
+ return createJMSContext(testPeer, false, optionsString, serverCapabilities, serverProperties, true, JMSContext.AUTO_ACKNOWLEDGE);
+ }
+
+ JMSContext createJMSContext(TestAmqpPeer testPeer, boolean ssl, String optionsString, Symbol[] serverCapabilities, Map<Symbol, Object> serverProperties, boolean setClientId) throws JMSException {
+ return createJMSContext(testPeer, false, optionsString, serverCapabilities, serverProperties, setClientId, JMSContext.AUTO_ACKNOWLEDGE);
+ }
+
+ JMSContext createJMSContext(TestAmqpPeer testPeer, boolean ssl, String optionsString, Symbol[] serverCapabilities, Map<Symbol, Object> serverProperties, boolean setClientId, int sessionMode) throws JMSException {
+ Symbol[] desiredCapabilities = new Symbol[] { AmqpSupport.SOLE_CONNECTION_CAPABILITY };
+
+ testPeer.expectSaslPlainConnect("guest", "guest", desiredCapabilities, serverCapabilities, serverProperties);
+
+ // Each connection creates a session for managing temporary destinations etc
+ testPeer.expectBegin();
+
+ String remoteURI = buildURI(testPeer, ssl, optionsString);
+
+ ConnectionFactory factory = new JmsConnectionFactory(remoteURI);
+ JMSContext context = factory.createContext("guest", "guest", sessionMode);
+
+ if (setClientId) {
+ // Set a clientId to provoke the actual AMQP connection process to occur.
+ context.setClientID("clientName");
+ }
+
+ assertNull(testPeer.getThrowable());
+
+ return context;
+ }
+
+ String buildURI(TestAmqpPeer testPeer, boolean ssl, String optionsString) {
+ String scheme = ssl ? "amqps" : "amqp";
+ final String baseURI = scheme + "://localhost:" + testPeer.getServerPort();
+ String remoteURI = baseURI;
+ if (optionsString != null) {
+ remoteURI = baseURI + optionsString;
+ }
+
+ return remoteURI;
+ }
}
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/0c39522c/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/JMSConsumerIntegrationTest.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/JMSConsumerIntegrationTest.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/JMSConsumerIntegrationTest.java
new file mode 100644
index 0000000..adc546d
--- /dev/null
+++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/JMSConsumerIntegrationTest.java
@@ -0,0 +1,559 @@
+/*
+ * 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.jms.integration;
+
+import static org.hamcrest.Matchers.notNullValue;
+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 static org.junit.Assert.fail;
+
+import java.io.ByteArrayOutputStream;
+import java.io.ObjectOutputStream;
+import java.util.Arrays;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import javax.jms.IllegalStateRuntimeException;
+import javax.jms.JMSConsumer;
+import javax.jms.JMSContext;
+import javax.jms.JMSRuntimeException;
+import javax.jms.Message;
+import javax.jms.MessageFormatRuntimeException;
+import javax.jms.MessageListener;
+import javax.jms.Queue;
+
+import org.apache.qpid.jms.provider.amqp.message.AmqpMessageSupport;
+import org.apache.qpid.jms.test.QpidJmsTestCase;
+import org.apache.qpid.jms.test.Wait;
+import org.apache.qpid.jms.test.testpeer.TestAmqpPeer;
+import org.apache.qpid.jms.test.testpeer.basictypes.AmqpError;
+import org.apache.qpid.jms.test.testpeer.describedtypes.sections.AmqpValueDescribedType;
+import org.apache.qpid.jms.test.testpeer.describedtypes.sections.DataDescribedType;
+import org.apache.qpid.jms.test.testpeer.describedtypes.sections.MessageAnnotationsDescribedType;
+import org.apache.qpid.jms.test.testpeer.describedtypes.sections.PropertiesDescribedType;
+import org.apache.qpid.proton.amqp.Binary;
+import org.apache.qpid.proton.amqp.DescribedType;
+import org.apache.qpid.proton.amqp.Symbol;
+import org.apache.qpid.proton.amqp.UnsignedInteger;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class JMSConsumerIntegrationTest extends QpidJmsTestCase {
+
+ private static final Logger LOG = LoggerFactory.getLogger(JMSConsumerIntegrationTest.class);
+
+ private final IntegrationTestFixture testFixture = new IntegrationTestFixture();
+
+ @Test(timeout = 20000)
+ public void testCreateConsumer() throws Exception {
+ try (TestAmqpPeer testPeer = new TestAmqpPeer();) {
+ JMSContext context = testFixture.createJMSContext(testPeer);
+ testPeer.expectBegin();
+ testPeer.expectReceiverAttach();
+ testPeer.expectLinkFlow();
+
+ Queue queue = context.createQueue("test");
+ JMSConsumer consumer = context.createConsumer(queue);
+ assertNotNull(consumer);
+
+ testPeer.expectEnd();
+ testPeer.expectClose();
+ context.close();
+
+ testPeer.waitForAllHandlersToComplete(1000);
+ }
+ }
+
+ @Test(timeout = 20000)
+ public void testRemotelyCloseJMSConsumer() throws Exception {
+ try (TestAmqpPeer testPeer = new TestAmqpPeer();) {
+ JMSContext context = testFixture.createJMSContext(testPeer);
+
+ testPeer.expectBegin();
+
+ // Create a consumer, then remotely end it afterwards.
+ testPeer.expectReceiverAttach();
+ testPeer.expectLinkFlow();
+ testPeer.remotelyDetachLastOpenedLinkOnLastOpenedSession(true, true, AmqpError.RESOURCE_DELETED, "resource closed");
+
+ Queue queue = context.createQueue("myQueue");
+ final JMSConsumer consumer = context.createConsumer(queue);
+
+ // Verify the consumer gets marked closed
+ testPeer.waitForAllHandlersToComplete(1000);
+ assertTrue("JMSConsumer never closed.", Wait.waitFor(new Wait.Condition() {
+ @Override
+ public boolean isSatisified() throws Exception {
+ try {
+ consumer.getMessageListener();
+ } catch (IllegalStateRuntimeException jmsise) {
+ return true;
+ }
+ return false;
+ }
+ }, 10000, 10));
+
+ // Try closing it explicitly, should effectively no-op in client.
+ // The test peer will throw during close if it sends anything.
+ consumer.close();
+
+ testPeer.expectEnd();
+ testPeer.expectClose();
+ context.close();
+
+ testPeer.waitForAllHandlersToComplete(1000);
+ }
+ }
+
+ @Test(timeout = 20000)
+ public void testReceiveMessageWithReceiveZeroTimeout() throws Exception {
+ try (TestAmqpPeer testPeer = new TestAmqpPeer();) {
+ JMSContext context = testFixture.createJMSContext(testPeer);
+
+ testPeer.expectBegin();
+
+ Queue queue = context.createQueue("myQueue");
+
+ DescribedType amqpValueNullContent = new AmqpValueDescribedType(null);
+
+ testPeer.expectReceiverAttach();
+ testPeer.expectLinkFlowRespondWithTransfer(null, null, null, null, amqpValueNullContent);
+ testPeer.expectDispositionThatIsAcceptedAndSettled();
+
+ JMSConsumer messageConsumer = context.createConsumer(queue);
+ Message receivedMessage = messageConsumer.receive(0);
+
+ assertNotNull("A message should have been recieved", receivedMessage);
+
+ testPeer.expectEnd();
+ testPeer.expectClose();
+ context.close();
+
+ testPeer.waitForAllHandlersToComplete(2000);
+ }
+ }
+
+ @Test(timeout=20000)
+ public void testConsumerReceiveNoWaitThrowsIfConnectionLost() throws Exception {
+ try (TestAmqpPeer testPeer = new TestAmqpPeer();) {
+ JMSContext context = testFixture.createJMSContext(testPeer);
+
+ testPeer.expectBegin();
+
+ Queue queue = context.createQueue("queue");
+
+ testPeer.expectReceiverAttach();
+ testPeer.expectLinkFlow(false, notNullValue(UnsignedInteger.class));
+ testPeer.expectLinkFlow(true, notNullValue(UnsignedInteger.class));
+ testPeer.dropAfterLastHandler();
+
+ final JMSConsumer consumer = context.createConsumer(queue);
+
+ try {
+ consumer.receiveNoWait();
+ fail("An exception should have been thrown");
+ } catch (JMSRuntimeException jmsre) {
+ // Expected
+ }
+ }
+ }
+
+ @Test(timeout=20000)
+ public void testNoReceivedMessagesWhenConnectionNotStarted() throws Exception {
+ try (TestAmqpPeer testPeer = new TestAmqpPeer();) {
+ JMSContext context = testFixture.createJMSContext(testPeer);
+ context.setAutoStart(false);
+
+ testPeer.expectBegin();
+
+ Queue destination = context.createQueue(getTestName());
+
+ testPeer.expectReceiverAttach();
+ testPeer.expectLinkFlowRespondWithTransfer(null, null, null, null, new AmqpValueDescribedType("content"), 3);
+ testPeer.expectDispositionThatIsAcceptedAndSettled();
+
+ JMSConsumer consumer = context.createConsumer(destination);
+
+ assertNull(consumer.receive(100));
+
+ context.start();
+
+ assertNotNull(consumer.receive(2000));
+
+ testPeer.expectEnd();
+ testPeer.expectClose();
+ context.close();
+
+ testPeer.waitForAllHandlersToComplete(2000);
+ }
+ }
+
+ @Test(timeout=60000)
+ public void testSyncReceiveFailsWhenListenerSet() throws Exception {
+ try (TestAmqpPeer testPeer = new TestAmqpPeer();) {
+ JMSContext context = testFixture.createJMSContext(testPeer);
+
+ testPeer.expectBegin();
+
+ Queue destination = context.createQueue(getTestName());
+
+ testPeer.expectReceiverAttach();
+ testPeer.expectLinkFlow();
+
+ JMSConsumer consumer = context.createConsumer(destination);
+
+ consumer.setMessageListener(new MessageListener() {
+ @Override
+ public void onMessage(Message m) {
+ LOG.warn("Async consumer got unexpected Message: {}", m);
+ }
+ });
+
+ try {
+ consumer.receive();
+ fail("Should have thrown an exception.");
+ } catch (JMSRuntimeException ex) {
+ }
+
+ try {
+ consumer.receive(1000);
+ fail("Should have thrown an exception.");
+ } catch (JMSRuntimeException ex) {
+ }
+
+ try {
+ consumer.receiveNoWait();
+ fail("Should have thrown an exception.");
+ } catch (JMSRuntimeException ex) {
+ }
+
+ testPeer.expectEnd();
+ testPeer.expectClose();
+ context.close();
+
+ testPeer.waitForAllHandlersToComplete(2000);
+ }
+ }
+
+ @Test(timeout = 20000)
+ public void testReceiveBodyMapMessage() throws Exception {
+ try (TestAmqpPeer testPeer = new TestAmqpPeer();) {
+ JMSContext context = testFixture.createJMSContext(testPeer);
+
+ testPeer.expectBegin();
+
+ Queue queue = context.createQueue("myQueue");
+
+ // Prepare an AMQP message for the test peer to send, containing an
+ // AmqpValue section holding a map with entries for each supported type,
+ // and annotated as a JMS map message.
+ String myBoolKey = "myBool";
+ boolean myBool = true;
+ String myByteKey = "myByte";
+ byte myByte = 4;
+ String myBytesKey = "myBytes";
+ byte[] myBytes = myBytesKey.getBytes();
+ String myCharKey = "myChar";
+ char myChar = 'd';
+ String myDoubleKey = "myDouble";
+ double myDouble = 1234567890123456789.1234;
+ String myFloatKey = "myFloat";
+ float myFloat = 1.1F;
+ String myIntKey = "myInt";
+ int myInt = Integer.MAX_VALUE;
+ String myLongKey = "myLong";
+ long myLong = Long.MAX_VALUE;
+ String myShortKey = "myShort";
+ short myShort = 25;
+ String myStringKey = "myString";
+ String myString = myStringKey;
+
+ Map<String, Object> map = new LinkedHashMap<String, Object>();
+ map.put(myBoolKey, myBool);
+ map.put(myByteKey, myByte);
+ map.put(myBytesKey, new Binary(myBytes));// the underlying AMQP message uses Binary rather than byte[] directly.
+ map.put(myCharKey, myChar);
+ map.put(myDoubleKey, myDouble);
+ map.put(myFloatKey, myFloat);
+ map.put(myIntKey, myInt);
+ map.put(myLongKey, myLong);
+ map.put(myShortKey, myShort);
+ map.put(myStringKey, myString);
+
+ MessageAnnotationsDescribedType msgAnnotations = new MessageAnnotationsDescribedType();
+ msgAnnotations.setSymbolKeyedAnnotation(AmqpMessageSupport.JMS_MSG_TYPE, AmqpMessageSupport.JMS_MAP_MESSAGE);
+
+ DescribedType amqpValueSectionContent = new AmqpValueDescribedType(map);
+
+ // receive the message from the test peer
+ testPeer.expectReceiverAttach();
+ testPeer.expectLinkFlowRespondWithTransfer(null, msgAnnotations, null, null, amqpValueSectionContent);
+ testPeer.expectDispositionThatIsAcceptedAndSettled();
+ testPeer.expectEnd();
+ testPeer.expectClose();
+
+ JMSConsumer messageConsumer = context.createConsumer(queue);
+ @SuppressWarnings("unchecked")
+ Map<String, Object> receivedMap = messageConsumer.receiveBody(Map.class, 3000);
+
+ // verify the content is as expected
+ assertNotNull("Map was not received", receivedMap);
+
+ assertEquals("Unexpected boolean value", myBool, receivedMap.get(myBoolKey));
+ assertEquals("Unexpected byte value", myByte, receivedMap.get(myByteKey));
+ byte[] readBytes = (byte[]) receivedMap.get(myBytesKey);
+ assertTrue("Read bytes were not as expected: " + Arrays.toString(readBytes), Arrays.equals(myBytes, readBytes));
+ assertEquals("Unexpected char value", myChar, receivedMap.get(myCharKey));
+ assertEquals("Unexpected double value", myDouble, (double) receivedMap.get(myDoubleKey), 0.0);
+ assertEquals("Unexpected float value", myFloat, (float) receivedMap.get(myFloatKey), 0.0);
+ assertEquals("Unexpected int value", myInt, receivedMap.get(myIntKey));
+ assertEquals("Unexpected long value", myLong, receivedMap.get(myLongKey));
+ assertEquals("Unexpected short value", myShort, receivedMap.get(myShortKey));
+ assertEquals("Unexpected UTF value", myString, receivedMap.get(myStringKey));
+
+ context.close();
+
+ testPeer.waitForAllHandlersToComplete(3000);
+ }
+ }
+
+ @Test(timeout = 20000)
+ public void testReceiveBodyTextMessage() throws Exception {
+ try (TestAmqpPeer testPeer = new TestAmqpPeer();) {
+ JMSContext context = testFixture.createJMSContext(testPeer);
+
+ testPeer.expectBegin();
+
+ final String content = "Message-Content";
+ Queue queue = context.createQueue("myQueue");
+
+ DescribedType amqpValueContent = new AmqpValueDescribedType(content);
+
+ testPeer.expectReceiverAttach();
+ testPeer.expectLinkFlowRespondWithTransfer(null, null, null, null, amqpValueContent);
+ testPeer.expectDispositionThatIsAcceptedAndSettled();
+ testPeer.expectEnd();
+ testPeer.expectClose();
+
+ JMSConsumer messageConsumer = context.createConsumer(queue);
+ String received = messageConsumer.receiveBody(String.class, 3000);
+
+ assertNotNull(received);
+ assertEquals(content, received);
+
+ context.close();
+
+ testPeer.waitForAllHandlersToComplete(3000);
+ }
+ }
+
+ @Test(timeout = 20000)
+ public void testReceiveBodyObjectMessage() throws Exception {
+ try (TestAmqpPeer testPeer = new TestAmqpPeer();) {
+ JMSContext context = testFixture.createJMSContext(testPeer);
+
+ testPeer.expectBegin();
+
+ Queue queue = context.createQueue("myQueue");
+
+ PropertiesDescribedType properties = new PropertiesDescribedType();
+ properties.setContentType(Symbol.valueOf(AmqpMessageSupport.SERIALIZED_JAVA_OBJECT_CONTENT_TYPE));
+
+ String expectedContent = "expectedContent";
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ ObjectOutputStream oos = new ObjectOutputStream(baos);
+ oos.writeObject(expectedContent);
+ oos.flush();
+ oos.close();
+ byte[] bytes = baos.toByteArray();
+
+ MessageAnnotationsDescribedType msgAnnotations = new MessageAnnotationsDescribedType();
+ msgAnnotations.setSymbolKeyedAnnotation(AmqpMessageSupport.JMS_MSG_TYPE, AmqpMessageSupport.JMS_OBJECT_MESSAGE);
+
+ DescribedType dataContent = new DataDescribedType(new Binary(bytes));
+
+ testPeer.expectReceiverAttach();
+ testPeer.expectLinkFlowRespondWithTransfer(null, msgAnnotations, properties, null, dataContent);
+ testPeer.expectDispositionThatIsAcceptedAndSettled();
+ testPeer.expectEnd();
+ testPeer.expectClose();
+
+ JMSConsumer messageConsumer = context.createConsumer(queue);
+ String received = messageConsumer.receiveBody(String.class, 3000);
+
+ assertNotNull(received);
+ assertEquals(expectedContent, received);
+
+ context.close();
+
+ testPeer.waitForAllHandlersToComplete(3000);
+ }
+ }
+
+ @Test(timeout = 20000)
+ public void testReceiveBodyBytesMessage() throws Exception {
+ try (TestAmqpPeer testPeer = new TestAmqpPeer();) {
+ JMSContext context = testFixture.createJMSContext(testPeer);
+
+ testPeer.expectBegin();
+
+ Queue queue = context.createQueue("myQueue");
+
+ PropertiesDescribedType properties = new PropertiesDescribedType();
+ properties.setContentType(Symbol.valueOf(AmqpMessageSupport.OCTET_STREAM_CONTENT_TYPE));
+
+ MessageAnnotationsDescribedType msgAnnotations = null;
+ msgAnnotations = new MessageAnnotationsDescribedType();
+ msgAnnotations.setSymbolKeyedAnnotation(AmqpMessageSupport.JMS_MSG_TYPE, AmqpMessageSupport.JMS_BYTES_MESSAGE);
+
+ final byte[] expectedContent = "expectedContent".getBytes();
+ DescribedType dataContent = new DataDescribedType(new Binary(expectedContent));
+
+ testPeer.expectReceiverAttach();
+ testPeer.expectLinkFlowRespondWithTransfer(null, msgAnnotations, properties, null, dataContent);
+ testPeer.expectDispositionThatIsAcceptedAndSettled();
+
+ JMSConsumer messageConsumer = context.createConsumer(queue);
+ byte[] received = messageConsumer.receiveBody(byte[].class, 3000);
+ testPeer.waitForAllHandlersToComplete(3000);
+
+ assertNotNull(received);
+ assertTrue(Arrays.equals(expectedContent, received));
+
+ testPeer.expectEnd();
+ testPeer.expectClose();
+
+ context.close();
+
+ testPeer.waitForAllHandlersToComplete(3000);
+ }
+ }
+
+ @Test(timeout = 20000)
+ public void testReceiveBodyFailsDoesNotAcceptMessageAutoAck() throws Exception {
+ doTestReceiveBodyFailsDoesNotAcceptMessage(JMSContext.AUTO_ACKNOWLEDGE);
+ }
+
+ @Test(timeout = 20000)
+ public void testReceiveBodyFailsDoesNotAcceptMessageDupsOk() throws Exception {
+ doTestReceiveBodyFailsDoesNotAcceptMessage(JMSContext.DUPS_OK_ACKNOWLEDGE);
+ }
+
+ @Test(timeout = 20000)
+ public void testReceiveBodyFailsDoesNotAcceptMessageClientAck() throws Exception {
+ doTestReceiveBodyFailsDoesNotAcceptMessage(JMSContext.CLIENT_ACKNOWLEDGE);
+ }
+
+ public void doTestReceiveBodyFailsDoesNotAcceptMessage(int sessionMode) throws Exception {
+ try (TestAmqpPeer testPeer = new TestAmqpPeer();) {
+ JMSContext context = testFixture.createJMSContext(testPeer);
+
+ testPeer.expectBegin();
+
+ final String content = "Message-Content";
+ Queue queue = context.createQueue("myQueue");
+
+ DescribedType amqpValueContent = new AmqpValueDescribedType(content);
+
+ testPeer.expectReceiverAttach();
+ testPeer.expectLinkFlowRespondWithTransfer(null, null, null, null, amqpValueContent);
+ testPeer.expectEnd();
+ testPeer.expectClose();
+
+ JMSConsumer messageConsumer = context.createConsumer(queue);
+ try {
+ messageConsumer.receiveBody(Boolean.class, 3000);
+ fail("Should not read as Boolean type");
+ } catch (MessageFormatRuntimeException mfre) {
+ }
+
+ context.close();
+
+ testPeer.waitForAllHandlersToComplete(3000);
+ }
+ }
+
+ @Test(timeout = 20000)
+ public void testReceiveBodyFailsThenAcceptsOnSuccessfullyNextCallAutoAck() throws Exception {
+ doTestReceiveBodyFailsDoesNotAcceptMessage(JMSContext.AUTO_ACKNOWLEDGE);
+ }
+
+ @Test(timeout = 20000)
+ public void testReceiveBodyFailsThenAcceptsOnSuccessfullyNextCallDupsOk() throws Exception {
+ doTestReceiveBodyFailsDoesNotAcceptMessage(JMSContext.DUPS_OK_ACKNOWLEDGE);
+ }
+
+ @Test(timeout = 20000)
+ public void testReceiveBodyFailsThenGetNullOnNextAttemptClientAck() throws Exception {
+ doTestReceiveBodyFailsDoesNotAcceptMessage(JMSContext.CLIENT_ACKNOWLEDGE);
+ }
+
+ public void doTestReceiveBodyFailsThenCalledWithCorrectType(int sessionMode) throws Exception {
+ try (TestAmqpPeer testPeer = new TestAmqpPeer();) {
+ JMSContext context = testFixture.createJMSContext(testPeer);
+
+ testPeer.expectBegin();
+
+ final String content = "Message-Content";
+ Queue queue = context.createQueue("myQueue");
+
+ DescribedType amqpValueContent = new AmqpValueDescribedType(content);
+
+ testPeer.expectReceiverAttach();
+ testPeer.expectLinkFlowRespondWithTransfer(null, null, null, null, amqpValueContent);
+
+ JMSConsumer messageConsumer = context.createConsumer(queue);
+ try {
+ messageConsumer.receiveBody(Boolean.class, 3000);
+ fail("Should not read as Boolean type");
+ } catch (MessageFormatRuntimeException mfre) {
+ }
+
+ testPeer.waitForAllHandlersToComplete(3000);
+
+ if (sessionMode == JMSContext.AUTO_ACKNOWLEDGE ||
+ sessionMode == JMSContext.DUPS_OK_ACKNOWLEDGE) {
+
+ testPeer.expectDispositionThatIsAcceptedAndSettled();
+ }
+
+ String received = messageConsumer.receiveBody(String.class, 3000);
+
+ if (sessionMode == JMSContext.AUTO_ACKNOWLEDGE ||
+ sessionMode == JMSContext.DUPS_OK_ACKNOWLEDGE) {
+
+ assertNotNull(received);
+ assertEquals(content, received);
+ } else {
+ assertNull(received);
+ }
+
+ testPeer.expectEnd();
+ testPeer.expectClose();
+
+ context.close();
+
+ testPeer.waitForAllHandlersToComplete(3000);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/0c39522c/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/JMSContextIntegrationTest.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/JMSContextIntegrationTest.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/JMSContextIntegrationTest.java
new file mode 100644
index 0000000..7c3d2d1
--- /dev/null
+++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/JMSContextIntegrationTest.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.jms.integration;
+
+import static org.apache.qpid.jms.provider.amqp.AmqpSupport.ANONYMOUS_RELAY;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import java.util.UUID;
+
+import javax.jms.JMSContext;
+import javax.jms.JMSProducer;
+
+import org.apache.qpid.jms.test.QpidJmsTestCase;
+import org.apache.qpid.jms.test.testpeer.TestAmqpPeer;
+import org.apache.qpid.proton.amqp.Binary;
+import org.apache.qpid.proton.amqp.Symbol;
+import org.junit.Test;
+
+public class JMSContextIntegrationTest extends QpidJmsTestCase {
+
+ private final IntegrationTestFixture testFixture = new IntegrationTestFixture();
+
+ private Symbol[] SERVER_ANONYMOUS_RELAY = new Symbol[]{ANONYMOUS_RELAY};
+
+ @Test(timeout = 20000)
+ public void testCreateAndCloseContext() throws Exception {
+ try (TestAmqpPeer testPeer = new TestAmqpPeer();) {
+ JMSContext context = testFixture.createJMSContext(testPeer);
+ testPeer.expectClose();
+ context.close();
+
+ testPeer.waitForAllHandlersToComplete(1000);
+ }
+ }
+
+ @Test(timeout = 20000)
+ public void testCreateContextWithClientId() throws Exception {
+ try (TestAmqpPeer testPeer = new TestAmqpPeer();) {
+ JMSContext context = testFixture.createJMSContext(testPeer, false, null, null, null, true);
+ testPeer.expectClose();
+ context.close();
+
+ testPeer.waitForAllHandlersToComplete(1000);
+ }
+ }
+
+ @Test(timeout = 20000)
+ public void testCreateContextAndSetClientID() throws Exception {
+ try (TestAmqpPeer testPeer = new TestAmqpPeer();) {
+ JMSContext context = testFixture.createJMSContext(testPeer, false, null, null, null, false);
+ context.setClientID(UUID.randomUUID().toString());
+ testPeer.expectClose();
+ context.close();
+
+ testPeer.waitForAllHandlersToComplete(1000);
+ }
+ }
+
+ @Test(timeout = 20000)
+ public void testCreateAutoAckSessionByDefault() throws Exception {
+ try (TestAmqpPeer testPeer = new TestAmqpPeer();) {
+ JMSContext context = testFixture.createJMSContext(testPeer);
+ assertEquals(JMSContext.AUTO_ACKNOWLEDGE, context.getSessionMode());
+ testPeer.expectBegin();
+ context.createTopic("TopicName");
+ testPeer.expectEnd();
+ testPeer.expectClose();
+ context.close();
+
+ testPeer.waitForAllHandlersToComplete(1000);
+ }
+ }
+
+ @Test(timeout = 20000)
+ public void testCreateContextWithTransactedSessionMode() throws Exception {
+ Binary txnId = new Binary(new byte[]{ (byte) 5, (byte) 6, (byte) 7, (byte) 8});
+
+ try (TestAmqpPeer testPeer = new TestAmqpPeer();) {
+ JMSContext context = testFixture.createJMSContext(testPeer, JMSContext.SESSION_TRANSACTED);
+ assertEquals(JMSContext.SESSION_TRANSACTED, context.getSessionMode());
+
+ // Session should be created and a coordinator should be attached since this
+ // should be a TX session, then a new TX is declared, once closed the TX should
+ // be discharged as a roll back.
+ testPeer.expectBegin();
+ testPeer.expectCoordinatorAttach();
+ testPeer.expectDeclare(txnId);
+ testPeer.expectDischarge(txnId, true);
+ testPeer.expectEnd();
+ testPeer.expectClose();
+
+ context.createTopic("TopicName");
+
+ context.close();
+
+ testPeer.waitForAllHandlersToComplete(1000);
+ }
+ }
+
+ @Test(timeout = 20000)
+ public void testCreateContextFromContextWithSessionsActive() throws Exception {
+ try (TestAmqpPeer testPeer = new TestAmqpPeer();) {
+ JMSContext context = testFixture.createJMSContext(testPeer);
+ assertEquals(JMSContext.AUTO_ACKNOWLEDGE, context.getSessionMode());
+ testPeer.expectBegin();
+ context.createTopic("TopicName");
+
+ // Create a second should not create a new session yet, once a new connection is
+ // create on demand then close of the second context should only close the session
+ JMSContext other = context.createContext(JMSContext.CLIENT_ACKNOWLEDGE);
+ assertEquals(JMSContext.CLIENT_ACKNOWLEDGE, other.getSessionMode());
+ testPeer.expectBegin();
+ testPeer.expectEnd();
+ other.createTopic("TopicName");
+ other.close();
+
+ testPeer.waitForAllHandlersToComplete(1000);
+
+ // Now the connection should close down.
+ testPeer.expectEnd();
+ testPeer.expectClose();
+ context.close();
+
+ testPeer.waitForAllHandlersToComplete(1000);
+ }
+ }
+
+ @Test(timeout = 20000)
+ public void testOnlyOneProducerCreatedInSingleContext() throws Exception {
+ try (TestAmqpPeer testPeer = new TestAmqpPeer();) {
+ JMSContext context = testFixture.createJMSContext(testPeer, SERVER_ANONYMOUS_RELAY);
+ assertEquals(JMSContext.AUTO_ACKNOWLEDGE, context.getSessionMode());
+ testPeer.expectBegin();
+ testPeer.expectSenderAttach();
+
+ // One producer created should send an attach.
+ JMSProducer producer1 = context.createProducer();
+ assertNotNull(producer1);
+
+ // An additional one should not result in an attach
+ JMSProducer producer2 = context.createProducer();
+ assertNotNull(producer2);
+
+ testPeer.expectEnd();
+ testPeer.expectClose();
+ context.close();
+
+ testPeer.waitForAllHandlersToComplete(1000);
+ }
+ }
+
+ @Test(timeout = 20000)
+ public void testEachContextGetsItsOwnProducer() throws Exception {
+ try (TestAmqpPeer testPeer = new TestAmqpPeer();) {
+ JMSContext context = testFixture.createJMSContext(testPeer, SERVER_ANONYMOUS_RELAY);
+ assertEquals(JMSContext.AUTO_ACKNOWLEDGE, context.getSessionMode());
+ testPeer.expectBegin();
+ testPeer.expectSenderAttach();
+ testPeer.expectBegin();
+ testPeer.expectSenderAttach();
+
+ // One producer created should send an attach.
+ JMSProducer producer1 = context.createProducer();
+ assertNotNull(producer1);
+
+ // An additional one should not result in an attach
+ JMSContext other = context.createContext(JMSContext.AUTO_ACKNOWLEDGE);
+ JMSProducer producer2 = other.createProducer();
+ assertNotNull(producer2);
+
+ testPeer.expectEnd();
+ testPeer.expectEnd();
+ testPeer.expectClose();
+
+ other.close();
+ context.close();
+
+ testPeer.waitForAllHandlersToComplete(1000);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/0c39522c/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/JMSProducerIntegrationTest.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/JMSProducerIntegrationTest.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/JMSProducerIntegrationTest.java
new file mode 100644
index 0000000..4096c1f
--- /dev/null
+++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/JMSProducerIntegrationTest.java
@@ -0,0 +1,200 @@
+/*
+ * 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.jms.integration;
+
+import static org.apache.qpid.jms.provider.amqp.AmqpSupport.ANONYMOUS_RELAY;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.nullValue;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import javax.jms.JMSContext;
+import javax.jms.JMSProducer;
+import javax.jms.Message;
+import javax.jms.Queue;
+
+import org.apache.qpid.jms.test.QpidJmsTestCase;
+import org.apache.qpid.jms.test.testpeer.TestAmqpPeer;
+import org.apache.qpid.jms.test.testpeer.matchers.sections.ApplicationPropertiesSectionMatcher;
+import org.apache.qpid.jms.test.testpeer.matchers.sections.MessageAnnotationsSectionMatcher;
+import org.apache.qpid.jms.test.testpeer.matchers.sections.MessageHeaderSectionMatcher;
+import org.apache.qpid.jms.test.testpeer.matchers.sections.MessagePropertiesSectionMatcher;
+import org.apache.qpid.jms.test.testpeer.matchers.sections.TransferPayloadCompositeMatcher;
+import org.apache.qpid.proton.amqp.Symbol;
+import org.junit.Test;
+
+public class JMSProducerIntegrationTest extends QpidJmsTestCase {
+
+ private final IntegrationTestFixture testFixture = new IntegrationTestFixture();
+
+ private Symbol[] SERVER_ANONYMOUS_RELAY = new Symbol[]{ANONYMOUS_RELAY};
+
+ private static final String NULL_STRING_PROP = "nullStringProperty";
+ private static final String NULL_STRING_PROP_VALUE = null;
+ private static final String STRING_PROP = "stringProperty";
+ private static final String STRING_PROP_VALUE = "string";
+ private static final String BOOLEAN_PROP = "booleanProperty";
+ private static final boolean BOOLEAN_PROP_VALUE = true;
+ private static final String BYTE_PROP = "byteProperty";
+ private static final byte BYTE_PROP_VALUE = (byte)1;
+ private static final String SHORT_PROP = "shortProperty";
+ private static final short SHORT_PROP_VALUE = (short)1;
+ private static final String INT_PROP = "intProperty";
+ private static final int INT_PROP_VALUE = Integer.MAX_VALUE;
+ private static final String LONG_PROP = "longProperty";
+ private static final long LONG_PROP_VALUE = Long.MAX_VALUE;
+ private static final String FLOAT_PROP = "floatProperty";
+ private static final float FLOAT_PROP_VALUE = Float.MAX_VALUE;
+ private static final String DOUBLE_PROP = "doubleProperty";
+ private static final double DOUBLE_PROP_VALUE = Double.MAX_VALUE;
+
+ @Test(timeout = 20000)
+ public void testCreateProducer() throws Exception {
+ try (TestAmqpPeer testPeer = new TestAmqpPeer();) {
+ JMSContext context = testFixture.createJMSContext(testPeer, SERVER_ANONYMOUS_RELAY);
+ testPeer.expectBegin();
+ testPeer.expectSenderAttach();
+
+ JMSProducer producer = context.createProducer();
+ assertNotNull(producer);
+
+ testPeer.expectEnd();
+ testPeer.expectClose();
+ context.close();
+
+ testPeer.waitForAllHandlersToComplete(1000);
+ }
+ }
+
+ @Test(timeout = 20000)
+ public void testJMSProducerHasDefaultConfiguration() throws Exception {
+ try (TestAmqpPeer testPeer = new TestAmqpPeer();) {
+ JMSContext context = testFixture.createJMSContext(testPeer, SERVER_ANONYMOUS_RELAY);
+ testPeer.expectBegin();
+ testPeer.expectSenderAttach();
+
+ JMSProducer producer = context.createProducer();
+ assertNotNull(producer);
+
+ assertEquals(Message.DEFAULT_DELIVERY_DELAY, producer.getDeliveryDelay());
+ assertEquals(Message.DEFAULT_DELIVERY_MODE, producer.getDeliveryMode());
+ assertEquals(Message.DEFAULT_PRIORITY, producer.getPriority());
+ assertEquals(Message.DEFAULT_TIME_TO_LIVE, producer.getTimeToLive());
+
+ testPeer.expectEnd();
+ testPeer.expectClose();
+ context.close();
+
+ testPeer.waitForAllHandlersToComplete(1000);
+ }
+ }
+
+ @Test(timeout = 20000)
+ public void testJMSProducerSetPropertySendsApplicationProperties() throws Exception {
+ try (TestAmqpPeer testPeer = new TestAmqpPeer();) {
+ JMSContext context = testFixture.createJMSContext(testPeer, SERVER_ANONYMOUS_RELAY);
+ testPeer.expectBegin();
+ testPeer.expectSenderAttach();
+
+ String queueName = "myQueue";
+ Queue queue = context.createQueue(queueName);
+ JMSProducer producer = context.createProducer();
+
+ ApplicationPropertiesSectionMatcher appPropsMatcher = new ApplicationPropertiesSectionMatcher(true);
+ appPropsMatcher.withEntry(NULL_STRING_PROP, nullValue());
+ appPropsMatcher.withEntry(STRING_PROP, equalTo(STRING_PROP_VALUE));
+ appPropsMatcher.withEntry(BOOLEAN_PROP, equalTo(BOOLEAN_PROP_VALUE));
+ appPropsMatcher.withEntry(BYTE_PROP, equalTo(BYTE_PROP_VALUE));
+ appPropsMatcher.withEntry(SHORT_PROP, equalTo(SHORT_PROP_VALUE));
+ appPropsMatcher.withEntry(INT_PROP, equalTo(INT_PROP_VALUE));
+ appPropsMatcher.withEntry(LONG_PROP, equalTo(LONG_PROP_VALUE));
+ appPropsMatcher.withEntry(FLOAT_PROP, equalTo(FLOAT_PROP_VALUE));
+ appPropsMatcher.withEntry(DOUBLE_PROP, equalTo(DOUBLE_PROP_VALUE));
+
+ MessageHeaderSectionMatcher headersMatcher = new MessageHeaderSectionMatcher(true).withDurable(equalTo(true));
+ MessageAnnotationsSectionMatcher msgAnnotationsMatcher = new MessageAnnotationsSectionMatcher(true);
+ MessagePropertiesSectionMatcher propsMatcher = new MessagePropertiesSectionMatcher(true).withTo(equalTo(queueName));
+
+ TransferPayloadCompositeMatcher messageMatcher = new TransferPayloadCompositeMatcher();
+ messageMatcher.setHeadersMatcher(headersMatcher);
+ messageMatcher.setMessageAnnotationsMatcher(msgAnnotationsMatcher);
+ messageMatcher.setPropertiesMatcher(propsMatcher);
+ messageMatcher.setApplicationPropertiesMatcher(appPropsMatcher);
+ testPeer.expectTransfer(messageMatcher);
+
+ producer.setProperty(NULL_STRING_PROP, NULL_STRING_PROP_VALUE);
+ producer.setProperty(STRING_PROP, STRING_PROP_VALUE);
+ producer.setProperty(BOOLEAN_PROP, BOOLEAN_PROP_VALUE);
+ producer.setProperty(BYTE_PROP, BYTE_PROP_VALUE);
+ producer.setProperty(SHORT_PROP, SHORT_PROP_VALUE);
+ producer.setProperty(INT_PROP, INT_PROP_VALUE);
+ producer.setProperty(LONG_PROP, LONG_PROP_VALUE);
+ producer.setProperty(FLOAT_PROP, FLOAT_PROP_VALUE);
+ producer.setProperty(DOUBLE_PROP, DOUBLE_PROP_VALUE);
+
+ producer.send(queue, "test");
+
+ testPeer.expectEnd();
+ testPeer.expectClose();
+
+ context.close();
+
+ testPeer.waitForAllHandlersToComplete(1000);
+ }
+ }
+
+ @Test(timeout = 20000)
+ public void testJMSProducerPropertyOverridesMessageValue() throws Exception {
+ try (TestAmqpPeer testPeer = new TestAmqpPeer();) {
+ JMSContext context = testFixture.createJMSContext(testPeer, SERVER_ANONYMOUS_RELAY);
+ testPeer.expectBegin();
+ testPeer.expectSenderAttach();
+
+ String queueName = "myQueue";
+ Queue queue = context.createQueue(queueName);
+ Message message = context.createMessage();
+ JMSProducer producer = context.createProducer();
+
+ ApplicationPropertiesSectionMatcher appPropsMatcher = new ApplicationPropertiesSectionMatcher(true);
+ appPropsMatcher.withEntry(STRING_PROP, equalTo(STRING_PROP_VALUE));
+
+ MessageHeaderSectionMatcher headersMatcher = new MessageHeaderSectionMatcher(true).withDurable(equalTo(true));
+ MessageAnnotationsSectionMatcher msgAnnotationsMatcher = new MessageAnnotationsSectionMatcher(true);
+ MessagePropertiesSectionMatcher propsMatcher = new MessagePropertiesSectionMatcher(true).withTo(equalTo(queueName));
+
+ TransferPayloadCompositeMatcher messageMatcher = new TransferPayloadCompositeMatcher();
+ messageMatcher.setHeadersMatcher(headersMatcher);
+ messageMatcher.setMessageAnnotationsMatcher(msgAnnotationsMatcher);
+ messageMatcher.setPropertiesMatcher(propsMatcher);
+ messageMatcher.setApplicationPropertiesMatcher(appPropsMatcher);
+ testPeer.expectTransfer(messageMatcher);
+
+ message.setStringProperty(STRING_PROP, "ThisShouldNotBeTransmitted");
+ producer.setProperty(STRING_PROP, STRING_PROP_VALUE);
+ producer.send(queue, message);
+
+ testPeer.expectEnd();
+ testPeer.expectClose();
+
+ context.close();
+
+ testPeer.waitForAllHandlersToComplete(1000);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/0c39522c/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/MapMessageIntegrationTest.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/MapMessageIntegrationTest.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/MapMessageIntegrationTest.java
index 8141751..10bcffd 100644
--- a/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/MapMessageIntegrationTest.java
+++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/MapMessageIntegrationTest.java
@@ -20,8 +20,10 @@ package org.apache.qpid.jms.integration;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import java.util.Arrays;
import java.util.LinkedHashMap;
@@ -31,6 +33,7 @@ import javax.jms.Connection;
import javax.jms.MapMessage;
import javax.jms.Message;
import javax.jms.MessageConsumer;
+import javax.jms.MessageFormatException;
import javax.jms.MessageProducer;
import javax.jms.Queue;
import javax.jms.Session;
@@ -116,10 +119,10 @@ public class MapMessageIntegrationTest extends QpidJmsTestCase {
testPeer.expectReceiverAttach();
testPeer.expectLinkFlowRespondWithTransfer(null, msgAnnotations, null, null, amqpValueSectionContent);
testPeer.expectDispositionThatIsAcceptedAndSettled();
+ testPeer.expectClose();
MessageConsumer messageConsumer = session.createConsumer(queue);
Message receivedMessage = messageConsumer.receive(3000);
- testPeer.waitForAllHandlersToComplete(3000);
// verify the content is as expected
assertNotNull("Message was not received", receivedMessage);
@@ -137,6 +140,23 @@ public class MapMessageIntegrationTest extends QpidJmsTestCase {
assertEquals("Unexpected long value", myLong, receivedMapMessage.getLong(myLongKey));
assertEquals("Unexpected short value", myShort, receivedMapMessage.getShort(myShortKey));
assertEquals("Unexpected UTF value", myString, receivedMapMessage.getString(myStringKey));
+
+ assertTrue(receivedMapMessage.isBodyAssignableTo(Map.class));
+ assertTrue(receivedMapMessage.isBodyAssignableTo(Object.class));
+ assertFalse(receivedMapMessage.isBodyAssignableTo(Boolean.class));
+ assertFalse(receivedMapMessage.isBodyAssignableTo(byte[].class));
+
+ assertNotNull(receivedMapMessage.getBody(Object.class));
+ assertNotNull(receivedMapMessage.getBody(Map.class));
+ try {
+ receivedMapMessage.getBody(byte[].class);
+ fail("Cannot read TextMessage with this type.");
+ } catch (MessageFormatException mfe) {
+ }
+
+ connection.close();
+
+ testPeer.waitForAllHandlersToComplete(3000);
}
}
@@ -219,9 +239,28 @@ public class MapMessageIntegrationTest extends QpidJmsTestCase {
messageMatcher.setPropertiesMatcher(propertiesMatcher);
messageMatcher.setMessageContentMatcher(new EncodedAmqpValueMatcher(map));
- // send the message
testPeer.expectTransfer(messageMatcher);
+ testPeer.expectClose();
+
+ // send the message
producer.send(mapMessage);
+
+ assertTrue(mapMessage.isBodyAssignableTo(Map.class));
+ assertTrue(mapMessage.isBodyAssignableTo(Object.class));
+ assertFalse(mapMessage.isBodyAssignableTo(Boolean.class));
+ assertFalse(mapMessage.isBodyAssignableTo(byte[].class));
+
+ assertNotNull(mapMessage.getBody(Object.class));
+ assertNotNull(mapMessage.getBody(Map.class));
+ try {
+ mapMessage.getBody(byte[].class);
+ fail("Cannot read TextMessage with this type.");
+ } catch (MessageFormatException mfe) {
+ }
+
+ connection.close();
+
+ testPeer.waitForAllHandlersToComplete(3000);
}
}
}
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org
[4/7] qpid-jms git commit: QPIDJMS-207 Adds dependency on JMS 2.0 API
and initial implementation.
Posted by ta...@apache.org.
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/0c39522c/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/MessageIntegrationTest.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/MessageIntegrationTest.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/MessageIntegrationTest.java
index ec84479..48adaea 100644
--- a/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/MessageIntegrationTest.java
+++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/MessageIntegrationTest.java
@@ -31,6 +31,7 @@ import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.IOException;
+import java.io.Serializable;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
@@ -98,6 +99,45 @@ public class MessageIntegrationTest extends QpidJmsTestCase
private final IntegrationTestFixture testFixture = new IntegrationTestFixture();
+ @Test(timeout = 20000)
+ public void testReceiveMessageAndGetBody() throws Exception {
+ try (TestAmqpPeer testPeer = new TestAmqpPeer();) {
+ Connection connection = testFixture.establishConnecton(testPeer);
+ connection.start();
+
+ testPeer.expectBegin();
+
+ Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ Queue queue = session.createQueue("myQueue");
+
+ DescribedType amqpValueNullContent = new AmqpValueDescribedType(null);
+
+ testPeer.expectReceiverAttach();
+ testPeer.expectLinkFlowRespondWithTransfer(null, null, null, null, amqpValueNullContent);
+ testPeer.expectDispositionThatIsAcceptedAndSettled();
+ testPeer.expectClose();
+
+ MessageConsumer messageConsumer = session.createConsumer(queue);
+ Message receivedMessage = messageConsumer.receive(3000);
+
+ assertTrue(receivedMessage.isBodyAssignableTo(Object.class));
+ assertTrue(receivedMessage.isBodyAssignableTo(String.class));
+ assertTrue(receivedMessage.isBodyAssignableTo(byte[].class));
+ assertTrue(receivedMessage.isBodyAssignableTo(Serializable.class));
+ assertTrue(receivedMessage.isBodyAssignableTo(Map.class));
+
+ assertNull(receivedMessage.getBody(Object.class));
+ assertNull(receivedMessage.getBody(String.class));
+ assertNull(receivedMessage.getBody(byte[].class));
+ assertNull(receivedMessage.getBody(Serializable.class));
+ assertNull(receivedMessage.getBody(Map.class));
+
+ connection.close();
+
+ testPeer.waitForAllHandlersToComplete(3000);
+ }
+ }
+
//==== Application Properties Section ====
//========================================
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/0c39522c/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/ObjectMessageIntegrationTest.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/ObjectMessageIntegrationTest.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/ObjectMessageIntegrationTest.java
index a9bc0c5..e382324 100644
--- a/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/ObjectMessageIntegrationTest.java
+++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/ObjectMessageIntegrationTest.java
@@ -21,20 +21,25 @@ package org.apache.qpid.jms.integration;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.nullValue;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
+import java.io.Serializable;
import java.util.HashMap;
+import java.util.Map;
import java.util.UUID;
import javax.jms.Connection;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
+import javax.jms.MessageFormatException;
import javax.jms.MessageProducer;
import javax.jms.ObjectMessage;
import javax.jms.Queue;
@@ -113,6 +118,7 @@ public class ObjectMessageIntegrationTest extends QpidJmsTestCase {
messageMatcher.setMessageContentMatcher(new EncodedDataMatcher(new Binary(bytes)));
testPeer.expectTransfer(messageMatcher);
+ testPeer.expectClose();
ObjectMessage message = session.createObjectMessage();
if (content != null || setObjectIfNull) {
@@ -121,6 +127,38 @@ public class ObjectMessageIntegrationTest extends QpidJmsTestCase {
producer.send(message);
+ if (content == null) {
+ assertTrue(message.isBodyAssignableTo(String.class));
+ assertTrue(message.isBodyAssignableTo(Serializable.class));
+ assertTrue(message.isBodyAssignableTo(Object.class));
+ assertTrue(message.isBodyAssignableTo(Boolean.class));
+ assertTrue(message.isBodyAssignableTo(byte[].class));
+ } else {
+ assertTrue(message.isBodyAssignableTo(String.class));
+ assertTrue(message.isBodyAssignableTo(Serializable.class));
+ assertTrue(message.isBodyAssignableTo(Object.class));
+ assertFalse(message.isBodyAssignableTo(Boolean.class));
+ assertFalse(message.isBodyAssignableTo(byte[].class));
+ }
+
+ if (content == null) {
+ assertNull(message.getBody(Object.class));
+ assertNull(message.getBody(Serializable.class));
+ assertNull(message.getBody(String.class));
+ assertNull(message.getBody(byte[].class));
+ } else {
+ assertNotNull(message.getBody(Object.class));
+ assertNotNull(message.getBody(Serializable.class));
+ assertNotNull(message.getBody(String.class));
+ try {
+ message.getBody(byte[].class);
+ fail("Cannot read TextMessage with this type.");
+ } catch (MessageFormatException mfe) {
+ }
+ }
+
+ connection.close();
+
testPeer.waitForAllHandlersToComplete(3000);
}
}
@@ -156,10 +194,10 @@ public class ObjectMessageIntegrationTest extends QpidJmsTestCase {
testPeer.expectReceiverAttach();
testPeer.expectLinkFlowRespondWithTransfer(null, msgAnnotations, properties, null, dataContent);
testPeer.expectDispositionThatIsAcceptedAndSettled();
+ testPeer.expectClose();
MessageConsumer messageConsumer = session.createConsumer(queue);
Message receivedMessage = messageConsumer.receive(3000);
- testPeer.waitForAllHandlersToComplete(3000);
assertNotNull(receivedMessage);
assertTrue(receivedMessage instanceof ObjectMessage);
@@ -168,6 +206,25 @@ public class ObjectMessageIntegrationTest extends QpidJmsTestCase {
Object object = objectMessage.getObject();
assertNotNull("Expected object but got null", object);
assertEquals("Message body object was not as expected", expectedContent, object);
+
+ assertTrue(receivedMessage.isBodyAssignableTo(String.class));
+ assertTrue(receivedMessage.isBodyAssignableTo(Serializable.class));
+ assertTrue(receivedMessage.isBodyAssignableTo(Object.class));
+ assertFalse(receivedMessage.isBodyAssignableTo(Boolean.class));
+ assertFalse(receivedMessage.isBodyAssignableTo(byte[].class));
+
+ assertNotNull(receivedMessage.getBody(Object.class));
+ assertNotNull(receivedMessage.getBody(Serializable.class));
+ assertNotNull(receivedMessage.getBody(String.class));
+ try {
+ receivedMessage.getBody(byte[].class);
+ fail("Cannot read TextMessage with this type.");
+ } catch (MessageFormatException mfe) {
+ }
+
+ connection.close();
+
+ testPeer.waitForAllHandlersToComplete(3000);
}
}
@@ -224,9 +281,27 @@ public class ObjectMessageIntegrationTest extends QpidJmsTestCase {
messageMatcher.setMessageContentMatcher(new EncodedDataMatcher(new Binary(bytes)));
testPeer.expectTransfer(messageMatcher);
+ testPeer.expectClose();
producer.send(receivedMessage);
+ assertTrue(receivedMessage.isBodyAssignableTo(String.class));
+ assertTrue(receivedMessage.isBodyAssignableTo(Serializable.class));
+ assertTrue(receivedMessage.isBodyAssignableTo(Object.class));
+ assertFalse(receivedMessage.isBodyAssignableTo(Boolean.class));
+ assertFalse(receivedMessage.isBodyAssignableTo(byte[].class));
+
+ assertNotNull(receivedMessage.getBody(Object.class));
+ assertNotNull(receivedMessage.getBody(Serializable.class));
+ assertNotNull(receivedMessage.getBody(String.class));
+ try {
+ receivedMessage.getBody(byte[].class);
+ fail("Cannot read TextMessage with this type.");
+ } catch (MessageFormatException mfe) {
+ }
+
+ connection.close();
+
testPeer.waitForAllHandlersToComplete(3000);
}
}
@@ -373,6 +448,7 @@ public class ObjectMessageIntegrationTest extends QpidJmsTestCase {
messageMatcher.setMessageContentMatcher(new EncodedAmqpValueMatcher(map));
testPeer.expectTransfer(messageMatcher);
+ testPeer.expectClose();
ObjectMessage message = session.createObjectMessage();
message.setBooleanProperty(AmqpMessageSupport.JMS_AMQP_TYPED_ENCODING, true);
@@ -380,6 +456,23 @@ public class ObjectMessageIntegrationTest extends QpidJmsTestCase {
producer.send(message);
+ assertTrue(message.isBodyAssignableTo(Map.class));
+ assertTrue(message.isBodyAssignableTo(Serializable.class));
+ assertTrue(message.isBodyAssignableTo(Object.class));
+ assertFalse(message.isBodyAssignableTo(Boolean.class));
+ assertFalse(message.isBodyAssignableTo(byte[].class));
+
+ assertNotNull(message.getBody(Object.class));
+ assertNotNull(message.getBody(Serializable.class));
+ assertNotNull(message.getBody(Map.class));
+ try {
+ message.getBody(byte[].class);
+ fail("Cannot read TextMessage with this type.");
+ } catch (MessageFormatException mfe) {
+ }
+
+ connection.close();
+
testPeer.waitForAllHandlersToComplete(3000);
}
}
@@ -406,10 +499,10 @@ public class ObjectMessageIntegrationTest extends QpidJmsTestCase {
testPeer.expectReceiverAttach();
testPeer.expectLinkFlowRespondWithTransfer(null, msgAnnotations, null, null, amqpValueContent);
testPeer.expectDispositionThatIsAcceptedAndSettled();
+ testPeer.expectClose();
MessageConsumer messageConsumer = session.createConsumer(queue);
Message receivedMessage = messageConsumer.receive(3000);
- testPeer.waitForAllHandlersToComplete(3000);
assertNotNull(receivedMessage);
assertTrue("Expected ObjectMessage instance, but got: " + receivedMessage.getClass().getName(), receivedMessage instanceof ObjectMessage);
@@ -418,6 +511,25 @@ public class ObjectMessageIntegrationTest extends QpidJmsTestCase {
Object object = objectMessage.getObject();
assertNotNull("Expected object but got null", object);
assertEquals("Message body object was not as expected", map, object);
+
+ assertTrue(receivedMessage.isBodyAssignableTo(Map.class));
+ assertTrue(receivedMessage.isBodyAssignableTo(Serializable.class));
+ assertTrue(receivedMessage.isBodyAssignableTo(Object.class));
+ assertFalse(receivedMessage.isBodyAssignableTo(Boolean.class));
+ assertFalse(receivedMessage.isBodyAssignableTo(byte[].class));
+
+ assertNotNull(receivedMessage.getBody(Object.class));
+ assertNotNull(receivedMessage.getBody(Serializable.class));
+ assertNotNull(receivedMessage.getBody(Map.class));
+ try {
+ receivedMessage.getBody(byte[].class);
+ fail("Cannot read TextMessage with this type.");
+ } catch (MessageFormatException mfe) {
+ }
+
+ connection.close();
+
+ testPeer.waitForAllHandlersToComplete(3000);
}
}
}
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/0c39522c/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/PresettledProducerIntegrationTest.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/PresettledProducerIntegrationTest.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/PresettledProducerIntegrationTest.java
index 2ed9f41..ba851a6 100644
--- a/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/PresettledProducerIntegrationTest.java
+++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/PresettledProducerIntegrationTest.java
@@ -29,6 +29,7 @@ import static org.junit.Assert.fail;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
+import javax.jms.CompletionListener;
import javax.jms.Connection;
import javax.jms.Destination;
import javax.jms.Message;
@@ -40,7 +41,6 @@ import javax.jms.TemporaryTopic;
import javax.jms.TextMessage;
import javax.jms.Topic;
-import org.apache.qpid.jms.JmsCompletionListener;
import org.apache.qpid.jms.JmsMessageProducer;
import org.apache.qpid.jms.test.QpidJmsTestCase;
import org.apache.qpid.jms.test.testpeer.ListDescribedType;
@@ -608,7 +608,7 @@ public class PresettledProducerIntegrationTest extends QpidJmsTestCase {
}
}
- private class TestJmsCompletionListener implements JmsCompletionListener {
+ private class TestJmsCompletionListener implements CompletionListener {
private final CountDownLatch completed;
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/0c39522c/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/ProducerIntegrationTest.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/ProducerIntegrationTest.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/ProducerIntegrationTest.java
index 1e69eb8..0dee795 100644
--- a/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/ProducerIntegrationTest.java
+++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/ProducerIntegrationTest.java
@@ -44,6 +44,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import javax.jms.BytesMessage;
+import javax.jms.CompletionListener;
import javax.jms.Connection;
import javax.jms.DeliveryMode;
import javax.jms.ExceptionListener;
@@ -57,7 +58,6 @@ import javax.jms.Session;
import javax.jms.TextMessage;
import javax.jms.Topic;
-import org.apache.qpid.jms.JmsCompletionListener;
import org.apache.qpid.jms.JmsConnection;
import org.apache.qpid.jms.JmsConnectionFactory;
import org.apache.qpid.jms.JmsDefaultConnectionListener;
@@ -2381,7 +2381,7 @@ public class ProducerIntegrationTest extends QpidJmsTestCase {
}
}
- private class TestJmsCompletionListener implements JmsCompletionListener {
+ private class TestJmsCompletionListener implements CompletionListener {
private final CountDownLatch completed;
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/0c39522c/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/SessionIntegrationTest.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/SessionIntegrationTest.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/SessionIntegrationTest.java
index 34d60f1..7e5744f 100644
--- a/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/SessionIntegrationTest.java
+++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/SessionIntegrationTest.java
@@ -37,6 +37,7 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
+import javax.jms.CompletionListener;
import javax.jms.Connection;
import javax.jms.Destination;
import javax.jms.IllegalStateException;
@@ -57,7 +58,6 @@ import javax.jms.TextMessage;
import javax.jms.Topic;
import javax.jms.TopicSubscriber;
-import org.apache.qpid.jms.JmsCompletionListener;
import org.apache.qpid.jms.JmsConnection;
import org.apache.qpid.jms.JmsDefaultConnectionListener;
import org.apache.qpid.jms.JmsMessageProducer;
@@ -1198,6 +1198,33 @@ public class SessionIntegrationTest extends QpidJmsTestCase {
}
@Test(timeout = 20000)
+ public void testCreateDurableConsumer() throws Exception {
+ try (TestAmqpPeer testPeer = new TestAmqpPeer();) {
+ Connection connection = testFixture.establishConnecton(testPeer);
+ connection.start();
+
+ testPeer.expectBegin();
+ Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ String topicName = "myTopic";
+ Topic dest = session.createTopic(topicName);
+ String subscriptionName = "mySubscription";
+
+ testPeer.expectDurableSubscriberAttach(topicName, subscriptionName);
+ testPeer.expectLinkFlow();
+
+ MessageConsumer consumer = session.createDurableConsumer(dest, subscriptionName);
+ assertNotNull("MessageConsumer object was null", consumer);
+ assertNull("MessageConsumer should not have a selector", consumer.getMessageSelector());
+
+ testPeer.expectClose();
+ connection.close();
+
+ testPeer.waitForAllHandlersToComplete(1000);
+ }
+ }
+
+ @Test(timeout = 20000)
public void testDurableSubscriptionUnsubscribeInUseThrowsJMSEx() throws Exception {
try (TestAmqpPeer testPeer = new TestAmqpPeer();) {
Connection connection = testFixture.establishConnecton(testPeer);
@@ -1995,7 +2022,7 @@ public class SessionIntegrationTest extends QpidJmsTestCase {
}
}
- private class TestJmsCompletionListener implements JmsCompletionListener {
+ private class TestJmsCompletionListener implements CompletionListener {
private final CountDownLatch completed;
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/0c39522c/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/StreamMessageIntegrationTest.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/StreamMessageIntegrationTest.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/StreamMessageIntegrationTest.java
index 21e26f8..507d34e 100644
--- a/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/StreamMessageIntegrationTest.java
+++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/StreamMessageIntegrationTest.java
@@ -21,8 +21,10 @@ package org.apache.qpid.jms.integration;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import java.util.ArrayList;
import java.util.List;
@@ -30,6 +32,7 @@ import java.util.List;
import javax.jms.Connection;
import javax.jms.Message;
import javax.jms.MessageConsumer;
+import javax.jms.MessageFormatException;
import javax.jms.MessageProducer;
import javax.jms.Queue;
import javax.jms.Session;
@@ -106,10 +109,10 @@ public class StreamMessageIntegrationTest extends QpidJmsTestCase {
testPeer.expectReceiverAttach();
testPeer.expectLinkFlowRespondWithTransfer(null, msgAnnotations, null, null, amqpValueSectionContent);
testPeer.expectDispositionThatIsAcceptedAndSettled();
+ testPeer.expectClose();
MessageConsumer messageConsumer = session.createConsumer(queue);
Message receivedMessage = messageConsumer.receive(3000);
- testPeer.waitForAllHandlersToComplete(3000);
//verify the content is as expected
assertNotNull("Message was not received", receivedMessage);
@@ -127,6 +130,33 @@ public class StreamMessageIntegrationTest extends QpidJmsTestCase {
assertEquals("Unexpected long value", myLong, receivedStreamMessage.readLong());
assertEquals("Unexpected short value", myShort, receivedStreamMessage.readShort());
assertEquals("Unexpected UTF value", myString, receivedStreamMessage.readString());
+
+ assertFalse(receivedStreamMessage.isBodyAssignableTo(String.class));
+ assertFalse(receivedStreamMessage.isBodyAssignableTo(Object.class));
+ assertFalse(receivedStreamMessage.isBodyAssignableTo(Boolean.class));
+ assertFalse(receivedStreamMessage.isBodyAssignableTo(byte[].class));
+
+ try {
+ receivedStreamMessage.getBody(Object.class);
+ fail("Cannot read TextMessage with this type.");
+ } catch (MessageFormatException mfe) {
+ }
+
+ try {
+ receivedStreamMessage.getBody(byte[].class);
+ fail("Cannot read TextMessage with this type.");
+ } catch (MessageFormatException mfe) {
+ }
+
+ try {
+ receivedStreamMessage.getBody(String.class);
+ fail("Cannot read TextMessage with this type.");
+ } catch (MessageFormatException mfe) {
+ }
+
+ connection.close();
+
+ testPeer.waitForAllHandlersToComplete(3000);
}
}
@@ -204,9 +234,38 @@ public class StreamMessageIntegrationTest extends QpidJmsTestCase {
messageMatcher.setPropertiesMatcher(propertiesMatcher);
messageMatcher.setMessageContentMatcher(new EncodedAmqpSequenceMatcher(list));
- //send the message
testPeer.expectTransfer(messageMatcher);
+ testPeer.expectClose();
+
+ //send the message
producer.send(streamMessage);
+
+ assertFalse(streamMessage.isBodyAssignableTo(String.class));
+ assertFalse(streamMessage.isBodyAssignableTo(Object.class));
+ assertFalse(streamMessage.isBodyAssignableTo(Boolean.class));
+ assertFalse(streamMessage.isBodyAssignableTo(byte[].class));
+
+ try {
+ streamMessage.getBody(Object.class);
+ fail("Cannot read TextMessage with this type.");
+ } catch (MessageFormatException mfe) {
+ }
+
+ try {
+ streamMessage.getBody(byte[].class);
+ fail("Cannot read TextMessage with this type.");
+ } catch (MessageFormatException mfe) {
+ }
+
+ try {
+ streamMessage.getBody(String.class);
+ fail("Cannot read TextMessage with this type.");
+ } catch (MessageFormatException mfe) {
+ }
+
+ connection.close();
+
+ testPeer.waitForAllHandlersToComplete(3000);
}
}
}
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/0c39522c/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/TextMessageIntegrationTest.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/TextMessageIntegrationTest.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/TextMessageIntegrationTest.java
index b7b27e8..2869d03 100644
--- a/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/TextMessageIntegrationTest.java
+++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/TextMessageIntegrationTest.java
@@ -20,9 +20,11 @@ package org.apache.qpid.jms.integration;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import java.io.IOException;
@@ -30,6 +32,7 @@ import javax.jms.Connection;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
+import javax.jms.MessageFormatException;
import javax.jms.MessageProducer;
import javax.jms.Queue;
import javax.jms.Session;
@@ -75,10 +78,27 @@ public class TextMessageIntegrationTest extends QpidJmsTestCase {
messageMatcher.setPropertiesMatcher(propsMatcher);
messageMatcher.setMessageContentMatcher(new EncodedAmqpValueMatcher(text));
testPeer.expectTransfer(messageMatcher);
+ testPeer.expectClose();
Message message = session.createTextMessage(text);
-
producer.send(message);
+
+ assertTrue(message.isBodyAssignableTo(String.class));
+ assertTrue(message.isBodyAssignableTo(Object.class));
+ assertFalse(message.isBodyAssignableTo(Boolean.class));
+ assertFalse(message.isBodyAssignableTo(byte[].class));
+
+ assertNotNull(message.getBody(Object.class));
+ assertNotNull(message.getBody(String.class));
+ try {
+ message.getBody(byte[].class);
+ fail("Cannot read TextMessage with this type.");
+ } catch (MessageFormatException mfe) {
+ }
+
+ connection.close();
+
+ testPeer.waitForAllHandlersToComplete(1000);
}
}
@@ -99,14 +119,31 @@ public class TextMessageIntegrationTest extends QpidJmsTestCase {
testPeer.expectReceiverAttach();
testPeer.expectLinkFlowRespondWithTransfer(null, null, null, null, amqpValueStringContent);
testPeer.expectDispositionThatIsAcceptedAndSettled();
+ testPeer.expectClose();
MessageConsumer messageConsumer = session.createConsumer(queue);
Message receivedMessage = messageConsumer.receive(3000);
- testPeer.waitForAllHandlersToComplete(3000);
+
+ assertTrue(receivedMessage.isBodyAssignableTo(String.class));
+ assertTrue(receivedMessage.isBodyAssignableTo(Object.class));
+ assertFalse(receivedMessage.isBodyAssignableTo(Boolean.class));
+ assertFalse(receivedMessage.isBodyAssignableTo(byte[].class));
+
+ assertNotNull(receivedMessage.getBody(Object.class));
+ assertNotNull(receivedMessage.getBody(String.class));
+ try {
+ receivedMessage.getBody(byte[].class);
+ fail("Cannot read TextMessage with this type.");
+ } catch (MessageFormatException mfe) {
+ }
assertNotNull(receivedMessage);
assertTrue(receivedMessage instanceof TextMessage);
assertEquals(expectedMessageContent, ((TextMessage) receivedMessage).getText());
+
+ connection.close();
+
+ testPeer.waitForAllHandlersToComplete(3000);
}
}
@@ -131,10 +168,21 @@ public class TextMessageIntegrationTest extends QpidJmsTestCase {
messageMatcher.setPropertiesMatcher(propsMatcher);
messageMatcher.setMessageContentMatcher(new EncodedAmqpValueMatcher(null));
testPeer.expectTransfer(messageMatcher);
+ testPeer.expectClose();
Message message = session.createTextMessage();
producer.send(message);
+
+ // Message has no content so all are assignable
+ assertTrue(message.isBodyAssignableTo(String.class));
+ assertTrue(message.isBodyAssignableTo(Object.class));
+ assertTrue(message.isBodyAssignableTo(Boolean.class));
+ assertTrue(message.isBodyAssignableTo(byte[].class));
+
+ connection.close();
+
+ testPeer.waitForAllHandlersToComplete(3000);
}
}
@@ -154,14 +202,24 @@ public class TextMessageIntegrationTest extends QpidJmsTestCase {
testPeer.expectReceiverAttach();
testPeer.expectLinkFlowRespondWithTransfer(null, null, null, null, amqpValueNullContent);
testPeer.expectDispositionThatIsAcceptedAndSettled();
+ testPeer.expectClose();
MessageConsumer messageConsumer = session.createConsumer(queue);
Message receivedMessage = messageConsumer.receive(3000);
- testPeer.waitForAllHandlersToComplete(3000);
assertNotNull(receivedMessage);
assertTrue(receivedMessage instanceof TextMessage);
assertNull(((TextMessage) receivedMessage).getText());
+
+ // Message has no content so all are assignable
+ assertTrue(receivedMessage.isBodyAssignableTo(String.class));
+ assertTrue(receivedMessage.isBodyAssignableTo(Object.class));
+ assertTrue(receivedMessage.isBodyAssignableTo(Boolean.class));
+ assertTrue(receivedMessage.isBodyAssignableTo(byte[].class));
+
+ connection.close();
+
+ testPeer.waitForAllHandlersToComplete(3000);
}
}
@@ -232,16 +290,33 @@ public class TextMessageIntegrationTest extends QpidJmsTestCase {
testPeer.expectReceiverAttach();
testPeer.expectLinkFlowRespondWithTransfer(null, null, properties, null, dataContent);
testPeer.expectDispositionThatIsAcceptedAndSettled();
+ testPeer.expectClose();
MessageConsumer messageConsumer = session.createConsumer(queue);
Message receivedMessage = messageConsumer.receive(3000);
- testPeer.waitForAllHandlersToComplete(3000);
assertNotNull(receivedMessage);
assertTrue(receivedMessage instanceof TextMessage);
String text = ((TextMessage) receivedMessage).getText();
assertEquals(expectedString, text);
+
+ assertTrue(receivedMessage.isBodyAssignableTo(String.class));
+ assertTrue(receivedMessage.isBodyAssignableTo(Object.class));
+ assertFalse(receivedMessage.isBodyAssignableTo(Boolean.class));
+ assertFalse(receivedMessage.isBodyAssignableTo(byte[].class));
+
+ assertNotNull(receivedMessage.getBody(Object.class));
+ assertNotNull(receivedMessage.getBody(String.class));
+ try {
+ receivedMessage.getBody(byte[].class);
+ fail("Cannot read TextMessage with this type.");
+ } catch (MessageFormatException mfe) {
+ }
+
+ connection.close();
+
+ testPeer.waitForAllHandlersToComplete(3000);
}
}
}
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/0c39522c/qpid-jms-client/src/test/java/org/apache/qpid/jms/message/JmsMessageTest.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/message/JmsMessageTest.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/message/JmsMessageTest.java
index a0994a6..16cc9a7 100644
--- a/qpid-jms-client/src/test/java/org/apache/qpid/jms/message/JmsMessageTest.java
+++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/message/JmsMessageTest.java
@@ -26,13 +26,21 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import java.io.Serializable;
import java.util.Enumeration;
+import java.util.Map;
+import java.util.UUID;
+import javax.jms.BytesMessage;
import javax.jms.DeliveryMode;
import javax.jms.JMSException;
+import javax.jms.MapMessage;
import javax.jms.Message;
import javax.jms.MessageFormatException;
import javax.jms.MessageNotWriteableException;
+import javax.jms.ObjectMessage;
+import javax.jms.StreamMessage;
+import javax.jms.TextMessage;
import org.apache.qpid.jms.JmsAcknowledgeCallback;
import org.apache.qpid.jms.JmsConnection;
@@ -1498,6 +1506,333 @@ public class JmsMessageTest {
assertEquals("Unexpected ack type value after setting prop", RELEASED, callback.getAckType());
}
+ //--------- Test isBodyAssignableTo method -------------------------------//
+
+ @Test
+ public void testMessageIsBodyAssignableTo() throws Exception {
+ Message message = factory.createMessage();
+
+ assertTrue(message.isBodyAssignableTo(String.class));
+ assertTrue(message.isBodyAssignableTo(Boolean.class));
+ assertTrue(message.isBodyAssignableTo(Object.class));
+ assertTrue(message.isBodyAssignableTo(Map.class));
+ }
+
+ @Test
+ public void testTextMessageIsBodyAssignableTo() throws Exception {
+ JmsTextMessage message = factory.createTextMessage();
+
+ assertTrue(message.isBodyAssignableTo(String.class));
+ assertTrue(message.isBodyAssignableTo(Boolean.class));
+ assertTrue(message.isBodyAssignableTo(Map.class));
+ assertTrue(message.isBodyAssignableTo(Object.class));
+
+ message.setText("test");
+
+ assertTrue(message.isBodyAssignableTo(String.class));
+ assertFalse(message.isBodyAssignableTo(Boolean.class));
+ assertFalse(message.isBodyAssignableTo(Map.class));
+ assertTrue(message.isBodyAssignableTo(Object.class));
+ }
+
+ @Test
+ public void testStreamMessageIsBodyAssignableTo() throws Exception {
+ JmsStreamMessage message = factory.createStreamMessage();
+
+ assertFalse(message.isBodyAssignableTo(String.class));
+ assertFalse(message.isBodyAssignableTo(Boolean.class));
+ assertFalse(message.isBodyAssignableTo(Map.class));
+ assertFalse(message.isBodyAssignableTo(Object.class));
+
+ message.writeBoolean(false);
+
+ assertFalse(message.isBodyAssignableTo(String.class));
+ assertFalse(message.isBodyAssignableTo(Boolean.class));
+ assertFalse(message.isBodyAssignableTo(Map.class));
+ assertFalse(message.isBodyAssignableTo(Object.class));
+ }
+
+ @Test
+ public void testMapMessageIsBodyAssignableTo() throws Exception {
+ JmsMapMessage message = factory.createMapMessage();
+
+ assertTrue(message.isBodyAssignableTo(String.class));
+ assertTrue(message.isBodyAssignableTo(Boolean.class));
+ assertTrue(message.isBodyAssignableTo(Map.class));
+ assertTrue(message.isBodyAssignableTo(Object.class));
+
+ message.setBoolean("Boolean", true);
+
+ assertFalse(message.isBodyAssignableTo(String.class));
+ assertFalse(message.isBodyAssignableTo(Boolean.class));
+ assertTrue(message.isBodyAssignableTo(Map.class));
+ assertTrue(message.isBodyAssignableTo(Object.class));
+ }
+
+ @Test
+ public void testBytesMessageIsBodyAssignableTo() throws Exception {
+ JmsBytesMessage message = factory.createBytesMessage();
+
+ assertTrue(message.isBodyAssignableTo(byte[].class));
+ assertTrue(message.isBodyAssignableTo(Boolean.class));
+ assertTrue(message.isBodyAssignableTo(Map.class));
+ assertTrue(message.isBodyAssignableTo(String.class));
+ assertTrue(message.isBodyAssignableTo(Object.class));
+
+ message.writeBoolean(false);
+
+ // The message doesn't technically have a body until it is reset
+ message.reset();
+
+ assertTrue(message.isBodyAssignableTo(byte[].class));
+ assertFalse(message.isBodyAssignableTo(Boolean.class));
+ assertFalse(message.isBodyAssignableTo(Map.class));
+ assertFalse(message.isBodyAssignableTo(String.class));
+ assertTrue(message.isBodyAssignableTo(Object.class));
+ }
+
+ @Test
+ public void testObjectMessageIsBodyAssignableTo() throws Exception {
+ JmsObjectMessage message = factory.createObjectMessage();
+
+ assertTrue(message.isBodyAssignableTo(Boolean.class));
+ assertTrue(message.isBodyAssignableTo(Map.class));
+ assertTrue(message.isBodyAssignableTo(String.class));
+ assertTrue(message.isBodyAssignableTo(Serializable.class));
+ assertTrue(message.isBodyAssignableTo(Object.class));
+
+ message.setObject(UUID.randomUUID());
+
+ assertFalse(message.isBodyAssignableTo(Boolean.class));
+ assertFalse(message.isBodyAssignableTo(Map.class));
+ assertFalse(message.isBodyAssignableTo(String.class));
+ assertTrue(message.isBodyAssignableTo(Serializable.class));
+ assertTrue(message.isBodyAssignableTo(Object.class));
+ assertTrue(message.isBodyAssignableTo(UUID.class));
+ }
+
+ //--------- Test for getBody method --------------------------------------//
+
+ @Test
+ public void testGetBodyOnMessage() throws Exception {
+ Message message = factory.createMessage();
+
+ assertNull(message.getBody(String.class));
+ assertNull(message.getBody(Boolean.class));
+ assertNull(message.getBody(byte[].class));
+ assertNull(message.getBody(Object.class));
+ }
+
+ @Test
+ public void testGetBodyOnTextMessage() throws Exception {
+ TextMessage message = factory.createTextMessage();
+
+ assertNull(message.getBody(String.class));
+ assertNull(message.getBody(Boolean.class));
+ assertNull(message.getBody(byte[].class));
+ assertNull(message.getBody(Object.class));
+
+ message.setText("test");
+
+ assertNotNull(message.getBody(String.class));
+ assertNotNull(message.getBody(Object.class));
+
+ try {
+ message.getBody(Boolean.class);
+ fail("Should have thrown an exception");
+ } catch (MessageFormatException mfe) {
+ LOG.info("caught expected MessageFormatException");
+ }
+
+ try {
+ message.getBody(Map.class);
+ fail("Should have thrown an exception");
+ } catch (MessageFormatException mfe) {
+ LOG.info("caught expected MessageFormatException");
+ }
+
+ try {
+ message.getBody(byte[].class);
+ fail("Should have thrown an exception");
+ } catch (MessageFormatException mfe) {
+ LOG.info("caught expected MessageFormatException");
+ }
+ }
+
+ @Test
+ public void testGetBodyOnMapMessage() throws Exception {
+ MapMessage message = factory.createMapMessage();
+
+ assertNull(message.getBody(String.class));
+ assertNull(message.getBody(Boolean.class));
+ assertNull(message.getBody(byte[].class));
+ assertNull(message.getBody(Object.class));
+
+ message.setString("test", "test");
+
+ assertNotNull(message.getBody(Map.class));
+ assertNotNull(message.getBody(Object.class));
+
+ try {
+ message.getBody(Boolean.class);
+ fail("Should have thrown an exception");
+ } catch (MessageFormatException mfe) {
+ LOG.info("caught expected MessageFormatException");
+ }
+
+ try {
+ message.getBody(String.class);
+ fail("Should have thrown an exception");
+ } catch (MessageFormatException mfe) {
+ LOG.info("caught expected MessageFormatException");
+ }
+
+ try {
+ message.getBody(byte[].class);
+ fail("Should have thrown an exception");
+ } catch (MessageFormatException mfe) {
+ LOG.info("caught expected MessageFormatException");
+ }
+ }
+
+ @Test
+ public void testGetBodyOnObjectMessage() throws Exception {
+ ObjectMessage message = factory.createObjectMessage();
+
+ assertNull(message.getBody(String.class));
+ assertNull(message.getBody(Boolean.class));
+ assertNull(message.getBody(byte[].class));
+ assertNull(message.getBody(Serializable.class));
+ assertNull(message.getBody(Object.class));
+
+ message.setObject(UUID.randomUUID());
+
+ assertNotNull(message.getBody(UUID.class));
+ assertNotNull(message.getBody(Serializable.class));
+ assertNotNull(message.getBody(Object.class));
+
+ try {
+ message.getBody(Boolean.class);
+ fail("Should have thrown an exception");
+ } catch (MessageFormatException mfe) {
+ LOG.info("caught expected MessageFormatException");
+ }
+
+ try {
+ message.getBody(String.class);
+ fail("Should have thrown an exception");
+ } catch (MessageFormatException mfe) {
+ LOG.info("caught expected MessageFormatException");
+ }
+
+ try {
+ message.getBody(byte[].class);
+ fail("Should have thrown an exception");
+ } catch (MessageFormatException mfe) {
+ LOG.info("caught expected MessageFormatException");
+ }
+ }
+
+ @Test
+ public void testGetBodyOnBytesMessage() throws Exception {
+ BytesMessage message = factory.createBytesMessage();
+
+ assertNull(message.getBody(String.class));
+ assertNull(message.getBody(Boolean.class));
+ assertNull(message.getBody(byte[].class));
+ assertNull(message.getBody(Object.class));
+
+ message.writeUTF("test");
+ message.reset();
+
+ assertNotNull(message.getBody(byte[].class));
+ assertNotNull(message.getBody(Object.class));
+
+ try {
+ message.getBody(Boolean.class);
+ fail("Should have thrown an exception");
+ } catch (MessageFormatException mfe) {
+ LOG.info("caught expected MessageFormatException");
+ }
+
+ try {
+ message.getBody(Map.class);
+ fail("Should have thrown an exception");
+ } catch (MessageFormatException mfe) {
+ LOG.info("caught expected MessageFormatException");
+ }
+
+ try {
+ message.getBody(String.class);
+ fail("Should have thrown an exception");
+ } catch (MessageFormatException mfe) {
+ LOG.info("caught expected MessageFormatException");
+ }
+ }
+
+ @Test
+ public void testGetBodyOnStreamMessage() throws Exception {
+ StreamMessage message = factory.createStreamMessage();
+
+ try {
+ message.getBody(Object.class);
+ fail("Should have thrown an exception");
+ } catch (MessageFormatException mfe) {
+ LOG.info("caught expected MessageFormatException");
+ }
+
+ try {
+ message.getBody(Boolean.class);
+ fail("Should have thrown an exception");
+ } catch (MessageFormatException mfe) {
+ LOG.info("caught expected MessageFormatException");
+ }
+
+ try {
+ message.getBody(String.class);
+ fail("Should have thrown an exception");
+ } catch (MessageFormatException mfe) {
+ LOG.info("caught expected MessageFormatException");
+ }
+
+ try {
+ message.getBody(byte[].class);
+ fail("Should have thrown an exception");
+ } catch (MessageFormatException mfe) {
+ LOG.info("caught expected MessageFormatException");
+ }
+
+ message.writeBoolean(false);
+
+ try {
+ message.getBody(Object.class);
+ fail("Should have thrown an exception");
+ } catch (MessageFormatException mfe) {
+ LOG.info("caught expected MessageFormatException");
+ }
+
+ try {
+ message.getBody(Boolean.class);
+ fail("Should have thrown an exception");
+ } catch (MessageFormatException mfe) {
+ LOG.info("caught expected MessageFormatException");
+ }
+
+ try {
+ message.getBody(String.class);
+ fail("Should have thrown an exception");
+ } catch (MessageFormatException mfe) {
+ LOG.info("caught expected MessageFormatException");
+ }
+
+ try {
+ message.getBody(byte[].class);
+ fail("Should have thrown an exception");
+ } catch (MessageFormatException mfe) {
+ LOG.info("caught expected MessageFormatException");
+ }
+ }
+
//--------- Test support method ------------------------------------------//
private void assertGetMissingPropertyThrowsNumberFormatException(JmsMessage testMessage, String propertyName, Class<?> clazz) throws JMSException {
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/0c39522c/qpid-jms-client/src/test/java/org/apache/qpid/jms/message/facade/test/JmsTestBytesMessageFacade.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/message/facade/test/JmsTestBytesMessageFacade.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/message/facade/test/JmsTestBytesMessageFacade.java
index 286682c..8b53651 100644
--- a/qpid-jms-client/src/test/java/org/apache/qpid/jms/message/facade/test/JmsTestBytesMessageFacade.java
+++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/message/facade/test/JmsTestBytesMessageFacade.java
@@ -16,11 +16,6 @@
*/
package org.apache.qpid.jms.message.facade.test;
-import io.netty.buffer.ByteBuf;
-import io.netty.buffer.ByteBufInputStream;
-import io.netty.buffer.ByteBufOutputStream;
-import io.netty.buffer.Unpooled;
-
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -30,6 +25,11 @@ import javax.jms.JMSException;
import org.apache.qpid.jms.message.facade.JmsBytesMessageFacade;
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.ByteBufInputStream;
+import io.netty.buffer.ByteBufOutputStream;
+import io.netty.buffer.Unpooled;
+
/**
* A test implementation of the JmsBytesMessageFacade that simply holds a raw Buffer
*/
@@ -133,4 +133,19 @@ public final class JmsTestBytesMessageFacade extends JmsTestMessageFacade implem
public int getBodyLength() {
return content.readableBytes();
}
+
+ @Override
+ public boolean hasBody() {
+ return content.isReadable();
+ }
+
+ @Override
+ public byte[] copyBody() {
+ ByteBuf duplicate = content.duplicate();
+ byte[] result = new byte[content.readableBytes()];
+
+ duplicate.readBytes(result);
+
+ return result;
+ }
}
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/0c39522c/qpid-jms-client/src/test/java/org/apache/qpid/jms/message/facade/test/JmsTestMapMessageFacade.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/message/facade/test/JmsTestMapMessageFacade.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/message/facade/test/JmsTestMapMessageFacade.java
index 8629cd9..6f6b8c5 100644
--- a/qpid-jms-client/src/test/java/org/apache/qpid/jms/message/facade/test/JmsTestMapMessageFacade.java
+++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/message/facade/test/JmsTestMapMessageFacade.java
@@ -72,4 +72,9 @@ public class JmsTestMapMessageFacade extends JmsTestMessageFacade implements Jms
public void clearBody() {
map.clear();
}
+
+ @Override
+ public boolean hasBody() {
+ return !map.isEmpty();
+ }
}
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/0c39522c/qpid-jms-client/src/test/java/org/apache/qpid/jms/message/facade/test/JmsTestMessageFacade.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/message/facade/test/JmsTestMessageFacade.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/message/facade/test/JmsTestMessageFacade.java
index 3a6bf93..f1ccb4c 100644
--- a/qpid-jms-client/src/test/java/org/apache/qpid/jms/message/facade/test/JmsTestMessageFacade.java
+++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/message/facade/test/JmsTestMessageFacade.java
@@ -60,6 +60,7 @@ public class JmsTestMessageFacade implements JmsMessageFacade {
protected int groupSequence;
protected Object messageId;
protected long expiration;
+ protected long deliveryTime;
protected long timestamp;
protected String correlationId;
protected boolean persistent;
@@ -283,6 +284,16 @@ public class JmsTestMessageFacade implements JmsMessageFacade {
}
@Override
+ public long getDeliveryTime() {
+ return deliveryTime;
+ }
+
+ @Override
+ public void setDeliveryTime(long deliveryTime) {
+ this.deliveryTime = deliveryTime;
+ }
+
+ @Override
public JmsDestination getDestination() {
return this.destination;
}
@@ -345,4 +356,9 @@ public class JmsTestMessageFacade implements JmsMessageFacade {
public void setGroupSequence(int groupSequence) {
this.groupSequence = groupSequence;
}
+
+ @Override
+ public boolean hasBody() {
+ return false;
+ }
}
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/0c39522c/qpid-jms-client/src/test/java/org/apache/qpid/jms/message/facade/test/JmsTestObjectMessageFacade.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/message/facade/test/JmsTestObjectMessageFacade.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/message/facade/test/JmsTestObjectMessageFacade.java
index 44bab21..a2fc530 100644
--- a/qpid-jms-client/src/test/java/org/apache/qpid/jms/message/facade/test/JmsTestObjectMessageFacade.java
+++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/message/facade/test/JmsTestObjectMessageFacade.java
@@ -96,4 +96,9 @@ public class JmsTestObjectMessageFacade extends JmsTestMessageFacade implements
this.object = serialized;
}
+
+ @Override
+ public boolean hasBody() {
+ return object != null && object.length > 0;
+ }
}
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/0c39522c/qpid-jms-client/src/test/java/org/apache/qpid/jms/message/facade/test/JmsTestStreamMessageFacade.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/message/facade/test/JmsTestStreamMessageFacade.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/message/facade/test/JmsTestStreamMessageFacade.java
index 983569c..aadee67 100644
--- a/qpid-jms-client/src/test/java/org/apache/qpid/jms/message/facade/test/JmsTestStreamMessageFacade.java
+++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/message/facade/test/JmsTestStreamMessageFacade.java
@@ -82,4 +82,9 @@ public class JmsTestStreamMessageFacade extends JmsTestMessageFacade implements
public void reset() {
index = -1;
}
+
+ @Override
+ public boolean hasBody() {
+ return !stream.isEmpty();
+ }
}
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/0c39522c/qpid-jms-client/src/test/java/org/apache/qpid/jms/message/facade/test/JmsTestTextMessageFacade.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/message/facade/test/JmsTestTextMessageFacade.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/message/facade/test/JmsTestTextMessageFacade.java
index f701570..a4a103f 100644
--- a/qpid-jms-client/src/test/java/org/apache/qpid/jms/message/facade/test/JmsTestTextMessageFacade.java
+++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/message/facade/test/JmsTestTextMessageFacade.java
@@ -54,4 +54,9 @@ public final class JmsTestTextMessageFacade extends JmsTestMessageFacade impleme
public void setText(String text) {
this.text = text;
}
+
+ @Override
+ public boolean hasBody() {
+ return text != null;
+ }
}
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/0c39522c/qpid-jms-client/src/test/java/org/apache/qpid/jms/message/foreign/ForeignJmsMessage.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/message/foreign/ForeignJmsMessage.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/message/foreign/ForeignJmsMessage.java
index 8ef1643..51215a2 100644
--- a/qpid-jms-client/src/test/java/org/apache/qpid/jms/message/foreign/ForeignJmsMessage.java
+++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/message/foreign/ForeignJmsMessage.java
@@ -265,4 +265,24 @@ public class ForeignJmsMessage implements Message {
public void clearBody() throws JMSException {
message.clearBody();
}
+
+ @Override
+ public long getJMSDeliveryTime() throws JMSException {
+ return message.getJMSDeliveryTime();
+ }
+
+ @Override
+ public void setJMSDeliveryTime(long delay) throws JMSException {
+ message.setJMSDeliveryTime(delay);
+ }
+
+ @Override
+ public <T> T getBody(Class<T> asType) throws JMSException {
+ return message.getBody(asType);
+ }
+
+ @Override
+ public boolean isBodyAssignableTo(@SuppressWarnings("rawtypes") Class target) throws JMSException {
+ return message.isBodyAssignableTo(target);
+ }
}
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/0c39522c/qpid-jms-client/src/test/java/org/apache/qpid/jms/producer/JmsMessageProducerTest.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/producer/JmsMessageProducerTest.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/producer/JmsMessageProducerTest.java
index 7117e9f..a2629a9 100644
--- a/qpid-jms-client/src/test/java/org/apache/qpid/jms/producer/JmsMessageProducerTest.java
+++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/producer/JmsMessageProducerTest.java
@@ -26,6 +26,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import javax.jms.CompletionListener;
import javax.jms.Connection;
import javax.jms.DeliveryMode;
import javax.jms.Destination;
@@ -35,7 +36,6 @@ import javax.jms.Message;
import javax.jms.MessageProducer;
import javax.jms.Session;
-import org.apache.qpid.jms.JmsCompletionListener;
import org.apache.qpid.jms.JmsConnectionFactory;
import org.apache.qpid.jms.JmsConnectionTestSupport;
import org.apache.qpid.jms.JmsDestination;
@@ -541,7 +541,7 @@ public class JmsMessageProducerTest extends JmsConnectionTestSupport {
}
}
- private class MyCompletionListener implements JmsCompletionListener {
+ private class MyCompletionListener implements CompletionListener {
private final List<Message> completed = new ArrayList<Message>();
private final List<Message> failed = new ArrayList<Message>();
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/0c39522c/qpid-jms-client/src/test/java/org/apache/qpid/jms/producer/JmsProducerTest.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/producer/JmsProducerTest.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/producer/JmsProducerTest.java
new file mode 100644
index 0000000..5a0938a
--- /dev/null
+++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/producer/JmsProducerTest.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.jms.producer;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.util.UUID;
+
+import javax.jms.JMSProducer;
+import javax.jms.MessageFormatRuntimeException;
+
+import org.apache.qpid.jms.JmsConnectionTestSupport;
+import org.apache.qpid.jms.JmsContext;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Test various behaviors of the JMSProducer implementation.
+ */
+public class JmsProducerTest extends JmsConnectionTestSupport {
+
+ private JmsContext context;
+
+ private final String BAD_PROPERTY_NAME = "%_BAD_PROPERTY_NAME";
+ private final String GOOD_PROPERTY_NAME = "GOOD_PROPERTY_NAME";
+
+ @Override
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+
+ context = createJMSContextToMockProvider();
+ }
+
+ @Test
+ public void testGetPropertyNames() {
+ JMSProducer producer = context.createProducer();
+
+ producer.setProperty("Property_1", "1");
+ producer.setProperty("Property_2", "2");
+ producer.setProperty("Property_3", "3");
+
+ assertEquals(3, producer.getPropertyNames().size());
+
+ assertTrue(producer.getPropertyNames().contains("Property_1"));
+ assertTrue(producer.getPropertyNames().contains("Property_2"));
+ assertTrue(producer.getPropertyNames().contains("Property_3"));
+ }
+
+ @Test
+ public void testClearProperties() {
+ JMSProducer producer = context.createProducer();
+
+ producer.setProperty("Property_1", "1");
+ producer.setProperty("Property_2", "2");
+ producer.setProperty("Property_3", "3");
+
+ assertEquals(3, producer.getPropertyNames().size());
+
+ producer.clearProperties();
+
+ assertEquals(0, producer.getPropertyNames().size());
+ }
+
+ @Test
+ public void testSetStringPropetryWithBadPropetyName() {
+ JMSProducer producer = context.createProducer();
+
+ try {
+ producer.setProperty(BAD_PROPERTY_NAME, "X");
+ fail("Should not accept invalid property name");
+ } catch (IllegalArgumentException iae) {}
+ }
+
+ @Test
+ public void testSetBytePropetryWithBadPropetyName() {
+ JMSProducer producer = context.createProducer();
+
+ try {
+ producer.setProperty(BAD_PROPERTY_NAME, (byte) 1);
+ fail("Should not accept invalid property name");
+ } catch (IllegalArgumentException iae) {}
+ }
+
+ @Test
+ public void testSetBooleanPropetryWithBadPropetyName() {
+ JMSProducer producer = context.createProducer();
+
+ try {
+ producer.setProperty(BAD_PROPERTY_NAME, true);
+ fail("Should not accept invalid property name");
+ } catch (IllegalArgumentException iae) {}
+ }
+
+ @Test
+ public void testSetDoublePropetryWithBadPropetyName() {
+ JMSProducer producer = context.createProducer();
+
+ try {
+ producer.setProperty(BAD_PROPERTY_NAME, 100.0);
+ fail("Should not accept invalid property name");
+ } catch (IllegalArgumentException iae) {}
+ }
+
+ @Test
+ public void testSetFloatPropetryWithBadPropetyName() {
+ JMSProducer producer = context.createProducer();
+
+ try {
+ producer.setProperty(BAD_PROPERTY_NAME, 100.0f);
+ fail("Should not accept invalid property name");
+ } catch (IllegalArgumentException iae) {}
+ }
+
+ @Test
+ public void testSetShortPropetryWithBadPropetyName() {
+ JMSProducer producer = context.createProducer();
+
+ try {
+ producer.setProperty(BAD_PROPERTY_NAME, (short) 100);
+ fail("Should not accept invalid property name");
+ } catch (IllegalArgumentException iae) {}
+ }
+
+ @Test
+ public void testSetIntPropetryWithBadPropetyName() {
+ JMSProducer producer = context.createProducer();
+
+ try {
+ producer.setProperty(BAD_PROPERTY_NAME, 100);
+ fail("Should not accept invalid property name");
+ } catch (IllegalArgumentException iae) {}
+ }
+
+ @Test
+ public void testSetLongPropetryWithBadPropetyName() {
+ JMSProducer producer = context.createProducer();
+
+ try {
+ producer.setProperty(BAD_PROPERTY_NAME, 100l);
+ fail("Should not accept invalid property name");
+ } catch (IllegalArgumentException iae) {}
+ }
+
+ @Test
+ public void testSetObjectPropetryWithBadPropetyName() {
+ JMSProducer producer = context.createProducer();
+
+ try {
+ producer.setProperty(BAD_PROPERTY_NAME, UUID.randomUUID());
+ fail("Should not accept invalid property name");
+ } catch (IllegalArgumentException iae) {}
+ }
+
+ @Test
+ public void testSetObjectPropetryWithInvalidObject() {
+ JMSProducer producer = context.createProducer();
+
+ try {
+ producer.setProperty(GOOD_PROPERTY_NAME, UUID.randomUUID());
+ fail("Should not accept invalid property name");
+ } catch (MessageFormatRuntimeException mfre) {}
+ }
+}
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/0c39522c/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/failover/FailoverIntegrationTest.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/failover/FailoverIntegrationTest.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/failover/FailoverIntegrationTest.java
index d6dc443..301f93a 100644
--- a/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/failover/FailoverIntegrationTest.java
+++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/failover/FailoverIntegrationTest.java
@@ -32,6 +32,7 @@ import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
+import javax.jms.CompletionListener;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.InvalidDestinationException;
@@ -46,7 +47,6 @@ import javax.jms.Session;
import javax.jms.TextMessage;
import javax.jms.Topic;
-import org.apache.qpid.jms.JmsCompletionListener;
import org.apache.qpid.jms.JmsConnection;
import org.apache.qpid.jms.JmsConnectionFactory;
import org.apache.qpid.jms.JmsDefaultConnectionListener;
@@ -1228,7 +1228,7 @@ public class FailoverIntegrationTest extends QpidJmsTestCase {
return "amqp://localhost:" + peer.getServerPort() + (params != null ? "?" + params : "");
}
- private class TestJmsCompletionListener implements JmsCompletionListener {
+ private class TestJmsCompletionListener implements CompletionListener {
private final CountDownLatch completed;
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/0c39522c/qpid-jms-interop-tests/qpid-jms-activemq-tests/src/test/java/org/apache/qpid/jms/JmsMessageIntegrityTest.java
----------------------------------------------------------------------
diff --git a/qpid-jms-interop-tests/qpid-jms-activemq-tests/src/test/java/org/apache/qpid/jms/JmsMessageIntegrityTest.java b/qpid-jms-interop-tests/qpid-jms-activemq-tests/src/test/java/org/apache/qpid/jms/JmsMessageIntegrityTest.java
index d09d7bc..c9ca39e 100644
--- a/qpid-jms-interop-tests/qpid-jms-activemq-tests/src/test/java/org/apache/qpid/jms/JmsMessageIntegrityTest.java
+++ b/qpid-jms-interop-tests/qpid-jms-activemq-tests/src/test/java/org/apache/qpid/jms/JmsMessageIntegrityTest.java
@@ -33,6 +33,7 @@ import javax.jms.JMSException;
import javax.jms.MapMessage;
import javax.jms.MessageConsumer;
import javax.jms.MessageEOFException;
+import javax.jms.MessageFormatException;
import javax.jms.MessageProducer;
import javax.jms.ObjectMessage;
import javax.jms.Session;
@@ -212,6 +213,7 @@ public class JmsMessageIntegrityTest extends AmqpTestSupport {
public int deliveryMode;
+ private long deliveryTime;
private String messageId;
private long timestamp;
private String correlationId;
@@ -447,6 +449,32 @@ public class JmsMessageIntegrityTest extends AmqpTestSupport {
public String getText() throws JMSException {
return text;
}
+
+ @Override
+ public void setJMSDeliveryTime(long deliveryTime) throws JMSException {
+ this.deliveryTime = deliveryTime;
+ }
+
+ @Override
+ public long getJMSDeliveryTime() throws JMSException {
+ return deliveryTime;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public <T> T getBody(Class<T> target) throws JMSException {
+ if (isBodyAssignableTo(target)) {
+ return (T) text;
+ }
+
+ throw new MessageFormatException("Cannot covert body to type: " + target.getName());
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public boolean isBodyAssignableTo(@SuppressWarnings("rawtypes") Class target) throws JMSException {
+ return target.isAssignableFrom(String.class);
+ }
}
// TODO - implement proper handling of foreign JMS Message and Destination types.
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org
[6/7] qpid-jms git commit: QPIDJMS-207 Adds dependency on JMS 2.0 API
and initial implementation.
Posted by ta...@apache.org.
QPIDJMS-207 Adds dependency on JMS 2.0 API and initial implementation.
Project: http://git-wip-us.apache.org/repos/asf/qpid-jms/repo
Commit: http://git-wip-us.apache.org/repos/asf/qpid-jms/commit/0c39522c
Tree: http://git-wip-us.apache.org/repos/asf/qpid-jms/tree/0c39522c
Diff: http://git-wip-us.apache.org/repos/asf/qpid-jms/diff/0c39522c
Branch: refs/heads/master
Commit: 0c39522cde1c845d8b57978dddfa931ee440e9e3
Parents: 3a03663
Author: Timothy Bish <ta...@gmail.com>
Authored: Mon Sep 12 14:39:42 2016 -0400
Committer: Timothy Bish <ta...@gmail.com>
Committed: Mon Sep 12 14:39:42 2016 -0400
----------------------------------------------------------------------
pom.xml | 7 +-
qpid-jms-client/pom.xml | 6 +-
.../apache/qpid/jms/JmsCompletionListener.java | 47 --
.../java/org/apache/qpid/jms/JmsConnection.java | 24 +
.../apache/qpid/jms/JmsConnectionFactory.java | 34 +-
.../java/org/apache/qpid/jms/JmsConsumer.java | 135 +++++
.../java/org/apache/qpid/jms/JmsContext.java | 510 +++++++++++++++++
.../org/apache/qpid/jms/JmsMessageConsumer.java | 53 ++
.../org/apache/qpid/jms/JmsMessageProducer.java | 106 ++--
.../java/org/apache/qpid/jms/JmsProducer.java | 454 +++++++++++++++
.../java/org/apache/qpid/jms/JmsSession.java | 80 ++-
.../jms/exceptions/JmsExceptionSupport.java | 66 +++
.../qpid/jms/message/JmsBytesMessage.java | 15 +
.../apache/qpid/jms/message/JmsMapMessage.java | 24 +
.../org/apache/qpid/jms/message/JmsMessage.java | 111 ++--
.../message/JmsMessagePropertyIntercepter.java | 73 +--
.../jms/message/JmsMessagePropertySupport.java | 124 ++++
.../qpid/jms/message/JmsObjectMessage.java | 19 +
.../qpid/jms/message/JmsStreamMessage.java | 5 +
.../apache/qpid/jms/message/JmsTextMessage.java | 11 +
.../message/facade/JmsBytesMessageFacade.java | 6 +
.../jms/message/facade/JmsMessageFacade.java | 26 +
.../amqp/message/AmqpJmsBytesMessageFacade.java | 24 +-
.../amqp/message/AmqpJmsMapMessageFacade.java | 5 +
.../amqp/message/AmqpJmsMessageFacade.java | 17 +
.../message/AmqpJmsObjectMessageFacade.java | 5 +
.../message/AmqpJmsStreamMessageFacade.java | 5 +
.../amqp/message/AmqpJmsTextMessageFacade.java | 9 +
.../amqp/message/AmqpObjectTypeDelegate.java | 3 +
.../message/AmqpSerializedObjectDelegate.java | 9 +
.../amqp/message/AmqpTypedObjectDelegate.java | 9 +
.../org/apache/qpid/jms/JmsConnectionTest.java | 10 +-
.../qpid/jms/JmsConnectionTestSupport.java | 9 +
.../integration/ConnectionIntegrationTest.java | 50 ++
.../jms/integration/IntegrationTestFixture.java | 73 ++-
.../integration/JMSConsumerIntegrationTest.java | 559 +++++++++++++++++++
.../integration/JMSContextIntegrationTest.java | 198 +++++++
.../integration/JMSProducerIntegrationTest.java | 200 +++++++
.../integration/MapMessageIntegrationTest.java | 43 +-
.../jms/integration/MessageIntegrationTest.java | 40 ++
.../ObjectMessageIntegrationTest.java | 116 +++-
.../PresettledProducerIntegrationTest.java | 4 +-
.../integration/ProducerIntegrationTest.java | 4 +-
.../jms/integration/SessionIntegrationTest.java | 31 +-
.../StreamMessageIntegrationTest.java | 63 ++-
.../integration/TextMessageIntegrationTest.java | 83 ++-
.../apache/qpid/jms/message/JmsMessageTest.java | 335 +++++++++++
.../facade/test/JmsTestBytesMessageFacade.java | 25 +-
.../facade/test/JmsTestMapMessageFacade.java | 5 +
.../facade/test/JmsTestMessageFacade.java | 16 +
.../facade/test/JmsTestObjectMessageFacade.java | 5 +
.../facade/test/JmsTestStreamMessageFacade.java | 5 +
.../facade/test/JmsTestTextMessageFacade.java | 5 +
.../jms/message/foreign/ForeignJmsMessage.java | 20 +
.../jms/producer/JmsMessageProducerTest.java | 4 +-
.../qpid/jms/producer/JmsProducerTest.java | 182 ++++++
.../failover/FailoverIntegrationTest.java | 4 +-
.../qpid/jms/JmsMessageIntegrityTest.java | 28 +
58 files changed, 3825 insertions(+), 314 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/0c39522c/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index bd7a642..fbb9396 100644
--- a/pom.xml
+++ b/pom.xml
@@ -46,7 +46,8 @@
<proton-version>0.14.0</proton-version>
<netty-version>4.0.41.Final</netty-version>
<slf4j-version>1.7.21</slf4j-version>
- <geronimo-jms-1-1-spec-version>1.1.1</geronimo-jms-1-1-spec-version>
+ <!-- <geronimo-jms-1-1-spec-version>1.1.1</geronimo-jms-1-1-spec-version> -->
+ <geronimo.jms.2.spec.version>1.0-alpha-2</geronimo.jms.2.spec.version>
<!-- Test Dependency Versions for this Project -->
<activemq-version>5.14.0</activemq-version>
<junit-version>4.12</junit-version>
@@ -113,8 +114,8 @@
</dependency>
<dependency>
<groupId>org.apache.geronimo.specs</groupId>
- <artifactId>geronimo-jms_1.1_spec</artifactId>
- <version>${geronimo-jms-1-1-spec-version}</version>
+ <artifactId>geronimo-jms_2.0_spec</artifactId>
+ <version>${geronimo.jms.2.spec.version}</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/0c39522c/qpid-jms-client/pom.xml
----------------------------------------------------------------------
diff --git a/qpid-jms-client/pom.xml b/qpid-jms-client/pom.xml
index a5d1dd6..24b647e 100644
--- a/qpid-jms-client/pom.xml
+++ b/qpid-jms-client/pom.xml
@@ -38,8 +38,12 @@
</dependency>
<dependency>
<groupId>org.apache.geronimo.specs</groupId>
- <artifactId>geronimo-jms_1.1_spec</artifactId>
+ <artifactId>geronimo-jms_2.0_spec</artifactId>
</dependency>
+ <!-- <dependency>
+ <groupId>org.apache.geronimo.specs</groupId>
+ <artifactId>geronimo-jms_1.1_spec</artifactId>
+ </dependency> -->
<dependency>
<groupId>org.apache.qpid</groupId>
<artifactId>proton-j</artifactId>
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/0c39522c/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsCompletionListener.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsCompletionListener.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsCompletionListener.java
deleted file mode 100644
index 7a6c4d6..0000000
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsCompletionListener.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * 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.jms;
-
-import javax.jms.Message;
-
-/**
- * Interface used to implement listeners for asynchronous {@link javax.jms.Message}
- * sends which will be notified on successful completion of a send or be notified of an
- * error that was encountered while attempting to send a {@link javax.jms.Message}.
- */
-public interface JmsCompletionListener {
-
- /**
- * Called when an asynchronous send operation completes successfully.
- *
- * @param message
- * the {@link javax.jms.Message} that was successfully sent.
- */
- void onCompletion(Message message);
-
- /**
- * Called when an asynchronous send operation fails to complete, the state
- * of the send is unknown at this point.
- *
- * @param message
- * the {@link javax.jms.Message} that was to be sent.
- * @param exception
- * the {@link java.lang.Exception} that describes the send error.
- */
- void onException(Message message, Exception exception);
-
-}
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/0c39522c/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsConnection.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsConnection.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsConnection.java
index a04d1b3..79fe8d8 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsConnection.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsConnection.java
@@ -257,6 +257,16 @@ public class JmsConnection implements AutoCloseable, Connection, TopicConnection
}
@Override
+ public Session createSession() throws JMSException {
+ return createSession(false, Session.AUTO_ACKNOWLEDGE);
+ }
+
+ @Override
+ public Session createSession(int acknowledgeMode) throws JMSException {
+ return createSession(acknowledgeMode == Session.SESSION_TRANSACTED ? true : false, acknowledgeMode);
+ }
+
+ @Override
public Session createSession(boolean transacted, int acknowledgeMode) throws JMSException {
checkClosedOrFailed();
connect();
@@ -347,6 +357,20 @@ public class JmsConnection implements AutoCloseable, Connection, TopicConnection
}
@Override
+ public ConnectionConsumer createSharedConnectionConsumer(Topic topic, String subscriptionName, String messageSelector, ServerSessionPool sessionPool, int maxMessages) throws JMSException {
+ checkClosedOrFailed();
+ connect();
+ throw new JMSException("Not supported");
+ }
+
+ @Override
+ public ConnectionConsumer createSharedDurableConnectionConsumer(Topic topic, String subscriptionName, String messageSelector, ServerSessionPool sessionPool, int maxMessages) throws JMSException {
+ checkClosedOrFailed();
+ connect();
+ throw new JMSException("Not supported");
+ }
+
+ @Override
public ConnectionConsumer createDurableConnectionConsumer(Topic topic, String subscriptionName,
String messageSelector, ServerSessionPool sessionPool, int maxMessages) throws JMSException {
checkClosedOrFailed();
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/0c39522c/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsConnectionFactory.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsConnectionFactory.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsConnectionFactory.java
index 217749c..4105842 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsConnectionFactory.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsConnectionFactory.java
@@ -25,6 +25,7 @@ import java.util.Map;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.ExceptionListener;
+import javax.jms.JMSContext;
import javax.jms.JMSException;
import javax.jms.QueueConnection;
import javax.jms.QueueConnectionFactory;
@@ -234,6 +235,35 @@ public class JmsConnectionFactory extends JNDIStorable implements ConnectionFact
}
}
+ //----- JMSContext Creation methods --------------------------------------//
+
+ @Override
+ public JMSContext createContext() {
+ return createContext(getUsername(), getPassword(), JMSContext.AUTO_ACKNOWLEDGE);
+ }
+
+ @Override
+ public JMSContext createContext(int sessionMode) {
+ return createContext(getUsername(), getPassword(), sessionMode);
+ }
+
+ @Override
+ public JMSContext createContext(String username, String password) {
+ return createContext(username, password, JMSContext.AUTO_ACKNOWLEDGE);
+ }
+
+ @Override
+ public JMSContext createContext(String username, String password, int sessionMode) {
+ try {
+ JmsConnection connection = (JmsConnection) createConnection(username, password);
+ return new JmsContext(connection, sessionMode);
+ } catch (JMSException jmse) {
+ throw JmsExceptionSupport.createRuntimeException(jmse);
+ }
+ }
+
+ //----- Internal Support Methods -----------------------------------------//
+
protected Provider createProvider(URI remoteURI) throws Exception {
if (remoteURI == null) {
remoteURI = new URI(getDefaultRemoteAddress());
@@ -279,9 +309,7 @@ public class JmsConnectionFactory extends JNDIStorable implements ConnectionFact
this.connectionIdGenerator = connectionIdGenerator;
}
- //////////////////////////////////////////////////////////////////////////
- // Property getters and setters
- //////////////////////////////////////////////////////////////////////////
+ //----- Property Access Methods ------------------------------------------//
/**
* @return the remoteURI
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/0c39522c/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsConsumer.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsConsumer.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsConsumer.java
new file mode 100644
index 0000000..19691e7
--- /dev/null
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsConsumer.java
@@ -0,0 +1,135 @@
+/*
+ * 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.jms;
+
+import javax.jms.JMSConsumer;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageListener;
+
+import org.apache.qpid.jms.exceptions.JmsExceptionSupport;
+
+@SuppressWarnings("unused")
+public class JmsConsumer implements JMSConsumer, AutoCloseable {
+
+ private final JmsSession session;
+ private final JmsMessageConsumer consumer;
+
+ public JmsConsumer(JmsSession session, JmsMessageConsumer consumer) {
+ this.session = session;
+ this.consumer = consumer;
+ }
+
+ @Override
+ public void close() {
+ try {
+ consumer.close();
+ } catch (JMSException e) {
+ throw JmsExceptionSupport.createRuntimeException(e);
+ }
+ }
+
+ //----- MessageConsumer Property Methods ---------------------------------//
+
+ @Override
+ public MessageListener getMessageListener() {
+ try {
+ return consumer.getMessageListener();
+ } catch (JMSException e) {
+ throw JmsExceptionSupport.createRuntimeException(e);
+ }
+ }
+
+ @Override
+ public String getMessageSelector() {
+ try {
+ return consumer.getMessageSelector();
+ } catch (JMSException e) {
+ throw JmsExceptionSupport.createRuntimeException(e);
+ }
+ }
+
+ @Override
+ public void setMessageListener(MessageListener listener) {
+ try {
+ consumer.setMessageListener(listener);
+ } catch (JMSException e) {
+ throw JmsExceptionSupport.createRuntimeException(e);
+ }
+ }
+
+ //----- Receive Methods --------------------------------------------------//
+
+ @Override
+ public Message receive() {
+ try {
+ return consumer.receive();
+ } catch (JMSException e) {
+ throw JmsExceptionSupport.createRuntimeException(e);
+ }
+ }
+
+ @Override
+ public Message receive(long timeout) {
+ try {
+ return consumer.receive(timeout);
+ } catch (JMSException e) {
+ throw JmsExceptionSupport.createRuntimeException(e);
+ }
+ }
+
+ @Override
+ public Message receiveNoWait() {
+ try {
+ return consumer.receiveNoWait();
+ } catch (JMSException e) {
+ throw JmsExceptionSupport.createRuntimeException(e);
+ }
+ }
+
+ @Override
+ public <T> T receiveBody(Class<T> desired) {
+ try {
+ return consumer.receiveBody(desired, -1);
+ } catch (JMSException e) {
+ throw JmsExceptionSupport.createRuntimeException(e);
+ }
+ }
+
+ @Override
+ public <T> T receiveBody(Class<T> desired, long timeout) {
+ try {
+ // Configure for infinite wait when timeout is zero (JMS Spec)
+ if (timeout == 0) {
+ timeout = -1;
+ }
+
+ return consumer.receiveBody(desired, timeout);
+ } catch (JMSException e) {
+ throw JmsExceptionSupport.createRuntimeException(e);
+ }
+ }
+
+ @Override
+ public <T> T receiveBodyNoWait(Class<T> desired) {
+ try {
+ return consumer.receiveBody(desired, 0);
+ } catch (JMSException e) {
+ throw JmsExceptionSupport.createRuntimeException(e);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/0c39522c/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsContext.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsContext.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsContext.java
new file mode 100644
index 0000000..008da24
--- /dev/null
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsContext.java
@@ -0,0 +1,510 @@
+/*
+ * 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.jms;
+
+import java.io.Serializable;
+import java.util.concurrent.atomic.AtomicLong;
+
+import javax.jms.BytesMessage;
+import javax.jms.ConnectionMetaData;
+import javax.jms.Destination;
+import javax.jms.ExceptionListener;
+import javax.jms.IllegalStateRuntimeException;
+import javax.jms.JMSConsumer;
+import javax.jms.JMSContext;
+import javax.jms.JMSException;
+import javax.jms.JMSProducer;
+import javax.jms.JMSRuntimeException;
+import javax.jms.MapMessage;
+import javax.jms.Message;
+import javax.jms.ObjectMessage;
+import javax.jms.Queue;
+import javax.jms.QueueBrowser;
+import javax.jms.Session;
+import javax.jms.StreamMessage;
+import javax.jms.TemporaryQueue;
+import javax.jms.TemporaryTopic;
+import javax.jms.TextMessage;
+import javax.jms.Topic;
+
+import org.apache.qpid.jms.exceptions.JmsExceptionSupport;
+import org.apache.qpid.jms.provider.ProviderConstants.ACK_TYPE;
+
+public class JmsContext implements JMSContext, AutoCloseable {
+
+ private final JmsConnection connection;
+ private final AtomicLong connectionRefCount;
+ private final int sessionMode;
+
+ private JmsSession session;
+ private JmsMessageProducer sharedProducer;
+ private boolean autoStart = true;
+
+ public JmsContext(JmsConnection connection, int sessionMode) {
+ this(connection, sessionMode, new AtomicLong(1));
+ }
+
+ private JmsContext(JmsConnection connection, int sessionMode, AtomicLong connectionRefCount) {
+ this.connection = connection;
+ this.sessionMode = sessionMode;
+ this.connectionRefCount = connectionRefCount;
+ }
+
+ @Override
+ public void start() {
+ try {
+ connection.start();
+ } catch (JMSException jmse) {
+ throw JmsExceptionSupport.createRuntimeException(jmse);
+ }
+ }
+
+ @Override
+ public void stop() {
+ try {
+ connection.stop();
+ } catch (JMSException jmse) {
+ throw JmsExceptionSupport.createRuntimeException(jmse);
+ }
+ }
+
+ @Override
+ public void close() {
+ JMSRuntimeException failure = null;
+
+ synchronized (this) {
+ try {
+ if (session != null) {
+ session.close();
+ }
+ } catch (JMSException jmse) {
+ failure = JmsExceptionSupport.createRuntimeException(jmse);
+ }
+
+ if (connectionRefCount.decrementAndGet() == 0) {
+ try {
+ connection.close();
+ } catch (JMSException jmse) {
+ failure = JmsExceptionSupport.createRuntimeException(jmse);
+ }
+ }
+ }
+
+ if (failure != null) {
+ throw failure;
+ }
+ }
+
+ //----- Session state management -----------------------------------------//
+
+ @Override
+ public void acknowledge() {
+ if (getSessionMode() == Session.CLIENT_ACKNOWLEDGE) {
+ try {
+ getSession().acknowledge(ACK_TYPE.ACCEPTED);
+ } catch (JMSException jmse) {
+ throw JmsExceptionSupport.createRuntimeException(jmse);
+ }
+ }
+ }
+
+ @Override
+ public void commit() {
+ try {
+ getSession().commit();
+ } catch (JMSException jmse) {
+ throw JmsExceptionSupport.createRuntimeException(jmse);
+ }
+ }
+
+ @Override
+ public void rollback() {
+ try {
+ getSession().rollback();
+ } catch (JMSException jmse) {
+ throw JmsExceptionSupport.createRuntimeException(jmse);
+ }
+ }
+
+ @Override
+ public void recover() {
+ try {
+ getSession().recover();
+ } catch (JMSException jmse) {
+ throw JmsExceptionSupport.createRuntimeException(jmse);
+ }
+ }
+
+ @Override
+ public void unsubscribe(String name) {
+ try {
+ getSession().unsubscribe(name);
+ } catch (JMSException jmse) {
+ throw JmsExceptionSupport.createRuntimeException(jmse);
+ }
+ }
+
+ //----- Message Factory methods ------------------------------------------//
+
+ @Override
+ public BytesMessage createBytesMessage() {
+ try {
+ return getSession().createBytesMessage();
+ } catch (JMSException jmse) {
+ throw JmsExceptionSupport.createRuntimeException(jmse);
+ }
+ }
+
+ @Override
+ public MapMessage createMapMessage() {
+ try {
+ return getSession().createMapMessage();
+ } catch (JMSException jmse) {
+ throw JmsExceptionSupport.createRuntimeException(jmse);
+ }
+ }
+
+ @Override
+ public Message createMessage() {
+ try {
+ return getSession().createMessage();
+ } catch (JMSException jmse) {
+ throw JmsExceptionSupport.createRuntimeException(jmse);
+ }
+ }
+
+ @Override
+ public ObjectMessage createObjectMessage() {
+ try {
+ return getSession().createObjectMessage();
+ } catch (JMSException jmse) {
+ throw JmsExceptionSupport.createRuntimeException(jmse);
+ }
+ }
+
+ @Override
+ public ObjectMessage createObjectMessage(Serializable object) {
+ try {
+ return getSession().createObjectMessage(object);
+ } catch (JMSException jmse) {
+ throw JmsExceptionSupport.createRuntimeException(jmse);
+ }
+ }
+
+ @Override
+ public StreamMessage createStreamMessage() {
+ try {
+ return getSession().createStreamMessage();
+ } catch (JMSException jmse) {
+ throw JmsExceptionSupport.createRuntimeException(jmse);
+ }
+ }
+
+ @Override
+ public TextMessage createTextMessage() {
+ try {
+ return getSession().createTextMessage();
+ } catch (JMSException jmse) {
+ throw JmsExceptionSupport.createRuntimeException(jmse);
+ }
+ }
+
+ @Override
+ public TextMessage createTextMessage(String text) {
+ try {
+ return getSession().createTextMessage(text);
+ } catch (JMSException jmse) {
+ throw JmsExceptionSupport.createRuntimeException(jmse);
+ }
+ }
+
+ //----- Destination Creation ---------------------------------------------//
+
+ @Override
+ public Queue createQueue(String queueName) {
+ try {
+ return getSession().createQueue(queueName);
+ } catch (JMSException jmse) {
+ throw JmsExceptionSupport.createRuntimeException(jmse);
+ }
+ }
+
+ @Override
+ public Topic createTopic(String topicName) {
+ try {
+ return getSession().createTopic(topicName);
+ } catch (JMSException jmse) {
+ throw JmsExceptionSupport.createRuntimeException(jmse);
+ }
+ }
+
+ @Override
+ public TemporaryQueue createTemporaryQueue() {
+ try {
+ return getSession().createTemporaryQueue();
+ } catch (JMSException jmse) {
+ throw JmsExceptionSupport.createRuntimeException(jmse);
+ }
+ }
+
+ @Override
+ public TemporaryTopic createTemporaryTopic() {
+ try {
+ return getSession().createTemporaryTopic();
+ } catch (JMSException jmse) {
+ throw JmsExceptionSupport.createRuntimeException(jmse);
+ }
+ }
+
+ //----- JMSContext factory methods --------------------------------------//
+
+ @Override
+ public JMSContext createContext(int sessionMode) {
+ synchronized (this) {
+ if (connectionRefCount.get() == 0) {
+ throw new IllegalStateRuntimeException("The Connection is closed");
+ }
+
+ connectionRefCount.incrementAndGet();
+
+ return new JmsContext(connection, sessionMode, connectionRefCount);
+ }
+ }
+
+ //----- JMSProducer factory methods --------------------------------------//
+
+ @Override
+ public JMSProducer createProducer() {
+ try {
+ if (sharedProducer == null) {
+ synchronized (this) {
+ if (sharedProducer == null) {
+ sharedProducer = (JmsMessageProducer) getSession().createProducer(null);
+ }
+ }
+ }
+
+ return new JmsProducer(getSession(), sharedProducer);
+ } catch (JMSException jmse) {
+ throw JmsExceptionSupport.createRuntimeException(jmse);
+ }
+ }
+
+ //----- JMSConsumer factory methods --------------------------------------//
+
+ @Override
+ public JMSConsumer createConsumer(Destination destination) {
+ try {
+ return startIfNeeded(new JmsConsumer(getSession(), (JmsMessageConsumer) getSession().createConsumer(destination)));
+ } catch (JMSException jmse) {
+ throw JmsExceptionSupport.createRuntimeException(jmse);
+ }
+ }
+
+ @Override
+ public JMSConsumer createConsumer(Destination destination, String selector) {
+ try {
+ return startIfNeeded(new JmsConsumer(getSession(), (JmsMessageConsumer) getSession().createConsumer(destination, selector)));
+ } catch (JMSException jmse) {
+ throw JmsExceptionSupport.createRuntimeException(jmse);
+ }
+ }
+
+ @Override
+ public JMSConsumer createConsumer(Destination destination, String selector, boolean noLocal) {
+ try {
+ return startIfNeeded(new JmsConsumer(getSession(), (JmsMessageConsumer) getSession().createConsumer(destination, selector, noLocal)));
+ } catch (JMSException jmse) {
+ throw JmsExceptionSupport.createRuntimeException(jmse);
+ }
+ }
+
+ @Override
+ public JMSConsumer createDurableConsumer(Topic topic, String name) {
+ try {
+ return startIfNeeded(new JmsConsumer(getSession(), (JmsMessageConsumer) getSession().createDurableConsumer(topic, name)));
+ } catch (JMSException jmse) {
+ throw JmsExceptionSupport.createRuntimeException(jmse);
+ }
+ }
+
+ @Override
+ public JMSConsumer createDurableConsumer(Topic topic, String name, String selector, boolean noLocal) {
+ try {
+ return startIfNeeded(new JmsConsumer(getSession(), (JmsMessageConsumer) getSession().createDurableConsumer(topic, name, selector, noLocal)));
+ } catch (JMSException jmse) {
+ throw JmsExceptionSupport.createRuntimeException(jmse);
+ }
+ }
+
+ @Override
+ public JMSConsumer createSharedConsumer(Topic topic, String name) {
+ try {
+ return startIfNeeded(new JmsConsumer(getSession(), (JmsMessageConsumer) getSession().createSharedConsumer(topic, name)));
+ } catch (JMSException jmse) {
+ throw JmsExceptionSupport.createRuntimeException(jmse);
+ }
+ }
+
+ @Override
+ public JMSConsumer createSharedConsumer(Topic topic, String name, String selector) {
+ try {
+ return startIfNeeded(new JmsConsumer(getSession(), (JmsMessageConsumer) getSession().createSharedConsumer(topic, name, selector)));
+ } catch (JMSException jmse) {
+ throw JmsExceptionSupport.createRuntimeException(jmse);
+ }
+ }
+
+ @Override
+ public JMSConsumer createSharedDurableConsumer(Topic topic, String name) {
+ try {
+ return startIfNeeded(new JmsConsumer(getSession(), (JmsMessageConsumer) getSession().createSharedDurableConsumer(topic, name)));
+ } catch (JMSException jmse) {
+ throw JmsExceptionSupport.createRuntimeException(jmse);
+ }
+ }
+
+ @Override
+ public JMSConsumer createSharedDurableConsumer(Topic topic, String name, String selector) {
+ try {
+ return startIfNeeded(new JmsConsumer(getSession(), (JmsMessageConsumer) getSession().createSharedDurableConsumer(topic, name, selector)));
+ } catch (JMSException jmse) {
+ throw JmsExceptionSupport.createRuntimeException(jmse);
+ }
+ }
+
+ //----- QueueBrowser Factory Methods -------------------------------------//
+
+ @Override
+ public QueueBrowser createBrowser(Queue queue) {
+ try {
+ return startIfNeeded(getSession().createBrowser(queue));
+ } catch (JMSException jmse) {
+ throw JmsExceptionSupport.createRuntimeException(jmse);
+ }
+ }
+
+ @Override
+ public QueueBrowser createBrowser(Queue queue, String selector) {
+ try {
+ return startIfNeeded(getSession().createBrowser(queue, selector));
+ } catch (JMSException jmse) {
+ throw JmsExceptionSupport.createRuntimeException(jmse);
+ }
+ }
+
+ //----- Get or Set Context and Session values ----------------------------//
+
+ @Override
+ public boolean getAutoStart() {
+ return autoStart;
+ }
+
+ @Override
+ public void setAutoStart(boolean autoStart) {
+ this.autoStart = autoStart;
+ }
+
+ @Override
+ public String getClientID() {
+ try {
+ return connection.getClientID();
+ } catch (JMSException jmse) {
+ throw JmsExceptionSupport.createRuntimeException(jmse);
+ }
+ }
+
+ @Override
+ public void setClientID(String clientID) {
+ try {
+ connection.setClientID(clientID);
+ } catch (JMSException jmse) {
+ throw JmsExceptionSupport.createRuntimeException(jmse);
+ }
+ }
+
+ @Override
+ public ExceptionListener getExceptionListener() {
+ try {
+ return connection.getExceptionListener();
+ } catch (JMSException jmse) {
+ throw JmsExceptionSupport.createRuntimeException(jmse);
+ }
+ }
+
+ @Override
+ public void setExceptionListener(ExceptionListener listener) {
+ try {
+ connection.setExceptionListener(listener);
+ } catch (JMSException jmse) {
+ throw JmsExceptionSupport.createRuntimeException(jmse);
+ }
+ }
+
+ @Override
+ public ConnectionMetaData getMetaData() {
+ try {
+ return connection.getMetaData();
+ } catch (JMSException jmse) {
+ throw JmsExceptionSupport.createRuntimeException(jmse);
+ }
+ }
+
+ @Override
+ public int getSessionMode() {
+ return sessionMode;
+ }
+
+ @Override
+ public boolean getTransacted() {
+ return sessionMode == JMSContext.SESSION_TRANSACTED;
+ }
+
+ //----- Internal implementation methods ----------------------------------//
+
+ private JmsSession getSession() {
+ if (session == null) {
+ synchronized (this) {
+ if (session == null) {
+ try {
+ session = (JmsSession) connection.createSession(getSessionMode());
+ } catch (JMSException jmse) {
+ throw JmsExceptionSupport.createRuntimeException(jmse);
+ }
+ }
+ }
+ }
+
+ return session;
+ }
+
+ private QueueBrowser startIfNeeded(QueueBrowser browser) throws JMSException {
+ if (getAutoStart()) {
+ connection.start();
+ }
+
+ return browser;
+ }
+
+ private JmsConsumer startIfNeeded(JmsConsumer consumer) throws JMSException {
+ if (getAutoStart()) {
+ connection.start();
+ }
+
+ return consumer;
+ }
+}
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/0c39522c/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsMessageConsumer.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsMessageConsumer.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsMessageConsumer.java
index 18fd764..ea93019 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsMessageConsumer.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsMessageConsumer.java
@@ -26,6 +26,7 @@ import javax.jms.IllegalStateException;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
+import javax.jms.MessageFormatException;
import javax.jms.MessageListener;
import javax.jms.Session;
@@ -213,6 +214,58 @@ public class JmsMessageConsumer implements AutoCloseable, MessageConsumer, JmsMe
}
/**
+ * Reads the next available message for this consumer and returns the body of that message
+ * if the type requested matches that of the message. The amount of time this method blocks
+ * is based on the timeout value.
+ *
+ * {@literal timeout < 0} then it blocks until a message is received.
+ * {@literal timeout = 0} then it returns the body immediately or null if none available.
+ * {@literal timeout > 0} then it blocks up to timeout amount of time.
+ *
+ * @param desired
+ * The type to assign the body of the message to for return.
+ * @param timeout
+ * The time to wait for an incoming message before this method returns null.
+ *
+ * @return the assigned body of the next available message or null if the consumer is closed
+ * or the specified timeout elapses.
+ *
+ * @throws MessageFormatException if the message body cannot be assigned to the requested type.
+ * @throws JMSException if an error occurs while receiving the next message.
+ */
+ public <T> T receiveBody(Class<T> desired, long timeout) throws JMSException {
+ checkClosed();
+ checkMessageListener();
+
+ T messageBody = null;
+ JmsInboundMessageDispatch envelope = null;
+
+ try {
+ envelope = dequeue(timeout, connection.isReceiveLocalOnly());
+ if (envelope != null) {
+ messageBody = envelope.getMessage().getBody(desired);
+ }
+ } catch (MessageFormatException mfe) {
+ // Should behave as if receiveBody never happened in these modes.
+ if (acknowledgementMode == Session.AUTO_ACKNOWLEDGE ||
+ acknowledgementMode == Session.DUPS_OK_ACKNOWLEDGE) {
+
+ envelope.setEnqueueFirst(true);
+ onInboundMessage(envelope);
+ envelope = null;
+ }
+
+ throw mfe;
+ } finally {
+ if (envelope != null) {
+ ackFromReceive(envelope);
+ }
+ }
+
+ return messageBody;
+ }
+
+ /**
* Used to get an enqueued message from the unconsumedMessages list. The
* amount of time this method blocks is based on the timeout value.
*
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/0c39522c/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsMessageProducer.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsMessageProducer.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsMessageProducer.java
index 65812b7..6e9c96d 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsMessageProducer.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsMessageProducer.java
@@ -20,6 +20,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
+import javax.jms.CompletionListener;
import javax.jms.DeliveryMode;
import javax.jms.Destination;
import javax.jms.IllegalStateException;
@@ -43,6 +44,7 @@ public class JmsMessageProducer implements AutoCloseable, MessageProducer {
protected final JmsConnection connection;
protected JmsProducerInfo producerInfo;
protected final boolean anonymousProducer;
+ protected long deliveryDelay = Message.DEFAULT_DELIVERY_DELAY;
protected int deliveryMode = DeliveryMode.PERSISTENT;
protected int priority = Message.DEFAULT_PRIORITY;
protected long timeToLive = Message.DEFAULT_TIME_TO_LIVE;
@@ -110,44 +112,50 @@ public class JmsMessageProducer implements AutoCloseable, MessageProducer {
}
@Override
+ public long getDeliveryDelay() throws JMSException {
+ checkClosed();
+ return deliveryMode;
+ }
+
+ @Override
public int getDeliveryMode() throws JMSException {
checkClosed();
- return this.deliveryMode;
+ return deliveryMode;
}
@Override
public Destination getDestination() throws JMSException {
checkClosed();
- return this.producerInfo.getDestination();
+ return producerInfo.getDestination();
}
@Override
public boolean getDisableMessageID() throws JMSException {
checkClosed();
- return this.disableMessageId;
+ return disableMessageId;
}
@Override
public boolean getDisableMessageTimestamp() throws JMSException {
checkClosed();
- return this.disableTimestamp;
+ return disableTimestamp;
}
@Override
public int getPriority() throws JMSException {
checkClosed();
- return this.priority;
+ return priority;
}
@Override
public long getTimeToLive() throws JMSException {
checkClosed();
- return this.timeToLive;
+ return timeToLive;
}
@Override
public void send(Message message) throws JMSException {
- send(message, this.deliveryMode, this.priority, this.timeToLive);
+ send(message, deliveryMode, priority, timeToLive);
}
@Override
@@ -163,7 +171,7 @@ public class JmsMessageProducer implements AutoCloseable, MessageProducer {
@Override
public void send(Destination destination, Message message) throws JMSException {
- send(destination, message, this.deliveryMode, this.priority, this.timeToLive);
+ send(destination, message, deliveryMode, priority, timeToLive);
}
@Override
@@ -177,37 +185,13 @@ public class JmsMessageProducer implements AutoCloseable, MessageProducer {
sendMessage(destination, message, deliveryMode, priority, timeToLive, null);
}
- /**
- * Sends the message asynchronously and notifies the assigned listener on success or failure
- *
- * @param message
- * the {@link javax.jms.Message} to send.
- * @param listener
- * the {@link JmsCompletionListener} to notify on send success or failure.
- *
- * @throws JMSException if an error occurs while attempting to send the Message.
- */
- public void send(Message message, JmsCompletionListener listener) throws JMSException {
- send(message, this.deliveryMode, this.priority, this.timeToLive, listener);
+ @Override
+ public void send(Message message, CompletionListener listener) throws JMSException {
+ send(message, deliveryMode, priority, timeToLive, listener);
}
- /**
- * Sends the message asynchronously and notifies the assigned listener on success or failure
- *
- * @param message
- * the {@link javax.jms.Message} to send.
- * @param deliveryMode
- * the delivery mode to assign to the outbound Message.
- * @param priority
- * the priority to assign to the outbound Message.
- * @param timeToLive
- * the time to live value to assign to the outbound Message.
- * @param listener
- * the {@link JmsCompletionListener} to notify on send success or failure.
- *
- * @throws JMSException if an error occurs while attempting to send the Message.
- */
- public void send(Message message, int deliveryMode, int priority, long timeToLive, JmsCompletionListener listener) throws JMSException {
+ @Override
+ public void send(Message message, int deliveryMode, int priority, long timeToLive, CompletionListener listener) throws JMSException {
checkClosed();
if (anonymousProducer) {
@@ -221,41 +205,13 @@ public class JmsMessageProducer implements AutoCloseable, MessageProducer {
sendMessage(producerInfo.getDestination(), message, deliveryMode, priority, timeToLive, listener);
}
- /**
- * Sends the message asynchronously and notifies the assigned listener on success or failure
- *
- * @param destination
- * the Destination to send the given Message to.
- * @param message
- * the {@link javax.jms.Message} to send.
- * @param listener
- * the {@link JmsCompletionListener} to notify on send success or failure.
- *
- * @throws JMSException if an error occurs while attempting to send the Message.
- */
- public void send(Destination destination, Message message, JmsCompletionListener listener) throws JMSException {
+ @Override
+ public void send(Destination destination, Message message, CompletionListener listener) throws JMSException {
send(destination, message, this.deliveryMode, this.priority, this.timeToLive, listener);
}
- /**
- * Sends the message asynchronously and notifies the assigned listener on success or failure
- *
- * @param destination
- * the Destination to send the given Message to.
- * @param message
- * the {@link javax.jms.Message} to send.
- * @param deliveryMode
- * the delivery mode to assign to the outbound Message.
- * @param priority
- * the priority to assign to the outbound Message.
- * @param timeToLive
- * the time to live value to assign to the outbound Message.
- * @param listener
- * the {@link JmsCompletionListener} to notify on send success or failure.
- *
- * @throws JMSException if an error occurs while attempting to send the Message.
- */
- public void send(Destination destination, Message message, int deliveryMode, int priority, long timeToLive, JmsCompletionListener listener) throws JMSException {
+ @Override
+ public void send(Destination destination, Message message, int deliveryMode, int priority, long timeToLive, CompletionListener listener) throws JMSException {
checkClosed();
if (!anonymousProducer) {
@@ -269,12 +225,18 @@ public class JmsMessageProducer implements AutoCloseable, MessageProducer {
sendMessage(destination, message, deliveryMode, priority, timeToLive, listener);
}
- private void sendMessage(Destination destination, Message message, int deliveryMode, int priority, long timeToLive, JmsCompletionListener listener) throws JMSException {
+ private void sendMessage(Destination destination, Message message, int deliveryMode, int priority, long timeToLive, CompletionListener listener) throws JMSException {
if (destination == null) {
throw new InvalidDestinationException("Don't understand null destinations");
}
- this.session.send(this, destination, message, deliveryMode, priority, timeToLive, disableMessageId, disableTimestamp, listener);
+ this.session.send(this, destination, message, deliveryMode, priority, timeToLive, disableMessageId, disableTimestamp, deliveryDelay, listener);
+ }
+
+ @Override
+ public void setDeliveryDelay(long deliveryDelay) throws JMSException {
+ checkClosed();
+ this.deliveryDelay = deliveryDelay;
}
@Override
@@ -318,7 +280,7 @@ public class JmsMessageProducer implements AutoCloseable, MessageProducer {
* @return the next logical sequence for a Message sent from this Producer.
*/
protected long getNextMessageSequence() {
- return this.messageSequence.incrementAndGet();
+ return messageSequence.incrementAndGet();
}
protected void checkClosed() throws IllegalStateException {
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/0c39522c/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsProducer.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsProducer.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsProducer.java
new file mode 100644
index 0000000..a90ec23
--- /dev/null
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsProducer.java
@@ -0,0 +1,454 @@
+/*
+ * 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.jms;
+
+import static org.apache.qpid.jms.message.JmsMessagePropertySupport.checkPropertyNameIsValid;
+import static org.apache.qpid.jms.message.JmsMessagePropertySupport.checkValidObject;
+import static org.apache.qpid.jms.message.JmsMessagePropertySupport.convertPropertyTo;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import javax.jms.BytesMessage;
+import javax.jms.CompletionListener;
+import javax.jms.DeliveryMode;
+import javax.jms.Destination;
+import javax.jms.JMSException;
+import javax.jms.JMSProducer;
+import javax.jms.MapMessage;
+import javax.jms.Message;
+import javax.jms.ObjectMessage;
+import javax.jms.TextMessage;
+
+import org.apache.qpid.jms.exceptions.JmsExceptionSupport;
+import org.apache.qpid.jms.message.JmsMessageTransformation;
+
+public class JmsProducer implements JMSProducer {
+
+ private final JmsSession session;
+ private final JmsMessageProducer producer;
+
+ private CompletionListener completionListener;
+
+ // Message Headers
+ private String correlationId;
+ private String type;
+ private Destination replyTo;
+ private byte[] correlationIdBytes;
+
+ // Producer send configuration
+ private long deliveryDelay = Message.DEFAULT_DELIVERY_DELAY;
+ private int deliveryMode = DeliveryMode.PERSISTENT;
+ private int priority = Message.DEFAULT_PRIORITY;
+ private long timeToLive = Message.DEFAULT_TIME_TO_LIVE;
+ private boolean disableMessageId;
+ private boolean disableTimestamp;
+
+ // Message Properties
+ private final Map<String, Object> messageProperties = new HashMap<String, Object>();
+
+ /**
+ * Create a new JMSProducer instance.
+ *
+ * The producer is backed by the given Session object and uses the shared MessageProducer
+ * instance to send all of its messages.
+ *
+ * @param session
+ * The Session that created this JMSProducer
+ * @param producer
+ * The shared MessageProducer owned by the parent Session.
+ */
+ public JmsProducer(JmsSession session, JmsMessageProducer producer) {
+ this.session = session;
+ this.producer = producer;
+ }
+
+ //----- Send Methods -----------------------------------------------------//
+
+ @Override
+ public JMSProducer send(Destination destination, Message message) {
+ try {
+ doSend(destination, message);
+ } catch (JMSException jmse) {
+ throw JmsExceptionSupport.createRuntimeException(jmse);
+ }
+
+ return this;
+ }
+
+ @Override
+ public JMSProducer send(Destination destination, byte[] body) {
+ try {
+ BytesMessage message = session.createBytesMessage();
+ message.writeBytes(body);
+ doSend(destination, message);
+ } catch (JMSException jmse) {
+ throw JmsExceptionSupport.createRuntimeException(jmse);
+ }
+
+ return this;
+ }
+
+ @Override
+ public JMSProducer send(Destination destination, Map<String, Object> body) {
+ try {
+ MapMessage message = session.createMapMessage();
+ for (Map.Entry<String, Object> entry : body.entrySet()) {
+ message.setObject(entry.getKey(), entry.getValue());
+ }
+
+ doSend(destination, message);
+ } catch (JMSException jmse) {
+ throw JmsExceptionSupport.createRuntimeException(jmse);
+ }
+
+ return this;
+ }
+
+ @Override
+ public JMSProducer send(Destination destination, Serializable body) {
+ try {
+ ObjectMessage message = session.createObjectMessage();
+ message.setObject(body);
+ doSend(destination, message);
+ } catch (JMSException jmse) {
+ throw JmsExceptionSupport.createRuntimeException(jmse);
+ }
+
+ return this;
+ }
+
+ @Override
+ public JMSProducer send(Destination destination, String body) {
+ try {
+ TextMessage message = session.createTextMessage(body);
+ doSend(destination, message);
+ } catch (JMSException jmse) {
+ throw JmsExceptionSupport.createRuntimeException(jmse);
+ }
+
+ return this;
+ }
+
+ private void doSend(Destination destination, Message message) throws JMSException {
+
+ for (Map.Entry<String, Object> entry : messageProperties.entrySet()) {
+ message.setObjectProperty(entry.getKey(), entry.getValue());
+ }
+
+ if (correlationId != null) {
+ message.setJMSCorrelationID(correlationId);
+ }
+ if (correlationIdBytes != null) {
+ message.setJMSCorrelationIDAsBytes(correlationIdBytes);
+ }
+ if (type != null) {
+ message.setJMSType(type);
+ }
+ if (replyTo != null) {
+ message.setJMSReplyTo(replyTo);
+ }
+
+ session.send(producer, destination, message, deliveryMode, priority, timeToLive, disableMessageId, disableTimestamp, deliveryDelay, completionListener);
+ }
+
+ //----- Message Property Methods -----------------------------------------//
+
+ @Override
+ public JMSProducer clearProperties() {
+ messageProperties.clear();
+ return this;
+ }
+
+ @Override
+ public Set<String> getPropertyNames() {
+ return new HashSet<String>(messageProperties.keySet());
+ }
+
+ @Override
+ public boolean propertyExists(String name) {
+ return messageProperties.containsKey(name);
+ }
+
+ @Override
+ public boolean getBooleanProperty(String name) {
+ try {
+ return convertPropertyTo(name, messageProperties.get(name), Boolean.class);
+ } catch (JMSException jmse) {
+ throw JmsExceptionSupport.createRuntimeException(jmse);
+ }
+ }
+
+ @Override
+ public byte getByteProperty(String name) {
+ try {
+ return convertPropertyTo(name, messageProperties.get(name), Byte.class);
+ } catch (JMSException jmse) {
+ throw JmsExceptionSupport.createRuntimeException(jmse);
+ }
+ }
+
+ @Override
+ public double getDoubleProperty(String name) {
+ try {
+ return convertPropertyTo(name, messageProperties.get(name), Double.class);
+ } catch (JMSException jmse) {
+ throw JmsExceptionSupport.createRuntimeException(jmse);
+ }
+ }
+
+ @Override
+ public float getFloatProperty(String name) {
+ try {
+ return convertPropertyTo(name, messageProperties.get(name), Float.class);
+ } catch (JMSException jmse) {
+ throw JmsExceptionSupport.createRuntimeException(jmse);
+ }
+ }
+
+ @Override
+ public int getIntProperty(String name) {
+ try {
+ return convertPropertyTo(name, messageProperties.get(name), Integer.class);
+ } catch (JMSException jmse) {
+ throw JmsExceptionSupport.createRuntimeException(jmse);
+ }
+ }
+
+ @Override
+ public long getLongProperty(String name) {
+ try {
+ return convertPropertyTo(name, messageProperties.get(name), Long.class);
+ } catch (JMSException jmse) {
+ throw JmsExceptionSupport.createRuntimeException(jmse);
+ }
+ }
+
+ @Override
+ public Object getObjectProperty(String name) {
+ return messageProperties.get(name);
+ }
+
+ @Override
+ public short getShortProperty(String name) {
+ try {
+ return convertPropertyTo(name, messageProperties.get(name), Short.class);
+ } catch (JMSException jmse) {
+ throw JmsExceptionSupport.createRuntimeException(jmse);
+ }
+ }
+
+ @Override
+ public String getStringProperty(String name) {
+ try {
+ return convertPropertyTo(name, messageProperties.get(name), String.class);
+ } catch (JMSException jmse) {
+ throw JmsExceptionSupport.createRuntimeException(jmse);
+ }
+ }
+
+ @Override
+ public JMSProducer setProperty(String name, boolean value) {
+ return setObjectProperty(name, value);
+ }
+
+ @Override
+ public JMSProducer setProperty(String name, byte value) {
+ return setObjectProperty(name, value);
+ }
+
+ @Override
+ public JMSProducer setProperty(String name, double value) {
+ return setObjectProperty(name, value);
+ }
+
+ @Override
+ public JMSProducer setProperty(String name, float value) {
+ return setObjectProperty(name, value);
+ }
+
+ @Override
+ public JMSProducer setProperty(String name, int value) {
+ return setObjectProperty(name, value);
+ }
+
+ @Override
+ public JMSProducer setProperty(String name, long value) {
+ return setObjectProperty(name, value);
+ }
+
+ @Override
+ public JMSProducer setProperty(String name, Object value) {
+ return setObjectProperty(name, value);
+ }
+
+ @Override
+ public JMSProducer setProperty(String name, short value) {
+ return setObjectProperty(name, value);
+ }
+
+ @Override
+ public JMSProducer setProperty(String name, String value) {
+ return setObjectProperty(name, value);
+ }
+
+ //----- Message Headers --------------------------------------------------//
+
+ @Override
+ public String getJMSCorrelationID() {
+ return correlationId;
+ }
+
+ @Override
+ public JMSProducer setJMSCorrelationID(String correlationId) {
+ this.correlationId = correlationId;
+ return this;
+ }
+
+ @Override
+ public byte[] getJMSCorrelationIDAsBytes() {
+ return correlationIdBytes;
+ }
+
+ @Override
+ public JMSProducer setJMSCorrelationIDAsBytes(byte[] correlationIdBytes) {
+ this.correlationIdBytes = correlationIdBytes;
+ return this;
+ }
+
+ @Override
+ public Destination getJMSReplyTo() {
+ return replyTo;
+ }
+
+ @Override
+ public JMSProducer setJMSReplyTo(Destination replyTo) {
+ try {
+ JmsMessageTransformation.transformDestination(session.getConnection(), replyTo);
+ } catch (JMSException e) {
+ throw JmsExceptionSupport.createRuntimeException(e);
+ }
+
+ return this;
+ }
+
+ @Override
+ public String getJMSType() {
+ return type;
+ }
+
+ @Override
+ public JMSProducer setJMSType(String type) {
+ this.type = type;
+ return this;
+ }
+
+ //----- Producer Send Configuration --------------------------------------//
+
+ @Override
+ public CompletionListener getAsync() {
+ return completionListener;
+ }
+
+ @Override
+ public JMSProducer setAsync(CompletionListener completionListener) {
+ this.completionListener = completionListener;
+ return this;
+ }
+
+ @Override
+ public long getDeliveryDelay() {
+ return deliveryDelay;
+ }
+
+ @Override
+ public JMSProducer setDeliveryDelay(long deliveryDelay) {
+ this.deliveryDelay = deliveryDelay;
+ return this;
+ }
+
+ @Override
+ public int getDeliveryMode() {
+ return deliveryMode;
+ }
+
+ @Override
+ public JMSProducer setDeliveryMode(int deliveryMode) {
+ this.deliveryMode = deliveryMode;
+ return this;
+ }
+
+ @Override
+ public boolean getDisableMessageID() {
+ return disableMessageId;
+ }
+
+ @Override
+ public JMSProducer setDisableMessageID(boolean disableMessageId) {
+ this.disableMessageId = disableMessageId;
+ return this;
+ }
+
+ @Override
+ public boolean getDisableMessageTimestamp() {
+ return disableTimestamp;
+ }
+
+ @Override
+ public JMSProducer setDisableMessageTimestamp(boolean disableTimestamp) {
+ this.disableTimestamp = disableTimestamp;
+ return this;
+ }
+
+ @Override
+ public int getPriority() {
+ return priority;
+ }
+
+ @Override
+ public JMSProducer setPriority(int priority) {
+ this.priority = priority;
+ return this;
+ }
+
+ @Override
+ public long getTimeToLive() {
+ return timeToLive;
+ }
+
+ @Override
+ public JMSProducer setTimeToLive(long timeToLive) {
+ this.timeToLive = timeToLive;
+ return this;
+ }
+
+ //----- Internal support methods -----------------------------------------//
+
+ private JMSProducer setObjectProperty(String name, Object value) {
+ try {
+ checkPropertyNameIsValid(name, session.getConnection().isValidatePropertyNames());
+ checkValidObject(value);
+ messageProperties.put(name, value);
+ return this;
+ } catch (JMSException e) {
+ throw JmsExceptionSupport.createRuntimeException(e);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/0c39522c/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsSession.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsSession.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsSession.java
index 817f342..e740ba7 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsSession.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsSession.java
@@ -34,6 +34,7 @@ import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.ReentrantLock;
import javax.jms.BytesMessage;
+import javax.jms.CompletionListener;
import javax.jms.DeliveryMode;
import javax.jms.Destination;
import javax.jms.IllegalStateException;
@@ -471,6 +472,22 @@ public class JmsSession implements AutoCloseable, Session, QueueSession, TopicSe
return result;
}
+ /**
+ * @see javax.jms.Session#createDurableConsumer(javax.jms.Topic, java.lang.String)
+ */
+ @Override
+ public MessageConsumer createDurableConsumer(Topic topic, String name) throws JMSException {
+ return createDurableSubscriber(topic, name, null, false);
+ }
+
+ /**
+ * @see javax.jms.Session#createDurableConsumer(javax.jms.Topic, java.lang.String, java.lang.String, boolean)
+ */
+ @Override
+ public MessageConsumer createDurableConsumer(Topic topic, String name, String messageSelector, boolean noLocal) throws JMSException {
+ return createDurableSubscriber(topic, name, messageSelector, noLocal);
+ }
+
protected void checkClientIDWasSetExplicitly() throws IllegalStateException {
if (!connection.isExplicitClientID()) {
throw new IllegalStateException("You must specify a unique clientID for the Connection to use a DurableSubscriber");
@@ -486,6 +503,46 @@ public class JmsSession implements AutoCloseable, Session, QueueSession, TopicSe
connection.unsubscribe(name);
}
+ /**
+ * @see javax.jms.Session#createSharedConsumer(javax.jms.Topic, java.lang.String)
+ */
+ @Override
+ public MessageConsumer createSharedConsumer(Topic topic, String name) throws JMSException {
+ checkClosed();
+ // TODO Auto-generated method stub
+ throw new JMSException("Not yet implemented");
+ }
+
+ /**
+ * @see javax.jms.Session#createSharedConsumer(javax.jms.Topic, java.lang.String, java.lang.String)
+ */
+ @Override
+ public MessageConsumer createSharedConsumer(Topic topic, String name, String selector) throws JMSException {
+ checkClosed();
+ // TODO Auto-generated method stub
+ throw new JMSException("Not yet implemented");
+ }
+
+ /**
+ * @see javax.jms.Session#createSharedDurableConsumer(javax.jms.Topic, java.lang.String)
+ */
+ @Override
+ public MessageConsumer createSharedDurableConsumer(Topic topic, String name) throws JMSException {
+ checkClosed();
+ // TODO Auto-generated method stub
+ throw new JMSException("Not yet implemented");
+ }
+
+ /**
+ * @see javax.jms.Session#createSharedDurableConsumer(javax.jms.Topic, java.lang.String, java.lang.String)
+ */
+ @Override
+ public MessageConsumer createSharedDurableConsumer(Topic topic, String name, String selector) throws JMSException {
+ checkClosed();
+ // TODO Auto-generated method stub
+ throw new JMSException("Not yet implemented");
+ }
+
//////////////////////////////////////////////////////////////////////////
// Producer creation
//////////////////////////////////////////////////////////////////////////
@@ -653,17 +710,17 @@ public class JmsSession implements AutoCloseable, Session, QueueSession, TopicSe
connection.onException(ex);
}
- protected void send(JmsMessageProducer producer, Destination dest, Message msg, int deliveryMode, int priority, long timeToLive, boolean disableMsgId, boolean disableTimestamp, JmsCompletionListener listener) throws JMSException {
+ protected void send(JmsMessageProducer producer, Destination dest, Message msg, int deliveryMode, int priority, long timeToLive, boolean disableMsgId, boolean disableTimestamp, long deliveryDelay, CompletionListener listener) throws JMSException {
JmsDestination destination = JmsMessageTransformation.transformDestination(connection, dest);
if (destination.isTemporary() && ((JmsTemporaryDestination) destination).isDeleted()) {
throw new IllegalStateException("Temporary destination has been deleted");
}
- send(producer, destination, msg, deliveryMode, priority, timeToLive, disableMsgId, disableTimestamp, listener);
+ send(producer, destination, msg, deliveryMode, priority, timeToLive, disableMsgId, disableTimestamp, deliveryDelay, listener);
}
- private void send(JmsMessageProducer producer, JmsDestination destination, Message original, int deliveryMode, int priority, long timeToLive, boolean disableMsgId, boolean disableTimestamp, JmsCompletionListener listener) throws JMSException {
+ private void send(JmsMessageProducer producer, JmsDestination destination, Message original, int deliveryMode, int priority, long timeToLive, boolean disableMsgId, boolean disableTimestamp, long deliveryDelay, CompletionListener listener) throws JMSException {
sendLock.lock();
try {
original.setJMSDeliveryMode(deliveryMode);
@@ -672,7 +729,8 @@ public class JmsSession implements AutoCloseable, Session, QueueSession, TopicSe
original.setJMSDestination(destination);
long timeStamp = System.currentTimeMillis();
- boolean hasTTL = timeToLive > 0;
+ boolean hasTTL = timeToLive > Message.DEFAULT_TIME_TO_LIVE;
+ boolean hasDelay = deliveryDelay > Message.DEFAULT_DELIVERY_DELAY;
if (!disableTimestamp) {
original.setJMSTimestamp(timeStamp);
@@ -686,6 +744,12 @@ public class JmsSession implements AutoCloseable, Session, QueueSession, TopicSe
original.setJMSExpiration(0);
}
+ if (hasDelay) {
+ original.setJMSDeliveryTime(timeStamp + timeToLive);
+ } else {
+ original.setJMSDeliveryTime(0);
+ }
+
boolean isJmsMessage = original instanceof JmsMessage;
long messageSequence = producer.getNextMessageSequence();
@@ -966,6 +1030,10 @@ public class JmsSession implements AutoCloseable, Session, QueueSession, TopicSe
return sessionInfo.getId();
}
+ protected int getSessionMode() {
+ return acknowledgementMode;
+ }
+
protected JmsConsumerId getNextConsumerId() {
return new JmsConsumerId(sessionInfo.getId(), consumerIdGenerator.incrementAndGet());
}
@@ -1272,12 +1340,12 @@ public class JmsSession implements AutoCloseable, Session, QueueSession, TopicSe
private final class SendCompletion {
private final JmsOutboundMessageDispatch envelope;
- private final JmsCompletionListener listener;
+ private final CompletionListener listener;
private Exception failureCause;
private boolean completed;
- public SendCompletion(JmsOutboundMessageDispatch envelope, JmsCompletionListener listener) {
+ public SendCompletion(JmsOutboundMessageDispatch envelope, CompletionListener listener) {
this.envelope = envelope;
this.listener = listener;
}
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/0c39522c/qpid-jms-client/src/main/java/org/apache/qpid/jms/exceptions/JmsExceptionSupport.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/exceptions/JmsExceptionSupport.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/exceptions/JmsExceptionSupport.java
index 48493bd..5f255f2 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/exceptions/JmsExceptionSupport.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/exceptions/JmsExceptionSupport.java
@@ -16,9 +16,29 @@
*/
package org.apache.qpid.jms.exceptions;
+import javax.jms.IllegalStateException;
+import javax.jms.IllegalStateRuntimeException;
+import javax.jms.InvalidClientIDException;
+import javax.jms.InvalidClientIDRuntimeException;
+import javax.jms.InvalidDestinationException;
+import javax.jms.InvalidDestinationRuntimeException;
+import javax.jms.InvalidSelectorException;
+import javax.jms.InvalidSelectorRuntimeException;
import javax.jms.JMSException;
+import javax.jms.JMSRuntimeException;
+import javax.jms.JMSSecurityException;
+import javax.jms.JMSSecurityRuntimeException;
import javax.jms.MessageEOFException;
import javax.jms.MessageFormatException;
+import javax.jms.MessageFormatRuntimeException;
+import javax.jms.MessageNotWriteableException;
+import javax.jms.MessageNotWriteableRuntimeException;
+import javax.jms.ResourceAllocationException;
+import javax.jms.ResourceAllocationRuntimeException;
+import javax.jms.TransactionInProgressException;
+import javax.jms.TransactionInProgressRuntimeException;
+import javax.jms.TransactionRolledBackException;
+import javax.jms.TransactionRolledBackRuntimeException;
/**
* Exception support class.
@@ -143,4 +163,50 @@ public final class JmsExceptionSupport {
exception.initCause(cause);
return exception;
}
+
+ /**
+ * Creates the proper instance of a JMSRuntimeException based on the type
+ * of JMSException that is passed.
+ *
+ * @param exception
+ * The JMSException instance to convert to a JMSRuntimeException
+ *
+ * @return a new {@link JMSRuntimeException} instance that reflects the original error.
+ */
+ public static JMSRuntimeException createRuntimeException(Exception exception) {
+ JMSRuntimeException result = null;
+ JMSException source = null;
+
+ if (!(exception instanceof JMSException)) {
+ throw new JMSRuntimeException(exception.getMessage(), null, exception);
+ } else {
+ source = (JMSException) exception;
+ }
+
+ if (source instanceof IllegalStateException) {
+ result = new IllegalStateRuntimeException(source.getMessage(), source.getErrorCode(), source);
+ } else if (source instanceof InvalidClientIDException) {
+ result = new InvalidClientIDRuntimeException(source.getMessage(), source.getErrorCode(), source);
+ } else if (source instanceof InvalidDestinationException) {
+ result = new InvalidDestinationRuntimeException(source.getMessage(), source.getErrorCode(), source);
+ } else if (source instanceof InvalidSelectorException) {
+ result = new InvalidSelectorRuntimeException(source.getMessage(), source.getErrorCode(), source);
+ } else if (source instanceof JMSSecurityException) {
+ result = new JMSSecurityRuntimeException(source.getMessage(), source.getErrorCode(), source);
+ } else if (source instanceof MessageFormatException) {
+ result = new MessageFormatRuntimeException(source.getMessage(), source.getErrorCode(), source);
+ } else if (source instanceof MessageNotWriteableException) {
+ result = new MessageNotWriteableRuntimeException(source.getMessage(), source.getErrorCode(), source);
+ } else if (source instanceof ResourceAllocationException) {
+ result = new ResourceAllocationRuntimeException(source.getMessage(), source.getErrorCode(), source);
+ } else if (source instanceof TransactionInProgressException) {
+ result = new TransactionInProgressRuntimeException(source.getMessage(), source.getErrorCode(), source);
+ } else if (source instanceof TransactionRolledBackException) {
+ result = new TransactionRolledBackRuntimeException(source.getMessage(), source.getErrorCode(), source);
+ } else {
+ result = new JMSRuntimeException(source.getMessage(), source.getErrorCode(), source);
+ }
+
+ return result;
+ }
}
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/0c39522c/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/JmsBytesMessage.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/JmsBytesMessage.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/JmsBytesMessage.java
index 95e51e0..75bc30c 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/JmsBytesMessage.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/JmsBytesMessage.java
@@ -28,6 +28,7 @@ import javax.jms.MessageFormatException;
import org.apache.qpid.jms.exceptions.JmsExceptionSupport;
import org.apache.qpid.jms.message.facade.JmsBytesMessageFacade;
+@SuppressWarnings("unchecked")
public class JmsBytesMessage extends JmsMessage implements BytesMessage {
protected transient DataOutputStream dataOut;
@@ -396,6 +397,20 @@ public class JmsBytesMessage extends JmsMessage implements BytesMessage {
return "JmsBytesMessage { " + facade + " }";
}
+ @Override
+ public boolean isBodyAssignableTo(@SuppressWarnings("rawtypes") Class target) throws JMSException {
+ return facade.hasBody() ? target.isAssignableFrom(byte[].class) : true;
+ }
+
+ @Override
+ protected <T> T doGetBody(Class<T> asType) throws JMSException {
+ if (!facade.hasBody()) {
+ return null;
+ }
+
+ return (T) facade.copyBody();
+ }
+
private void initializeWriting() throws JMSException {
checkReadOnlyBody();
if (this.dataOut == null) {
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/0c39522c/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/JmsMapMessage.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/JmsMapMessage.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/JmsMapMessage.java
index add21f1..4ec02fe 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/JmsMapMessage.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/JmsMapMessage.java
@@ -17,6 +17,8 @@
package org.apache.qpid.jms.message;
import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
import javax.jms.JMSException;
import javax.jms.MapMessage;
@@ -27,6 +29,7 @@ import org.apache.qpid.jms.message.facade.JmsMapMessageFacade;
/**
* Implementation of the JMS MapMessage.
*/
+@SuppressWarnings("unchecked")
public class JmsMapMessage extends JmsMessage implements MapMessage {
JmsMapMessageFacade facade;
@@ -299,6 +302,27 @@ public class JmsMapMessage extends JmsMessage implements MapMessage {
return "JmsMapMessage { " + facade + " }";
}
+ @Override
+ public boolean isBodyAssignableTo(@SuppressWarnings("rawtypes") Class target) throws JMSException {
+ return facade.hasBody() ? target.isAssignableFrom(Map.class) : true;
+ }
+
+ @Override
+ protected <T> T doGetBody(Class<T> asType) throws JMSException {
+ if (!facade.hasBody()) {
+ return null;
+ }
+
+ Map<String, Object> copy = new HashMap<String, Object>();
+ Enumeration<String> keys = facade.getMapNames();
+ while (keys.hasMoreElements()) {
+ String key = keys.nextElement();
+ copy.put(key, getObject(key));
+ }
+
+ return (T) copy;
+ }
+
private void put(String name, Object value) throws JMSException {
checkReadOnlyBody();
checkKeyNameIsValid(name);
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/0c39522c/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/JmsMessage.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/JmsMessage.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/JmsMessage.java
index 68db061..05143d8 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/JmsMessage.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/JmsMessage.java
@@ -16,6 +16,8 @@
*/
package org.apache.qpid.jms.message;
+import static org.apache.qpid.jms.message.JmsMessagePropertySupport.convertPropertyTo;
+
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
@@ -32,7 +34,6 @@ import org.apache.qpid.jms.JmsAcknowledgeCallback;
import org.apache.qpid.jms.JmsConnection;
import org.apache.qpid.jms.exceptions.JmsExceptionSupport;
import org.apache.qpid.jms.message.facade.JmsMessageFacade;
-import org.apache.qpid.jms.util.TypeConversionSupport;
public class JmsMessage implements javax.jms.Message {
@@ -103,6 +104,24 @@ public class JmsMessage implements javax.jms.Message {
}
@Override
+ public boolean isBodyAssignableTo(@SuppressWarnings("rawtypes") Class target) throws JMSException {
+ return true;
+ }
+
+ @Override
+ public final <T> T getBody(Class<T> asType) throws JMSException {
+ if (isBodyAssignableTo(asType)) {
+ return doGetBody(asType);
+ }
+
+ throw new MessageFormatException("Message body cannot be read as type: " + asType);
+ }
+
+ protected <T> T doGetBody(Class<T> asType) throws JMSException {
+ return null;
+ }
+
+ @Override
public void clearBody() throws JMSException {
readOnlyBody = false;
facade.clearBody();
@@ -248,6 +267,16 @@ public class JmsMessage implements javax.jms.Message {
}
@Override
+ public long getJMSDeliveryTime() throws JMSException {
+ return facade.getDeliveryTime();
+ }
+
+ @Override
+ public void setJMSDeliveryTime(long deliveryTime) throws JMSException {
+ facade.setDeliveryTime(deliveryTime);
+ }
+
+ @Override
public void clearProperties() throws JMSException {
JmsMessagePropertyIntercepter.clearProperties(this, true);
}
@@ -288,106 +317,42 @@ public class JmsMessage implements javax.jms.Message {
@Override
public boolean getBooleanProperty(String name) throws JMSException {
- Object value = getObjectProperty(name);
- if (value == null) {
- return false;
- }
- Boolean rc = (Boolean) TypeConversionSupport.convert(value, Boolean.class);
- if (rc == null) {
- throw new MessageFormatException("Property " + name + " was a " + value.getClass().getName() + " and cannot be read as a boolean");
- }
- return rc.booleanValue();
+ return convertPropertyTo(name, getObjectProperty(name), Boolean.class);
}
@Override
public byte getByteProperty(String name) throws JMSException {
- Object value = getObjectProperty(name);
- if (value == null) {
- throw new NumberFormatException("property " + name + " was null");
- }
- Byte rc = (Byte) TypeConversionSupport.convert(value, Byte.class);
- if (rc == null) {
- throw new MessageFormatException("Property " + name + " was a " + value.getClass().getName() + " and cannot be read as a byte");
- }
- return rc.byteValue();
+ return convertPropertyTo(name, getObjectProperty(name), Byte.class);
}
@Override
public short getShortProperty(String name) throws JMSException {
- Object value = getObjectProperty(name);
- if (value == null) {
- throw new NumberFormatException("property " + name + " was null");
- }
- Short rc = (Short) TypeConversionSupport.convert(value, Short.class);
- if (rc == null) {
- throw new MessageFormatException("Property " + name + " was a " + value.getClass().getName() + " and cannot be read as a short");
- }
- return rc.shortValue();
+ return convertPropertyTo(name, getObjectProperty(name), Short.class);
}
@Override
public int getIntProperty(String name) throws JMSException {
- Object value = getObjectProperty(name);
- if (value == null) {
- throw new NumberFormatException("property " + name + " was null");
- }
- Integer rc = (Integer) TypeConversionSupport.convert(value, Integer.class);
- if (rc == null) {
- throw new MessageFormatException("Property " + name + " was a " + value.getClass().getName() + " and cannot be read as an integer");
- }
- return rc.intValue();
+ return convertPropertyTo(name, getObjectProperty(name), Integer.class);
}
@Override
public long getLongProperty(String name) throws JMSException {
- Object value = getObjectProperty(name);
- if (value == null) {
- throw new NumberFormatException("property " + name + " was null");
- }
- Long rc = (Long) TypeConversionSupport.convert(value, Long.class);
- if (rc == null) {
- throw new MessageFormatException("Property " + name + " was a " + value.getClass().getName() + " and cannot be read as a long");
- }
- return rc.longValue();
+ return convertPropertyTo(name, getObjectProperty(name), Long.class);
}
@Override
public float getFloatProperty(String name) throws JMSException {
- Object value = getObjectProperty(name);
- if (value == null) {
- throw new NullPointerException("property " + name + " was null");
- }
- Float rc = (Float) TypeConversionSupport.convert(value, Float.class);
- if (rc == null) {
- throw new MessageFormatException("Property " + name + " was a " + value.getClass().getName() + " and cannot be read as a float");
- }
- return rc.floatValue();
+ return convertPropertyTo(name, getObjectProperty(name), Float.class);
}
@Override
public double getDoubleProperty(String name) throws JMSException {
- Object value = getObjectProperty(name);
- if (value == null) {
- throw new NullPointerException("property " + name + " was null");
- }
- Double rc = (Double) TypeConversionSupport.convert(value, Double.class);
- if (rc == null) {
- throw new MessageFormatException("Property " + name + " was a " + value.getClass().getName() + " and cannot be read as a double");
- }
- return rc.doubleValue();
+ return convertPropertyTo(name, getObjectProperty(name), Double.class);
}
@Override
public String getStringProperty(String name) throws JMSException {
- Object value = getObjectProperty(name);
- if (value == null) {
- return null;
- }
- String rc = (String) TypeConversionSupport.convert(value, String.class);
- if (rc == null) {
- throw new MessageFormatException("Property " + name + " was a " + value.getClass().getName() + " and cannot be read as a String");
- }
- return rc;
+ return convertPropertyTo(name, getObjectProperty(name), String.class);
}
@Override
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org
[7/7] qpid-jms git commit: QPIDJMS-207 Adds support for the JMS 2.0
Delayed Delivery feature
Posted by ta...@apache.org.
QPIDJMS-207 Adds support for the JMS 2.0 Delayed Delivery feature
Project: http://git-wip-us.apache.org/repos/asf/qpid-jms/repo
Commit: http://git-wip-us.apache.org/repos/asf/qpid-jms/commit/6e442f4c
Tree: http://git-wip-us.apache.org/repos/asf/qpid-jms/tree/6e442f4c
Diff: http://git-wip-us.apache.org/repos/asf/qpid-jms/diff/6e442f4c
Branch: refs/heads/master
Commit: 6e442f4c6aa1401a14031c6f2f05d7edbd58037c
Parents: 0c39522
Author: Timothy Bish <ta...@gmail.com>
Authored: Mon Sep 12 15:20:25 2016 -0400
Committer: Timothy Bish <ta...@gmail.com>
Committed: Mon Sep 12 15:20:25 2016 -0400
----------------------------------------------------------------------
.../message/JmsMessagePropertyIntercepter.java | 32 +++++
.../qpid/jms/message/JmsMessageSupport.java | 1 +
.../provider/amqp/AmqpConnectionProperties.java | 23 ++++
.../jms/provider/amqp/AmqpFixedProducer.java | 13 +-
.../qpid/jms/provider/amqp/AmqpSupport.java | 1 +
.../amqp/message/AmqpJmsMessageFacade.java | 16 ++-
.../amqp/message/AmqpMessageSupport.java | 5 +
.../integration/ProducerIntegrationTest.java | 82 +++++++++++++
.../JmsMessagePropertyIntercepterTest.java | 110 +++++++++++++++++
.../amqp/message/AmqpJmsMessageFacadeTest.java | 10 ++
.../transports/netty/NettySimpleAmqpServer.java | 123 ++++++++++++++++++-
11 files changed, 407 insertions(+), 9 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/6e442f4c/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/JmsMessagePropertyIntercepter.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/JmsMessagePropertyIntercepter.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/JmsMessagePropertyIntercepter.java
index bb2eb0b..65c8c9a 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/JmsMessagePropertyIntercepter.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/JmsMessagePropertyIntercepter.java
@@ -24,6 +24,7 @@ import static org.apache.qpid.jms.message.JmsMessageSupport.JMSX_GROUPSEQ;
import static org.apache.qpid.jms.message.JmsMessageSupport.JMSX_USERID;
import static org.apache.qpid.jms.message.JmsMessageSupport.JMS_AMQP_ACK_TYPE;
import static org.apache.qpid.jms.message.JmsMessageSupport.JMS_CORRELATIONID;
+import static org.apache.qpid.jms.message.JmsMessageSupport.JMS_DELIVERYTIME;
import static org.apache.qpid.jms.message.JmsMessageSupport.JMS_DELIVERY_MODE;
import static org.apache.qpid.jms.message.JmsMessageSupport.JMS_DESTINATION;
import static org.apache.qpid.jms.message.JmsMessageSupport.JMS_EXPIRATION;
@@ -135,6 +136,7 @@ public class JmsMessagePropertyIntercepter {
STANDARD_HEADERS.add(JMS_TYPE);
STANDARD_HEADERS.add(JMS_EXPIRATION);
STANDARD_HEADERS.add(JMS_PRIORITY);
+ STANDARD_HEADERS.add(JMS_DELIVERYTIME);
VENDOR_PROPERTIES.add(JMS_AMQP_ACK_TYPE);
@@ -638,6 +640,36 @@ public class JmsMessagePropertyIntercepter {
return true;
}
});
+ PROPERTY_INTERCEPTERS.put(JMS_DELIVERYTIME, new PropertyIntercepter() {
+ @Override
+ public Object getProperty(JmsMessage message) throws JMSException {
+ return Long.valueOf(message.getFacade().getDeliveryTime());
+ }
+
+ @Override
+ public void setProperty(JmsMessage message, Object value) throws JMSException {
+ Long rc = (Long) TypeConversionSupport.convert(value, Long.class);
+ if (rc == null) {
+ throw new JMSException("Property JMSDeliveryTime cannot be set from a " + value.getClass().getName() + ".");
+ }
+ message.getFacade().setDeliveryTime(rc.longValue());
+ }
+
+ @Override
+ public boolean propertyExists(JmsMessage message) {
+ return message.getFacade().getDeliveryTime() > 0;
+ }
+
+ @Override
+ public void clearProperty(JmsMessage message) {
+ message.getFacade().setDeliveryTime(0);
+ }
+
+ @Override
+ public boolean isAlwaysWritable() {
+ return false;
+ }
+ });
}
/**
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/6e442f4c/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/JmsMessageSupport.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/JmsMessageSupport.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/JmsMessageSupport.java
index c3ce451..657542c 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/JmsMessageSupport.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/JmsMessageSupport.java
@@ -31,6 +31,7 @@ public class JmsMessageSupport {
public static final String JMS_CORRELATIONID = "JMSCorrelationID";
public static final String JMS_EXPIRATION = "JMSExpiration";
public static final String JMS_REDELIVERED = "JMSRedelivered";
+ public static final String JMS_DELIVERYTIME = "JMSDeliveryTime";
public static final String JMSX_GROUPID = "JMSXGroupID";
public static final String JMSX_GROUPSEQ = "JMSXGroupSeq";
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/6e442f4c/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpConnectionProperties.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpConnectionProperties.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpConnectionProperties.java
index 79a0d95..c090853 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpConnectionProperties.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpConnectionProperties.java
@@ -18,6 +18,7 @@ package org.apache.qpid.jms.provider.amqp;
import static org.apache.qpid.jms.provider.amqp.AmqpSupport.ANONYMOUS_RELAY;
import static org.apache.qpid.jms.provider.amqp.AmqpSupport.CONNECTION_OPEN_FAILED;
+import static org.apache.qpid.jms.provider.amqp.AmqpSupport.DELAYED_DELIVERY;
import static org.apache.qpid.jms.provider.amqp.AmqpSupport.QUEUE_PREFIX;
import static org.apache.qpid.jms.provider.amqp.AmqpSupport.TOPIC_PREFIX;
@@ -41,6 +42,7 @@ public class AmqpConnectionProperties {
private final JmsConnectionInfo connectionInfo;
+ private boolean delayedDeliverySupported = false;
private boolean anonymousRelaySupported = false;
private boolean connectionOpenFailed = false;
@@ -78,6 +80,10 @@ public class AmqpConnectionProperties {
if (list.contains(ANONYMOUS_RELAY)) {
anonymousRelaySupported = true;
}
+
+ if (list.contains(DELAYED_DELIVERY)) {
+ delayedDeliverySupported = true;
+ }
}
protected void processProperties(Map<Symbol, Object> properties) {
@@ -104,6 +110,23 @@ public class AmqpConnectionProperties {
}
/**
+ * @return true if the connection supports sending message with delivery delays.
+ */
+ public boolean isDelayedDeliverySupported() {
+ return delayedDeliverySupported;
+ }
+
+ /**
+ * Sets if the connection supports sending message with assigned delivery delays.
+ *
+ * @param deliveryDelaySupported
+ * true if the delivery delay features is supported.
+ */
+ public void setDeliveryDelaySupported(boolean deliveryDelaySupported) {
+ this.delayedDeliverySupported = deliveryDelaySupported;
+ }
+
+ /**
* @return true if the connection supports sending to an anonymous relay.
*/
public boolean isAnonymousRelaySupported() {
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/6e442f4c/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpFixedProducer.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpFixedProducer.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpFixedProducer.java
index 9233ce1..df39b78 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpFixedProducer.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpFixedProducer.java
@@ -68,12 +68,16 @@ public class AmqpFixedProducer extends AmqpProducer {
private AsyncResult sendCompletionWatcher;
+ private final AmqpConnection connection;
+
public AmqpFixedProducer(AmqpSession session, JmsProducerInfo info) {
- super(session, info);
+ this(session, info, null);
}
public AmqpFixedProducer(AmqpSession session, JmsProducerInfo info, Sender sender) {
super(session, info, sender);
+
+ connection = session.getConnection();
}
@Override
@@ -93,7 +97,12 @@ public class AmqpFixedProducer extends AmqpProducer {
request.onFailure(new IllegalStateException("The MessageProducer is closed"));
}
- if (getEndpoint().getCredit() <= 0) {
+ if (!connection.getProperties().isDelayedDeliverySupported() &&
+ envelope.getMessage().getJMSDeliveryTime() != 0) {
+
+ // Don't allow sends with delay if the remote said it can't handle them
+ request.onFailure(new JMSException("Remote does not support delayed message delivery"));
+ } else if (getEndpoint().getCredit() <= 0) {
LOG.trace("Holding Message send until credit is available.");
InFlightSend send = new InFlightSend(envelope, request);
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/6e442f4c/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpSupport.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpSupport.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpSupport.java
index 10ae94f..9738d68 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpSupport.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpSupport.java
@@ -43,6 +43,7 @@ public class AmqpSupport {
// Symbols used for connection capabilities
public static final Symbol SOLE_CONNECTION_CAPABILITY = Symbol.valueOf("sole-connection-for-container");
public static final Symbol ANONYMOUS_RELAY = Symbol.valueOf("ANONYMOUS-RELAY");
+ public static final Symbol DELAYED_DELIVERY = Symbol.valueOf("DELAYED_DELIVERY");
// Symbols used to announce connection error information
public static final Symbol CONNECTION_OPEN_FAILED = Symbol.valueOf("amqp:connection-establishment-failed");
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/6e442f4c/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/message/AmqpJmsMessageFacade.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/message/AmqpJmsMessageFacade.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/message/AmqpJmsMessageFacade.java
index 82f63e0..89094a1 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/message/AmqpJmsMessageFacade.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/message/AmqpJmsMessageFacade.java
@@ -17,6 +17,7 @@
package org.apache.qpid.jms.provider.amqp.message;
import static org.apache.qpid.jms.provider.amqp.message.AmqpMessageSupport.JMS_AMQP_TTL;
+import static org.apache.qpid.jms.provider.amqp.message.AmqpMessageSupport.JMS_DELIVERY_TIME;
import static org.apache.qpid.jms.provider.amqp.message.AmqpMessageSupport.JMS_MESSAGE;
import static org.apache.qpid.jms.provider.amqp.message.AmqpMessageSupport.JMS_MSG_TYPE;
@@ -555,14 +556,21 @@ public class AmqpJmsMessageFacade implements JmsMessageFacade {
@Override
public long getDeliveryTime() {
- // TODO Auto-generated method stub
- return 0;
+ Object deliveryTime = getMessageAnnotation(JMS_DELIVERY_TIME);
+ if (deliveryTime != null) {
+ return (long) deliveryTime;
+ }
+
+ return 0l;
}
@Override
public void setDeliveryTime(long deliveryTime) {
- // TODO Auto-generated method stub
-
+ if (deliveryTime != 0) {
+ setMessageAnnotation(JMS_DELIVERY_TIME, deliveryTime);
+ } else {
+ removeMessageAnnotation(JMS_DELIVERY_TIME);
+ }
}
/**
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/6e442f4c/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/message/AmqpMessageSupport.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/message/AmqpMessageSupport.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/message/AmqpMessageSupport.java
index 271481b..40987e1 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/message/AmqpMessageSupport.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/message/AmqpMessageSupport.java
@@ -37,6 +37,11 @@ public final class AmqpMessageSupport {
public static final String JMS_MSG_TYPE = "x-opt-jms-msg-type";
/**
+ * Attribute used to mark the Application defined delivery time assigned to the message
+ */
+ public static final String JMS_DELIVERY_TIME = "x-opt-delivery-time";
+
+ /**
* Value mapping for JMS_MSG_TYPE which indicates the message is a generic JMS Message
* which has no body.
*/
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/6e442f4c/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/ProducerIntegrationTest.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/ProducerIntegrationTest.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/ProducerIntegrationTest.java
index 0dee795..0e6b445 100644
--- a/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/ProducerIntegrationTest.java
+++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/ProducerIntegrationTest.java
@@ -18,11 +18,13 @@
*/
package org.apache.qpid.jms.integration;
+import static org.apache.qpid.jms.provider.amqp.AmqpSupport.DELAYED_DELIVERY;
import static org.hamcrest.Matchers.both;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
import static org.hamcrest.Matchers.isA;
import static org.hamcrest.Matchers.lessThanOrEqualTo;
+import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.nullValue;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@@ -86,6 +88,7 @@ import org.apache.qpid.jms.test.testpeer.matchers.sections.MessagePropertiesSect
import org.apache.qpid.jms.test.testpeer.matchers.sections.TransferPayloadCompositeMatcher;
import org.apache.qpid.jms.test.testpeer.matchers.types.EncodedAmqpValueMatcher;
import org.apache.qpid.proton.amqp.Binary;
+import org.apache.qpid.proton.amqp.Symbol;
import org.apache.qpid.proton.amqp.UnsignedByte;
import org.apache.qpid.proton.amqp.UnsignedInteger;
import org.hamcrest.Matcher;
@@ -1834,6 +1837,85 @@ public class ProducerIntegrationTest extends QpidJmsTestCase {
}
@Test(timeout = 20000)
+ public void testSendFailsWhenDelayedDeliveryIsNotSupported() throws Exception {
+ try (TestAmqpPeer testPeer = new TestAmqpPeer();) {
+
+ // DO NOT add capability to indicate server support for DELAYED-DELIVERY
+
+ Connection connection = testFixture.establishConnecton(testPeer);
+
+ connection.start();
+
+ testPeer.expectBegin();
+ testPeer.expectSenderAttach();
+
+ Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ String topicName = "myTopic";
+ Topic dest = session.createTopic(topicName);
+
+ MessageProducer producer = session.createProducer(dest);
+ producer.setDeliveryDelay(5000);
+
+ // Producer should fail to send when message has delivery delay since remote
+ // did not report that it supports that option.
+ Message message = session.createMessage();
+ try {
+ producer.send(message);
+ fail("Send should fail");
+ } catch (JMSException jmsEx) {
+ LOG.debug("Caught expected error from failed send.");
+ }
+
+ testPeer.expectClose();
+ connection.close();
+
+ testPeer.waitForAllHandlersToComplete(1000);
+ }
+ }
+
+ @Test(timeout = 20000)
+ public void testSendWorksWhenDelayedDeliveryIsSupported() throws Exception {
+ try (TestAmqpPeer testPeer = new TestAmqpPeer();) {
+
+ String topicName = "myTopic";
+
+ // DO add capability to indicate server support for DELAYED-DELIVERY
+
+ Connection connection = testFixture.establishConnecton(testPeer, new Symbol[]{ DELAYED_DELIVERY });
+
+ connection.start();
+
+ testPeer.expectBegin();
+ testPeer.expectSenderAttach();
+
+ MessageHeaderSectionMatcher headersMatcher = new MessageHeaderSectionMatcher(true).withDurable(equalTo(true));
+ MessageAnnotationsSectionMatcher msgAnnotationsMatcher = new MessageAnnotationsSectionMatcher(true);
+ Symbol annotationKey = AmqpMessageSupport.getSymbol(AmqpMessageSupport.JMS_DELIVERY_TIME);
+ msgAnnotationsMatcher.withEntry(annotationKey, notNullValue());
+
+ TransferPayloadCompositeMatcher messageMatcher = new TransferPayloadCompositeMatcher();
+ messageMatcher.setHeadersMatcher(headersMatcher);
+ messageMatcher.setMessageAnnotationsMatcher(msgAnnotationsMatcher);
+
+ testPeer.expectTransfer(messageMatcher);
+
+ Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ Topic dest = session.createTopic(topicName);
+
+ MessageProducer producer = session.createProducer(dest);
+ producer.setDeliveryDelay(5000);
+ producer.send(session.createMessage());
+
+ testPeer.expectClose();
+ connection.close();
+
+ testPeer.waitForAllHandlersToComplete(1000);
+ }
+ }
+
+ @Test(timeout = 20000)
public void testAsyncCompletionAfterSendMessageGetDispoation() throws Exception {
try (TestAmqpPeer testPeer = new TestAmqpPeer();) {
Connection connection = testFixture.establishConnecton(testPeer);
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/6e442f4c/qpid-jms-client/src/test/java/org/apache/qpid/jms/message/JmsMessagePropertyIntercepterTest.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/message/JmsMessagePropertyIntercepterTest.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/message/JmsMessagePropertyIntercepterTest.java
index 75a1a78..055602d 100644
--- a/qpid-jms-client/src/test/java/org/apache/qpid/jms/message/JmsMessagePropertyIntercepterTest.java
+++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/message/JmsMessagePropertyIntercepterTest.java
@@ -22,6 +22,7 @@ import static org.apache.qpid.jms.message.JmsMessageSupport.JMSX_GROUPSEQ;
import static org.apache.qpid.jms.message.JmsMessageSupport.JMSX_USERID;
import static org.apache.qpid.jms.message.JmsMessageSupport.JMS_AMQP_ACK_TYPE;
import static org.apache.qpid.jms.message.JmsMessageSupport.JMS_CORRELATIONID;
+import static org.apache.qpid.jms.message.JmsMessageSupport.JMS_DELIVERYTIME;
import static org.apache.qpid.jms.message.JmsMessageSupport.JMS_DELIVERY_MODE;
import static org.apache.qpid.jms.message.JmsMessageSupport.JMS_DESTINATION;
import static org.apache.qpid.jms.message.JmsMessageSupport.JMS_EXPIRATION;
@@ -1778,4 +1779,113 @@ public class JmsMessagePropertyIntercepterTest {
JmsMessagePropertyIntercepter.clearProperties(message, true);
assertFalse(JmsMessagePropertyIntercepter.propertyExists(message, JMS_AMQP_ACK_TYPE));
}
+
+ //---------- JMSDeliveryTime ---------------------------------------------//
+
+ @Test
+ public void testJMSDeliveryTimeInGetAllPropertyNames() throws JMSException {
+ JmsMessageFacade facade = Mockito.mock(JmsMessageFacade.class);
+ JmsMessage message = Mockito.mock(JmsMapMessage.class);
+ Mockito.when(message.getFacade()).thenReturn(facade);
+ assertTrue(JmsMessagePropertyIntercepter.getAllPropertyNames(message).contains(JMS_DELIVERYTIME));
+ }
+
+ @Test
+ public void testGetJMSDeliveryWhenNotSet() throws JMSException {
+ JmsMessageFacade facade = Mockito.mock(JmsMessageFacade.class);
+ JmsMessage message = Mockito.mock(JmsMapMessage.class);
+ Mockito.when(message.getFacade()).thenReturn(facade);
+ Mockito.when(facade.getDeliveryTime()).thenReturn(0L);
+ assertEquals(Long.valueOf(0L), JmsMessagePropertyIntercepter.getProperty(message, JMS_DELIVERYTIME));
+ Mockito.verify(facade).getDeliveryTime();
+ }
+
+ @Test
+ public void testGetJMSDeliveryTimeWhenSet() throws JMSException {
+ JmsMessageFacade facade = Mockito.mock(JmsMessageFacade.class);
+ JmsMessage message = Mockito.mock(JmsMapMessage.class);
+ Mockito.when(message.getFacade()).thenReturn(facade);
+ Mockito.when(facade.getDeliveryTime()).thenReturn(900L);
+ assertEquals(900L, JmsMessagePropertyIntercepter.getProperty(message, JMS_DELIVERYTIME));
+ }
+
+ @Test
+ public void testSetJMSDeliveryTime() throws JMSException {
+ JmsMessageFacade facade = Mockito.mock(JmsMessageFacade.class);
+ JmsMessage message = Mockito.mock(JmsMapMessage.class);
+ Mockito.when(message.getFacade()).thenReturn(facade);
+ JmsMessagePropertyIntercepter.setProperty(message, JMS_DELIVERYTIME, 65536L);
+ Mockito.verify(facade).setDeliveryTime(65536L);
+ }
+
+ @Test
+ public void testJMSDeliveryTimeInGetPropertyNamesWhenSet() throws JMSException {
+ doJMSDeliveryTimeInGetPropertyNamesWhenSetTestImpl(false);
+ }
+
+ @Test
+ public void testJMSDeliveryTimeNotInGetPropertyNamesWhenSetAndExcludingStandardJMSHeaders() throws JMSException {
+ doJMSDeliveryTimeInGetPropertyNamesWhenSetTestImpl(true);
+ }
+
+ private void doJMSDeliveryTimeInGetPropertyNamesWhenSetTestImpl(boolean excludeStandardJmsHeaders) throws JMSException {
+ JmsMessageFacade facade = Mockito.mock(JmsMessageFacade.class);
+ JmsMessage message = Mockito.mock(JmsMapMessage.class);
+ Mockito.when(message.getFacade()).thenReturn(facade);
+ Mockito.when(facade.getDeliveryTime()).thenReturn(900L);
+ if (excludeStandardJmsHeaders) {
+ assertFalse(JmsMessagePropertyIntercepter.getPropertyNames(message, true).contains(JMS_DELIVERYTIME));
+ } else {
+ assertTrue(JmsMessagePropertyIntercepter.getPropertyNames(message, false).contains(JMS_DELIVERYTIME));
+ }
+ }
+
+ @Test
+ public void testJMSDeliveryTimeNotInGetPropertyNamesWhenNotSet() throws JMSException {
+ JmsMessageFacade facade = Mockito.mock(JmsMessageFacade.class);
+ JmsMessage message = Mockito.mock(JmsMapMessage.class);
+ Mockito.when(message.getFacade()).thenReturn(facade);
+ assertFalse(JmsMessagePropertyIntercepter.getPropertyNames(message, false).contains(JMS_DELIVERYTIME));
+ }
+
+ @Test
+ public void testJMSDeliveryTimePropertExistsWhenSet() throws JMSException {
+ JmsMessageFacade facade = Mockito.mock(JmsMessageFacade.class);
+ JmsMessage message = Mockito.mock(JmsMapMessage.class);
+ Mockito.when(message.getFacade()).thenReturn(facade);
+ Mockito.when(facade.getDeliveryTime()).thenReturn(900L);
+ assertTrue(JmsMessagePropertyIntercepter.propertyExists(message, JMS_DELIVERYTIME));
+ }
+
+ @Test
+ public void testJMSDeliveryTimePropertExistsWhenNotSet() throws JMSException {
+ JmsMessageFacade facade = Mockito.mock(JmsMessageFacade.class);
+ JmsMessage message = Mockito.mock(JmsMapMessage.class);
+ Mockito.when(message.getFacade()).thenReturn(facade);
+ Mockito.when(facade.getDeliveryTime()).thenReturn(0L);
+ assertFalse(JmsMessagePropertyIntercepter.propertyExists(message, JMS_DELIVERYTIME));
+ }
+
+ @Test
+ public void testSetJMSDeliveryTimeConversionChecks() throws JMSException {
+ JmsMessageFacade facade = Mockito.mock(JmsMessageFacade.class);
+ JmsMessage message = Mockito.mock(JmsMapMessage.class);
+ Mockito.when(message.getFacade()).thenReturn(facade);
+ try {
+ JmsMessagePropertyIntercepter.setProperty(message, JMS_DELIVERYTIME, new byte[1]);
+ fail("Should have thrown an exception for this call");
+ } catch (JMSException e) {
+ }
+ }
+
+ @Test
+ public void testJMSDeliveryTimeClearedWhenRequested() throws JMSException {
+ JmsMessageFacade facade = Mockito.mock(JmsMessageFacade.class);
+ JmsMessage message = Mockito.mock(JmsMapMessage.class);
+ Mockito.when(message.getFacade()).thenReturn(facade);
+ JmsMessagePropertyIntercepter.clearProperties(message, true);
+ Mockito.verify(facade, Mockito.never()).setDeliveryTime(0);
+ JmsMessagePropertyIntercepter.clearProperties(message, false);
+ Mockito.verify(facade).setDeliveryTime(0);
+ }
}
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/6e442f4c/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/amqp/message/AmqpJmsMessageFacadeTest.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/amqp/message/AmqpJmsMessageFacadeTest.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/amqp/message/AmqpJmsMessageFacadeTest.java
index e430f87..bd50a67 100644
--- a/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/amqp/message/AmqpJmsMessageFacadeTest.java
+++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/amqp/message/AmqpJmsMessageFacadeTest.java
@@ -1518,6 +1518,16 @@ public class AmqpJmsMessageFacadeTest extends AmqpJmsMessageTypesTestCase {
}
@Test
+ public void testNewMessageDoesNotHaveUnderlyingMessageAnnotationsSectionWithDeliveryTime() {
+ AmqpJmsMessageFacade amqpMessageFacade = createNewMessageFacade();;
+
+ Message underlying = amqpMessageFacade.getAmqpMessage();
+ assertNotNull(underlying.getMessageAnnotations());
+ Symbol annotationKey = AmqpMessageSupport.getSymbol(AmqpMessageSupport.JMS_DELIVERY_TIME);
+ assertNull(underlying.getMessageAnnotations().getValue().get(annotationKey));
+ }
+
+ @Test
public void testMessageAnnotationExistsUsingReceivedMessageWithoutMessageAnnotationsSection() {
String symbolKeyName = "myTestSymbolName";
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/6e442f4c/qpid-jms-client/src/test/java/org/apache/qpid/jms/transports/netty/NettySimpleAmqpServer.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/transports/netty/NettySimpleAmqpServer.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/transports/netty/NettySimpleAmqpServer.java
index 7c23d86..1525144 100644
--- a/qpid-jms-client/src/test/java/org/apache/qpid/jms/transports/netty/NettySimpleAmqpServer.java
+++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/transports/netty/NettySimpleAmqpServer.java
@@ -19,6 +19,7 @@ package org.apache.qpid.jms.transports.netty;
import static org.apache.qpid.jms.provider.amqp.AmqpSupport.ANONYMOUS_RELAY;
import static org.apache.qpid.jms.provider.amqp.AmqpSupport.CONNECTION_OPEN_FAILED;
import static org.apache.qpid.jms.provider.amqp.AmqpSupport.CONTAINER_ID;
+import static org.apache.qpid.jms.provider.amqp.AmqpSupport.DELAYED_DELIVERY;
import static org.apache.qpid.jms.provider.amqp.AmqpSupport.INVALID_FIELD;
import static org.apache.qpid.jms.provider.amqp.AmqpSupport.PLATFORM;
import static org.apache.qpid.jms.provider.amqp.AmqpSupport.PRODUCT;
@@ -32,8 +33,13 @@ import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.atomic.AtomicInteger;
import org.apache.qpid.jms.transports.TransportOptions;
+import org.apache.qpid.jms.util.IdGenerator;
import org.apache.qpid.proton.Proton;
import org.apache.qpid.proton.amqp.Symbol;
import org.apache.qpid.proton.amqp.transport.AmqpError;
@@ -42,7 +48,9 @@ import org.apache.qpid.proton.engine.Collector;
import org.apache.qpid.proton.engine.Connection;
import org.apache.qpid.proton.engine.EndpointState;
import org.apache.qpid.proton.engine.Event;
+import org.apache.qpid.proton.engine.Receiver;
import org.apache.qpid.proton.engine.Sasl;
+import org.apache.qpid.proton.engine.Sender;
import org.apache.qpid.proton.engine.Session;
import org.apache.qpid.proton.engine.Transport;
import org.apache.qpid.proton.engine.impl.CollectorImpl;
@@ -60,15 +68,19 @@ import io.netty.channel.SimpleChannelInboundHandler;
* Simple Netty based server that can handle a small subset of AMQP events
* using Proton-J as the protocol engine.
*/
+@SuppressWarnings( "unused" )
public class NettySimpleAmqpServer extends NettyServer {
private static final Logger LOG = LoggerFactory.getLogger(NettySimpleAmqpServer.class);
+ private static final AtomicInteger SERVER_SEQUENCE = new AtomicInteger();
+
private static final int CHANNEL_MAX = 32767;
private static final int HEADER_SIZE = 8;
private static final int SASL_PROTOCOL = 3;
private final Map<String, List<Connection>> connections = new HashMap<String, List<Connection>>();
+ private final ScheduledExecutorService serializer;
private boolean allowNonSaslConnections;
private ConnectionIntercepter connectionIntercepter;
@@ -83,6 +95,18 @@ public class NettySimpleAmqpServer extends NettyServer {
public NettySimpleAmqpServer(TransportOptions options, boolean needClientAuth, boolean webSocketServer) {
super(options, needClientAuth, webSocketServer);
+
+ this.serializer = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() {
+
+ @Override
+ public Thread newThread(Runnable runner) {
+ Thread serial = new Thread(runner);
+ serial.setDaemon(true);
+ serial.setName(NettySimpleAmqpServer.this.getClass().getSimpleName() + ":(" +
+ SERVER_SEQUENCE.incrementAndGet() + "):Worker");
+ return serial;
+ }
+ });
}
@Override
@@ -111,11 +135,15 @@ public class NettySimpleAmqpServer extends NettyServer {
private final class ProtonConnection extends SimpleChannelInboundHandler<ByteBuf> {
+ private final IdGenerator sessionIdGenerator = new IdGenerator();
+
private final Transport protonTransport = Proton.transport();
private final Connection protonConnection = Proton.connection();
private final Collector eventCollector = new CollectorImpl();
private SaslAuthenticator authenticator;
+ private final Map<String, ProtonSession> sessions = new HashMap<String, ProtonSession>();
+
private boolean exclusiveContainerId;
private boolean headerRead;
private final ByteBuf headerBuf = Unpooled.buffer(HEADER_SIZE, HEADER_SIZE);
@@ -223,6 +251,21 @@ public class NettySimpleAmqpServer extends NettyServer {
case SESSION_REMOTE_CLOSE:
processSessionClose(event.getSession());
break;
+ case LINK_REMOTE_OPEN:
+ //processLinkOpen(event.getLink());
+ break;
+ case LINK_REMOTE_DETACH:
+ //processLinkDetach(event.getLink());
+ break;
+ case LINK_REMOTE_CLOSE:
+ //processLinkClose(event.getLink());
+ break;
+ case LINK_FLOW:
+ //processLinkFlow(event.getLink());
+ break;
+ case DELIVERY:
+ //processDelivery(event.getDelivery());
+ break;
default:
break;
}
@@ -263,11 +306,17 @@ public class NettySimpleAmqpServer extends NettyServer {
}
private void processSessionClose(Session session) {
+ ProtonSession protonSession = (ProtonSession) session.getContext();
+
+ sessions.remove(protonSession.getId());
+
session.close();
session.free();
}
private void processSessionOpen(Session session) {
+ ProtonSession protonSession = new ProtonSession(sessionIdGenerator.generateId(), session);
+ sessions.put(protonSession.getId(), protonSession);
session.open();
}
@@ -387,7 +436,7 @@ public class NettySimpleAmqpServer extends NettyServer {
}
private Symbol[] getConnectionCapabilitiesOffered() {
- return new Symbol[]{ ANONYMOUS_RELAY };
+ return new Symbol[]{ ANONYMOUS_RELAY, DELAYED_DELIVERY };
}
private Map<Symbol, Object> getConnetionProperties() {
@@ -453,7 +502,74 @@ public class NettySimpleAmqpServer extends NettyServer {
}
- //----- Internal Type Implementations ------------------------------------//
+ //----- Session Manager --------------------------------------------------//
+
+ private class ProtonSession {
+
+ private final String sessionId;
+ private final Session session;
+
+ private Map<String, ProtonSender> senders = new HashMap<String, ProtonSender>();
+ private Map<String, ProtonReceiver> receivers = new HashMap<String, ProtonReceiver>();
+
+ public ProtonSession(String sessionId, Session session) {
+ this.sessionId = sessionId;
+ this.session = session;
+ this.session.setContext(this);
+ }
+
+ public Session getSession() {
+ return session;
+ }
+
+ public String getId() {
+ return sessionId;
+ }
+ }
+
+ //----- Sender Manager ---------------------------------------------------//
+
+ private class ProtonSender {
+
+ private final String senderId;
+ private final Sender sender;
+
+ public ProtonSender(String senderId, Sender sender) {
+ this.senderId = senderId;
+ this.sender = sender;
+ }
+
+ public String getId() {
+ return senderId;
+ }
+
+ public Sender getSender() {
+ return sender;
+ }
+ }
+
+ //----- Receiver Manager ---------------------------------------------------//
+
+ private class ProtonReceiver {
+
+ private final String receiverId;
+ private final Receiver receiver;
+
+ public ProtonReceiver(String receiverId, Receiver receiver) {
+ this.receiverId = receiverId;
+ this.receiver = receiver;
+ }
+
+ public String getId() {
+ return receiverId;
+ }
+
+ public Receiver getReceiver() {
+ return receiver;
+ }
+ }
+
+ //----- SASL Authentication Manager --------------------------------------//
private class SaslAuthenticator {
@@ -496,7 +612,8 @@ public class NettySimpleAmqpServer extends NettyServer {
}
}
- @SuppressWarnings("unused")
+ //----- Simple AMQP Header Wrapper ---------------------------------------//
+
private class AmqpHeader {
private final byte[] PREFIX = new byte[] { 'A', 'M', 'Q', 'P' };
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org
[3/7] qpid-jms git commit: QPIDJMS-207 Adds support for Asynchronous
JMS 2.0 sends.
Posted by ta...@apache.org.
QPIDJMS-207 Adds support for Asynchronous JMS 2.0 sends.
Project: http://git-wip-us.apache.org/repos/asf/qpid-jms/repo
Commit: http://git-wip-us.apache.org/repos/asf/qpid-jms/commit/3a03663b
Tree: http://git-wip-us.apache.org/repos/asf/qpid-jms/tree/3a03663b
Diff: http://git-wip-us.apache.org/repos/asf/qpid-jms/diff/3a03663b
Branch: refs/heads/master
Commit: 3a03663b79f98f80cd75f297cd9b70241ac68da3
Parents: 6553cfd
Author: Timothy Bish <ta...@gmail.com>
Authored: Mon Sep 12 13:09:56 2016 -0400
Committer: Timothy Bish <ta...@gmail.com>
Committed: Mon Sep 12 13:09:56 2016 -0400
----------------------------------------------------------------------
.../apache/qpid/jms/JmsCompletionListener.java | 47 ++
.../java/org/apache/qpid/jms/JmsConnection.java | 36 +-
.../org/apache/qpid/jms/JmsMessageConsumer.java | 6 +-
.../org/apache/qpid/jms/JmsMessageProducer.java | 100 +++-
.../java/org/apache/qpid/jms/JmsSession.java | 349 +++++++++--
.../jms/message/JmsInboundMessageDispatch.java | 21 +-
.../jms/message/JmsOutboundMessageDispatch.java | 42 +-
.../jms/provider/DefaultProviderListener.java | 9 +
.../qpid/jms/provider/ProviderListener.java | 22 +
.../qpid/jms/provider/ProviderWrapper.java | 10 +
.../jms/provider/amqp/AmqpAbstractResource.java | 5 +-
.../amqp/AmqpAnonymousFallbackProducer.java | 17 +-
.../qpid/jms/provider/amqp/AmqpConsumer.java | 55 +-
.../qpid/jms/provider/amqp/AmqpEventSink.java | 6 +-
.../jms/provider/amqp/AmqpExceptionBuilder.java | 34 ++
.../jms/provider/amqp/AmqpFixedProducer.java | 257 +++++---
.../qpid/jms/provider/amqp/AmqpProducer.java | 15 +-
.../qpid/jms/provider/amqp/AmqpProvider.java | 39 +-
.../provider/amqp/AmqpTransactionContext.java | 155 +++--
.../amqp/AmqpTransactionCoordinator.java | 4 +-
.../amqp/builders/AmqpResourceBuilder.java | 13 +-
.../jms/provider/failover/FailoverProvider.java | 18 +
.../integration/ConsumerIntegrationTest.java | 109 ++++
.../PresettledProducerIntegrationTest.java | 231 ++++++++
.../integration/ProducerIntegrationTest.java | 592 +++++++++++++++++++
.../jms/integration/SessionIntegrationTest.java | 114 +++-
.../jms/producer/JmsMessageProducerTest.java | 421 ++++++++++++-
.../failover/FailoverIntegrationTest.java | 194 ++++++
.../qpid/jms/provider/mock/MockProvider.java | 12 +-
.../mock/MockProviderConfiguration.java | 10 +
.../qpid/jms/provider/mock/MockRemotePeer.java | 123 ++++
31 files changed, 2793 insertions(+), 273 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/3a03663b/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsCompletionListener.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsCompletionListener.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsCompletionListener.java
new file mode 100644
index 0000000..7a6c4d6
--- /dev/null
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsCompletionListener.java
@@ -0,0 +1,47 @@
+/*
+ * 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.jms;
+
+import javax.jms.Message;
+
+/**
+ * Interface used to implement listeners for asynchronous {@link javax.jms.Message}
+ * sends which will be notified on successful completion of a send or be notified of an
+ * error that was encountered while attempting to send a {@link javax.jms.Message}.
+ */
+public interface JmsCompletionListener {
+
+ /**
+ * Called when an asynchronous send operation completes successfully.
+ *
+ * @param message
+ * the {@link javax.jms.Message} that was successfully sent.
+ */
+ void onCompletion(Message message);
+
+ /**
+ * Called when an asynchronous send operation fails to complete, the state
+ * of the send is unknown at this point.
+ *
+ * @param message
+ * the {@link javax.jms.Message} that was to be sent.
+ * @param exception
+ * the {@link java.lang.Exception} that describes the send error.
+ */
+ void onException(Message message, Exception exception);
+
+}
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/3a03663b/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsConnection.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsConnection.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsConnection.java
index 827da11..a04d1b3 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsConnection.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsConnection.java
@@ -156,6 +156,11 @@ public class JmsConnection implements AutoCloseable, Connection, TopicConnection
public void close() throws JMSException {
boolean interrupted = Thread.interrupted();
+ for (JmsSession session : sessions.values()) {
+ session.checkIsDeliveryThread();
+ session.checkIsCompletionThread();
+ }
+
try {
if (!closed.get() && !failed.get()) {
@@ -1072,6 +1077,26 @@ public class JmsConnection implements AutoCloseable, Connection, TopicConnection
}
@Override
+ public void onCompletedMessageSend(JmsOutboundMessageDispatch envelope) {
+ JmsSession session = sessions.get(envelope.getProducerId().getParentId());
+ if (session != null) {
+ session.onCompletedMessageSend(envelope);
+ } else {
+ LOG.debug("No matching Session found for async send result");
+ }
+ }
+
+ @Override
+ public void onFailedMessageSend(JmsOutboundMessageDispatch envelope, Throwable cause) {
+ JmsSession session = sessions.get(envelope.getProducerId().getParentId());
+ if (session != null) {
+ session.onFailedMessageSend(envelope, cause);
+ } else {
+ LOG.debug("No matching Session found for failed async send result");
+ }
+ }
+
+ @Override
public void onConnectionInterrupted(final URI remoteURI) {
for (JmsSession session : sessions.values()) {
session.onConnectionInterrupted();
@@ -1161,6 +1186,12 @@ public class JmsConnection implements AutoCloseable, Connection, TopicConnection
public void onConnectionFailure(final IOException ex) {
providerFailed(ex);
+ // Signal that connection dropped we need to mark transactions as
+ // failed, deliver failure events to asynchronous send completions etc.
+ for (JmsSession session : sessions.values()) {
+ session.onConnectionInterrupted();
+ }
+
onProviderException(ex);
for (AsyncResult request : requests.keySet()) {
@@ -1304,10 +1335,7 @@ public class JmsConnection implements AutoCloseable, Connection, TopicConnection
if (!closed.get() && !closing.get()) {
if (this.exceptionListener != null) {
- if (!(error instanceof JMSException)) {
- error = JmsExceptionSupport.create(error);
- }
- final JMSException jmsError = (JMSException)error;
+ final JMSException jmsError = JmsExceptionSupport.create(error);
executor.execute(new Runnable() {
@Override
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/3a03663b/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsMessageConsumer.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsMessageConsumer.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsMessageConsumer.java
index de7ef63..18fd764 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsMessageConsumer.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsMessageConsumer.java
@@ -438,10 +438,10 @@ public class JmsMessageConsumer implements AutoCloseable, MessageConsumer, JmsMe
}
if (this.messageListener != null && this.started) {
- session.getExecutor().execute(new MessageDeliverTask());
+ session.getDispatcherExecutor().execute(new MessageDeliverTask());
} else {
if (availableListener != null) {
- session.getExecutor().execute(new Runnable() {
+ session.getDispatcherExecutor().execute(new Runnable() {
@Override
public void run() {
if (session.isStarted()) {
@@ -507,7 +507,7 @@ public class JmsMessageConsumer implements AutoCloseable, MessageConsumer, JmsMe
void drainMessageQueueToListener() {
if (this.messageListener != null && this.started) {
- session.getExecutor().execute(new MessageDeliverTask());
+ session.getDispatcherExecutor().execute(new MessageDeliverTask());
}
}
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/3a03663b/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsMessageProducer.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsMessageProducer.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsMessageProducer.java
index a1bbe38..65812b7 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsMessageProducer.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsMessageProducer.java
@@ -158,7 +158,7 @@ public class JmsMessageProducer implements AutoCloseable, MessageProducer {
throw new UnsupportedOperationException("Using this method is not supported on producers created without an explicit Destination");
}
- sendMessage(producerInfo.getDestination(), message, deliveryMode, priority, timeToLive);
+ sendMessage(producerInfo.getDestination(), message, deliveryMode, priority, timeToLive, null);
}
@Override
@@ -174,15 +174,107 @@ public class JmsMessageProducer implements AutoCloseable, MessageProducer {
throw new UnsupportedOperationException("Using this method is not supported on producers created with an explicit Destination.");
}
- sendMessage(destination, message, deliveryMode, priority, timeToLive);
+ sendMessage(destination, message, deliveryMode, priority, timeToLive, null);
}
- private void sendMessage(Destination destination, Message message, int deliveryMode, int priority, long timeToLive) throws JMSException {
+ /**
+ * Sends the message asynchronously and notifies the assigned listener on success or failure
+ *
+ * @param message
+ * the {@link javax.jms.Message} to send.
+ * @param listener
+ * the {@link JmsCompletionListener} to notify on send success or failure.
+ *
+ * @throws JMSException if an error occurs while attempting to send the Message.
+ */
+ public void send(Message message, JmsCompletionListener listener) throws JMSException {
+ send(message, this.deliveryMode, this.priority, this.timeToLive, listener);
+ }
+
+ /**
+ * Sends the message asynchronously and notifies the assigned listener on success or failure
+ *
+ * @param message
+ * the {@link javax.jms.Message} to send.
+ * @param deliveryMode
+ * the delivery mode to assign to the outbound Message.
+ * @param priority
+ * the priority to assign to the outbound Message.
+ * @param timeToLive
+ * the time to live value to assign to the outbound Message.
+ * @param listener
+ * the {@link JmsCompletionListener} to notify on send success or failure.
+ *
+ * @throws JMSException if an error occurs while attempting to send the Message.
+ */
+ public void send(Message message, int deliveryMode, int priority, long timeToLive, JmsCompletionListener listener) throws JMSException {
+ checkClosed();
+
+ if (anonymousProducer) {
+ throw new UnsupportedOperationException("Using this method is not supported on producers created without an explicit Destination");
+ }
+
+ if (listener == null) {
+ throw new IllegalArgumentException("JmsCompletetionListener cannot be null");
+ }
+
+ sendMessage(producerInfo.getDestination(), message, deliveryMode, priority, timeToLive, listener);
+ }
+
+ /**
+ * Sends the message asynchronously and notifies the assigned listener on success or failure
+ *
+ * @param destination
+ * the Destination to send the given Message to.
+ * @param message
+ * the {@link javax.jms.Message} to send.
+ * @param listener
+ * the {@link JmsCompletionListener} to notify on send success or failure.
+ *
+ * @throws JMSException if an error occurs while attempting to send the Message.
+ */
+ public void send(Destination destination, Message message, JmsCompletionListener listener) throws JMSException {
+ send(destination, message, this.deliveryMode, this.priority, this.timeToLive, listener);
+ }
+
+ /**
+ * Sends the message asynchronously and notifies the assigned listener on success or failure
+ *
+ * @param destination
+ * the Destination to send the given Message to.
+ * @param message
+ * the {@link javax.jms.Message} to send.
+ * @param deliveryMode
+ * the delivery mode to assign to the outbound Message.
+ * @param priority
+ * the priority to assign to the outbound Message.
+ * @param timeToLive
+ * the time to live value to assign to the outbound Message.
+ * @param listener
+ * the {@link JmsCompletionListener} to notify on send success or failure.
+ *
+ * @throws JMSException if an error occurs while attempting to send the Message.
+ */
+ public void send(Destination destination, Message message, int deliveryMode, int priority, long timeToLive, JmsCompletionListener listener) throws JMSException {
+ checkClosed();
+
+ if (!anonymousProducer) {
+ throw new UnsupportedOperationException("Using this method is not supported on producers created with an explicit Destination.");
+ }
+
+ if (listener == null) {
+ throw new IllegalArgumentException("JmsCompletetionListener cannot be null");
+ }
+
+ sendMessage(destination, message, deliveryMode, priority, timeToLive, listener);
+ }
+
+ private void sendMessage(Destination destination, Message message, int deliveryMode, int priority, long timeToLive, JmsCompletionListener listener) throws JMSException {
if (destination == null) {
throw new InvalidDestinationException("Don't understand null destinations");
}
- this.session.send(this, destination, message, deliveryMode, priority, timeToLive, disableMessageId, disableTimestamp);
+ this.session.send(this, destination, message, deliveryMode, priority, timeToLive, disableMessageId, disableTimestamp, listener);
}
@Override
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/3a03663b/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsSession.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsSession.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsSession.java
index 4644267..817f342 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsSession.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsSession.java
@@ -18,8 +18,11 @@ package org.apache.qpid.jms;
import java.io.Serializable;
import java.util.ArrayList;
+import java.util.Deque;
+import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -58,6 +61,7 @@ import javax.jms.TopicPublisher;
import javax.jms.TopicSession;
import javax.jms.TopicSubscriber;
+import org.apache.qpid.jms.exceptions.JmsExceptionSupport;
import org.apache.qpid.jms.message.JmsInboundMessageDispatch;
import org.apache.qpid.jms.message.JmsMessage;
import org.apache.qpid.jms.message.JmsMessageTransformation;
@@ -98,14 +102,18 @@ public class JmsSession implements AutoCloseable, Session, QueueSession, TopicSe
private final LinkedBlockingQueue<JmsInboundMessageDispatch> stoppedMessages =
new LinkedBlockingQueue<JmsInboundMessageDispatch>(10000);
private final JmsSessionInfo sessionInfo;
- private volatile ExecutorService executor;
private final ReentrantLock sendLock = new ReentrantLock();
+ private volatile ExecutorService deliveryExecutor;
+ private volatile ExecutorService completionExcecutor;
+ private Thread deliveryThread;
+ private Thread completionThread;
private final AtomicLong consumerIdGenerator = new AtomicLong();
private final AtomicLong producerIdGenerator = new AtomicLong();
private JmsTransactionContext transactionContext;
private boolean sessionRecovered;
private final AtomicReference<Exception> failureCause = new AtomicReference<Exception>();
+ private final Deque<SendCompletion> asyncSendQueue = new ConcurrentLinkedDeque<SendCompletion>();
protected JmsSession(JmsConnection connection, JmsSessionId sessionId, int acknowledgementMode) throws JMSException {
this.connection = connection;
@@ -178,6 +186,7 @@ public class JmsSession implements AutoCloseable, Session, QueueSession, TopicSe
@Override
public void commit() throws JMSException {
checkClosed();
+ checkIsCompletionThread();
if (!getTransacted()) {
throw new javax.jms.IllegalStateException("Not a transacted session");
@@ -189,6 +198,7 @@ public class JmsSession implements AutoCloseable, Session, QueueSession, TopicSe
@Override
public void rollback() throws JMSException {
checkClosed();
+ checkIsCompletionThread();
if (!getTransacted()) {
throw new javax.jms.IllegalStateException("Not a transacted session");
@@ -223,6 +233,9 @@ public class JmsSession implements AutoCloseable, Session, QueueSession, TopicSe
@Override
public void close() throws JMSException {
+ checkIsDeliveryThread();
+ checkIsCompletionThread();
+
if (!closed.get()) {
doClose();
}
@@ -272,11 +285,22 @@ public class JmsSession implements AutoCloseable, Session, QueueSession, TopicSe
}
transactionContext.shutdown();
+
+ synchronized (sessionInfo) {
+ if (completionExcecutor != null) {
+ completionExcecutor.shutdown();
+ completionExcecutor = null;
+ }
+ }
}
}
void sessionClosed(Exception cause) {
try {
+ // TODO - This assumes we can't rely on the AmqpProvider to signal all pending
+ // asynchronous send completions that they are failed when the session
+ // is remotely closed.
+ getCompletionExecutor().execute(new FailOrCompleteAsyncCompletionsTask(JmsExceptionSupport.create(cause)));
shutdown(cause);
} catch (Throwable error) {
LOG.trace("Ignoring exception thrown during cleanup of closed session", error);
@@ -306,6 +330,11 @@ public class JmsSession implements AutoCloseable, Session, QueueSession, TopicSe
try {
if (producer != null) {
+ // TODO - This assumes we can't rely on the AmqpProvider to signal all pending
+ // asynchronous send completions that they are failed when the producer
+ // is remotely closed.
+ getCompletionExecutor().execute(new FailOrCompleteAsyncCompletionsTask(
+ producer.getProducerId(), JmsExceptionSupport.create(cause)));
producer.shutdown(cause);
}
} catch (Throwable error) {
@@ -624,17 +653,17 @@ public class JmsSession implements AutoCloseable, Session, QueueSession, TopicSe
connection.onException(ex);
}
- protected void send(JmsMessageProducer producer, Destination dest, Message msg, int deliveryMode, int priority, long timeToLive, boolean disableMsgId, boolean disableTimestamp) throws JMSException {
+ protected void send(JmsMessageProducer producer, Destination dest, Message msg, int deliveryMode, int priority, long timeToLive, boolean disableMsgId, boolean disableTimestamp, JmsCompletionListener listener) throws JMSException {
JmsDestination destination = JmsMessageTransformation.transformDestination(connection, dest);
if (destination.isTemporary() && ((JmsTemporaryDestination) destination).isDeleted()) {
throw new IllegalStateException("Temporary destination has been deleted");
}
- send(producer, destination, msg, deliveryMode, priority, timeToLive, disableMsgId, disableTimestamp);
+ send(producer, destination, msg, deliveryMode, priority, timeToLive, disableMsgId, disableTimestamp, listener);
}
- private void send(JmsMessageProducer producer, JmsDestination destination, Message original, int deliveryMode, int priority, long timeToLive, boolean disableMsgId, boolean disableTimestamp) throws JMSException {
+ private void send(JmsMessageProducer producer, JmsDestination destination, Message original, int deliveryMode, int priority, long timeToLive, boolean disableMsgId, boolean disableTimestamp, JmsCompletionListener listener) throws JMSException {
sendLock.lock();
try {
original.setJMSDeliveryMode(deliveryMode);
@@ -707,14 +736,35 @@ public class JmsSession implements AutoCloseable, Session, QueueSession, TopicSe
envelope.setMessage(copy);
envelope.setProducerId(producer.getProducerId());
envelope.setDestination(destination);
- envelope.setSendAsync(!sync);
+ envelope.setSendAsync(listener == null ? !sync : true);
envelope.setDispatchId(messageSequence);
+ envelope.setCompletionRequired(listener != null);
if (producer.isAnonymous()) {
envelope.setPresettle(getPresettlePolicy().isProducerPresttled(this, destination));
}
- transactionContext.send(connection, envelope);
+ SendCompletion completion = null;
+ if (envelope.isCompletionRequired()) {
+ completion = new SendCompletion(envelope, listener);
+ asyncSendQueue.addLast(completion);
+ }
+
+ try {
+ transactionContext.send(connection, envelope);
+ } catch (JMSException jmsEx) {
+ // If the synchronous portion of the send fails the completion be
+ // notified but might depending on the circumstances of the failures,
+ // remove it from the queue and check if is is already completed.
+ if (completion != null) {
+ asyncSendQueue.remove(completion);
+ if (completion.hasCompleted()) {
+ return;
+ }
+ }
+
+ throw jmsEx;
+ }
} finally {
sendLock.unlock();
}
@@ -837,9 +887,9 @@ public class JmsSession implements AutoCloseable, Session, QueueSession, TopicSe
}
synchronized (sessionInfo) {
- if (executor != null) {
- executor.shutdown();
- executor = null;
+ if (deliveryExecutor != null) {
+ deliveryExecutor.shutdown();
+ deliveryExecutor = null;
}
}
}
@@ -852,29 +902,62 @@ public class JmsSession implements AutoCloseable, Session, QueueSession, TopicSe
return connection;
}
- Executor getExecutor() {
- ExecutorService exec = executor;
- if(exec == null) {
+ Executor getDispatcherExecutor() {
+ ExecutorService exec = deliveryExecutor;
+ if (exec == null) {
synchronized (sessionInfo) {
- if (executor == null) {
- executor = Executors.newSingleThreadExecutor(new ThreadFactory() {
- @Override
- public Thread newThread(Runnable runner) {
- Thread executor = new Thread(runner);
- executor.setName("JmsSession ["+ sessionInfo.getId() + "] dispatcher");
- executor.setDaemon(true);
- return executor;
- }
- });
+ if (deliveryExecutor == null) {
+ deliveryExecutor = createExecutor("delivery dispatcher");
}
- exec = executor;
+ exec = deliveryExecutor;
+ exec.execute(new Runnable() {
+
+ @Override
+ public void run() {
+ JmsSession.this.deliveryThread = Thread.currentThread();
+ }
+ });
}
}
return exec;
}
+ Executor getCompletionExecutor() {
+ ExecutorService exec = completionExcecutor;
+ if (exec == null) {
+ synchronized (sessionInfo) {
+ if (completionExcecutor == null) {
+ completionExcecutor = createExecutor("completion dispatcher");
+ }
+
+ exec = completionExcecutor;
+ exec.execute(new Runnable() {
+
+ @Override
+ public void run() {
+ JmsSession.this.completionThread = Thread.currentThread();
+ }
+ });
+ }
+ }
+
+ return exec;
+ }
+
+ private ExecutorService createExecutor(final String threadNameSuffix) {
+ return Executors.newSingleThreadExecutor(new ThreadFactory() {
+ @Override
+ public Thread newThread(Runnable runner) {
+ Thread executor = new Thread(runner);
+ executor.setName("JmsSession ["+ sessionInfo.getId() + "] " + threadNameSuffix);
+ executor.setDaemon(true);
+ return executor;
+ }
+ });
+ }
+
protected JmsSessionInfo getSessionInfo() {
return sessionInfo;
}
@@ -925,6 +1008,18 @@ public class JmsSession implements AutoCloseable, Session, QueueSession, TopicSe
}
}
+ void checkIsDeliveryThread() throws JMSException {
+ if (Thread.currentThread().equals(deliveryThread)) {
+ throw new IllegalStateException("Illegal invocation from MessageListener callback");
+ }
+ }
+
+ void checkIsCompletionThread() throws JMSException {
+ if (Thread.currentThread().equals(completionThread)) {
+ throw new IllegalStateException("Illegal invocation from CompletionListener callback");
+ }
+ }
+
public JmsMessageIDPolicy getMessageIDPolicy() {
return sessionInfo.getMessageIDPolicy();
}
@@ -945,6 +1040,36 @@ public class JmsSession implements AutoCloseable, Session, QueueSession, TopicSe
return sessionInfo.getDeserializationPolicy();
}
+ /**
+ * Sets the transaction context of the session.
+ *
+ * @param transactionContext
+ * provides the means to control a JMS transaction.
+ */
+ public void setTransactionContext(JmsTransactionContext transactionContext) {
+ this.transactionContext = transactionContext;
+ }
+
+ /**
+ * Returns the transaction context of the session.
+ *
+ * @return transactionContext
+ * session's transaction context.
+ */
+ public JmsTransactionContext getTransactionContext() {
+ return transactionContext;
+ }
+
+ boolean isSessionRecovered() {
+ return sessionRecovered;
+ }
+
+ void clearSessionRecovered() {
+ sessionRecovered = false;
+ }
+
+ //----- Event handlers ---------------------------------------------------//
+
@Override
public void onInboundMessage(JmsInboundMessageDispatch envelope) {
if (started.get()) {
@@ -954,10 +1079,22 @@ public class JmsSession implements AutoCloseable, Session, QueueSession, TopicSe
}
}
+ protected void onCompletedMessageSend(final JmsOutboundMessageDispatch envelope) {
+ getCompletionExecutor().execute(new AsyncCompletionTask(envelope));
+ }
+
+ protected void onFailedMessageSend(final JmsOutboundMessageDispatch envelope, final Throwable cause) {
+ getCompletionExecutor().execute(new AsyncCompletionTask(envelope, cause));
+ }
+
protected void onConnectionInterrupted() {
transactionContext.onConnectionInterrupted();
+ // TODO - Synthesize a better exception
+ JMSException failureCause = new JMSException("Send failed due to connection loss");
+ getCompletionExecutor().execute(new FailOrCompleteAsyncCompletionsTask(failureCause));
+
for (JmsMessageProducer producer : producers.values()) {
producer.onConnectionInterrupted();
}
@@ -1019,31 +1156,155 @@ public class JmsSession implements AutoCloseable, Session, QueueSession, TopicSe
}
}
- /**
- * Sets the transaction context of the session.
- *
- * @param transactionContext
- * provides the means to control a JMS transaction.
- */
- public void setTransactionContext(JmsTransactionContext transactionContext) {
- this.transactionContext = transactionContext;
- }
+ //----- Asynchronous Send Helpers ----------------------------------------//
- /**
- * Returns the transaction context of the session.
- *
- * @return transactionContext
- * session's transaction context.
- */
- public JmsTransactionContext getTransactionContext() {
- return transactionContext;
+ private final class FailOrCompleteAsyncCompletionsTask implements Runnable {
+
+ private final JMSException failureCause;
+ private final JmsProducerId producerId;
+
+ public FailOrCompleteAsyncCompletionsTask(JMSException failureCause) {
+ this(null, failureCause);
+ }
+
+ public FailOrCompleteAsyncCompletionsTask(JmsProducerId producerId, JMSException failureCause) {
+ this.failureCause = failureCause;
+ this.producerId = producerId;
+ }
+
+ @Override
+ public void run() {
+ // For any completion that is not yet marked as complete we fail it
+ // otherwise we send the already marked completion state event.
+ Iterator<SendCompletion> pending = asyncSendQueue.iterator();
+ while (pending.hasNext()) {
+ SendCompletion completion = pending.next();
+
+ if (!completion.hasCompleted()) {
+ if (producerId == null || producerId.equals(completion.envelope.getProducerId())) {
+ completion.markAsFailed(failureCause);
+ }
+ }
+
+ try {
+ completion.signalCompletion();
+ } catch (Throwable error) {
+ LOG.trace("Signaled completion of send: {}", completion.envelope);
+ }
+ }
+
+ asyncSendQueue.clear();
+ }
}
- boolean isSessionRecovered() {
- return sessionRecovered;
+ private final class AsyncCompletionTask implements Runnable {
+
+ private final JmsOutboundMessageDispatch envelope;
+ private final Throwable cause;
+
+ public AsyncCompletionTask(JmsOutboundMessageDispatch envelope) {
+ this(envelope, null);
+ }
+
+ public AsyncCompletionTask(JmsOutboundMessageDispatch envelope, Throwable cause) {
+ this.envelope = envelope;
+ this.cause = cause;
+ }
+
+ @Override
+ public void run() {
+ try {
+ SendCompletion completion = asyncSendQueue.peek();
+ if (completion.getEnvelope().getDispatchId() == envelope.getDispatchId()) {
+ try {
+ completion = asyncSendQueue.remove();
+ if (cause == null) {
+ completion.markAsComplete();
+ } else {
+ completion.markAsFailed(JmsExceptionSupport.create(cause));
+ }
+ completion.signalCompletion();
+ } catch (Throwable error) {
+ LOG.trace("Failed while performing send completion: {}", envelope);
+ // TODO - What now?
+ }
+
+ // Signal any trailing completions that have been marked complete
+ // before this one was that they have now that the one in front has
+ Iterator<SendCompletion> pending = asyncSendQueue.iterator();
+ while (pending.hasNext()) {
+ completion = pending.next();
+ if (completion.hasCompleted()) {
+ try {
+ completion.signalCompletion();
+ } catch (Throwable error) {
+ LOG.trace("Failed while performing send completion: {}", envelope);
+ // TODO - What now?
+ } finally {
+ pending.remove();
+ }
+ } else {
+ break;
+ }
+ }
+ } else {
+ // Not head so mark as complete and wait for the one in front to send
+ // the notification of completion.
+ Iterator<SendCompletion> pending = asyncSendQueue.iterator();
+ while (pending.hasNext()) {
+ completion = pending.next();
+ if (completion.getEnvelope().getDispatchId() == envelope.getDispatchId()) {
+ if (cause == null) {
+ completion.markAsComplete();
+ } else {
+ completion.markAsFailed(JmsExceptionSupport.create(cause));
+ }
+ }
+ }
+ }
+ } catch (Exception ex) {
+ LOG.debug("Send completion task encounted unexpected error: {}", ex.getMessage());
+ // TODO - What now
+ }
+ }
}
- void clearSessionRecovered() {
- sessionRecovered = false;
+ private final class SendCompletion {
+
+ private final JmsOutboundMessageDispatch envelope;
+ private final JmsCompletionListener listener;
+
+ private Exception failureCause;
+ private boolean completed;
+
+ public SendCompletion(JmsOutboundMessageDispatch envelope, JmsCompletionListener listener) {
+ this.envelope = envelope;
+ this.listener = listener;
+ }
+
+ public void markAsComplete() {
+ completed = true;
+ }
+
+ public void markAsFailed(Exception cause) {
+ completed = true;
+ failureCause = cause;
+ }
+
+ public boolean hasCompleted() {
+ return completed;
+ }
+
+ public void signalCompletion() {
+ if (failureCause == null) {
+ listener.onCompletion(envelope.getMessage());
+ } else {
+ listener.onException(envelope.getMessage(), failureCause);
+ }
+ }
+
+ public JmsOutboundMessageDispatch getEnvelope() {
+ return envelope;
+ }
}
}
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/3a03663b/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/JmsInboundMessageDispatch.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/JmsInboundMessageDispatch.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/JmsInboundMessageDispatch.java
index 577ac17..c038519 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/JmsInboundMessageDispatch.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/JmsInboundMessageDispatch.java
@@ -30,6 +30,8 @@ public class JmsInboundMessageDispatch extends JmsAbstractResourceId {
private JmsMessage message;
private boolean enqueueFirst;
+ private transient String stringView;
+
public JmsInboundMessageDispatch(long sequence) {
this.sequence = sequence;
}
@@ -74,10 +76,21 @@ public class JmsInboundMessageDispatch extends JmsAbstractResourceId {
@Override
public String toString() {
- return "JmsInboundMessageDispatch {sequence = " + sequence
- + ", messageId = " + messageId
- + ", consumerId = " + consumerId
- + "}";
+ if (stringView == null) {
+ StringBuilder builder = new StringBuilder();
+
+ builder.append("JmsInboundMessageDispatch { sequence = ");
+ builder.append(sequence);
+ builder.append(", messageId = ");
+ builder.append(messageId);
+ builder.append(", consumerId = ");
+ builder.append(consumerId);
+ builder.append(" }");
+
+ stringView = builder.toString();
+ }
+
+ return stringView;
}
@Override
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/3a03663b/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/JmsOutboundMessageDispatch.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/JmsOutboundMessageDispatch.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/JmsOutboundMessageDispatch.java
index 05b9089..a34768f 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/JmsOutboundMessageDispatch.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/JmsOutboundMessageDispatch.java
@@ -29,8 +29,11 @@ public class JmsOutboundMessageDispatch {
private JmsDestination destination;
private boolean sendAsync;
private boolean presettle;
+ private boolean completionRequired;
private long dispatchId;
+ private transient String stringView;
+
public JmsDestination getDestination() {
return destination;
}
@@ -39,6 +42,10 @@ public class JmsOutboundMessageDispatch {
this.destination = destination;
}
+ public Object getMessageId() {
+ return message.getFacade().getProviderMessageIdObject();
+ }
+
public JmsMessage getMessage() {
return message;
}
@@ -79,15 +86,34 @@ public class JmsOutboundMessageDispatch {
this.presettle = presettle;
}
- @Override
- public String toString() {
- StringBuilder value = new StringBuilder();
+ public boolean isCompletionRequired() {
+ return completionRequired;
+ }
- value.append("JmsOutboundMessageDispatch {dispatchId = ");
- value.append(getProducerId());
- value.append("-");
- value.append(getDispatchId());
+ public void setCompletionRequired(boolean completionRequired) {
+ this.completionRequired = completionRequired;
+ }
- return value.toString();
+ @Override
+ public String toString() {
+ if (stringView == null) {
+ StringBuilder value = new StringBuilder();
+
+ value.append("JmsOutboundMessageDispatch {dispatchId = ");
+ value.append(getProducerId());
+ value.append("-");
+ value.append(getDispatchId());
+ value.append(", MessageID = ");
+ try {
+ value.append(message.getJMSMessageID());
+ } catch (Throwable e) {
+ value.append("<unknown>");
+ }
+ value.append(" }");
+
+ stringView = value.toString();
+ }
+
+ return stringView;
}
}
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/3a03663b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/DefaultProviderListener.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/DefaultProviderListener.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/DefaultProviderListener.java
index 22e204e..d2eb95c 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/DefaultProviderListener.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/DefaultProviderListener.java
@@ -20,6 +20,7 @@ import java.io.IOException;
import java.net.URI;
import org.apache.qpid.jms.message.JmsInboundMessageDispatch;
+import org.apache.qpid.jms.message.JmsOutboundMessageDispatch;
import org.apache.qpid.jms.meta.JmsResource;
/**
@@ -32,6 +33,14 @@ public class DefaultProviderListener implements ProviderListener {
}
@Override
+ public void onCompletedMessageSend(JmsOutboundMessageDispatch envelope) {
+ }
+
+ @Override
+ public void onFailedMessageSend(JmsOutboundMessageDispatch envelope, Throwable cause) {
+ }
+
+ @Override
public void onConnectionInterrupted(URI remoteURI) {
}
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/3a03663b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderListener.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderListener.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderListener.java
index 5c758ed..11a5f6b 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderListener.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderListener.java
@@ -20,6 +20,7 @@ import java.io.IOException;
import java.net.URI;
import org.apache.qpid.jms.message.JmsInboundMessageDispatch;
+import org.apache.qpid.jms.message.JmsOutboundMessageDispatch;
import org.apache.qpid.jms.meta.JmsResource;
/**
@@ -36,6 +37,27 @@ public interface ProviderListener {
void onInboundMessage(JmsInboundMessageDispatch envelope);
/**
+ * Called when an outbound message dispatch that requested a completion callback
+ * has reached a state where the send can be considered successful based on the QoS
+ * level associated of the outbound message.
+ *
+ * @param envelope
+ * the original outbound message dispatch that is now complete.
+ */
+ void onCompletedMessageSend(JmsOutboundMessageDispatch envelope);
+
+ /**
+ * Called when an outbound message dispatch that requested a completion callback
+ * has reached a state where the send can be considered failed.
+ *
+ * @param envelope
+ * the original outbound message dispatch that should be treated as a failed send.
+ * @param cause
+ * the exception that describes the cause of the failed send.
+ */
+ void onFailedMessageSend(JmsOutboundMessageDispatch envelope, Throwable cause);
+
+ /**
* Called from a fault tolerant Provider instance to signal that the underlying
* connection to the Broker has been lost. The Provider will attempt to reconnect
* following this event unless closed.
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/3a03663b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderWrapper.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderWrapper.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderWrapper.java
index 8574e04..3a3d383 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderWrapper.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/ProviderWrapper.java
@@ -154,6 +154,16 @@ public class ProviderWrapper<E extends Provider> implements Provider, ProviderLi
}
@Override
+ public void onCompletedMessageSend(JmsOutboundMessageDispatch envelope) {
+ listener.onCompletedMessageSend(envelope);
+ }
+
+ @Override
+ public void onFailedMessageSend(JmsOutboundMessageDispatch envelope, Throwable cause) {
+ listener.onFailedMessageSend(envelope, cause);
+ }
+
+ @Override
public void onConnectionInterrupted(URI remoteURI) {
listener.onConnectionInterrupted(remoteURI);
}
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/3a03663b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpAbstractResource.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpAbstractResource.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpAbstractResource.java
index a8599ac..634cafd 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpAbstractResource.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpAbstractResource.java
@@ -23,6 +23,7 @@ import org.apache.qpid.jms.JmsOperationTimedOutException;
import org.apache.qpid.jms.meta.JmsConnectionInfo;
import org.apache.qpid.jms.meta.JmsResource;
import org.apache.qpid.jms.provider.AsyncResult;
+import org.apache.qpid.proton.engine.Delivery;
import org.apache.qpid.proton.engine.Endpoint;
import org.apache.qpid.proton.engine.EndpointState;
import org.slf4j.Logger;
@@ -158,7 +159,7 @@ public abstract class AmqpAbstractResource<R extends JmsResource, E extends Endp
closeOrDetachEndpoint();
}
- // Process the close now, so that child close operations see the correct state.
+ // Process the close before moving on to closing down child resources
provider.pumpToProtonTransport();
handleResourceClosure(provider, error);
@@ -253,7 +254,7 @@ public abstract class AmqpAbstractResource<R extends JmsResource, E extends Endp
}
@Override
- public void processDeliveryUpdates(AmqpProvider provider) throws IOException {
+ public void processDeliveryUpdates(AmqpProvider provider, Delivery delivery) throws IOException {
// Nothing do be done here, subclasses can override as needed.
}
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/3a03663b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpAnonymousFallbackProducer.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpAnonymousFallbackProducer.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpAnonymousFallbackProducer.java
index b44e3b3..3e07b25 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpAnonymousFallbackProducer.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpAnonymousFallbackProducer.java
@@ -69,7 +69,7 @@ public class AmqpAnonymousFallbackProducer extends AmqpProducer {
}
@Override
- public boolean send(JmsOutboundMessageDispatch envelope, AsyncResult request) throws IOException, JMSException {
+ public void send(JmsOutboundMessageDispatch envelope, AsyncResult request) throws IOException, JMSException {
LOG.trace("Started send chain for anonymous producer: {}", getProducerId());
// Force sends marked as asynchronous to be sent synchronous so that the temporary
@@ -91,7 +91,8 @@ public class AmqpAnonymousFallbackProducer extends AmqpProducer {
// We open a Fixed Producer instance with the target destination. Once it opens
// it will trigger the open event which will in turn trigger the send event.
- // If caching is disabled the created producer will be closed immediately.
+ // If caching is disabled the created producer will be closed immediately after
+ // the entire send chain has finished and the delivery has been acknowledged.
AmqpProducerBuilder builder = new AmqpProducerBuilder(session, info);
builder.buildResource(new AnonymousSendRequest(request, builder, envelope));
@@ -100,9 +101,9 @@ public class AmqpAnonymousFallbackProducer extends AmqpProducer {
producerCache.put(envelope.getDestination(), builder.getResource());
}
- return true;
+ getParent().getProvider().pumpToProtonTransport(request);
} else {
- return producer.send(envelope, request);
+ producer.send(envelope, request);
}
}
@@ -135,6 +136,14 @@ public class AmqpAnonymousFallbackProducer extends AmqpProducer {
return new JmsProducerId(producerIdKey, -1, producerIdCount++);
}
+ @Override
+ public void addSendCompletionWatcher(AsyncResult watcher) {
+ throw new UnsupportedOperationException(
+ "The fallback producer parent should never have a watcher assigned.");
+ }
+
+ //----- AsyncResult objects used to complete the sends -------------------//
+
private abstract class AnonymousRequest extends WrappedAsyncResult {
protected final JmsOutboundMessageDispatch envelope;
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/3a03663b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpConsumer.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpConsumer.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpConsumer.java
index c15fd08..89586e3 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpConsumer.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpConsumer.java
@@ -385,42 +385,35 @@ public class AmqpConsumer extends AmqpAbstractResource<JmsConsumerInfo, Receiver
}
@Override
- public void processDeliveryUpdates(AmqpProvider provider) throws IOException {
- Delivery incoming = null;
- do {
- incoming = getEndpoint().current();
- if (incoming != null) {
- if (incoming.isReadable() && !incoming.isPartial()) {
- LOG.trace("{} has incoming Message(s).", this);
- try {
- if (processDelivery(incoming)) {
- // We processed a message, signal completion
- // of a message pull request if there is one.
- if (pullRequest != null) {
- pullRequest.onSuccess();
- pullRequest = null;
- }
- }
- } catch (Exception e) {
- throw IOExceptionSupport.create(e);
+ public void processDeliveryUpdates(AmqpProvider provider, Delivery delivery) throws IOException {
+ if (delivery.isReadable() && !delivery.isPartial()) {
+ LOG.trace("{} has incoming Message(s).", this);
+ try {
+ if (processDelivery(delivery)) {
+ // We processed a message, signal completion
+ // of a message pull request if there is one.
+ if (pullRequest != null) {
+ pullRequest.onSuccess();
+ pullRequest = null;
}
- } else {
- LOG.trace("{} has a partial incoming Message(s), deferring.", this);
- incoming = null;
}
- } else {
- // We have exhausted the locally queued messages on this link.
- // Check if we tried to stop and have now run out of credit.
- if (getEndpoint().getRemoteCredit() <= 0) {
- if (stopRequest != null) {
- stopRequest.onSuccess();
- stopRequest = null;
- }
+ } catch (Exception e) {
+ throw IOExceptionSupport.create(e);
+ }
+ }
+
+ if (getEndpoint().current() == null) {
+ // We have exhausted the locally queued messages on this link.
+ // Check if we tried to stop and have now run out of credit.
+ if (getEndpoint().getRemoteCredit() <= 0) {
+ if (stopRequest != null) {
+ stopRequest.onSuccess();
+ stopRequest = null;
}
}
- } while (incoming != null);
+ }
- super.processDeliveryUpdates(provider);
+ super.processDeliveryUpdates(provider, delivery);
}
private boolean processDelivery(Delivery incoming) throws Exception {
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/3a03663b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpEventSink.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpEventSink.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpEventSink.java
index b3e7501..2e25ad1 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpEventSink.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpEventSink.java
@@ -18,6 +18,8 @@ package org.apache.qpid.jms.provider.amqp;
import java.io.IOException;
+import org.apache.qpid.proton.engine.Delivery;
+
/**
* Interface used by classes that want to process AMQP events sent from
* the transport layer.
@@ -60,10 +62,12 @@ public interface AmqpEventSink {
*
* @param provider
* the AmqpProvider instance for easier access to fire events.
+ * @param delivery
+ * the Delivery that has an update to its state which needs handled.
*
* @throws IOException if an error occurs while processing the update.
*/
- void processDeliveryUpdates(AmqpProvider provider) throws IOException;
+ void processDeliveryUpdates(AmqpProvider provider, Delivery delivery) throws IOException;
/**
* Called when the Proton Engine signals an Flow related event has been triggered
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/3a03663b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpExceptionBuilder.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpExceptionBuilder.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpExceptionBuilder.java
new file mode 100644
index 0000000..2ecf245
--- /dev/null
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpExceptionBuilder.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.jms.provider.amqp;
+
+/**
+ * Used to provide a source for an exception based on some event such as
+ * operation timed out, etc.
+ */
+public interface AmqpExceptionBuilder {
+
+ /**
+ * Creates an exception appropriate to some failure condition
+ *
+ * @return a new Exception instance that describes a failure condition.
+ */
+ Exception createException();
+
+}
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/3a03663b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpFixedProducer.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpFixedProducer.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpFixedProducer.java
index c354822..9233ce1 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpFixedProducer.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpFixedProducer.java
@@ -18,10 +18,10 @@ package org.apache.qpid.jms.provider.amqp;
import java.io.IOException;
import java.util.ArrayList;
-import java.util.LinkedHashSet;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Set;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.Map;
import java.util.concurrent.ScheduledFuture;
import javax.jms.IllegalStateException;
@@ -62,10 +62,12 @@ public class AmqpFixedProducer extends AmqpProducer {
private static final byte[] EMPTY_BYTE_ARRAY = new byte[] {};
private final AmqpTransferTagGenerator tagGenerator = new AmqpTransferTagGenerator(true);
- private final Set<Delivery> sent = new LinkedHashSet<Delivery>();
- private final LinkedList<InFlightSend> blocked = new LinkedList<InFlightSend>();
+ private final Map<Object, InFlightSend> sent = new LinkedHashMap<Object, InFlightSend>();
+ private final Map<Object, InFlightSend> blocked = new LinkedHashMap<Object, InFlightSend>();
private byte[] encodeBuffer = new byte[1024 * 8];
+ private AsyncResult sendCompletionWatcher;
+
public AmqpFixedProducer(AmqpSession session, JmsProducerInfo info) {
super(session, info);
}
@@ -86,29 +88,24 @@ public class AmqpFixedProducer extends AmqpProducer {
}
@Override
- public boolean send(JmsOutboundMessageDispatch envelope, AsyncResult request) throws IOException, JMSException {
+ public void send(JmsOutboundMessageDispatch envelope, AsyncResult request) throws IOException, JMSException {
if (isClosed()) {
request.onFailure(new IllegalStateException("The MessageProducer is closed"));
}
if (getEndpoint().getCredit() <= 0) {
LOG.trace("Holding Message send until credit is available.");
- // Once a message goes into a held mode we no longer can send it async, so
- // we clear the async flag if set to avoid the sender never getting notified.
- envelope.setSendAsync(false);
InFlightSend send = new InFlightSend(envelope, request);
if (getSendTimeout() > JmsConnectionInfo.INFINITE) {
- send.requestTimeout = getParent().getProvider().scheduleRequestTimeout(
- send, getSendTimeout(), new JmsSendTimedOutException("Timed out waiting for credit to send Message", envelope.getMessage()));
+ send.requestTimeout = getParent().getProvider().scheduleRequestTimeout(send, getSendTimeout(), send);
}
- blocked.addLast(send);
- return false;
+ blocked.put(envelope.getMessageId(), send);
+ getParent().getProvider().pumpToProtonTransport(request);
} else {
doSend(envelope, request);
- return true;
}
}
@@ -133,35 +130,54 @@ public class AmqpFixedProducer extends AmqpProducer {
delivery = getEndpoint().delivery(tag, 0, tag.length);
}
- delivery.setContext(request);
-
if (session.isTransacted()) {
- Binary amqpTxId = session.getTransactionContext().getAmqpTransactionId();
+ AmqpTransactionContext context = session.getTransactionContext();
+ Binary amqpTxId = context.getAmqpTransactionId();
TransactionalState state = new TransactionalState();
state.setTxnId(amqpTxId);
delivery.disposition(state);
+ context.registerTxProducer(this);
}
AmqpJmsMessageFacade amqpMessageFacade = (AmqpJmsMessageFacade) facade;
encodeAndSend(amqpMessageFacade.getAmqpMessage(), delivery);
+ AmqpProvider provider = getParent().getProvider();
+
+ InFlightSend send = null;
+ if (request instanceof InFlightSend) {
+ send = (InFlightSend) request;
+ } else {
+ send = new InFlightSend(envelope, request);
+
+ if (!presettle && getSendTimeout() != JmsConnectionInfo.INFINITE) {
+ send.requestTimeout = getParent().getProvider().scheduleRequestTimeout(send, getSendTimeout(), send);
+ }
+ }
+
if (presettle) {
delivery.settle();
} else {
- sent.add(delivery);
+ sent.put(envelope.getMessageId(), send);
getEndpoint().advance();
}
- if (envelope.isSendAsync() || presettle) {
- request.onSuccess();
- } else if (getSendTimeout() != JmsConnectionInfo.INFINITE) {
- InFlightSend send = new InFlightSend(envelope, request);
-
- send.requestTimeout = getParent().getProvider().scheduleRequestTimeout(
- send, getSendTimeout(), new JmsSendTimedOutException("Timed out waiting for disposition of sent Message", envelope.getMessage()));
-
- // Update context so the incoming disposition can cancel any pending timeout
- delivery.setContext(send);
+ send.setDelivery(delivery);
+ delivery.setContext(send);
+
+ // Put it on the wire and let it fail if the connection is broken, if it does
+ // get written then continue on to determine when we should complete it.
+ if (provider.pumpToProtonTransport(request)) {
+ // For presettled messages we can just mark as successful and we are done, but
+ // for any other message we still track it until the remote settles. If the send
+ // was tagged as asynchronous we must mark the original request as complete but
+ // we still need to wait for the disposition before we can consider the send as
+ // having been successful.
+ if (presettle) {
+ send.onSuccess();
+ } else if (envelope.isSendAsync()) {
+ send.getOriginalRequest().onSuccess();
+ }
}
}
@@ -195,13 +211,16 @@ public class AmqpFixedProducer extends AmqpProducer {
@Override
public void processFlowUpdates(AmqpProvider provider) throws IOException {
if (!blocked.isEmpty() && getEndpoint().getCredit() > 0) {
- while (getEndpoint().getCredit() > 0 && !blocked.isEmpty()) {
+ Iterator<InFlightSend> blockedSends = blocked.values().iterator();
+ while (getEndpoint().getCredit() > 0 && blockedSends.hasNext()) {
LOG.trace("Dispatching previously held send");
- InFlightSend held = blocked.pop();
+ InFlightSend held = blockedSends.next();
try {
- doSend(held.envelope, held);
+ doSend(held.getEnvelope(), held);
} catch (JMSException e) {
throw IOExceptionSupport.create(e);
+ } finally {
+ blockedSends.remove();
}
}
}
@@ -211,25 +230,18 @@ public class AmqpFixedProducer extends AmqpProducer {
getEndpoint().drained();
}
- // Once the pending sends queue is drained we can propagate the close request.
- if (blocked.isEmpty() && isAwaitingClose() && !isClosed()) {
- super.close(closeRequest);
- }
-
super.processFlowUpdates(provider);
}
@Override
- public void processDeliveryUpdates(AmqpProvider provider) throws IOException {
- List<Delivery> toRemove = new ArrayList<Delivery>();
-
- for (Delivery delivery : sent) {
- DeliveryState state = delivery.getRemoteState();
- if (state == null) {
- continue;
- }
+ public void processDeliveryUpdates(AmqpProvider provider, Delivery delivery) throws IOException {
+ DeliveryState state = delivery.getRemoteState();
+ if (state != null) {
+ InFlightSend send = (InFlightSend) delivery.getContext();
+ Exception deliveryError = null;
Outcome outcome = null;
+
if (state instanceof TransactionalState) {
LOG.trace("State of delivery is Transactional, retrieving outcome: {}", state);
outcome = ((TransactionalState) state).getOutcome();
@@ -240,14 +252,9 @@ public class AmqpFixedProducer extends AmqpProducer {
outcome = null;
}
- AsyncResult request = (AsyncResult) delivery.getContext();
- Exception deliveryError = null;
-
if (outcome instanceof Accepted) {
LOG.trace("Outcome of delivery was accepted: {}", delivery);
- if (request != null && !request.isComplete()) {
- request.onSuccess();
- }
+ send.onSuccess();
} else if (outcome instanceof Rejected) {
LOG.trace("Outcome of delivery was rejected: {}", delivery);
ErrorCondition remoteError = ((Rejected) outcome).getError();
@@ -265,21 +272,11 @@ public class AmqpFixedProducer extends AmqpProducer {
}
if (deliveryError != null) {
- if (request != null && !request.isComplete()) {
- request.onFailure(deliveryError);
- } else {
- connection.getProvider().fireNonFatalProviderException(deliveryError);
- }
+ send.onFailure(deliveryError);
}
-
- tagGenerator.returnTag(delivery.getTag());
- toRemove.add(delivery);
- delivery.settle();
}
- sent.removeAll(toRemove);
-
- super.processDeliveryUpdates(provider);
+ super.processDeliveryUpdates(provider, delivery);
}
public AmqpSession getSession() {
@@ -312,45 +309,45 @@ public class AmqpFixedProducer extends AmqpProducer {
error = new JMSException("Producer closed remotely before message transfer result was notified");
}
- for (Delivery delivery : sent) {
+ Collection<InFlightSend> inflightSends = new ArrayList<InFlightSend>(sent.values());
+ for (InFlightSend send : inflightSends) {
try {
- AsyncResult request = (AsyncResult) delivery.getContext();
-
- if (request != null && !request.isComplete()) {
- request.onFailure(error);
- }
-
- delivery.settle();
- tagGenerator.returnTag(delivery.getTag());
+ send.onFailure(error);
} catch (Exception e) {
- LOG.debug("Caught exception when failing pending send during remote producer closure: {}", delivery, e);
+ LOG.debug("Caught exception when failing pending send during remote producer closure: {}", send, e);
}
}
- sent.clear();
-
- for (InFlightSend blockedSend : blocked) {
+ Collection<InFlightSend> blockedSends = new ArrayList<InFlightSend>(blocked.values());
+ for (InFlightSend send : blockedSends) {
try {
- AsyncResult request = blockedSend.request;
- if (request != null && !request.isComplete()) {
- request.onFailure(error);
- }
+ send.onFailure(error);
} catch (Exception e) {
- LOG.debug("Caught exception when failing blocked send during remote producer closure: {}", blockedSend, e);
+ LOG.debug("Caught exception when failing blocked send during remote producer closure: {}", send, e);
}
}
+ }
- blocked.clear();
+ @Override
+ public void addSendCompletionWatcher(AsyncResult watcher) {
+ // If none pending signal done already.
+ // TODO - If we don't include blocked sends then update this.
+ if (blocked.isEmpty() && sent.isEmpty()) {
+ watcher.onSuccess();
+ } else {
+ this.sendCompletionWatcher = watcher;
+ }
}
//----- Class used to manage held sends ----------------------------------//
- private class InFlightSend implements AsyncResult {
+ private class InFlightSend implements AsyncResult, AmqpExceptionBuilder {
- public final JmsOutboundMessageDispatch envelope;
- public final AsyncResult request;
+ private final JmsOutboundMessageDispatch envelope;
+ private final AsyncResult request;
- public ScheduledFuture<?> requestTimeout;
+ private Delivery delivery;
+ private ScheduledFuture<?> requestTimeout;
public InFlightSend(JmsOutboundMessageDispatch envelope, AsyncResult request) {
this.envelope = envelope;
@@ -359,31 +356,95 @@ public class AmqpFixedProducer extends AmqpProducer {
@Override
public void onFailure(Throwable cause) {
- if (requestTimeout != null) {
- requestTimeout.cancel(false);
- requestTimeout = null;
- }
-
- blocked.remove(this);
+ handleSendCompletion(false);
- request.onFailure(cause);
+ if (request.isComplete()) {
+ // Asynchronous sends can still be awaiting a completion in which case we
+ // send to them otherwise send to the listener to be reported.
+ if (envelope.isCompletionRequired()) {
+ getParent().getProvider().getProviderListener().onFailedMessageSend(envelope, cause);
+ } else {
+ getParent().getProvider().fireNonFatalProviderException(IOExceptionSupport.create(cause));
+ }
+ } else {
+ request.onFailure(cause);
+ }
}
@Override
public void onSuccess() {
- if (requestTimeout != null) {
- requestTimeout.cancel(false);
- requestTimeout = null;
+ handleSendCompletion(true);
+
+ if (!request.isComplete()) {
+ request.onSuccess();
}
- blocked.remove(this);
+ if (envelope.isCompletionRequired()) {
+ getParent().getProvider().getProviderListener().onCompletedMessageSend(envelope);
+ }
+ }
- request.onSuccess();
+ public void setRequestTimeout(ScheduledFuture<?> requestTimeout) {
+ if (this.requestTimeout != null) {
+ this.requestTimeout.cancel(false);
+ }
+
+ this.requestTimeout = requestTimeout;
+ }
+
+ public JmsOutboundMessageDispatch getEnvelope() {
+ return envelope;
+ }
+
+ public AsyncResult getOriginalRequest() {
+ return request;
+ }
+
+ public void setDelivery(Delivery delivery) {
+ this.delivery = delivery;
+ }
+
+ public Delivery getDelivery() {
+ return delivery;
}
@Override
public boolean isComplete() {
return request.isComplete();
}
+
+ private void handleSendCompletion(boolean successful) {
+ setRequestTimeout(null);
+
+ if (getDelivery() != null) {
+ sent.remove(envelope.getMessageId());
+ delivery.settle();
+ tagGenerator.returnTag(delivery.getTag());
+ } else {
+ blocked.remove(envelope.getMessageId());
+ }
+
+ // TODO - Should this take blocked sends into consideration.
+ // Signal the watcher that all pending sends have completed if one is registered
+ // and both the in-flight sends and blocked sends have completed.
+ if (sendCompletionWatcher != null && sent.isEmpty() && blocked.isEmpty()) {
+ sendCompletionWatcher.onSuccess();
+ }
+
+ // Once the pending sends queue is drained and all in-flight sends have been
+ // settled we can propagate the close request.
+ if (isAwaitingClose() && !isClosed() && blocked.isEmpty() && sent.isEmpty()) {
+ AmqpFixedProducer.super.close(closeRequest);
+ }
+ }
+
+ @Override
+ public Exception createException() {
+ if (delivery == null) {
+ return new JmsSendTimedOutException("Timed out waiting for credit to send Message", envelope.getMessage());
+ } else {
+ return new JmsSendTimedOutException("Timed out waiting for disposition of sent Message", envelope.getMessage());
+ }
+ }
}
}
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/3a03663b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpProducer.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpProducer.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpProducer.java
index 74fe457..3ace6d2 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpProducer.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpProducer.java
@@ -54,13 +54,10 @@ public abstract class AmqpProducer extends AmqpAbstractResource<JmsProducerInfo,
* @param request
* The AsyncRequest that will be notified on send success or failure.
*
- * @return true if the producer had credit to send or false if there was no available
- * credit and the send needed to be deferred.
- *
* @throws IOException if an error occurs sending the message
* @throws JMSException if an error occurs while preparing the message for send.
*/
- public abstract boolean send(JmsOutboundMessageDispatch envelope, AsyncResult request) throws IOException, JMSException;
+ public abstract void send(JmsOutboundMessageDispatch envelope, AsyncResult request) throws IOException, JMSException;
/**
* @return true if this is an anonymous producer or false if fixed to a given destination.
@@ -92,4 +89,14 @@ public abstract class AmqpProducer extends AmqpAbstractResource<JmsProducerInfo,
public void setPresettle(boolean presettle) {
this.presettle = presettle;
}
+
+ /**
+ * Allows a completion request to be added to this producer that will be notified
+ * once all outstanding sends have completed.
+ *
+ * @param watcher
+ * The AsyncResult that will be signaled once this producer has no pending sends.
+ */
+ public abstract void addSendCompletionWatcher(AsyncResult watcher);
+
}
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/3a03663b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpProvider.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpProvider.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpProvider.java
index 3bfe099..ca8a0e1 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpProvider.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpProvider.java
@@ -59,6 +59,7 @@ import org.apache.qpid.jms.transports.TransportListener;
import org.apache.qpid.jms.util.IOExceptionSupport;
import org.apache.qpid.proton.engine.Collector;
import org.apache.qpid.proton.engine.Connection;
+import org.apache.qpid.proton.engine.Delivery;
import org.apache.qpid.proton.engine.EndpointState;
import org.apache.qpid.proton.engine.Event;
import org.apache.qpid.proton.engine.Event.Type;
@@ -477,11 +478,7 @@ public class AmqpProvider implements Provider, TransportListener , AmqpResourceP
producer = session.getProducer(producerId);
}
- boolean couldSend = producer.send(envelope, request);
- pumpToProtonTransport(request);
- if (couldSend && envelope.isSendAsync()) {
- request.onSuccess();
- }
+ producer.send(envelope, request);
} catch (Throwable t) {
request.onFailure(t);
}
@@ -816,7 +813,7 @@ public class AmqpProvider implements Provider, TransportListener , AmqpResourceP
case DELIVERY:
amqpEventSink = (AmqpEventSink) protonEvent.getLink().getContext();
if (amqpEventSink != null) {
- amqpEventSink.processDeliveryUpdates(this);
+ amqpEventSink.processDeliveryUpdates(this, (Delivery) protonEvent.getContext());
}
break;
default:
@@ -1175,6 +1172,36 @@ public class AmqpProvider implements Provider, TransportListener , AmqpResourceP
return null;
}
+ /**
+ * Allows a resource to request that its parent resource schedule a future
+ * cancellation of a request and return it a {@link Future} instance that
+ * can be used to cancel the scheduled automatic failure of the request.
+ *
+ * @param request
+ * The request that should be marked as failed based on configuration.
+ * @param timeout
+ * The time to wait before marking the request as failed.
+ * @param builder
+ * An AmqpExceptionBuilder to use when creating a timed out exception.
+ *
+ * @return a {@link ScheduledFuture} that can be stored by the caller.
+ */
+ public ScheduledFuture<?> scheduleRequestTimeout(final AsyncResult request, long timeout, final AmqpExceptionBuilder builder) {
+ if (timeout != JmsConnectionInfo.INFINITE) {
+ return serializer.schedule(new Runnable() {
+
+ @Override
+ public void run() {
+ request.onFailure(builder.createException());
+ pumpToProtonTransport();
+ }
+
+ }, timeout, TimeUnit.MILLISECONDS);
+ }
+
+ return null;
+ }
+
//----- Internal implementation ------------------------------------------//
private void checkClosed() throws ProviderClosedException {
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/3a03663b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpTransactionContext.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpTransactionContext.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpTransactionContext.java
index 577c128..2d08bc7 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpTransactionContext.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpTransactionContext.java
@@ -44,6 +44,7 @@ public class AmqpTransactionContext implements AmqpResourceParent {
private final AmqpSession session;
private final Set<AmqpConsumer> txConsumers = new LinkedHashSet<AmqpConsumer>();
+ private final Set<AmqpProducer> txProducers = new LinkedHashSet<AmqpProducer>();
private JmsTransactionId current;
private AmqpTransactionCoordinator coordinator;
@@ -85,7 +86,6 @@ public class AmqpTransactionContext implements AmqpResourceParent {
}
};
-
if (coordinator == null || coordinator.isClosed()) {
AmqpTransactionCoordinatorBuilder builder =
new AmqpTransactionCoordinatorBuilder(this, session.getResourceInfo());
@@ -115,7 +115,7 @@ public class AmqpTransactionContext implements AmqpResourceParent {
}
}
- public void commit(JmsTransactionInfo transactionInfo, final AsyncResult request) throws Exception {
+ public void commit(final JmsTransactionInfo transactionInfo, final AsyncResult request) throws Exception {
if (!transactionInfo.getId().equals(current)) {
if (!transactionInfo.isInDoubt() && current == null) {
throw new IllegalStateException("Commit called with no active Transaction.");
@@ -128,29 +128,10 @@ public class AmqpTransactionContext implements AmqpResourceParent {
preCommit();
- LOG.trace("TX Context[{}] committing current TX[[]]", this, current);
- coordinator.discharge(current, new AsyncResult() {
-
- @Override
- public void onSuccess() {
- current = null;
- postCommit();
- request.onSuccess();
- }
-
- @Override
- public void onFailure(Throwable result) {
- current = null;
- postCommit();
- request.onFailure(result);
- }
-
- @Override
- public boolean isComplete() {
- return current == null;
- }
+ DischargeCompletion dischargeResult = new DischargeCompletion(request, true);
- }, true);
+ LOG.trace("TX Context[{}] committing current TX[[]]", this, current);
+ coordinator.discharge(current, dischargeResult, true);
}
public void rollback(JmsTransactionInfo transactionInfo, final AsyncResult request) throws Exception {
@@ -167,29 +148,17 @@ public class AmqpTransactionContext implements AmqpResourceParent {
preRollback();
- LOG.trace("TX Context[{}] rolling back current TX[[]]", this, current);
- coordinator.discharge(current, new AsyncResult() {
-
- @Override
- public void onSuccess() {
- current = null;
- postRollback();
- request.onSuccess();
- }
-
- @Override
- public void onFailure(Throwable result) {
- current = null;
- postRollback();
- request.onFailure(result);
- }
+ DischargeCompletion dischargeResult = new DischargeCompletion(request, false);
- @Override
- public boolean isComplete() {
- return current == null;
+ if (txProducers.isEmpty()) {
+ LOG.trace("TX Context[{}] rolling back current TX[[]]", this, current);
+ coordinator.discharge(current, dischargeResult, false);
+ } else {
+ SendCompletion producersSendCompletion = new SendCompletion(transactionInfo, dischargeResult, txProducers.size(), false);
+ for (AmqpProducer producer : txProducers) {
+ producer.addSendCompletionWatcher(producersSendCompletion);
}
-
- }, false);
+ }
}
//----- Context utility methods ------------------------------------------//
@@ -198,6 +167,10 @@ public class AmqpTransactionContext implements AmqpResourceParent {
txConsumers.add(consumer);
}
+ public void registerTxProducer(AmqpProducer producer) {
+ txProducers.add(producer);
+ }
+
public AmqpSession getSession() {
return session;
}
@@ -243,6 +216,7 @@ public class AmqpTransactionContext implements AmqpResourceParent {
}
txConsumers.clear();
+ txProducers.clear();
}
private void postRollback() {
@@ -251,6 +225,7 @@ public class AmqpTransactionContext implements AmqpResourceParent {
}
txConsumers.clear();
+ txProducers.clear();
}
//----- Resource Parent implementation -----------------------------------//
@@ -273,4 +248,94 @@ public class AmqpTransactionContext implements AmqpResourceParent {
public AmqpProvider getProvider() {
return session.getProvider();
}
+
+ //----- Completion for Commit or Rollback operation ----------------------//
+
+ private class DischargeCompletion implements AsyncResult {
+
+ private final AsyncResult request;
+ private final boolean commit;
+
+ public DischargeCompletion(AsyncResult request, boolean commit) {
+ this.request = request;
+ this.commit = commit;
+ }
+
+ @Override
+ public void onFailure(Throwable result) {
+ cleanup();
+ request.onFailure(result);
+ }
+
+ @Override
+ public void onSuccess() {
+ cleanup();
+ request.onSuccess();
+ }
+
+ @Override
+ public boolean isComplete() {
+ return request.isComplete();
+ }
+
+ private void cleanup() {
+ current = null;
+ if (commit) {
+ postCommit();
+ } else {
+ postRollback();
+ }
+ }
+ }
+
+ //----- Completion result for Producers ----------------------------------//
+
+ @SuppressWarnings("unused")
+ private class SendCompletion implements AsyncResult {
+
+ private int pendingCompletions;
+
+ private final JmsTransactionInfo info;
+ private final DischargeCompletion request;
+
+ private boolean commit;
+
+ public SendCompletion(JmsTransactionInfo info, DischargeCompletion request, int pendingCompletions, boolean commit) {
+ this.info = info;
+ this.request = request;
+ this.pendingCompletions = pendingCompletions;
+ this.commit = commit;
+ }
+
+ @Override
+ public void onFailure(Throwable result) {
+ if (--pendingCompletions == 0) {
+ try {
+ LOG.trace("TX Context[{}] rolling back current TX[[]]", this, current);
+ coordinator.discharge(current, request, false);
+ } catch (Throwable error) {
+ request.onFailure(error);
+ }
+ } else {
+ commit = false;
+ }
+ }
+
+ @Override
+ public void onSuccess() {
+ if (--pendingCompletions == 0) {
+ try {
+ LOG.trace("TX Context[{}] {} current TX[[]]", this, commit ? "committing" : "rolling back" ,current);
+ coordinator.discharge(current, request, commit);
+ } catch (Throwable error) {
+ request.onFailure(error);
+ }
+ }
+ }
+
+ @Override
+ public boolean isComplete() {
+ return request.isComplete();
+ }
+ }
}
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org
[2/7] qpid-jms git commit: QPIDJMS-207 Adds support for Asynchronous
JMS 2.0 sends.
Posted by ta...@apache.org.
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/3a03663b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpTransactionCoordinator.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpTransactionCoordinator.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpTransactionCoordinator.java
index 2fa2644..d18e95b 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpTransactionCoordinator.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpTransactionCoordinator.java
@@ -67,7 +67,7 @@ public class AmqpTransactionCoordinator extends AmqpAbstractResource<JmsSessionI
}
@Override
- public void processDeliveryUpdates(AmqpProvider provider) throws IOException {
+ public void processDeliveryUpdates(AmqpProvider provider, Delivery delivery) throws IOException {
try {
if (pendingDelivery != null && pendingDelivery.remotelySettled()) {
DeliveryState state = pendingDelivery.getRemoteState();
@@ -105,7 +105,7 @@ public class AmqpTransactionCoordinator extends AmqpAbstractResource<JmsSessionI
}
}
- super.processDeliveryUpdates(provider);
+ super.processDeliveryUpdates(provider, delivery);
} catch (Exception e) {
throw IOExceptionSupport.create(e);
}
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/3a03663b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/builders/AmqpResourceBuilder.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/builders/AmqpResourceBuilder.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/builders/AmqpResourceBuilder.java
index 229e61f..5912f7f 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/builders/AmqpResourceBuilder.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/builders/AmqpResourceBuilder.java
@@ -24,10 +24,12 @@ import org.apache.qpid.jms.meta.JmsConnectionInfo;
import org.apache.qpid.jms.meta.JmsResource;
import org.apache.qpid.jms.provider.AsyncResult;
import org.apache.qpid.jms.provider.amqp.AmqpEventSink;
+import org.apache.qpid.jms.provider.amqp.AmqpExceptionBuilder;
import org.apache.qpid.jms.provider.amqp.AmqpProvider;
import org.apache.qpid.jms.provider.amqp.AmqpResource;
import org.apache.qpid.jms.provider.amqp.AmqpResourceParent;
import org.apache.qpid.jms.provider.amqp.AmqpSupport;
+import org.apache.qpid.proton.engine.Delivery;
import org.apache.qpid.proton.engine.Endpoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -40,7 +42,7 @@ import org.slf4j.LoggerFactory;
* @param <INFO> The Type of JmsResource used to describe the target resource.
* @param <ENDPOINT> The AMQP Endpoint that the target resource encapsulates.
*/
-public abstract class AmqpResourceBuilder<TARGET extends AmqpResource, PARENT extends AmqpResourceParent, INFO extends JmsResource, ENDPOINT extends Endpoint> implements AmqpEventSink {
+public abstract class AmqpResourceBuilder<TARGET extends AmqpResource, PARENT extends AmqpResourceParent, INFO extends JmsResource, ENDPOINT extends Endpoint> implements AmqpEventSink, AmqpExceptionBuilder {
private static final Logger LOG = LoggerFactory.getLogger(AmqpResourceBuilder.class);
@@ -97,7 +99,7 @@ public abstract class AmqpResourceBuilder<TARGET extends AmqpResource, PARENT ex
return request.isComplete();
}
- }, getRequestTimeout(), new JmsOperationTimedOutException("Request to open resource " + getResource() + " timed out"));
+ }, getRequestTimeout(), this);
}
}
@@ -119,7 +121,7 @@ public abstract class AmqpResourceBuilder<TARGET extends AmqpResource, PARENT ex
}
@Override
- public void processDeliveryUpdates(AmqpProvider provider) throws IOException {
+ public void processDeliveryUpdates(AmqpProvider provider, Delivery delivery) throws IOException {
// No implementation needed here for this event.
}
@@ -185,6 +187,11 @@ public abstract class AmqpResourceBuilder<TARGET extends AmqpResource, PARENT ex
getRequest().onFailure(openError);
}
+ @Override
+ public Exception createException() {
+ return new JmsOperationTimedOutException("Request to open resource " + getResource() + " timed out");
+ }
+
//----- Implementation methods used to customize the build process -------//
/**
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/3a03663b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/failover/FailoverProvider.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/failover/FailoverProvider.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/failover/FailoverProvider.java
index e6e36df..2490b19 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/failover/FailoverProvider.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/failover/FailoverProvider.java
@@ -801,6 +801,24 @@ public class FailoverProvider extends DefaultProviderListener implements Provide
}
@Override
+ public void onCompletedMessageSend(final JmsOutboundMessageDispatch envelope) {
+ if (closingConnection.get() || closed.get() || failed.get()) {
+ return;
+ }
+
+ listener.onCompletedMessageSend(envelope);
+ }
+
+ @Override
+ public void onFailedMessageSend(final JmsOutboundMessageDispatch envelope, Throwable cause) {
+ if (closingConnection.get() || closed.get() || failed.get()) {
+ return;
+ }
+
+ listener.onFailedMessageSend(envelope, cause);
+ }
+
+ @Override
public void onConnectionFailure(final IOException ex) {
if (closingConnection.get() || closed.get() || failed.get()) {
return;
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/3a03663b/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/ConsumerIntegrationTest.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/ConsumerIntegrationTest.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/ConsumerIntegrationTest.java
index c8914ae..a8fcf2d 100644
--- a/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/ConsumerIntegrationTest.java
+++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/ConsumerIntegrationTest.java
@@ -29,6 +29,7 @@ import java.io.IOException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
import javax.jms.Connection;
import javax.jms.IllegalStateException;
@@ -932,4 +933,112 @@ public class ConsumerIntegrationTest extends QpidJmsTestCase {
testPeer.waitForAllHandlersToComplete(3000);
}
}
+
+ @Test(timeout=20000)
+ public void testMessageListenerCallsConnectionCloseThrowsIllegalStateException() throws Exception {
+ final CountDownLatch latch = new CountDownLatch(1);
+ final AtomicReference<Exception> asyncError = new AtomicReference<Exception>(null);
+
+ try (TestAmqpPeer testPeer = new TestAmqpPeer();) {
+ final Connection connection = testFixture.establishConnecton(testPeer);
+ connection.start();
+
+ testPeer.expectBegin();
+
+ Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ Queue destination = session.createQueue(getTestName());
+ connection.start();
+
+ testPeer.expectReceiverAttach();
+ testPeer.expectLinkFlowRespondWithTransfer(null, null, null, null, new AmqpValueDescribedType("content"), 1);
+
+ MessageConsumer consumer = session.createConsumer(destination);
+
+ testPeer.expectDisposition(true, new AcceptedMatcher());
+
+ consumer.setMessageListener(new MessageListener() {
+ @Override
+ public void onMessage(Message m) {
+ try {
+ LOG.debug("Async consumer got Message: {}", m);
+ connection.close();
+ } catch (Exception ex) {
+ asyncError.set(ex);
+ }
+
+ latch.countDown();
+ }
+ });
+
+ boolean await = latch.await(3000, TimeUnit.MILLISECONDS);
+ assertTrue("Messages not received within given timeout. Count remaining: " + latch.getCount(), await);
+
+ assertNotNull(asyncError.get());
+ assertTrue(asyncError.get() instanceof IllegalStateException);
+
+ testPeer.waitForAllHandlersToComplete(2000);
+
+ testPeer.expectDetach(true, true, true);
+ consumer.close();
+
+ testPeer.expectClose();
+ connection.close();
+
+ testPeer.waitForAllHandlersToComplete(2000);
+ }
+ }
+
+ @Test(timeout=20000)
+ public void testMessageListenerCallsSessionCloseThrowsIllegalStateException() throws Exception {
+ final CountDownLatch latch = new CountDownLatch(1);
+ final AtomicReference<Exception> asyncError = new AtomicReference<Exception>(null);
+
+ try (TestAmqpPeer testPeer = new TestAmqpPeer();) {
+ Connection connection = testFixture.establishConnecton(testPeer);
+ connection.start();
+
+ testPeer.expectBegin();
+
+ final Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ Queue destination = session.createQueue(getTestName());
+ connection.start();
+
+ testPeer.expectReceiverAttach();
+ testPeer.expectLinkFlowRespondWithTransfer(null, null, null, null, new AmqpValueDescribedType("content"), 1);
+
+ MessageConsumer consumer = session.createConsumer(destination);
+
+ testPeer.expectDisposition(true, new AcceptedMatcher());
+
+ consumer.setMessageListener(new MessageListener() {
+ @Override
+ public void onMessage(Message m) {
+ try {
+ LOG.debug("Async consumer got Message: {}", m);
+ session.close();
+ } catch (Exception ex) {
+ asyncError.set(ex);
+ }
+
+ latch.countDown();
+ }
+ });
+
+ boolean await = latch.await(3000, TimeUnit.MILLISECONDS);
+ assertTrue("Messages not received within given timeout. Count remaining: " + latch.getCount(), await);
+
+ assertNotNull(asyncError.get());
+ assertTrue(asyncError.get() instanceof IllegalStateException);
+
+ testPeer.waitForAllHandlersToComplete(2000);
+
+ testPeer.expectDetach(true, true, true);
+ consumer.close();
+
+ testPeer.expectClose();
+ connection.close();
+
+ testPeer.waitForAllHandlersToComplete(2000);
+ }
+ }
}
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/3a03663b/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/PresettledProducerIntegrationTest.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/PresettledProducerIntegrationTest.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/PresettledProducerIntegrationTest.java
index 5485857..2ed9f41 100644
--- a/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/PresettledProducerIntegrationTest.java
+++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/PresettledProducerIntegrationTest.java
@@ -20,8 +20,15 @@ import static org.apache.qpid.jms.provider.amqp.AmqpSupport.ANONYMOUS_RELAY;
import static org.hamcrest.Matchers.arrayContaining;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.nullValue;
+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 static org.junit.Assert.fail;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
import javax.jms.Connection;
import javax.jms.Destination;
import javax.jms.Message;
@@ -30,8 +37,11 @@ import javax.jms.Queue;
import javax.jms.Session;
import javax.jms.TemporaryQueue;
import javax.jms.TemporaryTopic;
+import javax.jms.TextMessage;
import javax.jms.Topic;
+import org.apache.qpid.jms.JmsCompletionListener;
+import org.apache.qpid.jms.JmsMessageProducer;
import org.apache.qpid.jms.test.QpidJmsTestCase;
import org.apache.qpid.jms.test.testpeer.ListDescribedType;
import org.apache.qpid.jms.test.testpeer.TestAmqpPeer;
@@ -47,12 +57,16 @@ import org.apache.qpid.proton.amqp.Symbol;
import org.apache.qpid.proton.amqp.transaction.TxnCapability;
import org.hamcrest.Matcher;
import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
* Test MessageProducers created using various configuration of the presettle options
*/
public class PresettledProducerIntegrationTest extends QpidJmsTestCase {
+ private static final Logger LOG = LoggerFactory.getLogger(PresettledProducerIntegrationTest.class);
+
private final IntegrationTestFixture testFixture = new IntegrationTestFixture();
private final Symbol[] serverCapabilities = new Symbol[] { ANONYMOUS_RELAY };
@@ -419,4 +433,221 @@ public class PresettledProducerIntegrationTest extends QpidJmsTestCase {
testPeer.waitForAllHandlersToComplete(1000);
}
}
+
+ //----- Test the jms.presettleAll with asynchronous completion -----------//
+
+ @Test(timeout = 20000)
+ public void testAsyncCompletionPresettleAllSendToTopic() throws Exception {
+ String presettleConfig = "?jms.presettlePolicy.presettleAll=true";
+ doTestAsyncCompletionProducerWithPresettleOptions(presettleConfig, false, false, true, true, Topic.class);
+ }
+
+ @Test(timeout = 20000)
+ public void testAsyncCompletionPresettleAllSendToQueue() throws Exception {
+ String presettleConfig = "?jms.presettlePolicy.presettleAll=true";
+ doTestAsyncCompletionProducerWithPresettleOptions(presettleConfig, false, false, true, true, Queue.class);
+ }
+
+ @Test(timeout = 20000)
+ public void testsyncCompletionPresettleAllAnonymousSendToTopic() throws Exception {
+ String presettleConfig = "?jms.presettlePolicy.presettleAll=true";
+ doTestAsyncCompletionProducerWithPresettleOptions(presettleConfig, false, true, true, true, Topic.class);
+ }
+
+ @Test(timeout = 20000)
+ public void testsyncCompletionPresettleAllAnonymousSendToQueue() throws Exception {
+ String presettleConfig = "?jms.presettlePolicy.presettleAll=true";
+ doTestAsyncCompletionProducerWithPresettleOptions(presettleConfig, false, true, true, true, Queue.class);
+ }
+
+ //----- Test the jms.presettleProducers with asynchronous completion -----//
+
+ @Test(timeout = 20000)
+ public void testAsyncCompletionPresettleProducersTopic() throws Exception {
+ String presettleConfig = "?jms.presettlePolicy.presettleProducers=true";
+ doTestAsyncCompletionProducerWithPresettleOptions(presettleConfig, false, false, true, true, Topic.class);
+ }
+
+ @Test(timeout = 20000)
+ public void testAsyncCompletionPresettleProducersQueue() throws Exception {
+ String presettleConfig = "?jms.presettlePolicy.presettleProducers=true";
+ doTestAsyncCompletionProducerWithPresettleOptions(presettleConfig, false, false, true, true, Queue.class);
+ }
+
+ @Test(timeout = 20000)
+ public void testAsyncCompletionPresettleProducersAnonymousTopic() throws Exception {
+ String presettleConfig = "?jms.presettlePolicy.presettleProducers=true";
+ doTestAsyncCompletionProducerWithPresettleOptions(presettleConfig, false, true, true, true, Topic.class);
+ }
+
+ @Test(timeout = 20000)
+ public void testAsyncCompletionPresettleProducersAnonymousQueue() throws Exception {
+ String presettleConfig = "?jms.presettlePolicy.presettleProducers=true";
+ doTestAsyncCompletionProducerWithPresettleOptions(presettleConfig, false, true, true, true, Queue.class);
+ }
+
+ //----- Asynchronous Completion test method implementation ---------------//
+
+ private void doTestAsyncCompletionProducerWithPresettleOptions(String uriOptions, boolean transacted, boolean anonymous, boolean senderSettled, boolean transferSettled, Class<? extends Destination> destType) throws Exception {
+ doTestAsyncCompletionProducerWithPresettleOptions(uriOptions, transacted, anonymous, true, senderSettled, transferSettled, destType);
+ }
+
+ private void doTestAsyncCompletionProducerWithPresettleOptions(String uriOptions, boolean transacted, boolean anonymous, boolean relaySupported, boolean senderSettled, boolean transferSettled, Class<? extends Destination> destType) throws Exception {
+ try (TestAmqpPeer testPeer = new TestAmqpPeer();) {
+ Connection connection = testFixture.establishConnecton(testPeer, uriOptions, relaySupported ? serverCapabilities : null, null);
+ testPeer.expectBegin();
+
+ Session session = null;
+ Binary txnId = null;
+
+ if (transacted) {
+ // Expect the session, with an immediate link to the transaction coordinator
+ // using a target with the expected capabilities only.
+ CoordinatorMatcher txCoordinatorMatcher = new CoordinatorMatcher();
+ txCoordinatorMatcher.withCapabilities(arrayContaining(TxnCapability.LOCAL_TXN));
+ testPeer.expectSenderAttach(txCoordinatorMatcher, false, false);
+
+ // First expect an unsettled 'declare' transfer to the txn coordinator, and
+ // reply with a declared disposition state containing the txnId.
+ txnId = new Binary(new byte[]{ (byte) 1, (byte) 2, (byte) 3, (byte) 4});
+ testPeer.expectDeclare(txnId);
+
+ session = connection.createSession(true, Session.SESSION_TRANSACTED);
+ } else {
+ session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ }
+
+ Destination destination = null;
+ if (destType == Queue.class) {
+ destination = session.createQueue("MyQueue");
+ } else if (destType == Topic.class) {
+ destination = session.createTopic("MyTopis");
+ } else if (destType == TemporaryQueue.class) {
+ String dynamicAddress = "myTempQueueAddress";
+ testPeer.expectTempQueueCreationAttach(dynamicAddress);
+ destination = session.createTemporaryQueue();
+ } else if (destType == TemporaryTopic.class) {
+ String dynamicAddress = "myTempTopicAddress";
+ testPeer.expectTempTopicCreationAttach(dynamicAddress);
+ destination = session.createTemporaryTopic();
+ } else {
+ fail("unexpected type");
+ }
+
+ if (senderSettled) {
+ testPeer.expectSettledSenderAttach();
+ } else {
+ testPeer.expectSenderAttach();
+ }
+
+ TestJmsCompletionListener listener = new TestJmsCompletionListener();
+ // TODO Can change to plain MessageProducer when JMS 2.0 API dependency is added.
+ JmsMessageProducer producer = null;
+ if (anonymous) {
+ producer = (JmsMessageProducer) session.createProducer(null);
+ } else {
+ producer = (JmsMessageProducer) session.createProducer(destination);
+ }
+
+ // Create and transfer a new message
+ MessageHeaderSectionMatcher headersMatcher = new MessageHeaderSectionMatcher(true);
+ headersMatcher.withDurable(equalTo(true));
+ MessageAnnotationsSectionMatcher msgAnnotationsMatcher = new MessageAnnotationsSectionMatcher(true);
+ TransferPayloadCompositeMatcher messageMatcher = new TransferPayloadCompositeMatcher();
+ messageMatcher.setHeadersMatcher(headersMatcher);
+ messageMatcher.setMessageAnnotationsMatcher(msgAnnotationsMatcher);
+
+ Matcher<?> stateMatcher = nullValue();
+ if (transacted) {
+ stateMatcher = new TransactionalStateMatcher();
+ ((TransactionalStateMatcher) stateMatcher).withTxnId(equalTo(txnId));
+ ((TransactionalStateMatcher) stateMatcher).withOutcome(nullValue());
+ }
+
+ ListDescribedType responseState = new Accepted();
+ if (transacted) {
+ TransactionalState txState = new TransactionalState();
+ txState.setTxnId(txnId);
+ txState.setOutcome(new Accepted());
+ }
+
+ if (transferSettled) {
+ testPeer.expectTransfer(messageMatcher, stateMatcher, true, false, responseState, false);
+ } else {
+ testPeer.expectTransfer(messageMatcher, stateMatcher, false, true, responseState, true);
+ }
+
+ if (anonymous && !relaySupported) {
+ testPeer.expectDetach(true, true, true);
+ }
+
+ Message message = session.createTextMessage();
+
+ if (anonymous) {
+ producer.send(destination, message, listener);
+ } else {
+ producer.send(message, listener);
+ }
+
+ if (transacted) {
+ testPeer.expectDischarge(txnId, true);
+ }
+
+ testPeer.expectClose();
+
+ assertTrue("Did not get async callback", listener.awaitCompletion(2000, TimeUnit.SECONDS));
+ assertNull(listener.exception);
+ assertNotNull(listener.message);
+ assertTrue(listener.message instanceof TextMessage);
+ assertEquals(1, listener.successCount);
+ assertEquals(0, listener.errorCount);
+
+ connection.close();
+
+ testPeer.waitForAllHandlersToComplete(1000);
+ }
+ }
+
+ private class TestJmsCompletionListener implements JmsCompletionListener {
+
+ private final CountDownLatch completed;
+
+ public volatile int successCount;
+ public volatile int errorCount;
+
+ public volatile Message message;
+ public volatile Exception exception;
+
+ public TestJmsCompletionListener() {
+ this(1);
+ }
+
+ public TestJmsCompletionListener(int expected) {
+ this.completed = new CountDownLatch(expected);
+ }
+
+ public boolean awaitCompletion(long timeout, TimeUnit units) throws InterruptedException {
+ return completed.await(timeout, units);
+ }
+
+ @Override
+ public void onCompletion(Message message) {
+ LOG.info("JmsCompletionListener onCompletion called with message: {}", message);
+ this.message = message;
+ this.successCount++;
+
+ completed.countDown();
+ }
+
+ @Override
+ public void onException(Message message, Exception exception) {
+ LOG.info("JmsCompletionListener onException called with message: {} error {}", message, exception);
+
+ this.message = message;
+ this.exception = exception;
+ this.errorCount++;
+
+ completed.countDown();
+ }
+ }
}
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/3a03663b/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/ProducerIntegrationTest.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/ProducerIntegrationTest.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/ProducerIntegrationTest.java
index 6c53398..1e69eb8 100644
--- a/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/ProducerIntegrationTest.java
+++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/ProducerIntegrationTest.java
@@ -40,7 +40,10 @@ import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+import javax.jms.BytesMessage;
import javax.jms.Connection;
import javax.jms.DeliveryMode;
import javax.jms.ExceptionListener;
@@ -54,9 +57,11 @@ import javax.jms.Session;
import javax.jms.TextMessage;
import javax.jms.Topic;
+import org.apache.qpid.jms.JmsCompletionListener;
import org.apache.qpid.jms.JmsConnection;
import org.apache.qpid.jms.JmsConnectionFactory;
import org.apache.qpid.jms.JmsDefaultConnectionListener;
+import org.apache.qpid.jms.JmsMessageProducer;
import org.apache.qpid.jms.JmsOperationTimedOutException;
import org.apache.qpid.jms.JmsSendTimedOutException;
import org.apache.qpid.jms.message.foreign.ForeignJmsMessage;
@@ -68,10 +73,13 @@ import org.apache.qpid.jms.test.testpeer.ListDescribedType;
import org.apache.qpid.jms.test.testpeer.TestAmqpPeer;
import org.apache.qpid.jms.test.testpeer.basictypes.AmqpError;
import org.apache.qpid.jms.test.testpeer.basictypes.TerminusDurability;
+import org.apache.qpid.jms.test.testpeer.describedtypes.Accepted;
import org.apache.qpid.jms.test.testpeer.describedtypes.Modified;
import org.apache.qpid.jms.test.testpeer.describedtypes.Rejected;
import org.apache.qpid.jms.test.testpeer.describedtypes.Released;
+import org.apache.qpid.jms.test.testpeer.describedtypes.TransactionalState;
import org.apache.qpid.jms.test.testpeer.matchers.TargetMatcher;
+import org.apache.qpid.jms.test.testpeer.matchers.TransactionalStateMatcher;
import org.apache.qpid.jms.test.testpeer.matchers.sections.MessageAnnotationsSectionMatcher;
import org.apache.qpid.jms.test.testpeer.matchers.sections.MessageHeaderSectionMatcher;
import org.apache.qpid.jms.test.testpeer.matchers.sections.MessagePropertiesSectionMatcher;
@@ -1000,6 +1008,79 @@ public class ProducerIntegrationTest extends QpidJmsTestCase {
}
@Test(timeout = 20000)
+ public void testRemotelyEndProducerCompletesAsyncSends() throws Exception {
+ final String BREAD_CRUMB = "ErrorMessage";
+
+ try (TestAmqpPeer testPeer = new TestAmqpPeer();) {
+ final AtomicBoolean producerClosed = new AtomicBoolean();
+ JmsConnection connection = (JmsConnection) testFixture.establishConnecton(testPeer);
+ connection.addConnectionListener(new JmsDefaultConnectionListener() {
+ @Override
+ public void onProducerClosed(MessageProducer producer, Exception exception) {
+ producerClosed.set(true);
+ }
+ });
+
+ testPeer.expectBegin();
+ Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ // Create a producer, then remotely end the session afterwards.
+ testPeer.expectSenderAttach();
+
+ Queue queue = session.createQueue("myQueue");
+ // TODO - Can revert to just MessageProducer once JMS 2.0 API is used
+ final JmsMessageProducer producer = (JmsMessageProducer) session.createProducer(queue);
+
+ Message message = session.createTextMessage("content");
+
+ final int MSG_COUNT = 3;
+
+ for (int i = 0; i < MSG_COUNT; ++i) {
+ testPeer.expectTransferButDoNotRespond(new TransferPayloadCompositeMatcher());
+ }
+
+ testPeer.remotelyDetachLastOpenedLinkOnLastOpenedSession(true, true, AmqpError.RESOURCE_LIMIT_EXCEEDED, BREAD_CRUMB, 50);
+
+ TestJmsCompletionListener listener = new TestJmsCompletionListener(MSG_COUNT);
+ try {
+ for (int i = 0; i < MSG_COUNT; ++i) {
+ producer.send(message, listener);
+ }
+ } catch (JMSException e) {
+ LOG.warn("Caught unexpected error: {}", e.getMessage());
+ fail("No expected exception for this send.");
+ }
+
+ testPeer.waitForAllHandlersToComplete(1000);
+
+ // Verify the producer gets marked closed
+ assertTrue(listener.awaitCompletion(2000, TimeUnit.SECONDS));
+ assertEquals(MSG_COUNT, listener.errorCount);
+
+ // Verify the session is now marked closed
+ try {
+ producer.getDeliveryMode();
+ fail("Expected ISE to be thrown due to being closed");
+ } catch (IllegalStateException jmsise) {
+ String errorMessage = jmsise.getCause().getMessage();
+ assertTrue(errorMessage.contains(AmqpError.RESOURCE_LIMIT_EXCEEDED.toString()));
+ assertTrue(errorMessage.contains(BREAD_CRUMB));
+ }
+
+ assertTrue("Producer closed callback didn't trigger", producerClosed.get());
+
+ // Try closing it explicitly, should effectively no-op in client.
+ // The test peer will throw during close if it sends anything.
+ producer.close();
+
+ testPeer.expectClose();
+ connection.close();
+
+ testPeer.waitForAllHandlersToComplete(1000);
+ }
+ }
+
+ @Test(timeout = 20000)
public void testRemotelyCloseConnectionDuringSyncSend() throws Exception {
final String BREAD_CRUMB = "ErrorMessageBreadCrumb";
@@ -1150,6 +1231,50 @@ public class ProducerIntegrationTest extends QpidJmsTestCase {
}
@Test(timeout = 20000)
+ public void testAsyncCompletionGetsTimedOutErrorWhenNoDispostionArrives() throws Exception {
+ try(TestAmqpPeer testPeer = new TestAmqpPeer();) {
+ JmsConnection connection = (JmsConnection) testFixture.establishConnecton(testPeer);
+ connection.setSendTimeout(500);
+
+ testPeer.expectBegin();
+
+ Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ String queueName = "myQueue";
+ Queue queue = session.createQueue(queueName);
+
+ Message message = session.createTextMessage("text");
+ TransferPayloadCompositeMatcher messageMatcher = new TransferPayloadCompositeMatcher();
+
+ // Expect the producer to attach and grant it some credit, it should send
+ // a transfer which we will not send any response for which should cause the
+ // send operation to time out.
+ testPeer.expectSenderAttach();
+ testPeer.expectTransferButDoNotRespond(messageMatcher);
+ testPeer.expectClose();
+
+ // TODO - Can revert to plain JMS once 2.0 is supported.
+ JmsMessageProducer producer = (JmsMessageProducer) session.createProducer(queue);
+ TestJmsCompletionListener listener = new TestJmsCompletionListener();
+
+ try {
+ producer.send(message, listener);
+ } catch (Throwable error) {
+ LOG.info("Caught expected error: {}", error.getMessage());
+ fail("Send should not fail for async.");
+ }
+
+ assertTrue("Did not get async callback", listener.awaitCompletion(2000, TimeUnit.SECONDS));
+ assertNotNull(listener.exception);
+ assertTrue(listener.exception instanceof JmsSendTimedOutException);
+ assertNotNull(listener.message);
+
+ connection.close();
+
+ testPeer.waitForAllHandlersToComplete(1000);
+ }
+ }
+
+ @Test(timeout = 20000)
public void testSyncSendMessageRejected() throws Exception {
doSyncSendMessageNotAcceptedTestImpl(new Rejected());
}
@@ -1709,6 +1834,430 @@ public class ProducerIntegrationTest extends QpidJmsTestCase {
}
@Test(timeout = 20000)
+ public void testAsyncCompletionAfterSendMessageGetDispoation() throws Exception {
+ try (TestAmqpPeer testPeer = new TestAmqpPeer();) {
+ Connection connection = testFixture.establishConnecton(testPeer);
+ testPeer.expectBegin();
+ testPeer.expectSenderAttach();
+
+ Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ Queue queue = session.createQueue("myQueue");
+
+ // TODO Can change to plain MessageProducer when JMS 2.0 API dependency is added.
+ JmsMessageProducer producer = (JmsMessageProducer) session.createProducer(queue);
+
+ // Create and transfer a new message
+ String text = "myMessage";
+ testPeer.expectTransfer(new TransferPayloadCompositeMatcher());
+ testPeer.expectClose();
+
+ TextMessage message = session.createTextMessage(text);
+ TestJmsCompletionListener listener = new TestJmsCompletionListener();
+
+ producer.send(message, listener);
+
+ assertTrue("Did not get async callback", listener.awaitCompletion(2000, TimeUnit.SECONDS));
+ assertNull(listener.exception);
+ assertNotNull(listener.message);
+ assertTrue(listener.message instanceof TextMessage);
+
+ connection.close();
+
+ testPeer.waitForAllHandlersToComplete(1000);
+ }
+ }
+
+ @Test(timeout = 20000)
+ public void testAsyncCompletionResetsBytesMessage() throws Exception {
+ try (TestAmqpPeer testPeer = new TestAmqpPeer();) {
+ Connection connection = testFixture.establishConnecton(testPeer);
+ testPeer.expectBegin();
+ testPeer.expectSenderAttach();
+
+ Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ Queue queue = session.createQueue("myQueue");
+
+ // TODO Can change to plain MessageProducer when JMS 2.0 API dependency is added.
+ JmsMessageProducer producer = (JmsMessageProducer) session.createProducer(queue);
+
+ // Create and transfer a new message
+ testPeer.expectTransfer(new TransferPayloadCompositeMatcher());
+ testPeer.expectClose();
+
+ Binary payload = new Binary(new byte[] {1, 2, 3, 4});
+ BytesMessage message = session.createBytesMessage();
+ message.writeBytes(payload.getArray());
+
+ TestJmsCompletionListener listener = new TestJmsCompletionListener();
+
+ producer.send(message, listener);
+
+ assertTrue("Did not get async callback", listener.awaitCompletion(2000, TimeUnit.SECONDS));
+ assertNull(listener.exception);
+ assertNotNull(listener.message);
+ assertTrue(listener.message instanceof BytesMessage);
+
+ BytesMessage completed = (BytesMessage) listener.message;
+ assertEquals(payload.getLength(), completed.getBodyLength());
+ byte[] data = new byte[payload.getLength()];
+ completed.readBytes(data);
+
+ connection.close();
+
+ testPeer.waitForAllHandlersToComplete(1000);
+ }
+ }
+
+ @Test(timeout = 20000)
+ public void testAsyncCompletionSendMessageRejected() throws Exception {
+ doAsyncCompletionSendMessageNotAcceptedTestImpl(new Rejected());
+ }
+
+ @Test(timeout = 20000)
+ public void testAsyncCompletionSendMessageReleased() throws Exception {
+ doAsyncCompletionSendMessageNotAcceptedTestImpl(new Released());
+ }
+
+ @Test(timeout = 20000)
+ public void testAsyncCompletionSendMessageModifiedDeliveryFailed() throws Exception {
+ Modified modified = new Modified();
+ modified.setDeliveryFailed(true);
+
+ doAsyncCompletionSendMessageNotAcceptedTestImpl(modified);
+ }
+
+ @Test(timeout = 20000)
+ public void testAsyncCompletionSendMessageModifiedUndeliverable() throws Exception {
+ Modified modified = new Modified();
+ modified.setUndeliverableHere(true);
+
+ doAsyncCompletionSendMessageNotAcceptedTestImpl(modified);
+ }
+
+ @Test(timeout = 20000)
+ public void testAsyncCompletionSendMessageModifiedDeliveryFailedUndeliverable() throws Exception {
+ Modified modified = new Modified();
+ modified.setDeliveryFailed(true);
+ modified.setUndeliverableHere(true);
+
+ doAsyncCompletionSendMessageNotAcceptedTestImpl(modified);
+ }
+
+ private void doAsyncCompletionSendMessageNotAcceptedTestImpl(ListDescribedType responseState) throws JMSException, InterruptedException, Exception, IOException {
+ try (TestAmqpPeer testPeer = new TestAmqpPeer();) {
+ JmsConnection connection = (JmsConnection) testFixture.establishConnecton(testPeer);
+
+ final CountDownLatch asyncError = new CountDownLatch(1);
+
+ connection.setExceptionListener(new ExceptionListener() {
+
+ @Override
+ public void onException(JMSException exception) {
+ LOG.debug("ExceptionListener got error: {}", exception.getMessage());
+ asyncError.countDown();
+ }
+ });
+
+ testPeer.expectBegin();
+ testPeer.expectSenderAttach();
+ testPeer.expectSenderAttach();
+
+ Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ Queue queue = session.createQueue("myQueue");
+
+ // TODO Can change to plain MessageProducer when JMS 2.0 API dependency is added.
+ JmsMessageProducer producer = (JmsMessageProducer) session.createProducer(queue);
+
+ // Create a second producer which allows for a safe wait for credit for the
+ // first producer without the need for a sleep. Otherwise the first producer
+ // might not do an actual async send due to not having received credit yet.
+ session.createProducer(queue);
+
+ Message message = session.createTextMessage("content");
+
+ testPeer.expectTransfer(new TransferPayloadCompositeMatcher(), nullValue(), false, responseState, true);
+
+ assertNull("Should not yet have a JMSDestination", message.getJMSDestination());
+
+ TestJmsCompletionListener listener = new TestJmsCompletionListener();
+ try {
+ producer.send(message, listener);
+ } catch (JMSException e) {
+ LOG.warn("Caught unexpected error: {}", e.getMessage());
+ fail("No expected exception for this send.");
+ }
+
+ assertTrue("Did not get async callback", listener.awaitCompletion(2000, TimeUnit.SECONDS));
+ assertNotNull(listener.exception);
+ assertNotNull(listener.message);
+ assertTrue(listener.message instanceof TextMessage);
+
+ testPeer.expectTransfer(new TransferPayloadCompositeMatcher());
+ testPeer.expectClose();
+
+ listener = new TestJmsCompletionListener();
+ try {
+ producer.send(message, listener);
+ } catch (JMSException e) {
+ LOG.warn("Caught unexpected error: {}", e.getMessage());
+ fail("No expected exception for this send.");
+ }
+
+ assertTrue("Did not get async callback", listener.awaitCompletion(2000, TimeUnit.SECONDS));
+ assertNull(listener.exception);
+ assertNotNull(listener.message);
+ assertTrue(listener.message instanceof TextMessage);
+
+ connection.close();
+
+ testPeer.waitForAllHandlersToComplete(2000);
+ }
+ }
+
+ @Test(timeout = 20000)
+ public void testAsyncCompletionSessionCloseThrowsIllegalStateException() throws Exception {
+ try (TestAmqpPeer testPeer = new TestAmqpPeer();) {
+ Connection connection = testFixture.establishConnecton(testPeer);
+ testPeer.expectBegin();
+ testPeer.expectSenderAttach();
+
+ final Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ Queue queue = session.createQueue("myQueue");
+
+ // TODO Can change to plain MessageProducer when JMS 2.0 API dependency is added.
+ JmsMessageProducer producer = (JmsMessageProducer) session.createProducer(queue);
+
+ // Create and transfer a new message
+ String text = "myMessage";
+ testPeer.expectTransfer(new TransferPayloadCompositeMatcher());
+ testPeer.expectClose();
+
+ final AtomicReference<JMSException> closeError = new AtomicReference<JMSException>(null);
+ TextMessage message = session.createTextMessage(text);
+ TestJmsCompletionListener listener = new TestJmsCompletionListener() {
+
+ @Override
+ public void onCompletion(Message message) {
+
+ try {
+ session.close();
+ } catch (JMSException jmsEx) {
+ closeError.set(jmsEx);
+ }
+
+ super.onCompletion(message);
+ };
+ };
+
+ producer.send(message, listener);
+
+ assertTrue("Did not get async callback", listener.awaitCompletion(2000, TimeUnit.SECONDS));
+ assertNull(listener.exception);
+ assertNotNull(listener.message);
+ assertTrue(listener.message instanceof TextMessage);
+ assertNotNull(closeError.get());
+ assertTrue(closeError.get() instanceof IllegalStateException);
+
+ connection.close();
+
+ testPeer.waitForAllHandlersToComplete(1000);
+ }
+ }
+
+ @Test(timeout = 20000)
+ public void testAsyncCompletionConnectionCloseThrowsIllegalStateException() throws Exception {
+ try (TestAmqpPeer testPeer = new TestAmqpPeer();) {
+ final Connection connection = testFixture.establishConnecton(testPeer);
+ testPeer.expectBegin();
+ testPeer.expectSenderAttach();
+
+ final Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ Queue queue = session.createQueue("myQueue");
+
+ // TODO Can change to plain MessageProducer when JMS 2.0 API dependency is added.
+ JmsMessageProducer producer = (JmsMessageProducer) session.createProducer(queue);
+
+ // Create and transfer a new message
+ String text = "myMessage";
+ testPeer.expectTransfer(new TransferPayloadCompositeMatcher());
+ testPeer.expectClose();
+
+ final AtomicReference<JMSException> closeError = new AtomicReference<JMSException>(null);
+ TextMessage message = session.createTextMessage(text);
+ TestJmsCompletionListener listener = new TestJmsCompletionListener() {
+
+ @Override
+ public void onCompletion(Message message) {
+
+ try {
+ connection.close();
+ } catch (JMSException jmsEx) {
+ closeError.set(jmsEx);
+ }
+
+ super.onCompletion(message);
+ };
+ };
+
+ producer.send(message, listener);
+
+ assertTrue("Did not get async callback", listener.awaitCompletion(2000, TimeUnit.SECONDS));
+ assertNull(listener.exception);
+ assertNotNull(listener.message);
+ assertNotNull(closeError.get());
+ assertTrue(closeError.get() instanceof IllegalStateException);
+
+ connection.close();
+
+ testPeer.waitForAllHandlersToComplete(1000);
+ }
+ }
+
+ @Test(timeout = 20000)
+ public void testAsyncCompletionSessionCommitThrowsIllegalStateException() throws Exception {
+ try (TestAmqpPeer testPeer = new TestAmqpPeer();) {
+ final Connection connection = testFixture.establishConnecton(testPeer);
+ testPeer.expectBegin();
+ testPeer.expectCoordinatorAttach();
+
+ // First expect an unsettled 'declare' transfer to the txn coordinator, and
+ // reply with a declared disposition state containing the txnId.
+ Binary txnId = new Binary(new byte[]{ (byte) 5, (byte) 6, (byte) 7, (byte) 8});
+ testPeer.expectDeclare(txnId);
+
+ testPeer.expectSenderAttach();
+
+ final Session session = connection.createSession(true, Session.SESSION_TRANSACTED);
+ Queue queue = session.createQueue("myQueue");
+
+ // TODO Can change to plain MessageProducer when JMS 2.0 API dependency is added.
+ JmsMessageProducer producer = (JmsMessageProducer) session.createProducer(queue);
+
+ // Create and transfer a new message
+ String text = "myMessage";
+ MessageHeaderSectionMatcher headersMatcher = new MessageHeaderSectionMatcher(true);
+ MessageAnnotationsSectionMatcher msgAnnotationsMatcher = new MessageAnnotationsSectionMatcher(true);
+ MessagePropertiesSectionMatcher propsMatcher = new MessagePropertiesSectionMatcher(true);
+ TransferPayloadCompositeMatcher messageMatcher = new TransferPayloadCompositeMatcher();
+ messageMatcher.setHeadersMatcher(headersMatcher);
+ messageMatcher.setMessageAnnotationsMatcher(msgAnnotationsMatcher);
+ messageMatcher.setPropertiesMatcher(propsMatcher);
+ messageMatcher.setMessageContentMatcher(new EncodedAmqpValueMatcher(text));
+ TransactionalStateMatcher stateMatcher = new TransactionalStateMatcher();
+ stateMatcher.withTxnId(equalTo(txnId));
+ stateMatcher.withOutcome(nullValue());
+ TransactionalState txState = new TransactionalState();
+ txState.setTxnId(txnId);
+ txState.setOutcome(new Accepted());
+
+ testPeer.expectTransfer(messageMatcher, stateMatcher, false, txState, true);
+ testPeer.expectDischarge(txnId, true);
+ testPeer.expectClose();
+
+ final AtomicReference<JMSException> commitError = new AtomicReference<JMSException>(null);
+ TextMessage message = session.createTextMessage(text);
+ TestJmsCompletionListener listener = new TestJmsCompletionListener() {
+
+ @Override
+ public void onCompletion(Message message) {
+
+ try {
+ session.commit();
+ } catch (JMSException jmsEx) {
+ commitError.set(jmsEx);
+ }
+
+ super.onCompletion(message);
+ };
+ };
+
+ producer.send(message, listener);
+
+ assertTrue("Did not get async callback", listener.awaitCompletion(2000, TimeUnit.SECONDS));
+ assertNull(listener.exception);
+ assertNotNull(listener.message);
+ assertNotNull(commitError.get());
+ assertTrue(commitError.get() instanceof IllegalStateException);
+
+ connection.close();
+
+ testPeer.waitForAllHandlersToComplete(1000);
+ }
+ }
+
+ @Test(timeout = 20000)
+ public void testAsyncCompletionSessionRollbackThrowsIllegalStateException() throws Exception {
+ try (TestAmqpPeer testPeer = new TestAmqpPeer();) {
+ final Connection connection = testFixture.establishConnecton(testPeer);
+ testPeer.expectBegin();
+ testPeer.expectCoordinatorAttach();
+
+ // First expect an unsettled 'declare' transfer to the txn coordinator, and
+ // reply with a declared disposition state containing the txnId.
+ Binary txnId = new Binary(new byte[]{ (byte) 5, (byte) 6, (byte) 7, (byte) 8});
+ testPeer.expectDeclare(txnId);
+
+ testPeer.expectSenderAttach();
+
+ final Session session = connection.createSession(true, Session.SESSION_TRANSACTED);
+ Queue queue = session.createQueue("myQueue");
+
+ // TODO Can change to plain MessageProducer when JMS 2.0 API dependency is added.
+ JmsMessageProducer producer = (JmsMessageProducer) session.createProducer(queue);
+
+ // Create and transfer a new message
+ String text = "myMessage";
+ MessageHeaderSectionMatcher headersMatcher = new MessageHeaderSectionMatcher(true);
+ MessageAnnotationsSectionMatcher msgAnnotationsMatcher = new MessageAnnotationsSectionMatcher(true);
+ MessagePropertiesSectionMatcher propsMatcher = new MessagePropertiesSectionMatcher(true);
+ TransferPayloadCompositeMatcher messageMatcher = new TransferPayloadCompositeMatcher();
+ messageMatcher.setHeadersMatcher(headersMatcher);
+ messageMatcher.setMessageAnnotationsMatcher(msgAnnotationsMatcher);
+ messageMatcher.setPropertiesMatcher(propsMatcher);
+ messageMatcher.setMessageContentMatcher(new EncodedAmqpValueMatcher(text));
+ TransactionalStateMatcher stateMatcher = new TransactionalStateMatcher();
+ stateMatcher.withTxnId(equalTo(txnId));
+ stateMatcher.withOutcome(nullValue());
+ TransactionalState txState = new TransactionalState();
+ txState.setTxnId(txnId);
+ txState.setOutcome(new Accepted());
+
+ testPeer.expectTransfer(messageMatcher, stateMatcher, false, txState, true);
+ testPeer.expectDischarge(txnId, true);
+ testPeer.expectClose();
+
+ final AtomicReference<JMSException> rollback = new AtomicReference<JMSException>(null);
+ TextMessage message = session.createTextMessage(text);
+ TestJmsCompletionListener listener = new TestJmsCompletionListener() {
+
+ @Override
+ public void onCompletion(Message message) {
+
+ try {
+ session.rollback();
+ } catch (JMSException jmsEx) {
+ rollback.set(jmsEx);
+ }
+
+ super.onCompletion(message);
+ };
+ };
+
+ producer.send(message, listener);
+
+ assertTrue("Did not get async callback", listener.awaitCompletion(2000, TimeUnit.SECONDS));
+ assertNull(listener.exception);
+ assertNotNull(listener.message);
+ assertNotNull(rollback.get());
+ assertTrue(rollback.get() instanceof IllegalStateException);
+
+ connection.close();
+
+ testPeer.waitForAllHandlersToComplete(1000);
+ }
+ }
+
+ @Test(timeout = 20000)
public void testAnonymousProducerSendFailureHandledWhenAnonymousRelayNodeIsNotSupported() throws Exception {
try (TestAmqpPeer testPeer = new TestAmqpPeer();) {
@@ -1831,4 +2380,47 @@ public class ProducerIntegrationTest extends QpidJmsTestCase {
testPeer.waitForAllHandlersToComplete(1000);
}
}
+
+ private class TestJmsCompletionListener implements JmsCompletionListener {
+
+ private final CountDownLatch completed;
+
+ public volatile int successCount;
+ public volatile int errorCount;
+
+ public volatile Message message;
+ public volatile Exception exception;
+
+ public TestJmsCompletionListener() {
+ this(1);
+ }
+
+ public TestJmsCompletionListener(int expected) {
+ this.completed = new CountDownLatch(expected);
+ }
+
+ public boolean awaitCompletion(long timeout, TimeUnit units) throws InterruptedException {
+ return completed.await(timeout, units);
+ }
+
+ @Override
+ public void onCompletion(Message message) {
+ LOG.info("JmsCompletionListener onCompletion called with message: {}", message);
+ this.message = message;
+ this.successCount++;
+
+ completed.countDown();
+ }
+
+ @Override
+ public void onException(Message message, Exception exception) {
+ LOG.info("JmsCompletionListener onException called with message: {} error {}", message, exception);
+
+ this.message = message;
+ this.exception = exception;
+ this.errorCount++;
+
+ completed.countDown();
+ }
+ }
}
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/3a03663b/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/SessionIntegrationTest.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/SessionIntegrationTest.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/SessionIntegrationTest.java
index 7bf35e4..34d60f1 100644
--- a/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/SessionIntegrationTest.java
+++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/SessionIntegrationTest.java
@@ -57,8 +57,10 @@ import javax.jms.TextMessage;
import javax.jms.Topic;
import javax.jms.TopicSubscriber;
+import org.apache.qpid.jms.JmsCompletionListener;
import org.apache.qpid.jms.JmsConnection;
import org.apache.qpid.jms.JmsDefaultConnectionListener;
+import org.apache.qpid.jms.JmsMessageProducer;
import org.apache.qpid.jms.JmsOperationTimedOutException;
import org.apache.qpid.jms.JmsSession;
import org.apache.qpid.jms.policy.JmsDefaultPrefetchPolicy;
@@ -1446,7 +1448,7 @@ public class SessionIntegrationTest extends QpidJmsTestCase {
public void testCreateAnonymousProducerWhenAnonymousRelayNodeIsNotSupported() throws Exception {
try (TestAmqpPeer testPeer = new TestAmqpPeer();) {
- //DO NOT add capability to indicate server support for ANONYMOUS-RELAY
+ // DO NOT add capability to indicate server support for ANONYMOUS-RELAY
Connection connection = testFixture.establishConnecton(testPeer);
connection.start();
@@ -1460,12 +1462,12 @@ public class SessionIntegrationTest extends QpidJmsTestCase {
// Expect no AMQP traffic when we create the anonymous producer, as it will wait
// for an actual send to occur on the producer before anything occurs on the wire
- //Create an anonymous producer
+ // Create an anonymous producer
MessageProducer producer = session.createProducer(null);
assertNotNull("Producer object was null", producer);
- //Expect a new message sent by the above producer to cause creation of a new
- //sender link to the given destination, then closing the link after the message is sent.
+ // Expect a new message sent by the above producer to cause creation of a new
+ // sender link to the given destination, then closing the link after the message is sent.
TargetMatcher targetMatcher = new TargetMatcher();
targetMatcher.withAddress(equalTo(topicName));
targetMatcher.withDynamic(equalTo(false));
@@ -1484,7 +1486,7 @@ public class SessionIntegrationTest extends QpidJmsTestCase {
Message message = session.createMessage();
producer.send(dest, message);
- //Repeat the send and observe another attach->transfer->detach.
+ // Repeat the send and observe another attach->transfer->detach.
testPeer.expectSenderAttach(targetMatcher, false, false);
testPeer.expectTransfer(messageMatcher);
testPeer.expectDetach(true, true, true);
@@ -1675,6 +1677,78 @@ public class SessionIntegrationTest extends QpidJmsTestCase {
}
@Test(timeout = 20000)
+ public void testRemotelyEndSessionWithProducerCompletesAsyncSends() throws Exception {
+ final String BREAD_CRUMB = "ErrorMessage";
+
+ try (TestAmqpPeer testPeer = new TestAmqpPeer();) {
+ final AtomicBoolean sessionClosed = new AtomicBoolean();
+ JmsConnection connection = (JmsConnection) testFixture.establishConnecton(testPeer);
+ connection.addConnectionListener(new JmsDefaultConnectionListener() {
+ @Override
+ public void onSessionClosed(Session session, Exception exception) {
+ sessionClosed.set(true);
+ }
+ });
+
+ testPeer.expectBegin();
+ Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ // Create a producer, then remotely end the session afterwards.
+ testPeer.expectSenderAttach();
+
+ Queue queue = session.createQueue("myQueue");
+ // TODO - Can revert to just MessageProducer once JMS 2.0 API is used
+ final JmsMessageProducer producer = (JmsMessageProducer) session.createProducer(queue);
+
+ Message message = session.createTextMessage("content");
+
+ final int MSG_COUNT = 3;
+
+ for (int i = 0; i < MSG_COUNT; ++i) {
+ testPeer.expectTransferButDoNotRespond(new TransferPayloadCompositeMatcher());
+ }
+
+ testPeer.remotelyEndLastOpenedSession(true, 0, AmqpError.RESOURCE_DELETED, BREAD_CRUMB);
+
+ TestJmsCompletionListener listener = new TestJmsCompletionListener(MSG_COUNT);
+ try {
+ for (int i = 0; i < MSG_COUNT; ++i) {
+ producer.send(message, listener);
+ }
+ } catch (JMSException e) {
+ LOG.warn("Caught unexpected error: {}", e.getMessage());
+ fail("No expected exception for this send.");
+ }
+
+ testPeer.waitForAllHandlersToComplete(1000);
+
+ // Verify the producer gets marked closed
+ assertTrue(listener.awaitCompletion(2000, TimeUnit.SECONDS));
+ assertEquals(MSG_COUNT, listener.errorCount);
+ assertEquals(0, listener.successCount);
+
+ // Verify the session is now marked closed
+ try {
+ session.getAcknowledgeMode();
+ fail("Expected ISE to be thrown due to being closed");
+ } catch (IllegalStateException jmsise) {
+ String errorMessage = jmsise.getCause().getMessage();
+ assertTrue(errorMessage.contains(AmqpError.RESOURCE_DELETED.toString()));
+ assertTrue(errorMessage.contains(BREAD_CRUMB));
+ }
+
+ assertTrue("Session closed callback didn't trigger", sessionClosed.get());
+
+ // Try closing it explicitly, should effectively no-op in client.
+ // The test peer will throw during close if it sends anything.
+ producer.close();
+
+ testPeer.expectClose();
+ connection.close();
+ }
+ }
+
+ @Test(timeout = 20000)
public void testRemotelyEndSessionWithConsumer() throws Exception {
final String BREAD_CRUMB = "ErrorMessage";
@@ -1920,4 +1994,34 @@ public class SessionIntegrationTest extends QpidJmsTestCase {
connection.close();
}
}
+
+ private class TestJmsCompletionListener implements JmsCompletionListener {
+
+ private final CountDownLatch completed;
+
+ public volatile int successCount;
+ public volatile int errorCount;
+
+ public TestJmsCompletionListener(int expected) {
+ completed = new CountDownLatch(expected);
+ }
+
+ public boolean awaitCompletion(long timeout, TimeUnit units) throws InterruptedException {
+ return completed.await(timeout, units);
+ }
+
+ @Override
+ public void onCompletion(Message message) {
+ LOG.info("JmsCompletionListener onCompletion called with message: {}", message);
+ successCount++;
+ completed.countDown();
+ }
+
+ @Override
+ public void onException(Message message, Exception exception) {
+ LOG.info("JmsCompletionListener onException called with message: {} error {}", message, exception);
+ errorCount++;
+ completed.countDown();
+ }
+ }
}
http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/3a03663b/qpid-jms-client/src/test/java/org/apache/qpid/jms/producer/JmsMessageProducerTest.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/producer/JmsMessageProducerTest.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/producer/JmsMessageProducerTest.java
index 80a6e6a..7117e9f 100644
--- a/qpid-jms-client/src/test/java/org/apache/qpid/jms/producer/JmsMessageProducerTest.java
+++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/producer/JmsMessageProducerTest.java
@@ -22,35 +22,63 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import javax.jms.Connection;
import javax.jms.DeliveryMode;
+import javax.jms.Destination;
import javax.jms.InvalidDestinationException;
+import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageProducer;
import javax.jms.Session;
+import org.apache.qpid.jms.JmsCompletionListener;
+import org.apache.qpid.jms.JmsConnectionFactory;
import org.apache.qpid.jms.JmsConnectionTestSupport;
import org.apache.qpid.jms.JmsDestination;
+import org.apache.qpid.jms.JmsMessageProducer;
import org.apache.qpid.jms.JmsQueue;
import org.apache.qpid.jms.JmsSession;
+import org.apache.qpid.jms.message.JmsOutboundMessageDispatch;
+import org.apache.qpid.jms.provider.mock.MockRemotePeer;
+import org.apache.qpid.jms.test.Wait;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
* Test basic functionality around JmsConnection
*/
public class JmsMessageProducerTest extends JmsConnectionTestSupport {
+ private static final Logger LOG = LoggerFactory.getLogger(JmsMessageProducerTest.class);
+
+ private final MyCompletionListener completionListener = new MyCompletionListener();
private JmsSession session;
+ private final MockRemotePeer remotePeer = new MockRemotePeer();
@Override
@Before
public void setUp() throws Exception {
super.setUp();
+ remotePeer.start();
connection = createConnectionToMockProvider();
session = (JmsSession) connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
}
+ @Override
+ @After
+ public void tearDown() throws Exception {
+ remotePeer.shutdown();
+ super.tearDown();
+ }
+
@Test(timeout = 10000)
public void testMultipleCloseCallsNoErrors() throws Exception {
MessageProducer producer = session.createProducer(null);
@@ -106,7 +134,7 @@ public class JmsMessageProducerTest extends JmsConnectionTestSupport {
@Test(timeout = 10000)
public void testAnonymousProducerThrowsUOEWhenExplictDestinationNotProvided() throws Exception {
- MessageProducer producer = session.createProducer(null);
+ JmsMessageProducer producer = (JmsMessageProducer) session.createProducer(null);
Message message = Mockito.mock(Message.class);
try {
@@ -117,17 +145,31 @@ public class JmsMessageProducerTest extends JmsConnectionTestSupport {
}
try {
+ producer.send(message, completionListener);
+ fail("Expected exception not thrown");
+ } catch (UnsupportedOperationException uoe) {
+ // expected
+ }
+
+ try {
producer.send(message, Message.DEFAULT_DELIVERY_MODE, Message.DEFAULT_PRIORITY, Message.DEFAULT_TIME_TO_LIVE);
fail("Expected exception not thrown");
} catch (UnsupportedOperationException uoe) {
// expected
}
+
+ try {
+ producer.send(message, Message.DEFAULT_DELIVERY_MODE, Message.DEFAULT_PRIORITY, Message.DEFAULT_TIME_TO_LIVE, completionListener);
+ fail("Expected exception not thrown");
+ } catch (UnsupportedOperationException uoe) {
+ // expected
+ }
}
@Test(timeout = 10000)
public void testExplicitProducerThrowsUOEWhenExplictDestinationIsProvided() throws Exception {
JmsDestination dest = new JmsQueue("explicitDestination");
- MessageProducer producer = session.createProducer(new JmsQueue());
+ JmsMessageProducer producer = (JmsMessageProducer) session.createProducer(dest);
Message message = Mockito.mock(Message.class);
try {
@@ -138,16 +180,30 @@ public class JmsMessageProducerTest extends JmsConnectionTestSupport {
}
try {
+ producer.send(dest, message, completionListener);
+ fail("Expected exception not thrown");
+ } catch (UnsupportedOperationException uoe) {
+ // expected
+ }
+
+ try {
producer.send(dest, message, Message.DEFAULT_DELIVERY_MODE, Message.DEFAULT_PRIORITY, Message.DEFAULT_TIME_TO_LIVE);
fail("Expected exception not thrown");
} catch (UnsupportedOperationException uoe) {
// expected
}
+
+ try {
+ producer.send(dest, message, Message.DEFAULT_DELIVERY_MODE, Message.DEFAULT_PRIORITY, Message.DEFAULT_TIME_TO_LIVE, completionListener);
+ fail("Expected exception not thrown");
+ } catch (UnsupportedOperationException uoe) {
+ // expected
+ }
}
@Test(timeout = 10000)
public void testAnonymousDestinationProducerThrowsIDEWhenNullDestinationIsProvided() throws Exception {
- MessageProducer producer = session.createProducer(null);
+ JmsMessageProducer producer = (JmsMessageProducer) session.createProducer(null);
Message message = Mockito.mock(Message.class);
try {
@@ -158,10 +214,369 @@ public class JmsMessageProducerTest extends JmsConnectionTestSupport {
}
try {
+ producer.send(null, message, completionListener);
+ fail("Expected exception not thrown");
+ } catch (InvalidDestinationException ide) {
+ // expected
+ }
+
+ try {
producer.send(null, message, Message.DEFAULT_DELIVERY_MODE, Message.DEFAULT_PRIORITY, Message.DEFAULT_TIME_TO_LIVE);
fail("Expected exception not thrown");
} catch (InvalidDestinationException ide) {
// expected
}
+
+ try {
+ producer.send(null, message, Message.DEFAULT_DELIVERY_MODE, Message.DEFAULT_PRIORITY, Message.DEFAULT_TIME_TO_LIVE, completionListener);
+ fail("Expected exception not thrown");
+ } catch (InvalidDestinationException ide) {
+ // expected
+ }
+ }
+
+ @Test(timeout = 10000)
+ public void testAnonymousProducerThrowsIAEWhenNullCompletionListenerProvided() throws Exception {
+ JmsMessageProducer producer = (JmsMessageProducer) session.createProducer(null);
+ JmsDestination dest = new JmsQueue("explicitDestination");
+
+ Message message = Mockito.mock(Message.class);
+
+ try {
+ producer.send(dest, message, null);
+ fail("Expected exception not thrown");
+ } catch (IllegalArgumentException iae) {
+ // expected
+ }
+
+ try {
+ producer.send(dest, message, Message.DEFAULT_DELIVERY_MODE, Message.DEFAULT_PRIORITY, Message.DEFAULT_TIME_TO_LIVE, null);
+ fail("Expected exception not thrown");
+ } catch (IllegalArgumentException iae) {
+ // expected
+ }
+ }
+
+ @Test(timeout = 10000)
+ public void testExplicitProducerThrowsIAEWhenNullCompletionListenerIsProvided() throws Exception {
+ JmsDestination dest = new JmsQueue("explicitDestination");
+ JmsMessageProducer producer = (JmsMessageProducer) session.createProducer(dest);
+
+ Message message = Mockito.mock(Message.class);
+ try {
+ producer.send(message, null);
+ fail("Expected exception not thrown");
+ } catch (IllegalArgumentException iae) {
+ // expected
+ }
+
+ try {
+ producer.send(message, Message.DEFAULT_DELIVERY_MODE, Message.DEFAULT_PRIORITY, Message.DEFAULT_TIME_TO_LIVE, null);
+ fail("Expected exception not thrown");
+ } catch (IllegalArgumentException iae) {
+ // expected
+ }
+ }
+
+ @Test(timeout = 10000)
+ public void testInOrderSendAcksCompletionsReturnInOrder() throws Exception {
+ final int MESSAGE_COUNT = 3;
+
+ final MockRemotePeer remotePoor = MockRemotePeer.INSTANCE;
+
+ JmsConnectionFactory factory = new JmsConnectionFactory(
+ "mock://localhost?mock.delayCompletionCalls=true");
+
+ Connection connection = factory.createConnection();
+ Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ final Destination destination = new JmsQueue("explicitDestination");
+ JmsMessageProducer producer = (JmsMessageProducer) session.createProducer(destination);
+ final MyCompletionListener listener = new MyCompletionListener();
+
+ sendMessages(MESSAGE_COUNT, producer, listener);
+
+ assertTrue("Not all sends made it to the remote", Wait.waitFor(new Wait.Condition() {
+
+ @Override
+ public boolean isSatisified() throws Exception {
+ return remotePoor.getPendingCompletions(destination).size() == MESSAGE_COUNT;
+ }
+ }));
+
+ remotePoor.completeAllPendingSends(destination);
+
+ assertTrue("Not all completions triggered", Wait.waitFor(new Wait.Condition() {
+
+ @Override
+ public boolean isSatisified() throws Exception {
+ return listener.getCompletedSends().size() == MESSAGE_COUNT;
+ }
+ }));
+
+ assertMessageCompletedInOrder(MESSAGE_COUNT, listener);
+
+ connection.close();
+ }
+
+ @Test(timeout = 10000)
+ public void testReversedOrderSendAcksCompletionsReturnInOrder() throws Exception {
+ final int MESSAGE_COUNT = 3;
+
+ final MockRemotePeer remotePoor = MockRemotePeer.INSTANCE;
+
+ JmsConnectionFactory factory = new JmsConnectionFactory(
+ "mock://localhost?mock.delayCompletionCalls=true");
+
+ Connection connection = factory.createConnection();
+ Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ final Destination destination = new JmsQueue("explicitDestination");
+ JmsMessageProducer producer = (JmsMessageProducer) session.createProducer(destination);
+ final MyCompletionListener listener = new MyCompletionListener();
+
+ sendMessages(MESSAGE_COUNT, producer, listener);
+
+ assertTrue("Not all sends made it to the remote", Wait.waitFor(new Wait.Condition() {
+
+ @Override
+ public boolean isSatisified() throws Exception {
+ return remotePoor.getPendingCompletions(destination).size() == MESSAGE_COUNT;
+ }
+ }));
+
+ List<JmsOutboundMessageDispatch> pending = remotePoor.getPendingCompletions(destination);
+ assertEquals(MESSAGE_COUNT, pending.size());
+ Collections.reverse(pending);
+
+ for (JmsOutboundMessageDispatch envelope : pending) {
+ LOG.info("Trigger completion of message: {}", envelope.getMessage().getJMSMessageID());
+ remotePoor.completePendingSend(envelope);
+ }
+
+ assertTrue("Not all completions triggered", Wait.waitFor(new Wait.Condition() {
+
+ @Override
+ public boolean isSatisified() throws Exception {
+ return listener.getCompletedSends().size() == MESSAGE_COUNT;
+ }
+ }));
+
+ assertMessageCompletedInOrder(MESSAGE_COUNT, listener);
+
+ connection.close();
+ }
+
+ @Test(timeout = 10000)
+ public void testInOrderSendFailuresCompletionsReturnInOrder() throws Exception {
+ final int MESSAGE_COUNT = 3;
+
+ final MockRemotePeer remotePoor = MockRemotePeer.INSTANCE;
+
+ JmsConnectionFactory factory = new JmsConnectionFactory(
+ "mock://localhost?mock.delayCompletionCalls=true");
+
+ Connection connection = factory.createConnection();
+ Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ final Destination destination = new JmsQueue("explicitDestination");
+ JmsMessageProducer producer = (JmsMessageProducer) session.createProducer(destination);
+ final MyCompletionListener listener = new MyCompletionListener();
+
+ sendMessages(MESSAGE_COUNT, producer, listener);
+ assertTrue("Not all messages sent", Wait.waitFor(new Wait.Condition() {
+
+ @Override
+ public boolean isSatisified() throws Exception {
+ return remotePoor.getPendingCompletions(destination).size() == MESSAGE_COUNT;
+ }
+ }));
+ remotePoor.failAllPendingSends(destination, new JMSException("Could not send message"));
+
+ assertTrue("Not all completions triggered", Wait.waitFor(new Wait.Condition() {
+
+ @Override
+ public boolean isSatisified() throws Exception {
+ return listener.getFailedSends().size() == MESSAGE_COUNT;
+ }
+ }));
+
+ assertMessageFailedInOrder(MESSAGE_COUNT, listener);
+
+ connection.close();
+ }
+
+ @Test(timeout = 10000)
+ public void testReversedOrderSendAcksFailuresReturnInOrder() throws Exception {
+ final int MESSAGE_COUNT = 3;
+
+ final MockRemotePeer remotePoor = MockRemotePeer.INSTANCE;
+
+ JmsConnectionFactory factory = new JmsConnectionFactory(
+ "mock://localhost?mock.delayCompletionCalls=true");
+
+ Connection connection = factory.createConnection();
+ Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ final Destination destination = new JmsQueue("explicitDestination");
+ JmsMessageProducer producer = (JmsMessageProducer) session.createProducer(destination);
+ final MyCompletionListener listener = new MyCompletionListener();
+
+ sendMessages(MESSAGE_COUNT, producer, listener);
+
+ assertTrue("Not all sends made it to the remote", Wait.waitFor(new Wait.Condition() {
+
+ @Override
+ public boolean isSatisified() throws Exception {
+ return remotePoor.getPendingCompletions(destination).size() == MESSAGE_COUNT;
+ }
+ }));
+
+ List<JmsOutboundMessageDispatch> pending = remotePoor.getPendingCompletions(destination);
+ assertEquals(MESSAGE_COUNT, pending.size());
+ Collections.reverse(pending);
+
+ for (JmsOutboundMessageDispatch envelope : pending) {
+ LOG.info("Trigger failure of message: {}", envelope.getMessage().getJMSMessageID());
+ remotePoor.failPendingSend(envelope, new JMSException("Failed to send message"));
+ }
+
+ assertTrue("Not all failures triggered", Wait.waitFor(new Wait.Condition() {
+
+ @Override
+ public boolean isSatisified() throws Exception {
+ return listener.getFailedSends().size() == MESSAGE_COUNT;
+ }
+ }));
+
+ assertMessageFailedInOrder(MESSAGE_COUNT, listener);
+
+ connection.close();
+ }
+
+ @Test(timeout = 10000)
+ public void testInterleavedCompletionsReturnedInOrder() throws Exception {
+ final int MESSAGE_COUNT = 3;
+
+ final MockRemotePeer remotePoor = MockRemotePeer.INSTANCE;
+
+ JmsConnectionFactory factory = new JmsConnectionFactory(
+ "mock://localhost?mock.delayCompletionCalls=true");
+
+ Connection connection = factory.createConnection();
+ Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ final Destination destination = new JmsQueue("explicitDestination");
+ JmsMessageProducer producer = (JmsMessageProducer) session.createProducer(destination);
+ final MyCompletionListener listener = new MyCompletionListener();
+
+ sendMessages(MESSAGE_COUNT, producer, listener);
+
+ assertTrue("Not all sends made it to the remote", Wait.waitFor(new Wait.Condition() {
+
+ @Override
+ public boolean isSatisified() throws Exception {
+ return remotePoor.getPendingCompletions(destination).size() == MESSAGE_COUNT;
+ }
+ }));
+
+ List<JmsOutboundMessageDispatch> pending = remotePoor.getPendingCompletions(destination);
+ assertEquals(MESSAGE_COUNT, pending.size());
+ Collections.reverse(pending);
+
+ for (JmsOutboundMessageDispatch envelope : pending) {
+ int sequence = envelope.getMessage().getIntProperty("sequence");
+ if (sequence % 2 == 0) {
+ LOG.info("Trigger completion of message: {}", envelope.getMessage().getJMSMessageID());
+ remotePoor.completePendingSend(envelope);
+ } else {
+ LOG.info("Trigger failure of message: {}", envelope.getMessage().getJMSMessageID());
+ remotePoor.failPendingSend(envelope, new JMSException("Failed to send message"));
+ }
+ }
+
+ assertTrue("Not all completions triggered", Wait.waitFor(new Wait.Condition() {
+
+ @Override
+ public boolean isSatisified() throws Exception {
+ return listener.getCombinedSends().size() == MESSAGE_COUNT;
+ }
+ }));
+
+ assertTotalCompletionOrder(MESSAGE_COUNT, listener);
+
+ connection.close();
+ }
+
+ private void sendMessages(int count, JmsMessageProducer producer, MyCompletionListener listener) throws Exception {
+ for (int i = 0; i < count; ++i) {
+ Message message = session.createMessage();
+ message.setIntProperty("sequence", i);
+
+ producer.send(message, listener);
+ }
+ }
+
+ private void assertMessageCompletedInOrder(int expected, MyCompletionListener listener) throws Exception {
+ assertEquals("Did not get expected number of completions", expected, listener.completed.size());
+ for (int i = 0; i < listener.completed.size(); ++i) {
+ int sequence = listener.completed.get(i).getIntProperty("sequence");
+ assertEquals("Did not complete expected message: " + i + " got: " + sequence, i, sequence);
+ }
+ }
+
+ private void assertMessageFailedInOrder(int expected, MyCompletionListener listener) throws Exception {
+ assertEquals("Did not get expected number of failures", expected, listener.failed.size());
+ for (int i = 0; i < listener.failed.size(); ++i) {
+ int sequence = listener.failed.get(i).getIntProperty("sequence");
+ assertEquals("Did not fail expected message: " + i + " got: " + sequence, i, sequence);
+ }
+ }
+
+ private void assertTotalCompletionOrder(int expected, MyCompletionListener listener) throws Exception {
+ assertEquals("Did not get expected number of failures", expected, listener.combinedResult.size());
+ for (int i = 0; i < listener.combinedResult.size(); ++i) {
+ int sequence = listener.combinedResult.get(i).getIntProperty("sequence");
+ assertEquals("Did not fail expected message: " + i + " got: " + sequence, i, sequence);
+ }
+ }
+
+ private class MyCompletionListener implements JmsCompletionListener {
+
+ private final List<Message> completed = new ArrayList<Message>();
+ private final List<Message> failed = new ArrayList<Message>();
+ private final List<Message> combinedResult = new ArrayList<Message>();
+
+ @Override
+ public void onCompletion(Message message) {
+ try {
+ LOG.debug("Recording completed send: {}", message.getJMSMessageID());
+ } catch (JMSException e) {
+ }
+ completed.add(message);
+ combinedResult.add(message);
+ }
+
+ @Override
+ public void onException(Message message, Exception exception) {
+ try {
+ LOG.debug("Recording failed send: {} -> error {}", message.getJMSMessageID(), exception.getMessage());
+ } catch (JMSException e) {
+ }
+ failed.add(message);
+ combinedResult.add(message);
+ }
+
+ public List<Message> getCombinedSends() {
+ return combinedResult;
+ }
+
+ public List<Message> getCompletedSends() {
+ return completed;
+ }
+
+ public List<Message> getFailedSends() {
+ return failed;
+ }
}
}
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org