diff --git a/broker/src/main/java/org/eclipse/moquette/interception/DefaultInterceptHandler.java b/broker/src/main/java/org/eclipse/moquette/interception/DefaultInterceptHandler.java
new file mode 100644
index 00000000..44cbd748
--- /dev/null
+++ b/broker/src/main/java/org/eclipse/moquette/interception/DefaultInterceptHandler.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2012-2014 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 org.eclipse.moquette.interception;
+
+import org.eclipse.moquette.proto.messages.*;
+import org.eclipse.moquette.spi.impl.subscriptions.Subscription;
+
+/**
+ * Default empty implementation that can be used to create custom handlers
+ * without defining every method.
+ *
+ * @author Wagner Macedo
+ */
+public class DefaultInterceptHandler implements InterceptHandler {
+ @Override
+ public void onConnect(ConnectMessage msg) {
+ }
+
+ @Override
+ public void onDisconnect(String clientID) {
+ }
+
+ @Override
+ public void onPublish(PublishMessage msg) {
+ }
+
+ @Override
+ public void onSubscribe(Subscription sub) {
+ }
+
+ @Override
+ public void onUnsubscribe(Subscription sub) {
+ }
+}
diff --git a/broker/src/main/java/org/eclipse/moquette/interception/InterceptHandler.java b/broker/src/main/java/org/eclipse/moquette/interception/InterceptHandler.java
new file mode 100644
index 00000000..97fca7fd
--- /dev/null
+++ b/broker/src/main/java/org/eclipse/moquette/interception/InterceptHandler.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2012-2014 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 org.eclipse.moquette.interception;
+
+import org.eclipse.moquette.proto.messages.*;
+import org.eclipse.moquette.spi.impl.subscriptions.Subscription;
+
+/**
+ * This interface is used to inject code for intercepting broker events.
+ *
+ * The events can act only as observers.
+ *
+ * Almost every method receives a subclass of {@link AbstractMessage}, except
+ * onDisconnect that receives the client id string and
+ * onSubscribe and onUnsubscribe that receive a
+ * {@link Subscription} object.
+ *
+ * @author Wagner Macedo
+ */
+public interface InterceptHandler {
+
+ void onConnect(ConnectMessage msg);
+
+ void onDisconnect(String clientID);
+
+ void onPublish(PublishMessage msg);
+
+ void onSubscribe(Subscription sub);
+
+ void onUnsubscribe(Subscription sub);
+}
diff --git a/broker/src/main/java/org/eclipse/moquette/interception/Interceptor.java b/broker/src/main/java/org/eclipse/moquette/interception/Interceptor.java
new file mode 100644
index 00000000..62dfe1f8
--- /dev/null
+++ b/broker/src/main/java/org/eclipse/moquette/interception/Interceptor.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2012-2014 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 org.eclipse.moquette.interception;
+
+import org.eclipse.moquette.proto.messages.ConnectMessage;
+import org.eclipse.moquette.proto.messages.PublishMessage;
+import org.eclipse.moquette.spi.impl.subscriptions.Subscription;
+
+/**
+ * This interface is to be used internally by the broker components.
+ *
+ * An interface is used instead of a class to allow more flexibility in changing
+ * an implementation. For instance, this interface provide a lightweight
+ * no-handler interceptor.
+ *
+ * Interceptor implementations forward notifications to a InterceptHandler,
+ * 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 {
+ /**
+ * When no custom handler is provided, this interceptor should be used.
+ */
+ Interceptor NO_HANDLER_INTERCEPTOR = new NoHandlerInterceptor();
+
+ void notifyClientConnected(ConnectMessage msg);
+
+ void notifyClientDisconnected(String clientID);
+
+ void notifyTopicPublished(PublishMessage msg);
+
+ void notifyTopicSubscribed(Subscription sub);
+
+ void notifyTopicUnsubscribed(Subscription sub);
+
+}
diff --git a/broker/src/main/java/org/eclipse/moquette/interception/NoHandlerInterceptor.java b/broker/src/main/java/org/eclipse/moquette/interception/NoHandlerInterceptor.java
new file mode 100644
index 00000000..b1dcf293
--- /dev/null
+++ b/broker/src/main/java/org/eclipse/moquette/interception/NoHandlerInterceptor.java
@@ -0,0 +1,33 @@
+package org.eclipse.moquette.interception;
+
+import org.eclipse.moquette.proto.messages.ConnectMessage;
+import org.eclipse.moquette.proto.messages.PublishMessage;
+import org.eclipse.moquette.spi.impl.subscriptions.Subscription;
+
+/**
+ * @author Wagner Macedo
+ */
+public class NoHandlerInterceptor implements Interceptor {
+ NoHandlerInterceptor() {
+ }
+
+ @Override
+ public void notifyClientConnected(ConnectMessage msg) {
+ }
+
+ @Override
+ public void notifyClientDisconnected(String clientID) {
+ }
+
+ @Override
+ public void notifyTopicPublished(PublishMessage msg) {
+ }
+
+ @Override
+ public void notifyTopicSubscribed(Subscription sub) {
+ }
+
+ @Override
+ public void notifyTopicUnsubscribed(Subscription sub) {
+ }
+}
diff --git a/broker/src/main/java/org/eclipse/moquette/server/Server.java b/broker/src/main/java/org/eclipse/moquette/server/Server.java
index 5f5a2573..b07ecc9f 100644
--- a/broker/src/main/java/org/eclipse/moquette/server/Server.java
+++ b/broker/src/main/java/org/eclipse/moquette/server/Server.java
@@ -15,6 +15,7 @@
*/
package org.eclipse.moquette.server;
+import org.eclipse.moquette.interception.InterceptHandler;
import org.eclipse.moquette.server.netty.NettyAcceptor;
import org.eclipse.moquette.spi.impl.SimpleMessaging;
import org.slf4j.Logger;
@@ -86,8 +87,12 @@ public class Server {
*
*/
public void startServer(Properties configProps) throws IOException {
- ConfigurationParser confParser = new ConfigurationParser(configProps);
+ ConfigurationParser confParser = new ConfigurationParser(configProps);
m_properties = confParser.getProperties();
+ final String handlerProp = System.getProperty("intercept.handler");
+ if (handlerProp != null) {
+ m_properties.setProperty("intercept.handler", handlerProp);
+ }
LOG.info("Persistent store file: " + m_properties.get(PERSISTENT_STORE_PROPERTY_NAME));
messaging = SimpleMessaging.getInstance();
messaging.init(m_properties);
diff --git a/broker/src/main/java/org/eclipse/moquette/spi/impl/BrokerInterceptor.java b/broker/src/main/java/org/eclipse/moquette/spi/impl/BrokerInterceptor.java
new file mode 100644
index 00000000..44b0013e
--- /dev/null
+++ b/broker/src/main/java/org/eclipse/moquette/spi/impl/BrokerInterceptor.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2012-2014 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 org.eclipse.moquette.spi.impl;
+
+import org.eclipse.moquette.interception.InterceptHandler;
+import org.eclipse.moquette.interception.Interceptor;
+import org.eclipse.moquette.proto.messages.ConnectMessage;
+import org.eclipse.moquette.proto.messages.PublishMessage;
+import org.eclipse.moquette.spi.impl.subscriptions.Subscription;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+/**
+ * An interceptor that execute the interception tasks asynchronously.
+ *
+ * @author Wagner Macedo
+ */
+final class BrokerInterceptor implements Interceptor {
+ private final InterceptHandler handler;
+ private final ExecutorService executor;
+
+ BrokerInterceptor(InterceptHandler handler) {
+ this.handler = handler;
+ executor = Executors.newFixedThreadPool(1);
+ }
+
+ /**
+ * Shutdown graciously the executor service
+ */
+ void stop() {
+ executor.shutdown();
+ }
+
+ @Override
+ public void notifyClientConnected(final ConnectMessage msg) {
+ executor.execute(new Runnable() {
+ @Override
+ public void run() {
+ handler.onConnect(msg.readOnlyClone());
+ }
+ });
+ }
+
+ @Override
+ public void notifyClientDisconnected(final String clientID) {
+ executor.execute(new Runnable() {
+ @Override
+ public void run() {
+ handler.onDisconnect(clientID);
+ }
+ });
+ }
+
+ @Override
+ public void notifyTopicPublished(final PublishMessage msg) {
+ executor.execute(new Runnable() {
+ @Override
+ public void run() {
+ handler.onPublish(msg.readOnlyClone());
+ }
+ });
+ }
+
+ @Override
+ public void notifyTopicSubscribed(final Subscription sub) {
+ executor.execute(new Runnable() {
+ @Override
+ public void run() {
+ handler.onSubscribe(sub.clone());
+ }
+ });
+ }
+
+ @Override
+ public void notifyTopicUnsubscribed(final Subscription sub) {
+ executor.execute(new Runnable() {
+ @Override
+ public void run() {
+ handler.onUnsubscribe(sub.clone());
+ }
+ });
+ }
+}
diff --git a/broker/src/main/java/org/eclipse/moquette/spi/impl/ProtocolProcessor.java b/broker/src/main/java/org/eclipse/moquette/spi/impl/ProtocolProcessor.java
index 6cc139ae..5e363c8e 100644
--- a/broker/src/main/java/org/eclipse/moquette/spi/impl/ProtocolProcessor.java
+++ b/broker/src/main/java/org/eclipse/moquette/spi/impl/ProtocolProcessor.java
@@ -25,6 +25,7 @@ import java.util.concurrent.Executors;
import io.netty.util.internal.StringUtil;
import org.apache.commons.codec.binary.StringUtils;
+import org.eclipse.moquette.interception.Interceptor;
import org.eclipse.moquette.server.netty.NettyChannel;
import org.eclipse.moquette.spi.IMatchingCondition;
import org.eclipse.moquette.spi.IMessagesStore;
@@ -105,6 +106,8 @@ class ProtocolProcessor implements EventHandler {
private IMessagesStore m_messagesStore;
private ISessionsStore m_sessionsStore;
private IAuthenticator m_authenticator;
+ private Interceptor interceptor = Interceptor.NO_HANDLER_INTERCEPTOR;
+
//maps clientID to Will testament, if specified on CONNECT
private Map m_willStore = new HashMap<>();
@@ -113,6 +116,13 @@ class ProtocolProcessor implements EventHandler {
ProtocolProcessor() {}
+ void init(SubscriptionsStore subscriptions, IMessagesStore storageService,
+ ISessionsStore sessionsStore,
+ IAuthenticator authenticator,
+ boolean allowAnonymous, IAuthorizator authorizator) {
+ init(subscriptions, storageService, sessionsStore, authenticator, allowAnonymous, authorizator, null);
+ }
+
/**
* @param subscriptions the subscription store where are stored all the existing
* clients subscriptions.
@@ -122,12 +132,16 @@ class ProtocolProcessor implements EventHandler {
* @param authenticator the authenticator used in connect messages.
* @param allowAnonymous true connection to clients without credentials.
* @param authorizator used to apply ACL policies to publishes and subscriptions.
+ * @param interceptor to notify events to an intercept handler
*/
void init(SubscriptionsStore subscriptions, IMessagesStore storageService,
ISessionsStore sessionsStore,
IAuthenticator authenticator,
- boolean allowAnonymous, IAuthorizator authorizator) {
+ boolean allowAnonymous, IAuthorizator authorizator, Interceptor interceptor) {
//m_clientIDs = clientIDs;
+ if (interceptor != null) {
+ this.interceptor = interceptor;
+ }
this.subscriptions = subscriptions;
this.allowAnonymous = allowAnonymous;
m_authorizator = authorizator;
@@ -163,6 +177,7 @@ class ProtocolProcessor implements EventHandler {
ConnAckMessage okResp = new ConnAckMessage();
okResp.setReturnCode(ConnAckMessage.IDENTIFIER_REJECTED);
session.write(okResp);
+ interceptor.notifyClientConnected(msg);
return;
}
@@ -238,6 +253,7 @@ class ProtocolProcessor implements EventHandler {
okResp.setSessionPresent(true);
}
session.write(okResp);
+ interceptor.notifyClientConnected(msg);
LOG.info("Create persistent session for clientID <{}>", msg.getClientID());
m_sessionsStore.addNewSubscription(Subscription.createEmptySubscription(msg.getClientID(), true)); //null means EmptySubscription
@@ -301,6 +317,7 @@ class ProtocolProcessor implements EventHandler {
String user = (String) session.getAttribute(NettyChannel.ATTR_KEY_USERNAME);
if (m_authorizator.canWrite(topic, user, clientID)) {
executePublish(clientID, msg);
+ interceptor.notifyTopicPublished(msg);
} else {
LOG.debug("topic {} doesn't have write credentials", topic);
}
@@ -559,6 +576,7 @@ class ProtocolProcessor implements EventHandler {
m_willStore.remove(clientID);
LOG.info("DISCONNECT client <{}> with clean session {}", clientID, cleanSession);
+ interceptor.notifyClientDisconnected(clientID);
}
void processConnectionLost(LostConnectionEvent evt) {
diff --git a/broker/src/main/java/org/eclipse/moquette/spi/impl/SimpleMessaging.java b/broker/src/main/java/org/eclipse/moquette/spi/impl/SimpleMessaging.java
index 46f90b06..0eb81d18 100644
--- a/broker/src/main/java/org/eclipse/moquette/spi/impl/SimpleMessaging.java
+++ b/broker/src/main/java/org/eclipse/moquette/spi/impl/SimpleMessaging.java
@@ -19,6 +19,8 @@ import com.lmax.disruptor.EventHandler;
import com.lmax.disruptor.RingBuffer;
import com.lmax.disruptor.dsl.Disruptor;
import org.HdrHistogram.Histogram;
+import org.eclipse.moquette.interception.InterceptHandler;
+import org.eclipse.moquette.interception.Interceptor;
import org.eclipse.moquette.proto.messages.AbstractMessage;
import org.eclipse.moquette.spi.impl.security.*;
import org.eclipse.moquette.server.ServerChannel;
@@ -55,7 +57,7 @@ import static org.eclipse.moquette.commons.Constants.ACL_FILE_PROPERTY_NAME;
* Singleton class that orchestrate the execution of the protocol.
*
* Uses the LMAX Disruptor to serialize the incoming, requests, because it work in a evented fashion;
- * the requests income from front Netty connectors and are dispatched to the
+ * the requests income from front Netty connectors and are dispatched to the
* ProtocolProcessor.
*
* @author andrea
@@ -63,9 +65,9 @@ import static org.eclipse.moquette.commons.Constants.ACL_FILE_PROPERTY_NAME;
public class SimpleMessaging implements IMessaging, EventHandler {
private static final Logger LOG = LoggerFactory.getLogger(SimpleMessaging.class);
-
+
private SubscriptionsStore subscriptions;
-
+
private RingBuffer m_ringBuffer;
private IMessagesStore m_storageService;
@@ -73,6 +75,7 @@ public class SimpleMessaging implements IMessaging, EventHandler {
private ExecutorService m_executor;
private Disruptor m_disruptor;
+ private Interceptor interceptor;
private static SimpleMessaging INSTANCE;
@@ -192,9 +195,16 @@ public class SimpleMessaging implements IMessaging, EventHandler {
m_storageService.initStore();
+ try {
+ final InterceptHandler handler = Class.forName(props.getProperty("intercept.handler"))
+ .asSubclass(InterceptHandler.class).newInstance();
+ interceptor = new BrokerInterceptor(handler);
+ } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | NullPointerException | ClassCastException ignored) {
+ }
+
//List storedSubscriptions = m_sessionsStore.listAllSubscriptions();
//subscriptions.init(storedSubscriptions);
- subscriptions.init(m_sessionsStore);
+ subscriptions.init(m_sessionsStore, interceptor);
String configPath = System.getProperty("moquette.path", null);
String authenticatorClassName = props.getProperty(Constants.AUTHENTICATOR_CLASS_NAME, "");
@@ -240,7 +250,7 @@ public class SimpleMessaging implements IMessaging, EventHandler {
}
boolean allowAnonymous = Boolean.parseBoolean(props.getProperty(ALLOW_ANONYMOUS_PROPERTY_NAME, "true"));
- m_processor.init(subscriptions, m_storageService, m_sessionsStore, authenticator, allowAnonymous, authorizator);
+ m_processor.init(subscriptions, m_storageService, m_sessionsStore, authenticator, allowAnonymous, authorizator, interceptor);
}
private Object loadClass(String className, Class> cls) {
@@ -280,6 +290,9 @@ public class SimpleMessaging implements IMessaging, EventHandler {
private void processStop() {
LOG.debug("processStop invoked");
+ if (interceptor != null) {
+ ((BrokerInterceptor) interceptor).stop();
+ }
m_storageService.close();
LOG.debug("subscription tree {}", subscriptions.dumpTree());
// m_eventProcessor.halt();
diff --git a/broker/src/main/java/org/eclipse/moquette/spi/impl/subscriptions/Subscription.java b/broker/src/main/java/org/eclipse/moquette/spi/impl/subscriptions/Subscription.java
index a4b41bc1..739402bf 100644
--- a/broker/src/main/java/org/eclipse/moquette/spi/impl/subscriptions/Subscription.java
+++ b/broker/src/main/java/org/eclipse/moquette/spi/impl/subscriptions/Subscription.java
@@ -106,4 +106,13 @@ public class Subscription implements Serializable {
public String toString() {
return String.format("[filter:%s, cliID: %s, qos: %s, active: %s]", this.topicFilter, this.clientId, this.requestedQos, this.active);
}
+
+ @Override
+ public Subscription clone() {
+ try {
+ return (Subscription) super.clone();
+ } catch (CloneNotSupportedException e) {
+ return null;
+ }
+ }
}
diff --git a/broker/src/main/java/org/eclipse/moquette/spi/impl/subscriptions/SubscriptionsStore.java b/broker/src/main/java/org/eclipse/moquette/spi/impl/subscriptions/SubscriptionsStore.java
index dff1c5ea..8b1a5e53 100644
--- a/broker/src/main/java/org/eclipse/moquette/spi/impl/subscriptions/SubscriptionsStore.java
+++ b/broker/src/main/java/org/eclipse/moquette/spi/impl/subscriptions/SubscriptionsStore.java
@@ -19,6 +19,7 @@ import java.text.ParseException;
import java.util.*;
import java.util.concurrent.LinkedBlockingDeque;
+import org.eclipse.moquette.interception.Interceptor;
import org.eclipse.moquette.spi.ISessionsStore;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -30,6 +31,8 @@ import org.slf4j.LoggerFactory;
*/
public class SubscriptionsStore {
+ private Interceptor interceptor = Interceptor.NO_HANDLER_INTERCEPTOR;
+
/**
* Check if the topic filter of the subscription is well formed
* */
@@ -96,9 +99,20 @@ public class SubscriptionsStore {
/**
* Initialize the subscription tree with the list of subscriptions.
+ * Maintained for compatibility reasons.
*/
public void init(ISessionsStore sessionsStore) {
+ init(sessionsStore, null);
+ }
+
+ /**
+ * Initialize the subscription tree with the list of subscriptions and an interceptor.
+ */
+ public void init(ISessionsStore sessionsStore, Interceptor interceptor) {
LOG.debug("init invoked");
+ if (interceptor != null) {
+ this.interceptor = interceptor;
+ }
m_sessionsStore = sessionsStore;
List subscriptions = sessionsStore.listAllSubscriptions();
//reload any subscriptions persisted
@@ -118,6 +132,7 @@ public class SubscriptionsStore {
protected void addDirect(Subscription newSubscription) {
TreeNode current = findMatchingNode(newSubscription.topicFilter);
current.addSubscription(newSubscription);
+ interceptor.notifyTopicSubscribed(newSubscription);
}
private TreeNode findMatchingNode(String topic) {
@@ -171,6 +186,7 @@ public class SubscriptionsStore {
if (toBeRemoved != null) {
matchNode.subscriptions().remove(toBeRemoved);
+ interceptor.notifyTopicUnsubscribed(toBeRemoved);
}
}
@@ -184,6 +200,7 @@ public class SubscriptionsStore {
List allSubscriptions = subsCollector.getResult();
for (Subscription subscription : allSubscriptions) {
removeSubscription(subscription.getTopicFilter(), subscription.getClientId());
+ interceptor.notifyTopicUnsubscribed(subscription);
}
}
@@ -191,9 +208,16 @@ public class SubscriptionsStore {
* Visit the topics tree to remove matching subscriptions with clientID
*/
public void removeForClient(String clientID) {
- subscriptions.removeClientSubscriptions(clientID);
+ final List clientSubscriptions = new ArrayList<>();
+
+ subscriptions.removeClientSubscriptions(clientID, clientSubscriptions);
//persist the update
m_sessionsStore.wipeSubscriptions(clientID);
+
+ // Notify the unsubscribe event
+ for (Subscription sub : clientSubscriptions) {
+ interceptor.notifyTopicUnsubscribed(sub);
+ }
}
public void deactivate(String clientID) {
diff --git a/broker/src/main/java/org/eclipse/moquette/spi/impl/subscriptions/TreeNode.java b/broker/src/main/java/org/eclipse/moquette/spi/impl/subscriptions/TreeNode.java
index 2a165ff0..70c6a738 100644
--- a/broker/src/main/java/org/eclipse/moquette/spi/impl/subscriptions/TreeNode.java
+++ b/broker/src/main/java/org/eclipse/moquette/spi/impl/subscriptions/TreeNode.java
@@ -130,7 +130,7 @@ class TreeNode {
return res;
}
- void removeClientSubscriptions(String clientID) {
+ void removeClientSubscriptions(String clientID, List collect) {
//collect what to delete and then delete to avoid ConcurrentModification
List subsToRemove = new ArrayList<>();
for (Subscription s : m_subscriptions) {
@@ -143,9 +143,11 @@ class TreeNode {
m_subscriptions.remove(s);
}
+ collect.addAll(subsToRemove);
+
//go deep
for (TreeNode child : m_children) {
- child.removeClientSubscriptions(clientID);
+ child.removeClientSubscriptions(clientID, collect);
}
}
diff --git a/broker/src/test/java/org/eclipse/moquette/spi/impl/BrokerInterceptorTest.java b/broker/src/test/java/org/eclipse/moquette/spi/impl/BrokerInterceptorTest.java
new file mode 100644
index 00000000..2a6bc70c
--- /dev/null
+++ b/broker/src/test/java/org/eclipse/moquette/spi/impl/BrokerInterceptorTest.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2012-2014 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 org.eclipse.moquette.spi.impl;
+
+import org.eclipse.moquette.interception.InterceptHandler;
+import org.eclipse.moquette.proto.messages.AbstractMessage;
+import org.eclipse.moquette.proto.messages.ConnectMessage;
+import org.eclipse.moquette.proto.messages.PublishMessage;
+import org.eclipse.moquette.spi.impl.subscriptions.Subscription;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+import static org.junit.Assert.*;
+
+/**
+ * @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 BrokerInterceptor interceptor = new BrokerInterceptor(new InterceptHandler() {
+ @Override
+ public void onConnect(ConnectMessage msg) {
+ n.set(40);
+ }
+
+ @Override
+ public void onDisconnect(String clientID) {
+ n.set(50);
+ }
+
+ @Override
+ public void onPublish(PublishMessage msg) {
+ n.set(60);
+ }
+
+ @Override
+ public void onSubscribe(Subscription sub) {
+ n.set(70);
+ }
+
+ @Override
+ public void onUnsubscribe(Subscription sub) {
+ n.set(80);
+ }
+ });
+
+ @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(new ConnectMessage());
+ interval();
+ assertEquals(40, n.get());
+ }
+
+ @Test
+ public void testNotifyClientDisconnected() throws Exception {
+ interceptor.notifyClientDisconnected("cli1234");
+ interval();
+ assertEquals(50, n.get());
+ }
+
+ @Test
+ public void testNotifyTopicPublished() throws Exception {
+ interceptor.notifyTopicPublished(new PublishMessage());
+ interval();
+ assertEquals(60, n.get());
+ }
+
+ @Test
+ public void testNotifyTopicSubscribed() throws Exception {
+ interceptor.notifyTopicSubscribed(new Subscription("cli1", "o2", AbstractMessage.QOSType.MOST_ONE, true));
+ interval();
+ assertEquals(70, n.get());
+ }
+
+ @Test
+ public void testNotifyTopicUnsubscribed() throws Exception {
+ interceptor.notifyTopicUnsubscribed(new Subscription("cli1", "o2", AbstractMessage.QOSType.MOST_ONE, true));
+ interval();
+ assertEquals(80, n.get());
+ }
+}
diff --git a/parser_commons/src/main/java/org/eclipse/moquette/proto/messages/ConnectMessage.java b/parser_commons/src/main/java/org/eclipse/moquette/proto/messages/ConnectMessage.java
index 67b02c62..51896ba1 100644
--- a/parser_commons/src/main/java/org/eclipse/moquette/proto/messages/ConnectMessage.java
+++ b/parser_commons/src/main/java/org/eclipse/moquette/proto/messages/ConnectMessage.java
@@ -164,4 +164,12 @@ public class ConnectMessage extends AbstractMessage {
}
return base;
}
+
+ public ConnectMessage readOnlyClone() {
+ try {
+ return (ConnectMessage) super.clone();
+ } catch (CloneNotSupportedException e) {
+ return null;
+ }
+ }
}
diff --git a/parser_commons/src/main/java/org/eclipse/moquette/proto/messages/PublishMessage.java b/parser_commons/src/main/java/org/eclipse/moquette/proto/messages/PublishMessage.java
index bb8f1bf9..42aa23a5 100644
--- a/parser_commons/src/main/java/org/eclipse/moquette/proto/messages/PublishMessage.java
+++ b/parser_commons/src/main/java/org/eclipse/moquette/proto/messages/PublishMessage.java
@@ -54,4 +54,14 @@ public class PublishMessage extends MessageIDMessage {
public void setPayload(ByteBuffer payload) {
this.m_payload = payload;
}
+
+ public PublishMessage readOnlyClone() {
+ try {
+ final PublishMessage clone = (PublishMessage) super.clone();
+ clone.m_payload = this.m_payload.asReadOnlyBuffer();
+ return clone;
+ } catch (CloneNotSupportedException e) {
+ return null;
+ }
+ }
}