Minor fixed comment (#281)
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* 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
|
||||
|
||||
@@ -1,66 +1,66 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* ------------------------------------------------------
|
||||
* 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.interception;
|
||||
|
||||
import io.moquette.interception.messages.InterceptAcknowledgedMessage;
|
||||
import io.moquette.interception.messages.InterceptConnectMessage;
|
||||
import io.moquette.interception.messages.InterceptConnectionLostMessage;
|
||||
import io.moquette.interception.messages.InterceptDisconnectMessage;
|
||||
import io.moquette.interception.messages.InterceptPublishMessage;
|
||||
import io.moquette.interception.messages.InterceptSubscribeMessage;
|
||||
import io.moquette.interception.messages.InterceptUnsubscribeMessage;
|
||||
|
||||
/**
|
||||
* Basic abstract class usefull to avoid empty methods creation in subclasses.
|
||||
*
|
||||
* Created by andrea on 08/12/15.
|
||||
*/
|
||||
public abstract class AbstractInterceptHandler implements InterceptHandler {
|
||||
|
||||
@Override
|
||||
public Class<?>[] getInterceptedMessageTypes() {
|
||||
return InterceptHandler.ALL_MESSAGE_TYPES;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnect(InterceptConnectMessage msg) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisconnect(InterceptDisconnectMessage msg) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnectionLost(InterceptConnectionLostMessage msg) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPublish(InterceptPublishMessage msg) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSubscribe(InterceptSubscribeMessage msg) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUnsubscribe(InterceptUnsubscribeMessage msg) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMessageAcknowledged(InterceptAcknowledgedMessage msg) {
|
||||
}
|
||||
}
|
||||
/*
|
||||
* 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.interception;
|
||||
|
||||
import io.moquette.interception.messages.InterceptAcknowledgedMessage;
|
||||
import io.moquette.interception.messages.InterceptConnectMessage;
|
||||
import io.moquette.interception.messages.InterceptConnectionLostMessage;
|
||||
import io.moquette.interception.messages.InterceptDisconnectMessage;
|
||||
import io.moquette.interception.messages.InterceptPublishMessage;
|
||||
import io.moquette.interception.messages.InterceptSubscribeMessage;
|
||||
import io.moquette.interception.messages.InterceptUnsubscribeMessage;
|
||||
|
||||
/**
|
||||
* Basic abstract class usefull to avoid empty methods creation in subclasses.
|
||||
*
|
||||
* Created by andrea on 08/12/15.
|
||||
*/
|
||||
public abstract class AbstractInterceptHandler implements InterceptHandler {
|
||||
|
||||
@Override
|
||||
public Class<?>[] getInterceptedMessageTypes() {
|
||||
return InterceptHandler.ALL_MESSAGE_TYPES;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnect(InterceptConnectMessage msg) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisconnect(InterceptDisconnectMessage msg) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnectionLost(InterceptConnectionLostMessage msg) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPublish(InterceptPublishMessage msg) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSubscribe(InterceptSubscribeMessage msg) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUnsubscribe(InterceptUnsubscribeMessage msg) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMessageAcknowledged(InterceptAcknowledgedMessage msg) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,68 +1,68 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* ------------------------------------------------------
|
||||
* 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.interception;
|
||||
|
||||
import io.moquette.interception.messages.*;
|
||||
import io.moquette.spi.impl.subscriptions.Subscription;
|
||||
import io.netty.handler.codec.mqtt.MqttMessage;
|
||||
|
||||
/**
|
||||
* This interface is used to inject code for intercepting broker events.
|
||||
* <p>
|
||||
* The events can act only as observers.
|
||||
* <p>
|
||||
* Almost every method receives a subclass of {@link MqttMessage}, except <code>onDisconnect</code>
|
||||
* that receives the client id string and <code>onSubscribe</code> and <code>onUnsubscribe</code>
|
||||
* that receive a {@link Subscription} object.
|
||||
*
|
||||
* @author Wagner Macedo
|
||||
*/
|
||||
public interface InterceptHandler {
|
||||
|
||||
Class<?>[] ALL_MESSAGE_TYPES = {InterceptConnectMessage.class, InterceptDisconnectMessage.class,
|
||||
InterceptConnectionLostMessage.class, InterceptPublishMessage.class, InterceptSubscribeMessage.class,
|
||||
InterceptUnsubscribeMessage.class, InterceptAcknowledgedMessage.class};
|
||||
|
||||
/**
|
||||
* Returns the identifier of this intercept handler.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
String getID();
|
||||
|
||||
/**
|
||||
* Returns the InterceptMessage subtypes that this handler can process. If the result is null or
|
||||
* equal to ALL_MESSAGE_TYPES, all the message types will be processed.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Class<?>[] getInterceptedMessageTypes();
|
||||
|
||||
void onConnect(InterceptConnectMessage msg);
|
||||
|
||||
void onDisconnect(InterceptDisconnectMessage msg);
|
||||
|
||||
void onConnectionLost(InterceptConnectionLostMessage msg);
|
||||
|
||||
void onPublish(InterceptPublishMessage msg);
|
||||
|
||||
void onSubscribe(InterceptSubscribeMessage msg);
|
||||
|
||||
void onUnsubscribe(InterceptUnsubscribeMessage msg);
|
||||
|
||||
void onMessageAcknowledged(InterceptAcknowledgedMessage msg);
|
||||
}
|
||||
/*
|
||||
* 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.interception;
|
||||
|
||||
import io.moquette.interception.messages.*;
|
||||
import io.moquette.spi.impl.subscriptions.Subscription;
|
||||
import io.netty.handler.codec.mqtt.MqttMessage;
|
||||
|
||||
/**
|
||||
* This interface is used to inject code for intercepting broker events.
|
||||
* <p>
|
||||
* The events can act only as observers.
|
||||
* <p>
|
||||
* Almost every method receives a subclass of {@link MqttMessage}, except <code>onDisconnect</code>
|
||||
* that receives the client id string and <code>onSubscribe</code> and <code>onUnsubscribe</code>
|
||||
* that receive a {@link Subscription} object.
|
||||
*
|
||||
* @author Wagner Macedo
|
||||
*/
|
||||
public interface InterceptHandler {
|
||||
|
||||
Class<?>[] ALL_MESSAGE_TYPES = {InterceptConnectMessage.class, InterceptDisconnectMessage.class,
|
||||
InterceptConnectionLostMessage.class, InterceptPublishMessage.class, InterceptSubscribeMessage.class,
|
||||
InterceptUnsubscribeMessage.class, InterceptAcknowledgedMessage.class};
|
||||
|
||||
/**
|
||||
* Returns the identifier of this intercept handler.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
String getID();
|
||||
|
||||
/**
|
||||
* Returns the InterceptMessage subtypes that this handler can process. If the result is null or
|
||||
* equal to ALL_MESSAGE_TYPES, all the message types will be processed.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Class<?>[] getInterceptedMessageTypes();
|
||||
|
||||
void onConnect(InterceptConnectMessage msg);
|
||||
|
||||
void onDisconnect(InterceptDisconnectMessage msg);
|
||||
|
||||
void onConnectionLost(InterceptConnectionLostMessage msg);
|
||||
|
||||
void onPublish(InterceptPublishMessage msg);
|
||||
|
||||
void onSubscribe(InterceptSubscribeMessage msg);
|
||||
|
||||
void onUnsubscribe(InterceptUnsubscribeMessage msg);
|
||||
|
||||
void onMessageAcknowledged(InterceptAcknowledgedMessage msg);
|
||||
}
|
||||
|
||||
@@ -1,54 +1,54 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* ------------------------------------------------------
|
||||
* 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.interception;
|
||||
|
||||
import io.moquette.interception.messages.InterceptAcknowledgedMessage;
|
||||
import io.moquette.spi.impl.subscriptions.Subscription;
|
||||
import io.netty.handler.codec.mqtt.MqttConnectMessage;
|
||||
import io.netty.handler.codec.mqtt.MqttPublishMessage;
|
||||
|
||||
/**
|
||||
* This interface is to be used internally by the broker components.
|
||||
* <p>
|
||||
* An interface is used instead of a class to allow more flexibility in changing an implementation.
|
||||
* <p>
|
||||
* Interceptor implementations forward notifications to a <code>InterceptHandler</code>, that is
|
||||
* normally a field. So, the implementations should act as a proxy to a custom intercept handler.
|
||||
*
|
||||
* @see InterceptHandler
|
||||
* @author Wagner Macedo
|
||||
*/
|
||||
public interface Interceptor {
|
||||
|
||||
void notifyClientConnected(MqttConnectMessage msg);
|
||||
|
||||
void notifyClientDisconnected(String clientID, String username);
|
||||
|
||||
void notifyClientConnectionLost(String clientID, String username);
|
||||
|
||||
void notifyTopicPublished(MqttPublishMessage msg, String clientID, final String username);
|
||||
|
||||
void notifyTopicSubscribed(Subscription sub, final String username);
|
||||
|
||||
void notifyTopicUnsubscribed(String topic, String clientID, final String username);
|
||||
|
||||
void notifyMessageAcknowledged(InterceptAcknowledgedMessage msg);
|
||||
|
||||
void addInterceptHandler(InterceptHandler interceptHandler);
|
||||
|
||||
void removeInterceptHandler(InterceptHandler interceptHandler);
|
||||
}
|
||||
/*
|
||||
* 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.interception;
|
||||
|
||||
import io.moquette.interception.messages.InterceptAcknowledgedMessage;
|
||||
import io.moquette.spi.impl.subscriptions.Subscription;
|
||||
import io.netty.handler.codec.mqtt.MqttConnectMessage;
|
||||
import io.netty.handler.codec.mqtt.MqttPublishMessage;
|
||||
|
||||
/**
|
||||
* This interface is to be used internally by the broker components.
|
||||
* <p>
|
||||
* An interface is used instead of a class to allow more flexibility in changing an implementation.
|
||||
* <p>
|
||||
* Interceptor implementations forward notifications to a <code>InterceptHandler</code>, that is
|
||||
* normally a field. So, the implementations should act as a proxy to a custom intercept handler.
|
||||
*
|
||||
* @see InterceptHandler
|
||||
* @author Wagner Macedo
|
||||
*/
|
||||
public interface Interceptor {
|
||||
|
||||
void notifyClientConnected(MqttConnectMessage msg);
|
||||
|
||||
void notifyClientDisconnected(String clientID, String username);
|
||||
|
||||
void notifyClientConnectionLost(String clientID, String username);
|
||||
|
||||
void notifyTopicPublished(MqttPublishMessage msg, String clientID, final String username);
|
||||
|
||||
void notifyTopicSubscribed(Subscription sub, final String username);
|
||||
|
||||
void notifyTopicUnsubscribed(String topic, String clientID, final String username);
|
||||
|
||||
void notifyMessageAcknowledged(InterceptAcknowledgedMessage msg);
|
||||
|
||||
void addInterceptHandler(InterceptHandler interceptHandler);
|
||||
|
||||
void removeInterceptHandler(InterceptHandler interceptHandler);
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* 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
|
||||
|
||||
@@ -1,213 +1,213 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* ------------------------------------------------------
|
||||
* 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.spi;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import io.moquette.spi.IMessagesStore.StoredMessage;
|
||||
import io.moquette.spi.impl.subscriptions.Subscription;
|
||||
import io.moquette.spi.impl.subscriptions.Topic;
|
||||
|
||||
/**
|
||||
* Store used to handle the persistence of the subscriptions tree.
|
||||
*
|
||||
* @author andrea
|
||||
*/
|
||||
public interface ISessionsStore {
|
||||
|
||||
class ClientTopicCouple {
|
||||
|
||||
public final Topic topicFilter;
|
||||
public final String clientID;
|
||||
|
||||
public ClientTopicCouple(String clientID, Topic topicFilter) {
|
||||
this.clientID = clientID;
|
||||
this.topicFilter = topicFilter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o)
|
||||
return true;
|
||||
if (o == null || getClass() != o.getClass())
|
||||
return false;
|
||||
|
||||
ClientTopicCouple that = (ClientTopicCouple) o;
|
||||
|
||||
if (topicFilter != null ? !topicFilter.equals(that.topicFilter) : that.topicFilter != null)
|
||||
return false;
|
||||
return !(clientID != null ? !clientID.equals(that.clientID) : that.clientID != null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = topicFilter != null ? topicFilter.hashCode() : 0;
|
||||
result = 31 * result + (clientID != null ? clientID.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ClientTopicCouple{" + "topicFilter='" + topicFilter + '\'' + ", clientID='" + clientID + '\'' + '}';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void initStore();
|
||||
|
||||
void updateCleanStatus(String clientID, boolean cleanSession);
|
||||
|
||||
/**
|
||||
* Add a new subscription to the session
|
||||
*
|
||||
* @param newSubscription
|
||||
* the subscription to add.
|
||||
*/
|
||||
void addNewSubscription(Subscription newSubscription);
|
||||
|
||||
/**
|
||||
* Removed a specific subscription
|
||||
*
|
||||
* @param topic
|
||||
* the topic of the subscription.
|
||||
* @param clientID
|
||||
* the session client.
|
||||
*/
|
||||
void removeSubscription(Topic topic, String clientID);
|
||||
|
||||
/**
|
||||
* Remove all the subscriptions of the session
|
||||
*
|
||||
* @param sessionID
|
||||
* the client ID
|
||||
*/
|
||||
void wipeSubscriptions(String sessionID);
|
||||
|
||||
/**
|
||||
* Return all topic filters to recreate the subscription tree.
|
||||
*/
|
||||
List<ClientTopicCouple> listAllSubscriptions();
|
||||
|
||||
/**
|
||||
* @param couple
|
||||
* the subscription descriptor.
|
||||
* @return the subscription stored by clientID and topicFilter, if any else null;
|
||||
*/
|
||||
Subscription getSubscription(ClientTopicCouple couple);
|
||||
|
||||
/*
|
||||
* @return all subscriptions stored.
|
||||
*/
|
||||
List<Subscription> getSubscriptions();
|
||||
|
||||
/**
|
||||
* @param clientID
|
||||
* the session client ID.
|
||||
* @return true iff there are subscriptions persisted with clientID
|
||||
*/
|
||||
boolean contains(String clientID);
|
||||
|
||||
ClientSession createNewSession(String clientID, boolean cleanSession);
|
||||
|
||||
/**
|
||||
* @param clientID
|
||||
* the client owning the session.
|
||||
* @return the session for the given clientID, null if not found.
|
||||
*/
|
||||
ClientSession sessionForClient(String clientID);
|
||||
|
||||
/**
|
||||
* Returns all the sessions
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Collection<ClientSession> getAllSessions();
|
||||
|
||||
void inFlightAck(String clientID, int messageID);
|
||||
|
||||
/**
|
||||
* Save the binding messageID, clientID - guid
|
||||
*
|
||||
* @param clientID
|
||||
* the client ID
|
||||
* @param messageID
|
||||
* the message ID
|
||||
* @param guid
|
||||
* the uuid of the message to mark as inflight.
|
||||
*/
|
||||
void inFlight(String clientID, int messageID, MessageGUID guid);
|
||||
|
||||
/**
|
||||
* Return the next valid packetIdentifier for the given client session.
|
||||
*
|
||||
* @param clientID
|
||||
* the clientID requesting next packet id.
|
||||
* @return the next valid id.
|
||||
*/
|
||||
int nextPacketID(String clientID);
|
||||
|
||||
/**
|
||||
* List the guids for retained messages for the session
|
||||
*
|
||||
* @param clientID
|
||||
* the client ID owning the queue.
|
||||
* @return the list of queue message UUIDs.
|
||||
*/
|
||||
BlockingQueue<StoredMessage> queue(String clientID);
|
||||
|
||||
void dropQueue(String clientID);
|
||||
|
||||
void moveInFlightToSecondPhaseAckWaiting(String clientID, int messageID);
|
||||
|
||||
/**
|
||||
* @param clientID
|
||||
* the client ID accessing the second phase.
|
||||
* @param messageID
|
||||
* the message ID that reached the second phase.
|
||||
* @return the guid of message just acked.
|
||||
*/
|
||||
MessageGUID secondPhaseAcknowledged(String clientID, int messageID);
|
||||
|
||||
MessageGUID mapToGuid(String clientID, int messageID);
|
||||
|
||||
StoredMessage getInflightMessage(String clientID, int messageID);
|
||||
|
||||
/**
|
||||
* Returns the number of inflight messages for the given client ID
|
||||
*
|
||||
* @param clientID
|
||||
* @return
|
||||
*/
|
||||
int getInflightMessagesNo(String clientID);
|
||||
|
||||
/**
|
||||
* Returns the size of the session queue for the given client ID
|
||||
*
|
||||
* @param clientID
|
||||
* @return
|
||||
*/
|
||||
int getPendingPublishMessagesNo(String clientID);
|
||||
|
||||
/**
|
||||
* Returns the number of second-phase ACK pending messages for the given client ID
|
||||
*
|
||||
* @param clientID
|
||||
* @return
|
||||
*/
|
||||
int getSecondPhaseAckPendingMessages(String clientID);
|
||||
}
|
||||
/*
|
||||
* 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.spi;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import io.moquette.spi.IMessagesStore.StoredMessage;
|
||||
import io.moquette.spi.impl.subscriptions.Subscription;
|
||||
import io.moquette.spi.impl.subscriptions.Topic;
|
||||
|
||||
/**
|
||||
* Store used to handle the persistence of the subscriptions tree.
|
||||
*
|
||||
* @author andrea
|
||||
*/
|
||||
public interface ISessionsStore {
|
||||
|
||||
class ClientTopicCouple {
|
||||
|
||||
public final Topic topicFilter;
|
||||
public final String clientID;
|
||||
|
||||
public ClientTopicCouple(String clientID, Topic topicFilter) {
|
||||
this.clientID = clientID;
|
||||
this.topicFilter = topicFilter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o)
|
||||
return true;
|
||||
if (o == null || getClass() != o.getClass())
|
||||
return false;
|
||||
|
||||
ClientTopicCouple that = (ClientTopicCouple) o;
|
||||
|
||||
if (topicFilter != null ? !topicFilter.equals(that.topicFilter) : that.topicFilter != null)
|
||||
return false;
|
||||
return !(clientID != null ? !clientID.equals(that.clientID) : that.clientID != null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = topicFilter != null ? topicFilter.hashCode() : 0;
|
||||
result = 31 * result + (clientID != null ? clientID.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ClientTopicCouple{" + "topicFilter='" + topicFilter + '\'' + ", clientID='" + clientID + '\'' + '}';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void initStore();
|
||||
|
||||
void updateCleanStatus(String clientID, boolean cleanSession);
|
||||
|
||||
/**
|
||||
* Add a new subscription to the session
|
||||
*
|
||||
* @param newSubscription
|
||||
* the subscription to add.
|
||||
*/
|
||||
void addNewSubscription(Subscription newSubscription);
|
||||
|
||||
/**
|
||||
* Removed a specific subscription
|
||||
*
|
||||
* @param topic
|
||||
* the topic of the subscription.
|
||||
* @param clientID
|
||||
* the session client.
|
||||
*/
|
||||
void removeSubscription(Topic topic, String clientID);
|
||||
|
||||
/**
|
||||
* Remove all the subscriptions of the session
|
||||
*
|
||||
* @param sessionID
|
||||
* the client ID
|
||||
*/
|
||||
void wipeSubscriptions(String sessionID);
|
||||
|
||||
/**
|
||||
* Return all topic filters to recreate the subscription tree.
|
||||
*/
|
||||
List<ClientTopicCouple> listAllSubscriptions();
|
||||
|
||||
/**
|
||||
* @param couple
|
||||
* the subscription descriptor.
|
||||
* @return the subscription stored by clientID and topicFilter, if any else null;
|
||||
*/
|
||||
Subscription getSubscription(ClientTopicCouple couple);
|
||||
|
||||
/*
|
||||
* @return all subscriptions stored.
|
||||
*/
|
||||
List<Subscription> getSubscriptions();
|
||||
|
||||
/**
|
||||
* @param clientID
|
||||
* the session client ID.
|
||||
* @return true iff there are subscriptions persisted with clientID
|
||||
*/
|
||||
boolean contains(String clientID);
|
||||
|
||||
ClientSession createNewSession(String clientID, boolean cleanSession);
|
||||
|
||||
/**
|
||||
* @param clientID
|
||||
* the client owning the session.
|
||||
* @return the session for the given clientID, null if not found.
|
||||
*/
|
||||
ClientSession sessionForClient(String clientID);
|
||||
|
||||
/**
|
||||
* Returns all the sessions
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Collection<ClientSession> getAllSessions();
|
||||
|
||||
void inFlightAck(String clientID, int messageID);
|
||||
|
||||
/**
|
||||
* Save the binding messageID, clientID - guid
|
||||
*
|
||||
* @param clientID
|
||||
* the client ID
|
||||
* @param messageID
|
||||
* the message ID
|
||||
* @param guid
|
||||
* the uuid of the message to mark as inflight.
|
||||
*/
|
||||
void inFlight(String clientID, int messageID, MessageGUID guid);
|
||||
|
||||
/**
|
||||
* Return the next valid packetIdentifier for the given client session.
|
||||
*
|
||||
* @param clientID
|
||||
* the clientID requesting next packet id.
|
||||
* @return the next valid id.
|
||||
*/
|
||||
int nextPacketID(String clientID);
|
||||
|
||||
/**
|
||||
* List the guids for retained messages for the session
|
||||
*
|
||||
* @param clientID
|
||||
* the client ID owning the queue.
|
||||
* @return the list of queue message UUIDs.
|
||||
*/
|
||||
BlockingQueue<StoredMessage> queue(String clientID);
|
||||
|
||||
void dropQueue(String clientID);
|
||||
|
||||
void moveInFlightToSecondPhaseAckWaiting(String clientID, int messageID);
|
||||
|
||||
/**
|
||||
* @param clientID
|
||||
* the client ID accessing the second phase.
|
||||
* @param messageID
|
||||
* the message ID that reached the second phase.
|
||||
* @return the guid of message just acked.
|
||||
*/
|
||||
MessageGUID secondPhaseAcknowledged(String clientID, int messageID);
|
||||
|
||||
MessageGUID mapToGuid(String clientID, int messageID);
|
||||
|
||||
StoredMessage getInflightMessage(String clientID, int messageID);
|
||||
|
||||
/**
|
||||
* Returns the number of inflight messages for the given client ID
|
||||
*
|
||||
* @param clientID
|
||||
* @return
|
||||
*/
|
||||
int getInflightMessagesNo(String clientID);
|
||||
|
||||
/**
|
||||
* Returns the size of the session queue for the given client ID
|
||||
*
|
||||
* @param clientID
|
||||
* @return
|
||||
*/
|
||||
int getPendingPublishMessagesNo(String clientID);
|
||||
|
||||
/**
|
||||
* Returns the number of second-phase ACK pending messages for the given client ID
|
||||
*
|
||||
* @param clientID
|
||||
* @return
|
||||
*/
|
||||
int getSecondPhaseAckPendingMessages(String clientID);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* 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
|
||||
|
||||
@@ -1,260 +1,260 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* ------------------------------------------------------
|
||||
* 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.spi.impl;
|
||||
|
||||
import io.moquette.BrokerConstants;
|
||||
import io.moquette.interception.InterceptHandler;
|
||||
import io.moquette.interception.Interceptor;
|
||||
import io.moquette.interception.messages.*;
|
||||
import io.moquette.server.config.IConfig;
|
||||
import io.moquette.spi.impl.subscriptions.Subscription;
|
||||
import io.netty.handler.codec.mqtt.MqttConnectMessage;
|
||||
import io.netty.handler.codec.mqtt.MqttPublishMessage;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import static io.moquette.logging.LoggingUtils.getInterceptorIds;
|
||||
|
||||
/**
|
||||
* An interceptor that execute the interception tasks asynchronously.
|
||||
*
|
||||
* @author Wagner Macedo
|
||||
*/
|
||||
final class BrokerInterceptor implements Interceptor {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(BrokerInterceptor.class);
|
||||
private final Map<Class<?>, List<InterceptHandler>> handlers;
|
||||
private final ExecutorService executor;
|
||||
|
||||
private BrokerInterceptor(int poolSize, List<InterceptHandler> handlers) {
|
||||
LOG.info("Initializing broker interceptor. InterceptorIds = {}.", getInterceptorIds(handlers));
|
||||
this.handlers = new HashMap<>();
|
||||
for (Class<?> messageType : InterceptHandler.ALL_MESSAGE_TYPES) {
|
||||
this.handlers.put(messageType, new CopyOnWriteArrayList<InterceptHandler>());
|
||||
}
|
||||
for (InterceptHandler handler : handlers) {
|
||||
this.addInterceptHandler(handler);
|
||||
}
|
||||
executor = Executors.newFixedThreadPool(poolSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures a broker interceptor, with a thread pool of one thread.
|
||||
*
|
||||
* @param handlers
|
||||
*/
|
||||
public BrokerInterceptor(List<InterceptHandler> handlers) {
|
||||
this(1, handlers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures a broker interceptor using the pool size specified in the IConfig argument.
|
||||
*/
|
||||
public BrokerInterceptor(IConfig props, List<InterceptHandler> handlers) {
|
||||
this(Integer.parseInt(props.getProperty(BrokerConstants.BROKER_INTERCEPTOR_THREAD_POOL_SIZE, "1")), handlers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdown graciously the executor service
|
||||
*/
|
||||
void stop() {
|
||||
LOG.info("Shutting down interceptor thread pool...");
|
||||
executor.shutdown();
|
||||
try {
|
||||
LOG.info("Waiting for thread pool tasks to terminate...");
|
||||
executor.awaitTermination(10L, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
if (!executor.isTerminated()) {
|
||||
LOG.warn("Forcing shutdown of interceptor thread pool...");
|
||||
executor.shutdownNow();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyClientConnected(final MqttConnectMessage msg) {
|
||||
for (final InterceptHandler handler : this.handlers.get(InterceptConnectMessage.class)) {
|
||||
LOG.debug(
|
||||
"Sending MQTT CONNECT message to interceptor. MqttClientId = {}, interceptorId = {}.",
|
||||
msg.payload().clientIdentifier(),
|
||||
handler.getID());
|
||||
executor.execute(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
handler.onConnect(new InterceptConnectMessage(msg));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyClientDisconnected(final String clientID, final String username) {
|
||||
for (final InterceptHandler handler : this.handlers.get(InterceptDisconnectMessage.class)) {
|
||||
LOG.debug(
|
||||
"Notifying MQTT client disconnection to interceptor. MqttClientId = {}, username = {}, "
|
||||
+ "interceptorId = {}.",
|
||||
clientID,
|
||||
username,
|
||||
handler.getID());
|
||||
executor.execute(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
handler.onDisconnect(new InterceptDisconnectMessage(clientID, username));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyClientConnectionLost(final String clientID, final String username) {
|
||||
for (final InterceptHandler handler : this.handlers.get(InterceptConnectionLostMessage.class)) {
|
||||
LOG.debug(
|
||||
"Notifying unexpected MQTT client disconnection to interceptor. MqttClientId = {}, "
|
||||
+ "username = {}, interceptorId = {}.",
|
||||
clientID,
|
||||
username,
|
||||
handler.getID());
|
||||
executor.execute(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
handler.onConnectionLost(new InterceptConnectionLostMessage(clientID, username));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyTopicPublished(final MqttPublishMessage msg, final String clientID, final String username) {
|
||||
int messageId = msg.variableHeader().messageId();
|
||||
String topic = msg.variableHeader().topicName();
|
||||
for (final InterceptHandler handler : this.handlers.get(InterceptPublishMessage.class)) {
|
||||
LOG.debug(
|
||||
"Notifying MQTT PUBLISH message to interceptor. MqttClientId = {}, messageId = {}, topic = {}, "
|
||||
+ "interceptorId = {}.",
|
||||
clientID,
|
||||
messageId,
|
||||
topic,
|
||||
handler.getID());
|
||||
executor.execute(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
handler.onPublish(new InterceptPublishMessage(msg, clientID, username));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyTopicSubscribed(final Subscription sub, final String username) {
|
||||
for (final InterceptHandler handler : this.handlers.get(InterceptSubscribeMessage.class)) {
|
||||
LOG.debug(
|
||||
"Notifying MQTT SUBSCRIBE message to interceptor. MqttClientId = {}, topicFilter = {}, "
|
||||
+ "interceptorId = {}.",
|
||||
sub.getClientId(),
|
||||
sub.getTopicFilter(),
|
||||
handler.getID());
|
||||
executor.execute(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
handler.onSubscribe(new InterceptSubscribeMessage(sub, username));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyTopicUnsubscribed(final String topic, final String clientID, final String username) {
|
||||
for (final InterceptHandler handler : this.handlers.get(InterceptUnsubscribeMessage.class)) {
|
||||
LOG.debug(
|
||||
"Notifying MQTT UNSUBSCRIBE message to interceptor. MqttClientId = {}, topic = {}, "
|
||||
+ "interceptorId = {}.",
|
||||
clientID,
|
||||
topic,
|
||||
handler.getID());
|
||||
executor.execute(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
handler.onUnsubscribe(new InterceptUnsubscribeMessage(topic, clientID, username));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyMessageAcknowledged(final InterceptAcknowledgedMessage msg) {
|
||||
for (final InterceptHandler handler : this.handlers.get(InterceptAcknowledgedMessage.class)) {
|
||||
LOG.debug(
|
||||
"Notifying MQTT ACK message to interceptor. MqttClientId = {}, messageId = {}, topic = {}, "
|
||||
+ "interceptorId = {}.",
|
||||
msg.getMsg().getClientID(),
|
||||
msg.getMsg().getMessageID(),
|
||||
msg.getTopic(),
|
||||
handler.getID());
|
||||
executor.execute(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
handler.onMessageAcknowledged(msg);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addInterceptHandler(InterceptHandler interceptHandler) {
|
||||
Class<?>[] interceptedMessageTypes = getInterceptedMessageTypes(interceptHandler);
|
||||
LOG.info(
|
||||
"Adding MQTT message interceptor. InterceptorId = {}, handledMessageTypes = {}.",
|
||||
interceptHandler.getID(),
|
||||
interceptedMessageTypes);
|
||||
for (Class<?> interceptMessageType : interceptedMessageTypes) {
|
||||
this.handlers.get(interceptMessageType).add(interceptHandler);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeInterceptHandler(InterceptHandler interceptHandler) {
|
||||
Class<?>[] interceptedMessageTypes = getInterceptedMessageTypes(interceptHandler);
|
||||
LOG.info(
|
||||
"Removing MQTT message interceptor. InterceptorId = {}, handledMessageTypes = {}.",
|
||||
interceptHandler.getID(),
|
||||
interceptedMessageTypes);
|
||||
for (Class<?> interceptMessageType : interceptedMessageTypes) {
|
||||
this.handlers.get(interceptMessageType).remove(interceptHandler);
|
||||
}
|
||||
}
|
||||
|
||||
private static Class<?>[] getInterceptedMessageTypes(InterceptHandler interceptHandler) {
|
||||
Class<?>[] interceptedMessageTypes = interceptHandler.getInterceptedMessageTypes();
|
||||
if (interceptedMessageTypes == null) {
|
||||
return InterceptHandler.ALL_MESSAGE_TYPES;
|
||||
}
|
||||
return interceptedMessageTypes;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* 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.spi.impl;
|
||||
|
||||
import io.moquette.BrokerConstants;
|
||||
import io.moquette.interception.InterceptHandler;
|
||||
import io.moquette.interception.Interceptor;
|
||||
import io.moquette.interception.messages.*;
|
||||
import io.moquette.server.config.IConfig;
|
||||
import io.moquette.spi.impl.subscriptions.Subscription;
|
||||
import io.netty.handler.codec.mqtt.MqttConnectMessage;
|
||||
import io.netty.handler.codec.mqtt.MqttPublishMessage;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import static io.moquette.logging.LoggingUtils.getInterceptorIds;
|
||||
|
||||
/**
|
||||
* An interceptor that execute the interception tasks asynchronously.
|
||||
*
|
||||
* @author Wagner Macedo
|
||||
*/
|
||||
final class BrokerInterceptor implements Interceptor {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(BrokerInterceptor.class);
|
||||
private final Map<Class<?>, List<InterceptHandler>> handlers;
|
||||
private final ExecutorService executor;
|
||||
|
||||
private BrokerInterceptor(int poolSize, List<InterceptHandler> handlers) {
|
||||
LOG.info("Initializing broker interceptor. InterceptorIds = {}.", getInterceptorIds(handlers));
|
||||
this.handlers = new HashMap<>();
|
||||
for (Class<?> messageType : InterceptHandler.ALL_MESSAGE_TYPES) {
|
||||
this.handlers.put(messageType, new CopyOnWriteArrayList<InterceptHandler>());
|
||||
}
|
||||
for (InterceptHandler handler : handlers) {
|
||||
this.addInterceptHandler(handler);
|
||||
}
|
||||
executor = Executors.newFixedThreadPool(poolSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures a broker interceptor, with a thread pool of one thread.
|
||||
*
|
||||
* @param handlers
|
||||
*/
|
||||
public BrokerInterceptor(List<InterceptHandler> handlers) {
|
||||
this(1, handlers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures a broker interceptor using the pool size specified in the IConfig argument.
|
||||
*/
|
||||
public BrokerInterceptor(IConfig props, List<InterceptHandler> handlers) {
|
||||
this(Integer.parseInt(props.getProperty(BrokerConstants.BROKER_INTERCEPTOR_THREAD_POOL_SIZE, "1")), handlers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdown graciously the executor service
|
||||
*/
|
||||
void stop() {
|
||||
LOG.info("Shutting down interceptor thread pool...");
|
||||
executor.shutdown();
|
||||
try {
|
||||
LOG.info("Waiting for thread pool tasks to terminate...");
|
||||
executor.awaitTermination(10L, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
if (!executor.isTerminated()) {
|
||||
LOG.warn("Forcing shutdown of interceptor thread pool...");
|
||||
executor.shutdownNow();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyClientConnected(final MqttConnectMessage msg) {
|
||||
for (final InterceptHandler handler : this.handlers.get(InterceptConnectMessage.class)) {
|
||||
LOG.debug(
|
||||
"Sending MQTT CONNECT message to interceptor. MqttClientId = {}, interceptorId = {}.",
|
||||
msg.payload().clientIdentifier(),
|
||||
handler.getID());
|
||||
executor.execute(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
handler.onConnect(new InterceptConnectMessage(msg));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyClientDisconnected(final String clientID, final String username) {
|
||||
for (final InterceptHandler handler : this.handlers.get(InterceptDisconnectMessage.class)) {
|
||||
LOG.debug(
|
||||
"Notifying MQTT client disconnection to interceptor. MqttClientId = {}, username = {}, "
|
||||
+ "interceptorId = {}.",
|
||||
clientID,
|
||||
username,
|
||||
handler.getID());
|
||||
executor.execute(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
handler.onDisconnect(new InterceptDisconnectMessage(clientID, username));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyClientConnectionLost(final String clientID, final String username) {
|
||||
for (final InterceptHandler handler : this.handlers.get(InterceptConnectionLostMessage.class)) {
|
||||
LOG.debug(
|
||||
"Notifying unexpected MQTT client disconnection to interceptor. MqttClientId = {}, "
|
||||
+ "username = {}, interceptorId = {}.",
|
||||
clientID,
|
||||
username,
|
||||
handler.getID());
|
||||
executor.execute(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
handler.onConnectionLost(new InterceptConnectionLostMessage(clientID, username));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyTopicPublished(final MqttPublishMessage msg, final String clientID, final String username) {
|
||||
int messageId = msg.variableHeader().messageId();
|
||||
String topic = msg.variableHeader().topicName();
|
||||
for (final InterceptHandler handler : this.handlers.get(InterceptPublishMessage.class)) {
|
||||
LOG.debug(
|
||||
"Notifying MQTT PUBLISH message to interceptor. MqttClientId = {}, messageId = {}, topic = {}, "
|
||||
+ "interceptorId = {}.",
|
||||
clientID,
|
||||
messageId,
|
||||
topic,
|
||||
handler.getID());
|
||||
executor.execute(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
handler.onPublish(new InterceptPublishMessage(msg, clientID, username));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyTopicSubscribed(final Subscription sub, final String username) {
|
||||
for (final InterceptHandler handler : this.handlers.get(InterceptSubscribeMessage.class)) {
|
||||
LOG.debug(
|
||||
"Notifying MQTT SUBSCRIBE message to interceptor. MqttClientId = {}, topicFilter = {}, "
|
||||
+ "interceptorId = {}.",
|
||||
sub.getClientId(),
|
||||
sub.getTopicFilter(),
|
||||
handler.getID());
|
||||
executor.execute(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
handler.onSubscribe(new InterceptSubscribeMessage(sub, username));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyTopicUnsubscribed(final String topic, final String clientID, final String username) {
|
||||
for (final InterceptHandler handler : this.handlers.get(InterceptUnsubscribeMessage.class)) {
|
||||
LOG.debug(
|
||||
"Notifying MQTT UNSUBSCRIBE message to interceptor. MqttClientId = {}, topic = {}, "
|
||||
+ "interceptorId = {}.",
|
||||
clientID,
|
||||
topic,
|
||||
handler.getID());
|
||||
executor.execute(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
handler.onUnsubscribe(new InterceptUnsubscribeMessage(topic, clientID, username));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyMessageAcknowledged(final InterceptAcknowledgedMessage msg) {
|
||||
for (final InterceptHandler handler : this.handlers.get(InterceptAcknowledgedMessage.class)) {
|
||||
LOG.debug(
|
||||
"Notifying MQTT ACK message to interceptor. MqttClientId = {}, messageId = {}, topic = {}, "
|
||||
+ "interceptorId = {}.",
|
||||
msg.getMsg().getClientID(),
|
||||
msg.getMsg().getMessageID(),
|
||||
msg.getTopic(),
|
||||
handler.getID());
|
||||
executor.execute(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
handler.onMessageAcknowledged(msg);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addInterceptHandler(InterceptHandler interceptHandler) {
|
||||
Class<?>[] interceptedMessageTypes = getInterceptedMessageTypes(interceptHandler);
|
||||
LOG.info(
|
||||
"Adding MQTT message interceptor. InterceptorId = {}, handledMessageTypes = {}.",
|
||||
interceptHandler.getID(),
|
||||
interceptedMessageTypes);
|
||||
for (Class<?> interceptMessageType : interceptedMessageTypes) {
|
||||
this.handlers.get(interceptMessageType).add(interceptHandler);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeInterceptHandler(InterceptHandler interceptHandler) {
|
||||
Class<?>[] interceptedMessageTypes = getInterceptedMessageTypes(interceptHandler);
|
||||
LOG.info(
|
||||
"Removing MQTT message interceptor. InterceptorId = {}, handledMessageTypes = {}.",
|
||||
interceptHandler.getID(),
|
||||
interceptedMessageTypes);
|
||||
for (Class<?> interceptMessageType : interceptedMessageTypes) {
|
||||
this.handlers.get(interceptMessageType).remove(interceptHandler);
|
||||
}
|
||||
}
|
||||
|
||||
private static Class<?>[] getInterceptedMessageTypes(InterceptHandler interceptHandler) {
|
||||
Class<?>[] interceptedMessageTypes = interceptHandler.getInterceptedMessageTypes();
|
||||
if (interceptedMessageTypes == null) {
|
||||
return InterceptHandler.ALL_MESSAGE_TYPES;
|
||||
}
|
||||
return interceptedMessageTypes;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* 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
|
||||
|
||||
@@ -1,348 +1,348 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* ------------------------------------------------------
|
||||
* 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.spi.persistence;
|
||||
|
||||
import io.moquette.spi.ClientSession;
|
||||
import io.moquette.spi.IMessagesStore.StoredMessage;
|
||||
import io.moquette.spi.ISessionsStore;
|
||||
import io.moquette.spi.MessageGUID;
|
||||
import io.moquette.spi.impl.Utils;
|
||||
import io.moquette.spi.impl.subscriptions.Subscription;
|
||||
import io.moquette.spi.impl.subscriptions.Topic;
|
||||
import io.moquette.spi.persistence.MapDBPersistentStore.PersistentSession;
|
||||
import org.mapdb.DB;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
/**
|
||||
* ISessionsStore implementation backed by MapDB.
|
||||
*
|
||||
* @author andrea
|
||||
*/
|
||||
class MapDBSessionsStore implements ISessionsStore {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(MapDBSessionsStore.class);
|
||||
|
||||
// maps clientID->[MessageId -> guid]
|
||||
private ConcurrentMap<String, ConcurrentMap<Integer, MessageGUID>> m_inflightStore;
|
||||
// map clientID <-> set of currently in flight packet identifiers
|
||||
private Map<String, Set<Integer>> m_inFlightIds;
|
||||
private ConcurrentMap<String, PersistentSession> m_persistentSessions;
|
||||
// maps clientID->[MessageId -> guid]
|
||||
private ConcurrentMap<String, Map<Integer, MessageGUID>> m_secondPhaseStore;
|
||||
|
||||
private final DB m_db;
|
||||
private final MapDBMessagesStore m_messagesStore;
|
||||
|
||||
MapDBSessionsStore(DB db, MapDBMessagesStore messagesStore) {
|
||||
m_db = db;
|
||||
m_messagesStore = messagesStore;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initStore() {
|
||||
m_inflightStore = m_db.getHashMap("inflight");
|
||||
m_inFlightIds = m_db.getHashMap("inflightPacketIDs");
|
||||
m_persistentSessions = m_db.getHashMap("sessions");
|
||||
m_secondPhaseStore = m_db.getHashMap("secondPhase");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addNewSubscription(Subscription newSubscription) {
|
||||
LOG.info(
|
||||
"Adding new subscription. CId= {}, topics = {}.",
|
||||
newSubscription.getClientId(),
|
||||
newSubscription.getTopicFilter());
|
||||
final String clientID = newSubscription.getClientId();
|
||||
m_db.getHashMap("subscriptions_" + clientID).put(newSubscription.getTopicFilter(), newSubscription);
|
||||
|
||||
if (LOG.isTraceEnabled()) {
|
||||
LOG.trace(
|
||||
"The subscription has been added. CId= {}, topics = {}, clientSubscriptions = {}.",
|
||||
newSubscription.getClientId(),
|
||||
newSubscription.getTopicFilter(),
|
||||
m_db.getHashMap("subscriptions_" + clientID));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeSubscription(Topic topicFilter, String clientID) {
|
||||
LOG.info("Removing subscription. CId= {}, topics = {}.", clientID, topicFilter);
|
||||
if (!m_db.exists("subscriptions_" + clientID)) {
|
||||
return;
|
||||
}
|
||||
m_db.getHashMap("subscriptions_" + clientID).remove(topicFilter);
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug(
|
||||
"The subscription has been removed. CId= {}, topics = {}, clientSubscriptions = {}.",
|
||||
clientID,
|
||||
topicFilter,
|
||||
m_db.getHashMap("subscriptions_" + clientID));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void wipeSubscriptions(String clientID) {
|
||||
LOG.info("Wiping subscriptions. CId= {}.", clientID);
|
||||
m_db.delete("subscriptions_" + clientID);
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug(
|
||||
"The subscriptions have been removed. CId= {}, clientSubscriptions = {}.",
|
||||
clientID,
|
||||
m_db.getHashMap("subscriptions_" + clientID));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ClientTopicCouple> listAllSubscriptions() {
|
||||
LOG.info("Retrieving existing subscriptions...");
|
||||
final List<ClientTopicCouple> allSubscriptions = new ArrayList<>();
|
||||
for (String clientID : m_persistentSessions.keySet()) {
|
||||
ConcurrentMap<Topic, Subscription> clientSubscriptions = m_db.getHashMap("subscriptions_" + clientID);
|
||||
for (Topic topicFilter : clientSubscriptions.keySet()) {
|
||||
allSubscriptions.add(new ClientTopicCouple(clientID, topicFilter));
|
||||
}
|
||||
}
|
||||
if (LOG.isTraceEnabled()) {
|
||||
LOG.trace("The existing subscriptions have been retrieved. Result = {}.", allSubscriptions);
|
||||
}
|
||||
return allSubscriptions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Subscription getSubscription(ClientTopicCouple couple) {
|
||||
ConcurrentMap<Topic, Subscription> clientSubscriptions = m_db.getHashMap("subscriptions_" + couple.clientID);
|
||||
LOG.info("Retrieving subscriptions. CId= {}, subscriptions = {}.", couple.clientID, clientSubscriptions);
|
||||
return clientSubscriptions.get(couple.topicFilter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Subscription> getSubscriptions() {
|
||||
LOG.info("Retrieving existing subscriptions...");
|
||||
List<Subscription> subscriptions = new ArrayList<>();
|
||||
for (String clientID : m_persistentSessions.keySet()) {
|
||||
ConcurrentMap<Topic, Subscription> clientSubscriptions = m_db.getHashMap("subscriptions_" + clientID);
|
||||
subscriptions.addAll(clientSubscriptions.values());
|
||||
}
|
||||
LOG.debug("The existing subscriptions have been retrieved. Result = {}.", subscriptions);
|
||||
return subscriptions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(String clientID) {
|
||||
return m_db.exists("subscriptions_" + clientID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientSession createNewSession(String clientID, boolean cleanSession) {
|
||||
if (m_persistentSessions.containsKey(clientID)) {
|
||||
LOG.error(
|
||||
"Unable to create a new session: the client ID is already in use. CId= {}, cleanSession = {}.",
|
||||
clientID,
|
||||
cleanSession);
|
||||
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);
|
||||
m_persistentSessions.putIfAbsent(clientID, new PersistentSession(cleanSession));
|
||||
return new ClientSession(clientID, m_messagesStore, this, cleanSession);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientSession sessionForClient(String clientID) {
|
||||
LOG.info("Retrieving session. CId= {}.", clientID);
|
||||
if (!m_persistentSessions.containsKey(clientID)) {
|
||||
LOG.warn("The session does not exist. CId= {}.", clientID);
|
||||
return null;
|
||||
}
|
||||
|
||||
PersistentSession storedSession = m_persistentSessions.get(clientID);
|
||||
return new ClientSession(clientID, m_messagesStore, this, storedSession.cleanSession);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ClientSession> getAllSessions() {
|
||||
Collection<ClientSession> result = new ArrayList<>();
|
||||
for (Map.Entry<String, PersistentSession> entry : m_persistentSessions.entrySet()) {
|
||||
result.add(new ClientSession(entry.getKey(), m_messagesStore, this, entry.getValue().cleanSession));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateCleanStatus(String clientID, boolean cleanSession) {
|
||||
LOG.info("Updating cleanSession flag. CId= {}, cleanSession = {}.", clientID, cleanSession);
|
||||
m_persistentSessions.put(clientID, new MapDBPersistentStore.PersistentSession(cleanSession));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the next valid packetIdentifier for the given client session.
|
||||
*/
|
||||
@Override
|
||||
public int nextPacketID(String clientID) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Generating next packet ID. CId= {}.", clientID);
|
||||
}
|
||||
Set<Integer> inFlightForClient = this.m_inFlightIds.get(clientID);
|
||||
if (inFlightForClient == null) {
|
||||
int nextPacketId = 1;
|
||||
inFlightForClient = Collections.newSetFromMap(new ConcurrentHashMap<Integer, Boolean>());
|
||||
inFlightForClient.add(nextPacketId);
|
||||
this.m_inFlightIds.put(clientID, inFlightForClient);
|
||||
return nextPacketId;
|
||||
|
||||
}
|
||||
|
||||
int maxId = inFlightForClient.isEmpty() ? 0 : Collections.max(inFlightForClient);
|
||||
int nextPacketId = (maxId + 1) % 0xFFFF;
|
||||
inFlightForClient.add(nextPacketId);
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("The next packet ID has been generated. CId= {}, result = {}.", clientID, nextPacketId);
|
||||
}
|
||||
return nextPacketId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void inFlightAck(String clientID, int messageID) {
|
||||
LOG.debug("Acknowledging inflight message. CId= {}, messageId = {}.", clientID, messageID);
|
||||
Map<Integer, MessageGUID> m = this.m_inflightStore.get(clientID);
|
||||
if (m == null) {
|
||||
LOG.warn("Unable to retrieve inflight message record. CId= {}, messageId = {}.", clientID, messageID);
|
||||
return;
|
||||
}
|
||||
m.remove(messageID);
|
||||
|
||||
// remove from the ids store
|
||||
Set<Integer> inFlightForClient = this.m_inFlightIds.get(clientID);
|
||||
if (inFlightForClient != null) {
|
||||
inFlightForClient.remove(messageID);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void inFlight(String clientID, int messageID, MessageGUID guid) {
|
||||
LOG.debug("Storing inflight message. CId= {}, messageId = {}, guid = {}.", clientID, messageID, guid);
|
||||
ConcurrentMap<Integer, MessageGUID> m = this.m_inflightStore.get(clientID);
|
||||
if (m == null) {
|
||||
m = new ConcurrentHashMap<>();
|
||||
}
|
||||
m.put(messageID, guid);
|
||||
LOG.info("storing inflight clientID <{}> messageID {} guid <{}>", clientID, messageID, guid);
|
||||
this.m_inflightStore.put(clientID, m);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockingQueue<StoredMessage> queue(String clientID) {
|
||||
LOG.info("Queuing pending message. CId= {}, guid = {}.", clientID);
|
||||
return this.m_db.getQueue(clientID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dropQueue(String clientID) {
|
||||
LOG.info("Removing pending messages. CId= {}.", clientID);
|
||||
this.m_db.delete(clientID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void moveInFlightToSecondPhaseAckWaiting(String clientID, int messageID) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Moving inflight message to 2nd phase ack state. CId= {}, messageID = {}.", clientID, messageID);
|
||||
}
|
||||
Map<Integer, MessageGUID> m = this.m_inflightStore.get(clientID);
|
||||
if (m == null) {
|
||||
LOG.warn("Unable to retrieve inflight message record. CId= {}, messageId = {}.", clientID, messageID);
|
||||
return;
|
||||
}
|
||||
MessageGUID guid = m.remove(messageID);
|
||||
|
||||
// remove from the ids store
|
||||
Set<Integer> inFlightForClient = this.m_inFlightIds.get(clientID);
|
||||
if (inFlightForClient != null) {
|
||||
inFlightForClient.remove(messageID);
|
||||
}
|
||||
|
||||
Map<Integer, MessageGUID> messageIDs = Utils
|
||||
.defaultGet(m_secondPhaseStore, clientID, new HashMap<Integer, MessageGUID>());
|
||||
messageIDs.put(messageID, guid);
|
||||
m_secondPhaseStore.put(clientID, messageIDs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageGUID secondPhaseAcknowledged(String clientID, int messageID) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Processing second phase ACK CId={}, messageId={}", clientID, messageID);
|
||||
}
|
||||
Map<Integer, MessageGUID> messageIDs = Utils
|
||||
.defaultGet(m_secondPhaseStore, clientID, new HashMap<Integer, MessageGUID>());
|
||||
MessageGUID guid = messageIDs.remove(messageID);
|
||||
m_secondPhaseStore.put(clientID, messageIDs);
|
||||
return guid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageGUID mapToGuid(String clientID, int messageID) {
|
||||
LOG.debug("Mapping message ID to GUID CId={}, messageId={}", clientID, messageID);
|
||||
ConcurrentMap<Integer, MessageGUID> messageIdToGuid = m_db.getHashMap(messageId2GuidsMapName(clientID));
|
||||
MessageGUID result = messageIdToGuid.get(messageID);
|
||||
LOG.debug("Message ID has been mapped to a GUID CId={}, messageId={}, guid={}", clientID, messageID, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
static String messageId2GuidsMapName(String clientID) {
|
||||
return "guidsMapping_" + clientID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StoredMessage getInflightMessage(String clientID, int messageID) {
|
||||
LOG.info("Retrieving inflight message CId={}, messageId={}", clientID, messageID);
|
||||
Map<Integer, MessageGUID> clientEntries = m_inflightStore.get(clientID);
|
||||
if (clientEntries == null) {
|
||||
LOG.warn("The client has no inflight messages CId={}, messageId={}", clientID, messageID);
|
||||
return null;
|
||||
}
|
||||
MessageGUID guid = clientEntries.get(messageID);
|
||||
if (guid == null) {
|
||||
LOG.warn("The message ID does not have an associated GUID. CId= {}, messageId = {}.", clientID, messageID);
|
||||
return null;
|
||||
}
|
||||
return m_messagesStore.getMessageByGuid(guid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInflightMessagesNo(String clientID) {
|
||||
if (!m_inflightStore.containsKey(clientID))
|
||||
return 0;
|
||||
else
|
||||
return m_inflightStore.get(clientID).size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPendingPublishMessagesNo(String clientID) {
|
||||
return m_messagesStore.getPendingPublishMessages(clientID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSecondPhaseAckPendingMessages(String clientID) {
|
||||
if (!m_secondPhaseStore.containsKey(clientID))
|
||||
return 0;
|
||||
else
|
||||
return m_secondPhaseStore.get(clientID).size();
|
||||
}
|
||||
}
|
||||
/*
|
||||
* 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.spi.persistence;
|
||||
|
||||
import io.moquette.spi.ClientSession;
|
||||
import io.moquette.spi.IMessagesStore.StoredMessage;
|
||||
import io.moquette.spi.ISessionsStore;
|
||||
import io.moquette.spi.MessageGUID;
|
||||
import io.moquette.spi.impl.Utils;
|
||||
import io.moquette.spi.impl.subscriptions.Subscription;
|
||||
import io.moquette.spi.impl.subscriptions.Topic;
|
||||
import io.moquette.spi.persistence.MapDBPersistentStore.PersistentSession;
|
||||
import org.mapdb.DB;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
/**
|
||||
* ISessionsStore implementation backed by MapDB.
|
||||
*
|
||||
* @author andrea
|
||||
*/
|
||||
class MapDBSessionsStore implements ISessionsStore {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(MapDBSessionsStore.class);
|
||||
|
||||
// maps clientID->[MessageId -> guid]
|
||||
private ConcurrentMap<String, ConcurrentMap<Integer, MessageGUID>> m_inflightStore;
|
||||
// map clientID <-> set of currently in flight packet identifiers
|
||||
private Map<String, Set<Integer>> m_inFlightIds;
|
||||
private ConcurrentMap<String, PersistentSession> m_persistentSessions;
|
||||
// maps clientID->[MessageId -> guid]
|
||||
private ConcurrentMap<String, Map<Integer, MessageGUID>> m_secondPhaseStore;
|
||||
|
||||
private final DB m_db;
|
||||
private final MapDBMessagesStore m_messagesStore;
|
||||
|
||||
MapDBSessionsStore(DB db, MapDBMessagesStore messagesStore) {
|
||||
m_db = db;
|
||||
m_messagesStore = messagesStore;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initStore() {
|
||||
m_inflightStore = m_db.getHashMap("inflight");
|
||||
m_inFlightIds = m_db.getHashMap("inflightPacketIDs");
|
||||
m_persistentSessions = m_db.getHashMap("sessions");
|
||||
m_secondPhaseStore = m_db.getHashMap("secondPhase");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addNewSubscription(Subscription newSubscription) {
|
||||
LOG.info(
|
||||
"Adding new subscription. CId= {}, topics = {}.",
|
||||
newSubscription.getClientId(),
|
||||
newSubscription.getTopicFilter());
|
||||
final String clientID = newSubscription.getClientId();
|
||||
m_db.getHashMap("subscriptions_" + clientID).put(newSubscription.getTopicFilter(), newSubscription);
|
||||
|
||||
if (LOG.isTraceEnabled()) {
|
||||
LOG.trace(
|
||||
"The subscription has been added. CId= {}, topics = {}, clientSubscriptions = {}.",
|
||||
newSubscription.getClientId(),
|
||||
newSubscription.getTopicFilter(),
|
||||
m_db.getHashMap("subscriptions_" + clientID));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeSubscription(Topic topicFilter, String clientID) {
|
||||
LOG.info("Removing subscription. CId= {}, topics = {}.", clientID, topicFilter);
|
||||
if (!m_db.exists("subscriptions_" + clientID)) {
|
||||
return;
|
||||
}
|
||||
m_db.getHashMap("subscriptions_" + clientID).remove(topicFilter);
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug(
|
||||
"The subscription has been removed. CId= {}, topics = {}, clientSubscriptions = {}.",
|
||||
clientID,
|
||||
topicFilter,
|
||||
m_db.getHashMap("subscriptions_" + clientID));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void wipeSubscriptions(String clientID) {
|
||||
LOG.info("Wiping subscriptions. CId= {}.", clientID);
|
||||
m_db.delete("subscriptions_" + clientID);
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug(
|
||||
"The subscriptions have been removed. CId= {}, clientSubscriptions = {}.",
|
||||
clientID,
|
||||
m_db.getHashMap("subscriptions_" + clientID));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ClientTopicCouple> listAllSubscriptions() {
|
||||
LOG.info("Retrieving existing subscriptions...");
|
||||
final List<ClientTopicCouple> allSubscriptions = new ArrayList<>();
|
||||
for (String clientID : m_persistentSessions.keySet()) {
|
||||
ConcurrentMap<Topic, Subscription> clientSubscriptions = m_db.getHashMap("subscriptions_" + clientID);
|
||||
for (Topic topicFilter : clientSubscriptions.keySet()) {
|
||||
allSubscriptions.add(new ClientTopicCouple(clientID, topicFilter));
|
||||
}
|
||||
}
|
||||
if (LOG.isTraceEnabled()) {
|
||||
LOG.trace("The existing subscriptions have been retrieved. Result = {}.", allSubscriptions);
|
||||
}
|
||||
return allSubscriptions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Subscription getSubscription(ClientTopicCouple couple) {
|
||||
ConcurrentMap<Topic, Subscription> clientSubscriptions = m_db.getHashMap("subscriptions_" + couple.clientID);
|
||||
LOG.info("Retrieving subscriptions. CId= {}, subscriptions = {}.", couple.clientID, clientSubscriptions);
|
||||
return clientSubscriptions.get(couple.topicFilter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Subscription> getSubscriptions() {
|
||||
LOG.info("Retrieving existing subscriptions...");
|
||||
List<Subscription> subscriptions = new ArrayList<>();
|
||||
for (String clientID : m_persistentSessions.keySet()) {
|
||||
ConcurrentMap<Topic, Subscription> clientSubscriptions = m_db.getHashMap("subscriptions_" + clientID);
|
||||
subscriptions.addAll(clientSubscriptions.values());
|
||||
}
|
||||
LOG.debug("The existing subscriptions have been retrieved. Result = {}.", subscriptions);
|
||||
return subscriptions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(String clientID) {
|
||||
return m_db.exists("subscriptions_" + clientID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientSession createNewSession(String clientID, boolean cleanSession) {
|
||||
if (m_persistentSessions.containsKey(clientID)) {
|
||||
LOG.error(
|
||||
"Unable to create a new session: the client ID is already in use. CId= {}, cleanSession = {}.",
|
||||
clientID,
|
||||
cleanSession);
|
||||
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);
|
||||
m_persistentSessions.putIfAbsent(clientID, new PersistentSession(cleanSession));
|
||||
return new ClientSession(clientID, m_messagesStore, this, cleanSession);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientSession sessionForClient(String clientID) {
|
||||
LOG.info("Retrieving session. CId= {}.", clientID);
|
||||
if (!m_persistentSessions.containsKey(clientID)) {
|
||||
LOG.warn("The session does not exist. CId= {}.", clientID);
|
||||
return null;
|
||||
}
|
||||
|
||||
PersistentSession storedSession = m_persistentSessions.get(clientID);
|
||||
return new ClientSession(clientID, m_messagesStore, this, storedSession.cleanSession);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ClientSession> getAllSessions() {
|
||||
Collection<ClientSession> result = new ArrayList<>();
|
||||
for (Map.Entry<String, PersistentSession> entry : m_persistentSessions.entrySet()) {
|
||||
result.add(new ClientSession(entry.getKey(), m_messagesStore, this, entry.getValue().cleanSession));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateCleanStatus(String clientID, boolean cleanSession) {
|
||||
LOG.info("Updating cleanSession flag. CId= {}, cleanSession = {}.", clientID, cleanSession);
|
||||
m_persistentSessions.put(clientID, new MapDBPersistentStore.PersistentSession(cleanSession));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the next valid packetIdentifier for the given client session.
|
||||
*/
|
||||
@Override
|
||||
public int nextPacketID(String clientID) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Generating next packet ID. CId= {}.", clientID);
|
||||
}
|
||||
Set<Integer> inFlightForClient = this.m_inFlightIds.get(clientID);
|
||||
if (inFlightForClient == null) {
|
||||
int nextPacketId = 1;
|
||||
inFlightForClient = Collections.newSetFromMap(new ConcurrentHashMap<Integer, Boolean>());
|
||||
inFlightForClient.add(nextPacketId);
|
||||
this.m_inFlightIds.put(clientID, inFlightForClient);
|
||||
return nextPacketId;
|
||||
|
||||
}
|
||||
|
||||
int maxId = inFlightForClient.isEmpty() ? 0 : Collections.max(inFlightForClient);
|
||||
int nextPacketId = (maxId + 1) % 0xFFFF;
|
||||
inFlightForClient.add(nextPacketId);
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("The next packet ID has been generated. CId= {}, result = {}.", clientID, nextPacketId);
|
||||
}
|
||||
return nextPacketId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void inFlightAck(String clientID, int messageID) {
|
||||
LOG.debug("Acknowledging inflight message. CId= {}, messageId = {}.", clientID, messageID);
|
||||
Map<Integer, MessageGUID> m = this.m_inflightStore.get(clientID);
|
||||
if (m == null) {
|
||||
LOG.warn("Unable to retrieve inflight message record. CId= {}, messageId = {}.", clientID, messageID);
|
||||
return;
|
||||
}
|
||||
m.remove(messageID);
|
||||
|
||||
// remove from the ids store
|
||||
Set<Integer> inFlightForClient = this.m_inFlightIds.get(clientID);
|
||||
if (inFlightForClient != null) {
|
||||
inFlightForClient.remove(messageID);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void inFlight(String clientID, int messageID, MessageGUID guid) {
|
||||
LOG.debug("Storing inflight message. CId= {}, messageId = {}, guid = {}.", clientID, messageID, guid);
|
||||
ConcurrentMap<Integer, MessageGUID> m = this.m_inflightStore.get(clientID);
|
||||
if (m == null) {
|
||||
m = new ConcurrentHashMap<>();
|
||||
}
|
||||
m.put(messageID, guid);
|
||||
LOG.info("storing inflight clientID <{}> messageID {} guid <{}>", clientID, messageID, guid);
|
||||
this.m_inflightStore.put(clientID, m);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockingQueue<StoredMessage> queue(String clientID) {
|
||||
LOG.info("Queuing pending message. CId= {}, guid = {}.", clientID);
|
||||
return this.m_db.getQueue(clientID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dropQueue(String clientID) {
|
||||
LOG.info("Removing pending messages. CId= {}.", clientID);
|
||||
this.m_db.delete(clientID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void moveInFlightToSecondPhaseAckWaiting(String clientID, int messageID) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Moving inflight message to 2nd phase ack state. CId= {}, messageID = {}.", clientID, messageID);
|
||||
}
|
||||
Map<Integer, MessageGUID> m = this.m_inflightStore.get(clientID);
|
||||
if (m == null) {
|
||||
LOG.warn("Unable to retrieve inflight message record. CId= {}, messageId = {}.", clientID, messageID);
|
||||
return;
|
||||
}
|
||||
MessageGUID guid = m.remove(messageID);
|
||||
|
||||
// remove from the ids store
|
||||
Set<Integer> inFlightForClient = this.m_inFlightIds.get(clientID);
|
||||
if (inFlightForClient != null) {
|
||||
inFlightForClient.remove(messageID);
|
||||
}
|
||||
|
||||
Map<Integer, MessageGUID> messageIDs = Utils
|
||||
.defaultGet(m_secondPhaseStore, clientID, new HashMap<Integer, MessageGUID>());
|
||||
messageIDs.put(messageID, guid);
|
||||
m_secondPhaseStore.put(clientID, messageIDs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageGUID secondPhaseAcknowledged(String clientID, int messageID) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Processing second phase ACK CId={}, messageId={}", clientID, messageID);
|
||||
}
|
||||
Map<Integer, MessageGUID> messageIDs = Utils
|
||||
.defaultGet(m_secondPhaseStore, clientID, new HashMap<Integer, MessageGUID>());
|
||||
MessageGUID guid = messageIDs.remove(messageID);
|
||||
m_secondPhaseStore.put(clientID, messageIDs);
|
||||
return guid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageGUID mapToGuid(String clientID, int messageID) {
|
||||
LOG.debug("Mapping message ID to GUID CId={}, messageId={}", clientID, messageID);
|
||||
ConcurrentMap<Integer, MessageGUID> messageIdToGuid = m_db.getHashMap(messageId2GuidsMapName(clientID));
|
||||
MessageGUID result = messageIdToGuid.get(messageID);
|
||||
LOG.debug("Message ID has been mapped to a GUID CId={}, messageId={}, guid={}", clientID, messageID, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
static String messageId2GuidsMapName(String clientID) {
|
||||
return "guidsMapping_" + clientID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StoredMessage getInflightMessage(String clientID, int messageID) {
|
||||
LOG.info("Retrieving inflight message CId={}, messageId={}", clientID, messageID);
|
||||
Map<Integer, MessageGUID> clientEntries = m_inflightStore.get(clientID);
|
||||
if (clientEntries == null) {
|
||||
LOG.warn("The client has no inflight messages CId={}, messageId={}", clientID, messageID);
|
||||
return null;
|
||||
}
|
||||
MessageGUID guid = clientEntries.get(messageID);
|
||||
if (guid == null) {
|
||||
LOG.warn("The message ID does not have an associated GUID. CId= {}, messageId = {}.", clientID, messageID);
|
||||
return null;
|
||||
}
|
||||
return m_messagesStore.getMessageByGuid(guid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInflightMessagesNo(String clientID) {
|
||||
if (!m_inflightStore.containsKey(clientID))
|
||||
return 0;
|
||||
else
|
||||
return m_inflightStore.get(clientID).size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPendingPublishMessagesNo(String clientID) {
|
||||
return m_messagesStore.getPendingPublishMessages(clientID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSecondPhaseAckPendingMessages(String clientID) {
|
||||
if (!m_secondPhaseStore.containsKey(clientID))
|
||||
return 0;
|
||||
else
|
||||
return m_secondPhaseStore.get(clientID).size();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* 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
|
||||
|
||||
@@ -1,174 +1,174 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* ------------------------------------------------------
|
||||
* 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.spi.impl;
|
||||
|
||||
import io.moquette.interception.InterceptHandler;
|
||||
import io.moquette.interception.messages.*;
|
||||
import io.moquette.server.netty.MessageBuilder;
|
||||
import io.moquette.spi.impl.subscriptions.Subscription;
|
||||
import io.moquette.spi.impl.subscriptions.Topic;
|
||||
import io.netty.handler.codec.mqtt.MqttQoS;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.Matchers.refEq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
/**
|
||||
* @author Wagner Macedo
|
||||
*/
|
||||
public class BrokerInterceptorTest {
|
||||
|
||||
// value to check for changes after every notification
|
||||
private static final AtomicInteger n = new AtomicInteger(0);
|
||||
|
||||
// Interceptor loaded with a custom InterceptHandler special for the tests
|
||||
private static final class MockObserver implements InterceptHandler {
|
||||
|
||||
@Override
|
||||
public String getID() {
|
||||
return "MockObserver";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?>[] getInterceptedMessageTypes() {
|
||||
return InterceptHandler.ALL_MESSAGE_TYPES;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnect(InterceptConnectMessage msg) {
|
||||
n.set(40);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisconnect(InterceptDisconnectMessage msg) {
|
||||
n.set(50);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnectionLost(InterceptConnectionLostMessage msg) {
|
||||
n.set(30);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPublish(InterceptPublishMessage msg) {
|
||||
n.set(60);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSubscribe(InterceptSubscribeMessage msg) {
|
||||
n.set(70);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUnsubscribe(InterceptUnsubscribeMessage msg) {
|
||||
n.set(80);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMessageAcknowledged(InterceptAcknowledgedMessage msg) {
|
||||
n.set(90);
|
||||
}
|
||||
}
|
||||
|
||||
private static final BrokerInterceptor interceptor = new BrokerInterceptor(
|
||||
Arrays.<InterceptHandler>asList(new MockObserver()));
|
||||
|
||||
@BeforeClass
|
||||
public static void beforeAllTests() {
|
||||
// check if any of the handler methods was called before notifications
|
||||
assertEquals(0, n.get());
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void afterAllTests() {
|
||||
interceptor.stop();
|
||||
}
|
||||
|
||||
/* Used to wait handler notification by the interceptor internal thread */
|
||||
private static void interval() throws InterruptedException {
|
||||
Thread.sleep(100);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotifyClientConnected() throws Exception {
|
||||
interceptor.notifyClientConnected(MessageBuilder.connect().build());
|
||||
interval();
|
||||
assertEquals(40, n.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotifyClientDisconnected() throws Exception {
|
||||
interceptor.notifyClientDisconnected("cli1234", "cli1234");
|
||||
interval();
|
||||
assertEquals(50, n.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotifyTopicPublished() throws Exception {
|
||||
interceptor.notifyTopicPublished(
|
||||
MessageBuilder.publish().qos(MqttQoS.AT_MOST_ONCE).payload("Hello".getBytes()).build(),
|
||||
"cli1234",
|
||||
"cli1234");
|
||||
interval();
|
||||
assertEquals(60, n.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotifyTopicSubscribed() throws Exception {
|
||||
interceptor.notifyTopicSubscribed(new Subscription("cli1", new Topic("o2"), MqttQoS.AT_MOST_ONCE), "cli1234");
|
||||
interval();
|
||||
assertEquals(70, n.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotifyTopicUnsubscribed() throws Exception {
|
||||
interceptor.notifyTopicUnsubscribed("o2", "cli1234", "cli1234");
|
||||
interval();
|
||||
assertEquals(80, n.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddAndRemoveInterceptHandler() throws Exception {
|
||||
InterceptHandler interceptHandlerMock1 = mock(InterceptHandler.class);
|
||||
InterceptHandler interceptHandlerMock2 = mock(InterceptHandler.class);
|
||||
// add
|
||||
interceptor.addInterceptHandler(interceptHandlerMock1);
|
||||
interceptor.addInterceptHandler(interceptHandlerMock2);
|
||||
|
||||
Subscription subscription = new Subscription("cli1", new Topic("o2"), MqttQoS.AT_MOST_ONCE);
|
||||
interceptor.notifyTopicSubscribed(subscription, "cli1234");
|
||||
interval();
|
||||
|
||||
verify(interceptHandlerMock1).onSubscribe(refEq(new InterceptSubscribeMessage(subscription, "cli1234")));
|
||||
verify(interceptHandlerMock2).onSubscribe(refEq(new InterceptSubscribeMessage(subscription, "cli1234")));
|
||||
|
||||
// remove
|
||||
interceptor.removeInterceptHandler(interceptHandlerMock1);
|
||||
|
||||
interceptor.notifyTopicSubscribed(subscription, "cli1235");
|
||||
interval();
|
||||
// removeInterceptHandler() performs another interaction
|
||||
// TODO: fix this
|
||||
// verifyNoMoreInteractions(interceptHandlerMock1);
|
||||
verify(interceptHandlerMock2).onSubscribe(refEq(new InterceptSubscribeMessage(subscription, "cli1235")));
|
||||
}
|
||||
}
|
||||
/*
|
||||
* 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.spi.impl;
|
||||
|
||||
import io.moquette.interception.InterceptHandler;
|
||||
import io.moquette.interception.messages.*;
|
||||
import io.moquette.server.netty.MessageBuilder;
|
||||
import io.moquette.spi.impl.subscriptions.Subscription;
|
||||
import io.moquette.spi.impl.subscriptions.Topic;
|
||||
import io.netty.handler.codec.mqtt.MqttQoS;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.Matchers.refEq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
/**
|
||||
* @author Wagner Macedo
|
||||
*/
|
||||
public class BrokerInterceptorTest {
|
||||
|
||||
// value to check for changes after every notification
|
||||
private static final AtomicInteger n = new AtomicInteger(0);
|
||||
|
||||
// Interceptor loaded with a custom InterceptHandler special for the tests
|
||||
private static final class MockObserver implements InterceptHandler {
|
||||
|
||||
@Override
|
||||
public String getID() {
|
||||
return "MockObserver";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?>[] getInterceptedMessageTypes() {
|
||||
return InterceptHandler.ALL_MESSAGE_TYPES;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnect(InterceptConnectMessage msg) {
|
||||
n.set(40);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisconnect(InterceptDisconnectMessage msg) {
|
||||
n.set(50);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnectionLost(InterceptConnectionLostMessage msg) {
|
||||
n.set(30);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPublish(InterceptPublishMessage msg) {
|
||||
n.set(60);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSubscribe(InterceptSubscribeMessage msg) {
|
||||
n.set(70);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUnsubscribe(InterceptUnsubscribeMessage msg) {
|
||||
n.set(80);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMessageAcknowledged(InterceptAcknowledgedMessage msg) {
|
||||
n.set(90);
|
||||
}
|
||||
}
|
||||
|
||||
private static final BrokerInterceptor interceptor = new BrokerInterceptor(
|
||||
Arrays.<InterceptHandler>asList(new MockObserver()));
|
||||
|
||||
@BeforeClass
|
||||
public static void beforeAllTests() {
|
||||
// check if any of the handler methods was called before notifications
|
||||
assertEquals(0, n.get());
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void afterAllTests() {
|
||||
interceptor.stop();
|
||||
}
|
||||
|
||||
/* Used to wait handler notification by the interceptor internal thread */
|
||||
private static void interval() throws InterruptedException {
|
||||
Thread.sleep(100);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotifyClientConnected() throws Exception {
|
||||
interceptor.notifyClientConnected(MessageBuilder.connect().build());
|
||||
interval();
|
||||
assertEquals(40, n.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotifyClientDisconnected() throws Exception {
|
||||
interceptor.notifyClientDisconnected("cli1234", "cli1234");
|
||||
interval();
|
||||
assertEquals(50, n.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotifyTopicPublished() throws Exception {
|
||||
interceptor.notifyTopicPublished(
|
||||
MessageBuilder.publish().qos(MqttQoS.AT_MOST_ONCE).payload("Hello".getBytes()).build(),
|
||||
"cli1234",
|
||||
"cli1234");
|
||||
interval();
|
||||
assertEquals(60, n.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotifyTopicSubscribed() throws Exception {
|
||||
interceptor.notifyTopicSubscribed(new Subscription("cli1", new Topic("o2"), MqttQoS.AT_MOST_ONCE), "cli1234");
|
||||
interval();
|
||||
assertEquals(70, n.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotifyTopicUnsubscribed() throws Exception {
|
||||
interceptor.notifyTopicUnsubscribed("o2", "cli1234", "cli1234");
|
||||
interval();
|
||||
assertEquals(80, n.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddAndRemoveInterceptHandler() throws Exception {
|
||||
InterceptHandler interceptHandlerMock1 = mock(InterceptHandler.class);
|
||||
InterceptHandler interceptHandlerMock2 = mock(InterceptHandler.class);
|
||||
// add
|
||||
interceptor.addInterceptHandler(interceptHandlerMock1);
|
||||
interceptor.addInterceptHandler(interceptHandlerMock2);
|
||||
|
||||
Subscription subscription = new Subscription("cli1", new Topic("o2"), MqttQoS.AT_MOST_ONCE);
|
||||
interceptor.notifyTopicSubscribed(subscription, "cli1234");
|
||||
interval();
|
||||
|
||||
verify(interceptHandlerMock1).onSubscribe(refEq(new InterceptSubscribeMessage(subscription, "cli1234")));
|
||||
verify(interceptHandlerMock2).onSubscribe(refEq(new InterceptSubscribeMessage(subscription, "cli1234")));
|
||||
|
||||
// remove
|
||||
interceptor.removeInterceptHandler(interceptHandlerMock1);
|
||||
|
||||
interceptor.notifyTopicSubscribed(subscription, "cli1235");
|
||||
interval();
|
||||
// removeInterceptHandler() performs another interaction
|
||||
// TODO: fix this
|
||||
// verifyNoMoreInteractions(interceptHandlerMock1);
|
||||
verify(interceptHandlerMock2).onSubscribe(refEq(new InterceptSubscribeMessage(subscription, "cli1235")));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* 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
|
||||
|
||||
@@ -1,273 +1,273 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* ------------------------------------------------------
|
||||
* 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.spi.persistence;
|
||||
|
||||
import io.moquette.server.Constants;
|
||||
import io.moquette.spi.ClientSession;
|
||||
import io.moquette.spi.IMessagesStore;
|
||||
import io.moquette.spi.IMessagesStore.StoredMessage;
|
||||
import io.moquette.spi.ISessionsStore;
|
||||
import io.moquette.spi.MessageGUID;
|
||||
import io.moquette.spi.impl.Utils;
|
||||
import io.moquette.spi.impl.subscriptions.Subscription;
|
||||
import io.moquette.spi.impl.subscriptions.Topic;
|
||||
import io.moquette.spi.persistence.MapDBPersistentStore.PersistentSession;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author andrea
|
||||
*/
|
||||
public class MemorySessionStore implements ISessionsStore {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(MemorySessionStore.class);
|
||||
|
||||
private Map<String, Map<Topic, Subscription>> m_persistentSubscriptions = new HashMap<>();
|
||||
|
||||
private Map<String, MapDBPersistentStore.PersistentSession> m_persistentSessions = new HashMap<>();
|
||||
|
||||
// maps clientID->[MessageId -> guid]
|
||||
private Map<String, Map<Integer, MessageGUID>> m_inflightStore = new HashMap<>();
|
||||
// private Map<String, Set<Integer>> m_inflightIDs = new HashMap<>();
|
||||
// maps clientID->BlockingQueue
|
||||
private Map<String, BlockingQueue<StoredMessage>> queues = new HashMap<>();
|
||||
// maps clientID->[MessageId -> guid]
|
||||
private Map<String, Map<Integer, MessageGUID>> m_secondPhaseStore = new HashMap<>();
|
||||
|
||||
private Map<String, Map<Integer, MessageGUID>> m_messageToGuids;
|
||||
private final IMessagesStore m_messagesStore;
|
||||
|
||||
public MemorySessionStore(IMessagesStore messagesStore, Map<String, Map<Integer, MessageGUID>> messageToGuids) {
|
||||
m_messageToGuids = messageToGuids;
|
||||
this.m_messagesStore = messagesStore;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeSubscription(Topic topic, String clientID) {
|
||||
LOG.debug("removeSubscription topic filter: {} for clientID: {}", topic, clientID);
|
||||
if (!m_persistentSubscriptions.containsKey(clientID)) {
|
||||
return;
|
||||
}
|
||||
Map<Topic, Subscription> clientSubscriptions = m_persistentSubscriptions.get(clientID);
|
||||
clientSubscriptions.remove(topic);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initStore() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addNewSubscription(Subscription newSubscription) {
|
||||
final String clientID = newSubscription.getClientId();
|
||||
if (!m_persistentSubscriptions.containsKey(clientID)) {
|
||||
m_persistentSubscriptions.put(clientID, new HashMap<Topic, Subscription>());
|
||||
}
|
||||
|
||||
m_persistentSubscriptions.get(clientID).put(newSubscription.getTopicFilter(), newSubscription);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void wipeSubscriptions(String clientID) {
|
||||
m_persistentSubscriptions.remove(clientID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(String clientID) {
|
||||
return m_persistentSubscriptions.containsKey(clientID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientSession createNewSession(String clientID, boolean cleanSession) {
|
||||
LOG.debug("createNewSession for client <{}>", clientID);
|
||||
if (m_persistentSessions.containsKey(clientID)) {
|
||||
LOG.error("already exists a session for client <{}>, bad condition", clientID);
|
||||
throw new IllegalArgumentException("Can't create a session with the ID of an already existing" + clientID);
|
||||
}
|
||||
LOG.debug("clientID {} is a newcome, creating it's empty subscriptions set", clientID);
|
||||
m_persistentSubscriptions.put(clientID, new HashMap<Topic, Subscription>());
|
||||
m_persistentSessions.put(clientID, new MapDBPersistentStore.PersistentSession(cleanSession));
|
||||
return new ClientSession(clientID, m_messagesStore, this, cleanSession);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientSession sessionForClient(String clientID) {
|
||||
if (!m_persistentSessions.containsKey(clientID)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
MapDBPersistentStore.PersistentSession storedSession = m_persistentSessions.get(clientID);
|
||||
return new ClientSession(clientID, m_messagesStore, this, storedSession.cleanSession);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ClientSession> getAllSessions() {
|
||||
Collection<ClientSession> result = new ArrayList<>();
|
||||
for (Map.Entry<String, PersistentSession> entry : m_persistentSessions.entrySet()) {
|
||||
result.add(new ClientSession(entry.getKey(), m_messagesStore, this, entry.getValue().cleanSession));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateCleanStatus(String clientID, boolean cleanSession) {
|
||||
m_persistentSessions.put(clientID, new MapDBPersistentStore.PersistentSession(cleanSession));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ClientTopicCouple> listAllSubscriptions() {
|
||||
List<ClientTopicCouple> allSubscriptions = new ArrayList<>();
|
||||
for (Map.Entry<String, Map<Topic, Subscription>> entry : m_persistentSubscriptions.entrySet()) {
|
||||
for (Subscription sub : entry.getValue().values()) {
|
||||
allSubscriptions.add(sub.asClientTopicCouple());
|
||||
}
|
||||
}
|
||||
return allSubscriptions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Subscription getSubscription(ClientTopicCouple couple) {
|
||||
Map<Topic, Subscription> subscriptions = m_persistentSubscriptions.get(couple.clientID);
|
||||
if (subscriptions == null || subscriptions.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return subscriptions.get(couple.topicFilter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Subscription> getSubscriptions() {
|
||||
List<Subscription> subscriptions = new ArrayList<>();
|
||||
for (Map.Entry<String, Map<Topic, Subscription>> entry : m_persistentSubscriptions.entrySet()) {
|
||||
subscriptions.addAll(entry.getValue().values());
|
||||
}
|
||||
return subscriptions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void inFlightAck(String clientID, int messageID) {
|
||||
Map<Integer, MessageGUID> m = this.m_inflightStore.get(clientID);
|
||||
if (m == null) {
|
||||
LOG.error("Can't find the inFlight record for client <{}>", clientID);
|
||||
return;
|
||||
}
|
||||
m.remove(messageID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void inFlight(String clientID, int messageID, MessageGUID guid) {
|
||||
Map<Integer, MessageGUID> m = this.m_inflightStore.get(clientID);
|
||||
if (m == null) {
|
||||
m = new HashMap<>();
|
||||
}
|
||||
m.put(messageID, guid);
|
||||
this.m_inflightStore.put(clientID, m);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the next valid packetIdentifier for the given client session.
|
||||
*/
|
||||
@Override
|
||||
public int nextPacketID(String clientID) {
|
||||
Map<Integer, MessageGUID> m = this.m_inflightStore.get(clientID);
|
||||
if (m == null) {
|
||||
m = new HashMap<>();
|
||||
int nextPacketId = 1;
|
||||
m.put(nextPacketId, null);
|
||||
return nextPacketId;
|
||||
}
|
||||
int maxId = Collections.max(m.keySet());
|
||||
int nextPacketId = (maxId + 1) % 0xFFFF;
|
||||
m.put(nextPacketId, null);
|
||||
return nextPacketId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockingQueue<StoredMessage> queue(String clientID) {
|
||||
return Utils.defaultGet(queues, clientID, new ArrayBlockingQueue<StoredMessage>(Constants.MAX_MESSAGE_QUEUE));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dropQueue(String clientID) {
|
||||
queues.remove(clientID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void moveInFlightToSecondPhaseAckWaiting(String clientID, int messageID) {
|
||||
LOG.info("acknowledging inflight clientID <{}> messageID {}", clientID, messageID);
|
||||
Map<Integer, MessageGUID> m = this.m_inflightStore.get(clientID);
|
||||
if (m == null) {
|
||||
LOG.error("Can't find the inFlight record for client <{}>", clientID);
|
||||
return;
|
||||
}
|
||||
MessageGUID guid = m.remove(messageID);
|
||||
|
||||
LOG.info("Moving to second phase store");
|
||||
Map<Integer, MessageGUID> messageIDs = Utils
|
||||
.defaultGet(m_secondPhaseStore, clientID, new HashMap<Integer, MessageGUID>());
|
||||
messageIDs.put(messageID, guid);
|
||||
m_secondPhaseStore.put(clientID, messageIDs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageGUID secondPhaseAcknowledged(String clientID, int messageID) {
|
||||
Map<Integer, MessageGUID> messageIDs = Utils
|
||||
.defaultGet(m_secondPhaseStore, clientID, new HashMap<Integer, MessageGUID>());
|
||||
MessageGUID guid = messageIDs.remove(messageID);
|
||||
m_secondPhaseStore.put(clientID, messageIDs);
|
||||
return guid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageGUID mapToGuid(String clientID, int messageID) {
|
||||
HashMap<Integer, MessageGUID> guids = (HashMap<Integer, MessageGUID>) Utils
|
||||
.defaultGet(m_messageToGuids, clientID, new HashMap<Integer, MessageGUID>());
|
||||
return guids.get(messageID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StoredMessage getInflightMessage(String clientID, int messageID) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInflightMessagesNo(String clientID) {
|
||||
Map<Integer, MessageGUID> inflightMessages = m_inflightStore.get(clientID);
|
||||
if (inflightMessages == null)
|
||||
return 0;
|
||||
else
|
||||
return inflightMessages.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPendingPublishMessagesNo(String clientID) {
|
||||
return m_messagesStore.getPendingPublishMessages(clientID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSecondPhaseAckPendingMessages(String clientID) {
|
||||
Map<Integer, MessageGUID> pendingAcks = m_secondPhaseStore.get(clientID);
|
||||
if (pendingAcks == null)
|
||||
return 0;
|
||||
else
|
||||
return pendingAcks.size();
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.spi.persistence;
|
||||
|
||||
import io.moquette.server.Constants;
|
||||
import io.moquette.spi.ClientSession;
|
||||
import io.moquette.spi.IMessagesStore;
|
||||
import io.moquette.spi.IMessagesStore.StoredMessage;
|
||||
import io.moquette.spi.ISessionsStore;
|
||||
import io.moquette.spi.MessageGUID;
|
||||
import io.moquette.spi.impl.Utils;
|
||||
import io.moquette.spi.impl.subscriptions.Subscription;
|
||||
import io.moquette.spi.impl.subscriptions.Topic;
|
||||
import io.moquette.spi.persistence.MapDBPersistentStore.PersistentSession;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author andrea
|
||||
*/
|
||||
public class MemorySessionStore implements ISessionsStore {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(MemorySessionStore.class);
|
||||
|
||||
private Map<String, Map<Topic, Subscription>> m_persistentSubscriptions = new HashMap<>();
|
||||
|
||||
private Map<String, MapDBPersistentStore.PersistentSession> m_persistentSessions = new HashMap<>();
|
||||
|
||||
// maps clientID->[MessageId -> guid]
|
||||
private Map<String, Map<Integer, MessageGUID>> m_inflightStore = new HashMap<>();
|
||||
// private Map<String, Set<Integer>> m_inflightIDs = new HashMap<>();
|
||||
// maps clientID->BlockingQueue
|
||||
private Map<String, BlockingQueue<StoredMessage>> queues = new HashMap<>();
|
||||
// maps clientID->[MessageId -> guid]
|
||||
private Map<String, Map<Integer, MessageGUID>> m_secondPhaseStore = new HashMap<>();
|
||||
|
||||
private Map<String, Map<Integer, MessageGUID>> m_messageToGuids;
|
||||
private final IMessagesStore m_messagesStore;
|
||||
|
||||
public MemorySessionStore(IMessagesStore messagesStore, Map<String, Map<Integer, MessageGUID>> messageToGuids) {
|
||||
m_messageToGuids = messageToGuids;
|
||||
this.m_messagesStore = messagesStore;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeSubscription(Topic topic, String clientID) {
|
||||
LOG.debug("removeSubscription topic filter: {} for clientID: {}", topic, clientID);
|
||||
if (!m_persistentSubscriptions.containsKey(clientID)) {
|
||||
return;
|
||||
}
|
||||
Map<Topic, Subscription> clientSubscriptions = m_persistentSubscriptions.get(clientID);
|
||||
clientSubscriptions.remove(topic);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initStore() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addNewSubscription(Subscription newSubscription) {
|
||||
final String clientID = newSubscription.getClientId();
|
||||
if (!m_persistentSubscriptions.containsKey(clientID)) {
|
||||
m_persistentSubscriptions.put(clientID, new HashMap<Topic, Subscription>());
|
||||
}
|
||||
|
||||
m_persistentSubscriptions.get(clientID).put(newSubscription.getTopicFilter(), newSubscription);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void wipeSubscriptions(String clientID) {
|
||||
m_persistentSubscriptions.remove(clientID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(String clientID) {
|
||||
return m_persistentSubscriptions.containsKey(clientID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientSession createNewSession(String clientID, boolean cleanSession) {
|
||||
LOG.debug("createNewSession for client <{}>", clientID);
|
||||
if (m_persistentSessions.containsKey(clientID)) {
|
||||
LOG.error("already exists a session for client <{}>, bad condition", clientID);
|
||||
throw new IllegalArgumentException("Can't create a session with the ID of an already existing" + clientID);
|
||||
}
|
||||
LOG.debug("clientID {} is a newcome, creating it's empty subscriptions set", clientID);
|
||||
m_persistentSubscriptions.put(clientID, new HashMap<Topic, Subscription>());
|
||||
m_persistentSessions.put(clientID, new MapDBPersistentStore.PersistentSession(cleanSession));
|
||||
return new ClientSession(clientID, m_messagesStore, this, cleanSession);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientSession sessionForClient(String clientID) {
|
||||
if (!m_persistentSessions.containsKey(clientID)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
MapDBPersistentStore.PersistentSession storedSession = m_persistentSessions.get(clientID);
|
||||
return new ClientSession(clientID, m_messagesStore, this, storedSession.cleanSession);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ClientSession> getAllSessions() {
|
||||
Collection<ClientSession> result = new ArrayList<>();
|
||||
for (Map.Entry<String, PersistentSession> entry : m_persistentSessions.entrySet()) {
|
||||
result.add(new ClientSession(entry.getKey(), m_messagesStore, this, entry.getValue().cleanSession));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateCleanStatus(String clientID, boolean cleanSession) {
|
||||
m_persistentSessions.put(clientID, new MapDBPersistentStore.PersistentSession(cleanSession));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ClientTopicCouple> listAllSubscriptions() {
|
||||
List<ClientTopicCouple> allSubscriptions = new ArrayList<>();
|
||||
for (Map.Entry<String, Map<Topic, Subscription>> entry : m_persistentSubscriptions.entrySet()) {
|
||||
for (Subscription sub : entry.getValue().values()) {
|
||||
allSubscriptions.add(sub.asClientTopicCouple());
|
||||
}
|
||||
}
|
||||
return allSubscriptions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Subscription getSubscription(ClientTopicCouple couple) {
|
||||
Map<Topic, Subscription> subscriptions = m_persistentSubscriptions.get(couple.clientID);
|
||||
if (subscriptions == null || subscriptions.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return subscriptions.get(couple.topicFilter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Subscription> getSubscriptions() {
|
||||
List<Subscription> subscriptions = new ArrayList<>();
|
||||
for (Map.Entry<String, Map<Topic, Subscription>> entry : m_persistentSubscriptions.entrySet()) {
|
||||
subscriptions.addAll(entry.getValue().values());
|
||||
}
|
||||
return subscriptions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void inFlightAck(String clientID, int messageID) {
|
||||
Map<Integer, MessageGUID> m = this.m_inflightStore.get(clientID);
|
||||
if (m == null) {
|
||||
LOG.error("Can't find the inFlight record for client <{}>", clientID);
|
||||
return;
|
||||
}
|
||||
m.remove(messageID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void inFlight(String clientID, int messageID, MessageGUID guid) {
|
||||
Map<Integer, MessageGUID> m = this.m_inflightStore.get(clientID);
|
||||
if (m == null) {
|
||||
m = new HashMap<>();
|
||||
}
|
||||
m.put(messageID, guid);
|
||||
this.m_inflightStore.put(clientID, m);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the next valid packetIdentifier for the given client session.
|
||||
*/
|
||||
@Override
|
||||
public int nextPacketID(String clientID) {
|
||||
Map<Integer, MessageGUID> m = this.m_inflightStore.get(clientID);
|
||||
if (m == null) {
|
||||
m = new HashMap<>();
|
||||
int nextPacketId = 1;
|
||||
m.put(nextPacketId, null);
|
||||
return nextPacketId;
|
||||
}
|
||||
int maxId = Collections.max(m.keySet());
|
||||
int nextPacketId = (maxId + 1) % 0xFFFF;
|
||||
m.put(nextPacketId, null);
|
||||
return nextPacketId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockingQueue<StoredMessage> queue(String clientID) {
|
||||
return Utils.defaultGet(queues, clientID, new ArrayBlockingQueue<StoredMessage>(Constants.MAX_MESSAGE_QUEUE));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dropQueue(String clientID) {
|
||||
queues.remove(clientID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void moveInFlightToSecondPhaseAckWaiting(String clientID, int messageID) {
|
||||
LOG.info("acknowledging inflight clientID <{}> messageID {}", clientID, messageID);
|
||||
Map<Integer, MessageGUID> m = this.m_inflightStore.get(clientID);
|
||||
if (m == null) {
|
||||
LOG.error("Can't find the inFlight record for client <{}>", clientID);
|
||||
return;
|
||||
}
|
||||
MessageGUID guid = m.remove(messageID);
|
||||
|
||||
LOG.info("Moving to second phase store");
|
||||
Map<Integer, MessageGUID> messageIDs = Utils
|
||||
.defaultGet(m_secondPhaseStore, clientID, new HashMap<Integer, MessageGUID>());
|
||||
messageIDs.put(messageID, guid);
|
||||
m_secondPhaseStore.put(clientID, messageIDs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageGUID secondPhaseAcknowledged(String clientID, int messageID) {
|
||||
Map<Integer, MessageGUID> messageIDs = Utils
|
||||
.defaultGet(m_secondPhaseStore, clientID, new HashMap<Integer, MessageGUID>());
|
||||
MessageGUID guid = messageIDs.remove(messageID);
|
||||
m_secondPhaseStore.put(clientID, messageIDs);
|
||||
return guid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageGUID mapToGuid(String clientID, int messageID) {
|
||||
HashMap<Integer, MessageGUID> guids = (HashMap<Integer, MessageGUID>) Utils
|
||||
.defaultGet(m_messageToGuids, clientID, new HashMap<Integer, MessageGUID>());
|
||||
return guids.get(messageID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StoredMessage getInflightMessage(String clientID, int messageID) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInflightMessagesNo(String clientID) {
|
||||
Map<Integer, MessageGUID> inflightMessages = m_inflightStore.get(clientID);
|
||||
if (inflightMessages == null)
|
||||
return 0;
|
||||
else
|
||||
return inflightMessages.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPendingPublishMessagesNo(String clientID) {
|
||||
return m_messagesStore.getPendingPublishMessages(clientID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSecondPhaseAckPendingMessages(String clientID) {
|
||||
Map<Integer, MessageGUID> pendingAcks = m_secondPhaseStore.get(clientID);
|
||||
if (pendingAcks == null)
|
||||
return 0;
|
||||
else
|
||||
return pendingAcks.size();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2017 The original author or authorsgetRockQuestions()
|
||||
* 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
|
||||
@@ -78,4 +78,4 @@ package io.moquette.testembedded;
|
||||
mqttBroker.internalPublish(message, "INTRLPUB");
|
||||
System.out.println("After self publish");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user