/* * 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.artemis.tests.integration.stomp.v12; import javax.jms.BytesMessage; import javax.jms.JMSException; import javax.jms.Message; import javax.jms.MessageConsumer; import javax.jms.MessageListener; import javax.jms.MessageProducer; import javax.jms.TextMessage; import java.io.IOException; import java.nio.channels.ClosedChannelException; import java.nio.charset.StandardCharsets; import java.util.UUID; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.apache.activemq.artemis.api.core.SimpleString; import org.apache.activemq.artemis.core.protocol.stomp.Stomp; import org.apache.activemq.artemis.api.core.RoutingType; import org.apache.activemq.artemis.core.settings.impl.AddressSettings; import org.apache.activemq.artemis.tests.integration.IntegrationTestLogger; import org.apache.activemq.artemis.tests.integration.stomp.StompTestBase; import org.apache.activemq.artemis.tests.integration.stomp.util.ClientStompFrame; import org.apache.activemq.artemis.tests.integration.stomp.util.StompClientConnection; import org.apache.activemq.artemis.tests.integration.stomp.util.StompClientConnectionFactory; import org.apache.activemq.artemis.tests.integration.stomp.util.StompClientConnectionV11; import org.apache.activemq.artemis.tests.integration.stomp.util.StompClientConnectionV12; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; /** * Testing Stomp version 1.2 functionalities */ public class StompV12Test extends StompTestBase { private static final transient IntegrationTestLogger log = IntegrationTestLogger.LOGGER; public static final String CLIENT_ID = "myclientid"; private StompClientConnectionV12 conn; @Override @Before public void setUp() throws Exception { super.setUp(); conn = (StompClientConnectionV12) StompClientConnectionFactory.createClientConnection("1.2", hostname, port); } @Override @After public void tearDown() throws Exception { try { boolean connected = conn != null && conn.isConnected(); log.debug("Connection 1.2 : " + connected); if (connected) { conn.disconnect(); } } finally { super.tearDown(); } } @Test public void testConnection() throws Exception { server.getActiveMQServer().getConfiguration().setSecurityEnabled(true); StompClientConnection connection = StompClientConnectionFactory.createClientConnection("1.0", hostname, port); connection.connect(defUser, defPass); Assert.assertTrue(connection.isConnected()); Assert.assertEquals("1.0", connection.getVersion()); connection.disconnect(); connection = StompClientConnectionFactory.createClientConnection("1.2", hostname, port); connection.connect(defUser, defPass); Assert.assertTrue(connection.isConnected()); Assert.assertEquals("1.2", connection.getVersion()); connection.disconnect(); connection = StompClientConnectionFactory.createClientConnection("1.2", hostname, port); connection.connect(); Assert.assertFalse(connection.isConnected()); //new way of connection StompClientConnectionV11 conn = (StompClientConnectionV11) StompClientConnectionFactory.createClientConnection("1.1", hostname, port); conn.connect1(defUser, defPass); Assert.assertTrue(conn.isConnected()); conn.disconnect(); } @Test public void testConnectionAsInSpec() throws Exception { StompClientConnection conn = StompClientConnectionFactory.createClientConnection("1.0", hostname, port); ClientStompFrame frame = conn.createFrame(Stomp.Commands.CONNECT); frame.addHeader(Stomp.Headers.Connect.LOGIN, this.defUser); frame.addHeader(Stomp.Headers.Connect.PASSCODE, this.defPass); frame.addHeader(Stomp.Headers.ACCEPT_VERSION, "1.2"); frame.addHeader(Stomp.Headers.Connect.HOST, "127.0.0.1"); ClientStompFrame reply = conn.sendFrame(frame); Assert.assertEquals(Stomp.Responses.CONNECTED, reply.getCommand()); Assert.assertEquals("1.2", reply.getHeader(Stomp.Headers.Error.VERSION)); conn.disconnect(); //need 1.2 client conn = StompClientConnectionFactory.createClientConnection("1.2", hostname, port); frame = conn.createFrame(Stomp.Commands.STOMP); frame.addHeader(Stomp.Headers.Connect.LOGIN, this.defUser); frame.addHeader(Stomp.Headers.Connect.PASSCODE, this.defPass); frame.addHeader(Stomp.Headers.ACCEPT_VERSION, "1.2"); frame.addHeader(Stomp.Headers.Connect.HOST, "127.0.0.1"); reply = conn.sendFrame(frame); Assert.assertEquals(Stomp.Responses.CONNECTED, reply.getCommand()); Assert.assertEquals("1.2", reply.getHeader(Stomp.Headers.Error.VERSION)); conn.disconnect(); } @Test public void testNegotiation() throws Exception { StompClientConnection conn = StompClientConnectionFactory.createClientConnection("1.0", hostname, port); // case 1 accept-version absent. It is a 1.0 connect ClientStompFrame frame = conn.createFrame(Stomp.Commands.CONNECT); frame.addHeader(Stomp.Headers.Connect.HOST, "127.0.0.1"); frame.addHeader(Stomp.Headers.Connect.LOGIN, this.defUser); frame.addHeader(Stomp.Headers.Connect.PASSCODE, this.defPass); ClientStompFrame reply = conn.sendFrame(frame); Assert.assertEquals(Stomp.Responses.CONNECTED, reply.getCommand()); //reply headers: version, session, server Assert.assertEquals(null, reply.getHeader(Stomp.Headers.Error.VERSION)); conn.disconnect(); // case 2 accept-version=1.0, result: 1.0 conn = StompClientConnectionFactory.createClientConnection("1.1", hostname, port); frame = conn.createFrame(Stomp.Commands.CONNECT); frame.addHeader(Stomp.Headers.ACCEPT_VERSION, "1.0"); frame.addHeader(Stomp.Headers.Connect.HOST, "127.0.0.1"); frame.addHeader(Stomp.Headers.Connect.LOGIN, this.defUser); frame.addHeader(Stomp.Headers.Connect.PASSCODE, this.defPass); reply = conn.sendFrame(frame); Assert.assertEquals(Stomp.Responses.CONNECTED, reply.getCommand()); //reply headers: version, session, server Assert.assertEquals("1.0", reply.getHeader(Stomp.Headers.Error.VERSION)); conn.disconnect(); // case 3 accept-version=1.1, result: 1.1 conn = StompClientConnectionFactory.createClientConnection("1.1", hostname, port); frame = conn.createFrame(Stomp.Commands.CONNECT); frame.addHeader(Stomp.Headers.ACCEPT_VERSION, "1.1"); frame.addHeader(Stomp.Headers.Connect.HOST, "127.0.0.1"); frame.addHeader(Stomp.Headers.Connect.LOGIN, this.defUser); frame.addHeader(Stomp.Headers.Connect.PASSCODE, this.defPass); reply = conn.sendFrame(frame); Assert.assertEquals(Stomp.Responses.CONNECTED, reply.getCommand()); //reply headers: version, session, server Assert.assertEquals("1.1", reply.getHeader(Stomp.Headers.Error.VERSION)); conn.disconnect(); // case 4 accept-version=1.0,1.1,1.3, result 1.2 conn = StompClientConnectionFactory.createClientConnection("1.1", hostname, port); frame = conn.createFrame(Stomp.Commands.CONNECT); frame.addHeader(Stomp.Headers.ACCEPT_VERSION, "1.0,1.1,1.3"); frame.addHeader(Stomp.Headers.Connect.HOST, "127.0.0.1"); frame.addHeader(Stomp.Headers.Connect.LOGIN, this.defUser); frame.addHeader(Stomp.Headers.Connect.PASSCODE, this.defPass); reply = conn.sendFrame(frame); Assert.assertEquals(Stomp.Responses.CONNECTED, reply.getCommand()); //reply headers: version, session, server Assert.assertEquals("1.1", reply.getHeader(Stomp.Headers.Error.VERSION)); conn.disconnect(); // case 5 accept-version=1.3, result error conn = StompClientConnectionFactory.createClientConnection("1.1", hostname, port); frame = conn.createFrame(Stomp.Commands.CONNECT); frame.addHeader(Stomp.Headers.ACCEPT_VERSION, "1.3"); frame.addHeader(Stomp.Headers.Connect.HOST, "127.0.0.1"); frame.addHeader(Stomp.Headers.Connect.LOGIN, this.defUser); frame.addHeader(Stomp.Headers.Connect.PASSCODE, this.defPass); reply = conn.sendFrame(frame); Assert.assertEquals(Stomp.Responses.ERROR, reply.getCommand()); System.out.println("Got error frame " + reply); } @Test public void testSendAndReceive() throws Exception { conn.connect(defUser, defPass); ClientStompFrame response = send(conn, getQueuePrefix() + getQueueName(), "text/plain", "Hello World 1!"); Assert.assertNull(response); send(conn, getQueuePrefix() + getQueueName(), "text/plain", "Hello World 2!", true); //subscribe StompClientConnection newConn = StompClientConnectionFactory.createClientConnection("1.2", hostname, port); newConn.connect(defUser, defPass); subscribe(newConn, "a-sub"); ClientStompFrame frame = newConn.receiveFrame(); System.out.println("received " + frame); Assert.assertEquals(Stomp.Responses.MESSAGE, frame.getCommand()); Assert.assertEquals("a-sub", frame.getHeader(Stomp.Headers.Message.SUBSCRIPTION)); //'auto' ack mode doesn't require 'ack' header Assert.assertNull(frame.getHeader(Stomp.Headers.Subscribe.ACK_MODE)); Assert.assertEquals(getQueuePrefix() + getQueueName(), frame.getHeader(Stomp.Headers.Subscribe.DESTINATION)); Assert.assertEquals("Hello World 1!", frame.getBody()); frame = newConn.receiveFrame(); System.out.println("received " + frame); //unsub unsubscribe(newConn, "a-sub"); newConn.disconnect(); } @Test public void testHeaderContentType() throws Exception { conn.connect(defUser, defPass); send(conn, getQueuePrefix() + getQueueName(), "application/xml", "Hello World 1!"); //subscribe StompClientConnection newConn = StompClientConnectionFactory.createClientConnection("1.1", hostname, port); newConn.connect(defUser, defPass); subscribe(newConn, "a-sub"); ClientStompFrame frame = newConn.receiveFrame(); System.out.println("received " + frame); Assert.assertEquals(Stomp.Responses.MESSAGE, frame.getCommand()); Assert.assertEquals("application/xml", frame.getHeader(Stomp.Headers.CONTENT_TYPE)); //unsub unsubscribe(newConn, "a-sub"); newConn.disconnect(); } @Test public void testHeaderContentLength() throws Exception { conn.connect(defUser, defPass); String body = "Hello World 1!"; String cLen = String.valueOf(body.getBytes(StandardCharsets.UTF_8).length); ClientStompFrame frame = conn.createFrame(Stomp.Commands.SEND) .addHeader(Stomp.Headers.Subscribe.DESTINATION, getQueuePrefix() + getQueueName()) .addHeader(Stomp.Headers.CONTENT_TYPE, "application/xml") .addHeader(Stomp.Headers.CONTENT_LENGTH, cLen) .setBody(body + "extra"); conn.sendFrame(frame); //subscribe StompClientConnection newConn = StompClientConnectionFactory.createClientConnection("1.2", hostname, port); newConn.connect(defUser, defPass); subscribe(newConn, "a-sub"); frame = newConn.receiveFrame(); Assert.assertEquals(Stomp.Responses.MESSAGE, frame.getCommand()); Assert.assertEquals(body, frame.getBody()); Assert.assertEquals(cLen, frame.getHeader(Stomp.Headers.CONTENT_LENGTH)); //send again without content-length header frame = conn.createFrame(Stomp.Commands.SEND) .addHeader(Stomp.Headers.Subscribe.DESTINATION, getQueuePrefix() + getQueueName()) .addHeader(Stomp.Headers.CONTENT_TYPE, "application/xml") .setBody(body + "extra"); conn.sendFrame(frame); //receive again. extra should received. frame = newConn.receiveFrame(); Assert.assertEquals(body + "extra", frame.getBody()); //although sender didn't send the content-length header, //the server should add it anyway Assert.assertEquals((body + "extra").getBytes(StandardCharsets.UTF_8).length, Integer.valueOf(frame.getHeader(Stomp.Headers.CONTENT_LENGTH)).intValue()); //unsub unsubscribe(newConn, "a-sub"); newConn.disconnect(); } //test that repetitive headers @Test public void testHeaderRepetitive() throws Exception { AddressSettings addressSettings = new AddressSettings(); addressSettings.setAutoCreateQueues(false); addressSettings.setAutoCreateAddresses(false); server.getActiveMQServer().getAddressSettingsRepository().addMatch("#", addressSettings); conn.connect(defUser, defPass); String body = "Hello World!"; String cLen = String.valueOf(body.getBytes(StandardCharsets.UTF_8).length); ClientStompFrame frame = conn.createFrame(Stomp.Commands.SEND) .addHeader(Stomp.Headers.Subscribe.DESTINATION, getQueuePrefix() + getQueueName()) .addHeader(Stomp.Headers.Subscribe.DESTINATION, "aNonexistentQueue") .addHeader(Stomp.Headers.CONTENT_TYPE, "application/xml") .addHeader(Stomp.Headers.CONTENT_LENGTH, cLen) .addHeader("foo", "value1") .addHeader("foo", "value2") .addHeader("foo", "value3"); frame.setBody(body); conn.sendFrame(frame); //subscribe StompClientConnection newConn = StompClientConnectionFactory.createClientConnection("1.2", hostname, port); newConn.connect(defUser, defPass); subscribe(newConn, "a-sub", null, null, true); frame = newConn.receiveFrame(); Assert.assertEquals(Stomp.Responses.MESSAGE, frame.getCommand()); Assert.assertEquals(body, frame.getBody()); System.out.println("received: " + frame); Assert.assertEquals("value1", frame.getHeader("foo")); //unsub unsubscribe(newConn, "a-sub", true); newConn.disconnect(); //should get error body = "Hello World!"; cLen = String.valueOf(body.getBytes(StandardCharsets.UTF_8).length); frame = conn.createFrame(Stomp.Commands.SEND) .addHeader(Stomp.Headers.Subscribe.DESTINATION, "aNonexistentQueue") .addHeader(Stomp.Headers.Subscribe.DESTINATION, getQueuePrefix() + getQueueName()) .addHeader(Stomp.Headers.CONTENT_TYPE, "application/xml") .addHeader(Stomp.Headers.CONTENT_LENGTH, cLen) .addHeader(Stomp.Headers.RECEIPT_REQUESTED, "1234") .setBody(body); ClientStompFrame reply = conn.sendFrame(frame); // TODO this is broken because queue auto-creation is always on Assert.assertEquals(Stomp.Responses.ERROR, reply.getCommand()); } //padding shouldn't be trimmed @Test public void testHeadersPadding() throws Exception { conn.connect(defUser, defPass); String body = "<p>Hello World!</p>"; String cLen = String.valueOf(body.getBytes(StandardCharsets.UTF_8).length); ClientStompFrame frame = conn.createFrame(Stomp.Commands.SEND) .addHeader(Stomp.Headers.Subscribe.DESTINATION, getQueuePrefix() + getQueueName()) .addHeader(Stomp.Headers.CONTENT_TYPE, "application/xml") .addHeader(Stomp.Headers.CONTENT_LENGTH, cLen) .addHeader(" header1", "value1 ") .addHeader(" header2", "value2 ") .addHeader("header3 ", " value3") .addHeader(" header4 ", " value4 ") .addHeader(" header 5 ", " value 5 ") .addHeader("header6", "\t value\t 6 \t") .setBody(body); conn.sendFrame(frame); //subscribe StompClientConnection newConn = StompClientConnectionFactory.createClientConnection("1.2", hostname, port); newConn.connect(defUser, defPass); subscribe(newConn, "a-sub"); frame = newConn.receiveFrame(); Assert.assertEquals(Stomp.Responses.MESSAGE, frame.getCommand()); Assert.assertEquals(body, frame.getBody()); System.out.println("received: " + frame); Assert.assertEquals(null, frame.getHeader("header1")); Assert.assertEquals("value1 ", frame.getHeader(" header1")); Assert.assertEquals("value2 ", frame.getHeader(" header2")); Assert.assertEquals(" value3", frame.getHeader("header3 ")); Assert.assertEquals(" value4 ", frame.getHeader(" header4 ")); Assert.assertEquals(" value 5 ", frame.getHeader(" header 5 ")); Assert.assertEquals("\t value\t 6 \t", frame.getHeader("header6")); //unsub unsubscribe(newConn, "a-sub"); newConn.disconnect(); } /** * Since 1.2 the CR ('\r') needs to be escaped. */ @Test public void testHeaderEncoding() throws Exception { conn.connect(defUser, defPass); String body = "Hello World 1!"; String cLen = String.valueOf(body.getBytes(StandardCharsets.UTF_8).length); String hKey = "\\rspecial-header\\\\\\n\\c\\r\\n"; String hVal = "\\c\\\\\\ngood\\n\\r"; ClientStompFrame frame = conn.createFrame(Stomp.Commands.SEND) .addHeader(Stomp.Headers.Subscribe.DESTINATION, getQueuePrefix() + getQueueName()) .addHeader(Stomp.Headers.CONTENT_TYPE, "application/xml") .addHeader(Stomp.Headers.CONTENT_LENGTH, cLen) .addHeader(hKey, hVal) .setBody(body); System.out.println("key: |" + hKey + "| val: |" + hVal + "|"); conn.sendFrame(frame); //subscribe StompClientConnection newConn = StompClientConnectionFactory.createClientConnection("1.2", hostname, port); newConn.connect(defUser, defPass); subscribe(newConn, "a-sub"); frame = newConn.receiveFrame(); System.out.println("received " + frame); Assert.assertEquals(Stomp.Responses.MESSAGE, frame.getCommand()); String value = frame.getHeader("\r" + "special-header" + "\\" + "\n" + ":" + "\r\n"); Assert.assertEquals(":" + "\\" + "\n" + "good\n\r", value); //unsub unsubscribe(newConn, "a-sub"); newConn.disconnect(); } /** * In 1.2, undefined escapes must cause a fatal protocol error. */ @Test public void testHeaderUndefinedEscape() throws Exception { conn.connect(defUser, defPass); ClientStompFrame frame = conn.createFrame("SEND"); String body = "Hello World 1!"; String cLen = String.valueOf(body.getBytes(StandardCharsets.UTF_8).length); frame.addHeader("destination", getQueuePrefix() + getQueueName()); frame.addHeader("content-type", "text/plain"); frame.addHeader("content-length", cLen); String hKey = "undefined-escape"; String hVal = "is\\ttab"; frame.addHeader(hKey, hVal); System.out.println("key: |" + hKey + "| val: |" + hVal + "|"); frame.setBody(body); conn.sendFrame(frame); ClientStompFrame error = conn.receiveFrame(); System.out.println("received " + error); String desc = "Should have received an ERROR for undefined escape sequence"; Assert.assertNotNull(desc, error); Assert.assertEquals(desc, "ERROR", error.getCommand()); waitDisconnect(conn); Assert.assertFalse("Should be disconnected in STOMP 1.2 after ERROR", conn.isConnected()); } @Test public void testHeartBeat() throws Exception { StompClientConnection conn = StompClientConnectionFactory.createClientConnection("1.2", hostname, port); //no heart beat at all if heat-beat absent ClientStompFrame frame = conn.createFrame(Stomp.Commands.CONNECT) .addHeader(Stomp.Headers.Connect.HOST, "127.0.0.1") .addHeader(Stomp.Headers.Connect.LOGIN, this.defUser) .addHeader(Stomp.Headers.Connect.PASSCODE, this.defPass); ClientStompFrame reply = conn.sendFrame(frame); Assert.assertEquals(Stomp.Responses.CONNECTED, reply.getCommand()); Thread.sleep(5000); Assert.assertEquals(0, conn.getFrameQueueSize()); conn.disconnect(); //no heart beat for (0,0) conn = StompClientConnectionFactory.createClientConnection("1.2", hostname, port); frame = conn.createFrame(Stomp.Commands.CONNECT) .addHeader(Stomp.Headers.Connect.HOST, "127.0.0.1") .addHeader(Stomp.Headers.Connect.LOGIN, this.defUser) .addHeader(Stomp.Headers.Connect.PASSCODE, this.defPass) .addHeader(Stomp.Headers.Connect.HEART_BEAT, "0,0") .addHeader(Stomp.Headers.ACCEPT_VERSION, "1.0,1.1,1.2"); reply = conn.sendFrame(frame); Assert.assertEquals(Stomp.Responses.CONNECTED, reply.getCommand()); Assert.assertEquals("0,30000", reply.getHeader(Stomp.Headers.Connect.HEART_BEAT)); Thread.sleep(5000); Assert.assertEquals(0, conn.getFrameQueueSize()); conn.disconnect(); //heart-beat (1,0), should receive a min client ping accepted by server conn = StompClientConnectionFactory.createClientConnection("1.2", hostname, port); frame = conn.createFrame(Stomp.Commands.CONNECT) .addHeader(Stomp.Headers.Connect.HOST, "127.0.0.1") .addHeader(Stomp.Headers.Connect.LOGIN, this.defUser) .addHeader(Stomp.Headers.Connect.PASSCODE, this.defPass) .addHeader(Stomp.Headers.Connect.HEART_BEAT, "1,0") .addHeader(Stomp.Headers.ACCEPT_VERSION, "1.0,1.2"); reply = conn.sendFrame(frame); Assert.assertEquals(Stomp.Responses.CONNECTED, reply.getCommand()); Assert.assertEquals("0,500", reply.getHeader(Stomp.Headers.Connect.HEART_BEAT)); Thread.sleep(2000); //now server side should be disconnected because we didn't send ping for 2 sec //send will fail try { send(conn, getQueuePrefix() + getQueueName(), "text/plain", "Hello World"); Assert.fail("connection should have been destroyed by now"); } catch (IOException e) { //ignore } //heart-beat (1,0), start a ping, then send a message, should be ok. conn = StompClientConnectionFactory.createClientConnection("1.2", hostname, port); frame = conn.createFrame(Stomp.Commands.CONNECT) .addHeader(Stomp.Headers.Connect.HOST, "127.0.0.1") .addHeader(Stomp.Headers.Connect.LOGIN, this.defUser) .addHeader(Stomp.Headers.Connect.PASSCODE, this.defPass) .addHeader(Stomp.Headers.Connect.HEART_BEAT, "1,0") .addHeader(Stomp.Headers.ACCEPT_VERSION, "1.0,1.1,1.2"); reply = conn.sendFrame(frame); Assert.assertEquals(Stomp.Responses.CONNECTED, reply.getCommand()); Assert.assertEquals("0,500", reply.getHeader(Stomp.Headers.Connect.HEART_BEAT)); conn.startPinger(500); Thread.sleep(2000); //send will be ok send(conn, getQueuePrefix() + getQueueName(), "text/plain", "Hello World"); conn.stopPinger(); conn.disconnect(); } //server ping @Test public void testHeartBeat2() throws Exception { //heart-beat (1,1) ClientStompFrame frame = conn.createFrame(Stomp.Commands.CONNECT) .addHeader(Stomp.Headers.Connect.HOST, "127.0.0.1") .addHeader(Stomp.Headers.Connect.LOGIN, this.defUser) .addHeader(Stomp.Headers.Connect.PASSCODE, this.defPass) .addHeader(Stomp.Headers.Connect.HEART_BEAT, "1,1") .addHeader(Stomp.Headers.ACCEPT_VERSION, "1.0,1.1,1.2"); ClientStompFrame reply = conn.sendFrame(frame); Assert.assertEquals(Stomp.Responses.CONNECTED, reply.getCommand()); Assert.assertEquals("500,500", reply.getHeader(Stomp.Headers.Connect.HEART_BEAT)); conn.disconnect(); //heart-beat (500,1000) conn = (StompClientConnectionV12) StompClientConnectionFactory.createClientConnection("1.2", hostname, port); frame = conn.createFrame(Stomp.Commands.CONNECT) .addHeader(Stomp.Headers.Connect.HOST, "127.0.0.1") .addHeader(Stomp.Headers.Connect.LOGIN, this.defUser) .addHeader(Stomp.Headers.Connect.PASSCODE, this.defPass) .addHeader(Stomp.Headers.Connect.HEART_BEAT, "500,1000") .addHeader(Stomp.Headers.ACCEPT_VERSION, "1.0,1.1,1.2"); reply = conn.sendFrame(frame); Assert.assertEquals(Stomp.Responses.CONNECTED, reply.getCommand()); Assert.assertEquals("1000,500", reply.getHeader(Stomp.Headers.Connect.HEART_BEAT)); conn.startPinger(500); Thread.sleep(10000); //now check the frame size int size = conn.getServerPingNumber(); System.out.println("ping received: " + size); Assert.assertTrue("size: " + size, size > 5); //now server side should be disconnected because we didn't send ping for 2 sec //send will be ok send(conn, getQueuePrefix() + getQueueName(), "text/plain", "Hello World"); conn.disconnect(); } @Test public void testSendWithHeartBeatsAndReceive() throws Exception { StompClientConnection newConn = null; try { ClientStompFrame frame = conn.createFrame(Stomp.Commands.CONNECT); frame.addHeader(Stomp.Headers.Connect.HOST, "127.0.0.1") .addHeader(Stomp.Headers.Connect.LOGIN, this.defUser) .addHeader(Stomp.Headers.Connect.PASSCODE, this.defPass) .addHeader(Stomp.Headers.Connect.HEART_BEAT, "500,1000") .addHeader(Stomp.Headers.ACCEPT_VERSION, "1.0,1.1,1.2"); conn.sendFrame(frame); conn.startPinger(500); for (int i = 0; i < 10; i++) { send(conn, getQueuePrefix() + getQueueName(), "text/plain", "Hello World " + i + "!"); Thread.sleep(500); } // subscribe newConn = StompClientConnectionFactory.createClientConnection("1.2", hostname, port); newConn.connect(defUser, defPass); subscribe(newConn, "a-sub"); int cnt = 0; frame = newConn.receiveFrame(); while (frame != null) { cnt++; Thread.sleep(500); frame = newConn.receiveFrame(5000); } Assert.assertEquals(10, cnt); // unsub unsubscribe(newConn, "a-sub"); } finally { if (newConn != null) newConn.disconnect(); conn.disconnect(); } } @Test public void testSendAndReceiveWithHeartBeats() throws Exception { conn.connect(defUser, defPass); for (int i = 0; i < 10; i++) { send(conn, getQueuePrefix() + getQueueName(), "text/plain", "Hello World " + i + "!"); Thread.sleep(500); } //subscribe StompClientConnection newConn = StompClientConnectionFactory.createClientConnection("1.1", hostname, port); try { ClientStompFrame frame = newConn.createFrame(Stomp.Commands.CONNECT) .addHeader(Stomp.Headers.Connect.HOST, "127.0.0.1") .addHeader(Stomp.Headers.Connect.LOGIN, this.defUser) .addHeader(Stomp.Headers.Connect.PASSCODE, this.defPass) .addHeader(Stomp.Headers.Connect.HEART_BEAT, "500,1000") .addHeader(Stomp.Headers.ACCEPT_VERSION, "1.0,1.1"); newConn.sendFrame(frame); newConn.startPinger(500); Thread.sleep(500); subscribe(newConn, "a-sub"); int cnt = 0; frame = newConn.receiveFrame(); while (frame != null) { cnt++; Thread.sleep(500); frame = newConn.receiveFrame(5000); } Assert.assertEquals(10, cnt); // unsub unsubscribe(newConn, "a-sub"); } finally { newConn.disconnect(); } } @Test public void testSendWithHeartBeatsAndReceiveWithHeartBeats() throws Exception { StompClientConnection newConn = null; try { ClientStompFrame frame = conn.createFrame(Stomp.Commands.CONNECT) .addHeader(Stomp.Headers.Connect.HOST, "127.0.0.1") .addHeader(Stomp.Headers.Connect.LOGIN, this.defUser) .addHeader(Stomp.Headers.Connect.PASSCODE, this.defPass) .addHeader(Stomp.Headers.Connect.HEART_BEAT, "500,1000") .addHeader(Stomp.Headers.ACCEPT_VERSION, "1.0,1.1,1.2"); conn.sendFrame(frame); conn.startPinger(500); for (int i = 0; i < 10; i++) { send(conn, getQueuePrefix() + getQueueName(), "text/plain", "Hello World " + i + "!"); Thread.sleep(500); } // subscribe newConn = StompClientConnectionFactory.createClientConnection("1.2", hostname, port); frame = newConn.createFrame(Stomp.Commands.CONNECT) .addHeader(Stomp.Headers.Connect.HOST, "127.0.0.1") .addHeader(Stomp.Headers.Connect.LOGIN, this.defUser) .addHeader(Stomp.Headers.Connect.PASSCODE, this.defPass) .addHeader(Stomp.Headers.Connect.HEART_BEAT, "500,1000") .addHeader(Stomp.Headers.ACCEPT_VERSION, "1.0,1.1,1.2"); newConn.sendFrame(frame); newConn.startPinger(500); Thread.sleep(500); subscribe(newConn, "a-sub"); int cnt = 0; frame = newConn.receiveFrame(); while (frame != null) { cnt++; Thread.sleep(500); frame = newConn.receiveFrame(5000); } Assert.assertEquals(10, cnt); // unsub unsubscribe(newConn, "a-sub"); } finally { if (newConn != null) newConn.disconnect(); conn.disconnect(); } } @Test public void testNack() throws Exception { conn.connect(defUser, defPass); subscribe(conn, "sub1", "client"); sendJmsMessage(getName()); ClientStompFrame frame = conn.receiveFrame(); String messageID = frame.getHeader(Stomp.Headers.Message.MESSAGE_ID); nack(conn, messageID); unsubscribe(conn, "sub1"); conn.disconnect(); //Nack makes the message be dropped. MessageConsumer consumer = session.createConsumer(queue); Message message = consumer.receive(1000); Assert.assertNull(message); } @Test public void testNackWithWrongSubId() throws Exception { conn.connect(defUser, defPass); subscribe(conn, "sub1", "client"); sendJmsMessage(getName()); ClientStompFrame frame = conn.receiveFrame(); String messageID = frame.getHeader(Stomp.Headers.Subscribe.ACK_MODE); nack(conn, messageID + "0"); ClientStompFrame error = conn.receiveFrame(); Assert.assertEquals(Stomp.Responses.ERROR, error.getCommand()); waitDisconnect(conn); Assert.assertFalse("Should be disconnected in STOMP 1.2 after ERROR", conn.isConnected()); //message should be still there MessageConsumer consumer = session.createConsumer(queue); Message message = consumer.receive(1000); Assert.assertNotNull(message); } @Test public void testNackWithWrongMessageId() throws Exception { conn.connect(defUser, defPass); subscribe(conn, "sub1", "client"); sendJmsMessage(getName()); ClientStompFrame frame = conn.receiveFrame(); Assert.assertNotNull(frame); Assert.assertNotNull(frame.getHeader(Stomp.Headers.Subscribe.ACK_MODE)); nack(conn, "someother"); ClientStompFrame error = conn.receiveFrame(); System.out.println("Receiver error: " + error); waitDisconnect(conn); Assert.assertFalse("Should be disconnected in STOMP 1.2 after ERROR", conn.isConnected()); //message should still there MessageConsumer consumer = session.createConsumer(queue); Message message = consumer.receive(1000); Assert.assertNotNull(message); } @Test public void testAck() throws Exception { conn.connect(defUser, defPass); subscribe(conn, "sub1", "client"); sendJmsMessage(getName()); ClientStompFrame frame = conn.receiveFrame(); String messageID = frame.getHeader(Stomp.Headers.Subscribe.ACK_MODE); Assert.assertNotNull(messageID); ack(conn, messageID); unsubscribe(conn, "sub1"); conn.disconnect(); //Nack makes the message be dropped. MessageConsumer consumer = session.createConsumer(queue); Message message = consumer.receive(1000); Assert.assertNull(message); } @Test public void testAckNoIDHeader() throws Exception { conn.connect(defUser, defPass); subscribe(conn, "sub1", "client-individual"); sendJmsMessage(getName()); ClientStompFrame frame = conn.receiveFrame(); String messageID = frame.getHeader(Stomp.Headers.Subscribe.ACK_MODE); Assert.assertNotNull(messageID); ClientStompFrame ackFrame = conn.createFrame(Stomp.Commands.ACK); conn.sendFrame(ackFrame); frame = conn.receiveFrame(); Assert.assertEquals(Stomp.Responses.ERROR, frame.getCommand()); waitDisconnect(conn); Assert.assertFalse("Should be disconnected in STOMP 1.2 after ERROR", conn.isConnected()); //message still there. MessageConsumer consumer = session.createConsumer(queue); Message message = consumer.receive(1000); Assert.assertNotNull(message); } @Test public void testAckWithWrongMessageId() throws Exception { conn.connect(defUser, defPass); subscribe(conn, "sub1", "client"); sendJmsMessage(getName()); ClientStompFrame frame = conn.receiveFrame(); Assert.assertNotNull(frame); ack(conn, "someother"); ClientStompFrame error = conn.receiveFrame(); System.out.println("Receiver error: " + error); waitDisconnect(conn); Assert.assertFalse("Should be disconnected in STOMP 1.2 after ERROR", conn.isConnected()); //message should still there MessageConsumer consumer = session.createConsumer(queue); Message message = consumer.receive(1000); Assert.assertNotNull(message); } @Test public void testErrorWithReceipt() throws Exception { conn.connect(defUser, defPass); subscribe(conn, "sub1", "client"); sendJmsMessage(getName()); ClientStompFrame frame = conn.receiveFrame(); String messageID = frame.getHeader(Stomp.Headers.Message.MESSAGE_ID); ClientStompFrame ackFrame = conn.createFrame(Stomp.Commands.ACK); //give it a wrong sub id ackFrame.addHeader(Stomp.Headers.Message.SUBSCRIPTION, "sub2"); ackFrame.addHeader(Stomp.Headers.Message.MESSAGE_ID, messageID); ackFrame.addHeader(Stomp.Headers.RECEIPT_REQUESTED, "answer-me"); ClientStompFrame error = conn.sendFrame(ackFrame); System.out.println("Receiver error: " + error); Assert.assertEquals(Stomp.Responses.ERROR, error.getCommand()); Assert.assertEquals("answer-me", error.getHeader("receipt-id")); waitDisconnect(conn); Assert.assertFalse("Should be disconnected in STOMP 1.2 after ERROR", conn.isConnected()); //message should still there MessageConsumer consumer = session.createConsumer(queue); Message message = consumer.receive(1000); Assert.assertNotNull(message); } @Test public void testErrorWithReceipt2() throws Exception { conn.connect(defUser, defPass); subscribe(conn, "sub1", "client"); sendJmsMessage(getName()); ClientStompFrame frame = conn.receiveFrame(); String messageID = frame.getHeader(Stomp.Headers.Message.MESSAGE_ID); ClientStompFrame ackFrame = conn.createFrame(Stomp.Commands.ACK); //give it a wrong sub id ackFrame.addHeader(Stomp.Headers.Message.SUBSCRIPTION, "sub1"); ackFrame.addHeader(Stomp.Headers.Message.MESSAGE_ID, String.valueOf(Long.valueOf(messageID) + 1)); ackFrame.addHeader(Stomp.Headers.RECEIPT_REQUESTED, "answer-me"); ClientStompFrame error = conn.sendFrame(ackFrame); System.out.println("Receiver error: " + error); Assert.assertEquals(Stomp.Responses.ERROR, error.getCommand()); Assert.assertEquals("answer-me", error.getHeader("receipt-id")); waitDisconnect(conn); Assert.assertFalse("Should be disconnected in STOMP 1.2 after ERROR", conn.isConnected()); //message should still there MessageConsumer consumer = session.createConsumer(queue); Message message = consumer.receive(1000); Assert.assertNotNull(message); } protected void waitDisconnect(StompClientConnectionV12 connection) throws Exception { long timeout = System.currentTimeMillis() + 10000; while (timeout > System.currentTimeMillis() && connection.isConnected()) { Thread.sleep(10); } } @Test public void testAckModeClient() throws Exception { conn.connect(defUser, defPass); subscribe(conn, "sub1", "client"); int num = 50; //send a bunch of messages for (int i = 0; i < num; i++) { this.sendJmsMessage("client-ack" + i); } ClientStompFrame frame = null; for (int i = 0; i < num; i++) { frame = conn.receiveFrame(); Assert.assertNotNull(frame); } //ack the last ack(conn, frame); unsubscribe(conn, "sub1"); conn.disconnect(); //no messages can be received. MessageConsumer consumer = session.createConsumer(queue); Message message = consumer.receive(1000); Assert.assertNull(message); } @Test public void testAckModeClient2() throws Exception { conn.connect(defUser, defPass); subscribe(conn, "sub1", "client"); int num = 50; //send a bunch of messages for (int i = 0; i < num; i++) { this.sendJmsMessage("client-ack" + i); } ClientStompFrame frame = null; for (int i = 0; i < num; i++) { frame = conn.receiveFrame(); Assert.assertNotNull(frame); //ack the 49th if (i == num - 2) { ack(conn, frame); } } unsubscribe(conn, "sub1"); conn.disconnect(); //one can be received. MessageConsumer consumer = session.createConsumer(queue); Message message = consumer.receive(1000); Assert.assertNotNull(message); message = consumer.receive(1000); Assert.assertNull(message); } //when ack is missing the mode default to auto @Test public void testAckModeDefault() throws Exception { conn.connect(defUser, defPass); subscribe(conn, "sub1", null); int num = 50; //send a bunch of messages for (int i = 0; i < num; i++) { this.sendJmsMessage("auto-ack" + i); } ClientStompFrame frame = null; for (int i = 0; i < num; i++) { frame = conn.receiveFrame(); Assert.assertNotNull(frame); } unsubscribe(conn, "sub1"); conn.disconnect(); //no messages can be received. MessageConsumer consumer = session.createConsumer(queue); Message message = consumer.receive(1000); Assert.assertNull(message); } @Test public void testAckModeAuto() throws Exception { conn.connect(defUser, defPass); subscribe(conn, "sub1", Stomp.Headers.Subscribe.AckModeValues.AUTO); int num = 50; //send a bunch of messages for (int i = 0; i < num; i++) { this.sendJmsMessage("auto-ack" + i); } ClientStompFrame frame = null; for (int i = 0; i < num; i++) { frame = conn.receiveFrame(); Assert.assertNotNull(frame); } unsubscribe(conn, "sub1"); conn.disconnect(); //no messages can be received. MessageConsumer consumer = session.createConsumer(queue); Message message = consumer.receive(1000); Assert.assertNull(message); } @Test public void testAckModeClientIndividual() throws Exception { conn.connect(defUser, defPass); subscribe(conn, "sub1", "client-individual"); int num = 50; //send a bunch of messages for (int i = 0; i < num; i++) { this.sendJmsMessage("client-individual-ack" + i); } ClientStompFrame frame = null; for (int i = 0; i < num; i++) { frame = conn.receiveFrame(); Assert.assertNotNull(frame); System.out.println(i + " == received: " + frame); //ack on even numbers if (i % 2 == 0) { ack(conn, frame); } } unsubscribe(conn, "sub1"); conn.disconnect(); //no messages can be received. MessageConsumer consumer = session.createConsumer(queue); TextMessage message = null; for (int i = 0; i < num / 2; i++) { message = (TextMessage) consumer.receive(1000); Assert.assertNotNull(message); System.out.println("Legal: " + message.getText()); } message = (TextMessage) consumer.receive(1000); Assert.assertNull(message); } @Test public void testTwoSubscribers() throws Exception { conn.connect(defUser, defPass, CLIENT_ID); this.subscribeTopic(conn, "sub1", Stomp.Headers.Subscribe.AckModeValues.AUTO, null); StompClientConnection newConn = StompClientConnectionFactory.createClientConnection("1.1", hostname, port); newConn.connect(defUser, defPass, "myclientid2"); this.subscribeTopic(newConn, "sub2", Stomp.Headers.Subscribe.AckModeValues.AUTO, null); send(conn, getTopicPrefix() + getTopicName(), null, "Hello World"); // receive message from socket ClientStompFrame frame = conn.receiveFrame(1000); System.out.println("received frame : " + frame); Assert.assertEquals("Hello World", frame.getBody()); Assert.assertEquals("sub1", frame.getHeader(Stomp.Headers.Message.SUBSCRIPTION)); frame = newConn.receiveFrame(1000); System.out.println("received 2 frame : " + frame); Assert.assertEquals("Hello World", frame.getBody()); Assert.assertEquals("sub2", frame.getHeader(Stomp.Headers.Message.SUBSCRIPTION)); // remove suscription this.unsubscribe(conn, "sub1", true); this.unsubscribe(newConn, "sub2", true); conn.disconnect(); newConn.disconnect(); } @Test public void testSendAndReceiveOnDifferentConnections() throws Exception { conn.connect(defUser, defPass); send(conn, getQueuePrefix() + getQueueName(), null, "Hello World"); StompClientConnection connV12_2 = StompClientConnectionFactory.createClientConnection("1.1", hostname, port); connV12_2.connect(defUser, defPass); this.subscribe(connV12_2, "sub1", Stomp.Headers.Subscribe.AckModeValues.AUTO); ClientStompFrame frame = connV12_2.receiveFrame(2000); Assert.assertEquals(Stomp.Responses.MESSAGE, frame.getCommand()); Assert.assertEquals("Hello World", frame.getBody()); conn.disconnect(); connV12_2.disconnect(); } //----------------Note: tests below are adapted from StompTest @Test public void testBeginSameTransactionTwice() throws Exception { conn.connect(defUser, defPass); beginTransaction(conn, "tx1"); beginTransaction(conn, "tx1"); ClientStompFrame f = conn.receiveFrame(); Assert.assertTrue(f.getCommand().equals(Stomp.Responses.ERROR)); } @Test public void testBodyWithUTF8() throws Exception { conn.connect(defUser, defPass); this.subscribe(conn, getName(), Stomp.Headers.Subscribe.AckModeValues.AUTO); String text = "A" + "\u00ea" + "\u00f1" + "\u00fc" + "C"; System.out.println(text); sendJmsMessage(text); ClientStompFrame frame = conn.receiveFrame(); System.out.println(frame); Assert.assertTrue(frame.getCommand().equals(Stomp.Responses.MESSAGE)); Assert.assertNotNull(frame.getHeader(Stomp.Headers.Subscribe.DESTINATION)); Assert.assertTrue(frame.getBody().equals(text)); conn.disconnect(); } @Test public void testClientAckNotPartOfTransaction() throws Exception { conn.connect(defUser, defPass); this.subscribe(conn, getName(), "client"); sendJmsMessage(getName()); ClientStompFrame frame = conn.receiveFrame(); Assert.assertTrue(frame.getCommand().equals(Stomp.Responses.MESSAGE)); Assert.assertNotNull(frame.getHeader(Stomp.Headers.Subscribe.DESTINATION)); Assert.assertTrue(frame.getBody().equals(getName())); Assert.assertNotNull(frame.getHeader(Stomp.Headers.Message.MESSAGE_ID)); Assert.assertNotNull(frame.getHeader(Stomp.Headers.Subscribe.ACK_MODE)); String messageID = frame.getHeader(Stomp.Headers.Subscribe.ACK_MODE); beginTransaction(conn, "tx1"); ack(conn, messageID, "tx1"); abortTransaction(conn, "tx1"); frame = conn.receiveFrame(500); Assert.assertNull(frame); this.unsubscribe(conn, getName()); conn.disconnect(); } @Test public void testDisconnectAndError() throws Exception { conn.connect(defUser, defPass); this.subscribe(conn, getName(), "client"); ClientStompFrame frame = conn.createFrame("DISCONNECT"); frame.addHeader(Stomp.Headers.RECEIPT_REQUESTED, "1"); ClientStompFrame result = conn.sendFrame(frame); if (result == null || (!Stomp.Responses.RECEIPT.equals(result.getCommand())) || (!"1".equals(result.getHeader("receipt-id")))) { Assert.fail("Disconnect failed! " + result); } final CountDownLatch latch = new CountDownLatch(1); Thread thr = new Thread() { @Override public void run() { while (latch.getCount() != 0) { try { send(conn, getQueuePrefix() + getQueueName(), null, "Hello World"); Thread.sleep(500); } catch (InterruptedException e) { //retry } catch (ClosedChannelException e) { //ok. latch.countDown(); break; } catch (IOException e) { //ok. latch.countDown(); break; } finally { conn.destroy(); } } } }; thr.start(); latch.await(10, TimeUnit.SECONDS); long count = latch.getCount(); if (count != 0) { latch.countDown(); } thr.join(); Assert.assertTrue("Server failed to disconnect.", count == 0); } @Test public void testDurableSubscriber() throws Exception { conn.connect(defUser, defPass); this.subscribe(conn, "sub1", "client", getName()); this.subscribe(conn, "sub1", "client", getName()); ClientStompFrame frame = conn.receiveFrame(); Assert.assertTrue(frame.getCommand().equals(Stomp.Responses.ERROR)); waitDisconnect(conn); Assert.assertFalse("Should be disconnected in STOMP 1.2 after ERROR", conn.isConnected()); } @Test public void testDurableSubscriberWithReconnection() throws Exception { conn.connect(defUser, defPass, CLIENT_ID); this.subscribeTopic(conn, "sub1", Stomp.Headers.Subscribe.AckModeValues.AUTO, getName()); ClientStompFrame frame = conn.createFrame("DISCONNECT"); frame.addHeader(Stomp.Headers.RECEIPT_REQUESTED, "1"); ClientStompFrame result = conn.sendFrame(frame); if (result == null || (!Stomp.Responses.RECEIPT.equals(result.getCommand())) || (!"1".equals(result.getHeader("receipt-id")))) { Assert.fail("Disconnect failed! " + result); } // send the message when the durable subscriber is disconnected sendJmsMessage(getName(), topic); conn.destroy(); conn = (StompClientConnectionV12) StompClientConnectionFactory.createClientConnection("1.2", hostname, port); conn.connect(defUser, defPass, CLIENT_ID); this.subscribeTopic(conn, "sub1", Stomp.Headers.Subscribe.AckModeValues.AUTO, getName()); // we must have received the message frame = conn.receiveFrame(); Assert.assertTrue(frame.getCommand().equals(Stomp.Responses.MESSAGE)); Assert.assertNotNull(frame.getHeader(Stomp.Headers.Subscribe.DESTINATION)); Assert.assertEquals(getName(), frame.getBody()); this.unsubscribe(conn, "sub1"); conn.disconnect(); } @Test public void testDurableUnSubscribe() throws Exception { conn.connect(defUser, defPass, CLIENT_ID); this.subscribeTopic(conn, null, Stomp.Headers.Subscribe.AckModeValues.AUTO, getName()); conn.disconnect(); conn.destroy(); conn = (StompClientConnectionV12) StompClientConnectionFactory.createClientConnection("1.2", hostname, port); conn.connect(defUser, defPass, CLIENT_ID); this.unsubscribe(conn, getName(), null, false, true); long start = System.currentTimeMillis(); SimpleString queueName = SimpleString.toSimpleString(CLIENT_ID + "." + getName()); while (server.getActiveMQServer().locateQueue(queueName) != null && (System.currentTimeMillis() - start) < 5000) { Thread.sleep(100); } assertNull(server.getActiveMQServer().locateQueue(queueName)); conn.disconnect(); } @Test public void testJMSXGroupIdCanBeSet() throws Exception { MessageConsumer consumer = session.createConsumer(queue); conn.connect(defUser, defPass); ClientStompFrame frame = conn.createFrame(Stomp.Commands.SEND); frame.addHeader(Stomp.Headers.Subscribe.DESTINATION, getQueuePrefix() + getQueueName()); frame.addHeader("JMSXGroupID", "TEST"); frame.setBody("Hello World"); conn.sendFrame(frame); TextMessage message = (TextMessage) consumer.receive(1000); Assert.assertNotNull(message); Assert.assertEquals("Hello World", message.getText()); // differ from StompConnect Assert.assertEquals("TEST", message.getStringProperty("JMSXGroupID")); } @Test public void testMessagesAreInOrder() throws Exception { int ctr = 10; String[] data = new String[ctr]; conn.connect(defUser, defPass); this.subscribe(conn, "sub1", Stomp.Headers.Subscribe.AckModeValues.AUTO); for (int i = 0; i < ctr; ++i) { data[i] = getName() + i; sendJmsMessage(data[i]); } ClientStompFrame frame = null; for (int i = 0; i < ctr; ++i) { frame = conn.receiveFrame(); Assert.assertTrue("Message not in order", frame.getBody().equals(data[i])); } for (int i = 0; i < ctr; ++i) { data[i] = getName() + ":second:" + i; sendJmsMessage(data[i]); } for (int i = 0; i < ctr; ++i) { frame = conn.receiveFrame(); Assert.assertTrue("Message not in order", frame.getBody().equals(data[i])); } conn.disconnect(); } @Test public void testSubscribeWithAutoAckAndSelector() throws Exception { conn.connect(defUser, defPass); this.subscribe(conn, "sub1", Stomp.Headers.Subscribe.AckModeValues.AUTO, null, "foo = 'zzz'"); sendJmsMessage("Ignored message", "foo", "1234"); sendJmsMessage("Real message", "foo", "zzz"); ClientStompFrame frame = conn.receiveFrame(); Assert.assertTrue("Should have received the real message but got: " + frame, frame.getBody().equals("Real message")); conn.disconnect(); } @Test public void testRedeliveryWithClientAck() throws Exception { conn.connect(defUser, defPass); this.subscribe(conn, "subId", "client"); sendJmsMessage(getName()); ClientStompFrame frame = conn.receiveFrame(); Assert.assertTrue(frame.getCommand().equals(Stomp.Responses.MESSAGE)); conn.disconnect(); // message should be received since message was not acknowledged MessageConsumer consumer = session.createConsumer(queue); Message message = consumer.receive(1000); Assert.assertNotNull(message); Assert.assertTrue(message.getJMSRedelivered()); } @Test public void testSendManyMessages() throws Exception { MessageConsumer consumer = session.createConsumer(queue); conn.connect(defUser, defPass); int count = 1000; final CountDownLatch latch = new CountDownLatch(count); consumer.setMessageListener(new MessageListener() { @Override public void onMessage(Message arg0) { TextMessage m = (TextMessage) arg0; latch.countDown(); try { System.out.println("___> latch now: " + latch.getCount() + " m: " + m.getText()); } catch (JMSException e) { Assert.fail("here failed"); e.printStackTrace(); } } }); for (int i = 1; i <= count; i++) { send(conn, getQueuePrefix() + getQueueName(), null, "Hello World"); } Assert.assertTrue(latch.await(60, TimeUnit.SECONDS)); conn.disconnect(); } @Test public void testSendMessage() throws Exception { MessageConsumer consumer = session.createConsumer(queue); conn.connect(defUser, defPass); send(conn, getQueuePrefix() + getQueueName(), null, "Hello World"); TextMessage message = (TextMessage) consumer.receive(1000); Assert.assertNotNull(message); Assert.assertEquals("Hello World", message.getText()); // Assert default priority 4 is used when priority header is not set Assert.assertEquals("getJMSPriority", 4, message.getJMSPriority()); // Make sure that the timestamp is valid - should // be very close to the current time. long tnow = System.currentTimeMillis(); long tmsg = message.getJMSTimestamp(); Assert.assertTrue(Math.abs(tnow - tmsg) < 1000); } @Test public void testSendMessageWithContentLength() throws Exception { MessageConsumer consumer = session.createConsumer(queue); conn.connect(defUser, defPass); byte[] data = new byte[]{1, 0, 0, 4}; ClientStompFrame frame = conn.createFrame(Stomp.Commands.SEND) .addHeader(Stomp.Headers.Subscribe.DESTINATION, getQueuePrefix() + getQueueName()) .setBody(new String(data, StandardCharsets.UTF_8)) .addHeader(Stomp.Headers.CONTENT_LENGTH, String.valueOf(data.length)); conn.sendFrame(frame); BytesMessage message = (BytesMessage) consumer.receive(10000); Assert.assertNotNull(message); Assert.assertEquals(data.length, message.getBodyLength()); Assert.assertEquals(data[0], message.readByte()); Assert.assertEquals(data[1], message.readByte()); Assert.assertEquals(data[2], message.readByte()); Assert.assertEquals(data[3], message.readByte()); } @Test public void testSendMessageWithCustomHeadersAndSelector() throws Exception { MessageConsumer consumer = session.createConsumer(queue, "foo = 'abc'"); conn.connect(defUser, defPass); ClientStompFrame frame = conn.createFrame(Stomp.Commands.SEND) .addHeader(Stomp.Headers.Subscribe.DESTINATION, getQueuePrefix() + getQueueName()) .addHeader("foo", "abc") .addHeader("bar", "123") .setBody("Hello World"); conn.sendFrame(frame); TextMessage message = (TextMessage) consumer.receive(1000); Assert.assertNotNull(message); Assert.assertEquals("Hello World", message.getText()); Assert.assertEquals("foo", "abc", message.getStringProperty("foo")); Assert.assertEquals("bar", "123", message.getStringProperty("bar")); } @Test public void testSendMessageWithLeadingNewLine() throws Exception { MessageConsumer consumer = session.createConsumer(queue); conn.connect(defUser, defPass); ClientStompFrame frame = conn.createFrame(Stomp.Commands.SEND) .addHeader(Stomp.Headers.Subscribe.DESTINATION, getQueuePrefix() + getQueueName()) .setBody("Hello World"); conn.sendWickedFrame(frame); TextMessage message = (TextMessage) consumer.receive(1000); Assert.assertNotNull(message); Assert.assertEquals("Hello World", message.getText()); // Make sure that the timestamp is valid - should // be very close to the current time. long tnow = System.currentTimeMillis(); long tmsg = message.getJMSTimestamp(); Assert.assertTrue(Math.abs(tnow - tmsg) < 1000); Assert.assertNull(consumer.receive(1000)); conn.disconnect(); } @Test public void testSendMessageWithReceipt() throws Exception { MessageConsumer consumer = session.createConsumer(queue); conn.connect(defUser, defPass); send(conn, getQueuePrefix() + getQueueName(), null, "Hello World", true); TextMessage message = (TextMessage) consumer.receive(1000); Assert.assertNotNull(message); Assert.assertEquals("Hello World", message.getText()); // Make sure that the timestamp is valid - should // be very close to the current time. long tnow = System.currentTimeMillis(); long tmsg = message.getJMSTimestamp(); Assert.assertTrue(Math.abs(tnow - tmsg) < 1000); conn.disconnect(); } @Test public void testSendMessageWithStandardHeaders() throws Exception { MessageConsumer consumer = session.createConsumer(queue); conn.connect(defUser, defPass); ClientStompFrame frame = conn.createFrame(Stomp.Commands.SEND) .addHeader(Stomp.Headers.Subscribe.DESTINATION, getQueuePrefix() + getQueueName()) .addHeader("correlation-id", "c123") .addHeader("persistent", "true") .addHeader("priority", "3") .addHeader(Stomp.Headers.Message.TYPE, "t345") .addHeader("JMSXGroupID", "abc") .addHeader("foo", "abc") .addHeader("bar", "123") .setBody("Hello World"); conn.sendFrame(frame); TextMessage message = (TextMessage) consumer.receive(1000); Assert.assertNotNull(message); Assert.assertEquals("Hello World", message.getText()); Assert.assertEquals("JMSCorrelationID", "c123", message.getJMSCorrelationID()); Assert.assertEquals("getJMSType", "t345", message.getJMSType()); Assert.assertEquals("getJMSPriority", 3, message.getJMSPriority()); Assert.assertEquals(javax.jms.DeliveryMode.PERSISTENT, message.getJMSDeliveryMode()); Assert.assertEquals("foo", "abc", message.getStringProperty("foo")); Assert.assertEquals("bar", "123", message.getStringProperty("bar")); Assert.assertEquals("JMSXGroupID", "abc", message.getStringProperty("JMSXGroupID")); conn.disconnect(); } @Test public void testSendMessageWithLongHeaders() throws Exception { MessageConsumer consumer = session.createConsumer(queue); conn.connect(defUser, defPass); StringBuffer buffer = new StringBuffer(); for (int i = 0; i < 2048; i++) { buffer.append("a"); } ClientStompFrame frame = conn.createFrame(Stomp.Commands.SEND) .addHeader(Stomp.Headers.Subscribe.DESTINATION, getQueuePrefix() + getQueueName()) .addHeader("correlation-id", "c123") .addHeader("persistent", "true") .addHeader("priority", "3") .addHeader(Stomp.Headers.Message.TYPE, "t345") .addHeader("JMSXGroupID", "abc") .addHeader("foo", "abc") .addHeader("very-very-long-stomp-message-header", buffer.toString()) .setBody("Hello World"); conn.sendFrame(frame); TextMessage message = (TextMessage) consumer.receive(1000); Assert.assertNotNull(message); Assert.assertEquals("Hello World", message.getText()); Assert.assertEquals("JMSCorrelationID", "c123", message.getJMSCorrelationID()); Assert.assertEquals("getJMSType", "t345", message.getJMSType()); Assert.assertEquals("getJMSPriority", 3, message.getJMSPriority()); Assert.assertEquals(javax.jms.DeliveryMode.PERSISTENT, message.getJMSDeliveryMode()); Assert.assertEquals("foo", "abc", message.getStringProperty("foo")); Assert.assertEquals("very-very-long-stomp-message-header", 2048, message.getStringProperty("very-very-long-stomp-message-header").length()); Assert.assertEquals("JMSXGroupID", "abc", message.getStringProperty("JMSXGroupID")); conn.disconnect(); } @Test public void testSubscribeToTopic() throws Exception { conn.connect(defUser, defPass); this.subscribeTopic(conn, "sub1", null, null, true); sendJmsMessage(getName(), topic); ClientStompFrame frame = conn.receiveFrame(); Assert.assertTrue(frame.getCommand().equals(Stomp.Responses.MESSAGE)); Assert.assertTrue(frame.getHeader(Stomp.Headers.Subscribe.DESTINATION).equals(getTopicPrefix() + getTopicName())); Assert.assertTrue(frame.getBody().equals(getName())); this.unsubscribe(conn, "sub1", true); sendJmsMessage(getName(), topic); frame = conn.receiveFrame(1000); Assert.assertNull(frame); conn.disconnect(); } @Test public void testSubscribeToTopicWithNoLocal() throws Exception { conn.connect(defUser, defPass); this.subscribeTopic(conn, "sub1", null, null, true, true); // send a message on the same connection => it should not be received send(conn, getTopicPrefix() + getTopicName(), null, "Hello World"); ClientStompFrame frame = conn.receiveFrame(2000); Assert.assertNull(frame); // send message on another JMS connection => it should be received sendJmsMessage(getName(), topic); frame = conn.receiveFrame(); Assert.assertTrue(frame.getCommand().equals(Stomp.Responses.MESSAGE)); Assert.assertTrue(frame.getHeader(Stomp.Headers.Subscribe.DESTINATION).equals(getTopicPrefix() + getTopicName())); Assert.assertTrue(frame.getBody().equals(getName())); this.unsubscribe(conn, "sub1"); conn.disconnect(); } @Test public void testSubscribeWithAutoAck() throws Exception { conn.connect(defUser, defPass); this.subscribe(conn, "sub1", Stomp.Headers.Subscribe.AckModeValues.AUTO); sendJmsMessage(getName()); ClientStompFrame frame = conn.receiveFrame(); Assert.assertEquals(Stomp.Responses.MESSAGE, frame.getCommand()); Assert.assertNotNull(frame.getHeader(Stomp.Headers.Subscribe.DESTINATION)); Assert.assertEquals(getName(), frame.getBody()); conn.disconnect(); // message should not be received as it was auto-acked MessageConsumer consumer = session.createConsumer(queue); Message message = consumer.receive(1000); Assert.assertNull(message); } @Test public void testSubscribeWithAutoAckAndBytesMessage() throws Exception { conn.connect(defUser, defPass); this.subscribe(conn, "sub1", Stomp.Headers.Subscribe.AckModeValues.AUTO); byte[] payload = new byte[]{1, 2, 3, 4, 5}; sendJmsMessage(payload, queue); ClientStompFrame frame = conn.receiveFrame(); Assert.assertEquals(Stomp.Responses.MESSAGE, frame.getCommand()); System.out.println("Message: " + frame); Assert.assertEquals("5", frame.getHeader(Stomp.Headers.CONTENT_LENGTH)); Assert.assertEquals(null, frame.getHeader(Stomp.Headers.Message.TYPE)); Assert.assertEquals(frame.getBody(), new String(payload, StandardCharsets.UTF_8)); conn.disconnect(); } @Test public void testSubscribeWithClientAck() throws Exception { conn.connect(defUser, defPass); this.subscribe(conn, "sub1", "client"); sendJmsMessage(getName()); ClientStompFrame frame = conn.receiveFrame(); ack(conn, frame); conn.disconnect(); // message should not be received since message was acknowledged by the client MessageConsumer consumer = session.createConsumer(queue); Message message = consumer.receive(1000); Assert.assertNull(message); } @Test public void testSubscribeWithClientAckThenConsumingAgainWithAutoAckWithExplicitDisconnect() throws Exception { assertSubscribeWithClientAckThenConsumeWithAutoAck(true); } @Test public void testSubscribeWithClientAckThenConsumingAgainWithAutoAckWithNoDisconnectFrame() throws Exception { assertSubscribeWithClientAckThenConsumeWithAutoAck(false); } @Test public void testSubscribeWithID() throws Exception { conn.connect(defUser, defPass); this.subscribe(conn, "mysubid", Stomp.Headers.Subscribe.AckModeValues.AUTO); sendJmsMessage(getName()); ClientStompFrame frame = conn.receiveFrame(); Assert.assertTrue(frame.getHeader(Stomp.Headers.Message.SUBSCRIPTION) != null); conn.disconnect(); } @Test public void testSubscribeWithMessageSentWithProperties() throws Exception { conn.connect(defUser, defPass); this.subscribe(conn, "sub1", Stomp.Headers.Subscribe.AckModeValues.AUTO); MessageProducer producer = session.createProducer(queue); BytesMessage message = session.createBytesMessage(); message.setStringProperty("S", "value"); message.setBooleanProperty("n", false); message.setByteProperty("byte", (byte) 9); message.setDoubleProperty("d", 2.0); message.setFloatProperty("f", (float) 6.0); message.setIntProperty("i", 10); message.setLongProperty("l", 121); message.setShortProperty("s", (short) 12); message.writeBytes("Hello World".getBytes(StandardCharsets.UTF_8)); producer.send(message); ClientStompFrame frame = conn.receiveFrame(); Assert.assertNotNull(frame); Assert.assertTrue(frame.getHeader("S") != null); Assert.assertTrue(frame.getHeader("n") != null); Assert.assertTrue(frame.getHeader("byte") != null); Assert.assertTrue(frame.getHeader("d") != null); Assert.assertTrue(frame.getHeader("f") != null); Assert.assertTrue(frame.getHeader("i") != null); Assert.assertTrue(frame.getHeader("l") != null); Assert.assertTrue(frame.getHeader("s") != null); Assert.assertEquals("Hello World", frame.getBody()); conn.disconnect(); } @Test public void testSuccessiveTransactionsWithSameID() throws Exception { MessageConsumer consumer = session.createConsumer(queue); conn.connect(defUser, defPass); // first tx this.beginTransaction(conn, "tx1"); ClientStompFrame frame = conn.createFrame(Stomp.Commands.SEND) .addHeader(Stomp.Headers.Subscribe.DESTINATION, getQueuePrefix() + getQueueName()) .addHeader(Stomp.Headers.TRANSACTION, "tx1") .setBody("Hello World"); conn.sendFrame(frame); this.commitTransaction(conn, "tx1"); Message message = consumer.receive(1000); Assert.assertNotNull("Should have received a message", message); // 2nd tx with same tx ID this.beginTransaction(conn, "tx1"); frame = conn.createFrame(Stomp.Commands.SEND) .addHeader(Stomp.Headers.Subscribe.DESTINATION, getQueuePrefix() + getQueueName()) .addHeader(Stomp.Headers.TRANSACTION, "tx1") .setBody("Hello World"); conn.sendFrame(frame); this.commitTransaction(conn, "tx1"); message = consumer.receive(1000); Assert.assertNotNull("Should have received a message", message); conn.disconnect(); } @Test public void testTransactionCommit() throws Exception { MessageConsumer consumer = session.createConsumer(queue); conn.connect(defUser, defPass); this.beginTransaction(conn, "tx1"); ClientStompFrame frame = conn.createFrame(Stomp.Commands.SEND) .addHeader(Stomp.Headers.Subscribe.DESTINATION, getQueuePrefix() + getQueueName()) .addHeader(Stomp.Headers.TRANSACTION, "tx1") .addHeader(Stomp.Headers.RECEIPT_REQUESTED, "123") .setBody("Hello World"); frame = conn.sendFrame(frame); Assert.assertEquals("123", frame.getHeader("receipt-id")); // check the message is not committed Assert.assertNull(consumer.receive(100)); this.commitTransaction(conn, "tx1", true); Message message = consumer.receive(1000); Assert.assertNotNull("Should have received a message", message); conn.disconnect(); } @Test public void testTransactionRollback() throws Exception { MessageConsumer consumer = session.createConsumer(queue); conn.connect(defUser, defPass); this.beginTransaction(conn, "tx1"); ClientStompFrame frame = conn.createFrame(Stomp.Commands.SEND) .addHeader(Stomp.Headers.Subscribe.DESTINATION, getQueuePrefix() + getQueueName()) .addHeader(Stomp.Headers.TRANSACTION, "tx1") .setBody("first message"); conn.sendFrame(frame); // rollback first message this.abortTransaction(conn, "tx1"); this.beginTransaction(conn, "tx1"); frame = conn.createFrame(Stomp.Commands.SEND) .addHeader(Stomp.Headers.Subscribe.DESTINATION, getQueuePrefix() + getQueueName()) .addHeader(Stomp.Headers.TRANSACTION, "tx1") .setBody("second message"); conn.sendFrame(frame); this.commitTransaction(conn, "tx1", true); // only second msg should be received since first msg was rolled back TextMessage message = (TextMessage) consumer.receive(1000); Assert.assertNotNull(message); Assert.assertEquals("second message", message.getText()); conn.disconnect(); } @Test public void testUnsubscribe() throws Exception { conn.connect(defUser, defPass); this.subscribe(conn, "sub1", Stomp.Headers.Subscribe.AckModeValues.AUTO); // send a message to our queue sendJmsMessage("first message"); // receive message from socket ClientStompFrame frame = conn.receiveFrame(); Assert.assertTrue(frame.getCommand().equals(Stomp.Responses.MESSAGE)); // remove suscription this.unsubscribe(conn, "sub1", true); // send a message to our queue sendJmsMessage("second message"); frame = conn.receiveFrame(1000); Assert.assertNull(frame); conn.disconnect(); } @Test public void testDisconnectWithoutUnsubscribe() throws Exception { conn.connect(defUser, defPass); this.subscribe(conn, "sub1", Stomp.Headers.Subscribe.AckModeValues.AUTO); // send a message to our queue sendJmsMessage("first message"); // receive message from socket ClientStompFrame frame = conn.receiveFrame(); Assert.assertTrue(frame.getCommand().equals(Stomp.Responses.MESSAGE)); //now disconnect without unsubscribe conn.disconnect(); // send a message to our queue sendJmsMessage("second message"); //reconnect conn = (StompClientConnectionV12) StompClientConnectionFactory.createClientConnection("1.2", hostname, port); conn.connect(defUser, defPass); frame = conn.receiveFrame(1000); Assert.assertNull("not expected: " + frame, frame); //subscribe again. this.subscribe(conn, "sub1", Stomp.Headers.Subscribe.AckModeValues.AUTO); // receive message from socket frame = conn.receiveFrame(); Assert.assertNotNull(frame); Assert.assertTrue(frame.getCommand().equals(Stomp.Responses.MESSAGE)); frame = conn.receiveFrame(1000); Assert.assertNull("not expected: " + frame, frame); this.unsubscribe(conn, "sub1", true); frame = conn.receiveFrame(1000); Assert.assertNull(frame); conn.disconnect(); } protected void assertSubscribeWithClientAckThenConsumeWithAutoAck(boolean sendDisconnect) throws Exception { conn.connect(defUser, defPass); this.subscribe(conn, "sub1", "client"); sendJmsMessage(getName()); ClientStompFrame frame = conn.receiveFrame(); Assert.assertEquals(Stomp.Responses.MESSAGE, frame.getCommand()); log.info("Reconnecting!"); if (sendDisconnect) { conn.disconnect(); conn = (StompClientConnectionV12) StompClientConnectionFactory.createClientConnection("1.2", hostname, port); } else { conn.destroy(); conn = (StompClientConnectionV12) StompClientConnectionFactory.createClientConnection("1.2", hostname, port); } // message should be received since message was not acknowledged conn.connect(defUser, defPass); this.subscribe(conn, "sub1", null); frame = conn.receiveFrame(); Assert.assertTrue(frame.getCommand().equals(Stomp.Responses.MESSAGE)); conn.disconnect(); // now let's make sure we don't see the message again conn.destroy(); conn = (StompClientConnectionV12) StompClientConnectionFactory.createClientConnection("1.2", hostname, port); conn.connect(defUser, defPass); this.subscribe(conn, "sub1", null, null, true); sendJmsMessage("shouldBeNextMessage"); frame = conn.receiveFrame(); Assert.assertTrue(frame.getCommand().equals(Stomp.Responses.MESSAGE)); Assert.assertEquals("shouldBeNextMessage", frame.getBody()); } @Test public void testSendMessageToNonExistentQueueWithAutoCreation() throws Exception { conn.connect(defUser, defPass); send(conn, "NonExistentQueue" + UUID.randomUUID().toString(), null, "Hello World", true, RoutingType.ANYCAST); conn.disconnect(); } @Test public void testInvalidStompCommand() throws Exception { try { conn.connect(defUser, defPass); ClientStompFrame frame = conn.createAnyFrame("INVALID"); frame.setBody("Hello World"); frame.addHeader(Stomp.Headers.RECEIPT_REQUESTED, "1234");//make the client receives this reply. frame = conn.sendFrame(frame); Assert.assertTrue(frame.getCommand().equals(Stomp.Responses.ERROR)); } finally { //because the last frame is ERROR, the connection //might already have closed by the server. //this is expected so we ignore it. conn.destroy(); } } @Test public void testSendAndReceiveWithEscapedCharactersInSenderId() throws Exception { conn.connect(defUser, defPass); ClientStompFrame response = send(conn, getQueuePrefix() + getQueueName(), "text/plain", "Hello World 1!"); Assert.assertNull(response); //subscribe subscribe(conn, "ID\\cMYMACHINE-50616-635482262727823605-1\\c1\\c1\\c1"); ClientStompFrame frame = conn.receiveFrame(); System.out.println("Received: " + frame); Assert.assertEquals(Stomp.Responses.MESSAGE, frame.getCommand()); Assert.assertEquals("ID:MYMACHINE-50616-635482262727823605-1:1:1:1", frame.getHeader(Stomp.Headers.Message.SUBSCRIPTION)); Assert.assertNotNull(frame.getHeader(Stomp.Headers.Message.MESSAGE_ID)); Assert.assertEquals(getQueuePrefix() + getQueueName(), frame.getHeader(Stomp.Headers.Subscribe.DESTINATION)); Assert.assertEquals("Hello World 1!", frame.getBody()); //unsub unsubscribe(conn, "ID\\cMYMACHINE-50616-635482262727823605-1\\c1\\c1\\c1"); conn.disconnect(); } private void ack(StompClientConnection conn, ClientStompFrame frame) throws IOException, InterruptedException { String messageID = frame.getHeader(Stomp.Headers.Subscribe.ACK_MODE); ClientStompFrame ackFrame = conn.createFrame(Stomp.Commands.ACK); ackFrame.addHeader(Stomp.Headers.Subscribe.ID, messageID); ClientStompFrame response = conn.sendFrame(ackFrame); if (response != null) { throw new IOException("failed to ack " + response); } } // STOMP 1.2-specific ACK and NACK methods private void ack(StompClientConnection conn, String mid) throws IOException, InterruptedException { ClientStompFrame ackFrame = conn.createFrame(Stomp.Commands.ACK); ackFrame.addHeader(Stomp.Headers.Subscribe.ID, mid); conn.sendFrame(ackFrame); } private void ack(StompClientConnection conn, String mid, String txID) throws IOException, InterruptedException { ClientStompFrame ackFrame = conn.createFrame(Stomp.Commands.ACK); ackFrame.addHeader(Stomp.Headers.Subscribe.ID, mid); ackFrame.addHeader(Stomp.Headers.TRANSACTION, txID); conn.sendFrame(ackFrame); } private void nack(StompClientConnection conn, String mid) throws IOException, InterruptedException { ClientStompFrame ackFrame = conn.createFrame(Stomp.Commands.NACK); ackFrame.addHeader(Stomp.Headers.Subscribe.ID, mid); conn.sendFrame(ackFrame); } }