├── .gitignore ├── .mvn └── wrapper │ ├── MavenWrapperDownloader.java │ └── maven-wrapper.properties ├── README.md ├── images └── Mqtt-server-arch.jpg ├── lib └── protobuf-java-3.1.0.jar ├── mvnw ├── mvnw.cmd ├── pom.xml └── src ├── main ├── java │ └── com │ │ └── braincs │ │ └── mqttprotobufserver │ │ ├── MqttProtobufServerApplication.java │ │ ├── controller │ │ └── MQTTRestController.java │ │ ├── mqtt │ │ ├── IMQTTPublisher.java │ │ ├── IMQTTSubscriber.java │ │ ├── MQTTPublisher.java │ │ ├── MQTTSubscriber.java │ │ └── MqttConfig.java │ │ └── utils │ │ └── FileUtil.java ├── proto │ └── mqtt.proto └── resources │ ├── application.properties │ └── static │ └── static │ └── test.png └── test └── java └── com └── braincs └── mqttprotobufserver └── MqttProtobufServerApplicationTests.java /.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | /target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | 5 | ### STS ### 6 | .apt_generated 7 | .classpath 8 | .factorypath 9 | .project 10 | .settings 11 | .springBeans 12 | .sts4-cache 13 | 14 | ### IntelliJ IDEA ### 15 | .idea 16 | *.iws 17 | *.iml 18 | *.ipr 19 | 20 | ### NetBeans ### 21 | /nbproject/private/ 22 | /nbbuild/ 23 | /dist/ 24 | /nbdist/ 25 | /.nb-gradle/ 26 | /build/ 27 | 28 | ### VS Code ### 29 | .vscode/ 30 | ### JetBrains template 31 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm 32 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 33 | 34 | # User-specific stuff 35 | .idea/**/workspace.xml 36 | .idea/**/tasks.xml 37 | .idea/**/usage.statistics.xml 38 | .idea/**/dictionaries 39 | .idea/**/shelf 40 | 41 | # Sensitive or high-churn files 42 | .idea/**/dataSources/ 43 | .idea/**/dataSources.ids 44 | .idea/**/dataSources.local.xml 45 | .idea/**/sqlDataSources.xml 46 | .idea/**/dynamic.xml 47 | .idea/**/uiDesigner.xml 48 | .idea/**/dbnavigator.xml 49 | 50 | # Gradle 51 | .idea/**/gradle.xml 52 | .idea/**/libraries 53 | 54 | # Gradle and Maven with auto-import 55 | # When using Gradle or Maven with auto-import, you should exclude module files, 56 | # since they will be recreated, and may cause churn. Uncomment if using 57 | # auto-import. 58 | # .idea/modules.xml 59 | # .idea/*.iml 60 | # .idea/modules 61 | 62 | # CMake 63 | cmake-build-*/ 64 | 65 | # Mongo Explorer plugin 66 | .idea/**/mongoSettings.xml 67 | 68 | # File-based project format 69 | 70 | # IntelliJ 71 | out/ 72 | 73 | # mpeltonen/sbt-idea plugin 74 | .idea_modules/ 75 | 76 | # JIRA plugin 77 | atlassian-ide-plugin.xml 78 | 79 | # Cursive Clojure plugin 80 | .idea/replstate.xml 81 | 82 | # Crashlytics plugin (for Android Studio and IntelliJ) 83 | com_crashlytics_export_strings.xml 84 | crashlytics.properties 85 | crashlytics-build.properties 86 | fabric.properties 87 | 88 | # Editor-based Rest Client 89 | .idea/httpRequests 90 | ### Maven template 91 | target/ 92 | pom.xml.tag 93 | pom.xml.releaseBackup 94 | pom.xml.versionsBackup 95 | pom.xml.next 96 | release.properties 97 | dependency-reduced-pom.xml 98 | buildNumber.properties 99 | .mvn/timing.properties 100 | .mvn/wrapper/maven-wrapper.jar 101 | /tmp/ 102 | -------------------------------------------------------------------------------- /.mvn/wrapper/MavenWrapperDownloader.java: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to the Apache Software Foundation (ASF) under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. The ASF licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | https://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | */ 19 | 20 | import java.io.File; 21 | import java.io.FileInputStream; 22 | import java.io.FileOutputStream; 23 | import java.io.IOException; 24 | import java.net.URL; 25 | import java.nio.channels.Channels; 26 | import java.nio.channels.ReadableByteChannel; 27 | import java.util.Properties; 28 | 29 | public class MavenWrapperDownloader { 30 | 31 | /** 32 | * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. 33 | */ 34 | private static final String DEFAULT_DOWNLOAD_URL = 35 | "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar"; 36 | 37 | /** 38 | * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to 39 | * use instead of the default one. 40 | */ 41 | private static final String MAVEN_WRAPPER_PROPERTIES_PATH = 42 | ".mvn/wrapper/maven-wrapper.properties"; 43 | 44 | /** 45 | * Path where the maven-wrapper.jar will be saved to. 46 | */ 47 | private static final String MAVEN_WRAPPER_JAR_PATH = 48 | ".mvn/wrapper/maven-wrapper.jar"; 49 | 50 | /** 51 | * Name of the property which should be used to override the default download url for the wrapper. 52 | */ 53 | private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; 54 | 55 | public static void main(String args[]) { 56 | System.out.println("- Downloader started"); 57 | File baseDirectory = new File(args[0]); 58 | System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); 59 | 60 | // If the maven-wrapper.properties exists, read it and check if it contains a custom 61 | // wrapperUrl parameter. 62 | File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); 63 | String url = DEFAULT_DOWNLOAD_URL; 64 | if (mavenWrapperPropertyFile.exists()) { 65 | FileInputStream mavenWrapperPropertyFileInputStream = null; 66 | try { 67 | mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); 68 | Properties mavenWrapperProperties = new Properties(); 69 | mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); 70 | url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); 71 | } catch (IOException e) { 72 | System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); 73 | } finally { 74 | try { 75 | if (mavenWrapperPropertyFileInputStream != null) { 76 | mavenWrapperPropertyFileInputStream.close(); 77 | } 78 | } catch (IOException e) { 79 | // Ignore ... 80 | } 81 | } 82 | } 83 | System.out.println("- Downloading from: : " + url); 84 | 85 | File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); 86 | if (!outputFile.getParentFile().exists()) { 87 | if (!outputFile.getParentFile().mkdirs()) { 88 | System.out.println( 89 | "- ERROR creating output direcrory '" + outputFile.getParentFile().getAbsolutePath() + "'"); 90 | } 91 | } 92 | System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); 93 | try { 94 | downloadFileFromURL(url, outputFile); 95 | System.out.println("Done"); 96 | System.exit(0); 97 | } catch (Throwable e) { 98 | System.out.println("- Error downloading"); 99 | e.printStackTrace(); 100 | System.exit(1); 101 | } 102 | } 103 | 104 | private static void downloadFileFromURL(String urlString, File destination) throws Exception { 105 | URL website = new URL(urlString); 106 | ReadableByteChannel rbc; 107 | rbc = Channels.newChannel(website.openStream()); 108 | FileOutputStream fos = new FileOutputStream(destination); 109 | fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); 110 | fos.close(); 111 | rbc.close(); 112 | } 113 | 114 | } 115 | -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.0/apache-maven-3.6.0-bin.zip 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Architecture 2 | 3 | ![Mqtt-server-arch](images/Mqtt-server-arch.jpg) 4 | 5 | Server side 构成 6 | 7 | - broker (mqtt核心:用于消息的发送管理) 8 | - Application Server用于处理RestFul的请求,转发为Mqtt消息 9 | - Publisher **本质是Mqtt client**用于发布server端消息 10 | - Subscriber **本质是Mqtt client**用于订阅client端消息,并显示 11 | - Client side 12 | - Publisher用于发布client端消息 13 | - Subscriber用于订阅server端的消息 14 | - Client 用于发送RestFul 请求给Application Server触发消息pub/sub 15 | 16 | **总结**:从结构上Broker算是Mqtt的本质上的Server端,从业务上讲封装了Mqtt Client pub/sub的Application server和Broker共同构成了业务上的Server端 17 | 18 | ## 安装mosquitto及基本使用 19 | 20 | ### 安装 21 | 22 | ```bash 23 | # Install Mosquitto Broker 24 | sudo apt-get update 25 | sudo apt-get install mosquitto 26 | 27 | # Install the Clients 28 | sudo apt-get install mosquitto-clients 29 | 30 | ``` 31 | 32 | ### 开启、停止查看状态 33 | 34 | ```bash 35 | # 查看状态 36 | sudo service mosquitto status 37 | 38 | # 使用默认配置打开mosquitto, 使用-v打开log功能 39 | sudo mosquitto -c /etc/mosquitto/mosquitto.conf -v 40 | 41 | # 停止 42 | sudo service mosquitto stop 43 | 44 | #开启 45 | sudo service mosquitto start 46 | ``` 47 | 48 | ### 使用mosquitto测试pub/sub 49 | 50 | `注意` pub和sub的clientid不能相同,相同会刷屏。 51 | 52 | ```bash 53 | # 简单测试发布。 -h host -t topic -m message 54 | mosquitto_pub -h localhost -t mqtt-test -m 'hello mqtt' 55 | 56 | # 简单测试订阅。 57 | mosquitto_sub -h localhost -t mqtt-test 58 | 59 | # 发布设置用户密码 -u user -P password 60 | mosquitto_pub -u admin -P admin -h localhost -t mqtt/loop/message -m 'test mqtt' 61 | mosquitto_sub -u admin -P admin -h localhost -t mqtt/loop/message 62 | 63 | # 指定发布clientid -i (id to use for this client) 64 | mosquitto_sub -u admin -P admin -i shuai-ubuntu-test -h localhost -t mqtt/loop/message 65 | mosquitto_pub -u admin -P admin -i shuai-ubuntu-test-client -h localhost -t mqtt/loop/message -m 'test mqtt client' 66 | ``` 67 | 68 | ### 查看broker的log 69 | 70 | mosquitto的默认log 地址是:/var/log/mosquitto/xxx.log 71 | 72 | ```bash 73 | tailf /var/log/mosquitto/mosquitto.log 74 | ``` 75 | 76 | 77 | 78 | ## 构建Java-Mqtt-Server(Springboot + Mqtt) 79 | 80 | ### requirement依赖 81 | 82 | 1. mosquitto broker 83 | 84 | 可以使用Eclipse公开的broker,据说底层也是mosquitto。地址为`iot.eclipse.org` 85 | 86 | 可以部署安装mosquitto(本文方案) 87 | 88 | 2. springboot (2.1.5.RELEASE) 89 | 90 | 3. Eclipse Paho 91 | 92 | 4. curl/postman 93 | 94 | ### 构建springboot项目 95 | 96 | #### 1. 使用idea springboot initializer 初始化springboot工程 97 | 98 | 使用springboot版本**2.1.5.RELEASE** 99 | 100 | #### 2. pom中添加 101 | 102 | ```xml 103 | 104 | org.springframework.boot 105 | spring-boot-starter-integration 106 | 107 | 108 | org.springframework.integration 109 | spring-integration-stream 110 | 111 | 112 | 113 | org.springframework.integration 114 | spring-integration-mqtt 115 | 116 | 117 | org.projectlombok 118 | lombok 119 | 1.16.10 120 | provided 121 | 122 | ``` 123 | 124 | #### 3. MQTT Configuration 125 | 126 | * 配置broker地址, 127 | * 端口号, 128 | * 是否使用ssl, 129 | * 用户名 130 | * 密码 131 | 132 | ~~~java 133 | public abstract class MQTTConfig { 134 | 135 | protected final String broker = "10.156.2.132"; 136 | protected final int qos = 2; 137 | protected Boolean hasSSL = false; /* By default SSL is disabled */ 138 | protected Integer port = 1883; /* Default port */ 139 | protected final String userName = "admin"; 140 | protected final String password = "admin"; 141 | protected final String TCP = "tcp://"; 142 | protected final String SSL = "ssl://"; 143 | 144 | /** 145 | * Custom Configuration 146 | * 147 | * @param broker 148 | * @param port 149 | * @param ssl 150 | * @param withUserNamePass 151 | */ 152 | protected abstract void config(String broker, Integer port, Boolean ssl, Boolean withUserNamePass); 153 | 154 | /** 155 | * Default Configuration 156 | */ 157 | protected abstract void config(); 158 | } 159 | 160 | ~~~ 161 | 162 | 163 | 164 | #### 4. Publisher推送者 165 | 166 | 定义接口 167 | 168 | ```java 169 | public interface IMQTTPublisher { 170 | /** 171 | * Publish message 172 | * 173 | * @param topic 174 | * @param String Message 175 | */ 176 | public void publishMessage(String topic, String message); 177 | 178 | /** 179 | * Disconnect MQTT Client 180 | */ 181 | public void disconnect(); 182 | } 183 | 184 | ``` 185 | 186 | 定义类 187 | 188 | ```java 189 | 190 | import org.eclipse.paho.client.mqttv3.*; 191 | import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence; 192 | import org.slf4j.Logger; 193 | import org.slf4j.LoggerFactory; 194 | import org.springframework.stereotype.Component; 195 | 196 | @Component 197 | public class MQTTPublisher extends MQTTConfig implements MqttCallback, IMQTTPublisher { 198 | 199 | private String brokerUrl = null; 200 | 201 | final private String colon = ":"; 202 | final private String clientId = "mqtt_server_pub"; 203 | 204 | private MqttClient mqttClient = null; 205 | private MqttConnectOptions connectionOptions = null; 206 | private MemoryPersistence persistence = null; 207 | 208 | private static final Logger logger = LoggerFactory.getLogger(MQTTPublisher.class); 209 | 210 | /** 211 | * Private default constructor 212 | */ 213 | private MQTTPublisher() { 214 | this.config(); 215 | } 216 | 217 | /** 218 | * Private constructor 219 | */ 220 | private MQTTPublisher(String broker, Integer port, Boolean ssl, Boolean withUserNamePass) { 221 | this.config(broker, port, ssl, withUserNamePass); 222 | } 223 | 224 | /** 225 | * Factory method to get instance of MQTTPublisher 226 | * 227 | * @return MQTTPublisher 228 | */ 229 | public static MQTTPublisher getInstance() { 230 | return new MQTTPublisher(); 231 | } 232 | 233 | /** 234 | * Factory method to get instance of MQTTPublisher 235 | * 236 | * @param broker 237 | * @param port 238 | * @param ssl 239 | * @param withUserNamePass 240 | * @return MQTTPublisher 241 | */ 242 | public static MQTTPublisher getInstance(String broker, Integer port, Boolean ssl, Boolean withUserNamePass) { 243 | return new MQTTPublisher(broker, port, ssl, withUserNamePass); 244 | } 245 | 246 | /* 247 | * (non-Javadoc) 248 | * 249 | * @see 250 | * com.bjitgroup.jasmysp.mqtt.publisher.MQTTPublisherBase#configurePublisher() 251 | */ 252 | @Override 253 | protected void config() { 254 | 255 | this.brokerUrl = this.TCP + this.broker + colon + this.port; 256 | this.persistence = new MemoryPersistence(); 257 | this.connectionOptions = new MqttConnectOptions(); 258 | try { 259 | this.mqttClient = new MqttClient(brokerUrl, clientId, persistence); 260 | this.connectionOptions.setCleanSession(true); 261 | this.mqttClient.connect(this.connectionOptions); 262 | this.mqttClient.setCallback(this); 263 | } catch (MqttException me) { 264 | logger.error("ERROR", me); 265 | } 266 | } 267 | 268 | /* 269 | * (non-Javadoc) 270 | * 271 | * @see 272 | * com.bjitgroup.jasmysp.mqtt.publisher.MQTTPublisherBase#configurePublisher( 273 | * java.lang.String, java.lang.Integer, java.lang.Boolean, java.lang.Boolean) 274 | */ 275 | @Override 276 | protected void config(String broker, Integer port, Boolean ssl, Boolean withUserNamePass) { 277 | 278 | String protocal = this.TCP; 279 | if (true == ssl) { 280 | protocal = this.SSL; 281 | } 282 | 283 | this.brokerUrl = protocal + this.broker + colon + port; 284 | this.persistence = new MemoryPersistence(); 285 | this.connectionOptions = new MqttConnectOptions(); 286 | 287 | try { 288 | this.mqttClient = new MqttClient(brokerUrl, clientId, persistence); 289 | this.connectionOptions.setCleanSession(true); 290 | if (true == withUserNamePass) { 291 | if (password != null) { 292 | this.connectionOptions.setPassword(this.password.toCharArray()); 293 | } 294 | if (userName != null) { 295 | this.connectionOptions.setUserName(this.userName); 296 | } 297 | } 298 | this.mqttClient.connect(this.connectionOptions); 299 | this.mqttClient.setCallback(this); 300 | } catch (MqttException me) { 301 | logger.error("ERROR", me); 302 | } 303 | } 304 | 305 | 306 | /* 307 | * (non-Javadoc) 308 | * @see com.monirthought.mqtt.publisher.MQTTPublisherBase#publishMessage(java.lang.String, java.lang.String) 309 | */ 310 | @Override 311 | public void publishMessage(String topic, String message) { 312 | 313 | try { 314 | MqttMessage mqttmessage = new MqttMessage(message.getBytes()); 315 | mqttmessage.setQos(this.qos); 316 | this.mqttClient.publish(topic, mqttmessage); 317 | } catch (MqttException me) { 318 | logger.error("ERROR", me); 319 | } 320 | 321 | } 322 | 323 | /* 324 | * (non-Javadoc) 325 | * @see org.eclipse.paho.client.mqttv3.MqttCallback#connectionLost(java.lang.Throwable) 326 | */ 327 | @Override 328 | public void connectionLost(Throwable arg0) { 329 | logger.info("Connection Lost"); 330 | 331 | } 332 | 333 | /* 334 | * (non-Javadoc) 335 | * @see org.eclipse.paho.client.mqttv3.MqttCallback#deliveryComplete(org.eclipse.paho.client.mqttv3.IMqttDeliveryToken) 336 | */ 337 | @Override 338 | public void deliveryComplete(IMqttDeliveryToken arg0) { 339 | logger.info("delivery completed"); 340 | 341 | } 342 | 343 | /* 344 | * (non-Javadoc) 345 | * @see org.eclipse.paho.client.mqttv3.MqttCallback#messageArrived(java.lang.String, org.eclipse.paho.client.mqttv3.MqttMessage) 346 | */ 347 | @Override 348 | public void messageArrived(String arg0, MqttMessage arg1) throws Exception { 349 | // Leave it blank for Publisher 350 | 351 | } 352 | 353 | /* 354 | * (non-Javadoc) 355 | * @see com.monirthought.mqtt.publisher.MQTTPublisherBase#disconnect() 356 | */ 357 | @Override 358 | public void disconnect() { 359 | try { 360 | this.mqttClient.disconnect(); 361 | } catch (MqttException me) { 362 | logger.error("ERROR", me); 363 | } 364 | } 365 | 366 | } 367 | ``` 368 | 369 | 370 | 371 | #### 5. Subscriber 订阅者 372 | 373 | 定义接口 374 | 375 | ```java 376 | import org.slf4j.Logger; 377 | import org.slf4j.LoggerFactory; 378 | 379 | public interface IMQTTSubscriber { 380 | public static final Logger logger = LoggerFactory.getLogger(IMQTTSubscriber.class); 381 | 382 | /** 383 | * Subscribe message 384 | * 385 | * @param topic 386 | * @param jasonMessage 387 | */ 388 | public void subscribeMessage(String topic); 389 | 390 | /** 391 | * Disconnect MQTT Client 392 | */ 393 | public void disconnect(); 394 | } 395 | 396 | ``` 397 | 398 | 类定义 399 | 400 | ```java 401 | import org.eclipse.paho.client.mqttv3.*; 402 | import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence; 403 | import org.slf4j.Logger; 404 | import org.slf4j.LoggerFactory; 405 | import org.springframework.stereotype.Component; 406 | 407 | import java.sql.Timestamp; 408 | 409 | @Component 410 | public class MQTTSubscriber extends MQTTConfig implements MqttCallback, IMQTTSubscriber { 411 | 412 | private String brokerUrl = null; 413 | final private String colon = ":"; 414 | final private String clientId = "mqtt_server_sub"; 415 | 416 | private MqttClient mqttClient = null; 417 | private MqttConnectOptions connectionOptions = null; 418 | private MemoryPersistence persistence = null; 419 | 420 | private static final Logger logger = LoggerFactory.getLogger(MQTTSubscriber.class); 421 | 422 | public MQTTSubscriber() { 423 | this.config(); 424 | } 425 | 426 | /* 427 | * (non-Javadoc) 428 | * 429 | * @see org.eclipse.paho.client.mqttv3.MqttCallback#connectionLost(java.lang. 430 | * Throwable) 431 | */ 432 | @Override 433 | public void connectionLost(Throwable cause) { 434 | logger.info("Connection Lost"); 435 | 436 | } 437 | 438 | /* 439 | * (non-Javadoc) 440 | * 441 | * @see 442 | * org.eclipse.paho.client.mqttv3.MqttCallback#messageArrived(java.lang.String, 443 | * org.eclipse.paho.client.mqttv3.MqttMessage) 444 | */ 445 | @Override 446 | public void messageArrived(String topic, MqttMessage message) throws Exception { 447 | // Called when a message arrives from the server that matches any 448 | // subscription made by the client 449 | String time = new Timestamp(System.currentTimeMillis()).toString(); 450 | System.out.println(); 451 | System.out.println("***********************************************************************"); 452 | System.out.println("Message Arrived at Time: " + time + " Topic: " + topic + " Message: " 453 | + new String(message.getPayload())); 454 | System.out.println("***********************************************************************"); 455 | System.out.println(); 456 | } 457 | 458 | /* 459 | * (non-Javadoc) 460 | * 461 | * @see 462 | * org.eclipse.paho.client.mqttv3.MqttCallback#deliveryComplete(org.eclipse.paho 463 | * .client.mqttv3.IMqttDeliveryToken) 464 | */ 465 | @Override 466 | public void deliveryComplete(IMqttDeliveryToken token) { 467 | // Leave it blank for subscriber 468 | 469 | } 470 | 471 | /* 472 | * (non-Javadoc) 473 | * 474 | * @see 475 | * com.monirthought.mqtt.subscriber.MQTTSubscriberBase#subscribeMessage(java. 476 | * lang.String) 477 | */ 478 | @Override 479 | public void subscribeMessage(String topic) { 480 | try { 481 | this.mqttClient.subscribe(topic, this.qos); 482 | } catch (MqttException me) { 483 | me.printStackTrace(); 484 | } 485 | } 486 | 487 | /* 488 | * (non-Javadoc) 489 | * 490 | * @see com.monirthought.mqtt.subscriber.MQTTSubscriberBase#disconnect() 491 | */ 492 | public void disconnect() { 493 | try { 494 | this.mqttClient.disconnect(); 495 | } catch (MqttException me) { 496 | logger.error("ERROR", me); 497 | } 498 | } 499 | 500 | /* 501 | * (non-Javadoc) 502 | * 503 | * @see com.monirthought.config.MQTTConfig#config(java.lang.String, 504 | * java.lang.Integer, java.lang.Boolean, java.lang.Boolean) 505 | */ 506 | @Override 507 | protected void config(String broker, Integer port, Boolean ssl, Boolean withUserNamePass) { 508 | 509 | String protocal = this.TCP; 510 | if (true == ssl) { 511 | protocal = this.SSL; 512 | } 513 | 514 | this.brokerUrl = protocal + this.broker + colon + port; 515 | this.persistence = new MemoryPersistence(); 516 | this.connectionOptions = new MqttConnectOptions(); 517 | 518 | try { 519 | this.mqttClient = new MqttClient(brokerUrl, clientId, persistence); 520 | this.connectionOptions.setCleanSession(true); 521 | if (true == withUserNamePass) { 522 | if (password != null) { 523 | this.connectionOptions.setPassword(this.password.toCharArray()); 524 | } 525 | if (userName != null) { 526 | this.connectionOptions.setUserName(this.userName); 527 | } 528 | } 529 | this.mqttClient.connect(this.connectionOptions); 530 | this.mqttClient.setCallback(this); 531 | } catch (MqttException me) { 532 | me.printStackTrace(); 533 | } 534 | 535 | } 536 | 537 | /* 538 | * (non-Javadoc) 539 | * 540 | * @see com.monirthought.config.MQTTConfig#config() 541 | */ 542 | @Override 543 | protected void config() { 544 | 545 | this.brokerUrl = this.TCP + this.broker + colon + this.port; 546 | this.persistence = new MemoryPersistence(); 547 | this.connectionOptions = new MqttConnectOptions(); 548 | try { 549 | this.mqttClient = new MqttClient(brokerUrl, clientId, persistence); 550 | this.connectionOptions.setCleanSession(true); 551 | this.mqttClient.connect(this.connectionOptions); 552 | this.mqttClient.setCallback(this); 553 | } catch (MqttException me) { 554 | me.printStackTrace(); 555 | } 556 | 557 | } 558 | 559 | } 560 | ``` 561 | 562 | #### 6. 构建 RestFul接口 563 | 564 | 构建Controller 565 | 566 | ```java 567 | import lombok.extern.slf4j.Slf4j; 568 | import org.slf4j.Logger; 569 | import org.slf4j.LoggerFactory; 570 | import org.springframework.beans.factory.annotation.Autowired; 571 | import org.springframework.web.bind.annotation.*; 572 | 573 | import javax.annotation.PostConstruct; 574 | 575 | @Slf4j 576 | @RestController 577 | public class DemoRestController { 578 | public static String TOPIC_LOOP_TEST = "mqtt/loop/message"; 579 | 580 | @Autowired 581 | IMQTTPublisher publisher; 582 | 583 | @Autowired 584 | IMQTTSubscriber subscriber; 585 | 586 | @PostConstruct 587 | public void init() { 588 | subscriber.subscribeMessage(TOPIC_LOOP_TEST); 589 | } 590 | 591 | @RequestMapping(value = "/mqtt/loop/message", method = RequestMethod.POST) 592 | public String index(@RequestBody String data) { 593 | publisher.publishMessage(TOPIC_LOOP_TEST, data); 594 | return "Success"; 595 | } 596 | 597 | } 598 | ``` 599 | 600 | #### 7. 使用curl命令进行api调用测试 601 | 602 | ```bash 603 | ❯ curl -X POST "http://127.0.0.1:8080/mqtt/loop/message" -d "test" 604 | Success% 605 | 606 | # springboot 窗口中可以看到自己sub的回显 607 | *********************************************************************** 608 | Message Arrived at Time: 2019-05-21 16:11:13.675 Topic: mqtt/loop/message Message: test= 609 | *********************************************************************** 610 | ``` 611 | 612 | 也可以使用postman 调用8080 端口调试。 613 | 614 | ## 构建Java-Mqtt-Server (Springboot + Mqtt +protobuf) 615 | 616 | 在现有基础上添加protobuf包装pub/sub 消息 617 | 618 | #### 1. proto文件 619 | 620 | 将.proto文件放到`src/main/proto/`下 621 | 622 | #### 2. 使用maven生成protobuf java代码 623 | 624 | pom中properties中添加 625 | 626 | ```xml 627 | 628 | 1.8 629 | 1.6.1 630 | 3.3.0 631 | 632 | ``` 633 | 634 | pom dependencies中添加 635 | 636 | ```xml 637 | 638 | io.grpc 639 | grpc-netty 640 | ${grpc.version} 641 | provided 642 | 643 | 644 | io.grpc 645 | grpc-protobuf 646 | ${grpc.version} 647 | provided 648 | 649 | 650 | io.grpc 651 | grpc-stub 652 | ${grpc.version} 653 | provided 654 | 655 | 656 | com.google.protobuf 657 | protobuf-java 658 | ${protobuf.version} 659 | 660 | ``` 661 | 662 | pom build中添加,pom plugins中添加 663 | 664 | ```xml 665 | 666 | 667 | 668 | 669 | kr.motd.maven 670 | os-maven-plugin 671 | 1.5.0.Final 672 | 673 | 674 | 675 | 676 | 677 | org.springframework.boot 678 | spring-boot-maven-plugin 679 | 680 | 681 | 682 | org.xolstice.maven.plugins 683 | protobuf-maven-plugin 684 | 0.5.0 685 | 686 | com.google.protobuf:protoc:${protobuf.version}:exe:${os.detected.classifier} 687 | grpc-java 688 | io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier} 689 | 690 | 691 | 692 | 693 | compile 694 | compile-custom 695 | 696 | 697 | 698 | 699 | 700 | 701 | ``` 702 | 703 | 704 | 705 | 使用IDE中右侧Maven Projects -> Lifecycle ->compile 生成java对应的protobuf文件 706 | 707 | 生成的路径在:`target/generated-sources/protobuf/java/对应的包名下 ` 708 | 709 | #### 3. 使用proto封装mqtt message 710 | 711 | 使用Proto中的newBuilder构建builder。使用builder中的set方法设置proto中的参数,例如: 712 | 713 | ```java 714 | KylinProto.Group.Builder builder = KylinProto.Group.newBuilder(); 715 | KylinProto.Group group = builder.setThreshold(85.f) 716 | .setTop(1) 717 | .setGroup(group_name).build(); 718 | publisher.publish(topic, group.toByteArray(), 2, false); 719 | 720 | ``` 721 | 722 | -------------------------------------------------------------------------------- /images/Mqtt-server-arch.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/braincs/mqtt-protobuf-server/d20dcfdcc75e149b986a06729577b4dfc4327f63/images/Mqtt-server-arch.jpg -------------------------------------------------------------------------------- /lib/protobuf-java-3.1.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/braincs/mqtt-protobuf-server/d20dcfdcc75e149b986a06729577b4dfc4327f63/lib/protobuf-java-3.1.0.jar -------------------------------------------------------------------------------- /mvnw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------- 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # https://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # ---------------------------------------------------------------------------- 20 | 21 | # ---------------------------------------------------------------------------- 22 | # Maven2 Start Up Batch script 23 | # 24 | # Required ENV vars: 25 | # ------------------ 26 | # JAVA_HOME - location of a JDK home dir 27 | # 28 | # Optional ENV vars 29 | # ----------------- 30 | # M2_HOME - location of maven2's installed home dir 31 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven 32 | # e.g. to debug Maven itself, use 33 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 34 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files 35 | # ---------------------------------------------------------------------------- 36 | 37 | if [ -z "$MAVEN_SKIP_RC" ] ; then 38 | 39 | if [ -f /etc/mavenrc ] ; then 40 | . /etc/mavenrc 41 | fi 42 | 43 | if [ -f "$HOME/.mavenrc" ] ; then 44 | . "$HOME/.mavenrc" 45 | fi 46 | 47 | fi 48 | 49 | # OS specific support. $var _must_ be set to either true or false. 50 | cygwin=false; 51 | darwin=false; 52 | mingw=false 53 | case "`uname`" in 54 | CYGWIN*) cygwin=true ;; 55 | MINGW*) mingw=true;; 56 | Darwin*) darwin=true 57 | # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home 58 | # See https://developer.apple.com/library/mac/qa/qa1170/_index.html 59 | if [ -z "$JAVA_HOME" ]; then 60 | if [ -x "/usr/libexec/java_home" ]; then 61 | export JAVA_HOME="`/usr/libexec/java_home`" 62 | else 63 | export JAVA_HOME="/Library/Java/Home" 64 | fi 65 | fi 66 | ;; 67 | esac 68 | 69 | if [ -z "$JAVA_HOME" ] ; then 70 | if [ -r /etc/gentoo-release ] ; then 71 | JAVA_HOME=`java-config --jre-home` 72 | fi 73 | fi 74 | 75 | if [ -z "$M2_HOME" ] ; then 76 | ## resolve links - $0 may be a link to maven's home 77 | PRG="$0" 78 | 79 | # need this for relative symlinks 80 | while [ -h "$PRG" ] ; do 81 | ls=`ls -ld "$PRG"` 82 | link=`expr "$ls" : '.*-> \(.*\)$'` 83 | if expr "$link" : '/.*' > /dev/null; then 84 | PRG="$link" 85 | else 86 | PRG="`dirname "$PRG"`/$link" 87 | fi 88 | done 89 | 90 | saveddir=`pwd` 91 | 92 | M2_HOME=`dirname "$PRG"`/.. 93 | 94 | # make it fully qualified 95 | M2_HOME=`cd "$M2_HOME" && pwd` 96 | 97 | cd "$saveddir" 98 | # echo Using m2 at $M2_HOME 99 | fi 100 | 101 | # For Cygwin, ensure paths are in UNIX format before anything is touched 102 | if $cygwin ; then 103 | [ -n "$M2_HOME" ] && 104 | M2_HOME=`cygpath --unix "$M2_HOME"` 105 | [ -n "$JAVA_HOME" ] && 106 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 107 | [ -n "$CLASSPATH" ] && 108 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"` 109 | fi 110 | 111 | # For Mingw, ensure paths are in UNIX format before anything is touched 112 | if $mingw ; then 113 | [ -n "$M2_HOME" ] && 114 | M2_HOME="`(cd "$M2_HOME"; pwd)`" 115 | [ -n "$JAVA_HOME" ] && 116 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" 117 | # TODO classpath? 118 | fi 119 | 120 | if [ -z "$JAVA_HOME" ]; then 121 | javaExecutable="`which javac`" 122 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then 123 | # readlink(1) is not available as standard on Solaris 10. 124 | readLink=`which readlink` 125 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then 126 | if $darwin ; then 127 | javaHome="`dirname \"$javaExecutable\"`" 128 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" 129 | else 130 | javaExecutable="`readlink -f \"$javaExecutable\"`" 131 | fi 132 | javaHome="`dirname \"$javaExecutable\"`" 133 | javaHome=`expr "$javaHome" : '\(.*\)/bin'` 134 | JAVA_HOME="$javaHome" 135 | export JAVA_HOME 136 | fi 137 | fi 138 | fi 139 | 140 | if [ -z "$JAVACMD" ] ; then 141 | if [ -n "$JAVA_HOME" ] ; then 142 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 143 | # IBM's JDK on AIX uses strange locations for the executables 144 | JAVACMD="$JAVA_HOME/jre/sh/java" 145 | else 146 | JAVACMD="$JAVA_HOME/bin/java" 147 | fi 148 | else 149 | JAVACMD="`which java`" 150 | fi 151 | fi 152 | 153 | if [ ! -x "$JAVACMD" ] ; then 154 | echo "Error: JAVA_HOME is not defined correctly." >&2 155 | echo " We cannot execute $JAVACMD" >&2 156 | exit 1 157 | fi 158 | 159 | if [ -z "$JAVA_HOME" ] ; then 160 | echo "Warning: JAVA_HOME environment variable is not set." 161 | fi 162 | 163 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher 164 | 165 | # traverses directory structure from process work directory to filesystem root 166 | # first directory with .mvn subdirectory is considered project base directory 167 | find_maven_basedir() { 168 | 169 | if [ -z "$1" ] 170 | then 171 | echo "Path not specified to find_maven_basedir" 172 | return 1 173 | fi 174 | 175 | basedir="$1" 176 | wdir="$1" 177 | while [ "$wdir" != '/' ] ; do 178 | if [ -d "$wdir"/.mvn ] ; then 179 | basedir=$wdir 180 | break 181 | fi 182 | # workaround for JBEAP-8937 (on Solaris 10/Sparc) 183 | if [ -d "${wdir}" ]; then 184 | wdir=`cd "$wdir/.."; pwd` 185 | fi 186 | # end of workaround 187 | done 188 | echo "${basedir}" 189 | } 190 | 191 | # concatenates all lines of a file 192 | concat_lines() { 193 | if [ -f "$1" ]; then 194 | echo "$(tr -s '\n' ' ' < "$1")" 195 | fi 196 | } 197 | 198 | BASE_DIR=`find_maven_basedir "$(pwd)"` 199 | if [ -z "$BASE_DIR" ]; then 200 | exit 1; 201 | fi 202 | 203 | ########################################################################################## 204 | # Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 205 | # This allows using the maven wrapper in projects that prohibit checking in binary data. 206 | ########################################################################################## 207 | if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then 208 | if [ "$MVNW_VERBOSE" = true ]; then 209 | echo "Found .mvn/wrapper/maven-wrapper.jar" 210 | fi 211 | else 212 | if [ "$MVNW_VERBOSE" = true ]; then 213 | echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." 214 | fi 215 | jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar" 216 | while IFS="=" read key value; do 217 | case "$key" in (wrapperUrl) jarUrl="$value"; break ;; 218 | esac 219 | done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" 220 | if [ "$MVNW_VERBOSE" = true ]; then 221 | echo "Downloading from: $jarUrl" 222 | fi 223 | wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" 224 | 225 | if command -v wget > /dev/null; then 226 | if [ "$MVNW_VERBOSE" = true ]; then 227 | echo "Found wget ... using wget" 228 | fi 229 | wget "$jarUrl" -O "$wrapperJarPath" 230 | elif command -v curl > /dev/null; then 231 | if [ "$MVNW_VERBOSE" = true ]; then 232 | echo "Found curl ... using curl" 233 | fi 234 | curl -o "$wrapperJarPath" "$jarUrl" 235 | else 236 | if [ "$MVNW_VERBOSE" = true ]; then 237 | echo "Falling back to using Java to download" 238 | fi 239 | javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" 240 | if [ -e "$javaClass" ]; then 241 | if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then 242 | if [ "$MVNW_VERBOSE" = true ]; then 243 | echo " - Compiling MavenWrapperDownloader.java ..." 244 | fi 245 | # Compiling the Java class 246 | ("$JAVA_HOME/bin/javac" "$javaClass") 247 | fi 248 | if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then 249 | # Running the downloader 250 | if [ "$MVNW_VERBOSE" = true ]; then 251 | echo " - Running MavenWrapperDownloader.java ..." 252 | fi 253 | ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") 254 | fi 255 | fi 256 | fi 257 | fi 258 | ########################################################################################## 259 | # End of extension 260 | ########################################################################################## 261 | 262 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} 263 | if [ "$MVNW_VERBOSE" = true ]; then 264 | echo $MAVEN_PROJECTBASEDIR 265 | fi 266 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 267 | 268 | # For Cygwin, switch paths to Windows format before running java 269 | if $cygwin; then 270 | [ -n "$M2_HOME" ] && 271 | M2_HOME=`cygpath --path --windows "$M2_HOME"` 272 | [ -n "$JAVA_HOME" ] && 273 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` 274 | [ -n "$CLASSPATH" ] && 275 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"` 276 | [ -n "$MAVEN_PROJECTBASEDIR" ] && 277 | MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` 278 | fi 279 | 280 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 281 | 282 | exec "$JAVACMD" \ 283 | $MAVEN_OPTS \ 284 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ 285 | "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 286 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" 287 | -------------------------------------------------------------------------------- /mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM https://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven2 Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM set title of command window 39 | title %0 40 | @REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' 41 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 42 | 43 | @REM set %HOME% to equivalent of $HOME 44 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 45 | 46 | @REM Execute a user defined script before this one 47 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 48 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 49 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 50 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 51 | :skipRcPre 52 | 53 | @setlocal 54 | 55 | set ERROR_CODE=0 56 | 57 | @REM To isolate internal variables from possible post scripts, we use another setlocal 58 | @setlocal 59 | 60 | @REM ==== START VALIDATION ==== 61 | if not "%JAVA_HOME%" == "" goto OkJHome 62 | 63 | echo. 64 | echo Error: JAVA_HOME not found in your environment. >&2 65 | echo Please set the JAVA_HOME variable in your environment to match the >&2 66 | echo location of your Java installation. >&2 67 | echo. 68 | goto error 69 | 70 | :OkJHome 71 | if exist "%JAVA_HOME%\bin\java.exe" goto init 72 | 73 | echo. 74 | echo Error: JAVA_HOME is set to an invalid directory. >&2 75 | echo JAVA_HOME = "%JAVA_HOME%" >&2 76 | echo Please set the JAVA_HOME variable in your environment to match the >&2 77 | echo location of your Java installation. >&2 78 | echo. 79 | goto error 80 | 81 | @REM ==== END VALIDATION ==== 82 | 83 | :init 84 | 85 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 86 | @REM Fallback to current working directory if not found. 87 | 88 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 89 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 90 | 91 | set EXEC_DIR=%CD% 92 | set WDIR=%EXEC_DIR% 93 | :findBaseDir 94 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 95 | cd .. 96 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 97 | set WDIR=%CD% 98 | goto findBaseDir 99 | 100 | :baseDirFound 101 | set MAVEN_PROJECTBASEDIR=%WDIR% 102 | cd "%EXEC_DIR%" 103 | goto endDetectBaseDir 104 | 105 | :baseDirNotFound 106 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 107 | cd "%EXEC_DIR%" 108 | 109 | :endDetectBaseDir 110 | 111 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 112 | 113 | @setlocal EnableExtensions EnableDelayedExpansion 114 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 115 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 116 | 117 | :endReadAdditionalConfig 118 | 119 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 120 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 121 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 122 | 123 | set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar" 124 | FOR /F "tokens=1,2 delims==" %%A IN (%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties) DO ( 125 | IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B 126 | ) 127 | 128 | @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 129 | @REM This allows using the maven wrapper in projects that prohibit checking in binary data. 130 | if exist %WRAPPER_JAR% ( 131 | echo Found %WRAPPER_JAR% 132 | ) else ( 133 | echo Couldn't find %WRAPPER_JAR%, downloading it ... 134 | echo Downloading from: %DOWNLOAD_URL% 135 | powershell -Command "(New-Object Net.WebClient).DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')" 136 | echo Finished downloading %WRAPPER_JAR% 137 | ) 138 | @REM End of extension 139 | 140 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 141 | if ERRORLEVEL 1 goto error 142 | goto end 143 | 144 | :error 145 | set ERROR_CODE=1 146 | 147 | :end 148 | @endlocal & set ERROR_CODE=%ERROR_CODE% 149 | 150 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 151 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 152 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 153 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 154 | :skipRcPost 155 | 156 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 157 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 158 | 159 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 160 | 161 | exit /B %ERROR_CODE% 162 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 2.1.5.RELEASE 9 | 10 | 11 | com.braincs 12 | mqtt-protobuf-server 13 | 1.0.1-SNAPSHOT 14 | mqtt-protobuf-server 15 | Demo project for mock mqtt protobuf server 16 | 17 | 18 | 1.8 19 | 1.6.1 20 | 3.1.0 21 | 22 | 23 | 24 | 25 | org.springframework.boot 26 | spring-boot-starter-web 27 | 28 | 29 | 30 | org.springframework.boot 31 | spring-boot-starter-test 32 | test 33 | 34 | 35 | org.springframework.boot 36 | spring-boot-starter-integration 37 | 38 | 39 | org.springframework.integration 40 | spring-integration-stream 41 | 42 | 43 | 44 | org.springframework.integration 45 | spring-integration-mqtt 46 | 47 | 48 | org.projectlombok 49 | lombok 50 | 1.16.10 51 | provided 52 | 53 | 54 | 55 | 56 | io.grpc 57 | grpc-netty 58 | ${grpc.version} 59 | provided 60 | 61 | 62 | io.grpc 63 | grpc-protobuf 64 | ${grpc.version} 65 | provided 66 | 67 | 68 | io.grpc 69 | grpc-stub 70 | ${grpc.version} 71 | provided 72 | 73 | 74 | com.google.protobuf 75 | protobuf-java 76 | ${protobuf.version} 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | kr.motd.maven 85 | os-maven-plugin 86 | 1.5.0.Final 87 | 88 | 89 | 90 | 91 | 92 | org.springframework.boot 93 | spring-boot-maven-plugin 94 | 95 | 96 | 97 | org.xolstice.maven.plugins 98 | protobuf-maven-plugin 99 | 0.5.0 100 | 101 | com.google.protobuf:protoc:${protobuf.version}:exe:${os.detected.classifier} 102 | grpc-java 103 | io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier} 104 | 105 | 106 | 107 | 108 | compile 109 | compile-custom 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | -------------------------------------------------------------------------------- /src/main/java/com/braincs/mqttprotobufserver/MqttProtobufServerApplication.java: -------------------------------------------------------------------------------- 1 | package com.braincs.mqttprotobufserver; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | //@Configuration 8 | //@EnableConfigurationProperties(MqttProperties.class) 9 | public class MqttProtobufServerApplication { 10 | 11 | public static void main(String[] args) { 12 | SpringApplication.run(MqttProtobufServerApplication.class, args); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/braincs/mqttprotobufserver/controller/MQTTRestController.java: -------------------------------------------------------------------------------- 1 | package com.braincs.mqttprotobufserver.controller; 2 | 3 | import com.braincs.kylinprotocol.pb.KylinProto; 4 | import com.braincs.mqttprotobufserver.mqtt.IMQTTPublisher; 5 | import com.braincs.mqttprotobufserver.mqtt.IMQTTSubscriber; 6 | import com.google.protobuf.InvalidProtocolBufferException; 7 | import lombok.extern.slf4j.Slf4j; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.web.bind.annotation.*; 12 | 13 | import javax.annotation.PostConstruct; 14 | 15 | @Slf4j 16 | @RestController 17 | public class MQTTRestController { 18 | 19 | @Autowired 20 | IMQTTPublisher publisher; 21 | 22 | @Autowired 23 | IMQTTSubscriber subscriber; 24 | 25 | 26 | private boolean isOpen = false; 27 | public static String IP_PREFIX = "panel/m5/"; 28 | public static String CLIENTID = "12345"; 29 | //region topics 30 | public static String TOPIC_LOOP_TEST = "mqtt/loop/message"; 31 | public static String TOPIC_PANEL_SATAUS = IP_PREFIX + CLIENTID + "/out/start";//status 主题 32 | public static String TOPIC_PANEL_CHECK = IP_PREFIX + CLIENTID + "/in/check"; 33 | ;//check 主题 34 | public static String TOPIC_PANEL_RECOGNIZE = IP_PREFIX + CLIENTID + "/in/recognize";//recognize 主题 35 | public static String TOPIC_PANEL_ADD_GROUP = IP_PREFIX + CLIENTID + "/out/add_group";//group 主题 36 | public static String TOPIC_PANEL_ADD_GROUP_RES = IP_PREFIX + CLIENTID + "/in/add_group/+";//group 主题 37 | public static String TOPIC_PANEL_DEL_GROUP = IP_PREFIX + CLIENTID + "/out/del_group/";//group 主题 38 | public static String TOPIC_PANEL_DEL_GROUP_RES = IP_PREFIX + CLIENTID + "/in/del_group/+";//group 主题 39 | public static String TOPIC_PANEL_ADD_FACE = IP_PREFIX + CLIENTID + "/out/add_face";//add_face 主题 40 | public static String TOPIC_PANEL_ADD_FACE_RES = IP_PREFIX + CLIENTID + "/in/add_face/+/+";//add_face 返回主题 41 | public static String TOPIC_PANEL_DEL_FACE = IP_PREFIX + CLIENTID + "/out/del_face/+/+";//del_face 主题 42 | public static String TOPIC_PANEL_DEL_FACE_RES = IP_PREFIX + CLIENTID + "/in/del_face/+/+";//del_face 返回主题 43 | public static String TOPIC_PANEL_SNAPSHOT = IP_PREFIX + CLIENTID + "/out/snapshot";//snapshot 主题 44 | public static String TOPIC_PANEL_SNAPSHOT_RES = IP_PREFIX + CLIENTID + "/in/snapshot";//snapshot result 主题 45 | 46 | //endregion 47 | 48 | //region last_will 49 | public static String LAST_WILL_PANEL_SATAUS = IP_PREFIX + CLIENTID + "/out/stop";//status 遗言 50 | public static String LAST_WILL_PANEL_CHECK = IP_PREFIX + CLIENTID + "/in/disconnect";//check 遗言 51 | //endregion 52 | 53 | @PostConstruct 54 | public void init() { 55 | subscriber.subscribeMessage(TOPIC_LOOP_TEST); 56 | subscriber.subscribeMessage(TOPIC_PANEL_CHECK); 57 | subscriber.subscribeMessage(TOPIC_PANEL_RECOGNIZE); 58 | subscriber.subscribeMessage(TOPIC_PANEL_ADD_GROUP_RES); 59 | subscriber.subscribeMessage(TOPIC_PANEL_DEL_GROUP_RES); 60 | subscriber.subscribeMessage(TOPIC_PANEL_ADD_FACE_RES); 61 | subscriber.subscribeMessage(TOPIC_PANEL_DEL_FACE_RES); 62 | subscriber.subscribeMessage(TOPIC_PANEL_SNAPSHOT_RES); 63 | } 64 | 65 | private static final Logger logger = LoggerFactory.getLogger(MQTTRestController.class); 66 | 67 | @RequestMapping(value = "/mqtt/loop/message", method = RequestMethod.POST) 68 | public String index(@RequestBody String data) { 69 | publisher.publishMessage(TOPIC_LOOP_TEST, data); 70 | return "Success"; 71 | } 72 | 73 | @RequestMapping(value = "/start", method = RequestMethod.POST) 74 | public String status(@RequestParam String modeStr, @RequestParam int health_check_interval) { 75 | int mode = 1; 76 | if (modeStr.contains("capture")) { 77 | mode = 0; 78 | } 79 | // else if (modeStr.contains("recognize")) { 80 | // mode = 1; 81 | // } 82 | String res = "mode = " + mode + ", health_check_interval = " + health_check_interval; 83 | System.out.println(res); 84 | //construct message via protobuf 85 | isOpen = !isOpen; 86 | KylinProto.Start.Builder builder = KylinProto.Start.newBuilder(); 87 | KylinProto.Start start = builder.setIsOpen(isOpen) 88 | .setHealthCheckInterval(health_check_interval) 89 | .setMode(KylinProto.Start.Mode.recognize) 90 | .build(); 91 | 92 | try { 93 | KylinProto.Start parseStart = KylinProto.Start.parseFrom(start.toByteArray()); 94 | boolean isOpen = parseStart.getIsOpen(); 95 | KylinProto.Start.Mode startMode = parseStart.getMode(); 96 | int healthCheckInterval = parseStart.getHealthCheckInterval(); 97 | System.out.println("isOpen: " + isOpen + ", Mode: " + startMode.name() + ", interval: " + healthCheckInterval); 98 | 99 | } catch (InvalidProtocolBufferException e) { 100 | e.printStackTrace(); 101 | } 102 | 103 | publisher.publish(TOPIC_PANEL_SATAUS, start.toByteArray(), 2, false); 104 | // publisher.publish(TOPIC_PANEL_SATAUS, res.getBytes(), 2, false); 105 | // publisher.publishMessage(TOPIC_PANEL_SATAUS, res); 106 | return "Success"; 107 | } 108 | 109 | @RequestMapping(value = "/group/add", method = RequestMethod.POST) 110 | public String groupAdd(@RequestParam String url, 111 | @RequestParam String group_name, 112 | @RequestParam float threshold, 113 | @RequestParam int top) { 114 | // System.out.println("url = " + url); 115 | // System.out.println("group_name = " + group_name); 116 | System.out.println("--收到 groupAdd RestFul请求--"); 117 | String[] split = url.split("@"); 118 | String topic = split[split.length - 1]; 119 | topic = topic + "/out/add_group"; 120 | System.out.println("topic = " + topic); 121 | 122 | KylinProto.Group.Builder builder = KylinProto.Group.newBuilder(); 123 | KylinProto.Group group = builder.setThreshold(threshold) 124 | .setTop(top) 125 | .setGroup(group_name).build(); 126 | System.out.println("pub group: " + group.toString()); 127 | 128 | publisher.publish(topic, group.toByteArray(), 2, false); 129 | return "Success"; 130 | } 131 | 132 | 133 | @RequestMapping(value = "/group/delete", method = RequestMethod.DELETE) 134 | public String groupDelete(@RequestParam String url, @RequestParam String group_name) { 135 | System.out.println("--收到 groupDelete RestFul请求--"); 136 | 137 | // System.out.println("url = " + url); 138 | // System.out.println("group_name = " + group_name); 139 | String[] split = url.split("@"); 140 | String topic = split[split.length - 1]; 141 | topic = topic + "/out/del_group/" + group_name; 142 | 143 | System.out.println("pub del_group topic: " + topic); 144 | publisher.publishMessage(topic, "del_group:group_name"); 145 | return "Success"; 146 | } 147 | 148 | @RequestMapping(value = "/face/add", method = RequestMethod.POST) 149 | public String faceAdd(@RequestParam String url, 150 | @RequestParam String group_name, 151 | @RequestParam String cert_no, 152 | @RequestParam String name, 153 | @RequestParam String image_url) { 154 | System.out.println("--收到 faceAdd RestFul请求--"); 155 | 156 | System.out.println("url = " + url); 157 | System.out.println("group_name = " + group_name + ", cert_no = " + cert_no + ", name = " + name + ", image_url = " + image_url); 158 | String[] split = url.split("@"); 159 | String topic = split[split.length - 1]; 160 | topic = topic + "/out/add_face"; 161 | System.out.println("topic = " + topic); 162 | KylinProto.Face.Builder builder = KylinProto.Face.newBuilder(); 163 | KylinProto.Face face = builder.setGroup(group_name) 164 | .setFace(cert_no) 165 | .setName(name) 166 | .setUrl(image_url).build(); 167 | System.out.println("pub face: " + face.toString()); 168 | 169 | publisher.publish(topic, face.toByteArray(), 2, false); 170 | return "Success"; 171 | } 172 | 173 | @RequestMapping(value = "/face/delete", method = RequestMethod.DELETE) 174 | public String faceDelete(@RequestParam String url, @RequestParam String group_name, @RequestParam String cert_no) { 175 | System.out.println("--收到 faceDelete RestFul请求--"); 176 | 177 | // System.out.println("url = " + url); 178 | // System.out.println("group_name = " + group_name + ", cert_no = " + cert_no); 179 | String[] split = url.split("@"); 180 | String topic = split[split.length - 1]; 181 | topic = topic + "/out/del_face/" + group_name + "/" + cert_no; 182 | System.out.println("pub del_face topic: " + topic); 183 | 184 | publisher.publishMessage(topic, "del_face:" + group_name + "/" + cert_no); 185 | return "Success"; 186 | } 187 | 188 | @RequestMapping(value = "/snapshot", method = RequestMethod.POST) 189 | public String snapshot(@RequestParam String url) { 190 | // System.out.println("url = " + url); 191 | // System.out.println("group_name = " + group_name); 192 | System.out.println("--收到 snapshot RestFul请求--"); 193 | 194 | String[] split = url.split("@"); 195 | String topic = split[split.length - 1]; 196 | topic = topic + "/out/snapshot"; 197 | System.out.println("pub snapshot topic: " + topic); 198 | publisher.publishMessage(topic, "snapshot"); 199 | return "Success"; 200 | } 201 | } 202 | -------------------------------------------------------------------------------- /src/main/java/com/braincs/mqttprotobufserver/mqtt/IMQTTPublisher.java: -------------------------------------------------------------------------------- 1 | package com.braincs.mqttprotobufserver.mqtt; 2 | 3 | public interface IMQTTPublisher { 4 | /** 5 | * Publish message 6 | * 7 | * @param topic 8 | * @param String Message 9 | */ 10 | public void publishMessage(String topic, String message); 11 | 12 | /** 13 | * publish message to topic with qos 14 | * @param topic to deliver the message to, for example "finance/stock/ibm" 15 | * @param message message to sub 16 | * @param qos the Quality of Service to deliver the message at. Valid values 17 | * are 0, 1 or 2. 18 | * @param retained whether or not this message should be retained by the server. 19 | */ 20 | public void publish(String topic, byte[] message, int qos, boolean retained); 21 | /** 22 | * Disconnect MQTT Client 23 | */ 24 | public void disconnect(); 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/braincs/mqttprotobufserver/mqtt/IMQTTSubscriber.java: -------------------------------------------------------------------------------- 1 | package com.braincs.mqttprotobufserver.mqtt; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | public interface IMQTTSubscriber { 7 | public static final Logger logger = LoggerFactory.getLogger(IMQTTSubscriber.class); 8 | 9 | /** 10 | * Subscribe message 11 | * 12 | * @param topic 13 | * @param jasonMessage 14 | */ 15 | public void subscribeMessage(String topic); 16 | 17 | /** 18 | * Disconnect MQTT Client 19 | */ 20 | public void disconnect(); 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/braincs/mqttprotobufserver/mqtt/MQTTPublisher.java: -------------------------------------------------------------------------------- 1 | package com.braincs.mqttprotobufserver.mqtt; 2 | 3 | import org.eclipse.paho.client.mqttv3.*; 4 | import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import org.springframework.stereotype.Component; 8 | 9 | @Component 10 | public class MQTTPublisher extends MQTTConfig implements MqttCallback, IMQTTPublisher { 11 | 12 | private String brokerUrl = null; 13 | 14 | final private String colon = ":"; 15 | final private String clientId = "mqtt_server_pub"; 16 | 17 | private MqttClient mqttClient = null; 18 | private MqttConnectOptions connectionOptions = null; 19 | private MemoryPersistence persistence = null; 20 | 21 | private static final Logger logger = LoggerFactory.getLogger(MQTTPublisher.class); 22 | 23 | /** 24 | * Private default constructor 25 | */ 26 | private MQTTPublisher() { 27 | this.config(); 28 | } 29 | 30 | /** 31 | * Private constructor 32 | */ 33 | private MQTTPublisher(String broker, Integer port, Boolean ssl, Boolean withUserNamePass) { 34 | this.config(broker, port, ssl, withUserNamePass); 35 | } 36 | 37 | /** 38 | * Factory method to get instance of MQTTPublisher 39 | * 40 | * @return MQTTPublisher 41 | */ 42 | public static MQTTPublisher getInstance() { 43 | return new MQTTPublisher(); 44 | } 45 | 46 | /** 47 | * Factory method to get instance of MQTTPublisher 48 | * 49 | * @param broker 50 | * @param port 51 | * @param ssl 52 | * @param withUserNamePass 53 | * @return MQTTPublisher 54 | */ 55 | public static MQTTPublisher getInstance(String broker, Integer port, Boolean ssl, Boolean withUserNamePass) { 56 | return new MQTTPublisher(broker, port, ssl, withUserNamePass); 57 | } 58 | 59 | /* 60 | * (non-Javadoc) 61 | * 62 | * @see 63 | * com.bjitgroup.jasmysp.mqtt.publisher.MQTTPublisherBase#configurePublisher() 64 | */ 65 | @Override 66 | protected void config() { 67 | 68 | this.brokerUrl = this.TCP + this.broker + colon + this.port; 69 | this.persistence = new MemoryPersistence(); 70 | this.connectionOptions = new MqttConnectOptions(); 71 | try { 72 | this.mqttClient = new MqttClient(brokerUrl, clientId, persistence); 73 | this.connectionOptions.setCleanSession(true); 74 | this.mqttClient.connect(this.connectionOptions); 75 | this.mqttClient.setCallback(this); 76 | } catch (MqttException me) { 77 | logger.error("ERROR", me); 78 | } 79 | } 80 | 81 | /* 82 | * (non-Javadoc) 83 | * 84 | * @see 85 | * com.bjitgroup.jasmysp.mqtt.publisher.MQTTPublisherBase#configurePublisher( 86 | * java.lang.String, java.lang.Integer, java.lang.Boolean, java.lang.Boolean) 87 | */ 88 | @Override 89 | protected void config(String broker, Integer port, Boolean ssl, Boolean withUserNamePass) { 90 | 91 | String protocal = this.TCP; 92 | if (true == ssl) { 93 | protocal = this.SSL; 94 | } 95 | 96 | this.brokerUrl = protocal + this.broker + colon + port; 97 | this.persistence = new MemoryPersistence(); 98 | this.connectionOptions = new MqttConnectOptions(); 99 | 100 | try { 101 | this.mqttClient = new MqttClient(brokerUrl, clientId, persistence); 102 | this.connectionOptions.setCleanSession(true); 103 | if (true == withUserNamePass) { 104 | if (password != null) { 105 | this.connectionOptions.setPassword(this.password.toCharArray()); 106 | } 107 | if (userName != null) { 108 | this.connectionOptions.setUserName(this.userName); 109 | } 110 | } 111 | this.mqttClient.connect(this.connectionOptions); 112 | this.mqttClient.setCallback(this); 113 | } catch (MqttException me) { 114 | logger.error("ERROR", me); 115 | } 116 | } 117 | 118 | 119 | /* 120 | * (non-Javadoc) 121 | * @see com.monirthought.mqtt.publisher.MQTTPublisherBase#publishMessage(java.lang.String, java.lang.String) 122 | */ 123 | @Override 124 | public void publishMessage(String topic, String message) { 125 | 126 | try { 127 | MqttMessage mqttmessage = new MqttMessage(message.getBytes()); 128 | mqttmessage.setQos(this.qos); 129 | this.mqttClient.publish(topic, mqttmessage); 130 | } catch (MqttException me) { 131 | logger.error("ERROR", me); 132 | } 133 | 134 | } 135 | 136 | public void publish(String topic, byte[] message, int qos, boolean retained){ 137 | try { 138 | this.mqttClient.publish(topic, message, qos, retained ); 139 | } catch (MqttException e) { 140 | e.printStackTrace(); 141 | } 142 | } 143 | /* 144 | * (non-Javadoc) 145 | * @see org.eclipse.paho.client.mqttv3.MqttCallback#connectionLost(java.lang.Throwable) 146 | */ 147 | @Override 148 | public void connectionLost(Throwable arg0) { 149 | logger.info("Connection Lost"); 150 | 151 | } 152 | 153 | /* 154 | * (non-Javadoc) 155 | * @see org.eclipse.paho.client.mqttv3.MqttCallback#deliveryComplete(org.eclipse.paho.client.mqttv3.IMqttDeliveryToken) 156 | */ 157 | @Override 158 | public void deliveryComplete(IMqttDeliveryToken arg0) { 159 | logger.info("delivery completed"); 160 | 161 | } 162 | 163 | /* 164 | * (non-Javadoc) 165 | * @see org.eclipse.paho.client.mqttv3.MqttCallback#messageArrived(java.lang.String, org.eclipse.paho.client.mqttv3.MqttMessage) 166 | */ 167 | @Override 168 | public void messageArrived(String arg0, MqttMessage arg1) throws Exception { 169 | // Leave it blank for Publisher 170 | 171 | } 172 | 173 | /* 174 | * (non-Javadoc) 175 | * @see com.monirthought.mqtt.publisher.MQTTPublisherBase#disconnect() 176 | */ 177 | @Override 178 | public void disconnect() { 179 | try { 180 | this.mqttClient.disconnect(); 181 | } catch (MqttException me) { 182 | logger.error("ERROR", me); 183 | } 184 | } 185 | 186 | } 187 | -------------------------------------------------------------------------------- /src/main/java/com/braincs/mqttprotobufserver/mqtt/MQTTSubscriber.java: -------------------------------------------------------------------------------- 1 | package com.braincs.mqttprotobufserver.mqtt; 2 | 3 | import com.braincs.kylinprotocol.pb.KylinProto; 4 | import com.braincs.mqttprotobufserver.utils.FileUtil; 5 | import org.eclipse.paho.client.mqttv3.*; 6 | import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | import org.springframework.stereotype.Component; 10 | 11 | import java.sql.Timestamp; 12 | import java.text.SimpleDateFormat; 13 | import java.util.Date; 14 | 15 | @Component 16 | public class MQTTSubscriber extends MQTTConfig implements MqttCallback, IMQTTSubscriber { 17 | 18 | private String brokerUrl = null; 19 | final private String colon = ":"; 20 | final private String clientId = "mqtt_server_sub"; 21 | private final String folder = "/Users/changshuai/IdeaProjects/mqtt-protobuf-server/tmp"; 22 | private final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss"); 23 | private MqttClient mqttClient = null; 24 | private MqttConnectOptions connectionOptions = null; 25 | private MemoryPersistence persistence = null; 26 | 27 | private static final Logger logger = LoggerFactory.getLogger(MQTTSubscriber.class); 28 | 29 | public MQTTSubscriber() { 30 | this.config(); 31 | } 32 | 33 | /* 34 | * (non-Javadoc) 35 | * 36 | * @see org.eclipse.paho.client.mqttv3.MqttCallback#connectionLost(java.lang. 37 | * Throwable) 38 | */ 39 | @Override 40 | public void connectionLost(Throwable cause) { 41 | logger.info("Connection Lost"); 42 | 43 | } 44 | 45 | /* 46 | * (non-Javadoc) 47 | * 48 | * @see 49 | * org.eclipse.paho.client.mqttv3.MqttCallback#messageArrived(java.lang.String, 50 | * org.eclipse.paho.client.mqttv3.MqttMessage) 51 | */ 52 | @Override 53 | public void messageArrived(String topic, MqttMessage message) throws Exception { 54 | // Called when a message arrives from the server that matches any 55 | // subscription made by the client 56 | System.out.println(); 57 | System.out.println("***********************************************************************"); 58 | String time = new Timestamp(System.currentTimeMillis()).toString(); 59 | if (topic.endsWith("/in/recognize")) { 60 | System.out.println("Message Arrived at Time: " + time + " Topic: " + topic); 61 | KylinProto.Recognize recognize = KylinProto.Recognize.parseFrom(message.getPayload()); 62 | byte[] data = recognize.getCrop().toByteArray(); 63 | String dateStr = this.format.format(new Date()); 64 | String saveFile = FileUtil.save(folder, "recognize_" + dateStr + ".jpg", data); 65 | System.out.println("saved success : " + saveFile); 66 | } else if (topic.endsWith("/in/snapshot")) { 67 | System.out.println("Message Arrived at Time: " + time + " Topic: " + topic); 68 | KylinProto.SnapShot snapShot = KylinProto.SnapShot.parseFrom(message.getPayload()); 69 | byte[] data = snapShot.getImage().toByteArray(); 70 | String dateStr = this.format.format(new Date()); 71 | String saveFile = FileUtil.save(folder, "snapshot_" + dateStr + ".jpg", data); 72 | System.out.println("saved success : " + saveFile); 73 | } else if (topic.endsWith("/in/check")){ 74 | System.out.println("Message Arrived at Time: " + time + " Topic: " + topic); 75 | KylinProto.Status status = KylinProto.Status.parseFrom(message.getPayload()); 76 | System.out.println("received status : " + status.toString()); 77 | } else { 78 | System.out.println("Message Arrived at Time: " + time + " Topic: " + topic + " Message: " 79 | + new String(message.getPayload())); 80 | } 81 | System.out.println("***********************************************************************"); 82 | System.out.println(); 83 | } 84 | 85 | /* 86 | * (non-Javadoc) 87 | * 88 | * @see 89 | * org.eclipse.paho.client.mqttv3.MqttCallback#deliveryComplete(org.eclipse.paho 90 | * .client.mqttv3.IMqttDeliveryToken) 91 | */ 92 | @Override 93 | public void deliveryComplete(IMqttDeliveryToken token) { 94 | // Leave it blank for subscriber 95 | 96 | } 97 | 98 | /* 99 | * (non-Javadoc) 100 | * 101 | * @see 102 | * com.monirthought.mqtt.subscriber.MQTTSubscriberBase#subscribeMessage(java. 103 | * lang.String) 104 | */ 105 | @Override 106 | public void subscribeMessage(String topic) { 107 | try { 108 | this.mqttClient.subscribe(topic, this.qos); 109 | } catch (MqttException me) { 110 | me.printStackTrace(); 111 | } 112 | } 113 | 114 | /* 115 | * (non-Javadoc) 116 | * 117 | * @see com.monirthought.mqtt.subscriber.MQTTSubscriberBase#disconnect() 118 | */ 119 | public void disconnect() { 120 | try { 121 | this.mqttClient.disconnect(); 122 | } catch (MqttException me) { 123 | logger.error("ERROR", me); 124 | } 125 | } 126 | 127 | /* 128 | * (non-Javadoc) 129 | * 130 | * @see com.monirthought.config.MQTTConfig#config(java.lang.String, 131 | * java.lang.Integer, java.lang.Boolean, java.lang.Boolean) 132 | */ 133 | @Override 134 | protected void config(String broker, Integer port, Boolean ssl, Boolean withUserNamePass) { 135 | 136 | String protocal = this.TCP; 137 | if (true == ssl) { 138 | protocal = this.SSL; 139 | } 140 | 141 | this.brokerUrl = protocal + this.broker + colon + port; 142 | this.persistence = new MemoryPersistence(); 143 | this.connectionOptions = new MqttConnectOptions(); 144 | 145 | try { 146 | this.mqttClient = new MqttClient(brokerUrl, clientId, persistence); 147 | this.connectionOptions.setCleanSession(true); 148 | if (true == withUserNamePass) { 149 | if (password != null) { 150 | this.connectionOptions.setPassword(this.password.toCharArray()); 151 | } 152 | if (userName != null) { 153 | this.connectionOptions.setUserName(this.userName); 154 | } 155 | } 156 | this.mqttClient.connect(this.connectionOptions); 157 | this.mqttClient.setCallback(this); 158 | } catch (MqttException me) { 159 | me.printStackTrace(); 160 | } 161 | 162 | } 163 | 164 | /* 165 | * (non-Javadoc) 166 | * 167 | * @see com.monirthought.config.MQTTConfig#config() 168 | */ 169 | @Override 170 | protected void config() { 171 | 172 | this.brokerUrl = this.TCP + this.broker + colon + this.port; 173 | this.persistence = new MemoryPersistence(); 174 | this.connectionOptions = new MqttConnectOptions(); 175 | try { 176 | this.mqttClient = new MqttClient(brokerUrl, clientId, persistence); 177 | this.connectionOptions.setCleanSession(true); 178 | this.mqttClient.connect(this.connectionOptions); 179 | this.mqttClient.setCallback(this); 180 | } catch (MqttException me) { 181 | me.printStackTrace(); 182 | } 183 | 184 | } 185 | 186 | } -------------------------------------------------------------------------------- /src/main/java/com/braincs/mqttprotobufserver/mqtt/MqttConfig.java: -------------------------------------------------------------------------------- 1 | package com.braincs.mqttprotobufserver.mqtt; 2 | 3 | public abstract class MQTTConfig { 4 | 5 | protected final String broker = "10.156.2.132"; 6 | protected final int qos = 2; 7 | protected Boolean hasSSL = false; /* By default SSL is disabled */ 8 | protected Integer port = 1883; /* Default port */ 9 | protected final String userName = "admin"; 10 | protected final String password = "admin"; 11 | protected final String TCP = "tcp://"; 12 | protected final String SSL = "ssl://"; 13 | 14 | /** 15 | * Custom Configuration 16 | * 17 | * @param broker 18 | * @param port 19 | * @param ssl 20 | * @param withUserNamePass 21 | */ 22 | protected abstract void config(String broker, Integer port, Boolean ssl, Boolean withUserNamePass); 23 | 24 | /** 25 | * Default Configuration 26 | */ 27 | protected abstract void config(); 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/braincs/mqttprotobufserver/utils/FileUtil.java: -------------------------------------------------------------------------------- 1 | package com.braincs.mqttprotobufserver.utils; 2 | 3 | import java.io.File; 4 | import java.io.FileNotFoundException; 5 | import java.io.FileOutputStream; 6 | import java.io.IOException; 7 | 8 | public class FileUtil { 9 | public static String save(String path, String name, byte[] data) { 10 | File parent = new File(path); 11 | if (!parent.exists() ){//&& parent.isDirectory() 12 | parent.mkdirs(); 13 | } 14 | FileOutputStream fos = null; 15 | File dstFile = new File(path, name); 16 | try{ 17 | if (!dstFile.exists()){ 18 | dstFile.createNewFile(); 19 | } 20 | fos = new FileOutputStream(dstFile); 21 | fos.write(data); 22 | fos.close(); 23 | } catch (IOException e) { 24 | e.printStackTrace(); 25 | } finally { 26 | try { 27 | if (fos != null) { 28 | fos.close(); 29 | } 30 | } catch (IOException e) { 31 | e.printStackTrace(); 32 | } 33 | } 34 | return dstFile.getAbsolutePath(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/proto/mqtt.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | option java_package = "com.braincs.kylinprotocol.pb"; 3 | option java_outer_classname = "KylinProto"; 4 | package pb; 5 | 6 | message Rect { 7 | int32 left = 1; 8 | int32 top = 2; 9 | int32 width = 3; 10 | int32 height = 4; 11 | } 12 | 13 | message RectF { 14 | float left = 1; 15 | float top = 2; 16 | float width = 3; 17 | float height = 4; 18 | } 19 | 20 | message Response { 21 | int32 success = 1; 22 | string err = 2; 23 | } 24 | 25 | message Start { 26 | enum Mode { 27 | capture = 0; 28 | recognize = 1; 29 | } 30 | Mode mode = 1; 31 | bool is_open = 2; 32 | int32 health_check_interval = 3; 33 | } 34 | 35 | message Group { 36 | string group = 1; 37 | int32 top = 2; 38 | float threshold = 3; 39 | } 40 | 41 | message Face { 42 | string group = 1; 43 | string face = 2; 44 | string name = 3; 45 | 46 | string url = 10; 47 | bytes image = 100; 48 | } 49 | 50 | message Capture { 51 | int32 track = 1; 52 | int32 seq_num = 2; 53 | 54 | int64 timestamp = 10; 55 | float quality = 11; 56 | 57 | bytes crop = 100; 58 | RectF crop_rect = 101; 59 | 60 | bytes full = 200; 61 | } 62 | 63 | message Recognize { 64 | message Result { 65 | string face = 1; 66 | string name = 2; 67 | 68 | float score = 11; 69 | } 70 | repeated Result top = 1; 71 | string group = 2; 72 | 73 | bytes crop = 100; 74 | bytes full = 101; 75 | } 76 | 77 | message SnapShot { 78 | bytes image = 100; 79 | } 80 | 81 | message Upgrade { 82 | int32 timeout = 1; 83 | string url = 10; 84 | } 85 | 86 | message UpgradeProgress { 87 | int32 progress = 1; 88 | } 89 | 90 | message Status { 91 | string version = 1; 92 | string algorithm = 2; 93 | 94 | string local_ip = 10; 95 | } -------------------------------------------------------------------------------- /src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | # mqtt configs 2 | #mqtt.config.username=admin 3 | #mqtt.config.password=admin 4 | #mqtt.config.host=tcp://127.0.0.1:1883 5 | #mqtt.config.clientid=panel 6 | #mqtt.config.topic=start 7 | #mqtt.config.timeout=10 8 | #mqtt.config.keepalive=20 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/main/resources/static/static/test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/braincs/mqtt-protobuf-server/d20dcfdcc75e149b986a06729577b4dfc4327f63/src/main/resources/static/static/test.png -------------------------------------------------------------------------------- /src/test/java/com/braincs/mqttprotobufserver/MqttProtobufServerApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.braincs.mqttprotobufserver; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.junit4.SpringRunner; 7 | 8 | @RunWith(SpringRunner.class) 9 | @SpringBootTest 10 | public class MqttProtobufServerApplicationTests { 11 | 12 | @Test 13 | public void contextLoads() { 14 | } 15 | 16 | } 17 | --------------------------------------------------------------------------------