Added implementation of retained store in H2

This commit is contained in:
andsel
2018-11-28 23:05:24 +01:00
parent 80daadff47
commit 213851335a
9 changed files with 131 additions and 19 deletions
@@ -18,6 +18,8 @@ package io.moquette.broker;
import io.moquette.broker.subscriptions.Topic;
import io.netty.handler.codec.mqtt.MqttPublishMessage;
import java.util.List;
public interface IRetainedRepository {
void cleanRetained(Topic topic);
@@ -26,5 +28,5 @@ public interface IRetainedRepository {
boolean isEmpty();
MqttPublishMessage retainedOnTopic(String topic);
List<RetainedMessage> retainedOnTopic(String topic);
}
@@ -16,8 +16,12 @@
package io.moquette.broker;
import io.moquette.broker.subscriptions.Topic;
import io.netty.buffer.ByteBuf;
import io.netty.handler.codec.mqtt.MqttPublishMessage;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
@@ -26,16 +30,20 @@ import java.util.concurrent.ConcurrentMap;
* */
final class MemoryRetainedRepository implements IRetainedRepository {
private final ConcurrentMap<String, MqttPublishMessage> storage = new ConcurrentHashMap<>();
private final ConcurrentMap<Topic, RetainedMessage> storage = new ConcurrentHashMap<>();
@Override
public void cleanRetained(Topic topic) {
storage.remove(topic.toString());
storage.remove(topic);
}
@Override
public void retain(Topic topic, MqttPublishMessage msg) {
storage.put(topic.toString(), msg);
final ByteBuf payload = msg.content();
byte[] rawPayload = new byte[payload.readableBytes()];
payload.getBytes(0, rawPayload);
final RetainedMessage toStore = new RetainedMessage(msg.fixedHeader().qosLevel(), rawPayload);
storage.put(topic, toStore);
}
@Override
@@ -44,7 +52,15 @@ final class MemoryRetainedRepository implements IRetainedRepository {
}
@Override
public MqttPublishMessage retainedOnTopic(String topic) {
return storage.get(topic);
public List<RetainedMessage> retainedOnTopic(String topic) {
final Topic searchTopic = new Topic(topic);
final List<RetainedMessage> matchingMessages = new ArrayList<>();
for (Map.Entry<Topic, RetainedMessage> entry : storage.entrySet()) {
final Topic scanTopic = entry.getKey();
if (searchTopic.match(scanTopic)) {
matchingMessages.add(entry.getValue());
}
}
return matchingMessages;
}
}
@@ -21,6 +21,7 @@ import io.moquette.broker.subscriptions.Subscription;
import io.moquette.broker.subscriptions.Topic;
import io.moquette.broker.security.IAuthorizatorPolicy;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.handler.codec.mqtt.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -99,19 +100,21 @@ class PostOffice {
Session targetSession = this.sessionRegistry.retrieve(clientID);
for (Subscription subscription : newSubscriptions) {
final String topicFilter = subscription.getTopicFilter().toString();
final MqttPublishMessage retainedMsg = retainedRepository.retainedOnTopic(topicFilter);
final List<RetainedMessage> retainedMsgs = retainedRepository.retainedOnTopic(topicFilter);
if (retainedMsg == null) {
if (retainedMsgs.isEmpty()) {
// not found
continue;
}
for (RetainedMessage retainedMsg : retainedMsgs) {
final MqttQoS retainedQos = retainedMsg.qosLevel();
MqttQoS qos = lowerQosToTheSubscriptionDesired(subscription, retainedQos);
final MqttQoS retainedQos = retainedMsg.fixedHeader().qosLevel();
MqttQoS qos = lowerQosToTheSubscriptionDesired(subscription, retainedQos);
final ByteBuf origPayload = retainedMsg.payload();
ByteBuf payload = origPayload.retainedDuplicate();
targetSession.sendRetainedPublishOnSessionAtQos(subscription.getTopicFilter(), qos, payload);
// final ByteBuf origPayload = retainedMsg.getPayload();
final ByteBuf payloadBuf = Unpooled.wrappedBuffer(retainedMsg.getPayload());
// ByteBuf payload = origPayload.retainedDuplicate();
targetSession.sendRetainedPublishOnSessionAtQos(subscription.getTopicFilter(), qos, payloadBuf);
}
}
}
@@ -0,0 +1,24 @@
package io.moquette.broker;
import io.netty.handler.codec.mqtt.MqttQoS;
import java.io.Serializable;
public class RetainedMessage implements Serializable{
private final MqttQoS qos;
private final byte[] payload;
public RetainedMessage(MqttQoS qos, byte[] payload) {
this.qos = qos;
this.payload = payload;
}
public MqttQoS qosLevel() {
return qos;
}
public byte[] getPayload() {
return payload;
}
}
@@ -162,21 +162,24 @@ public class Server {
final ISubscriptionsRepository subscriptionsRepository;
final IQueueRepository queueRepository;
final IRetainedRepository retainedRepository;
if (persistencePath != null && !persistencePath.isEmpty()) {
LOG.trace("Configuring H2 subscriptions store to {}", persistencePath);
h2Builder = new H2Builder(config, scheduler).initStore();
subscriptionsRepository = h2Builder.subscriptionsRepository();
queueRepository = h2Builder.queueRepository();
retainedRepository = h2Builder.retainedRepository();
} else {
LOG.trace("Configuring in-memory subscriptions store");
subscriptionsRepository = new MemorySubscriptionsRepository();
queueRepository = new MemoryQueueRepository();
retainedRepository = new MemoryRetainedRepository();
}
ISubscriptionsDirectory subscriptions = new CTrieSubscriptionDirectory();
subscriptions.init(subscriptionsRepository);
SessionRegistry sessions = new SessionRegistry(subscriptions, queueRepository);
dispatcher = new PostOffice(subscriptions, authorizatorPolicy, new MemoryRetainedRepository(), sessions,
dispatcher = new PostOffice(subscriptions, authorizatorPolicy, retainedRepository, sessions,
interceptor);
final BrokerConfiguration brokerConfig = new BrokerConfiguration(config);
MQTTConnectionFactory connectionFactory = new MQTTConnectionFactory(brokerConfig, authenticator, sessions,
@@ -2,6 +2,7 @@ package io.moquette.persistence;
import io.moquette.BrokerConstants;
import io.moquette.broker.IQueueRepository;
import io.moquette.broker.IRetainedRepository;
import io.moquette.broker.ISubscriptionsRepository;
import io.moquette.broker.config.IConfig;
import org.h2.mvstore.MVStore;
@@ -57,4 +58,8 @@ public class H2Builder {
public IQueueRepository queueRepository() {
return new H2QueueRepository(mvStore);
}
public IRetainedRepository retainedRepository() {
return new H2RetainedRepository(mvStore);
}
}
@@ -0,0 +1,55 @@
package io.moquette.persistence;
import io.moquette.broker.IRetainedRepository;
import io.moquette.broker.RetainedMessage;
import io.moquette.broker.subscriptions.Topic;
import io.netty.buffer.ByteBuf;
import io.netty.handler.codec.mqtt.MqttPublishMessage;
import org.h2.mvstore.MVMap;
import org.h2.mvstore.MVStore;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class H2RetainedRepository implements IRetainedRepository {
private final MVMap<Topic, RetainedMessage> queueMap;
public H2RetainedRepository(MVStore mvStore) {
this.queueMap = mvStore.openMap("retained_store");
}
@Override
public void cleanRetained(Topic topic) {
queueMap.remove(topic);
}
@Override
public void retain(Topic topic, MqttPublishMessage msg) {
final ByteBuf payload = msg.content();
byte[] rawPayload = new byte[payload.readableBytes()];
payload.getBytes(0, rawPayload);
final RetainedMessage toStore = new RetainedMessage(msg.fixedHeader().qosLevel(), rawPayload);
queueMap.put(topic, toStore);
}
@Override
public boolean isEmpty() {
return queueMap.isEmpty();
}
@Override
public List<RetainedMessage> retainedOnTopic(String topic) {
final Topic searchTopic = new Topic(topic);
final List<RetainedMessage> matchingMessages = new ArrayList<>();
for (Map.Entry<Topic, RetainedMessage> entry : queueMap.entrySet()) {
final Topic scanTopic = entry.getKey();
if (searchTopic.match(scanTopic)) {
matchingMessages.add(entry.getValue());
}
}
return matchingMessages;
}
}