Fixed implementation for HawtDB storage of subscriptions
This commit is contained in:
@@ -27,4 +27,6 @@ public interface IMessaging {
|
|||||||
* was subscribed
|
* was subscribed
|
||||||
*/
|
*/
|
||||||
void removeSubscriptions(String clientID);
|
void removeSubscriptions(String clientID);
|
||||||
|
|
||||||
|
void close();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,4 +58,10 @@ public class SimpleMessaging implements IMessaging {
|
|||||||
subscriptions.removeForClient(clientID);
|
subscriptions.removeForClient(clientID);
|
||||||
rwLock.writeLock().unlock();
|
rwLock.writeLock().unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void close() {
|
||||||
|
rwLock.writeLock().lock();
|
||||||
|
subscriptions.close();
|
||||||
|
rwLock.writeLock().unlock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.dna.mqtt.moquette.messaging.spi.impl;
|
package org.dna.mqtt.moquette.messaging.spi.impl;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
import org.dna.mqtt.moquette.proto.messages.AbstractMessage.QOSType;
|
import org.dna.mqtt.moquette.proto.messages.AbstractMessage.QOSType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -9,7 +10,7 @@ import org.dna.mqtt.moquette.proto.messages.AbstractMessage.QOSType;
|
|||||||
*
|
*
|
||||||
* @author andrea
|
* @author andrea
|
||||||
*/
|
*/
|
||||||
public class Subscription {
|
public class Subscription implements Serializable {
|
||||||
|
|
||||||
QOSType requestedQos;
|
QOSType requestedQos;
|
||||||
String clientId;
|
String clientId;
|
||||||
|
|||||||
+31
-15
@@ -9,8 +9,10 @@ import java.util.Map;
|
|||||||
import java.util.Queue;
|
import java.util.Queue;
|
||||||
import java.util.concurrent.LinkedBlockingDeque;
|
import java.util.concurrent.LinkedBlockingDeque;
|
||||||
import java.util.concurrent.LinkedBlockingQueue;
|
import java.util.concurrent.LinkedBlockingQueue;
|
||||||
|
import java.util.logging.Level;
|
||||||
import org.dna.mqtt.moquette.MQTTException;
|
import org.dna.mqtt.moquette.MQTTException;
|
||||||
import org.dna.mqtt.moquette.server.MQTTHandler;
|
import org.dna.mqtt.moquette.server.MQTTHandler;
|
||||||
|
import org.dna.mqtt.moquette.server.Server;
|
||||||
import org.fusesource.hawtbuf.codec.StringCodec;
|
import org.fusesource.hawtbuf.codec.StringCodec;
|
||||||
import org.fusesource.hawtdb.api.BTreeIndexFactory;
|
import org.fusesource.hawtdb.api.BTreeIndexFactory;
|
||||||
import org.fusesource.hawtdb.api.PageFile;
|
import org.fusesource.hawtdb.api.PageFile;
|
||||||
@@ -197,41 +199,46 @@ public class SubscriptionsStore {
|
|||||||
}
|
}
|
||||||
// private List<Subscription> subscriptions = new ArrayList<Subscription>();
|
// private List<Subscription> subscriptions = new ArrayList<Subscription>();
|
||||||
private TreeNode subscriptions = new TreeNode(null);
|
private TreeNode subscriptions = new TreeNode(null);
|
||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(SubscriptionsStore.class);
|
private static final Logger LOG = LoggerFactory.getLogger(SubscriptionsStore.class);
|
||||||
|
|
||||||
//pesistent Map of clientID, list of Subscriptions
|
//pesistent Map of clientID, list of Subscriptions
|
||||||
private SortedIndex<String, List<Subscription>> m_persistent;
|
private SortedIndex<String, List<Subscription>> m_persistent;
|
||||||
private PageFile pageFile;
|
// private PageFile pageFile;
|
||||||
|
private PageFileFactory factory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize basic store structures, like the FS storage to maintain
|
* Initialize basic store structures, like the FS storage to maintain
|
||||||
* client's topics subscriptions
|
* client's topics subscriptions
|
||||||
*/
|
*/
|
||||||
public void init() {
|
public void init() {
|
||||||
PageFileFactory factory = new PageFileFactory();
|
/*StringBuilder storeFile = new StringBuilder();
|
||||||
|
storeFile.append(System.getProperty("user.home"))
|
||||||
|
.append(File.separator).append("moquette_store.hawtdb");*/
|
||||||
|
String storeFile = Server.STORAGE_FILE_PATH;
|
||||||
|
|
||||||
|
factory = new PageFileFactory();
|
||||||
File tmpFile;
|
File tmpFile;
|
||||||
try {
|
try {
|
||||||
tmpFile = File.createTempFile("hawtdb", "test");
|
tmpFile = new File(storeFile.toString());
|
||||||
|
tmpFile.createNewFile();
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
LOG.error(null, ex);
|
LOG.error(null, ex);
|
||||||
throw new MQTTException("Can't create temp file for subscriptions storage", ex);
|
throw new MQTTException("Can't create temp file for subscriptions storage [" + storeFile + "]", ex);
|
||||||
}
|
}
|
||||||
factory.setFile(/*new File("mydb.dat")*/tmpFile);
|
factory.setFile(/*new File("mydb.dat")*/tmpFile);
|
||||||
factory.open();
|
factory.open();
|
||||||
pageFile = factory.getPageFile();
|
PageFile pageFile = factory.getPageFile();
|
||||||
|
|
||||||
// HashIndexFactory<String, List<Subscription>> indexFactory =
|
// HashIndexFactory<String, List<Subscription>> indexFactory =
|
||||||
// new HashIndexFactory<String, List<Subscription>>();
|
// new HashIndexFactory<String, List<Subscription>>();
|
||||||
BTreeIndexFactory<String, List<Subscription>> indexFactory =
|
BTreeIndexFactory<String, List<Subscription>> indexFactory =
|
||||||
new BTreeIndexFactory<String, List<Subscription>>();
|
new BTreeIndexFactory<String, List<Subscription>>();
|
||||||
indexFactory.setKeyCodec(StringCodec.INSTANCE);
|
indexFactory.setKeyCodec(StringCodec.INSTANCE);
|
||||||
|
|
||||||
m_persistent = indexFactory.openOrCreate(pageFile);
|
m_persistent = indexFactory.openOrCreate(pageFile);
|
||||||
|
|
||||||
//reaload any subscriptions persisted
|
//reaload any subscriptions persisted
|
||||||
LOG.debug("Reloading all stored subscriptions...");
|
LOG.debug("Reloading all stored subscriptions...");
|
||||||
for(Map.Entry<String, List<Subscription>> entry: m_persistent) {
|
for (Map.Entry<String, List<Subscription>> entry : m_persistent) {
|
||||||
for (Subscription subscription : entry.getValue()) {
|
for (Subscription subscription : entry.getValue()) {
|
||||||
addDirect(subscription);
|
addDirect(subscription);
|
||||||
}
|
}
|
||||||
@@ -239,7 +246,15 @@ public class SubscriptionsStore {
|
|||||||
LOG.debug("Finished loading");
|
LOG.debug("Finished loading");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void close() {
|
||||||
|
try {
|
||||||
|
factory.close();
|
||||||
|
} catch (IOException ex) {
|
||||||
|
//TODO handle probably firing
|
||||||
|
LOG.error(null, ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected void addDirect(Subscription newSubscription) {
|
protected void addDirect(Subscription newSubscription) {
|
||||||
List<Token> tokens = new ArrayList<Token>();
|
List<Token> tokens = new ArrayList<Token>();
|
||||||
try {
|
try {
|
||||||
@@ -275,10 +290,11 @@ public class SubscriptionsStore {
|
|||||||
if (!m_persistent.containsKey(clientID)) {
|
if (!m_persistent.containsKey(clientID)) {
|
||||||
m_persistent.put(clientID, new ArrayList<Subscription>());
|
m_persistent.put(clientID, new ArrayList<Subscription>());
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Subscription> subs = m_persistent.get(clientID);
|
List<Subscription> subs = m_persistent.get(clientID);
|
||||||
subs.add(newSubscription);
|
subs.add(newSubscription);
|
||||||
pageFile.flush();
|
m_persistent.put(clientID, subs);
|
||||||
|
// pageFile.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -286,7 +302,7 @@ public class SubscriptionsStore {
|
|||||||
*/
|
*/
|
||||||
public void removeForClient(String clientID) {
|
public void removeForClient(String clientID) {
|
||||||
subscriptions.removeClientSubscriptions(clientID);
|
subscriptions.removeClientSubscriptions(clientID);
|
||||||
|
|
||||||
//remove from log all subscriptions
|
//remove from log all subscriptions
|
||||||
m_persistent.remove(clientID);
|
m_persistent.remove(clientID);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.dna.mqtt.moquette.server;
|
package org.dna.mqtt.moquette.server;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import org.apache.mina.core.service.IoAcceptor;
|
import org.apache.mina.core.service.IoAcceptor;
|
||||||
@@ -36,7 +37,10 @@ public class Server {
|
|||||||
|
|
||||||
public static final int PORT = 9191;
|
public static final int PORT = 9191;
|
||||||
public static final int DEFAULT_CONNECT_TIMEOUT = 10;
|
public static final int DEFAULT_CONNECT_TIMEOUT = 10;
|
||||||
|
public static final String STORAGE_FILE_PATH = System.getProperty("user.home") +
|
||||||
|
File.separator + "moquette_store.hawtdb";
|
||||||
private IoAcceptor m_acceptor;
|
private IoAcceptor m_acceptor;
|
||||||
|
SimpleMessaging messaging;
|
||||||
|
|
||||||
public static void main(String[] args) throws IOException {
|
public static void main(String[] args) throws IOException {
|
||||||
new Server().startServer();
|
new Server().startServer();
|
||||||
@@ -64,7 +68,7 @@ public class Server {
|
|||||||
m_acceptor.getFilterChain().addLast( "codec", new ProtocolCodecFilter(encoder, decoder));
|
m_acceptor.getFilterChain().addLast( "codec", new ProtocolCodecFilter(encoder, decoder));
|
||||||
|
|
||||||
MQTTHandler handler = new MQTTHandler();
|
MQTTHandler handler = new MQTTHandler();
|
||||||
SimpleMessaging messaging = new SimpleMessaging();
|
messaging = new SimpleMessaging();
|
||||||
//TODO fix this hugly wiring
|
//TODO fix this hugly wiring
|
||||||
handler.setMessaging(messaging);
|
handler.setMessaging(messaging);
|
||||||
messaging.setNotifier(handler);
|
messaging.setNotifier(handler);
|
||||||
@@ -89,6 +93,9 @@ public class Server {
|
|||||||
|
|
||||||
protected void stopServer() {
|
protected void stopServer() {
|
||||||
LOG.info("Server stopping...");
|
LOG.info("Server stopping...");
|
||||||
|
|
||||||
|
messaging.close();
|
||||||
|
|
||||||
for(IoSession session: m_acceptor.getManagedSessions().values()) {
|
for(IoSession session: m_acceptor.getManagedSessions().values()) {
|
||||||
if(session.isConnected() && !session.isClosing()){
|
if(session.isConnected() && !session.isClosing()){
|
||||||
session.close(false);
|
session.close(false);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.dna.mqtt.moquette.server;
|
package org.dna.mqtt.moquette.server;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import org.dna.mqtt.moquette.client.Client;
|
import org.dna.mqtt.moquette.client.Client;
|
||||||
import org.dna.mqtt.moquette.client.IPublishCallback;
|
import org.dna.mqtt.moquette.client.IPublishCallback;
|
||||||
@@ -30,6 +31,10 @@ public class ServerIntegrationTest {
|
|||||||
@After
|
@After
|
||||||
public void tearDown() {
|
public void tearDown() {
|
||||||
server.stopServer();
|
server.stopServer();
|
||||||
|
File dbFile = new File(Server.STORAGE_FILE_PATH);
|
||||||
|
if (dbFile.exists()) {
|
||||||
|
dbFile.delete();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,65 @@
|
|||||||
|
@Grab(group='org.fusesource.hawtdb', module='hawtdb', version='1.6')
|
||||||
|
|
||||||
|
import org.fusesource.hawtbuf.codec.StringCodec
|
||||||
|
import org.fusesource.hawtdb.api.BTreeIndexFactory
|
||||||
|
import org.fusesource.hawtdb.api.PageFile
|
||||||
|
import org.fusesource.hawtdb.api.PageFileFactory
|
||||||
|
import org.fusesource.hawtdb.api.SortedIndex
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
File tmpFile = new File(System.getProperty("user.home") + File.separator + "test_hawtdb.dat")
|
||||||
|
//tmpFile.createNewFile()
|
||||||
|
|
||||||
|
def writeSomeData(tmpFile) {
|
||||||
|
def factory = new PageFileFactory()
|
||||||
|
factory.file = tmpFile
|
||||||
|
factory.open()
|
||||||
|
def pageFile = factory.getPageFile()
|
||||||
|
|
||||||
|
BTreeIndexFactory<String, String> indexFactory = new BTreeIndexFactory<String, String>()
|
||||||
|
indexFactory.setKeyCodec(StringCodec.INSTANCE)
|
||||||
|
|
||||||
|
SortedIndex<String, String> writeIndex = indexFactory.openOrCreate(pageFile)
|
||||||
|
println "Hawt opened, try to read"
|
||||||
|
println "Size: ${writeIndex.size()}"
|
||||||
|
|
||||||
|
println "write something"
|
||||||
|
writeIndex.put("titolo", "capucetto rosso")
|
||||||
|
pageFile.flush()
|
||||||
|
|
||||||
|
println "check after write content: ${writeIndex.get("titolo")}, size ${writeIndex.size()}"
|
||||||
|
|
||||||
|
//pageFile.close()
|
||||||
|
factory.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def readAllFileContents(tmpFile) {
|
||||||
|
def factory = new PageFileFactory()
|
||||||
|
factory.file = tmpFile
|
||||||
|
factory.open()
|
||||||
|
def pageFile = factory.getPageFile()
|
||||||
|
|
||||||
|
BTreeIndexFactory<String, String> indexFactory = new BTreeIndexFactory<String, String>()
|
||||||
|
indexFactory.setKeyCodec(StringCodec.INSTANCE)
|
||||||
|
|
||||||
|
SortedIndex<String, String> readingIndex = indexFactory.openOrCreate(pageFile)
|
||||||
|
|
||||||
|
println "read something, size ${readingIndex.size()}"
|
||||||
|
for (def entry : readingIndex) {
|
||||||
|
println "read k, v -> ${entry.key}, ${entry.value} "
|
||||||
|
}
|
||||||
|
|
||||||
|
factory.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
writeSomeData(tmpFile)
|
||||||
|
readAllFileContents(tmpFile)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
@Grab(group='org.fusesource.hawtdb', module='hawtdb', version='1.6')
|
||||||
|
|
||||||
|
import org.fusesource.hawtbuf.codec.StringCodec
|
||||||
|
import org.fusesource.hawtdb.api.BTreeIndexFactory
|
||||||
|
import org.fusesource.hawtdb.api.PageFile
|
||||||
|
import org.fusesource.hawtdb.api.PageFileFactory
|
||||||
|
import org.fusesource.hawtdb.api.SortedIndex
|
||||||
|
import org.fusesource.hawtbuf.Buffer
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
File tmpFile = new File(System.getProperty("user.home") + File.separator + "test_hawtdb.dat")
|
||||||
|
//tmpFile.createNewFile()
|
||||||
|
|
||||||
|
def writeSomeData(tmpFile) {
|
||||||
|
def factory = new PageFileFactory()
|
||||||
|
factory.file = tmpFile
|
||||||
|
factory.open()
|
||||||
|
def pageFile = factory.getPageFile()
|
||||||
|
int pageIdx = pageFile.alloc()
|
||||||
|
pageFile.write(pageIdx, new Buffer("Test".getBytes()))
|
||||||
|
println "writed at page index ${pageIdx}"
|
||||||
|
pageFile.flush()
|
||||||
|
pageFile.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def readTest(tmpFile) {
|
||||||
|
def factory = new PageFileFactory()
|
||||||
|
factory.file = tmpFile
|
||||||
|
factory.open()
|
||||||
|
def pageFile = factory.getPageFile()
|
||||||
|
println "page size is: " + pageFile.pageSize
|
||||||
|
Buffer b = new Buffer(512)
|
||||||
|
pageFile.read(0, b)
|
||||||
|
println "content try read " + b
|
||||||
|
|
||||||
|
pageFile.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
writeSomeData(tmpFile)
|
||||||
|
readTest(tmpFile)
|
||||||
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user