Refactored ProtocolProcessor.processSubscribe to use the ClientSession concept instead of directe access to map storage concepts
This commit is contained in:
@@ -15,22 +15,36 @@
|
||||
*/
|
||||
package org.eclipse.moquette.spi;
|
||||
|
||||
import org.eclipse.moquette.proto.messages.AbstractMessage;
|
||||
import org.eclipse.moquette.spi.impl.events.PublishEvent;
|
||||
import org.eclipse.moquette.spi.impl.subscriptions.Subscription;
|
||||
import org.eclipse.moquette.spi.impl.subscriptions.SubscriptionsStore;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author andrea
|
||||
*/
|
||||
public class ClientSession {
|
||||
|
||||
private final static Logger LOG = LoggerFactory.getLogger(ClientSession.class);
|
||||
|
||||
public final String clientID;
|
||||
|
||||
private final IMessagesStore messagesStore;
|
||||
|
||||
public ClientSession(String clientID, IMessagesStore messagesStore) {
|
||||
private final ISessionsStore m_sessionsStore;
|
||||
|
||||
private Set<Subscription> subscriptions = new HashSet<>();
|
||||
|
||||
public ClientSession(String clientID, IMessagesStore messagesStore, ISessionsStore sessionsStore) {
|
||||
this.clientID = clientID;
|
||||
this.messagesStore = messagesStore;
|
||||
this.m_sessionsStore = sessionsStore;
|
||||
}
|
||||
//List of client's subscriptions
|
||||
//list of messages not acknowledged by client
|
||||
@@ -56,4 +70,18 @@ public class ClientSession {
|
||||
"clientID='" + clientID + '\'' +
|
||||
'}';
|
||||
}
|
||||
|
||||
public boolean subscribe(String topicFilter, Subscription newSubscription) {
|
||||
LOG.info("<{}> subscribed to topicFilter <{}> with QoS {}",
|
||||
newSubscription.getClientId(), topicFilter,
|
||||
AbstractMessage.QOSType.formatQoS(newSubscription.getRequestedQos()));
|
||||
boolean validTopic = SubscriptionsStore.validate(newSubscription.getTopicFilter());
|
||||
if (!validTopic) {
|
||||
//send SUBACK with 0x80 for this topic filter
|
||||
return false;
|
||||
}
|
||||
subscriptions.add(newSubscription);
|
||||
m_sessionsStore.addNewSubscription(newSubscription);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -465,9 +465,8 @@ public class ProtocolProcessor {
|
||||
if (m_clientIDs.get(clientId) == null) {
|
||||
throw new RuntimeException(String.format("Can't find a ConnectionDescriptor for client %s in cache %s", clientId, m_clientIDs));
|
||||
}
|
||||
// LOG.debug("Session for clientId " + clientId + " is " + m_clientIDs.get(clientId).getSession());
|
||||
m_clientIDs.get(clientId).getSession().write(pubAckMessage);
|
||||
}catch(Throwable t) {
|
||||
} catch(Throwable t) {
|
||||
LOG.error(null, t);
|
||||
}
|
||||
}
|
||||
@@ -531,7 +530,6 @@ public class ProtocolProcessor {
|
||||
//cleanup topic subscriptions
|
||||
cleanSession(clientID);
|
||||
}
|
||||
// m_notifier.disconnect(evt.getSession());
|
||||
m_clientIDs.remove(clientID);
|
||||
session.close(true);
|
||||
|
||||
@@ -596,35 +594,36 @@ public class ProtocolProcessor {
|
||||
boolean cleanSession = (Boolean) session.getAttribute(NettyChannel.ATTR_KEY_CLEANSESSION);
|
||||
LOG.debug("SUBSCRIBE client <{}> packetID {}", clientID, msg.getMessageID());
|
||||
|
||||
ClientSession clientSession = m_sessionsStore.sessionForClient(clientID);
|
||||
//ack the client
|
||||
SubAckMessage ackMessage = new SubAckMessage();
|
||||
ackMessage.setMessageID(msg.getMessageID());
|
||||
|
||||
List<Subscription> newSubscriptions = new ArrayList<>();
|
||||
for (SubscribeMessage.Couple req : msg.subscriptions()) {
|
||||
AbstractMessage.QOSType qos = AbstractMessage.QOSType.valueOf(req.getQos());
|
||||
Subscription newSubscription = new Subscription(clientID, req.getTopicFilter(), qos, cleanSession);
|
||||
boolean valid = subscribeSingleTopic(newSubscription, req.getTopicFilter());
|
||||
//boolean valid = subscribeSingleTopic(newSubscription, req.getTopicFilter());
|
||||
boolean valid = clientSession.subscribe(req.getTopicFilter(), newSubscription);
|
||||
ackMessage.addType(valid ? qos : AbstractMessage.QOSType.FAILURE);
|
||||
if (valid) {
|
||||
newSubscriptions.add(newSubscription);
|
||||
}
|
||||
}
|
||||
|
||||
//save session, persist subscriptions from session
|
||||
LOG.debug("SUBACK for packetID {}", msg.getMessageID());
|
||||
session.write(ackMessage);
|
||||
|
||||
//fire the publish
|
||||
for(Subscription subscription : newSubscriptions) {
|
||||
subscribeSingleTopic(subscription, subscription.getTopicFilter());
|
||||
}
|
||||
}
|
||||
|
||||
private boolean subscribeSingleTopic(Subscription newSubscription, final String topic) {
|
||||
LOG.info("<{}> subscribed to topic <{}> with QoS {}",
|
||||
newSubscription.getClientId(), topic,
|
||||
AbstractMessage.QOSType.formatQoS(newSubscription.getRequestedQos()));
|
||||
boolean validTopic = SubscriptionsStore.validate(newSubscription.getTopicFilter());
|
||||
if (!validTopic) {
|
||||
//send SUBACK with 0x80 for this topic filter
|
||||
return false;
|
||||
}
|
||||
subscriptions.add(newSubscription);
|
||||
|
||||
//notify the Observables
|
||||
m_interceptor.notifyTopicSubscribed(newSubscription);
|
||||
|
||||
//scans retained messages to be published to the new subscription
|
||||
Collection<IMessagesStore.StoredMessage> messages = m_messagesStore.searchMatching(new IMatchingCondition() {
|
||||
public boolean match(String key) {
|
||||
@@ -640,6 +639,9 @@ public class ProtocolProcessor {
|
||||
m_messagesStore.nextPacketID(newSubscription.getClientId());
|
||||
sendPublish(newSubscription.getClientId(), storedMsg.getTopic(), storedMsg.getQos(), storedMsg.getPayload(), true, packetID);
|
||||
}
|
||||
|
||||
//notify the Observables
|
||||
m_interceptor.notifyTopicSubscribed(newSubscription);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
+8
-1
@@ -20,6 +20,7 @@ import java.util.*;
|
||||
import java.util.concurrent.LinkedBlockingDeque;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.eclipse.moquette.spi.ClientSession;
|
||||
import org.eclipse.moquette.spi.ISessionsStore;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -163,8 +164,14 @@ public class SubscriptionsStore {
|
||||
return new NodeCouple(newRoot, current);
|
||||
}
|
||||
|
||||
// public void save(ClientSession clientSession) {
|
||||
// for(Subscription subscription : clientSession.getSubscriptions()) {
|
||||
// add(subscription);
|
||||
// }
|
||||
// }
|
||||
|
||||
public void add(Subscription newSubscription) {
|
||||
m_sessionsStore.addNewSubscription(newSubscription);
|
||||
// m_sessionsStore.addNewSubscription(newSubscription);
|
||||
addDirect(newSubscription);
|
||||
}
|
||||
|
||||
|
||||
@@ -309,7 +309,7 @@ public class MapDBPersistentStore implements IMessagesStore, ISessionsStore {
|
||||
}
|
||||
LOG.debug("clientID {} is a newcome, creating it's empty subscriptions set", clientID);
|
||||
m_persistentSubscriptions.put(clientID, new HashSet<Subscription>());
|
||||
return new ClientSession(clientID, this);
|
||||
return new ClientSession(clientID, this, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -318,7 +318,7 @@ public class MapDBPersistentStore implements IMessagesStore, ISessionsStore {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new ClientSession(clientID, this);
|
||||
return new ClientSession(clientID, this, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -98,7 +98,7 @@ public class MemorySessionStore implements ISessionsStore {
|
||||
}
|
||||
LOG.debug("clientID {} is a newcome, creating it's empty subscriptions set", clientID);
|
||||
m_persistentSubscriptions.put(clientID, new HashSet<Subscription>());
|
||||
return new ClientSession(clientID, m_messagesStore);
|
||||
return new ClientSession(clientID, m_messagesStore, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -107,12 +107,12 @@ public class MemorySessionStore implements ISessionsStore {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new ClientSession(clientID, m_messagesStore);
|
||||
return new ClientSession(clientID, m_messagesStore, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Subscription> listAllSubscriptions() {
|
||||
List<Subscription> allSubscriptions = new ArrayList<Subscription>();
|
||||
List<Subscription> allSubscriptions = new ArrayList<>();
|
||||
for (Map.Entry<String, Set<Subscription>> entry : m_persistentSubscriptions.entrySet()) {
|
||||
allSubscriptions.addAll(entry.getValue());
|
||||
}
|
||||
|
||||
@@ -207,6 +207,7 @@ public class ProtocolProcessorTest {
|
||||
msg.addSubscription(new SubscribeMessage.Couple(AbstractMessage.QOSType.MOST_ONE.byteValue(), FAKE_TOPIC));
|
||||
m_session.setAttribute(NettyChannel.ATTR_KEY_CLIENTID, FAKE_CLIENT_ID);
|
||||
m_session.setAttribute(NettyChannel.ATTR_KEY_CLEANSESSION, false);
|
||||
m_sessionStore.createNewSession(FAKE_CLIENT_ID);
|
||||
m_processor.processSubscribe(m_session, msg/*, FAKE_CLIENT_ID, false*/);
|
||||
|
||||
//Verify
|
||||
@@ -221,6 +222,7 @@ public class ProtocolProcessorTest {
|
||||
msg.addSubscription(new SubscribeMessage.Couple(AbstractMessage.QOSType.MOST_ONE.byteValue(), FAKE_TOPIC));
|
||||
m_session.setAttribute(NettyChannel.ATTR_KEY_CLIENTID, FAKE_CLIENT_ID);
|
||||
m_session.setAttribute(NettyChannel.ATTR_KEY_CLEANSESSION, false);
|
||||
m_sessionStore.createNewSession(FAKE_CLIENT_ID);
|
||||
assertEquals(0, subscriptions.size());
|
||||
|
||||
m_processor.processSubscribe(m_session, msg);
|
||||
@@ -241,6 +243,7 @@ public class ProtocolProcessorTest {
|
||||
msg.addSubscription(new SubscribeMessage.Couple(AbstractMessage.QOSType.MOST_ONE.byteValue(), BAD_FORMATTED_TOPIC));
|
||||
m_session.setAttribute(NettyChannel.ATTR_KEY_CLIENTID, FAKE_CLIENT_ID);
|
||||
m_session.setAttribute(NettyChannel.ATTR_KEY_CLEANSESSION, false);
|
||||
m_sessionStore.createNewSession(FAKE_CLIENT_ID);
|
||||
assertEquals(0, subscriptions.size());
|
||||
|
||||
//Exercise
|
||||
|
||||
+9
-3
@@ -238,7 +238,9 @@ public class SubscriptionsStoreTest {
|
||||
|
||||
private void assertMatch(String subscription, String topic) {
|
||||
store = new SubscriptionsStore();
|
||||
store.init(new MemoryStorageService());
|
||||
MemoryStorageService memStore = new MemoryStorageService();
|
||||
memStore.initStore();
|
||||
store.init(memStore);
|
||||
Subscription sub = new Subscription("FAKE_CLI_ID_1", subscription, AbstractMessage.QOSType.MOST_ONE, false);
|
||||
store.add(sub);
|
||||
assertFalse(store.matches(topic).isEmpty());
|
||||
@@ -246,7 +248,9 @@ public class SubscriptionsStoreTest {
|
||||
|
||||
private void assertNotMatch(String subscription, String topic) {
|
||||
store = new SubscriptionsStore();
|
||||
store.init(new MemoryStorageService());
|
||||
MemoryStorageService memStore = new MemoryStorageService();
|
||||
memStore.initStore();
|
||||
store.init(memStore);
|
||||
Subscription sub = new Subscription("FAKE_CLI_ID_1", subscription, AbstractMessage.QOSType.MOST_ONE, false);
|
||||
store.add(sub);
|
||||
assertTrue(store.matches(topic).isEmpty());
|
||||
@@ -345,7 +349,9 @@ public class SubscriptionsStoreTest {
|
||||
@Test
|
||||
public void removeSubscription_withDifferentClients_subscribedSameTopic() {
|
||||
SubscriptionsStore aStore = new SubscriptionsStore();
|
||||
aStore.init(new MemoryStorageService());
|
||||
MemoryStorageService memStore = new MemoryStorageService();
|
||||
memStore.initStore();
|
||||
aStore.init(memStore);
|
||||
//subscribe a not active clientID1 to /topic
|
||||
Subscription slashSub = new Subscription("FAKE_CLI_ID_1", "/topic", AbstractMessage.QOSType.MOST_ONE, false);
|
||||
aStore.add(slashSub);
|
||||
|
||||
Reference in New Issue
Block a user