From 23b4958592ae5337d11671aeda8255b7bd913baa Mon Sep 17 00:00:00 2001 From: andrea Date: Mon, 9 Sep 2013 11:04:43 +0200 Subject: [PATCH 01/41] Renamed parser to mina-parser --- benchmarking/pom.xml | 2 +- broker/pom.xml | 2 +- parser/pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/benchmarking/pom.xml b/benchmarking/pom.xml index d3947ca0..cddbcdea 100644 --- a/benchmarking/pom.xml +++ b/benchmarking/pom.xml @@ -40,7 +40,7 @@ org.dna.mqtt - moquette-parser + moquette-mina-parser 0.1 diff --git a/broker/pom.xml b/broker/pom.xml index 0b5cdc10..7805d770 100644 --- a/broker/pom.xml +++ b/broker/pom.xml @@ -21,7 +21,7 @@ org.dna.mqtt - moquette-parser + moquette-mina-parser 0.2 diff --git a/parser/pom.xml b/parser/pom.xml index 7c04c1a5..8401f5f5 100644 --- a/parser/pom.xml +++ b/parser/pom.xml @@ -9,7 +9,7 @@ 0.2 - moquette-parser + moquette-mina-parser jar 0.2 Moquette - Parser From d9131b4b079d35f422a86f5db949097a16dddeee Mon Sep 17 00:00:00 2001 From: andrea Date: Mon, 9 Sep 2013 11:59:59 +0200 Subject: [PATCH 02/41] Added skeleton for Netty parser --- benchmarking/pom.xml | 2 +- netty_parser/pom.xml | 125 +++++++++++++++++++++++++++++++++++++++++++ parser/pom.xml | 2 +- pom.xml | 1 + 4 files changed, 128 insertions(+), 2 deletions(-) create mode 100644 netty_parser/pom.xml diff --git a/benchmarking/pom.xml b/benchmarking/pom.xml index cddbcdea..dcaeacf3 100644 --- a/benchmarking/pom.xml +++ b/benchmarking/pom.xml @@ -41,7 +41,7 @@ org.dna.mqtt moquette-mina-parser - 0.1 + 0.2 diff --git a/netty_parser/pom.xml b/netty_parser/pom.xml new file mode 100644 index 00000000..03628397 --- /dev/null +++ b/netty_parser/pom.xml @@ -0,0 +1,125 @@ + + + 4.0.0 + + ../pom.xml + moquette-parent + org.dna.mqtt + 0.2 + + + moquette-netty-parser + jar + 0.2 + Moquette - Netty Parser + + + UTF-8 + + + + + io.netty + netty-all + 4.0.8.Final + + + + + + + + + + org.slf4j + slf4j-log4j12 + 1.6.4 + + + + + + junit + junit + 4.8.2 + test + + + + + org.mockito + mockito-core + 1.9.0 + test + + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.5 + 1.5 + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + org/dna/mqtt/moquette/proto/TestUtils.java + org/dna/mqtt/moquette/proto/TestUtils$* + + + + + + + + + + + + diff --git a/parser/pom.xml b/parser/pom.xml index 8401f5f5..999fc9f1 100644 --- a/parser/pom.xml +++ b/parser/pom.xml @@ -12,7 +12,7 @@ moquette-mina-parser jar 0.2 - Moquette - Parser + Moquette - MINA Parser UTF-8 diff --git a/pom.xml b/pom.xml index 8d25bbba..798ec1bb 100644 --- a/pom.xml +++ b/pom.xml @@ -17,6 +17,7 @@ parser + netty_parser broker benchmarking From 2886aa0babf703ab5fc7bd0ddc000ac796cd2648 Mon Sep 17 00:00:00 2001 From: andrea Date: Tue, 10 Sep 2013 15:00:18 +0200 Subject: [PATCH 03/41] Extracted common pojo messages definitions --- .gitignore | 1 + parser/pom.xml | 6 ++ parser_commons/pom.xml | 86 +++++++++++++++++++ .../proto/messages/AbstractMessage.java | 0 .../proto/messages/ConnAckMessage.java | 0 .../proto/messages/ConnectMessage.java | 0 .../proto/messages/DisconnectMessage.java | 0 .../proto/messages/MessageIDMessage.java | 0 .../proto/messages/PingReqMessage.java | 0 .../proto/messages/PingRespMessage.java | 0 .../proto/messages/PubAckMessage.java | 0 .../proto/messages/PubRecMessage.java | 0 .../proto/messages/PubRelMessage.java | 0 .../proto/messages/PublishMessage.java | 0 .../proto/messages/SubAckMessage.java | 0 .../proto/messages/SubscribeMessage.java | 0 .../proto/messages/UnsubAckMessage.java | 0 .../proto/messages/UnsubscribeMessage.java | 0 .../proto/messages/ZeroLengthMessage.java | 0 pom.xml | 2 + 20 files changed, 95 insertions(+) create mode 100644 parser_commons/pom.xml rename {parser => parser_commons}/src/main/java/org/dna/mqtt/moquette/proto/messages/AbstractMessage.java (100%) rename {parser => parser_commons}/src/main/java/org/dna/mqtt/moquette/proto/messages/ConnAckMessage.java (100%) rename {parser => parser_commons}/src/main/java/org/dna/mqtt/moquette/proto/messages/ConnectMessage.java (100%) rename {parser => parser_commons}/src/main/java/org/dna/mqtt/moquette/proto/messages/DisconnectMessage.java (100%) rename {parser => parser_commons}/src/main/java/org/dna/mqtt/moquette/proto/messages/MessageIDMessage.java (100%) rename {parser => parser_commons}/src/main/java/org/dna/mqtt/moquette/proto/messages/PingReqMessage.java (100%) rename {parser => parser_commons}/src/main/java/org/dna/mqtt/moquette/proto/messages/PingRespMessage.java (100%) rename {parser => parser_commons}/src/main/java/org/dna/mqtt/moquette/proto/messages/PubAckMessage.java (100%) rename {parser => parser_commons}/src/main/java/org/dna/mqtt/moquette/proto/messages/PubRecMessage.java (100%) rename {parser => parser_commons}/src/main/java/org/dna/mqtt/moquette/proto/messages/PubRelMessage.java (100%) rename {parser => parser_commons}/src/main/java/org/dna/mqtt/moquette/proto/messages/PublishMessage.java (100%) rename {parser => parser_commons}/src/main/java/org/dna/mqtt/moquette/proto/messages/SubAckMessage.java (100%) rename {parser => parser_commons}/src/main/java/org/dna/mqtt/moquette/proto/messages/SubscribeMessage.java (100%) rename {parser => parser_commons}/src/main/java/org/dna/mqtt/moquette/proto/messages/UnsubAckMessage.java (100%) rename {parser => parser_commons}/src/main/java/org/dna/mqtt/moquette/proto/messages/UnsubscribeMessage.java (100%) rename {parser => parser_commons}/src/main/java/org/dna/mqtt/moquette/proto/messages/ZeroLengthMessage.java (100%) diff --git a/.gitignore b/.gitignore index b00a78f7..2bbda7ed 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ *.iml broker/target/ client/target/ +/parser_commons/target/ \ No newline at end of file diff --git a/parser/pom.xml b/parser/pom.xml index 999fc9f1..7609c266 100644 --- a/parser/pom.xml +++ b/parser/pom.xml @@ -25,6 +25,12 @@ 2.0.4 + + org.dna.mqtt + moquette-parser-commons + 0.2 + + + + org.mockito + mockito-core + 1.9.0 + test + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.5 + 1.5 + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + org/dna/mqtt/moquette/proto/TestUtils.java + org/dna/mqtt/moquette/proto/TestUtils$* + + + + + + + + + + + + diff --git a/parser/src/main/java/org/dna/mqtt/moquette/proto/messages/AbstractMessage.java b/parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/AbstractMessage.java similarity index 100% rename from parser/src/main/java/org/dna/mqtt/moquette/proto/messages/AbstractMessage.java rename to parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/AbstractMessage.java diff --git a/parser/src/main/java/org/dna/mqtt/moquette/proto/messages/ConnAckMessage.java b/parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/ConnAckMessage.java similarity index 100% rename from parser/src/main/java/org/dna/mqtt/moquette/proto/messages/ConnAckMessage.java rename to parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/ConnAckMessage.java diff --git a/parser/src/main/java/org/dna/mqtt/moquette/proto/messages/ConnectMessage.java b/parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/ConnectMessage.java similarity index 100% rename from parser/src/main/java/org/dna/mqtt/moquette/proto/messages/ConnectMessage.java rename to parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/ConnectMessage.java diff --git a/parser/src/main/java/org/dna/mqtt/moquette/proto/messages/DisconnectMessage.java b/parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/DisconnectMessage.java similarity index 100% rename from parser/src/main/java/org/dna/mqtt/moquette/proto/messages/DisconnectMessage.java rename to parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/DisconnectMessage.java diff --git a/parser/src/main/java/org/dna/mqtt/moquette/proto/messages/MessageIDMessage.java b/parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/MessageIDMessage.java similarity index 100% rename from parser/src/main/java/org/dna/mqtt/moquette/proto/messages/MessageIDMessage.java rename to parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/MessageIDMessage.java diff --git a/parser/src/main/java/org/dna/mqtt/moquette/proto/messages/PingReqMessage.java b/parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/PingReqMessage.java similarity index 100% rename from parser/src/main/java/org/dna/mqtt/moquette/proto/messages/PingReqMessage.java rename to parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/PingReqMessage.java diff --git a/parser/src/main/java/org/dna/mqtt/moquette/proto/messages/PingRespMessage.java b/parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/PingRespMessage.java similarity index 100% rename from parser/src/main/java/org/dna/mqtt/moquette/proto/messages/PingRespMessage.java rename to parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/PingRespMessage.java diff --git a/parser/src/main/java/org/dna/mqtt/moquette/proto/messages/PubAckMessage.java b/parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/PubAckMessage.java similarity index 100% rename from parser/src/main/java/org/dna/mqtt/moquette/proto/messages/PubAckMessage.java rename to parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/PubAckMessage.java diff --git a/parser/src/main/java/org/dna/mqtt/moquette/proto/messages/PubRecMessage.java b/parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/PubRecMessage.java similarity index 100% rename from parser/src/main/java/org/dna/mqtt/moquette/proto/messages/PubRecMessage.java rename to parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/PubRecMessage.java diff --git a/parser/src/main/java/org/dna/mqtt/moquette/proto/messages/PubRelMessage.java b/parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/PubRelMessage.java similarity index 100% rename from parser/src/main/java/org/dna/mqtt/moquette/proto/messages/PubRelMessage.java rename to parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/PubRelMessage.java diff --git a/parser/src/main/java/org/dna/mqtt/moquette/proto/messages/PublishMessage.java b/parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/PublishMessage.java similarity index 100% rename from parser/src/main/java/org/dna/mqtt/moquette/proto/messages/PublishMessage.java rename to parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/PublishMessage.java diff --git a/parser/src/main/java/org/dna/mqtt/moquette/proto/messages/SubAckMessage.java b/parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/SubAckMessage.java similarity index 100% rename from parser/src/main/java/org/dna/mqtt/moquette/proto/messages/SubAckMessage.java rename to parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/SubAckMessage.java diff --git a/parser/src/main/java/org/dna/mqtt/moquette/proto/messages/SubscribeMessage.java b/parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/SubscribeMessage.java similarity index 100% rename from parser/src/main/java/org/dna/mqtt/moquette/proto/messages/SubscribeMessage.java rename to parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/SubscribeMessage.java diff --git a/parser/src/main/java/org/dna/mqtt/moquette/proto/messages/UnsubAckMessage.java b/parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/UnsubAckMessage.java similarity index 100% rename from parser/src/main/java/org/dna/mqtt/moquette/proto/messages/UnsubAckMessage.java rename to parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/UnsubAckMessage.java diff --git a/parser/src/main/java/org/dna/mqtt/moquette/proto/messages/UnsubscribeMessage.java b/parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/UnsubscribeMessage.java similarity index 100% rename from parser/src/main/java/org/dna/mqtt/moquette/proto/messages/UnsubscribeMessage.java rename to parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/UnsubscribeMessage.java diff --git a/parser/src/main/java/org/dna/mqtt/moquette/proto/messages/ZeroLengthMessage.java b/parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/ZeroLengthMessage.java similarity index 100% rename from parser/src/main/java/org/dna/mqtt/moquette/proto/messages/ZeroLengthMessage.java rename to parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/ZeroLengthMessage.java diff --git a/pom.xml b/pom.xml index 798ec1bb..a9a4e809 100644 --- a/pom.xml +++ b/pom.xml @@ -16,10 +16,12 @@ + parser_commons parser netty_parser broker benchmarking + parser_commons From 2ee84797ba8ca6b80de56af4a88aa0e09e139aa0 Mon Sep 17 00:00:00 2001 From: andrea Date: Wed, 11 Sep 2013 11:06:53 +0200 Subject: [PATCH 04/41] Introduced raw decoder for CONNECT messages --- netty_parser/pom.xml | 6 + .../moquette/parser/netty/MQTTDecoder.java | 183 ++++++++++++++++++ .../dna/mqtt/moquette/parser/netty/Utils.java | 105 ++++++++++ .../parser/netty/MQTTDecoderTest.java | 80 ++++++++ .../mqtt/moquette/parser/netty/UtilsTest.java | 50 +++++ 5 files changed, 424 insertions(+) create mode 100644 netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTDecoder.java create mode 100644 netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/Utils.java create mode 100644 netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/MQTTDecoderTest.java create mode 100644 netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/UtilsTest.java diff --git a/netty_parser/pom.xml b/netty_parser/pom.xml index 03628397..a33bafe4 100644 --- a/netty_parser/pom.xml +++ b/netty_parser/pom.xml @@ -19,6 +19,12 @@ + + org.dna.mqtt + moquette-parser-commons + 0.2 + + io.netty netty-all diff --git a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTDecoder.java b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTDecoder.java new file mode 100644 index 00000000..708afa66 --- /dev/null +++ b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTDecoder.java @@ -0,0 +1,183 @@ +package org.dna.mqtt.moquette.parser.netty; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.ByteToMessageDecoder; +import io.netty.handler.codec.CorruptedFrameException; +import java.util.List; +import org.dna.mqtt.moquette.proto.messages.AbstractMessage; +import org.dna.mqtt.moquette.proto.messages.ConnectMessage; + +/** + * + * @author andrea + */ +public class MQTTDecoder extends ByteToMessageDecoder { + + @Override + protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception { + in.markReaderIndex(); + if (!Utils.checkHeaderAvailability(in)) { + in.resetReaderIndex(); + return; + } + in.resetReaderIndex(); + + byte messageType = Utils.readMessageType(in); + + if (messageType == AbstractMessage.CONNECT) { + in.resetReaderIndex(); + //Common decoding part + ConnectMessage message = new ConnectMessage(); + if (!decodeCommonHeader(message, in)) { + in.resetReaderIndex(); + return; + } + int remainingLength = message.getRemainingLength(); + int start = in.readerIndex(); + + //Connect specific decoding part + //ProtocolName 8 bytes + if (in.readableBytes() < 12) { + in.resetReaderIndex(); + return; + } + byte[] encProtoName = new byte[6]; + in.skipBytes(2); //size, is 0x06 + in.readBytes(encProtoName); + String protoName = new String(encProtoName, "UTF-8"); + if (!"MQIsdp".equals(protoName)) { + in.resetReaderIndex(); + throw new CorruptedFrameException("Invalid protoName: " + protoName); + } + message.setProtocolName(protoName); + + //ProtocolVersion 1 byte (value 0x03) + message.setProcotolVersion(in.readByte()); + + //Connection flag + byte connFlags = in.readByte(); + 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) { + in.resetReaderIndex(); + throw new CorruptedFrameException("Expected will QoS in range 0..2 but found: " + willQos); + } + 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) { + in.resetReaderIndex(); + throw new CorruptedFrameException("Expected password flag to true if the user flag is true but was: " + passwordFlag); + } + 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); + int keepAlive = in.readShort(); + message.setKeepAlive(keepAlive); + + if (remainingLength == 12) { + out.add(message); + return; + } + + //Decode the ClientID + String clientID = Utils.decodeString(in); + if (clientID == null) { + in.resetReaderIndex(); + return; + } + message.setClientID(clientID); + + //Decode willTopic + if (willFlag) { + String willTopic = Utils.decodeString(in); + if (willTopic == null) { + in.resetReaderIndex(); + return; + } + message.setWillTopic(willTopic); + } + + //Decode willMessage + if (willFlag) { + String willMessage = Utils.decodeString(in); + if (willMessage == null) { + in.resetReaderIndex(); + return; + } + message.setWillMessage(willMessage); + } + + //Compatibility check wieth v3.0, remaining length has precedence over + //the user and password flags + int readed = in.readerIndex() - start; + if (readed == remainingLength) { + out.add(message); + return; + } + + //Decode username + if (userFlag) { + String userName = Utils.decodeString(in); + if (userName == null) { + in.resetReaderIndex(); + return; + } + message.setUsername(userName); + } + + readed = in.readerIndex() - start; + if (readed == remainingLength) { + out.add(message); + return; + } + + //Decode password + if (passwordFlag) { + String password = Utils.decodeString(in); + if (password == null) { + in.resetReaderIndex(); + return; + } + message.setPassword(password); + } + + out.add(message); + return; + } + } + + + protected boolean decodeCommonHeader(AbstractMessage message, ByteBuf in) { + //Common decoding part + if (in.readableBytes() < 2) { + return false; + } + byte h1 = in.readByte(); + 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 false; + } + + message.setMessageType(messageType); + message.setDupFlag(dupFlag); + message.setQos(AbstractMessage.QOSType.values()[qosLevel]); + message.setRetainFlag(retainFlag); + message.setRemainingLength(remainingLength); + return true; + } + +} diff --git a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/Utils.java b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/Utils.java new file mode 100644 index 00000000..04267ff2 --- /dev/null +++ b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/Utils.java @@ -0,0 +1,105 @@ +package org.dna.mqtt.moquette.parser.netty; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import java.io.UnsupportedEncodingException; +import org.slf4j.LoggerFactory; + +/** + * + * @author andrea + */ +public class Utils { + + static byte readMessageType(ByteBuf in) { + byte h1 = in.readByte(); + byte messageType = (byte) ((h1 & 0x00F0) >> 4); + return messageType; + } + + static boolean checkHeaderAvailability(ByteBuf in) { + if (in.readableBytes() < 1) { + return false; + } + //byte h1 = in.get(); + //byte messageType = (byte) ((h1 & 0x00F0) >> 4); + in.skipBytes(1); //skip the messageType byte + + int remainingLength = Utils.decodeRemainingLenght(in); + if (remainingLength == -1) { + return false; + } + + //check remaining length + if (in.readableBytes() < remainingLength) { + return false; + } + + //return messageType == type ? MessageDecoderResult.OK : MessageDecoderResult.NOT_OK; + return true; + } + + /** + * Decode the variable remaining length as defined in MQTT v3.1 specification + * (section 2.1). + * + * @return the decoded length or -1 if needed more data to decode the length field. + */ + static int decodeRemainingLenght(ByteBuf in) { + int multiplier = 1; + int value = 0; + byte digit; + do { + if (in.readableBytes() < 1) { + return -1; + } + digit = in.readByte(); + value += (digit & 0x7F) * multiplier; + multiplier *= 128; + } while ((digit & 0x80) != 0); + return value; + } + + /** + * Load a string from the given buffer, reading first the two bytes of len + * and then the UTF-8 bytes of the string. + * + * @return the decoded string or null if NEED_DATA + */ + static String decodeString(ByteBuf in) throws UnsupportedEncodingException { + if (in.readableBytes() < 2) { + return null; + } + //int strLen = Utils.readWord(in); + int strLen = in.readShort(); + if (in.readableBytes() < strLen) { + return null; + } + byte[] strRaw = new byte[strLen]; + in.readBytes(strRaw); + + return new String(strRaw, "UTF-8"); + } + + + /** + * Return the IoBuffer with string encoded as MSB, LSB and UTF-8 encoded + * string content. + */ + static ByteBuf encodeString(String str) { + ByteBuf out = Unpooled.buffer(2); + byte[] raw; + try { + raw = str.getBytes("UTF-8"); + //NB every Java platform has got UTF-8 encoding by default, so this + //exception are never raised. + } catch (UnsupportedEncodingException ex) { + LoggerFactory.getLogger(Utils.class).error(null, ex); + return null; + } + //Utils.writeWord(out, raw.length); + out.writeShort(raw.length); + out.writeBytes(raw); + return out; + } +} diff --git a/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/MQTTDecoderTest.java b/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/MQTTDecoderTest.java new file mode 100644 index 00000000..18e57be7 --- /dev/null +++ b/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/MQTTDecoderTest.java @@ -0,0 +1,80 @@ +package org.dna.mqtt.moquette.parser.netty; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import java.io.UnsupportedEncodingException; +import java.util.ArrayList; +import java.util.List; +import org.dna.mqtt.moquette.proto.messages.AbstractMessage; +import org.dna.mqtt.moquette.proto.messages.ConnectMessage; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import org.junit.Before; +import org.junit.Test; + + +public class MQTTDecoderTest { + + ByteBuf m_buff; + MQTTDecoder m_msgdec; + + @Before + public void setUp() { + m_msgdec = new MQTTDecoder(); + } + + @Test + public void testBaseHeader() throws Exception { + m_buff = Unpooled.buffer(14); + initBaseHeader(m_buff); + //m_buff.flip(); + List results = new ArrayList(); + + //Excercise + m_msgdec.decode(null, m_buff, results); + + //Verify + assertFalse(results.isEmpty()); + verifyBaseHeader((ConnectMessage)results.get(0)); + } + + + private void initBaseHeader(ByteBuf buff) throws UnsupportedEncodingException { + initHeader(buff, (byte)0x0C); + } + + private void initHeader(ByteBuf buff, byte remaingLength) throws UnsupportedEncodingException { + buff.clear().writeByte((byte)(AbstractMessage.CONNECT << 4)).writeByte(remaingLength); + //Proto name + encodeString(buff, "MQIsdp"); + //version + buff.writeByte(3); + //conn flags + buff.writeByte(0xCE); + //keepAlive + buff.writeBytes(new byte[]{(byte)0, (byte) 0x0A}); + } + + /** + * Encode and insert the given string into the given buff + */ + private ByteBuf encodeString(ByteBuf buff, String str) throws UnsupportedEncodingException { + buff.writeBytes(Utils.encodeString(str)); + return buff; + } + + 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()); + } +} \ No newline at end of file diff --git a/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/UtilsTest.java b/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/UtilsTest.java new file mode 100644 index 00000000..623f5171 --- /dev/null +++ b/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/UtilsTest.java @@ -0,0 +1,50 @@ +package org.dna.mqtt.moquette.parser.netty; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import static org.junit.Assert.assertEquals; +import org.junit.Before; +import org.junit.Test; + +/** + * + * @author andrea + */ +/*@RunWith(Suite.class) +@Suite.SuiteClasses({})*/ +public class UtilsTest { + + ByteBuf m_buff; + + @Before + public void setUp() { + m_buff = Unpooled.buffer(4); + } + + @Test + public void testDecodeRemainingLength() { + //1 byte length + m_buff.writeByte(0x0); + assertEquals(0, Utils.decodeRemainingLenght(m_buff)); + m_buff.clear().writeByte(0x007F); + assertEquals(127, Utils.decodeRemainingLenght(m_buff)); + + //2 byte length + m_buff.clear().writeBytes(new byte[]{(byte)0x80, (byte)0x01}); + assertEquals(128, Utils.decodeRemainingLenght(m_buff)); + m_buff.clear().writeBytes(new byte[]{(byte)0xFF, (byte)0x7F}); + assertEquals(16383, Utils.decodeRemainingLenght(m_buff)); + + //3 byte length + m_buff.clear().writeBytes(new byte[]{(byte)0x80, (byte)0x80, (byte)0x01}); + assertEquals(16384, Utils.decodeRemainingLenght(m_buff)); + m_buff.clear().writeBytes(new byte[]{(byte)0xFF, (byte)0xFF, (byte)0x7F}); + assertEquals(2097151, Utils.decodeRemainingLenght(m_buff)); + + //4 byte length + m_buff.clear().writeBytes(new byte[]{(byte)0x80, (byte)0x80, (byte)0x80, (byte)0x01}); + assertEquals(2097152, Utils.decodeRemainingLenght(m_buff)); + m_buff.clear().writeBytes(new byte[]{(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0x7F}); + assertEquals(268435455, Utils.decodeRemainingLenght(m_buff)); + } +} From 557c682a5e34113841aa6027a9c0f729875babab Mon Sep 17 00:00:00 2001 From: andrea Date: Wed, 11 Sep 2013 11:25:34 +0200 Subject: [PATCH 05/41] Ported all existing test for MINA ConnectDecoder to Netty's --- .../parser/netty/MQTTDecoderTest.java | 68 ++++++++++++++++++- 1 file changed, 67 insertions(+), 1 deletion(-) diff --git a/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/MQTTDecoderTest.java b/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/MQTTDecoderTest.java index 18e57be7..121fffaf 100644 --- a/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/MQTTDecoderTest.java +++ b/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/MQTTDecoderTest.java @@ -2,6 +2,7 @@ package org.dna.mqtt.moquette.parser.netty; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; +import io.netty.handler.codec.CorruptedFrameException; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.List; @@ -29,7 +30,6 @@ public class MQTTDecoderTest { public void testBaseHeader() throws Exception { m_buff = Unpooled.buffer(14); initBaseHeader(m_buff); - //m_buff.flip(); List results = new ArrayList(); //Excercise @@ -40,6 +40,72 @@ public class MQTTDecoderTest { verifyBaseHeader((ConnectMessage)results.get(0)); } + @Test + public void testBaseHeader_ClientID() throws UnsupportedEncodingException, Exception { + m_buff = Unpooled.buffer(40); + initHeader(m_buff, (byte) 38); + encodeString(m_buff, "ABCDEFGH"); + encodeString(m_buff, "Topic"); + encodeString(m_buff, "Message"); + List results = new ArrayList(); + + //Excercise + m_msgdec.decode(null, m_buff, results); + + //Verify + assertFalse(results.isEmpty()); + ConnectMessage message = (ConnectMessage)results.get(0); + verifyBaseHeader(message); + assertEquals("ABCDEFGH", message.getClientID()); + assertEquals("Topic", message.getWillTopic()); + assertEquals("Message", message.getWillMessage()); + } + + + @Test + public void testBaseHeader_extra_with_user_pwd() throws UnsupportedEncodingException, Exception { + m_buff = Unpooled.buffer(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"); + List results = new ArrayList(); + + //Excercise + m_msgdec.decode(null, m_buff, results); + + //Verify + assertFalse(results.isEmpty()); + ConnectMessage message = (ConnectMessage)results.get(0); + verifyBaseHeader(message); + assertEquals(AbstractMessage.CONNECT, message.getMessageType()); + assertEquals("ABCDEFGH", message.getClientID()); + assertEquals("Topic", message.getWillTopic()); + assertEquals("Message", message.getWillMessage()); + assertEquals("Fakeuser", message.getUsername()); + assertEquals("pwd", message.getPassword()); + } + + @Test(expected = CorruptedFrameException.class) + public void testBadFlagUserPwd() throws UnsupportedEncodingException, Exception { + m_buff = Unpooled.buffer(14); + m_buff.writeByte((AbstractMessage.CONNECT << 4)).writeByte(12); + //Proto name + encodeString(m_buff, "MQIsdp"); + //version + m_buff.writeByte(3); + //conn flags + m_buff.writeByte(0x4E); //sets user to false and password to true + //keepAlive + m_buff.writeByte(0).writeByte(0x0A); + List results = new ArrayList(); + + //Excercise + m_msgdec.decode(null, m_buff, results); + } + private void initBaseHeader(ByteBuf buff) throws UnsupportedEncodingException { initHeader(buff, (byte)0x0C); From 3f44eaee397868917d22c168c93ce20953bf76c1 Mon Sep 17 00:00:00 2001 From: andrea Date: Wed, 11 Sep 2013 15:42:00 +0200 Subject: [PATCH 06/41] Added Connect encoder part --- .../moquette/parser/netty/MQTTEncoder.java | 77 ++++++++++ .../dna/mqtt/moquette/parser/netty/Utils.java | 29 ++++ .../parser/netty/MQTTEncoderTest.java | 133 ++++++++++++++++++ .../mqtt/moquette/parser/netty/TestUtils.java | 49 +++++++ .../mqtt/moquette/parser/netty/UtilsTest.java | 33 ++++- 5 files changed, 320 insertions(+), 1 deletion(-) create mode 100644 netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTEncoder.java create mode 100644 netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/MQTTEncoderTest.java create mode 100644 netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/TestUtils.java diff --git a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTEncoder.java b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTEncoder.java new file mode 100644 index 00000000..c89aefb1 --- /dev/null +++ b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTEncoder.java @@ -0,0 +1,77 @@ +package org.dna.mqtt.moquette.parser.netty; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.MessageToByteEncoder; +import org.dna.mqtt.moquette.proto.messages.AbstractMessage; +import org.dna.mqtt.moquette.proto.messages.ConnectMessage; + +/** + * + * @author andrea + */ +public class MQTTEncoder extends MessageToByteEncoder { + + @Override + protected void encode(ChannelHandlerContext chc, AbstractMessage msg, ByteBuf bb) throws Exception { + if (msg.getMessageType() == AbstractMessage.CONNECT) { + encodeConnect(chc, (ConnectMessage)msg, bb); + } + } + + private void encodeConnect(ChannelHandlerContext chc, ConnectMessage message, ByteBuf out) { + ByteBuf staticHeaderBuff = chc.alloc().buffer(12); + staticHeaderBuff.writeBytes(Utils.encodeString("MQIsdp")); + + //version + staticHeaderBuff.writeByte(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.writeByte(connectionFlags); + + //Keep alive timer + staticHeaderBuff.writeShort(message.getKeepAlive()); + + //Variable part + ByteBuf variableHeaderBuff = chc.alloc().buffer(12); + if (message.getClientID() != null) { + variableHeaderBuff.writeBytes(Utils.encodeString(message.getClientID())); + if (message.isWillFlag()) { + variableHeaderBuff.writeBytes(Utils.encodeString(message.getWillTopic())); + variableHeaderBuff.writeBytes(Utils.encodeString(message.getWillMessage())); + } + if (message.isUserFlag() && message.getUsername() != null) { + variableHeaderBuff.writeBytes(Utils.encodeString(message.getUsername())); + if (message.isPasswordFlag() && message.getPassword() != null) { + variableHeaderBuff.writeBytes(Utils.encodeString(message.getPassword())); + } + } + } + + int variableHeaderSize = variableHeaderBuff.readableBytes(); + ByteBuf buff = chc.alloc().buffer(); + buff.writeByte(AbstractMessage.CONNECT << 4); + buff.writeBytes(Utils.encodeRemainingLength(12 + variableHeaderSize)); + buff.writeBytes(staticHeaderBuff).writeBytes(variableHeaderBuff); + + out.writeBytes(buff); + } + +} diff --git a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/Utils.java b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/Utils.java index 04267ff2..f0814bb3 100644 --- a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/Utils.java +++ b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/Utils.java @@ -2,6 +2,7 @@ package org.dna.mqtt.moquette.parser.netty; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; +import io.netty.handler.codec.CorruptedFrameException; import java.io.UnsupportedEncodingException; import org.slf4j.LoggerFactory; @@ -11,6 +12,8 @@ import org.slf4j.LoggerFactory; */ public class Utils { + public static final int MAX_LENGTH_LIMIT = 268435455; + static byte readMessageType(ByteBuf in) { byte h1 = in.readByte(); byte messageType = (byte) ((h1 & 0x00F0) >> 4); @@ -60,6 +63,32 @@ public class Utils { return value; } + /** + * Encode the value in the format defined in specification as variable length + * array. + * + * @throws IllegalArgumentException if the value is not in the specification bounds + * [0..268435455]. + */ + static ByteBuf encodeRemainingLength(int value) throws CorruptedFrameException { + if (value > MAX_LENGTH_LIMIT || value < 0) { + throw new CorruptedFrameException("Value should in range 0.." + MAX_LENGTH_LIMIT + " found " + value); + } + + ByteBuf encoded = Unpooled.buffer(4); + byte digit; + do { + digit = (byte) (value % 128); + value = value / 128; + // if there are more digits to encode, set the top bit of this digit + if (value > 0) { + digit = (byte) (digit | 0x80); + } + encoded.writeByte(digit); + } while (value > 0); + return encoded; + } + /** * Load a string from the given buffer, reading first the two bytes of len * and then the UTF-8 bytes of the string. diff --git a/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/MQTTEncoderTest.java b/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/MQTTEncoderTest.java new file mode 100644 index 00000000..6fe50536 --- /dev/null +++ b/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/MQTTEncoderTest.java @@ -0,0 +1,133 @@ +package org.dna.mqtt.moquette.parser.netty; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufAllocator; +import io.netty.buffer.Unpooled; +import io.netty.buffer.UnpooledByteBufAllocator; +import io.netty.channel.ChannelHandlerContext; +import org.dna.mqtt.moquette.proto.messages.ConnectMessage; +import org.junit.Test; +import org.junit.Before; +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.*; +import static org.dna.mqtt.moquette.parser.netty.TestUtils.*; + + +/** + * + * @author andrea + */ +public class MQTTEncoderTest { + MQTTEncoder m_encoder = new MQTTEncoder(); + ChannelHandlerContext m_mockedContext; + + @Before + public void setUp() { + //mock the ChannelHandlerContext to return an UnpooledAllocator + m_mockedContext = mock(ChannelHandlerContext.class); + ByteBufAllocator allocator = UnpooledByteBufAllocator.DEFAULT; + when(m_mockedContext.alloc()).thenReturn(allocator); + } + + @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); + //mock the ChannelHandlerContext to return an UnpooledAllocator + /*ChannelHandlerContext mockedContext = mock(ChannelHandlerContext.class); + ByteBufAllocator allocator = UnpooledByteBufAllocator.DEFAULT; + when(mockedContext.alloc()).thenReturn(allocator);*/ + ByteBuf out = Unpooled.buffer(); + + //Exercise + m_encoder.encode(m_mockedContext, msg, out); + + //Verify + assertEquals(0x10, out.readByte()); //1 byte + assertEquals(12, out.readByte()); //remaining length + verifyString("MQIsdp", out); + assertEquals(0x03, out.readByte()); //protocol version + assertEquals(0x32, out.readByte()); //flags + assertEquals(2, out.readByte()); //keepAliveTimer msb + assertEquals(0, out.readByte()); //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"); + + ByteBuf out = Unpooled.buffer(); + + //Exercise + m_encoder.encode(m_mockedContext, msg, out); + + //Verify + assertEquals(0x10, out.readByte()); //1 byte + assertEquals(36, out.readByte()); //remaining length + verifyString("MQIsdp", out); + assertEquals(0x03, out.readByte()); //protocol version + assertEquals(0x36, out.readByte()); //flags + assertEquals(2, out.readByte()); //keepAliveTimer msb + assertEquals(0, out.readByte()); //keepAliveTimer lsb + + //Variable part + verifyString("ABCDEF", out); + verifyString("Topic", out); + verifyString("Message", out); + } + + @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"); + + ByteBuf out = Unpooled.buffer(); + + //Exercise + m_encoder.encode(m_mockedContext, msg, out); + + //Verify + assertEquals(0x10, out.readByte()); //1 byte + assertEquals(48, out.readByte()); //remaining length + verifyString("MQIsdp", out); + assertEquals(0x03, out.readByte()); //protocol version + assertEquals((byte)0xF6, (byte)out.readByte()); //flags + assertEquals(2, out.readByte()); //keepAliveTimer msb + assertEquals(0, out.readByte()); //keepAliveTimer lsb + + //Variable part + verifyString("ABCDEF", out); + verifyString("Topic", out); + verifyString("Message", out); + verifyString("Pablo", out);//username + verifyString("PBL", out);//password + } +} diff --git a/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/TestUtils.java b/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/TestUtils.java new file mode 100644 index 00000000..d1d80e57 --- /dev/null +++ b/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/TestUtils.java @@ -0,0 +1,49 @@ +package org.dna.mqtt.moquette.parser.netty; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import java.io.UnsupportedEncodingException; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +/** + * Common test methods + * + * @author andrea + */ +public class TestUtils { + + /** + * 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, ByteBuf buff) { + assertTrue(numBytes <= buff.readableBytes()); + byte[] toCheck = new byte[numBytes]; + buff.readBytes(toCheck); + + for (int i = 0; i < numBytes; i++) { + assertEquals(bytes[i], toCheck[i]); + } + } + + /** + * Verify the presence of the given string starting from the current position + * inside the buffer. + */ + static void verifyString(String str, ByteBuf buff) throws UnsupportedEncodingException { + ByteBuf tmpBuff = Unpooled.buffer(2); + byte[] raw = str.getBytes("UTF-8"); + tmpBuff.writeShort(raw.length); + tmpBuff.writeBytes(raw); + int buffLen = raw.length + 2; + verifyByteBuf(tmpBuff, buff.slice(buff.readerIndex(), buffLen)); + buff.skipBytes(buffLen); + } + + + static void verifyByteBuf(ByteBuf expected, ByteBuf found) { + assertEquals(expected, found); + } + +} diff --git a/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/UtilsTest.java b/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/UtilsTest.java index 623f5171..adfbc366 100644 --- a/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/UtilsTest.java +++ b/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/UtilsTest.java @@ -2,9 +2,11 @@ package org.dna.mqtt.moquette.parser.netty; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; -import static org.junit.Assert.assertEquals; +import io.netty.handler.codec.CorruptedFrameException; import org.junit.Before; import org.junit.Test; +import static org.junit.Assert.*; +import static org.dna.mqtt.moquette.parser.netty.TestUtils.*; /** * @@ -47,4 +49,33 @@ public class UtilsTest { m_buff.clear().writeBytes(new byte[]{(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0x7F}); assertEquals(268435455, Utils.decodeRemainingLenght(m_buff)); } + + @Test(expected=CorruptedFrameException.class) + public void testEncodeRemainingLength_invalid_upper() { + Utils.encodeRemainingLength(Utils.MAX_LENGTH_LIMIT + 1); + } + + @Test(expected=CorruptedFrameException.class) + public void testEncodeRemainingLength_invalid_lower() { + Utils.encodeRemainingLength(-1); + } + + @Test + public void testEncodeRemainingLenght() { + //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)); + } } From 103d7725334974b6e92131aa006aaec9c74c003d Mon Sep 17 00:00:00 2001 From: andrea Date: Wed, 11 Sep 2013 15:44:15 +0200 Subject: [PATCH 07/41] Minor changes --- .../java/org/dna/mqtt/moquette/proto/ConnectDecoderTest.java | 2 +- .../src/test/java/org/dna/mqtt/moquette/proto/TestUtils.java | 2 +- .../org/dna/mqtt/moquette/proto/messages/ConnectMessage.java | 4 ++++ pom.xml | 1 - 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/parser/src/test/java/org/dna/mqtt/moquette/proto/ConnectDecoderTest.java b/parser/src/test/java/org/dna/mqtt/moquette/proto/ConnectDecoderTest.java index 63b8b83a..583aecf6 100644 --- a/parser/src/test/java/org/dna/mqtt/moquette/proto/ConnectDecoderTest.java +++ b/parser/src/test/java/org/dna/mqtt/moquette/proto/ConnectDecoderTest.java @@ -152,7 +152,7 @@ public class ConnectDecoderTest { } /** - * Encode and insert the given stirng into the given buff + * Encode and insert the given string into the given buff */ private IoBuffer encodeString(IoBuffer buff, String str) throws UnsupportedEncodingException { buff.put(Utils.encodeString(str)); diff --git a/parser/src/test/java/org/dna/mqtt/moquette/proto/TestUtils.java b/parser/src/test/java/org/dna/mqtt/moquette/proto/TestUtils.java index fb4bb602..78e34225 100644 --- a/parser/src/test/java/org/dna/mqtt/moquette/proto/TestUtils.java +++ b/parser/src/test/java/org/dna/mqtt/moquette/proto/TestUtils.java @@ -79,7 +79,7 @@ public class TestUtils { } /** - * Verify that the given bytes buffer og the given numBytes length is present + * 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) { diff --git a/parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/ConnectMessage.java b/parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/ConnectMessage.java index f286d682..123041c7 100644 --- a/parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/ConnectMessage.java +++ b/parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/ConnectMessage.java @@ -24,6 +24,10 @@ public class ConnectMessage extends AbstractMessage { String m_clientID; String m_willtopic; String m_willMessage; + + public ConnectMessage() { + m_messageType = AbstractMessage.CONNECT; + } public boolean isCleanSession() { return m_cleanSession; diff --git a/pom.xml b/pom.xml index a9a4e809..0c2343a3 100644 --- a/pom.xml +++ b/pom.xml @@ -21,7 +21,6 @@ netty_parser broker benchmarking - parser_commons From 24e34278aaee230bceafebdf58bc05e67c5f7493 Mon Sep 17 00:00:00 2001 From: andrea Date: Wed, 11 Sep 2013 16:19:31 +0200 Subject: [PATCH 08/41] Introduced DemuxDecoder to root the hierarchy of all message decoders --- .../moquette/parser/netty/ConnectDecoder.java | 146 +++++++++++++++ .../moquette/parser/netty/DemuxDecoder.java | 38 ++++ .../moquette/parser/netty/MQTTDecoder.java | 166 ++---------------- ...coderTest.java => ConnectDecoderTest.java} | 6 +- 4 files changed, 199 insertions(+), 157 deletions(-) create mode 100644 netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/ConnectDecoder.java create mode 100644 netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/DemuxDecoder.java rename netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/{MQTTDecoderTest.java => ConnectDecoderTest.java} (97%) diff --git a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/ConnectDecoder.java b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/ConnectDecoder.java new file mode 100644 index 00000000..a21b1f53 --- /dev/null +++ b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/ConnectDecoder.java @@ -0,0 +1,146 @@ +package org.dna.mqtt.moquette.parser.netty; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.CorruptedFrameException; +import java.io.UnsupportedEncodingException; +import java.util.List; +import org.dna.mqtt.moquette.proto.messages.ConnectMessage; + +/** + * + * @author andrea + */ +public class ConnectDecoder extends DemuxDecoder { + + @Override + void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws UnsupportedEncodingException { + in.resetReaderIndex(); + //Common decoding part + ConnectMessage message = new ConnectMessage(); + if (!decodeCommonHeader(message, in)) { + in.resetReaderIndex(); + return; + } + int remainingLength = message.getRemainingLength(); + int start = in.readerIndex(); + + //Connect specific decoding part + //ProtocolName 8 bytes + if (in.readableBytes() < 12) { + in.resetReaderIndex(); + return; + } + byte[] encProtoName = new byte[6]; + in.skipBytes(2); //size, is 0x06 + in.readBytes(encProtoName); + String protoName = new String(encProtoName, "UTF-8"); + if (!"MQIsdp".equals(protoName)) { + in.resetReaderIndex(); + throw new CorruptedFrameException("Invalid protoName: " + protoName); + } + message.setProtocolName(protoName); + + //ProtocolVersion 1 byte (value 0x03) + message.setProcotolVersion(in.readByte()); + + //Connection flag + byte connFlags = in.readByte(); + 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) { + in.resetReaderIndex(); + throw new CorruptedFrameException("Expected will QoS in range 0..2 but found: " + willQos); + } + 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) { + in.resetReaderIndex(); + throw new CorruptedFrameException("Expected password flag to true if the user flag is true but was: " + passwordFlag); + } + 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); + int keepAlive = in.readShort(); + message.setKeepAlive(keepAlive); + + if (remainingLength == 12) { + out.add(message); + return; + } + + //Decode the ClientID + String clientID = Utils.decodeString(in); + if (clientID == null) { + in.resetReaderIndex(); + return; + } + message.setClientID(clientID); + + //Decode willTopic + if (willFlag) { + String willTopic = Utils.decodeString(in); + if (willTopic == null) { + in.resetReaderIndex(); + return; + } + message.setWillTopic(willTopic); + } + + //Decode willMessage + if (willFlag) { + String willMessage = Utils.decodeString(in); + if (willMessage == null) { + in.resetReaderIndex(); + return; + } + message.setWillMessage(willMessage); + } + + //Compatibility check wieth v3.0, remaining length has precedence over + //the user and password flags + int readed = in.readerIndex() - start; + if (readed == remainingLength) { + out.add(message); + return; + } + + //Decode username + if (userFlag) { + String userName = Utils.decodeString(in); + if (userName == null) { + in.resetReaderIndex(); + return; + } + message.setUsername(userName); + } + + readed = in.readerIndex() - start; + if (readed == remainingLength) { + out.add(message); + return; + } + + //Decode password + if (passwordFlag) { + String password = Utils.decodeString(in); + if (password == null) { + in.resetReaderIndex(); + return; + } + message.setPassword(password); + } + + out.add(message); + } + +} diff --git a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/DemuxDecoder.java b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/DemuxDecoder.java new file mode 100644 index 00000000..8c055fb7 --- /dev/null +++ b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/DemuxDecoder.java @@ -0,0 +1,38 @@ +package org.dna.mqtt.moquette.parser.netty; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import java.util.List; +import org.dna.mqtt.moquette.proto.messages.AbstractMessage; + +/** + * + * @author andrea + */ +abstract class DemuxDecoder { + abstract void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception; + + protected boolean decodeCommonHeader(AbstractMessage message, ByteBuf in) { + //Common decoding part + if (in.readableBytes() < 2) { + return false; + } + byte h1 = in.readByte(); + 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 false; + } + + message.setMessageType(messageType); + message.setDupFlag(dupFlag); + message.setQos(AbstractMessage.QOSType.values()[qosLevel]); + message.setRetainFlag(retainFlag); + message.setRemainingLength(remainingLength); + return true; + } + +} diff --git a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTDecoder.java b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTDecoder.java index 708afa66..3021a397 100644 --- a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTDecoder.java +++ b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTDecoder.java @@ -4,15 +4,22 @@ import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.ByteToMessageDecoder; import io.netty.handler.codec.CorruptedFrameException; +import java.util.HashMap; import java.util.List; +import java.util.Map; import org.dna.mqtt.moquette.proto.messages.AbstractMessage; -import org.dna.mqtt.moquette.proto.messages.ConnectMessage; /** * * @author andrea */ public class MQTTDecoder extends ByteToMessageDecoder { + + private Map m_decoderMap = new HashMap(); + + MQTTDecoder() { + m_decoderMap.put(AbstractMessage.CONNECT, new ConnectDecoder()); + } @Override protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception { @@ -25,159 +32,10 @@ public class MQTTDecoder extends ByteToMessageDecoder { byte messageType = Utils.readMessageType(in); - if (messageType == AbstractMessage.CONNECT) { - in.resetReaderIndex(); - //Common decoding part - ConnectMessage message = new ConnectMessage(); - if (!decodeCommonHeader(message, in)) { - in.resetReaderIndex(); - return; - } - int remainingLength = message.getRemainingLength(); - int start = in.readerIndex(); - - //Connect specific decoding part - //ProtocolName 8 bytes - if (in.readableBytes() < 12) { - in.resetReaderIndex(); - return; - } - byte[] encProtoName = new byte[6]; - in.skipBytes(2); //size, is 0x06 - in.readBytes(encProtoName); - String protoName = new String(encProtoName, "UTF-8"); - if (!"MQIsdp".equals(protoName)) { - in.resetReaderIndex(); - throw new CorruptedFrameException("Invalid protoName: " + protoName); - } - message.setProtocolName(protoName); - - //ProtocolVersion 1 byte (value 0x03) - message.setProcotolVersion(in.readByte()); - - //Connection flag - byte connFlags = in.readByte(); - 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) { - in.resetReaderIndex(); - throw new CorruptedFrameException("Expected will QoS in range 0..2 but found: " + willQos); - } - 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) { - in.resetReaderIndex(); - throw new CorruptedFrameException("Expected password flag to true if the user flag is true but was: " + passwordFlag); - } - 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); - int keepAlive = in.readShort(); - message.setKeepAlive(keepAlive); - - if (remainingLength == 12) { - out.add(message); - return; - } - - //Decode the ClientID - String clientID = Utils.decodeString(in); - if (clientID == null) { - in.resetReaderIndex(); - return; - } - message.setClientID(clientID); - - //Decode willTopic - if (willFlag) { - String willTopic = Utils.decodeString(in); - if (willTopic == null) { - in.resetReaderIndex(); - return; - } - message.setWillTopic(willTopic); - } - - //Decode willMessage - if (willFlag) { - String willMessage = Utils.decodeString(in); - if (willMessage == null) { - in.resetReaderIndex(); - return; - } - message.setWillMessage(willMessage); - } - - //Compatibility check wieth v3.0, remaining length has precedence over - //the user and password flags - int readed = in.readerIndex() - start; - if (readed == remainingLength) { - out.add(message); - return; - } - - //Decode username - if (userFlag) { - String userName = Utils.decodeString(in); - if (userName == null) { - in.resetReaderIndex(); - return; - } - message.setUsername(userName); - } - - readed = in.readerIndex() - start; - if (readed == remainingLength) { - out.add(message); - return; - } - - //Decode password - if (passwordFlag) { - String password = Utils.decodeString(in); - if (password == null) { - in.resetReaderIndex(); - return; - } - message.setPassword(password); - } - - out.add(message); - return; + DemuxDecoder decoder = m_decoderMap.get(messageType); + if (decoder == null) { + throw new CorruptedFrameException("Can't find any suitable decoder for message type: " + messageType); } + decoder.decode(ctx, in, out); } - - - protected boolean decodeCommonHeader(AbstractMessage message, ByteBuf in) { - //Common decoding part - if (in.readableBytes() < 2) { - return false; - } - byte h1 = in.readByte(); - 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 false; - } - - message.setMessageType(messageType); - message.setDupFlag(dupFlag); - message.setQos(AbstractMessage.QOSType.values()[qosLevel]); - message.setRetainFlag(retainFlag); - message.setRemainingLength(remainingLength); - return true; - } - } diff --git a/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/MQTTDecoderTest.java b/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/ConnectDecoderTest.java similarity index 97% rename from netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/MQTTDecoderTest.java rename to netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/ConnectDecoderTest.java index 121fffaf..f457e99e 100644 --- a/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/MQTTDecoderTest.java +++ b/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/ConnectDecoderTest.java @@ -16,14 +16,14 @@ import org.junit.Before; import org.junit.Test; -public class MQTTDecoderTest { +public class ConnectDecoderTest { ByteBuf m_buff; - MQTTDecoder m_msgdec; + ConnectDecoder m_msgdec; @Before public void setUp() { - m_msgdec = new MQTTDecoder(); + m_msgdec = new ConnectDecoder(); } @Test From 6afaa46cf4540102d63b3c9654387da9c41798d1 Mon Sep 17 00:00:00 2001 From: andrea Date: Wed, 11 Sep 2013 16:33:16 +0200 Subject: [PATCH 09/41] Moved PubCompMessage to protocol commons module --- .../messaging/spi/impl/ProtocolProcessor.java | 2 +- .../moquette/messaging/spi/impl/SimpleMessaging.java | 2 +- .../java/org/dna/mqtt/moquette/server/Server.java | 1 + .../org/dna/mqtt/moquette/proto/PubCompDecoder.java | 1 + .../org/dna/mqtt/moquette/proto/PubCompEncoder.java | 1 + .../org/dna/mqtt/moquette/proto/PubCompMessage.java | 11 ----------- .../mqtt/moquette/proto/messages/PubCompMessage.java | 9 +++++++++ 7 files changed, 14 insertions(+), 13 deletions(-) delete mode 100644 parser/src/main/java/org/dna/mqtt/moquette/proto/PubCompMessage.java create mode 100644 parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/PubCompMessage.java diff --git a/broker/src/main/java/org/dna/mqtt/moquette/messaging/spi/impl/ProtocolProcessor.java b/broker/src/main/java/org/dna/mqtt/moquette/messaging/spi/impl/ProtocolProcessor.java index 2ea592eb..5388f3ae 100644 --- a/broker/src/main/java/org/dna/mqtt/moquette/messaging/spi/impl/ProtocolProcessor.java +++ b/broker/src/main/java/org/dna/mqtt/moquette/messaging/spi/impl/ProtocolProcessor.java @@ -11,7 +11,7 @@ import org.dna.mqtt.moquette.messaging.spi.impl.events.PubAckEvent; import org.dna.mqtt.moquette.messaging.spi.impl.events.PublishEvent; import org.dna.mqtt.moquette.messaging.spi.impl.subscriptions.Subscription; import org.dna.mqtt.moquette.messaging.spi.impl.subscriptions.SubscriptionsStore; -import org.dna.mqtt.moquette.proto.PubCompMessage; +import org.dna.mqtt.moquette.proto.messages.PubCompMessage; import org.dna.mqtt.moquette.proto.messages.AbstractMessage; import org.dna.mqtt.moquette.proto.messages.ConnAckMessage; import org.dna.mqtt.moquette.proto.messages.ConnectMessage; diff --git a/broker/src/main/java/org/dna/mqtt/moquette/messaging/spi/impl/SimpleMessaging.java b/broker/src/main/java/org/dna/mqtt/moquette/messaging/spi/impl/SimpleMessaging.java index e00a772f..d8141dee 100644 --- a/broker/src/main/java/org/dna/mqtt/moquette/messaging/spi/impl/SimpleMessaging.java +++ b/broker/src/main/java/org/dna/mqtt/moquette/messaging/spi/impl/SimpleMessaging.java @@ -14,7 +14,7 @@ import org.dna.mqtt.moquette.messaging.spi.IMessaging; import org.dna.mqtt.moquette.messaging.spi.IStorageService; import org.dna.mqtt.moquette.messaging.spi.impl.events.*; import org.dna.mqtt.moquette.messaging.spi.impl.subscriptions.SubscriptionsStore; -import org.dna.mqtt.moquette.proto.PubCompMessage; +import org.dna.mqtt.moquette.proto.messages.PubCompMessage; import org.dna.mqtt.moquette.proto.messages.*; import org.dna.mqtt.moquette.proto.messages.AbstractMessage.QOSType; import org.dna.mqtt.moquette.server.ConnectionDescriptor; diff --git a/broker/src/main/java/org/dna/mqtt/moquette/server/Server.java b/broker/src/main/java/org/dna/mqtt/moquette/server/Server.java index 5c658f28..ec0b6392 100644 --- a/broker/src/main/java/org/dna/mqtt/moquette/server/Server.java +++ b/broker/src/main/java/org/dna/mqtt/moquette/server/Server.java @@ -1,5 +1,6 @@ package org.dna.mqtt.moquette.server; +import org.dna.mqtt.moquette.proto.messages.PubCompMessage; import java.io.File; import java.io.IOException; import java.net.InetSocketAddress; diff --git a/parser/src/main/java/org/dna/mqtt/moquette/proto/PubCompDecoder.java b/parser/src/main/java/org/dna/mqtt/moquette/proto/PubCompDecoder.java index 5c3d53d6..bc9ac49a 100644 --- a/parser/src/main/java/org/dna/mqtt/moquette/proto/PubCompDecoder.java +++ b/parser/src/main/java/org/dna/mqtt/moquette/proto/PubCompDecoder.java @@ -1,5 +1,6 @@ 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; diff --git a/parser/src/main/java/org/dna/mqtt/moquette/proto/PubCompEncoder.java b/parser/src/main/java/org/dna/mqtt/moquette/proto/PubCompEncoder.java index a368cff8..c77c82bc 100644 --- a/parser/src/main/java/org/dna/mqtt/moquette/proto/PubCompEncoder.java +++ b/parser/src/main/java/org/dna/mqtt/moquette/proto/PubCompEncoder.java @@ -1,5 +1,6 @@ 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.ProtocolEncoderOutput; diff --git a/parser/src/main/java/org/dna/mqtt/moquette/proto/PubCompMessage.java b/parser/src/main/java/org/dna/mqtt/moquette/proto/PubCompMessage.java deleted file mode 100644 index 1b5f0e2e..00000000 --- a/parser/src/main/java/org/dna/mqtt/moquette/proto/PubCompMessage.java +++ /dev/null @@ -1,11 +0,0 @@ -package org.dna.mqtt.moquette.proto; - -import org.dna.mqtt.moquette.proto.messages.MessageIDMessage; - -/** - * - * @author andrea - */ -public class PubCompMessage extends MessageIDMessage { -} - diff --git a/parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/PubCompMessage.java b/parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/PubCompMessage.java new file mode 100644 index 00000000..a4dbdff5 --- /dev/null +++ b/parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/PubCompMessage.java @@ -0,0 +1,9 @@ +package org.dna.mqtt.moquette.proto.messages; + +/** + * + * @author andrea + */ +public class PubCompMessage extends MessageIDMessage { +} + From 22fe25da3287bf9bc33a532fbdedf70767f7046c Mon Sep 17 00:00:00 2001 From: andrea Date: Wed, 11 Sep 2013 16:47:41 +0200 Subject: [PATCH 10/41] Introduced hierarchy for message encoders --- .../moquette/parser/netty/ConnectEncoder.java | 70 +++++++++++++++++ .../moquette/parser/netty/DemuxEncoder.java | 13 ++++ .../moquette/parser/netty/MQTTEncoder.java | 78 ++++--------------- ...coderTest.java => ConnectEncoderTest.java} | 4 +- 4 files changed, 101 insertions(+), 64 deletions(-) create mode 100644 netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/ConnectEncoder.java create mode 100644 netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/DemuxEncoder.java rename netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/{MQTTEncoderTest.java => ConnectEncoderTest.java} (98%) diff --git a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/ConnectEncoder.java b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/ConnectEncoder.java new file mode 100644 index 00000000..030b32bf --- /dev/null +++ b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/ConnectEncoder.java @@ -0,0 +1,70 @@ +package org.dna.mqtt.moquette.parser.netty; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import org.dna.mqtt.moquette.proto.messages.AbstractMessage; +import org.dna.mqtt.moquette.proto.messages.ConnectMessage; + +/** + * + * @author andrea + */ +public class ConnectEncoder extends DemuxEncoder { + + @Override + protected void encode(ChannelHandlerContext chc, ConnectMessage message, ByteBuf out) { + ByteBuf staticHeaderBuff = chc.alloc().buffer(12); + staticHeaderBuff.writeBytes(Utils.encodeString("MQIsdp")); + + //version + staticHeaderBuff.writeByte(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.writeByte(connectionFlags); + + //Keep alive timer + staticHeaderBuff.writeShort(message.getKeepAlive()); + + //Variable part + ByteBuf variableHeaderBuff = chc.alloc().buffer(12); + if (message.getClientID() != null) { + variableHeaderBuff.writeBytes(Utils.encodeString(message.getClientID())); + if (message.isWillFlag()) { + variableHeaderBuff.writeBytes(Utils.encodeString(message.getWillTopic())); + variableHeaderBuff.writeBytes(Utils.encodeString(message.getWillMessage())); + } + if (message.isUserFlag() && message.getUsername() != null) { + variableHeaderBuff.writeBytes(Utils.encodeString(message.getUsername())); + if (message.isPasswordFlag() && message.getPassword() != null) { + variableHeaderBuff.writeBytes(Utils.encodeString(message.getPassword())); + } + } + } + + int variableHeaderSize = variableHeaderBuff.readableBytes(); + ByteBuf buff = chc.alloc().buffer(); + buff.writeByte(AbstractMessage.CONNECT << 4); + buff.writeBytes(Utils.encodeRemainingLength(12 + variableHeaderSize)); + buff.writeBytes(staticHeaderBuff).writeBytes(variableHeaderBuff); + + out.writeBytes(buff); + } + +} diff --git a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/DemuxEncoder.java b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/DemuxEncoder.java new file mode 100644 index 00000000..9499dd82 --- /dev/null +++ b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/DemuxEncoder.java @@ -0,0 +1,13 @@ +package org.dna.mqtt.moquette.parser.netty; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import org.dna.mqtt.moquette.proto.messages.AbstractMessage; + +/** + * + * @author andrea + */ +abstract class DemuxEncoder { + abstract protected void encode(ChannelHandlerContext chc, T msg, ByteBuf bb); +} diff --git a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTEncoder.java b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTEncoder.java index c89aefb1..50f4062b 100644 --- a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTEncoder.java +++ b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTEncoder.java @@ -2,76 +2,30 @@ package org.dna.mqtt.moquette.parser.netty; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.CorruptedFrameException; import io.netty.handler.codec.MessageToByteEncoder; +import java.util.HashMap; +import java.util.Map; import org.dna.mqtt.moquette.proto.messages.AbstractMessage; -import org.dna.mqtt.moquette.proto.messages.ConnectMessage; /** * * @author andrea */ public class MQTTEncoder extends MessageToByteEncoder { - - @Override - protected void encode(ChannelHandlerContext chc, AbstractMessage msg, ByteBuf bb) throws Exception { - if (msg.getMessageType() == AbstractMessage.CONNECT) { - encodeConnect(chc, (ConnectMessage)msg, bb); - } - } - - private void encodeConnect(ChannelHandlerContext chc, ConnectMessage message, ByteBuf out) { - ByteBuf staticHeaderBuff = chc.alloc().buffer(12); - staticHeaderBuff.writeBytes(Utils.encodeString("MQIsdp")); - - //version - staticHeaderBuff.writeByte(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.writeByte(connectionFlags); - - //Keep alive timer - staticHeaderBuff.writeShort(message.getKeepAlive()); - - //Variable part - ByteBuf variableHeaderBuff = chc.alloc().buffer(12); - if (message.getClientID() != null) { - variableHeaderBuff.writeBytes(Utils.encodeString(message.getClientID())); - if (message.isWillFlag()) { - variableHeaderBuff.writeBytes(Utils.encodeString(message.getWillTopic())); - variableHeaderBuff.writeBytes(Utils.encodeString(message.getWillMessage())); - } - if (message.isUserFlag() && message.getUsername() != null) { - variableHeaderBuff.writeBytes(Utils.encodeString(message.getUsername())); - if (message.isPasswordFlag() && message.getPassword() != null) { - variableHeaderBuff.writeBytes(Utils.encodeString(message.getPassword())); - } - } - } - - int variableHeaderSize = variableHeaderBuff.readableBytes(); - ByteBuf buff = chc.alloc().buffer(); - buff.writeByte(AbstractMessage.CONNECT << 4); - buff.writeBytes(Utils.encodeRemainingLength(12 + variableHeaderSize)); - buff.writeBytes(staticHeaderBuff).writeBytes(variableHeaderBuff); - - out.writeBytes(buff); + + private Map m_encoderMap = new HashMap(); + + MQTTEncoder() { + m_encoderMap.put(AbstractMessage.CONNECT, new ConnectEncoder()); } + @Override + protected void encode(ChannelHandlerContext chc, AbstractMessage msg, ByteBuf bb) throws Exception { + DemuxEncoder encoder = m_encoderMap.get(msg.getMessageType()); + if (encoder == null) { + throw new CorruptedFrameException("Can't find any suitable decoder for message type: " + msg.getMessageType()); + } + encoder.encode(chc, msg, bb); + } } diff --git a/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/MQTTEncoderTest.java b/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/ConnectEncoderTest.java similarity index 98% rename from netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/MQTTEncoderTest.java rename to netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/ConnectEncoderTest.java index 6fe50536..8bbb3a3c 100644 --- a/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/MQTTEncoderTest.java +++ b/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/ConnectEncoderTest.java @@ -17,8 +17,8 @@ import static org.dna.mqtt.moquette.parser.netty.TestUtils.*; * * @author andrea */ -public class MQTTEncoderTest { - MQTTEncoder m_encoder = new MQTTEncoder(); +public class ConnectEncoderTest { + ConnectEncoder m_encoder = new ConnectEncoder(); ChannelHandlerContext m_mockedContext; @Before From 93859d1a631069b0662cd126de14b6a211dc8a35 Mon Sep 17 00:00:00 2001 From: andrea Date: Wed, 11 Sep 2013 17:48:07 +0200 Subject: [PATCH 11/41] Added encode/decode for ConnAck message --- .../moquette/parser/netty/ConnAckDecoder.java | 31 +++++++++++ .../moquette/parser/netty/ConnAckEncoder.java | 22 ++++++++ .../moquette/parser/netty/MQTTDecoder.java | 1 + .../moquette/parser/netty/MQTTEncoder.java | 1 + .../parser/netty/ConnAckDecoderTest.java | 53 +++++++++++++++++++ .../parser/netty/ConnAckEncoderTest.java | 48 +++++++++++++++++ .../parser/netty/ConnectEncoderTest.java | 2 +- 7 files changed, 157 insertions(+), 1 deletion(-) create mode 100644 netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/ConnAckDecoder.java create mode 100644 netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/ConnAckEncoder.java create mode 100644 netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/ConnAckDecoderTest.java create mode 100644 netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/ConnAckEncoderTest.java diff --git a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/ConnAckDecoder.java b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/ConnAckDecoder.java new file mode 100644 index 00000000..6100e70a --- /dev/null +++ b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/ConnAckDecoder.java @@ -0,0 +1,31 @@ +package org.dna.mqtt.moquette.parser.netty; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import java.util.List; +import org.dna.mqtt.moquette.proto.messages.ConnAckMessage; + +/** + * + * @author andrea + */ +class ConnAckDecoder extends DemuxDecoder { + + @Override + void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception { + in.resetReaderIndex(); + //Common decoding part + ConnAckMessage message = new ConnAckMessage(); + if (!decodeCommonHeader(message, in)) { + in.resetReaderIndex(); + return; + } + //skip reserved byte + in.skipBytes(1); + + //read return code + message.setReturnCode(in.readByte()); + out.add(message); + } + +} diff --git a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/ConnAckEncoder.java b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/ConnAckEncoder.java new file mode 100644 index 00000000..e84abdcd --- /dev/null +++ b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/ConnAckEncoder.java @@ -0,0 +1,22 @@ +package org.dna.mqtt.moquette.parser.netty; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import org.dna.mqtt.moquette.proto.messages.AbstractMessage; +import org.dna.mqtt.moquette.proto.messages.ConnAckMessage; + +/** + * + * @author andrea + */ +class ConnAckEncoder extends DemuxEncoder{ + + @Override + protected void encode(ChannelHandlerContext chc, ConnAckMessage message, ByteBuf out) { + out.writeByte(AbstractMessage.CONNACK << 4); + out.writeBytes(Utils.encodeRemainingLength(2)); + out.writeByte(0); + out.writeByte(message.getReturnCode()); + } + +} diff --git a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTDecoder.java b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTDecoder.java index 3021a397..86fb02c5 100644 --- a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTDecoder.java +++ b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTDecoder.java @@ -19,6 +19,7 @@ public class MQTTDecoder extends ByteToMessageDecoder { MQTTDecoder() { m_decoderMap.put(AbstractMessage.CONNECT, new ConnectDecoder()); + m_decoderMap.put(AbstractMessage.CONNACK, new ConnAckDecoder()); } @Override diff --git a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTEncoder.java b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTEncoder.java index 50f4062b..8b06003b 100644 --- a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTEncoder.java +++ b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTEncoder.java @@ -18,6 +18,7 @@ public class MQTTEncoder extends MessageToByteEncoder { MQTTEncoder() { m_encoderMap.put(AbstractMessage.CONNECT, new ConnectEncoder()); + m_encoderMap.put(AbstractMessage.CONNACK, new ConnAckEncoder()); } @Override diff --git a/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/ConnAckDecoderTest.java b/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/ConnAckDecoderTest.java new file mode 100644 index 00000000..8dd1915e --- /dev/null +++ b/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/ConnAckDecoderTest.java @@ -0,0 +1,53 @@ +package org.dna.mqtt.moquette.parser.netty; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import java.util.ArrayList; +import java.util.List; +import org.dna.mqtt.moquette.proto.messages.AbstractMessage; +import org.dna.mqtt.moquette.proto.messages.ConnAckMessage; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import org.junit.Before; +import org.junit.Test; + +/** + * + * @author andrea + */ +public class ConnAckDecoderTest { + ByteBuf m_buff; + ConnAckDecoder m_msgdec; + + @Before + public void setUp() { + m_msgdec = new ConnAckDecoder(); + } + + @Test + public void testHeader() throws Exception { + m_buff = Unpooled.buffer(14); + initHeader(m_buff); + List results = new ArrayList(); + + //Excercise + m_msgdec.decode(null, m_buff, results); + + //Verify + assertFalse(results.isEmpty()); + ConnAckMessage message = (ConnAckMessage)results.get(0); + assertNotNull(message); + assertEquals(ConnAckMessage.CONNECTION_ACCEPTED, message.getReturnCode()); + assertEquals(AbstractMessage.CONNACK, message.getMessageType()); + } + + private void initHeader(ByteBuf buff) { + buff.clear().writeByte(AbstractMessage.CONNACK << 4).writeByte(2); + //reserved + buff.writeByte((byte)0); + //return code + buff.writeByte(ConnAckMessage.CONNECTION_ACCEPTED); + } + +} diff --git a/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/ConnAckEncoderTest.java b/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/ConnAckEncoderTest.java new file mode 100644 index 00000000..c223301d --- /dev/null +++ b/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/ConnAckEncoderTest.java @@ -0,0 +1,48 @@ +package org.dna.mqtt.moquette.parser.netty; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufAllocator; +import io.netty.buffer.Unpooled; +import io.netty.buffer.UnpooledByteBufAllocator; +import io.netty.channel.ChannelHandlerContext; +import org.dna.mqtt.moquette.proto.messages.ConnAckMessage; +import org.junit.Before; +import org.junit.Test; +import static org.junit.Assert.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +/** + * + * @author andrea + */ +public class ConnAckEncoderTest { + + ConnAckEncoder m_encoder = new ConnAckEncoder(); + ChannelHandlerContext m_mockedContext; + + @Before + public void setUp() { + //mock the ChannelHandlerContext to return an UnpooledAllocator + m_mockedContext = mock(ChannelHandlerContext.class); + ByteBufAllocator allocator = UnpooledByteBufAllocator.DEFAULT; + when(m_mockedContext.alloc()).thenReturn(allocator); + } + + @Test + public void testHeaderEncode() throws Exception { + ConnAckMessage msg = new ConnAckMessage(); + + //Exercise + ByteBuf out = Unpooled.buffer(); + + //Exercise + m_encoder.encode(m_mockedContext, msg, out); + + //Verify + assertEquals(0x20, out.readByte()); //1 byte + assertEquals(0x02, out.readByte()); //2 byte, length + assertEquals(ConnAckMessage.CONNECTION_ACCEPTED, out.skipBytes(1).readByte()); + } + +} diff --git a/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/ConnectEncoderTest.java b/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/ConnectEncoderTest.java index 8bbb3a3c..eccbc394 100644 --- a/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/ConnectEncoderTest.java +++ b/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/ConnectEncoderTest.java @@ -8,7 +8,7 @@ import io.netty.channel.ChannelHandlerContext; import org.dna.mqtt.moquette.proto.messages.ConnectMessage; import org.junit.Test; import org.junit.Before; -import static org.junit.Assert.assertEquals; +import static org.junit.Assert.*; import static org.mockito.Mockito.*; import static org.dna.mqtt.moquette.parser.netty.TestUtils.*; From 1baa16b8e3dda8c3aae5b95f7e7625a39e8821da Mon Sep 17 00:00:00 2001 From: andrea Date: Wed, 11 Sep 2013 17:51:59 +0200 Subject: [PATCH 12/41] Extracted a TestUtils method mockChannelHandler --- .../mqtt/moquette/parser/netty/ConnectEncoderTest.java | 7 +------ .../org/dna/mqtt/moquette/parser/netty/TestUtils.java | 10 ++++++++++ 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/ConnectEncoderTest.java b/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/ConnectEncoderTest.java index eccbc394..21235a81 100644 --- a/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/ConnectEncoderTest.java +++ b/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/ConnectEncoderTest.java @@ -1,15 +1,12 @@ package org.dna.mqtt.moquette.parser.netty; import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; import io.netty.buffer.Unpooled; -import io.netty.buffer.UnpooledByteBufAllocator; import io.netty.channel.ChannelHandlerContext; import org.dna.mqtt.moquette.proto.messages.ConnectMessage; import org.junit.Test; import org.junit.Before; import static org.junit.Assert.*; -import static org.mockito.Mockito.*; import static org.dna.mqtt.moquette.parser.netty.TestUtils.*; @@ -24,9 +21,7 @@ public class ConnectEncoderTest { @Before public void setUp() { //mock the ChannelHandlerContext to return an UnpooledAllocator - m_mockedContext = mock(ChannelHandlerContext.class); - ByteBufAllocator allocator = UnpooledByteBufAllocator.DEFAULT; - when(m_mockedContext.alloc()).thenReturn(allocator); + m_mockedContext = mockChannelHandler(); } @Test diff --git a/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/TestUtils.java b/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/TestUtils.java index d1d80e57..14242230 100644 --- a/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/TestUtils.java +++ b/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/TestUtils.java @@ -1,10 +1,14 @@ package org.dna.mqtt.moquette.parser.netty; import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufAllocator; import io.netty.buffer.Unpooled; +import io.netty.buffer.UnpooledByteBufAllocator; +import io.netty.channel.ChannelHandlerContext; import java.io.UnsupportedEncodingException; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.*; /** * Common test methods @@ -46,4 +50,10 @@ public class TestUtils { assertEquals(expected, found); } + static ChannelHandlerContext mockChannelHandler() { + ChannelHandlerContext m_mockedContext = mock(ChannelHandlerContext.class); + ByteBufAllocator allocator = UnpooledByteBufAllocator.DEFAULT; + when(m_mockedContext.alloc()).thenReturn(allocator); + return m_mockedContext; + } } From ecfc5e645a7ac5b3e549409f9f1170c60d71be99 Mon Sep 17 00:00:00 2001 From: andrea Date: Wed, 11 Sep 2013 17:54:47 +0200 Subject: [PATCH 13/41] Minor cleaned imports --- .../java/org/dna/mqtt/moquette/proto/ConnAckEncoderTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/parser/src/test/java/org/dna/mqtt/moquette/proto/ConnAckEncoderTest.java b/parser/src/test/java/org/dna/mqtt/moquette/proto/ConnAckEncoderTest.java index b4b61cf3..9b537892 100644 --- a/parser/src/test/java/org/dna/mqtt/moquette/proto/ConnAckEncoderTest.java +++ b/parser/src/test/java/org/dna/mqtt/moquette/proto/ConnAckEncoderTest.java @@ -1,6 +1,5 @@ package org.dna.mqtt.moquette.proto; -import org.dna.mqtt.moquette.proto.ConnAckEncoder; import org.dna.mqtt.moquette.proto.messages.ConnAckMessage; import org.junit.Test; import static org.junit.Assert.*; From 4335a566f129858241d3d9a36ca2ef29fe766610 Mon Sep 17 00:00:00 2001 From: andrea Date: Thu, 12 Sep 2013 09:19:31 +0200 Subject: [PATCH 14/41] Minor cleaned imports --- .../java/org/dna/mqtt/moquette/proto/PublishDecoderTest.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/parser/src/test/java/org/dna/mqtt/moquette/proto/PublishDecoderTest.java b/parser/src/test/java/org/dna/mqtt/moquette/proto/PublishDecoderTest.java index d4e0510f..153d0564 100644 --- a/parser/src/test/java/org/dna/mqtt/moquette/proto/PublishDecoderTest.java +++ b/parser/src/test/java/org/dna/mqtt/moquette/proto/PublishDecoderTest.java @@ -1,9 +1,5 @@ package org.dna.mqtt.moquette.proto; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; import org.apache.mina.core.buffer.IoBuffer; import org.apache.mina.filter.codec.demux.MessageDecoder; import org.apache.mina.filter.codec.demux.MessageDecoderResult; @@ -12,7 +8,6 @@ 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.Ignore; import org.junit.Test; /** From 4dd633cef80c6d6f886df0311d8c49647e7b97c2 Mon Sep 17 00:00:00 2001 From: andrea Date: Thu, 12 Sep 2013 12:11:10 +0200 Subject: [PATCH 15/41] Minor cleaned imports --- .../java/org/dna/mqtt/moquette/proto/PublishEncoderTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/parser/src/test/java/org/dna/mqtt/moquette/proto/PublishEncoderTest.java b/parser/src/test/java/org/dna/mqtt/moquette/proto/PublishEncoderTest.java index c3dd9f15..e11404be 100644 --- a/parser/src/test/java/org/dna/mqtt/moquette/proto/PublishEncoderTest.java +++ b/parser/src/test/java/org/dna/mqtt/moquette/proto/PublishEncoderTest.java @@ -1,6 +1,5 @@ package org.dna.mqtt.moquette.proto; -import org.apache.mina.core.buffer.IoBuffer; import org.dna.mqtt.moquette.proto.messages.AbstractMessage.QOSType; import org.dna.mqtt.moquette.proto.messages.PublishMessage; import org.junit.Before; From 28b37f68ea43df3f8f9096351d5020839d75908b Mon Sep 17 00:00:00 2001 From: andrea Date: Thu, 12 Sep 2013 12:38:31 +0200 Subject: [PATCH 16/41] Added encode/decode for Publish message --- .../moquette/parser/netty/MQTTDecoder.java | 1 + .../moquette/parser/netty/MQTTEncoder.java | 1 + .../moquette/parser/netty/PublishDecoder.java | 61 ++++++ .../moquette/parser/netty/PublishEncoder.java | 45 +++++ .../dna/mqtt/moquette/parser/netty/Utils.java | 25 +++ .../parser/netty/ConnectEncoderTest.java | 4 - .../parser/netty/PublishDecoderTest.java | 167 +++++++++++++++++ .../parser/netty/PublishEncoderTest.java | 175 ++++++++++++++++++ .../mqtt/moquette/parser/netty/TestUtils.java | 16 ++ .../mqtt/moquette/parser/netty/UtilsTest.java | 12 ++ 10 files changed, 503 insertions(+), 4 deletions(-) create mode 100644 netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/PublishDecoder.java create mode 100644 netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/PublishEncoder.java create mode 100644 netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/PublishDecoderTest.java create mode 100644 netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/PublishEncoderTest.java diff --git a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTDecoder.java b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTDecoder.java index 86fb02c5..8cfe6e9d 100644 --- a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTDecoder.java +++ b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTDecoder.java @@ -20,6 +20,7 @@ public class MQTTDecoder extends ByteToMessageDecoder { MQTTDecoder() { m_decoderMap.put(AbstractMessage.CONNECT, new ConnectDecoder()); m_decoderMap.put(AbstractMessage.CONNACK, new ConnAckDecoder()); + m_decoderMap.put(AbstractMessage.PUBLISH, new PublishDecoder()); } @Override diff --git a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTEncoder.java b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTEncoder.java index 8b06003b..d640fc71 100644 --- a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTEncoder.java +++ b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTEncoder.java @@ -19,6 +19,7 @@ public class MQTTEncoder extends MessageToByteEncoder { MQTTEncoder() { m_encoderMap.put(AbstractMessage.CONNECT, new ConnectEncoder()); m_encoderMap.put(AbstractMessage.CONNACK, new ConnAckEncoder()); + m_encoderMap.put(AbstractMessage.PUBLISH, new PublishEncoder()); } @Override diff --git a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/PublishDecoder.java b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/PublishDecoder.java new file mode 100644 index 00000000..876252c6 --- /dev/null +++ b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/PublishDecoder.java @@ -0,0 +1,61 @@ +package org.dna.mqtt.moquette.parser.netty; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import java.util.List; +import org.dna.mqtt.moquette.proto.messages.AbstractMessage; +import org.dna.mqtt.moquette.proto.messages.PublishMessage; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * + * @author andrea + */ +class PublishDecoder extends DemuxDecoder { + + private static Logger LOG = LoggerFactory.getLogger(PublishDecoder.class); + + @Override + void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception { + LOG.debug("decode invoked with buffer " + in); + int startPos = in.readerIndex(); + in.resetReaderIndex(); + + //Common decoding part + PublishMessage message = new PublishMessage(); + if (!decodeCommonHeader(message, in)) { + LOG.info("decode ask for more data after " + in); + in.resetReaderIndex(); + return; + } + int remainingLength = message.getRemainingLength(); + + //Topic name + String topic = Utils.decodeString(in); + if (topic == null) { + in.resetReaderIndex(); + return; + } + message.setTopicName(topic); + + if (message.getQos() == AbstractMessage.QOSType.LEAST_ONE || + message.getQos() == AbstractMessage.QOSType.EXACTLY_ONCE) { + message.setMessageID((int)in.readShort()); + } + int stopPos = in.readerIndex(); + + //read the payload + int payloadSize = remainingLength - (stopPos - startPos - 2) + (Utils.numBytesToEncode(remainingLength) - 1); + if (in.readableBytes() < payloadSize) { + in.resetReaderIndex(); + return; + } + byte[] b = new byte[payloadSize]; + in.readBytes(b); + message.setPayload(b); + + out.add(message); + } + +} diff --git a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/PublishEncoder.java b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/PublishEncoder.java new file mode 100644 index 00000000..171b4dcb --- /dev/null +++ b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/PublishEncoder.java @@ -0,0 +1,45 @@ +package org.dna.mqtt.moquette.parser.netty; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import org.dna.mqtt.moquette.proto.messages.AbstractMessage; +import org.dna.mqtt.moquette.proto.messages.PublishMessage; + +/** + * + * @author andrea + */ +class PublishEncoder extends DemuxEncoder { + + @Override + protected void encode(ChannelHandlerContext ctx, PublishMessage message, ByteBuf out) { + if (message.getQos() == AbstractMessage.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"); + } + + ByteBuf variableHeaderBuff = ctx.alloc().buffer(2); + variableHeaderBuff.writeBytes(Utils.encodeString(message.getTopicName())); + if (message.getQos() == AbstractMessage.QOSType.LEAST_ONE || + message.getQos() == AbstractMessage.QOSType.EXACTLY_ONCE ) { + if (message.getMessageID() == null) { + throw new IllegalArgumentException("Found a message with QOS 1 or 2 and not MessageID setted"); + } + variableHeaderBuff.writeShort(message.getMessageID()); + } + variableHeaderBuff.writeBytes(message.getPayload()); + int variableHeaderSize = variableHeaderBuff.readableBytes(); + + byte flags = Utils.encodeFlags(message); + + ByteBuf buff = ctx.alloc().buffer(2 + variableHeaderSize); + buff.writeByte(AbstractMessage.PUBLISH << 4 | flags); + buff.writeBytes(Utils.encodeRemainingLength(variableHeaderSize)); + buff.writeBytes(variableHeaderBuff); + + out.writeBytes(buff); + } + +} diff --git a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/Utils.java b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/Utils.java index f0814bb3..1252e22c 100644 --- a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/Utils.java +++ b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/Utils.java @@ -4,6 +4,7 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.handler.codec.CorruptedFrameException; import java.io.UnsupportedEncodingException; +import org.dna.mqtt.moquette.proto.messages.AbstractMessage; import org.slf4j.LoggerFactory; /** @@ -131,4 +132,28 @@ public class Utils { out.writeBytes(raw); return out; } + + /** + * Return the number of bytes to encode the given remaining length value + */ + static int numBytesToEncode(int len) { + if (0 <= len && len <= 127) return 1; + if (128 <= len && len <= 16383) return 2; + if (16384 <= len && len <= 2097151) return 3; + if (2097152 <= len && len <= 268435455) return 4; + throw new IllegalArgumentException("value shoul be in the range [0..268435455]"); + } + + static byte encodeFlags(AbstractMessage message) { + byte flags = 0; + if (message.isDupFlag()) { + flags |= 0x08; + } + if (message.isRetainFlag()) { + flags |= 0x01; + } + + flags |= ((message.getQos().ordinal() & 0x03) << 1); + return flags; + } } diff --git a/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/ConnectEncoderTest.java b/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/ConnectEncoderTest.java index 21235a81..b3c7e40d 100644 --- a/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/ConnectEncoderTest.java +++ b/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/ConnectEncoderTest.java @@ -32,10 +32,6 @@ public class ConnectEncoderTest { msg.setWillFlag(false); msg.setCleanSession(true); msg.setKeepAlive(512); - //mock the ChannelHandlerContext to return an UnpooledAllocator - /*ChannelHandlerContext mockedContext = mock(ChannelHandlerContext.class); - ByteBufAllocator allocator = UnpooledByteBufAllocator.DEFAULT; - when(mockedContext.alloc()).thenReturn(allocator);*/ ByteBuf out = Unpooled.buffer(); //Exercise diff --git a/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/PublishDecoderTest.java b/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/PublishDecoderTest.java new file mode 100644 index 00000000..c935d3cf --- /dev/null +++ b/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/PublishDecoderTest.java @@ -0,0 +1,167 @@ +package org.dna.mqtt.moquette.parser.netty; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import java.util.ArrayList; +import java.util.List; +import org.dna.mqtt.moquette.proto.messages.AbstractMessage; +import org.dna.mqtt.moquette.proto.messages.PublishMessage; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import org.junit.Before; +import org.junit.Test; + +/** + * + * @author andrea + */ +public class PublishDecoderTest { + ByteBuf m_buff; + PublishDecoder m_msgdec; + List m_results; + + @Before + public void setUp() { + m_msgdec = new PublishDecoder(); + m_results = new ArrayList(); + } + + @Test + public void testHeader() throws Exception { + m_buff = Unpooled.buffer(14); + initHeader(m_buff); + + //Excercise + m_msgdec.decode(null, m_buff, m_results); + + assertFalse(m_results.isEmpty()); + PublishMessage message = (PublishMessage)m_results.get(0); + assertNotNull(message); + assertEquals("Fake Topic", message.getTopicName()); + assertNull(message.getMessageID()); + assertEquals(AbstractMessage.PUBLISH, message.getMessageType()); + } + + @Test + public void testHeaderWithMessageID() throws Exception { + m_buff = Unpooled.buffer(14); + int messageID = 123; + initHeaderWithMessageID(m_buff, messageID); + + //Exercise + m_msgdec.decode(null, m_buff, m_results); + + assertFalse(m_results.isEmpty()); + PublishMessage message = (PublishMessage)m_results.get(0); + assertNotNull(message); + assertEquals("Fake Topic", message.getTopicName()); + assertEquals(messageID, (int) message.getMessageID()); + } + + + @Test + public void testHeaderWithMessageID_Payload() throws Exception { + m_buff = Unpooled.buffer(14); + int messageID = 123; + byte[] payload = new byte[]{0x0A, 0x0B, 0x0C}; + initHeaderWithMessageID_Payload(m_buff, messageID, payload); + + //Exercise + m_msgdec.decode(null, m_buff, m_results); + + assertFalse(m_results.isEmpty()); + PublishMessage message = (PublishMessage)m_results.get(0); + assertNotNull(message); + assertEquals("Fake Topic", message.getTopicName()); + assertEquals(messageID, (int) message.getMessageID()); + TestUtils.verifyEquals(payload, message.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 = Unpooled.buffer(overallMessage.length); + m_buff.writeBytes(overallMessage); + + //Exercise + m_msgdec.decode(null, m_buff, m_results); + + assertFalse(m_results.isEmpty()); + PublishMessage message = (PublishMessage)m_results.get(0); + assertNotNull(message); + } + + @Test + public void testDecodeBigContent() throws Exception { + int size = 129; + ByteBuf payload = TestUtils.generateRandomPayload(size); + + ByteBuf firstPublish = generatePublishQoS0(payload); + ByteBuf secondPublish = generatePublishQoS0(TestUtils.generateRandomPayload(size)); + + ByteBuf doubleMessageBuf = Unpooled.buffer(size * 2); + doubleMessageBuf.writeBytes(firstPublish).writeBytes(secondPublish); + + + //Exercise + m_msgdec.decode(null, doubleMessageBuf, m_results); + + assertFalse(m_results.isEmpty()); + PublishMessage message = (PublishMessage)m_results.get(0); + assertNotNull(message); + m_results.clear(); + + m_msgdec.decode(null, doubleMessageBuf, m_results); + + assertFalse(m_results.isEmpty()); + message = (PublishMessage)m_results.get(0); + assertNotNull(message); + } + + private void initHeader(ByteBuf buff) throws IllegalAccessException { + ByteBuf tmp = Unpooled.buffer(4).writeBytes(Utils.encodeString("Fake Topic")); + buff.clear().writeByte(AbstractMessage.PUBLISH << 4).writeBytes(Utils.encodeRemainingLength(tmp.readableBytes())); + //topic name + buff.writeBytes(tmp); + } + + private void initHeaderWithMessageID(ByteBuf buff, int messageID) throws IllegalAccessException { + ByteBuf tmp = Unpooled.buffer(4).writeBytes(Utils.encodeString("Fake Topic")); + tmp.writeShort(messageID); + buff.clear().writeByte(AbstractMessage.PUBLISH << 4 | 0x02) //set Qos to 1 + .writeBytes(Utils.encodeRemainingLength(tmp.readableBytes())); + //topic name + buff.writeBytes(tmp); + } + + private void initHeaderWithMessageID_Payload(ByteBuf buff, int messageID, byte[] payload) throws IllegalAccessException { + ByteBuf tmp = Unpooled.buffer(4).writeBytes(Utils.encodeString("Fake Topic")); + tmp.writeShort(messageID); + tmp.writeBytes(payload); + buff.clear().writeByte(AbstractMessage.PUBLISH << 4 | 0x02) //set Qos to 1 + .writeBytes(Utils.encodeRemainingLength(tmp.readableBytes())); + //topic name + buff.writeBytes(tmp); + } + + private ByteBuf generatePublishQoS0(ByteBuf payload) throws IllegalAccessException { + int size = payload.capacity(); + ByteBuf messageBody = Unpooled.buffer(size); + messageBody.writeBytes(Utils.encodeString("/topic")); + + //ONLY for QoS > 1 Utils.writeWord(messageBody, messageID); + messageBody.writeBytes(payload); + + ByteBuf completeMsg = Unpooled.buffer(size); + completeMsg.clear().writeByte(AbstractMessage.PUBLISH << 4 | 0x00) //set Qos to 0 + .writeBytes(Utils.encodeRemainingLength(messageBody.readableBytes())); + completeMsg.writeBytes(messageBody); + + return completeMsg; + } +} diff --git a/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/PublishEncoderTest.java b/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/PublishEncoderTest.java new file mode 100644 index 00000000..a7216af1 --- /dev/null +++ b/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/PublishEncoderTest.java @@ -0,0 +1,175 @@ +package org.dna.mqtt.moquette.parser.netty; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.ChannelHandlerContext; +import org.junit.Before; +import org.junit.Test; +import static org.junit.Assert.*; +import static org.dna.mqtt.moquette.parser.netty.TestUtils.*; +import org.dna.mqtt.moquette.proto.messages.AbstractMessage.QOSType; +import org.dna.mqtt.moquette.proto.messages.PublishMessage; + +/** + * + * @author andrea + */ +public class PublishEncoderTest { + PublishEncoder m_encoder = new PublishEncoder(); + ChannelHandlerContext m_mockedContext; + ByteBuf m_out; + + @Before + public void setUp() { + //mock the ChannelHandlerContext to return an UnpooledAllocator + m_mockedContext = mockChannelHandler(); + m_out = Unpooled.buffer(); + } + + + @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(m_mockedContext, msg, m_out); + + //Verify + assertEquals(0x30, m_out.readByte()); //1 byte + //(2+7) topic + 3 payload + assertEquals(12, m_out.readByte()); //remaining length + + //Variable part + verifyString(topic, m_out); + verifyBuff(payload.length, payload, m_out); + } + + + @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(m_mockedContext, msg, m_out); + + //Verify + assertEquals(0x32, m_out.readByte()); //1 byte + //(2+7) topic + 2 messageID + 3 payload + assertEquals(14, m_out.readByte()); //remaining length + + //Variable part + verifyString(topic, m_out); + assertEquals(0, m_out.readByte()); //MessageID MSB + assertEquals(1, m_out.readByte()); //MessageID LSB + verifyBuff(payload.length, payload, m_out); + } + + @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(m_mockedContext, msg, m_out); + } + + @Test(expected = IllegalArgumentException.class) + public void testEncode_empty_topic() throws Exception { + PublishMessage msg = new PublishMessage(); + msg.setQos(QOSType.LEAST_ONE); + msg.setMessageID(1); + ByteBuf out = Unpooled.buffer(); + + //Exercise + m_encoder.encode(m_mockedContext, msg, out); + } + + @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(m_mockedContext, msg, m_out); + } + + @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); + ByteBuf out = Unpooled.buffer(); + + //Exercise + m_encoder.encode(m_mockedContext, msg, out); + } + + @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(m_mockedContext, msg, m_out); + + //Verify + //2 byte header + (2+6) topic name + [2 message ID if QoQ <> 0]+ payload + int size = m_out.readableBytes(); + assertEquals(10+payload.length, size); + assertEquals(0x30, m_out.readByte()); //1 byte + assertEquals(23, m_out.readByte()); //1 byte the length + } + + @Test + public void testEncodeBigMessage() throws Exception { + byte[] payload = new byte[256]; + + TestUtils.generateRandomPayload(256).readBytes(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(m_mockedContext, msg, m_out); + + int size = m_out.readableBytes(); + assertEquals(11+payload.length, size); + assertEquals(0x30, m_out.readByte()); //1 byte + } + +} diff --git a/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/TestUtils.java b/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/TestUtils.java index 14242230..29a93411 100644 --- a/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/TestUtils.java +++ b/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/TestUtils.java @@ -56,4 +56,20 @@ public class TestUtils { when(m_mockedContext.alloc()).thenReturn(allocator); return m_mockedContext; } + + 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 ByteBuf generateRandomPayload(int size) { + ByteBuf payloadBuffer = Unpooled.buffer(size); + for (int i = 0; i < size; i++) { + payloadBuffer.writeByte((byte) (Math.random() * 255)); + } + return payloadBuffer; + } } diff --git a/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/UtilsTest.java b/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/UtilsTest.java index adfbc366..e2a257a2 100644 --- a/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/UtilsTest.java +++ b/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/UtilsTest.java @@ -7,6 +7,8 @@ import org.junit.Before; import org.junit.Test; import static org.junit.Assert.*; import static org.dna.mqtt.moquette.parser.netty.TestUtils.*; +import org.dna.mqtt.moquette.proto.messages.AbstractMessage; +import org.dna.mqtt.moquette.proto.messages.UnsubscribeMessage; /** * @@ -78,4 +80,14 @@ public class UtilsTest { 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 testEncodeFlags() { + UnsubscribeMessage msg = new UnsubscribeMessage(); + msg.setRetainFlag(true); + msg.setQos(AbstractMessage.QOSType.MOST_ONE); + + //Exercise + assertEquals(1, Utils.encodeFlags(msg)); + } } From facec04ac2410643ebee5d58d43df2fe3cddd5cb Mon Sep 17 00:00:00 2001 From: andrea Date: Thu, 12 Sep 2013 14:30:25 +0200 Subject: [PATCH 17/41] Minor cleaned imports --- .../test/java/org/dna/mqtt/moquette/proto/PubAckEncoderTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/parser/src/test/java/org/dna/mqtt/moquette/proto/PubAckEncoderTest.java b/parser/src/test/java/org/dna/mqtt/moquette/proto/PubAckEncoderTest.java index 0a690be8..e7aa6168 100644 --- a/parser/src/test/java/org/dna/mqtt/moquette/proto/PubAckEncoderTest.java +++ b/parser/src/test/java/org/dna/mqtt/moquette/proto/PubAckEncoderTest.java @@ -1,6 +1,5 @@ package org.dna.mqtt.moquette.proto; -import org.dna.mqtt.moquette.proto.PubAckEncoder; import org.dna.mqtt.moquette.proto.messages.PubAckMessage; import org.junit.Test; import static org.junit.Assert.*; From 1b1cc715be90804a8120298d43989308906d2166 Mon Sep 17 00:00:00 2001 From: andrea Date: Thu, 12 Sep 2013 14:45:04 +0200 Subject: [PATCH 18/41] Added encode/decode for PubAck message --- .../moquette/parser/netty/ConnectDecoder.java | 2 +- .../moquette/parser/netty/MQTTDecoder.java | 1 + .../moquette/parser/netty/MQTTEncoder.java | 1 + .../parser/netty/MessageIDDecoder.java | 31 +++++++++++ .../moquette/parser/netty/PubAckDecoder.java | 17 +++++++ .../moquette/parser/netty/PubAckEncoder.java | 23 +++++++++ .../moquette/parser/netty/PublishDecoder.java | 2 +- .../dna/mqtt/moquette/parser/netty/Utils.java | 2 +- .../parser/netty/PubAckDecoderTest.java | 51 +++++++++++++++++++ .../parser/netty/PubAckEncoderTest.java | 42 +++++++++++++++ 10 files changed, 169 insertions(+), 3 deletions(-) create mode 100644 netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MessageIDDecoder.java create mode 100644 netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/PubAckDecoder.java create mode 100644 netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/PubAckEncoder.java create mode 100644 netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/PubAckDecoderTest.java create mode 100644 netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/PubAckEncoderTest.java diff --git a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/ConnectDecoder.java b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/ConnectDecoder.java index a21b1f53..3ecbcf25 100644 --- a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/ConnectDecoder.java +++ b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/ConnectDecoder.java @@ -70,7 +70,7 @@ public class ConnectDecoder extends DemuxDecoder { //Keep Alive timer 2 bytes //int keepAlive = Utils.readWord(in); - int keepAlive = in.readShort(); + int keepAlive = in.readUnsignedShort(); message.setKeepAlive(keepAlive); if (remainingLength == 12) { diff --git a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTDecoder.java b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTDecoder.java index 8cfe6e9d..469c0bac 100644 --- a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTDecoder.java +++ b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTDecoder.java @@ -21,6 +21,7 @@ public class MQTTDecoder extends ByteToMessageDecoder { m_decoderMap.put(AbstractMessage.CONNECT, new ConnectDecoder()); m_decoderMap.put(AbstractMessage.CONNACK, new ConnAckDecoder()); m_decoderMap.put(AbstractMessage.PUBLISH, new PublishDecoder()); + m_decoderMap.put(AbstractMessage.PUBACK, new PubAckDecoder()); } @Override diff --git a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTEncoder.java b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTEncoder.java index d640fc71..d34892f2 100644 --- a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTEncoder.java +++ b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTEncoder.java @@ -20,6 +20,7 @@ public class MQTTEncoder extends MessageToByteEncoder { m_encoderMap.put(AbstractMessage.CONNECT, new ConnectEncoder()); m_encoderMap.put(AbstractMessage.CONNACK, new ConnAckEncoder()); m_encoderMap.put(AbstractMessage.PUBLISH, new PublishEncoder()); + m_encoderMap.put(AbstractMessage.PUBACK, new PubAckEncoder()); } @Override diff --git a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MessageIDDecoder.java b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MessageIDDecoder.java new file mode 100644 index 00000000..41a71164 --- /dev/null +++ b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MessageIDDecoder.java @@ -0,0 +1,31 @@ +package org.dna.mqtt.moquette.parser.netty; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import java.util.List; +import org.dna.mqtt.moquette.proto.messages.MessageIDMessage; + +/** + * + * @author andrea + */ +abstract class MessageIDDecoder extends DemuxDecoder { + + protected abstract MessageIDMessage createMessage(); + + @Override + void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception { + in.resetReaderIndex(); + //Common decoding part + MessageIDMessage message = createMessage(); + if (!decodeCommonHeader(message, in)) { + in.resetReaderIndex(); + return; + } + + //read messageIDs + message.setMessageID(in.readUnsignedShort()); + out.add(message); + } + +} diff --git a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/PubAckDecoder.java b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/PubAckDecoder.java new file mode 100644 index 00000000..7aa20ee2 --- /dev/null +++ b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/PubAckDecoder.java @@ -0,0 +1,17 @@ +package org.dna.mqtt.moquette.parser.netty; + +import org.dna.mqtt.moquette.proto.messages.MessageIDMessage; +import org.dna.mqtt.moquette.proto.messages.PubAckMessage; + +/** + * + * @author andrea + */ +class PubAckDecoder extends MessageIDDecoder { + + @Override + protected MessageIDMessage createMessage() { + return new PubAckMessage(); + } + +} diff --git a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/PubAckEncoder.java b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/PubAckEncoder.java new file mode 100644 index 00000000..56d608a4 --- /dev/null +++ b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/PubAckEncoder.java @@ -0,0 +1,23 @@ +package org.dna.mqtt.moquette.parser.netty; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import org.dna.mqtt.moquette.proto.messages.AbstractMessage; +import org.dna.mqtt.moquette.proto.messages.PubAckMessage; + +/** + * + * @author andrea + */ +class PubAckEncoder extends DemuxEncoder { + + @Override + protected void encode(ChannelHandlerContext chc, PubAckMessage msg, ByteBuf out) { + ByteBuf buff = chc.alloc().buffer(4); + buff.writeByte(AbstractMessage.PUBACK << 4); + buff.writeBytes(Utils.encodeRemainingLength(2)); + buff.writeShort(msg.getMessageID()); + out.writeBytes(buff); + } + +} diff --git a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/PublishDecoder.java b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/PublishDecoder.java index 876252c6..9a29ff14 100644 --- a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/PublishDecoder.java +++ b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/PublishDecoder.java @@ -41,7 +41,7 @@ class PublishDecoder extends DemuxDecoder { if (message.getQos() == AbstractMessage.QOSType.LEAST_ONE || message.getQos() == AbstractMessage.QOSType.EXACTLY_ONCE) { - message.setMessageID((int)in.readShort()); + message.setMessageID(in.readUnsignedShort()); } int stopPos = in.readerIndex(); diff --git a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/Utils.java b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/Utils.java index 1252e22c..68bb0a53 100644 --- a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/Utils.java +++ b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/Utils.java @@ -101,7 +101,7 @@ public class Utils { return null; } //int strLen = Utils.readWord(in); - int strLen = in.readShort(); + int strLen = in.readUnsignedShort(); if (in.readableBytes() < strLen) { return null; } diff --git a/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/PubAckDecoderTest.java b/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/PubAckDecoderTest.java new file mode 100644 index 00000000..0cea6581 --- /dev/null +++ b/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/PubAckDecoderTest.java @@ -0,0 +1,51 @@ +package org.dna.mqtt.moquette.parser.netty; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import java.util.ArrayList; +import java.util.List; +import org.dna.mqtt.moquette.proto.messages.AbstractMessage; +import org.dna.mqtt.moquette.proto.messages.PubAckMessage; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import org.junit.Before; +import org.junit.Test; + +/** + * + * @author andrea + */ +public class PubAckDecoderTest { + ByteBuf m_buff; + PubAckDecoder m_msgdec; + + @Before + public void setUp() { + m_msgdec = new PubAckDecoder(); + } + + @Test + public void testHeader() throws Exception { + m_buff = Unpooled.buffer(14); + int messageId = 0xAABB; + initHeader(m_buff, messageId); + List results = new ArrayList(); + + //Excercise + m_msgdec.decode(null, m_buff, results); + + assertFalse(results.isEmpty()); + PubAckMessage message = (PubAckMessage)results.get(0); + assertNotNull(message); + assertEquals(messageId, message.getMessageID().intValue()); + assertEquals(AbstractMessage.PUBACK, message.getMessageType()); + } + + private void initHeader(ByteBuf buff, int messageID) { + buff.clear().writeByte(AbstractMessage.PUBACK << 4).writeByte(2); + + //return code + buff.writeShort(messageID); + } +} diff --git a/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/PubAckEncoderTest.java b/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/PubAckEncoderTest.java new file mode 100644 index 00000000..9b83c285 --- /dev/null +++ b/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/PubAckEncoderTest.java @@ -0,0 +1,42 @@ +package org.dna.mqtt.moquette.parser.netty; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.ChannelHandlerContext; +import org.junit.Test; +import org.junit.Before; +import static org.junit.Assert.*; +import static org.dna.mqtt.moquette.parser.netty.TestUtils.*; +import org.dna.mqtt.moquette.proto.messages.PubAckMessage; + +/** + * + * @author andrea + */ +public class PubAckEncoderTest { + ChannelHandlerContext m_mockedContext; + + @Before + public void setUp() { + //mock the ChannelHandlerContext to return an UnpooledAllocator + m_mockedContext = mockChannelHandler(); + } + + @Test + public void testHeaderEncode() throws Exception { + int messageID = 0xAABB; + PubAckEncoder encoder = new PubAckEncoder(); + PubAckMessage msg = new PubAckMessage(); + msg.setMessageID(messageID); + ByteBuf out = Unpooled.buffer(); + + //Exercise + encoder.encode(m_mockedContext, msg, out); + + //Verify + assertEquals(0x40, out.readByte()); //1 byte + assertEquals(0x02, out.readByte()); //2 byte, length + assertEquals((byte)0xAA, out.readByte()); + assertEquals((byte)0xBB, out.readByte()); + } +} From c3f5a69d68e3e148971687021b747ec82b5444d6 Mon Sep 17 00:00:00 2001 From: andrea Date: Thu, 12 Sep 2013 15:16:55 +0200 Subject: [PATCH 19/41] Added encode/decode for Subscribe message --- .../moquette/parser/netty/MQTTDecoder.java | 1 + .../moquette/parser/netty/MQTTEncoder.java | 1 + .../parser/netty/SubscribeDecoder.java | 53 +++++++++++++ .../parser/netty/SubscribeEncoder.java | 42 ++++++++++ .../parser/netty/SubscribeDecoderTest.java | 71 +++++++++++++++++ .../parser/netty/SubscribeEncoderTest.java | 78 +++++++++++++++++++ 6 files changed, 246 insertions(+) create mode 100644 netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/SubscribeDecoder.java create mode 100644 netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/SubscribeEncoder.java create mode 100644 netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/SubscribeDecoderTest.java create mode 100644 netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/SubscribeEncoderTest.java diff --git a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTDecoder.java b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTDecoder.java index 469c0bac..f7d2792f 100644 --- a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTDecoder.java +++ b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTDecoder.java @@ -22,6 +22,7 @@ public class MQTTDecoder extends ByteToMessageDecoder { m_decoderMap.put(AbstractMessage.CONNACK, new ConnAckDecoder()); m_decoderMap.put(AbstractMessage.PUBLISH, new PublishDecoder()); m_decoderMap.put(AbstractMessage.PUBACK, new PubAckDecoder()); + m_decoderMap.put(AbstractMessage.SUBSCRIBE, new SubscribeDecoder()); } @Override diff --git a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTEncoder.java b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTEncoder.java index d34892f2..3153f208 100644 --- a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTEncoder.java +++ b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTEncoder.java @@ -21,6 +21,7 @@ public class MQTTEncoder extends MessageToByteEncoder { m_encoderMap.put(AbstractMessage.CONNACK, new ConnAckEncoder()); m_encoderMap.put(AbstractMessage.PUBLISH, new PublishEncoder()); m_encoderMap.put(AbstractMessage.PUBACK, new PubAckEncoder()); + m_encoderMap.put(AbstractMessage.SUBSCRIBE, new SubscribeEncoder()); } @Override diff --git a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/SubscribeDecoder.java b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/SubscribeDecoder.java new file mode 100644 index 00000000..efdb84e4 --- /dev/null +++ b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/SubscribeDecoder.java @@ -0,0 +1,53 @@ +package org.dna.mqtt.moquette.parser.netty; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.CorruptedFrameException; +import java.io.UnsupportedEncodingException; +import java.util.List; +import org.dna.mqtt.moquette.proto.messages.AbstractMessage.QOSType; +import org.dna.mqtt.moquette.proto.messages.SubscribeMessage; + +/** + * + * @author andrea + */ +class SubscribeDecoder extends DemuxDecoder { + + @Override + void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception { + //Common decoding part + SubscribeMessage message = new SubscribeMessage(); + in.resetReaderIndex(); + if (!decodeCommonHeader(message, in)) { + in.resetReaderIndex(); + return; + } + + //check qos level + if (message.getQos() != QOSType.LEAST_ONE) { + throw new CorruptedFrameException("Received Subscribe message with QoS other than LEAST_ONE, was: " + message.getQos()); + } + + int start = in.readerIndex(); + //read messageIDs + message.setMessageID(in.readUnsignedShort()); + int readed = in.readerIndex() - start; + while (readed < message.getRemainingLength()) { + decodeSubscription(in, message); + readed = in.readerIndex()- start; + } + + out.add(message); + } + + /** + * Populate the message with couple of Qos, topic + */ + private void decodeSubscription(ByteBuf in, SubscribeMessage message) throws UnsupportedEncodingException { + String topic = Utils.decodeString(in); + byte qos = (byte)(in.readByte() & 0x03); + message.addSubscription(new SubscribeMessage.Couple(qos, topic)); + } + +} diff --git a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/SubscribeEncoder.java b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/SubscribeEncoder.java new file mode 100644 index 00000000..c97749f7 --- /dev/null +++ b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/SubscribeEncoder.java @@ -0,0 +1,42 @@ +package org.dna.mqtt.moquette.parser.netty; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import org.dna.mqtt.moquette.proto.messages.AbstractMessage; +import org.dna.mqtt.moquette.proto.messages.SubscribeMessage; + +/** + * + * @author andrea + */ +class SubscribeEncoder extends DemuxEncoder { + + @Override + protected void encode(ChannelHandlerContext chc, SubscribeMessage message, ByteBuf out) { + if (message.subscriptions().isEmpty()) { + throw new IllegalArgumentException("Found a subscribe message with empty topics"); + } + + if (message.getQos() != AbstractMessage.QOSType.LEAST_ONE) { + throw new IllegalArgumentException("Expected a message with QOS 1, found " + message.getQos()); + } + + ByteBuf variableHeaderBuff = chc.alloc().buffer(4); + variableHeaderBuff.writeShort(message.getMessageID()); + for (SubscribeMessage.Couple c : message.subscriptions()) { + variableHeaderBuff.writeBytes(Utils.encodeString(c.getTopic())); + variableHeaderBuff.writeByte(c.getQos()); + } + + int variableHeaderSize = variableHeaderBuff.readableBytes(); + byte flags = Utils.encodeFlags(message); + ByteBuf buff = chc.alloc().buffer(2 + variableHeaderSize); + + buff.writeByte(AbstractMessage.SUBSCRIBE << 4 | flags); + buff.writeBytes(Utils.encodeRemainingLength(variableHeaderSize)); + buff.writeBytes(variableHeaderBuff); + + out.writeBytes(buff); + } + +} diff --git a/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/SubscribeDecoderTest.java b/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/SubscribeDecoderTest.java new file mode 100644 index 00000000..f140adf3 --- /dev/null +++ b/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/SubscribeDecoderTest.java @@ -0,0 +1,71 @@ +package org.dna.mqtt.moquette.parser.netty; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.handler.codec.CorruptedFrameException; +import java.util.ArrayList; +import java.util.List; +import org.dna.mqtt.moquette.proto.messages.AbstractMessage; +import org.dna.mqtt.moquette.proto.messages.SubscribeMessage; +import static org.junit.Assert.*; +import org.junit.Before; +import org.junit.Test; + +/** + * + * @author andrea + */ +public class SubscribeDecoderTest { + ByteBuf m_buff; + SubscribeDecoder m_msgdec; + List m_results; + + @Before + public void setUp() { + m_msgdec = new SubscribeDecoder(); + m_results = new ArrayList(); + } + + @Test(expected = CorruptedFrameException.class) + public void testBadQos() throws Exception { + m_buff = Unpooled.buffer(2); + initHeaderBadQos(m_buff); + + //Excercise + m_msgdec.decode(null, m_buff, m_results); + } + + @Test + public void testMultiTopic() throws Exception { + m_buff = Unpooled.buffer(4); + SubscribeMessage.Couple c1 = new SubscribeMessage.Couple((byte)2, "a/b"); + SubscribeMessage.Couple c2 = new SubscribeMessage.Couple((byte)1, "c/d/e"); + initMultiTopic(m_buff, 123, c1, c2); + + //Excercise + m_msgdec.decode(null, m_buff, m_results); + + //Verify + assertFalse(m_results.isEmpty()); + SubscribeMessage message = (SubscribeMessage)m_results.get(0); + assertEquals(2, message.subscriptions().size()); + assertEquals(AbstractMessage.SUBSCRIBE, message.getMessageType()); + } + + private void initHeaderBadQos(ByteBuf buff) { + buff.clear().writeByte(AbstractMessage.SUBSCRIBE << 4).writeByte(0); + } + + private void initMultiTopic(ByteBuf buff, int messageID, SubscribeMessage.Couple... topics) throws IllegalAccessException { + ByteBuf topicBuffer = Unpooled.buffer(4); + topicBuffer.writeShort(messageID); + for (SubscribeMessage.Couple couple : topics) { + topicBuffer.writeBytes(Utils.encodeString(couple.getTopic())); + topicBuffer.writeByte(couple.getQos()); + } + + buff.clear().writeByte(AbstractMessage.SUBSCRIBE << 4 | (byte)0x02) + .writeBytes(Utils.encodeRemainingLength(topicBuffer.readableBytes())); + buff.writeBytes(topicBuffer); + } +} diff --git a/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/SubscribeEncoderTest.java b/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/SubscribeEncoderTest.java new file mode 100644 index 00000000..014abe23 --- /dev/null +++ b/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/SubscribeEncoderTest.java @@ -0,0 +1,78 @@ +package org.dna.mqtt.moquette.parser.netty; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.ChannelHandlerContext; +import org.dna.mqtt.moquette.proto.messages.AbstractMessage; +import org.dna.mqtt.moquette.proto.messages.SubscribeMessage; +import static org.junit.Assert.assertEquals; +import org.junit.Before; +import org.junit.Test; +import static org.dna.mqtt.moquette.parser.netty.TestUtils.*; + +/** + * + * @author andrea + */ +public class SubscribeEncoderTest { + SubscribeEncoder m_encoder = new SubscribeEncoder(); + ChannelHandlerContext m_mockedContext; + ByteBuf m_out; + + @Before + public void setUp() { + //mock the ChannelHandlerContext to return an UnpooledAllocator + m_mockedContext = mockChannelHandler(); + m_out = Unpooled.buffer(); + } + + @Test + public void testEncodeWithMultiTopic() throws Exception { + SubscribeMessage msg = new SubscribeMessage(); + msg.setQos(AbstractMessage.QOSType.LEAST_ONE); + msg.setMessageID(0xAABB); + + //variable part + SubscribeMessage.Couple c1 = new SubscribeMessage.Couple((byte)1, "a/b"); + SubscribeMessage.Couple c2 = new SubscribeMessage.Couple((byte)0, "a/b/c"); + msg.addSubscription(c1); + msg.addSubscription(c2); + + //Exercise + m_encoder.encode(m_mockedContext, msg, m_out); + + //Verify + assertEquals((byte)0x82, (byte)m_out.readByte()); //1 byte + assertEquals(16, m_out.readByte()); //remaining length + + //verify MessageID + assertEquals((byte)0xAA, m_out.readByte()); + assertEquals((byte)0xBB, m_out.readByte()); + + //Variable part + verifyString(c1.getTopic(), m_out); + assertEquals(c1.getQos(), m_out.readByte()); + verifyString(c2.getTopic(), m_out); + assertEquals(c2.getQos(), m_out.readByte()); + } + + @Test(expected = IllegalArgumentException.class) + public void testEncode_empty_subscription() throws Exception { + SubscribeMessage msg = new SubscribeMessage(); + msg.setQos(AbstractMessage.QOSType.LEAST_ONE); + msg.setMessageID(0xAABB); + + //Exercise + m_encoder.encode(m_mockedContext, msg, m_out); + } + + @Test(expected = IllegalArgumentException.class) + public void testEncode_badQos() throws Exception { + SubscribeMessage msg = new SubscribeMessage(); + msg.setQos(AbstractMessage.QOSType.EXACTLY_ONCE); + msg.setMessageID(0xAABB); + + //Exercise + m_encoder.encode(m_mockedContext, msg, m_out); + } +} From 9f2497e0f9745440b98a52146dbda46a6b4b74ac Mon Sep 17 00:00:00 2001 From: andrea Date: Thu, 12 Sep 2013 15:17:42 +0200 Subject: [PATCH 20/41] Minor cleaned imports --- .../test/java/org/dna/mqtt/moquette/proto/SubAckEncoderTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/parser/src/test/java/org/dna/mqtt/moquette/proto/SubAckEncoderTest.java b/parser/src/test/java/org/dna/mqtt/moquette/proto/SubAckEncoderTest.java index 5d681608..1cf00bd6 100644 --- a/parser/src/test/java/org/dna/mqtt/moquette/proto/SubAckEncoderTest.java +++ b/parser/src/test/java/org/dna/mqtt/moquette/proto/SubAckEncoderTest.java @@ -1,6 +1,5 @@ package org.dna.mqtt.moquette.proto; -import org.dna.mqtt.moquette.proto.SubAckEncoder; 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; From 2c9858c261b8c9d5fc8622e54af74a7b5be0c04c Mon Sep 17 00:00:00 2001 From: andrea Date: Thu, 12 Sep 2013 15:58:53 +0200 Subject: [PATCH 21/41] Added encode/decode for SubAck message --- .../moquette/parser/netty/MQTTDecoder.java | 1 + .../moquette/parser/netty/MQTTEncoder.java | 1 + .../moquette/parser/netty/SubAckDecoder.java | 43 ++++++++++ .../moquette/parser/netty/SubAckEncoder.java | 36 +++++++++ .../parser/netty/SubAckDecoderTest.java | 78 +++++++++++++++++++ .../parser/netty/SubAckEncoderTest.java | 62 +++++++++++++++ 6 files changed, 221 insertions(+) create mode 100644 netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/SubAckDecoder.java create mode 100644 netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/SubAckEncoder.java create mode 100644 netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/SubAckDecoderTest.java create mode 100644 netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/SubAckEncoderTest.java diff --git a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTDecoder.java b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTDecoder.java index f7d2792f..6730b745 100644 --- a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTDecoder.java +++ b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTDecoder.java @@ -23,6 +23,7 @@ public class MQTTDecoder extends ByteToMessageDecoder { m_decoderMap.put(AbstractMessage.PUBLISH, new PublishDecoder()); m_decoderMap.put(AbstractMessage.PUBACK, new PubAckDecoder()); m_decoderMap.put(AbstractMessage.SUBSCRIBE, new SubscribeDecoder()); + m_decoderMap.put(AbstractMessage.SUBACK, new SubAckDecoder()); } @Override diff --git a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTEncoder.java b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTEncoder.java index 3153f208..91d1a528 100644 --- a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTEncoder.java +++ b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTEncoder.java @@ -22,6 +22,7 @@ public class MQTTEncoder extends MessageToByteEncoder { m_encoderMap.put(AbstractMessage.PUBLISH, new PublishEncoder()); m_encoderMap.put(AbstractMessage.PUBACK, new PubAckEncoder()); m_encoderMap.put(AbstractMessage.SUBSCRIBE, new SubscribeEncoder()); + m_encoderMap.put(AbstractMessage.SUBACK, new SubAckEncoder()); } @Override diff --git a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/SubAckDecoder.java b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/SubAckDecoder.java new file mode 100644 index 00000000..50538e83 --- /dev/null +++ b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/SubAckDecoder.java @@ -0,0 +1,43 @@ +package org.dna.mqtt.moquette.parser.netty; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import java.util.List; +import org.dna.mqtt.moquette.proto.messages.AbstractMessage; +import org.dna.mqtt.moquette.proto.messages.SubAckMessage; + +/** + * + * @author andrea + */ +class SubAckDecoder extends DemuxDecoder { + + @Override + void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception { + //Common decoding part + in.resetReaderIndex(); + SubAckMessage message = new SubAckMessage(); + if (!decodeCommonHeader(message, in)) { + in.resetReaderIndex(); + return; + } + int remainingLength = message.getRemainingLength(); + + //MessageID + message.setMessageID(in.readUnsignedShort()); + remainingLength -= 2; + + //Qos array + if (in.readableBytes() < remainingLength ) { + in.resetReaderIndex(); + return; + } + for (int i = 0; i < remainingLength; i++) { + byte qos = in.readByte(); + message.addType(AbstractMessage.QOSType.values()[qos]); + } + + out.add(message); + } + +} diff --git a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/SubAckEncoder.java b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/SubAckEncoder.java new file mode 100644 index 00000000..6cb0dd48 --- /dev/null +++ b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/SubAckEncoder.java @@ -0,0 +1,36 @@ +package org.dna.mqtt.moquette.parser.netty; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import org.dna.mqtt.moquette.proto.messages.AbstractMessage; +import org.dna.mqtt.moquette.proto.messages.SubAckMessage; + +/** + * + * @author andrea + */ +class SubAckEncoder extends DemuxEncoder { + + @Override + protected void encode(ChannelHandlerContext chc, SubAckMessage message, ByteBuf out) { + if (message.types().isEmpty()) { + throw new IllegalArgumentException("Found a suback message with empty topics"); + } + + ByteBuf variableHeaderBuff = chc.alloc().buffer(4); + variableHeaderBuff.writeShort(message.getMessageID()); + for (AbstractMessage.QOSType c : message.types()) { + variableHeaderBuff.writeByte(c.ordinal()); + } + + int variableHeaderSize = variableHeaderBuff.readableBytes(); + ByteBuf buff = chc.alloc().buffer(2 + variableHeaderSize); + + buff.writeByte(AbstractMessage.SUBACK << 4 ); + buff.writeBytes(Utils.encodeRemainingLength(variableHeaderSize)); + buff.writeBytes(variableHeaderBuff); + + out.writeBytes(buff); + } + +} diff --git a/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/SubAckDecoderTest.java b/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/SubAckDecoderTest.java new file mode 100644 index 00000000..728344bc --- /dev/null +++ b/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/SubAckDecoderTest.java @@ -0,0 +1,78 @@ +package org.dna.mqtt.moquette.parser.netty; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import java.util.ArrayList; +import java.util.List; +import org.dna.mqtt.moquette.proto.messages.AbstractMessage; +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 { + ByteBuf m_buff; + SubAckDecoder m_msgdec; + List m_results; + + @Before + public void setUp() { + m_msgdec = new SubAckDecoder(); + m_results = new ArrayList(); + m_buff = Unpooled.buffer(7); + } + + @Test + public void testBadQos() throws Exception { + initHeaderQos(m_buff, 0xAABB, AbstractMessage.QOSType.LEAST_ONE, AbstractMessage.QOSType.MOST_ONE, AbstractMessage.QOSType.MOST_ONE); + + //Excercise + m_msgdec.decode(null, m_buff, m_results); + + //Verify + assertFalse(m_results.isEmpty()); + SubAckMessage message = (SubAckMessage)m_results.get(0); + assertNotNull(message); + assertEquals(0xAABB, message.getMessageID().intValue()); + List qoses = message.types(); + assertEquals(3, qoses.size()); + assertEquals(AbstractMessage.QOSType.LEAST_ONE, qoses.get(0)); + assertEquals(AbstractMessage.QOSType.MOST_ONE, qoses.get(1)); + assertEquals(AbstractMessage.QOSType.MOST_ONE, qoses.get(2)); + assertEquals(AbstractMessage.SUBACK, message.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 = Unpooled.buffer(overallMessage.length); + m_buff.writeBytes(overallMessage); + + //Exercise + m_msgdec.decode(null, m_buff, m_results); + + assertFalse(m_results.isEmpty()); + SubAckMessage message = (SubAckMessage)m_results.get(0); + assertNotNull(message); + assertEquals(0x0A, message.getMessageID().intValue()); + assertEquals(1, message.types().size()); + assertEquals(AbstractMessage.QOSType.LEAST_ONE, message.types().get(0)); + } + + private void initHeaderQos(ByteBuf buff, int messageID, AbstractMessage.QOSType... qoss) throws IllegalAccessException { + buff.clear().writeByte(AbstractMessage.SUBACK << 4). + writeBytes(Utils.encodeRemainingLength(2 + qoss.length)); + + buff.writeShort(messageID); + for (AbstractMessage.QOSType qos : qoss) { + buff.writeByte(qos.ordinal()); + } + } +} diff --git a/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/SubAckEncoderTest.java b/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/SubAckEncoderTest.java new file mode 100644 index 00000000..425fb78b --- /dev/null +++ b/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/SubAckEncoderTest.java @@ -0,0 +1,62 @@ +package org.dna.mqtt.moquette.parser.netty; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.ChannelHandlerContext; +import static org.dna.mqtt.moquette.parser.netty.TestUtils.mockChannelHandler; +import org.dna.mqtt.moquette.proto.messages.AbstractMessage; +import org.dna.mqtt.moquette.proto.messages.SubAckMessage; +import static org.junit.Assert.assertEquals; +import org.junit.Before; +import org.junit.Test; + +/** + * + * @author andrea + */ +public class SubAckEncoderTest { + SubAckEncoder m_encoder = new SubAckEncoder(); + ChannelHandlerContext m_mockedContext; + ByteBuf m_out; + + @Before + public void setUp() { + //mock the ChannelHandlerContext to return an UnpooledAllocator + m_mockedContext = mockChannelHandler(); + m_out = Unpooled.buffer(); + } + + @Test(expected = IllegalArgumentException.class) + public void testEncodeWithNoQoss() throws Exception { + SubAckMessage msg = new SubAckMessage(); + msg.setMessageID(123); + + //Exercise + m_encoder.encode(m_mockedContext, msg, m_out); + } + + @Test + public void testEncodeWithMultipleQos() throws Exception { + SubAckMessage msg = new SubAckMessage(); + + int messageID = 0xAABB; + msg.setMessageID(messageID); + msg.addType(AbstractMessage.QOSType.MOST_ONE); + msg.addType(AbstractMessage.QOSType.LEAST_ONE); + msg.addType(AbstractMessage.QOSType.EXACTLY_ONCE); + + //Exercise + m_encoder.encode(m_mockedContext, msg, m_out); + + //Verify + assertEquals((byte) (AbstractMessage.SUBACK << 4 ), m_out.readByte()); //1 byte + assertEquals(5, m_out.readByte()); //remaining length + + //Variable part + assertEquals((byte)0xAA, m_out.readByte()); //MessageID MSB + assertEquals((byte)0xBB, m_out.readByte()); //MessageID LSB + assertEquals((byte)AbstractMessage.QOSType.MOST_ONE.ordinal(), m_out.readByte()); + assertEquals((byte)AbstractMessage.QOSType.LEAST_ONE.ordinal(), m_out.readByte()); + assertEquals((byte)AbstractMessage.QOSType.EXACTLY_ONCE.ordinal(), m_out.readByte()); + } +} From 58017f45a16016c120c22f1ed32d20b30f96d8e1 Mon Sep 17 00:00:00 2001 From: andrea Date: Thu, 12 Sep 2013 16:01:40 +0200 Subject: [PATCH 22/41] Minor cleaned imports --- .../java/org/dna/mqtt/moquette/proto/UnsubscribeDecoder.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/parser/src/main/java/org/dna/mqtt/moquette/proto/UnsubscribeDecoder.java b/parser/src/main/java/org/dna/mqtt/moquette/proto/UnsubscribeDecoder.java index 7cd85df2..6fa8f028 100644 --- a/parser/src/main/java/org/dna/mqtt/moquette/proto/UnsubscribeDecoder.java +++ b/parser/src/main/java/org/dna/mqtt/moquette/proto/UnsubscribeDecoder.java @@ -1,13 +1,11 @@ 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; import org.dna.mqtt.moquette.proto.messages.UnsubscribeMessage; /** From daa637c557ba95bf47e6e37ee0febfadc6a68474 Mon Sep 17 00:00:00 2001 From: andrea Date: Thu, 12 Sep 2013 16:14:51 +0200 Subject: [PATCH 23/41] Minor cleaned imports --- .../java/org/dna/mqtt/moquette/proto/UnsubscribeEncoderTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/parser/src/test/java/org/dna/mqtt/moquette/proto/UnsubscribeEncoderTest.java b/parser/src/test/java/org/dna/mqtt/moquette/proto/UnsubscribeEncoderTest.java index 4a4fb114..2d81e77a 100644 --- a/parser/src/test/java/org/dna/mqtt/moquette/proto/UnsubscribeEncoderTest.java +++ b/parser/src/test/java/org/dna/mqtt/moquette/proto/UnsubscribeEncoderTest.java @@ -1,6 +1,5 @@ package org.dna.mqtt.moquette.proto; -import org.dna.mqtt.moquette.proto.UnsubscribeEncoder; import org.dna.mqtt.moquette.proto.messages.UnsubscribeMessage; import org.dna.mqtt.moquette.proto.messages.AbstractMessage.QOSType; import org.junit.Before; From 52402536d99336dd00dae114fa60bab2233bbebb Mon Sep 17 00:00:00 2001 From: andrea Date: Thu, 12 Sep 2013 16:28:51 +0200 Subject: [PATCH 24/41] Added encode/decode for Unsubscribe message --- .../moquette/parser/netty/MQTTDecoder.java | 1 + .../moquette/parser/netty/MQTTEncoder.java | 1 + .../parser/netty/UnsubscribeDecoder.java | 43 ++++++++ .../parser/netty/UnsubscribeEncoder.java | 42 ++++++++ .../parser/netty/UnsubscribeDecoderTest.java | 95 ++++++++++++++++++ .../parser/netty/UnsubscribeEncoderTest.java | 99 +++++++++++++++++++ 6 files changed, 281 insertions(+) create mode 100644 netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/UnsubscribeDecoder.java create mode 100644 netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/UnsubscribeEncoder.java create mode 100644 netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/UnsubscribeDecoderTest.java create mode 100644 netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/UnsubscribeEncoderTest.java diff --git a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTDecoder.java b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTDecoder.java index 6730b745..fc5659eb 100644 --- a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTDecoder.java +++ b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTDecoder.java @@ -24,6 +24,7 @@ public class MQTTDecoder extends ByteToMessageDecoder { m_decoderMap.put(AbstractMessage.PUBACK, new PubAckDecoder()); m_decoderMap.put(AbstractMessage.SUBSCRIBE, new SubscribeDecoder()); m_decoderMap.put(AbstractMessage.SUBACK, new SubAckDecoder()); + m_decoderMap.put(AbstractMessage.UNSUBSCRIBE, new UnsubscribeDecoder()); } @Override diff --git a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTEncoder.java b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTEncoder.java index 91d1a528..92b8620b 100644 --- a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTEncoder.java +++ b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTEncoder.java @@ -23,6 +23,7 @@ public class MQTTEncoder extends MessageToByteEncoder { m_encoderMap.put(AbstractMessage.PUBACK, new PubAckEncoder()); m_encoderMap.put(AbstractMessage.SUBSCRIBE, new SubscribeEncoder()); m_encoderMap.put(AbstractMessage.SUBACK, new SubAckEncoder()); + m_encoderMap.put(AbstractMessage.UNSUBSCRIBE, new UnsubscribeEncoder()); } @Override diff --git a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/UnsubscribeDecoder.java b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/UnsubscribeDecoder.java new file mode 100644 index 00000000..6aba89e2 --- /dev/null +++ b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/UnsubscribeDecoder.java @@ -0,0 +1,43 @@ +package org.dna.mqtt.moquette.parser.netty; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.CorruptedFrameException; +import java.util.List; +import org.dna.mqtt.moquette.proto.messages.AbstractMessage; +import org.dna.mqtt.moquette.proto.messages.UnsubscribeMessage; + +/** + * + * @author andrea + */ +class UnsubscribeDecoder extends DemuxDecoder { + + @Override + void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception { + //Common decoding part + in.resetReaderIndex(); + UnsubscribeMessage message = new UnsubscribeMessage(); + if (!decodeCommonHeader(message, in)) { + in.resetReaderIndex(); + return; + } + + //check qos level + if (message.getQos() != AbstractMessage.QOSType.LEAST_ONE) { + throw new CorruptedFrameException("Found an Usubscribe message with qos other than LEAST_ONE, was: " + message.getQos()); + } + + int start = in.readerIndex(); + //read messageIDs + message.setMessageID(in.readUnsignedShort()); + int readed = in.readerIndex()- start; + while (readed < message.getRemainingLength()) { + message.addTopic(Utils.decodeString(in)); + readed = in.readerIndex()- start; + } + + out.add(message); + } + +} diff --git a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/UnsubscribeEncoder.java b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/UnsubscribeEncoder.java new file mode 100644 index 00000000..3800264a --- /dev/null +++ b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/UnsubscribeEncoder.java @@ -0,0 +1,42 @@ +package org.dna.mqtt.moquette.parser.netty; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import org.dna.mqtt.moquette.proto.messages.AbstractMessage; +import org.dna.mqtt.moquette.proto.messages.UnsubscribeMessage; + + +/** + * + * @author andrea + */ +class UnsubscribeEncoder extends DemuxEncoder { + + @Override + protected void encode(ChannelHandlerContext chc, UnsubscribeMessage message, ByteBuf out) { + if (message.topics().isEmpty()) { + throw new IllegalArgumentException("Found an unsubscribe message with empty topics"); + } + + if (message.getQos() != AbstractMessage.QOSType.LEAST_ONE) { + throw new IllegalArgumentException("Expected a message with QOS 1, found " + message.getQos()); + } + + ByteBuf variableHeaderBuff = chc.alloc().buffer(4); + variableHeaderBuff.writeShort(message.getMessageID()); + for (String topic : message.topics()) { + variableHeaderBuff.writeBytes(Utils.encodeString(topic)); + } + + int variableHeaderSize = variableHeaderBuff.readableBytes(); + byte flags = Utils.encodeFlags(message); + ByteBuf buff = chc.alloc().buffer(2 + variableHeaderSize); + + buff.writeByte(AbstractMessage.UNSUBSCRIBE << 4 | flags); + buff.writeBytes(Utils.encodeRemainingLength(variableHeaderSize)); + buff.writeBytes(variableHeaderBuff); + + out.writeBytes(buff); + } + +} diff --git a/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/UnsubscribeDecoderTest.java b/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/UnsubscribeDecoderTest.java new file mode 100644 index 00000000..8e465ea5 --- /dev/null +++ b/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/UnsubscribeDecoderTest.java @@ -0,0 +1,95 @@ +package org.dna.mqtt.moquette.parser.netty; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.handler.codec.CorruptedFrameException; +import java.util.ArrayList; +import java.util.List; +import org.dna.mqtt.moquette.proto.messages.AbstractMessage; +import org.dna.mqtt.moquette.proto.messages.SubscribeMessage; +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 { + ByteBuf m_buff; + UnsubscribeDecoder m_msgdec; + List m_results; + + @Before + public void setUp() { + m_msgdec = new UnsubscribeDecoder(); + m_results = new ArrayList(); + } + + + @Test(expected = CorruptedFrameException.class) + public void testBadQos() throws Exception { + m_buff = Unpooled.buffer(2); + initHeaderBadQos(m_buff); + + //Excercise + m_msgdec.decode(null, m_buff, m_results); + } + + @Test + public void testMultiTopic() throws Exception { + m_buff = Unpooled.buffer(4); + String topic1 = "a/b"; + String topic2 = "c/d/e"; + initMultiTopic(m_buff, 123, topic1, topic2); + + //Excercise + m_msgdec.decode(null, m_buff, m_results); + + //Verify + assertFalse(m_results.isEmpty()); + UnsubscribeMessage message = (UnsubscribeMessage)m_results.get(0); + assertNotNull(message); + assertEquals(2, message.topics().size()); + assertEquals(topic1, message.topics().get(0)); + assertEquals(topic2, message.topics().get(1)); + assertEquals(AbstractMessage.UNSUBSCRIBE, message.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 = Unpooled.buffer(overallMessage.length); + m_buff.writeBytes(overallMessage); + + m_msgdec.decode(null, m_buff, m_results); + + assertFalse(m_results.isEmpty()); + UnsubscribeMessage message = (UnsubscribeMessage)m_results.get(0); + assertNotNull(message); + assertEquals(0x01, (int)message.getMessageID()); + } + + + private void initHeaderBadQos(ByteBuf buff) { + buff.clear().writeByte(AbstractMessage.UNSUBSCRIBE << 4).writeByte(0); + } + + private void initMultiTopic(ByteBuf buff, int messageID, String... topics) throws IllegalAccessException { + ByteBuf topicBuffer = Unpooled.buffer(4); + topicBuffer.writeShort(messageID); + for (String topic : topics) { + topicBuffer.writeBytes(Utils.encodeString(topic)); + } + + buff.clear().writeByte(AbstractMessage.UNSUBSCRIBE << 4 | (byte)0x02) + .writeBytes(Utils.encodeRemainingLength(topicBuffer.readableBytes())); + buff.writeBytes(topicBuffer); + } + +} diff --git a/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/UnsubscribeEncoderTest.java b/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/UnsubscribeEncoderTest.java new file mode 100644 index 00000000..7e900549 --- /dev/null +++ b/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/UnsubscribeEncoderTest.java @@ -0,0 +1,99 @@ +package org.dna.mqtt.moquette.parser.netty; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.ChannelHandlerContext; +import static org.dna.mqtt.moquette.parser.netty.TestUtils.mockChannelHandler; +import org.dna.mqtt.moquette.proto.messages.AbstractMessage; +import org.dna.mqtt.moquette.proto.messages.UnsubscribeMessage; +import static org.junit.Assert.assertEquals; +import org.junit.Before; +import org.junit.Test; +import static org.dna.mqtt.moquette.parser.netty.TestUtils.*; + +/** + * + * @author andrea + */ +public class UnsubscribeEncoderTest { + UnsubscribeEncoder m_encoder = new UnsubscribeEncoder(); + ChannelHandlerContext m_mockedContext; + ByteBuf m_out; + UnsubscribeMessage m_msg; + + @Before + public void setUp() { + //mock the ChannelHandlerContext to return an UnpooledAllocator + m_mockedContext = mockChannelHandler(); + m_out = Unpooled.buffer(); + m_msg = new UnsubscribeMessage(); + m_msg.setMessageID(0xAABB); + } + + @Test + public void testEncodeWithSingleTopic() throws Exception { + m_msg.setQos(AbstractMessage.QOSType.LEAST_ONE); + + //variable part + String topic1 = "/topic"; + m_msg.addTopic(topic1); + + //Exercise + m_encoder.encode(m_mockedContext, m_msg, m_out); + + //Verify + assertEquals((byte)0xA2, (byte)m_out.readByte()); //1 byte + //2 messageID + 2 length + 6 chars = 10 + assertEquals(10, m_out.readByte()); //remaining length + + //verify M1ssageID + assertEquals((byte)0xAA, m_out.readByte()); + assertEquals((byte)0xBB, m_out.readByte()); + + //Variable part + verifyString(topic1, m_out); + } + + + @Test + public void testEncodeWithMultiTopic() throws Exception { + m_msg.setQos(AbstractMessage.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(m_mockedContext, m_msg, m_out); + + //Verify + assertEquals((byte)0xA2, (byte)m_out.readByte()); //1 byte + assertEquals(14, m_out.readByte()); //remaining length + + //verify M1ssageID + assertEquals((byte)0xAA, m_out.readByte()); + assertEquals((byte)0xBB, m_out.readByte()); + + //Variable part + verifyString(topic1, m_out); + verifyString(topic2, m_out); + } + + @Test(expected = IllegalArgumentException.class) + public void testEncode_empty_topics() throws Exception { + m_msg.setQos(AbstractMessage.QOSType.LEAST_ONE); + + //Exercise + m_encoder.encode(m_mockedContext, m_msg, m_out); + } + + @Test(expected = IllegalArgumentException.class) + public void testEncode_badQos() throws Exception { + m_msg.setQos(AbstractMessage.QOSType.EXACTLY_ONCE); + + //Exercise + m_encoder.encode(m_mockedContext, m_msg, m_out); + } +} From ef8f794b934a207c3416d18435ffe942764d3e33 Mon Sep 17 00:00:00 2001 From: andrea Date: Thu, 12 Sep 2013 16:44:06 +0200 Subject: [PATCH 25/41] Added encode/decode for Disconnect message --- .../parser/netty/DisconnectDecoder.java | 26 +++++++++++++++++++ .../parser/netty/DisconnectEncoder.java | 19 ++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/DisconnectDecoder.java create mode 100644 netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/DisconnectEncoder.java diff --git a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/DisconnectDecoder.java b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/DisconnectDecoder.java new file mode 100644 index 00000000..3ef795fe --- /dev/null +++ b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/DisconnectDecoder.java @@ -0,0 +1,26 @@ +package org.dna.mqtt.moquette.parser.netty; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import java.util.List; +import org.dna.mqtt.moquette.proto.messages.DisconnectMessage; + +/** + * + * @author andrea + */ +class DisconnectDecoder extends DemuxDecoder { + + @Override + void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception { + //Common decoding part + in.resetReaderIndex(); + DisconnectMessage message = new DisconnectMessage(); + if (!decodeCommonHeader(message, in)) { + in.resetReaderIndex(); + return; + } + out.add(message); + } + +} diff --git a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/DisconnectEncoder.java b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/DisconnectEncoder.java new file mode 100644 index 00000000..d5fa9cc5 --- /dev/null +++ b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/DisconnectEncoder.java @@ -0,0 +1,19 @@ +package org.dna.mqtt.moquette.parser.netty; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import org.dna.mqtt.moquette.proto.messages.AbstractMessage; +import org.dna.mqtt.moquette.proto.messages.DisconnectMessage; + +/** + * + * @author andrea + */ +public class DisconnectEncoder extends DemuxEncoder { + + @Override + protected void encode(ChannelHandlerContext chc, DisconnectMessage msg, ByteBuf out) { + out.writeByte(AbstractMessage.DISCONNECT << 4).writeByte(0); + } + +} From 3fa3705d550d947ae58d0b6b43cee5d7893dd8c4 Mon Sep 17 00:00:00 2001 From: andrea Date: Thu, 12 Sep 2013 16:50:36 +0200 Subject: [PATCH 26/41] Added encode/decode for PingReq message --- .../moquette/parser/netty/MQTTDecoder.java | 2 ++ .../moquette/parser/netty/MQTTEncoder.java | 2 ++ .../moquette/parser/netty/PingReqDecoder.java | 26 +++++++++++++++++++ .../moquette/parser/netty/PingReqEncoder.java | 19 ++++++++++++++ 4 files changed, 49 insertions(+) create mode 100644 netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/PingReqDecoder.java create mode 100644 netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/PingReqEncoder.java diff --git a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTDecoder.java b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTDecoder.java index fc5659eb..136c6b03 100644 --- a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTDecoder.java +++ b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTDecoder.java @@ -25,6 +25,8 @@ public class MQTTDecoder extends ByteToMessageDecoder { m_decoderMap.put(AbstractMessage.SUBSCRIBE, new SubscribeDecoder()); m_decoderMap.put(AbstractMessage.SUBACK, new SubAckDecoder()); m_decoderMap.put(AbstractMessage.UNSUBSCRIBE, new UnsubscribeDecoder()); + m_decoderMap.put(AbstractMessage.DISCONNECT, new DisconnectDecoder()); + m_decoderMap.put(AbstractMessage.PINGREQ, new PingReqDecoder()); } @Override diff --git a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTEncoder.java b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTEncoder.java index 92b8620b..f4333cb3 100644 --- a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTEncoder.java +++ b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTEncoder.java @@ -24,6 +24,8 @@ public class MQTTEncoder extends MessageToByteEncoder { m_encoderMap.put(AbstractMessage.SUBSCRIBE, new SubscribeEncoder()); m_encoderMap.put(AbstractMessage.SUBACK, new SubAckEncoder()); m_encoderMap.put(AbstractMessage.UNSUBSCRIBE, new UnsubscribeEncoder()); + m_encoderMap.put(AbstractMessage.DISCONNECT, new DisconnectEncoder()); + m_encoderMap.put(AbstractMessage.PINGREQ, new PingReqEncoder()); } @Override diff --git a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/PingReqDecoder.java b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/PingReqDecoder.java new file mode 100644 index 00000000..9bf8ff89 --- /dev/null +++ b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/PingReqDecoder.java @@ -0,0 +1,26 @@ +package org.dna.mqtt.moquette.parser.netty; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import java.util.List; +import org.dna.mqtt.moquette.parser.netty.DemuxDecoder; +import org.dna.mqtt.moquette.proto.messages.PingReqMessage; + +/** + * + * @author andrea + */ +class PingReqDecoder extends DemuxDecoder { + + @Override + void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception { + //Common decoding part + in.resetReaderIndex(); + PingReqMessage message = new PingReqMessage(); + if (!decodeCommonHeader(message, in)) { + in.resetReaderIndex(); + return; + } + out.add(message); + } +} diff --git a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/PingReqEncoder.java b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/PingReqEncoder.java new file mode 100644 index 00000000..c1b7d5e7 --- /dev/null +++ b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/PingReqEncoder.java @@ -0,0 +1,19 @@ +package org.dna.mqtt.moquette.parser.netty; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import org.dna.mqtt.moquette.parser.netty.DemuxEncoder; +import org.dna.mqtt.moquette.proto.messages.AbstractMessage; +import org.dna.mqtt.moquette.proto.messages.PingReqMessage; + +/** + * + * @author andrea + */ +class PingReqEncoder extends DemuxEncoder { + + @Override + protected void encode(ChannelHandlerContext chc, PingReqMessage msg, ByteBuf out) { + out.writeByte(AbstractMessage.PINGREQ << 4).writeByte(0); + } +} From 3066cfdd04e43c51a48824b0bac4f949952a6fb0 Mon Sep 17 00:00:00 2001 From: andrea Date: Thu, 12 Sep 2013 16:53:33 +0200 Subject: [PATCH 27/41] Added encode/decode for PingResp message --- .../moquette/parser/netty/MQTTDecoder.java | 1 + .../moquette/parser/netty/MQTTEncoder.java | 1 + .../parser/netty/PingRespDecoder.java | 25 +++++++++++++++++++ .../parser/netty/PingRespEncoder.java | 18 +++++++++++++ 4 files changed, 45 insertions(+) create mode 100644 netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/PingRespDecoder.java create mode 100644 netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/PingRespEncoder.java diff --git a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTDecoder.java b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTDecoder.java index 136c6b03..79d74027 100644 --- a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTDecoder.java +++ b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTDecoder.java @@ -27,6 +27,7 @@ public class MQTTDecoder extends ByteToMessageDecoder { m_decoderMap.put(AbstractMessage.UNSUBSCRIBE, new UnsubscribeDecoder()); m_decoderMap.put(AbstractMessage.DISCONNECT, new DisconnectDecoder()); m_decoderMap.put(AbstractMessage.PINGREQ, new PingReqDecoder()); + m_decoderMap.put(AbstractMessage.PINGRESP, new PingRespDecoder()); } @Override diff --git a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTEncoder.java b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTEncoder.java index f4333cb3..aea7e3d6 100644 --- a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTEncoder.java +++ b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTEncoder.java @@ -26,6 +26,7 @@ public class MQTTEncoder extends MessageToByteEncoder { m_encoderMap.put(AbstractMessage.UNSUBSCRIBE, new UnsubscribeEncoder()); m_encoderMap.put(AbstractMessage.DISCONNECT, new DisconnectEncoder()); m_encoderMap.put(AbstractMessage.PINGREQ, new PingReqEncoder()); + m_encoderMap.put(AbstractMessage.PINGRESP, new PingRespEncoder()); } @Override diff --git a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/PingRespDecoder.java b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/PingRespDecoder.java new file mode 100644 index 00000000..35d3b313 --- /dev/null +++ b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/PingRespDecoder.java @@ -0,0 +1,25 @@ +package org.dna.mqtt.moquette.parser.netty; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import java.util.List; +import org.dna.mqtt.moquette.proto.messages.PingRespMessage; + +/** + * + * @author andrea + */ +class PingRespDecoder extends DemuxDecoder { + + @Override + void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception { + //Common decoding part + in.resetReaderIndex(); + PingRespMessage message = new PingRespMessage(); + if (!decodeCommonHeader(message, in)) { + in.resetReaderIndex(); + return; + } + out.add(message); + } +} diff --git a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/PingRespEncoder.java b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/PingRespEncoder.java new file mode 100644 index 00000000..16833214 --- /dev/null +++ b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/PingRespEncoder.java @@ -0,0 +1,18 @@ +package org.dna.mqtt.moquette.parser.netty; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import org.dna.mqtt.moquette.proto.messages.AbstractMessage; +import org.dna.mqtt.moquette.proto.messages.PingRespMessage; + +/** + * + * @author andrea + */ +class PingRespEncoder extends DemuxEncoder { + + @Override + protected void encode(ChannelHandlerContext chc, PingRespMessage msg, ByteBuf out) { + out.writeByte(AbstractMessage.PINGRESP << 4).writeByte(0); + } +} From fa8a12a6f2a12c8c810076b6a50b34716e848a7d Mon Sep 17 00:00:00 2001 From: andrea Date: Thu, 12 Sep 2013 17:00:21 +0200 Subject: [PATCH 28/41] Added encode/decode for UnsubAck message --- .../moquette/parser/netty/MQTTDecoder.java | 1 + .../moquette/parser/netty/MQTTEncoder.java | 1 + .../parser/netty/UnsubAckDecoder.java | 17 ++++++++++++++++ .../parser/netty/UnsubAckEncoder.java | 20 +++++++++++++++++++ 4 files changed, 39 insertions(+) create mode 100644 netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/UnsubAckDecoder.java create mode 100644 netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/UnsubAckEncoder.java diff --git a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTDecoder.java b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTDecoder.java index 79d74027..804feb1f 100644 --- a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTDecoder.java +++ b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTDecoder.java @@ -28,6 +28,7 @@ public class MQTTDecoder extends ByteToMessageDecoder { m_decoderMap.put(AbstractMessage.DISCONNECT, new DisconnectDecoder()); m_decoderMap.put(AbstractMessage.PINGREQ, new PingReqDecoder()); m_decoderMap.put(AbstractMessage.PINGRESP, new PingRespDecoder()); + m_decoderMap.put(AbstractMessage.UNSUBACK, new UnsubAckDecoder()); } @Override diff --git a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTEncoder.java b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTEncoder.java index aea7e3d6..6d165fc9 100644 --- a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTEncoder.java +++ b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTEncoder.java @@ -27,6 +27,7 @@ public class MQTTEncoder extends MessageToByteEncoder { m_encoderMap.put(AbstractMessage.DISCONNECT, new DisconnectEncoder()); m_encoderMap.put(AbstractMessage.PINGREQ, new PingReqEncoder()); m_encoderMap.put(AbstractMessage.PINGRESP, new PingRespEncoder()); + m_encoderMap.put(AbstractMessage.UNSUBACK, new UnsubAckEncoder()); } @Override diff --git a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/UnsubAckDecoder.java b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/UnsubAckDecoder.java new file mode 100644 index 00000000..d0a73217 --- /dev/null +++ b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/UnsubAckDecoder.java @@ -0,0 +1,17 @@ +package org.dna.mqtt.moquette.parser.netty; + +import org.dna.mqtt.moquette.proto.messages.MessageIDMessage; +import org.dna.mqtt.moquette.proto.messages.UnsubAckMessage; + +/** + * + * @author andrea + */ +class UnsubAckDecoder extends MessageIDDecoder { + + @Override + protected MessageIDMessage createMessage() { + return new UnsubAckMessage(); + } +} + diff --git a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/UnsubAckEncoder.java b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/UnsubAckEncoder.java new file mode 100644 index 00000000..0f0d879b --- /dev/null +++ b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/UnsubAckEncoder.java @@ -0,0 +1,20 @@ +package org.dna.mqtt.moquette.parser.netty; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import org.dna.mqtt.moquette.proto.messages.AbstractMessage; +import org.dna.mqtt.moquette.proto.messages.UnsubAckMessage; + +/** + * + * @author andrea + */ +class UnsubAckEncoder extends DemuxEncoder { + + @Override + protected void encode(ChannelHandlerContext chc, UnsubAckMessage msg, ByteBuf out) { + out.writeByte(AbstractMessage.UNSUBACK << 4). + writeBytes(Utils.encodeRemainingLength(2)). + writeShort(msg.getMessageID()); + } +} \ No newline at end of file From dcffda5c4f0dae12ec86c50321936f21577f1014 Mon Sep 17 00:00:00 2001 From: andrea Date: Thu, 12 Sep 2013 17:08:20 +0200 Subject: [PATCH 29/41] Added encode/decode for PubComp message --- .../moquette/parser/netty/MQTTDecoder.java | 1 + .../moquette/parser/netty/MQTTEncoder.java | 1 + .../moquette/parser/netty/PubCompDecoder.java | 17 ++++++++++++++++ .../moquette/parser/netty/PubCompEncoder.java | 20 +++++++++++++++++++ 4 files changed, 39 insertions(+) create mode 100644 netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/PubCompDecoder.java create mode 100644 netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/PubCompEncoder.java diff --git a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTDecoder.java b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTDecoder.java index 804feb1f..c6fe90d7 100644 --- a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTDecoder.java +++ b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTDecoder.java @@ -29,6 +29,7 @@ public class MQTTDecoder extends ByteToMessageDecoder { m_decoderMap.put(AbstractMessage.PINGREQ, new PingReqDecoder()); m_decoderMap.put(AbstractMessage.PINGRESP, new PingRespDecoder()); m_decoderMap.put(AbstractMessage.UNSUBACK, new UnsubAckDecoder()); + m_decoderMap.put(AbstractMessage.PUBCOMP, new PubCompDecoder()); } @Override diff --git a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTEncoder.java b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTEncoder.java index 6d165fc9..935893bc 100644 --- a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTEncoder.java +++ b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTEncoder.java @@ -28,6 +28,7 @@ public class MQTTEncoder extends MessageToByteEncoder { m_encoderMap.put(AbstractMessage.PINGREQ, new PingReqEncoder()); m_encoderMap.put(AbstractMessage.PINGRESP, new PingRespEncoder()); m_encoderMap.put(AbstractMessage.UNSUBACK, new UnsubAckEncoder()); + m_encoderMap.put(AbstractMessage.PUBCOMP, new PubCompEncoder()); } @Override diff --git a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/PubCompDecoder.java b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/PubCompDecoder.java new file mode 100644 index 00000000..6ca4013d --- /dev/null +++ b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/PubCompDecoder.java @@ -0,0 +1,17 @@ +package org.dna.mqtt.moquette.parser.netty; + +import org.dna.mqtt.moquette.proto.messages.MessageIDMessage; +import org.dna.mqtt.moquette.proto.messages.PubCompMessage; + + +/** + * + * @author andrea + */ +class PubCompDecoder extends MessageIDDecoder { + + @Override + protected MessageIDMessage createMessage() { + return new PubCompMessage(); + } +} diff --git a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/PubCompEncoder.java b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/PubCompEncoder.java new file mode 100644 index 00000000..93bab459 --- /dev/null +++ b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/PubCompEncoder.java @@ -0,0 +1,20 @@ +package org.dna.mqtt.moquette.parser.netty; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import org.dna.mqtt.moquette.proto.messages.PubCompMessage; +import org.dna.mqtt.moquette.proto.messages.AbstractMessage; + +/** + * + * @author andrea + */ +class PubCompEncoder extends DemuxEncoder { + + @Override + protected void encode(ChannelHandlerContext chc, PubCompMessage msg, ByteBuf out) { + out.writeByte(AbstractMessage.PUBCOMP << 4); + out.writeBytes(Utils.encodeRemainingLength(2)); + out.writeShort(msg.getMessageID()); + } +} From 47bdb27261adb7377d9abe1bb6de97ee6cc87bb2 Mon Sep 17 00:00:00 2001 From: andrea Date: Thu, 12 Sep 2013 17:12:02 +0200 Subject: [PATCH 30/41] Added encode/decode for PubRec message --- .../moquette/parser/netty/MQTTDecoder.java | 1 + .../moquette/parser/netty/MQTTEncoder.java | 1 + .../moquette/parser/netty/PubRecDecoder.java | 16 +++++++++++++++ .../moquette/parser/netty/PubRecEncoder.java | 20 +++++++++++++++++++ 4 files changed, 38 insertions(+) create mode 100644 netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/PubRecDecoder.java create mode 100644 netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/PubRecEncoder.java diff --git a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTDecoder.java b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTDecoder.java index c6fe90d7..86acfc0c 100644 --- a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTDecoder.java +++ b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTDecoder.java @@ -30,6 +30,7 @@ public class MQTTDecoder extends ByteToMessageDecoder { m_decoderMap.put(AbstractMessage.PINGRESP, new PingRespDecoder()); m_decoderMap.put(AbstractMessage.UNSUBACK, new UnsubAckDecoder()); m_decoderMap.put(AbstractMessage.PUBCOMP, new PubCompDecoder()); + m_decoderMap.put(AbstractMessage.PUBREC, new PubRecDecoder()); } @Override diff --git a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTEncoder.java b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTEncoder.java index 935893bc..dee60c5b 100644 --- a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTEncoder.java +++ b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTEncoder.java @@ -29,6 +29,7 @@ public class MQTTEncoder extends MessageToByteEncoder { m_encoderMap.put(AbstractMessage.PINGRESP, new PingRespEncoder()); m_encoderMap.put(AbstractMessage.UNSUBACK, new UnsubAckEncoder()); m_encoderMap.put(AbstractMessage.PUBCOMP, new PubCompEncoder()); + m_encoderMap.put(AbstractMessage.PUBREC, new PubRecEncoder()); } @Override diff --git a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/PubRecDecoder.java b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/PubRecDecoder.java new file mode 100644 index 00000000..c6a01d0a --- /dev/null +++ b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/PubRecDecoder.java @@ -0,0 +1,16 @@ +package org.dna.mqtt.moquette.parser.netty; + +import org.dna.mqtt.moquette.proto.messages.MessageIDMessage; +import org.dna.mqtt.moquette.proto.messages.PubRecMessage; + +/** + * + * @author andrea + */ +class PubRecDecoder extends MessageIDDecoder { + + @Override + protected MessageIDMessage createMessage() { + return new PubRecMessage(); + } +} diff --git a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/PubRecEncoder.java b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/PubRecEncoder.java new file mode 100644 index 00000000..1aea2b06 --- /dev/null +++ b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/PubRecEncoder.java @@ -0,0 +1,20 @@ +package org.dna.mqtt.moquette.parser.netty; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import org.dna.mqtt.moquette.proto.messages.AbstractMessage; +import org.dna.mqtt.moquette.proto.messages.PubRecMessage; + +/** + * + * @author andrea + */ +class PubRecEncoder extends DemuxEncoder { + + @Override + protected void encode(ChannelHandlerContext chc, PubRecMessage msg, ByteBuf out) { + out.writeByte(AbstractMessage.PUBREC << 4); + out.writeBytes(Utils.encodeRemainingLength(2)); + out.writeShort(msg.getMessageID()); + } +} \ No newline at end of file From 6fb4682d8b450f13789a7d3d81091591f6881924 Mon Sep 17 00:00:00 2001 From: andrea Date: Thu, 12 Sep 2013 17:14:21 +0200 Subject: [PATCH 31/41] Added encode/decode for PubRel message --- .../moquette/parser/netty/MQTTDecoder.java | 1 + .../moquette/parser/netty/MQTTEncoder.java | 1 + .../moquette/parser/netty/PubRelDecoder.java | 18 +++++++++++++++++ .../moquette/parser/netty/PubRelEncoder.java | 20 +++++++++++++++++++ 4 files changed, 40 insertions(+) create mode 100644 netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/PubRelDecoder.java create mode 100644 netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/PubRelEncoder.java diff --git a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTDecoder.java b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTDecoder.java index 86acfc0c..645a2105 100644 --- a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTDecoder.java +++ b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTDecoder.java @@ -31,6 +31,7 @@ public class MQTTDecoder extends ByteToMessageDecoder { m_decoderMap.put(AbstractMessage.UNSUBACK, new UnsubAckDecoder()); m_decoderMap.put(AbstractMessage.PUBCOMP, new PubCompDecoder()); m_decoderMap.put(AbstractMessage.PUBREC, new PubRecDecoder()); + m_decoderMap.put(AbstractMessage.PUBREL, new PubRelDecoder()); } @Override diff --git a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTEncoder.java b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTEncoder.java index dee60c5b..d2585b19 100644 --- a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTEncoder.java +++ b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTEncoder.java @@ -30,6 +30,7 @@ public class MQTTEncoder extends MessageToByteEncoder { m_encoderMap.put(AbstractMessage.UNSUBACK, new UnsubAckEncoder()); m_encoderMap.put(AbstractMessage.PUBCOMP, new PubCompEncoder()); m_encoderMap.put(AbstractMessage.PUBREC, new PubRecEncoder()); + m_encoderMap.put(AbstractMessage.PUBREL, new PubRelEncoder()); } @Override diff --git a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/PubRelDecoder.java b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/PubRelDecoder.java new file mode 100644 index 00000000..271248fa --- /dev/null +++ b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/PubRelDecoder.java @@ -0,0 +1,18 @@ +package org.dna.mqtt.moquette.parser.netty; + +import org.dna.mqtt.moquette.proto.messages.MessageIDMessage; +import org.dna.mqtt.moquette.proto.messages.PubRelMessage; + +/** + * + * @author andrea + */ +class PubRelDecoder extends MessageIDDecoder { + + @Override + protected MessageIDMessage createMessage() { + return new PubRelMessage(); + } + +} + diff --git a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/PubRelEncoder.java b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/PubRelEncoder.java new file mode 100644 index 00000000..1029f6ef --- /dev/null +++ b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/PubRelEncoder.java @@ -0,0 +1,20 @@ +package org.dna.mqtt.moquette.parser.netty; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import org.dna.mqtt.moquette.proto.messages.AbstractMessage; +import org.dna.mqtt.moquette.proto.messages.PubRelMessage; + +/** + * + * @author andrea + */ +class PubRelEncoder extends DemuxEncoder { + + @Override + protected void encode(ChannelHandlerContext chc, PubRelMessage msg, ByteBuf out) { + out.writeByte(AbstractMessage.PUBREL << 4); + out.writeBytes(Utils.encodeRemainingLength(2)); + out.writeShort(msg.getMessageID()); + } +} \ No newline at end of file From 8ade5f68aa9e37ad09b94ac8b655ccc5c5603b3f Mon Sep 17 00:00:00 2001 From: andrea Date: Fri, 13 Sep 2013 10:24:04 +0200 Subject: [PATCH 32/41] Extracted front MINA connector --- .../org/dna/mqtt/moquette/server/Server.java | 98 +------------- .../mqtt/moquette/server/ServerAcceptor.java | 15 +++ .../moquette/server/mina/MinaAcceptor.java | 122 ++++++++++++++++++ 3 files changed, 142 insertions(+), 93 deletions(-) create mode 100644 broker/src/main/java/org/dna/mqtt/moquette/server/ServerAcceptor.java create mode 100644 broker/src/main/java/org/dna/mqtt/moquette/server/mina/MinaAcceptor.java diff --git a/broker/src/main/java/org/dna/mqtt/moquette/server/Server.java b/broker/src/main/java/org/dna/mqtt/moquette/server/Server.java index ec0b6392..d4872f78 100644 --- a/broker/src/main/java/org/dna/mqtt/moquette/server/Server.java +++ b/broker/src/main/java/org/dna/mqtt/moquette/server/Server.java @@ -1,21 +1,9 @@ package org.dna.mqtt.moquette.server; -import org.dna.mqtt.moquette.proto.messages.PubCompMessage; import java.io.File; import java.io.IOException; -import java.net.InetSocketAddress; -import org.apache.mina.core.service.IoAcceptor; -import org.apache.mina.core.service.IoServiceStatistics; -import org.apache.mina.core.session.IdleStatus; -import org.apache.mina.core.session.IoSession; -import org.apache.mina.filter.codec.ProtocolCodecFilter; -import org.apache.mina.filter.codec.demux.DemuxingProtocolDecoder; -import org.apache.mina.filter.codec.demux.DemuxingProtocolEncoder; -import org.apache.mina.transport.socket.nio.NioSocketAcceptor; -import org.dna.mqtt.commons.Constants; import org.dna.mqtt.moquette.messaging.spi.impl.SimpleMessaging; -import org.dna.mqtt.moquette.proto.*; -import org.dna.mqtt.moquette.proto.messages.*; +import org.dna.mqtt.moquette.server.mina.MinaAcceptor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** @@ -26,17 +14,11 @@ public class Server { private static final Logger LOG = LoggerFactory.getLogger(Server.class); -// public static final int PORT = 1883; -// public static final int DEFAULT_CONNECT_TIMEOUT = 10; public static final String STORAGE_FILE_PATH = System.getProperty("user.home") + File.separator + "moquette_store.hawtdb"; - //TODO one notifier per outbound queue, else no serialization of message flow is granted!! - //private static final int NOTIFIER_POOL_SIZE = 1; - private IoAcceptor m_acceptor; + private ServerAcceptor m_acceptor; SimpleMessaging messaging; -// Thread messagingEventLoop; - //private ExecutorService m_notifierPool/* = Executors.newFixedThreadPool(NOTIFIER_POOL_SIZE)*/; public static void main(String[] args) throws IOException { final Server server = new Server(); @@ -52,87 +34,17 @@ public class Server { } public void startServer() throws IOException { - DemuxingProtocolDecoder decoder = new DemuxingProtocolDecoder(); - decoder.addMessageDecoder(new ConnectDecoder()); - decoder.addMessageDecoder(new PublishDecoder()); - decoder.addMessageDecoder(new PubAckDecoder()); - decoder.addMessageDecoder(new PubRelDecoder()); - decoder.addMessageDecoder(new PubRecDecoder()); - decoder.addMessageDecoder(new PubCompDecoder()); - decoder.addMessageDecoder(new SubscribeDecoder()); - decoder.addMessageDecoder(new UnsubscribeDecoder()); - decoder.addMessageDecoder(new DisconnectDecoder()); - decoder.addMessageDecoder(new PingReqDecoder()); - - DemuxingProtocolEncoder encoder = new DemuxingProtocolEncoder(); -// encoder.addMessageEncoder(ConnectMessage.class, new ConnectEncoder()); - encoder.addMessageEncoder(ConnAckMessage.class, new ConnAckEncoder()); - encoder.addMessageEncoder(SubAckMessage.class, new SubAckEncoder()); - encoder.addMessageEncoder(UnsubAckMessage.class, new UnsubAckEncoder()); - encoder.addMessageEncoder(PubAckMessage.class, new PubAckEncoder()); - encoder.addMessageEncoder(PubRecMessage.class, new PubRecEncoder()); - encoder.addMessageEncoder(PubCompMessage.class, new PubCompEncoder()); - encoder.addMessageEncoder(PubRelMessage.class, new PubRelEncoder()); - encoder.addMessageEncoder(PublishMessage.class, new PublishEncoder()); - encoder.addMessageEncoder(PingRespMessage.class, new PingRespEncoder()); - - m_acceptor = new NioSocketAcceptor(); - - m_acceptor.getFilterChain().addLast( "logger", new MQTTLoggingFilter("SERVER LOG") ); - m_acceptor.getFilterChain().addLast( "codec", new ProtocolCodecFilter(encoder, decoder)); - - MQTTHandler handler = new MQTTHandler(); messaging = SimpleMessaging.getInstance(); messaging.init(); - //TODO fix this hugly wiring - handler.setMessaging(messaging); -// messaging.setNotifier(handler); -// messagingEventLoop = new Thread(messaging); -// messagingEventLoop.setName("Event Loop" + System.currentTimeMillis()); -// messagingEventLoop.start(); - - m_acceptor.setHandler(handler); - ((NioSocketAcceptor)m_acceptor).setReuseAddress(true); - ((NioSocketAcceptor)m_acceptor).getSessionConfig().setReuseAddress(true); - m_acceptor.getSessionConfig().setReadBufferSize( 2048 ); - m_acceptor.getSessionConfig().setIdleTime( IdleStatus.BOTH_IDLE, Constants.DEFAULT_CONNECT_TIMEOUT ); - m_acceptor.getStatistics().setThroughputCalculationInterval(10); - m_acceptor.getStatistics().updateThroughput(System.currentTimeMillis()); - m_acceptor.bind( new InetSocketAddress(Constants.PORT) ); - LOG.info("Server binded"); - + m_acceptor = new MinaAcceptor(); + m_acceptor.initialize(messaging); } public void stopServer() { LOG.info("Server stopping..."); - messaging.stop(); - //messaging.stop(); -// messagingEventLoop.interrupt(); -// LOG.info("shutting down evet loop"); -// try { -// messagingEventLoop.join(); -// } catch (InterruptedException ex) { -// LOG.error(null, ex); -// } - - /*m_notifierPool.shutdown();*/ - - //log statistics - IoServiceStatistics statistics = m_acceptor.getStatistics(); - statistics.updateThroughput(System.currentTimeMillis()); - System.out.println(String.format("Total read bytes: %d, read throughtput: %f (b/s)", statistics.getReadBytes(), statistics.getReadBytesThroughput())); - System.out.println(String.format("Total read msgs: %d, read msg throughtput: %f (msg/s)", statistics.getReadMessages(), statistics.getReadMessagesThroughput())); - - for(IoSession session: m_acceptor.getManagedSessions().values()) { - if(session.isConnected() && !session.isClosing()){ - session.close(false); - } - } - - m_acceptor.unbind(); - m_acceptor.dispose(); + m_acceptor.close(); LOG.info("Server stopped"); } diff --git a/broker/src/main/java/org/dna/mqtt/moquette/server/ServerAcceptor.java b/broker/src/main/java/org/dna/mqtt/moquette/server/ServerAcceptor.java new file mode 100644 index 00000000..7ce5e549 --- /dev/null +++ b/broker/src/main/java/org/dna/mqtt/moquette/server/ServerAcceptor.java @@ -0,0 +1,15 @@ +package org.dna.mqtt.moquette.server; + +import java.io.IOException; +import org.dna.mqtt.moquette.messaging.spi.IMessaging; + +/** + * + * @author andrea + */ +public interface ServerAcceptor { + + void initialize(IMessaging messaging) throws IOException; + + void close(); +} diff --git a/broker/src/main/java/org/dna/mqtt/moquette/server/mina/MinaAcceptor.java b/broker/src/main/java/org/dna/mqtt/moquette/server/mina/MinaAcceptor.java new file mode 100644 index 00000000..324586aa --- /dev/null +++ b/broker/src/main/java/org/dna/mqtt/moquette/server/mina/MinaAcceptor.java @@ -0,0 +1,122 @@ +package org.dna.mqtt.moquette.server.mina; + +import java.io.IOException; +import java.net.InetSocketAddress; +import org.apache.mina.core.service.IoAcceptor; +import org.apache.mina.core.service.IoServiceStatistics; +import org.apache.mina.core.session.IdleStatus; +import org.apache.mina.core.session.IoSession; +import org.apache.mina.filter.codec.ProtocolCodecFilter; +import org.apache.mina.filter.codec.demux.DemuxingProtocolDecoder; +import org.apache.mina.filter.codec.demux.DemuxingProtocolEncoder; +import org.apache.mina.transport.socket.nio.NioSocketAcceptor; +import org.dna.mqtt.commons.Constants; +import org.dna.mqtt.moquette.messaging.spi.IMessaging; +import org.dna.mqtt.moquette.proto.ConnAckEncoder; +import org.dna.mqtt.moquette.proto.ConnectDecoder; +import org.dna.mqtt.moquette.proto.DisconnectDecoder; +import org.dna.mqtt.moquette.proto.MQTTLoggingFilter; +import org.dna.mqtt.moquette.proto.PingReqDecoder; +import org.dna.mqtt.moquette.proto.PingRespEncoder; +import org.dna.mqtt.moquette.proto.PubAckDecoder; +import org.dna.mqtt.moquette.proto.PubAckEncoder; +import org.dna.mqtt.moquette.proto.PubCompDecoder; +import org.dna.mqtt.moquette.proto.PubCompEncoder; +import org.dna.mqtt.moquette.proto.PubRecDecoder; +import org.dna.mqtt.moquette.proto.PubRecEncoder; +import org.dna.mqtt.moquette.proto.PubRelDecoder; +import org.dna.mqtt.moquette.proto.PubRelEncoder; +import org.dna.mqtt.moquette.proto.PublishDecoder; +import org.dna.mqtt.moquette.proto.PublishEncoder; +import org.dna.mqtt.moquette.proto.SubAckEncoder; +import org.dna.mqtt.moquette.proto.SubscribeDecoder; +import org.dna.mqtt.moquette.proto.UnsubAckEncoder; +import org.dna.mqtt.moquette.proto.UnsubscribeDecoder; +import org.dna.mqtt.moquette.proto.messages.ConnAckMessage; +import org.dna.mqtt.moquette.proto.messages.PingRespMessage; +import org.dna.mqtt.moquette.proto.messages.PubAckMessage; +import org.dna.mqtt.moquette.proto.messages.PubCompMessage; +import org.dna.mqtt.moquette.proto.messages.PubRecMessage; +import org.dna.mqtt.moquette.proto.messages.PubRelMessage; +import org.dna.mqtt.moquette.proto.messages.PublishMessage; +import org.dna.mqtt.moquette.proto.messages.SubAckMessage; +import org.dna.mqtt.moquette.proto.messages.UnsubAckMessage; +import org.dna.mqtt.moquette.server.MQTTHandler; +import org.dna.mqtt.moquette.server.ServerAcceptor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +/** + * + * @author andrea + */ +public class MinaAcceptor implements ServerAcceptor { + + private static final Logger LOG = LoggerFactory.getLogger(MinaAcceptor.class); + + private IoAcceptor m_acceptor; + + public void initialize(IMessaging messaging) throws IOException { + DemuxingProtocolDecoder decoder = new DemuxingProtocolDecoder(); + decoder.addMessageDecoder(new ConnectDecoder()); + decoder.addMessageDecoder(new PublishDecoder()); + decoder.addMessageDecoder(new PubAckDecoder()); + decoder.addMessageDecoder(new PubRelDecoder()); + decoder.addMessageDecoder(new PubRecDecoder()); + decoder.addMessageDecoder(new PubCompDecoder()); + decoder.addMessageDecoder(new SubscribeDecoder()); + decoder.addMessageDecoder(new UnsubscribeDecoder()); + decoder.addMessageDecoder(new DisconnectDecoder()); + decoder.addMessageDecoder(new PingReqDecoder()); + + DemuxingProtocolEncoder encoder = new DemuxingProtocolEncoder(); +// encoder.addMessageEncoder(ConnectMessage.class, new ConnectEncoder()); + encoder.addMessageEncoder(ConnAckMessage.class, new ConnAckEncoder()); + encoder.addMessageEncoder(SubAckMessage.class, new SubAckEncoder()); + encoder.addMessageEncoder(UnsubAckMessage.class, new UnsubAckEncoder()); + encoder.addMessageEncoder(PubAckMessage.class, new PubAckEncoder()); + encoder.addMessageEncoder(PubRecMessage.class, new PubRecEncoder()); + encoder.addMessageEncoder(PubCompMessage.class, new PubCompEncoder()); + encoder.addMessageEncoder(PubRelMessage.class, new PubRelEncoder()); + encoder.addMessageEncoder(PublishMessage.class, new PublishEncoder()); + encoder.addMessageEncoder(PingRespMessage.class, new PingRespEncoder()); + + m_acceptor = new NioSocketAcceptor(); + + m_acceptor.getFilterChain().addLast( "logger", new MQTTLoggingFilter("SERVER LOG") ); + m_acceptor.getFilterChain().addLast( "codec", new ProtocolCodecFilter(encoder, decoder)); + + MQTTHandler handler = new MQTTHandler(); + + handler.setMessaging(messaging); + + m_acceptor.setHandler(handler); + ((NioSocketAcceptor)m_acceptor).setReuseAddress(true); + ((NioSocketAcceptor)m_acceptor).getSessionConfig().setReuseAddress(true); + m_acceptor.getSessionConfig().setReadBufferSize( 2048 ); + m_acceptor.getSessionConfig().setIdleTime( IdleStatus.BOTH_IDLE, Constants.DEFAULT_CONNECT_TIMEOUT ); + m_acceptor.getStatistics().setThroughputCalculationInterval(10); + m_acceptor.getStatistics().updateThroughput(System.currentTimeMillis()); + m_acceptor.bind( new InetSocketAddress(Constants.PORT) ); + LOG.info("Server binded"); + } + + public void close() { + //log statistics + IoServiceStatistics statistics = m_acceptor.getStatistics(); + statistics.updateThroughput(System.currentTimeMillis()); + System.out.println(String.format("Total read bytes: %d, read throughtput: %f (b/s)", statistics.getReadBytes(), statistics.getReadBytesThroughput())); + System.out.println(String.format("Total read msgs: %d, read msg throughtput: %f (msg/s)", statistics.getReadMessages(), statistics.getReadMessagesThroughput())); + + for(IoSession session: m_acceptor.getManagedSessions().values()) { + if(session.isConnected() && !session.isClosing()){ + session.close(false); + } + } + + m_acceptor.unbind(); + m_acceptor.dispose(); + } + +} From 5382a9c467d537f786fdfd38ee1570a5af0426a8 Mon Sep 17 00:00:00 2001 From: andrea Date: Fri, 13 Sep 2013 11:33:53 +0200 Subject: [PATCH 33/41] Removed old unused client lib --- broker/pom.xml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/broker/pom.xml b/broker/pom.xml index 7805d770..e14f8dca 100644 --- a/broker/pom.xml +++ b/broker/pom.xml @@ -79,13 +79,6 @@ test - - org.dna.mqtt - moquette-client - 0.1 - test - - org.fusesource.hawtbuf hawtbuf From 70befbcd77db1cdf4d33be63c8b423f9284ca698 Mon Sep 17 00:00:00 2001 From: andrea Date: Fri, 13 Sep 2013 11:50:11 +0200 Subject: [PATCH 34/41] Fixed version of parent pom --- benchmarking/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmarking/pom.xml b/benchmarking/pom.xml index dcaeacf3..8cfb0af2 100644 --- a/benchmarking/pom.xml +++ b/benchmarking/pom.xml @@ -6,7 +6,7 @@ ../pom.xml moquette-parent org.dna.mqtt - 0.1 + 0.2 moquette-benchmark From 30e165c8cec52dc12e0b8a7c66cb78e6a8638f7c Mon Sep 17 00:00:00 2001 From: andrea Date: Fri, 13 Sep 2013 12:44:53 +0200 Subject: [PATCH 35/41] Separated all mina related stuff in classes to avoid main protocol loop to depend on mina related stuff --- .../moquette/messaging/spi/IMessaging.java | 6 +-- .../messaging/spi/impl/ProtocolProcessor.java | 15 ++++--- .../messaging/spi/impl/SimpleMessaging.java | 9 ++--- .../spi/impl/events/DisconnectEvent.java | 8 ++-- .../spi/impl/events/ProtocolEvent.java | 8 ++-- .../spi/impl/events/PublishEvent.java | 10 ++--- .../moquette/server/ConnectionDescriptor.java | 8 ++-- .../dna/mqtt/moquette/server/MQTTHandler.java | 11 ++++- .../mqtt/moquette/server/ServerChannel.java | 18 +++++++++ .../moquette/server/mina/MinaChannel.java | 40 +++++++++++++++++++ .../spi/impl/ProtocolProcessorTest.java | 16 +++++--- .../server/ServerIntegrationFuseTest.java | 12 ++++++ 12 files changed, 121 insertions(+), 40 deletions(-) create mode 100644 broker/src/main/java/org/dna/mqtt/moquette/server/ServerChannel.java create mode 100644 broker/src/main/java/org/dna/mqtt/moquette/server/mina/MinaChannel.java diff --git a/broker/src/main/java/org/dna/mqtt/moquette/messaging/spi/IMessaging.java b/broker/src/main/java/org/dna/mqtt/moquette/messaging/spi/IMessaging.java index 379ebe53..8fbd48a1 100644 --- a/broker/src/main/java/org/dna/mqtt/moquette/messaging/spi/IMessaging.java +++ b/broker/src/main/java/org/dna/mqtt/moquette/messaging/spi/IMessaging.java @@ -1,7 +1,7 @@ package org.dna.mqtt.moquette.messaging.spi; -import org.apache.mina.core.session.IoSession; import org.dna.mqtt.moquette.proto.messages.AbstractMessage; +import org.dna.mqtt.moquette.server.ServerChannel; /** * Interface to the underling messaging system used to publish, subscribe. @@ -12,7 +12,7 @@ public interface IMessaging { void stop(); - void disconnect(IoSession session); + void disconnect(ServerChannel session); - void handleProtocolMessage(IoSession session, AbstractMessage msg); + void handleProtocolMessage(ServerChannel session, AbstractMessage msg); } diff --git a/broker/src/main/java/org/dna/mqtt/moquette/messaging/spi/impl/ProtocolProcessor.java b/broker/src/main/java/org/dna/mqtt/moquette/messaging/spi/impl/ProtocolProcessor.java index 5388f3ae..1d457c3b 100644 --- a/broker/src/main/java/org/dna/mqtt/moquette/messaging/spi/impl/ProtocolProcessor.java +++ b/broker/src/main/java/org/dna/mqtt/moquette/messaging/spi/impl/ProtocolProcessor.java @@ -3,8 +3,6 @@ package org.dna.mqtt.moquette.messaging.spi.impl; import java.util.Collection; import java.util.List; import java.util.Map; -import org.apache.mina.core.session.IdleStatus; -import org.apache.mina.core.session.IoSession; import org.dna.mqtt.moquette.messaging.spi.IMatchingCondition; import org.dna.mqtt.moquette.messaging.spi.IStorageService; import org.dna.mqtt.moquette.messaging.spi.impl.events.PubAckEvent; @@ -25,6 +23,7 @@ import org.dna.mqtt.moquette.proto.messages.UnsubAckMessage; import org.dna.mqtt.moquette.server.ConnectionDescriptor; import org.dna.mqtt.moquette.server.Constants; import org.dna.mqtt.moquette.server.IAuthenticator; +import org.dna.mqtt.moquette.server.ServerChannel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -56,7 +55,7 @@ class ProtocolProcessor { m_storageService = storageService; } - void processConnect(IoSession session, ConnectMessage msg) { + void processConnect(ServerChannel session, ConnectMessage msg) { if (msg.getProcotolVersion() != 0x03) { ConnAckMessage badProto = new ConnAckMessage(); badProto.setReturnCode(ConnAckMessage.UNNACEPTABLE_PROTOCOL_VERSION); @@ -75,7 +74,7 @@ class ProtocolProcessor { //if an old client with the same ID already exists close its session. if (m_clientIDs.containsKey(msg.getClientID())) { //clean the subscriptions if the old used a cleanSession = true - IoSession oldSession = m_clientIDs.get(msg.getClientID()).getSession(); + ServerChannel oldSession = m_clientIDs.get(msg.getClientID()).getSession(); boolean cleanSession = (Boolean) oldSession.getAttribute(Constants.CLEAN_SESSION); if (cleanSession) { //cleanup topic subscriptions @@ -95,7 +94,7 @@ class ProtocolProcessor { //used to track the client in the subscription and publishing phases. session.setAttribute(Constants.ATTR_CLIENTID, msg.getClientID()); - session.getConfig().setIdleTime(IdleStatus.READER_IDLE, Math.round(keepAlive * 1.5f)); + session.setIdleTime(Math.round(keepAlive * 1.5f)); //Handle will flag if (msg.isWillFlag()) { @@ -331,7 +330,7 @@ class ProtocolProcessor { m_storageService.cleanInFlight(publishKey); } - void processDisconnect(IoSession session, String clientID, boolean cleanSession) throws InterruptedException { + void processDisconnect(ServerChannel session, String clientID, boolean cleanSession) throws InterruptedException { if (cleanSession) { //cleanup topic subscriptions processRemoveAllSubscriptions(clientID); @@ -349,7 +348,7 @@ class ProtocolProcessor { * Remove the clientID from topic subscription, if not previously subscribed, * doesn't reply any error */ - void processUnsubscribe(IoSession session, String clientID, List topics, int messageID) { + void processUnsubscribe(ServerChannel session, String clientID, List topics, int messageID) { LOG.debug("processSubscribe invoked"); for (String topic : topics) { @@ -364,7 +363,7 @@ class ProtocolProcessor { } - void processSubscribe(IoSession session, SubscribeMessage msg, String clientID, boolean cleanSession) { + void processSubscribe(ServerChannel session, SubscribeMessage msg, String clientID, boolean cleanSession) { LOG.debug("processSubscribe invoked"); for (SubscribeMessage.Couple req : msg.subscriptions()) { diff --git a/broker/src/main/java/org/dna/mqtt/moquette/messaging/spi/impl/SimpleMessaging.java b/broker/src/main/java/org/dna/mqtt/moquette/messaging/spi/impl/SimpleMessaging.java index d8141dee..0dd55221 100644 --- a/broker/src/main/java/org/dna/mqtt/moquette/messaging/spi/impl/SimpleMessaging.java +++ b/broker/src/main/java/org/dna/mqtt/moquette/messaging/spi/impl/SimpleMessaging.java @@ -5,11 +5,9 @@ import com.lmax.disruptor.EventHandler; import com.lmax.disruptor.RingBuffer; import com.lmax.disruptor.SequenceBarrier; import java.util.HashMap; -import java.util.List; import java.util.Map; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -import org.apache.mina.core.session.IoSession; import org.dna.mqtt.moquette.messaging.spi.IMessaging; import org.dna.mqtt.moquette.messaging.spi.IStorageService; import org.dna.mqtt.moquette.messaging.spi.impl.events.*; @@ -19,6 +17,7 @@ import org.dna.mqtt.moquette.proto.messages.*; import org.dna.mqtt.moquette.proto.messages.AbstractMessage.QOSType; import org.dna.mqtt.moquette.server.ConnectionDescriptor; import org.dna.mqtt.moquette.server.Constants; +import org.dna.mqtt.moquette.server.ServerChannel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -83,11 +82,11 @@ public class SimpleMessaging implements IMessaging, EventHandler { } - public void disconnect(IoSession session) { + public void disconnect(ServerChannel session) { disruptorPublish(new DisconnectEvent(session)); } - public void handleProtocolMessage(IoSession session, AbstractMessage msg) { + public void handleProtocolMessage(ServerChannel session, AbstractMessage msg) { disruptorPublish(new ProtocolEvent(session, msg)); } @@ -107,7 +106,7 @@ public class SimpleMessaging implements IMessaging, EventHandler { String clientID = (String) disEvt.getSession().getAttribute(Constants.ATTR_CLIENTID); m_processor.processDisconnect(disEvt.getSession(), clientID, false); } else if (evt instanceof ProtocolEvent) { - IoSession session = ((ProtocolEvent) evt).getSession(); + ServerChannel session = ((ProtocolEvent) evt).getSession(); AbstractMessage message = ((ProtocolEvent) evt).getMessage(); if (message instanceof ConnectMessage) { m_processor.processConnect(session, (ConnectMessage) message); diff --git a/broker/src/main/java/org/dna/mqtt/moquette/messaging/spi/impl/events/DisconnectEvent.java b/broker/src/main/java/org/dna/mqtt/moquette/messaging/spi/impl/events/DisconnectEvent.java index bf8ecd27..f90ac9c7 100644 --- a/broker/src/main/java/org/dna/mqtt/moquette/messaging/spi/impl/events/DisconnectEvent.java +++ b/broker/src/main/java/org/dna/mqtt/moquette/messaging/spi/impl/events/DisconnectEvent.java @@ -1,6 +1,6 @@ package org.dna.mqtt.moquette.messaging.spi.impl.events; -import org.apache.mina.core.session.IoSession; +import org.dna.mqtt.moquette.server.ServerChannel; /** * @@ -8,13 +8,13 @@ import org.apache.mina.core.session.IoSession; */ public class DisconnectEvent extends MessagingEvent { - IoSession m_session; + ServerChannel m_session; - public DisconnectEvent(IoSession session) { + public DisconnectEvent(ServerChannel session) { m_session = session; } - public IoSession getSession() { + public ServerChannel getSession() { return m_session; } diff --git a/broker/src/main/java/org/dna/mqtt/moquette/messaging/spi/impl/events/ProtocolEvent.java b/broker/src/main/java/org/dna/mqtt/moquette/messaging/spi/impl/events/ProtocolEvent.java index 7bc6036d..46a638ea 100644 --- a/broker/src/main/java/org/dna/mqtt/moquette/messaging/spi/impl/events/ProtocolEvent.java +++ b/broker/src/main/java/org/dna/mqtt/moquette/messaging/spi/impl/events/ProtocolEvent.java @@ -1,21 +1,21 @@ package org.dna.mqtt.moquette.messaging.spi.impl.events; -import org.apache.mina.core.session.IoSession; import org.dna.mqtt.moquette.proto.messages.AbstractMessage; +import org.dna.mqtt.moquette.server.ServerChannel; /** * Event used to carry ProtocolMessages from front handler to event processor */ public class ProtocolEvent extends MessagingEvent { - IoSession m_session; + ServerChannel m_session; AbstractMessage message; - public ProtocolEvent(IoSession session, AbstractMessage message) { + public ProtocolEvent(ServerChannel session, AbstractMessage message) { this.m_session = session; this.message = message; } - public IoSession getSession() { + public ServerChannel getSession() { return m_session; } diff --git a/broker/src/main/java/org/dna/mqtt/moquette/messaging/spi/impl/events/PublishEvent.java b/broker/src/main/java/org/dna/mqtt/moquette/messaging/spi/impl/events/PublishEvent.java index 316c3a65..7e2c2b5b 100644 --- a/broker/src/main/java/org/dna/mqtt/moquette/messaging/spi/impl/events/PublishEvent.java +++ b/broker/src/main/java/org/dna/mqtt/moquette/messaging/spi/impl/events/PublishEvent.java @@ -1,9 +1,9 @@ package org.dna.mqtt.moquette.messaging.spi.impl.events; -import org.apache.mina.core.session.IoSession; import org.dna.mqtt.moquette.proto.messages.AbstractMessage.QOSType; import java.io.Serializable; +import org.dna.mqtt.moquette.server.ServerChannel; /** * @@ -18,10 +18,10 @@ public class PublishEvent extends MessagingEvent implements Serializable { //Optional attribute, available only fo QoS 1 and 2 int m_msgID; - transient IoSession m_session; + transient ServerChannel m_session; public PublishEvent(String topic, QOSType qos, byte[] message, boolean retain, - String clientID, IoSession session) { + String clientID, ServerChannel session) { m_topic = topic; m_qos = qos; m_message = message; @@ -31,7 +31,7 @@ public class PublishEvent extends MessagingEvent implements Serializable { } public PublishEvent(String topic, QOSType qos, byte[] message, boolean retain, - String clientID, int msgID, IoSession session) { + String clientID, int msgID, ServerChannel session) { this(topic, qos, message, retain, clientID, session); m_msgID = msgID; } @@ -60,7 +60,7 @@ public class PublishEvent extends MessagingEvent implements Serializable { return m_msgID; } - public IoSession getSession() { + public ServerChannel getSession() { return m_session; } diff --git a/broker/src/main/java/org/dna/mqtt/moquette/server/ConnectionDescriptor.java b/broker/src/main/java/org/dna/mqtt/moquette/server/ConnectionDescriptor.java index 326bc196..f2a4634f 100644 --- a/broker/src/main/java/org/dna/mqtt/moquette/server/ConnectionDescriptor.java +++ b/broker/src/main/java/org/dna/mqtt/moquette/server/ConnectionDescriptor.java @@ -1,7 +1,5 @@ package org.dna.mqtt.moquette.server; -import org.apache.mina.core.session.IoSession; - /** * Maintains the information of single connection, like ClientID, IoSession, * and other connection related flags. @@ -12,10 +10,10 @@ import org.apache.mina.core.session.IoSession; public class ConnectionDescriptor { private String m_clientID; - private IoSession m_session; + private ServerChannel m_session; private boolean m_cleanSession; - public ConnectionDescriptor(String clientID, IoSession session, boolean cleanSession) { + public ConnectionDescriptor(String clientID, ServerChannel session, boolean cleanSession) { this.m_clientID = clientID; this.m_session = session; this.m_cleanSession = cleanSession; @@ -29,7 +27,7 @@ public class ConnectionDescriptor { return m_clientID; } - public IoSession getSession() { + public ServerChannel getSession() { return m_session; } } diff --git a/broker/src/main/java/org/dna/mqtt/moquette/server/MQTTHandler.java b/broker/src/main/java/org/dna/mqtt/moquette/server/MQTTHandler.java index 507d4e06..36cfdfe5 100644 --- a/broker/src/main/java/org/dna/mqtt/moquette/server/MQTTHandler.java +++ b/broker/src/main/java/org/dna/mqtt/moquette/server/MQTTHandler.java @@ -1,5 +1,7 @@ package org.dna.mqtt.moquette.server; +import java.util.HashMap; +import java.util.Map; import org.apache.mina.core.service.IoHandlerAdapter; import org.apache.mina.core.session.IdleStatus; import org.apache.mina.core.session.IoSession; @@ -8,6 +10,7 @@ import org.dna.mqtt.moquette.proto.Utils; import org.dna.mqtt.moquette.proto.messages.AbstractMessage; import static org.dna.mqtt.moquette.proto.messages.AbstractMessage.*; import org.dna.mqtt.moquette.proto.messages.PingRespMessage; +import org.dna.mqtt.moquette.server.mina.MinaChannel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -20,6 +23,7 @@ public class MQTTHandler extends IoHandlerAdapter { private static final Logger LOG = LoggerFactory.getLogger(MQTTHandler.class); private IMessaging m_messaging; + private Map m_channelMapper = new HashMap(); @Override public void messageReceived(IoSession session, Object message) throws Exception { @@ -35,7 +39,11 @@ public class MQTTHandler extends IoHandlerAdapter { case PUBCOMP: case PUBREL: case DISCONNECT: - m_messaging.handleProtocolMessage(session, msg); + if (!m_channelMapper.containsKey(session)) { + m_channelMapper.put(session, new MinaChannel(session)); + } + + m_messaging.handleProtocolMessage(m_channelMapper.get(session), msg); break; case PINGREQ: PingRespMessage pingResp = new PingRespMessage(); @@ -51,6 +59,7 @@ public class MQTTHandler extends IoHandlerAdapter { public void sessionIdle(IoSession session, IdleStatus status) { if (status == IdleStatus.READER_IDLE) { session.close(false); + m_channelMapper.remove(session); //TODO send a notification to messaging part to remove the bining clientID-ConnConfig } } diff --git a/broker/src/main/java/org/dna/mqtt/moquette/server/ServerChannel.java b/broker/src/main/java/org/dna/mqtt/moquette/server/ServerChannel.java new file mode 100644 index 00000000..b741de5f --- /dev/null +++ b/broker/src/main/java/org/dna/mqtt/moquette/server/ServerChannel.java @@ -0,0 +1,18 @@ +package org.dna.mqtt.moquette.server; + +/** + * + * @author andrea + */ +public interface ServerChannel { + + Object getAttribute(Object key); + + void setAttribute(Object key, Object value); + + void setIdleTime(int idleTime); + + void close(boolean immediately); + + void write(Object value); +} diff --git a/broker/src/main/java/org/dna/mqtt/moquette/server/mina/MinaChannel.java b/broker/src/main/java/org/dna/mqtt/moquette/server/mina/MinaChannel.java new file mode 100644 index 00000000..be591d51 --- /dev/null +++ b/broker/src/main/java/org/dna/mqtt/moquette/server/mina/MinaChannel.java @@ -0,0 +1,40 @@ +package org.dna.mqtt.moquette.server.mina; + +import org.apache.mina.core.session.IdleStatus; +import org.apache.mina.core.session.IoSession; +import org.dna.mqtt.moquette.server.ServerChannel; + +/** + * + * @author andrea + */ +public class MinaChannel implements ServerChannel { + + private IoSession m_session; + + public MinaChannel(IoSession session) { + m_session = session; + } + + public Object getAttribute(Object key) { + return m_session.getAttribute(key); + } + + public void setAttribute(Object key, Object value) { + m_session.setAttribute(key, value); + } + + public void close(boolean immediately) { + m_session.close(immediately); + } + + public void write(Object value) { + m_session.write(value); + } + + public void setIdleTime(int idleTime) { + m_session.getConfig().setIdleTime(IdleStatus.READER_IDLE, idleTime); + } + + +} diff --git a/broker/src/test/java/org/dna/mqtt/moquette/messaging/spi/impl/ProtocolProcessorTest.java b/broker/src/test/java/org/dna/mqtt/moquette/messaging/spi/impl/ProtocolProcessorTest.java index 393bbbc0..9021579a 100644 --- a/broker/src/test/java/org/dna/mqtt/moquette/messaging/spi/impl/ProtocolProcessorTest.java +++ b/broker/src/test/java/org/dna/mqtt/moquette/messaging/spi/impl/ProtocolProcessorTest.java @@ -21,6 +21,8 @@ import org.dna.mqtt.moquette.proto.messages.PublishMessage; import org.dna.mqtt.moquette.proto.messages.SubAckMessage; import org.dna.mqtt.moquette.proto.messages.SubscribeMessage; import org.dna.mqtt.moquette.server.ConnectionDescriptor; +import org.dna.mqtt.moquette.server.ServerChannel; +import org.dna.mqtt.moquette.server.mina.MinaChannel; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; @@ -35,7 +37,7 @@ public class ProtocolProcessorTest { final static String FAKE_CLIENT_ID = "FAKE_123"; final static String FAKE_TOPIC = "/news"; - IoSession m_session; + ServerChannel m_session; byte m_returnCode; ConnectMessage connMsg; ProtocolProcessor m_processor; @@ -44,15 +46,19 @@ public class ProtocolProcessorTest { SubscriptionsStore subscriptions; AbstractMessage m_receivedMessage; + IoSession m_minaSession; + @Before public void setUp() throws InterruptedException { connMsg = new ConnectMessage(); connMsg.setProcotolVersion((byte) 0x03); - m_session = new DummySession(); + m_minaSession = new DummySession(); + + m_session = new MinaChannel(m_minaSession); - m_session.getFilterChain().addFirst("MessageCatcher", new IoFilterAdapter() { + m_minaSession.getFilterChain().addFirst("MessageCatcher", new IoFilterAdapter() { @Override public void filterWrite(IoFilter.NextFilter nextFilter, IoSession session, @@ -190,8 +196,8 @@ public class ProtocolProcessorTest { @Test public void testPublishOfRetainedMessage_afterNewSubscription() throws Exception { - m_session.getFilterChain().remove("MessageCatcher"); - m_session.getFilterChain().addFirst("MessageCatcher", new IoFilterAdapter() { + m_minaSession.getFilterChain().remove("MessageCatcher"); + m_minaSession.getFilterChain().addFirst("MessageCatcher", new IoFilterAdapter() { @Override public void filterWrite(IoFilter.NextFilter nextFilter, IoSession session, diff --git a/broker/src/test/java/org/dna/mqtt/moquette/server/ServerIntegrationFuseTest.java b/broker/src/test/java/org/dna/mqtt/moquette/server/ServerIntegrationFuseTest.java index ded1696e..cea7268e 100644 --- a/broker/src/test/java/org/dna/mqtt/moquette/server/ServerIntegrationFuseTest.java +++ b/broker/src/test/java/org/dna/mqtt/moquette/server/ServerIntegrationFuseTest.java @@ -51,6 +51,7 @@ public class ServerIntegrationFuseTest { @Test public void testSubscribe() throws Exception { + System.out.println("*** testSubscribe ***"); m_connection = m_mqtt.blockingConnection(); m_connection.connect(); @@ -66,6 +67,7 @@ public class ServerIntegrationFuseTest { @Test public void testCleanSession_maintainClientSubscriptions() throws Exception { + System.out.println("*** testCleanSession_maintainClientSubscriptions ***"); MQTT localMqtt = new MQTT(m_mqtt); localMqtt.setCleanSession(false); BlockingConnection connection = localMqtt.blockingConnection(); @@ -87,6 +89,7 @@ public class ServerIntegrationFuseTest { @Test public void testCleanSession_maintainClientSubscriptions_againstClientDestruction() throws Exception { + System.out.println("*** testCleanSession_maintainClientSubscriptions_againstClientDestruction ***"); MQTT localMqtt = new MQTT(m_mqtt); localMqtt.setCleanSession(false); BlockingConnection connection = localMqtt.blockingConnection(); @@ -114,6 +117,7 @@ public class ServerIntegrationFuseTest { */ @Test public void testCleanSession_correctlyClientSubscriptions() throws Exception { + System.out.println("*** testCleanSession_correctlyClientSubscriptions ***"); MQTT localMqtt = new MQTT(m_mqtt); localMqtt.setCleanSession(false); BlockingConnection connection = localMqtt.blockingConnection(); @@ -134,6 +138,7 @@ public class ServerIntegrationFuseTest { @Test public void testCleanSession_maintainClientSubscriptions_withServerRestart() throws Exception { + System.out.println("*** testCleanSession_maintainClientSubscriptions_withServerRestart ***"); MQTT localMqtt = new MQTT(m_mqtt); localMqtt.setCleanSession(false); BlockingConnection connection = localMqtt.blockingConnection(); @@ -160,6 +165,7 @@ public class ServerIntegrationFuseTest { @Test public void testRetain_maintainMessage_againstClientDestruction() throws Exception { + System.out.println("*** testRetain_maintainMessage_againstClientDestruction ***"); m_connection = m_mqtt.blockingConnection(); m_connection.connect(); m_connection.publish("/topic", "Test my payload".getBytes(), QoS.AT_MOST_ONCE, true); @@ -176,6 +182,7 @@ public class ServerIntegrationFuseTest { @Test public void testUnsubscribe_do_not_notify_anymore_same_session() throws Exception { + System.out.println("*** testUnsubscribe_do_not_notify_anymore_same_session ***"); m_connection = m_mqtt.blockingConnection(); m_connection.connect(); m_connection.subscribe(new Topic[] {new Topic("/topic", QoS.AT_MOST_ONCE)}); @@ -195,6 +202,7 @@ public class ServerIntegrationFuseTest { @Test public void testUnsubscribe_do_not_notify_anymore_new_session() throws Exception { + System.out.println("*** testUnsubscribe_do_not_notify_anymore_new_session ***"); m_connection = m_mqtt.blockingConnection(); m_connection.connect(); m_connection.subscribe(new Topic[] {new Topic("/topic", QoS.AT_MOST_ONCE)}); @@ -217,6 +225,7 @@ public class ServerIntegrationFuseTest { @Test public void testPublishWithQoS1() throws Exception { + System.out.println("*** testPublishWithQoS1 ***"); m_connection = m_mqtt.blockingConnection(); m_connection.connect(); @@ -236,6 +245,7 @@ public class ServerIntegrationFuseTest { @Test public void testPublishWithQoS1_notCleanSession() throws Exception { + System.out.println("*** testPublishWithQoS1_notCleanSession ***"); MQTT mqtt = new MQTT(m_mqtt); mqtt.setCleanSession(false); m_connection = mqtt.blockingConnection(); @@ -275,6 +285,7 @@ public class ServerIntegrationFuseTest { @Test public void testPublishWithQoS2() throws Exception { + System.out.println("*** testPublishWithQoS2 ***"); m_mqtt.setClientId("TestClient"); m_connection = m_mqtt.blockingConnection(); m_connection.connect(); @@ -295,6 +306,7 @@ public class ServerIntegrationFuseTest { @Test public void testPublishReceiveWithQoS2() throws Exception { + System.out.println("*** testPublishReceiveWithQoS2 ***"); m_mqtt.setCleanSession(false); m_connection = m_mqtt.blockingConnection(); m_connection.connect(); From 51f0ba7e1cf2c38b627773802b8029b92f299a70 Mon Sep 17 00:00:00 2001 From: andrea Date: Sat, 14 Sep 2013 12:57:06 +0200 Subject: [PATCH 36/41] Added message type to every message --- .../org/dna/mqtt/moquette/proto/messages/ConnAckMessage.java | 4 ++++ .../dna/mqtt/moquette/proto/messages/DisconnectMessage.java | 4 ++++ .../org/dna/mqtt/moquette/proto/messages/PingReqMessage.java | 4 ++++ .../org/dna/mqtt/moquette/proto/messages/PingRespMessage.java | 4 ++++ .../org/dna/mqtt/moquette/proto/messages/PubAckMessage.java | 4 ++++ .../org/dna/mqtt/moquette/proto/messages/PubCompMessage.java | 4 ++++ .../org/dna/mqtt/moquette/proto/messages/PubRecMessage.java | 3 +++ .../org/dna/mqtt/moquette/proto/messages/PubRelMessage.java | 4 ++++ .../org/dna/mqtt/moquette/proto/messages/PublishMessage.java | 4 ++++ .../org/dna/mqtt/moquette/proto/messages/SubAckMessage.java | 4 ++++ .../dna/mqtt/moquette/proto/messages/SubscribeMessage.java | 1 + .../org/dna/mqtt/moquette/proto/messages/UnsubAckMessage.java | 4 ++++ .../dna/mqtt/moquette/proto/messages/UnsubscribeMessage.java | 4 ++++ 13 files changed, 48 insertions(+) diff --git a/parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/ConnAckMessage.java b/parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/ConnAckMessage.java index 9d8d4a7f..8102b534 100644 --- a/parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/ConnAckMessage.java +++ b/parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/ConnAckMessage.java @@ -14,6 +14,10 @@ public class ConnAckMessage extends AbstractMessage { public static final byte NOT_AUTHORIZED = 0x05; private byte m_returnCode; + + public ConnAckMessage() { + m_messageType = AbstractMessage.CONNACK; + } public byte getReturnCode() { return m_returnCode; diff --git a/parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/DisconnectMessage.java b/parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/DisconnectMessage.java index 12c4f179..df1c998a 100644 --- a/parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/DisconnectMessage.java +++ b/parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/DisconnectMessage.java @@ -6,4 +6,8 @@ package org.dna.mqtt.moquette.proto.messages; * @author andrea */ public class DisconnectMessage extends ZeroLengthMessage { + + public DisconnectMessage() { + m_messageType = AbstractMessage.DISCONNECT; + } } diff --git a/parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/PingReqMessage.java b/parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/PingReqMessage.java index 4bd1f1ba..672187d0 100644 --- a/parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/PingReqMessage.java +++ b/parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/PingReqMessage.java @@ -6,4 +6,8 @@ package org.dna.mqtt.moquette.proto.messages; * @author andrea */ public class PingReqMessage extends ZeroLengthMessage { + + public PingReqMessage() { + m_messageType = AbstractMessage.PINGREQ; + } } diff --git a/parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/PingRespMessage.java b/parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/PingRespMessage.java index aad82e57..4f876ed5 100644 --- a/parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/PingRespMessage.java +++ b/parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/PingRespMessage.java @@ -5,4 +5,8 @@ package org.dna.mqtt.moquette.proto.messages; * @author andrea */ public class PingRespMessage extends ZeroLengthMessage { + + public PingRespMessage() { + m_messageType = AbstractMessage.PINGRESP; + } } diff --git a/parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/PubAckMessage.java b/parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/PubAckMessage.java index 5c0a87e7..67448532 100644 --- a/parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/PubAckMessage.java +++ b/parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/PubAckMessage.java @@ -6,4 +6,8 @@ package org.dna.mqtt.moquette.proto.messages; * @author andrea */ public class PubAckMessage extends MessageIDMessage { + + public PubAckMessage() { + m_messageType = AbstractMessage.PUBACK; + } } diff --git a/parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/PubCompMessage.java b/parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/PubCompMessage.java index a4dbdff5..c28314ba 100644 --- a/parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/PubCompMessage.java +++ b/parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/PubCompMessage.java @@ -5,5 +5,9 @@ package org.dna.mqtt.moquette.proto.messages; * @author andrea */ public class PubCompMessage extends MessageIDMessage { + + public PubCompMessage() { + m_messageType = AbstractMessage.PUBCOMP; + } } diff --git a/parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/PubRecMessage.java b/parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/PubRecMessage.java index 648e6e42..d2b41123 100644 --- a/parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/PubRecMessage.java +++ b/parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/PubRecMessage.java @@ -7,4 +7,7 @@ package org.dna.mqtt.moquette.proto.messages; */ public class PubRecMessage extends MessageIDMessage { + public PubRecMessage() { + m_messageType = AbstractMessage.PUBREC; + } } diff --git a/parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/PubRelMessage.java b/parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/PubRelMessage.java index aef4a5bd..5bbd29a9 100644 --- a/parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/PubRelMessage.java +++ b/parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/PubRelMessage.java @@ -5,4 +5,8 @@ package org.dna.mqtt.moquette.proto.messages; * @author andrea */ public class PubRelMessage extends MessageIDMessage { + + public PubRelMessage() { + m_messageType = AbstractMessage.PUBREL; + } } diff --git a/parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/PublishMessage.java b/parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/PublishMessage.java index 41742e2a..67a5da3f 100644 --- a/parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/PublishMessage.java +++ b/parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/PublishMessage.java @@ -17,6 +17,10 @@ public class PublishMessage extends MessageIDMessage { public void setMessageID(Integer messageID) { this.m_messageID = messageID; }*/ + + public PublishMessage() { + m_messageType = AbstractMessage.PUBLISH; + } public String getTopicName() { return m_topicName; diff --git a/parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/SubAckMessage.java b/parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/SubAckMessage.java index debe6247..d4ea6316 100644 --- a/parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/SubAckMessage.java +++ b/parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/SubAckMessage.java @@ -10,6 +10,10 @@ import java.util.List; public class SubAckMessage extends MessageIDMessage { List m_types = new ArrayList(); + + public SubAckMessage() { + m_messageType = AbstractMessage.SUBACK; + } public List types() { return m_types; diff --git a/parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/SubscribeMessage.java b/parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/SubscribeMessage.java index d93a52eb..67accee3 100644 --- a/parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/SubscribeMessage.java +++ b/parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/SubscribeMessage.java @@ -31,6 +31,7 @@ public class SubscribeMessage extends MessageIDMessage { public SubscribeMessage() { //Subscribe has always QoS 1 + m_messageType = AbstractMessage.SUBSCRIBE; m_qos = AbstractMessage.QOSType.LEAST_ONE; } diff --git a/parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/UnsubAckMessage.java b/parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/UnsubAckMessage.java index e5f92f6d..75bfd98e 100644 --- a/parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/UnsubAckMessage.java +++ b/parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/UnsubAckMessage.java @@ -5,5 +5,9 @@ package org.dna.mqtt.moquette.proto.messages; * @author andrea */ public class UnsubAckMessage extends MessageIDMessage { + + public UnsubAckMessage() { + m_messageType = AbstractMessage.UNSUBACK; + } } diff --git a/parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/UnsubscribeMessage.java b/parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/UnsubscribeMessage.java index 1d3912db..a1befe17 100644 --- a/parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/UnsubscribeMessage.java +++ b/parser_commons/src/main/java/org/dna/mqtt/moquette/proto/messages/UnsubscribeMessage.java @@ -9,6 +9,10 @@ import java.util.List; */ public class UnsubscribeMessage extends MessageIDMessage { List m_types = new ArrayList(); + + public UnsubscribeMessage() { + m_messageType = AbstractMessage.UNSUBSCRIBE; + } public List topics() { return m_types; From 466e0946d607adf68cc20da72a463725b3cf4a0b Mon Sep 17 00:00:00 2001 From: andrea Date: Sat, 14 Sep 2013 15:06:19 +0200 Subject: [PATCH 37/41] Given public visibility to MQTT Endoer/Decoer constructors to be used from the broker --- .../java/org/dna/mqtt/moquette/parser/netty/MQTTDecoder.java | 2 +- .../java/org/dna/mqtt/moquette/parser/netty/MQTTEncoder.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTDecoder.java b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTDecoder.java index 645a2105..35676e2f 100644 --- a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTDecoder.java +++ b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTDecoder.java @@ -17,7 +17,7 @@ public class MQTTDecoder extends ByteToMessageDecoder { private Map m_decoderMap = new HashMap(); - MQTTDecoder() { + public MQTTDecoder() { m_decoderMap.put(AbstractMessage.CONNECT, new ConnectDecoder()); m_decoderMap.put(AbstractMessage.CONNACK, new ConnAckDecoder()); m_decoderMap.put(AbstractMessage.PUBLISH, new PublishDecoder()); diff --git a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTEncoder.java b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTEncoder.java index d2585b19..0b9eabbe 100644 --- a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTEncoder.java +++ b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/MQTTEncoder.java @@ -16,7 +16,7 @@ public class MQTTEncoder extends MessageToByteEncoder { private Map m_encoderMap = new HashMap(); - MQTTEncoder() { + public MQTTEncoder() { m_encoderMap.put(AbstractMessage.CONNECT, new ConnectEncoder()); m_encoderMap.put(AbstractMessage.CONNACK, new ConnAckEncoder()); m_encoderMap.put(AbstractMessage.PUBLISH, new PublishEncoder()); From 6269330364f523c11939c96ce3480362b0b0bdf7 Mon Sep 17 00:00:00 2001 From: andrea Date: Sat, 14 Sep 2013 15:06:44 +0200 Subject: [PATCH 38/41] Minor change, reorganized imports --- .../main/java/org/dna/mqtt/moquette/proto/PubCompEncoder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parser/src/main/java/org/dna/mqtt/moquette/proto/PubCompEncoder.java b/parser/src/main/java/org/dna/mqtt/moquette/proto/PubCompEncoder.java index c77c82bc..93534271 100644 --- a/parser/src/main/java/org/dna/mqtt/moquette/proto/PubCompEncoder.java +++ b/parser/src/main/java/org/dna/mqtt/moquette/proto/PubCompEncoder.java @@ -1,11 +1,11 @@ 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.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; /** * From 3ce369ab43e30943d9f4b1dcc1895c92b6de40ce Mon Sep 17 00:00:00 2001 From: andrea Date: Sun, 15 Sep 2013 15:31:09 +0200 Subject: [PATCH 39/41] Fixed bug in the calculation of payload site in publish decoder due to bad startPos recording --- .../moquette/parser/netty/PublishDecoder.java | 4 ++-- .../parser/netty/PublishDecoderTest.java | 21 +++++++++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/PublishDecoder.java b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/PublishDecoder.java index 9a29ff14..00e869f8 100644 --- a/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/PublishDecoder.java +++ b/netty_parser/src/main/java/org/dna/mqtt/moquette/parser/netty/PublishDecoder.java @@ -18,9 +18,9 @@ class PublishDecoder extends DemuxDecoder { @Override void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception { - LOG.debug("decode invoked with buffer " + in); - int startPos = in.readerIndex(); + LOG.info("decode invoked with buffer " + in); in.resetReaderIndex(); + int startPos = in.readerIndex(); //Common decoding part PublishMessage message = new PublishMessage(); diff --git a/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/PublishDecoderTest.java b/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/PublishDecoderTest.java index c935d3cf..4cebae39 100644 --- a/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/PublishDecoderTest.java +++ b/netty_parser/src/test/java/org/dna/mqtt/moquette/parser/netty/PublishDecoderTest.java @@ -123,6 +123,27 @@ public class PublishDecoderTest { assertNotNull(message); } + @Test + public void testBadClaimMoreData() throws Exception { + byte[] rawMessage = new byte[] { + 0x30, 0x17, 0x00, 0x06, 0x2f, (byte)0x74, 0x6f, (byte)0x70, 0x69, 0x63, 0x54, 0x65, (byte)0x73, 0x74, 0x20, 0x6d, + (byte)0x79, 0x20, (byte)0x70, 0x61, (byte)0x79, 0x6c, 0x6f, 0x61, 0x64 + }; + + ByteBuf msgBuf = Unpooled.buffer(25); + msgBuf.writeBytes(rawMessage); + msgBuf.readByte(); //to simulate the reading of messageType done by MQTTDecoder dispatcher + + //Exercise + m_msgdec.decode(null, msgBuf, m_results); + + assertFalse(m_results.isEmpty()); + PublishMessage message = (PublishMessage)m_results.get(0); + assertNotNull(message); + assertEquals("/topic", message.getTopicName()); + assertEquals("Test my payload", new String(message.getPayload())); + } + private void initHeader(ByteBuf buff) throws IllegalAccessException { ByteBuf tmp = Unpooled.buffer(4).writeBytes(Utils.encodeString("Fake Topic")); buff.clear().writeByte(AbstractMessage.PUBLISH << 4).writeBytes(Utils.encodeRemainingLength(tmp.readableBytes())); From 63842088a4dad872a2b687291fcc4b6e2c043803 Mon Sep 17 00:00:00 2001 From: andrea Date: Sun, 15 Sep 2013 16:17:03 +0200 Subject: [PATCH 40/41] Minor change, implemented trivial toString() --- .../moquette/messaging/spi/impl/events/ProtocolEvent.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/broker/src/main/java/org/dna/mqtt/moquette/messaging/spi/impl/events/ProtocolEvent.java b/broker/src/main/java/org/dna/mqtt/moquette/messaging/spi/impl/events/ProtocolEvent.java index 46a638ea..2b0232c4 100644 --- a/broker/src/main/java/org/dna/mqtt/moquette/messaging/spi/impl/events/ProtocolEvent.java +++ b/broker/src/main/java/org/dna/mqtt/moquette/messaging/spi/impl/events/ProtocolEvent.java @@ -1,5 +1,6 @@ package org.dna.mqtt.moquette.messaging.spi.impl.events; +import org.dna.mqtt.moquette.proto.Utils; import org.dna.mqtt.moquette.proto.messages.AbstractMessage; import org.dna.mqtt.moquette.server.ServerChannel; @@ -22,4 +23,9 @@ public class ProtocolEvent extends MessagingEvent { public AbstractMessage getMessage() { return message; } + + @Override + public String toString() { + return "ProtocolEvent wrapping " + Utils.msgType2String(message.getMessageType()); + } } From ac5fb3c9ac7bfbd48ecc4e3719457152c4109945 Mon Sep 17 00:00:00 2001 From: andrea Date: Sun, 15 Sep 2013 17:18:26 +0200 Subject: [PATCH 41/41] Added Netty connection stuff for the I/O --- broker/pom.xml | 6 ++ .../messaging/spi/impl/ProtocolProcessor.java | 3 +- .../messaging/spi/impl/SimpleMessaging.java | 4 +- .../dna/mqtt/moquette/server/Constants.java | 1 + .../org/dna/mqtt/moquette/server/Server.java | 37 +++++++- .../moquette/server/mina/MinaAcceptor.java | 3 +- .../moquette/server/mina/MinaChannel.java | 2 - .../MinaMQTTHandler.java} | 18 ++-- .../moquette/server/netty/NettyAcceptor.java | 79 +++++++++++++++++ .../moquette/server/netty/NettyChannel.java | 86 +++++++++++++++++++ .../server/netty/NettyMQTTHandler.java | 72 ++++++++++++++++ .../server/ServerIntegrationFuseTest.java | 6 +- 12 files changed, 298 insertions(+), 19 deletions(-) rename broker/src/main/java/org/dna/mqtt/moquette/server/{MQTTHandler.java => mina/MinaMQTTHandler.java} (75%) create mode 100644 broker/src/main/java/org/dna/mqtt/moquette/server/netty/NettyAcceptor.java create mode 100644 broker/src/main/java/org/dna/mqtt/moquette/server/netty/NettyChannel.java create mode 100644 broker/src/main/java/org/dna/mqtt/moquette/server/netty/NettyMQTTHandler.java diff --git a/broker/pom.xml b/broker/pom.xml index e14f8dca..59744733 100644 --- a/broker/pom.xml +++ b/broker/pom.xml @@ -24,6 +24,12 @@ moquette-mina-parser 0.2 + + + org.dna.mqtt + moquette-netty-parser + 0.2 + org.apache.mina diff --git a/broker/src/main/java/org/dna/mqtt/moquette/messaging/spi/impl/ProtocolProcessor.java b/broker/src/main/java/org/dna/mqtt/moquette/messaging/spi/impl/ProtocolProcessor.java index 1d457c3b..dd4053a2 100644 --- a/broker/src/main/java/org/dna/mqtt/moquette/messaging/spi/impl/ProtocolProcessor.java +++ b/broker/src/main/java/org/dna/mqtt/moquette/messaging/spi/impl/ProtocolProcessor.java @@ -89,7 +89,7 @@ class ProtocolProcessor { int keepAlive = msg.getKeepAlive(); LOG.debug(String.format("Connect with keepAlive %d s", keepAlive)); - session.setAttribute("keepAlive", keepAlive); + session.setAttribute(Constants.KEEP_ALIVE, keepAlive); session.setAttribute(Constants.CLEAN_SESSION, msg.isCleanSession()); //used to track the client in the subscription and publishing phases. session.setAttribute(Constants.ATTR_CLIENTID, msg.getClientID()); @@ -186,6 +186,7 @@ class ProtocolProcessor { } m_storageService.cleanInFlight(publishKey); sendPubAck(new PubAckEvent(evt.getMessageID(), evt.getClientID())); + LOG.info("replying with PubAck to MSG ID " + evt.getMessageID()); } if (retain) { diff --git a/broker/src/main/java/org/dna/mqtt/moquette/messaging/spi/impl/SimpleMessaging.java b/broker/src/main/java/org/dna/mqtt/moquette/messaging/spi/impl/SimpleMessaging.java index 0dd55221..7d42c95c 100644 --- a/broker/src/main/java/org/dna/mqtt/moquette/messaging/spi/impl/SimpleMessaging.java +++ b/broker/src/main/java/org/dna/mqtt/moquette/messaging/spi/impl/SimpleMessaging.java @@ -65,6 +65,7 @@ public class SimpleMessaging implements IMessaging, EventHandler { SequenceBarrier barrier = m_ringBuffer.newBarrier(); m_eventProcessor = new BatchEventProcessor(m_ringBuffer, barrier, this); + //TODO in a presentation is said to don't do the followinf line!! m_ringBuffer.setGatingSequences(m_eventProcessor.getSequence()); m_executor.submit(m_eventProcessor); @@ -73,6 +74,7 @@ public class SimpleMessaging implements IMessaging, EventHandler { private void disruptorPublish(MessagingEvent msgEvent) { + LOG.info("disruptorPublish publishing event " + msgEvent); long sequence = m_ringBuffer.next(); ValueEvent event = m_ringBuffer.get(sequence); @@ -96,7 +98,7 @@ public class SimpleMessaging implements IMessaging, EventHandler { public void onEvent(ValueEvent t, long l, boolean bln) throws Exception { MessagingEvent evt = t.getEvent(); - LOG.debug("onEvent processing messaging event " + evt); + LOG.info("onEvent processing messaging event " + evt); if (evt instanceof PublishEvent) { m_processor.processPublish((PublishEvent) evt); } else if (evt instanceof StopEvent) { diff --git a/broker/src/main/java/org/dna/mqtt/moquette/server/Constants.java b/broker/src/main/java/org/dna/mqtt/moquette/server/Constants.java index 0a2ea7fd..17a691c5 100644 --- a/broker/src/main/java/org/dna/mqtt/moquette/server/Constants.java +++ b/broker/src/main/java/org/dna/mqtt/moquette/server/Constants.java @@ -6,4 +6,5 @@ package org.dna.mqtt.moquette.server; public class Constants { public static final String ATTR_CLIENTID = "ClientID"; public static final String CLEAN_SESSION = "cleanSession"; + public static final String KEEP_ALIVE = "keepAlive"; } diff --git a/broker/src/main/java/org/dna/mqtt/moquette/server/Server.java b/broker/src/main/java/org/dna/mqtt/moquette/server/Server.java index d4872f78..5a9412bb 100644 --- a/broker/src/main/java/org/dna/mqtt/moquette/server/Server.java +++ b/broker/src/main/java/org/dna/mqtt/moquette/server/Server.java @@ -4,6 +4,7 @@ import java.io.File; import java.io.IOException; import org.dna.mqtt.moquette.messaging.spi.impl.SimpleMessaging; import org.dna.mqtt.moquette.server.mina.MinaAcceptor; +import org.dna.mqtt.moquette.server.netty.NettyAcceptor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** @@ -16,13 +17,27 @@ public class Server { public static final String STORAGE_FILE_PATH = System.getProperty("user.home") + File.separator + "moquette_store.hawtdb"; - + public static String NETTY_NIO_FRAMEWORK = "netty"; + public static String MINA_NIO_FRAMEWORK = "mina"; + public static String DEFAULT_NIO_FRAMEWORK = MINA_NIO_FRAMEWORK; + private ServerAcceptor m_acceptor; SimpleMessaging messaging; public static void main(String[] args) throws IOException { + String nioFrameworkType = DEFAULT_NIO_FRAMEWORK; + if (args.length > 0) { + //TODO check the typing and in case print usge message + nioFrameworkType = args[1]; + if (!(nioFrameworkType.equals(DEFAULT_NIO_FRAMEWORK) || + nioFrameworkType.equals(DEFAULT_NIO_FRAMEWORK))) { + printUsage(nioFrameworkType); + return; + } + } + final Server server = new Server(); - server.startServer(); + server.startServer(nioFrameworkType); //Bind a shutdown hook Runtime.getRuntime().addShutdownHook(new Thread() { @Override @@ -34,10 +49,19 @@ public class Server { } public void startServer() throws IOException { + startServer(DEFAULT_NIO_FRAMEWORK); + } + + public void startServer(String nioFrameworkType) throws IOException { + LOG.info("Starting server with " + nioFrameworkType + " connectors"); messaging = SimpleMessaging.getInstance(); messaging.init(); - m_acceptor = new MinaAcceptor(); + if (nioFrameworkType.equals(MINA_NIO_FRAMEWORK)) { + m_acceptor = new MinaAcceptor(); + } else if (nioFrameworkType.equals(NETTY_NIO_FRAMEWORK)) { + m_acceptor = new NettyAcceptor(); + } m_acceptor.initialize(messaging); } @@ -47,5 +71,12 @@ public class Server { m_acceptor.close(); LOG.info("Server stopped"); } + + + private static void printUsage(String nioFrameworkType) { + System.out.println("The broker should be invoked with the following command line: \n"); + System.out.println("> java -jar [mina|netty]\n"); + System.out.println("while was invoked with type: " + nioFrameworkType); + } } diff --git a/broker/src/main/java/org/dna/mqtt/moquette/server/mina/MinaAcceptor.java b/broker/src/main/java/org/dna/mqtt/moquette/server/mina/MinaAcceptor.java index 324586aa..98be370b 100644 --- a/broker/src/main/java/org/dna/mqtt/moquette/server/mina/MinaAcceptor.java +++ b/broker/src/main/java/org/dna/mqtt/moquette/server/mina/MinaAcceptor.java @@ -41,7 +41,6 @@ import org.dna.mqtt.moquette.proto.messages.PubRelMessage; import org.dna.mqtt.moquette.proto.messages.PublishMessage; import org.dna.mqtt.moquette.proto.messages.SubAckMessage; import org.dna.mqtt.moquette.proto.messages.UnsubAckMessage; -import org.dna.mqtt.moquette.server.MQTTHandler; import org.dna.mqtt.moquette.server.ServerAcceptor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -87,7 +86,7 @@ public class MinaAcceptor implements ServerAcceptor { m_acceptor.getFilterChain().addLast( "logger", new MQTTLoggingFilter("SERVER LOG") ); m_acceptor.getFilterChain().addLast( "codec", new ProtocolCodecFilter(encoder, decoder)); - MQTTHandler handler = new MQTTHandler(); + MinaMQTTHandler handler = new MinaMQTTHandler(); handler.setMessaging(messaging); diff --git a/broker/src/main/java/org/dna/mqtt/moquette/server/mina/MinaChannel.java b/broker/src/main/java/org/dna/mqtt/moquette/server/mina/MinaChannel.java index be591d51..14064ddd 100644 --- a/broker/src/main/java/org/dna/mqtt/moquette/server/mina/MinaChannel.java +++ b/broker/src/main/java/org/dna/mqtt/moquette/server/mina/MinaChannel.java @@ -35,6 +35,4 @@ public class MinaChannel implements ServerChannel { public void setIdleTime(int idleTime) { m_session.getConfig().setIdleTime(IdleStatus.READER_IDLE, idleTime); } - - } diff --git a/broker/src/main/java/org/dna/mqtt/moquette/server/MQTTHandler.java b/broker/src/main/java/org/dna/mqtt/moquette/server/mina/MinaMQTTHandler.java similarity index 75% rename from broker/src/main/java/org/dna/mqtt/moquette/server/MQTTHandler.java rename to broker/src/main/java/org/dna/mqtt/moquette/server/mina/MinaMQTTHandler.java index 36cfdfe5..bc6d8ad9 100644 --- a/broker/src/main/java/org/dna/mqtt/moquette/server/MQTTHandler.java +++ b/broker/src/main/java/org/dna/mqtt/moquette/server/mina/MinaMQTTHandler.java @@ -1,5 +1,6 @@ -package org.dna.mqtt.moquette.server; +package org.dna.mqtt.moquette.server.mina; +import io.netty.channel.ChannelHandler; import java.util.HashMap; import java.util.Map; import org.apache.mina.core.service.IoHandlerAdapter; @@ -10,7 +11,6 @@ import org.dna.mqtt.moquette.proto.Utils; import org.dna.mqtt.moquette.proto.messages.AbstractMessage; import static org.dna.mqtt.moquette.proto.messages.AbstractMessage.*; import org.dna.mqtt.moquette.proto.messages.PingRespMessage; -import org.dna.mqtt.moquette.server.mina.MinaChannel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -19,9 +19,9 @@ import org.slf4j.LoggerFactory; * * @author andrea */ -public class MQTTHandler extends IoHandlerAdapter { +public class MinaMQTTHandler extends IoHandlerAdapter { - private static final Logger LOG = LoggerFactory.getLogger(MQTTHandler.class); + private static final Logger LOG = LoggerFactory.getLogger(MinaMQTTHandler.class); private IMessaging m_messaging; private Map m_channelMapper = new HashMap(); @@ -39,11 +39,15 @@ public class MQTTHandler extends IoHandlerAdapter { case PUBCOMP: case PUBREL: case DISCONNECT: - if (!m_channelMapper.containsKey(session)) { - m_channelMapper.put(session, new MinaChannel(session)); + MinaChannel channel; + synchronized(m_channelMapper) { + if (!m_channelMapper.containsKey(session)) { + m_channelMapper.put(session, new MinaChannel(session)); + } + channel = m_channelMapper.get(session); } - m_messaging.handleProtocolMessage(m_channelMapper.get(session), msg); + m_messaging.handleProtocolMessage(channel, msg); break; case PINGREQ: PingRespMessage pingResp = new PingRespMessage(); diff --git a/broker/src/main/java/org/dna/mqtt/moquette/server/netty/NettyAcceptor.java b/broker/src/main/java/org/dna/mqtt/moquette/server/netty/NettyAcceptor.java new file mode 100644 index 00000000..b10e576d --- /dev/null +++ b/broker/src/main/java/org/dna/mqtt/moquette/server/netty/NettyAcceptor.java @@ -0,0 +1,79 @@ +package org.dna.mqtt.moquette.server.netty; + +import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelOption; +import io.netty.channel.ChannelPipeline; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.SocketChannel; +import io.netty.channel.socket.nio.NioServerSocketChannel; +import io.netty.handler.logging.LogLevel; +import io.netty.handler.logging.LoggingHandler; +import java.io.IOException; +import org.dna.mqtt.commons.Constants; +import org.dna.mqtt.moquette.messaging.spi.IMessaging; +import org.dna.mqtt.moquette.parser.netty.MQTTDecoder; +import org.dna.mqtt.moquette.parser.netty.MQTTEncoder; +import org.dna.mqtt.moquette.server.ServerAcceptor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * + * @author andrea + */ +public class NettyAcceptor implements ServerAcceptor { + + private static final Logger LOG = LoggerFactory.getLogger(NettyAcceptor.class); + + EventLoopGroup m_bossGroup; + EventLoopGroup m_workerGroup; + + + public void initialize(IMessaging messaging) throws IOException { + m_bossGroup = new NioEventLoopGroup(); + m_workerGroup = new NioEventLoopGroup(); + + final NettyMQTTHandler handler = new NettyMQTTHandler(); + handler.setMessaging(messaging); + + ServerBootstrap b = new ServerBootstrap(); + b.group(m_bossGroup, m_workerGroup) + .channel(NioServerSocketChannel.class) + .childHandler(new ChannelInitializer() { + @Override + public void initChannel(SocketChannel ch) throws Exception { + ChannelPipeline pipeline = ch.pipeline(); + pipeline.addLast("logger", new LoggingHandler("Netty", LogLevel.ERROR)); + pipeline.addLast("decoder", new MQTTDecoder()); + pipeline.addLast("encoder", new MQTTEncoder()); + pipeline.addLast("handler", handler); + } + }) + .option(ChannelOption.SO_BACKLOG, 128) + .option(ChannelOption.SO_REUSEADDR, true) + .childOption(ChannelOption.SO_KEEPALIVE, true); + try { + // Bind and start to accept incoming connections. + ChannelFuture f = b.bind(Constants.PORT); + LOG.info("Server binded"); + f.sync(); + } catch (InterruptedException ex) { + LOG.error(null, ex); + } + } + + public void close() { + if (m_workerGroup == null) { + throw new IllegalStateException("Invoked close on an Acceptor that wasn't initialized"); + } + if (m_bossGroup == null) { + throw new IllegalStateException("Invoked close on an Acceptor that wasn't initialized"); + } + m_workerGroup.shutdownGracefully(); + m_bossGroup.shutdownGracefully(); + } + +} diff --git a/broker/src/main/java/org/dna/mqtt/moquette/server/netty/NettyChannel.java b/broker/src/main/java/org/dna/mqtt/moquette/server/netty/NettyChannel.java new file mode 100644 index 00000000..e83aa609 --- /dev/null +++ b/broker/src/main/java/org/dna/mqtt/moquette/server/netty/NettyChannel.java @@ -0,0 +1,86 @@ +package org.dna.mqtt.moquette.server.netty; + +import io.netty.channel.ChannelDuplexHandler; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.timeout.IdleState; +import io.netty.handler.timeout.IdleStateHandler; +import io.netty.util.Attribute; +import io.netty.util.AttributeKey; +import java.util.HashMap; +import java.util.Map; +import org.dna.mqtt.moquette.server.Constants; +import org.dna.mqtt.moquette.server.ServerChannel; + +/** + * + * @author andrea + */ +public class NettyChannel implements ServerChannel { + + private static class IdleTimoutHandler extends ChannelDuplexHandler { + @Override + public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { + if (evt instanceof IdleState) { + IdleState e = (IdleState) evt; + if (e == IdleState.ALL_IDLE) { + ctx.close(); + } /*else if (e.getState() == IdleState.WRITER_IDLE) { + ctx.writeAndFlush(new PingMessage()); + }*/ + } + } + } + + + private ChannelHandlerContext m_channel; + + private Map> m_attributesKeys = new HashMap>(); + + private static final AttributeKey ATTR_KEY_KEEPALIVE = new AttributeKey(Constants.KEEP_ALIVE); + private static final AttributeKey ATTR_KEY_CLEANSESSION = new AttributeKey(Constants.CLEAN_SESSION); + private static final AttributeKey ATTR_KEY_CLIENTID = new AttributeKey(Constants.ATTR_CLIENTID); + + NettyChannel(ChannelHandlerContext ctx) { + m_channel = ctx; + m_attributesKeys.put(Constants.KEEP_ALIVE, ATTR_KEY_KEEPALIVE); + m_attributesKeys.put(Constants.CLEAN_SESSION, ATTR_KEY_CLEANSESSION); + m_attributesKeys.put(Constants.ATTR_CLIENTID, ATTR_KEY_CLIENTID); + } + + public Object getAttribute(Object key) { + Attribute attr = m_channel.attr(mapKey(key)); + return attr.get(); + } + + public void setAttribute(Object key, Object value) { + Attribute attr = m_channel.attr(mapKey(key)); + attr.set(value); + } + + private synchronized AttributeKey mapKey(Object key) { + if (!m_attributesKeys.containsKey(key)) { + throw new IllegalArgumentException("mapKey can't find a matching AttributeKey for " + key); + } + return m_attributesKeys.get(key); + } + + public void setIdleTime(int idleTime) { + if (m_channel.pipeline().names().contains("idleStateHandler")) { + m_channel.pipeline().remove("idleStateHandler"); + } + if (m_channel.pipeline().names().contains("idleEventHandler")) { + m_channel.pipeline().remove("idleEventHandler"); + } + m_channel.pipeline().addFirst("idleStateHandler", new IdleStateHandler(0, 0, idleTime)); + m_channel.pipeline().addAfter("idleStateHandler", "idleEventHandler", new IdleTimoutHandler()); + } + + public void close(boolean immediately) { + m_channel.close(); + } + + public void write(Object value) { + m_channel.writeAndFlush(value); + } + +} diff --git a/broker/src/main/java/org/dna/mqtt/moquette/server/netty/NettyMQTTHandler.java b/broker/src/main/java/org/dna/mqtt/moquette/server/netty/NettyMQTTHandler.java new file mode 100644 index 00000000..7e39e1ca --- /dev/null +++ b/broker/src/main/java/org/dna/mqtt/moquette/server/netty/NettyMQTTHandler.java @@ -0,0 +1,72 @@ +package org.dna.mqtt.moquette.server.netty; + +import io.netty.channel.ChannelHandler.Sharable; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import java.util.HashMap; +import java.util.Map; +import org.dna.mqtt.moquette.messaging.spi.IMessaging; +import org.dna.mqtt.moquette.proto.Utils; +import org.dna.mqtt.moquette.proto.messages.AbstractMessage; +import static org.dna.mqtt.moquette.proto.messages.AbstractMessage.*; +import org.dna.mqtt.moquette.proto.messages.PingRespMessage; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * + * @author andrea + */ +@Sharable +public class NettyMQTTHandler extends ChannelInboundHandlerAdapter { + + private static final Logger LOG = LoggerFactory.getLogger(NettyMQTTHandler.class); + private IMessaging m_messaging; + private Map m_channelMapper = new HashMap(); + + @Override + public void channelRead(ChannelHandlerContext ctx, Object message) { + AbstractMessage msg = (AbstractMessage) message; + LOG.info(String.format("Received a message of type %s", Utils.msgType2String(msg.getMessageType()))); + try { + switch (msg.getMessageType()) { + case CONNECT: + case SUBSCRIBE: + case UNSUBSCRIBE: + case PUBLISH: + case PUBREC: + case PUBCOMP: + case PUBREL: + case DISCONNECT: + NettyChannel channel; + synchronized(m_channelMapper) { + if (!m_channelMapper.containsKey(ctx)) { + m_channelMapper.put(ctx, new NettyChannel(ctx)); + } + channel = m_channelMapper.get(ctx); + } + + m_messaging.handleProtocolMessage(channel, msg); + break; + case PINGREQ: + PingRespMessage pingResp = new PingRespMessage(); + ctx.write(pingResp); + break; + } + } catch (Exception ex) { + LOG.error("Bad error in processing the message", ex); + } + } + + @Override + public void channelInactive(ChannelHandlerContext ctx)throws Exception { + ctx.close(/*false*/); + synchronized(m_channelMapper) { + m_channelMapper.remove(ctx); + } + } + + public void setMessaging(IMessaging messaging) { + m_messaging = messaging; + } +} diff --git a/broker/src/test/java/org/dna/mqtt/moquette/server/ServerIntegrationFuseTest.java b/broker/src/test/java/org/dna/mqtt/moquette/server/ServerIntegrationFuseTest.java index cea7268e..11409151 100644 --- a/broker/src/test/java/org/dna/mqtt/moquette/server/ServerIntegrationFuseTest.java +++ b/broker/src/test/java/org/dna/mqtt/moquette/server/ServerIntegrationFuseTest.java @@ -17,7 +17,7 @@ import static org.junit.Assert.*; * Test the broker using the widely used FuseSource libraries * */ public class ServerIntegrationFuseTest { - + Server m_server; MQTT m_mqtt; BlockingConnection m_connection; @@ -57,9 +57,9 @@ public class ServerIntegrationFuseTest { Topic[] topics = {new Topic("/topic", QoS.AT_MOST_ONCE)}; byte[] futSub = m_connection.subscribe(topics); - + m_connection.publish("/topic", "Test my payload".getBytes(), QoS.AT_MOST_ONCE, false); - + Message msg = m_connection.receive(); msg.ack(); assertEquals("/topic", msg.getTopic());