First raw code move from parser to newly created broker module
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
package org.dna.mqtt.moquette.messaging.spi.impl;
|
||||
|
||||
import org.dna.mqtt.moquette.messaging.spi.IMatchingCondition;
|
||||
import org.dna.mqtt.moquette.messaging.spi.IStorageService;
|
||||
import org.dna.mqtt.moquette.messaging.spi.impl.events.PublishEvent;
|
||||
import org.dna.mqtt.moquette.messaging.spi.impl.subscriptions.Subscription;
|
||||
import org.dna.mqtt.moquette.proto.messages.AbstractMessage;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*/
|
||||
public class DummyStorageService implements IStorageService {
|
||||
public void initStore() {
|
||||
//To change body of implemented methods use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
public void storeRetained(String topic, byte[] message, AbstractMessage.QOSType qos) {
|
||||
//To change body of implemented methods use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
public Collection<HawtDBStorageService.StoredMessage> searchMatching(IMatchingCondition condition) {
|
||||
return null; //To change body of implemented methods use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
public void storePublishForFuture(PublishEvent evt) {
|
||||
//To change body of implemented methods use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
public List<PublishEvent> retrivePersistedPublishes(String clientID) {
|
||||
return null; //To change body of implemented methods use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
public void cleanPersistedPublishes(String clientID) {
|
||||
//To change body of implemented methods use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
public void cleanInFlight(String msgID) {
|
||||
//To change body of implemented methods use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
public void addInFlight(PublishEvent evt, String publishKey) {
|
||||
//To change body of implemented methods use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
public void addNewSubscription(Subscription newSubscription, String clientID) {
|
||||
//To change body of implemented methods use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
public void removeAllSubscriptions(String clientID) {
|
||||
//To change body of implemented methods use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
public List<Subscription> retrieveAllSubscriptions() {
|
||||
return Collections.EMPTY_LIST;
|
||||
}
|
||||
|
||||
public void close() {
|
||||
//To change body of implemented methods use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
public void persistQoS2Message(String publishKey, PublishEvent evt) {
|
||||
//To change body of implemented methods use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
public void removeQoS2Message(String publishKey) {
|
||||
//To change body of implemented methods use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
public PublishEvent retrieveQoS2Message(String publishKey) {
|
||||
return null; //To change body of implemented methods use File | Settings | File Templates.
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,194 @@
|
||||
package org.dna.mqtt.moquette.messaging.spi.impl;
|
||||
|
||||
import org.apache.mina.core.filterchain.IoFilterAdapter;
|
||||
import org.apache.mina.core.session.DummySession;
|
||||
import org.apache.mina.core.session.IoSession;
|
||||
import org.apache.mina.core.write.WriteRequest;
|
||||
import org.dna.mqtt.moquette.messaging.spi.impl.events.*;
|
||||
import org.dna.mqtt.moquette.messaging.spi.impl.subscriptions.Subscription;
|
||||
import org.dna.mqtt.moquette.proto.messages.AbstractMessage;
|
||||
import org.dna.mqtt.moquette.proto.messages.AbstractMessage.QOSType;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.dna.mqtt.moquette.proto.messages.ConnAckMessage;
|
||||
import org.dna.mqtt.moquette.proto.messages.ConnectMessage;
|
||||
import org.dna.mqtt.moquette.proto.messages.SubscribeMessage;
|
||||
import org.dna.mqtt.moquette.server.ConnectionDescriptor;
|
||||
import org.dna.mqtt.moquette.server.Constants;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author andrea
|
||||
*/
|
||||
public class SimpleMessagingTest {
|
||||
|
||||
final static String FAKE_CLIENT_ID = "FAKE_123";
|
||||
final static String FAKE_TOPIC = "/news";
|
||||
SimpleMessaging messaging;
|
||||
|
||||
byte m_returnCode;
|
||||
IoSession m_session;
|
||||
// MQTTHandler m_handler;
|
||||
ConnectMessage connMsg;
|
||||
|
||||
AbstractMessage m_receivedMessage;
|
||||
|
||||
@Before
|
||||
public void setUp() throws InterruptedException {
|
||||
messaging = SimpleMessaging.getInstance();
|
||||
messaging.init();
|
||||
connMsg = new ConnectMessage();
|
||||
connMsg.setProcotolVersion((byte) 0x03);
|
||||
|
||||
m_session = new DummySession();
|
||||
|
||||
m_session.getFilterChain().addFirst("MessageCatcher", new IoFilterAdapter() {
|
||||
|
||||
@Override
|
||||
public void filterWrite(NextFilter nextFilter, IoSession session,
|
||||
WriteRequest writeRequest) throws Exception {
|
||||
try {
|
||||
m_receivedMessage = (AbstractMessage) writeRequest.getMessage();
|
||||
if (m_receivedMessage instanceof ConnAckMessage) {
|
||||
ConnAckMessage buf = (ConnAckMessage) m_receivedMessage;
|
||||
m_returnCode = buf.getReturnCode();
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
throw new AssertionError("Wrong return code");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
//sleep to let the messaging batch processor to process the initEvent
|
||||
Thread.sleep(300);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
m_receivedMessage = null;
|
||||
messaging.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSubscribe() {
|
||||
//Exercise
|
||||
SubscribeMessage msg = new SubscribeMessage();
|
||||
msg.addSubscription(new SubscribeMessage.Couple((byte)QOSType.MOST_ONE.ordinal(), FAKE_TOPIC));
|
||||
messaging.processSubscribe(m_session, msg, FAKE_CLIENT_ID, false);
|
||||
|
||||
//Verify
|
||||
Subscription expectedSubscription = new Subscription(FAKE_CLIENT_ID, FAKE_TOPIC, QOSType.MOST_ONE, false);
|
||||
assertTrue(messaging.getSubscriptions().contains(expectedSubscription));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDoubleSubscribe() {
|
||||
// SubscribeEvent evt = new SubscribeEvent(new Subscription(FAKE_CLIENT_ID, FAKE_TOPIC, QOSType.MOST_ONE, false), 0);
|
||||
SubscribeMessage msg = new SubscribeMessage();
|
||||
msg.addSubscription(new SubscribeMessage.Couple((byte)QOSType.MOST_ONE.ordinal(), FAKE_TOPIC));
|
||||
|
||||
messaging.processSubscribe(m_session, msg, FAKE_CLIENT_ID, false);
|
||||
|
||||
//Exercise
|
||||
messaging.processSubscribe(m_session, msg, FAKE_CLIENT_ID, false);
|
||||
|
||||
//Verify
|
||||
Subscription subscription = new Subscription(FAKE_CLIENT_ID, FAKE_TOPIC, QOSType.MOST_ONE, false);
|
||||
assertEquals(1, messaging.getSubscriptions().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPublish() throws InterruptedException {
|
||||
//simulate a connect that register a clientID to an IoSession
|
||||
ConnectionDescriptor connDescr = new ConnectionDescriptor(FAKE_CLIENT_ID, m_session, true);
|
||||
messaging.m_clientIDs.put(FAKE_CLIENT_ID, connDescr);
|
||||
|
||||
// SubscribeEvent evt = new SubscribeEvent(new Subscription(FAKE_CLIENT_ID, FAKE_TOPIC, QOSType.MOST_ONE, false), 0);
|
||||
SubscribeMessage msg = new SubscribeMessage();
|
||||
msg.addSubscription(new SubscribeMessage.Couple((byte)QOSType.MOST_ONE.ordinal(), FAKE_TOPIC));
|
||||
|
||||
messaging.processSubscribe(m_session, msg, FAKE_CLIENT_ID, false);
|
||||
|
||||
//Exercise
|
||||
PublishEvent pubEvt = new PublishEvent(FAKE_TOPIC, QOSType.MOST_ONE, "Hello".getBytes(), false, "FakeCLI", null);
|
||||
messaging.processPublish(pubEvt);
|
||||
|
||||
//Verify
|
||||
assertNotNull(m_receivedMessage);
|
||||
//TODO check received message attributes
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandleConnect_BadProtocol() {
|
||||
connMsg.setProcotolVersion((byte) 0x02);
|
||||
|
||||
//Exercise
|
||||
messaging.processConnect(m_session, connMsg);
|
||||
|
||||
//Verify
|
||||
assertEquals(ConnAckMessage.UNNACEPTABLE_PROTOCOL_VERSION, m_returnCode);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConnect_badClientID() {
|
||||
connMsg.setClientID("extremely_long_clientID_greater_than_23");
|
||||
|
||||
//Exercise
|
||||
messaging.processConnect(m_session, connMsg);
|
||||
|
||||
//Verify
|
||||
assertEquals(ConnAckMessage.IDENTIFIER_REJECTED, m_returnCode);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWill() {
|
||||
connMsg.setClientID("123");
|
||||
connMsg.setWillFlag(true);
|
||||
connMsg.setWillTopic("topic");
|
||||
connMsg.setWillMessage("Topic message");
|
||||
//IMessaging mockedMessaging = mock(IMessaging.class);
|
||||
|
||||
//Exercise
|
||||
//m_handler.setMessaging(mockedMessaging);
|
||||
messaging.processConnect(m_session, connMsg);
|
||||
|
||||
//Verify
|
||||
assertEquals(ConnAckMessage.CONNECTION_ACCEPTED, m_returnCode);
|
||||
//TODO verify the call
|
||||
/*verify(mockedMessaging).publish(eq("topic"), eq("Topic message".getBytes()),
|
||||
any(AbstractMessage.QOSType.class), anyBoolean(), eq("123"), any(IoSession.class));*/
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testHandleSubscribe() {
|
||||
String topicName = "/news";
|
||||
SubscribeMessage subscribeMsg = new SubscribeMessage();
|
||||
subscribeMsg.setMessageID(0x555);
|
||||
subscribeMsg.addSubscription(new SubscribeMessage.Couple(
|
||||
(byte)AbstractMessage.QOSType.EXACTLY_ONCE.ordinal(), topicName));
|
||||
//IMessaging mockedMessaging = mock(IMessaging.class);
|
||||
|
||||
m_session.setAttribute(Constants.ATTR_CLIENTID, "fakeID");
|
||||
m_session.setAttribute(Constants.CLEAN_SESSION, false);
|
||||
Subscription subscription = new Subscription("fakeID", "/news", QOSType.EXACTLY_ONCE, true);
|
||||
|
||||
//Exercise
|
||||
//m_handler.setMessaging(mockedMessaging);
|
||||
// SubscribeEvent pubEvt = new SubscribeEvent(subscription, 0);
|
||||
SubscribeMessage msg = new SubscribeMessage();
|
||||
msg.addSubscription(new SubscribeMessage.Couple((byte)QOSType.EXACTLY_ONCE.ordinal(), "/news"));
|
||||
messaging.processSubscribe(m_session, msg, "fakeID", false);
|
||||
|
||||
//Verify
|
||||
/*ArgumentCaptor<AbstractMessage.QOSType> argument = ArgumentCaptor.forClass(AbstractMessage.QOSType.class);
|
||||
verify(mockedMessaging).subscribe(eq("fakeID"), eq(topicName), argument.capture(), eq(false));
|
||||
assertEquals(AbstractMessage.QOSType.EXACTLY_ONCE, argument.getValue());*/
|
||||
//TODO verify the message ACK and corresponding QoS!!
|
||||
}
|
||||
}
|
||||
+284
@@ -0,0 +1,284 @@
|
||||
package org.dna.mqtt.moquette.messaging.spi.impl.subscriptions;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.text.ParseException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.dna.mqtt.moquette.messaging.spi.impl.DummyStorageService;
|
||||
import org.dna.mqtt.moquette.proto.messages.AbstractMessage;
|
||||
import org.fusesource.hawtdb.api.MultiIndexFactory;
|
||||
import org.fusesource.hawtdb.api.PageFile;
|
||||
import org.fusesource.hawtdb.api.PageFileFactory;
|
||||
import org.junit.After;
|
||||
import static org.junit.Assert.*;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author andrea
|
||||
*/
|
||||
public class SubscriptionsStoreTest {
|
||||
|
||||
private File hawtFile;
|
||||
private SubscriptionsStore store;
|
||||
|
||||
public SubscriptionsStoreTest() {
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() throws IOException {
|
||||
hawtFile = File.createTempFile("moquette_persistent_store", ".dat");
|
||||
|
||||
store = new SubscriptionsStore();
|
||||
// store.init(hawtFile.getAbsolutePath());
|
||||
PageFileFactory pageFactory = new PageFileFactory();
|
||||
|
||||
pageFactory.setFile(hawtFile);
|
||||
pageFactory.open();
|
||||
PageFile pageFile = pageFactory.getPageFile();
|
||||
MultiIndexFactory multiIndexFactory = new MultiIndexFactory(pageFile);
|
||||
store.init(new DummyStorageService());
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
if (hawtFile.exists()) {
|
||||
hawtFile.delete();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSplitTopic() throws ParseException {
|
||||
List tokens = store.splitTopic("finance/stock/ibm");
|
||||
assertEqualsSeq(asArray("finance", "stock", "ibm"), tokens);
|
||||
|
||||
tokens = store.splitTopic("/finance/stock/ibm");
|
||||
assertEqualsSeq(asArray(Token.EMPTY, "finance", "stock", "ibm"), tokens);
|
||||
|
||||
tokens = store.splitTopic("/");
|
||||
assertEqualsSeq(asArray(Token.EMPTY), tokens);
|
||||
}
|
||||
|
||||
@Test(expected = ParseException.class)
|
||||
public void testSplitTopicTwinsSlashAvoided() throws ParseException {
|
||||
store.splitTopic("/finance//stock/ibm");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSplitTopicMultiValid() throws ParseException {
|
||||
List tokens = store.splitTopic("finance/stock/#");
|
||||
assertEqualsSeq(asArray("finance", "stock", Token.MULTI), tokens);
|
||||
|
||||
tokens = store.splitTopic("#");
|
||||
assertEqualsSeq(asArray(Token.MULTI), tokens);
|
||||
}
|
||||
|
||||
@Test(expected = ParseException.class)
|
||||
public void testSplitTopicMultiInTheMiddleNotValid() throws ParseException {
|
||||
store.splitTopic("finance/#/closingprice");
|
||||
}
|
||||
|
||||
@Test(expected = ParseException.class)
|
||||
public void testSplitTopicMultiNotAferSeparatorNotValid() throws ParseException {
|
||||
store.splitTopic("finance#");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSplitTopicSingleValid() throws ParseException {
|
||||
List tokens = store.splitTopic("finance/stock/+");
|
||||
assertEqualsSeq(asArray("finance", "stock", Token.SINGLE), tokens);
|
||||
|
||||
tokens = store.splitTopic("+");
|
||||
assertEqualsSeq(asArray(Token.SINGLE), tokens);
|
||||
|
||||
tokens = store.splitTopic("finance/+/ibm");
|
||||
assertEqualsSeq(asArray("finance", Token.SINGLE, "ibm"), tokens);
|
||||
}
|
||||
|
||||
@Test(expected = ParseException.class)
|
||||
public void testSplitTopicSingleNotAferSeparatorNotValid() throws ParseException {
|
||||
store.splitTopic("finance+");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatchSimple() {
|
||||
Subscription slashSub = new Subscription("FAKE_CLI_ID_1", "/", AbstractMessage.QOSType.MOST_ONE, false);
|
||||
store.add(slashSub);
|
||||
assertTrue(store.matches("finance").isEmpty());
|
||||
|
||||
Subscription slashFinanceSub = new Subscription("FAKE_CLI_ID_1", "/finance", AbstractMessage.QOSType.MOST_ONE, false);
|
||||
store.add(slashFinanceSub);
|
||||
assertTrue(store.matches("finance").isEmpty());
|
||||
|
||||
assertTrue(store.matches("/finance").contains(slashFinanceSub));
|
||||
assertTrue(store.matches("/").contains(slashSub));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatchSimpleMulti() {
|
||||
Subscription anySub = new Subscription("FAKE_CLI_ID_1", "#", AbstractMessage.QOSType.MOST_ONE, false);
|
||||
store.add(anySub);
|
||||
assertTrue(store.matches("finance").contains(anySub));
|
||||
|
||||
Subscription financeAnySub = new Subscription("FAKE_CLI_ID_1", "finance/#", AbstractMessage.QOSType.MOST_ONE, false);
|
||||
store.add(financeAnySub);
|
||||
assertTrue(store.matches("finance").containsAll(Arrays.asList(financeAnySub, anySub)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatchingDeepMulti_one_layer() {
|
||||
Subscription anySub = new Subscription("FAKE_CLI_ID_1", "#", AbstractMessage.QOSType.MOST_ONE, false);
|
||||
Subscription financeAnySub = new Subscription("FAKE_CLI_ID_1", "finance/#", AbstractMessage.QOSType.MOST_ONE, false);
|
||||
store.add(anySub);
|
||||
store.add(financeAnySub);
|
||||
|
||||
//Verify
|
||||
assertTrue(store.matches("finance/stock").containsAll(Arrays.asList(financeAnySub, anySub)));
|
||||
assertTrue(store.matches("finance/stock/ibm").containsAll(Arrays.asList(financeAnySub, anySub)));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testMatchingDeepMulti_two_layer() {
|
||||
Subscription financeAnySub = new Subscription("FAKE_CLI_ID_1", "finance/stock/#", AbstractMessage.QOSType.MOST_ONE, false);
|
||||
store.add(financeAnySub);
|
||||
|
||||
//Verify
|
||||
assertTrue(store.matches("finance/stock/ibm").contains(financeAnySub));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatchSimpleSingle() {
|
||||
Subscription anySub = new Subscription("FAKE_CLI_ID_1", "+", AbstractMessage.QOSType.MOST_ONE, false);
|
||||
store.add(anySub);
|
||||
assertTrue(store.matches("finance").contains(anySub));
|
||||
|
||||
Subscription financeOne = new Subscription("FAKE_CLI_ID_1", "finance/+", AbstractMessage.QOSType.MOST_ONE, false);
|
||||
store.add(financeOne);
|
||||
assertTrue(store.matches("finance/stock").contains(financeOne));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatchManySingle() {
|
||||
Subscription manySub = new Subscription("FAKE_CLI_ID_1", "+/+", AbstractMessage.QOSType.MOST_ONE, false);
|
||||
store.add(manySub);
|
||||
|
||||
//verify
|
||||
assertTrue(store.matches("/finance").contains(manySub));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testMatchSlashSingle() {
|
||||
Subscription slashPlusSub = new Subscription("FAKE_CLI_ID_1", "/+", AbstractMessage.QOSType.MOST_ONE, false);
|
||||
store.add(slashPlusSub);
|
||||
Subscription anySub = new Subscription("FAKE_CLI_ID_1", "+", AbstractMessage.QOSType.MOST_ONE, false);
|
||||
store.add(anySub);
|
||||
|
||||
//Verify
|
||||
assertEquals(1, store.matches("/finance").size());
|
||||
assertTrue(store.matches("/finance").contains(slashPlusSub));
|
||||
assertFalse(store.matches("/finance").contains(anySub));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testMatchManyDeepSingle() {
|
||||
Subscription slashPlusSub = new Subscription("FAKE_CLI_ID_1", "/finance/+/ibm", AbstractMessage.QOSType.MOST_ONE, false);
|
||||
store.add(slashPlusSub);
|
||||
|
||||
Subscription slashPlusDeepSub = new Subscription("FAKE_CLI_ID_1", "/+/stock/+", AbstractMessage.QOSType.MOST_ONE, false);
|
||||
store.add(slashPlusDeepSub);
|
||||
|
||||
//Verify
|
||||
assertTrue(store.matches("/finance/stock/ibm").containsAll(Arrays.asList(slashPlusSub, slashPlusDeepSub)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatchSimpleMulti_allTheTree() {
|
||||
store.add(new Subscription("FAKE_CLI_ID_1", "#", AbstractMessage.QOSType.MOST_ONE, false));
|
||||
assertFalse(store.matches("finance").isEmpty());
|
||||
assertFalse(store.matches("finance/ibm").isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatchSimpleMulti_zeroLevel() {
|
||||
//check MULTI in case of zero level match
|
||||
store.add(new Subscription("FAKE_CLI_ID_1", "finance/#", AbstractMessage.QOSType.MOST_ONE, false));
|
||||
assertFalse(store.matches("finance").isEmpty());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testRemoveClientSubscriptions_existingClientID() {
|
||||
String cliendID = "FAKE_CLID_1";
|
||||
store.add(new Subscription(cliendID, "finance/#", AbstractMessage.QOSType.MOST_ONE, false));
|
||||
|
||||
//Exercise
|
||||
store.removeForClient(cliendID);
|
||||
|
||||
//Verify
|
||||
assertEquals(0, store.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveClientSubscriptions_notexistingClientID() {
|
||||
String cliendID = "FAKE_CLID_1";
|
||||
store.add(new Subscription(cliendID, "finance/#", AbstractMessage.QOSType.MOST_ONE, false));
|
||||
|
||||
//Exercise
|
||||
store.removeForClient("FAKE_CLID_2");
|
||||
|
||||
//Verify
|
||||
assertEquals(1, store.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatchTopics_simple() {
|
||||
assertTrue(SubscriptionsStore.matchTopics("/", "/"));
|
||||
assertTrue(SubscriptionsStore.matchTopics("/finance", "/finance"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatchTopics_multi() {
|
||||
assertTrue(SubscriptionsStore.matchTopics("finance", "#"));
|
||||
assertTrue(SubscriptionsStore.matchTopics("finance", "finance/#"));
|
||||
assertTrue(SubscriptionsStore.matchTopics("finance/stock", "finance/#"));
|
||||
assertTrue(SubscriptionsStore.matchTopics("finance/stock/ibm", "finance/#"));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testMatchTopics_single() {
|
||||
assertTrue(SubscriptionsStore.matchTopics("finance", "+"));
|
||||
assertTrue(SubscriptionsStore.matchTopics("finance/stock", "finance/+"));
|
||||
assertTrue(SubscriptionsStore.matchTopics("/finance", "/+"));
|
||||
assertFalse(SubscriptionsStore.matchTopics("/finance", "+"));
|
||||
assertTrue(SubscriptionsStore.matchTopics("/finance", "+/+"));
|
||||
assertTrue(SubscriptionsStore.matchTopics("/finance/stock/ibm", "/finance/+/ibm"));
|
||||
assertFalse(SubscriptionsStore.matchTopics("/finance/stock", "+"));
|
||||
}
|
||||
|
||||
|
||||
private static Token[] asArray(Object... l) {
|
||||
Token[] tokens = new Token[l.length];
|
||||
for (int i = 0; i < l.length; i++) {
|
||||
Object o = l[i];
|
||||
if (o instanceof Token) {
|
||||
tokens[i] = (Token) o;
|
||||
} else {
|
||||
tokens[i] = new Token(o.toString());
|
||||
}
|
||||
}
|
||||
|
||||
return tokens;
|
||||
}
|
||||
|
||||
private void assertEqualsSeq(Token[] exptected, List<Token> result) {
|
||||
List<Token> expectedList = Arrays.asList(exptected);
|
||||
assertEquals(expectedList, result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
package org.dna.mqtt.moquette.server;
|
||||
|
||||
import org.fusesource.mqtt.client.*;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
|
||||
/**
|
||||
* Test the broker using the widely used FuseSource libraries
|
||||
* */
|
||||
public class ServerIntegrationFuseTest {
|
||||
|
||||
Server m_server;
|
||||
MQTT m_mqtt;
|
||||
FutureConnection m_connection;
|
||||
|
||||
protected void startServer() throws IOException {
|
||||
m_server = new Server();
|
||||
m_server.startServer();
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
startServer();
|
||||
|
||||
m_mqtt = new MQTT();
|
||||
m_mqtt.setHost("localhost", Server.PORT);
|
||||
m_mqtt.setClientId("TestClient");
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
m_connection.disconnect().await();
|
||||
|
||||
m_server.stopServer();
|
||||
File dbFile = new File(Server.STORAGE_FILE_PATH);
|
||||
if (dbFile.exists()) {
|
||||
dbFile.delete();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSubscribe() throws Exception {
|
||||
m_connection = m_mqtt.futureConnection();
|
||||
m_connection.connect().await();
|
||||
|
||||
Topic[] topics = {new Topic("/topic", QoS.AT_MOST_ONCE)};
|
||||
Future<byte[]> futSub = m_connection.subscribe(topics);
|
||||
|
||||
m_connection.publish("/topic", "Test my payload".getBytes(), QoS.AT_MOST_ONCE, false).await();
|
||||
|
||||
byte[] qoses = futSub.await();
|
||||
assertEquals(1, qoses.length);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPublishWithQoS1() throws Exception {
|
||||
m_mqtt.setClientId("TestClient");
|
||||
m_connection = m_mqtt.futureConnection();
|
||||
m_connection.connect().await();
|
||||
|
||||
//subscribe to a QoS 1 topic
|
||||
Topic[] topics = {new Topic("/topic", QoS.AT_LEAST_ONCE)};
|
||||
Future<byte[]> subscribeFuture = m_connection.subscribe(topics);
|
||||
byte[] qoses = subscribeFuture.await();
|
||||
|
||||
Future<Message> receive = m_connection.receive();
|
||||
|
||||
//publish a QoS 1 message
|
||||
Future<Void> f3 = m_connection.publish("/topic", "Hello MQTT".getBytes(), QoS.AT_LEAST_ONCE, false);
|
||||
|
||||
//verify the reception
|
||||
Message message = receive.await();
|
||||
message.ack();
|
||||
|
||||
assertEquals("Hello MQTT", new String(message.getPayload()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPublishWithQoS1_notCleanSession() throws Exception {
|
||||
m_mqtt.setCleanSession(false);
|
||||
m_connection = m_mqtt.futureConnection() ;
|
||||
m_connection.connect().await();
|
||||
|
||||
//subscribe to a QoS 1 topic
|
||||
Topic[] topics = {new Topic("/topic", QoS.AT_LEAST_ONCE)};
|
||||
Future<byte[]> subscribeFuture = m_connection.subscribe(topics);
|
||||
byte[] qoses = subscribeFuture.await();
|
||||
|
||||
m_connection.disconnect().await();
|
||||
|
||||
//publish a QoS 1 message another client publish a message on the topic
|
||||
publishFromAnotherClient("/topic", "Hello MQTT".getBytes(), QoS.AT_LEAST_ONCE);
|
||||
|
||||
|
||||
//Reconnect
|
||||
m_connection = m_mqtt.futureConnection();
|
||||
m_connection.connect().await();
|
||||
Future<Message> receive = m_connection.receive();
|
||||
|
||||
//verify the reception
|
||||
Message message = receive.await();
|
||||
message.ack();
|
||||
|
||||
assertEquals("Hello MQTT", new String(message.getPayload()));
|
||||
}
|
||||
|
||||
private void publishFromAnotherClient(String topic, byte[] payload, QoS qos) throws Exception {
|
||||
MQTT mqttPub = new MQTT();
|
||||
mqttPub.setHost("localhost", Server.PORT);
|
||||
mqttPub.setClientId("TestClientPUB");
|
||||
FutureConnection connectionPub = mqttPub.futureConnection();
|
||||
connectionPub.connect().await();
|
||||
Future<Void> futurePublish = connectionPub.publish(topic, payload, qos, false);
|
||||
futurePublish.await();
|
||||
connectionPub.disconnect().await();
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testPublishWithQoS2() throws Exception {
|
||||
m_mqtt.setClientId("TestClient");
|
||||
m_connection = m_mqtt.futureConnection();
|
||||
m_connection.connect().await();
|
||||
|
||||
//subscribe to a QoS 2 topic
|
||||
Topic[] topics = {new Topic("/topic", QoS.EXACTLY_ONCE)};
|
||||
Future<byte[]> subscribeFuture = m_connection.subscribe(topics);
|
||||
byte[] qoses = subscribeFuture.await();
|
||||
|
||||
Future<Message> receive = m_connection.receive();
|
||||
|
||||
//publish a QoS 2 message
|
||||
Future<Void> f3 = m_connection.publish("/topic", "Hello MQTT".getBytes(), QoS.EXACTLY_ONCE, false);
|
||||
|
||||
//verify the reception
|
||||
Message message = receive.await();
|
||||
message.ack();
|
||||
|
||||
assertEquals("Hello MQTT", new String(message.getPayload()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPublishReceiveWithQoS2() throws Exception {
|
||||
m_mqtt.setCleanSession(false);
|
||||
m_connection = m_mqtt.futureConnection();
|
||||
m_connection.connect().await();
|
||||
|
||||
//subscribe to a QoS 2 topic
|
||||
Topic[] topics = {new Topic("/topic", QoS.EXACTLY_ONCE)};
|
||||
Future<byte[]> subscribeFuture = m_connection.subscribe(topics);
|
||||
byte[] qoses = subscribeFuture.await();
|
||||
|
||||
// m_connection.disconnect().await();
|
||||
|
||||
//publish a QoS 1 message another client publish a message on the topic
|
||||
publishFromAnotherClient("/topic", "Hello MQTT".getBytes(), QoS.EXACTLY_ONCE);
|
||||
|
||||
|
||||
//Reconnect
|
||||
/*m_connection = m_mqtt.futureConnection();
|
||||
m_connection.connect().await();*/
|
||||
Future<Message> receive = m_connection.receive();
|
||||
|
||||
//verify the reception
|
||||
Message message = receive.await();
|
||||
message.ack();
|
||||
|
||||
assertEquals("Hello MQTT", new String(message.getPayload()));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,347 @@
|
||||
package org.dna.mqtt.moquette.server;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.dna.mqtt.moquette.client.Client;
|
||||
import org.dna.mqtt.moquette.client.IPublishCallback;
|
||||
import org.dna.mqtt.moquette.proto.messages.AbstractMessage;
|
||||
import org.junit.After;
|
||||
import static org.junit.Assert.*;
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author andrea
|
||||
*/
|
||||
public class ServerIntegrationTest {
|
||||
|
||||
boolean received;
|
||||
Server server;
|
||||
|
||||
protected void startServer() throws IOException {
|
||||
server = new Server();
|
||||
server.startServer();
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() throws IOException {
|
||||
startServer();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
server.stopServer();
|
||||
File dbFile = new File(Server.STORAGE_FILE_PATH);
|
||||
if (dbFile.exists()) {
|
||||
dbFile.delete();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSubscribe() throws IOException, InterruptedException {
|
||||
// startServer();
|
||||
Client client = new Client("localhost", Server.PORT);
|
||||
// Client client = new Client("test.mosquitto.org", 1883);
|
||||
client.connect();
|
||||
|
||||
|
||||
client.subscribe("/topic", new IPublishCallback() {
|
||||
|
||||
public void published(String topic, byte[] message) {
|
||||
received = true;
|
||||
}
|
||||
});
|
||||
|
||||
client.publish("/topic", "Test my payload".getBytes());
|
||||
|
||||
client.close();
|
||||
client.shutdown();
|
||||
|
||||
assertTrue(received);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testCleanSession_maintainClientSubscriptions() {
|
||||
Client client = new Client("localhost", Server.PORT/*, "CLID_123"*/);
|
||||
client.connect(false); //without session cleanup
|
||||
|
||||
client.subscribe("/topic", new IPublishCallback() {
|
||||
|
||||
public void published(String topic, byte[] message) {
|
||||
received = true;
|
||||
}
|
||||
});
|
||||
client.close();
|
||||
|
||||
//Exercise that the client maintain the subscriptions
|
||||
client.connect(false);
|
||||
client.publish("/topic", "Test my payload".getBytes());
|
||||
client.close();
|
||||
|
||||
//Verify
|
||||
assertTrue(received);
|
||||
|
||||
//TearDown
|
||||
client.shutdown();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCleanSession_maintainClientSubscriptions_againstClientDestruction() {
|
||||
Client client = new Client("localhost", Server.PORT, "CLID_123");
|
||||
client.connect(false); //without session cleanup
|
||||
|
||||
client.subscribe("/topic", new IPublishCallback() {
|
||||
|
||||
public void published(String topic, byte[] message) {
|
||||
received = true;
|
||||
}
|
||||
});
|
||||
client.close();
|
||||
client.shutdown();
|
||||
|
||||
//Exercise that the client maintain the subscriptions
|
||||
client = new Client("localhost", Server.PORT, "CLID_123");
|
||||
client.register("/topic", new IPublishCallback() {
|
||||
|
||||
public void published(String topic, byte[] message) {
|
||||
received = true;
|
||||
}
|
||||
});
|
||||
client.connect(false);
|
||||
client.publish("/topic", "Test my payload".getBytes());
|
||||
client.close();
|
||||
|
||||
//Verify
|
||||
assertTrue(received);
|
||||
|
||||
//TearDown
|
||||
client.shutdown();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check that after a client has connected with clean session false, subscribed
|
||||
* to some topic and exited, if it reconnect with clean session true, the m_server
|
||||
* correctly cleanup every previous subscription
|
||||
*/
|
||||
@Test
|
||||
public void testCleanSession_correctlyClientSubscriptions() {
|
||||
Client client = new Client("localhost", Server.PORT, "CLID_123");
|
||||
client.connect(false); //without session cleanup
|
||||
|
||||
client.subscribe("/topic", new IPublishCallback() {
|
||||
|
||||
public void published(String topic, byte[] message) {
|
||||
received = true;
|
||||
}
|
||||
});
|
||||
client.close();
|
||||
client.shutdown();
|
||||
|
||||
//Exercise that the client maintain the subscriptions
|
||||
client = new Client("localhost", Server.PORT, "CLID_123");
|
||||
client.register("/topic", new IPublishCallback() {
|
||||
|
||||
public void published(String topic, byte[] message) {
|
||||
received = true;
|
||||
}
|
||||
});
|
||||
client.connect(true);
|
||||
client.publish("/topic", "Test my payload".getBytes());
|
||||
client.close();
|
||||
|
||||
//Verify
|
||||
assertFalse(received);
|
||||
|
||||
//TearDown
|
||||
client.shutdown();
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testCleanSession_maintainClientSubscriptions_withServerRestart() throws IOException, InterruptedException {
|
||||
final CountDownLatch barrier = new CountDownLatch(1);
|
||||
Client client = new Client("localhost", Server.PORT, "CLID_123");
|
||||
client.connect(false); //without session cleanup
|
||||
|
||||
client.subscribe("/topic", new IPublishCallback() {
|
||||
|
||||
public void published(String topic, byte[] message) {
|
||||
received = true;
|
||||
barrier.countDown();
|
||||
}
|
||||
});
|
||||
//never called because no notification is pending
|
||||
assertFalse(barrier.await(1, TimeUnit.SECONDS));
|
||||
|
||||
client.close();
|
||||
client.shutdown();
|
||||
server.stopServer();
|
||||
|
||||
server.startServer();
|
||||
|
||||
final CountDownLatch barrier2 = new CountDownLatch(1);
|
||||
|
||||
//Exercise that the client maintain the subscriptions
|
||||
client = new Client("localhost", Server.PORT, "CLID_123");
|
||||
client.register("/topic", new IPublishCallback() {
|
||||
|
||||
public void published(String topic, byte[] message) {
|
||||
received = true;
|
||||
barrier2.countDown();
|
||||
}
|
||||
});
|
||||
client.connect(false);
|
||||
client.publish("/topic", "Test my payload".getBytes());
|
||||
client.close();
|
||||
|
||||
//Verify
|
||||
assertTrue(barrier2.await(1, TimeUnit.SECONDS));
|
||||
assertTrue(received);
|
||||
|
||||
//TearDown
|
||||
client.shutdown();
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testRetain_maintainMessage_againstClientDestruction() throws InterruptedException {
|
||||
final CountDownLatch barrier = new CountDownLatch(1);
|
||||
Client client = new Client("localhost", Server.PORT, "CLID_123");
|
||||
client.connect();
|
||||
|
||||
client.publish("/topic", "Test my payload".getBytes(), true);
|
||||
client.close();
|
||||
client.shutdown();
|
||||
|
||||
//Exercise that the client maintain the subscriptions
|
||||
client = new Client("localhost", Server.PORT, "CLID_123");
|
||||
client.connect();
|
||||
client.subscribe("/topic", new IPublishCallback() {
|
||||
|
||||
public void published(String topic, byte[] message) {
|
||||
received = true;
|
||||
barrier.countDown();
|
||||
}
|
||||
});
|
||||
client.close();
|
||||
|
||||
//Verify
|
||||
barrier.await(1, TimeUnit.SECONDS);
|
||||
assertTrue(received);
|
||||
|
||||
//TearDown
|
||||
client.shutdown();
|
||||
}
|
||||
|
||||
|
||||
@Ignore
|
||||
public void testUnsubscribe_do_not_notify_anymore_same_session() throws InterruptedException {
|
||||
Client client = new Client("localhost", Server.PORT, "CLID_123");
|
||||
// Client client = new Client("test.mosquitto.org", 1883);
|
||||
client.connect();
|
||||
|
||||
final CountDownLatch barrier = new CountDownLatch(1);
|
||||
|
||||
client.subscribe("/topic", new IPublishCallback() {
|
||||
|
||||
public void published(String topic, byte[] message) {
|
||||
received = true;
|
||||
barrier.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
client.publish("/topic", "Test my payload".getBytes());
|
||||
|
||||
//wait 1 second to receive the published message
|
||||
boolean unlocked = barrier.await(1000, TimeUnit.MILLISECONDS);
|
||||
assertTrue(unlocked); //we were unlocked by the message reception
|
||||
assertTrue(received);
|
||||
|
||||
//reinit the flag
|
||||
received = false;
|
||||
//unsubscrbe and republish to check no notification is raised up
|
||||
client.unsubscribe("/topic");
|
||||
client.publish("/topic", "Test my payload".getBytes());
|
||||
assertFalse(received);
|
||||
|
||||
client.close();
|
||||
client.shutdown();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUnsubscribe_do_not_notify_anymore_new_session() throws InterruptedException {
|
||||
Client client = new Client("localhost", Server.PORT, "CLID_123");
|
||||
// Client client = new Client("test.mosquitto.org", 1883);
|
||||
client.connect();
|
||||
|
||||
final CountDownLatch barrier = new CountDownLatch(1);
|
||||
|
||||
client.subscribe("/topic", new IPublishCallback() {
|
||||
|
||||
public void published(String topic, byte[] message) {
|
||||
received = true;
|
||||
barrier.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
client.publish("/topic", "Test my payload".getBytes());
|
||||
|
||||
//wait 1 second to receive the published message
|
||||
boolean unlocked = barrier.await(1000, TimeUnit.MILLISECONDS);
|
||||
assertTrue(unlocked); //we were unlocked by the message reception
|
||||
assertTrue(received);
|
||||
|
||||
//unsubscrbe and republish to check no notification is raised up
|
||||
client.unsubscribe("/topic");
|
||||
|
||||
client.close();
|
||||
client.shutdown();
|
||||
|
||||
//Exercise that the client maintain the subscriptions
|
||||
client = new Client("localhost", Server.PORT, "CLID_123");
|
||||
client.connect();
|
||||
|
||||
//reinit the flag
|
||||
received = false;
|
||||
final CountDownLatch barrier2 = new CountDownLatch(1);
|
||||
|
||||
client.publish("/topic", "Test my payload".getBytes());
|
||||
unlocked = barrier2.await(1000, TimeUnit.MILLISECONDS);
|
||||
assertFalse(unlocked); //we were unlocked by the timeout exipration, no message received
|
||||
assertFalse(received);
|
||||
|
||||
client.close();
|
||||
client.shutdown();
|
||||
}
|
||||
|
||||
/*-------------- QoS 1 tests ----*/
|
||||
@Test
|
||||
public void testPublishWithQoS1() throws IOException, InterruptedException {
|
||||
// startServer();
|
||||
Client client = new Client("localhost", Server.PORT);
|
||||
// Client client = new Client("test.mosquitto.org", 1883);
|
||||
client.connect();
|
||||
|
||||
|
||||
client.subscribe("/topic", new IPublishCallback() {
|
||||
|
||||
public void published(String topic, byte[] message) {
|
||||
received = true;
|
||||
}
|
||||
});
|
||||
|
||||
client.publish("/topic", "Test my payload".getBytes(), AbstractMessage.QOSType.LEAST_ONE, false);
|
||||
|
||||
client.close();
|
||||
client.shutdown();
|
||||
|
||||
assertTrue(received);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user