Separated persistence layer, created submodule for MapDB persistence letting the broker use the in-memory message store

This commit is contained in:
andsel
2017-04-01 16:21:34 +02:00
parent 7ffb8fb43b
commit 5c6889fbd5
39 changed files with 477 additions and 499 deletions
+3
View File
@@ -16,6 +16,7 @@ broker/runner
/bundle/runner/ /bundle/runner/
/osgi_test/target/ /osgi_test/target/
/perf/target/ /perf/target/
/mapdb_storage/target/
*~ *~
*log* *log*
.DS_Store .DS_Store
@@ -40,3 +41,5 @@ broker/*moquette_store.*
embedding_moquette/target/ embedding_moquette/target/
broker/nb-configuration.xml broker/nb-configuration.xml
broker/nbactions-Server.xml broker/nbactions-Server.xml
maven-metadata.xml
+7 -9
View File
@@ -18,6 +18,13 @@
</properties> </properties>
<dependencies> <dependencies>
<!--<dependency>-->
<!--<groupId>${project.groupId}</groupId>-->
<!--<artifactId>moquette-mapdb-storage</artifactId>-->
<!--<version>${project.version}</version>-->
<!--</dependency>-->
<dependency> <dependency>
<groupId>org.slf4j</groupId> <groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId> <artifactId>slf4j-api</artifactId>
@@ -72,15 +79,6 @@
<version>1.10</version> <version>1.10</version>
</dependency> </dependency>
<dependency>
<groupId>org.mapdb</groupId>
<artifactId>mapdb</artifactId>
<!--version>1.1.0-SNAPSHOT</version -->
<!--<version>2.0.0-SNAPSHOT</version> -->
<!--<version>2.0-alpha3</version> -->
<version>1.0.8</version>
</dependency>
<dependency> <dependency>
<groupId>org.hdrhistogram</groupId> <groupId>org.hdrhistogram</groupId>
<artifactId>HdrHistogram</artifactId> <artifactId>HdrHistogram</artifactId>
@@ -14,11 +14,12 @@
* You may elect to redistribute this code under either of these licenses. * You may elect to redistribute this code under either of these licenses.
*/ */
package io.moquette.spi.impl; package io.moquette.persistence;
import io.moquette.spi.IMessagesStore; import io.moquette.spi.IMessagesStore;
import io.moquette.spi.IMatchingCondition; import io.moquette.spi.IMatchingCondition;
import io.moquette.spi.MessageGUID; import io.moquette.spi.MessageGUID;
import io.moquette.spi.impl.Utils;
import io.moquette.spi.impl.subscriptions.Topic; import io.moquette.spi.impl.subscriptions.Topic;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -71,11 +72,10 @@ public class MemoryMessagesStore implements IMessagesStore {
MessageGUID guid = new MessageGUID(UUID.randomUUID().toString()); MessageGUID guid = new MessageGUID(UUID.randomUUID().toString());
storedMessage.setGuid(guid); storedMessage.setGuid(guid);
m_persistentMessageStore.put(guid, storedMessage); m_persistentMessageStore.put(guid, storedMessage);
HashMap<Integer, MessageGUID> guids = (HashMap<Integer, MessageGUID>) defaultGet( final HashMap<Integer, MessageGUID> emptyGuids = new HashMap<>();
m_messageToGuids, Map<Integer, MessageGUID> guids = defaultGet(m_messageToGuids, storedMessage.getClientID(), emptyGuids);
storedMessage.getClientID(),
new HashMap<Integer, MessageGUID>());
guids.put(storedMessage.getMessageID(), guid); guids.put(storedMessage.getMessageID(), guid);
m_messageToGuids.put(storedMessage.getClientID(), guids);
return guid; return guid;
} }
@@ -85,7 +85,11 @@ public class MemoryMessagesStore implements IMessagesStore {
if (messageGUIDMap == null || messageGUIDMap.isEmpty()) { if (messageGUIDMap == null || messageGUIDMap.isEmpty()) {
return; return;
} }
for (MessageGUID guid : messageGUIDMap.values()) { //remove all guids from retained
Collection<MessageGUID> messagesToRemove = new HashSet<>(messageGUIDMap.values());
messagesToRemove.removeAll(m_retainedStore.values());
for (MessageGUID guid : messagesToRemove) {
m_persistentMessageStore.remove(guid); m_persistentMessageStore.remove(guid);
} }
} }
@@ -111,8 +115,8 @@ public class MemoryMessagesStore implements IMessagesStore {
@Override @Override
public MessageGUID mapToGuid(String clientID, int messageID) { public MessageGUID mapToGuid(String clientID, int messageID) {
HashMap<Integer, MessageGUID> guids = (HashMap<Integer, MessageGUID>) Utils final HashMap<Integer, MessageGUID> emptyGuids = new HashMap<>();
.defaultGet(m_messageToGuids, clientID, new HashMap<Integer, MessageGUID>()); Map<Integer, MessageGUID> guids = Utils.defaultGet(m_messageToGuids, clientID, emptyGuids);
return guids.get(messageID); return guids.get(messageID);
} }
} }
@@ -14,7 +14,7 @@
* You may elect to redistribute this code under either of these licenses. * You may elect to redistribute this code under either of these licenses.
*/ */
package io.moquette.spi.persistence; package io.moquette.persistence;
import io.moquette.server.Constants; import io.moquette.server.Constants;
import io.moquette.spi.ClientSession; import io.moquette.spi.ClientSession;
@@ -25,7 +25,6 @@ import io.moquette.spi.MessageGUID;
import io.moquette.spi.impl.Utils; import io.moquette.spi.impl.Utils;
import io.moquette.spi.impl.subscriptions.Subscription; import io.moquette.spi.impl.subscriptions.Subscription;
import io.moquette.spi.impl.subscriptions.Topic; import io.moquette.spi.impl.subscriptions.Topic;
import io.moquette.spi.persistence.MapDBPersistentStore.PersistentSession;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.util.*; import java.util.*;
@@ -42,7 +41,7 @@ public class MemorySessionStore implements ISessionsStore {
private Map<String, Map<Topic, Subscription>> m_persistentSubscriptions = new HashMap<>(); private Map<String, Map<Topic, Subscription>> m_persistentSubscriptions = new HashMap<>();
private Map<String, MapDBPersistentStore.PersistentSession> m_persistentSessions = new HashMap<>(); private Map<String, PersistentSession> m_persistentSessions = new HashMap<>();
// maps clientID->[MessageId -> guid] // maps clientID->[MessageId -> guid]
private Map<String, Map<Integer, MessageGUID>> m_inflightStore = new HashMap<>(); private Map<String, Map<Integer, MessageGUID>> m_inflightStore = new HashMap<>();
@@ -102,7 +101,7 @@ public class MemorySessionStore implements ISessionsStore {
} }
LOG.debug("clientID {} is a newcome, creating it's empty subscriptions set", clientID); LOG.debug("clientID {} is a newcome, creating it's empty subscriptions set", clientID);
m_persistentSubscriptions.put(clientID, new HashMap<Topic, Subscription>()); m_persistentSubscriptions.put(clientID, new HashMap<Topic, Subscription>());
m_persistentSessions.put(clientID, new MapDBPersistentStore.PersistentSession(cleanSession)); m_persistentSessions.put(clientID, new PersistentSession(cleanSession));
return new ClientSession(clientID, m_messagesStore, this, cleanSession); return new ClientSession(clientID, m_messagesStore, this, cleanSession);
} }
@@ -112,7 +111,7 @@ public class MemorySessionStore implements ISessionsStore {
return null; return null;
} }
MapDBPersistentStore.PersistentSession storedSession = m_persistentSessions.get(clientID); PersistentSession storedSession = m_persistentSessions.get(clientID);
return new ClientSession(clientID, m_messagesStore, this, storedSession.cleanSession); return new ClientSession(clientID, m_messagesStore, this, storedSession.cleanSession);
} }
@@ -127,7 +126,7 @@ public class MemorySessionStore implements ISessionsStore {
@Override @Override
public void updateCleanStatus(String clientID, boolean cleanSession) { public void updateCleanStatus(String clientID, boolean cleanSession) {
m_persistentSessions.put(clientID, new MapDBPersistentStore.PersistentSession(cleanSession)); m_persistentSessions.put(clientID, new PersistentSession(cleanSession));
} }
@Override @Override
@@ -191,7 +190,7 @@ public class MemorySessionStore implements ISessionsStore {
m.put(nextPacketId, null); m.put(nextPacketId, null);
return nextPacketId; return nextPacketId;
} }
int maxId = Collections.max(m.keySet()); int maxId = m.keySet().isEmpty() ? 0 :Collections.max(m.keySet());
int nextPacketId = (maxId + 1) % 0xFFFF; int nextPacketId = (maxId + 1) % 0xFFFF;
m.put(nextPacketId, null); m.put(nextPacketId, null);
return nextPacketId; return nextPacketId;
@@ -199,7 +198,10 @@ public class MemorySessionStore implements ISessionsStore {
@Override @Override
public BlockingQueue<StoredMessage> queue(String clientID) { public BlockingQueue<StoredMessage> queue(String clientID) {
return Utils.defaultGet(queues, clientID, new ArrayBlockingQueue<StoredMessage>(Constants.MAX_MESSAGE_QUEUE)); final ArrayBlockingQueue<StoredMessage> emptyQueue = new ArrayBlockingQueue<>(Constants.MAX_MESSAGE_QUEUE);
BlockingQueue<StoredMessage> messagesQueue = Utils.defaultGet(queues, clientID, emptyQueue);
queues.put(clientID, messagesQueue);
return messagesQueue;
} }
@Override @Override
@@ -218,16 +220,16 @@ public class MemorySessionStore implements ISessionsStore {
MessageGUID guid = m.remove(messageID); MessageGUID guid = m.remove(messageID);
LOG.info("Moving to second phase store"); LOG.info("Moving to second phase store");
Map<Integer, MessageGUID> messageIDs = Utils final HashMap<Integer, MessageGUID> emptyGuids = new HashMap<>();
.defaultGet(m_secondPhaseStore, clientID, new HashMap<Integer, MessageGUID>()); Map<Integer, MessageGUID> messageIDs = Utils.defaultGet(m_secondPhaseStore, clientID, emptyGuids);
messageIDs.put(messageID, guid); messageIDs.put(messageID, guid);
m_secondPhaseStore.put(clientID, messageIDs); m_secondPhaseStore.put(clientID, messageIDs);
} }
@Override @Override
public MessageGUID secondPhaseAcknowledged(String clientID, int messageID) { public MessageGUID secondPhaseAcknowledged(String clientID, int messageID) {
Map<Integer, MessageGUID> messageIDs = Utils final HashMap<Integer, MessageGUID> emptyGuids = new HashMap<>();
.defaultGet(m_secondPhaseStore, clientID, new HashMap<Integer, MessageGUID>()); Map<Integer, MessageGUID> messageIDs = Utils.defaultGet(m_secondPhaseStore, clientID, emptyGuids);
MessageGUID guid = messageIDs.remove(messageID); MessageGUID guid = messageIDs.remove(messageID);
m_secondPhaseStore.put(clientID, messageIDs); m_secondPhaseStore.put(clientID, messageIDs);
return guid; return guid;
@@ -235,7 +237,9 @@ public class MemorySessionStore implements ISessionsStore {
@Override @Override
public StoredMessage getInflightMessage(String clientID, int messageID) { public StoredMessage getInflightMessage(String clientID, int messageID) {
return null; Map<Integer, MessageGUID> inflightMessages = m_inflightStore.get(clientID);
MessageGUID guid = inflightMessages.get(messageID);
return m_messagesStore.getMessageByGuid(guid);
} }
@Override @Override
@@ -14,12 +14,11 @@
* You may elect to redistribute this code under either of these licenses. * You may elect to redistribute this code under either of these licenses.
*/ */
package io.moquette.spi.impl; package io.moquette.persistence;
import io.moquette.spi.IMessagesStore; import io.moquette.spi.IMessagesStore;
import io.moquette.spi.ISessionsStore; import io.moquette.spi.ISessionsStore;
import io.moquette.spi.IStore; import io.moquette.spi.IStore;
import io.moquette.spi.persistence.MemorySessionStore;
public class MemoryStorageService implements IStore { public class MemoryStorageService implements IStore {
@@ -0,0 +1,32 @@
/*
* Copyright (c) 2012-2017 The original author or authors
* ------------------------------------------------------
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*/
package io.moquette.persistence;
import java.io.Serializable;
/**
* This is a DTO used to persist minimal status (clean session and activation status) of a
* session.
*/
public class PersistentSession implements Serializable {
private static final long serialVersionUID = 5052054783220481854L;
public final boolean cleanSession;
public PersistentSession(boolean cleanSession) {
this.cleanSession = cleanSession;
}
}
@@ -54,11 +54,8 @@ public class MQTTMessageLogger extends ChannelDuplexHandler {
break; break;
case SUBSCRIBE: case SUBSCRIBE:
MqttSubscribeMessage subscribe = (MqttSubscribeMessage) msg; MqttSubscribeMessage subscribe = (MqttSubscribeMessage) msg;
LOG.info( LOG.info("{} SUBSCRIBE <{}> to topics {}", direction, clientID,
"{} SUBSCRIBE <{}> to topics {}", subscribe.payload().topicSubscriptions());
direction,
clientID,
subscribe.payload().topicSubscriptions());
break; break;
case UNSUBSCRIBE: case UNSUBSCRIBE:
MqttUnsubscribeMessage unsubscribe = (MqttUnsubscribeMessage) msg; MqttUnsubscribeMessage unsubscribe = (MqttUnsubscribeMessage) msg;
@@ -74,7 +74,7 @@ public class ClientSession {
* @return the list of messages to be delivered for client related to the session. * @return the list of messages to be delivered for client related to the session.
*/ */
public BlockingQueue<IMessagesStore.StoredMessage> queue() { public BlockingQueue<IMessagesStore.StoredMessage> queue() {
LOG.info("Retrieving stored messages. MqttClientId = {}.", clientID); LOG.info("Retrieving enqueued messages. ClientId={}", clientID);
return this.m_sessionsStore.queue(clientID); return this.m_sessionsStore.queue(clientID);
} }
@@ -85,14 +85,14 @@ public class ClientSession {
public boolean subscribe(Subscription newSubscription) { public boolean subscribe(Subscription newSubscription) {
LOG.info( LOG.info(
"Adding new subscription. MqttClientId = {}, topics = {}, qos = {}.", "Adding new subscription. ClientId={}, topics={}, qos={}",
newSubscription.getClientId(), newSubscription.getClientId(),
newSubscription.getTopicFilter(), newSubscription.getTopicFilter(),
newSubscription.getRequestedQos()); newSubscription.getRequestedQos());
boolean validTopic = newSubscription.getTopicFilter().isValid(); boolean validTopic = newSubscription.getTopicFilter().isValid();
if (!validTopic) { if (!validTopic) {
LOG.warn( LOG.warn(
"The topic filter is not valid. MqttClientId = {}, topics = {}.", "The topic filter is not valid. ClientId={}, topics={}",
newSubscription.getClientId(), newSubscription.getClientId(),
newSubscription.getTopicFilter()); newSubscription.getTopicFilter());
// send SUBACK with 0x80 for this topic filter // send SUBACK with 0x80 for this topic filter
@@ -103,13 +103,9 @@ public class ClientSession {
// update the selected subscriptions if not present or if has a greater qos // 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 || existingSub.getRequestedQos().value() < newSubscription.getRequestedQos().value()) {
if (existingSub != null) { if (existingSub != null) {
LOG.info( LOG.info("Subscription already existed with a lower QoS value. It will be updated. ClientId={}, " +
"The subscription already existed with a lower QoS value. It will be updated. " "topics={}, existingQos={}, newQos={}", newSubscription.getClientId(),
+ "MqttClientId = {}, topics = {}, existingQos = {}, newQos = {}.", newSubscription.getTopicFilter(), existingSub.getRequestedQos(), newSubscription.getRequestedQos());
newSubscription.getClientId(),
newSubscription.getTopicFilter(),
existingSub.getRequestedQos(),
newSubscription.getRequestedQos());
subscriptions.remove(newSubscription); subscriptions.remove(newSubscription);
} }
subscriptions.add(newSubscription); subscriptions.add(newSubscription);
@@ -119,7 +115,7 @@ public class ClientSession {
} }
public void unsubscribeFrom(Topic topicFilter) { public void unsubscribeFrom(Topic topicFilter) {
LOG.info("Removing subscription. MqttClientID = {}, topics = {}.", clientID, topicFilter); LOG.info("Removing subscription. ClientID={}, topics={}", clientID, topicFilter);
m_sessionsStore.removeSubscription(topicFilter, clientID); m_sessionsStore.removeSubscription(topicFilter, clientID);
Set<Subscription> subscriptionsToRemove = new HashSet<>(); Set<Subscription> subscriptionsToRemove = new HashSet<>();
for (Subscription sub : this.subscriptions) { for (Subscription sub : this.subscriptions) {
@@ -132,18 +128,18 @@ public class ClientSession {
public void disconnect() { public void disconnect() {
if (this.cleanSession) { if (this.cleanSession) {
LOG.info("The client has disconnected. Removing its subscriptions. MqttClientId = {}.", clientID); LOG.info("Client disconnected. Removing its subscriptions. ClientId={}", clientID);
// cleanup topic subscriptions // cleanup topic subscriptions
cleanSession(); cleanSession();
} }
} }
public void cleanSession() { public void cleanSession() {
LOG.info("Wiping existing subscriptions. MqttClientId = {}.", this.clientID); LOG.info("Wiping existing subscriptions. ClientId={}", this.clientID);
m_sessionsStore.wipeSubscriptions(this.clientID); m_sessionsStore.wipeSubscriptions(this.clientID);
// remove also the messages stored of type QoS1/2 // remove also the messages stored of type QoS1/2
LOG.info("Removing stored messages with QoS 1 and 2. MqttClientId = {}.", this.clientID); LOG.info("Removing stored messages with QoS 1 and 2. ClientId={}", this.clientID);
messagesStore.dropMessagesInSession(this.clientID); messagesStore.dropMessagesInSession(this.clientID);
} }
@@ -27,13 +27,8 @@ class InternalRepublisher {
// fire as retained the message // fire as retained the message
Integer packetID = storedMsg.getQos() == MqttQoS.AT_MOST_ONCE ? null : targetSession.nextPacketId(); Integer packetID = storedMsg.getQos() == MqttQoS.AT_MOST_ONCE ? null : targetSession.nextPacketId();
if (packetID != null) { if (packetID != null) {
LOG.debug( LOG.debug("Adding message to inflight zone. ClientId={}, packetId={}, messageId={}, topic={}",
"Adding message to inflight zone. " targetSession.clientID, packetID, storedMsg.getMessageID(), storedMsg.getTopic());
+ "MqttClientId = {}, packetId = {}, messageId = {}, topic = {}.",
targetSession.clientID,
packetID,
storedMsg.getMessageID(),
storedMsg.getTopic());
targetSession.inFlightAckWaiting(storedMsg.getGuid(), packetID); targetSession.inFlightAckWaiting(storedMsg.getGuid(), packetID);
} }
MqttPublishMessage publishMsg = retainedPublish(storedMsg); MqttPublishMessage publishMsg = retainedPublish(storedMsg);
@@ -51,12 +46,8 @@ class InternalRepublisher {
for (IMessagesStore.StoredMessage pubEvt : storedPublishes) { for (IMessagesStore.StoredMessage pubEvt : storedPublishes) {
// put in flight zone // put in flight zone
LOG.debug( LOG.debug("Adding message ot inflight zone. ClientId={}, guid={}, messageId={}, topic={}",
"Adding message ot inflight zone. MqttClientId = {}, guid = {}, messageId = {}, topic = {}.", clientSession.clientID, pubEvt.getGuid(), pubEvt.getMessageID(), pubEvt.getTopic());
clientSession.clientID,
pubEvt.getGuid(),
pubEvt.getMessageID(),
pubEvt.getTopic());
clientSession.inFlightAckWaiting(pubEvt.getGuid(), pubEvt.getMessageID()); clientSession.inFlightAckWaiting(pubEvt.getGuid(), pubEvt.getMessageID());
MqttPublishMessage publishMsg = notRetainedPublish(pubEvt); MqttPublishMessage publishMsg = notRetainedPublish(pubEvt);
// set the PacketIdentifier only for QoS > 0 // set the PacketIdentifier only for QoS > 0
@@ -68,39 +59,23 @@ class InternalRepublisher {
} }
private MqttPublishMessage notRetainedPublish(IMessagesStore.StoredMessage storedMessage, Integer messageID) { private MqttPublishMessage notRetainedPublish(IMessagesStore.StoredMessage storedMessage, Integer messageID) {
return createPublishForQos( return createPublishForQos(storedMessage.getTopic(), storedMessage.getQos(), storedMessage.getMessage(), false,
storedMessage.getTopic(), messageID);
storedMessage.getQos(),
storedMessage.getMessage(),
false,
messageID);
} }
private MqttPublishMessage notRetainedPublish(IMessagesStore.StoredMessage storedMessage) { private MqttPublishMessage notRetainedPublish(IMessagesStore.StoredMessage storedMessage) {
return createPublishForQos( return createPublishForQos(storedMessage.getTopic(), storedMessage.getQos(), storedMessage.getMessage(), false,
storedMessage.getTopic(), 0);
storedMessage.getQos(),
storedMessage.getMessage(),
false,
0);
} }
private MqttPublishMessage retainedPublish(IMessagesStore.StoredMessage storedMessage) { private MqttPublishMessage retainedPublish(IMessagesStore.StoredMessage storedMessage) {
return createPublishForQos( return createPublishForQos(storedMessage.getTopic(), storedMessage.getQos(), storedMessage.getMessage(), true,
storedMessage.getTopic(), 0);
storedMessage.getQos(),
storedMessage.getMessage(),
true,
0);
} }
private MqttPublishMessage retainedPublish(IMessagesStore.StoredMessage storedMessage, Integer packetID) { private MqttPublishMessage retainedPublish(IMessagesStore.StoredMessage storedMessage, Integer packetID) {
return createPublishForQos( return createPublishForQos(storedMessage.getTopic(), storedMessage.getQos(), storedMessage.getMessage(), true,
storedMessage.getTopic(), packetID);
storedMessage.getQos(),
storedMessage.getMessage(),
true,
packetID);
} }
public static MqttPublishMessage createPublishForQos(String topic, MqttQoS qos, ByteBuf message, boolean retained, public static MqttPublishMessage createPublishForQos(String topic, MqttQoS qos, ByteBuf message, boolean retained,
@@ -80,12 +80,8 @@ class MessagesPublisher {
this.messageSender.sendPublish(targetSession, publishMsg); this.messageSender.sendPublish(targetSession, publishMsg);
} else { } else {
if (!targetSession.isCleanSession()) { if (!targetSession.isCleanSession()) {
LOG.debug( LOG.debug("Storing pending PUBLISH inactive message. MessageId={}, CId={}, topicFilter={}, qos={}",
"Storing pending PUBLISH inactive message. MessageId={}, CId={}, topicFilter={}, qos={}", pubMsg.getMessageID(), sub.getClientId(), sub.getTopicFilter(), qos);
pubMsg.getMessageID(),
sub.getClientId(),
sub.getTopicFilter(),
qos);
// store the message in targetSession queue to deliver // store the message in targetSession queue to deliver
targetSession.enqueue(pubMsg); targetSession.enqueue(pubMsg);
} }
@@ -24,12 +24,8 @@ class PersistentQueueMessageSender {
final int messageId = pubMessage.variableHeader().messageId(); final int messageId = pubMessage.variableHeader().messageId();
final String topicName = pubMessage.variableHeader().topicName(); final String topicName = pubMessage.variableHeader().topicName();
if (LOG.isDebugEnabled()) { if (LOG.isDebugEnabled()) {
LOG.debug( LOG.debug("Sending PUBLISH message. MessageId={}, CId={}, topic={}, qos={}, payload={}", messageId,
"Sending PUBLISH message. MessageId={}, CId={}, topic={}, qos={}, payload={}", clientId, topicName, DebugUtils.payload2Str(pubMessage.payload()));
messageId,
clientId,
topicName,
DebugUtils.payload2Str(pubMessage.payload()));
} else { } else {
LOG.info("Sending PUBLISH message. MessageId={}, CId={}, topic={}", messageId, clientId, topicName); LOG.info("Sending PUBLISH message. MessageId={}, CId={}, topic={}", messageId, clientId, topicName);
} }
@@ -38,24 +34,12 @@ class PersistentQueueMessageSender {
MqttQoS qos = pubMessage.fixedHeader().qosLevel(); MqttQoS qos = pubMessage.fixedHeader().qosLevel();
if (!messageDelivered && qos != AT_MOST_ONCE && !clientsession.isCleanSession()) { if (!messageDelivered && qos != AT_MOST_ONCE && !clientsession.isCleanSession()) {
LOG.warn( LOG.warn("PUBLISH message could not be delivered. It will be stored. MessageId={}, CId={}, topic={}, qos={}, " +
"The PUBLISH message could not be delivered. It will be stored. " "cleanSession={}", messageId, clientId, topicName, qos, false);
+ "MessageId = {}, CId={}, topic = {}, qos = {}, cleanSession = {}.",
messageId,
clientId,
topicName,
qos,
false);
clientsession.enqueue(asStoredMessage(pubMessage)); clientsession.enqueue(asStoredMessage(pubMessage));
} else { } else {
LOG.warn( LOG.warn("PUBLISH message could not be delivered. It will be discarded. MessageId={}, CId={}, topic={}, " +
"The PUBLISH message could not be delivered. It will be discarded. " "qos={}, cleanSession={}", messageId, clientId, topicName, qos, true);
+ "MessageId = {}, CId={}, topic = {}, qos = {}, cleanSession = {}.",
messageId,
clientId,
topicName,
qos,
true);
} }
} }
} }
@@ -209,7 +209,7 @@ public class ProtocolProcessor {
this.allowZeroByteClientId = allowZeroByteClientId; this.allowZeroByteClientId = allowZeroByteClientId;
m_authorizator = authorizator; m_authorizator = authorizator;
if (LOG.isDebugEnabled()) { if (LOG.isDebugEnabled()) {
LOG.debug("Initial subscriptions tree={}.", subscriptions.dumpTree()); LOG.debug("Initial subscriptions tree={}", subscriptions.dumpTree());
} }
m_authenticator = authenticator; m_authenticator = authenticator;
m_messagesStore = storageService; m_messagesStore = storageService;
@@ -302,7 +302,7 @@ public class ProtocolProcessor {
channel.close(); channel.close();
} }
LOG.info("The CONNECT message has been processed. CId={}, username={}.", clientId, payload.userName()); LOG.info("The CONNECT message has been processed. CId={}, username={}", clientId, payload.userName());
} }
private MqttConnAckMessage connAck(MqttConnectReturnCode returnCode) { private MqttConnAckMessage connAck(MqttConnectReturnCode returnCode) {
@@ -347,7 +347,7 @@ public class ProtocolProcessor {
} }
private boolean sendAck(ConnectionDescriptor descriptor, MqttConnectMessage msg, final String clientId) { private boolean sendAck(ConnectionDescriptor descriptor, MqttConnectMessage msg, final String clientId) {
LOG.info("Sending connect ACK. CId={}.", clientId); LOG.info("Sending connect ACK. CId={}", clientId);
final boolean success = descriptor.assignState(DISCONNECTED, SENDACK); final boolean success = descriptor.assignState(DISCONNECTED, SENDACK);
if (!success) { if (!success) {
return false; return false;
@@ -363,17 +363,17 @@ public class ProtocolProcessor {
} }
if (isSessionAlreadyStored) { if (isSessionAlreadyStored) {
LOG.info("Cleaning session. CId={}.", clientId); LOG.info("Cleaning session. CId={}", clientId);
clientSession.cleanSession(msg.variableHeader().isCleanSession()); clientSession.cleanSession(msg.variableHeader().isCleanSession());
} }
descriptor.writeAndFlush(okResp); descriptor.writeAndFlush(okResp);
LOG.info("The connect ACK has been sent. CId={}.", clientId); LOG.info("The connect ACK has been sent. CId={}", clientId);
return true; return true;
} }
private void initializeKeepAliveTimeout(Channel channel, MqttConnectMessage msg, final String clientId) { private void initializeKeepAliveTimeout(Channel channel, MqttConnectMessage msg, final String clientId) {
int keepAlive = msg.variableHeader().keepAliveTimeSeconds(); int keepAlive = msg.variableHeader().keepAliveTimeSeconds();
LOG.info("Configuring connection. CId={}.", clientId); LOG.info("Configuring connection. CId={}", clientId);
NettyUtils.keepAlive(channel, keepAlive); NettyUtils.keepAlive(channel, keepAlive);
// session.attr(NettyUtils.ATTR_KEY_CLEANSESSION).set(msg.variableHeader().isCleanSession()); // session.attr(NettyUtils.ATTR_KEY_CLEANSESSION).set(msg.variableHeader().isCleanSession());
NettyUtils.cleanSession(channel, msg.variableHeader().isCleanSession()); NettyUtils.cleanSession(channel, msg.variableHeader().isCleanSession());
@@ -399,7 +399,7 @@ public class ProtocolProcessor {
WillMessage will = new WillMessage(msg.payload().willTopic(), bb, msg.variableHeader().isWillRetain(), WillMessage will = new WillMessage(msg.payload().willTopic(), bb, msg.variableHeader().isWillRetain(),
willQos); willQos);
m_willStore.put(clientId, will); m_willStore.put(clientId, will);
LOG.info("MQTT last will and testament has been configured. CId={}.", clientId); LOG.info("MQTT last will and testament has been configured. CId={}", clientId);
} }
} }
@@ -416,7 +416,7 @@ public class ProtocolProcessor {
clientSession = m_sessionsStore.createNewSession(clientId, msg.variableHeader().isCleanSession()); clientSession = m_sessionsStore.createNewSession(clientId, msg.variableHeader().isCleanSession());
} }
if (msg.variableHeader().isCleanSession()) { if (msg.variableHeader().isCleanSession()) {
LOG.info("Cleaning session. CId={}.", clientId); LOG.info("Cleaning session. CId={}", clientId);
clientSession.cleanSession(); clientSession.cleanSession();
} }
return clientSession; return clientSession;
@@ -453,10 +453,10 @@ public class ProtocolProcessor {
* Republish QoS1 and QoS2 messages stored into the session for the clientID. * Republish QoS1 and QoS2 messages stored into the session for the clientID.
*/ */
private void republishStoredInSession(ClientSession clientSession) { private void republishStoredInSession(ClientSession clientSession) {
LOG.info("Republishing stored publish events. CId={}.", clientSession.clientID); LOG.info("Republishing stored publish events. CId={}", clientSession.clientID);
BlockingQueue<StoredMessage> publishedEvents = clientSession.queue(); BlockingQueue<StoredMessage> publishedEvents = clientSession.queue();
if (publishedEvents.isEmpty()) { if (publishedEvents.isEmpty()) {
LOG.info("There are no stored publish events. ClientId={}.", clientSession.clientID); LOG.info("There are no stored publish events to ClientId={}", clientSession.clientID);
return; return;
} }
@@ -531,7 +531,7 @@ public class ProtocolProcessor {
public void internalPublish(MqttPublishMessage msg, final String clientId) { public void internalPublish(MqttPublishMessage msg, final String clientId) {
final MqttQoS qos = msg.fixedHeader().qosLevel(); final MqttQoS qos = msg.fixedHeader().qosLevel();
final Topic topic = new Topic(msg.variableHeader().topicName()); final Topic topic = new Topic(msg.variableHeader().topicName());
LOG.info("Sending PUBLISH message. Topic={}, qos={}.", topic, qos); LOG.info("Sending PUBLISH message. Topic={}, qos={}", topic, qos);
MessageGUID guid = null; MessageGUID guid = null;
IMessagesStore.StoredMessage toStoreMsg = asStoredMessage(msg); IMessagesStore.StoredMessage toStoreMsg = asStoredMessage(msg);
@@ -579,7 +579,7 @@ public class ProtocolProcessor {
Topic topic = new Topic(tobeStored.getTopic()); Topic topic = new Topic(tobeStored.getTopic());
List<Subscription> topicMatchingSubscriptions = subscriptions.matches(topic); List<Subscription> topicMatchingSubscriptions = subscriptions.matches(topic);
LOG.info("Publishing will message. CId={}, messageId={}, topic={}.", clientID, messageId, topic); LOG.info("Publishing will message. CId={}, messageId={}, topic={}", clientID, messageId, topic);
this.messagesPublisher.publish2Subscribers(tobeStored, topicMatchingSubscriptions); this.messagesPublisher.publish2Subscribers(tobeStored, topicMatchingSubscriptions);
} }
@@ -610,7 +610,7 @@ public class ProtocolProcessor {
int messageID = messageId(msg); int messageID = messageId(msg);
targetSession.moveInFlightToSecondPhaseAckWaiting(messageID); targetSession.moveInFlightToSecondPhaseAckWaiting(messageID);
// once received a PUBREC reply with a PUBREL(messageID) // once received a PUBREC reply with a PUBREL(messageID)
LOG.debug("Processing PUBREC message. CId={}, messageId={}.", clientID, messageID); LOG.debug("Processing PUBREC message. CId={}, messageId={}", clientID, messageID);
MqttFixedHeader pubRelHeader = new MqttFixedHeader(MqttMessageType.PUBREL, false, AT_LEAST_ONCE, false, 0); MqttFixedHeader pubRelHeader = new MqttFixedHeader(MqttMessageType.PUBREL, false, AT_LEAST_ONCE, false, 0);
MqttMessage pubRelMessage = new MqttMessage(pubRelHeader, from(messageID)); MqttMessage pubRelMessage = new MqttMessage(pubRelHeader, from(messageID));
@@ -620,7 +620,7 @@ public class ProtocolProcessor {
public void processPubComp(Channel channel, MqttMessage msg) { public void processPubComp(Channel channel, MqttMessage msg) {
String clientID = NettyUtils.clientID(channel); String clientID = NettyUtils.clientID(channel);
int messageID = messageId(msg); int messageID = messageId(msg);
LOG.debug("Processing PUBCOMP message. CId={}, messageId={}.", clientID, messageID); LOG.debug("Processing PUBCOMP message. CId={}, messageId={}", clientID, messageID);
// once received the PUBCOMP then remove the message from the temp memory // once received the PUBCOMP then remove the message from the temp memory
ClientSession targetSession = m_sessionsStore.sessionForClient(clientID); ClientSession targetSession = m_sessionsStore.sessionForClient(clientID);
StoredMessage inflightMsg = targetSession.secondPhaseAcknowledged(messageID); StoredMessage inflightMsg = targetSession.secondPhaseAcknowledged(messageID);
@@ -631,7 +631,7 @@ public class ProtocolProcessor {
public void processDisconnect(Channel channel) throws InterruptedException { public void processDisconnect(Channel channel) throws InterruptedException {
final String clientID = NettyUtils.clientID(channel); final String clientID = NettyUtils.clientID(channel);
LOG.info("Processing DISCONNECT message. CId={}.", clientID); LOG.info("Processing DISCONNECT message. CId={}", clientID);
channel.flush(); channel.flush();
final ConnectionDescriptor existingDescriptor = this.connectionDescriptors.getConnection(clientID); final ConnectionDescriptor existingDescriptor = this.connectionDescriptors.getConnection(clientID);
if (existingDescriptor == null) { if (existingDescriptor == null) {
@@ -642,42 +642,42 @@ public class ProtocolProcessor {
if (existingDescriptor.doesNotUseChannel(channel)) { if (existingDescriptor.doesNotUseChannel(channel)) {
// another client saved it's descriptor, exit // another client saved it's descriptor, exit
LOG.warn("Another client is using the connection descriptor. Closing connection. CId={}.", clientID); LOG.warn("Another client is using the connection descriptor. Closing connection. CId={}", clientID);
existingDescriptor.abort(); existingDescriptor.abort();
return; return;
} }
if (!removeSubscriptions(existingDescriptor, clientID)) { if (!removeSubscriptions(existingDescriptor, clientID)) {
LOG.warn("Unable to remove subscriptions. Closing connection. CId={}.", clientID); LOG.warn("Unable to remove subscriptions. Closing connection. CId={}", clientID);
existingDescriptor.abort(); existingDescriptor.abort();
return; return;
} }
if (!dropStoredMessages(existingDescriptor, clientID)) { if (!dropStoredMessages(existingDescriptor, clientID)) {
LOG.warn("Unable to drop stored messages. Closing connection. CId={}.", clientID); LOG.warn("Unable to drop stored messages. Closing connection. CId={}", clientID);
existingDescriptor.abort(); existingDescriptor.abort();
return; return;
} }
if (!cleanWillMessageAndNotifyInterceptor(existingDescriptor, clientID)) { if (!cleanWillMessageAndNotifyInterceptor(existingDescriptor, clientID)) {
LOG.warn("Unable to drop will message. Closing connection. CId={}.", clientID); LOG.warn("Unable to drop will message. Closing connection. CId={}", clientID);
existingDescriptor.abort(); existingDescriptor.abort();
return; return;
} }
if (!existingDescriptor.close()) { if (!existingDescriptor.close()) {
LOG.info("The connection has been closed. CId={}.", clientID); LOG.info("The connection has been closed. CId={}", clientID);
return; return;
} }
boolean stillPresent = this.connectionDescriptors.removeConnection(existingDescriptor); boolean stillPresent = this.connectionDescriptors.removeConnection(existingDescriptor);
if (!stillPresent) { if (!stillPresent) {
// another descriptor was inserted // another descriptor was inserted
LOG.warn("Another descriptor has been inserted. CId={}.", clientID); LOG.warn("Another descriptor has been inserted. CId={}", clientID);
return; return;
} }
LOG.info("The DISCONNECT message has been processed. CId={}.", clientID); LOG.info("The DISCONNECT message has been processed. CId={}", clientID);
} }
private boolean removeSubscriptions(ConnectionDescriptor descriptor, String clientID) { private boolean removeSubscriptions(ConnectionDescriptor descriptor, String clientID) {
@@ -687,9 +687,9 @@ public class ProtocolProcessor {
} }
if (descriptor.cleanSession) { if (descriptor.cleanSession) {
LOG.info("Removing saved subscriptions. CId={}.", descriptor.clientID); LOG.info("Removing saved subscriptions. CId={}", descriptor.clientID);
m_sessionsStore.wipeSubscriptions(clientID); m_sessionsStore.wipeSubscriptions(clientID);
LOG.info("The saved subscriptions have been removed. CId={}.", descriptor.clientID); LOG.info("The saved subscriptions have been removed. CId={}", descriptor.clientID);
} }
return true; return true;
} }
@@ -701,9 +701,9 @@ public class ProtocolProcessor {
} }
if (descriptor.cleanSession) { if (descriptor.cleanSession) {
LOG.debug("Removing messages of session. CId={}.", descriptor.clientID); LOG.debug("Removing messages of session. CId={}", descriptor.clientID);
this.m_sessionsStore.dropQueue(clientID); this.m_sessionsStore.dropQueue(clientID);
LOG.debug("The messages of the session have been removed. CId={}.", descriptor.clientID); LOG.debug("The messages of the session have been removed. CId={}", descriptor.clientID);
} }
return true; return true;
} }
@@ -714,7 +714,7 @@ public class ProtocolProcessor {
return false; return false;
} }
LOG.info("Removing will message. ClientId={}.", descriptor.clientID); LOG.info("Removing will message. ClientId={}", descriptor.clientID);
// cleanup the will store // cleanup the will store
m_willStore.remove(clientID); m_willStore.remove(clientID);
String username = descriptor.getUsername(); String username = descriptor.getUsername();
@@ -723,7 +723,7 @@ public class ProtocolProcessor {
} }
public void processConnectionLost(String clientID, Channel channel) { public void processConnectionLost(String clientID, Channel channel) {
LOG.info("Processing connection lost event. CId={}.", clientID); LOG.info("Processing connection lost event. CId={}", clientID);
ConnectionDescriptor oldConnDescr = new ConnectionDescriptor(clientID, channel, true); ConnectionDescriptor oldConnDescr = new ConnectionDescriptor(clientID, channel, true);
connectionDescriptors.removeConnection(oldConnDescr); connectionDescriptors.removeConnection(oldConnDescr);
// publish the Will message (if any) for the clientID // publish the Will message (if any) for the clientID
@@ -750,7 +750,7 @@ public class ProtocolProcessor {
List<String> topics = msg.payload().topics(); List<String> topics = msg.payload().topics();
String clientID = NettyUtils.clientID(channel); String clientID = NettyUtils.clientID(channel);
LOG.info("Processing UNSUBSCRIBE message. CId={}, topics={}.", clientID, topics); LOG.info("Processing UNSUBSCRIBE message. CId={}, topics={}", clientID, topics);
ClientSession clientSession = m_sessionsStore.sessionForClient(clientID); ClientSession clientSession = m_sessionsStore.sessionForClient(clientID);
for (String t : topics) { for (String t : topics) {
@@ -764,7 +764,7 @@ public class ProtocolProcessor {
return; return;
} }
LOG.debug("Removing subscription. CId={}, topic={}.", clientID, topic); LOG.debug("Removing subscription. CId={}, topic={}", clientID, topic);
subscriptions.removeSubscription(topic, clientID); subscriptions.removeSubscription(topic, clientID);
clientSession.unsubscribeFrom(topic); clientSession.unsubscribeFrom(topic);
String username = NettyUtils.userName(channel); String username = NettyUtils.userName(channel);
@@ -776,29 +776,28 @@ public class ProtocolProcessor {
MqttFixedHeader fixedHeader = new MqttFixedHeader(MqttMessageType.UNSUBACK, false, AT_LEAST_ONCE, false, 0); MqttFixedHeader fixedHeader = new MqttFixedHeader(MqttMessageType.UNSUBACK, false, AT_LEAST_ONCE, false, 0);
MqttUnsubAckMessage ackMessage = new MqttUnsubAckMessage(fixedHeader, from(messageID)); MqttUnsubAckMessage ackMessage = new MqttUnsubAckMessage(fixedHeader, from(messageID));
LOG.info("Sending UNSUBACK message. CId={}, topics={}, messageId={}.", clientID, topics, messageID); LOG.info("Sending UNSUBACK message. CId={}, topics={}, messageId={}", clientID, topics, messageID);
channel.writeAndFlush(ackMessage); channel.writeAndFlush(ackMessage);
} }
public void processSubscribe(Channel channel, MqttSubscribeMessage msg) { public void processSubscribe(Channel channel, MqttSubscribeMessage msg) {
String clientID = NettyUtils.clientID(channel); String clientID = NettyUtils.clientID(channel);
int messageID = messageId(msg); int messageID = messageId(msg);
LOG.info("Processing SUBSCRIBE message. CId={}, messageId={}.", clientID, messageID); LOG.info("Processing SUBSCRIBE message. CId={}, messageId={}", clientID, messageID);
RunningSubscription executionKey = new RunningSubscription(clientID, messageID); RunningSubscription executionKey = new RunningSubscription(clientID, messageID);
SubscriptionState currentStatus = this.subscriptionInCourse SubscriptionState currentStatus = subscriptionInCourse.putIfAbsent(executionKey, SubscriptionState.VERIFIED);
.putIfAbsent(executionKey, SubscriptionState.VERIFIED);
if (currentStatus != null) { if (currentStatus != null) {
LOG.warn("Client sent another SUBSCRIBE message while this one was being processed " + LOG.warn("Client sent another SUBSCRIBE message while this one was being processed CId={}, messageId={}",
"CId={}, messageId={}", clientID, messageID); clientID, messageID);
return; return;
} }
String username = NettyUtils.userName(channel); String username = NettyUtils.userName(channel);
List<MqttTopicSubscription> ackTopics = doVerify(clientID, username, msg); List<MqttTopicSubscription> ackTopics = doVerify(clientID, username, msg);
MqttSubAckMessage ackMessage = doAckMessageFromValidateFilters(ackTopics, messageID); MqttSubAckMessage ackMessage = doAckMessageFromValidateFilters(ackTopics, messageID);
if (!this.subscriptionInCourse.replace(executionKey, SubscriptionState.VERIFIED, SubscriptionState.STORED)) { if (!this.subscriptionInCourse.replace(executionKey, SubscriptionState.VERIFIED, SubscriptionState.STORED)) {
LOG.warn("Client sent another SUBSCRIBE message while the topic filters were being verified " + LOG.warn("Client sent another SUBSCRIBE message while the topic filters were being verified CId={}, " +
"CId={}, messageId={}", clientID, messageID); "messageId={}", clientID, messageID);
return; return;
} }
@@ -861,19 +860,18 @@ public class ProtocolProcessor {
Topic topic = new Topic(req.topicName()); Topic topic = new Topic(req.topicName());
if (!m_authorizator.canRead(topic, username, clientSession.clientID)) { if (!m_authorizator.canRead(topic, username, clientSession.clientID)) {
// send SUBACK with 0x80, the user hasn't credentials to read the topic // send SUBACK with 0x80, the user hasn't credentials to read the topic
LOG.error("Client does not have read permissions on the topic " + LOG.error("Client does not have read permissions on the topic CId={}, username={}, messageId={}, " +
"CId={}, username={}, messageId={}, topic={}", "topic={}", clientID, username, messageId, topic);
clientID, username, messageId, topic);
ackTopics.add(new MqttTopicSubscription(topic.toString(), FAILURE)); ackTopics.add(new MqttTopicSubscription(topic.toString(), FAILURE));
} else { } else {
MqttQoS qos; MqttQoS qos;
if (topic.isValid()) { if (topic.isValid()) {
LOG.info("Client will be subscribed to the topic " + LOG.info("Client will be subscribed to the topic CId={}, username={}, messageId={}, topic={}",
"CId={}, username={}, messageId={}, topic={}", clientID, username, messageId, topic); clientID, username, messageId, topic);
qos = req.qualityOfService(); qos = req.qualityOfService();
} else { } else {
LOG.error("Topic filter is not valid CId={}, username={}, messageId={}, topic={}", LOG.error("Topic filter is not valid CId={}, username={}, messageId={}, topic={}", clientID,
clientID, username, messageId, topic); username, messageId, topic);
qos = FAILURE; qos = FAILURE;
} }
ackTopics.add(new MqttTopicSubscription(topic.toString(), qos)); ackTopics.add(new MqttTopicSubscription(topic.toString(), qos));
@@ -32,7 +32,6 @@ import io.moquette.spi.impl.security.PermitAllAuthorizator;
import io.moquette.spi.impl.security.ResourceAuthenticator; import io.moquette.spi.impl.security.ResourceAuthenticator;
import io.moquette.spi.impl.subscriptions.Subscription; import io.moquette.spi.impl.subscriptions.Subscription;
import io.moquette.spi.impl.subscriptions.SubscriptionsStore; import io.moquette.spi.impl.subscriptions.SubscriptionsStore;
import io.moquette.spi.persistence.MapDBPersistentStore;
import io.moquette.spi.security.IAuthenticator; import io.moquette.spi.security.IAuthenticator;
import io.moquette.spi.security.IAuthorizator; import io.moquette.spi.security.IAuthorizator;
import org.slf4j.Logger; import org.slf4j.Logger;
@@ -51,6 +50,7 @@ import java.util.List;
public class ProtocolProcessorBootstrapper { public class ProtocolProcessorBootstrapper {
private static final Logger LOG = LoggerFactory.getLogger(ProtocolProcessorBootstrapper.class); private static final Logger LOG = LoggerFactory.getLogger(ProtocolProcessorBootstrapper.class);
public static final String MAPDB_STORE_CLASS = "io.moquette.persistence.MemoryStorageService";
private SubscriptionsStore subscriptions; private SubscriptionsStore subscriptions;
@@ -83,7 +83,7 @@ public class ProtocolProcessorBootstrapper {
* an implementation of the authorizator to be used, if null load that specified in * an implementation of the authorizator to be used, if null load that specified in
* config and fallback on the default one (permit all). * config and fallback on the default one (permit all).
* @param server * @param server
* the serber to init. * the server to init.
* @return the processor created for the broker. * @return the processor created for the broker.
*/ */
public ProtocolProcessor init(IConfig props, List<? extends InterceptHandler> embeddedObservers, public ProtocolProcessor init(IConfig props, List<? extends InterceptHandler> embeddedObservers,
@@ -92,31 +92,21 @@ public class ProtocolProcessorBootstrapper {
IMessagesStore messagesStore; IMessagesStore messagesStore;
LOG.info("Initializing messages and sessions stores..."); LOG.info("Initializing messages and sessions stores...");
String storageClassName = props.getProperty(BrokerConstants.STORAGE_CLASS_NAME, ""); String storageClassName = props.getProperty(BrokerConstants.STORAGE_CLASS_NAME, MAPDB_STORE_CLASS);
if (storageClassName != null && !storageClassName.isEmpty()) { if (storageClassName == null || storageClassName.isEmpty()) {
final IStore store = loadClass(storageClassName, IStore.class, Server.class, server); LOG.error("storage_class property not defined");
messagesStore = store.messagesStore(); throw new IllegalArgumentException("Can't start a valid persistence layer");
m_sessionsStore = store.sessionsStore();
storeShutdown = new Runnable() {
@Override
public void run() {
store.close();
}
};
} else {
final MapDBPersistentStore m_mapStorage = new MapDBPersistentStore(props, server.getScheduler());
m_mapStorage.initStore();
messagesStore = m_mapStorage.messagesStore();
m_sessionsStore = m_mapStorage.sessionsStore();
storeShutdown = new Runnable() {
@Override
public void run() {
m_mapStorage.close();
}
};
} }
final IStore store = loadClass(storageClassName, IStore.class, Server.class, server);
messagesStore = store.messagesStore();
m_sessionsStore = store.sessionsStore();
storeShutdown = new Runnable() {
@Override
public void run() {
store.close();
}
};
LOG.info("Configuring message interceptors..."); LOG.info("Configuring message interceptors...");
@@ -148,7 +138,7 @@ public class ProtocolProcessorBootstrapper {
} else { } else {
authenticator = new ResourceAuthenticator(resourceLoader, passwdPath); authenticator = new ResourceAuthenticator(resourceLoader, passwdPath);
} }
LOG.info("An {} authenticator instance will be used.", authenticator.getClass().getName()); LOG.info("An {} authenticator instance will be used", authenticator.getClass().getName());
} }
LOG.info("Configuring MQTT authorizator..."); LOG.info("Configuring MQTT authorizator...");
@@ -162,119 +152,78 @@ public class ProtocolProcessorBootstrapper {
if (aclFilePath != null && !aclFilePath.isEmpty()) { if (aclFilePath != null && !aclFilePath.isEmpty()) {
authorizator = new DenyAllAuthorizator(); authorizator = new DenyAllAuthorizator();
try { try {
LOG.info("Parsing ACL file. Path = {}.", aclFilePath); LOG.info("Parsing ACL file. Path = {}", aclFilePath);
authorizator = ACLFileParser.parse(resourceLoader.loadResource(aclFilePath)); authorizator = ACLFileParser.parse(resourceLoader.loadResource(aclFilePath));
} catch (ParseException pex) { } catch (ParseException pex) {
LOG.error( LOG.error("Unable to parse ACL file. path="+ aclFilePath, pex);
"Unable to parse ACL file. Path = {}, cause = {}, errorMessage = {}.",
aclFilePath,
pex.getCause(),
pex.getMessage());
} }
} else { } else {
authorizator = new PermitAllAuthorizator(); authorizator = new PermitAllAuthorizator();
} }
LOG.info("An {} authorizator instance will be used.", authorizator.getClass().getName()); LOG.info("An {} authorizator instance will be used", authorizator.getClass().getName());
} }
LOG.info("Initializing connection descriptor store..."); LOG.info("Initializing connection descriptor store...");
connectionDescriptors = new ConnectionDescriptorStore(m_sessionsStore); connectionDescriptors = new ConnectionDescriptorStore(m_sessionsStore);
LOG.info("Initializing MQTT protocol processor..."); LOG.info("Initializing MQTT protocol processor...");
boolean allowAnonymous = Boolean boolean allowAnonymous = Boolean.parseBoolean(props.getProperty(BrokerConstants.ALLOW_ANONYMOUS_PROPERTY_NAME, "true"));
.parseBoolean(props.getProperty(BrokerConstants.ALLOW_ANONYMOUS_PROPERTY_NAME, "true")); boolean allowZeroByteClientId = Boolean.parseBoolean(props.getProperty(BrokerConstants.ALLOW_ZERO_BYTE_CLIENT_ID_PROPERTY_NAME, "false"));
boolean allowZeroByteClientId = Boolean m_processor.init(connectionDescriptors, subscriptions, messagesStore, m_sessionsStore, authenticator,
.parseBoolean(props.getProperty(BrokerConstants.ALLOW_ZERO_BYTE_CLIENT_ID_PROPERTY_NAME, "false")); allowAnonymous, allowZeroByteClientId, authorizator, m_interceptor,
m_processor.init(
connectionDescriptors,
subscriptions,
messagesStore,
m_sessionsStore,
authenticator,
allowAnonymous,
allowZeroByteClientId,
authorizator,
m_interceptor,
props.getProperty(BrokerConstants.PORT_PROPERTY_NAME)); props.getProperty(BrokerConstants.PORT_PROPERTY_NAME));
return m_processor; return m_processor;
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private <T, U> T loadClass(String className, Class<T> iface, Class<U> constructorArgClass, U props) { private <T, U> T loadClass(String className, Class<T> intrface, Class<U> constructorArgClass, U props) {
T instance = null; T instance = null;
try { try {
LOG.info("Loading class. ClassName = {}, interfaceName = {}.", className, iface.getName()); LOG.info("Loading class. ClassName={}, interfaceName={}", className, intrface.getName());
Class<?> clazz = Class.forName(className); Class<?> clazz = Class.forName(className);
// check if method getInstance exists // check if method getInstance exists
Method method = clazz.getMethod("getInstance", new Class[]{}); Method method = clazz.getMethod("getInstance", new Class[]{});
try { try {
LOG.info( LOG.info("Invoking getInstance() method. ClassName = {}, interfaceName = {}.", className, intrface.getName());
"Invoking getInstance() method. ClassName = {}, interfaceName = {}.",
className,
iface.getName());
instance = (T) method.invoke(null, new Object[]{}); instance = (T) method.invoke(null, new Object[]{});
} catch (IllegalArgumentException | InvocationTargetException | IllegalAccessException ex) { } catch (IllegalArgumentException | InvocationTargetException | IllegalAccessException ex) {
LOG.error( LOG.error("Unable to invoke getInstance() method. ClassName = {}, interfaceName = {}, cause = {}, errorMessage = {}.",
"Unable to invoke getInstance() method. " className, intrface.getName(), ex.getCause(), ex.getMessage());
+ "ClassName = {}, interfaceName = {}, cause = {}, errorMessage = {}.",
className,
iface.getName(),
ex.getCause(),
ex.getMessage());
return null; return null;
} }
} catch (NoSuchMethodException nsmex) { } catch (NoSuchMethodException nsmex) {
try { try {
// check if constructor with constructor arg class parameter // check if constructor with constructor arg class parameter
// exists // exists
LOG.info( LOG.info("Invoking constructor with {} argument. ClassName = {}, interfaceName = {}.",
"Invoking constructor with {} argument. ClassName = {}, interfaceName = {}.", constructorArgClass.getName(), className, intrface.getName());
constructorArgClass.getName(), instance = this.getClass().getClassLoader()
className, .loadClass(className)
iface.getName()); .asSubclass(intrface)
instance = this.getClass().getClassLoader().loadClass(className).asSubclass(iface) .getConstructor(constructorArgClass)
.getConstructor(constructorArgClass).newInstance(props); .newInstance(props);
} catch (InstantiationException | IllegalAccessException | ClassNotFoundException ex) { } catch (InstantiationException | IllegalAccessException | ClassNotFoundException ex) {
LOG.warn( LOG.warn("Unable to invoke constructor with {} argument. ClassName = {}, interfaceName = {}, cause = {}, errorMessage = {}.",
"Unable to invoke constructor with {} argument. " constructorArgClass.getName(), className, intrface.getName(), ex.getCause(), ex.getMessage());
+ "ClassName = {}, interfaceName = {}, cause = {}, errorMessage = {}.",
constructorArgClass.getName(),
className,
iface.getName(),
ex.getCause(),
ex.getMessage());
return null; return null;
} catch (NoSuchMethodException | InvocationTargetException e) { } catch (NoSuchMethodException | InvocationTargetException e) {
try { try {
LOG.info( LOG.info("Invoking default constructor. ClassName = {}, interfaceName = {}.", className, intrface.getName());
"Invoking default constructor. ClassName = {}, interfaceName = {}.",
className,
iface.getName());
// fallback to default constructor // fallback to default constructor
instance = this.getClass().getClassLoader().loadClass(className).asSubclass(iface).newInstance(); instance = this.getClass().getClassLoader().loadClass(className).asSubclass(intrface).newInstance();
} catch (InstantiationException | IllegalAccessException | ClassNotFoundException ex) { } catch (InstantiationException | IllegalAccessException | ClassNotFoundException ex) {
LOG.error( LOG.error("Unable to invoke default constructor. ClassName = {}, interfaceName = {}, cause = {}, errorMessage = {}.",
"Unable to invoke default constructor. " className, intrface.getName(), ex.getCause(), ex.getMessage());
+ "ClassName = {}, interfaceName = {}, cause = {}, errorMessage = {}.",
className,
iface.getName(),
ex.getCause(),
ex.getMessage());
return null; return null;
} }
} }
} catch (ClassNotFoundException ex) { } catch (ClassNotFoundException ex) {
LOG.error("The class does not exist. ClassName = {}, interfaceName = {}.", className, iface.getName()); LOG.error("The class does not exist. ClassName = {}, interfaceName = {}.", className, intrface.getName());
return null; return null;
} catch (SecurityException ex) { } catch (SecurityException ex) {
LOG.error( LOG.error("Unable to load class due to a security violation. ClassName = {}, interfaceName = {}, cause = {}, errorMessage = {}.",
"Unable to load class due to a security violation. " className, intrface.getName(), ex.getCause(), ex.getMessage());
+ "ClassName = {}, interfaceName = {}, cause = {}, errorMessage = {}.",
className,
iface.getName(),
ex.getCause(),
ex.getMessage());
return null; return null;
} }
@@ -47,20 +47,12 @@ class Qos0PublishHandler extends QosPublishHandler {
final int messageID = msg.variableHeader().messageId(); final int messageID = msg.variableHeader().messageId();
if (LOG.isTraceEnabled()) { if (LOG.isTraceEnabled()) {
LOG.trace( LOG.trace("Sending publish message to subscribers. ClientId={}, topic={}, messageId={}, payload={}, " +
"Sending publish message to subscribers. " "subscriptionTree={}", clientID, topic, messageID, DebugUtils.payload2Str(toStoreMsg.getMessage()),
+ "MqttClientId = {}, topic = {}, messageId = {}, payload = {}, subscriptionTree = {}.", subscriptions.dumpTree());
clientID,
topic,
messageID,
DebugUtils.payload2Str(toStoreMsg.getMessage()),
subscriptions.dumpTree());
} else { } else {
LOG.info( LOG.info("Sending publish message to subscribers. ClientId={}, topic={}, messageId={}", clientID, topic,
"Sending publish message to subscribers. MqttClientId = {}, topic = {}, messageId = {}.", messageID);
clientID,
topic,
messageID);
} }
List<Subscription> topicMatchingSubscriptions = subscriptions.matches(topic); List<Subscription> topicMatchingSubscriptions = subscriptions.matches(topic);
@@ -57,37 +57,26 @@ class Qos1PublishHandler extends QosPublishHandler {
toStoreMsg.setClientID(clientID); toStoreMsg.setClientID(clientID);
if (LOG.isTraceEnabled()) { if (LOG.isTraceEnabled()) {
LOG.trace( LOG.trace("Sending publish message to subscribers. ClientId={}, topic={}, messageId={}, payload={}, " +
"Sending publish message to subscribers. " "subscriptionTree={}", clientID, topic, messageID, DebugUtils.payload2Str(toStoreMsg.getMessage()),
+ "MqttClientId = {}, topic = {}, messageId = {}, payload = {}, subscriptionTree = {}.", subscriptions.dumpTree());
clientID,
topic,
messageID,
DebugUtils.payload2Str(toStoreMsg.getMessage()),
subscriptions.dumpTree());
} else { } else {
LOG.info( LOG.info("Sending publish message to subscribers. ClientId={}, topic={}, messageId={}", clientID, topic,
"Sending publish message to subscribers. MqttClientId = {}, topic = {}, messageId = {}.", messageID);
clientID,
topic,
messageID);
} }
List<Subscription> topicMatchingSubscriptions = subscriptions.matches(topic); List<Subscription> topicMatchingSubscriptions = subscriptions.matches(topic);
this.publisher.publish2Subscribers(toStoreMsg, topicMatchingSubscriptions); this.publisher.publish2Subscribers(toStoreMsg, topicMatchingSubscriptions);
// send PUBACK
// TODO Don't send PUBREC for Hz publish notification, if (msg.isLocal()) {
sendPubAck(clientID, messageID); sendPubAck(clientID, messageID);
// }
if (msg.fixedHeader().isRetain()) { if (msg.fixedHeader().isRetain()) {
if (!msg.payload().isReadable()) { if (!msg.payload().isReadable()) {
m_messagesStore.cleanRetained(topic); m_messagesStore.cleanRetained(topic);
} else { } else {
// before wasn't stored // before wasn't stored
MessageGUID guid = m_messagesStore.storePublishForFuture(toStoreMsg); //MessageGUID guid = m_messagesStore.storePublishForFuture(toStoreMsg);
m_messagesStore.storeRetained(topic, guid); m_messagesStore.storeRetained(topic, toStoreMsg.getGuid());
} }
} }
@@ -102,17 +91,13 @@ class Qos1PublishHandler extends QosPublishHandler {
try { try {
if (connectionDescriptors == null) { if (connectionDescriptors == null) {
throw new RuntimeException( throw new RuntimeException("Internal bad error, found connectionDescriptors to null while it should " +
"Internal bad error, found connectionDescriptors to null while it should be initialized, " "be initialized, somewhere it's overwritten!!");
+ "somewhere it's overwritten!!");
} }
LOG.debug("clientIDs are {}", connectionDescriptors); LOG.debug("clientIDs are {}", connectionDescriptors);
if (!connectionDescriptors.isConnected(clientId)) { if (!connectionDescriptors.isConnected(clientId)) {
throw new RuntimeException( throw new RuntimeException(String.format("Can't find a ConnectionDescriptor for client %s in cache %s",
String.format( clientId,connectionDescriptors));
"Can't find a ConnectionDescriptor for client %s in cache %s",
clientId,
connectionDescriptors));
} }
connectionDescriptors.sendMessage(pubAckMessage, messageID, clientId); connectionDescriptors.sendMessage(pubAckMessage, messageID, clientId);
} catch (Throwable t) { } catch (Throwable t) {
@@ -58,27 +58,19 @@ class Qos2PublishHandler extends QosPublishHandler {
toStoreMsg.setClientID(clientID); toStoreMsg.setClientID(clientID);
if (LOG.isTraceEnabled()) { if (LOG.isTraceEnabled()) {
LOG.trace( LOG.trace( "Sending publish message to subscribers. ClientId={}, topic={}, messageId={}, payload={}, " +
"Sending publish message to subscribers. MqttClientId = {}, topic = {}, messageId = {}, " "subscriptionTree={}", clientID, topic, messageID, DebugUtils.payload2Str(toStoreMsg.getMessage()),
+ "payload = {}, subscriptionTree = {}.", subscriptions.dumpTree());
clientID,
topic,
messageID,
DebugUtils.payload2Str(toStoreMsg.getMessage()),
subscriptions.dumpTree());
} else { } else {
LOG.info( LOG.info("Sending publish message to subscribers. ClientId={}, topic={}, messageId={}", clientID, topic,
"Sending publish message to subscribers. MqttClientId = {}, topic = {}, messageId = {}.", messageID);
clientID,
topic,
messageID);
} }
// QoS2 // QoS2
MessageGUID guid = m_messagesStore.storePublishForFuture(toStoreMsg); MessageGUID guid = m_messagesStore.storePublishForFuture(toStoreMsg);
// TODO Don't send PUBREC for Hz publish notification, if (msg.isLocal()) { // TODO Don't send PUBREC for Hz publish notification, if (msg.isLocal()) {
sendPubRec(clientID, messageID); sendPubRec(clientID, messageID);
// }
// Next the client will send us a pub rel // Next the client will send us a pub rel
// NB publish to subscribers for QoS 2 happen upon PUBREL from publisher // NB publish to subscribers for QoS 2 happen upon PUBREL from publisher
@@ -100,27 +92,19 @@ class Qos2PublishHandler extends QosPublishHandler {
void processPubRel(Channel channel, MqttMessage msg) { void processPubRel(Channel channel, MqttMessage msg) {
String clientID = NettyUtils.clientID(channel); String clientID = NettyUtils.clientID(channel);
int messageID = messageId(msg); int messageID = messageId(msg);
LOG.info("Processing PUBREL message. MqttClientId = {}, messageId = {}.", clientID, messageID); LOG.info("Processing PUBREL message. ClientId={}, messageId={}", clientID, messageID);
ClientSession targetSession = m_sessionsStore.sessionForClient(clientID); ClientSession targetSession = m_sessionsStore.sessionForClient(clientID);
IMessagesStore.StoredMessage evt = targetSession.storedMessage(messageID); IMessagesStore.StoredMessage evt = targetSession.storedMessage(messageID);
final Topic topic = new Topic(evt.getTopic()); final Topic topic = new Topic(evt.getTopic());
List<Subscription> topicMatchingSubscriptions = subscriptions.matches(topic); List<Subscription> topicMatchingSubscriptions = subscriptions.matches(topic);
if (LOG.isDebugEnabled()) { if (LOG.isDebugEnabled()) {
LOG.debug( LOG.debug("Sending publish message to subscribers. ClientId={}, topic={}, messageId={}, payload={}, " +
"Sending publish message to subscribers. " "subscriptionTree={}", clientID, topic, messageID, DebugUtils.payload2Str(evt.getMessage()),
+ "MqttClientId = {}, topic = {}, messageId = {}, payload = {}, subscriptionTree = {}.", subscriptions.dumpTree());
clientID,
topic,
messageID,
DebugUtils.payload2Str(evt.getMessage()),
subscriptions.dumpTree());
} else { } else {
LOG.info( LOG.info("Sending publish message to subscribers. ClientId={}, topic={}, messageId={}", clientID, topic,
"Sending publish message to subscribers. MqttClientId = {}, topic = {}, messageId = {}.", messageID);
clientID,
topic,
messageID);
} }
this.publisher.publish2Subscribers(evt, topicMatchingSubscriptions); this.publisher.publish2Subscribers(evt, topicMatchingSubscriptions);
@@ -136,14 +120,14 @@ class Qos2PublishHandler extends QosPublishHandler {
} }
private void sendPubRec(String clientID, int messageID) { private void sendPubRec(String clientID, int messageID) {
LOG.debug("Sending PUBREC message. MqttClientId = {}, messageId = {}.", clientID, messageID); LOG.debug("Sending PUBREC message. ClientId={}, messageId={}", clientID, messageID);
MqttFixedHeader fixedHeader = new MqttFixedHeader(MqttMessageType.PUBREC, false, AT_MOST_ONCE, false, 0); MqttFixedHeader fixedHeader = new MqttFixedHeader(MqttMessageType.PUBREC, false, AT_MOST_ONCE, false, 0);
MqttMessage pubRecMessage = new MqttMessage(fixedHeader, from(messageID)); MqttMessage pubRecMessage = new MqttMessage(fixedHeader, from(messageID));
connectionDescriptors.sendMessage(pubRecMessage, messageID, clientID); connectionDescriptors.sendMessage(pubRecMessage, messageID, clientID);
} }
private void sendPubComp(String clientID, int messageID) { private void sendPubComp(String clientID, int messageID) {
LOG.debug("Sending PUBCOMP message. MqttClientId = {}, messageId = {}.", clientID, messageID); LOG.debug("Sending PUBCOMP message. ClientId={}, messageId={}", clientID, messageID);
MqttFixedHeader fixedHeader = new MqttFixedHeader(MqttMessageType.PUBCOMP, false, AT_MOST_ONCE, false, 0); MqttFixedHeader fixedHeader = new MqttFixedHeader(MqttMessageType.PUBCOMP, false, AT_MOST_ONCE, false, 0);
MqttMessage pubCompMessage = new MqttMessage(fixedHeader, from(messageID)); MqttMessage pubCompMessage = new MqttMessage(fixedHeader, from(messageID));
connectionDescriptors.sendMessage(pubCompMessage, messageID, clientID); connectionDescriptors.sendMessage(pubCompMessage, messageID, clientID);
@@ -47,7 +47,7 @@ public class Topic implements Serializable {
valid = true; valid = true;
} catch (ParseException e) { } catch (ParseException e) {
valid = false; valid = false;
LOG.error(e.toString(), e); LOG.error("Error parsing the topic: {}, message: {}", topic, e.getMessage());
} }
} }
@@ -20,9 +20,9 @@ import io.moquette.BrokerConstants;
import io.moquette.server.config.IConfig; import io.moquette.server.config.IConfig;
import io.moquette.server.config.MemoryConfig; import io.moquette.server.config.MemoryConfig;
import io.moquette.spi.impl.subscriptions.Topic; import io.moquette.spi.impl.subscriptions.Topic;
import io.moquette.spi.impl.MemoryMessagesStore; import io.moquette.persistence.MemoryMessagesStore;
import io.moquette.spi.impl.MemoryStorageService; import io.moquette.persistence.MemoryStorageService;
import io.moquette.spi.persistence.MemorySessionStore; import io.moquette.persistence.MemorySessionStore;
import io.moquette.spi.security.IAuthenticator; import io.moquette.spi.security.IAuthenticator;
import io.moquette.spi.security.IAuthorizator; import io.moquette.spi.security.IAuthorizator;
import org.junit.After; import org.junit.After;
@@ -55,7 +55,7 @@ public class IntegrationUtils {
return testProperties; return testProperties;
} }
public static void cleanPersistenceFile(IConfig config) { /*public static void cleanPersistenceFile(IConfig config) {
String fileName = config.getProperty(PERSISTENT_STORE_PROPERTY_NAME); String fileName = config.getProperty(PERSISTENT_STORE_PROPERTY_NAME);
cleanPersistenceFile(fileName); cleanPersistenceFile(fileName);
} }
@@ -68,5 +68,5 @@ public class IntegrationUtils {
new File(fileName + ".t").delete(); new File(fileName + ".t").delete();
} }
assertFalse(dbFile.exists()); assertFalse(dbFile.exists());
} }*/
} }
@@ -53,7 +53,6 @@ public class ServerIntegrationDBAuthenticatorTest {
private void stopServer() { private void stopServer() {
m_server.stopServer(); m_server.stopServer();
IntegrationUtils.cleanPersistenceFile(m_config);
} }
private Properties addDBAuthenticatorConf(Properties properties) { private Properties addDBAuthenticatorConf(Properties properties) {
@@ -67,9 +66,6 @@ public class ServerIntegrationDBAuthenticatorTest {
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
String dbPath = IntegrationUtils.localMapDBPath();
IntegrationUtils.cleanPersistenceFile(dbPath);
startServer(); startServer();
m_client = new MqttClient("tcp://localhost:1883", "TestClient", s_dataStore); m_client = new MqttClient("tcp://localhost:1883", "TestClient", s_dataStore);
@@ -62,9 +62,6 @@ public class ServerIntegrationEmbeddedPublishTest {
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
String dbPath = IntegrationUtils.localMapDBPath();
IntegrationUtils.cleanPersistenceFile(dbPath);
startServer(); startServer();
m_subscriber = new MqttClient("tcp://localhost:1883", "TestClient", s_dataStore); m_subscriber = new MqttClient("tcp://localhost:1883", "TestClient", s_dataStore);
@@ -79,7 +76,6 @@ public class ServerIntegrationEmbeddedPublishTest {
} }
m_server.stopServer(); m_server.stopServer();
IntegrationUtils.cleanPersistenceFile(m_config);
} }
private void subscribeToWithQos(String topic, int qos) throws Exception { private void subscribeToWithQos(String topic, int qos) throws Exception {
@@ -69,7 +69,6 @@ public class ServerIntegrationFuseTest {
} }
m_server.stopServer(); m_server.stopServer();
IntegrationUtils.cleanPersistenceFile(m_config);
} }
@Test @Test
@@ -67,9 +67,6 @@ public class ServerIntegrationHazelcastHandlerInterceptorTest {
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
String dbPath = IntegrationUtils.localMapDBPath();
IntegrationUtils.cleanPersistenceFile(dbPath);
final Properties configProps = addHazelCastConf( final Properties configProps = addHazelCastConf(
IntegrationUtils.prepareTestClusterProperties(1883), IntegrationUtils.prepareTestClusterProperties(1883),
1883, 1883,
@@ -57,9 +57,6 @@ public class ServerIntegrationPahoTest {
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
String dbPath = IntegrationUtils.localMapDBPath();
IntegrationUtils.cleanPersistenceFile(dbPath);
startServer(); startServer();
m_client = new MqttClient("tcp://localhost:1883", "TestClient", s_dataStore); m_client = new MqttClient("tcp://localhost:1883", "TestClient", s_dataStore);
@@ -84,7 +81,6 @@ public class ServerIntegrationPahoTest {
private void stopServer() { private void stopServer() {
m_server.stopServer(); m_server.stopServer();
IntegrationUtils.cleanPersistenceFile(m_config);
} }
@Test @Test
@@ -176,6 +172,7 @@ public class ServerIntegrationPahoTest {
m_client.disconnect(); m_client.disconnect();
} }
@Ignore("This test hasn't any meaning using in memory storage service")
@Test @Test
public void testCleanSession_maintainClientSubscriptions_withServerRestart() throws Exception { public void testCleanSession_maintainClientSubscriptions_withServerRestart() throws Exception {
LOG.info("*** testCleanSession_maintainClientSubscriptions_withServerRestart ***"); LOG.info("*** testCleanSession_maintainClientSubscriptions_withServerRestart ***");
@@ -89,7 +89,6 @@ public class ServerIntegrationQoSValidationTest {
} }
m_server.stopServer(); m_server.stopServer();
IntegrationUtils.cleanPersistenceFile(m_config);
} }
@Test @Test
@@ -65,7 +65,6 @@ public class ServerIntegrationWebSocketTest {
client.stop(); client.stop();
m_server.stopServer(); m_server.stopServer();
IntegrationUtils.cleanPersistenceFile(m_config);
} }
@Test @Test
@@ -70,7 +70,6 @@ public class ServerLowlevelMessagesIntegrationTests {
Thread.sleep(300); // to let the close event pass before server stop event Thread.sleep(300); // to let the close event pass before server stop event
m_server.stopServer(); m_server.stopServer();
LOG.debug("After asked server to stop"); LOG.debug("After asked server to stop");
IntegrationUtils.cleanPersistenceFile(m_config);
} }
@Test @Test
@@ -93,22 +92,11 @@ public class ServerLowlevelMessagesIntegrationTests {
} }
private static MqttConnectMessage createConnectMessage(String clientID, int keepAlive) { private static MqttConnectMessage createConnectMessage(String clientID, int keepAlive) {
MqttFixedHeader mqttFixedHeader = new MqttFixedHeader( MqttFixedHeader mqttFixedHeader = new MqttFixedHeader(MqttMessageType.CONNECT, false, MqttQoS.AT_MOST_ONCE,
MqttMessageType.CONNECT, false, 0);
false,
MqttQoS.AT_MOST_ONCE,
false,
0);
MqttConnectVariableHeader mqttConnectVariableHeader = new MqttConnectVariableHeader( MqttConnectVariableHeader mqttConnectVariableHeader = new MqttConnectVariableHeader(
MqttVersion.MQTT_3_1.protocolName(), MqttVersion.MQTT_3_1.protocolName(), MqttVersion.MQTT_3_1.protocolLevel(), false, false, false, 1, false,
MqttVersion.MQTT_3_1.protocolLevel(), true, keepAlive);
false,
false,
false,
1,
false,
true,
keepAlive);
MqttConnectPayload mqttConnectPayload = new MqttConnectPayload(clientID, null, null, null, null); MqttConnectPayload mqttConnectPayload = new MqttConnectPayload(clientID, null, null, null, null);
return new MqttConnectMessage(mqttFixedHeader, mqttConnectVariableHeader, mqttConnectPayload); return new MqttConnectMessage(mqttFixedHeader, mqttConnectVariableHeader, mqttConnectPayload);
} }
@@ -64,7 +64,6 @@ public class ServerRestartIntegrationTest {
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
String dbPath = IntegrationUtils.localMapDBPath(); String dbPath = IntegrationUtils.localMapDBPath();
IntegrationUtils.cleanPersistenceFile(dbPath);
startServer(); startServer();
m_subscriber = new MqttClient("tcp://localhost:1883", "Subscriber", s_dataStore); m_subscriber = new MqttClient("tcp://localhost:1883", "Subscriber", s_dataStore);
@@ -85,7 +84,6 @@ public class ServerRestartIntegrationTest {
} }
m_server.stopServer(); m_server.stopServer();
IntegrationUtils.cleanPersistenceFile(m_config);
} }
@Test @Test
@@ -97,7 +95,6 @@ public class ServerRestartIntegrationTest {
// shutdown the server // shutdown the server
m_server.stopServer(); m_server.stopServer();
IntegrationUtils.cleanPersistenceFile(m_config);
// restart the server // restart the server
m_server.startServer(IntegrationUtils.prepareTestProperties()); m_server.startServer(IntegrationUtils.prepareTestProperties());
@@ -1,7 +1,7 @@
package io.moquette.spi; package io.moquette.spi;
import io.moquette.spi.impl.MemoryStorageService; import io.moquette.persistence.MemoryStorageService;
import io.moquette.spi.impl.subscriptions.Subscription; import io.moquette.spi.impl.subscriptions.Subscription;
import io.moquette.spi.impl.subscriptions.SubscriptionsStore; import io.moquette.spi.impl.subscriptions.SubscriptionsStore;
import io.moquette.spi.impl.subscriptions.Topic; import io.moquette.spi.impl.subscriptions.Topic;
@@ -17,6 +17,7 @@
package io.moquette.spi.impl; package io.moquette.spi.impl;
import io.moquette.interception.InterceptHandler; import io.moquette.interception.InterceptHandler;
import io.moquette.persistence.MemoryStorageService;
import io.moquette.server.netty.MessageBuilder; import io.moquette.server.netty.MessageBuilder;
import io.moquette.server.netty.NettyUtils; import io.moquette.server.netty.NettyUtils;
import io.moquette.spi.IMatchingCondition; import io.moquette.spi.IMatchingCondition;
@@ -98,14 +99,8 @@ public class ProtocolProcessorTest {
subscriptions = new SubscriptionsStore(); subscriptions = new SubscriptionsStore();
subscriptions.init(memStorage.sessionsStore()); subscriptions.init(memStorage.sessionsStore());
m_processor = new ProtocolProcessor(); m_processor = new ProtocolProcessor();
m_processor.init( m_processor.init(subscriptions, m_messagesStore, m_sessionStore, m_mockAuthenticator, true,
subscriptions, new PermitAllAuthorizator(), NO_OBSERVERS_INTERCEPTOR);
m_messagesStore,
m_sessionStore,
m_mockAuthenticator,
true,
new PermitAllAuthorizator(),
NO_OBSERVERS_INTERCEPTOR);
} }
@Test @Test
@@ -176,13 +171,7 @@ public class ProtocolProcessorTest {
// simulate a connect that register a clientID to an IoSession // simulate a connect that register a clientID to an IoSession
MemoryStorageService storageService = new MemoryStorageService(); MemoryStorageService storageService = new MemoryStorageService();
subs.init(storageService.sessionsStore()); subs.init(storageService.sessionsStore());
m_processor.init( m_processor.init(subs, m_messagesStore, m_sessionStore, null, true, new PermitAllAuthorizator(),
subs,
m_messagesStore,
m_sessionStore,
null,
true,
new PermitAllAuthorizator(),
NO_OBSERVERS_INTERCEPTOR); NO_OBSERVERS_INTERCEPTOR);
EmbeddedChannel firstReceiverChannel = new EmbeddedChannel(); EmbeddedChannel firstReceiverChannel = new EmbeddedChannel();
@@ -228,10 +217,8 @@ public class ProtocolProcessorTest {
// Verify // Verify
assertTrue(m_channel.readOutbound() instanceof MqttSubAckMessage); assertTrue(m_channel.readOutbound() instanceof MqttSubAckMessage);
Subscription expectedSubscription = new Subscription( Subscription expectedSubscription = new Subscription(FAKE_CLIENT_ID, new Topic(FAKE_TOPIC),
FAKE_CLIENT_ID, MqttQoS.AT_MOST_ONCE);
new Topic(FAKE_TOPIC),
MqttQoS.AT_MOST_ONCE);
assertTrue(subscriptions.contains(expectedSubscription)); assertTrue(subscriptions.contains(expectedSubscription));
} }
@@ -244,13 +231,7 @@ public class ProtocolProcessorTest {
when(mockAuthorizator.canRead(eq(new Topic(FAKE_TOPIC)), eq(fakeUserName), eq(FAKE_CLIENT_ID))) when(mockAuthorizator.canRead(eq(new Topic(FAKE_TOPIC)), eq(fakeUserName), eq(FAKE_CLIENT_ID)))
.thenReturn(false); .thenReturn(false);
m_processor.init( m_processor.init(subscriptions, m_messagesStore, m_sessionStore, m_mockAuthenticator, true, mockAuthorizator,
subscriptions,
m_messagesStore,
m_sessionStore,
m_mockAuthenticator,
true,
mockAuthorizator,
NO_OBSERVERS_INTERCEPTOR); NO_OBSERVERS_INTERCEPTOR);
// Exercise // Exercise
@@ -288,10 +269,8 @@ public class ProtocolProcessorTest {
// Verify // Verify
assertEquals(1, subscriptions.size()); assertEquals(1, subscriptions.size());
Subscription expectedSubscription = new Subscription( Subscription expectedSubscription = new Subscription(FAKE_CLIENT_ID, new Topic(FAKE_TOPIC),
FAKE_CLIENT_ID, MqttQoS.AT_MOST_ONCE);
new Topic(FAKE_TOPIC),
MqttQoS.AT_MOST_ONCE);
assertTrue(subscriptions.contains(expectedSubscription)); assertTrue(subscriptions.contains(expectedSubscription));
} }
@@ -332,10 +311,7 @@ public class ProtocolProcessorTest {
@Test @Test
public void testPublishOfRetainedMessage_afterNewSubscription() throws Exception { public void testPublishOfRetainedMessage_afterNewSubscription() throws Exception {
// simulate a connect that register a clientID to an IoSession // simulate a connect that register a clientID to an IoSession
final Subscription subscription = new Subscription( final Subscription subscription = new Subscription(FAKE_PUBLISHER_ID, new Topic(FAKE_TOPIC), MqttQoS.AT_MOST_ONCE);
FAKE_PUBLISHER_ID,
new Topic(FAKE_TOPIC),
MqttQoS.AT_MOST_ONCE);
// subscriptions.matches(topic) redefine the method to return true // subscriptions.matches(topic) redefine the method to return true
SubscriptionsStore subs = new SubscriptionsStore() { SubscriptionsStore subs = new SubscriptionsStore() {
@@ -353,13 +329,7 @@ public class ProtocolProcessorTest {
subs.init(storageService.sessionsStore()); subs.init(storageService.sessionsStore());
// simulate a connect that register a clientID to an IoSession // simulate a connect that register a clientID to an IoSession
m_processor.init( m_processor.init(subs, m_messagesStore, m_sessionStore, null, true, new PermitAllAuthorizator(),
subs,
m_messagesStore,
m_sessionStore,
null,
true,
new PermitAllAuthorizator(),
NO_OBSERVERS_INTERCEPTOR); NO_OBSERVERS_INTERCEPTOR);
MqttConnectMessage connectMessage = MessageBuilder.connect().clientId(FAKE_PUBLISHER_ID) MqttConnectMessage connectMessage = MessageBuilder.connect().clientId(FAKE_PUBLISHER_ID)
.protocolVersion(MqttVersion.MQTT_3_1).cleanSession(true).build(); .protocolVersion(MqttVersion.MQTT_3_1).cleanSession(true).build();
@@ -398,13 +368,7 @@ public class ProtocolProcessorTest {
retainedMessage.setClientID(FAKE_PUBLISHER_ID); retainedMessage.setClientID(FAKE_PUBLISHER_ID);
m_messagesStore.storePublishForFuture(retainedMessage); m_messagesStore.storePublishForFuture(retainedMessage);
m_processor.init( m_processor.init(subs, m_messagesStore, m_sessionStore, null, true, new PermitAllAuthorizator(),
subs,
m_messagesStore,
m_sessionStore,
null,
true,
new PermitAllAuthorizator(),
NO_OBSERVERS_INTERCEPTOR); NO_OBSERVERS_INTERCEPTOR);
MqttConnectMessage connectMessage = MessageBuilder.connect().clientId(FAKE_PUBLISHER_ID) MqttConnectMessage connectMessage = MessageBuilder.connect().clientId(FAKE_PUBLISHER_ID)
@@ -427,13 +391,7 @@ public class ProtocolProcessorTest {
List<Subscription> inactiveSubscriptions = Collections.singletonList(inactiveSub); List<Subscription> inactiveSubscriptions = Collections.singletonList(inactiveSub);
when(mockedSubscriptions.matches(eq(new Topic("/topic")))).thenReturn(inactiveSubscriptions); when(mockedSubscriptions.matches(eq(new Topic("/topic")))).thenReturn(inactiveSubscriptions);
m_processor = new ProtocolProcessor(); m_processor = new ProtocolProcessor();
m_processor.init( m_processor.init(mockedSubscriptions, m_messagesStore, m_sessionStore, null, true, new PermitAllAuthorizator(),
mockedSubscriptions,
m_messagesStore,
m_sessionStore,
null,
true,
new PermitAllAuthorizator(),
NO_OBSERVERS_INTERCEPTOR); NO_OBSERVERS_INTERCEPTOR);
// Exercise // Exercise
@@ -16,6 +16,7 @@
package io.moquette.spi.impl; package io.moquette.spi.impl;
import io.moquette.persistence.MemoryStorageService;
import io.moquette.server.netty.MessageBuilder; import io.moquette.server.netty.MessageBuilder;
import io.moquette.server.netty.NettyUtils; import io.moquette.server.netty.NettyUtils;
import io.moquette.spi.ClientSession; import io.moquette.spi.ClientSession;
@@ -19,7 +19,7 @@ package io.moquette.spi.impl.subscriptions;
import io.moquette.spi.ClientSession; import io.moquette.spi.ClientSession;
import io.moquette.spi.ISessionsStore; import io.moquette.spi.ISessionsStore;
import io.moquette.spi.ISessionsStore.ClientTopicCouple; import io.moquette.spi.ISessionsStore.ClientTopicCouple;
import io.moquette.spi.impl.MemoryStorageService; import io.moquette.persistence.MemoryStorageService;
import io.netty.handler.codec.mqtt.MqttQoS; import io.netty.handler.codec.mqtt.MqttQoS;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
+164
View File
@@ -0,0 +1,164 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<relativePath>../</relativePath>
<artifactId>moquette-parent</artifactId>
<groupId>io.moquette</groupId>
<version>0.10-SNAPSHOT</version>
</parent>
<artifactId>moquette-mapdb-storage</artifactId>
<packaging>jar</packaging>
<name>Moquette - MapDB storage</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<bintray.package>io.moquette.moquette-broker</bintray.package>
</properties>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>moquette-broker</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.5</version>
</dependency>
<dependency>
<groupId>org.mapdb</groupId>
<artifactId>mapdb</artifactId>
<!--version>1.1.0-SNAPSHOT</version -->
<!--<version>2.0.0-SNAPSHOT</version> -->
<!--<version>2.0-alpha3</version> -->
<version>1.0.8</version>
</dependency>
<dependency>
<groupId>org.hdrhistogram</groupId>
<artifactId>HdrHistogram</artifactId>
<version>2.1.4</version>
</dependency>
<dependency>
<groupId>com.hazelcast</groupId>
<artifactId>hazelcast</artifactId>
<version>3.5.4</version>
</dependency>
<!-- Test dependencies -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.8.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.5</version>
<scope>test</scope>
</dependency>
<!-- needs extra dependencies: objenesis & hamcrest -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>1.9.0</version>
<scope>test</scope>
</dependency>
<!--<dependency>-->
<!--<groupId>org.fusesource.mqtt-client</groupId>-->
<!--<artifactId>mqtt-client</artifactId>-->
<!--<version>1.12</version>-->
<!--<scope>test</scope>-->
<!--</dependency>-->
<!--<dependency>-->
<!--<groupId>org.eclipse.paho</groupId>-->
<!--<artifactId>org.eclipse.paho.client.mqttv3</artifactId>-->
<!--<version>1.0.2</version>-->
<!--<scope>test</scope>-->
<!--</dependency>-->
<!--<dependency>-->
<!--<groupId>org.eclipse.jetty.websocket</groupId>-->
<!--<artifactId>websocket-client</artifactId>-->
<!--<version>9.2.0.M1</version>-->
<!--<scope>test</scope>-->
<!--</dependency>-->
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<!-- use 2.6.0 for Java 7 projects -->
<version>2.6.0</version>
<scope>test</scope>
</dependency>
<!--<dependency>-->
<!--<groupId>com.h2database</groupId>-->
<!--<artifactId>h2</artifactId>-->
<!--<version>1.4.191</version>-->
<!--<scope>test</scope>-->
<!--</dependency>-->
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<includes>
<include>**/*Test.java</include>
</includes>
</configuration>
</plugin>
<!-- Includes to generate OSGi headers -->
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
</archive>
</configuration>
<executions>
<execution>
<goals>
<goal>test-jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<executions>
<execution>
<id>bundle-manifest</id>
<phase>process-classes</phase>
<goals>
<goal>manifest</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<distributionManagement>
<repository>
<id>bintray</id>
<url>https://api.bintray.com/maven/${bintray.repo}/${bintray.package}</url>
</repository>
</distributionManagement>
</project>
@@ -14,7 +14,7 @@
* You may elect to redistribute this code under either of these licenses. * You may elect to redistribute this code under either of these licenses.
*/ */
package io.moquette.spi.persistence; package io.moquette.persistence.mapdb;
import io.moquette.spi.IMatchingCondition; import io.moquette.spi.IMatchingCondition;
import io.moquette.spi.IMessagesStore; import io.moquette.spi.IMessagesStore;
@@ -14,8 +14,9 @@
* You may elect to redistribute this code under either of these licenses. * You may elect to redistribute this code under either of these licenses.
*/ */
package io.moquette.spi.persistence; package io.moquette.persistence.mapdb;
import io.moquette.BrokerConstants;
import io.moquette.server.config.IConfig; import io.moquette.server.config.IConfig;
import io.moquette.spi.IMessagesStore; import io.moquette.spi.IMessagesStore;
import io.moquette.spi.ISessionsStore; import io.moquette.spi.ISessionsStore;
@@ -29,28 +30,12 @@ import java.io.IOException;
import java.io.Serializable; import java.io.Serializable;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import static io.moquette.BrokerConstants.AUTOSAVE_INTERVAL_PROPERTY_NAME;
import static io.moquette.BrokerConstants.PERSISTENT_STORE_PROPERTY_NAME;
/** /**
* MapDB main persistence implementation * MapDB main persistence implementation
*/ */
public class MapDBPersistentStore implements IStore { public class MapDBPersistentStore implements IStore {
/**
* This is a DTO used to persist minimal status (clean session and activation status) of a
* session.
*/
public static class PersistentSession implements Serializable {
private static final long serialVersionUID = 5052054783220481854L;
public final boolean cleanSession;
public PersistentSession(boolean cleanSession) {
this.cleanSession = cleanSession;
}
}
private static final Logger LOG = LoggerFactory.getLogger(MapDBPersistentStore.class); private static final Logger LOG = LoggerFactory.getLogger(MapDBPersistentStore.class);
private DB m_db; private DB m_db;
@@ -62,8 +47,8 @@ public class MapDBPersistentStore implements IStore {
private ISessionsStore m_sessionsStore; private ISessionsStore m_sessionsStore;
public MapDBPersistentStore(IConfig props, ScheduledExecutorService scheduler) { public MapDBPersistentStore(IConfig props, ScheduledExecutorService scheduler) {
this.m_storePath = props.getProperty(PERSISTENT_STORE_PROPERTY_NAME, ""); this.m_storePath = props.getProperty(BrokerConstants.PERSISTENT_STORE_PROPERTY_NAME, "");
this.m_autosaveInterval = Integer.parseInt(props.getProperty(AUTOSAVE_INTERVAL_PROPERTY_NAME, "30")); this.m_autosaveInterval = Integer.parseInt(props.getProperty(BrokerConstants.AUTOSAVE_INTERVAL_PROPERTY_NAME, "30"));
this.m_scheduler = scheduler; this.m_scheduler = scheduler;
} }
@@ -135,6 +120,9 @@ public class MapDBPersistentStore implements IStore {
LOG.info("Closing MapDB store..."); LOG.info("Closing MapDB store...");
this.m_db.close(); this.m_db.close();
LOG.info("Stopping MapDB commit tasks..."); LOG.info("Stopping MapDB commit tasks...");
//TODO th scheduler must be stopped by the owning (the instance of Server)
//invalidate the added task
this.m_scheduler.shutdown(); this.m_scheduler.shutdown();
try { try {
m_scheduler.awaitTermination(10L, TimeUnit.SECONDS); m_scheduler.awaitTermination(10L, TimeUnit.SECONDS);
@@ -14,8 +14,9 @@
* You may elect to redistribute this code under either of these licenses. * You may elect to redistribute this code under either of these licenses.
*/ */
package io.moquette.spi.persistence; package io.moquette.persistence.mapdb;
import io.moquette.persistence.PersistentSession;
import io.moquette.spi.ClientSession; import io.moquette.spi.ClientSession;
import io.moquette.spi.IMessagesStore; import io.moquette.spi.IMessagesStore;
import io.moquette.spi.IMessagesStore.StoredMessage; import io.moquette.spi.IMessagesStore.StoredMessage;
@@ -24,7 +25,6 @@ import io.moquette.spi.MessageGUID;
import io.moquette.spi.impl.Utils; import io.moquette.spi.impl.Utils;
import io.moquette.spi.impl.subscriptions.Subscription; import io.moquette.spi.impl.subscriptions.Subscription;
import io.moquette.spi.impl.subscriptions.Topic; import io.moquette.spi.impl.subscriptions.Topic;
import io.moquette.spi.persistence.MapDBPersistentStore.PersistentSession;
import org.mapdb.DB; import org.mapdb.DB;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -68,47 +68,38 @@ class MapDBSessionsStore implements ISessionsStore {
@Override @Override
public void addNewSubscription(Subscription newSubscription) { public void addNewSubscription(Subscription newSubscription) {
LOG.info( LOG.info("Adding new subscription. ClientId={}, topics={}", newSubscription.getClientId(),
"Adding new subscription. CId= {}, topics = {}.", newSubscription.getTopicFilter());
newSubscription.getClientId(),
newSubscription.getTopicFilter());
final String clientID = newSubscription.getClientId(); final String clientID = newSubscription.getClientId();
m_db.getHashMap("subscriptions_" + clientID).put(newSubscription.getTopicFilter(), newSubscription); m_db.getHashMap("subscriptions_" + clientID).put(newSubscription.getTopicFilter(), newSubscription);
if (LOG.isTraceEnabled()) { if (LOG.isTraceEnabled()) {
LOG.trace( LOG.trace("Subscription has been added. ClientId={}, topics={}, clientSubscriptions={}",
"The subscription has been added. CId= {}, topics = {}, clientSubscriptions = {}.", newSubscription.getClientId(), newSubscription.getTopicFilter(),
newSubscription.getClientId(), m_db.getHashMap("subscriptions_" + clientID));
newSubscription.getTopicFilter(),
m_db.getHashMap("subscriptions_" + clientID));
} }
} }
@Override @Override
public void removeSubscription(Topic topicFilter, String clientID) { public void removeSubscription(Topic topicFilter, String clientID) {
LOG.info("Removing subscription. CId= {}, topics = {}.", clientID, topicFilter); LOG.info("Removing subscription. ClientId={}, topics={}", clientID, topicFilter);
if (!m_db.exists("subscriptions_" + clientID)) { if (!m_db.exists("subscriptions_" + clientID)) {
return; return;
} }
m_db.getHashMap("subscriptions_" + clientID).remove(topicFilter); m_db.getHashMap("subscriptions_" + clientID).remove(topicFilter);
if (LOG.isDebugEnabled()) { if (LOG.isDebugEnabled()) {
LOG.debug( LOG.debug("Subscription has been removed. ClientId={}, topics={}, clientSubscriptions={}", clientID,
"The subscription has been removed. CId= {}, topics = {}, clientSubscriptions = {}.", topicFilter, m_db.getHashMap("subscriptions_" + clientID));
clientID,
topicFilter,
m_db.getHashMap("subscriptions_" + clientID));
} }
} }
@Override @Override
public void wipeSubscriptions(String clientID) { public void wipeSubscriptions(String clientID) {
LOG.info("Wiping subscriptions. CId= {}.", clientID); LOG.info("Wiping subscriptions. ClientId={}", clientID);
m_db.delete("subscriptions_" + clientID); m_db.delete("subscriptions_" + clientID);
if (LOG.isDebugEnabled()) { if (LOG.isDebugEnabled()) {
LOG.debug( LOG.debug("Subscriptions have been removed. ClientId={}, clientSubscriptions={}", clientID,
"The subscriptions have been removed. CId= {}, clientSubscriptions = {}.", m_db.getHashMap("subscriptions_" + clientID));
clientID,
m_db.getHashMap("subscriptions_" + clientID));
} }
} }
@@ -123,7 +114,7 @@ class MapDBSessionsStore implements ISessionsStore {
} }
} }
if (LOG.isTraceEnabled()) { if (LOG.isTraceEnabled()) {
LOG.trace("The existing subscriptions have been retrieved. Result = {}.", allSubscriptions); LOG.trace("The existing subscriptions have been retrieved. Result={}", allSubscriptions);
} }
return allSubscriptions; return allSubscriptions;
} }
@@ -131,7 +122,7 @@ class MapDBSessionsStore implements ISessionsStore {
@Override @Override
public Subscription getSubscription(ClientTopicCouple couple) { public Subscription getSubscription(ClientTopicCouple couple) {
ConcurrentMap<Topic, Subscription> clientSubscriptions = m_db.getHashMap("subscriptions_" + couple.clientID); ConcurrentMap<Topic, Subscription> clientSubscriptions = m_db.getHashMap("subscriptions_" + couple.clientID);
LOG.info("Retrieving subscriptions. CId= {}, subscriptions = {}.", couple.clientID, clientSubscriptions); LOG.info("Retrieving subscriptions. ClientId={}, subscriptions={}", couple.clientID, clientSubscriptions);
return clientSubscriptions.get(couple.topicFilter); return clientSubscriptions.get(couple.topicFilter);
} }
@@ -143,7 +134,7 @@ class MapDBSessionsStore implements ISessionsStore {
ConcurrentMap<Topic, Subscription> clientSubscriptions = m_db.getHashMap("subscriptions_" + clientID); ConcurrentMap<Topic, Subscription> clientSubscriptions = m_db.getHashMap("subscriptions_" + clientID);
subscriptions.addAll(clientSubscriptions.values()); subscriptions.addAll(clientSubscriptions.values());
} }
LOG.debug("The existing subscriptions have been retrieved. Result = {}.", subscriptions); LOG.debug("The existing subscriptions have been retrieved. Result={}", subscriptions);
return subscriptions; return subscriptions;
} }
@@ -155,22 +146,20 @@ class MapDBSessionsStore implements ISessionsStore {
@Override @Override
public ClientSession createNewSession(String clientID, boolean cleanSession) { public ClientSession createNewSession(String clientID, boolean cleanSession) {
if (m_persistentSessions.containsKey(clientID)) { if (m_persistentSessions.containsKey(clientID)) {
LOG.error( LOG.error("Unable to create a new session: the client ID is already in use. ClientId={}, cleanSession={}",
"Unable to create a new session: the client ID is already in use. CId= {}, cleanSession = {}.", clientID, cleanSession);
clientID,
cleanSession);
throw new IllegalArgumentException("Can't create a session with the ID of an already existing" + clientID); throw new IllegalArgumentException("Can't create a session with the ID of an already existing" + clientID);
} }
LOG.info("Creating new session. CId= {}, cleanSession = {}.", clientID, cleanSession); LOG.info("Creating new session. ClientId={}, cleanSession={}", clientID, cleanSession);
m_persistentSessions.putIfAbsent(clientID, new PersistentSession(cleanSession)); m_persistentSessions.putIfAbsent(clientID, new PersistentSession(cleanSession));
return new ClientSession(clientID, m_messagesStore, this, cleanSession); return new ClientSession(clientID, m_messagesStore, this, cleanSession);
} }
@Override @Override
public ClientSession sessionForClient(String clientID) { public ClientSession sessionForClient(String clientID) {
LOG.info("Retrieving session. CId= {}.", clientID); LOG.info("Retrieving session. ClientId={}", clientID);
if (!m_persistentSessions.containsKey(clientID)) { if (!m_persistentSessions.containsKey(clientID)) {
LOG.warn("The session does not exist. CId= {}.", clientID); LOG.warn("The session does not exist. ClientId={}", clientID);
return null; return null;
} }
@@ -189,8 +178,8 @@ class MapDBSessionsStore implements ISessionsStore {
@Override @Override
public void updateCleanStatus(String clientID, boolean cleanSession) { public void updateCleanStatus(String clientID, boolean cleanSession) {
LOG.info("Updating cleanSession flag. CId= {}, cleanSession = {}.", clientID, cleanSession); LOG.info("Updating cleanSession flag. ClientId={}, cleanSession={}", clientID, cleanSession);
m_persistentSessions.put(clientID, new MapDBPersistentStore.PersistentSession(cleanSession)); m_persistentSessions.put(clientID, new PersistentSession(cleanSession));
} }
/** /**
@@ -199,7 +188,7 @@ class MapDBSessionsStore implements ISessionsStore {
@Override @Override
public int nextPacketID(String clientID) { public int nextPacketID(String clientID) {
if (LOG.isDebugEnabled()) { if (LOG.isDebugEnabled()) {
LOG.debug("Generating next packet ID. CId= {}.", clientID); LOG.debug("Generating next packet ID. ClientId={}", clientID);
} }
Set<Integer> inFlightForClient = this.m_inFlightIds.get(clientID); Set<Integer> inFlightForClient = this.m_inFlightIds.get(clientID);
if (inFlightForClient == null) { if (inFlightForClient == null) {
@@ -215,17 +204,17 @@ class MapDBSessionsStore implements ISessionsStore {
int nextPacketId = (maxId % 0xFFFF) + 1; int nextPacketId = (maxId % 0xFFFF) + 1;
inFlightForClient.add(nextPacketId); inFlightForClient.add(nextPacketId);
if (LOG.isDebugEnabled()) { if (LOG.isDebugEnabled()) {
LOG.debug("The next packet ID has been generated. CId= {}, result = {}.", clientID, nextPacketId); LOG.debug("The next packet ID has been generated. ClientId={}, result={}", clientID, nextPacketId);
} }
return nextPacketId; return nextPacketId;
} }
@Override @Override
public void inFlightAck(String clientID, int messageID) { public void inFlightAck(String clientID, int messageID) {
LOG.debug("Acknowledging inflight message. CId= {}, messageId = {}.", clientID, messageID); LOG.debug("Acknowledging inflight message. ClientId={}, messageId={}", clientID, messageID);
Map<Integer, MessageGUID> m = this.m_inflightStore.get(clientID); Map<Integer, MessageGUID> m = this.m_inflightStore.get(clientID);
if (m == null) { if (m == null) {
LOG.warn("Unable to retrieve inflight message record. CId= {}, messageId = {}.", clientID, messageID); LOG.warn("Unable to retrieve inflight message record. ClientId={}, messageId={}", clientID, messageID);
return; return;
} }
m.remove(messageID); m.remove(messageID);
@@ -239,7 +228,7 @@ class MapDBSessionsStore implements ISessionsStore {
@Override @Override
public void inFlight(String clientID, int messageID, MessageGUID guid) { public void inFlight(String clientID, int messageID, MessageGUID guid) {
LOG.debug("Storing inflight message. CId= {}, messageId = {}, guid = {}.", clientID, messageID, guid); LOG.debug("Storing inflight message. ClientId={}, messageId={}, guid={}", clientID, messageID, guid);
ConcurrentMap<Integer, MessageGUID> m = this.m_inflightStore.get(clientID); ConcurrentMap<Integer, MessageGUID> m = this.m_inflightStore.get(clientID);
if (m == null) { if (m == null) {
m = new ConcurrentHashMap<>(); m = new ConcurrentHashMap<>();
@@ -251,24 +240,24 @@ class MapDBSessionsStore implements ISessionsStore {
@Override @Override
public BlockingQueue<StoredMessage> queue(String clientID) { public BlockingQueue<StoredMessage> queue(String clientID) {
LOG.info("Queuing pending message. CId= {}, guid = {}.", clientID); LOG.info("Queuing pending message. ClientId={}, guid={}", clientID);
return this.m_db.getQueue(clientID); return this.m_db.getQueue(clientID);
} }
@Override @Override
public void dropQueue(String clientID) { public void dropQueue(String clientID) {
LOG.info("Removing pending messages. CId= {}.", clientID); LOG.info("Removing pending messages. ClientId={}", clientID);
this.m_db.delete(clientID); this.m_db.delete(clientID);
} }
@Override @Override
public void moveInFlightToSecondPhaseAckWaiting(String clientID, int messageID) { public void moveInFlightToSecondPhaseAckWaiting(String clientID, int messageID) {
if (LOG.isDebugEnabled()) { if (LOG.isDebugEnabled()) {
LOG.debug("Moving inflight message to 2nd phase ack state. CId= {}, messageID = {}.", clientID, messageID); LOG.debug("Moving inflight message to 2nd phase ack state. ClientId={}, messageID={}", clientID, messageID);
} }
Map<Integer, MessageGUID> m = this.m_inflightStore.get(clientID); Map<Integer, MessageGUID> m = this.m_inflightStore.get(clientID);
if (m == null) { if (m == null) {
LOG.warn("Unable to retrieve inflight message record. CId= {}, messageId = {}.", clientID, messageID); LOG.warn("Unable to retrieve inflight message record. ClientId={}, messageId={}", clientID, messageID);
return; return;
} }
MessageGUID guid = m.remove(messageID); MessageGUID guid = m.remove(messageID);
@@ -279,8 +268,8 @@ class MapDBSessionsStore implements ISessionsStore {
inFlightForClient.remove(messageID); inFlightForClient.remove(messageID);
} }
Map<Integer, MessageGUID> messageIDs = Utils final HashMap<Integer, MessageGUID> emptyGuids = new HashMap<>();
.defaultGet(m_secondPhaseStore, clientID, new HashMap<Integer, MessageGUID>()); Map<Integer, MessageGUID> messageIDs = Utils.defaultGet(m_secondPhaseStore, clientID, emptyGuids);
messageIDs.put(messageID, guid); messageIDs.put(messageID, guid);
m_secondPhaseStore.put(clientID, messageIDs); m_secondPhaseStore.put(clientID, messageIDs);
} }
@@ -290,8 +279,8 @@ class MapDBSessionsStore implements ISessionsStore {
if (LOG.isDebugEnabled()) { if (LOG.isDebugEnabled()) {
LOG.debug("Processing second phase ACK CId={}, messageId={}", clientID, messageID); LOG.debug("Processing second phase ACK CId={}, messageId={}", clientID, messageID);
} }
Map<Integer, MessageGUID> messageIDs = Utils final HashMap<Integer, MessageGUID> emptyGuids = new HashMap<>();
.defaultGet(m_secondPhaseStore, clientID, new HashMap<Integer, MessageGUID>()); Map<Integer, MessageGUID> messageIDs = Utils.defaultGet(m_secondPhaseStore, clientID, emptyGuids);
MessageGUID guid = messageIDs.remove(messageID); MessageGUID guid = messageIDs.remove(messageID);
m_secondPhaseStore.put(clientID, messageIDs); m_secondPhaseStore.put(clientID, messageIDs);
return guid; return guid;
@@ -307,7 +296,7 @@ class MapDBSessionsStore implements ISessionsStore {
} }
MessageGUID guid = clientEntries.get(messageID); MessageGUID guid = clientEntries.get(messageID);
if (guid == null) { if (guid == null) {
LOG.warn("The message ID does not have an associated GUID. CId= {}, messageId = {}.", clientID, messageID); LOG.warn("The message ID does not have an associated GUID. ClientId={}, messageId={}", clientID, messageID);
return null; return null;
} }
return m_messagesStore.getMessageByGuid(guid); return m_messagesStore.getMessageByGuid(guid);
@@ -14,12 +14,12 @@
* You may elect to redistribute this code under either of these licenses. * You may elect to redistribute this code under either of these licenses.
*/ */
package io.moquette.spi.persistence; package io.moquette.persistence.mapdb;
import java.io.File;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
import io.moquette.BrokerConstants; import io.moquette.BrokerConstants;
import io.moquette.server.IntegrationUtils;
import io.moquette.server.config.IConfig; import io.moquette.server.config.IConfig;
import io.moquette.server.config.MemoryConfig; import io.moquette.server.config.MemoryConfig;
import io.moquette.spi.ClientSession; import io.moquette.spi.ClientSession;
@@ -36,6 +36,8 @@ import org.junit.Test;
import java.util.List; import java.util.List;
import java.util.Properties; import java.util.Properties;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import static io.moquette.BrokerConstants.PERSISTENT_STORE_PROPERTY_NAME;
import static org.junit.Assert.*; import static org.junit.Assert.*;
/** /**
@@ -55,7 +57,7 @@ public class MapDBPersistentStoreTest {
public void setUp() throws Exception { public void setUp() throws Exception {
scheduler = Executors.newScheduledThreadPool(1); scheduler = Executors.newScheduledThreadPool(1);
IntegrationUtils.cleanPersistenceFile(BrokerConstants.DEFAULT_PERSISTENT_PATH); cleanPersistenceFile(BrokerConstants.DEFAULT_PERSISTENT_PATH);
Properties props = new Properties(); Properties props = new Properties();
props.setProperty(BrokerConstants.PERSISTENT_STORE_PROPERTY_NAME, BrokerConstants.DEFAULT_PERSISTENT_PATH); props.setProperty(BrokerConstants.PERSISTENT_STORE_PROPERTY_NAME, BrokerConstants.DEFAULT_PERSISTENT_PATH);
IConfig conf = new MemoryConfig(props); IConfig conf = new MemoryConfig(props);
@@ -72,7 +74,17 @@ public class MapDBPersistentStoreTest {
} }
scheduler.shutdown(); scheduler.shutdown();
IntegrationUtils.cleanPersistenceFile(BrokerConstants.DEFAULT_PERSISTENT_PATH); cleanPersistenceFile(BrokerConstants.DEFAULT_PERSISTENT_PATH);
}
public static void cleanPersistenceFile(String fileName) {
File dbFile = new File(fileName);
if (dbFile.exists()) {
dbFile.delete();
new File(fileName + ".p").delete();
new File(fileName + ".t").delete();
}
assertFalse(dbFile.exists());
} }
@Test @Test
@@ -4,8 +4,9 @@ package io.moquette.spi.impl.subscriptions;
import java.io.IOException; import java.io.IOException;
import java.lang.management.ManagementFactory; import java.lang.management.ManagementFactory;
import java.lang.management.ThreadMXBean; import java.lang.management.ThreadMXBean;
import io.moquette.persistence.MemoryStorageService;
import io.moquette.spi.ISessionsStore; import io.moquette.spi.ISessionsStore;
import io.moquette.spi.impl.MemoryStorageService;
import io.netty.handler.codec.mqtt.MqttQoS; import io.netty.handler.codec.mqtt.MqttQoS;
public class PerformanceTest { public class PerformanceTest {
+1
View File
@@ -105,6 +105,7 @@
<modules> <modules>
<module>broker</module> <module>broker</module>
<module>mapdb_storage</module>
<module>distribution</module> <module>distribution</module>
<module>embedding_moquette</module> <module>embedding_moquette</module>
<module>osgi_test</module> <module>osgi_test</module>