Separated persistence layer, created submodule for MapDB persistence letting the broker use the in-memory message store
This commit is contained in:
@@ -27,13 +27,8 @@ class InternalRepublisher {
|
||||
// fire as retained the message
|
||||
Integer packetID = storedMsg.getQos() == MqttQoS.AT_MOST_ONCE ? null : targetSession.nextPacketId();
|
||||
if (packetID != null) {
|
||||
LOG.debug(
|
||||
"Adding message to inflight zone. "
|
||||
+ "MqttClientId = {}, packetId = {}, messageId = {}, topic = {}.",
|
||||
targetSession.clientID,
|
||||
packetID,
|
||||
storedMsg.getMessageID(),
|
||||
storedMsg.getTopic());
|
||||
LOG.debug("Adding message to inflight zone. ClientId={}, packetId={}, messageId={}, topic={}",
|
||||
targetSession.clientID, packetID, storedMsg.getMessageID(), storedMsg.getTopic());
|
||||
targetSession.inFlightAckWaiting(storedMsg.getGuid(), packetID);
|
||||
}
|
||||
MqttPublishMessage publishMsg = retainedPublish(storedMsg);
|
||||
@@ -51,12 +46,8 @@ class InternalRepublisher {
|
||||
|
||||
for (IMessagesStore.StoredMessage pubEvt : storedPublishes) {
|
||||
// put in flight zone
|
||||
LOG.debug(
|
||||
"Adding message ot inflight zone. MqttClientId = {}, guid = {}, messageId = {}, topic = {}.",
|
||||
clientSession.clientID,
|
||||
pubEvt.getGuid(),
|
||||
pubEvt.getMessageID(),
|
||||
pubEvt.getTopic());
|
||||
LOG.debug("Adding message ot inflight zone. ClientId={}, guid={}, messageId={}, topic={}",
|
||||
clientSession.clientID, pubEvt.getGuid(), pubEvt.getMessageID(), pubEvt.getTopic());
|
||||
clientSession.inFlightAckWaiting(pubEvt.getGuid(), pubEvt.getMessageID());
|
||||
MqttPublishMessage publishMsg = notRetainedPublish(pubEvt);
|
||||
// set the PacketIdentifier only for QoS > 0
|
||||
@@ -68,39 +59,23 @@ class InternalRepublisher {
|
||||
}
|
||||
|
||||
private MqttPublishMessage notRetainedPublish(IMessagesStore.StoredMessage storedMessage, Integer messageID) {
|
||||
return createPublishForQos(
|
||||
storedMessage.getTopic(),
|
||||
storedMessage.getQos(),
|
||||
storedMessage.getMessage(),
|
||||
false,
|
||||
messageID);
|
||||
return createPublishForQos(storedMessage.getTopic(), storedMessage.getQos(), storedMessage.getMessage(), false,
|
||||
messageID);
|
||||
}
|
||||
|
||||
private MqttPublishMessage notRetainedPublish(IMessagesStore.StoredMessage storedMessage) {
|
||||
return createPublishForQos(
|
||||
storedMessage.getTopic(),
|
||||
storedMessage.getQos(),
|
||||
storedMessage.getMessage(),
|
||||
false,
|
||||
0);
|
||||
return createPublishForQos(storedMessage.getTopic(), storedMessage.getQos(), storedMessage.getMessage(), false,
|
||||
0);
|
||||
}
|
||||
|
||||
private MqttPublishMessage retainedPublish(IMessagesStore.StoredMessage storedMessage) {
|
||||
return createPublishForQos(
|
||||
storedMessage.getTopic(),
|
||||
storedMessage.getQos(),
|
||||
storedMessage.getMessage(),
|
||||
true,
|
||||
0);
|
||||
return createPublishForQos(storedMessage.getTopic(), storedMessage.getQos(), storedMessage.getMessage(), true,
|
||||
0);
|
||||
}
|
||||
|
||||
private MqttPublishMessage retainedPublish(IMessagesStore.StoredMessage storedMessage, Integer packetID) {
|
||||
return createPublishForQos(
|
||||
storedMessage.getTopic(),
|
||||
storedMessage.getQos(),
|
||||
storedMessage.getMessage(),
|
||||
true,
|
||||
packetID);
|
||||
return createPublishForQos(storedMessage.getTopic(), storedMessage.getQos(), storedMessage.getMessage(), true,
|
||||
packetID);
|
||||
}
|
||||
|
||||
public static MqttPublishMessage createPublishForQos(String topic, MqttQoS qos, ByteBuf message, boolean retained,
|
||||
|
||||
@@ -80,12 +80,8 @@ class MessagesPublisher {
|
||||
this.messageSender.sendPublish(targetSession, publishMsg);
|
||||
} else {
|
||||
if (!targetSession.isCleanSession()) {
|
||||
LOG.debug(
|
||||
"Storing pending PUBLISH inactive message. MessageId={}, CId={}, topicFilter={}, qos={}",
|
||||
pubMsg.getMessageID(),
|
||||
sub.getClientId(),
|
||||
sub.getTopicFilter(),
|
||||
qos);
|
||||
LOG.debug("Storing pending PUBLISH inactive message. MessageId={}, CId={}, topicFilter={}, qos={}",
|
||||
pubMsg.getMessageID(), sub.getClientId(), sub.getTopicFilter(), qos);
|
||||
// store the message in targetSession queue to deliver
|
||||
targetSession.enqueue(pubMsg);
|
||||
}
|
||||
|
||||
@@ -24,12 +24,8 @@ class PersistentQueueMessageSender {
|
||||
final int messageId = pubMessage.variableHeader().messageId();
|
||||
final String topicName = pubMessage.variableHeader().topicName();
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug(
|
||||
"Sending PUBLISH message. MessageId={}, CId={}, topic={}, qos={}, payload={}",
|
||||
messageId,
|
||||
clientId,
|
||||
topicName,
|
||||
DebugUtils.payload2Str(pubMessage.payload()));
|
||||
LOG.debug("Sending PUBLISH message. MessageId={}, CId={}, topic={}, qos={}, payload={}", messageId,
|
||||
clientId, topicName, DebugUtils.payload2Str(pubMessage.payload()));
|
||||
} else {
|
||||
LOG.info("Sending PUBLISH message. MessageId={}, CId={}, topic={}", messageId, clientId, topicName);
|
||||
}
|
||||
@@ -38,24 +34,12 @@ class PersistentQueueMessageSender {
|
||||
|
||||
MqttQoS qos = pubMessage.fixedHeader().qosLevel();
|
||||
if (!messageDelivered && qos != AT_MOST_ONCE && !clientsession.isCleanSession()) {
|
||||
LOG.warn(
|
||||
"The PUBLISH message could not be delivered. It will be stored. "
|
||||
+ "MessageId = {}, CId={}, topic = {}, qos = {}, cleanSession = {}.",
|
||||
messageId,
|
||||
clientId,
|
||||
topicName,
|
||||
qos,
|
||||
false);
|
||||
LOG.warn("PUBLISH message could not be delivered. It will be stored. MessageId={}, CId={}, topic={}, qos={}, " +
|
||||
"cleanSession={}", messageId, clientId, topicName, qos, false);
|
||||
clientsession.enqueue(asStoredMessage(pubMessage));
|
||||
} else {
|
||||
LOG.warn(
|
||||
"The PUBLISH message could not be delivered. It will be discarded. "
|
||||
+ "MessageId = {}, CId={}, topic = {}, qos = {}, cleanSession = {}.",
|
||||
messageId,
|
||||
clientId,
|
||||
topicName,
|
||||
qos,
|
||||
true);
|
||||
LOG.warn("PUBLISH message could not be delivered. It will be discarded. MessageId={}, CId={}, topic={}, " +
|
||||
"qos={}, cleanSession={}", messageId, clientId, topicName, qos, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -209,7 +209,7 @@ public class ProtocolProcessor {
|
||||
this.allowZeroByteClientId = allowZeroByteClientId;
|
||||
m_authorizator = authorizator;
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Initial subscriptions tree={}.", subscriptions.dumpTree());
|
||||
LOG.debug("Initial subscriptions tree={}", subscriptions.dumpTree());
|
||||
}
|
||||
m_authenticator = authenticator;
|
||||
m_messagesStore = storageService;
|
||||
@@ -302,7 +302,7 @@ public class ProtocolProcessor {
|
||||
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) {
|
||||
@@ -347,7 +347,7 @@ public class ProtocolProcessor {
|
||||
}
|
||||
|
||||
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);
|
||||
if (!success) {
|
||||
return false;
|
||||
@@ -363,17 +363,17 @@ public class ProtocolProcessor {
|
||||
}
|
||||
|
||||
if (isSessionAlreadyStored) {
|
||||
LOG.info("Cleaning session. CId={}.", clientId);
|
||||
LOG.info("Cleaning session. CId={}", clientId);
|
||||
clientSession.cleanSession(msg.variableHeader().isCleanSession());
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
private void initializeKeepAliveTimeout(Channel channel, MqttConnectMessage msg, final String clientId) {
|
||||
int keepAlive = msg.variableHeader().keepAliveTimeSeconds();
|
||||
LOG.info("Configuring connection. CId={}.", clientId);
|
||||
LOG.info("Configuring connection. CId={}", clientId);
|
||||
NettyUtils.keepAlive(channel, keepAlive);
|
||||
// session.attr(NettyUtils.ATTR_KEY_CLEANSESSION).set(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(),
|
||||
willQos);
|
||||
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());
|
||||
}
|
||||
if (msg.variableHeader().isCleanSession()) {
|
||||
LOG.info("Cleaning session. CId={}.", clientId);
|
||||
LOG.info("Cleaning session. CId={}", clientId);
|
||||
clientSession.cleanSession();
|
||||
}
|
||||
return clientSession;
|
||||
@@ -453,10 +453,10 @@ public class ProtocolProcessor {
|
||||
* Republish QoS1 and QoS2 messages stored into the session for the clientID.
|
||||
*/
|
||||
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();
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -531,7 +531,7 @@ public class ProtocolProcessor {
|
||||
public void internalPublish(MqttPublishMessage msg, final String clientId) {
|
||||
final MqttQoS qos = msg.fixedHeader().qosLevel();
|
||||
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;
|
||||
IMessagesStore.StoredMessage toStoreMsg = asStoredMessage(msg);
|
||||
@@ -579,7 +579,7 @@ public class ProtocolProcessor {
|
||||
Topic topic = new Topic(tobeStored.getTopic());
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -610,7 +610,7 @@ public class ProtocolProcessor {
|
||||
int messageID = messageId(msg);
|
||||
targetSession.moveInFlightToSecondPhaseAckWaiting(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);
|
||||
MqttMessage pubRelMessage = new MqttMessage(pubRelHeader, from(messageID));
|
||||
@@ -620,7 +620,7 @@ public class ProtocolProcessor {
|
||||
public void processPubComp(Channel channel, MqttMessage msg) {
|
||||
String clientID = NettyUtils.clientID(channel);
|
||||
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
|
||||
ClientSession targetSession = m_sessionsStore.sessionForClient(clientID);
|
||||
StoredMessage inflightMsg = targetSession.secondPhaseAcknowledged(messageID);
|
||||
@@ -631,7 +631,7 @@ public class ProtocolProcessor {
|
||||
|
||||
public void processDisconnect(Channel channel) throws InterruptedException {
|
||||
final String clientID = NettyUtils.clientID(channel);
|
||||
LOG.info("Processing DISCONNECT message. CId={}.", clientID);
|
||||
LOG.info("Processing DISCONNECT message. CId={}", clientID);
|
||||
channel.flush();
|
||||
final ConnectionDescriptor existingDescriptor = this.connectionDescriptors.getConnection(clientID);
|
||||
if (existingDescriptor == null) {
|
||||
@@ -642,42 +642,42 @@ public class ProtocolProcessor {
|
||||
|
||||
if (existingDescriptor.doesNotUseChannel(channel)) {
|
||||
// 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();
|
||||
return;
|
||||
}
|
||||
|
||||
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();
|
||||
return;
|
||||
}
|
||||
|
||||
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();
|
||||
return;
|
||||
}
|
||||
|
||||
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();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!existingDescriptor.close()) {
|
||||
LOG.info("The connection has been closed. CId={}.", clientID);
|
||||
LOG.info("The connection has been closed. CId={}", clientID);
|
||||
return;
|
||||
}
|
||||
|
||||
boolean stillPresent = this.connectionDescriptors.removeConnection(existingDescriptor);
|
||||
if (!stillPresent) {
|
||||
// another descriptor was inserted
|
||||
LOG.warn("Another descriptor has been inserted. CId={}.", clientID);
|
||||
LOG.warn("Another descriptor has been inserted. CId={}", clientID);
|
||||
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) {
|
||||
@@ -687,9 +687,9 @@ public class ProtocolProcessor {
|
||||
}
|
||||
|
||||
if (descriptor.cleanSession) {
|
||||
LOG.info("Removing saved subscriptions. CId={}.", descriptor.clientID);
|
||||
LOG.info("Removing saved subscriptions. CId={}", descriptor.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;
|
||||
}
|
||||
@@ -701,9 +701,9 @@ public class ProtocolProcessor {
|
||||
}
|
||||
|
||||
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);
|
||||
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;
|
||||
}
|
||||
@@ -714,7 +714,7 @@ public class ProtocolProcessor {
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG.info("Removing will message. ClientId={}.", descriptor.clientID);
|
||||
LOG.info("Removing will message. ClientId={}", descriptor.clientID);
|
||||
// cleanup the will store
|
||||
m_willStore.remove(clientID);
|
||||
String username = descriptor.getUsername();
|
||||
@@ -723,7 +723,7 @@ public class ProtocolProcessor {
|
||||
}
|
||||
|
||||
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);
|
||||
connectionDescriptors.removeConnection(oldConnDescr);
|
||||
// publish the Will message (if any) for the clientID
|
||||
@@ -750,7 +750,7 @@ public class ProtocolProcessor {
|
||||
List<String> topics = msg.payload().topics();
|
||||
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);
|
||||
for (String t : topics) {
|
||||
@@ -764,7 +764,7 @@ public class ProtocolProcessor {
|
||||
return;
|
||||
}
|
||||
|
||||
LOG.debug("Removing subscription. CId={}, topic={}.", clientID, topic);
|
||||
LOG.debug("Removing subscription. CId={}, topic={}", clientID, topic);
|
||||
subscriptions.removeSubscription(topic, clientID);
|
||||
clientSession.unsubscribeFrom(topic);
|
||||
String username = NettyUtils.userName(channel);
|
||||
@@ -776,29 +776,28 @@ public class ProtocolProcessor {
|
||||
MqttFixedHeader fixedHeader = new MqttFixedHeader(MqttMessageType.UNSUBACK, false, AT_LEAST_ONCE, false, 0);
|
||||
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);
|
||||
}
|
||||
|
||||
public void processSubscribe(Channel channel, MqttSubscribeMessage msg) {
|
||||
String clientID = NettyUtils.clientID(channel);
|
||||
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);
|
||||
SubscriptionState currentStatus = this.subscriptionInCourse
|
||||
.putIfAbsent(executionKey, SubscriptionState.VERIFIED);
|
||||
SubscriptionState currentStatus = subscriptionInCourse.putIfAbsent(executionKey, SubscriptionState.VERIFIED);
|
||||
if (currentStatus != null) {
|
||||
LOG.warn("Client sent another SUBSCRIBE message while this one was being processed " +
|
||||
"CId={}, messageId={}", clientID, messageID);
|
||||
LOG.warn("Client sent another SUBSCRIBE message while this one was being processed CId={}, messageId={}",
|
||||
clientID, messageID);
|
||||
return;
|
||||
}
|
||||
String username = NettyUtils.userName(channel);
|
||||
List<MqttTopicSubscription> ackTopics = doVerify(clientID, username, msg);
|
||||
MqttSubAckMessage ackMessage = doAckMessageFromValidateFilters(ackTopics, messageID);
|
||||
if (!this.subscriptionInCourse.replace(executionKey, SubscriptionState.VERIFIED, SubscriptionState.STORED)) {
|
||||
LOG.warn("Client sent another SUBSCRIBE message while the topic filters were being verified " +
|
||||
"CId={}, messageId={}", clientID, messageID);
|
||||
LOG.warn("Client sent another SUBSCRIBE message while the topic filters were being verified CId={}, " +
|
||||
"messageId={}", clientID, messageID);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -861,19 +860,18 @@ public class ProtocolProcessor {
|
||||
Topic topic = new Topic(req.topicName());
|
||||
if (!m_authorizator.canRead(topic, username, clientSession.clientID)) {
|
||||
// send SUBACK with 0x80, the user hasn't credentials to read the topic
|
||||
LOG.error("Client does not have read permissions on the topic " +
|
||||
"CId={}, username={}, messageId={}, topic={}",
|
||||
clientID, username, messageId, topic);
|
||||
LOG.error("Client does not have read permissions on the topic CId={}, username={}, messageId={}, " +
|
||||
"topic={}", clientID, username, messageId, topic);
|
||||
ackTopics.add(new MqttTopicSubscription(topic.toString(), FAILURE));
|
||||
} else {
|
||||
MqttQoS qos;
|
||||
if (topic.isValid()) {
|
||||
LOG.info("Client will be subscribed to the topic " +
|
||||
"CId={}, username={}, messageId={}, topic={}", clientID, username, messageId, topic);
|
||||
LOG.info("Client will be subscribed to the topic CId={}, username={}, messageId={}, topic={}",
|
||||
clientID, username, messageId, topic);
|
||||
qos = req.qualityOfService();
|
||||
} else {
|
||||
LOG.error("Topic filter is not valid CId={}, username={}, messageId={}, topic={}",
|
||||
clientID, username, messageId, topic);
|
||||
LOG.error("Topic filter is not valid CId={}, username={}, messageId={}, topic={}", clientID,
|
||||
username, messageId, topic);
|
||||
qos = FAILURE;
|
||||
}
|
||||
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.subscriptions.Subscription;
|
||||
import io.moquette.spi.impl.subscriptions.SubscriptionsStore;
|
||||
import io.moquette.spi.persistence.MapDBPersistentStore;
|
||||
import io.moquette.spi.security.IAuthenticator;
|
||||
import io.moquette.spi.security.IAuthorizator;
|
||||
import org.slf4j.Logger;
|
||||
@@ -51,6 +50,7 @@ import java.util.List;
|
||||
public class ProtocolProcessorBootstrapper {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ProtocolProcessorBootstrapper.class);
|
||||
public static final String MAPDB_STORE_CLASS = "io.moquette.persistence.MemoryStorageService";
|
||||
|
||||
private SubscriptionsStore subscriptions;
|
||||
|
||||
@@ -83,7 +83,7 @@ public class ProtocolProcessorBootstrapper {
|
||||
* an implementation of the authorizator to be used, if null load that specified in
|
||||
* config and fallback on the default one (permit all).
|
||||
* @param server
|
||||
* the serber to init.
|
||||
* the server to init.
|
||||
* @return the processor created for the broker.
|
||||
*/
|
||||
public ProtocolProcessor init(IConfig props, List<? extends InterceptHandler> embeddedObservers,
|
||||
@@ -92,31 +92,21 @@ public class ProtocolProcessorBootstrapper {
|
||||
|
||||
IMessagesStore messagesStore;
|
||||
LOG.info("Initializing messages and sessions stores...");
|
||||
String storageClassName = props.getProperty(BrokerConstants.STORAGE_CLASS_NAME, "");
|
||||
if (storageClassName != null && !storageClassName.isEmpty()) {
|
||||
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();
|
||||
}
|
||||
};
|
||||
} 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();
|
||||
}
|
||||
};
|
||||
String storageClassName = props.getProperty(BrokerConstants.STORAGE_CLASS_NAME, MAPDB_STORE_CLASS);
|
||||
if (storageClassName == null || storageClassName.isEmpty()) {
|
||||
LOG.error("storage_class property not defined");
|
||||
throw new IllegalArgumentException("Can't start a valid persistence layer");
|
||||
}
|
||||
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...");
|
||||
|
||||
@@ -148,7 +138,7 @@ public class ProtocolProcessorBootstrapper {
|
||||
} else {
|
||||
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...");
|
||||
@@ -162,119 +152,78 @@ public class ProtocolProcessorBootstrapper {
|
||||
if (aclFilePath != null && !aclFilePath.isEmpty()) {
|
||||
authorizator = new DenyAllAuthorizator();
|
||||
try {
|
||||
LOG.info("Parsing ACL file. Path = {}.", aclFilePath);
|
||||
LOG.info("Parsing ACL file. Path = {}", aclFilePath);
|
||||
authorizator = ACLFileParser.parse(resourceLoader.loadResource(aclFilePath));
|
||||
} catch (ParseException pex) {
|
||||
LOG.error(
|
||||
"Unable to parse ACL file. Path = {}, cause = {}, errorMessage = {}.",
|
||||
aclFilePath,
|
||||
pex.getCause(),
|
||||
pex.getMessage());
|
||||
LOG.error("Unable to parse ACL file. path="+ aclFilePath, pex);
|
||||
}
|
||||
} else {
|
||||
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...");
|
||||
connectionDescriptors = new ConnectionDescriptorStore(m_sessionsStore);
|
||||
|
||||
LOG.info("Initializing MQTT protocol processor...");
|
||||
boolean allowAnonymous = Boolean
|
||||
.parseBoolean(props.getProperty(BrokerConstants.ALLOW_ANONYMOUS_PROPERTY_NAME, "true"));
|
||||
boolean allowZeroByteClientId = Boolean
|
||||
.parseBoolean(props.getProperty(BrokerConstants.ALLOW_ZERO_BYTE_CLIENT_ID_PROPERTY_NAME, "false"));
|
||||
m_processor.init(
|
||||
connectionDescriptors,
|
||||
subscriptions,
|
||||
messagesStore,
|
||||
m_sessionsStore,
|
||||
authenticator,
|
||||
allowAnonymous,
|
||||
allowZeroByteClientId,
|
||||
authorizator,
|
||||
m_interceptor,
|
||||
boolean allowAnonymous = Boolean.parseBoolean(props.getProperty(BrokerConstants.ALLOW_ANONYMOUS_PROPERTY_NAME, "true"));
|
||||
boolean allowZeroByteClientId = Boolean.parseBoolean(props.getProperty(BrokerConstants.ALLOW_ZERO_BYTE_CLIENT_ID_PROPERTY_NAME, "false"));
|
||||
m_processor.init(connectionDescriptors, subscriptions, messagesStore, m_sessionsStore, authenticator,
|
||||
allowAnonymous, allowZeroByteClientId, authorizator, m_interceptor,
|
||||
props.getProperty(BrokerConstants.PORT_PROPERTY_NAME));
|
||||
return m_processor;
|
||||
}
|
||||
|
||||
@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;
|
||||
try {
|
||||
LOG.info("Loading class. ClassName = {}, interfaceName = {}.", className, iface.getName());
|
||||
LOG.info("Loading class. ClassName={}, interfaceName={}", className, intrface.getName());
|
||||
Class<?> clazz = Class.forName(className);
|
||||
|
||||
// check if method getInstance exists
|
||||
Method method = clazz.getMethod("getInstance", new Class[]{});
|
||||
try {
|
||||
LOG.info(
|
||||
"Invoking getInstance() method. ClassName = {}, interfaceName = {}.",
|
||||
className,
|
||||
iface.getName());
|
||||
LOG.info("Invoking getInstance() method. ClassName = {}, interfaceName = {}.", className, intrface.getName());
|
||||
instance = (T) method.invoke(null, new Object[]{});
|
||||
} catch (IllegalArgumentException | InvocationTargetException | IllegalAccessException ex) {
|
||||
LOG.error(
|
||||
"Unable to invoke getInstance() method. "
|
||||
+ "ClassName = {}, interfaceName = {}, cause = {}, errorMessage = {}.",
|
||||
className,
|
||||
iface.getName(),
|
||||
ex.getCause(),
|
||||
ex.getMessage());
|
||||
LOG.error("Unable to invoke getInstance() method. ClassName = {}, interfaceName = {}, cause = {}, errorMessage = {}.",
|
||||
className, intrface.getName(), ex.getCause(), ex.getMessage());
|
||||
return null;
|
||||
}
|
||||
} catch (NoSuchMethodException nsmex) {
|
||||
try {
|
||||
// check if constructor with constructor arg class parameter
|
||||
// exists
|
||||
LOG.info(
|
||||
"Invoking constructor with {} argument. ClassName = {}, interfaceName = {}.",
|
||||
constructorArgClass.getName(),
|
||||
className,
|
||||
iface.getName());
|
||||
instance = this.getClass().getClassLoader().loadClass(className).asSubclass(iface)
|
||||
.getConstructor(constructorArgClass).newInstance(props);
|
||||
LOG.info("Invoking constructor with {} argument. ClassName = {}, interfaceName = {}.",
|
||||
constructorArgClass.getName(), className, intrface.getName());
|
||||
instance = this.getClass().getClassLoader()
|
||||
.loadClass(className)
|
||||
.asSubclass(intrface)
|
||||
.getConstructor(constructorArgClass)
|
||||
.newInstance(props);
|
||||
} catch (InstantiationException | IllegalAccessException | ClassNotFoundException ex) {
|
||||
LOG.warn(
|
||||
"Unable to invoke constructor with {} argument. "
|
||||
+ "ClassName = {}, interfaceName = {}, cause = {}, errorMessage = {}.",
|
||||
constructorArgClass.getName(),
|
||||
className,
|
||||
iface.getName(),
|
||||
ex.getCause(),
|
||||
ex.getMessage());
|
||||
LOG.warn("Unable to invoke constructor with {} argument. ClassName = {}, interfaceName = {}, cause = {}, errorMessage = {}.",
|
||||
constructorArgClass.getName(), className, intrface.getName(), ex.getCause(), ex.getMessage());
|
||||
return null;
|
||||
} catch (NoSuchMethodException | InvocationTargetException e) {
|
||||
try {
|
||||
LOG.info(
|
||||
"Invoking default constructor. ClassName = {}, interfaceName = {}.",
|
||||
className,
|
||||
iface.getName());
|
||||
LOG.info("Invoking default constructor. ClassName = {}, interfaceName = {}.", className, intrface.getName());
|
||||
// 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) {
|
||||
LOG.error(
|
||||
"Unable to invoke default constructor. "
|
||||
+ "ClassName = {}, interfaceName = {}, cause = {}, errorMessage = {}.",
|
||||
className,
|
||||
iface.getName(),
|
||||
ex.getCause(),
|
||||
ex.getMessage());
|
||||
LOG.error("Unable to invoke default constructor. ClassName = {}, interfaceName = {}, cause = {}, errorMessage = {}.",
|
||||
className, intrface.getName(), ex.getCause(), ex.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
} 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;
|
||||
} catch (SecurityException ex) {
|
||||
LOG.error(
|
||||
"Unable to load class due to a security violation. "
|
||||
+ "ClassName = {}, interfaceName = {}, cause = {}, errorMessage = {}.",
|
||||
className,
|
||||
iface.getName(),
|
||||
ex.getCause(),
|
||||
ex.getMessage());
|
||||
LOG.error("Unable to load class due to a security violation. ClassName = {}, interfaceName = {}, cause = {}, errorMessage = {}.",
|
||||
className, intrface.getName(), ex.getCause(), ex.getMessage());
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -47,20 +47,12 @@ class Qos0PublishHandler extends QosPublishHandler {
|
||||
final int messageID = msg.variableHeader().messageId();
|
||||
|
||||
if (LOG.isTraceEnabled()) {
|
||||
LOG.trace(
|
||||
"Sending publish message to subscribers. "
|
||||
+ "MqttClientId = {}, topic = {}, messageId = {}, payload = {}, subscriptionTree = {}.",
|
||||
clientID,
|
||||
topic,
|
||||
messageID,
|
||||
DebugUtils.payload2Str(toStoreMsg.getMessage()),
|
||||
subscriptions.dumpTree());
|
||||
LOG.trace("Sending publish message to subscribers. ClientId={}, topic={}, messageId={}, payload={}, " +
|
||||
"subscriptionTree={}", clientID, topic, messageID, DebugUtils.payload2Str(toStoreMsg.getMessage()),
|
||||
subscriptions.dumpTree());
|
||||
} else {
|
||||
LOG.info(
|
||||
"Sending publish message to subscribers. MqttClientId = {}, topic = {}, messageId = {}.",
|
||||
clientID,
|
||||
topic,
|
||||
messageID);
|
||||
LOG.info("Sending publish message to subscribers. ClientId={}, topic={}, messageId={}", clientID, topic,
|
||||
messageID);
|
||||
}
|
||||
|
||||
List<Subscription> topicMatchingSubscriptions = subscriptions.matches(topic);
|
||||
|
||||
@@ -57,37 +57,26 @@ class Qos1PublishHandler extends QosPublishHandler {
|
||||
toStoreMsg.setClientID(clientID);
|
||||
|
||||
if (LOG.isTraceEnabled()) {
|
||||
LOG.trace(
|
||||
"Sending publish message to subscribers. "
|
||||
+ "MqttClientId = {}, topic = {}, messageId = {}, payload = {}, subscriptionTree = {}.",
|
||||
clientID,
|
||||
topic,
|
||||
messageID,
|
||||
DebugUtils.payload2Str(toStoreMsg.getMessage()),
|
||||
subscriptions.dumpTree());
|
||||
LOG.trace("Sending publish message to subscribers. ClientId={}, topic={}, messageId={}, payload={}, " +
|
||||
"subscriptionTree={}", clientID, topic, messageID, DebugUtils.payload2Str(toStoreMsg.getMessage()),
|
||||
subscriptions.dumpTree());
|
||||
} else {
|
||||
LOG.info(
|
||||
"Sending publish message to subscribers. MqttClientId = {}, topic = {}, messageId = {}.",
|
||||
clientID,
|
||||
topic,
|
||||
messageID);
|
||||
LOG.info("Sending publish message to subscribers. ClientId={}, topic={}, messageId={}", clientID, topic,
|
||||
messageID);
|
||||
}
|
||||
|
||||
List<Subscription> topicMatchingSubscriptions = subscriptions.matches(topic);
|
||||
this.publisher.publish2Subscribers(toStoreMsg, topicMatchingSubscriptions);
|
||||
|
||||
// send PUBACK
|
||||
// TODO Don't send PUBREC for Hz publish notification, if (msg.isLocal()) {
|
||||
sendPubAck(clientID, messageID);
|
||||
// }
|
||||
|
||||
if (msg.fixedHeader().isRetain()) {
|
||||
if (!msg.payload().isReadable()) {
|
||||
m_messagesStore.cleanRetained(topic);
|
||||
} else {
|
||||
// before wasn't stored
|
||||
MessageGUID guid = m_messagesStore.storePublishForFuture(toStoreMsg);
|
||||
m_messagesStore.storeRetained(topic, guid);
|
||||
//MessageGUID guid = m_messagesStore.storePublishForFuture(toStoreMsg);
|
||||
m_messagesStore.storeRetained(topic, toStoreMsg.getGuid());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,17 +91,13 @@ class Qos1PublishHandler extends QosPublishHandler {
|
||||
|
||||
try {
|
||||
if (connectionDescriptors == null) {
|
||||
throw new RuntimeException(
|
||||
"Internal bad error, found connectionDescriptors to null while it should be initialized, "
|
||||
+ "somewhere it's overwritten!!");
|
||||
throw new RuntimeException("Internal bad error, found connectionDescriptors to null while it should " +
|
||||
"be initialized, somewhere it's overwritten!!");
|
||||
}
|
||||
LOG.debug("clientIDs are {}", connectionDescriptors);
|
||||
if (!connectionDescriptors.isConnected(clientId)) {
|
||||
throw new RuntimeException(
|
||||
String.format(
|
||||
"Can't find a ConnectionDescriptor for client %s in cache %s",
|
||||
clientId,
|
||||
connectionDescriptors));
|
||||
throw new RuntimeException(String.format("Can't find a ConnectionDescriptor for client %s in cache %s",
|
||||
clientId,connectionDescriptors));
|
||||
}
|
||||
connectionDescriptors.sendMessage(pubAckMessage, messageID, clientId);
|
||||
} catch (Throwable t) {
|
||||
|
||||
@@ -58,27 +58,19 @@ class Qos2PublishHandler extends QosPublishHandler {
|
||||
toStoreMsg.setClientID(clientID);
|
||||
|
||||
if (LOG.isTraceEnabled()) {
|
||||
LOG.trace(
|
||||
"Sending publish message to subscribers. MqttClientId = {}, topic = {}, messageId = {}, "
|
||||
+ "payload = {}, subscriptionTree = {}.",
|
||||
clientID,
|
||||
topic,
|
||||
messageID,
|
||||
DebugUtils.payload2Str(toStoreMsg.getMessage()),
|
||||
subscriptions.dumpTree());
|
||||
LOG.trace( "Sending publish message to subscribers. ClientId={}, topic={}, messageId={}, payload={}, " +
|
||||
"subscriptionTree={}", clientID, topic, messageID, DebugUtils.payload2Str(toStoreMsg.getMessage()),
|
||||
subscriptions.dumpTree());
|
||||
} else {
|
||||
LOG.info(
|
||||
"Sending publish message to subscribers. MqttClientId = {}, topic = {}, messageId = {}.",
|
||||
clientID,
|
||||
topic,
|
||||
messageID);
|
||||
LOG.info("Sending publish message to subscribers. ClientId={}, topic={}, messageId={}", clientID, topic,
|
||||
messageID);
|
||||
}
|
||||
|
||||
// QoS2
|
||||
MessageGUID guid = m_messagesStore.storePublishForFuture(toStoreMsg);
|
||||
// TODO Don't send PUBREC for Hz publish notification, if (msg.isLocal()) {
|
||||
sendPubRec(clientID, messageID);
|
||||
// }
|
||||
|
||||
// Next the client will send us a pub rel
|
||||
// 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) {
|
||||
String clientID = NettyUtils.clientID(channel);
|
||||
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);
|
||||
IMessagesStore.StoredMessage evt = targetSession.storedMessage(messageID);
|
||||
final Topic topic = new Topic(evt.getTopic());
|
||||
List<Subscription> topicMatchingSubscriptions = subscriptions.matches(topic);
|
||||
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug(
|
||||
"Sending publish message to subscribers. "
|
||||
+ "MqttClientId = {}, topic = {}, messageId = {}, payload = {}, subscriptionTree = {}.",
|
||||
clientID,
|
||||
topic,
|
||||
messageID,
|
||||
DebugUtils.payload2Str(evt.getMessage()),
|
||||
subscriptions.dumpTree());
|
||||
LOG.debug("Sending publish message to subscribers. ClientId={}, topic={}, messageId={}, payload={}, " +
|
||||
"subscriptionTree={}", clientID, topic, messageID, DebugUtils.payload2Str(evt.getMessage()),
|
||||
subscriptions.dumpTree());
|
||||
} else {
|
||||
LOG.info(
|
||||
"Sending publish message to subscribers. MqttClientId = {}, topic = {}, messageId = {}.",
|
||||
clientID,
|
||||
topic,
|
||||
messageID);
|
||||
LOG.info("Sending publish message to subscribers. ClientId={}, topic={}, messageId={}", clientID, topic,
|
||||
messageID);
|
||||
}
|
||||
this.publisher.publish2Subscribers(evt, topicMatchingSubscriptions);
|
||||
|
||||
@@ -136,14 +120,14 @@ class Qos2PublishHandler extends QosPublishHandler {
|
||||
}
|
||||
|
||||
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);
|
||||
MqttMessage pubRecMessage = new MqttMessage(fixedHeader, from(messageID));
|
||||
connectionDescriptors.sendMessage(pubRecMessage, messageID, clientID);
|
||||
}
|
||||
|
||||
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);
|
||||
MqttMessage pubCompMessage = new MqttMessage(fixedHeader, from(messageID));
|
||||
connectionDescriptors.sendMessage(pubCompMessage, messageID, clientID);
|
||||
|
||||
@@ -47,7 +47,7 @@ public class Topic implements Serializable {
|
||||
valid = true;
|
||||
} catch (ParseException e) {
|
||||
valid = false;
|
||||
LOG.error(e.toString(), e);
|
||||
LOG.error("Error parsing the topic: {}, message: {}", topic, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user