/* * Copyright 2002-2016 the original author or authors. * * 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 org.springframework.integration.mqtt; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.Mockito.mock; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.eclipse.paho.client.mqttv3.MqttClientPersistence; import org.eclipse.paho.client.mqttv3.persist.MqttDefaultFilePersistence; import org.junit.ClassRule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.junit.runner.RunWith; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationEventPublisher; import org.springframework.integration.channel.QueueChannel; import org.springframework.integration.mqtt.core.DefaultMqttPahoClientFactory; import org.springframework.integration.mqtt.event.MqttMessageDeliveredEvent; import org.springframework.integration.mqtt.event.MqttMessageSentEvent; import org.springframework.integration.mqtt.inbound.MqttPahoMessageDrivenChannelAdapter; import org.springframework.integration.mqtt.outbound.MqttPahoMessageHandler; import org.springframework.integration.mqtt.support.MqttHeaders; import org.springframework.integration.support.MessageBuilder; import org.springframework.messaging.Message; import org.springframework.messaging.MessageChannel; import org.springframework.messaging.MessagingException; import org.springframework.messaging.PollableChannel; import org.springframework.messaging.support.GenericMessage; import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.junit4.SpringRunner; /** * @author Gary Russell * @author Artem Bilan * @since 4.0 * */ @RunWith(SpringRunner.class) @DirtiesContext public class BackToBackAdapterTests { @ClassRule public static final BrokerRunning brokerRunning = BrokerRunning.isRunning(1883); @ClassRule public static final TemporaryFolder folder = new TemporaryFolder(); @Autowired private MessageChannel out; @Autowired private PollableChannel in; @Test public void testSingleTopic() { MqttPahoMessageHandler adapter = new MqttPahoMessageHandler("tcp://localhost:1883", "si-test-out"); adapter.setDefaultTopic("mqtt-foo"); adapter.setBeanFactory(mock(BeanFactory.class)); adapter.afterPropertiesSet(); adapter.start(); MqttPahoMessageDrivenChannelAdapter inbound = new MqttPahoMessageDrivenChannelAdapter("tcp://localhost:1883", "si-test-in", "mqtt-foo"); QueueChannel outputChannel = new QueueChannel(); inbound.setOutputChannel(outputChannel); ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler(); taskScheduler.initialize(); inbound.setTaskScheduler(taskScheduler); inbound.setBeanFactory(mock(BeanFactory.class)); inbound.afterPropertiesSet(); inbound.start(); adapter.handleMessage(new GenericMessage<String>("foo")); adapter.stop(); Message<?> out = outputChannel.receive(10000); assertNotNull(out); inbound.stop(); assertEquals("foo", out.getPayload()); assertEquals("mqtt-foo", out.getHeaders().get(MqttHeaders.RECEIVED_TOPIC)); } @Test public void testAddRemoveTopic() { MqttPahoMessageHandler adapter = new MqttPahoMessageHandler("tcp://localhost:1883", "si-test-out"); adapter.setDefaultTopic("mqtt-foo"); adapter.setBeanFactory(mock(BeanFactory.class)); adapter.afterPropertiesSet(); adapter.start(); MqttPahoMessageDrivenChannelAdapter inbound = new MqttPahoMessageDrivenChannelAdapter("tcp://localhost:1883", "si-test-in"); QueueChannel outputChannel = new QueueChannel(); inbound.setOutputChannel(outputChannel); ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler(); taskScheduler.initialize(); inbound.setTaskScheduler(taskScheduler); inbound.setBeanFactory(mock(BeanFactory.class)); inbound.afterPropertiesSet(); inbound.start(); inbound.addTopic("mqtt-foo"); adapter.handleMessage(new GenericMessage<String>("foo")); Message<?> out = outputChannel.receive(10_000); assertNotNull(out); assertEquals("foo", out.getPayload()); assertEquals("mqtt-foo", out.getHeaders().get(MqttHeaders.RECEIVED_TOPIC)); inbound.addTopic("mqtt-bar"); adapter.handleMessage(MessageBuilder.withPayload("bar").setHeader(MqttHeaders.TOPIC, "mqtt-bar").build()); out = outputChannel.receive(10_000); assertNotNull(out); assertEquals("bar", out.getPayload()); assertEquals("mqtt-bar", out.getHeaders().get(MqttHeaders.RECEIVED_TOPIC)); inbound.removeTopic("mqtt-bar"); adapter.handleMessage(MessageBuilder.withPayload("bar").setHeader(MqttHeaders.TOPIC, "mqtt-bar").build()); out = outputChannel.receive(10_000); assertNull(out); try { inbound.addTopic("mqtt-foo"); fail("Expected exception"); } catch (MessagingException e) { assertEquals("Topic 'mqtt-foo' is already subscribed.", e.getMessage()); } inbound.addTopic("mqqt-bar", "mqqt-baz"); inbound.removeTopic("mqqt-bar", "mqqt-baz"); inbound.addTopics(new String[] { "mqqt-bar", "mqqt-baz" }, new int[] { 0, 0 }); inbound.removeTopic("mqqt-bar", "mqqt-baz"); adapter.stop(); inbound.stop(); } @Test public void testTwoTopics() { MqttPahoMessageHandler adapter = new MqttPahoMessageHandler("tcp://localhost:1883", "si-test-out"); adapter.setDefaultTopic("mqtt-foo"); adapter.setBeanFactory(mock(BeanFactory.class)); adapter.afterPropertiesSet(); adapter.start(); MqttPahoMessageDrivenChannelAdapter inbound = new MqttPahoMessageDrivenChannelAdapter("tcp://localhost:1883", "si-test-in", "mqtt-foo", "mqtt-bar"); QueueChannel outputChannel = new QueueChannel(); inbound.setOutputChannel(outputChannel); ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler(); taskScheduler.initialize(); inbound.setTaskScheduler(taskScheduler); inbound.setBeanFactory(mock(BeanFactory.class)); inbound.afterPropertiesSet(); inbound.start(); adapter.handleMessage(new GenericMessage<String>("foo")); Message<?> message = MessageBuilder.withPayload("bar").setHeader(MqttHeaders.TOPIC, "mqtt-bar").build(); adapter.handleMessage(message); adapter.stop(); Message<?> out = outputChannel.receive(10000); assertNotNull(out); inbound.stop(); assertEquals("foo", out.getPayload()); assertEquals("mqtt-foo", out.getHeaders().get(MqttHeaders.RECEIVED_TOPIC)); out = outputChannel.receive(10000); assertNotNull(out); inbound.stop(); assertEquals("bar", out.getPayload()); assertEquals("mqtt-bar", out.getHeaders().get(MqttHeaders.RECEIVED_TOPIC)); } @Test public void testAsync() throws Exception { MqttPahoMessageHandler adapter = new MqttPahoMessageHandler("tcp://localhost:1883", "si-test-out"); adapter.setDefaultTopic("mqtt-foo"); adapter.setBeanFactory(mock(BeanFactory.class)); adapter.setAsync(true); adapter.setAsyncEvents(true); EventPublisher publisher = new EventPublisher(); adapter.setApplicationEventPublisher(publisher); adapter.afterPropertiesSet(); adapter.start(); MqttPahoMessageDrivenChannelAdapter inbound = new MqttPahoMessageDrivenChannelAdapter("tcp://localhost:1883", "si-test-in", "mqtt-foo"); QueueChannel outputChannel = new QueueChannel(); inbound.setOutputChannel(outputChannel); ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler(); taskScheduler.initialize(); inbound.setTaskScheduler(taskScheduler); inbound.setBeanFactory(mock(BeanFactory.class)); inbound.afterPropertiesSet(); inbound.start(); GenericMessage<String> message = new GenericMessage<String>("foo"); adapter.handleMessage(message); verifyEvents(adapter, publisher, message); adapter.stop(); Message<?> out = outputChannel.receive(10000); assertNotNull(out); inbound.stop(); assertEquals("foo", out.getPayload()); assertEquals("mqtt-foo", out.getHeaders().get(MqttHeaders.RECEIVED_TOPIC)); } @Test public void testAsyncPersisted() throws Exception { DefaultMqttPahoClientFactory factory = new DefaultMqttPahoClientFactory(); MqttClientPersistence persistence = new MqttDefaultFilePersistence(folder.getRoot().getAbsolutePath()); factory.setPersistence(persistence); MqttPahoMessageHandler adapter = new MqttPahoMessageHandler("tcp://localhost:1883", "si-test-out", factory); adapter.setDefaultTopic("mqtt-foo"); adapter.setBeanFactory(mock(BeanFactory.class)); adapter.setAsync(true); adapter.setAsyncEvents(true); adapter.setDefaultQos(1); EventPublisher publisher1 = new EventPublisher(); adapter.setApplicationEventPublisher(publisher1); adapter.afterPropertiesSet(); adapter.start(); MqttPahoMessageDrivenChannelAdapter inbound = new MqttPahoMessageDrivenChannelAdapter("tcp://localhost:1883", "si-test-in", "mqtt-foo", "mqtt-bar"); QueueChannel outputChannel = new QueueChannel(); inbound.setOutputChannel(outputChannel); ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler(); taskScheduler.initialize(); inbound.setTaskScheduler(taskScheduler); inbound.setBeanFactory(mock(BeanFactory.class)); inbound.afterPropertiesSet(); inbound.start(); Message<String> message1 = new GenericMessage<String>("foo"); adapter.handleMessage(message1); verifyEvents(adapter, publisher1, message1); Message<String> message2 = MessageBuilder.withPayload("bar") .setHeader(MqttHeaders.TOPIC, "mqtt-bar") .build(); EventPublisher publisher2 = new EventPublisher(); adapter.setApplicationEventPublisher(publisher2); adapter.handleMessage(message2); verifyEvents(adapter, publisher2, message2); verifyMessageIds(publisher1, publisher2); int clientInstance = publisher1.delivered.getClientInstance(); adapter.stop(); adapter.start(); // new client instance publisher1 = new EventPublisher(); adapter.setApplicationEventPublisher(publisher1); adapter.handleMessage(message1); verifyEvents(adapter, publisher1, message1); publisher2 = new EventPublisher(); adapter.setApplicationEventPublisher(publisher2); adapter.handleMessage(message2); verifyEvents(adapter, publisher2, message2); verifyMessageIds(publisher1, publisher2); assertNotEquals(clientInstance, publisher1.delivered.getClientInstance()); adapter.stop(); Message<?> out = null; for (int i = 0; i < 4; i++) { out = outputChannel.receive(10000); assertNotNull(out); if ("foo".equals(out.getPayload())) { assertEquals("mqtt-foo", out.getHeaders().get(MqttHeaders.RECEIVED_TOPIC)); } else if ("bar".equals(out.getPayload())) { assertEquals("mqtt-bar", out.getHeaders().get(MqttHeaders.RECEIVED_TOPIC)); } else { fail("unexpected payload " + out.getPayload()); } } inbound.stop(); } private void verifyEvents(MqttPahoMessageHandler adapter, EventPublisher publisher1, Message<String> message1) throws InterruptedException { assertTrue(publisher1.latch.await(10, TimeUnit.SECONDS)); assertNotNull(publisher1.sent); assertNotNull(publisher1.delivered); assertEquals(publisher1.sent.getMessageId(), publisher1.delivered.getMessageId()); assertEquals(adapter.getClientId(), publisher1.sent.getClientId()); assertEquals(adapter.getClientId(), publisher1.delivered.getClientId()); assertEquals(adapter.getClientInstance(), publisher1.sent.getClientInstance()); assertEquals(adapter.getClientInstance(), publisher1.delivered.getClientInstance()); assertSame(message1, publisher1.sent.getMessage()); } private void verifyMessageIds(EventPublisher publisher1, EventPublisher publisher2) { assertNotEquals(publisher1.delivered.getMessageId(), publisher2.delivered.getMessageId()); assertEquals(publisher1.delivered.getClientId(), publisher2.delivered.getClientId()); assertEquals(publisher1.delivered.getClientInstance(), publisher2.delivered.getClientInstance()); } @Test public void testMultiURIs() { out.send(new GenericMessage<String>("foo")); Message<?> message = in.receive(10000); assertNotNull(message); assertEquals("foo", message.getPayload()); } private class EventPublisher implements ApplicationEventPublisher { private volatile MqttMessageDeliveredEvent delivered; private MqttMessageSentEvent sent; private final CountDownLatch latch = new CountDownLatch(2); EventPublisher() { super(); } @Override public void publishEvent(ApplicationEvent event) { if (event instanceof MqttMessageSentEvent) { this.sent = (MqttMessageSentEvent) event; } else if (event instanceof MqttMessageDeliveredEvent) { this.delivered = (MqttMessageDeliveredEvent) event; } latch.countDown(); } @Override public void publishEvent(Object event) { } } }