Merge branch 'change_from_blocking_queue_to_disruptor'

This commit is contained in:
selva.andre
2012-09-04 10:15:52 +02:00
15 changed files with 422 additions and 364 deletions
@@ -4,13 +4,7 @@ import java.io.*;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import org.fusesource.mqtt.client.*;
import org.fusesource.mqtt.client.Future;
import org.fusesource.mqtt.client.FutureConnection;
import org.fusesource.mqtt.client.MQTT;
import org.fusesource.mqtt.client.Message;
import org.fusesource.mqtt.client.QoS;
import org.fusesource.mqtt.client.Topic;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
+6
View File
@@ -25,6 +25,12 @@
<version>2.0.4</version> <version>2.0.4</version>
</dependency> </dependency>
<dependency>
<groupId>com.googlecode.disruptor</groupId>
<artifactId>disruptor</artifactId>
<version>2.7.1</version>
</dependency>
<!-- <dependency> <!-- <dependency>
<groupId>org.slf4j</groupId> <groupId>org.slf4j</groupId>
<artifactId>slf4j-jdk14</artifactId> <artifactId>slf4j-jdk14</artifactId>
@@ -63,7 +63,7 @@ public final class Client {
private int m_connectRetries = DEFAULT_RETRIES; private int m_connectRetries = DEFAULT_RETRIES;
private String m_hostname; private String m_hostname;
private int m_port; private int m_port;
//internal mangement used for conversation with the server //internal management used for conversation with the server
private IoConnector m_connector; private IoConnector m_connector;
private IoSession m_session; private IoSession m_session;
private CountDownLatch m_connectBarrier; private CountDownLatch m_connectBarrier;
@@ -29,7 +29,7 @@ public interface IMessaging {
/** /**
* Subscribe a client to a specified topic with a defined level * Subscribe a client to a specified topic with a defined level
*/ */
void subscribe(String clientId, String topic, QOSType qos, boolean cleanSession); void subscribe(String clientId, String topic, QOSType qos, boolean cleanSession, int messageID);
/** /**
* Remove all subscription to any topic the client (identified by clientID) * Remove all subscription to any topic the client (identified by clientID)
@@ -37,7 +37,7 @@ public interface IMessaging {
*/ */
void removeSubscriptions(String clientID); void removeSubscriptions(String clientID);
void close(); void stop();
/** /**
* Remove the clientID from topic subscription, if not previously subscribed, * Remove the clientID from topic subscription, if not previously subscribed,
@@ -13,10 +13,7 @@ import org.slf4j.LoggerFactory;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.*;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import static org.dna.mqtt.moquette.messaging.spi.impl.SimpleMessaging.StoredMessage; import static org.dna.mqtt.moquette.messaging.spi.impl.SimpleMessaging.StoredMessage;
@@ -38,7 +35,8 @@ public class HawtDBStorageService implements IStorageService {
private SortedIndex<String, PublishEvent> m_inflightStore; private SortedIndex<String, PublishEvent> m_inflightStore;
//persistent Map of clientID, list of Subscriptions //persistent Map of clientID, list of Subscriptions
private SortedIndex<String, List<Subscription>> m_persistentSubscriptions; //TODO move to Set to avoid double subscriptions
private SortedIndex<String, Set<Subscription>> m_persistentSubscriptions;
public HawtDBStorageService() { public HawtDBStorageService() {
String storeFile = Server.STORAGE_FILE_PATH; String storeFile = Server.STORAGE_FILE_PATH;
@@ -84,11 +82,10 @@ public class HawtDBStorageService implements IStorageService {
} }
private void initPersistentSubscriptions() { private void initPersistentSubscriptions() {
BTreeIndexFactory<String, List<Subscription>> indexFactory = BTreeIndexFactory<String, Set<Subscription>> indexFactory = new BTreeIndexFactory<String, Set<Subscription>>();
new BTreeIndexFactory<String, List<Subscription>>();
indexFactory.setKeyCodec(StringCodec.INSTANCE); indexFactory.setKeyCodec(StringCodec.INSTANCE);
m_persistentSubscriptions = (SortedIndex<String, List<Subscription>>) m_multiIndexFactory.openOrCreate("subscriptions", indexFactory); m_persistentSubscriptions = (SortedIndex<String, Set<Subscription>>) m_multiIndexFactory.openOrCreate("subscriptions", indexFactory);
} }
/** /**
@@ -157,12 +154,14 @@ public class HawtDBStorageService implements IStorageService {
public void addNewSubscription(Subscription newSubscription, String clientID) { public void addNewSubscription(Subscription newSubscription, String clientID) {
if (!m_persistentSubscriptions.containsKey(clientID)) { if (!m_persistentSubscriptions.containsKey(clientID)) {
m_persistentSubscriptions.put(clientID, new ArrayList<Subscription>()); m_persistentSubscriptions.put(clientID, new HashSet<Subscription>());
} }
List<Subscription> subs = m_persistentSubscriptions.get(clientID); Set<Subscription> subs = m_persistentSubscriptions.get(clientID);
subs.add(newSubscription); if (!subs.contains(newSubscription)) {
m_persistentSubscriptions.put(clientID, subs); subs.add(newSubscription);
m_persistentSubscriptions.put(clientID, subs);
}
} }
public void removeAllSubscriptions(String clientID) { public void removeAllSubscriptions(String clientID) {
@@ -171,7 +170,7 @@ public class HawtDBStorageService implements IStorageService {
public List<Subscription> retrieveAllSubscriptions() { public List<Subscription> retrieveAllSubscriptions() {
List<Subscription> allSubscriptions = new ArrayList<Subscription>(); List<Subscription> allSubscriptions = new ArrayList<Subscription>();
for (Map.Entry<String, List<Subscription>> entry : m_persistentSubscriptions) { for (Map.Entry<String, Set<Subscription>> entry : m_persistentSubscriptions) {
allSubscriptions.addAll(entry.getValue()); allSubscriptions.addAll(entry.getValue());
} }
return allSubscriptions; return allSubscriptions;
@@ -1,38 +1,37 @@
package org.dna.mqtt.moquette.messaging.spi.impl; package org.dna.mqtt.moquette.messaging.spi.impl;
import org.apache.mina.core.session.IdleStatus; import com.lmax.disruptor.BatchEventProcessor;
import org.apache.mina.core.session.IoSession; import com.lmax.disruptor.EventHandler;
import org.dna.mqtt.moquette.messaging.spi.IMatchingCondition; import com.lmax.disruptor.RingBuffer;
import org.dna.mqtt.moquette.messaging.spi.IMessaging; import com.lmax.disruptor.SequenceBarrier;
import org.dna.mqtt.moquette.messaging.spi.INotifier;
import org.dna.mqtt.moquette.messaging.spi.IStorageService;
import org.dna.mqtt.moquette.messaging.spi.impl.SubscriptionsStore.Token;
import org.dna.mqtt.moquette.messaging.spi.impl.events.*;
import org.dna.mqtt.moquette.proto.messages.AbstractMessage.QOSType;
import org.dna.mqtt.moquette.proto.messages.ConnAckMessage;
import org.dna.mqtt.moquette.proto.messages.ConnectMessage;
import org.dna.mqtt.moquette.proto.messages.PubAckMessage;
import org.dna.mqtt.moquette.proto.messages.PublishMessage;
import org.dna.mqtt.moquette.server.ConnectionDescriptor;
import org.dna.mqtt.moquette.server.Constants;
import org.dna.mqtt.moquette.server.IAuthenticator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.Serializable; import java.io.Serializable;
import java.text.ParseException; import java.text.ParseException;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.BlockingQueue; import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.Executors;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.session.IoSession;
import org.dna.mqtt.moquette.messaging.spi.IMatchingCondition;
import org.dna.mqtt.moquette.messaging.spi.IMessaging;
import org.dna.mqtt.moquette.messaging.spi.IStorageService;
import org.dna.mqtt.moquette.messaging.spi.impl.SubscriptionsStore.Token;
import org.dna.mqtt.moquette.messaging.spi.impl.events.*;
import org.dna.mqtt.moquette.proto.messages.*;
import org.dna.mqtt.moquette.proto.messages.AbstractMessage.QOSType;
import org.dna.mqtt.moquette.server.ConnectionDescriptor;
import org.dna.mqtt.moquette.server.Constants;
import org.dna.mqtt.moquette.server.IAuthenticator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** /**
* *
* @author andrea * @author andrea
*/ */
public class SimpleMessaging implements IMessaging, Runnable { public class SimpleMessaging implements IMessaging, EventHandler<ValueEvent>/*, Runnable*/ {
//TODO probably move this //TODO probably move this
public static class StoredMessage implements Serializable { public static class StoredMessage implements Serializable {
@@ -57,9 +56,9 @@ public class SimpleMessaging implements IMessaging, Runnable {
private SubscriptionsStore subscriptions = new SubscriptionsStore(); private SubscriptionsStore subscriptions = new SubscriptionsStore();
private BlockingQueue<MessagingEvent> m_inboundQueue = new LinkedBlockingQueue<MessagingEvent>(); private RingBuffer<ValueEvent> m_ringBuffer;
private INotifier m_notifier; // private INotifier m_notifier;
private IStorageService m_storageService; private IStorageService m_storageService;
@@ -67,87 +66,86 @@ public class SimpleMessaging implements IMessaging, Runnable {
private IAuthenticator m_authenticator; private IAuthenticator m_authenticator;
public SimpleMessaging() { private ExecutorService m_executor;
m_storageService = new HawtDBStorageService(); BatchEventProcessor<ValueEvent> m_eventProcessor;
m_storageService.initStore();
subscriptions.init(m_storageService); private static SimpleMessaging INSTANCE;
private SimpleMessaging() {
}
public static SimpleMessaging getInstance() {
if (INSTANCE == null) {
INSTANCE = new SimpleMessaging();
}
return INSTANCE;
}
public void init() {
m_executor = Executors.newFixedThreadPool(1);
m_ringBuffer = new RingBuffer<ValueEvent>(ValueEvent.EVENT_FACTORY, 1024 * 32);
SequenceBarrier barrier = m_ringBuffer.newBarrier();
m_eventProcessor = new BatchEventProcessor<ValueEvent>(m_ringBuffer, barrier, this);
m_ringBuffer.setGatingSequences(m_eventProcessor.getSequence());
m_executor.submit(m_eventProcessor);
disruptorPublish(new InitEvent());
} }
public void setNotifier(INotifier notifier) { /* public void setNotifier(INotifier notifier) {
m_notifier= notifier; m_notifier= notifier;
} } */
public void run() {
eventLoop(); private void disruptorPublish(MessagingEvent msgEvent) {
long sequence = m_ringBuffer.next();
ValueEvent event = m_ringBuffer.get(sequence);
event.setEvent(msgEvent);
m_ringBuffer.publish(sequence);
} }
public void publish(String topic, byte[] message, QOSType qos, boolean retain, String clientID, IoSession session) { public void publish(String topic, byte[] message, QOSType qos, boolean retain, String clientID, IoSession session) {
try { disruptorPublish(new PublishEvent(topic, qos, message, retain, clientID, session));
m_inboundQueue.put(new PublishEvent(topic, qos, message, retain, clientID, session));
} catch (InterruptedException ex) {
LOG.error(null, ex);
}
} }
public void publish(String topic, byte[] message, QOSType qos, boolean retain, String clientID, int messageID, IoSession session) { public void publish(String topic, byte[] message, QOSType qos, boolean retain, String clientID, int messageID, IoSession session) {
try { disruptorPublish(new PublishEvent(topic, qos, message, retain, clientID, messageID, session));
m_inboundQueue.put(new PublishEvent(topic, qos, message, retain, clientID, messageID, session));
} catch (InterruptedException ex) {
LOG.error(null, ex);
}
} }
public void subscribe(String clientId, String topic, QOSType qos, boolean cleanSession) { public void subscribe(String clientId, String topic, QOSType qos, boolean cleanSession, int messageID) {
Subscription newSubscription = new Subscription(clientId, topic, qos, cleanSession); Subscription newSubscription = new Subscription(clientId, topic, qos, cleanSession);
try { LOG.debug("subscribe invoked for topic: " + topic);
LOG.debug("subscribe invoked for topic: " + topic); disruptorPublish(new SubscribeEvent(newSubscription, messageID));
m_inboundQueue.put(new SubscribeEvent(newSubscription));
} catch (InterruptedException ex) {
LOG.error(null, ex);
}
} }
public void unsubscribe(String topic, String clientID) { public void unsubscribe(String topic, String clientID) {
try { disruptorPublish(new UnsubscribeEvent(topic, clientID));
m_inboundQueue.put(new UnsubscribeEvent(topic, clientID));
} catch (InterruptedException ex) {
LOG.error(null, ex);
}
} }
public void disconnect(IoSession session) { public void disconnect(IoSession session) {
try { disruptorPublish(new DisconnectEvent(session));
m_inboundQueue.put(new DisconnectEvent(session));
} catch (InterruptedException ex) {
LOG.error(null, ex);
}
} }
//method used by hte Notifier to re-put an event on the inbound queue //method used by hte Notifier to re-put an event on the inbound queue
private void refill(MessagingEvent evt) throws InterruptedException { private void refill(MessagingEvent evt) throws InterruptedException {
m_inboundQueue.put(evt); disruptorPublish(evt);
} }
public void republishStored(String clientID) { public void republishStored(String clientID) {
//create the event to push //create the event to push
try { disruptorPublish(new RepublishEvent(clientID));
m_inboundQueue.put(new RepublishEvent(clientID));
} catch (InterruptedException iex) {
LOG.error(null, iex);
}
} }
public void connect(IoSession session, ConnectMessage msg) { public void connect(IoSession session, ConnectMessage msg) {
try { disruptorPublish(new ConnectEvent(session, msg));
m_inboundQueue.put(new ConnectEvent(session, msg));
} catch (InterruptedException iex) {
LOG.error(null, iex);
}
} }
/** /**
@@ -189,56 +187,49 @@ public class SimpleMessaging implements IMessaging, Runnable {
} }
public void removeSubscriptions(String clientID) { public void removeSubscriptions(String clientID) {
try { disruptorPublish(new RemoveAllSubscriptionsEvent(clientID));
m_inboundQueue.put(new RemoveAllSubscriptionsEvent(clientID));
} catch (InterruptedException ex) {
LOG.error(null, ex);
}
} }
public void close() { public void stop() {
try { disruptorPublish(new StopEvent());
m_inboundQueue.put(new CloseEvent());
} catch (InterruptedException ex) {
LOG.error(null, ex);
}
} }
public void onEvent(ValueEvent t, long l, boolean bln) throws Exception {
protected void eventLoop() { LOG.debug("onEvent processing event");
LOG.debug("Started event loop"); MessagingEvent evt = t.getEvent();
boolean interrupted = false; if (evt instanceof PublishEvent) {
while (!interrupted) { processPublish((PublishEvent) evt);
try { } else if (evt instanceof SubscribeEvent) {
MessagingEvent evt = m_inboundQueue.take(); processSubscribe((SubscribeEvent) evt);
if (evt instanceof PublishEvent) { } else if (evt instanceof UnsubscribeEvent) {
processPublish((PublishEvent) evt); processUnsubscribe((UnsubscribeEvent) evt);
} else if (evt instanceof SubscribeEvent) { } else if (evt instanceof RemoveAllSubscriptionsEvent) {
processSubscribe((SubscribeEvent) evt); processRemoveAllSubscriptions((RemoveAllSubscriptionsEvent) evt);
} else if (evt instanceof UnsubscribeEvent) { } else if (evt instanceof StopEvent) {
processUnsubscribe((UnsubscribeEvent) evt); processStop();
} else if (evt instanceof RemoveAllSubscriptionsEvent) { } else if (evt instanceof DisconnectEvent) {
processRemoveAllSubscriptions((RemoveAllSubscriptionsEvent) evt); processDisconnect((DisconnectEvent) evt);
} else if (evt instanceof CloseEvent) { } else if (evt instanceof CleanInFlightEvent) {
processClose(); //remove the message from inflight storage
} else if (evt instanceof DisconnectEvent) { m_storageService.cleanInFlight(((CleanInFlightEvent) evt).getMsgId());
processDisconnect((DisconnectEvent) evt); } else if (evt instanceof RepublishEvent) {
} else if (evt instanceof CleanInFlightEvent) { processRepublish((RepublishEvent) evt);
//remove the message from inflight storage } else if (evt instanceof ConnectEvent) {
m_storageService.cleanInFlight(((CleanInFlightEvent) evt).getMsgId()); processConnect((ConnectEvent) evt);
} else if (evt instanceof RepublishEvent) { } else if (evt instanceof InitEvent) {
processRepublish((RepublishEvent) evt); processInit();
} else if (evt instanceof ConnectEvent) {
processConnect((ConnectEvent) evt);
}
} catch (InterruptedException ex) {
processClose();
interrupted = true;
}
} }
} }
private void processConnect(ConnectEvent evt) { private void processInit() {
m_storageService = new HawtDBStorageService();
m_storageService.initStore();
subscriptions.init(m_storageService);
}
protected void processConnect(ConnectEvent evt) {
ConnectMessage msg = evt.getMessage(); ConnectMessage msg = evt.getMessage();
IoSession session = evt.getSession(); IoSession session = evt.getSession();
@@ -368,16 +359,29 @@ public class SimpleMessaging implements IMessaging, Runnable {
//scans retained messages to be published to the new subscription //scans retained messages to be published to the new subscription
Collection<StoredMessage> messages = m_storageService.searchMatching(new IMatchingCondition() { Collection<StoredMessage> messages = m_storageService.searchMatching(new IMatchingCondition() {
public boolean match(String key) { public boolean match(String key) {
return matchTopics(key, topic); //To change body of implemented methods use File | Settings | File Templates. return matchTopics(key, topic);
} }
}); });
for (StoredMessage storedMsg : messages) { for (StoredMessage storedMsg : messages) {
//fire the as retained the message //fire the as retained the message
LOG.debug("Inserting NotifyEvent into outbound for topic " + topic); LOG.debug("Inserting NotifyEvent into outbound for topic " + topic);
m_notifier.notify(new NotifyEvent(newSubscription.clientId, topic, storedMsg.getQos(), notify(new NotifyEvent(newSubscription.clientId, topic, storedMsg.getQos(),
storedMsg.getPayload(), true)); storedMsg.getPayload(), true));
} }
// //ack the client
// SubAckMessage ackMessage = new SubAckMessage();
// ackMessage.setMessageID(evt.getMessageID());
//
// //TODO by now it handles only QoS 0 messages
// /*for (int i = 0; i < evt.subscriptions().size(); i++) {
// ackMessage.addType(QOSType.MOST_ONE);
// } */
// LOG.info("replying with SubAct to MSG ID {0}", evt.getMessageID());
// //session.write(ackMessage);
// IoSession session = m_clientIDs.get(evt.getSubscription().clientId).getSession();
// session.write(ackMessage).awaitUninterruptibly();
} }
protected void processUnsubscribe(UnsubscribeEvent evt) { protected void processUnsubscribe(UnsubscribeEvent evt) {
@@ -405,9 +409,12 @@ public class SimpleMessaging implements IMessaging, Runnable {
subscriptions.disconnect(clientID); subscriptions.disconnect(clientID);
} }
private void processClose() { private void processStop() {
LOG.debug("processClose invoked"); LOG.debug("processStop invoked");
m_storageService.close(); m_storageService.close();
// m_eventProcessor.halt();
m_executor.shutdown();
} }
private void processRepublish(RepublishEvent evt) throws InterruptedException { private void processRepublish(RepublishEvent evt) throws InterruptedException {
@@ -417,7 +424,7 @@ public class SimpleMessaging implements IMessaging, Runnable {
} }
for (PublishEvent pubEvt : publishedEvents) { for (PublishEvent pubEvt : publishedEvents) {
m_notifier.notify(new NotifyEvent(pubEvt.getClientID(), pubEvt.getTopic(), pubEvt.getQos(), notify(new NotifyEvent(pubEvt.getClientID(), pubEvt.getTopic(), pubEvt.getQos(),
pubEvt.getMessage(), false, pubEvt.getMessageID())); pubEvt.getMessage(), false, pubEvt.getMessageID()));
} }
} }
@@ -0,0 +1,29 @@
package org.dna.mqtt.moquette.messaging.spi.impl;
import com.lmax.disruptor.EventFactory;
import org.dna.mqtt.moquette.messaging.spi.impl.events.MessagingEvent;
/**
* Carrier value object for the RingBuffer.
*
* @author andrea
*/
public final class ValueEvent {
private MessagingEvent m_event;
public MessagingEvent getEvent() {
return m_event;
}
public void setEvent(MessagingEvent event) {
m_event = event;
}
public final static EventFactory<ValueEvent> EVENT_FACTORY = new EventFactory<ValueEvent>() {
public ValueEvent newInstance() {
return new ValueEvent();
}
};
}
@@ -0,0 +1,6 @@
package org.dna.mqtt.moquette.messaging.spi.impl.events;
/**
*/
public class InitEvent extends MessagingEvent {
}
@@ -1,9 +1,10 @@
package org.dna.mqtt.moquette.messaging.spi.impl.events; package org.dna.mqtt.moquette.messaging.spi.impl.events;
/** /**
* Poison pill to shutdown the event loop
* *
* @author andrea * @author andrea
*/ */
public class CloseEvent extends MessagingEvent { public class StopEvent extends MessagingEvent {
} }
@@ -9,13 +9,19 @@ import org.dna.mqtt.moquette.messaging.spi.impl.Subscription;
public class SubscribeEvent extends MessagingEvent { public class SubscribeEvent extends MessagingEvent {
Subscription m_subscription; Subscription m_subscription;
int m_messageID;
public SubscribeEvent(Subscription subscription) { public SubscribeEvent(Subscription subscription, int messageID) {
m_subscription = subscription; m_subscription = subscription;
m_messageID = messageID;
} }
public Subscription getSubscription() { public Subscription getSubscription() {
return m_subscription; return m_subscription;
} }
public int getMessageID() {
return m_messageID;
}
} }
@@ -75,7 +75,7 @@ public class MQTTHandler extends IoHandlerAdapter/* implements INotifier*/ {
String clientID = (String) session.getAttribute(Constants.ATTR_CLIENTID); String clientID = (String) session.getAttribute(Constants.ATTR_CLIENTID);
boolean cleanSession = (Boolean) session.getAttribute(Constants.CLEAN_SESSION); boolean cleanSession = (Boolean) session.getAttribute(Constants.CLEAN_SESSION);
m_messaging.subscribe(clientID, req.getTopic(), AbstractMessage.QOSType.values()[req.getQos()], m_messaging.subscribe(clientID, req.getTopic(), AbstractMessage.QOSType.values()[req.getQos()],
cleanSession); cleanSession, msg.getMessageID());
} }
//ack the client //ack the client
@@ -3,10 +3,9 @@ package org.dna.mqtt.moquette.server;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.apache.mina.core.service.IoAcceptor; import org.apache.mina.core.service.IoAcceptor;
import org.apache.mina.core.service.IoServiceStatistics;
import org.apache.mina.core.session.IdleStatus; import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.session.IoSession; import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolCodecFilter; import org.apache.mina.filter.codec.ProtocolCodecFilter;
@@ -14,7 +13,6 @@ import org.apache.mina.filter.codec.demux.DemuxingProtocolDecoder;
import org.apache.mina.filter.codec.demux.DemuxingProtocolEncoder; import org.apache.mina.filter.codec.demux.DemuxingProtocolEncoder;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor; import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
import org.dna.mqtt.moquette.messaging.spi.impl.SimpleMessaging; import org.dna.mqtt.moquette.messaging.spi.impl.SimpleMessaging;
import org.dna.mqtt.moquette.messaging.spi.impl.events.MessagingEvent;
import org.dna.mqtt.moquette.proto.*; import org.dna.mqtt.moquette.proto.*;
import org.dna.mqtt.moquette.proto.messages.*; import org.dna.mqtt.moquette.proto.messages.*;
import org.slf4j.Logger; import org.slf4j.Logger;
@@ -36,7 +34,7 @@ public class Server {
//private static final int NOTIFIER_POOL_SIZE = 1; //private static final int NOTIFIER_POOL_SIZE = 1;
private IoAcceptor m_acceptor; private IoAcceptor m_acceptor;
SimpleMessaging messaging; SimpleMessaging messaging;
Thread messagingEventLoop; // Thread messagingEventLoop;
//private ExecutorService m_notifierPool/* = Executors.newFixedThreadPool(NOTIFIER_POOL_SIZE)*/; //private ExecutorService m_notifierPool/* = Executors.newFixedThreadPool(NOTIFIER_POOL_SIZE)*/;
public static void main(String[] args) throws IOException { public static void main(String[] args) throws IOException {
@@ -68,19 +66,22 @@ public class Server {
m_acceptor.getFilterChain().addLast( "codec", new ProtocolCodecFilter(encoder, decoder)); m_acceptor.getFilterChain().addLast( "codec", new ProtocolCodecFilter(encoder, decoder));
MQTTHandler handler = new MQTTHandler(); MQTTHandler handler = new MQTTHandler();
messaging = new SimpleMessaging(); messaging = SimpleMessaging.getInstance();
messaging.init();
//TODO fix this hugly wiring //TODO fix this hugly wiring
handler.setMessaging(messaging); handler.setMessaging(messaging);
// messaging.setNotifier(handler); // messaging.setNotifier(handler);
messagingEventLoop = new Thread(messaging); // messagingEventLoop = new Thread(messaging);
messagingEventLoop.setName("Event Loop" + System.currentTimeMillis()); // messagingEventLoop.setName("Event Loop" + System.currentTimeMillis());
messagingEventLoop.start(); // messagingEventLoop.start();
m_acceptor.setHandler(handler); m_acceptor.setHandler(handler);
((NioSocketAcceptor)m_acceptor).setReuseAddress(true); ((NioSocketAcceptor)m_acceptor).setReuseAddress(true);
((NioSocketAcceptor)m_acceptor).getSessionConfig().setReuseAddress(true); ((NioSocketAcceptor)m_acceptor).getSessionConfig().setReuseAddress(true);
m_acceptor.getSessionConfig().setReadBufferSize( 2048 ); m_acceptor.getSessionConfig().setReadBufferSize( 2048 );
m_acceptor.getSessionConfig().setIdleTime( IdleStatus.BOTH_IDLE, DEFAULT_CONNECT_TIMEOUT ); m_acceptor.getSessionConfig().setIdleTime( IdleStatus.BOTH_IDLE, DEFAULT_CONNECT_TIMEOUT );
m_acceptor.getStatistics().setThroughputCalculationInterval(10);
m_acceptor.getStatistics().updateThroughput(System.currentTimeMillis());
m_acceptor.bind( new InetSocketAddress(PORT) ); m_acceptor.bind( new InetSocketAddress(PORT) );
LOG.info("Server binded"); LOG.info("Server binded");
@@ -96,23 +97,30 @@ public class Server {
protected void stopServer() { protected void stopServer() {
LOG.info("Server stopping..."); LOG.info("Server stopping...");
messaging.close(); messaging.stop();
messagingEventLoop.interrupt(); //messaging.stop();
LOG.info("shutting down evet loop"); // messagingEventLoop.interrupt();
try { // LOG.info("shutting down evet loop");
messagingEventLoop.join(); // try {
} catch (InterruptedException ex) { // messagingEventLoop.join();
LOG.error(null, ex); // } catch (InterruptedException ex) {
} // LOG.error(null, ex);
// }
/*m_notifierPool.shutdown();*/ /*m_notifierPool.shutdown();*/
//log statistics
IoServiceStatistics statistics = m_acceptor.getStatistics();
statistics.updateThroughput(System.currentTimeMillis());
System.out.println(String.format("Total read bytes: %d, read throughtput: %f (b/s)", statistics.getReadBytes(), statistics.getReadBytesThroughput()));
System.out.println(String.format("Total read msgs: %d, read msg throughtput: %f (msg/s)", statistics.getReadMessages(), statistics.getReadMessagesThroughput()));
for(IoSession session: m_acceptor.getManagedSessions().values()) { for(IoSession session: m_acceptor.getManagedSessions().values()) {
if(session.isConnected() && !session.isClosing()){ if(session.isConnected() && !session.isClosing()){
session.close(false); session.close(false);
} }
} }
m_acceptor.unbind(); m_acceptor.unbind();
m_acceptor.dispose(); m_acceptor.dispose();
LOG.info("Server stopped"); LOG.info("Server stopped");
@@ -1,18 +1,24 @@
package org.dna.mqtt.moquette.messaging.spi.impl; package org.dna.mqtt.moquette.messaging.spi.impl;
import java.util.concurrent.BlockingQueue; import org.apache.mina.core.filterchain.IoFilterAdapter;
import org.apache.mina.core.session.DummySession;
import org.dna.mqtt.moquette.messaging.spi.INotifier; import org.apache.mina.core.session.IoSession;
import org.dna.mqtt.moquette.messaging.spi.impl.events.MessagingEvent; import org.apache.mina.core.write.WriteRequest;
import org.dna.mqtt.moquette.messaging.spi.impl.events.NotifyEvent; import org.dna.mqtt.moquette.messaging.spi.impl.events.*;
import org.dna.mqtt.moquette.messaging.spi.impl.events.PublishEvent; import org.dna.mqtt.moquette.proto.messages.AbstractMessage;
import org.dna.mqtt.moquette.messaging.spi.impl.events.SubscribeEvent;
import org.dna.mqtt.moquette.proto.messages.AbstractMessage.QOSType; import org.dna.mqtt.moquette.proto.messages.AbstractMessage.QOSType;
import static org.junit.Assert.*; 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.Before;
import org.junit.Test; import org.junit.Test;
import org.mockito.ArgumentCaptor;
import static org.mockito.Mockito.*; import static org.junit.Assert.assertEquals;
/** /**
* *
@@ -24,15 +30,53 @@ public class SimpleMessagingTest {
final static String FAKE_TOPIC = "/news"; final static String FAKE_TOPIC = "/news";
SimpleMessaging messaging; SimpleMessaging messaging;
byte m_returnCode;
IoSession m_session;
// MQTTHandler m_handler;
ConnectMessage connMsg;
AbstractMessage m_receivedMessage;
@Before @Before
public void setUp() { public void setUp() throws InterruptedException {
messaging = new SimpleMessaging(); 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 @Test
public void testSubscribe() { public void testSubscribe() {
//Exercise //Exercise
SubscribeEvent evt = new SubscribeEvent(new Subscription(FAKE_CLIENT_ID, FAKE_TOPIC, QOSType.MOST_ONE, false)); SubscribeEvent evt = new SubscribeEvent(new Subscription(FAKE_CLIENT_ID, FAKE_TOPIC, QOSType.MOST_ONE, false), 0);
messaging.processSubscribe(evt); messaging.processSubscribe(evt);
//Verify //Verify
@@ -42,7 +86,7 @@ public class SimpleMessagingTest {
@Test @Test
public void testDoubleSubscribe() { public void testDoubleSubscribe() {
SubscribeEvent evt = new SubscribeEvent(new Subscription(FAKE_CLIENT_ID, FAKE_TOPIC, QOSType.MOST_ONE, false)); SubscribeEvent evt = new SubscribeEvent(new Subscription(FAKE_CLIENT_ID, FAKE_TOPIC, QOSType.MOST_ONE, false), 0);
messaging.processSubscribe(evt); messaging.processSubscribe(evt);
//Exercise //Exercise
@@ -55,26 +99,28 @@ public class SimpleMessagingTest {
@Test @Test
public void testPublish() throws InterruptedException { public void testPublish() throws InterruptedException {
SubscribeEvent evt = new SubscribeEvent(new Subscription(FAKE_CLIENT_ID, FAKE_TOPIC, QOSType.MOST_ONE, false)); //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);
messaging.processSubscribe(evt); messaging.processSubscribe(evt);
INotifier notifier = mock(INotifier.class);
messaging.setNotifier(notifier);
//Exercise //Exercise
PublishEvent pubEvt = new PublishEvent(FAKE_TOPIC, QOSType.MOST_ONE, "Hello".getBytes(), false, "FakeCLI", null); PublishEvent pubEvt = new PublishEvent(FAKE_TOPIC, QOSType.MOST_ONE, "Hello".getBytes(), false, "FakeCLI", null);
messaging.processPublish(pubEvt); messaging.processPublish(pubEvt);
//Verify //Verify
ArgumentCaptor<NotifyEvent> argument = ArgumentCaptor.forClass(NotifyEvent.class); assertNotNull(m_receivedMessage);
verify(notifier).notify(argument.capture()); //TODO check received message attributes
} }
@Test @Test
public void testMatchTopics_simple() { public void testMatchTopics_simple() {
assertTrue(messaging.matchTopics("/", "/")); assertTrue(messaging.matchTopics("/", "/"));
assertTrue(messaging.matchTopics("/finance", "/finance")); assertTrue(messaging.matchTopics("/finance", "/finance"));
} }
@Test @Test
public void testMatchTopics_multi() { public void testMatchTopics_multi() {
assertTrue(messaging.matchTopics("finance", "#")); assertTrue(messaging.matchTopics("finance", "#"));
@@ -82,8 +128,8 @@ public class SimpleMessagingTest {
assertTrue(messaging.matchTopics("finance/stock", "finance/#")); assertTrue(messaging.matchTopics("finance/stock", "finance/#"));
assertTrue(messaging.matchTopics("finance/stock/ibm", "finance/#")); assertTrue(messaging.matchTopics("finance/stock/ibm", "finance/#"));
} }
@Test @Test
public void testMatchTopics_single() { public void testMatchTopics_single() {
assertTrue(messaging.matchTopics("finance", "+")); assertTrue(messaging.matchTopics("finance", "+"));
@@ -94,4 +140,74 @@ public class SimpleMessagingTest {
assertTrue(messaging.matchTopics("/finance/stock/ibm", "/finance/+/ibm")); assertTrue(messaging.matchTopics("/finance/stock/ibm", "/finance/+/ibm"));
assertFalse(messaging.matchTopics("/finance/stock", "+")); assertFalse(messaging.matchTopics("/finance/stock", "+"));
} }
@Test
public void testHandleConnect_BadProtocol() {
connMsg.setProcotolVersion((byte) 0x02);
//Exercise
ConnectEvent pubEvt = new ConnectEvent(m_session, connMsg);
messaging.processConnect(pubEvt);
//Verify
assertEquals(ConnAckMessage.UNNACEPTABLE_PROTOCOL_VERSION, m_returnCode);
}
@Test
public void testConnect_badClientID() {
connMsg.setClientID("extremely_long_clientID_greater_than_23");
//Exercise
ConnectEvent pubEvt = new ConnectEvent(m_session, connMsg);
messaging.processConnect(pubEvt);
//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);
ConnectEvent pubEvt = new ConnectEvent(m_session, connMsg);
messaging.processConnect(pubEvt);
//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);
messaging.processSubscribe(pubEvt);
//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!!
}
} }
@@ -1,120 +0,0 @@
package org.dna.mqtt.moquette.server;
import org.mockito.ArgumentCaptor;
import org.dna.mqtt.moquette.proto.messages.AbstractMessage;
import org.apache.mina.core.write.WriteRequest;
import org.dna.mqtt.moquette.proto.messages.ConnAckMessage;
import org.apache.mina.core.filterchain.IoFilterAdapter;
import org.apache.mina.core.session.DummySession;
import org.apache.mina.core.session.IoSession;
import org.dna.mqtt.moquette.messaging.spi.IMessaging;
import org.dna.mqtt.moquette.proto.messages.ConnectMessage;
import org.dna.mqtt.moquette.proto.messages.SubscribeMessage;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
/**
*
* @author andrea
*/
public class MQTTHandlerTest {
byte m_returnCode;
IoSession m_session;
MQTTHandler m_handler;
ConnectMessage connMsg;
@Before
public void setUp() {
m_handler = new MQTTHandler();
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 {
ConnAckMessage buf = (ConnAckMessage) writeRequest.getMessage();
m_returnCode = buf.getReturnCode();
} catch (Exception ex) {
throw new AssertionError("Wrong return code");
}
}
});
}
@Test
public void testHandleConnect_BadProtocol() {
connMsg.setProcotolVersion((byte) 0x02);
//Exercise
m_handler.handleConnect(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
m_handler.handleConnect(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);
m_handler.handleConnect(m_session, connMsg);
//Verify
assertEquals(ConnAckMessage.CONNECTION_ACCEPTED, m_returnCode);
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);
//Exercise
m_handler.setMessaging(mockedMessaging);
m_handler.handleSubscribe(m_session, subscribeMsg);
//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!!
}
// @Test
// public void testHandleSubscribeMultipleTopics() {
// fail("TOBE implemented");
// }
}
@@ -48,29 +48,29 @@ public class ServerIntegrationTest {
Client client = new Client("localhost", Server.PORT); Client client = new Client("localhost", Server.PORT);
// Client client = new Client("test.mosquitto.org", 1883); // Client client = new Client("test.mosquitto.org", 1883);
client.connect(); client.connect();
client.subscribe("/topic", new IPublishCallback() { client.subscribe("/topic", new IPublishCallback() {
public void published(String topic, byte[] message) { public void published(String topic, byte[] message) {
received = true; received = true;
} }
}); });
client.publish("/topic", "Test my payload".getBytes()); client.publish("/topic", "Test my payload".getBytes());
client.close(); client.close();
client.shutdown(); client.shutdown();
assertTrue(received); assertTrue(received);
} }
@Test @Test
public void testCleanSession_maintainClientSubscriptions() { public void testCleanSession_maintainClientSubscriptions() {
Client client = new Client("localhost", Server.PORT/*, "CLID_123"*/); Client client = new Client("localhost", Server.PORT/*, "CLID_123"*/);
client.connect(false); //without session cleanup client.connect(false); //without session cleanup
client.subscribe("/topic", new IPublishCallback() { client.subscribe("/topic", new IPublishCallback() {
public void published(String topic, byte[] message) { public void published(String topic, byte[] message) {
@@ -78,24 +78,24 @@ public class ServerIntegrationTest {
} }
}); });
client.close(); client.close();
//Exercise that the client maintain the subscriptions //Exercise that the client maintain the subscriptions
client.connect(false); client.connect(false);
client.publish("/topic", "Test my payload".getBytes()); client.publish("/topic", "Test my payload".getBytes());
client.close(); client.close();
//Verify //Verify
assertTrue(received); assertTrue(received);
//TearDown //TearDown
client.shutdown(); client.shutdown();
} }
@Test @Test
public void testCleanSession_maintainClientSubscriptions_againstClientDestruction() { public void testCleanSession_maintainClientSubscriptions_againstClientDestruction() {
Client client = new Client("localhost", Server.PORT, "CLID_123"); Client client = new Client("localhost", Server.PORT, "CLID_123");
client.connect(false); //without session cleanup client.connect(false); //without session cleanup
client.subscribe("/topic", new IPublishCallback() { client.subscribe("/topic", new IPublishCallback() {
public void published(String topic, byte[] message) { public void published(String topic, byte[] message) {
@@ -104,7 +104,7 @@ public class ServerIntegrationTest {
}); });
client.close(); client.close();
client.shutdown(); client.shutdown();
//Exercise that the client maintain the subscriptions //Exercise that the client maintain the subscriptions
client = new Client("localhost", Server.PORT, "CLID_123"); client = new Client("localhost", Server.PORT, "CLID_123");
client.register("/topic", new IPublishCallback() { client.register("/topic", new IPublishCallback() {
@@ -113,20 +113,20 @@ public class ServerIntegrationTest {
received = true; received = true;
} }
}); });
client.connect(false); client.connect(false);
client.publish("/topic", "Test my payload".getBytes()); client.publish("/topic", "Test my payload".getBytes());
client.close(); client.close();
//Verify //Verify
assertTrue(received); assertTrue(received);
//TearDown //TearDown
client.shutdown(); client.shutdown();
} }
/** /**
* Check that after a client has connected with clean session false, subscribed * 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 server * to some topic and exited, if it reconnect with clean session true, the server
* correctly cleanup every previous subscription * correctly cleanup every previous subscription
*/ */
@@ -134,7 +134,7 @@ public class ServerIntegrationTest {
public void testCleanSession_correctlyClientSubscriptions() { public void testCleanSession_correctlyClientSubscriptions() {
Client client = new Client("localhost", Server.PORT, "CLID_123"); Client client = new Client("localhost", Server.PORT, "CLID_123");
client.connect(false); //without session cleanup client.connect(false); //without session cleanup
client.subscribe("/topic", new IPublishCallback() { client.subscribe("/topic", new IPublishCallback() {
public void published(String topic, byte[] message) { public void published(String topic, byte[] message) {
@@ -143,7 +143,7 @@ public class ServerIntegrationTest {
}); });
client.close(); client.close();
client.shutdown(); client.shutdown();
//Exercise that the client maintain the subscriptions //Exercise that the client maintain the subscriptions
client = new Client("localhost", Server.PORT, "CLID_123"); client = new Client("localhost", Server.PORT, "CLID_123");
client.register("/topic", new IPublishCallback() { client.register("/topic", new IPublishCallback() {
@@ -152,16 +152,16 @@ public class ServerIntegrationTest {
received = true; received = true;
} }
}); });
client.connect(true); client.connect(true);
client.publish("/topic", "Test my payload".getBytes()); client.publish("/topic", "Test my payload".getBytes());
client.close(); client.close();
//Verify //Verify
assertFalse(received); assertFalse(received);
//TearDown //TearDown
client.shutdown(); client.shutdown();
} }
@Test @Test
@@ -174,21 +174,27 @@ public class ServerIntegrationTest {
public void published(String topic, byte[] message) { public void published(String topic, byte[] message) {
received = true; received = true;
barrier.countDown();
} }
}); });
//never called because no notification is pending
assertFalse(barrier.await(1, TimeUnit.SECONDS));
client.close(); client.close();
client.shutdown(); client.shutdown();
server.stopServer(); server.stopServer();
server.startServer(); server.startServer();
final CountDownLatch barrier2 = new CountDownLatch(1);
//Exercise that the client maintain the subscriptions //Exercise that the client maintain the subscriptions
client = new Client("localhost", Server.PORT, "CLID_123"); client = new Client("localhost", Server.PORT, "CLID_123");
client.register("/topic", new IPublishCallback() { client.register("/topic", new IPublishCallback() {
public void published(String topic, byte[] message) { public void published(String topic, byte[] message) {
received = true; received = true;
barrier.countDown(); barrier2.countDown();
} }
}); });
client.connect(false); client.connect(false);
@@ -196,7 +202,7 @@ public class ServerIntegrationTest {
client.close(); client.close();
//Verify //Verify
barrier.await(1, TimeUnit.SECONDS); assertTrue(barrier2.await(1, TimeUnit.SECONDS));
assertTrue(received); assertTrue(received);
//TearDown //TearDown
@@ -209,14 +215,14 @@ public class ServerIntegrationTest {
final CountDownLatch barrier = new CountDownLatch(1); final CountDownLatch barrier = new CountDownLatch(1);
Client client = new Client("localhost", Server.PORT, "CLID_123"); Client client = new Client("localhost", Server.PORT, "CLID_123");
client.connect(); client.connect();
client.publish("/topic", "Test my payload".getBytes(), true); client.publish("/topic", "Test my payload".getBytes(), true);
client.close(); client.close();
client.shutdown(); client.shutdown();
//Exercise that the client maintain the subscriptions //Exercise that the client maintain the subscriptions
client = new Client("localhost", Server.PORT, "CLID_123"); client = new Client("localhost", Server.PORT, "CLID_123");
client.connect(); client.connect();
client.subscribe("/topic", new IPublishCallback() { client.subscribe("/topic", new IPublishCallback() {
public void published(String topic, byte[] message) { public void published(String topic, byte[] message) {
@@ -225,24 +231,24 @@ public class ServerIntegrationTest {
} }
}); });
client.close(); client.close();
//Verify //Verify
barrier.await(1, TimeUnit.SECONDS); barrier.await(1, TimeUnit.SECONDS);
assertTrue(received); assertTrue(received);
//TearDown //TearDown
client.shutdown(); client.shutdown();
} }
@Ignore @Ignore
public void testUnsubscribe_do_not_notify_anymore_same_session() throws InterruptedException { public void testUnsubscribe_do_not_notify_anymore_same_session() throws InterruptedException {
Client client = new Client("localhost", Server.PORT, "CLID_123"); Client client = new Client("localhost", Server.PORT, "CLID_123");
// Client client = new Client("test.mosquitto.org", 1883); // Client client = new Client("test.mosquitto.org", 1883);
client.connect(); client.connect();
final CountDownLatch barrier = new CountDownLatch(1); final CountDownLatch barrier = new CountDownLatch(1);
client.subscribe("/topic", new IPublishCallback() { client.subscribe("/topic", new IPublishCallback() {
public void published(String topic, byte[] message) { public void published(String topic, byte[] message) {
@@ -250,33 +256,33 @@ public class ServerIntegrationTest {
barrier.countDown(); barrier.countDown();
} }
}); });
client.publish("/topic", "Test my payload".getBytes()); client.publish("/topic", "Test my payload".getBytes());
//wait 1 second to receive the published message //wait 1 second to receive the published message
boolean unlocked = barrier.await(1000, TimeUnit.MILLISECONDS); boolean unlocked = barrier.await(1000, TimeUnit.MILLISECONDS);
assertTrue(unlocked); //we were unlocked by the message reception assertTrue(unlocked); //we were unlocked by the message reception
assertTrue(received); assertTrue(received);
//reinit the flag //reinit the flag
received = false; received = false;
//unsubscrbe and republish to check no notification is raised up //unsubscrbe and republish to check no notification is raised up
client.unsubscribe("/topic"); client.unsubscribe("/topic");
client.publish("/topic", "Test my payload".getBytes()); client.publish("/topic", "Test my payload".getBytes());
assertFalse(received); assertFalse(received);
client.close(); client.close();
client.shutdown(); client.shutdown();
} }
@Test @Test
public void testUnsubscribe_do_not_notify_anymore_new_session() throws InterruptedException { public void testUnsubscribe_do_not_notify_anymore_new_session() throws InterruptedException {
Client client = new Client("localhost", Server.PORT, "CLID_123"); Client client = new Client("localhost", Server.PORT, "CLID_123");
// Client client = new Client("test.mosquitto.org", 1883); // Client client = new Client("test.mosquitto.org", 1883);
client.connect(); client.connect();
final CountDownLatch barrier = new CountDownLatch(1); final CountDownLatch barrier = new CountDownLatch(1);
client.subscribe("/topic", new IPublishCallback() { client.subscribe("/topic", new IPublishCallback() {
public void published(String topic, byte[] message) { public void published(String topic, byte[] message) {
@@ -284,33 +290,33 @@ public class ServerIntegrationTest {
barrier.countDown(); barrier.countDown();
} }
}); });
client.publish("/topic", "Test my payload".getBytes()); client.publish("/topic", "Test my payload".getBytes());
//wait 1 second to receive the published message //wait 1 second to receive the published message
boolean unlocked = barrier.await(1000, TimeUnit.MILLISECONDS); boolean unlocked = barrier.await(1000, TimeUnit.MILLISECONDS);
assertTrue(unlocked); //we were unlocked by the message reception assertTrue(unlocked); //we were unlocked by the message reception
assertTrue(received); assertTrue(received);
//unsubscrbe and republish to check no notification is raised up //unsubscrbe and republish to check no notification is raised up
client.unsubscribe("/topic"); client.unsubscribe("/topic");
client.close(); client.close();
client.shutdown(); client.shutdown();
//Exercise that the client maintain the subscriptions //Exercise that the client maintain the subscriptions
client = new Client("localhost", Server.PORT, "CLID_123"); client = new Client("localhost", Server.PORT, "CLID_123");
client.connect(); client.connect();
//reinit the flag //reinit the flag
received = false; received = false;
final CountDownLatch barrier2 = new CountDownLatch(1); final CountDownLatch barrier2 = new CountDownLatch(1);
client.publish("/topic", "Test my payload".getBytes()); client.publish("/topic", "Test my payload".getBytes());
unlocked = barrier2.await(1000, TimeUnit.MILLISECONDS); unlocked = barrier2.await(1000, TimeUnit.MILLISECONDS);
assertFalse(unlocked); //we were unlocked by the timeout exipration, no message received assertFalse(unlocked); //we were unlocked by the timeout exipration, no message received
assertFalse(received); assertFalse(received);
client.close(); client.close();
client.shutdown(); client.shutdown();
} }