Merged topic subscription search and publish into single MessagePublisher method

First step on implementing H2 storage

Minor, fixed not imported class

Cleaned MessageStore interface to avoid duplication of methods used in couple. Switched from guid + message to move messages

Separated ISubscriptionsStore from ISessionStore

Implemented H2 queue
This commit is contained in:
andsel
2017-06-02 10:13:34 +02:00
parent c18c915dd2
commit d9ff81131c
39 changed files with 1585 additions and 735 deletions
@@ -16,20 +16,19 @@
package io.moquette.persistence;
import io.moquette.spi.IMessagesStore;
import io.moquette.spi.IMatchingCondition;
import io.moquette.spi.MessageGUID;
import io.moquette.spi.IMessagesStore;
import io.moquette.spi.impl.subscriptions.Topic;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.*;
public class MemoryMessagesStore implements IMessagesStore {
private static final Logger LOG = LoggerFactory.getLogger(MemoryMessagesStore.class);
private Map<Topic, MessageGUID> m_retainedStore = new HashMap<>();
private Map<MessageGUID, StoredMessage> m_persistentMessageStore = new HashMap<>();
private Map<Topic, StoredMessage> m_retainedStore = new HashMap<>();
MemoryMessagesStore() {
}
@@ -39,8 +38,12 @@ public class MemoryMessagesStore implements IMessagesStore {
}
@Override
public void storeRetained(Topic topic, MessageGUID guid) {
m_retainedStore.put(topic, guid);
public void storeRetained(Topic topic, StoredMessage storedMessage) {
LOG.debug("Store retained message for topic={}, CId={}", topic, storedMessage.getClientID());
if (storedMessage.getClientID() == null) {
throw new IllegalArgumentException( "Message to be persisted must have a not null client ID");
}
m_retainedStore.put(topic, storedMessage);
}
@Override
@@ -49,9 +52,8 @@ public class MemoryMessagesStore implements IMessagesStore {
List<StoredMessage> results = new ArrayList<>();
for (Map.Entry<Topic, MessageGUID> entry : m_retainedStore.entrySet()) {
final MessageGUID guid = entry.getValue();
StoredMessage storedMsg = m_persistentMessageStore.get(guid);
for (Map.Entry<Topic, StoredMessage> entry : m_retainedStore.entrySet()) {
StoredMessage storedMsg = entry.getValue();
if (condition.match(entry.getKey())) {
results.add(storedMsg);
}
@@ -60,31 +62,6 @@ public class MemoryMessagesStore implements IMessagesStore {
return results;
}
@Override
public MessageGUID storePublishForFuture(StoredMessage storedMessage) {
LOG.debug("storePublishForFuture store evt {}", storedMessage);
MessageGUID guid = new MessageGUID(UUID.randomUUID().toString());
storedMessage.setGuid(guid);
m_persistentMessageStore.put(guid, storedMessage);
return guid;
}
@Override
public void dropInFlightMessagesInSession(Collection<MessageGUID> pendingAckMessages) {
//remove all guids from retained
Collection<MessageGUID> messagesToRemove = new HashSet<>(pendingAckMessages);
messagesToRemove.removeAll(m_retainedStore.values());
for (MessageGUID guid : messagesToRemove) {
m_persistentMessageStore.remove(guid);
}
}
@Override
public StoredMessage getMessageByGuid(MessageGUID guid) {
return m_persistentMessageStore.get(guid);
}
@Override
public void cleanRetained(Topic topic) {
m_retainedStore.remove(topic);
@@ -18,22 +18,20 @@ package io.moquette.persistence;
import io.moquette.server.Constants;
import io.moquette.spi.ClientSession;
import io.moquette.spi.IMessagesStore;
import io.moquette.spi.IMessagesStore.StoredMessage;
import io.moquette.spi.ISessionsStore;
import io.moquette.spi.MessageGUID;
import io.moquette.spi.ISubscriptionsStore;
import io.moquette.spi.impl.Utils;
import io.moquette.spi.impl.subscriptions.Subscription;
import io.moquette.spi.impl.subscriptions.Topic;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.*;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import static io.moquette.spi.impl.Utils.defaultGet;
public class MemorySessionStore implements ISessionsStore {
public class MemorySessionStore implements ISessionsStore, ISubscriptionsStore {
private static final Logger LOG = LoggerFactory.getLogger(MemorySessionStore.class);
@@ -41,21 +39,18 @@ public class MemorySessionStore implements ISessionsStore {
private Map<String, PersistentSession> m_persistentSessions = new HashMap<>();
// maps clientID->[MessageId -> guid]
private Map<String, Map<Integer, MessageGUID>> m_inflightStore = new HashMap<>();
// maps clientID->BlockingQueue
private Map<String, BlockingQueue<StoredMessage>> queues = new HashMap<>();
// maps clientID->[MessageId -> guid]
private Map<String, Map<Integer, MessageGUID>> m_secondPhaseStore = new HashMap<>();
// maps clientID->[MessageId -> msg]
private Map<String, Map<Integer, StoredMessage>> m_secondPhaseStore = new HashMap<>();
private Map<String, Map<Integer, MessageGUID>> outboundFlightMessageToGuid = new HashMap<>();
// maps clientID->[MessageId -> msg]
private Map<String, Map<Integer, StoredMessage>> outboundFlightMessages = new HashMap<>();
private Map<String, Map<Integer, MessageGUID>> inboundFlightMessageToGuid = new HashMap<>();
// maps clientID->[MessageId -> msg]
private Map<String, Map<Integer, StoredMessage>> inboundFlightMessages = new HashMap<>();
private final IMessagesStore m_messagesStore;
public MemorySessionStore(IMessagesStore messagesStore) {
this.m_messagesStore = messagesStore;
public MemorySessionStore() {
}
@Override
@@ -72,6 +67,11 @@ public class MemorySessionStore implements ISessionsStore {
public void initStore() {
}
@Override
public ISubscriptionsStore subscriptionStore() {
return this;
}
@Override
public void addNewSubscription(Subscription newSubscription) {
final String clientID = newSubscription.getClientId();
@@ -102,7 +102,7 @@ public class MemorySessionStore implements ISessionsStore {
LOG.debug("clientID {} is a newcome, creating it's empty subscriptions set", clientID);
m_persistentSubscriptions.put(clientID, new HashMap<Topic, Subscription>());
m_persistentSessions.put(clientID, new PersistentSession(cleanSession));
return new ClientSession(clientID, m_messagesStore, this, cleanSession);
return new ClientSession(clientID, this, this, cleanSession);
}
@Override
@@ -112,14 +112,14 @@ public class MemorySessionStore implements ISessionsStore {
}
PersistentSession storedSession = m_persistentSessions.get(clientID);
return new ClientSession(clientID, m_messagesStore, this, storedSession.cleanSession);
return new ClientSession(clientID, this, this, storedSession.cleanSession);
}
@Override
public Collection<ClientSession> getAllSessions() {
Collection<ClientSession> result = new ArrayList<>();
for (Map.Entry<String, PersistentSession> entry : m_persistentSessions.entrySet()) {
result.add(new ClientSession(entry.getKey(), m_messagesStore, this, entry.getValue().cleanSession));
result.add(new ClientSession(entry.getKey(), this, this, entry.getValue().cleanSession));
}
return result;
}
@@ -159,28 +159,25 @@ public class MemorySessionStore implements ISessionsStore {
}
@Override
public void inFlightAck(String clientID, int messageID) {
Map<Integer, MessageGUID> m = this.m_inflightStore.get(clientID);
public StoredMessage inFlightAck(String clientID, int messageID) {
Map<Integer, StoredMessage> m = this.outboundFlightMessages.get(clientID);
if (m == null) {
LOG.error("Can't find the inFlight record for client <{}>", clientID);
return;
throw new RuntimeException("Can't find the inFlight record for client <" + clientID + ">");
}
m.remove(messageID);
StoredMessage msg = m.remove(messageID);
this.outboundFlightMessages.put(clientID, m);
return msg;
}
@Override
public void inFlight(String clientID, int messageID, MessageGUID guid) {
Map<Integer, MessageGUID> m = this.m_inflightStore.get(clientID);
if (m == null) {
m = new HashMap<>();
public void inFlight(String clientID, int messageID, StoredMessage msg) {
Map<Integer, StoredMessage> messages = outboundFlightMessages.get(clientID);
if (messages == null) {
messages = new HashMap<>();
}
m.put(messageID, guid);
this.m_inflightStore.put(clientID, m);
final HashMap<Integer, MessageGUID> emptyGuids = new HashMap<>();
Map<Integer, MessageGUID> guids = defaultGet(outboundFlightMessageToGuid, clientID, emptyGuids);
guids.put(messageID, guid);
outboundFlightMessageToGuid.put(clientID, guids);
messages.put(messageID, msg);
outboundFlightMessages.put(clientID, messages);
}
/**
@@ -188,7 +185,7 @@ public class MemorySessionStore implements ISessionsStore {
*/
@Override
public int nextPacketID(String clientID) {
Map<Integer, MessageGUID> m = this.m_inflightStore.get(clientID);
Map<Integer, StoredMessage> m = this.outboundFlightMessages.get(clientID);
if (m == null) {
m = new HashMap<>();
int nextPacketId = 1;
@@ -215,61 +212,73 @@ public class MemorySessionStore implements ISessionsStore {
}
@Override
public void moveInFlightToSecondPhaseAckWaiting(String clientID, int messageID) {
LOG.info("acknowledging inflight clientID <{}> messageID {}", clientID, messageID);
Map<Integer, MessageGUID> m = this.m_inflightStore.get(clientID);
public void moveInFlightToSecondPhaseAckWaiting(String clientID, int messageID, StoredMessage msg) {
LOG.info("Moving msg inflight second phase store, clientID <{}> messageID {}", clientID, messageID);
Map<Integer, StoredMessage> m = this.m_secondPhaseStore.get(clientID);
if (m == null) {
LOG.error("Can't find the inFlight record for client <{}>", clientID);
return;
m = new HashMap<>();
}
MessageGUID guid = m.remove(messageID);
LOG.info("Moving to second phase store");
final HashMap<Integer, MessageGUID> emptyGuids = new HashMap<>();
Map<Integer, MessageGUID> messageIDs = Utils.defaultGet(m_secondPhaseStore, clientID, emptyGuids);
messageIDs.put(messageID, guid);
m_secondPhaseStore.put(clientID, messageIDs);
m.put(messageID, msg);
this.outboundFlightMessages.put(clientID, m);
}
@Override
public MessageGUID secondPhaseAcknowledged(String clientID, int messageID) {
final HashMap<Integer, MessageGUID> emptyGuids = new HashMap<>();
Map<Integer, MessageGUID> messageIDs = Utils.defaultGet(m_secondPhaseStore, clientID, emptyGuids);
MessageGUID guid = messageIDs.remove(messageID);
m_secondPhaseStore.put(clientID, messageIDs);
return guid;
}
public StoredMessage secondPhaseAcknowledged(String clientID, int messageID) {
LOG.info("Acknowledged message in second phase, clientID <{}> messageID {}", clientID, messageID);
final Map<Integer, StoredMessage> m = this.m_secondPhaseStore.get(clientID);
if (m == null) {
String error = String.format("Can't find the inFlight record for client <%s> during the second phase " +
"acking of QoS2 pub", clientID);
LOG.error(error);
throw new RuntimeException(error);
}
@Override
public StoredMessage getInflightMessage(String clientID, int messageID) {
Map<Integer, MessageGUID> inflightMessages = m_inflightStore.get(clientID);
MessageGUID guid = inflightMessages.get(messageID);
return m_messagesStore.getMessageByGuid(guid);
StoredMessage msg = m.remove(messageID);
m_secondPhaseStore.put(clientID, m);
return msg;
}
@Override
public int getInflightMessagesNo(String clientID) {
Map<Integer, MessageGUID> inflightMessages = m_inflightStore.get(clientID);
if (inflightMessages == null)
return 0;
else
return inflightMessages.size();
int totalInflight = 0;
Map<Integer, StoredMessage> inflightPerClient = this.inboundFlightMessages.get(clientID);
if (inflightPerClient != null) {
totalInflight += inflightPerClient.size();
}
Map<Integer, StoredMessage> secondPhaseInFlight = this.m_secondPhaseStore.get(clientID);
if (secondPhaseInFlight != null) {
totalInflight += secondPhaseInFlight.size();
}
Map<Integer, StoredMessage> outboundPerClient = outboundFlightMessages.get(clientID);
if (outboundPerClient != null) {
totalInflight += outboundPerClient.size();
}
return totalInflight;
}
@Override
public StoredMessage inboundInflight(String clientID, int messageID) {
final HashMap<Integer, MessageGUID> emptyGuids = new HashMap<>();
Map<Integer, MessageGUID> guids = Utils.defaultGet(inboundFlightMessageToGuid, clientID, emptyGuids);
final MessageGUID guid = guids.get(messageID);
return m_messagesStore.getMessageByGuid(guid);
Map<Integer, StoredMessage> inflightPerClient = this.inboundFlightMessages.get(clientID);
if (inflightPerClient == null) {
String error = String.format("Can't find inbound inflight zone for client <%s>", clientID);
LOG.error(error);
throw new RuntimeException(error);
}
return inflightPerClient.get(messageID);
}
@Override
public void markAsInboundInflight(String clientID, int messageID, MessageGUID guid) {
final HashMap<Integer, MessageGUID> emptyGuids = new HashMap<>();
Map<Integer, MessageGUID> guids = Utils.defaultGet(inboundFlightMessageToGuid, clientID, emptyGuids);
guids.put(messageID, guid);
inboundFlightMessageToGuid.put(clientID, guids);
public void markAsInboundInflight(String clientID, int messageID, StoredMessage msg) {
Map<Integer, StoredMessage> inflightPerClient = this.inboundFlightMessages.get(clientID);
if (inflightPerClient == null) {
inflightPerClient = new HashMap<>();
}
inflightPerClient.put(messageID, msg);
inboundFlightMessages.put(clientID, inflightPerClient);
}
@Override
@@ -279,7 +288,7 @@ public class MemorySessionStore implements ISessionsStore {
@Override
public int getSecondPhaseAckPendingMessages(String clientID) {
Map<Integer, MessageGUID> pendingAcks = m_secondPhaseStore.get(clientID);
Map<Integer, StoredMessage> pendingAcks = m_secondPhaseStore.get(clientID);
if (pendingAcks == null)
return 0;
else
@@ -287,12 +296,8 @@ public class MemorySessionStore implements ISessionsStore {
}
@Override
public Collection<MessageGUID> pendingAck(String clientID) {
Map<Integer, MessageGUID> messageGUIDMap = outboundFlightMessageToGuid.get(clientID);
if (messageGUIDMap == null || messageGUIDMap.isEmpty()) {
return Collections.emptyList();
}
return new ArrayList<>(messageGUIDMap.values());
public void dropInFlightMessagesInSession(String clientID) {
outboundFlightMessages.remove(clientID);
inboundFlightMessages.remove(clientID);
}
}
@@ -27,7 +27,7 @@ public class MemoryStorageService implements IStore {
public MemoryStorageService() {
m_messagesStore = new MemoryMessagesStore();
m_sessionsStore = new MemorySessionStore(m_messagesStore);
m_sessionsStore = new MemorySessionStore();
m_messagesStore.initStore();
m_sessionsStore.initStore();
}
@@ -42,6 +42,10 @@ public class MemoryStorageService implements IStore {
return m_sessionsStore;
}
@Override
public void initStore() {
}
@Override
public void close() {
}
@@ -16,11 +16,12 @@
package io.moquette.spi;
import io.moquette.spi.ISessionsStore.ClientTopicCouple;
import io.moquette.spi.ISubscriptionsStore.ClientTopicCouple;
import io.moquette.spi.impl.subscriptions.Subscription;
import io.moquette.spi.impl.subscriptions.Topic;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.BlockingQueue;
@@ -44,22 +45,22 @@ public class ClientSession {
class OutboundFlightZone {
/**
* Save the binding messageID, clientID - guid
* Save the binding messageID, clientID - message
*
* @param messageID the packet ID used in transmission
* @param guid the guid of the message being in flight.
* @param msg the message to put in flight zone
*/
void waitingAck(int messageID, MessageGUID guid) {
void waitingAck(int messageID, IMessagesStore.StoredMessage msg) {
if (LOG.isTraceEnabled()) {
LOG.trace("Adding to inflight {}, guid <{}>", messageID, guid);
LOG.trace("Adding to inflight {}, guid <{}>", messageID, msg.getGuid());
}
m_sessionsStore.inFlight(ClientSession.this.clientID, messageID, guid);
m_sessionsStore.inFlight(ClientSession.this.clientID, messageID, msg);
}
void acknowledged(int messageID) {
IMessagesStore.StoredMessage acknowledged(int messageID) {
if (LOG.isTraceEnabled())
LOG.trace("Acknowledging inflight, clientID <{}> messageID {}", ClientSession.this.clientID, messageID);
m_sessionsStore.inFlightAck(ClientSession.this.clientID, messageID);
return m_sessionsStore.inFlightAck(ClientSession.this.clientID, messageID);
}
}
@@ -69,8 +70,8 @@ public class ClientSession {
return m_sessionsStore.inboundInflight(clientID, messageID);
}
public void waitingRel(int messageID, MessageGUID guid) {
m_sessionsStore.markAsInboundInflight(clientID, messageID, guid);
public void waitingRel(int messageID, IMessagesStore.StoredMessage msg) {
m_sessionsStore.markAsInboundInflight(clientID, messageID, msg);
}
}
@@ -78,12 +79,11 @@ public class ClientSession {
public final String clientID;
private final IMessagesStore messagesStore;
private final ISessionsStore m_sessionsStore;
private Set<Subscription> subscriptions = new HashSet<>();
private final ISubscriptionsStore subscriptionsStore;
private volatile boolean cleanSession;
// private BlockingQueue<AbstractMessage> m_queueToPublish = new
@@ -91,11 +91,11 @@ public class ClientSession {
private final OutboundFlightZone outboundFlightZone;
private final InboundFlightZone inboundFlightZone;
public ClientSession(String clientID, IMessagesStore messagesStore, ISessionsStore sessionsStore,
boolean cleanSession) {
public ClientSession(String clientID, ISessionsStore sessionsStore, ISubscriptionsStore subscriptionsStore,
boolean cleanSession) {
this.clientID = clientID;
this.messagesStore = messagesStore;
this.m_sessionsStore = sessionsStore;
this.subscriptionsStore = subscriptionsStore;
this.cleanSession = cleanSession;
this.outboundFlightZone = new OutboundFlightZone();
this.inboundFlightZone = new InboundFlightZone();
@@ -129,7 +129,7 @@ public class ClientSession {
return false;
}
ClientTopicCouple matchingCouple = new ClientTopicCouple(this.clientID, newSubscription.getTopicFilter());
Subscription existingSub = m_sessionsStore.getSubscription(matchingCouple);
Subscription existingSub = subscriptionsStore.getSubscription(matchingCouple);
// update the selected subscriptions if not present or if has a greater qos
if (existingSub == null || existingSub.getRequestedQos().value() < newSubscription.getRequestedQos().value()) {
if (existingSub != null) {
@@ -139,14 +139,14 @@ public class ClientSession {
subscriptions.remove(newSubscription);
}
subscriptions.add(newSubscription);
m_sessionsStore.addNewSubscription(newSubscription);
subscriptionsStore.addNewSubscription(newSubscription);
}
return true;
}
public void unsubscribeFrom(Topic topicFilter) {
LOG.info("Removing subscription. ClientID={}, topics={}", clientID, topicFilter);
m_sessionsStore.removeSubscription(topicFilter, clientID);
subscriptionsStore.removeSubscription(topicFilter, clientID);
Set<Subscription> subscriptionsToRemove = new HashSet<>();
for (Subscription sub : this.subscriptions) {
if (sub.getTopicFilter().equals(topicFilter)) {
@@ -166,11 +166,11 @@ public class ClientSession {
public void cleanSession() {
LOG.info("Wiping existing subscriptions. ClientId={}", this.clientID);
m_sessionsStore.wipeSubscriptions(this.clientID);
subscriptionsStore.wipeSubscriptions(this.clientID);
// remove also the messages stored of type QoS1/2
LOG.info("Removing stored messages with QoS 1 and 2. ClientId={}", this.clientID);
messagesStore.dropInFlightMessagesInSession(m_sessionsStore.pendingAck(this.clientID));
m_sessionsStore.dropInFlightMessagesInSession(this.clientID);
//remove also the enqueued messages
this.m_sessionsStore.dropQueue(this.clientID);
@@ -189,8 +189,8 @@ public class ClientSession {
return this.m_sessionsStore.nextPacketID(this.clientID);
}
public void inFlightAcknowledged(int messageID) {
outboundFlightZone.acknowledged(messageID);
public IMessagesStore.StoredMessage inFlightAcknowledged(int messageID) {
return outboundFlightZone.acknowledged(messageID);
}
/**
@@ -200,21 +200,13 @@ public class ClientSession {
* */
public int inFlightAckWaiting(IMessagesStore.StoredMessage msg) {
LOG.debug("Adding message ot inflight zone. ClientId={}", clientID);
MessageGUID guid = msg.getGuid();
if (guid == null) {
guid = messagesStore.storePublishForFuture(msg);
msg.setGuid(guid);
LOG.debug("Not yet assigned guid to message, updating guid={}", guid);
}
int messageId = ClientSession.this.nextPacketId();
outboundFlightZone.waitingAck(messageId, guid);
outboundFlightZone.waitingAck(messageId, msg);
return messageId;
}
public IMessagesStore.StoredMessage secondPhaseAcknowledged(int messageID) {
MessageGUID guid = m_sessionsStore.secondPhaseAcknowledged(clientID, messageID);
return messagesStore.getMessageByGuid(guid);
return m_sessionsStore.secondPhaseAcknowledged(clientID, messageID);
}
/**
@@ -231,16 +223,12 @@ public class ClientSession {
return inboundFlightZone.lookup(messageID);
}
public void markAsInboundInflight(int messageID, MessageGUID guid) {
inboundFlightZone.waitingRel(messageID, guid);
public void markAsInboundInflight(int messageID, IMessagesStore.StoredMessage msg) {
inboundFlightZone.waitingRel(messageID, msg);
}
public void moveInFlightToSecondPhaseAckWaiting(int messageID) {
m_sessionsStore.moveInFlightToSecondPhaseAckWaiting(this.clientID, messageID);
}
public IMessagesStore.StoredMessage getInflightMessage(int messageID) {
return m_sessionsStore.getInflightMessage(clientID, messageID);
public void moveInFlightToSecondPhaseAckWaiting(int messageID, IMessagesStore.StoredMessage msg) {
m_sessionsStore.moveInFlightToSecondPhaseAckWaiting(this.clientID, messageID, msg);
}
public Set<Subscription> getSubscriptions() {
@@ -93,16 +93,6 @@ public interface IMessagesStore {
*/
void initStore();
/**
* Persist the message. If the message is empty then the topic is cleaned, else it's stored.
*
* @param topic
* for the retained.
* @param guid
* of the message to mark as retained.
*/
void storeRetained(Topic topic, MessageGUID guid);
/**
* Return a list of retained messages that satisfy the condition.
*
@@ -112,18 +102,7 @@ public interface IMessagesStore {
*/
Collection<StoredMessage> searchMatching(IMatchingCondition condition);
/**
* Persist the message.
*
* @param storedMessage
* the message to store for future usage.
* @return the unique id in the storage (guid).
*/
MessageGUID storePublishForFuture(StoredMessage storedMessage);
void dropInFlightMessagesInSession(Collection<MessageGUID> pendingAckMessages);
StoredMessage getMessageByGuid(MessageGUID guid);
void cleanRetained(Topic topic);
void storeRetained(Topic topic, StoredMessage storedMessage);
}
@@ -17,11 +17,8 @@
package io.moquette.spi;
import io.moquette.spi.IMessagesStore.StoredMessage;
import io.moquette.spi.impl.subscriptions.Subscription;
import io.moquette.spi.impl.subscriptions.Topic;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.BlockingQueue;
/**
@@ -29,90 +26,12 @@ import java.util.concurrent.BlockingQueue;
*/
public interface ISessionsStore {
class ClientTopicCouple {
public final Topic topicFilter;
public final String clientID;
public ClientTopicCouple(String clientID, Topic topicFilter) {
this.clientID = clientID;
this.topicFilter = topicFilter;
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
ClientTopicCouple that = (ClientTopicCouple) o;
if (topicFilter != null ? !topicFilter.equals(that.topicFilter) : that.topicFilter != null)
return false;
return !(clientID != null ? !clientID.equals(that.clientID) : that.clientID != null);
}
@Override
public int hashCode() {
int result = topicFilter != null ? topicFilter.hashCode() : 0;
result = 31 * result + (clientID != null ? clientID.hashCode() : 0);
return result;
}
@Override
public String toString() {
return "ClientTopicCouple{" + "topicFilter='" + topicFilter + '\'' + ", clientID='" + clientID + '\'' + '}';
}
}
void initStore();
ISubscriptionsStore subscriptionStore();
void updateCleanStatus(String clientID, boolean cleanSession);
/**
* Add a new subscription to the session
*
* @param newSubscription
* the subscription to add.
*/
void addNewSubscription(Subscription newSubscription);
/**
* Removed a specific subscription
*
* @param topic
* the topic of the subscription.
* @param clientID
* the session client.
*/
void removeSubscription(Topic topic, String clientID);
/**
* Remove all the subscriptions of the session
*
* @param sessionID
* the client ID
*/
void wipeSubscriptions(String sessionID);
/**
* Return all topic filters to recreate the subscription tree.
*/
List<ClientTopicCouple> listAllSubscriptions();
/**
* @param couple
* the subscription descriptor.
* @return the subscription stored by clientID and topicFilter, if any else null;
*/
Subscription getSubscription(ClientTopicCouple couple);
/*
* @return all subscriptions stored.
*/
List<Subscription> getSubscriptions();
/**
* @param clientID
* the session client ID.
@@ -132,23 +51,23 @@ public interface ISessionsStore {
/**
* Returns all the sessions
*
* @return
* @return the collection of all stored client sessions.
*/
Collection<ClientSession> getAllSessions();
void inFlightAck(String clientID, int messageID);
StoredMessage inFlightAck(String clientID, int messageID);
/**
* Save the binding messageID, clientID - guid
* Save the message msg with messageID, clientID as in flight
*
* @param clientID
* the client ID
* @param messageID
* the message ID
* @param guid
* the uuid of the message to mark as inflight.
* @param msg
* the message to put in flight zone
*/
void inFlight(String clientID, int messageID, MessageGUID guid);
void inFlight(String clientID, int messageID, StoredMessage msg);
/**
* Return the next valid packetIdentifier for the given client session.
@@ -170,7 +89,7 @@ public interface ISessionsStore {
void dropQueue(String clientID);
void moveInFlightToSecondPhaseAckWaiting(String clientID, int messageID);
void moveInFlightToSecondPhaseAckWaiting(String clientID, int messageID, StoredMessage msg);
/**
* @param clientID
@@ -179,9 +98,7 @@ public interface ISessionsStore {
* the message ID that reached the second phase.
* @return the guid of message just acked.
*/
MessageGUID secondPhaseAcknowledged(String clientID, int messageID);
StoredMessage getInflightMessage(String clientID, int messageID);
StoredMessage secondPhaseAcknowledged(String clientID, int messageID);
/**
* Returns the number of inflight messages for the given client ID
@@ -196,7 +113,7 @@ public interface ISessionsStore {
* */
IMessagesStore.StoredMessage inboundInflight(String clientID, int messageID);
void markAsInboundInflight(String clientID, int messageID, MessageGUID guid);
void markAsInboundInflight(String clientID, int messageID, StoredMessage msg);
/**
* Returns the size of the session queue for the given client ID
@@ -214,5 +131,5 @@ public interface ISessionsStore {
*/
int getSecondPhaseAckPendingMessages(String clientID);
Collection<MessageGUID> pendingAck(String clientID);
void dropInFlightMessagesInSession(String clientID);
}
@@ -18,9 +18,21 @@ package io.moquette.spi;
public interface IStore {
IMessagesStore messagesStore();
ISessionsStore sessionsStore();
void initStore();
void close();
/**
* Factory method to instantiate the messages store.
*
* @return the create instance of IMessagesStore.
* */
IMessagesStore messagesStore();
/**
* Factory method to instantiate the session store.
*
* @return the create instance of ISessionsStore.
* */
ISessionsStore sessionsStore();
}
@@ -0,0 +1,90 @@
package io.moquette.spi;
import io.moquette.spi.impl.subscriptions.Subscription;
import io.moquette.spi.impl.subscriptions.Topic;
import java.util.List;
public interface ISubscriptionsStore {
class ClientTopicCouple {
public final Topic topicFilter;
public final String clientID;
public ClientTopicCouple(String clientID, Topic topicFilter) {
this.clientID = clientID;
this.topicFilter = topicFilter;
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
ClientTopicCouple that = (ClientTopicCouple) o;
if (topicFilter != null ? !topicFilter.equals(that.topicFilter) : that.topicFilter != null)
return false;
return !(clientID != null ? !clientID.equals(that.clientID) : that.clientID != null);
}
@Override
public int hashCode() {
int result = topicFilter != null ? topicFilter.hashCode() : 0;
result = 31 * result + (clientID != null ? clientID.hashCode() : 0);
return result;
}
@Override
public String toString() {
return "ClientTopicCouple{" + "topicFilter='" + topicFilter + '\'' + ", clientID='" + clientID + '\'' + '}';
}
}
/**
* Add a new subscription to the session
*
* @param newSubscription
* the subscription to add.
*/
void addNewSubscription(Subscription newSubscription);
/**
* Removed a specific subscription
*
* @param topic
* the topic of the subscription.
* @param clientID
* the session client.
*/
void removeSubscription(Topic topic, String clientID);
/**
* Remove all the subscriptions of the session
*
* @param sessionID
* the client ID
*/
void wipeSubscriptions(String sessionID);
/**
* Return all topic filters to recreate the subscription tree.
*/
List<ClientTopicCouple> listAllSubscriptions();
/**
* @param couple
* the subscription descriptor.
* @return the subscription stored by clientID and topicFilter, if any else null;
*/
Subscription getSubscription(ClientTopicCouple couple);
/*
* @return all subscriptions stored.
*/
List<Subscription> getSubscriptions();
}
@@ -21,7 +21,7 @@ import io.moquette.spi.ClientSession;
import io.moquette.spi.IMessagesStore;
import io.moquette.spi.ISessionsStore;
import io.moquette.spi.impl.subscriptions.Subscription;
import io.moquette.spi.impl.subscriptions.SubscriptionsStore;
import io.moquette.spi.impl.subscriptions.SubscriptionsDirectory;
import io.moquette.spi.impl.subscriptions.Topic;
import io.netty.buffer.ByteBuf;
import io.netty.handler.codec.mqtt.*;
@@ -36,10 +36,10 @@ class MessagesPublisher {
private final ConnectionDescriptorStore connectionDescriptors;
private final ISessionsStore m_sessionsStore;
private final PersistentQueueMessageSender messageSender;
private final SubscriptionsStore subscriptions;
private final SubscriptionsDirectory subscriptions;
public MessagesPublisher(ConnectionDescriptorStore connectionDescriptors, ISessionsStore sessionsStore,
PersistentQueueMessageSender messageSender, SubscriptionsStore subscriptions) {
PersistentQueueMessageSender messageSender, SubscriptionsDirectory subscriptions) {
this.connectionDescriptors = connectionDescriptors;
this.m_sessionsStore = sessionsStore;
this.messageSender = messageSender;
@@ -59,11 +59,11 @@ class MessagesPublisher {
void publish2Subscribers(IMessagesStore.StoredMessage pubMsg, Topic topic, int messageID) {
if (LOG.isTraceEnabled()) {
LOG.trace("Sending publish message to subscribers. CId={}, topic={}, messageId={}, payload={}, " +
LOG.trace("Sending publish message to subscribers. ClientId={}, topic={}, messageId={}, payload={}, " +
"subscriptionTree={}", pubMsg.getClientID(), topic, messageID, DebugUtils.payload2Str(pubMsg.getPayload()),
subscriptions.dumpTree());
} else {
LOG.info("Sending publish message to subscribers. CId={}, topic={}, messageId={}", pubMsg.getClientID(), topic,
LOG.info("Sending publish message to subscribers. ClientId={}, topic={}, messageId={}", pubMsg.getClientID(), topic,
messageID);
}
publish2Subscribers(pubMsg, topic);
@@ -24,7 +24,7 @@ import io.moquette.server.netty.NettyUtils;
import io.moquette.spi.*;
import io.moquette.spi.IMessagesStore.StoredMessage;
import io.moquette.spi.impl.subscriptions.Subscription;
import io.moquette.spi.impl.subscriptions.SubscriptionsStore;
import io.moquette.spi.impl.subscriptions.SubscriptionsDirectory;
import io.moquette.spi.impl.subscriptions.Topic;
import io.moquette.spi.security.IAuthenticator;
import io.moquette.spi.security.IAuthorizator;
@@ -130,7 +130,8 @@ public class ProtocolProcessor {
protected ConnectionDescriptorStore connectionDescriptors;
protected ConcurrentMap<RunningSubscription, SubscriptionState> subscriptionInCourse;
private SubscriptionsStore subscriptions;
private SubscriptionsDirectory subscriptions;
private ISubscriptionsStore subscriptionStore;
private boolean allowAnonymous;
private boolean allowZeroByteClientId;
private IAuthorizator m_authorizator;
@@ -141,7 +142,6 @@ public class ProtocolProcessor {
private IAuthenticator m_authenticator;
private BrokerInterceptor m_interceptor;
private String m_server_port;
private Qos0PublishHandler qos0PublishHandler;
private Qos1PublishHandler qos1PublishHandler;
@@ -155,23 +155,23 @@ public class ProtocolProcessor {
ProtocolProcessor() {
}
public void init(SubscriptionsStore subscriptions, IMessagesStore storageService, ISessionsStore sessionsStore,
IAuthenticator authenticator, boolean allowAnonymous, IAuthorizator authorizator,
BrokerInterceptor interceptor) {
public void init(SubscriptionsDirectory subscriptions, IMessagesStore storageService, ISessionsStore sessionsStore,
IAuthenticator authenticator, boolean allowAnonymous, IAuthorizator authorizator,
BrokerInterceptor interceptor) {
init(subscriptions, storageService, sessionsStore, authenticator, allowAnonymous, false, authorizator,
interceptor, null);
}
public void init(SubscriptionsStore subscriptions, IMessagesStore storageService, ISessionsStore sessionsStore,
IAuthenticator authenticator, boolean allowAnonymous, boolean allowZeroByteClientId,
IAuthorizator authorizator, BrokerInterceptor interceptor) {
public void init(SubscriptionsDirectory subscriptions, IMessagesStore storageService, ISessionsStore sessionsStore,
IAuthenticator authenticator, boolean allowAnonymous, boolean allowZeroByteClientId,
IAuthorizator authorizator, BrokerInterceptor interceptor) {
init(subscriptions, storageService, sessionsStore, authenticator, allowAnonymous, allowZeroByteClientId,
authorizator, interceptor, null);
}
public void init(SubscriptionsStore subscriptions, IMessagesStore storageService, ISessionsStore sessionsStore,
IAuthenticator authenticator, boolean allowAnonymous, boolean allowZeroByteClientId,
IAuthorizator authorizator, BrokerInterceptor interceptor, String serverPort) {
public void init(SubscriptionsDirectory subscriptions, IMessagesStore storageService, ISessionsStore sessionsStore,
IAuthenticator authenticator, boolean allowAnonymous, boolean allowZeroByteClientId,
IAuthorizator authorizator, BrokerInterceptor interceptor, String serverPort) {
init(new ConnectionDescriptorStore(sessionsStore), subscriptions, storageService, sessionsStore, authenticator,
allowAnonymous, allowZeroByteClientId, authorizator, interceptor, serverPort);
}
@@ -194,7 +194,7 @@ public class ProtocolProcessor {
* @param interceptor
* to notify events to an intercept handler
*/
void init(ConnectionDescriptorStore connectionDescriptors, SubscriptionsStore subscriptions,
void init(ConnectionDescriptorStore connectionDescriptors, SubscriptionsDirectory subscriptions,
IMessagesStore storageService, ISessionsStore sessionsStore, IAuthenticator authenticator,
boolean allowAnonymous, boolean allowZeroByteClientId, IAuthorizator authorizator,
BrokerInterceptor interceptor, String serverPort) {
@@ -212,7 +212,7 @@ public class ProtocolProcessor {
m_authenticator = authenticator;
m_messagesStore = storageService;
m_sessionsStore = sessionsStore;
m_server_port = serverPort;
subscriptionStore = sessionsStore.subscriptionStore();
LOG.info("Initializing messages publisher...");
final PersistentQueueMessageSender messageSender = new PersistentQueueMessageSender(this.connectionDescriptors);
@@ -468,12 +468,11 @@ public class ProtocolProcessor {
LOG.trace("retrieving inflight for messageID <{}>", messageID);
ClientSession targetSession = m_sessionsStore.sessionForClient(clientID);
StoredMessage inflightMsg = targetSession.getInflightMessage(messageID);
targetSession.inFlightAcknowledged(messageID);
StoredMessage inflightMsg = targetSession.inFlightAcknowledged(messageID);
String topic = inflightMsg.getTopic();
m_interceptor
.notifyMessageAcknowledged(new InterceptAcknowledgedMessage(inflightMsg, topic, username, messageID));
InterceptAcknowledgedMessage wrapped = new InterceptAcknowledgedMessage(inflightMsg, topic, username, messageID);
m_interceptor.notifyMessageAcknowledged(wrapped);
}
public static IMessagesStore.StoredMessage asStoredMessage(MqttPublishMessage msg) {
@@ -539,9 +538,9 @@ public class ProtocolProcessor {
} else {
toStoreMsg.setClientID(clientId);
}
if (qos == EXACTLY_ONCE) { // QoS2
guid = m_messagesStore.storePublishForFuture(toStoreMsg);
}
// if (qos == EXACTLY_ONCE) { // QoS2
// guid = m_messagesStore.storePublishForFuture(toStoreMsg);
// }
this.messagesPublisher.publish2Subscribers(toStoreMsg, topic);
if (!msg.fixedHeader().isRetain()) {
@@ -552,11 +551,11 @@ public class ProtocolProcessor {
m_messagesStore.cleanRetained(topic);
return;
}
if (guid == null) {
// before wasn't stored
guid = m_messagesStore.storePublishForFuture(toStoreMsg);
}
m_messagesStore.storeRetained(topic, guid);
// if (guid == null) {
// // before wasn't stored
// guid = m_messagesStore.storePublishForFuture(toStoreMsg);
// }
m_messagesStore.storeRetained(topic, toStoreMsg);
}
/**
@@ -597,7 +596,8 @@ public class ProtocolProcessor {
ClientSession targetSession = m_sessionsStore.sessionForClient(clientID);
// remove from the inflight and move to the QoS2 second phase queue
int messageID = messageId(msg);
targetSession.moveInFlightToSecondPhaseAckWaiting(messageID);
StoredMessage ackedMsg = targetSession.inFlightAcknowledged(messageID);
targetSession.moveInFlightToSecondPhaseAckWaiting(messageID, ackedMsg);
// once received a PUBREC reply with a PUBREL(messageID)
LOG.debug("Processing PUBREC message. CId={}, messageId={}", clientID, messageID);
@@ -678,7 +678,7 @@ public class ProtocolProcessor {
if (descriptor.cleanSession) {
LOG.info("Removing saved subscriptions. CId={}", descriptor.clientID);
m_sessionsStore.wipeSubscriptions(clientID);
subscriptionStore.wipeSubscriptions(clientID);
LOG.info("The saved subscriptions have been removed. CId={}", descriptor.clientID);
}
return true;
@@ -918,9 +918,8 @@ public class ProtocolProcessor {
emptyQueue = true;
} else {
// recreate a publish from stored publish in queue
boolean retained = m_messagesStore.getMessageByGuid(msg.getGuid()) != null;
MqttPublishMessage pubMsg = createPublishForQos( msg.getTopic(), msg.getQos(), msg.getPayload(),
retained, 0);
msg.isRetained(), 0);
channel.write(pubMsg);
}
}
@@ -25,17 +25,15 @@ import io.moquette.server.config.IResourceLoader;
import io.moquette.spi.IMessagesStore;
import io.moquette.spi.ISessionsStore;
import io.moquette.spi.IStore;
import io.moquette.spi.impl.security.ACLFileParser;
import io.moquette.spi.impl.security.AcceptAllAuthenticator;
import io.moquette.spi.impl.security.DenyAllAuthorizator;
import io.moquette.spi.impl.security.PermitAllAuthorizator;
import io.moquette.spi.impl.security.ResourceAuthenticator;
import io.moquette.spi.ISubscriptionsStore;
import io.moquette.spi.impl.security.*;
import io.moquette.spi.impl.subscriptions.Subscription;
import io.moquette.spi.impl.subscriptions.SubscriptionsStore;
import io.moquette.spi.impl.subscriptions.SubscriptionsDirectory;
import io.moquette.spi.security.IAuthenticator;
import io.moquette.spi.security.IAuthorizator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.ParseException;
@@ -50,16 +48,13 @@ public class ProtocolProcessorBootstrapper {
private static final Logger LOG = LoggerFactory.getLogger(ProtocolProcessorBootstrapper.class);
public static final String MAPDB_STORE_CLASS = "io.moquette.persistence.MemoryStorageService";
private SubscriptionsStore subscriptions;
private ISessionsStore m_sessionsStore;
private ISubscriptionsStore subscriptionsStore;
private Runnable storeShutdown;
private BrokerInterceptor m_interceptor;
private final ProtocolProcessor m_processor = new ProtocolProcessor();
private ConnectionDescriptorStore connectionDescriptors;
public ProtocolProcessorBootstrapper() {
@@ -86,8 +81,6 @@ public class ProtocolProcessorBootstrapper {
*/
public ProtocolProcessor init(IConfig props, List<? extends InterceptHandler> embeddedObservers,
IAuthenticator authenticator, IAuthorizator authorizator, Server server) {
subscriptions = new SubscriptionsStore();
IMessagesStore messagesStore;
LOG.info("Initializing messages and sessions stores...");
String storageClassName = props.getProperty(BrokerConstants.STORAGE_CLASS_NAME, MAPDB_STORE_CLASS);
@@ -98,6 +91,7 @@ public class ProtocolProcessorBootstrapper {
final IStore store = loadClass(storageClassName, IStore.class, Server.class, server);
messagesStore = store.messagesStore();
m_sessionsStore = store.sessionsStore();
this.subscriptionsStore = m_sessionsStore.subscriptionStore();
storeShutdown = new Runnable() {
@Override
@@ -116,9 +110,10 @@ public class ProtocolProcessorBootstrapper {
observers.add(handler);
}
}
m_interceptor = new BrokerInterceptor(props, observers);
BrokerInterceptor interceptor = new BrokerInterceptor(props, observers);
LOG.info("Initializing subscriptions store...");
SubscriptionsDirectory subscriptions = new SubscriptionsDirectory();
subscriptions.init(m_sessionsStore);
LOG.info("Configuring MQTT authenticator...");
@@ -170,7 +165,7 @@ public class ProtocolProcessorBootstrapper {
boolean allowZeroByteClientId = Boolean
.parseBoolean(props.getProperty(BrokerConstants.ALLOW_ZERO_BYTE_CLIENT_ID_PROPERTY_NAME, "false"));
m_processor.init(connectionDescriptors, subscriptions, messagesStore, m_sessionsStore, authenticator,
allowAnonymous, allowZeroByteClientId, authorizator, m_interceptor,
allowAnonymous, allowZeroByteClientId, authorizator, interceptor,
props.getProperty(BrokerConstants.PORT_PROPERTY_NAME));
return m_processor;
}
@@ -262,7 +257,7 @@ public class ProtocolProcessorBootstrapper {
}
public List<Subscription> getSubscriptions() {
return m_sessionsStore.getSubscriptions();
return this.subscriptionsStore.getSubscriptions();
}
public void shutdown() {
@@ -19,9 +19,6 @@ package io.moquette.spi.impl;
import io.moquette.server.ConnectionDescriptorStore;
import io.moquette.server.netty.NettyUtils;
import io.moquette.spi.IMessagesStore;
import io.moquette.spi.MessageGUID;
import io.moquette.spi.impl.subscriptions.Subscription;
import io.moquette.spi.impl.subscriptions.SubscriptionsStore;
import io.moquette.spi.impl.subscriptions.Topic;
import io.moquette.spi.security.IAuthorizator;
import io.netty.channel.Channel;
@@ -31,7 +28,7 @@ import io.netty.handler.codec.mqtt.MqttPubAckMessage;
import io.netty.handler.codec.mqtt.MqttPublishMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
import static io.moquette.spi.impl.ProtocolProcessor.asStoredMessage;
import static io.netty.handler.codec.mqtt.MqttMessageIdVariableHeader.from;
import static io.netty.handler.codec.mqtt.MqttQoS.AT_MOST_ONCE;
@@ -79,11 +76,7 @@ class Qos1PublishHandler extends QosPublishHandler {
m_messagesStore.cleanRetained(topic);
} else {
// before wasn't stored
MessageGUID guid = m_messagesStore.storePublishForFuture(toStoreMsg);
//TODO Il messaggio non ha il guid settato
// m_messagesStore.storeRetained(topic, toStoreMsg.getGuid());
m_messagesStore.storeRetained(topic, guid);
m_messagesStore.storeRetained(topic, toStoreMsg);
}
}
@@ -21,16 +21,13 @@ import io.moquette.server.netty.NettyUtils;
import io.moquette.spi.ClientSession;
import io.moquette.spi.IMessagesStore;
import io.moquette.spi.ISessionsStore;
import io.moquette.spi.MessageGUID;
import io.moquette.spi.impl.subscriptions.Subscription;
import io.moquette.spi.impl.subscriptions.SubscriptionsStore;
import io.moquette.spi.impl.subscriptions.SubscriptionsDirectory;
import io.moquette.spi.impl.subscriptions.Topic;
import io.moquette.spi.security.IAuthorizator;
import io.netty.channel.Channel;
import io.netty.handler.codec.mqtt.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
import static io.moquette.spi.impl.DebugUtils.payload2Str;
import static io.moquette.spi.impl.ProtocolProcessor.asStoredMessage;
@@ -42,14 +39,14 @@ class Qos2PublishHandler extends QosPublishHandler {
private static final Logger LOG = LoggerFactory.getLogger(Qos2PublishHandler.class);
private final SubscriptionsStore subscriptions;
private final SubscriptionsDirectory subscriptions;
private final IMessagesStore m_messagesStore;
private final BrokerInterceptor m_interceptor;
private final ConnectionDescriptorStore connectionDescriptors;
private final ISessionsStore m_sessionsStore;
private final MessagesPublisher publisher;
public Qos2PublishHandler(IAuthorizator authorizator, SubscriptionsStore subscriptions,
public Qos2PublishHandler(IAuthorizator authorizator, SubscriptionsDirectory subscriptions,
IMessagesStore messagesStore, BrokerInterceptor interceptor,
ConnectionDescriptorStore connectionDescriptors, ISessionsStore sessionsStore,
MessagesPublisher messagesPublisher) {
@@ -81,23 +78,22 @@ class Qos2PublishHandler extends QosPublishHandler {
LOG.trace("payload={}, subs Tree={}", payload2Str(toStoreMsg.getPayload()), subscriptions.dumpTree());
}
// QoS2
MessageGUID guid = m_messagesStore.storePublishForFuture(toStoreMsg);
ClientSession sourceSession = m_sessionsStore.sessionForClient(clientID);
sourceSession.markAsInboundInflight(messageID, guid);
m_sessionsStore.sessionForClient(clientID)
.markAsInboundInflight(messageID, toStoreMsg);
sendPubRec(clientID, messageID);
// Next the client will send us a pub rel
// NB publish to subscribers for QoS 2 happen upon PUBREL from publisher
if (msg.fixedHeader().isRetain()) {
if (msg.payload().readableBytes() == 0) {
m_messagesStore.cleanRetained(topic);
} else {
m_messagesStore.storeRetained(topic, guid);
}
}
// if (msg.fixedHeader().isRetain()) {
// if (msg.payload().readableBytes() == 0) {
// m_messagesStore.cleanRetained(topic);
// } else {
// m_messagesStore.storeRetained(topic, toStoreMsg);
// }
// }
//TODO this should happen on PUB_REL, else we notify false positive
m_interceptor.notifyTopicPublished(msg, clientID, username);
}
@@ -123,10 +119,13 @@ class Qos2PublishHandler extends QosPublishHandler {
if (evt.getPayload().readableBytes() == 0) {
m_messagesStore.cleanRetained(topic);
} else {
m_messagesStore.storeRetained(topic, evt.getGuid());
m_messagesStore.storeRetained(topic, evt);
}
}
//TODO here we should notify to the listeners
//m_interceptor.notifyTopicPublished(msg, clientID, username);
sendPubComp(clientID, messageID);
}
@@ -16,7 +16,7 @@
package io.moquette.spi.impl.subscriptions;
import io.moquette.spi.ISessionsStore.ClientTopicCouple;
import io.moquette.spi.ISubscriptionsStore.ClientTopicCouple;
import io.netty.handler.codec.mqtt.MqttQoS;
import java.io.Serializable;
@@ -17,7 +17,8 @@
package io.moquette.spi.impl.subscriptions;
import io.moquette.spi.ISessionsStore;
import io.moquette.spi.ISessionsStore.ClientTopicCouple;
import io.moquette.spi.ISubscriptionsStore;
import io.moquette.spi.ISubscriptionsStore.ClientTopicCouple;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.*;
@@ -27,7 +28,7 @@ import java.util.concurrent.atomic.AtomicReference;
/**
* Represents a tree of topics subscriptions.
*/
public class SubscriptionsStore {
public class SubscriptionsDirectory {
public static class NodeCouple {
@@ -79,8 +80,9 @@ public class SubscriptionsStore {
}
private AtomicReference<TreeNode> subscriptions = new AtomicReference<>(new TreeNode());
private static final Logger LOG = LoggerFactory.getLogger(SubscriptionsStore.class);
private static final Logger LOG = LoggerFactory.getLogger(SubscriptionsDirectory.class);
private volatile ISessionsStore m_sessionsStore;
private volatile ISubscriptionsStore subscriptionsStore;
/**
* Initialize the subscription tree with the list of subscriptions. Maintained for compatibility
@@ -92,21 +94,20 @@ public class SubscriptionsStore {
public void init(ISessionsStore sessionsStore) {
LOG.info("Initializing subscriptions store...");
m_sessionsStore = sessionsStore;
List<ClientTopicCouple> subscriptions = sessionsStore.listAllSubscriptions();
this.subscriptionsStore = sessionsStore.subscriptionStore();
List<ClientTopicCouple> subscriptions = this.subscriptionsStore.listAllSubscriptions();
// reload any subscriptions persisted
if (LOG.isTraceEnabled()) {
LOG.trace("Reloading all stored subscriptions. SubscriptionTree = {}.", dumpTree());
LOG.trace("Reloading all stored subscriptions. SubscriptionTree = {}", dumpTree());
}
for (ClientTopicCouple clientTopic : subscriptions) {
LOG.info(
"Re-subscribing client to topic. ClientId = {}, topicFilter = {}.",
clientTopic.clientID,
clientTopic.topicFilter);
LOG.info("Re-subscribing client to topic CId={}, topicFilter={}", clientTopic.clientID,
clientTopic.topicFilter);
add(clientTopic);
}
if (LOG.isTraceEnabled()) {
LOG.trace("The stored subscriptions have been reloaded. SubscriptionTree = {}.", dumpTree());
LOG.trace("Stored subscriptions have been reloaded. SubscriptionTree = {}", dumpTree());
}
}
@@ -204,7 +205,7 @@ public class SubscriptionsStore {
Map<String, Subscription> subsForClient = new HashMap<>();
for (ClientTopicCouple matchingCouple : matchingSubs) {
Subscription existingSub = subsForClient.get(matchingCouple.clientID);
Subscription sub = m_sessionsStore.getSubscription(matchingCouple);
Subscription sub = this.subscriptionsStore.getSubscription(matchingCouple);
if (sub == null) {
// if the m_sessionStore hasn't the sub because the client disconnected
continue;
@@ -16,7 +16,7 @@
package io.moquette.spi.impl.subscriptions;
import io.moquette.spi.ISessionsStore.ClientTopicCouple;
import io.moquette.spi.ISubscriptionsStore.ClientTopicCouple;
import java.util.*;
import java.util.concurrent.LinkedBlockingQueue;