/** Copyright 2013 James McClure Licensed 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 net.xenqtt.message; import static org.junit.Assert.*; import static org.mockito.Matchers.*; import static org.mockito.Mockito.*; import java.net.ConnectException; import java.net.URI; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import net.xenqtt.MqttException; import net.xenqtt.MqttInvocationException; import net.xenqtt.client.MessageStats; import net.xenqtt.message.ChannelManagerImpl; import net.xenqtt.message.ConnAckMessage; import net.xenqtt.message.ConnectMessage; import net.xenqtt.message.ConnectReturnCode; import net.xenqtt.message.DisconnectMessage; import net.xenqtt.message.MessageType; import net.xenqtt.message.MqttChannel; import net.xenqtt.message.MqttChannelRef; import net.xenqtt.message.MqttMessage; import net.xenqtt.message.PubAckMessage; import net.xenqtt.message.PubMessage; import net.xenqtt.message.QoS; import net.xenqtt.message.SubAckMessage; import net.xenqtt.message.SubscribeMessage; import net.xenqtt.message.UnsubscribeMessage; import net.xenqtt.mock.MockMessageHandler; import net.xenqtt.mock.MockServer; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.mockito.MockitoAnnotations; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; public class ChannelManagerImplTest { MqttChannelRef clientChannel; MqttChannelRef brokerChannel; MockServer server = new MockServer(); MockMessageHandler clientHandler = new MockMessageHandler(); MockMessageHandler brokerHandler = new MockMessageHandler(); ChannelManagerImpl manager; @Before public void before() { MockitoAnnotations.initMocks(this); } @After public void after() { manager.shutdown(); server.close(); } @Test public void testInit_IsRunning_Shutdown_NonBLocking() throws Exception { manager = new ChannelManagerImpl(2); manager.init(); manager.shutdown(); manager = new ChannelManagerImpl(10); assertFalse(manager.isRunning()); manager.init(); assertTrue(manager.isRunning()); manager.shutdown(); assertFalse(manager.isRunning()); } @Test public void testInit_IsRunning_Shutdown_Blocking() throws Exception { manager = new ChannelManagerImpl(2, 0); manager.init(); manager.shutdown(); manager = new ChannelManagerImpl(10, 0); assertFalse(manager.isRunning()); manager.init(); assertTrue(manager.isRunning()); manager.shutdown(); assertFalse(manager.isRunning()); } @Test public void testShutdownClosesAll_NonBlocking() throws Exception { manager = new ChannelManagerImpl(2); manager.init(); clientChannel = manager.newClientChannel("localhost", server.getPort(), clientHandler); brokerChannel = manager.newBrokerChannel(server.nextClient(1000), brokerHandler); clientHandler.assertChannelClosedCount(0); brokerHandler.assertChannelClosedCount(0); manager.shutdown(); clientHandler.assertChannelClosedCount(1); brokerHandler.assertChannelClosedCount(1); } @Test public void testShutdownClosesAll_Blocking() throws Exception { manager = new ChannelManagerImpl(2, 0); manager.init(); clientChannel = manager.newClientChannel("localhost", server.getPort(), clientHandler); brokerChannel = manager.newBrokerChannel(server.nextClient(1000), brokerHandler); clientHandler.assertChannelClosedCount(0); brokerHandler.assertChannelClosedCount(0); manager.shutdown(); clientHandler.assertChannelClosedCount(1); brokerHandler.assertChannelClosedCount(1); } @Test public void testCancelBlockingCommands_Blocking() throws Exception { manager = new ChannelManagerImpl(2, 0); manager.init(); MqttChannel channel = mock(MqttChannel.class); manager.cancelBlockingCommands(channel); manager.shutdown(); verify(channel).cancelBlockingCommands(); } @Test public void testCancelBlockingCommands_NonBlocking() throws Exception { manager = new ChannelManagerImpl(2); manager.init(); MqttChannel channel = mock(MqttChannel.class); manager.cancelBlockingCommands(channel); manager.shutdown(); verify(channel, timeout(1000)).cancelBlockingCommands(); } @Test public void testNewClientChannel_InvalidHost_NonBlocking() throws Exception { manager = new ChannelManagerImpl(2); manager.init(); try { manager.newClientChannel("foo", 123, clientHandler); fail("Exception expected"); } catch (MqttInvocationException e) { clientHandler.assertLastChannelClosedCause(e.getRootCause()); } } @Test public void testNewClientChannel_InvalidHost_Blocking() throws Exception { manager = new ChannelManagerImpl(2, 0); manager.init(); try { manager.newClientChannel("foo", 123, clientHandler); fail("Exception expected"); } catch (MqttInvocationException e) { clientHandler.assertLastChannelClosedCause(e.getRootCause()); } } @Test public void testNewClientChannel_UnableToConnect_NonBlocking() throws Exception { manager = new ChannelManagerImpl(2); manager.init(); CountDownLatch trigger = new CountDownLatch(1); clientHandler.onChannelClosed(trigger); clientChannel = manager.newClientChannel("localhost", 19876, clientHandler); assertTrue(trigger.await(1000, TimeUnit.SECONDS)); clientHandler.assertLastChannelClosedCause(ConnectException.class); } @Test public void testNewClientChannel_UnableToConnect_Blocking() throws Exception { manager = new ChannelManagerImpl(2, 0); manager.init(); CountDownLatch trigger = new CountDownLatch(1); clientHandler.onChannelClosed(trigger); try { clientChannel = manager.newClientChannel("localhost", 19876, clientHandler); fail("expected exception"); } catch (MqttInvocationException e) { } assertTrue(trigger.await(1000, TimeUnit.SECONDS)); clientHandler.assertLastChannelClosedCause(ConnectException.class); } @Test public void testNewClientChannel_HostPort_NonBlocking() throws Exception { manager = new ChannelManagerImpl(2); manager.init(); CountDownLatch trigger = new CountDownLatch(1); clientHandler.onChannelOpened(trigger); clientChannel = manager.newClientChannel("localhost", server.getPort(), clientHandler); assertTrue(trigger.await(1000, TimeUnit.SECONDS)); clientHandler.assertChannelClosedCount(0); } @Test public void testNewClientChannel_HostPort_Blocking() throws Exception { manager = new ChannelManagerImpl(2, 0); manager.init(); CountDownLatch trigger = new CountDownLatch(1); clientHandler.onChannelOpened(trigger); clientChannel = manager.newClientChannel("localhost", server.getPort(), clientHandler); assertTrue(trigger.await(1000, TimeUnit.SECONDS)); clientHandler.assertChannelClosedCount(0); } @Test public void testNewClientChannel_NonTcpUri_NonBlocking() throws Exception { manager = new ChannelManagerImpl(2); manager.init(); CountDownLatch trigger = new CountDownLatch(1); clientHandler.onChannelOpened(trigger); try { clientChannel = manager.newClientChannel("http://localhost:3456", clientHandler); fail("expected exception"); } catch (MqttException e) { assertEquals("Invalid broker URI (scheme must be 'tcp'): http://localhost:3456", e.getMessage()); } clientHandler.assertChannelOpenedCount(0); } @Test public void testNewClientChannel_NonTcpUri_Blocking() throws Exception { manager = new ChannelManagerImpl(2, 0); manager.init(); CountDownLatch trigger = new CountDownLatch(1); clientHandler.onChannelOpened(trigger); try { clientChannel = manager.newClientChannel("http://localhost:3456", clientHandler); fail("expected exception"); } catch (MqttException e) { assertEquals("Invalid broker URI (scheme must be 'tcp'): http://localhost:3456", e.getMessage()); } clientHandler.assertChannelOpenedCount(0); } @Test public void testNewClientChannel_UriAsString_NonBlocking() throws Exception { manager = new ChannelManagerImpl(2); manager.init(); CountDownLatch trigger = new CountDownLatch(1); clientHandler.onChannelOpened(trigger); clientChannel = manager.newClientChannel("tcp://localhost:" + server.getPort(), clientHandler); assertTrue(trigger.await(1000, TimeUnit.SECONDS)); clientHandler.assertChannelClosedCount(0); } @Test public void testNewClientChannel_UriAsString_Blocking() throws Exception { manager = new ChannelManagerImpl(2, 0); manager.init(); CountDownLatch trigger = new CountDownLatch(1); clientHandler.onChannelOpened(trigger); clientChannel = manager.newClientChannel("tcp://localhost:" + server.getPort(), clientHandler); assertTrue(trigger.await(1000, TimeUnit.SECONDS)); clientHandler.assertChannelClosedCount(0); } @Test public void testNewClientChannel_Uri_NonBlocking() throws Exception { manager = new ChannelManagerImpl(2); manager.init(); CountDownLatch trigger = new CountDownLatch(1); clientHandler.onChannelOpened(trigger); clientChannel = manager.newClientChannel(new URI("tcp://localhost:" + server.getPort()), clientHandler); assertTrue(trigger.await(1000, TimeUnit.SECONDS)); clientHandler.assertChannelClosedCount(0); } @Test public void testNewClientChannel_Uri_Blocking() throws Exception { manager = new ChannelManagerImpl(2, 0); manager.init(); CountDownLatch trigger = new CountDownLatch(1); clientHandler.onChannelOpened(trigger); clientChannel = manager.newClientChannel(new URI("tcp://localhost:" + server.getPort()), clientHandler); assertTrue(trigger.await(1000, TimeUnit.SECONDS)); clientHandler.assertChannelClosedCount(0); } @Test public void testNewBrokerChannel_NonBlocking() throws Exception { manager = new ChannelManagerImpl(2); manager.init(); clientChannel = manager.newClientChannel("localhost", server.getPort(), clientHandler); brokerChannel = manager.newBrokerChannel(server.nextClient(1000), brokerHandler); clientHandler.assertChannelOpenedCount(1); brokerHandler.assertChannelOpenedCount(1); clientHandler.assertChannelClosedCount(0); brokerHandler.assertChannelClosedCount(0); } @Test public void testNewBrokerChannel_Blocking() throws Exception { manager = new ChannelManagerImpl(2, 0); manager.init(); clientChannel = manager.newClientChannel("localhost", server.getPort(), clientHandler); brokerChannel = manager.newBrokerChannel(server.nextClient(1000), brokerHandler); clientHandler.assertChannelOpenedCount(1); brokerHandler.assertChannelOpenedCount(1); clientHandler.assertChannelClosedCount(0); brokerHandler.assertChannelClosedCount(0); } @Test public void testSend_NonBlocking() throws Exception { manager = new ChannelManagerImpl(2); manager.init(); CountDownLatch trigger = new CountDownLatch(1); brokerHandler.onMessage(MessageType.PUBACK, trigger); clientChannel = manager.newClientChannel("localhost", server.getPort(), clientHandler); brokerChannel = manager.newBrokerChannel(server.nextClient(1000), brokerHandler); assertNull(manager.send(clientChannel, new PubAckMessage(1))); assertTrue(trigger.await(1, TimeUnit.SECONDS)); brokerHandler.assertMessages(new PubAckMessage(1)); assertEquals(1, manager.getStats(false).getMessagesSent()); } @Test public void testGetStats_Reset() throws Exception { manager = new ChannelManagerImpl(2); manager.init(); CountDownLatch trigger = new CountDownLatch(1); brokerHandler.onMessage(MessageType.PUBACK, trigger); clientChannel = manager.newClientChannel("localhost", server.getPort(), clientHandler); brokerChannel = manager.newBrokerChannel(server.nextClient(1000), brokerHandler); assertNull(manager.send(clientChannel, new PubAckMessage(1))); assertTrue(trigger.await(1, TimeUnit.SECONDS)); brokerHandler.assertMessages(new PubAckMessage(1)); assertEquals(1, manager.getStats(true).getMessagesSent()); assertEquals(0, manager.getStats(false).getMessagesSent()); } @Test public void testSend_Blocking_NonAckableMessage() throws Exception { manager = new ChannelManagerImpl(2, 0); manager.init(); CountDownLatch trigger = new CountDownLatch(1); brokerHandler.onMessage(MessageType.PUBACK, trigger); clientChannel = manager.newClientChannel("localhost", server.getPort(), clientHandler); brokerChannel = manager.newBrokerChannel(server.nextClient(1000), brokerHandler); assertNull(manager.send(clientChannel, new PubAckMessage(1))); assertTrue(trigger.await(1, TimeUnit.SECONDS)); brokerHandler.assertMessages(new PubAckMessage(1)); assertEquals(1, manager.getStats(false).getMessagesSent()); } @Test public void testSend_Blocking_AckableMessage() throws Exception { manager = new ChannelManagerImpl(2, 0); manager.init(); brokerHandler = mock(MockMessageHandler.class); doAnswer(new Answer<Void>() { @Override public Void answer(InvocationOnMock invocation) throws Throwable { MqttChannel channel = (MqttChannel) invocation.getArguments()[0]; SubscribeMessage msg = (SubscribeMessage) invocation.getArguments()[1]; channel.send(new SubAckMessage(msg.getMessageId(), msg.getRequestedQoSes()), null); return null; } }).when(brokerHandler).subscribe(isA(MqttChannel.class), isA(SubscribeMessage.class)); clientChannel = manager.newClientChannel("localhost", server.getPort(), clientHandler); brokerChannel = manager.newBrokerChannel(server.nextClient(1000), brokerHandler); SubAckMessage message = manager.send(clientChannel, new SubscribeMessage(1, new String[] { "foo" }, new QoS[] { QoS.AT_LEAST_ONCE })); assertEquals(1, message.getMessageId()); assertArrayEquals(new QoS[] { QoS.AT_LEAST_ONCE }, message.getGrantedQoses()); MessageStats stats = manager.getStats(false); assertEquals(2, stats.getMessagesSent()); // One for the sub and the other for the ack. } @Test public void testGetUnsentMessages_Blocking() throws Exception { manager = new ChannelManagerImpl(2, 0); manager.init(); MqttChannel channel = mock(MqttChannel.class); List<MqttMessage> unsentMessages = new ArrayList<MqttMessage>(); unsentMessages.add(new PubAckMessage(1)); unsentMessages.add(new PubAckMessage(2)); unsentMessages.add(new PubAckMessage(3)); when(channel.getUnsentMessages()).thenReturn(unsentMessages); List<MqttMessage> messages = manager.getUnsentMessages(channel); assertEquals(unsentMessages.size(), messages.size()); for (MqttMessage message : messages) { assertTrue(unsentMessages.contains(message)); } } @Test public void testGetUnsentMessages_NonBlocking() throws Exception { manager = new ChannelManagerImpl(2); manager.init(); MqttChannel channel = mock(MqttChannel.class); List<MqttMessage> unsentMessages = new ArrayList<MqttMessage>(); unsentMessages.add(new PubAckMessage(1)); unsentMessages.add(new PubAckMessage(2)); unsentMessages.add(new PubAckMessage(3)); when(channel.getUnsentMessages()).thenReturn(unsentMessages); List<MqttMessage> messages = manager.getUnsentMessages(channel); assertEquals(unsentMessages.size(), messages.size()); for (MqttMessage message : messages) { assertTrue(unsentMessages.contains(message)); } } @Test public void testTransfer() throws Exception { manager = new ChannelManagerImpl(2000); manager.init(); // create first client and broker and send a message CountDownLatch trigger = new CountDownLatch(1); brokerHandler.onMessage(MessageType.UNSUBSCRIBE, trigger); clientChannel = manager.newClientChannel("localhost", server.getPort(), clientHandler); brokerChannel = manager.newBrokerChannel(server.nextClient(1000), brokerHandler); UnsubscribeMessage message = new UnsubscribeMessage(1, new String[] { "foo" }); assertNull(manager.send(clientChannel, message)); assertTrue(trigger.await(10, TimeUnit.SECONDS)); brokerHandler.assertMessages(message); // create the second client and broker MockMessageHandler brokerHandler2 = new MockMessageHandler(); trigger = new CountDownLatch(1); brokerHandler2.onMessage(MessageType.UNSUBSCRIBE, trigger); MockMessageHandler clientHandler2 = new MockMessageHandler(); MqttChannelRef clientChannel2 = manager.newClientChannel("localhost", server.getPort(), clientHandler2); manager.newBrokerChannel(server.nextClient(1000), brokerHandler2); // verify the message gets resent on the new channel manager.transfer(clientChannel, clientChannel2); assertTrue(trigger.await(1000, TimeUnit.SECONDS)); brokerHandler2.assertMessages(message); // new messages sent from the old channel should go through the new channel trigger = new CountDownLatch(1); brokerHandler2.onMessage(MessageType.UNSUBSCRIBE, trigger); assertNull(manager.send(clientChannel, message)); assertTrue(trigger.await(1, TimeUnit.SECONDS)); brokerHandler2.assertMessages(message, message); // new messages sent from the new channel should go through the new channel trigger = new CountDownLatch(1); brokerHandler2.onMessage(MessageType.UNSUBSCRIBE, trigger); assertNull(manager.send(clientChannel2, message)); assertTrue(trigger.await(1, TimeUnit.SECONDS)); brokerHandler2.assertMessages(message, message, message); } @Test public void testDetachChannel() throws Exception { manager = new ChannelManagerImpl(2); manager.init(); clientChannel = manager.newClientChannel("localhost", server.getPort(), clientHandler); brokerChannel = manager.newBrokerChannel(server.nextClient(1000), brokerHandler); CountDownLatch trigger = new CountDownLatch(1); brokerHandler.onMessage(MessageType.PUBACK, trigger); manager.send(clientChannel, new PubAckMessage(1)); assertTrue(trigger.await(1, TimeUnit.SECONDS)); trigger = new CountDownLatch(1); clientHandler.onChannelDetached(trigger); manager.detachChannel(clientChannel); assertTrue(trigger.await(1, TimeUnit.SECONDS)); trigger = new CountDownLatch(1); brokerHandler.onMessage(MessageType.PUBACK, trigger); manager.send(clientChannel, new PubAckMessage(1)); assertFalse(trigger.await(1, TimeUnit.SECONDS)); } @Test public void testAttachChannel() throws Exception { ChannelManagerImpl manager2 = new ChannelManagerImpl(2); manager2.init(); manager = new ChannelManagerImpl(2); manager.init(); clientChannel = manager.newClientChannel("localhost", server.getPort(), clientHandler); brokerChannel = manager.newBrokerChannel(server.nextClient(1000), brokerHandler); CountDownLatch trigger = new CountDownLatch(1); brokerHandler.onMessage(MessageType.PUBACK, trigger); manager.send(clientChannel, new PubAckMessage(1)); assertTrue(trigger.await(1, TimeUnit.SECONDS)); trigger = new CountDownLatch(1); clientHandler.onChannelDetached(trigger); manager.detachChannel(clientChannel); assertTrue(trigger.await(1, TimeUnit.SECONDS)); trigger = new CountDownLatch(1); clientHandler.onChannelAttached(trigger); manager2.attachChannel(clientChannel, clientHandler); assertTrue(trigger.await(1, TimeUnit.SECONDS)); trigger = new CountDownLatch(1); brokerHandler.onMessage(MessageType.PUBACK, trigger); manager2.send(clientChannel, new PubAckMessage(1)); assertTrue(trigger.await(1, TimeUnit.SECONDS)); manager2.shutdown(); } @Test public void testClose_NoCause_NonBlocking() throws Exception { manager = new ChannelManagerImpl(2); manager.init(); clientChannel = manager.newClientChannel("localhost", server.getPort(), clientHandler); brokerChannel = manager.newBrokerChannel(server.nextClient(1000), brokerHandler); clientHandler.assertChannelClosedCount(0); brokerHandler.assertChannelClosedCount(0); manager.close(clientChannel); clientHandler.assertChannelClosedCount(1); clientHandler.assertLastChannelClosedCause((Throwable) null); manager.close(brokerChannel); clientHandler.assertChannelClosedCount(1); brokerHandler.assertChannelClosedCount(1); brokerHandler.assertLastChannelClosedCause((Throwable) null); } @Test public void testClose_NoCause_Blocking() throws Exception { manager = new ChannelManagerImpl(2, 0); manager.init(); clientChannel = manager.newClientChannel("localhost", server.getPort(), clientHandler); brokerChannel = manager.newBrokerChannel(server.nextClient(1000), brokerHandler); clientHandler.assertChannelClosedCount(0); brokerHandler.assertChannelClosedCount(0); manager.close(clientChannel); clientHandler.assertChannelClosedCount(1); clientHandler.assertLastChannelClosedCause((Throwable) null); brokerHandler.assertChannelClosedCount(0); manager.close(brokerChannel); clientHandler.assertChannelClosedCount(1); brokerHandler.assertChannelClosedCount(1); brokerHandler.assertLastChannelClosedCause((Throwable) null); } @Test public void testClose_WithCause_NonBlocking() throws Exception { manager = new ChannelManagerImpl(2); manager.init(); clientChannel = manager.newClientChannel("localhost", server.getPort(), clientHandler); brokerChannel = manager.newBrokerChannel(server.nextClient(1000), brokerHandler); clientHandler.assertChannelClosedCount(0); brokerHandler.assertChannelClosedCount(0); Exception cause = new Exception(); manager.close(clientChannel, cause); clientHandler.assertChannelClosedCount(1); clientHandler.assertLastChannelClosedCause(cause); brokerHandler.assertChannelClosedCount(0); manager.close(brokerChannel); clientHandler.assertChannelClosedCount(1); brokerHandler.assertChannelClosedCount(1); } @Test public void testClose_WithCause_Blocking() throws Exception { manager = new ChannelManagerImpl(2, 0); manager.init(); clientChannel = manager.newClientChannel("localhost", server.getPort(), clientHandler); brokerChannel = manager.newBrokerChannel(server.nextClient(1000), brokerHandler); clientHandler.assertChannelClosedCount(0); brokerHandler.assertChannelClosedCount(0); Exception cause = new Exception(); manager.close(clientChannel, cause); clientHandler.assertChannelClosedCount(1); clientHandler.assertLastChannelClosedCause(cause); brokerHandler.assertChannelClosedCount(0); manager.close(brokerChannel); clientHandler.assertChannelClosedCount(1); brokerHandler.assertChannelClosedCount(1); } @Test public void testKeepAlive_NonBlocking() throws Exception { manager = new ChannelManagerImpl(2); doTestKeepAlive(); } @Test public void testKeepAlive_Blocking() throws Exception { manager = new ChannelManagerImpl(2, 0); doTestKeepAlive(); } @Test public void testMessageResend_NonBlocking() throws Exception { manager = new ChannelManagerImpl(2); doTestMessageResend(); } @Test public void testMessageResend_Blocking() throws Exception { manager = new ChannelManagerImpl(2, 0); doTestMessageResend(); } private void doTestKeepAlive() throws Exception { manager.init(); clientHandler = new MockMessageHandler() { @Override public void channelOpened(MqttChannel channel) { super.channelOpened(channel); channel.send(new ConnectMessage("abc", false, 1), null); } }; brokerHandler = new MockMessageHandler() { @Override public void connect(MqttChannel channel, ConnectMessage message) throws Exception { super.connect(channel, message); channel.send(new ConnAckMessage(ConnectReturnCode.ACCEPTED), null); } }; CountDownLatch ackTrigger = new CountDownLatch(1); clientHandler.onMessage(MessageType.CONNACK, ackTrigger); CountDownLatch closedTrigger = new CountDownLatch(2); clientHandler.onChannelClosed(closedTrigger); brokerHandler.onChannelClosed(closedTrigger); clientChannel = manager.newClientChannel("localhost", server.getPort(), clientHandler); brokerChannel = manager.newBrokerChannel(server.nextClient(1000), brokerHandler); assertTrue(ackTrigger.await(1, TimeUnit.SECONDS)); // give time for pings, it would be nice to track the pings Thread.sleep(2000); clientHandler.assertChannelClosedCount(0); brokerHandler.assertChannelClosedCount(0); manager.send(clientChannel, new DisconnectMessage()); assertTrue(closedTrigger.await(1, TimeUnit.SECONDS)); brokerHandler.assertMessages(new ConnectMessage("abc", false, 1), new DisconnectMessage()); clientHandler.assertMessages(new ConnAckMessage(ConnectReturnCode.ACCEPTED)); clientHandler.assertChannelClosedCount(1); brokerHandler.assertChannelClosedCount(1); } private void doTestMessageResend() throws Exception { manager.init(); clientHandler = new MockMessageHandler() { @Override public void channelOpened(MqttChannel channel) { super.channelOpened(channel); channel.send(new ConnectMessage("abc", false, 1), null); } }; brokerHandler = new MockMessageHandler() { int publishCount; @Override public void connect(MqttChannel channel, ConnectMessage message) throws Exception { super.connect(channel, message); channel.send(new ConnAckMessage(ConnectReturnCode.ACCEPTED), null); } @Override public void publish(MqttChannel channel, PubMessage message) throws Exception { if (++publishCount == 3) { channel.send(new PubAckMessage(message.getMessageId()), null); } super.publish(channel, message); } }; CountDownLatch ackTrigger = new CountDownLatch(1); clientHandler.onMessage(MessageType.CONNACK, ackTrigger); CountDownLatch closedTrigger = new CountDownLatch(2); clientHandler.onChannelClosed(closedTrigger); brokerHandler.onChannelClosed(closedTrigger); clientChannel = manager.newClientChannel("localhost", server.getPort(), clientHandler); brokerChannel = manager.newBrokerChannel(server.nextClient(1000), brokerHandler); assertTrue(ackTrigger.await(1, TimeUnit.SECONDS)); manager.send(clientChannel, new PubMessage(QoS.AT_LEAST_ONCE, false, "foo", 123, new byte[] { 1, 2, 3 })); // give time for 3 resends but there should only be 2 since we ack the second resend Thread.sleep(3500); clientHandler.assertChannelClosedCount(0); brokerHandler.assertChannelClosedCount(0); manager.send(clientChannel, new DisconnectMessage()); assertTrue(closedTrigger.await(1, TimeUnit.SECONDS)); PubMessage dupMsg = new PubMessage(QoS.AT_LEAST_ONCE, false, "foo", 123, new byte[] { 1, 2, 3 }); dupMsg.setDuplicateFlag(); brokerHandler.assertMessages( // new ConnectMessage("abc", false, 1), // new PubMessage(QoS.AT_LEAST_ONCE, false, "foo", 123, new byte[] { 1, 2, 3 }), // dupMsg, // dupMsg, // new DisconnectMessage()); clientHandler.assertMessages(new ConnAckMessage(ConnectReturnCode.ACCEPTED), new PubAckMessage(123)); clientHandler.assertChannelClosedCount(1); brokerHandler.assertChannelClosedCount(1); assertEquals(2, manager.getStats(false).getMessagesResent()); } }