/** * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.activemq.transport.stomp; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.io.DataInputStream; import java.net.SocketTimeoutException; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import javax.jms.Connection; import javax.jms.MessageConsumer; import javax.jms.MessageProducer; import javax.jms.Session; import javax.jms.TextMessage; import javax.management.ObjectName; import org.apache.activemq.broker.jmx.BrokerViewMBean; import org.apache.activemq.command.ActiveMQQueue; import org.apache.activemq.command.ActiveMQTextMessage; import org.apache.activemq.util.Wait; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class Stomp11Test extends StompTestSupport { private static final Logger LOG = LoggerFactory.getLogger(Stomp11Test.class); private Connection connection; private Session session; private ActiveMQQueue queue; @Override public void setUp() throws Exception { super.setUp(); stompConnect(); connection = cf.createConnection("system", "manager"); session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); queue = new ActiveMQQueue(getQueueName()); connection.start(); } @Override public void tearDown() throws Exception { try { if (connection != null) { connection.close(); connection = null; } } catch (Exception ex) {} super.tearDown(); } @Test(timeout = 60000) public void testConnect() throws Exception { String connectFrame = "STOMP\n" + "login:system\n" + "passcode:manager\n" + "accept-version:1.1\n" + "host:localhost\n" + "request-id:1\n" + "\n" + Stomp.NULL; stompConnection.sendFrame(connectFrame); String f = stompConnection.receiveFrame(); LOG.debug("Broker sent: " + f); assertTrue(f.startsWith("CONNECTED")); assertTrue(f.indexOf("response-id:1") >= 0); assertTrue(f.indexOf("version:1.1") >= 0); assertTrue(f.indexOf("session:") >= 0); } @Test(timeout = 60000) public void testConnectedNeverEncoded() throws Exception { String connectFrame = "STOMP\n" + "login:system\n" + "passcode:manager\n" + "accept-version:1.1\n" + "host:localhost\n" + "request-id:1\n" + "\n" + Stomp.NULL; stompConnection.sendFrame(connectFrame); String f = stompConnection.receiveFrame(); LOG.debug("Broker sent: " + f); assertTrue(f.startsWith("CONNECTED")); assertTrue(f.indexOf("response-id:1") >= 0); assertTrue(f.indexOf("version:1.1") >= 0); assertTrue(f.indexOf("session:") >= 0); int sessionHeader = f.indexOf("session:"); f = f.substring(sessionHeader + "session:".length()); LOG.info("session header follows: " + f); assertTrue(f.startsWith("ID:")); } @Test(timeout = 60000) public void testConnectWithVersionOptions() throws Exception { String connectFrame = "STOMP\n" + "login:system\n" + "passcode:manager\n" + "accept-version:1.0,1.1\n" + "host:localhost\n" + "\n" + Stomp.NULL; stompConnection.sendFrame(connectFrame); String f = stompConnection.receiveFrame(); LOG.debug("Broker sent: " + f); assertTrue(f.startsWith("CONNECTED")); assertTrue(f.indexOf("version:1.1") >= 0); assertTrue(f.indexOf("session:") >= 0); } @Test(timeout = 60000) public void testConnectWithValidFallback() throws Exception { String connectFrame = "STOMP\n" + "login:system\n" + "passcode:manager\n" + "accept-version:1.0,10.1\n" + "host:localhost\n" + "\n" + Stomp.NULL; stompConnection.sendFrame(connectFrame); String f = stompConnection.receiveFrame(); LOG.debug("Broker sent: " + f); assertTrue(f.startsWith("CONNECTED")); assertTrue(f.indexOf("version:1.0") >= 0); assertTrue(f.indexOf("session:") >= 0); } @Test(timeout = 60000) public void testConnectWithInvalidFallback() throws Exception { String connectFrame = "STOMP\n" + "login:system\n" + "passcode:manager\n" + "accept-version:9.0,10.1\n" + "host:localhost\n" + "\n" + Stomp.NULL; stompConnection.sendFrame(connectFrame); String f = stompConnection.receiveFrame(); LOG.debug("Broker sent: " + f); assertTrue(f.startsWith("ERROR")); assertTrue(f.indexOf("version") >= 0); assertTrue(f.indexOf("message:") >= 0); } @Test(timeout = 60000) public void testHeartbeats() throws Exception { String connectFrame = "STOMP\n" + "login:system\n" + "passcode:manager\n" + "accept-version:1.1\n" + "heart-beat:0,1000\n" + "host:localhost\n" + "\n" + Stomp.NULL; stompConnection.sendFrame(connectFrame); String f = stompConnection.receiveFrame().trim(); LOG.info("Broker sent: " + f); assertTrue("Failed to receive a connected frame.", f.startsWith("CONNECTED")); assertTrue("Frame should have a versoion 1.1 header.", f.indexOf("version:1.1") >= 0); assertTrue("Frame should have a heart beat header.", f.indexOf("heart-beat:") >= 0); assertTrue("Frame should have a session header.", f.indexOf("session:") >= 0); stompConnection.getStompSocket().getOutputStream().write('\n'); DataInputStream in = new DataInputStream(stompConnection.getStompSocket().getInputStream()); in.read(); { long startTime = System.currentTimeMillis(); int input = in.read(); assertEquals("did not receive the correct hear beat value", '\n', input); long endTime = System.currentTimeMillis(); assertTrue("Broker did not send KeepAlive in time", (endTime - startTime) >= 900); } { long startTime = System.currentTimeMillis(); int input = in.read(); assertEquals("did not receive the correct hear beat value", '\n', input); long endTime = System.currentTimeMillis(); assertTrue("Broker did not send KeepAlive in time", (endTime - startTime) >= 900); } } @Test(timeout = 60000) public void testHeartbeatsDropsIdleConnection() throws Exception { String connectFrame = "STOMP\n" + "login:system\n" + "passcode:manager\n" + "accept-version:1.1\n" + "heart-beat:1000,0\n" + "host:localhost\n" + "\n" + Stomp.NULL; stompConnection.sendFrame(connectFrame); String f = stompConnection.receiveFrame(); assertTrue(f.startsWith("CONNECTED")); assertTrue(f.indexOf("version:1.1") >= 0); assertTrue(f.indexOf("heart-beat:") >= 0); assertTrue(f.indexOf("session:") >= 0); LOG.debug("Broker sent: " + f); long startTime = System.currentTimeMillis(); try { f = stompConnection.receiveFrame(); LOG.debug("Broker sent: " + f); fail(); } catch(Exception e) { } long endTime = System.currentTimeMillis(); assertTrue("Broker did close idle connection in time.", (endTime - startTime) >= 1000); } @Test(timeout = 60000) public void testHeartbeatsKeepsConnectionOpen() throws Exception { String connectFrame = "STOMP\n" + "login:system\n" + "passcode:manager\n" + "accept-version:1.1\n" + "heart-beat:2000,0\n" + "host:localhost\n" + "\n" + Stomp.NULL; stompConnection.sendFrame(connectFrame); String f = stompConnection.receiveFrame(); assertTrue(f.startsWith("CONNECTED")); assertTrue(f.indexOf("version:1.1") >= 0); assertTrue(f.indexOf("heart-beat:") >= 0); assertTrue(f.indexOf("session:") >= 0); LOG.debug("Broker sent: " + f); String message = "SEND\n" + "destination:/queue/" + getQueueName() + "\n\n" + "Hello World" + Stomp.NULL; stompConnection.sendFrame(message); ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor(); service.scheduleAtFixedRate(new Runnable() { @Override public void run() { try { LOG.info("Sending next KeepAlive"); stompConnection.keepAlive(); } catch (Exception e) { } } }, 1, 1, TimeUnit.SECONDS); TimeUnit.SECONDS.sleep(20); String frame = "SUBSCRIBE\n" + "destination:/queue/" + getQueueName() + "\n" + "id:12345\n" + "ack:auto\n\n" + Stomp.NULL; stompConnection.sendFrame(frame); StompFrame stompFrame = stompConnection.receive(); assertTrue(stompFrame.getAction().equals("MESSAGE")); service.shutdownNow(); } @Test(timeout = 60000) public void testSendAfterMissingHeartbeat() throws Exception { String connectFrame = "STOMP\n" + "login:system\n" + "passcode:manager\n" + "accept-version:1.1\n" + "heart-beat:1000,0\n" + "host:localhost\n" + "\n" + Stomp.NULL; stompConnection.sendFrame(connectFrame); String f = stompConnection.receiveFrame(); assertTrue(f.startsWith("CONNECTED")); assertTrue(f.indexOf("version:1.1") >= 0); assertTrue(f.indexOf("heart-beat:") >= 0); assertTrue(f.indexOf("session:") >= 0); LOG.debug("Broker sent: " + f); Wait.waitFor(new Wait.Condition() { @Override public boolean isSatisified() throws Exception { return getProxyToBroker().getCurrentConnectionsCount() == 0; } }, TimeUnit.SECONDS.toMillis(5), TimeUnit.MILLISECONDS.toMillis(25)); try { String message = "SEND\n" + "destination:/queue/" + getQueueName() + "\n" + "receipt:1\n\n" + "Hello World" + Stomp.NULL; stompConnection.sendFrame(message); stompConnection.receiveFrame(); fail("SEND frame has been accepted after missing heart beat"); } catch (Exception ex) { LOG.info(ex.getMessage()); } } @Test(timeout = 60000) public void testRejectInvalidHeartbeats1() throws Exception { String connectFrame = "STOMP\n" + "login:system\n" + "passcode:manager\n" + "accept-version:1.1\n" + "heart-beat:0\n" + "host:localhost\n" + "\n" + Stomp.NULL; stompConnection.sendFrame(connectFrame); String f = stompConnection.receiveFrame(); LOG.debug("Broker sent: " + f); assertTrue(f.startsWith("ERROR")); assertTrue(f.indexOf("heart-beat") >= 0); assertTrue(f.indexOf("message:") >= 0); } @Test(timeout = 60000) public void testRejectInvalidHeartbeats2() throws Exception { String connectFrame = "STOMP\n" + "login:system\n" + "passcode:manager\n" + "accept-version:1.1\n" + "heart-beat:T,0\n" + "host:localhost\n" + "\n" + Stomp.NULL; stompConnection.sendFrame(connectFrame); String f = stompConnection.receiveFrame(); LOG.debug("Broker sent: " + f); assertTrue(f.startsWith("ERROR")); assertTrue(f.indexOf("heart-beat") >= 0); assertTrue(f.indexOf("message:") >= 0); } @Test(timeout = 60000) public void testRejectInvalidHeartbeats3() throws Exception { String connectFrame = "STOMP\n" + "login:system\n" + "passcode:manager\n" + "accept-version:1.1\n" + "heart-beat:100,10,50\n" + "host:localhost\n" + "\n" + Stomp.NULL; stompConnection.sendFrame(connectFrame); String f = stompConnection.receiveFrame(); LOG.debug("Broker sent: " + f); assertTrue(f.startsWith("ERROR")); assertTrue(f.indexOf("heart-beat") >= 0); assertTrue(f.indexOf("message:") >= 0); } @Test(timeout = 60000) public void testSubscribeAndUnsubscribe() throws Exception { String connectFrame = "STOMP\n" + "login:system\n" + "passcode:manager\n" + "accept-version:1.1\n" + "host:localhost\n" + "\n" + Stomp.NULL; stompConnection.sendFrame(connectFrame); String f = stompConnection.receiveFrame(); LOG.debug("Broker sent: " + f); assertTrue(f.startsWith("CONNECTED")); String message = "SEND\n" + "destination:/queue/" + getQueueName() + "\n\n" + "Hello World" + Stomp.NULL; stompConnection.sendFrame(message); String frame = "SUBSCRIBE\n" + "destination:/queue/" + getQueueName() + "\n" + "id:12345\n" + "ack:auto\n\n" + Stomp.NULL; stompConnection.sendFrame(frame); StompFrame stompFrame = stompConnection.receive(); assertTrue(stompFrame.getAction().equals("MESSAGE")); frame = "UNSUBSCRIBE\n" + "destination:/queue/" + getQueueName() + "\n" + "receipt:1\n" + "id:12345\n\n" + Stomp.NULL; stompConnection.sendFrame(frame); stompFrame = stompConnection.receive(); assertTrue(stompFrame.getAction().equals("RECEIPT")); stompConnection.sendFrame(message); try { frame = stompConnection.receiveFrame(2000); LOG.info("Received frame: " + frame); fail("No message should have been received since subscription was removed"); } catch (SocketTimeoutException e) { } } @Test(timeout = 60000) public void testSubscribeWithNoId() throws Exception { String connectFrame = "STOMP\n" + "login:system\n" + "passcode:manager\n" + "accept-version:1.1\n" + "host:localhost\n" + "\n" + Stomp.NULL; stompConnection.sendFrame(connectFrame); String f = stompConnection.receiveFrame(); LOG.debug("Broker sent: " + f); assertTrue(f.startsWith("CONNECTED")); String frame = "SUBSCRIBE\n" + "destination:/queue/" + getQueueName() + "\n" + "ack:auto\n\n" + Stomp.NULL; stompConnection.sendFrame(frame); frame = stompConnection.receiveFrame(); assertTrue(frame.startsWith("ERROR")); } @Test(timeout = 60000) public void testUnsubscribeWithNoId() throws Exception { String connectFrame = "STOMP\n" + "login:system\n" + "passcode:manager\n" + "accept-version:1.1\n" + "host:localhost\n" + "\n" + Stomp.NULL; stompConnection.sendFrame(connectFrame); String f = stompConnection.receiveFrame(); LOG.debug("Broker sent: " + f); assertTrue(f.startsWith("CONNECTED")); String frame = "SUBSCRIBE\n" + "destination:/queue/" + getQueueName() + "\n" + "receipt:1\n" + "id:12345\n" + "ack:auto\n\n" + Stomp.NULL; stompConnection.sendFrame(frame); frame = stompConnection.receiveFrame(); assertTrue(frame.startsWith("RECEIPT")); frame = "UNSUBSCRIBE\n" + "destination:/queue/" + getQueueName() + "\n" + "\n\n" + Stomp.NULL; stompConnection.sendFrame(frame); frame = stompConnection.receiveFrame(); assertTrue(frame.startsWith("ERROR")); } @Test(timeout = 60000) public void testAckMessageWithId() throws Exception { String connectFrame = "STOMP\n" + "login:system\n" + "passcode:manager\n" + "accept-version:1.1\n" + "host:localhost\n" + "\n" + Stomp.NULL; stompConnection.sendFrame(connectFrame); String f = stompConnection.receiveFrame(); LOG.debug("Broker sent: " + f); assertTrue(f.startsWith("CONNECTED")); String message = "SEND\n" + "destination:/queue/" + getQueueName() + "\n\n" + "Hello World" + Stomp.NULL; stompConnection.sendFrame(message); String frame = "SUBSCRIBE\n" + "destination:/queue/" + getQueueName() + "\n" + "id:12345\n" + "ack:client\n\n" + Stomp.NULL; stompConnection.sendFrame(frame); StompFrame received = stompConnection.receive(); assertTrue(received.getAction().equals("MESSAGE")); frame = "ACK\n" + "subscription:12345\n" + "message-id:" + received.getHeaders().get("message-id") + "\n\n" + Stomp.NULL; stompConnection.sendFrame(frame); frame = "UNSUBSCRIBE\n" + "destination:/queue/" + getQueueName() + "\n" + "id:12345\n\n" + Stomp.NULL; stompConnection.sendFrame(frame); } @Test(timeout = 60000) public void testAckMessageWithNoId() throws Exception { String connectFrame = "STOMP\n" + "login:system\n" + "passcode:manager\n" + "accept-version:1.1\n" + "host:localhost\n" + "\n" + Stomp.NULL; stompConnection.sendFrame(connectFrame); String f = stompConnection.receiveFrame(); LOG.debug("Broker sent: " + f); assertTrue(f.startsWith("CONNECTED")); String message = "SEND\n" + "destination:/queue/" + getQueueName() + "\n\n" + "Hello World" + Stomp.NULL; stompConnection.sendFrame(message); String subscribe = "SUBSCRIBE\n" + "destination:/queue/" + getQueueName() + "\n" + "activemq.prefetchSize=1" + "\n" + "id:12345\n" + "ack:client\n\n" + Stomp.NULL; stompConnection.sendFrame(subscribe); StompFrame received = stompConnection.receive(); LOG.info("Received Frame: {}", received); assertTrue("Expected MESSAGE but got: " + received.getAction(), received.getAction().equals("MESSAGE")); String ack = "ACK\n" + "message-id:" + received.getHeaders().get("message-id") + "\n\n" + Stomp.NULL; stompConnection.sendFrame(ack); StompFrame error = stompConnection.receive(); LOG.info("Received Frame: {}", error); assertTrue("Expected ERROR but got: " + error.getAction(), error.getAction().equals("ERROR")); String unsub = "UNSUBSCRIBE\n" + "destination:/queue/" + getQueueName() + "\n" + "id:12345\n\n" + Stomp.NULL; stompConnection.sendFrame(unsub); } @Test(timeout = 60000) public void testSubscribeWithWildcardSubscription() throws Exception { String connectFrame = "STOMP\n" + "login:system\n" + "passcode:manager\n" + "accept-version:1.1\n" + "host:localhost\n" + "\n" + Stomp.NULL; stompConnection.sendFrame(connectFrame); String f = stompConnection.receiveFrame(); LOG.debug("Broker sent: " + f); assertTrue(f.startsWith("CONNECTED")); String message = "SEND\n" + "destination:/queue/a.b.c" + "\n\n" + "Hello World" + Stomp.NULL; stompConnection.sendFrame(message); message = "SEND\n" + "destination:/queue/a.b" + "\n\n" + "Hello World" + Stomp.NULL; stompConnection.sendFrame(message); String frame = "SUBSCRIBE\n" + "destination:/queue/a.b.>" + "\n" + "id:12345\n" + "ack:auto\n\n" + Stomp.NULL; stompConnection.sendFrame(frame); StompFrame received = stompConnection.receive(); assertNotNull(received); received = stompConnection.receive(); assertNotNull(received); } @Test(timeout = 60000) public void testQueueBrowerSubscription() throws Exception { final int MSG_COUNT = 10; String connectFrame = "STOMP\n" + "login:system\n" + "passcode:manager\n" + "accept-version:1.1\n" + "host:localhost\n" + "\n" + Stomp.NULL; stompConnection.sendFrame(connectFrame); String f = stompConnection.receiveFrame(); LOG.debug("Broker sent: " + f); assertTrue(f.startsWith("CONNECTED")); for(int i = 0; i < MSG_COUNT; ++i) { String message = "SEND\n" + "destination:/queue/" + getQueueName() + "\n" + "receipt:0\n" + "\n" + "Hello World {" + i + "}" + Stomp.NULL; stompConnection.sendFrame(message); StompFrame repsonse = stompConnection.receive(); assertEquals("0", repsonse.getHeaders().get(Stomp.Headers.Response.RECEIPT_ID)); } String subscribe = "SUBSCRIBE\n" + "destination:/queue/" + getQueueName() + "\n" + "id:12345\n" + "browser:true\n\n" + Stomp.NULL; stompConnection.sendFrame(subscribe); for(int i = 0; i < MSG_COUNT; ++i) { StompFrame message = stompConnection.receive(); assertEquals(Stomp.Responses.MESSAGE, message.getAction()); assertEquals("12345", message.getHeaders().get(Stomp.Headers.Message.SUBSCRIPTION)); } // We should now get a browse done message StompFrame browseDone = stompConnection.receive(); LOG.debug("Browse Done: " + browseDone.toString()); assertEquals(Stomp.Responses.MESSAGE, browseDone.getAction()); assertEquals("12345", browseDone.getHeaders().get(Stomp.Headers.Message.SUBSCRIPTION)); assertEquals("end", browseDone.getHeaders().get(Stomp.Headers.Message.BROWSER)); assertTrue(browseDone.getHeaders().get(Stomp.Headers.Message.DESTINATION) != null); String unsub = "UNSUBSCRIBE\n" + "destination:/queue/" + getQueueName() + "\n" + "receipt:1\n" + "id:12345\n\n" + Stomp.NULL; stompConnection.sendFrame(unsub); String receipt = stompConnection.receiveFrame(); assertTrue(receipt.contains("RECEIPT")); subscribe = "SUBSCRIBE\n" + "destination:/queue/" + getQueueName() + "\n" + "id:12345\n\n" + Stomp.NULL; stompConnection.sendFrame(subscribe); for(int i = 0; i < MSG_COUNT; ++i) { StompFrame message = stompConnection.receive(); assertEquals(Stomp.Responses.MESSAGE, message.getAction()); assertEquals("12345", message.getHeaders().get(Stomp.Headers.Message.SUBSCRIPTION)); } stompConnection.sendFrame(unsub); } @Test(timeout = 60000) public void testSendMessageWithStandardHeadersEncoded() throws Exception { MessageConsumer consumer = session.createConsumer(queue); String frame = "CONNECT\n" + "login:system\n" + "passcode:manager\n" + "accept-version:1.1" + "\n\n" + Stomp.NULL; stompConnection.sendFrame(frame); frame = stompConnection.receiveFrame(); assertTrue(frame.startsWith("CONNECTED")); frame = "SEND\n" + "correlation-id:c1\\:\\n\\23\n" + "priority:3\n" + "type:t34:5\n" + "JMSXGroupID:abc\n" + "foo:a\\bc\n" + "bar:123\n" + "destination:/queue/" + getQueueName() + "\n\n" + "Hello World" + Stomp.NULL; stompConnection.sendFrame(frame); TextMessage message = (TextMessage)consumer.receive(2500); assertNotNull(message); assertEquals("Hello World", message.getText()); assertEquals("JMSCorrelationID", "c1\\:\n\\23", message.getJMSCorrelationID()); assertEquals("getJMSType", "t34:5", message.getJMSType()); assertEquals("getJMSPriority", 3, message.getJMSPriority()); assertEquals("foo", "a\\bc", message.getStringProperty("foo")); assertEquals("bar", "123", message.getStringProperty("bar")); assertEquals("JMSXGroupID", "abc", message.getStringProperty("JMSXGroupID")); ActiveMQTextMessage amqMessage = (ActiveMQTextMessage)message; assertEquals("GroupID", "abc", amqMessage.getGroupID()); } @Test(timeout = 60000) public void testSendMessageWithRepeatedEntries() throws Exception { MessageConsumer consumer = session.createConsumer(queue); String frame = "CONNECT\n" + "login:system\n" + "passcode:manager\n" + "accept-version:1.1" + "\n\n" + Stomp.NULL; stompConnection.sendFrame(frame); frame = stompConnection.receiveFrame(); assertTrue(frame.startsWith("CONNECTED")); frame = "SEND\n" + "value:newest" + "\n" + "value:older" + "\n" + "value:oldest" + "\n" + "destination:/queue/" + getQueueName() + "\n\n" + "Hello World" + Stomp.NULL; stompConnection.sendFrame(frame); TextMessage message = (TextMessage)consumer.receive(2500); assertNotNull(message); assertEquals("Hello World", message.getText()); assertEquals("newest", message.getStringProperty("value")); } @Test(timeout = 60000) public void testSubscribeWithMessageSentWithEncodedProperties() throws Exception { String frame = "CONNECT\n" + "login:system\n" + "passcode:manager\n" + "accept-version:1.1" + "\n\n" + Stomp.NULL; stompConnection.sendFrame(frame); frame = stompConnection.receiveFrame(); assertTrue(frame.startsWith("CONNECTED")); frame = "SUBSCRIBE\n" + "destination:/queue/" + getQueueName() + "\n" + "id:12345\n" + "ack:auto\n\n" + Stomp.NULL; stompConnection.sendFrame(frame); MessageProducer producer = session.createProducer(queue); TextMessage message = session.createTextMessage("Hello World"); message.setStringProperty("s", "\\value:"); producer.send(message); frame = stompConnection.receiveFrame(); assertTrue("" + frame, frame.startsWith("MESSAGE")); int start = frame.indexOf("\ns:") + 3; final String expectedEncoded = "\\\\value\\c"; final String headerVal = frame.substring(start, start + expectedEncoded.length()); assertEquals("" + frame, expectedEncoded, headerVal); } @Test(timeout = 60000) public void testNackMessage() throws Exception { String connectFrame = "STOMP\n" + "login:system\n" + "passcode:manager\n" + "accept-version:1.1\n" + "host:localhost\n" + "\n" + Stomp.NULL; stompConnection.sendFrame(connectFrame); String f = stompConnection.receiveFrame(); LOG.debug("Broker sent: " + f); assertTrue(f.startsWith("CONNECTED")); String message = "SEND\n" + "destination:/queue/" + getQueueName() + "\npersistent:true\n\n" + "Hello World" + Stomp.NULL; stompConnection.sendFrame(message); String frame = "SUBSCRIBE\n" + "destination:/queue/" + getQueueName() + "\n" + "id:12345\n" + "ack:client\n\n" + Stomp.NULL; stompConnection.sendFrame(frame); StompFrame received = stompConnection.receive(); assertTrue(received.getAction().equals("MESSAGE")); // nack it frame = "NACK\n" + "subscription:12345\n" + "message-id:" + received.getHeaders().get("message-id") + "\n\n" + Stomp.NULL; stompConnection.sendFrame(frame); frame = "UNSUBSCRIBE\n" + "destination:/queue/" + getQueueName() + "\n" + "id:12345\n\n" + Stomp.NULL; stompConnection.sendFrame(frame); //consume it from dlq frame = "SUBSCRIBE\n" + "destination:/queue/ActiveMQ.DLQ\n" + "id:12345\n" + "ack:client\n\n" + Stomp.NULL; stompConnection.sendFrame(frame); StompFrame receivedDLQ = stompConnection.receive(200); assertEquals(receivedDLQ.getHeaders().get("message-id"), received.getHeaders().get("message-id")); frame = "ACK\n" + "subscription:12345\n" + "message-id:" + received.getHeaders().get("message-id") + "\n\n" + Stomp.NULL; stompConnection.sendFrame(frame); frame = "UNSUBSCRIBE\n" + "destination:/queue/ActiveMQ.DLQ\n" + "id:12345\n\n" + Stomp.NULL; stompConnection.sendFrame(frame); } @Test(timeout = 60000) public void testHeaderValuesAreNotWSTrimmed() throws Exception { stompConnection.setVersion(Stomp.V1_1); String connectFrame = "STOMP\n" + "login:system\n" + "passcode:manager\n" + "accept-version:1.1\n" + "host:localhost\n" + "\n" + Stomp.NULL; stompConnection.sendFrame(connectFrame); String f = stompConnection.receiveFrame(); LOG.debug("Broker sent: " + f); assertTrue(f.startsWith("CONNECTED")); String message = "SEND\n" + "destination:/queue/" + getQueueName() + "\ntest1: value" + "\ntest2:value " + "\ntest3: value " + "\n\n" + "Hello World" + Stomp.NULL; stompConnection.sendFrame(message); String frame = "SUBSCRIBE\n" + "destination:/queue/" + getQueueName() + "\n" + "id:12345\n" + "ack:auto\n\n" + Stomp.NULL; stompConnection.sendFrame(frame); StompFrame received = stompConnection.receive(); assertTrue(received.getAction().equals("MESSAGE")); assertEquals(" value", received.getHeaders().get("test1")); assertEquals("value ", received.getHeaders().get("test2")); assertEquals(" value ", received.getHeaders().get("test3")); frame = "UNSUBSCRIBE\n" + "destination:/queue/" + getQueueName() + "\n" + "id:12345\n\n" + Stomp.NULL; stompConnection.sendFrame(frame); } @Test(timeout = 60000) public void testDurableSubAndUnSubOnTwoTopics() throws Exception { stompConnection.setVersion(Stomp.V1_1); String domain = "org.apache.activemq"; ObjectName brokerName = new ObjectName(domain + ":type=Broker,brokerName=localhost"); BrokerViewMBean view = (BrokerViewMBean)brokerService.getManagementContext().newProxyInstance(brokerName, BrokerViewMBean.class, true); String connectFrame = "STOMP\n" + "login:system\n" + "passcode:manager\n" + "accept-version:1.1\n" + "host:localhost\n" + "client-id:test\n" + "\n" + Stomp.NULL; stompConnection.sendFrame(connectFrame); String frame = stompConnection.receiveFrame(); LOG.debug("Broker sent: " + frame); assertTrue(frame.startsWith("CONNECTED")); assertEquals(view.getDurableTopicSubscribers().length, 0); // subscribe to first destination durably frame = "SUBSCRIBE\n" + "destination:/topic/" + getQueueName() + "1" + "\n" + "ack:auto\n" + "receipt:1\n" + "id:durablesub-1\n" + "activemq.subscriptionName:test1\n\n" + Stomp.NULL; stompConnection.sendFrame(frame); StompFrame receipt = stompConnection.receive(); LOG.debug("Broker sent: " + receipt); assertTrue(receipt.getAction().startsWith("RECEIPT")); assertEquals("1", receipt.getHeaders().get("receipt-id")); assertEquals(view.getDurableTopicSubscribers().length, 1); // subscribe to second destination durably frame = "SUBSCRIBE\n" + "destination:/topic/" + getQueueName() + "2" + "\n" + "ack:auto\n" + "receipt:2\n" + "id:durablesub-2\n" + "activemq.subscriptionName:test2\n\n" + Stomp.NULL; stompConnection.sendFrame(frame); receipt = stompConnection.receive(); LOG.debug("Broker sent: " + receipt); assertTrue(receipt.getAction().startsWith("RECEIPT")); assertEquals("2", receipt.getHeaders().get("receipt-id")); assertEquals(view.getDurableTopicSubscribers().length, 2); frame = "DISCONNECT\nclient-id:test\n\n" + Stomp.NULL; stompConnection.sendFrame(frame); Wait.waitFor(new Wait.Condition() { @Override public boolean isSatisified() throws Exception { return getProxyToBroker().getCurrentConnectionsCount() == 0; } }, TimeUnit.SECONDS.toMillis(5), TimeUnit.MILLISECONDS.toMillis(25)); // reconnect and send some messages to the offline subscribers and then try to get // them after subscribing again. stompConnect(); stompConnection.sendFrame(connectFrame); frame = stompConnection.receiveFrame(); LOG.debug("Broker sent: " + frame); assertTrue(frame.startsWith("CONNECTED")); assertEquals(view.getDurableTopicSubscribers().length, 0); assertEquals(view.getInactiveDurableTopicSubscribers().length, 2); // unsubscribe from topic 1 frame = "UNSUBSCRIBE\n" + "destination:/topic/" + getQueueName() + "1\n" + "id:durablesub-1\n" + "receipt:3\n" + "activemq.subscriptionName:test1\n\n" + Stomp.NULL; stompConnection.sendFrame(frame); receipt = stompConnection.receive(); LOG.debug("Broker sent: " + frame); assertTrue(receipt.getAction().startsWith("RECEIPT")); assertEquals("3", receipt.getHeaders().get("receipt-id")); assertEquals(view.getInactiveDurableTopicSubscribers().length, 1); // unsubscribe from topic 2 frame = "UNSUBSCRIBE\n" + "destination:/topic/" + getQueueName() + "2\n" + "id:durablesub-2\n" + "receipt:4\n" + "activemq.subscriptionName:test2\n\n" + Stomp.NULL; stompConnection.sendFrame(frame); receipt = stompConnection.receive(); LOG.debug("Broker sent: " + frame); assertTrue(receipt.getAction().startsWith("RECEIPT")); assertEquals("4", receipt.getHeaders().get("receipt-id")); assertEquals(view.getInactiveDurableTopicSubscribers().length, 0); } @Test(timeout = 60000) public void testDurableSubAndUnSubFlow() throws Exception { stompConnection.setVersion(Stomp.V1_1); String domain = "org.apache.activemq"; ObjectName brokerName = new ObjectName(domain + ":type=Broker,brokerName=localhost"); BrokerViewMBean view = (BrokerViewMBean)brokerService.getManagementContext().newProxyInstance(brokerName, BrokerViewMBean.class, true); String connectFrame = "STOMP\n" + "login:system\n" + "passcode:manager\n" + "accept-version:1.1\n" + "host:localhost\n" + "client-id:test\n" + "\n" + Stomp.NULL; stompConnection.sendFrame(connectFrame); String frame = stompConnection.receiveFrame(); LOG.debug("Broker sent: " + frame); assertTrue(frame.startsWith("CONNECTED")); assertEquals(view.getDurableTopicSubscribers().length, 0); // subscribe to first destination durably frame = "SUBSCRIBE\n" + "destination:/topic/" + getQueueName() + "1" + "\n" + "ack:auto\n" + "receipt:1\n" + "id:durablesub-1\n" + "activemq.subscriptionName:test1\n\n" + Stomp.NULL; stompConnection.sendFrame(frame); StompFrame receipt = stompConnection.receive(); LOG.debug("Broker sent: " + receipt); assertTrue(receipt.getAction().startsWith("RECEIPT")); assertEquals("1", receipt.getHeaders().get("receipt-id")); assertEquals(view.getDurableTopicSubscribers().length, 1); // attempt to remove the durable subscription while there is an active subscription frame = "UNSUBSCRIBE\n" + "destination:/topic/" + getQueueName() + "1\n" + "id:durablesub-1\n" + "receipt:3\n" + "activemq.subscriptionName:test1\n\n" + Stomp.NULL; stompConnection.sendFrame(frame); receipt = stompConnection.receive(); LOG.debug("Broker sent: " + receipt); assertTrue(receipt.getAction().startsWith("ERROR")); assertEquals("3", receipt.getHeaders().get("receipt-id")); assertEquals(view.getInactiveDurableTopicSubscribers().length, 0); assertEquals(view.getDurableTopicSubscribers().length, 1); // attempt to remove the subscriber leaving the durable sub in place. frame = "UNSUBSCRIBE\n" + "destination:/topic/" + getQueueName() + "1\n" + "id:durablesub-1\n" + "receipt:4\n\n" + Stomp.NULL; stompConnection.sendFrame(frame); receipt = stompConnection.receive(); LOG.debug("Broker sent: " + receipt); assertTrue(receipt.getAction().startsWith("RECEIPT")); assertEquals("4", receipt.getHeaders().get("receipt-id")); assertEquals(view.getInactiveDurableTopicSubscribers().length, 1); assertEquals(view.getDurableTopicSubscribers().length, 0); // attempt to remove the durable subscription which should succeed since there are no // active durable subscribers frame = "UNSUBSCRIBE\n" + "destination:/topic/" + getQueueName() + "1\n" + "id:durablesub-1\n" + "receipt:5\n" + "activemq.subscriptionName:test1\n\n" + Stomp.NULL; stompConnection.sendFrame(frame); receipt = stompConnection.receive(); LOG.debug("Broker sent: " + receipt); assertTrue(receipt.getAction().startsWith("RECEIPT")); assertEquals("5", receipt.getHeaders().get("receipt-id")); assertEquals(view.getInactiveDurableTopicSubscribers().length, 0); assertEquals(view.getDurableTopicSubscribers().length, 0); } @Test(timeout = 60000) public void testMultipleDurableSubsWithOfflineMessages() throws Exception { stompConnection.setVersion(Stomp.V1_1); final BrokerViewMBean view = getProxyToBroker(); String connectFrame = "STOMP\n" + "login:system\n" + "passcode:manager\n" + "accept-version:1.1\n" + "host:localhost\n" + "client-id:test\n" + "\n" + Stomp.NULL; stompConnection.sendFrame(connectFrame); String frame = stompConnection.receiveFrame(); LOG.debug("Broker sent: " + frame); assertTrue(frame.startsWith("CONNECTED")); assertEquals(view.getDurableTopicSubscribers().length, 0); // subscribe to first destination durably frame = "SUBSCRIBE\n" + "destination:/topic/" + getQueueName() + "1" + "\n" + "ack:auto\n" + "receipt:1\n" + "id:durablesub-1\n" + "activemq.subscriptionName:test1\n\n" + Stomp.NULL; stompConnection.sendFrame(frame); StompFrame receipt = stompConnection.receive(); LOG.debug("Broker sent: " + receipt); assertTrue(receipt.getAction().startsWith("RECEIPT")); assertEquals("1", receipt.getHeaders().get("receipt-id")); assertEquals(view.getDurableTopicSubscribers().length, 1); // subscribe to second destination durably frame = "SUBSCRIBE\n" + "destination:/topic/" + getQueueName() + "2" + "\n" + "ack:auto\n" + "receipt:2\n" + "id:durablesub-2\n" + "activemq.subscriptionName:test2\n\n" + Stomp.NULL; stompConnection.sendFrame(frame); receipt = stompConnection.receive(); LOG.debug("Broker sent: " + receipt); assertTrue(receipt.getAction().startsWith("RECEIPT")); assertEquals("2", receipt.getHeaders().get("receipt-id")); assertEquals(view.getDurableTopicSubscribers().length, 2); frame = "DISCONNECT\nclient-id:test\n\n" + Stomp.NULL; stompConnection.sendFrame(frame); assertTrue(Wait.waitFor(new Wait.Condition() { @Override public boolean isSatisified() throws Exception { return view.getCurrentConnectionsCount() == 1; } }, TimeUnit.SECONDS.toMillis(5), TimeUnit.MILLISECONDS.toMillis(25))); // reconnect and send some messages to the offline subscribers and then try to get // them after subscribing again. stompConnect(); stompConnection.sendFrame(connectFrame); frame = stompConnection.receiveFrame(); LOG.debug("Broker sent: " + frame); assertTrue(frame.contains("CONNECTED")); assertEquals(view.getDurableTopicSubscribers().length, 0); assertEquals(view.getInactiveDurableTopicSubscribers().length, 2); frame = "SEND\n" + "destination:/topic/" + getQueueName() + "1\n" + "receipt:10\n" + "\n" + "Hello World 1" + Stomp.NULL; stompConnection.sendFrame(frame); receipt = stompConnection.receive(); assertEquals("10", receipt.getHeaders().get(Stomp.Headers.Response.RECEIPT_ID)); frame = "SEND\n" + "destination:/topic/" + getQueueName() + "2\n" + "receipt:11\n" + "\n" + "Hello World 2" + Stomp.NULL; stompConnection.sendFrame(frame); receipt = stompConnection.receive(); assertEquals("11", receipt.getHeaders().get(Stomp.Headers.Response.RECEIPT_ID)); // subscribe to first destination durably frame = "SUBSCRIBE\n" + "destination:/topic/" + getQueueName() + "1" + "\n" + "ack:auto\n" + "receipt:3\n" + "id:durablesub-1\n" + "activemq.subscriptionName:test1\n\n" + Stomp.NULL; stompConnection.sendFrame(frame); receipt = stompConnection.receive(); LOG.debug("Broker sent: " + receipt); assertTrue(receipt.getAction().startsWith("RECEIPT")); assertEquals("3", receipt.getHeaders().get("receipt-id")); assertEquals(view.getDurableTopicSubscribers().length, 1); StompFrame message = stompConnection.receive(); assertEquals(Stomp.Responses.MESSAGE, message.getAction()); assertEquals("durablesub-1", message.getHeaders().get(Stomp.Headers.Message.SUBSCRIPTION)); assertEquals(view.getDurableTopicSubscribers().length, 1); assertEquals(view.getInactiveDurableTopicSubscribers().length, 1); // subscribe to second destination durably frame = "SUBSCRIBE\n" + "destination:/topic/" + getQueueName() + "2" + "\n" + "ack:auto\n" + "receipt:4\n" + "id:durablesub-2\n" + "activemq.subscriptionName:test2\n\n" + Stomp.NULL; stompConnection.sendFrame(frame); receipt = stompConnection.receive(); LOG.debug("Broker sent: " + receipt); assertTrue(receipt.getAction().startsWith("RECEIPT")); assertEquals("4", receipt.getHeaders().get("receipt-id")); assertEquals(view.getDurableTopicSubscribers().length, 2); message = stompConnection.receive(); assertEquals(Stomp.Responses.MESSAGE, message.getAction()); assertEquals("durablesub-2", message.getHeaders().get(Stomp.Headers.Message.SUBSCRIPTION)); assertEquals(view.getDurableTopicSubscribers().length, 2); assertEquals(view.getInactiveDurableTopicSubscribers().length, 0); } }