From 89d0e7356aef0ef8c5d186322e93e0cc7e6f230a Mon Sep 17 00:00:00 2001 From: snickersun Date: Fri, 4 Sep 2020 22:25:39 +0800 Subject: [PATCH] add soure code core --- core | 1 - core/.classpath | 7 + core/.project | 23 + core/.properties.original | 1 + core/.travis.yml | 11 + core/README.md | 37 ++ core/build.gradle | 99 ++++ .../25ec1d29f49b0007a0a2169d49d41b12.bin | 1 + .../core/classes.dex | Bin 0 -> 27048 bytes .../5e84c834bc7ccd55cb3ab8cb23275e8e.bin | 1 + .../6217336ef9697f1a57d60152dcfed48e.bin | 1 + .../core/classes.dex | Bin 0 -> 27664 bytes .../main/se/wetcat/qatja/MQTTConstants.class | Bin 0 -> 1371 bytes .../main/se/wetcat/qatja/MQTTException.class | Bin 0 -> 634 bytes .../main/se/wetcat/qatja/MQTTHelper.class | Bin 0 -> 2515 bytes .../wetcat/qatja/MQTTIdentifierHelper.class | Bin 0 -> 3167 bytes .../main/se/wetcat/qatja/MQTTVersion.class | Bin 0 -> 537 bytes .../wetcat/qatja/messages/MQTTConnack.class | Bin 0 -> 1606 bytes .../wetcat/qatja/messages/MQTTConnect.class | Bin 0 -> 8829 bytes .../qatja/messages/MQTTDisconnect.class | Bin 0 -> 1363 bytes .../wetcat/qatja/messages/MQTTMessage.class | Bin 0 -> 3805 bytes .../wetcat/qatja/messages/MQTTPingreq.class | Bin 0 -> 1381 bytes .../wetcat/qatja/messages/MQTTPingresp.class | Bin 0 -> 1045 bytes .../se/wetcat/qatja/messages/MQTTPuback.class | Bin 0 -> 2325 bytes .../wetcat/qatja/messages/MQTTPubcomp.class | Bin 0 -> 2331 bytes .../wetcat/qatja/messages/MQTTPublish.class | Bin 0 -> 4311 bytes .../se/wetcat/qatja/messages/MQTTPubrec.class | Bin 0 -> 2303 bytes .../se/wetcat/qatja/messages/MQTTPubrel.class | Bin 0 -> 2343 bytes .../se/wetcat/qatja/messages/MQTTSuback.class | Bin 0 -> 1479 bytes .../wetcat/qatja/messages/MQTTSubscribe.class | Bin 0 -> 2770 bytes .../wetcat/qatja/messages/MQTTUnsuback.class | Bin 0 -> 1487 bytes .../qatja/messages/MQTTUnsubscribe.class | Bin 0 -> 2495 bytes core/build/libs/core.jar | Bin 0 -> 28046 bytes core/build/tmp/jar/MANIFEST.MF | 2 + core/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 51017 bytes core/gradle/wrapper/gradle-wrapper.properties | 5 + core/local.properties | 8 + .../java/se/wetcat/qatja/MQTTConstants.java | 162 ++++++ .../java/se/wetcat/qatja/MQTTException.java | 40 ++ .../main/java/se/wetcat/qatja/MQTTHelper.java | 183 ++++++ .../se/wetcat/qatja/MQTTIdentifierHelper.java | 143 +++++ .../java/se/wetcat/qatja/MQTTVersion.java | 52 ++ .../se/wetcat/qatja/messages/MQTTConnack.java | 106 ++++ .../se/wetcat/qatja/messages/MQTTConnect.java | 538 ++++++++++++++++++ .../wetcat/qatja/messages/MQTTDisconnect.java | 83 +++ .../se/wetcat/qatja/messages/MQTTMessage.java | 196 +++++++ .../se/wetcat/qatja/messages/MQTTPingreq.java | 88 +++ .../wetcat/qatja/messages/MQTTPingresp.java | 77 +++ .../se/wetcat/qatja/messages/MQTTPuback.java | 127 +++++ .../se/wetcat/qatja/messages/MQTTPubcomp.java | 130 +++++ .../se/wetcat/qatja/messages/MQTTPublish.java | 259 +++++++++ .../se/wetcat/qatja/messages/MQTTPubrec.java | 135 +++++ .../se/wetcat/qatja/messages/MQTTPubrel.java | 131 +++++ .../se/wetcat/qatja/messages/MQTTSuback.java | 93 +++ .../wetcat/qatja/messages/MQTTSubscribe.java | 135 +++++ .../wetcat/qatja/messages/MQTTUnsuback.java | 91 +++ .../qatja/messages/MQTTUnsubscribe.java | 116 ++++ core/travis_prebuild.sh | 12 + 58 files changed, 3093 insertions(+), 1 deletion(-) delete mode 160000 core create mode 100644 core/.classpath create mode 100644 core/.project create mode 100644 core/.properties.original create mode 100644 core/.travis.yml create mode 100644 core/README.md create mode 100644 core/build.gradle create mode 100644 core/build/.transforms/25ec1d29f49b0007a0a2169d49d41b12.bin create mode 100644 core/build/.transforms/25ec1d29f49b0007a0a2169d49d41b12/core/classes.dex create mode 100644 core/build/.transforms/5e84c834bc7ccd55cb3ab8cb23275e8e.bin create mode 100644 core/build/.transforms/6217336ef9697f1a57d60152dcfed48e.bin create mode 100644 core/build/.transforms/6217336ef9697f1a57d60152dcfed48e/core/classes.dex create mode 100644 core/build/classes/java/main/se/wetcat/qatja/MQTTConstants.class create mode 100644 core/build/classes/java/main/se/wetcat/qatja/MQTTException.class create mode 100644 core/build/classes/java/main/se/wetcat/qatja/MQTTHelper.class create mode 100644 core/build/classes/java/main/se/wetcat/qatja/MQTTIdentifierHelper.class create mode 100644 core/build/classes/java/main/se/wetcat/qatja/MQTTVersion.class create mode 100644 core/build/classes/java/main/se/wetcat/qatja/messages/MQTTConnack.class create mode 100644 core/build/classes/java/main/se/wetcat/qatja/messages/MQTTConnect.class create mode 100644 core/build/classes/java/main/se/wetcat/qatja/messages/MQTTDisconnect.class create mode 100644 core/build/classes/java/main/se/wetcat/qatja/messages/MQTTMessage.class create mode 100644 core/build/classes/java/main/se/wetcat/qatja/messages/MQTTPingreq.class create mode 100644 core/build/classes/java/main/se/wetcat/qatja/messages/MQTTPingresp.class create mode 100644 core/build/classes/java/main/se/wetcat/qatja/messages/MQTTPuback.class create mode 100644 core/build/classes/java/main/se/wetcat/qatja/messages/MQTTPubcomp.class create mode 100644 core/build/classes/java/main/se/wetcat/qatja/messages/MQTTPublish.class create mode 100644 core/build/classes/java/main/se/wetcat/qatja/messages/MQTTPubrec.class create mode 100644 core/build/classes/java/main/se/wetcat/qatja/messages/MQTTPubrel.class create mode 100644 core/build/classes/java/main/se/wetcat/qatja/messages/MQTTSuback.class create mode 100644 core/build/classes/java/main/se/wetcat/qatja/messages/MQTTSubscribe.class create mode 100644 core/build/classes/java/main/se/wetcat/qatja/messages/MQTTUnsuback.class create mode 100644 core/build/classes/java/main/se/wetcat/qatja/messages/MQTTUnsubscribe.class create mode 100644 core/build/libs/core.jar create mode 100644 core/build/tmp/jar/MANIFEST.MF create mode 100644 core/gradle/wrapper/gradle-wrapper.jar create mode 100644 core/gradle/wrapper/gradle-wrapper.properties create mode 100644 core/local.properties create mode 100644 core/src/main/java/se/wetcat/qatja/MQTTConstants.java create mode 100644 core/src/main/java/se/wetcat/qatja/MQTTException.java create mode 100644 core/src/main/java/se/wetcat/qatja/MQTTHelper.java create mode 100644 core/src/main/java/se/wetcat/qatja/MQTTIdentifierHelper.java create mode 100644 core/src/main/java/se/wetcat/qatja/MQTTVersion.java create mode 100644 core/src/main/java/se/wetcat/qatja/messages/MQTTConnack.java create mode 100644 core/src/main/java/se/wetcat/qatja/messages/MQTTConnect.java create mode 100644 core/src/main/java/se/wetcat/qatja/messages/MQTTDisconnect.java create mode 100644 core/src/main/java/se/wetcat/qatja/messages/MQTTMessage.java create mode 100644 core/src/main/java/se/wetcat/qatja/messages/MQTTPingreq.java create mode 100644 core/src/main/java/se/wetcat/qatja/messages/MQTTPingresp.java create mode 100644 core/src/main/java/se/wetcat/qatja/messages/MQTTPuback.java create mode 100644 core/src/main/java/se/wetcat/qatja/messages/MQTTPubcomp.java create mode 100644 core/src/main/java/se/wetcat/qatja/messages/MQTTPublish.java create mode 100644 core/src/main/java/se/wetcat/qatja/messages/MQTTPubrec.java create mode 100644 core/src/main/java/se/wetcat/qatja/messages/MQTTPubrel.java create mode 100644 core/src/main/java/se/wetcat/qatja/messages/MQTTSuback.java create mode 100644 core/src/main/java/se/wetcat/qatja/messages/MQTTSubscribe.java create mode 100644 core/src/main/java/se/wetcat/qatja/messages/MQTTUnsuback.java create mode 100644 core/src/main/java/se/wetcat/qatja/messages/MQTTUnsubscribe.java create mode 100644 core/travis_prebuild.sh diff --git a/core b/core deleted file mode 160000 index b6b76d5e..00000000 --- a/core +++ /dev/null @@ -1 +0,0 @@ -Subproject commit b6b76d5e81e7d87cfca5b1bd72c57d351461640c diff --git a/core/.classpath b/core/.classpath new file mode 100644 index 00000000..45b0c68c --- /dev/null +++ b/core/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/core/.project b/core/.project new file mode 100644 index 00000000..568a4ab0 --- /dev/null +++ b/core/.project @@ -0,0 +1,23 @@ + + + core + Project core created by Buildship. + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.buildship.core.gradleprojectbuilder + + + + + + org.eclipse.buildship.core.gradleprojectnature + org.eclipse.jdt.core.javanature + + diff --git a/core/.properties.original b/core/.properties.original new file mode 100644 index 00000000..4b7e5a3d --- /dev/null +++ b/core/.properties.original @@ -0,0 +1 @@ +ARTIFACT_VERSION=@VERSION@ diff --git a/core/.travis.yml b/core/.travis.yml new file mode 100644 index 00000000..7dd56bd1 --- /dev/null +++ b/core/.travis.yml @@ -0,0 +1,11 @@ +language: java + +before_install: + - ./travis_prebuild.sh; + +deploy: + provider: script + script: gradle clean bintrayUpload + skip_cleanup: true + on: + tags: true \ No newline at end of file diff --git a/core/README.md b/core/README.md new file mode 100644 index 00000000..542c6a15 --- /dev/null +++ b/core/README.md @@ -0,0 +1,37 @@ +## Qatja + +[![Build Status](https://travis-ci.org/Qatja/core.svg?branch=master)](https://travis-ci.org/Qatja/core) + +[![Download](https://api.bintray.com/packages/wetcat/Qatja/core/images/download.svg)](https://bintray.com/wetcat/Qatja/core/_latestVersion) + +MQTT Java Library, conforms to MQTT 3.1.1 + +This library is barebones, it contains no platform specific implementations such as threading or connectionhandling. If you plan on using this for Android projects you should look at [Qatja Android](https://github.com/Qatja/qatja-android) which uses a predefined service. + +### Installation + +Add the following to your build.gradle file + +#### Gradle + +```groovy +dependencies { + compile 'se.wetcat.qatja:core:1.0.1' +} +``` + +#### Maven + +```xml + + se.wetcat.qatja + core + 1.0.1 + pom + +``` + +### Acknowledgements + +Based on the work by [Andreas Göransson](https://github.com/agoransson) and [David Cuartielles](https://github.com/dcuartielles) in the book ["Professional Android Open Accessory programming with Arduino"](https://github.com/aoabook) and later [mqtt4processing](https://github.com/agoransson/mqtt4processing) and [mqtt4android](https://github.com/agoransson/mqtt4android). + diff --git a/core/build.gradle b/core/build.gradle new file mode 100644 index 00000000..3a3aa924 --- /dev/null +++ b/core/build.gradle @@ -0,0 +1,99 @@ +apply plugin: 'java' +apply plugin: 'maven-publish' + +buildscript { + repositories { + jcenter() + maven { + url 'https://maven.google.com/' + name 'Google' + } + } + dependencies { + classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.7.3' + } +} + +apply plugin: 'com.jfrog.bintray' + +repositories { + jcenter() + maven { url 'https://jitpack.io' } + maven { + url 'https://maven.google.com/' + name 'Google' + } +} + +dependencies { + compile fileTree(dir: 'libs', include: ['*.jar']) + + // https://mvnrepository.com/artifact/com.googlecode.juniversalchardet/juniversalchardet + compile group: 'com.googlecode.juniversalchardet', name: 'juniversalchardet', version: '1.0.3' +} + +sourceCompatibility = "1.7" +targetCompatibility = "1.7" + +task javadocJar(type: Jar) { + baseName = "$project.name" + version = getVersion() + classifier = 'docs' + from javadoc +} + +task sourcesJar(type: Jar) { + baseName = "$project.name" + version = getVersion() + from sourceSets.main.output +} + +publishToMavenLocal.dependsOn build +bintrayUpload.dependsOn build + +publishing { + publications { + driverPublish(MavenPublication) { + groupId 'se.wetcat.qatja' + artifactId "$project.name" + version getVersion() + artifact sourcesJar + artifact javadocJar + // artifact("$buildDir/libs/$project.name-release.jar") + pom.withXml { + def dependenciesNode = asNode().appendNode('dependencies') + configurations.compile.allDependencies.each { + if (it.group != null && it.name != null && + !"unspecified".equals(it.name) && it.version != null && + !"unspecified".equals(it.version)) { + def dependencyNode = dependenciesNode.appendNode('dependency') + dependencyNode.appendNode('groupId', it.group) + dependencyNode.appendNode('artifactId', it.name) + dependencyNode.appendNode('version', it.version) + } + } + } + } + } +} + +bintray { + user = System.getenv('BINTRAY_USER') + key = System.getenv('BINTRAY_API_KEY') + publications = ['driverPublish'] + + publish = true + + pkg { + repo = 'Qatja' + name = "$project.name" + userOrg = 'wetcat' + + version { + name = getVersion() + gpg { + sign = true + } + } + } +} diff --git a/core/build/.transforms/25ec1d29f49b0007a0a2169d49d41b12.bin b/core/build/.transforms/25ec1d29f49b0007a0a2169d49d41b12.bin new file mode 100644 index 00000000..ae91532f --- /dev/null +++ b/core/build/.transforms/25ec1d29f49b0007a0a2169d49d41b12.bin @@ -0,0 +1 @@ +o/core diff --git a/core/build/.transforms/25ec1d29f49b0007a0a2169d49d41b12/core/classes.dex b/core/build/.transforms/25ec1d29f49b0007a0a2169d49d41b12/core/classes.dex new file mode 100644 index 0000000000000000000000000000000000000000..919396697b692b75d0c935cc39b1567f0a4612a1 GIT binary patch literal 27048 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 e2(umhw?uX1Wf>kPPVf--MdQ! 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 M3ngG1*95%!18G^<-T(jq literal 0 HcmV?d00001 diff --git a/core/build/classes/java/main/se/wetcat/qatja/MQTTException.class b/core/build/classes/java/main/se/wetcat/qatja/MQTTException.class new file mode 100644 index 0000000000000000000000000000000000000000..7f266b6e92ed2db4a9eb2ded0cfe36c644f21435 GIT binary patch literal 634 zcmaKo%}&Bl5QWcekphAs3jQQ0m}syVyD+XWY>Xx*)x^-SztjdUfmYihzLqNz7e0Uw zWt>|o{t>&F&P>jHbMCyozq|rC!FCFz43Xc9friYcjqwV zP2oB><5Ab~E#4hcr)YXMA9lDeWF0pXVP6CcyJp}R6DPEJXgu(6z>Vu$%er{7o%>LD zZj+%@HwXNY8$<5)jCSY?x7U>Gw2>18ychBR2pIGQW8Xkl-}ffcPQ*%wpC#>EsA9;p zy|HgQmm+%750}SsKf{V_R_ETcNFpagfuZv6D;d_lUNE0W4GB88{8Z4jp;1gxbqa+t zK+}tC3gwwn1!;;I`U4e`?obY7XEH2Ph6s_15iL?mZBlP6Ak=8>UPLI*R55|{SV5i` s(oP{}U6DPNSeo8AnnL|;XP=}85v)3s&0$&$qfxA)NUP`&1X8g60pgT;wg3PC literal 0 HcmV?d00001 diff --git a/core/build/classes/java/main/se/wetcat/qatja/MQTTHelper.class b/core/build/classes/java/main/se/wetcat/qatja/MQTTHelper.class new file mode 100644 index 0000000000000000000000000000000000000000..e200d4017fab580551bbd44e37bf577c0a9b20b6 GIT binary patch literal 2515 zcmb7GS#KLv6#m9`;)(5S(-w-Glt338r>(ovC2dLVIE{(zEOt`5(N64%Gl?@!JmaJd zP>E-rctEHi1PGyuO}Iiz5lHy~{2tywl<&?=61NEmBAt8BJ@=gNo^_nx{`zGdz$rZN zBNM?`!OkFDu8QvOU8Av-;i-jg?iS6w6sv-6-`- zXiZcM(^3sdhL)`@Ol#G=I$fqjNmmMLc}lJ7 z;_M22)~s$y=t`SfY(=vQsuf#OtvNN88PDg3weq4?<&(|2={VFBxf$=hLBVuwW=5+@ z@Z5|`kc*n76|4%+$J3Q+DYj5q)yriyRx|Wvt!k>}!mL`QQfzWVFz8wkT-J<2rO4PM zgl1LqnqDpzs7R+SJIu@piTheI%7g-{bn;hmmyI*oN; zn;kvp$8rcOxW@t<2;n|f$@Ka0P6+Sfy%3UM8U2aTY&Mz5yWEw;2)VJzcsi9EW`B~i zMS__~CLGB`GVLfOMl)j)T5^-|Tw)>>Px6|)Z4jKyHi%{W(My{_>65}p$Z;C#F=drC4;o58qkD!-)$WcHPeLS_`2#!)Bj91{}*BdzW zE41NzL#o-U0SA&f=Vlea6KOM%O#!)lNKp@ZnY%4qUDQF1YFP`AvwE zE`%Ii-_ibdgT1_If0n6na(vdcZw)K$5B|xCQUgdUGkTHnv@pXPAg5iBfB@Oijim;J zz$U^aR>g6{B7!J&ceum9>ifdY?Zt3M^VaE-=pQH5ZMJd^ZS0XQ+_S-*vUcMQmKnTh z#}&j0-$hE0^Vnqw!WSBl{zOO`5VaWGOyp(^QST}LYDfF_QoR363>)2(#E=`qN$#Rk zN#M1Su9Ds)Wk_SB9BGPl zopg(Ihcr#nNVBAQlA$PdWmjFe z=@HI4xs9WP{6}=}V~pS-viKC^_zZb`&VQV~#0`9f+xQxH@dyQcgBg4W9Y3IqA5p<$ g{*wIE5NM5V4-ij`z%I1^fnm=xe~fU1S01GQ2IUs_Hvj+t literal 0 HcmV?d00001 diff --git a/core/build/classes/java/main/se/wetcat/qatja/MQTTIdentifierHelper.class b/core/build/classes/java/main/se/wetcat/qatja/MQTTIdentifierHelper.class new file mode 100644 index 0000000000000000000000000000000000000000..2a590923e04f4e935ef849f51273652828c515ad GIT binary patch literal 3167 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 literal 0 HcmV?d00001 diff --git a/core/build/classes/java/main/se/wetcat/qatja/messages/MQTTConnect.class b/core/build/classes/java/main/se/wetcat/qatja/messages/MQTTConnect.class new file mode 100644 index 0000000000000000000000000000000000000000..924bc70e3dc9b003ac60e8e98660e4f102610ee7 GIT binary patch literal 8829 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 Fe*~z^L974( literal 0 HcmV?d00001 diff --git a/core/build/classes/java/main/se/wetcat/qatja/messages/MQTTDisconnect.class b/core/build/classes/java/main/se/wetcat/qatja/messages/MQTTDisconnect.class new file mode 100644 index 0000000000000000000000000000000000000000..37be148b3fbdd1eebfc2a4fc2eeb543fe313546e GIT binary patch literal 1363 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 literal 0 HcmV?d00001 diff --git a/core/build/classes/java/main/se/wetcat/qatja/messages/MQTTPingreq.class b/core/build/classes/java/main/se/wetcat/qatja/messages/MQTTPingreq.class new file mode 100644 index 0000000000000000000000000000000000000000..4fb821d2607709ed510e4a70cdfcc264170f4e58 GIT binary patch literal 1381 zcmb7E+fGwK6kXGv_CPsY6hTqyU2KbTydVk+2#DHP1f`M~UmVIP2 z<+9AHvO9Mtn_ckye4w6P7O-ZDE4&`1*1yZZ)-nv@3spN_ZNFkAYFf9;S@$#M)5VXU^3avw# zC-S*;y`nq4q199n$c_1mE>-lR4wPIa`^Y1ch-3@O&+Es$oEB(b)H3&UPL^scDWX7Z zc0H#`+AI2H8zVL@3iLL(G1-S<$c;i#GJ9;XSP60@ZFK4SNxvb(PawWsYZEfV zT6_*oeU5KG|J_?S=*AugmoUnNf9>1B7!EkNj4KYt(dS^oER)8)ioSo9o7ZZ1!7#3IKxQ$8>$t)1Eu?FlhskS*Jon=O8Ixh1 zeG6`)A8~fn#ViMT8e%>dhbUs07ASD&8wP9~72+%Q3mhXBWz=z;V5Wg)@3X{=?Cp)- s8^pJSH2+4xZ4#fa5$fFvZqkUy0nim0w*UYD literal 0 HcmV?d00001 diff --git a/core/build/classes/java/main/se/wetcat/qatja/messages/MQTTPingresp.class b/core/build/classes/java/main/se/wetcat/qatja/messages/MQTTPingresp.class new file mode 100644 index 0000000000000000000000000000000000000000..cfb9142c3dc718bf5a357ec9e15da5344cb4e4d2 GIT binary patch literal 1045 zcma)4+fEZv6kTU#+JQP+S`;rJ77^$L7$Y&!s65n5tSJiEkQkm$+tV_novAZZ1!H{j z2mBL#sfi|jfFEVtd)k_&57JDs=bU|6d#`o&?>|3&0ocNA6E_kN$Rsd@B|hC`v&`le zn-vqQ0*Qw2wTs|rri4wU^~g*6%4Z)JEa?Y0U6*;IkOvsbC?I&RZfLFa*h z@z|>?fn?cn)yr)=0`Z5A>xA0^F>ZN4VBwJy z2&_%qT{r@wBrw}luJUE5o;fF~{#?mAGcmF`Zj*R=Qd6DK@!Y_~nu)By+%SJQ->@V( z4D8C&mM7~}{o3pLH3DW*sZsWu$MT&d2`7@u!VFSuau)I^SXjq~g-!O^NYQKj7iFif zzQFbIG#=<5KN5mxvK`MZy&CcASytB!R9HFf5Dg<6$(<(0zG_Q)1-DsNZZkaMSc#KU z6dM;OLB4@5Sfb^bq8$Tbn5K`H>0GpPI-P*&pAc{I-=Te_iw28qJPIu1Je{q65-uPa z#Yvn2z(?rx0jpH3>yPq1C@}h@08}nOK&FG6T$rfN>gF4xm z!yU}yE;6`>72J=yX8OoTW0rEMN7SDdk+Lr063uB)-DUbSm35kksc2%H!}bUVZGd)# eGUHTv71s!5egc#yXQC`jfb!xD6xtc)fW^Ne0K=#N literal 0 HcmV?d00001 diff --git a/core/build/classes/java/main/se/wetcat/qatja/messages/MQTTPuback.class b/core/build/classes/java/main/se/wetcat/qatja/messages/MQTTPuback.class new file mode 100644 index 0000000000000000000000000000000000000000..9f5b5e33736fe3c5bf57083548ef893ffc1bfd14 GIT binary patch literal 2325 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 B*Ot zX9i9L;KNA~i2!zELbOvNCPkd~Q=AE43Q5tF5wGpUEhWYCiY^Vh_Hw+OkBm6Ca#I?x`?k3GO%F6!lH=`MoeT8GVvBhnE#cC zn6PocL{6gQamd7y2uH*+ZWt(-D57Md4322;D@&bjF!+BuZbQsDwkk@_JZDwILJp&5 zyK`$5BB_Nvw(O!|_GMc4(|pk`T4ZD?pYlejp!;U?JhX<@>t!lfrF<;CR&(v50>34s z$doH<3L@bKT|9@G#brC=9!+jz^4Q)}-I~s*v?!ptUdj4O)p1#RU*2}N@+LrAn=(7x za+{wDc6m!Lom>LKvpcjEbD__aU8o56)#)^;FUL_%Kq{TUJG^~%^O^$;`0*x&pkR-8 z?WxoN{}cGG3_XJS6JLB7rtI(*_9IB$Y&Z#X#2Ifh;5GpwkDzT05Pc!Qy9jv+$lGRs zS*G*R*2~R$q_<-O9g#g7Fn;#@q>z4Slw*{K5x^1jVBCv2*w}~nu%GLU;FYJ)$^+4e zu3U%CMt}zrT|FL&4&U`DW5%uCp4<1BNsFuFnmVpG%;WliHlX+3xgA_u7}tV*SYL_{ z=zQG%?%AJ_0c``F5&gH$hww*-Bfr3}ir9RNK!nN;?hI2!Mrc{!?Qo48`vexY9MrogarHT z!~$!#*kz8Lsx^OE@*(#H16m5+ZbeM6E=rh7)06Xz+()ukEZ&D zsLgg7C#^y7I@?*nPOK8=Th9?Z)Gl&Qv&a{dA8RL{Y$oq&Cx0Q0{09QuVVd>l!gsa9 O>)V7^$UnId;{O6m*)aot1TPf9|qFl9=YU>+@)|V0!H6|X|Q@XvdaF@j^e(3ds z-;G~QH1{W5jW3Ow{s4c4|HH&OX8|>h(Qd*pGiT1t`&=Otr9Q)HO`<-*KNTNDLAf}ZPiM`EAmp6Jh4K(%&RxJ=SaKjG)mnP z%IzrzW5RJAKgXcUf;R}vUvQcX``1~UISedM&DUC0-)U4G*}Pg6E+N`Z#qsHw%50qT zsn;3a2%4brn20fKt_W9n+!q&|`@+5?xGl4_J-HccEik!2X@#rLk% zONsOjuJU%Z&TYz`tGB!|0n0f=zK?5ck>BGCd)F~p3!AY;ia#x!z)1_IFm7Q2F%zdP zv=rD)d%l6*4nRi;7B-T-DR^5{ z(tyx=u$!s3Q2r-FL4>3c91J5$y449nKVrc-QPTlnKE)a2Tc%`HJ)IgIdZe(7`5+ou zdh!OEmQ%Bun$xzon=<^aX}3k3*z}R-Pl#30t`1G%~hHht~eGmGvx+KlOrPKYWew z3+i1B?t3}7+ZZ(ZLKrxL0UX01j^htxv4d7{7$9leBAODEGXQ0k zT!x7qCU!DNh_)`t0;^#+CL1ocwzSpu zr7u48p%2;_U;5ISUZw`8%+UA#5&Z-DCv+U`?`#rA5Ida==R4o|&UY@qbI!N?`TX}4 z0LSpPiuYAq48ezZ2>a11+@%_lgpfYjURKc`LJfLFG9cWbh=+tEg$#>qM7YrqKEM?r zV=Ar&F&;t#t_5*Dhz~>f2sh-&P%#n0F7yVGk~J;Fl;cSuQ(|peg{44qovfAWu`;fi z%PXiKPuw(SjcCTOr=m$WXW3Jy6$A#IBoBS#F$J}@ITyF{u3@K41?R&XTo{kV+Y)&* zI%m2m!;RiH+?z&p#?0r9DKj7K8yp@^7AEqkoHb#J@h&G#OIs3_ZC)k|`w+RMSX=}=I>7TscDUjP!g3WTGu?5$>kjohh{RKB$aEYO5%rMQW zNioEDGp0S|PAk}ybdA)lJ|pX8#s*Lj>Yh)TS=Vyxyo#F&nkqq5K&ZsArE)M}EMy#m zW);?wQg?VwIU5=8Mg>)=S*mF$=@fD)X6YfmrZz{U6BM+rucRn-F+{e2OHdE4Ki|00 z8lreh$Sn;S%xE}(H#8i?n;L95bmR5i>j2b{MTdsla5Us_Tti-nE8Bu_vzSvcuVDdq zG<=LZuL*k)1bhyP2C$5ebSp0 z-LTU-0o1dGm6N1CQP8`w!ThvS$fWgYW0of9GH=Y7y6yG3?o6(+ny;8;w~P$;96qVy zu7*$XnTADtu3-E6(To+WjMtZY;`XeOvC?bI>871>(wyK+=Js`B9vqYVlo z+{IA^KiAr2_{JjbElcqK>?yaCR#OQ$fwTkPNWz>uy&kZfMLaZUo;(z*?c*#{i~Su^KaA3E(Eunw#N!jw340D9ST5D@01FS0163 ztGegFXzhn-Xivivf6TtP~7t=KfL&+0!XR8ox~eR6Oc(( zWD5IWu^Km7nGD{;46AAL8vhc?!w%N8rs)|J< zLPu8u>UNR0!Zv6{CXx)tYr+*C55!gHuCT8QZp&ybGcJ3JetowpT7lDaeG}6r3JimN z|Nc<@kXYYvgLmppZd3G1v+Y$WvD`#r|H`H|_$Fr^@s)*37_x8~Nec@YVidZZMHge zx0&*P4$GAdQ4MBG|Dy_%$Gn48%~l_QM2irogDu&@4V*PP417xWeuUl>tLuaWlA)cT=_X(S`5C0!p=&rS?KP*~o) z7mscI{sNj-R12C~(7Wn_p3+i!{MW7Y`kjK7PT>A}F{RVr*7n}t*_8GIdRBim@Epd( zaP}DtRg&Eum|0>5Lcx~i2(gjfM@Vj`r zls!@zCPqx0VDte0phbQs2~s75aFURa5Gw2-cIAE8DZM^Uk*7hCr!h)d#txC?=6kc8 RIYgH4-;;&Dd~yb4-U5>7E!+SA literal 0 HcmV?d00001 diff --git a/core/build/classes/java/main/se/wetcat/qatja/messages/MQTTUnsubscribe.class b/core/build/classes/java/main/se/wetcat/qatja/messages/MQTTUnsubscribe.class new file mode 100644 index 0000000000000000000000000000000000000000..c2e3fa789ea1105ab46152c8d084602831e62a41 GIT binary patch literal 2495 zcmb7FT~iZh6n-`dBra<~zEmWdR@;W4M5$UofNBsV8U#r|5vwlA5*EWICL2Eb$+Vr$ zc;k#Wo#}sHcGa48tw#i}b-Fv81*oz(5{Bi&xm!;40t zpr?&Oc=To>F=-WwvxQ{doHgjsGnh@$Q)A4uj2p$pStFm&XEW3^$FfO1Go|NE*;kaR znKCTfoHLEQ0wt=TzR$EwdqBbd-Jwh|f<13CpV0q7)si+WBd^=Wu(@KSMhrbA0X@O4 znTUeAl$kbd#;5Gg7AYMmQ)OnukyYEcoX_j4V?{exw8@*HFEY)#IdMe18N*83^9uIH zZ9VyLRL?n?vEmBUp_QbOv(2nk@FJ<8y$YgALKR12^KPkQ9n69O6!{_)y!&&6SDN|d(mkN&kC&Q!V zk>Z?INsNwdOVu6hV(WVgY>CwztLzCzMA1scIQx#n7~M8Wo?ui?J+@<8x12X^-r2{7 zs%<@$x~;Bjrnd=Wk+d&*g-d5$#=&P2NIP@(5?hQ)C@vD=j$u`5O9 zbo=aV`3=d0gIzleW&S0^6yNnO{sDzg&H<%*JjOA+OU--S4Zy<_K;3|Ax}$p??w`4H z;eE$a;Tq%*XfIo#3nA`k?|4)2l?+?OSCTevp0$P6;R!XVCDetE|AzX|p%T2m@SyCr z9d(pXGVm$7=|v+>qZMbIFoCiIoInq4=*kHwFxs4eF5U|BuFC=cH=p9`_6B^@>(~g?S&uw31O6YNwRtG6QPth% z;SK+@SATVv&?r>69V&XEQNF-WS3eGrG(QH3>G z@tx)O?-cX&2+~SsK8*9Te44}^;Jq_EbS{erGQ7P9CS0)rsvqlJSt+dh?R88C1`fQOfa1U9ZupfZf&db(JhdNDj;7E z-d7E-s7y~P{-he51*qSAhP@kTnQm@fN82Cn{!T~vBhvEh>}_r@VgGX+_+B8lOmE;I zOV`oN9&rpidnuIQm%H8N8zmgB=Hqy9_D)jO^CKk$zHuo(z51@2XSr-WXdt?~%<~?a za33Ae+4-}iIf*IxIPkZ`Ps7*LKjuqNI$c_@lW+ZgmO_Yqvc<-?6*i|&DQxWD<8n{lt)(+PrC!|(3s032j5i%>6{{`pZG5i1k literal 0 HcmV?d00001 diff --git a/core/build/libs/core.jar b/core/build/libs/core.jar new file mode 100644 index 0000000000000000000000000000000000000000..a218237beab473e817497eadb3c741bf44874f46 GIT binary patch literal 28046 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�$47Be77*?nU+{&KS5^#G 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 Cv)w2F literal 0 HcmV?d00001 diff --git a/core/build/tmp/jar/MANIFEST.MF b/core/build/tmp/jar/MANIFEST.MF new file mode 100644 index 00000000..58630c02 --- /dev/null +++ b/core/build/tmp/jar/MANIFEST.MF @@ -0,0 +1,2 @@ +Manifest-Version: 1.0 + diff --git a/core/gradle/wrapper/gradle-wrapper.jar b/core/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..3d0dee6e8edfecc92e04653ec780de06f7b34f8b GIT binary patch literal 51017 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 H-~Rf4?cF@p literal 0 HcmV?d00001 diff --git a/core/gradle/wrapper/gradle-wrapper.properties b/core/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..1a45772f --- /dev/null +++ b/core/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-2.6-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/core/local.properties b/core/local.properties new file mode 100644 index 00000000..6f448491 --- /dev/null +++ b/core/local.properties @@ -0,0 +1,8 @@ +## This file must *NOT* be checked into Version Control Systems, +# as it contains information specific to your local configuration. +# +# Location of the SDK. This is only used by Gradle. +# For customization when using a Version Control System, please read the +# header note. +#Fri Sep 04 17:00:12 CST 2020 +sdk.dir=C\:\\Users\\incar\\AppData\\Local\\Android\\Sdk diff --git a/core/src/main/java/se/wetcat/qatja/MQTTConstants.java b/core/src/main/java/se/wetcat/qatja/MQTTConstants.java new file mode 100644 index 00000000..7d903f04 --- /dev/null +++ b/core/src/main/java/se/wetcat/qatja/MQTTConstants.java @@ -0,0 +1,162 @@ +package se.wetcat.qatja; + +/* + * Copyright (C) 2012 Andreas Goransson, David Cuartielles + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Defines all MQTT constants; including message types, quality of service, etc. + * + * @author Andreas Goransson + * @version 1.0.0 + * @since 2017-05-06 + */ +public class MQTTConstants { + + /** + * Client request to connect to a server + */ + public static final byte CONNECT = 0x01; + + /** + * Connect Acknowledgement + */ + public static final byte CONNACK = 0x02; + + /** + * Publish message + */ + public static final byte PUBLISH = 0x03; + + /** + * Publish Acknowledgment + */ + public static final byte PUBACK = 0x04; + + /** + * Publish Received + */ + public static final byte PUBREC = 0x05; + + /** + * Publish Release + */ + public static final byte PUBREL = 0x06; + + /** + * Publish Complete + */ + public static final byte PUBCOMP = 0x07; + + /** + * Client Subscription request + */ + public static final byte SUBSCRIBE = 0x08; + + /** + * Subscription Acknowledgment + */ + public static final byte SUBACK = 0x09; + + /** + * Client Unsubscribe request + */ + public static final byte UNSUBSCRIBE = 0x0a; + + /** + * Unsubscribe Acknowledgment + */ + public static final byte UNSUBACK = 0x0b; + + /** + * PING Request + */ + public static final byte PINGREQ = 0x0c; + + /** + * PING Response + */ + public static final byte PINGRESP = 0x0d; + + /** + * Client is Disconnecting + */ + public static final byte DISCONNECT = 0x0e; + + /** + * Fire and Forget, QoS:0 + */ + public static final byte AT_MOST_ONCE = 0x00; + + /** + * Acknowledged deliver, QoS:1 + */ + public static final byte AT_LEAST_ONCE = 0x01; + + /** + * Assured Delivery, QoS:2 + */ + public static final byte EXACTLY_ONCE = 0x02; + + /** + * Connection was accepted by server + */ + public static final byte CONNECTION_ACCEPTED = 0x00; + + /** + * The Server does not support the level of the MQTT protocol requested by the + * Client + */ + public static final byte CONNECTION_REFUSED_VERSION = 0x01; + + /** + * The Client identifier is correct UTF-8 but not allowed by the Server + */ + public static final byte CONNECTION_REFUSED_IDENTIFIER = 0x02; + + /** + * The Network Connection has been made but the MQTT service is unavailable + */ + public static final byte CONNECTION_REFUSED_SERVER = 0x03; + + /** + * The data in the user name or password is malformed + */ + public static final byte CONNECTION_REFUSED_USER = 0x04; + + /** + * The Client is not authorized to connect + */ + public static final byte CONNECTION_REFUSED_AUTH = 0x05; + + /* + * Subscription return codes + */ + public static final byte SUBSCRIBE_SUCCESS_AT_MOST_ONCE = AT_MOST_ONCE; + public static final byte SUBSCRIBE_SUCCESS_AT_LEAST_ONCE = AT_LEAST_ONCE; + public static final byte SUBSCRIBE_SUCCESS_EXACTLY_ONCE = EXACTLY_ONCE; + public static final byte SUBSCRIBE_FAILURE = (byte) 0x80; + + /** + * Minimum length of strings (byte[] length) + */ + public static final int MIN_LENGTH = 0; + + /** + * Maximum length of strings (byte[] length) + */ + public static final int MAX_LENGTH = 65535; + +} diff --git a/core/src/main/java/se/wetcat/qatja/MQTTException.java b/core/src/main/java/se/wetcat/qatja/MQTTException.java new file mode 100644 index 00000000..032b9fc8 --- /dev/null +++ b/core/src/main/java/se/wetcat/qatja/MQTTException.java @@ -0,0 +1,40 @@ +package se.wetcat.qatja; + +/* + * Copyright (C) 2014 Andreas Goransson + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * MQTT Exception, defines a specific error according to MQTT rules + * + * @author Andreas Goransson + * @version 1.0.0 + * @since 2017-05-07 + */ +public class MQTTException extends Exception { + + public MQTTException() { + super("MQTT Exception"); + } + + public MQTTException(String message) { + super(message); + } + + public MQTTException(String message, Throwable throwable) { + super(message, throwable); + } + +} diff --git a/core/src/main/java/se/wetcat/qatja/MQTTHelper.java b/core/src/main/java/se/wetcat/qatja/MQTTHelper.java new file mode 100644 index 00000000..4be998ef --- /dev/null +++ b/core/src/main/java/se/wetcat/qatja/MQTTHelper.java @@ -0,0 +1,183 @@ +package se.wetcat.qatja; + +/* + * Copyright (C) 2014 Andreas Goransson + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import org.mozilla.universalchardet.UniversalDetector; +import se.wetcat.qatja.messages.MQTTMessage; + +import static se.wetcat.qatja.MQTTConstants.CONNECT; +import static se.wetcat.qatja.MQTTConstants.CONNACK; +import static se.wetcat.qatja.MQTTConstants.PUBLISH; +import static se.wetcat.qatja.MQTTConstants.PUBACK; +import static se.wetcat.qatja.MQTTConstants.PUBREC; +import static se.wetcat.qatja.MQTTConstants.PUBREL; +import static se.wetcat.qatja.MQTTConstants.PUBCOMP; +import static se.wetcat.qatja.MQTTConstants.SUBSCRIBE; +import static se.wetcat.qatja.MQTTConstants.SUBACK; +import static se.wetcat.qatja.MQTTConstants.UNSUBSCRIBE; +import static se.wetcat.qatja.MQTTConstants.UNSUBACK; +import static se.wetcat.qatja.MQTTConstants.PINGREQ; +import static se.wetcat.qatja.MQTTConstants.PINGRESP; +import static se.wetcat.qatja.MQTTConstants.DISCONNECT; + +/** + * Helper methods for MQTT + * + * @author Andreas Goransson + * @version 1.0.0 + * @since 2017-05-07 + */ +public class MQTTHelper { + +// private static int lastPackageIdentifier = 1; + + private static final String UTF8 = "UTF-8"; + + /** + * Detects if string is encoded with UTF-8 + * + * @param buffer String + * @return true if UTF-8, false otherwise + */ + public static boolean isUTF8(byte[] buffer) { + UniversalDetector detector = new UniversalDetector(null); + detector.handleData(buffer, 0, buffer.length); + detector.dataEnd(); + + String encoding = detector.getDetectedCharset(); + detector.reset(); + + return (UTF8.equals(encoding)); + } + + public static boolean hasWildcards(String topic) { + // TODO, is the $ acceptable? + return /*topic.contains("/") ||*/ topic.contains("#") || topic.contains("+"); + } + +// public static int getNewPackageIdentifier() { +// return (lastPackageIdentifier += 1); +// } + + /** + * Return the MSB of a string length + * + * @param buffer String + * @return MSB + */ + public static byte MSB(byte[] buffer) { + return (byte) ((buffer.length) >> 8 & 0xFF); + } + + /** + * Return the LSB of the string length + * + * @param buffer String + * @return LSB + */ + public static byte LSB(byte[] buffer) { + return (byte) (buffer.length & 0xFF); + } + + /** + * Return most significant byte of integer + * + * @param val the value + * @return the MSB + */ + public static byte MSB(int val) { + return (byte) ((val & 0xffff) >> 8 & 0xFF); + } + + /** + * Return least significant byte of integer + * + * @param val the value + * @return the LSB + */ + public static byte LSB(int val) { + return (byte) ((val & 0xffff) & 0xFF); + } + + public static void decodeFlags(byte flags) { + boolean username = ((flags >> 7) == 1 ? true : false); + + boolean password = ((flags >> 6) == 1 ? true : false); + } + + /** + * Decode message type + * + * @param buffer The message buffer to decode + * @return The message type + */ + public static byte decodeType(byte[] buffer) { + return (byte) ((buffer[0] >> 4) & 0x0F); + } + + /** + * Get the human readable name of a message type + * + * @param message the message + * @return human readable string of message type + */ + public static String decodePackageName(MQTTMessage message) { + return decodePackageName(message.getType()); + } + + /** + * Get the human readable name of a message type + * + * @param messageType the message type + * @return human readable string of message type + */ + public static String decodePackageName(byte messageType) { + switch (messageType) { + case CONNECT: + return "CONNECT"; + case CONNACK: + return "CONNACK"; + case PUBLISH: + return "PUBLISH"; + case PUBACK: + return "PUBACK"; + case PUBREC: + return "PUBREC"; + case PUBREL: + return "PUBREL"; + case PUBCOMP: + return "PUBCOMP"; + case SUBSCRIBE: + return "SUBSCRIBE"; + case SUBACK: + return "SUBACK"; + case UNSUBSCRIBE: + return "UNSUBSCRIBE"; + case UNSUBACK: + return "UNSUBACK"; + case PINGREQ: + return "PINGREQ"; + case PINGRESP: + return "PINGRESP"; + case DISCONNECT: + return "DISCONNECT"; + default: + return "Unknown message type"; + } + } + +} diff --git a/core/src/main/java/se/wetcat/qatja/MQTTIdentifierHelper.java b/core/src/main/java/se/wetcat/qatja/MQTTIdentifierHelper.java new file mode 100644 index 00000000..f8854ccb --- /dev/null +++ b/core/src/main/java/se/wetcat/qatja/MQTTIdentifierHelper.java @@ -0,0 +1,143 @@ +package se.wetcat.qatja; + +/* + * Copyright (C) 2014 Andreas Goransson + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.ConcurrentHashMap; + +import se.wetcat.qatja.messages.MQTTMessage; + +/** + * A helper class for handling package identifiers. + * + * @author andreasgoransson0@gmail.com + */ +public class MQTTIdentifierHelper { + + private ConcurrentHashMap sentPackages = new ConcurrentHashMap<>(); + private ConcurrentHashMap receivedPackages = new ConcurrentHashMap<>(); + + private List availablePackageIdentifiers = new ArrayList<>(); + + private final Object lock = new Object(); + + /** + * Add MQTTMessage to the list of sent packages, used for QoS + * {@link se.wetcat.qatja.MQTTConstants#AT_MOST_ONCE} and + * {@link se.wetcat.qatja.MQTTConstants#EXACTLY_ONCE} + * + * @param msg the MQTTMessage + */ + public void addSentPackage(MQTTMessage msg) { + synchronized (lock) { + sentPackages.put(msg.getPackageIdentifier(), msg); + } + } + + /** + * Remove MQTTMessage from the list of sent packages, used for QoS + * {@link se.wetcat.qatja.MQTTConstants#AT_MOST_ONCE} and + * {@link se.wetcat.qatja.MQTTConstants#EXACTLY_ONCE} + * + * @param msg the MQTTMessage + */ + public synchronized void removeSentPackage(MQTTMessage msg) { + synchronized (lock) { + sentPackages.remove(msg.getPackageIdentifier()); + availablePackageIdentifiers.add(msg.getPackageIdentifier()); + } + } + + public ConcurrentHashMap getSentPackages() { + synchronized (lock) { + return sentPackages; + } + } + + public ConcurrentHashMap getReceivedPackages() { + synchronized (lock) { + return receivedPackages; + } + } + + /** + * Add MQTTMessage to the list of received packages, used for QoS + * {@link MQTTConstants#AT_MOST_ONCE} and {@link MQTTConstants#EXACTLY_ONCE} + * + * @param msg the MQTTMessage + */ + public void addReceivedPackage(MQTTMessage msg) { + synchronized (lock) { + receivedPackages.put(msg.getPackageIdentifier(), msg); + } + } + + /** + * Remove MQTTMessage to the list of received packages, used for QoS + * {@link MQTTConstants#AT_MOST_ONCE} and {@link MQTTConstants#EXACTLY_ONCE} + * + * @param msg the MQTTMessage + */ + public void removeReceivedPackage(MQTTMessage msg) { + synchronized (lock) { + receivedPackages.remove(msg.getPackageIdentifier()); + } + } + + /** + * @return The next available identifier + */ + public synchronized int getIdentifier() { + synchronized (lock) { + int identifier = 1; + + if (availablePackageIdentifiers.size() > 0) { + // Reuse an identifier + identifier = availablePackageIdentifiers.remove(0); + } else if (sentPackages.size() > 0) { + // Generate the next, new, identifier by looping through the map and find the next biggest id +// int lastPackage = sentPackages.size() - 1; +// identifier = sentPackages.get(lastPackage).getPackageIdentifier() + 1; + +// boolean idFound = true; +// do { +// for (int id : Collections.list(sentPackages.keys())) { +// if (identifier == id) { +// idFound = true; +// } +// } +// } while (idFound); + + List keys = Collections.list(sentPackages.keys()); + for (int i = 0; i < keys.size(); i++) { + if (identifier == keys.get(i)) { + identifier++; + i = 0; + } + } + } else { + // Generate first identifier + identifier = 1; + } + + + return identifier; + } + } +} diff --git a/core/src/main/java/se/wetcat/qatja/MQTTVersion.java b/core/src/main/java/se/wetcat/qatja/MQTTVersion.java new file mode 100644 index 00000000..653f8fd7 --- /dev/null +++ b/core/src/main/java/se/wetcat/qatja/MQTTVersion.java @@ -0,0 +1,52 @@ +package se.wetcat.qatja; + +/* + * Copyright (C) 2012 Andreas Goransson, David Cuartielles + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Defines the current MQTT version. + * + * TODO: Find a better way of defining versions, defining a version should also + * affect the other classes of the project - and the specific platform + * implementations. + * + * @author Andreas Goransson + * @version 1.0.0 + * @since 2017-05-07 + */ +public class MQTTVersion { + + /** + * MQTT 3.1 Version name + */ + public static String NAME_31 = "MQIsdp"; + + /** + * MQTT 3.1 Version number + */ + public static byte VERSION_31 = (byte) 0x03; + + /** + * MQTT 3.1.1 Version name + */ + public static String NAME_311 = "MQTT"; + + /** + * MQTT 3.1 Version number + */ + public static byte VERSION_311 = (byte) 0x04; + +} diff --git a/core/src/main/java/se/wetcat/qatja/messages/MQTTConnack.java b/core/src/main/java/se/wetcat/qatja/messages/MQTTConnack.java new file mode 100644 index 00000000..da567ebe --- /dev/null +++ b/core/src/main/java/se/wetcat/qatja/messages/MQTTConnack.java @@ -0,0 +1,106 @@ +package se.wetcat.qatja.messages; + +/* + * Copyright (C) 2014 Andreas Goransson + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.io.IOException; + +import se.wetcat.qatja.MQTTException; + +/** + * The {@link se.wetcat.qatja.MQTTConstants#CONNACK} Packet is the packet sent by the Server in + * response to a {@link se.wetcat.qatja.MQTTConstants#CONNECT} Packet received from a Client. The + * first packet sent from the Server to the Client MUST be a + * {@link se.wetcat.qatja.MQTTConstants#CONNACK} Packet [MQTT-3.2.0-1]. + *

