Removed the Mina parser
This commit is contained in:
Binary file not shown.
@@ -1,64 +0,0 @@
|
||||
Per Mina
|
||||
- per gestire l'inattività su una sessione usare
|
||||
acceptor.getSessionConfig().setIdleTime( IdleStatus.BOTH_IDLE, 10 );
|
||||
e
|
||||
nell'implementazione del IoHandlerAdapter ridefinire il metodo
|
||||
public void sessionIdle( IoSession session, IdleStatus status ) throws Exception
|
||||
|
||||
|
||||
|
||||
Glossario:
|
||||
|
||||
QOS: presente solo per i messaggi PUBLISH, definisce 3 livelli
|
||||
QoS0: at most once, cioe' viene notificato al piu' uno (il che' significa che
|
||||
potrebbe anche non essere notificato) equivalente a messaggi non persistenti di JMS
|
||||
QoS1: at least once, delivery equivalente ai messaggi persistenti di JMS
|
||||
Q0S2: exactly once, delivery assicurato equivalente ai messagi persistenti di JMS + DUPS_OK_ACKNOWLEDGE
|
||||
|
||||
|
||||
RETAIN: Usato solo per i messaggi di PUBLISH, se true dice di persistere il messaggio dopo esser stato notificato a tutti
|
||||
i sottoscritori del topic. Se in un secondo momento un cliente si sottoscrive ad un
|
||||
messaggio pubblicato con RETAIN = 1 allora anche il messaggio di notifica dovrà avere RETAIN = 1,
|
||||
altrimenti non accade nulla. I messaggi notificati al momento del PUBLISH non hanno flag RETAIN = 1
|
||||
serve solo per dire ai posteri che il messaggio che stanno ricevendo non è "live".
|
||||
Un server PUO' eliminare un messaggio marcato con RETAIN se ne arriva un'altro sullo stesso topic con lunghezza = 0 e RETAIN = 1.
|
||||
|
||||
CLEAN SESSION: è un flag disponibile per il solo messaggio di CONNECT.
|
||||
Se = 0 allora il server DEVE memorizzare(mantenere) le sottoscrizioni del client
|
||||
dopo che si sia disconnesso. Questo prevede di continuare a salvare i messaggi
|
||||
QOS1 e QOS2 per i topic sottoscritti cosicche' possano essere consegnati quando
|
||||
il client si riconnetterà. Il server DEVE inoltre mantenere lo stato dei messaggi
|
||||
che stavano per essere consegnati prima che la connessione fosse persa.
|
||||
Questa informazione DEVE esser mantenuta finchè il client si ricconnetta.
|
||||
Se flag = 1 allora il server DEVE scartare qualsiasi informazione precedentemente
|
||||
memorizzata sul client e trattare la connessione come "pulita".
|
||||
Il server DEVE anche scartare qualunque stato qualora il client di disconnetta.
|
||||
Solitamente, un client opererà o in un modo o nell'altro. La scelta dipenderà dall'applicazione.
|
||||
Un sessione cliente pulita non riceverà informazioni vecchie e DOVRA' ri-sottoscriversi
|
||||
ogni volta che si connette. Una sessione cliente NON pulita non perderà nessun messaggio di
|
||||
tipo QoS1 o QoS2 che sia stato pubblicato mentro esso era disconnesso. I messaggi
|
||||
di tipo QoS0 NON vengono mai salvati. Un server deve fornire un meccanismo di
|
||||
amministrazione per la pulizia delle informazione salvate su di un client e
|
||||
che possa essere usata qualora si sappia che il client non si riconnetterà.
|
||||
|
||||
|
||||
**** CONNECT ****
|
||||
C S
|
||||
CONNECT ->
|
||||
<- CONNACK
|
||||
|
||||
**** PUBLISH ****
|
||||
A client PUBLISH a message on a server QoS 1:
|
||||
|
||||
C S
|
||||
PUBLISH ->
|
||||
<- PUBACK
|
||||
|
||||
A server send a PUBLISH notification to a client QoS 1:
|
||||
S C
|
||||
PUBLISH ->
|
||||
<- PUBACK
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,85 +0,0 @@
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<relativePath>../</relativePath>
|
||||
<!--<relativePath>../pom.xml</relativePath>-->
|
||||
<artifactId>moquette-parent</artifactId>
|
||||
<groupId>org.dna.mqtt</groupId>
|
||||
<version>0.4-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>moquette-mina-parser</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<!--<version>${moquette-version}</version>-->
|
||||
<name>Moquette - MINA Parser</name>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.apache.mina</groupId>
|
||||
<artifactId>mina-core</artifactId>
|
||||
<version>${mina-version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.dna.mqtt</groupId>
|
||||
<artifactId>moquette-parser-commons</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-log4j12</artifactId>
|
||||
<version>1.6.4</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.8.2</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- needs extra dependencies: objenesis & hamcrest -->
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<version>1.9.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>1.5</source>
|
||||
<target>1.5</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<excludes>
|
||||
<exclude>org/dna/mqtt/moquette/proto/TestUtils.java</exclude>
|
||||
<exclude>org/dna/mqtt/moquette/proto/TestUtils$*</exclude>
|
||||
</excludes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<repositories>
|
||||
</repositories>
|
||||
</project>
|
||||
@@ -1,20 +0,0 @@
|
||||
package org.dna.mqtt.commons;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author andrea
|
||||
*/
|
||||
public class MessageIDGenerator {
|
||||
private AtomicInteger m_current;
|
||||
|
||||
public MessageIDGenerator() {
|
||||
//NB we start at -1 because the first incAndGet has to return 0
|
||||
m_current = new AtomicInteger(-1);
|
||||
}
|
||||
|
||||
public int next() {
|
||||
return m_current.incrementAndGet();
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
package org.dna.mqtt.moquette;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author andrea
|
||||
*/
|
||||
public class ConnectionException extends MQTTException {
|
||||
|
||||
public ConnectionException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
public ConnectionException(Throwable e) {
|
||||
super(e);
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
package org.dna.mqtt.moquette;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author andrea
|
||||
*/
|
||||
public class PublishException extends ConnectionException {
|
||||
|
||||
public PublishException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
public PublishException(Throwable e) {
|
||||
super(e);
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
package org.dna.mqtt.moquette;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author andrea
|
||||
*/
|
||||
public class SubscribeException extends ConnectionException {
|
||||
|
||||
public SubscribeException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
public SubscribeException(Throwable e) {
|
||||
super(e);
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
package org.dna.mqtt.moquette.proto;
|
||||
|
||||
import org.apache.mina.core.buffer.IoBuffer;
|
||||
import org.apache.mina.core.session.IoSession;
|
||||
import org.apache.mina.filter.codec.ProtocolDecoderOutput;
|
||||
import org.apache.mina.filter.codec.demux.MessageDecoderResult;
|
||||
import org.dna.mqtt.moquette.proto.messages.AbstractMessage;
|
||||
import org.dna.mqtt.moquette.proto.messages.ConnAckMessage;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author andrea
|
||||
*/
|
||||
public class ConnAckDecoder extends MqttDecoder {
|
||||
|
||||
public MessageDecoderResult decodable(IoSession session, IoBuffer in) {
|
||||
return Utils.checkDecodable(AbstractMessage.CONNACK, in);
|
||||
}
|
||||
|
||||
public MessageDecoderResult decode(IoSession session, IoBuffer in, ProtocolDecoderOutput out) throws Exception {
|
||||
//Common decoding part
|
||||
ConnAckMessage message = new ConnAckMessage();
|
||||
if (decodeCommonHeader(message, in) == NEED_DATA) {
|
||||
return NEED_DATA;
|
||||
}
|
||||
//skip reserved byte
|
||||
in.skip(1);
|
||||
|
||||
//read return code
|
||||
message.setReturnCode(in.get());
|
||||
out.write(message);
|
||||
return OK;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
package org.dna.mqtt.moquette.proto;
|
||||
|
||||
import org.apache.mina.core.buffer.IoBuffer;
|
||||
import org.apache.mina.core.session.IoSession;
|
||||
import org.apache.mina.filter.codec.ProtocolEncoderOutput;
|
||||
import org.apache.mina.filter.codec.demux.MessageEncoder;
|
||||
import org.dna.mqtt.moquette.proto.messages.AbstractMessage;
|
||||
import org.dna.mqtt.moquette.proto.messages.ConnAckMessage;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author andrea
|
||||
*/
|
||||
public class ConnAckEncoder implements MessageEncoder<ConnAckMessage> {
|
||||
|
||||
public void encode(IoSession session, ConnAckMessage message, ProtocolEncoderOutput out) throws Exception {
|
||||
IoBuffer buff = IoBuffer.allocate(4);
|
||||
buff.put((byte) (AbstractMessage.CONNACK << 4));
|
||||
buff.put(Utils.encodeRemainingLength(2));
|
||||
buff.put((byte) 0);
|
||||
buff.put(message.getReturnCode()).flip();
|
||||
out.write(buff);
|
||||
}
|
||||
}
|
||||
@@ -1,138 +0,0 @@
|
||||
package org.dna.mqtt.moquette.proto;
|
||||
|
||||
import org.dna.mqtt.moquette.proto.messages.AbstractMessage;
|
||||
import org.dna.mqtt.moquette.proto.messages.ConnectMessage;
|
||||
import org.apache.mina.core.buffer.IoBuffer;
|
||||
import org.apache.mina.core.session.IoSession;
|
||||
import org.apache.mina.filter.codec.ProtocolDecoderOutput;
|
||||
import org.apache.mina.filter.codec.demux.MessageDecoderResult;
|
||||
|
||||
/**
|
||||
* Decoder for the CONNECT message
|
||||
*
|
||||
* @author andrea
|
||||
*/
|
||||
public class ConnectDecoder extends MqttDecoder {
|
||||
|
||||
public MessageDecoderResult decodable(IoSession session, IoBuffer in) {
|
||||
return Utils.checkDecodable(AbstractMessage.CONNECT, in);
|
||||
}
|
||||
|
||||
public MessageDecoderResult decode(IoSession session, IoBuffer in, ProtocolDecoderOutput out) throws Exception {
|
||||
//Common decoding part
|
||||
ConnectMessage message = new ConnectMessage();
|
||||
if (decodeCommonHeader(message, in) == NEED_DATA) {
|
||||
return NEED_DATA;
|
||||
}
|
||||
int remainingLength = message.getRemainingLength();
|
||||
int start = in.position();
|
||||
|
||||
//Connect specific decoding part
|
||||
//ProtocolName 8 bytes
|
||||
if (in.remaining() < 12) {
|
||||
return NEED_DATA;
|
||||
}
|
||||
byte[] encProtoName = new byte[6];
|
||||
in.skip(2); //size, is 0x06
|
||||
in.get(encProtoName);
|
||||
String protoName = new String(encProtoName, "UTF-8");
|
||||
if (!"MQIsdp".equals(protoName)) {
|
||||
return NOT_OK;
|
||||
}
|
||||
message.setProtocolName(protoName);
|
||||
|
||||
//ProtocolVersion 1 byte (value 0x03)
|
||||
message.setProcotolVersion(in.get());
|
||||
|
||||
//Connection flag
|
||||
byte connFlags = in.get();
|
||||
boolean cleanSession = ((connFlags & 0x02) >> 1) == 1 ? true : false;
|
||||
boolean willFlag = ((connFlags & 0x04) >> 2) == 1 ? true : false;
|
||||
byte willQos = (byte) ((connFlags & 0x18) >> 3);
|
||||
if (willQos > 2) {
|
||||
return NOT_OK; //admitted values for willqos are 0, 1, 2
|
||||
}
|
||||
boolean willRetain = ((connFlags & 0x20) >> 5) == 1 ? true : false;
|
||||
boolean passwordFlag = ((connFlags & 0x40) >> 6) == 1 ? true : false;
|
||||
boolean userFlag = ((connFlags & 0x80) >> 7) == 1 ? true : false;
|
||||
//a password is true iff user is true.
|
||||
if (!userFlag && passwordFlag) {
|
||||
return NOT_OK;
|
||||
}
|
||||
message.setCleanSession(cleanSession);
|
||||
message.setWillFlag(willFlag);
|
||||
message.setWillQos(willQos);
|
||||
message.setWillRetain(willRetain);
|
||||
message.setPasswordFlag(passwordFlag);
|
||||
message.setUserFlag(userFlag);
|
||||
|
||||
//Keep Alive timer 2 bytes
|
||||
int keepAlive = Utils.readWord(in);
|
||||
message.setKeepAlive(keepAlive);
|
||||
|
||||
if (remainingLength == 12) {
|
||||
out.write(message);
|
||||
return OK;
|
||||
}
|
||||
|
||||
//Decode the ClientID
|
||||
String clientID = Utils.decodeString(in);
|
||||
if (clientID == null) {
|
||||
return NEED_DATA;
|
||||
}
|
||||
message.setClientID(clientID);
|
||||
|
||||
//Decode willTopic
|
||||
if (willFlag) {
|
||||
String willTopic = Utils.decodeString(in);
|
||||
if (willTopic == null) {
|
||||
return NEED_DATA;
|
||||
}
|
||||
message.setWillTopic(willTopic);
|
||||
}
|
||||
|
||||
//Decode willMessage
|
||||
if (willFlag) {
|
||||
String willMessage = Utils.decodeString(in);
|
||||
if (willMessage == null) {
|
||||
return NEED_DATA;
|
||||
}
|
||||
message.setWillMessage(willMessage);
|
||||
}
|
||||
|
||||
//Compatibility check wieth v3.0, remaining length has precedence over
|
||||
//the user and password flags
|
||||
int readed = in.position() - start;
|
||||
if (readed == remainingLength) {
|
||||
out.write(message);
|
||||
return OK;
|
||||
}
|
||||
|
||||
//Decode username
|
||||
if (userFlag) {
|
||||
String userName = Utils.decodeString(in);
|
||||
if (userName == null) {
|
||||
return NEED_DATA;
|
||||
}
|
||||
message.setUsername(userName);
|
||||
}
|
||||
|
||||
readed = in.position() - start;
|
||||
if (readed == remainingLength) {
|
||||
out.write(message);
|
||||
return OK;
|
||||
}
|
||||
|
||||
//Decode password
|
||||
if (passwordFlag) {
|
||||
String password = Utils.decodeString(in);
|
||||
if (password == null) {
|
||||
return NEED_DATA;
|
||||
}
|
||||
message.setPassword(password);
|
||||
}
|
||||
|
||||
out.write(message);
|
||||
return OK;
|
||||
}
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
package org.dna.mqtt.moquette.proto;
|
||||
|
||||
import org.dna.mqtt.moquette.proto.messages.AbstractMessage;
|
||||
import org.dna.mqtt.moquette.proto.messages.ConnectMessage;
|
||||
import org.apache.mina.core.buffer.IoBuffer;
|
||||
import org.apache.mina.core.session.IoSession;
|
||||
import org.apache.mina.filter.codec.ProtocolEncoderOutput;
|
||||
import org.apache.mina.filter.codec.demux.MessageEncoder;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author andrea
|
||||
*/
|
||||
public class ConnectEncoder implements MessageEncoder<ConnectMessage> {
|
||||
|
||||
public void encode(IoSession session, ConnectMessage message, ProtocolEncoderOutput out) throws Exception {
|
||||
IoBuffer staticHeaderBuff = IoBuffer.allocate(12);
|
||||
staticHeaderBuff.put(Utils.encodeString("MQIsdp"));
|
||||
|
||||
//version
|
||||
staticHeaderBuff.put((byte)0x03);
|
||||
|
||||
//connection flags and Strings
|
||||
byte connectionFlags = 0;
|
||||
if (message.isCleanSession()) {
|
||||
connectionFlags |= 0x02;
|
||||
}
|
||||
if (message.isWillFlag()) {
|
||||
connectionFlags |= 0x04;
|
||||
}
|
||||
connectionFlags |= ((message.getWillQos() & 0x03) << 3);
|
||||
if (message.isWillRetain()) {
|
||||
connectionFlags |= 0x020;
|
||||
}
|
||||
if (message.isPasswordFlag()) {
|
||||
connectionFlags |= 0x040;
|
||||
}
|
||||
if (message.isUserFlag()) {
|
||||
connectionFlags |= 0x080;
|
||||
}
|
||||
staticHeaderBuff.put(connectionFlags);
|
||||
|
||||
//Keep alive timer
|
||||
Utils.writeWord(staticHeaderBuff, message.getKeepAlive());
|
||||
staticHeaderBuff.flip();
|
||||
|
||||
//Variable part
|
||||
IoBuffer variableHeaderBuff = IoBuffer.allocate(12).setAutoExpand(true);
|
||||
if (message.getClientID() != null) {
|
||||
variableHeaderBuff.put(Utils.encodeString(message.getClientID()));
|
||||
if (message.isWillFlag()) {
|
||||
variableHeaderBuff.put(Utils.encodeString(message.getWillTopic()));
|
||||
variableHeaderBuff.put(Utils.encodeString(message.getWillMessage()));
|
||||
}
|
||||
if (message.isUserFlag() && message.getUsername() != null) {
|
||||
variableHeaderBuff.put(Utils.encodeString(message.getUsername()));
|
||||
if (message.isPasswordFlag() && message.getPassword() != null) {
|
||||
variableHeaderBuff.put(Utils.encodeString(message.getPassword()));
|
||||
}
|
||||
}
|
||||
}
|
||||
variableHeaderBuff.flip();
|
||||
|
||||
int variableHeaderSize = variableHeaderBuff.remaining();
|
||||
IoBuffer buff = IoBuffer.allocate(14 + variableHeaderSize);
|
||||
buff.put((byte) (AbstractMessage.CONNECT << 4));
|
||||
buff.put(Utils.encodeRemainingLength(12 + variableHeaderSize));
|
||||
buff.put(staticHeaderBuff).put(variableHeaderBuff).flip();
|
||||
|
||||
out.write(buff);
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
package org.dna.mqtt.moquette.proto;
|
||||
|
||||
import org.apache.mina.core.buffer.IoBuffer;
|
||||
import org.apache.mina.core.session.IoSession;
|
||||
import org.apache.mina.filter.codec.ProtocolDecoderOutput;
|
||||
import org.apache.mina.filter.codec.demux.MessageDecoderResult;
|
||||
import org.dna.mqtt.moquette.proto.messages.AbstractMessage;
|
||||
import org.dna.mqtt.moquette.proto.messages.DisconnectMessage;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author andrea
|
||||
*/
|
||||
public class DisconnectDecoder extends MqttDecoder {
|
||||
|
||||
public MessageDecoderResult decodable(IoSession session, IoBuffer in) {
|
||||
return Utils.checkDecodable(AbstractMessage.DISCONNECT, in);
|
||||
}
|
||||
|
||||
public MessageDecoderResult decode(IoSession session, IoBuffer in, ProtocolDecoderOutput out) throws Exception {
|
||||
//Common decoding part
|
||||
DisconnectMessage message = new DisconnectMessage();
|
||||
if (decodeCommonHeader(message, in) == NEED_DATA) {
|
||||
return NEED_DATA;
|
||||
}
|
||||
out.write(message);
|
||||
return OK;
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
package org.dna.mqtt.moquette.proto;
|
||||
|
||||
import org.apache.mina.core.buffer.IoBuffer;
|
||||
import org.apache.mina.core.session.IoSession;
|
||||
import org.apache.mina.filter.codec.ProtocolEncoderOutput;
|
||||
import org.apache.mina.filter.codec.demux.MessageEncoder;
|
||||
import org.dna.mqtt.moquette.proto.messages.AbstractMessage;
|
||||
import org.dna.mqtt.moquette.proto.messages.DisconnectMessage;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author andrea
|
||||
*/
|
||||
public class DisconnectEncoder implements MessageEncoder<DisconnectMessage> {
|
||||
|
||||
public void encode(IoSession session, DisconnectMessage message, ProtocolEncoderOutput out) throws Exception {
|
||||
IoBuffer buff = IoBuffer.allocate(2);
|
||||
buff.put((byte) (AbstractMessage.DISCONNECT << 4)).put((byte)0).flip();
|
||||
out.write(buff);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,158 +0,0 @@
|
||||
package org.dna.mqtt.moquette.proto;
|
||||
|
||||
import org.apache.mina.core.buffer.AbstractIoBuffer;
|
||||
import org.apache.mina.core.buffer.IoBuffer;
|
||||
import org.apache.mina.core.session.IoSession;
|
||||
import org.apache.mina.core.write.WriteRequest;
|
||||
import org.apache.mina.filter.logging.LogLevel;
|
||||
import org.apache.mina.filter.logging.LoggingFilter;
|
||||
import static org.dna.mqtt.moquette.proto.messages.AbstractMessage.*;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Apache MINA logging filter that pretty prints the header of messages
|
||||
*
|
||||
* @author andrea
|
||||
*/
|
||||
public class MQTTLoggingFilter extends LoggingFilter {
|
||||
|
||||
private final Logger log;
|
||||
|
||||
protected static String computeLoggerName(String name) {
|
||||
return name == null ? MQTTLoggingFilter.class.getName() : name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default Constructor.
|
||||
*/
|
||||
public MQTTLoggingFilter() {
|
||||
this(LoggingFilter.class.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new NoopFilter using a class name
|
||||
*
|
||||
* @param clazz the class which name will be used to create the logger
|
||||
*/
|
||||
public MQTTLoggingFilter(Class<?> clazz) {
|
||||
this(clazz.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new NoopFilter using a name
|
||||
*
|
||||
* @param name the name used to create the logger. If null, will default to
|
||||
* "NoopFilter"
|
||||
*/
|
||||
public MQTTLoggingFilter(String name) {
|
||||
super(computeLoggerName(name));
|
||||
log = LoggerFactory.getLogger(getName());
|
||||
}
|
||||
|
||||
protected String decodeCommonHeader(IoBuffer in) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
byte h1 = in.get();
|
||||
byte messageType = (byte) ((h1 & 0x00F0) >> 4);
|
||||
boolean dupFlag = ((byte) ((h1 & 0x0008) >> 3) == 1);
|
||||
byte qosLevel = (byte) ((h1 & 0x0006) >> 1);
|
||||
boolean retainFlag = ((byte) (h1 & 0x0001) == 1);
|
||||
int remainingLength = Utils.decodeRemainingLenght(in);
|
||||
|
||||
sb.append("type: ").append(decodeMessageType(messageType)).append(", ");
|
||||
sb.append("dup: ").append(Boolean.toString(dupFlag)).append(", ");
|
||||
sb.append("QoS: ").append(qosLevel).append(", ");
|
||||
sb.append("retain: ").append(Boolean.toString(retainFlag));
|
||||
|
||||
if (remainingLength != -1) {
|
||||
sb.append(", remainingLen: ").append(remainingLength);
|
||||
}
|
||||
|
||||
in.rewind();
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
protected String decodeMessageType(byte type) {
|
||||
switch (type) {
|
||||
case CONNECT:
|
||||
return "CONNECT";
|
||||
case CONNACK:
|
||||
return "CONNACK";
|
||||
case PUBLISH:
|
||||
return "PUBLISH";
|
||||
case PUBACK:
|
||||
return "PUBACK";
|
||||
case PUBREC:
|
||||
return "PUBREC";
|
||||
case PUBREL:
|
||||
return "PUBREL";
|
||||
case PUBCOMP:
|
||||
return "PUBCOMP";
|
||||
case SUBSCRIBE:
|
||||
return "SUBSCRIBE";
|
||||
case SUBACK:
|
||||
return "SUBACK";
|
||||
case UNSUBSCRIBE:
|
||||
return "UNSUBSCRIBE";
|
||||
case UNSUBACK:
|
||||
return "UNSUBACK";
|
||||
case PINGREQ:
|
||||
return "PINGREQ";
|
||||
case PINGRESP:
|
||||
return "PINGRESP";
|
||||
case DISCONNECT:
|
||||
return "DISCONNECT";
|
||||
default:
|
||||
return "undefined";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void messageReceived(NextFilter nextFilter, IoSession session, Object message) throws Exception {
|
||||
if (message instanceof AbstractIoBuffer) {
|
||||
IoBuffer buff = (AbstractIoBuffer) message;
|
||||
log(getMessageReceivedLogLevel(), "-> RECEIVED: {}", decodeCommonHeader(buff) + "\n\t" + buff.getHexDump());
|
||||
} else {
|
||||
log(getMessageReceivedLogLevel(), "-> RECEIVED: {}", message);
|
||||
}
|
||||
nextFilter.messageReceived(session, message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void messageSent(NextFilter nextFilter, IoSession session, WriteRequest writeRequest) throws Exception {
|
||||
Object message = writeRequest.getMessage();
|
||||
if (message instanceof AbstractIoBuffer) {
|
||||
IoBuffer buff = (AbstractIoBuffer) message;
|
||||
log(getMessageReceivedLogLevel(), "<- SENT: {}", buff.getHexDump());
|
||||
} else {
|
||||
log(getMessageReceivedLogLevel(), "<- SENT: {}", message);
|
||||
}
|
||||
nextFilter.messageSent(session, writeRequest);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copied by parent
|
||||
*/
|
||||
private void log(LogLevel eventLevel, String message, Object param) {
|
||||
switch (eventLevel) {
|
||||
case TRACE:
|
||||
log.trace(message, param);
|
||||
return;
|
||||
case DEBUG:
|
||||
log.debug(message, param);
|
||||
return;
|
||||
case INFO:
|
||||
log.info(message, param);
|
||||
return;
|
||||
case WARN:
|
||||
log.warn(message, param);
|
||||
return;
|
||||
case ERROR:
|
||||
log.error(message, param);
|
||||
return;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
package org.dna.mqtt.moquette.proto;
|
||||
|
||||
import org.apache.mina.core.buffer.IoBuffer;
|
||||
import org.apache.mina.core.session.IoSession;
|
||||
import org.apache.mina.filter.codec.ProtocolDecoderOutput;
|
||||
import org.apache.mina.filter.codec.demux.MessageDecoderResult;
|
||||
import org.dna.mqtt.moquette.proto.messages.MessageIDMessage;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author andrea
|
||||
*/
|
||||
public abstract class MessageIDDecoder extends MqttDecoder {
|
||||
protected abstract MessageIDMessage createMessage();
|
||||
|
||||
public MessageDecoderResult decode(IoSession session, IoBuffer in, ProtocolDecoderOutput out) throws Exception {
|
||||
//Common decoding part
|
||||
MessageIDMessage message = createMessage();
|
||||
if (decodeCommonHeader(message, in) == NEED_DATA) {
|
||||
return NEED_DATA;
|
||||
}
|
||||
|
||||
//read messageIDs
|
||||
message.setMessageID(Utils.readWord(in));
|
||||
out.write(message);
|
||||
return OK;
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
package org.dna.mqtt.moquette.proto;
|
||||
|
||||
import org.apache.mina.core.buffer.IoBuffer;
|
||||
import org.apache.mina.filter.codec.demux.MessageDecoderAdapter;
|
||||
import org.apache.mina.filter.codec.demux.MessageDecoderResult;
|
||||
import org.dna.mqtt.moquette.proto.messages.AbstractMessage;
|
||||
|
||||
/**
|
||||
* Contains common code for all mqtt decoders.
|
||||
*
|
||||
* @author andrea
|
||||
*/
|
||||
public abstract class MqttDecoder extends MessageDecoderAdapter {
|
||||
|
||||
protected MessageDecoderResult decodeCommonHeader(AbstractMessage message, IoBuffer in) {
|
||||
//Common decoding part
|
||||
if (in.remaining() < 2) {
|
||||
return NEED_DATA;
|
||||
}
|
||||
byte h1 = in.get();
|
||||
byte messageType = (byte) ((h1 & 0x00F0) >> 4);
|
||||
boolean dupFlag = ((byte) ((h1 & 0x0008) >> 3) == 1);
|
||||
byte qosLevel = (byte) ((h1 & 0x0006) >> 1);
|
||||
boolean retainFlag = ((byte) (h1 & 0x0001) == 1);
|
||||
int remainingLength = Utils.decodeRemainingLenght(in);
|
||||
if (remainingLength == -1) {
|
||||
return NEED_DATA;
|
||||
}
|
||||
|
||||
message.setMessageType(messageType);
|
||||
message.setDupFlag(dupFlag);
|
||||
message.setQos(AbstractMessage.QOSType.values()[qosLevel]);
|
||||
message.setRetainFlag(retainFlag);
|
||||
message.setRemainingLength(remainingLength);
|
||||
return OK;
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
package org.dna.mqtt.moquette.proto;
|
||||
|
||||
import org.apache.mina.core.buffer.IoBuffer;
|
||||
import org.apache.mina.core.session.IoSession;
|
||||
import org.apache.mina.filter.codec.ProtocolDecoderOutput;
|
||||
import org.apache.mina.filter.codec.demux.MessageDecoderResult;
|
||||
import org.dna.mqtt.moquette.proto.messages.AbstractMessage;
|
||||
import org.dna.mqtt.moquette.proto.messages.PingReqMessage;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author andrea
|
||||
*/
|
||||
public class PingReqDecoder extends MqttDecoder {
|
||||
|
||||
public MessageDecoderResult decodable(IoSession session, IoBuffer in) {
|
||||
return Utils.checkDecodable(AbstractMessage.PINGREQ, in);
|
||||
}
|
||||
|
||||
public MessageDecoderResult decode(IoSession session, IoBuffer in, ProtocolDecoderOutput out) throws Exception {
|
||||
//Common decoding part
|
||||
PingReqMessage message = new PingReqMessage();
|
||||
if (decodeCommonHeader(message, in) == NEED_DATA) {
|
||||
return NEED_DATA;
|
||||
}
|
||||
out.write(message);
|
||||
return OK;
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
package org.dna.mqtt.moquette.proto;
|
||||
|
||||
import org.apache.mina.core.buffer.IoBuffer;
|
||||
import org.apache.mina.core.session.IoSession;
|
||||
import org.apache.mina.filter.codec.ProtocolEncoderOutput;
|
||||
import org.apache.mina.filter.codec.demux.MessageEncoder;
|
||||
import org.dna.mqtt.moquette.proto.messages.AbstractMessage;
|
||||
import org.dna.mqtt.moquette.proto.messages.PingReqMessage;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author andrea
|
||||
*/
|
||||
public class PingReqEncoder implements MessageEncoder<PingReqMessage> {
|
||||
|
||||
public void encode(IoSession session, PingReqMessage message, ProtocolEncoderOutput out) throws Exception {
|
||||
IoBuffer buff = IoBuffer.allocate(2);
|
||||
buff.put((byte) (AbstractMessage.PINGREQ << 4)).put((byte)0).flip();
|
||||
out.write(buff);
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* To change this template, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package org.dna.mqtt.moquette.proto;
|
||||
|
||||
import org.apache.mina.core.buffer.IoBuffer;
|
||||
import org.apache.mina.core.session.IoSession;
|
||||
import org.apache.mina.filter.codec.ProtocolDecoderOutput;
|
||||
import org.apache.mina.filter.codec.demux.MessageDecoderResult;
|
||||
import org.dna.mqtt.moquette.proto.messages.AbstractMessage;
|
||||
import org.dna.mqtt.moquette.proto.messages.PingRespMessage;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author andrea
|
||||
*/
|
||||
public class PingRespDecoder extends MqttDecoder {
|
||||
|
||||
public MessageDecoderResult decodable(IoSession session, IoBuffer in) {
|
||||
return Utils.checkDecodable(AbstractMessage.PINGRESP, in);
|
||||
}
|
||||
|
||||
public MessageDecoderResult decode(IoSession session, IoBuffer in, ProtocolDecoderOutput out) throws Exception {
|
||||
//Common decoding part
|
||||
PingRespMessage message = new PingRespMessage();
|
||||
if (decodeCommonHeader(message, in) == NEED_DATA) {
|
||||
return NEED_DATA;
|
||||
}
|
||||
out.write(message);
|
||||
return OK;
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
package org.dna.mqtt.moquette.proto;
|
||||
|
||||
import org.apache.mina.core.buffer.IoBuffer;
|
||||
import org.apache.mina.core.session.IoSession;
|
||||
import org.apache.mina.filter.codec.ProtocolEncoderOutput;
|
||||
import org.apache.mina.filter.codec.demux.MessageEncoder;
|
||||
import org.dna.mqtt.moquette.proto.messages.AbstractMessage;
|
||||
import org.dna.mqtt.moquette.proto.messages.PingRespMessage;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author andrea
|
||||
*/
|
||||
public class PingRespEncoder implements MessageEncoder<PingRespMessage> {
|
||||
|
||||
public void encode(IoSession session, PingRespMessage message, ProtocolEncoderOutput out) throws Exception {
|
||||
IoBuffer buff = IoBuffer.allocate(2);
|
||||
buff.put((byte) (AbstractMessage.PINGRESP << 4)).put((byte)0).flip();
|
||||
out.write(buff);
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
package org.dna.mqtt.moquette.proto;
|
||||
|
||||
import org.apache.mina.core.buffer.IoBuffer;
|
||||
import org.apache.mina.core.session.IoSession;
|
||||
import org.apache.mina.filter.codec.demux.MessageDecoderResult;
|
||||
import org.dna.mqtt.moquette.proto.messages.AbstractMessage;
|
||||
import org.dna.mqtt.moquette.proto.messages.MessageIDMessage;
|
||||
import org.dna.mqtt.moquette.proto.messages.PubAckMessage;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author andrea
|
||||
*/
|
||||
public class PubAckDecoder extends MessageIDDecoder {
|
||||
|
||||
@Override
|
||||
protected MessageIDMessage createMessage() {
|
||||
return new PubAckMessage();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageDecoderResult decodable(IoSession session, IoBuffer in) {
|
||||
return Utils.checkDecodable(AbstractMessage.PUBACK, in);
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
package org.dna.mqtt.moquette.proto;
|
||||
|
||||
import org.apache.mina.core.buffer.IoBuffer;
|
||||
import org.apache.mina.core.session.IoSession;
|
||||
import org.apache.mina.filter.codec.ProtocolEncoderOutput;
|
||||
import org.apache.mina.filter.codec.demux.MessageEncoder;
|
||||
import org.dna.mqtt.moquette.proto.messages.AbstractMessage;
|
||||
import org.dna.mqtt.moquette.proto.messages.PubAckMessage;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author andrea
|
||||
*/
|
||||
public class PubAckEncoder implements MessageEncoder<PubAckMessage> {
|
||||
public void encode(IoSession session, PubAckMessage message, ProtocolEncoderOutput out) throws Exception {
|
||||
IoBuffer buff = IoBuffer.allocate(4);
|
||||
buff.put((byte) (AbstractMessage.PUBACK << 4));
|
||||
buff.put(Utils.encodeRemainingLength(2));
|
||||
Utils.writeWord(buff, message.getMessageID());
|
||||
buff.flip();
|
||||
out.write(buff);
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
package org.dna.mqtt.moquette.proto;
|
||||
|
||||
import org.dna.mqtt.moquette.proto.messages.PubCompMessage;
|
||||
import org.apache.mina.core.buffer.IoBuffer;
|
||||
import org.apache.mina.core.session.IoSession;
|
||||
import org.apache.mina.filter.codec.demux.MessageDecoderResult;
|
||||
import org.dna.mqtt.moquette.proto.messages.AbstractMessage;
|
||||
import org.dna.mqtt.moquette.proto.messages.MessageIDMessage;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author andrea
|
||||
*/
|
||||
public class PubCompDecoder extends MessageIDDecoder {
|
||||
|
||||
@Override
|
||||
protected MessageIDMessage createMessage() {
|
||||
return new PubCompMessage();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageDecoderResult decodable(IoSession session, IoBuffer in) {
|
||||
return Utils.checkDecodable(AbstractMessage.PUBCOMP, in);
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
package org.dna.mqtt.moquette.proto;
|
||||
|
||||
import org.apache.mina.core.buffer.IoBuffer;
|
||||
import org.apache.mina.core.session.IoSession;
|
||||
import org.apache.mina.filter.codec.ProtocolEncoderOutput;
|
||||
import org.apache.mina.filter.codec.demux.MessageEncoder;
|
||||
import org.dna.mqtt.moquette.proto.messages.AbstractMessage;
|
||||
import org.dna.mqtt.moquette.proto.messages.PubCompMessage;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author andrea
|
||||
*/
|
||||
public class PubCompEncoder implements MessageEncoder<PubCompMessage> {
|
||||
public void encode(IoSession session, PubCompMessage message, ProtocolEncoderOutput out) throws Exception {
|
||||
IoBuffer buff = IoBuffer.allocate(4);
|
||||
buff.put((byte) (AbstractMessage.PUBCOMP << 4));
|
||||
buff.put(Utils.encodeRemainingLength(2));
|
||||
Utils.writeWord(buff, message.getMessageID());
|
||||
buff.flip();
|
||||
out.write(buff);
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
package org.dna.mqtt.moquette.proto;
|
||||
|
||||
import org.apache.mina.core.buffer.IoBuffer;
|
||||
import org.apache.mina.core.session.IoSession;
|
||||
import org.apache.mina.filter.codec.demux.MessageDecoderResult;
|
||||
import org.dna.mqtt.moquette.proto.messages.AbstractMessage;
|
||||
import org.dna.mqtt.moquette.proto.messages.MessageIDMessage;
|
||||
import org.dna.mqtt.moquette.proto.messages.PubRecMessage;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author andrea
|
||||
*/
|
||||
public class PubRecDecoder extends MessageIDDecoder {
|
||||
|
||||
@Override
|
||||
protected MessageIDMessage createMessage() {
|
||||
return new PubRecMessage();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageDecoderResult decodable(IoSession session, IoBuffer in) {
|
||||
return Utils.checkDecodable(AbstractMessage.PUBREC, in);
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
package org.dna.mqtt.moquette.proto;
|
||||
|
||||
import org.apache.mina.core.buffer.IoBuffer;
|
||||
import org.apache.mina.core.session.IoSession;
|
||||
import org.apache.mina.filter.codec.ProtocolEncoderOutput;
|
||||
import org.apache.mina.filter.codec.demux.MessageEncoder;
|
||||
import org.dna.mqtt.moquette.proto.messages.AbstractMessage;
|
||||
import org.dna.mqtt.moquette.proto.messages.PubRecMessage;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author andrea
|
||||
*/
|
||||
public class PubRecEncoder implements MessageEncoder<PubRecMessage> {
|
||||
public void encode(IoSession session, PubRecMessage message, ProtocolEncoderOutput out) throws Exception {
|
||||
IoBuffer buff = IoBuffer.allocate(4);
|
||||
buff.put((byte) (AbstractMessage.PUBREC << 4));
|
||||
buff.put(Utils.encodeRemainingLength(2));
|
||||
Utils.writeWord(buff, message.getMessageID());
|
||||
buff.flip();
|
||||
out.write(buff);
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
package org.dna.mqtt.moquette.proto;
|
||||
|
||||
import org.apache.mina.core.buffer.IoBuffer;
|
||||
import org.apache.mina.core.session.IoSession;
|
||||
import org.apache.mina.filter.codec.demux.MessageDecoderResult;
|
||||
import org.dna.mqtt.moquette.proto.messages.AbstractMessage;
|
||||
import org.dna.mqtt.moquette.proto.messages.MessageIDMessage;
|
||||
import org.dna.mqtt.moquette.proto.messages.PubRelMessage;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author andrea
|
||||
*/
|
||||
public class PubRelDecoder extends MessageIDDecoder {
|
||||
|
||||
@Override
|
||||
protected MessageIDMessage createMessage() {
|
||||
return new PubRelMessage();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageDecoderResult decodable(IoSession session, IoBuffer in) {
|
||||
return Utils.checkDecodable(AbstractMessage.PUBREL, in);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
package org.dna.mqtt.moquette.proto;
|
||||
|
||||
import org.apache.mina.core.buffer.IoBuffer;
|
||||
import org.apache.mina.core.session.IoSession;
|
||||
import org.apache.mina.filter.codec.ProtocolEncoderOutput;
|
||||
import org.apache.mina.filter.codec.demux.MessageEncoder;
|
||||
import org.dna.mqtt.moquette.proto.messages.AbstractMessage;
|
||||
import org.dna.mqtt.moquette.proto.messages.PubRelMessage;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author andrea
|
||||
*/
|
||||
public class PubRelEncoder implements MessageEncoder<PubRelMessage> {
|
||||
public void encode(IoSession session, PubRelMessage message, ProtocolEncoderOutput out) throws Exception {
|
||||
IoBuffer buff = IoBuffer.allocate(4);
|
||||
buff.put((byte) (AbstractMessage.PUBREL << 4));
|
||||
buff.put(Utils.encodeRemainingLength(2));
|
||||
Utils.writeWord(buff, message.getMessageID());
|
||||
buff.flip();
|
||||
out.write(buff);
|
||||
}
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
package org.dna.mqtt.moquette.proto;
|
||||
|
||||
import org.apache.mina.core.buffer.IoBuffer;
|
||||
import org.apache.mina.core.session.IoSession;
|
||||
import org.apache.mina.filter.codec.ProtocolDecoderOutput;
|
||||
import org.apache.mina.filter.codec.demux.MessageDecoderResult;
|
||||
import org.dna.mqtt.moquette.proto.messages.AbstractMessage;
|
||||
import org.dna.mqtt.moquette.proto.messages.AbstractMessage.QOSType;
|
||||
import org.dna.mqtt.moquette.proto.messages.PublishMessage;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author andrea
|
||||
*/
|
||||
public class PublishDecoder extends MqttDecoder {
|
||||
|
||||
private static Logger LOG = LoggerFactory.getLogger(PublishDecoder.class);
|
||||
|
||||
public MessageDecoderResult decodable(IoSession session, IoBuffer in) {
|
||||
return Utils.checkDecodable(AbstractMessage.PUBLISH, in);
|
||||
}
|
||||
|
||||
public MessageDecoderResult decode(IoSession session, IoBuffer in, ProtocolDecoderOutput out) throws Exception {
|
||||
LOG.debug("decode invoked with buffer " + in);
|
||||
int startPos = in.position();
|
||||
|
||||
//Common decoding part
|
||||
PublishMessage message = new PublishMessage();
|
||||
if (decodeCommonHeader(message, in) == NEED_DATA) {
|
||||
LOG.info("decode ask for more data after " + in);
|
||||
return NEED_DATA;
|
||||
}
|
||||
int remainingLength = message.getRemainingLength();
|
||||
|
||||
//Topic name
|
||||
String topic = Utils.decodeString(in);
|
||||
if (topic == null) {
|
||||
return NEED_DATA;
|
||||
}
|
||||
message.setTopicName(topic);
|
||||
|
||||
if (message.getQos() == QOSType.LEAST_ONE ||
|
||||
message.getQos() == QOSType.EXACTLY_ONCE) {
|
||||
message.setMessageID(Utils.readWord(in));
|
||||
}
|
||||
int stopPos = in.position();
|
||||
|
||||
//read the payload
|
||||
int payloadSize = remainingLength - (stopPos - startPos - 2) + (Utils.numBytesToEncode(remainingLength) - 1);
|
||||
if (in.remaining() < payloadSize) {
|
||||
return NEED_DATA;
|
||||
}
|
||||
byte[] b = new byte[payloadSize];
|
||||
in.get(b);
|
||||
message.setPayload(b);
|
||||
|
||||
out.write(message);
|
||||
return OK;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
package org.dna.mqtt.moquette.proto;
|
||||
|
||||
import org.apache.mina.core.buffer.IoBuffer;
|
||||
import org.apache.mina.core.session.IoSession;
|
||||
import org.apache.mina.filter.codec.ProtocolEncoderOutput;
|
||||
import org.apache.mina.filter.codec.demux.MessageEncoder;
|
||||
import org.dna.mqtt.moquette.proto.messages.AbstractMessage;
|
||||
import org.dna.mqtt.moquette.proto.messages.AbstractMessage.QOSType;
|
||||
import org.dna.mqtt.moquette.proto.messages.PublishMessage;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author andrea
|
||||
*/
|
||||
public class PublishEncoder implements MessageEncoder<PublishMessage> {
|
||||
|
||||
public void encode(IoSession session, PublishMessage message, ProtocolEncoderOutput out) throws Exception {
|
||||
if (message.getQos() == QOSType.RESERVED) {
|
||||
throw new IllegalArgumentException("Found a message with RESERVED Qos");
|
||||
}
|
||||
if (message.getTopicName() == null || message.getTopicName().isEmpty()) {
|
||||
throw new IllegalArgumentException("Found a message with empty or null topic name");
|
||||
}
|
||||
|
||||
IoBuffer variableHeaderBuff = IoBuffer.allocate(2).setAutoExpand(true);
|
||||
variableHeaderBuff.put(Utils.encodeString(message.getTopicName()));
|
||||
if (message.getQos() == QOSType.LEAST_ONE ||
|
||||
message.getQos() == QOSType.EXACTLY_ONCE ) {
|
||||
if (message.getMessageID() == null) {
|
||||
throw new IllegalArgumentException("Found a message with QOS 1 or 2 and not MessageID setted");
|
||||
}
|
||||
Utils.writeWord(variableHeaderBuff, message.getMessageID());
|
||||
}
|
||||
variableHeaderBuff.put(message.getPayload());
|
||||
variableHeaderBuff.flip();
|
||||
int variableHeaderSize = variableHeaderBuff.remaining();
|
||||
|
||||
byte flags = Utils.encodeFlags(message);
|
||||
|
||||
IoBuffer buff = IoBuffer.allocate(2 + variableHeaderSize).setAutoExpand(true);;
|
||||
buff.put((byte) (AbstractMessage.PUBLISH << 4 | flags));
|
||||
buff.put(Utils.encodeRemainingLength(variableHeaderSize));
|
||||
buff.put(variableHeaderBuff).flip();
|
||||
|
||||
out.write(buff);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
package org.dna.mqtt.moquette.proto;
|
||||
|
||||
import org.apache.mina.core.buffer.IoBuffer;
|
||||
import org.apache.mina.core.session.IoSession;
|
||||
import org.apache.mina.filter.codec.ProtocolDecoderOutput;
|
||||
import org.apache.mina.filter.codec.demux.MessageDecoderResult;
|
||||
import org.dna.mqtt.moquette.proto.messages.AbstractMessage;
|
||||
import org.dna.mqtt.moquette.proto.messages.AbstractMessage.QOSType;
|
||||
import org.dna.mqtt.moquette.proto.messages.SubAckMessage;
|
||||
|
||||
/**
|
||||
* QoS, DUP and RETAIN are NOT used.
|
||||
*
|
||||
* @author andrea
|
||||
*/
|
||||
public class SubAckDecoder extends MqttDecoder {
|
||||
|
||||
public MessageDecoderResult decodable(IoSession session, IoBuffer in) {
|
||||
return Utils.checkDecodable(AbstractMessage.SUBACK, in);
|
||||
}
|
||||
|
||||
public MessageDecoderResult decode(IoSession session, IoBuffer in, ProtocolDecoderOutput out) throws Exception {
|
||||
//Common decoding part
|
||||
SubAckMessage message = new SubAckMessage();
|
||||
if (decodeCommonHeader(message, in) == NEED_DATA) {
|
||||
return NEED_DATA;
|
||||
}
|
||||
int remainingLength = message.getRemainingLength();
|
||||
|
||||
//MessageID
|
||||
message.setMessageID(Utils.readWord(in));
|
||||
remainingLength -= 2;
|
||||
|
||||
//Qos array
|
||||
if (in.remaining() < remainingLength ) {
|
||||
return NEED_DATA;
|
||||
}
|
||||
for (int i = 0; i < remainingLength; i++) {
|
||||
byte qos = in.get();
|
||||
message.addType(QOSType.values()[qos]);
|
||||
}
|
||||
|
||||
out.write(message);
|
||||
return OK;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
package org.dna.mqtt.moquette.proto;
|
||||
|
||||
import org.apache.mina.core.buffer.IoBuffer;
|
||||
import org.apache.mina.core.session.IoSession;
|
||||
import org.apache.mina.filter.codec.ProtocolEncoderOutput;
|
||||
import org.apache.mina.filter.codec.demux.MessageEncoder;
|
||||
import org.dna.mqtt.moquette.proto.messages.AbstractMessage;
|
||||
import org.dna.mqtt.moquette.proto.messages.SubAckMessage;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author andrea
|
||||
*/
|
||||
public class SubAckEncoder implements MessageEncoder<SubAckMessage> {
|
||||
|
||||
public void encode(IoSession session, SubAckMessage message, ProtocolEncoderOutput out) throws Exception {
|
||||
if (message.types().isEmpty()) {
|
||||
throw new IllegalArgumentException("Found a suback message with empty topics");
|
||||
}
|
||||
|
||||
IoBuffer variableHeaderBuff = IoBuffer.allocate(4).setAutoExpand(true);
|
||||
Utils.writeWord(variableHeaderBuff, message.getMessageID());
|
||||
for (AbstractMessage.QOSType c : message.types()) {
|
||||
byte ord = (byte) c.ordinal();
|
||||
variableHeaderBuff.put((byte)c.ordinal());
|
||||
}
|
||||
|
||||
variableHeaderBuff.flip();
|
||||
int variableHeaderSize = variableHeaderBuff.remaining();
|
||||
IoBuffer buff = IoBuffer.allocate(2 + variableHeaderSize);
|
||||
|
||||
buff.put((byte) (AbstractMessage.SUBACK << 4 ));
|
||||
buff.put(Utils.encodeRemainingLength(variableHeaderSize));
|
||||
buff.put(variableHeaderBuff).flip();
|
||||
|
||||
out.write(buff);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
package org.dna.mqtt.moquette.proto;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import org.apache.mina.core.buffer.IoBuffer;
|
||||
import org.apache.mina.core.session.IoSession;
|
||||
import org.apache.mina.filter.codec.ProtocolDecoderOutput;
|
||||
import org.apache.mina.filter.codec.demux.MessageDecoderResult;
|
||||
import org.dna.mqtt.moquette.proto.messages.AbstractMessage;
|
||||
import org.dna.mqtt.moquette.proto.messages.AbstractMessage.QOSType;
|
||||
import org.dna.mqtt.moquette.proto.messages.SubscribeMessage;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author andrea
|
||||
*/
|
||||
public class SubscribeDecoder extends MqttDecoder {
|
||||
|
||||
public MessageDecoderResult decodable(IoSession session, IoBuffer in) {
|
||||
return Utils.checkDecodable(AbstractMessage.SUBSCRIBE, in);
|
||||
}
|
||||
|
||||
public MessageDecoderResult decode(IoSession session, IoBuffer in, ProtocolDecoderOutput out) throws Exception {
|
||||
//Common decoding part
|
||||
SubscribeMessage message = new SubscribeMessage();
|
||||
if (decodeCommonHeader(message, in) == NEED_DATA) {
|
||||
return NEED_DATA;
|
||||
}
|
||||
|
||||
//check qos level
|
||||
if (message.getQos() != QOSType.LEAST_ONE) {
|
||||
return NOT_OK;
|
||||
}
|
||||
|
||||
int start = in.position();
|
||||
//read messageIDs
|
||||
message.setMessageID(Utils.readWord(in));
|
||||
int readed = in.position() - start;
|
||||
while (readed < message.getRemainingLength()) {
|
||||
decodeSubscription(in, message);
|
||||
readed = in.position() - start;
|
||||
}
|
||||
|
||||
out.write(message);
|
||||
return OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate the message with couple of Qos, topic
|
||||
*/
|
||||
private void decodeSubscription(IoBuffer in, SubscribeMessage message) throws UnsupportedEncodingException {
|
||||
String topic = Utils.decodeString(in);
|
||||
byte qos = (byte)(in.get() & 0x03);
|
||||
message.addSubscription(new SubscribeMessage.Couple(qos, topic));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
package org.dna.mqtt.moquette.proto;
|
||||
|
||||
import org.apache.mina.core.buffer.IoBuffer;
|
||||
import org.apache.mina.core.session.IoSession;
|
||||
import org.apache.mina.filter.codec.ProtocolEncoderOutput;
|
||||
import org.apache.mina.filter.codec.demux.MessageEncoder;
|
||||
import org.dna.mqtt.moquette.proto.messages.AbstractMessage;
|
||||
import org.dna.mqtt.moquette.proto.messages.AbstractMessage.QOSType;
|
||||
import org.dna.mqtt.moquette.proto.messages.SubscribeMessage;
|
||||
import org.dna.mqtt.moquette.proto.messages.SubscribeMessage.Couple;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author andrea
|
||||
*/
|
||||
public class SubscribeEncoder implements MessageEncoder<SubscribeMessage> {
|
||||
|
||||
public void encode(IoSession session, SubscribeMessage message, ProtocolEncoderOutput out) throws Exception {
|
||||
if (message.subscriptions().isEmpty()) {
|
||||
throw new IllegalArgumentException("Found a subscribe message with empty topics");
|
||||
}
|
||||
|
||||
if (message.getQos() != QOSType.LEAST_ONE) {
|
||||
throw new IllegalArgumentException("Expected a message with QOS 1, found " + message.getQos());
|
||||
}
|
||||
|
||||
IoBuffer variableHeaderBuff = IoBuffer.allocate(4).setAutoExpand(true);
|
||||
Utils.writeWord(variableHeaderBuff, message.getMessageID());
|
||||
for (Couple c : message.subscriptions()) {
|
||||
variableHeaderBuff.put(Utils.encodeString(c.getTopic()));
|
||||
variableHeaderBuff.put(c.getQos());
|
||||
}
|
||||
|
||||
variableHeaderBuff.flip();
|
||||
int variableHeaderSize = variableHeaderBuff.remaining();
|
||||
byte flags = Utils.encodeFlags(message);
|
||||
IoBuffer buff = IoBuffer.allocate(2 + variableHeaderSize);
|
||||
|
||||
buff.put((byte) (AbstractMessage.SUBSCRIBE << 4 | flags));
|
||||
buff.put(Utils.encodeRemainingLength(variableHeaderSize));
|
||||
buff.put(variableHeaderBuff).flip();
|
||||
|
||||
out.write(buff);
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
package org.dna.mqtt.moquette.proto;
|
||||
|
||||
import org.apache.mina.core.buffer.IoBuffer;
|
||||
import org.apache.mina.core.session.IoSession;
|
||||
import org.apache.mina.filter.codec.demux.MessageDecoderResult;
|
||||
import org.dna.mqtt.moquette.proto.messages.AbstractMessage;
|
||||
import org.dna.mqtt.moquette.proto.messages.MessageIDMessage;
|
||||
import org.dna.mqtt.moquette.proto.messages.UnsubAckMessage;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author andrea
|
||||
*/
|
||||
public class UnsubAckDecoder extends MessageIDDecoder {
|
||||
|
||||
@Override
|
||||
protected MessageIDMessage createMessage() {
|
||||
return new UnsubAckMessage();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageDecoderResult decodable(IoSession session, IoBuffer in) {
|
||||
return Utils.checkDecodable(AbstractMessage.UNSUBACK, in);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
package org.dna.mqtt.moquette.proto;
|
||||
|
||||
import org.apache.mina.core.buffer.IoBuffer;
|
||||
import org.apache.mina.core.session.IoSession;
|
||||
import org.apache.mina.filter.codec.ProtocolEncoderOutput;
|
||||
import org.apache.mina.filter.codec.demux.MessageEncoder;
|
||||
import org.dna.mqtt.moquette.proto.messages.AbstractMessage;
|
||||
import org.dna.mqtt.moquette.proto.messages.UnsubAckMessage;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author andrea
|
||||
*/
|
||||
public class UnsubAckEncoder implements MessageEncoder<UnsubAckMessage> {
|
||||
public void encode(IoSession session, UnsubAckMessage message, ProtocolEncoderOutput out) throws Exception {
|
||||
IoBuffer buff = IoBuffer.allocate(4);
|
||||
buff.put((byte) (AbstractMessage.UNSUBACK << 4));
|
||||
buff.put(Utils.encodeRemainingLength(2));
|
||||
Utils.writeWord(buff, message.getMessageID());
|
||||
buff.flip();
|
||||
out.write(buff);
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
package org.dna.mqtt.moquette.proto;
|
||||
|
||||
import org.apache.mina.core.buffer.IoBuffer;
|
||||
import org.apache.mina.core.session.IoSession;
|
||||
import org.apache.mina.filter.codec.ProtocolDecoderOutput;
|
||||
import org.apache.mina.filter.codec.demux.MessageDecoderResult;
|
||||
import org.dna.mqtt.moquette.proto.messages.AbstractMessage;
|
||||
import org.dna.mqtt.moquette.proto.messages.AbstractMessage.QOSType;
|
||||
import org.dna.mqtt.moquette.proto.messages.UnsubscribeMessage;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author andrea
|
||||
*/
|
||||
public class UnsubscribeDecoder extends MqttDecoder {
|
||||
|
||||
public MessageDecoderResult decodable(IoSession session, IoBuffer in) {
|
||||
return Utils.checkDecodable(AbstractMessage.UNSUBSCRIBE, in);
|
||||
}
|
||||
|
||||
public MessageDecoderResult decode(IoSession session, IoBuffer in, ProtocolDecoderOutput out) throws Exception {
|
||||
//Common decoding part
|
||||
UnsubscribeMessage message = new UnsubscribeMessage();
|
||||
if (decodeCommonHeader(message, in) == NEED_DATA) {
|
||||
return NEED_DATA;
|
||||
}
|
||||
|
||||
//check qos level
|
||||
if (message.getQos() != QOSType.LEAST_ONE) {
|
||||
return NOT_OK;
|
||||
}
|
||||
|
||||
int start = in.position();
|
||||
//read messageIDs
|
||||
message.setMessageID(Utils.readWord(in));
|
||||
int readed = in.position() - start;
|
||||
while (readed < message.getRemainingLength()) {
|
||||
message.addTopic(Utils.decodeString(in));
|
||||
readed = in.position() - start;
|
||||
}
|
||||
|
||||
out.write(message);
|
||||
return OK;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
package org.dna.mqtt.moquette.proto;
|
||||
|
||||
import org.apache.mina.core.buffer.IoBuffer;
|
||||
import org.apache.mina.core.session.IoSession;
|
||||
import org.apache.mina.filter.codec.ProtocolEncoderOutput;
|
||||
import org.apache.mina.filter.codec.demux.MessageEncoder;
|
||||
import org.dna.mqtt.moquette.proto.messages.AbstractMessage;
|
||||
import org.dna.mqtt.moquette.proto.messages.AbstractMessage.QOSType;
|
||||
import org.dna.mqtt.moquette.proto.messages.UnsubscribeMessage;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author andrea
|
||||
*/
|
||||
public class UnsubscribeEncoder implements MessageEncoder<UnsubscribeMessage> {
|
||||
|
||||
public void encode(IoSession session, UnsubscribeMessage message, ProtocolEncoderOutput out) throws Exception {
|
||||
if (message.topics().isEmpty()) {
|
||||
throw new IllegalArgumentException("Found an unsubscribe message with empty topics");
|
||||
}
|
||||
|
||||
if (message.getQos() != QOSType.LEAST_ONE) {
|
||||
throw new IllegalArgumentException("Expected a message with QOS 1, found " + message.getQos());
|
||||
}
|
||||
|
||||
IoBuffer variableHeaderBuff = IoBuffer.allocate(4).setAutoExpand(true);
|
||||
Utils.writeWord(variableHeaderBuff, message.getMessageID());
|
||||
for (String topic : message.topics()) {
|
||||
variableHeaderBuff.put(Utils.encodeString(topic));
|
||||
}
|
||||
|
||||
variableHeaderBuff.flip();
|
||||
int variableHeaderSize = variableHeaderBuff.remaining();
|
||||
byte flags = Utils.encodeFlags(message);
|
||||
IoBuffer buff = IoBuffer.allocate(2 + variableHeaderSize);
|
||||
|
||||
buff.put((byte) (AbstractMessage.UNSUBSCRIBE << 4 | flags));
|
||||
buff.put(Utils.encodeRemainingLength(variableHeaderSize));
|
||||
buff.put(variableHeaderBuff).flip();
|
||||
|
||||
out.write(buff);
|
||||
}
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
package org.dna.mqtt.moquette.proto;
|
||||
|
||||
import org.apache.mina.core.buffer.IoBuffer;
|
||||
import org.apache.mina.filter.codec.demux.MessageDecoder;
|
||||
import org.apache.mina.filter.codec.demux.MessageDecoderResult;
|
||||
import org.dna.mqtt.moquette.proto.TestUtils.MockProtocolDecoderOutput;
|
||||
import org.dna.mqtt.moquette.proto.messages.AbstractMessage;
|
||||
import org.dna.mqtt.moquette.proto.messages.ConnAckMessage;
|
||||
import static org.junit.Assert.*;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
/**
|
||||
*
|
||||
* @author andrea
|
||||
*/
|
||||
public class ConnAckDecoderTest {
|
||||
IoBuffer m_buff;
|
||||
ConnAckDecoder m_msgdec;
|
||||
MockProtocolDecoderOutput<ConnAckMessage> m_mockProtoDecoder;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
m_buff = IoBuffer.allocate(4);
|
||||
m_msgdec = new ConnAckDecoder();
|
||||
m_mockProtoDecoder = new MockProtocolDecoderOutput<ConnAckMessage>();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDecodable_OK() {
|
||||
m_buff.put((byte)(AbstractMessage.CONNACK << 4))
|
||||
.put((byte)0) //0 length
|
||||
.flip();
|
||||
assertEquals(MessageDecoder.OK , m_msgdec.decodable(null, m_buff));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDecodable_NOT_OK() {
|
||||
m_buff.put((byte)(AbstractMessage.CONNECT << 4))
|
||||
.put((byte)0) //0 length
|
||||
.flip();
|
||||
assertEquals(MessageDecoder.NOT_OK , m_msgdec.decodable(null, m_buff));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHeader() throws Exception {
|
||||
m_buff = IoBuffer.allocate(14);
|
||||
initHeader(m_buff);
|
||||
m_buff.flip();
|
||||
|
||||
//Exercise
|
||||
MessageDecoderResult res = m_msgdec.decode(null, m_buff, m_mockProtoDecoder);
|
||||
|
||||
assertNotNull(m_mockProtoDecoder.getMessage());
|
||||
assertEquals(MessageDecoder.OK, res);
|
||||
assertEquals(ConnAckMessage.CONNECTION_ACCEPTED, m_mockProtoDecoder.getMessage().getReturnCode());
|
||||
assertEquals(AbstractMessage.CONNACK, m_mockProtoDecoder.getMessage().getMessageType());
|
||||
}
|
||||
|
||||
private void initHeader(IoBuffer buff) {
|
||||
buff.clear().put((byte)(AbstractMessage.CONNACK << 4)).put((byte)2);
|
||||
//reserved
|
||||
buff.put((byte)0);
|
||||
//return code
|
||||
buff.put(ConnAckMessage.CONNECTION_ACCEPTED);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
package org.dna.mqtt.moquette.proto;
|
||||
|
||||
import org.dna.mqtt.moquette.proto.messages.ConnAckMessage;
|
||||
import org.junit.Test;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author andrea
|
||||
*/
|
||||
public class ConnAckEncoderTest {
|
||||
|
||||
@Test
|
||||
public void testHeaderEncode() throws Exception {
|
||||
TestUtils.MockProtocolEncoderOutput mockProtoEncoder = new TestUtils.MockProtocolEncoderOutput();
|
||||
ConnAckEncoder encoder = new ConnAckEncoder();
|
||||
ConnAckMessage msg = new ConnAckMessage();
|
||||
|
||||
//Exercise
|
||||
encoder.encode(null, msg, mockProtoEncoder);
|
||||
|
||||
//Verify
|
||||
assertEquals(0x20, mockProtoEncoder.getBuffer().get()); //1 byte
|
||||
assertEquals(0x02, mockProtoEncoder.getBuffer().get()); //2 byte, length
|
||||
assertEquals(ConnAckMessage.CONNECTION_ACCEPTED, mockProtoEncoder.getBuffer().skip(1).get());
|
||||
}
|
||||
}
|
||||
@@ -1,190 +0,0 @@
|
||||
package org.dna.mqtt.moquette.proto;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import org.apache.mina.core.buffer.IoBuffer;
|
||||
import org.apache.mina.filter.codec.demux.MessageDecoder;
|
||||
import org.apache.mina.filter.codec.demux.MessageDecoderResult;
|
||||
import org.dna.mqtt.moquette.proto.TestUtils.MockProtocolDecoderOutput;
|
||||
import org.dna.mqtt.moquette.proto.messages.AbstractMessage;
|
||||
import org.dna.mqtt.moquette.proto.messages.ConnectMessage;
|
||||
import static org.junit.Assert.*;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
public class ConnectDecoderTest {
|
||||
|
||||
IoBuffer m_buff;
|
||||
ConnectDecoder m_msgdec;
|
||||
MockProtocolDecoderOutput<ConnectMessage> m_mockProtoDecoder;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
m_buff = IoBuffer.allocate(4);
|
||||
m_msgdec = new ConnectDecoder();
|
||||
m_mockProtoDecoder = new MockProtocolDecoderOutput<ConnectMessage>();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDecodable_OK() {
|
||||
m_buff.put((byte)(AbstractMessage.CONNECT << 4))
|
||||
.put((byte)0) //0 length
|
||||
.flip();
|
||||
assertEquals(MessageDecoder.OK , m_msgdec.decodable(null, m_buff));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDecodable_NOT_OK() {
|
||||
m_buff.put((byte)(AbstractMessage.CONNACK << 4))
|
||||
.put((byte)0) //0 length
|
||||
.flip();
|
||||
assertEquals(MessageDecoder.NOT_OK , m_msgdec.decodable(null, m_buff));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDecodable_NEED_DATA() {
|
||||
m_buff = IoBuffer.allocate(0);
|
||||
assertFalse(m_buff.hasRemaining());
|
||||
assertEquals(MessageDecoder.NEED_DATA , m_msgdec.decodable(null, m_buff));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDecodeRemainingLenght() {
|
||||
//1 byte length
|
||||
m_buff.put((byte)0x0).flip();
|
||||
assertEquals(0, Utils.decodeRemainingLenght(m_buff));
|
||||
m_buff.clear().put((byte) 0x007F).flip();
|
||||
assertEquals(127, Utils.decodeRemainingLenght(m_buff));
|
||||
|
||||
//2 byte length
|
||||
m_buff.clear().put((byte) 0x80).put((byte) 0x01).flip();
|
||||
assertEquals(128, Utils.decodeRemainingLenght(m_buff));
|
||||
m_buff.clear().put((byte) 0xFF).put((byte) 0x7F).flip();
|
||||
assertEquals(16383, Utils.decodeRemainingLenght(m_buff));
|
||||
|
||||
//3 byte length
|
||||
m_buff.clear().put((byte) 0x80).put((byte) 0x80).put((byte)0x01).flip();
|
||||
assertEquals(16384, Utils.decodeRemainingLenght(m_buff));
|
||||
m_buff.clear().put((byte) 0xFF).put((byte) 0xFF).put((byte)0x7F).flip();
|
||||
assertEquals(2097151, Utils.decodeRemainingLenght(m_buff));
|
||||
|
||||
//4 byte length
|
||||
m_buff.clear().put((byte) 0x80).put((byte) 0x80).put((byte)0x80).put((byte)0x01).flip();
|
||||
assertEquals(2097152, Utils.decodeRemainingLenght(m_buff));
|
||||
m_buff.clear().put((byte) 0xFF).put((byte) 0xFF).put((byte) 0xFF).put((byte)0x7F).flip();
|
||||
assertEquals(268435455, Utils.decodeRemainingLenght(m_buff));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBaseHeader() throws Exception {
|
||||
m_buff = IoBuffer.allocate(14);
|
||||
initBaseHeader(m_buff);
|
||||
m_buff.flip();
|
||||
|
||||
//Excercise
|
||||
m_msgdec.decode(null, m_buff, m_mockProtoDecoder);
|
||||
|
||||
//Verify
|
||||
verifyBaseHeader(m_mockProtoDecoder.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBaseHeader_ClientID() throws UnsupportedEncodingException, Exception {
|
||||
m_buff = IoBuffer.allocate(40);
|
||||
initHeader(m_buff, (byte) 38);
|
||||
encodeString(m_buff, "ABCDEFGH");
|
||||
encodeString(m_buff, "Topic");
|
||||
encodeString(m_buff, "Message");
|
||||
m_buff.flip();
|
||||
|
||||
//Excercise
|
||||
m_msgdec.decode(null, m_buff, m_mockProtoDecoder);
|
||||
|
||||
//Verify
|
||||
verifyBaseHeader(m_mockProtoDecoder.getMessage());
|
||||
assertEquals("ABCDEFGH", m_mockProtoDecoder.getMessage().getClientID());
|
||||
assertEquals("Topic", m_mockProtoDecoder.getMessage().getWillTopic());
|
||||
assertEquals("Message", m_mockProtoDecoder.getMessage().getWillMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBaseHeader_extra_with_user_pwd() throws UnsupportedEncodingException, Exception {
|
||||
m_buff = IoBuffer.allocate(55);
|
||||
initHeader(m_buff, (byte) 53);
|
||||
encodeString(m_buff, "ABCDEFGH");
|
||||
encodeString(m_buff, "Topic");
|
||||
encodeString(m_buff, "Message");
|
||||
encodeString(m_buff, "Fakeuser");
|
||||
encodeString(m_buff, "pwd");
|
||||
m_buff.flip();
|
||||
|
||||
//Excercise
|
||||
m_msgdec.decode(null, m_buff, m_mockProtoDecoder);
|
||||
|
||||
//Verify
|
||||
verifyBaseHeader(m_mockProtoDecoder.getMessage());
|
||||
assertEquals(AbstractMessage.CONNECT, m_mockProtoDecoder.getMessage().getMessageType());
|
||||
assertEquals("ABCDEFGH", m_mockProtoDecoder.getMessage().getClientID());
|
||||
assertEquals("Topic", m_mockProtoDecoder.getMessage().getWillTopic());
|
||||
assertEquals("Message", m_mockProtoDecoder.getMessage().getWillMessage());
|
||||
assertEquals("Fakeuser", m_mockProtoDecoder.getMessage().getUsername());
|
||||
assertEquals("pwd", m_mockProtoDecoder.getMessage().getPassword());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBadFlagUserPwd() throws UnsupportedEncodingException, Exception {
|
||||
m_buff = IoBuffer.allocate(14);
|
||||
m_buff.clear().put((byte)(AbstractMessage.CONNECT << 4)).put((byte)12);
|
||||
//Proto name
|
||||
encodeString(m_buff, "MQIsdp");
|
||||
//version
|
||||
m_buff.put((byte)3);
|
||||
//conn flags
|
||||
m_buff.put((byte)0x4E); //sets user to false and password to true
|
||||
//keepAlive
|
||||
m_buff.put((byte)0).put((byte) 0x0A);
|
||||
m_buff.flip();
|
||||
|
||||
//Excercise
|
||||
MessageDecoderResult res = m_msgdec.decode(null, m_buff, m_mockProtoDecoder);
|
||||
|
||||
assertNull(m_mockProtoDecoder.getMessage());
|
||||
assertEquals(MessageDecoder.NOT_OK, res);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode and insert the given string into the given buff
|
||||
*/
|
||||
private IoBuffer encodeString(IoBuffer buff, String str) throws UnsupportedEncodingException {
|
||||
buff.put(Utils.encodeString(str));
|
||||
return buff;
|
||||
}
|
||||
|
||||
private void initBaseHeader(IoBuffer buff) throws UnsupportedEncodingException {
|
||||
initHeader(buff, (byte)0x0C);
|
||||
}
|
||||
|
||||
private void initHeader(IoBuffer buff, byte remaingLength) throws UnsupportedEncodingException {
|
||||
buff.clear().put((byte)(AbstractMessage.CONNECT << 4)).put(remaingLength);
|
||||
//Proto name
|
||||
encodeString(buff, "MQIsdp");
|
||||
//version
|
||||
buff.put((byte)3);
|
||||
//conn flags
|
||||
buff.put((byte)0xCE);
|
||||
//keepAlive
|
||||
buff.put((byte)0).put((byte) 0x0A);
|
||||
}
|
||||
|
||||
private void verifyBaseHeader(ConnectMessage connMessage) {
|
||||
assertNotNull(connMessage);
|
||||
assertEquals("MQIsdp", connMessage.getProtocolName());
|
||||
assertEquals(3, connMessage.getProcotolVersion());
|
||||
assertTrue(connMessage.isUserFlag());
|
||||
assertTrue(connMessage.isPasswordFlag());
|
||||
assertTrue(connMessage.isCleanSession());
|
||||
assertEquals(10, connMessage.getKeepAlive());
|
||||
assertTrue(connMessage.isWillFlag());
|
||||
assertFalse(connMessage.isWillRetain());
|
||||
assertEquals(1, connMessage.getWillQos());
|
||||
}
|
||||
}
|
||||
@@ -1,147 +0,0 @@
|
||||
package org.dna.mqtt.moquette.proto;
|
||||
|
||||
import org.dna.mqtt.moquette.proto.ConnectEncoder;
|
||||
import org.dna.mqtt.moquette.proto.Utils;
|
||||
import org.dna.mqtt.moquette.proto.messages.ConnectMessage;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.dna.mqtt.moquette.proto.TestUtils.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author andrea
|
||||
*/
|
||||
public class ConnectEncoderTest {
|
||||
|
||||
ConnectEncoder m_encoder;
|
||||
TestUtils.MockProtocolEncoderOutput m_mockProtoEncoder;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
m_encoder = new ConnectEncoder();
|
||||
m_mockProtoEncoder = new TestUtils.MockProtocolEncoderOutput();
|
||||
}
|
||||
|
||||
@Test(expected=IllegalAccessException.class)
|
||||
public void testEncodeRemainingLength_invalid_upper() throws IllegalAccessException {
|
||||
Utils.encodeRemainingLength(Utils.MAX_LENGTH_LIMIT + 1);
|
||||
}
|
||||
|
||||
@Test(expected=IllegalAccessException.class)
|
||||
public void testEncodeRemainingLength_invalid_lower() throws IllegalAccessException {
|
||||
Utils.encodeRemainingLength(-1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncodeRemainingLenght() throws IllegalAccessException {
|
||||
//1 byte length
|
||||
verifyBuff(1, new byte[]{0}, Utils.encodeRemainingLength(0));
|
||||
verifyBuff(1, new byte[]{0x7F}, Utils.encodeRemainingLength(127));
|
||||
|
||||
//2 byte length
|
||||
verifyBuff(2, new byte[]{(byte)0x80, 0x01}, Utils.encodeRemainingLength(128));
|
||||
verifyBuff(2, new byte[]{(byte)0xFF, 0x7F}, Utils.encodeRemainingLength(16383));
|
||||
|
||||
//3 byte length
|
||||
verifyBuff(3, new byte[]{(byte)0x80, (byte)0x80, 0x01}, Utils.encodeRemainingLength(16384));
|
||||
verifyBuff(3, new byte[]{(byte)0xFF, (byte)0xFF, 0x7F}, Utils.encodeRemainingLength(2097151));
|
||||
|
||||
//4 byte length
|
||||
verifyBuff(4, new byte[]{(byte)0x80, (byte)0x80, (byte)0x80, 0x01}, Utils.encodeRemainingLength(2097152));
|
||||
verifyBuff(4, new byte[]{(byte)0xFF, (byte)0xFF, (byte)0xFF, 0x7F}, Utils.encodeRemainingLength(268435455));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBaseHeader() throws Exception {
|
||||
ConnectMessage msg = new ConnectMessage();
|
||||
msg.setWillRetain(true);
|
||||
msg.setWillQos((byte)2);
|
||||
msg.setWillFlag(false);
|
||||
msg.setCleanSession(true);
|
||||
msg.setKeepAlive(512);
|
||||
|
||||
//Exercise
|
||||
m_encoder.encode(null, msg, m_mockProtoEncoder);
|
||||
|
||||
//Verify
|
||||
assertEquals(0x10, m_mockProtoEncoder.getBuffer().get()); //1 byte
|
||||
assertEquals(12, m_mockProtoEncoder.getBuffer().get()); //remaining length
|
||||
verifyString("MQIsdp", m_mockProtoEncoder.getBuffer());
|
||||
assertEquals(0x03, m_mockProtoEncoder.getBuffer().get()); //protocol version
|
||||
assertEquals(0x32, m_mockProtoEncoder.getBuffer().get()); //flags
|
||||
assertEquals(2, m_mockProtoEncoder.getBuffer().get()); //keepAliveTimer msb
|
||||
assertEquals(0, m_mockProtoEncoder.getBuffer().get()); //keepAliveTimer lsb
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCompleteHeader() throws Exception {
|
||||
ConnectMessage msg = new ConnectMessage();
|
||||
msg.setWillRetain(true);
|
||||
msg.setWillQos((byte)2);
|
||||
msg.setWillFlag(true);
|
||||
msg.setCleanSession(true);
|
||||
msg.setKeepAlive(512);
|
||||
|
||||
//variable part
|
||||
msg.setClientID("ABCDEF");
|
||||
msg.setWillTopic("Topic");
|
||||
msg.setWillMessage("Message");
|
||||
|
||||
//Exercise
|
||||
m_encoder.encode(null, msg, m_mockProtoEncoder);
|
||||
|
||||
//Verify
|
||||
assertEquals(0x10, m_mockProtoEncoder.getBuffer().get()); //1 byte
|
||||
assertEquals(36, m_mockProtoEncoder.getBuffer().get()); //remaining length
|
||||
verifyString("MQIsdp", m_mockProtoEncoder.getBuffer());
|
||||
assertEquals(0x03, m_mockProtoEncoder.getBuffer().get()); //protocol version
|
||||
assertEquals(0x36, m_mockProtoEncoder.getBuffer().get()); //flags
|
||||
assertEquals(2, m_mockProtoEncoder.getBuffer().get()); //keepAliveTimer msb
|
||||
assertEquals(0, m_mockProtoEncoder.getBuffer().get()); //keepAliveTimer lsb
|
||||
|
||||
//Variable part
|
||||
verifyString("ABCDEF", m_mockProtoEncoder.getBuffer());
|
||||
verifyString("Topic", m_mockProtoEncoder.getBuffer());
|
||||
verifyString("Message", m_mockProtoEncoder.getBuffer());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCompleteHeaderWIthUser_password() throws Exception {
|
||||
ConnectMessage msg = new ConnectMessage();
|
||||
msg.setUserFlag(true);
|
||||
msg.setPasswordFlag(true);
|
||||
msg.setWillRetain(true);
|
||||
msg.setWillQos((byte)2);
|
||||
msg.setWillFlag(true);
|
||||
msg.setCleanSession(true);
|
||||
msg.setKeepAlive(512);
|
||||
|
||||
//variable part
|
||||
msg.setClientID("ABCDEF");
|
||||
msg.setWillTopic("Topic");
|
||||
msg.setWillMessage("Message");
|
||||
msg.setUsername("Pablo");
|
||||
msg.setPassword("PBL");
|
||||
|
||||
//Exercise
|
||||
m_encoder.encode(null, msg, m_mockProtoEncoder);
|
||||
|
||||
//Verify
|
||||
assertEquals(0x10, m_mockProtoEncoder.getBuffer().get()); //1 byte
|
||||
assertEquals(48, m_mockProtoEncoder.getBuffer().get()); //remaining length
|
||||
verifyString("MQIsdp", m_mockProtoEncoder.getBuffer());
|
||||
assertEquals(0x03, m_mockProtoEncoder.getBuffer().get()); //protocol version
|
||||
assertEquals((byte)0xF6, (byte)m_mockProtoEncoder.getBuffer().get()); //flags
|
||||
assertEquals(2, m_mockProtoEncoder.getBuffer().get()); //keepAliveTimer msb
|
||||
assertEquals(0, m_mockProtoEncoder.getBuffer().get()); //keepAliveTimer lsb
|
||||
|
||||
//Variable part
|
||||
verifyString("ABCDEF", m_mockProtoEncoder.getBuffer());
|
||||
verifyString("Topic", m_mockProtoEncoder.getBuffer());
|
||||
verifyString("Message", m_mockProtoEncoder.getBuffer());
|
||||
verifyString("Pablo", m_mockProtoEncoder.getBuffer());//username
|
||||
verifyString("PBL", m_mockProtoEncoder.getBuffer());//password
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
package org.dna.mqtt.moquette.proto;
|
||||
|
||||
import org.apache.mina.core.buffer.IoBuffer;
|
||||
import org.apache.mina.filter.codec.demux.MessageDecoder;
|
||||
import org.apache.mina.filter.codec.demux.MessageDecoderResult;
|
||||
import org.dna.mqtt.moquette.proto.TestUtils.MockProtocolDecoderOutput;
|
||||
import org.dna.mqtt.moquette.proto.messages.AbstractMessage;
|
||||
import org.dna.mqtt.moquette.proto.messages.PubAckMessage;
|
||||
import static org.junit.Assert.*;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author andrea
|
||||
*/
|
||||
public class PubAckDecoderTest {
|
||||
IoBuffer m_buff;
|
||||
PubAckDecoder m_msgdec;
|
||||
MockProtocolDecoderOutput<PubAckMessage> m_mockProtoDecoder;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
m_buff = IoBuffer.allocate(4);
|
||||
m_msgdec = new PubAckDecoder();
|
||||
m_mockProtoDecoder = new MockProtocolDecoderOutput<PubAckMessage>();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHeader() throws Exception {
|
||||
m_buff = IoBuffer.allocate(14);
|
||||
int messageId = 0xAABB;
|
||||
initHeader(m_buff, messageId);
|
||||
m_buff.flip();
|
||||
|
||||
//Exercise
|
||||
MessageDecoderResult res = m_msgdec.decode(null, m_buff, m_mockProtoDecoder);
|
||||
|
||||
assertNotNull(m_mockProtoDecoder.getMessage());
|
||||
assertEquals(MessageDecoder.OK, res);
|
||||
assertEquals(messageId, m_mockProtoDecoder.getMessage().getMessageID().intValue());
|
||||
assertEquals(AbstractMessage.PUBACK, m_mockProtoDecoder.getMessage().getMessageType());
|
||||
}
|
||||
|
||||
private void initHeader(IoBuffer buff, int messageID) {
|
||||
buff.clear().put((byte)(AbstractMessage.PUBACK << 4)).put((byte)2);
|
||||
|
||||
//return code
|
||||
Utils.writeWord(buff, messageID);
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
package org.dna.mqtt.moquette.proto;
|
||||
|
||||
import org.dna.mqtt.moquette.proto.messages.PubAckMessage;
|
||||
import org.junit.Test;
|
||||
import static org.junit.Assert.*;
|
||||
/**
|
||||
*
|
||||
* @author andrea
|
||||
*/
|
||||
public class PubAckEncoderTest {
|
||||
|
||||
@Test
|
||||
public void testHeaderEncode() throws Exception {
|
||||
int messageID = 0xAABB;
|
||||
TestUtils.MockProtocolEncoderOutput mockProtoEncoder = new TestUtils.MockProtocolEncoderOutput();
|
||||
PubAckEncoder encoder = new PubAckEncoder();
|
||||
PubAckMessage msg = new PubAckMessage();
|
||||
msg.setMessageID(messageID);
|
||||
|
||||
//Exercise
|
||||
encoder.encode(null, msg, mockProtoEncoder);
|
||||
|
||||
//Verify
|
||||
assertEquals(0x40, mockProtoEncoder.getBuffer().get()); //1 byte
|
||||
assertEquals(0x02, mockProtoEncoder.getBuffer().get()); //2 byte, length
|
||||
assertEquals((byte)0xAA, mockProtoEncoder.getBuffer().get());
|
||||
assertEquals((byte)0xBB, mockProtoEncoder.getBuffer().get());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,175 +0,0 @@
|
||||
package org.dna.mqtt.moquette.proto;
|
||||
|
||||
import org.apache.mina.core.buffer.IoBuffer;
|
||||
import org.apache.mina.filter.codec.demux.MessageDecoder;
|
||||
import org.apache.mina.filter.codec.demux.MessageDecoderResult;
|
||||
import org.dna.mqtt.moquette.proto.TestUtils.MockProtocolDecoderOutput;
|
||||
import org.dna.mqtt.moquette.proto.messages.AbstractMessage;
|
||||
import org.dna.mqtt.moquette.proto.messages.PublishMessage;
|
||||
import static org.junit.Assert.*;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author andrea
|
||||
*/
|
||||
public class PublishDecoderTest {
|
||||
|
||||
IoBuffer m_buff;
|
||||
PublishDecoder m_msgdec;
|
||||
MockProtocolDecoderOutput<PublishMessage> m_mockProtoDecoder;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
m_buff = IoBuffer.allocate(4);
|
||||
m_msgdec = new PublishDecoder();
|
||||
m_mockProtoDecoder = new MockProtocolDecoderOutput<PublishMessage>();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDecodable_OK() {
|
||||
m_buff.put((byte) (AbstractMessage.PUBLISH << 4))
|
||||
.put((byte) 0) //0 length
|
||||
.flip();
|
||||
assertEquals(MessageDecoder.OK, m_msgdec.decodable(null, m_buff));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHeader() throws Exception {
|
||||
m_buff = IoBuffer.allocate(14);
|
||||
initHeader(m_buff);
|
||||
m_buff.flip();
|
||||
|
||||
//Exercise
|
||||
MessageDecoderResult res = m_msgdec.decode(null, m_buff, m_mockProtoDecoder);
|
||||
|
||||
assertNotNull(m_mockProtoDecoder.getMessage());
|
||||
assertEquals(MessageDecoder.OK, res);
|
||||
assertEquals("Fake Topic", m_mockProtoDecoder.getMessage().getTopicName());
|
||||
assertNull(m_mockProtoDecoder.getMessage().getMessageID());
|
||||
assertEquals(AbstractMessage.PUBLISH, m_mockProtoDecoder.getMessage().getMessageType());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHeaderWithMessageID() throws Exception {
|
||||
m_buff = IoBuffer.allocate(14).setAutoExpand(true);
|
||||
int messageID = 123;
|
||||
initHeaderWithMessageID(m_buff, messageID);
|
||||
m_buff.flip();
|
||||
|
||||
//Exercise
|
||||
MessageDecoderResult res = m_msgdec.decode(null, m_buff, m_mockProtoDecoder);
|
||||
|
||||
assertNotNull(m_mockProtoDecoder.getMessage());
|
||||
assertEquals(MessageDecoder.OK, res);
|
||||
assertEquals("Fake Topic", m_mockProtoDecoder.getMessage().getTopicName());
|
||||
assertEquals(messageID, (int) m_mockProtoDecoder.getMessage().getMessageID());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHeaderWithMessageID_Payload() throws Exception {
|
||||
m_buff = IoBuffer.allocate(14).setAutoExpand(true);
|
||||
int messageID = 123;
|
||||
byte[] payload = new byte[]{0x0A, 0x0B, 0x0C};
|
||||
initHeaderWithMessageID_Payload(m_buff, messageID, payload);
|
||||
m_buff.flip();
|
||||
|
||||
//Exercise
|
||||
MessageDecoderResult res = m_msgdec.decode(null, m_buff, m_mockProtoDecoder);
|
||||
|
||||
assertNotNull(m_mockProtoDecoder.getMessage());
|
||||
assertEquals(MessageDecoder.OK, res);
|
||||
assertEquals("Fake Topic", m_mockProtoDecoder.getMessage().getTopicName());
|
||||
assertEquals(messageID, (int) m_mockProtoDecoder.getMessage().getMessageID());
|
||||
TestUtils.verifyEquals(payload, m_mockProtoDecoder.getMessage().getPayload());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBugOnRealCase() throws Exception {
|
||||
byte[] overallMessage = new byte[]{0x30, 0x17, //fixed header, 25 byte lenght
|
||||
0x00, 0x06, 0x2f, 0x74, 0x6f, 0x70, 0x69, 0x63, //[/topic] string 2 len + 6 content
|
||||
0x54, 0x65, 0x73, 0x74, 0x20, 0x6d, 0x79, // [Test my payload] encoding
|
||||
0x20, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64};
|
||||
m_buff = IoBuffer.allocate(overallMessage.length).setAutoExpand(true);
|
||||
m_buff.put(overallMessage);
|
||||
m_buff.flip();
|
||||
|
||||
//Exercise
|
||||
MessageDecoderResult res = m_msgdec.decode(null, m_buff, m_mockProtoDecoder);
|
||||
|
||||
assertNotNull(m_mockProtoDecoder.getMessage());
|
||||
assertEquals(MessageDecoder.OK, res);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDecodeBigContent() throws Exception {
|
||||
int size = 129;
|
||||
IoBuffer payload = TestUtils.generateRandomPayload(size);
|
||||
|
||||
IoBuffer firstPublish = generatePublishQoS0(payload);
|
||||
IoBuffer secondPublish = generatePublishQoS0(TestUtils.generateRandomPayload(size));
|
||||
|
||||
IoBuffer doubleMessageBuf = IoBuffer.allocate(size * 2).setAutoExpand(true);
|
||||
doubleMessageBuf.put(firstPublish).put(secondPublish).flip();
|
||||
|
||||
|
||||
//Exercise
|
||||
MessageDecoderResult res = m_msgdec.decode(null, doubleMessageBuf, m_mockProtoDecoder);
|
||||
|
||||
assertEquals(MessageDecoder.OK, res);
|
||||
PublishMessage pubMsg = m_mockProtoDecoder.getMessage();
|
||||
assertNotNull(pubMsg);
|
||||
|
||||
res = m_msgdec.decode(null, doubleMessageBuf, m_mockProtoDecoder);
|
||||
|
||||
assertNotNull(m_mockProtoDecoder.getMessage());
|
||||
assertEquals(MessageDecoder.OK, res);
|
||||
|
||||
m_buff.flip();
|
||||
}
|
||||
|
||||
private void initHeader(IoBuffer buff) throws IllegalAccessException {
|
||||
IoBuffer tmp = IoBuffer.allocate(4).setAutoExpand(true).put(Utils.encodeString("Fake Topic")).flip();
|
||||
buff.clear().put((byte) (AbstractMessage.PUBLISH << 4)).put(Utils.encodeRemainingLength(tmp.remaining()));
|
||||
//topic name
|
||||
buff.put(tmp);
|
||||
}
|
||||
|
||||
private void initHeaderWithMessageID(IoBuffer buff, int messageID) throws IllegalAccessException {
|
||||
IoBuffer tmp = IoBuffer.allocate(4).setAutoExpand(true).put(Utils.encodeString("Fake Topic"));
|
||||
Utils.writeWord(tmp, messageID);
|
||||
tmp.flip();
|
||||
buff.clear().put((byte) (AbstractMessage.PUBLISH << 4 | 0x02)) //set Qos to 1
|
||||
.put(Utils.encodeRemainingLength(tmp.remaining()));
|
||||
//topic name
|
||||
buff.put(tmp);
|
||||
}
|
||||
|
||||
private void initHeaderWithMessageID_Payload(IoBuffer buff, int messageID, byte[] payload) throws IllegalAccessException {
|
||||
IoBuffer tmp = IoBuffer.allocate(4).setAutoExpand(true).put(Utils.encodeString("Fake Topic"));
|
||||
Utils.writeWord(tmp, messageID);
|
||||
tmp.put(payload);
|
||||
tmp.flip();
|
||||
buff.clear().put((byte) (AbstractMessage.PUBLISH << 4 | 0x02)) //set Qos to 1
|
||||
.put(Utils.encodeRemainingLength(tmp.remaining()));
|
||||
//topic name
|
||||
buff.put(tmp);
|
||||
}
|
||||
|
||||
private IoBuffer generatePublishQoS0(IoBuffer payload) throws IllegalAccessException {
|
||||
int size = payload.capacity();
|
||||
IoBuffer messageBody = IoBuffer.allocate(size).setAutoExpand(true);
|
||||
messageBody.put(Utils.encodeString("/topic"));
|
||||
|
||||
//ONLY for QoS > 1 Utils.writeWord(messageBody, messageID);
|
||||
messageBody.put(payload).flip();
|
||||
|
||||
IoBuffer completeMsg = IoBuffer.allocate(size).setAutoExpand(true);
|
||||
completeMsg.clear().put((byte) (AbstractMessage.PUBLISH << 4 | 0x00)) //set Qos to 0
|
||||
.put(Utils.encodeRemainingLength(messageBody.remaining()));
|
||||
completeMsg.put(messageBody).flip();
|
||||
|
||||
return completeMsg;
|
||||
}
|
||||
}
|
||||
@@ -1,166 +0,0 @@
|
||||
package org.dna.mqtt.moquette.proto;
|
||||
|
||||
import org.dna.mqtt.moquette.proto.messages.AbstractMessage.QOSType;
|
||||
import org.dna.mqtt.moquette.proto.messages.PublishMessage;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.dna.mqtt.moquette.proto.TestUtils.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author andrea
|
||||
*/
|
||||
public class PublishEncoderTest {
|
||||
|
||||
PublishEncoder m_encoder;
|
||||
TestUtils.MockProtocolEncoderOutput m_mockProtoEncoder;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
m_encoder = new PublishEncoder();
|
||||
m_mockProtoEncoder = new TestUtils.MockProtocolEncoderOutput();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncodeWithQos_0_noMessageID() throws Exception {
|
||||
String topic = "/photos";
|
||||
PublishMessage msg = new PublishMessage();
|
||||
msg.setQos(QOSType.MOST_ONE);
|
||||
msg.setTopicName(topic);
|
||||
|
||||
//variable part
|
||||
byte[] payload = new byte[]{0x0A, 0x0B, 0x0C};
|
||||
msg.setPayload(payload);
|
||||
|
||||
//Exercise
|
||||
m_encoder.encode(null, msg, m_mockProtoEncoder);
|
||||
|
||||
//Verify
|
||||
assertEquals(0x30, m_mockProtoEncoder.getBuffer().get()); //1 byte
|
||||
//(2+7) topic + 3 payload
|
||||
assertEquals(12, m_mockProtoEncoder.getBuffer().get()); //remaining length
|
||||
|
||||
//Variable part
|
||||
verifyString(topic, m_mockProtoEncoder.getBuffer());
|
||||
verifyBuff(payload.length, payload, m_mockProtoEncoder.getBuffer());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncodeWithQos_1_MessageID() throws Exception {
|
||||
String topic = "/photos";
|
||||
PublishMessage msg = new PublishMessage();
|
||||
msg.setQos(QOSType.LEAST_ONE);
|
||||
msg.setMessageID(1);
|
||||
msg.setTopicName(topic);
|
||||
|
||||
//variable part
|
||||
byte[] payload = new byte[]{0x0A, 0x0B, 0x0C};
|
||||
msg.setPayload(payload);
|
||||
|
||||
//Exercise
|
||||
m_encoder.encode(null, msg, m_mockProtoEncoder);
|
||||
|
||||
//Verify
|
||||
assertEquals(0x32, m_mockProtoEncoder.getBuffer().get()); //1 byte
|
||||
//(2+7) topic + 2 messageID + 3 payload
|
||||
assertEquals(14, m_mockProtoEncoder.getBuffer().get()); //remaining length
|
||||
|
||||
//Variable part
|
||||
verifyString(topic, m_mockProtoEncoder.getBuffer());
|
||||
assertEquals(0, m_mockProtoEncoder.getBuffer().get()); //MessageID MSB
|
||||
assertEquals(1, m_mockProtoEncoder.getBuffer().get()); //MessageID LSB
|
||||
verifyBuff(payload.length, payload, m_mockProtoEncoder.getBuffer());
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testEncodeWithQos_1_noMessageID_fail() throws Exception {
|
||||
String topic = "pictures/photos";
|
||||
PublishMessage msg = new PublishMessage();
|
||||
msg.setQos(QOSType.LEAST_ONE);
|
||||
msg.setTopicName(topic);
|
||||
|
||||
//Exercise
|
||||
m_encoder.encode(null, msg, m_mockProtoEncoder);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testEncode_empty_topic() throws Exception {
|
||||
PublishMessage msg = new PublishMessage();
|
||||
msg.setQos(QOSType.LEAST_ONE);
|
||||
msg.setMessageID(1);
|
||||
|
||||
//Exercise
|
||||
m_encoder.encode(null, msg, m_mockProtoEncoder);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testEncode_null_topic() throws Exception {
|
||||
PublishMessage msg = new PublishMessage();
|
||||
msg.setQos(QOSType.LEAST_ONE);
|
||||
msg.setMessageID(1);
|
||||
msg.setTopicName(null);
|
||||
|
||||
//Exercise
|
||||
m_encoder.encode(null, msg, m_mockProtoEncoder);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testEncode_qos_reserved() throws Exception {
|
||||
PublishMessage msg = new PublishMessage();
|
||||
msg.setQos(QOSType.RESERVED);
|
||||
msg.setMessageID(1);
|
||||
msg.setTopicName(null);
|
||||
|
||||
//variable part
|
||||
byte[] payload = new byte[]{0x0A, 0x0B, 0x0C};
|
||||
msg.setPayload(payload);
|
||||
|
||||
//Exercise
|
||||
m_encoder.encode(null, msg, m_mockProtoEncoder);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBugMissedMessageIDEncoding() throws Exception{
|
||||
String topic = "/topic";
|
||||
PublishMessage msg = new PublishMessage();
|
||||
msg.setQos(QOSType.MOST_ONE);
|
||||
msg.setTopicName(topic);
|
||||
|
||||
//variable part
|
||||
byte[] payload = "Test my payload".getBytes();
|
||||
msg.setPayload(payload);
|
||||
|
||||
//Exercise
|
||||
m_encoder.encode(null, msg, m_mockProtoEncoder);
|
||||
|
||||
//Verify
|
||||
//2 byte header + (2+6) topic name + [2 message ID if QoQ <> 0]+ payload
|
||||
int size = m_mockProtoEncoder.getBuffer().remaining();
|
||||
assertEquals(10+payload.length, size);
|
||||
assertEquals(0x30, m_mockProtoEncoder.getBuffer().get()); //1 byte
|
||||
assertEquals(23, m_mockProtoEncoder.getBuffer().get()); //1 byte the length
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncodeBigMessage() throws Exception {
|
||||
byte[] payload = new byte[256];
|
||||
|
||||
TestUtils.generateRandomPayload(256).get(payload);
|
||||
|
||||
String topic = "/topic";
|
||||
PublishMessage msg = new PublishMessage();
|
||||
msg.setQos(QOSType.MOST_ONE);
|
||||
msg.setTopicName(topic);
|
||||
|
||||
//variable part
|
||||
msg.setPayload(payload);
|
||||
|
||||
//Exercise
|
||||
m_encoder.encode(null, msg, m_mockProtoEncoder);
|
||||
|
||||
int size = m_mockProtoEncoder.getBuffer().remaining();
|
||||
assertEquals(11+payload.length, size);
|
||||
assertEquals(0x30, m_mockProtoEncoder.getBuffer().get()); //1 byte
|
||||
}
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
package org.dna.mqtt.moquette.proto;
|
||||
|
||||
import java.util.List;
|
||||
import org.apache.mina.core.buffer.IoBuffer;
|
||||
import org.apache.mina.filter.codec.demux.MessageDecoder;
|
||||
import org.apache.mina.filter.codec.demux.MessageDecoderResult;
|
||||
import org.dna.mqtt.moquette.proto.TestUtils.MockProtocolDecoderOutput;
|
||||
import org.dna.mqtt.moquette.proto.messages.AbstractMessage;
|
||||
import org.dna.mqtt.moquette.proto.messages.AbstractMessage.QOSType;
|
||||
import org.dna.mqtt.moquette.proto.messages.SubAckMessage;
|
||||
import static org.junit.Assert.*;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author andrea
|
||||
*/
|
||||
public class SubAckDecoderTest {
|
||||
|
||||
IoBuffer m_buff;
|
||||
SubAckDecoder m_msgdec;
|
||||
MockProtocolDecoderOutput<SubAckMessage> m_mockProtoDecoder;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
m_buff = IoBuffer.allocate(7);
|
||||
m_msgdec = new SubAckDecoder();
|
||||
m_mockProtoDecoder = new MockProtocolDecoderOutput<SubAckMessage>();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBadQos() throws Exception {
|
||||
initHeaderQos(m_buff, 0xAABB, QOSType.LEAST_ONE, QOSType.MOST_ONE, QOSType.MOST_ONE);
|
||||
m_buff.flip();
|
||||
|
||||
//Excercise
|
||||
MessageDecoderResult res = m_msgdec.decode(null, m_buff, m_mockProtoDecoder);
|
||||
|
||||
//Verify
|
||||
assertEquals(MessageDecoderResult.OK, res);
|
||||
assertEquals(0xAABB, m_mockProtoDecoder.getMessage().getMessageID().intValue());
|
||||
List<QOSType> qoses = m_mockProtoDecoder.getMessage().types();
|
||||
assertEquals(3, qoses.size());
|
||||
assertEquals(QOSType.LEAST_ONE, qoses.get(0));
|
||||
assertEquals(QOSType.MOST_ONE, qoses.get(1));
|
||||
assertEquals(QOSType.MOST_ONE, qoses.get(2));
|
||||
assertEquals(AbstractMessage.SUBACK, m_mockProtoDecoder.getMessage().getMessageType());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testBugBadRemainingCalculation() throws Exception {
|
||||
byte[] overallMessage = new byte[] {(byte)0x90, 0x03, //fixed header
|
||||
0x00, 0x0A, //MSG ID
|
||||
0x01}; //QoS array
|
||||
m_buff = IoBuffer.allocate(overallMessage.length).setAutoExpand(true);
|
||||
m_buff.put(overallMessage);
|
||||
m_buff.flip();
|
||||
|
||||
//Exercise
|
||||
MessageDecoderResult res = m_msgdec.decode(null, m_buff, m_mockProtoDecoder);
|
||||
|
||||
assertNotNull(m_mockProtoDecoder.getMessage());
|
||||
assertEquals(MessageDecoder.OK, res);
|
||||
SubAckMessage message = (SubAckMessage) m_mockProtoDecoder.getMessage();
|
||||
assertEquals(0x0A, message.getMessageID().intValue());
|
||||
assertEquals(1, message.types().size());
|
||||
assertEquals(AbstractMessage.QOSType.LEAST_ONE, message.types().get(0));
|
||||
}
|
||||
|
||||
private void initHeaderQos(IoBuffer buff, int messageID, QOSType... qoss) throws IllegalAccessException {
|
||||
buff.clear().put((byte) (AbstractMessage.SUBACK << 4)).
|
||||
put(Utils.encodeRemainingLength(2 + qoss.length));
|
||||
|
||||
Utils.writeWord(buff, messageID);
|
||||
for (QOSType qos : qoss) {
|
||||
buff.put((byte)qos.ordinal());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
package org.dna.mqtt.moquette.proto;
|
||||
|
||||
import org.dna.mqtt.moquette.proto.messages.AbstractMessage;
|
||||
import org.dna.mqtt.moquette.proto.messages.AbstractMessage.QOSType;
|
||||
import org.dna.mqtt.moquette.proto.messages.SubAckMessage;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author andrea
|
||||
*/
|
||||
public class SubAckEncoderTest {
|
||||
SubAckEncoder m_encoder;
|
||||
TestUtils.MockProtocolEncoderOutput m_mockProtoEncoder;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
m_encoder = new SubAckEncoder();
|
||||
m_mockProtoEncoder = new TestUtils.MockProtocolEncoderOutput();
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testEncodeWithNoQoss() throws Exception {
|
||||
SubAckMessage msg = new SubAckMessage();
|
||||
msg.setMessageID(123);
|
||||
|
||||
//Exercise
|
||||
m_encoder.encode(null, msg, m_mockProtoEncoder);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncodeWithMultipleQos() throws Exception {
|
||||
SubAckMessage msg = new SubAckMessage();
|
||||
|
||||
int messageID = 0xAABB;
|
||||
msg.setMessageID(messageID);
|
||||
msg.addType(QOSType.MOST_ONE);
|
||||
msg.addType(QOSType.LEAST_ONE);
|
||||
msg.addType(QOSType.EXACTLY_ONCE);
|
||||
|
||||
//Exercise
|
||||
m_encoder.encode(null, msg, m_mockProtoEncoder);
|
||||
|
||||
//Verify
|
||||
assertEquals((byte) (AbstractMessage.SUBACK << 4 ), m_mockProtoEncoder.getBuffer().get()); //1 byte
|
||||
assertEquals(5, m_mockProtoEncoder.getBuffer().get()); //remaining length
|
||||
|
||||
//Variable part
|
||||
assertEquals((byte)0xAA, m_mockProtoEncoder.getBuffer().get()); //MessageID MSB
|
||||
assertEquals((byte)0xBB, m_mockProtoEncoder.getBuffer().get()); //MessageID LSB
|
||||
assertEquals((byte)QOSType.MOST_ONE.ordinal(), m_mockProtoEncoder.getBuffer().get());
|
||||
assertEquals((byte)QOSType.LEAST_ONE.ordinal(), m_mockProtoEncoder.getBuffer().get());
|
||||
assertEquals((byte)QOSType.EXACTLY_ONCE.ordinal(), m_mockProtoEncoder.getBuffer().get());
|
||||
}
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
package org.dna.mqtt.moquette.proto;
|
||||
|
||||
import org.apache.mina.core.buffer.IoBuffer;
|
||||
import org.apache.mina.filter.codec.demux.MessageDecoderResult;
|
||||
import org.dna.mqtt.moquette.proto.TestUtils.MockProtocolDecoderOutput;
|
||||
import org.dna.mqtt.moquette.proto.messages.AbstractMessage;
|
||||
import org.dna.mqtt.moquette.proto.messages.SubscribeMessage;
|
||||
import org.dna.mqtt.moquette.proto.messages.SubscribeMessage.Couple;
|
||||
import static org.junit.Assert.*;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author andrea
|
||||
*/
|
||||
public class SubscribeDecoderTest {
|
||||
|
||||
IoBuffer m_buff;
|
||||
SubscribeDecoder m_msgdec;
|
||||
MockProtocolDecoderOutput<SubscribeMessage> m_mockProtoDecoder;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
m_buff = IoBuffer.allocate(4);
|
||||
m_msgdec = new SubscribeDecoder();
|
||||
m_mockProtoDecoder = new MockProtocolDecoderOutput<SubscribeMessage>();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBadQos() throws Exception {
|
||||
m_buff = IoBuffer.allocate(2);
|
||||
initHeaderBadQos(m_buff);
|
||||
m_buff.flip();
|
||||
|
||||
//Excercise
|
||||
MessageDecoderResult res = m_msgdec.decode(null, m_buff, m_mockProtoDecoder);
|
||||
|
||||
//Verify
|
||||
assertEquals(MessageDecoderResult.NOT_OK, res);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultiTopic() throws Exception {
|
||||
m_buff = IoBuffer.allocate(4).setAutoExpand(true);
|
||||
Couple c1 = new Couple((byte)2, "a/b");
|
||||
Couple c2 = new Couple((byte)1, "c/d/e");
|
||||
initMultiTopic(m_buff, 123, c1, c2);
|
||||
m_buff.flip();
|
||||
|
||||
//Excercise
|
||||
MessageDecoderResult res = m_msgdec.decode(null, m_buff, m_mockProtoDecoder);
|
||||
|
||||
//Verify
|
||||
assertEquals(MessageDecoderResult.OK, res);
|
||||
assertEquals(2, m_mockProtoDecoder.getMessage().subscriptions().size());
|
||||
assertEquals(AbstractMessage.SUBSCRIBE, m_mockProtoDecoder.getMessage().getMessageType());
|
||||
}
|
||||
|
||||
private void initHeaderBadQos(IoBuffer buff) {
|
||||
buff.clear().put((byte)(AbstractMessage.SUBSCRIBE << 4)).put((byte)0);
|
||||
}
|
||||
|
||||
private void initMultiTopic(IoBuffer buff, int messageID, Couple... topics) throws IllegalAccessException {
|
||||
IoBuffer topicBuffer = IoBuffer.allocate(4).setAutoExpand(true);
|
||||
Utils.writeWord(topicBuffer, messageID);
|
||||
for (Couple couple : topics) {
|
||||
topicBuffer.put(Utils.encodeString(couple.getTopic()));
|
||||
topicBuffer.put((byte)couple.getQos());
|
||||
}
|
||||
topicBuffer.flip();
|
||||
|
||||
buff.clear().put((byte)(AbstractMessage.SUBSCRIBE << 4 | (byte)0x02))
|
||||
.put(Utils.encodeRemainingLength(topicBuffer.remaining()));
|
||||
buff.put(topicBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
package org.dna.mqtt.moquette.proto;
|
||||
|
||||
import static org.dna.mqtt.moquette.proto.TestUtils.*;
|
||||
import org.dna.mqtt.moquette.proto.messages.AbstractMessage.QOSType;
|
||||
import org.dna.mqtt.moquette.proto.messages.SubscribeMessage;
|
||||
import org.dna.mqtt.moquette.proto.messages.SubscribeMessage.Couple;
|
||||
import static org.junit.Assert.*;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author andrea
|
||||
*/
|
||||
public class SubscribeEncoderTest {
|
||||
|
||||
SubscribeEncoder m_encoder;
|
||||
TestUtils.MockProtocolEncoderOutput m_mockProtoEncoder;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
m_encoder = new SubscribeEncoder();
|
||||
m_mockProtoEncoder = new TestUtils.MockProtocolEncoderOutput();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncodeWithMultiTopic() throws Exception {
|
||||
SubscribeMessage msg = new SubscribeMessage();
|
||||
msg.setQos(QOSType.LEAST_ONE);
|
||||
msg.setMessageID(0xAABB);
|
||||
|
||||
//variable part
|
||||
Couple c1 = new Couple((byte)1, "a/b");
|
||||
Couple c2 = new Couple((byte)0, "a/b/c");
|
||||
msg.addSubscription(c1);
|
||||
msg.addSubscription(c2);
|
||||
|
||||
//Exercise
|
||||
m_encoder.encode(null, msg, m_mockProtoEncoder);
|
||||
|
||||
//Verify
|
||||
assertEquals((byte)0x82, (byte)m_mockProtoEncoder.getBuffer().get()); //1 byte
|
||||
assertEquals(16, m_mockProtoEncoder.getBuffer().get()); //remaining length
|
||||
|
||||
//verify M1ssageID
|
||||
assertEquals((byte)0xAA, m_mockProtoEncoder.getBuffer().get());
|
||||
assertEquals((byte)0xBB, m_mockProtoEncoder.getBuffer().get());
|
||||
|
||||
//Variable part
|
||||
verifyString(c1.getTopic(), m_mockProtoEncoder.getBuffer());
|
||||
assertEquals(c1.getQos(), m_mockProtoEncoder.getBuffer().get());
|
||||
verifyString(c2.getTopic(), m_mockProtoEncoder.getBuffer());
|
||||
assertEquals(c2.getQos(), m_mockProtoEncoder.getBuffer().get());
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testEncode_empty_subscription() throws Exception {
|
||||
SubscribeMessage msg = new SubscribeMessage();
|
||||
msg.setQos(QOSType.LEAST_ONE);
|
||||
msg.setMessageID(0xAABB);
|
||||
|
||||
//Exercise
|
||||
m_encoder.encode(null, msg, m_mockProtoEncoder);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testEncode_badQos() throws Exception {
|
||||
SubscribeMessage msg = new SubscribeMessage();
|
||||
msg.setQos(QOSType.EXACTLY_ONCE);
|
||||
msg.setMessageID(0xAABB);
|
||||
|
||||
//Exercise
|
||||
m_encoder.encode(null, msg, m_mockProtoEncoder);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
package org.dna.mqtt.moquette.proto;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import org.apache.mina.core.buffer.IoBuffer;
|
||||
import org.apache.mina.core.filterchain.IoFilter.NextFilter;
|
||||
import org.apache.mina.core.future.WriteFuture;
|
||||
import org.apache.mina.core.session.IoSession;
|
||||
import org.apache.mina.filter.codec.ProtocolDecoderOutput;
|
||||
import org.apache.mina.filter.codec.ProtocolEncoderOutput;
|
||||
import org.dna.mqtt.moquette.proto.messages.AbstractMessage;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author andrea
|
||||
*/
|
||||
public class TestUtils {
|
||||
|
||||
static final class MockProtocolDecoderOutput<T extends AbstractMessage> implements ProtocolDecoderOutput {
|
||||
|
||||
protected T m_connMessage;
|
||||
|
||||
public T getMessage() {
|
||||
return m_connMessage;
|
||||
}
|
||||
|
||||
public void write(Object message) {
|
||||
m_connMessage = (T) message;
|
||||
}
|
||||
|
||||
public void flush(NextFilter nextFilter, IoSession session) {
|
||||
throw new UnsupportedOperationException("Not supported yet.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static final class MockProtocolEncoderOutput implements ProtocolEncoderOutput {
|
||||
|
||||
protected IoBuffer m_buffer;
|
||||
|
||||
public IoBuffer getBuffer() {
|
||||
return m_buffer;
|
||||
}
|
||||
|
||||
public void write(Object buffer) {
|
||||
m_buffer = (IoBuffer) buffer;
|
||||
}
|
||||
|
||||
public void mergeAll() {
|
||||
throw new UnsupportedOperationException("Not supported yet.");
|
||||
}
|
||||
|
||||
public WriteFuture flush() {
|
||||
throw new UnsupportedOperationException("Not supported yet.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify the presence of the given string starting from the current position
|
||||
* inside the buffer.
|
||||
*/
|
||||
static void verifyString(String str, IoBuffer buff) throws UnsupportedEncodingException {
|
||||
IoBuffer tmpBuff = IoBuffer.allocate(2).setAutoExpand(true);
|
||||
byte[] raw = str.getBytes("UTF-8");
|
||||
Utils.writeWord(tmpBuff, raw.length);
|
||||
tmpBuff.put(raw).flip();
|
||||
|
||||
verifyIoBuffer(tmpBuff, buff);
|
||||
}
|
||||
|
||||
static void verifyIoBuffer(IoBuffer expected, IoBuffer found) {
|
||||
while(expected.hasRemaining()) {
|
||||
assertEquals(expected.get(), found.get());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that the given bytes buffer of the given numBytes length is present
|
||||
* in the buff starting from the current position.
|
||||
*/
|
||||
static void verifyBuff(int numBytes, byte[] bytes, IoBuffer buff) {
|
||||
assertTrue(numBytes <= buff.remaining());
|
||||
byte[] toCheck = new byte[numBytes];
|
||||
buff.get(toCheck);
|
||||
|
||||
for (int i = 0; i < numBytes; i++) {
|
||||
assertEquals(bytes[i], toCheck[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void verifyEquals(byte[] expected, byte[] found) {
|
||||
assertEquals(expected.length, found.length);
|
||||
for (int i = 0; i < expected.length; i++) {
|
||||
assertEquals(expected[i], found[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static IoBuffer generateRandomPayload(int size) {
|
||||
IoBuffer payloadBuffer = IoBuffer.allocate(size);
|
||||
for (int i = 0; i < size; i++) {
|
||||
payloadBuffer.put((byte) (Math.random() * 255));
|
||||
}
|
||||
payloadBuffer.flip();
|
||||
return payloadBuffer;
|
||||
}
|
||||
|
||||
public static IoBuffer loadFile(String path) throws FileNotFoundException, IOException {
|
||||
File f = new File(path);
|
||||
long size = f.length();
|
||||
byte[] payload = new byte[(int) size];
|
||||
|
||||
FileInputStream fr = new FileInputStream(f);
|
||||
fr.read(payload);
|
||||
|
||||
IoBuffer payloadBuffer = IoBuffer.allocate((int) size);
|
||||
payloadBuffer.put(payload).flip();
|
||||
return payloadBuffer;
|
||||
}
|
||||
}
|
||||
@@ -1,119 +0,0 @@
|
||||
package org.dna.mqtt.moquette.proto;
|
||||
|
||||
import org.apache.mina.core.buffer.IoBuffer;
|
||||
import org.apache.mina.filter.codec.demux.MessageDecoderResult;
|
||||
import org.dna.mqtt.moquette.proto.TestUtils.MockProtocolDecoderOutput;
|
||||
import org.dna.mqtt.moquette.proto.messages.AbstractMessage;
|
||||
import org.dna.mqtt.moquette.proto.messages.UnsubscribeMessage;
|
||||
import static org.junit.Assert.*;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author andrea
|
||||
*/
|
||||
public class UnsubscribeDecoderTest {
|
||||
|
||||
IoBuffer m_buff;
|
||||
UnsubscribeDecoder m_msgdec;
|
||||
MockProtocolDecoderOutput<UnsubscribeMessage> m_mockProtoDecoder;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
m_buff = IoBuffer.allocate(4);
|
||||
m_msgdec = new UnsubscribeDecoder();
|
||||
m_mockProtoDecoder = new MockProtocolDecoderOutput<UnsubscribeMessage>();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBadQos() throws Exception {
|
||||
m_buff = IoBuffer.allocate(2);
|
||||
initHeaderBadQos(m_buff);
|
||||
m_buff.flip();
|
||||
|
||||
//Excercise
|
||||
MessageDecoderResult res = m_msgdec.decode(null, m_buff, m_mockProtoDecoder);
|
||||
|
||||
//Verify
|
||||
assertEquals(MessageDecoderResult.NOT_OK, res);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultiTopic() throws Exception {
|
||||
m_buff = IoBuffer.allocate(4).setAutoExpand(true);
|
||||
String topic1 = "a/b";
|
||||
String topic2 = "c/d/e";
|
||||
initMultiTopic(m_buff, 123, topic1, topic2);
|
||||
m_buff.flip();
|
||||
|
||||
//Excercise
|
||||
MessageDecoderResult res = m_msgdec.decode(null, m_buff, m_mockProtoDecoder);
|
||||
|
||||
//Verify
|
||||
assertEquals(MessageDecoderResult.OK, res);
|
||||
assertEquals(2, m_mockProtoDecoder.getMessage().topics().size());
|
||||
assertEquals(topic1, m_mockProtoDecoder.getMessage().topics().get(0));
|
||||
assertEquals(topic2, m_mockProtoDecoder.getMessage().topics().get(1));
|
||||
assertEquals(AbstractMessage.UNSUBSCRIBE, m_mockProtoDecoder.getMessage().getMessageType());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testDecodeSingleTopic_bug() throws Exception {
|
||||
//A2 0C 00 01 00 06 2F 74 6F 70 69 63 //12 byte
|
||||
byte[] overallMessage = new byte[] {(byte)0xA2, 0x0A, //fixed header
|
||||
0x00, 0x01, //MSG ID
|
||||
0x00, 0x06, 0x2F, 0x74, 0x6F, 0x70, 0x69, 0x63}; //"/topic" string
|
||||
m_buff = IoBuffer.allocate(overallMessage.length).setAutoExpand(true);
|
||||
m_buff.put(overallMessage);
|
||||
m_buff.flip();
|
||||
|
||||
MessageDecoderResult res = m_msgdec.decode(null, m_buff, m_mockProtoDecoder);
|
||||
|
||||
assertNotNull(m_mockProtoDecoder.getMessage());
|
||||
UnsubscribeMessage message = (UnsubscribeMessage) m_mockProtoDecoder.getMessage();
|
||||
// assertEquals(0x0A, message.getMessageID());
|
||||
// assertEquals(1, message.types().size());
|
||||
// assertEquals(AbstractMessage.QOSType.LEAST_ONE, message.types().get(0));
|
||||
|
||||
}
|
||||
|
||||
// @Test
|
||||
// public void testDecodeSingleTopic_BadFormat() throws Exception {
|
||||
// //A2 0C 00 01 00 06 2F 74 6F 70 69 63 //10 byte of content, but lenght says 12!!
|
||||
// byte[] overallMessage = new byte[] {(byte)0xA2, 0x0C, //fixed header
|
||||
// 0x00, 0x01, //MSG ID
|
||||
// 0x00, 0x06, 0x2F, 0x74, 0x6F, 0x70, 0x69, 0x63}; //"/topic" string
|
||||
// m_buff = IoBuffer.allocate(overallMessage.length).setAutoExpand(true);
|
||||
// m_buff.put(overallMessage);
|
||||
// m_buff.flip();
|
||||
//
|
||||
// MessageDecoderResult res = m_msgdec.decode(null, m_buff, m_mockProtoDecoder);
|
||||
//
|
||||
// assertNotNull(m_mockProtoDecoder.getMessage());
|
||||
// UnsubscribeMessage message = (UnsubscribeMessage) m_mockProtoDecoder.getMessage();
|
||||
//// assertEquals(0x0A, message.getMessageID());
|
||||
//// assertEquals(1, message.types().size());
|
||||
//// assertEquals(AbstractMessage.QOSType.LEAST_ONE, message.types().get(0));
|
||||
//
|
||||
// }
|
||||
|
||||
private void initHeaderBadQos(IoBuffer buff) {
|
||||
buff.clear().put((byte)(AbstractMessage.UNSUBSCRIBE << 4)).put((byte)0);
|
||||
}
|
||||
|
||||
private void initMultiTopic(IoBuffer buff, int messageID, String... topics) throws IllegalAccessException {
|
||||
IoBuffer topicBuffer = IoBuffer.allocate(4).setAutoExpand(true);
|
||||
Utils.writeWord(topicBuffer, messageID);
|
||||
for (String topic : topics) {
|
||||
topicBuffer.put(Utils.encodeString(topic));
|
||||
}
|
||||
topicBuffer.flip();
|
||||
|
||||
buff.clear().put((byte)(AbstractMessage.UNSUBSCRIBE << 4 | (byte)0x02))
|
||||
.put(Utils.encodeRemainingLength(topicBuffer.remaining()));
|
||||
buff.put(topicBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,94 +0,0 @@
|
||||
package org.dna.mqtt.moquette.proto;
|
||||
|
||||
import org.dna.mqtt.moquette.proto.messages.UnsubscribeMessage;
|
||||
import org.dna.mqtt.moquette.proto.messages.AbstractMessage.QOSType;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.dna.mqtt.moquette.proto.TestUtils.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author andrea
|
||||
*/
|
||||
public class UnsubscribeEncoderTest {
|
||||
UnsubscribeMessage m_msg;
|
||||
UnsubscribeEncoder m_encoder;
|
||||
TestUtils.MockProtocolEncoderOutput m_mockProtoEncoder;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
m_encoder = new UnsubscribeEncoder();
|
||||
m_mockProtoEncoder = new TestUtils.MockProtocolEncoderOutput();
|
||||
m_msg = new UnsubscribeMessage();
|
||||
m_msg.setMessageID(0xAABB);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncodeWithSingleTopic() throws Exception {
|
||||
m_msg.setQos(QOSType.LEAST_ONE);
|
||||
|
||||
//variable part
|
||||
String topic1 = "/topic";
|
||||
m_msg.addTopic(topic1);
|
||||
|
||||
//Exercise
|
||||
m_encoder.encode(null, m_msg, m_mockProtoEncoder);
|
||||
|
||||
//Verify
|
||||
assertEquals((byte)0xA2, (byte)m_mockProtoEncoder.getBuffer().get()); //1 byte
|
||||
//2 messageID + 2 length + 6 chars = 10
|
||||
assertEquals(10, m_mockProtoEncoder.getBuffer().get()); //remaining length
|
||||
|
||||
//verify M1ssageID
|
||||
assertEquals((byte)0xAA, m_mockProtoEncoder.getBuffer().get());
|
||||
assertEquals((byte)0xBB, m_mockProtoEncoder.getBuffer().get());
|
||||
|
||||
//Variable part
|
||||
verifyString(topic1, m_mockProtoEncoder.getBuffer());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testEncodeWithMultiTopic() throws Exception {
|
||||
m_msg.setQos(QOSType.LEAST_ONE);
|
||||
|
||||
//variable part
|
||||
String topic1 = "a/b";
|
||||
String topic2 = "a/b/c";
|
||||
m_msg.addTopic(topic1);
|
||||
m_msg.addTopic(topic2);
|
||||
|
||||
//Exercise
|
||||
m_encoder.encode(null, m_msg, m_mockProtoEncoder);
|
||||
|
||||
//Verify
|
||||
assertEquals((byte)0xA2, (byte)m_mockProtoEncoder.getBuffer().get()); //1 byte
|
||||
assertEquals(14, m_mockProtoEncoder.getBuffer().get()); //remaining length
|
||||
|
||||
//verify M1ssageID
|
||||
assertEquals((byte)0xAA, m_mockProtoEncoder.getBuffer().get());
|
||||
assertEquals((byte)0xBB, m_mockProtoEncoder.getBuffer().get());
|
||||
|
||||
//Variable part
|
||||
verifyString(topic1, m_mockProtoEncoder.getBuffer());
|
||||
verifyString(topic2, m_mockProtoEncoder.getBuffer());
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testEncode_empty_topics() throws Exception {
|
||||
m_msg.setQos(QOSType.LEAST_ONE);
|
||||
|
||||
//Exercise
|
||||
m_encoder.encode(null, m_msg, m_mockProtoEncoder);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testEncode_badQos() throws Exception {
|
||||
m_msg.setQos(QOSType.EXACTLY_ONCE);
|
||||
|
||||
//Exercise
|
||||
m_encoder.encode(null, m_msg, m_mockProtoEncoder);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
package org.dna.mqtt.moquette.proto;
|
||||
|
||||
import org.dna.mqtt.moquette.proto.messages.AbstractMessage;
|
||||
import org.dna.mqtt.moquette.proto.messages.UnsubscribeMessage;
|
||||
import static org.junit.Assert.*;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author andrea
|
||||
*/
|
||||
public class UtilsTests {
|
||||
|
||||
@Test
|
||||
public void testEncodeFlags() {
|
||||
UnsubscribeMessage msg = new UnsubscribeMessage();
|
||||
msg.setRetainFlag(true);
|
||||
msg.setQos(AbstractMessage.QOSType.MOST_ONE);
|
||||
|
||||
//Exercise
|
||||
assertEquals(1, Utils.encodeFlags(msg));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
# Set root logger level to DEBUG and its only appender to A1.
|
||||
#log4j.rootLogger=ERROR, A1
|
||||
log4j.rootLogger=INFO, A1
|
||||
#log4j.rootLogger=WARN, A1
|
||||
#log4j.logger.org.dna.mqtt.moquette.client=WARN
|
||||
#log4j.logger.org.mina=WARN
|
||||
#log4j.logger.org.dna.mqtt.moquette.server=DEBUG
|
||||
#log4j.logger.org.dna.mqtt.moquette.messaging=DEBUG
|
||||
#log4j.logger.org.dna.mqtt.moquette.proto.MQTTLoggingFilter=DEBUG
|
||||
#log4j.logger.org.dna.mqtt.moquette.proto.PublishDecoder=DEBUG
|
||||
|
||||
log4j.logger.org.dna.mqtt.bechnmark=INFO
|
||||
|
||||
# A1 is set to be a ConsoleAppender.
|
||||
log4j.appender.A1=org.apache.log4j.ConsoleAppender
|
||||
|
||||
# A1 uses PatternLayout.
|
||||
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.A1.layout.ConversionPattern=%-4r [%t] %-5p %c{1} %x - %m%n
|
||||
Reference in New Issue
Block a user