+ * If the Client does not receive a {@link se.wetcat.qatja.MQTTConstants#CONNACK} Packet from the + * Server within a reasonable amount of time, the Client SHOULD close the Network Connection. A + * "reasonable" amount of time depends on the type of application and the communications + * infrastructure. + * + * @author Andreas Goransson + * @version 1.0.0 + * @since 2017-05-06 + */ +public class MQTTConnack extends MQTTMessage { + + @SuppressWarnings("unused") + private byte RESERVED; + private byte returnCode; + + public static MQTTConnack fromBuffer(byte[] buffer) { + return new MQTTConnack(buffer); + } + + private MQTTConnack(byte[] buffer) { + int i = 0; + + // Type (just for clarity sake we'll set it...) + this.setType((byte) ((buffer[i++] >> 4) & 0x0F)); + + // Remaining length + int multiplier = 1; + int len = 0; + byte digit = 0; + do { + digit = buffer[i++]; + len += (digit & 127) * multiplier; + multiplier *= 128; + } while ((digit & 128) != 0); + this.setRemainingLength(len); + + // Get length of protocol name + len = ((buffer[i] >> 8) & 0xFF) | (buffer[i + 1] & 0xFF); + + // Get variable header (always length 2 in CONNACK) + variableHeader = new byte[2]; + System.arraycopy(buffer, i, variableHeader, 0, variableHeader.length); + + // Get payload + payload = new byte[remainingLength - variableHeader.length]; + if (payload.length > 0) + System.arraycopy(buffer, i + variableHeader.length, payload, 0, remainingLength - variableHeader.length); + + RESERVED = variableHeader[0]; + returnCode = variableHeader[1]; + } + + /** + * @return the returnCode + */ + public byte getReturnCode() { + return returnCode; + } + + @Override + protected byte[] generateFixedHeader() throws MQTTException, IOException { + // Client doesn't generate CONNACK + return null; + } + + @Override + protected byte[] generateVariableHeader() throws MQTTException, IOException { + // Client doesn't generate CONNACK + return null; + } + + @Override + protected byte[] generatePayload() throws MQTTException, IOException { + // Client doesn't generate CONNACK + return null; + } + +} diff --git a/core/src/main/java/se/wetcat/qatja/messages/MQTTConnect.java b/core/src/main/java/se/wetcat/qatja/messages/MQTTConnect.java new file mode 100644 index 00000000..c582f4e3 --- /dev/null +++ b/core/src/main/java/se/wetcat/qatja/messages/MQTTConnect.java @@ -0,0 +1,538 @@ +package se.wetcat.qatja.messages; + +/* + * Copyright (C) 2014 Andreas Goransson + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.logging.Logger; + +import se.wetcat.qatja.MQTTException; +import se.wetcat.qatja.MQTTHelper; + +import static se.wetcat.qatja.MQTTConstants.AT_MOST_ONCE; +import static se.wetcat.qatja.MQTTConstants.CONNECT; +import static se.wetcat.qatja.MQTTConstants.MAX_LENGTH; +import static se.wetcat.qatja.MQTTConstants.MIN_LENGTH; +import static se.wetcat.qatja.MQTTVersion.VERSION_311; + +/** + * After a Network Connection is established by a Client to a Server, the first Packet sent from the + * Client to the Server MUST be a {@link se.wetcat.qatja.MQTTConstants#CONNECT} Packet + * [MQTT-3.1.0-1]. + *

+ * A Client can only send the {@link se.wetcat.qatja.MQTTConstants#CONNECT} Packet once over a + * Network Connection. The Server MUST process a second + * {@link se.wetcat.qatja.MQTTConstants#CONNECT} Packet sent from a Client as a protocol violation + * and disconnect the Client [MQTT-3.1.0-2]. See section 4.8 for information about handling errors. + *

+ * The payload contains one or more encoded fields. They specify a unique Client identifier for the + * Client, a Will topic, Will Message, User Name and Password. All but the Client identifier are + * optional and their presence is determined based on flags in the variable header. + * + * @author Andreas Goransson + * @version 1.0.0 + * @since 2017-05-06 + */ +public class MQTTConnect extends MQTTMessage { + + /** + * The identifier for the client, MUST be present in all {@link se.wetcat.qatja.MQTTConstants#CONNECT} + * messages + */ + private String clientIdentifier; + + protected byte protocolVersion; + + protected String protocolName; + + /** + * Position 5 of Connect Flags. If the Will Flag is set to 0, the Will + * Retain Flag MUST be set to 0. + */ + private boolean willRetain; + private boolean willFlag = true; + private String willTopic = "wtopic"; + private String willMessage = "wmessage"; + private byte willQoS; + + private String username; + private String password; + + private boolean cleanSession; + private int keepAlive; + + public static MQTTConnect newInstance() { + return new MQTTConnect(); + } + + public static MQTTConnect newInstance(String clientIdentifier) { + return new MQTTConnect(clientIdentifier); + } + + public static MQTTConnect newInstance(String clientIdentifier, String username, String password) { + return new MQTTConnect(clientIdentifier, username, password); + } + + public static MQTTConnect newInstance(String clientIdentifier, String username, String password, + boolean cleanSession) { + return new MQTTConnect(clientIdentifier, username, password, cleanSession); + } + + public static MQTTConnect newInstance(String clientIdentifier, String username, String password, + boolean willRetain, byte willQoS, String willTopic, + String willMessage, boolean cleanSession, int keepAlive) { + return new MQTTConnect(clientIdentifier, username, password, willRetain, willQoS, willTopic, willMessage, cleanSession, keepAlive); + } + + public static MQTTConnect fromButton(byte[] buffer) { + return new MQTTConnect(buffer); + } + + /** + * Default MQTTConnect constructor. All flags set to default values, and no + * client identifier set (cleanSession set to true). Keep alive set to 10 + * seconds. + */ + private MQTTConnect() { + this("Qatja-" + System.nanoTime()); + } + + /** + * Create a new MQTT Connect message + * + * @param clientIdentifier The client identifier + */ + private MQTTConnect(String clientIdentifier) { + this(clientIdentifier, null, null); + } + + /** + * Create a new MQTT Connect message + * + * @param clientIdentifier The client identifier + * @param username The client username + * @param password The client password + */ + private MQTTConnect(String clientIdentifier, String username, String password) { + this(clientIdentifier, username, password, false); + } + + /** + * Create a new MQTT Connect message + * + * @param clientIdentifier The client identifier + * @param username The client username + * @param password The client password + * @param cleanSession Connect with clear session state + */ + private MQTTConnect(String clientIdentifier, String username, String password, boolean cleanSession) { + this(clientIdentifier, username, password, false, AT_MOST_ONCE, null, null, cleanSession, 10); + } + + /** + * Create a new MQTT Connect message + * + * @param clientIdentifier The client identifier + * @param username The client username + * @param password The client password + * @param willRetain Set last will and testament + * @param willQoS Last will QoS + * @param willTopic Last will topic + * @param willMessage Last will message + * @param cleanSession Connect with clean session state + * @param keepAlive Time interval in seconds + */ + private MQTTConnect(String clientIdentifier, String username, String password, boolean willRetain, byte willQoS, + String willTopic, String willMessage, boolean cleanSession, int keepAlive) { + this.type = CONNECT; + + this.clientIdentifier = clientIdentifier; + this.cleanSession = cleanSession; + this.keepAlive = keepAlive; + + this.willRetain = willRetain; + this.willFlag = (willTopic != null && willMessage != null); + this.willTopic = willTopic; + this.willMessage = willMessage; + this.willQoS = willQoS; + + this.username = username; + this.password = password; + } + + /** + * Create MQTT Connect message from a byte[]. + * + * @param buffer Buffer from which to create message + */ + private MQTTConnect(byte[] buffer) { + int i = 0; + // Type (just for clarity sake we'll set it...) + this.setType((byte) ((buffer[i++] >> 4) & 0x0F)); + + // Remaining length + int multiplier = 1; + int len = 0; + byte digit = 0; + do { + digit = buffer[i++]; + len += (digit & 127) * multiplier; + multiplier *= 128; + } while ((digit & 128) != 0); + this.setRemainingLength(len); + + // Get length of protocol name + len = ((buffer[i] >> 8) & 0xFF) | (buffer[i + 1] & 0xFF); + + // Get variable header (always length 10 in CONNECT) + variableHeader = new byte[10]; + System.arraycopy(buffer, i, variableHeader, 0, variableHeader.length); + + // Get payload + payload = new byte[remainingLength - variableHeader.length]; + System.arraycopy(buffer, i + variableHeader.length, payload, 0, remainingLength - variableHeader.length); + + i = 2; + protocolName = new String(variableHeader, i, len); + protocolVersion = variableHeader[(i += len)]; + flags = variableHeader[++i]; + keepAlive = (variableHeader[++i] >> 8) & 0xFF | (variableHeader[++i] & 0xFF); + } + + /** + * Set last will and testament + * + * @param willTopic Set the last will topic + * @param willMessage Set last will message + * @param willRetain Set last will retain flag + * @param willQoS Set last will QoS + */ + public void setWill(String willTopic, String willMessage, boolean willRetain, byte willQoS) { + this.willFlag = true; + this.willTopic = willTopic; + this.willMessage = willMessage; + this.willQoS = willQoS; + } + + @Override + protected byte[] generateFixedHeader() throws MQTTException, IOException { + // FIXED HEADER + ByteArrayOutputStream out = new ByteArrayOutputStream(); + + // Type + out.write((byte) (type << 4)); + + // Flags (none for CONNECT) + + // Remaining length + int length = variableHeader.length + payload.length; + this.setRemainingLength(length); + do { + byte digit = (byte) (length % 128); + length /= 128; + if (length > 0) + digit = (byte) (digit | 0x80); + out.write(digit); + } while (length > 0); + + return out.toByteArray(); + } + + @Override + protected byte[] generateVariableHeader() throws MQTTException, IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + + // Some packages has this + if (protocolName == null) + out.write(getProtocol()); + else { + // For some reason I need to test this in the interop testing... + out.write((protocolName.getBytes("UTF-8").length >> 8) & 0xFF); // MSB + out.write(protocolName.getBytes("UTF-8").length & 0xFF); // LSB + out.write(protocolName.getBytes("UTF-8")); + + // Protocol Level (Used by all messages) + out.write((byte) VERSION_311); + } + + // CONNECT FLAGS + byte flags = (byte) (0x00); + + byte username = (byte) ((this.username != null && this.username.length() >= 0) ? 0x01 : 0x00); + byte password = (byte) ((this.password != null && this.password.length() >= 0) ? 0x01 : 0x00); + byte willRetain = (byte) (this.willRetain ? 0x01 : 0x00); + byte willQoS = this.willQoS; + byte willFlag = (byte) ((this.willRetain && this.willFlag) ? 0x01 : 0x00); + byte cleanSession = (byte) (this.cleanSession ? 0x01 : 0x00); + byte RESERVED = (byte) 0x00; + + flags = (byte) ((username << 7) | (password << 6) | (willRetain << 5) | (willQoS << 3) | (willFlag << 2) + | (cleanSession << 1) | (RESERVED << 0)); + + // Write flags + out.write(flags); + + // Keep alive (Use lower two bytes of integer) + out.write(MQTTHelper.MSB(keepAlive)); + out.write(MQTTHelper.LSB(keepAlive)); + + return out.toByteArray(); + } + + @Override + protected byte[] generatePayload() throws MQTTException, IOException { + // PAYLOAD + ByteArrayOutputStream payload = new ByteArrayOutputStream(); + + // CLIENT IDENTIFIER + // ClientID MUST be present and MUST be the first field in payload + if (clientIdentifier == null) + throw new MQTTException("Client identifier invalid"); + // ClientID MUST be UTF-8 + if (MQTTHelper.isUTF8(clientIdentifier.getBytes("UTF-8"))) + throw new MQTTException("Invalid identifier encoding"); + // ClientID MUST be length between 0 and 65535 + if (clientIdentifier.getBytes("UTF-8").length < MIN_LENGTH + || clientIdentifier.getBytes("UTF-8").length > MAX_LENGTH) + throw new MQTTException("Client identifier invalid length"); + + payload.write(MQTTHelper.MSB(clientIdentifier.getBytes("UTF-8"))); // MSB + payload.write(MQTTHelper.LSB(clientIdentifier.getBytes("UTF-8"))); // LSB + payload.write(clientIdentifier.getBytes("UTF-8")); + + // WILL + if (willFlag) { + // Will topic MUST be present + if (willTopic == null) + throw new MQTTException("Will flag set, will topic MUST be present"); + // Will topic MUST be UTF-8 + if (MQTTHelper.isUTF8(willTopic.getBytes("UTF-8"))) + throw new MQTTException("Invalid will topic encoding"); + // Write Will topic + payload.write(MQTTHelper.MSB(willTopic.getBytes("UTF-8"))); // MSB + payload.write(MQTTHelper.LSB(willTopic.getBytes("UTF-8"))); // LSB + payload.write(willTopic.getBytes("UTF-8")); + + // Will message MUST be UTF-8 + if (willMessage == null) + throw new MQTTException("Will flag set, will message MUST be present"); + if (MQTTHelper.isUTF8(willMessage.getBytes("UTF-8"))) + throw new MQTTException("Invalid will message encoding"); + // Write Will message + payload.write(MQTTHelper.MSB(willMessage.getBytes("UTF-8"))); // MSB + payload.write(MQTTHelper.LSB(willMessage.getBytes("UTF-8"))); // LSB + payload.write(willMessage.getBytes("UTF-8")); + } + + // USERNAME + if (username != null && username.length() >= 0) { + // Username MUST be UTF-8 + if (MQTTHelper.isUTF8(username.getBytes("UTF-8"))) + throw new MQTTException("Invalid username encoding"); + // Write username + payload.write(MQTTHelper.MSB(username.getBytes("UTF-8"))); // MSB + payload.write(MQTTHelper.LSB(username.getBytes("UTF-8"))); // LSB + payload.write(username.getBytes("UTF-8")); + } + + // PASSWORD + if (password != null && password.length() >= 0) { + // Password MUST be UTF-8 + if (MQTTHelper.isUTF8(password.getBytes("UTF-8"))) + throw new MQTTException("Invalid password encoding"); + // Password contains 0 to 65535 bytes + if (password.getBytes("UTF-8").length < MIN_LENGTH || password.getBytes("UTF-8").length > MAX_LENGTH) + throw new MQTTException("Password invalid length"); + // Write password + payload.write(MQTTHelper.MSB(password.getBytes("UTF-8"))); // MSB + payload.write(MQTTHelper.LSB(password.getBytes("UTF-8"))); // LSB + payload.write(password.getBytes("UTF-8")); +// Logger.getGlobal().info("mqtt_"+payload.toString()); + } + + return payload.toByteArray(); + } + + /** + * @return The clientIdentifier + */ + public String getClientIdentifier() { + return clientIdentifier; + } + + /** + * @param clientIdentifier The clientIdentifier to set + */ + public void setClientIdentifier(String clientIdentifier) { + this.clientIdentifier = clientIdentifier; + } + + /** + * @return The protocolVersion + */ + public byte getProtocolVersion() { + return protocolVersion; + } + + /** + * @param protocolVersion The protocolVersion to set + */ + public void setProtocolVersion(byte protocolVersion) { + this.protocolVersion = protocolVersion; + } + + /** + * @return The protocolName + */ + public String getProtocolName() { + return protocolName; + } + + /** + * @param protocolName The protocolName to set + */ + public void setProtocolName(String protocolName) { + this.protocolName = protocolName; + } + + /** + * @return The willRetain + */ + public boolean isWillRetain() { + return willRetain; + } + + /** + * @param willRetain The willRetain to set + */ + public void setWillRetain(boolean willRetain) { + this.willRetain = willRetain; + } + + /** + * @return The willFlag + */ + public boolean isWillFlag() { + return willFlag; + } + + /** + * @param willFlag The willFlag to set + */ + public void setWillFlag(boolean willFlag) { + this.willFlag = willFlag; + } + + /** + * @return The willTopic + */ + public String getWillTopic() { + return willTopic; + } + + /** + * @param willTopic The willTopic to set + */ + public void setWillTopic(String willTopic) { + this.willTopic = willTopic; + } + + /** + * @return The willMessage + */ + public String getWillMessage() { + return willMessage; + } + + /** + * @param willMessage The willMessage to set + */ + public void setWillMessage(String willMessage) { + this.willMessage = willMessage; + } + + /** + * @return The willQoS + */ + public byte getWillQoS() { + return willQoS; + } + + /** + * @param willQoS The willQoS to set + */ + public void setWillQoS(byte willQoS) { + this.willQoS = willQoS; + } + + /** + * @return The username + */ + public String getUsername() { + return username; + } + + /** + * @param username The username to set + */ + public void setUsername(String username) { + this.username = username; + } + + /** + * @return The password + */ + public String getPassword() { + return password; + } + + /** + * @param password The password to set + */ + public void setPassword(String password) { + this.password = password; + } + + /** + * @return The cleanSession + */ + public boolean isCleanSession() { + return cleanSession; + } + + /** + * @param cleanSession The cleanSession to set + */ + public void setCleanSession(boolean cleanSession) { + this.cleanSession = cleanSession; + } + + /** + * @return The keepAlive + */ + public int getKeepAlive() { + return keepAlive; + } + + /** + * @param keepAlive The keepAlive to set + */ + public void setKeepAlive(int keepAlive) { + this.keepAlive = keepAlive; + } + +} diff --git a/core/src/main/java/se/wetcat/qatja/messages/MQTTDisconnect.java b/core/src/main/java/se/wetcat/qatja/messages/MQTTDisconnect.java new file mode 100644 index 00000000..ecd502a7 --- /dev/null +++ b/core/src/main/java/se/wetcat/qatja/messages/MQTTDisconnect.java @@ -0,0 +1,83 @@ +package se.wetcat.qatja.messages; + +/* + * Copyright (C) 2014 Andreas Goransson + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +import se.wetcat.qatja.MQTTException; + +import static se.wetcat.qatja.MQTTConstants.DISCONNECT; + +/** + * The {@link se.wetcat.qatja.MQTTConstants#DISCONNECT} Packet is the final Control Packet sent from + * the Client to the Server. It indicates that the Client is disconnecting cleanly. + * + * @author Andreas Goransson + * @version 1.0.0 + * @since 2017-05-06 + */ +public class MQTTDisconnect extends MQTTMessage { + + public static MQTTDisconnect newInstance() { + return new MQTTDisconnect(); + } + + /** + * Construct a {@link se.wetcat.qatja.MQTTConstants#DISCONNECT} message + */ + private MQTTDisconnect() { + this.setType(DISCONNECT); + } + + @Override + protected byte[] generateFixedHeader() throws MQTTException, IOException { + // FIXED HEADER + ByteArrayOutputStream out = new ByteArrayOutputStream(); + + // Type + out.write((byte) (type << 4)); + + // Flags (none for PING) + + // Remaining length + int length = variableHeader.length + payload.length; + this.setRemainingLength(length); + do { + byte digit = (byte) (length % 128); + length /= 128; + if (length > 0) + digit = (byte) (digit | 0x80); + out.write(digit); + } while (length > 0); + + return out.toByteArray(); + } + + @Override + protected byte[] generateVariableHeader() throws MQTTException, IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + return out.toByteArray(); + } + + @Override + protected byte[] generatePayload() throws MQTTException, IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + return out.toByteArray(); + } + +} diff --git a/core/src/main/java/se/wetcat/qatja/messages/MQTTMessage.java b/core/src/main/java/se/wetcat/qatja/messages/MQTTMessage.java new file mode 100644 index 00000000..31b772a5 --- /dev/null +++ b/core/src/main/java/se/wetcat/qatja/messages/MQTTMessage.java @@ -0,0 +1,196 @@ +package se.wetcat.qatja.messages; + +/* + * Copyright (C) 2012 Andreas Goransson, David Cuartielles + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.logging.Logger; + +import se.wetcat.qatja.MQTTException; +import se.wetcat.qatja.MQTTHelper; + +import static se.wetcat.qatja.MQTTVersion.NAME_311; +import static se.wetcat.qatja.MQTTVersion.VERSION_311; + +/** + * MQTT Message super class, should never be instantiated. + * + * @author Andreas Goransson + * @version 1.0.0 + * @since 2017-05-06 + */ +public abstract class MQTTMessage { + + protected byte flags; + + protected int packageIdentifier; + + protected byte[] fixedHeader; + protected byte[] variableHeader; + protected byte[] payload; + + protected byte type; + protected int remainingLength; + + protected abstract byte[] generateFixedHeader() throws MQTTException, IOException; + + protected abstract byte[] generateVariableHeader() throws MQTTException, IOException; + + protected abstract byte[] generatePayload() throws MQTTException, IOException; + + /** + * @return The packageIdentifier + */ + public int getPackageIdentifier() { + return packageIdentifier; + } + + /** + * @param packageIdentifier The packageIdentifier to set + */ + public void setPackageIdentifier(int packageIdentifier) { + this.packageIdentifier = packageIdentifier; + } + + /** + * @return The fixedHeader + */ + public byte[] getFixedHeader() { + return fixedHeader; + } + + /** + * @param fixedHeader The fixedHeader to set + */ + public void setFixedHeader(byte[] fixedHeader) { + this.fixedHeader = fixedHeader; + } + + /** + * @return The variableHeader + */ + public byte[] getVariableHeader() { + return variableHeader; + } + + /** + * @param variableHeader The variableHeader to set + */ + public void setVariableHeader(byte[] variableHeader) { + this.variableHeader = variableHeader; + } + + /** + * @return The payload + */ + public byte[] getPayload() { + return payload; + } + + /** + * @param payload The payload to set + */ + public void setPayload(byte[] payload) { + this.payload = payload; + } + + /** + * @return The type + */ + public byte getType() { + return type; + } + + /** + * @param type The type to set + */ + public void setType(byte type) { + this.type = type; + } + + /** + * @return The remainingLength + */ + public int getRemainingLength() { + return remainingLength; + } + + /** + * @param remainingLength The remainingLength to set + */ + public void setRemainingLength(int remainingLength) { + this.remainingLength = remainingLength; + } + + protected byte[] getProtocol() throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + + // Protocol Name (Used by all messages) + out.write((NAME_311.getBytes("UTF-8").length >> 8) & 0xFF); // MSB + out.write(NAME_311.getBytes("UTF-8").length & 0xFF); // LSB + out.write(NAME_311.getBytes("UTF-8")); + + // Protocol Level (Used by all messages) + out.write((byte) VERSION_311); + + return out.toByteArray(); + } + + public byte[] get() throws IOException, MQTTException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + + this.payload = generatePayload(); + this.variableHeader = generateVariableHeader(); + this.fixedHeader = generateFixedHeader(); + + out.write(fixedHeader); + out.write(variableHeader); + out.write(payload); + + Logger.getGlobal().info("mqtt_ "+out.toString()); + Logger.getGlobal().info("mqtt_ "+bytesToHexString(out.toByteArray())); + + return out.toByteArray(); + } + public String bytesToHexString(byte[] src) { + StringBuilder stringBuilder = new StringBuilder(""); + if (src == null || src.length <= 0) { + return null; + } + for (int i = 0; i < src.length; i++) { + int v = src[i] & 0xFF; + String hv = Integer.toHexString(v); + if (hv.length() < 2) { + stringBuilder.append(0); + } + stringBuilder.append(hv); + } + return stringBuilder.toString(); + } + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + + sb.append("MQTT Package ").append("\n"); + sb.append(" type: " + MQTTHelper.decodePackageName(this.getType())).append("\n"); + sb.append(" remaining length: " + this.getRemainingLength()).append("\n"); + sb.append(" package identifier: " + this.getPackageIdentifier()); + + return sb.toString(); + } + +} diff --git a/core/src/main/java/se/wetcat/qatja/messages/MQTTPingreq.java b/core/src/main/java/se/wetcat/qatja/messages/MQTTPingreq.java new file mode 100644 index 00000000..9010be47 --- /dev/null +++ b/core/src/main/java/se/wetcat/qatja/messages/MQTTPingreq.java @@ -0,0 +1,88 @@ +package se.wetcat.qatja.messages; + +/* + * Copyright (C) 2014 Andreas Goransson + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +import se.wetcat.qatja.MQTTException; + +import static se.wetcat.qatja.MQTTConstants.PINGREQ; + +/** + * The {@link se.wetcat.qatja.MQTTConstants#PINGREQ} Packet is sent from a Client to the Server. It + * can be used to: + * 1. Indicate to the Server that the Client is alive in the absence of any + * other Control Packets being sent from the Client to the Server. + * 2. Request that the Server responds to confirm that it is alive. + * 3. Exercise the network to indicate that the Network Connection is active. + * + * @author Andreas Goransson + * @version 1.0.0 + * @since 2017-05-06 + */ +public class MQTTPingreq extends MQTTMessage { + + public static MQTTPingreq newInstance() { + return new MQTTPingreq(); + } + + /** + * Construct a {@link se.wetcat.qatja.MQTTConstants#PINGREQ} message + */ + private MQTTPingreq() { + this.setType(PINGREQ); + } + + @Override + protected byte[] generateFixedHeader() throws MQTTException, IOException { + // FIXED HEADER + ByteArrayOutputStream out = new ByteArrayOutputStream(); + + // Type + byte fixed = (byte) (type << 4); + out.write(fixed); + + // Flags (none for PINGREQ) + + // Remaining length + int length = getVariableHeader().length + getPayload().length; + this.setRemainingLength(length); + do { + byte digit = (byte) (length % 128); + length /= 128; + if (length > 0) + digit = (byte) (digit | 0x80); + out.write(digit); + } while (length > 0); + + return out.toByteArray(); + } + + @Override + protected byte[] generateVariableHeader() throws MQTTException, IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + return out.toByteArray(); + } + + @Override + protected byte[] generatePayload() throws MQTTException, IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + return out.toByteArray(); + } + +} diff --git a/core/src/main/java/se/wetcat/qatja/messages/MQTTPingresp.java b/core/src/main/java/se/wetcat/qatja/messages/MQTTPingresp.java new file mode 100644 index 00000000..ce43669f --- /dev/null +++ b/core/src/main/java/se/wetcat/qatja/messages/MQTTPingresp.java @@ -0,0 +1,77 @@ +package se.wetcat.qatja.messages; + +/* + * Copyright (C) 2014 Andreas Goransson + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.io.IOException; + +import se.wetcat.qatja.MQTTException; + +/** + * A {@link se.wetcat.qatja.MQTTConstants#PINGRESP} Packet is sent by the Server to the Client in + * response to a {@link se.wetcat.qatja.MQTTConstants#PINGREQ} Packet. It indicates that the Server + * is alive. + * + * @author Andreas Goransson + * @version 1.0.0 + * @since 2017-05-06 + */ +public class MQTTPingresp extends MQTTMessage { + + public static MQTTPingresp fromBuffer(byte[] buffer) { + return new MQTTPingresp(buffer); + } + + /** + * Construct a {@link se.wetcat.qatja.MQTTConstants#PINGRESP} message from a buffer + * + * @param buffer The buffer + */ + private MQTTPingresp(byte[] buffer) { + + int i = 0; + // Type (just for clarity sake we'll read it...) + this.setType((byte) ((buffer[i++] >> 4) & 0x0F)); + + // Remaining length + this.setRemainingLength(0); + + // No variable header + setVariableHeader(null); + + // No payload + setPayload(null); + } + + @Override + protected byte[] generateFixedHeader() throws MQTTException, IOException { + // Client doesn't create the PINGRESP + return null; + } + + @Override + protected byte[] generateVariableHeader() throws MQTTException, IOException { + // Client doesn't create the PINGRESP + return null; + } + + @Override + protected byte[] generatePayload() throws MQTTException, IOException { + // Client doesn't create the PINGRESP + return null; + } + +} diff --git a/core/src/main/java/se/wetcat/qatja/messages/MQTTPuback.java b/core/src/main/java/se/wetcat/qatja/messages/MQTTPuback.java new file mode 100644 index 00000000..71809fd3 --- /dev/null +++ b/core/src/main/java/se/wetcat/qatja/messages/MQTTPuback.java @@ -0,0 +1,127 @@ +package se.wetcat.qatja.messages; + +/* + * Copyright (C) 2014 Andreas Goransson + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +import se.wetcat.qatja.MQTTException; +import se.wetcat.qatja.MQTTHelper; + +import static se.wetcat.qatja.MQTTConstants.PUBACK; + +/** + * A {@link se.wetcat.qatja.MQTTConstants#PUBACK} Packet is the response to a + * {@link se.wetcat.qatja.MQTTConstants#PUBLISH} Packet with se.wetcat.qatja.MQTTConstantsQoS level + * {@link se.wetcat.qatja.MQTTConstants#AT_LEAST_ONCE}. + * + * @author Andreas Goransson + * @version 1.0.0 + * @since 2017-05-06 + */ +public class MQTTPuback extends MQTTMessage { + + public static MQTTPuback newInstance(int packageIdentifier) { + return new MQTTPuback(packageIdentifier); + } + + public static MQTTPuback fromBuffer(byte[] buffer) { + return new MQTTPuback(buffer); + } + + private MQTTPuback(int packageIdentifier) { + this.setType(PUBACK); + this.setPackageIdentifier(packageIdentifier); + } + + /** + * Construct a {@link se.wetcat.qatja.MQTTConstants#PUBACK} message from a byte array + * + * @param buffer The byte array + */ + private MQTTPuback(byte[] buffer) { + + int i = 0; + // Type (just for clarity sake we'll set it...) + this.setType((byte) ((buffer[i++] >> 4) & 0x0F)); + + // Remaining length + int multiplier = 1; + int len = 0; + byte digit = 0; + do { + digit = buffer[i++]; + len += (digit & 127) * multiplier; + multiplier *= 128; + } while ((digit & 128) != 0); + this.setRemainingLength(len); + + // Get variable header (always length 2 in PUBACK) + variableHeader = new byte[2]; + System.arraycopy(buffer, i, variableHeader, 0, variableHeader.length); + + // Get payload + payload = new byte[remainingLength - variableHeader.length]; + if (payload.length > 0) + System.arraycopy(buffer, i + variableHeader.length, payload, 0, remainingLength - variableHeader.length); + + // Get package identifier + packageIdentifier = (variableHeader[variableHeader.length - 2] >> 8 & 0xFF) | (variableHeader[variableHeader.length - 1] & 0xFF); + } + + @Override + protected byte[] generateFixedHeader() throws MQTTException, IOException { + // FIXED HEADER + ByteArrayOutputStream out = new ByteArrayOutputStream(); + + // Type and PUBACK flags, reserved bits MUST be [0,0,0,0] + byte fixed = (byte) ((type << 4) | (0x00 << 3) | (0x00 << 2) | (0x00 << 1) | (0x00) << 0); + out.write(fixed); + + // Flags (none for PUBACK) + + // Remaining length + int length = getVariableHeader().length + getPayload().length; + this.setRemainingLength(length); + do { + byte digit = (byte) (length % 128); + length /= 128; + if (length > 0) + digit = (byte) (digit | 0x80); + out.write(digit); + } while (length > 0); + + return out.toByteArray(); + } + + @Override + protected byte[] generateVariableHeader() throws MQTTException, IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + + out.write(MQTTHelper.MSB(packageIdentifier)); + out.write(MQTTHelper.LSB(packageIdentifier)); + + return out.toByteArray(); + } + + @Override + protected byte[] generatePayload() throws MQTTException, IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + return out.toByteArray(); + } + +} diff --git a/core/src/main/java/se/wetcat/qatja/messages/MQTTPubcomp.java b/core/src/main/java/se/wetcat/qatja/messages/MQTTPubcomp.java new file mode 100644 index 00000000..669bc636 --- /dev/null +++ b/core/src/main/java/se/wetcat/qatja/messages/MQTTPubcomp.java @@ -0,0 +1,130 @@ +package se.wetcat.qatja.messages; + +/* + * Copyright (C) 2014 Andreas Goransson + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +import se.wetcat.qatja.MQTTConstants; +import se.wetcat.qatja.MQTTException; +import se.wetcat.qatja.MQTTHelper; + +/** + * The {@link se.wetcat.qatja.MQTTConstants#PUBCOMP} Packet is the response to a + * {@link se.wetcat.qatja.MQTTConstants#PUBREL} Packet. It is the fourth and final packet of the + * {@link se.wetcat.qatja.MQTTConstants#EXACTLY_ONCE} protocol exchange + * + * @author Andreas Goransson + * @version 1.0.0 + * @since 2017-05-06 + */ +public class MQTTPubcomp extends MQTTMessage { + + public static MQTTPubcomp newInstance(int packageIdentifier) { + return new MQTTPubcomp(packageIdentifier); + } + + public static MQTTPubcomp fromBuffer(byte[] buffer) { + return new MQTTPubcomp(buffer); + } + + private MQTTPubcomp(int packageIdentifier) { + setType(MQTTConstants.PUBCOMP); + setPackageIdentifier(packageIdentifier); + } + + /** + * Construct a {@link se.wetcat.qatja.MQTTConstants#PUBCOMP} message from a byte array + * + * @param buffer The byte array + */ + private MQTTPubcomp(byte[] buffer) { + + // setBuffer(bufferIn, bufferLength); + + int i = 0; + // Type (just for clarity sake we'll set it...) + this.setType((byte) ((buffer[i++] >> 4) & 0x0F)); + + // Remaining length + int multiplier = 1; + int len = 0; + byte digit = 0; + do { + digit = buffer[i++]; + len += (digit & 127) * multiplier; + multiplier *= 128; + } while ((digit & 128) != 0); + this.setRemainingLength(len); + + // No flags for PUBBCOMP + + // Get variable header (always length 2 in PUBACK) + variableHeader = new byte[2]; + System.arraycopy(buffer, i, variableHeader, 0, variableHeader.length); + + // Get payload + payload = new byte[remainingLength - variableHeader.length]; + if (payload.length > 0) + System.arraycopy(buffer, i + variableHeader.length, payload, 0, remainingLength - variableHeader.length); + + // Get package identifier + packageIdentifier = (variableHeader[variableHeader.length - 2] >> 8 & 0xFF) | (variableHeader[variableHeader.length - 1] & 0xFF); + } + + @Override + protected byte[] generateFixedHeader() throws MQTTException, IOException { + // FIXED HEADER + ByteArrayOutputStream out = new ByteArrayOutputStream(); + + // Type and PUBACK flags, reserved bits MUST be [0,0,0,0] + byte fixed = (byte) ((type << 4) | (0x00 << 3) | (0x00 << 2) | (0x00 << 1) | (0x00) << 0); + out.write(fixed); + + // Flags (none for PUBACK) + + // Remaining length + int length = getVariableHeader().length + getPayload().length; + this.setRemainingLength(length); + do { + byte digit = (byte) (length % 128); + length /= 128; + if (length > 0) + digit = (byte) (digit | 0x80); + out.write(digit); + } while (length > 0); + + return out.toByteArray(); + } + + @Override + protected byte[] generateVariableHeader() throws MQTTException, IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + + out.write(MQTTHelper.MSB(packageIdentifier)); + out.write(MQTTHelper.LSB(packageIdentifier)); + + return out.toByteArray(); + } + + @Override + protected byte[] generatePayload() throws MQTTException, IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + return out.toByteArray(); + } + +} diff --git a/core/src/main/java/se/wetcat/qatja/messages/MQTTPublish.java b/core/src/main/java/se/wetcat/qatja/messages/MQTTPublish.java new file mode 100644 index 00000000..5067f1de --- /dev/null +++ b/core/src/main/java/se/wetcat/qatja/messages/MQTTPublish.java @@ -0,0 +1,259 @@ +package se.wetcat.qatja.messages; + +/* + * Copyright (C) 2014 Andreas Goransson + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +import se.wetcat.qatja.MQTTException; +import se.wetcat.qatja.MQTTHelper; + +import static se.wetcat.qatja.MQTTConstants.AT_LEAST_ONCE; +import static se.wetcat.qatja.MQTTConstants.AT_MOST_ONCE; +import static se.wetcat.qatja.MQTTConstants.EXACTLY_ONCE; +import static se.wetcat.qatja.MQTTConstants.PUBLISH; + +/** + * A {@link se.wetcat.qatja.MQTTConstants#PUBLISH} Control Packet is sent from a Client to a Server + * or from Server to a Client to transport an Application Message. + * + * @author Andreas Goransson + * @version 1.0.0 + * @since 2017-05-06 + */ +public class MQTTPublish extends MQTTMessage { + + private boolean dup; + private byte QoS; + private boolean retain; + + private String topicName; + + public static MQTTPublish fromBuffer(byte[] buffer) { + return new MQTTPublish(buffer); + } + + public static MQTTPublish newInstance(String topic, byte[] payload, int identifier) { + return new MQTTPublish(topic, payload, identifier); + } + + public static MQTTPublish newInstance(String topic, byte[] payload, byte QoS, int identifier) { + return new MQTTPublish(topic, payload, QoS, identifier); + } + + /** + * Construct a {@link se.wetcat.qatja.MQTTConstants#PUBLISH} message + * + * @param topic The topic to publish to + * @param payload The message payload + */ + private MQTTPublish(String topic, byte[] payload, int identifier) { + this(topic, payload, AT_MOST_ONCE, identifier); + } + + /** + * Construct a {@link se.wetcat.qatja.MQTTConstants#PUBLISH} message + * + * @param topic the topic to publish to + * @param payload the message payload + * @param QoS the quality of service for message + */ + private MQTTPublish(String topic, byte[] payload, byte QoS, int identifier) { + this.type = PUBLISH; + + this.topicName = topic; + this.payload = payload; + + this.QoS = QoS; + + if (this.QoS > AT_MOST_ONCE) { + setPackageIdentifier(identifier); + } + } + + /** + * Construct a {@link se.wetcat.qatja.MQTTConstants#PUBLISH} message from a buffer + * + * @param buffer The buffer + */ + private MQTTPublish(byte[] buffer) { + int i = 0; + + // Type (just for clarity sake we'll set it...) + this.retain = (buffer[i] >> 0 & 1) == 0x01 ? true : false; + this.QoS = (byte) ((buffer[i] & ((0x00 << 3) | (0x01 << 2) | (0x01 << 1) | (0x00 << 0))) >> 1); + this.dup = ((buffer[i] >> 3) & 1) == 1 ? true : false; + this.setType((byte) ((buffer[i++] >> 4) & 0x0F)); + + // Remaining length + int multiplier = 1; + int len = 0; + byte digit = 0; + do { + digit = buffer[i++]; + len += (digit & 127) * multiplier; + multiplier *= 128; + } while ((digit & 128) != 0); + this.setRemainingLength(len); + + len = (byte) ((buffer[i] >> 8 & 0xFF) | (buffer[i + 1] & 0xFF)); + + // Get variable header (topic length + 2[topic len] + 2[pkg id]) + + switch (getQoS()) { + case AT_MOST_ONCE: + // No packageIdentifier + variableHeader = new byte[len + 2]; + break; + + case AT_LEAST_ONCE: + case EXACTLY_ONCE: + // 2 byte packageIdentifier + variableHeader = new byte[len + 2 + 2]; + break; + } + + System.arraycopy(buffer, i, variableHeader, 0, variableHeader.length); + i += variableHeader.length; + + // Get topic + topicName = new String(variableHeader, 2, len); + + // Get payload + payload = new byte[remainingLength - variableHeader.length]; + + switch (getQoS()) { + case AT_MOST_ONCE: + // No packageIdentifier + break; + + case AT_LEAST_ONCE: + case EXACTLY_ONCE: + // 2 byte packageIdentifier + packageIdentifier = (variableHeader[variableHeader.length - 1]) | (variableHeader[variableHeader.length - 2]); + break; + } + + System.arraycopy(buffer, i, payload, 0, payload.length); + } + + @Override + protected byte[] generateFixedHeader() throws MQTTException, IOException { + // FIXED HEADER + ByteArrayOutputStream out = new ByteArrayOutputStream(); + + byte type = this.type; + byte dup = (byte) (this.dup ? 0x01 : 0x00); + byte QoS = this.QoS; + byte retain = (byte) (this.retain ? 0x01 : 0x00); + + // Type and PUBLISH flags + byte fixed = (byte) ((type << 4) | (dup << 3) | (QoS << 1) | (retain << 0)); + out.write(fixed); + + // Flags (none for PUBLISH) + + // Remaining length + int length = getVariableHeader().length + getPayload().length; + this.setRemainingLength(length); + do { + byte digit = (byte) (length % 128); + length /= 128; + if (length > 0) + digit = (byte) (digit | 0x80); + out.write(digit); + } while (length > 0); + + return out.toByteArray(); + } + + @Override + protected byte[] generateVariableHeader() throws MQTTException, IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + + // TOPIC + // Topic MUST be UTF-8 + if (MQTTHelper.isUTF8(topicName.getBytes("UTF-8"))) + throw new MQTTException("Invalid topic encoding"); + // Topic MUST NOT contain wildcards + if (MQTTHelper.hasWildcards(topicName)) + throw new MQTTException("Invalid topic, may not contain wildcards"); + out.write(MQTTHelper.MSB(topicName.length())); + out.write(MQTTHelper.LSB(topicName.length())); + out.write(topicName.getBytes("UTF-8")); + + // Package identifier ONLY exists if QoS is above AT_MOST_ONCE + if (QoS > AT_MOST_ONCE) { + // Package identifier MUST exist for AT_LEAST_ONCE and EXACTLY_ONCE + if (packageIdentifier == 0) + throw new MQTTException("Package identifier must not be 0"); + + out.write(MQTTHelper.MSB(packageIdentifier)); + out.write(MQTTHelper.LSB(packageIdentifier)); + } + + return out.toByteArray(); + } + + @Override + protected byte[] generatePayload() throws MQTTException, IOException { + return payload; + } + + /** + * Mark this message as duplicate + */ + public void setDup() { + this.dup = true; + } + + /** + * @return The dup + */ + public boolean isDup() { + return dup; + } + + /** + * @return The qoS + */ + public byte getQoS() { + return QoS; + } + + /** + * @return The retain + */ + public boolean isRetain() { + return retain; + } + + /** + * @param retain The retain to set + */ + public void setRetain(boolean retain) { + this.retain = retain; + } + + /** + * @return The topicName + */ + public String getTopicName() { + return topicName; + } + +} diff --git a/core/src/main/java/se/wetcat/qatja/messages/MQTTPubrec.java b/core/src/main/java/se/wetcat/qatja/messages/MQTTPubrec.java new file mode 100644 index 00000000..5a42c478 --- /dev/null +++ b/core/src/main/java/se/wetcat/qatja/messages/MQTTPubrec.java @@ -0,0 +1,135 @@ +package se.wetcat.qatja.messages; + +/* + * Copyright (C) 2014 Andreas Goransson + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +import se.wetcat.qatja.MQTTConstants; +import se.wetcat.qatja.MQTTException; +import se.wetcat.qatja.MQTTHelper; + +/** + * MQTT {@link se.wetcat.qatja.MQTTConstants#PUBREC} message is the response to a + * {@link se.wetcat.qatja.MQTTConstants#PUBLISH} message. It is the second packet of the + * {@link se.wetcat.qatja.MQTTConstants#EXACTLY_ONCE} flow + * + * @author Andreas Goransson + * @version 1.0.0 + * @since 2017-05-06 + */ +public class MQTTPubrec extends MQTTMessage { + + public static MQTTPubrec newInstance(int packageIdentifier) { + return new MQTTPubrec(packageIdentifier); + } + + public static MQTTPubrec fromBuffer(byte[] buffer) { + return new MQTTPubrec(buffer); + } + + /** + * Construct a {@link se.wetcat.qatja.MQTTConstants#PUBREC} message + * + * @param packageIdentifier The package identifier + */ + private MQTTPubrec(int packageIdentifier) { + setType(MQTTConstants.PUBREC); + setPackageIdentifier(packageIdentifier); + } + + /** + * Construct a {@link se.wetcat.qatja.MQTTConstants#PUBREC} message from a buffer + * + * @param buffer The buffer + */ + private MQTTPubrec(byte[] buffer) { + + int i = 0; + + // Type (just for clarity sake we'll set it...) + this.setType((byte) ((buffer[i++] >> 4) & 0x0F)); + + // Remaining length + int multiplier = 1; + int len = 0; + byte digit = 0; + do { + digit = buffer[i++]; + len += (digit & 127) * multiplier; + multiplier *= 128; + } while ((digit & 128) != 0); + this.setRemainingLength(len); + + // Get variable header always 2 (just the pkg id) for PUBREC + variableHeader = new byte[2]; + + // We have to step back two bytes since + System.arraycopy(buffer, i, variableHeader, 0, variableHeader.length); + + // Get payload + payload = new byte[remainingLength - variableHeader.length]; + + if (payload.length > 0) + System.arraycopy(buffer, i, payload, 0, payload.length); + + // Only get package identifier if the QoS is above AT_MOST_ONCE + packageIdentifier = (variableHeader[variableHeader.length - 1]) | (variableHeader[variableHeader.length - 2]); + } + + @Override + protected byte[] generateFixedHeader() throws MQTTException, IOException { + // FIXED HEADER + ByteArrayOutputStream out = new ByteArrayOutputStream(); + + // Type and PUBREC flags, reserved bits MUST be [0,0,0,0] + byte fixed = (byte) ((type << 4) | (0x00 << 3) | (0x00 << 2) | (0x00 << 1) | (0x00) << 0); + out.write(fixed); + + // Flags (none for PUBREC) + + // Remaining length + int length = getVariableHeader().length + getPayload().length; + this.setRemainingLength(length); + do { + byte digit = (byte) (length % 128); + length /= 128; + if (length > 0) + digit = (byte) (digit | 0x80); + out.write(digit); + } while (length > 0); + + return out.toByteArray(); + } + + @Override + protected byte[] generateVariableHeader() throws MQTTException, IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + + out.write(MQTTHelper.MSB(packageIdentifier)); + out.write(MQTTHelper.LSB(packageIdentifier)); + + return out.toByteArray(); + } + + @Override + protected byte[] generatePayload() throws MQTTException, IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + return out.toByteArray(); + } + +} diff --git a/core/src/main/java/se/wetcat/qatja/messages/MQTTPubrel.java b/core/src/main/java/se/wetcat/qatja/messages/MQTTPubrel.java new file mode 100644 index 00000000..ef53c364 --- /dev/null +++ b/core/src/main/java/se/wetcat/qatja/messages/MQTTPubrel.java @@ -0,0 +1,131 @@ +package se.wetcat.qatja.messages; + +/* + * Copyright (C) 2014 Andreas Goransson + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +import se.wetcat.qatja.MQTTException; +import se.wetcat.qatja.MQTTHelper; + +import static se.wetcat.qatja.MQTTConstants.PUBREL; + +/** + * A {@link se.wetcat.qatja.MQTTConstants#PUBREL} Packet is the response to a + * {@link se.wetcat.qatja.MQTTConstants#PUBREC} Packet. It is the third packet of the + * {@link se.wetcat.qatja.MQTTConstants#EXACTLY_ONCE} protocol exchange. + * + * @author Andreas Goransson + * @version 1.0.0 + * @since 2017-05-07 + */ +public class MQTTPubrel extends MQTTMessage { + + public static MQTTPubrel newInstance(int packageIdentifier) { + return new MQTTPubrel(packageIdentifier); + } + + public static MQTTPubrel fromBuffer(byte[] buffer) { + return new MQTTPubrel(buffer); + } + + /** + * Construct a {@link se.wetcat.qatja.MQTTConstants#PUBREL} message + * + * @param packageIdentifier The package identifier + */ + private MQTTPubrel(int packageIdentifier) { + setType(PUBREL); + setPackageIdentifier(packageIdentifier); + } + + private MQTTPubrel(byte[] buffer) { + + int i = 0; + + // Type (just for clarity sake we'll set it...) + this.setType((byte) ((buffer[i++] >> 4) & 0x0F)); + + // Remaining length + int multiplier = 1; + int len = 0; + byte digit = 0; + do { + digit = buffer[i++]; + len += (digit & 127) * multiplier; + multiplier *= 128; + } while ((digit & 128) != 0); + this.setRemainingLength(len); + + // Get variable header always 2 (just the pkg id) for PUBREL + variableHeader = new byte[2]; + + // We have to step back two bytes since + System.arraycopy(buffer, i, variableHeader, 0, variableHeader.length); + + // Get payload + payload = new byte[remainingLength - variableHeader.length]; + + if (payload.length > 0) + System.arraycopy(buffer, i, payload, 0, payload.length); + + // Only get package identifier if the QoS is above AT_MOST_ONCE + packageIdentifier = (variableHeader[variableHeader.length - 1]) | (variableHeader[variableHeader.length - 2]); + } + + @Override + protected byte[] generateFixedHeader() throws MQTTException, IOException { + // FIXED HEADER + ByteArrayOutputStream out = new ByteArrayOutputStream(); + + // Type and PUBLISH flags (The last four bits MUST be [0 0 1 0]) + byte fixed = (byte) ((type << 4) | (0x00 << 3) | (0x00 << 2) | (0x01 << 1) | (0x00 << 0)); + out.write(fixed); + + // Flags (none for PUBREL) + + // Remaining length + int length = getVariableHeader().length + getPayload().length; + this.setRemainingLength(length); + do { + byte digit = (byte) (length % 128); + length /= 128; + if (length > 0) + digit = (byte) (digit | 0x80); + out.write(digit); + } while (length > 0); + + return out.toByteArray(); + } + + @Override + protected byte[] generateVariableHeader() throws MQTTException, IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + + out.write(MQTTHelper.MSB(getPackageIdentifier())); + out.write(MQTTHelper.LSB(getPackageIdentifier())); + + return out.toByteArray(); + } + + @Override + protected byte[] generatePayload() throws MQTTException, IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + return out.toByteArray(); + } + +} diff --git a/core/src/main/java/se/wetcat/qatja/messages/MQTTSuback.java b/core/src/main/java/se/wetcat/qatja/messages/MQTTSuback.java new file mode 100644 index 00000000..0dcc7e39 --- /dev/null +++ b/core/src/main/java/se/wetcat/qatja/messages/MQTTSuback.java @@ -0,0 +1,93 @@ +package se.wetcat.qatja.messages; + +/* + * Copyright (C) 2014 Andreas Goransson + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.io.IOException; + +import se.wetcat.qatja.MQTTException; + +/** + * A {@link se.wetcat.qatja.MQTTConstants#SUBACK} Packet is sent by the Server to the Client to + * confirm receipt and processing of a {@link se.wetcat.qatja.MQTTConstants#SUBSCRIBE} Packet. + *

+ * A {@link se.wetcat.qatja.MQTTConstants#SUBACK} Packet contains a list of return codes, that + * specify the maximum QoS level that was granted in each Subscription that was requested by the + * {@link se.wetcat.qatja.MQTTConstants#SUBSCRIBE}. + * + * @author Andreas Goransson + * @version 1.0.0 + * @since 2017-05-07 + */ +public class MQTTSuback extends MQTTMessage { + + public static MQTTSuback fromBuffer(byte[] buffer) { + return new MQTTSuback(buffer); + } + + /** + * Construct a {@link se.wetcat.qatja.MQTTConstants#SUBACK} message from a buffer + * + * @param buffer The buffer + */ + private MQTTSuback(byte[] buffer) { + int i = 0; + // Type (just for clarity sake we'll set it...) + this.setType((byte) ((buffer[i++] >> 4) & 0x0F)); + + // Remaining length + int multiplier = 1; + int len = 0; + byte digit = 0; + do { + digit = buffer[i++]; + len += (digit & 127) * multiplier; + multiplier *= 128; + } while ((digit & 128) != 0); + this.setRemainingLength(len); + + // Get variable header (always length 2 in SUBACK) + variableHeader = new byte[2]; + System.arraycopy(buffer, i, variableHeader, 0, variableHeader.length); + + // Get payload + payload = new byte[remainingLength - variableHeader.length]; + if (payload.length > 0) + System.arraycopy(buffer, i + variableHeader.length, payload, 0, remainingLength - variableHeader.length); + + // Get package identifier + packageIdentifier = (variableHeader[variableHeader.length - 2] >> 8 & 0xFF) | (variableHeader[variableHeader.length - 1] & 0xFF); + } + + @Override + protected byte[] generateFixedHeader() throws MQTTException, IOException { + // Client doesn't create SUBACK + return null; + } + + @Override + protected byte[] generateVariableHeader() throws MQTTException, IOException { + // Client doesn't create SUBACK + return null; + } + + @Override + protected byte[] generatePayload() throws MQTTException, IOException { + // Client doesn't create SUBACK + return null; + } + +} diff --git a/core/src/main/java/se/wetcat/qatja/messages/MQTTSubscribe.java b/core/src/main/java/se/wetcat/qatja/messages/MQTTSubscribe.java new file mode 100644 index 00000000..554d62b6 --- /dev/null +++ b/core/src/main/java/se/wetcat/qatja/messages/MQTTSubscribe.java @@ -0,0 +1,135 @@ +package se.wetcat.qatja.messages; + +/* + * Copyright (C) 2014 Andreas Goransson + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +import se.wetcat.qatja.MQTTException; +import se.wetcat.qatja.MQTTHelper; + +import static se.wetcat.qatja.MQTTConstants.SUBSCRIBE; + +/** + * The {@link se.wetcat.qatja.MQTTConstants#SUBSCRIBE} Packet is sent from the Client to the Server + * to create one or more Subscriptions. Each Subscription registers a Client’s interest in one or + * more Topics. The Server sends {@link se.wetcat.qatja.MQTTConstants#PUBLISH} Packets to the Client + * in order to forward Application Messages that were published to Topics that match these + * Subscriptions. The {@link se.wetcat.qatja.MQTTConstants#SUBSCRIBE} Packet also specifies (for + * each Subscription) the maximum QoS with which the Server can send Application Messages to the + * Client. + * + * @author Andreas Goransson + * @version 1.0.0 + * @since 2017-05-07 + */ +public class MQTTSubscribe extends MQTTMessage { + + private String[] topicFilters; + private byte[] QoSs; + + public static MQTTSubscribe newInstance(String[] topicFilters, byte[] QoSs, int identifier) { + return new MQTTSubscribe(topicFilters, QoSs, identifier); + } + + /** + * Construct a {@link se.wetcat.qatja.MQTTConstants#SUBSCRIBE} message + * + * @param topicFilters The topics to subscribe to + * @param QoSs The QoS for each topic + */ + private MQTTSubscribe(String[] topicFilters, byte[] QoSs, int identifier) { + this.setType(SUBSCRIBE); + this.topicFilters = topicFilters; + this.QoSs = QoSs; + + setPackageIdentifier(identifier); + } + + @Override + protected byte[] generateFixedHeader() throws MQTTException, IOException { + // FIXED HEADER + ByteArrayOutputStream out = new ByteArrayOutputStream(); + + // Type and reserved bits, MUST be [0 0 1 0] + byte fixed = (byte) ((type << 4) | (0x00 << 3) | (0x00 << 2) | (0x01 << 1) | (0x00 << 0)); + out.write(fixed); + + // Flags (none for SUBSCRIBE) + + // Remaining length + int length = getVariableHeader().length + getPayload().length; + this.setRemainingLength(length); + do { + byte digit = (byte) (length % 128); + length /= 128; + if (length > 0) + digit = (byte) (digit | 0x80); + out.write(digit); + } while (length > 0); + + return out.toByteArray(); + } + + @Override + protected byte[] generateVariableHeader() throws MQTTException, IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + + out.write(MQTTHelper.MSB(getPackageIdentifier())); + out.write(MQTTHelper.LSB(getPackageIdentifier())); + + return out.toByteArray(); + } + + @Override + protected byte[] generatePayload() throws MQTTException, IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + + if (topicFilters.length <= 0 || QoSs.length <= 0) + throw new MQTTException("The SUBSCRIBE message must contain at least one topic filter and QoS pair"); + + if (topicFilters.length != QoSs.length) + throw new MQTTException("The SUBSCRIBE message should have the same number of topic filters and QoS"); + + for (int i = 0; i < topicFilters.length; i++) { + if (MQTTHelper.isUTF8(topicFilters[i].getBytes("UTF-8"))) + throw new MQTTException("Invalid topic filter encoding: " + topicFilters[i]); + + out.write(MQTTHelper.MSB(topicFilters[i].length())); + out.write(MQTTHelper.LSB(topicFilters[i].length())); + out.write(topicFilters[i].getBytes()); + out.write(QoSs[i]); + } + + return out.toByteArray(); + } + + /** + * @return The topicFilters + */ + public String[] getTopicFilters() { + return topicFilters; + } + + /** + * @return The qoSs + */ + public byte[] getQoSs() { + return QoSs; + } + +} diff --git a/core/src/main/java/se/wetcat/qatja/messages/MQTTUnsuback.java b/core/src/main/java/se/wetcat/qatja/messages/MQTTUnsuback.java new file mode 100644 index 00000000..f0c1dba2 --- /dev/null +++ b/core/src/main/java/se/wetcat/qatja/messages/MQTTUnsuback.java @@ -0,0 +1,91 @@ +package se.wetcat.qatja.messages; + +/* + * Copyright (C) 2014 Andreas Goransson + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.io.IOException; + +import se.wetcat.qatja.MQTTException; + +/** + * A {@link se.wetcat.qatja.MQTTConstants#UNSUBACK} package is the response to a + * {@link se.wetcat.qatja.MQTTConstants#UNSUBSCRIBE} packet + * + * @author andreas + */ +public class MQTTUnsuback extends MQTTMessage { + + public static MQTTUnsuback fromBuffer(byte[] buffer) { + return new MQTTUnsuback(buffer); + } + + /** + * Construct a {@link se.wetcat.qatja.MQTTConstants#UNSUBACK} message from a buffer + * + * @param buffer the buffer + */ + private MQTTUnsuback(byte[] buffer) { + + // setBuffer(bufferIn, bufferLength); + + int i = 0; + // Type (just for clarity sake we'll set it...) + this.setType((byte) ((buffer[i++] >> 4) & 0x0F)); + + // Remaining length + int multiplier = 1; + int len = 0; + byte digit = 0; + do { + digit = buffer[i++]; + len += (digit & 127) * multiplier; + multiplier *= 128; + } while ((digit & 128) != 0); + this.setRemainingLength(len); + + // Get variable header (always length 2 in SUBACK) + variableHeader = new byte[2]; + System.arraycopy(buffer, i, variableHeader, 0, variableHeader.length); + + // Get payload + payload = new byte[remainingLength - variableHeader.length]; + if (payload.length > 0) + System.arraycopy(buffer, i + variableHeader.length, payload, 0, + remainingLength - variableHeader.length); + + // Get package identifier + packageIdentifier = (variableHeader[variableHeader.length - 2] >> 8 & 0xFF) | (variableHeader[variableHeader.length - 1] & 0xFF); + } + + @Override + protected byte[] generateFixedHeader() throws MQTTException, IOException { + // Client doesn't create UNSUBACK + return null; + } + + @Override + protected byte[] generateVariableHeader() throws MQTTException, IOException { + // Client doesn't create UNSUBACK + return null; + } + + @Override + protected byte[] generatePayload() throws MQTTException, IOException { + // Client doesn't create UNSUBACK + return null; + } + +} diff --git a/core/src/main/java/se/wetcat/qatja/messages/MQTTUnsubscribe.java b/core/src/main/java/se/wetcat/qatja/messages/MQTTUnsubscribe.java new file mode 100644 index 00000000..7d452a4c --- /dev/null +++ b/core/src/main/java/se/wetcat/qatja/messages/MQTTUnsubscribe.java @@ -0,0 +1,116 @@ +package se.wetcat.qatja.messages; + +/* + * Copyright (C) 2014 Andreas Goransson + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +import se.wetcat.qatja.MQTTException; +import se.wetcat.qatja.MQTTHelper; + +import static se.wetcat.qatja.MQTTConstants.UNSUBSCRIBE; + +/** + * An {@link se.wetcat.qatja.MQTTConstants#UNSUBSCRIBE} Packet is sent by the Client to the Server, + * to unsubscribe from topics. + * + * @author Andreas Goransson + * @version 1.0.0 + * @since 2017-05-07 + */ +public class MQTTUnsubscribe extends MQTTMessage { + + private String[] topicFilters; + + public static MQTTUnsubscribe newInstance(int identifier, String... topicFilters) { + return new MQTTUnsubscribe(identifier, topicFilters); + } + + /** + * Construct a {@link se.wetcat.qatja.MQTTConstants#UNSUBSCRIBE} message + * + * @param topicFilters Topic filters to unsubscribe to + */ + private MQTTUnsubscribe(int identifier, String... topicFilters) { + this.setType(UNSUBSCRIBE); + this.topicFilters = topicFilters; + + setPackageIdentifier(identifier); + } + + @Override + protected byte[] generateFixedHeader() throws MQTTException, IOException { + // FIXED HEADER + ByteArrayOutputStream out = new ByteArrayOutputStream(); + + // Type and reserved bits, MUST be [0 0 1 0] + byte fixed = (byte) ((type << 4) | (0x00 << 3) | (0x00 << 2) | (0x01 << 1) | (0x00 << 0)); + out.write(fixed); + + // Flags (none for UNSUBSCRIBE) + + // Remaining length + int length = getVariableHeader().length + getPayload().length; + this.setRemainingLength(length); + do { + byte digit = (byte) (length % 128); + length /= 128; + if (length > 0) + digit = (byte) (digit | 0x80); + out.write(digit); + } while (length > 0); + + return out.toByteArray(); + } + + @Override + protected byte[] generateVariableHeader() throws MQTTException, IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + + out.write(MQTTHelper.MSB(getPackageIdentifier())); + out.write(MQTTHelper.LSB(getPackageIdentifier())); + + return out.toByteArray(); + } + + @Override + protected byte[] generatePayload() throws MQTTException, IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + + if (topicFilters.length <= 0) + throw new MQTTException("The SUBSCRIBE message must contain at least one topic filter"); + + for (int i = 0; i < topicFilters.length; i++) { + if (MQTTHelper.isUTF8(topicFilters[i].getBytes("UTF-8"))) + throw new MQTTException("Invalid topic filter encoding: " + topicFilters[i]); + + out.write(MQTTHelper.MSB(topicFilters[i].length())); + out.write(MQTTHelper.LSB(topicFilters[i].length())); + out.write(topicFilters[i].getBytes()); + } + + return out.toByteArray(); + } + + /** + * @return The topicFilters + */ + public String[] getTopicFilters() { + return topicFilters; + } + +} diff --git a/core/travis_prebuild.sh b/core/travis_prebuild.sh new file mode 100644 index 00000000..bc2e20fa --- /dev/null +++ b/core/travis_prebuild.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +VERSIONVAL=master + +# Use tag as version +if [ $TRAVIS_TAG ]; then + VERSIONVAL=$TRAVIS_TAG +else + VERSIONVAL="dev" +fi + +sed -e s%@VERSION@%${VERSIONVAL}% .properties.original > gradle.properties \ No newline at end of file -- 2.17.1