├── .dockerignore ├── .gitignore ├── Dockerfile ├── LICENSE ├── README.md ├── README_CN.md ├── ccm_im_iot_msg_service.png ├── jmqtt-acceptance ├── README.md └── pom.xml ├── jmqtt-admin └── pom.xml ├── jmqtt-broker ├── pom.xml └── src │ ├── main │ ├── java │ │ └── org │ │ │ └── jmqtt │ │ │ └── broker │ │ │ ├── BrokerController.java │ │ │ └── BrokerStartup.java │ └── resources │ │ ├── bin │ │ ├── jmqttshutdown │ │ └── runbroker.sh │ │ └── conf │ │ ├── client.cer │ │ ├── jmqtt.properties │ │ ├── jmqtt.sql │ │ ├── log4j2.xml │ │ └── server.pfx │ └── test │ └── java │ └── org │ └── jmqtt │ └── broker │ └── test │ └── Runtime.java ├── jmqtt-bus ├── README.md ├── pom.xml └── src │ ├── main │ └── java │ │ └── org │ │ └── jmqtt │ │ └── bus │ │ ├── Authenticator.java │ │ ├── BusController.java │ │ ├── ClusterEventManager.java │ │ ├── DeviceMessageManager.java │ │ ├── DeviceSessionManager.java │ │ ├── DeviceSubscriptionManager.java │ │ ├── enums │ │ ├── ClusterEventCodeEnum.java │ │ ├── DeviceOnlineStateEnum.java │ │ ├── MessageAckEnum.java │ │ ├── MessageFlowEnum.java │ │ ├── MessageSourceEnum.java │ │ └── TransportProtocolEnum.java │ │ ├── event │ │ ├── EventCenter.java │ │ └── GatewayListener.java │ │ ├── impl │ │ ├── ClusterEventManagerImpl.java │ │ ├── DefaultAuthenticator.java │ │ ├── DeviceMessageManagerImpl.java │ │ ├── DeviceSessionManagerImpl.java │ │ └── DeviceSubscriptionManagerImpl.java │ │ ├── model │ │ ├── ClusterEvent.java │ │ ├── DeviceInboxMessage.java │ │ ├── DeviceMessage.java │ │ ├── DeviceSession.java │ │ └── DeviceSubscription.java │ │ ├── store │ │ ├── AbstractDBStore.java │ │ ├── DBCallback.java │ │ ├── DBUtils.java │ │ ├── daoobject │ │ │ ├── DeviceInboxMessageDO.java │ │ │ ├── EventDO.java │ │ │ ├── MessageDO.java │ │ │ ├── RetainMessageDO.java │ │ │ ├── SessionDO.java │ │ │ └── SubscriptionDO.java │ │ └── mapper │ │ │ ├── ClientInboxMessageMapper.java │ │ │ ├── EventMapper.java │ │ │ ├── MessageMapper.java │ │ │ ├── RetainMessageMapper.java │ │ │ ├── SessionMapper.java │ │ │ └── SubscriptionMapper.java │ │ └── subscription │ │ ├── CNode.java │ │ ├── CTrie.java │ │ ├── CTrieSubscriptionMatcher.java │ │ ├── DefaultSubscriptionTreeMatcher.java │ │ ├── DumpTreeVisitor.java │ │ ├── INode.java │ │ ├── SubscriptionCounterVisitor.java │ │ ├── SubscriptionMatcher.java │ │ ├── TNode.java │ │ ├── Token.java │ │ ├── Topic.java │ │ └── model │ │ └── Subscription.java │ └── test │ └── java │ └── org │ └── jmqtt │ ├── AppTest.java │ └── bus │ └── subscription │ ├── CTrieSubscriptionMatcherMatchingTest.java │ ├── CTrieTest.java │ └── TopicTest.java ├── jmqtt-doc ├── README.md ├── docs │ ├── README.md │ └── TEST_REPORT.md └── pom.xml ├── jmqtt-example ├── pom.xml └── src │ └── main │ └── java │ └── org │ └── jmqtt │ ├── java │ ├── Consumer.java │ └── Producer.java │ └── websocket │ ├── paho-mqtt-min.js │ ├── paho-mqtt.js │ └── webSocket.html ├── jmqtt-manager ├── jmqtt-mqtt ├── README.md ├── pom.xml └── src │ └── main │ └── java │ └── org │ └── jmqtt │ └── mqtt │ ├── ConnectManager.java │ ├── MQTTConnection.java │ ├── MQTTConnectionFactory.java │ ├── MQTTServer.java │ ├── codec │ ├── ByteBuf2WebSocketEncoder.java │ └── WebSocket2ByteBufDecoder.java │ ├── event │ └── MqttEventListener.java │ ├── model │ └── MqttTopic.java │ ├── netty │ ├── MqttNettyUtils.java │ ├── MqttRemotingServer.java │ ├── NettyMqttHandler.java │ └── NettySslHandler.java │ ├── protocol │ ├── RequestProcessor.java │ └── impl │ │ ├── ConnectProcessor.java │ │ ├── DisconnectProcessor.java │ │ ├── PingProcessor.java │ │ ├── PubAckProcessor.java │ │ ├── PubCompProcessor.java │ │ ├── PubRecProcessor.java │ │ ├── PubRelProcessor.java │ │ ├── PublishProcessor.java │ │ ├── SubscribeProcessor.java │ │ └── UnSubscribeProcessor.java │ ├── retain │ ├── RetainMessageHandler.java │ └── impl │ │ └── RetainMessageHandlerImpl.java │ ├── session │ └── MqttSession.java │ └── utils │ ├── MqttMessageUtil.java │ └── MqttMsgHeader.java ├── jmqtt-support ├── README.md ├── pom.xml └── src │ └── main │ └── java │ └── org │ └── jmqtt │ └── support │ ├── config │ ├── BrokerConfig.java │ └── NettyConfig.java │ ├── exception │ ├── RemotingConnectException.java │ ├── RemotingException.java │ ├── RemotingSendRequestException.java │ ├── RemotingTimeoutException.java │ └── RemotingTooMuchRequestException.java │ ├── helper │ ├── MixAll.java │ ├── Pair.java │ ├── RejectHandler.java │ └── ThreadFactoryImpl.java │ ├── log │ ├── JmqttLogger.java │ └── LogUtil.java │ └── remoting │ ├── RemotingHelper.java │ └── RemotingService.java ├── jmqtt-tcp ├── README.md ├── pom.xml └── src │ └── main │ └── java │ └── org │ └── jmqtt │ └── App.java ├── jmqtt.png ├── jmqtt_qq2.png ├── pom.xml └── vx_mangdagou.jpg /.dockerignore: -------------------------------------------------------------------------------- 1 | rocksdb.db 2 | .git 3 | .env 4 | *.jpg 5 | *.png 6 | *.md 7 | **/target -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | storage/ 2 | run/ 3 | test_out/ 4 | tmp/ 5 | doc/ 6 | .directory 7 | .loadpath 8 | .metadata 9 | *.pydevproject 10 | *.bak 11 | *.swp 12 | *.tmp 13 | *~.nib 14 | local.properties 15 | */dependency-reduced-pom.xml 16 | 17 | 18 | # Maven # 19 | ######### 20 | target/ 21 | 22 | # Eclipse # 23 | ########### 24 | .project 25 | .classpath 26 | .settings/ 27 | .externalToolBuilders/ 28 | *.launch 29 | .recommenders/ 30 | 31 | # Intellij # 32 | ############ 33 | .idea/ 34 | *.iml 35 | *.ipr 36 | *.iws 37 | 38 | # Compiled source # 39 | ################### 40 | *.class 41 | *.com 42 | *.dll 43 | *.exe 44 | *.o 45 | *.so 46 | 47 | 48 | ======= 49 | # Compiled class file 50 | *.class 51 | 52 | # Log file 53 | *.log 54 | 55 | # BlueJ files 56 | *.ctxt 57 | 58 | # Mobile Tools for Java (J2ME) 59 | .mtj.tmp/ 60 | 61 | # Package Files # 62 | *.jar 63 | *.war 64 | *.nar 65 | *.ear 66 | *.zip 67 | *.tar.gz 68 | *.rar 69 | 70 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 71 | hs_err_pid* 72 | rocksdb.db/ 73 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Builder 2 | FROM maven:3-jdk-8-slim AS builder 3 | 4 | RUN mkdir /opt/jmqtt 5 | WORKDIR /opt/jmqtt 6 | 7 | ADD . ./ 8 | 9 | RUN mvn -Ppackage-all -DskipTests clean install -U 10 | 11 | # App 12 | FROM openjdk:8-jre-slim 13 | 14 | RUN mkdir /opt/jmqtt 15 | VOLUME /var/jmqtt-data 16 | WORKDIR /opt/jmqtt 17 | 18 | COPY --from=builder /opt/jmqtt ./ 19 | COPY --from=builder /opt/jmqtt/jmqtt-distribution/conf /var/jmqtt-data/conf/ 20 | 21 | 22 | EXPOSE 1883 23 | EXPOSE 1884 24 | 25 | CMD ["./jmqtt-distribution/target/jmqtt/bin/jmqttstart", "-h", "/var/jmqtt-data", "-c", "/var/jmqtt-data/conf/jmqtt.properties"] 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **English** | [中文](README_CN.md) 2 | # Jmqtt 3 | 4 | ![Jmqtt logo](jmqtt.png) 5 | 6 | ## Features 7 | * Full support of mqtt3.1.1 protocol 8 | * Support data persistence and clustering based on MySQL 9 | * Support friendly secondary development, plug-in development: cluster / storage / device connection, publish subscribe authentication 10 | * Support tcp, websocket, SSL, WSS 11 | 12 | ## Official documents 13 | [Official documents](https://arrogant95.github.io/jmqtt-docs/) 14 | 15 | ## Quick start 16 | 1. Download [release](https://github.com/Cicizz/jmqtt/releases) (Version above 3. X) Or `clone` this project 17 | 2. Execute in the broker directory:`mvn -Ppackage-all -DskipTests clean install -U` 18 | 3. Configuration file for configuration response:`/jmqtt-broker/resources/conf` 19 | 4. Execute the start command:`java -jar jmqtt-broker-3.0.0.jar -h ${conf文件目录}` -H is followed by the configuration file directory, which needs to contain jmqtt.properties And log4j2. XML 20 | 21 | ## Online trial 22 | Server address: 81.69.46.38 23 | TCP port: 1883 24 | Websocket port: 8883 25 | SSL port: 1884 26 | WSS port: 8884 27 | 28 | ## QQ technology exchange group 29 | ![jmqtt技术交流群](jmqtt_qq2.png) 30 | 31 | ## test report 32 | jmqtt-doc/docs/TEST_REPORT.md 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /README_CN.md: -------------------------------------------------------------------------------- 1 | [English](README.md) | **中文** 2 | ## Jmqtt 3 | 4 | ![Jmqtt logo](jmqtt.png) 5 | 6 | ## 功能特性 7 | * 完整支持mqtt3.1.1协议 8 | * 支持基于mysql的数据持久化和集群 9 | * 支持友好的二次开发,插件化开发:集群/存储/设备连接,发布订阅认证 10 | * 支持tcp, websocket,ssl,wss 11 | 12 | ## 官方文档 13 | [官方文档](https://arrogant95.github.io/jmqtt-docs/) 14 | 15 | ## 快速开始 16 | 1. 下载 [release](https://github.com/Cicizz/jmqtt/releases)(3.x以上版本) 或`clone`本项目 17 | 2. 在broker模块下执行:`mvn -Ppackage-all -DskipTests clean install -U` 18 | 3. 配置配置文件并初始化db的sql:`/jmqtt-broker/resources/conf`目录下 19 | 4. 执行启动命令:`java -jar jmqtt-broker-3.0.0.jar -h ${conf文件目录}` -h后是配置文件目录,里面需要包含jmqtt.properties和log4j2.xml等配置文件 20 | 21 | ## 技术交流群 22 | 欢迎小伙伴们给个star并使用。 23 | 24 | ![jmqtt技术交流群](jmqtt_qq2.png) 25 | 26 | ## 测试报告 27 | https://www.yuque.com/tristan-ku8np/zze/xghq80 28 | 29 | 30 | -------------------------------------------------------------------------------- /ccm_im_iot_msg_service.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cicizz/jmqtt/032ac7eababf54e072910b49e86c18716abdaab6/ccm_im_iot_msg_service.png -------------------------------------------------------------------------------- /jmqtt-acceptance/README.md: -------------------------------------------------------------------------------- 1 | 这里是存放iot mq所有的功能测试,性能测试脚本及测试结果 2 | ## 功能测试计划(基于集群) 3 | ### 1.连接功能项 4 | 1. 连接 5 | 2. 连接 with cleasession = false(mqtt) 6 | 3. 连接 with will 消息(mqtt) 7 | 4. 使用ws连接 8 | 5. 连接使用ssl 9 | 6. 重新发起连接 10 | ### 2.发布消息 11 | 1. 发布qos0消息(mqtt) 12 | 2. 发布qos1消息(mqtt) 13 | 3. 发布qos2消息(mqtt) 14 | 4. 发布retain消息(mqtt) 15 | ### 3. 订阅 16 | 1. 订阅普通topic 17 | 2. 订阅多级topic 18 | 3. 订阅收到retain消息(mqtt) 19 | 4. 发布订阅拉通测试发送消息 20 | ### 4. 关闭连接 21 | 22 | 23 | ## 性能测试计划 24 | ## 1. 同时在线连接数压测 25 | 1. 连接数分级:100连接数/单机,500连接数/单机,1000连接数/单机,5000连接数/单机,1W连接数/单机 26 | 2. 同时请求连接:10/s,50/s,100/s,200/s,500/s -- 单机 27 | 3. 集群测试(2+服务器):1W连接数/集群;5W连接数/集群;10W连接数/集群 28 | 4. 集群测试(2+服务器):1000/s,2000/s 29 | 5. 集群测试(2+服务器):同时在线10W设备,1000设备发送消息 30 | ## 2.发送消息TPS 31 | 消息字节大小:100byte[] 32 | 1. 单机:500/s,1000/s,5000/s,10000/s 33 | 2. 集群(2+台服务器):10000/s,15000/s,20000/s 34 | ## 3.订阅 35 | 1. 单设备订阅topic:10topic/单设备,50topic/单设备,100topic/单设备 36 | 2. 单机订阅树:10topic/单设备,50topic/单设备,100topic/单设备:分别连接1000设备,5000设备,10000设备 37 | ## 4.消费消息 38 | 1. 集群测试:同时在线10W设备,1000设备消费消息 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /jmqtt-acceptance/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | jmqtt 7 | org.jmqtt 8 | 3.0.0 9 | 10 | 4.0.0 11 | 12 | 13 | UTF-8 14 | 1.8 15 | 1.8 16 | 17 | 18 | jmqtt-acceptance 19 | 20 | 21 | 22 | org.eclipse.paho 23 | org.eclipse.paho.client.mqttv3 24 | 1.2.5 25 | 26 | 27 | 28 | commons-cli 29 | commons-cli 30 | ${commons-cli.version} 31 | 32 | 33 | 34 | 35 | 36 | 37 | org.apache.maven.plugins 38 | maven-assembly-plugin 39 | 40 | 41 | jar-with-dependencies 42 | 43 | 44 | 45 | 46 | make-assembly 47 | package 48 | 49 | single 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /jmqtt-admin/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | jmqtt 7 | org.jmqtt 8 | 3.0.0 9 | 10 | 4.0.0 11 | 3.0.0 12 | jmqtt-admin 13 | 14 | 15 | 16 | org.jmqtt 17 | jmqtt-broker 18 | 19 | 20 | io.netty 21 | netty-all 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /jmqtt-broker/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | jmqtt 7 | org.jmqtt 8 | 3.0.0 9 | 10 | 4.0.0 11 | 12 | jmqtt-broker 13 | 3.0.0 14 | 15 | jmqtt-broker 16 | 17 | 18 | UTF-8 19 | 1.8 20 | 1.8 21 | 2.6.13 22 | 2.13 23 | 24 | 25 | 26 | 27 | org.jmqtt 28 | jmqtt-mqtt 29 | 30 | 31 | 32 | org.assertj 33 | assertj-core 34 | 35 | 36 | 37 | org.junit.jupiter 38 | junit-jupiter-api 39 | test 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | org.springframework.boot 49 | spring-boot-maven-plugin 50 | 51 | true 52 | 53 | 54 | org.jmqtt.broker.BrokerStartup 55 | ZIP 56 | 57 | 58 | 59 | 60 | repackage 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /jmqtt-broker/src/main/java/org/jmqtt/broker/BrokerController.java: -------------------------------------------------------------------------------- 1 | package org.jmqtt.broker; 2 | 3 | import org.jmqtt.bus.BusController; 4 | import org.jmqtt.mqtt.MQTTServer; 5 | import org.jmqtt.support.config.BrokerConfig; 6 | import org.jmqtt.support.config.NettyConfig; 7 | import org.jmqtt.support.log.JmqttLogger; 8 | import org.jmqtt.support.log.LogUtil; 9 | import org.slf4j.Logger; 10 | 11 | /** 12 | * 具体控制类:负责加载配置文件,初始化环境,启动服务等 13 | */ 14 | public class BrokerController { 15 | 16 | private static final Logger log = JmqttLogger.brokerLog; 17 | 18 | private BrokerConfig brokerConfig; 19 | private NettyConfig nettyConfig; 20 | 21 | /** bus */ 22 | private BusController busController; 23 | 24 | /** mqtt gateway */ 25 | private MQTTServer mqttServer; 26 | 27 | /** coap gateway todo */ 28 | 29 | public BrokerController(BrokerConfig brokerConfig, NettyConfig nettyConfig){ 30 | this.brokerConfig = brokerConfig; 31 | this.nettyConfig = nettyConfig; 32 | this.busController = new BusController(brokerConfig); 33 | this.mqttServer = new MQTTServer(busController,brokerConfig,nettyConfig); 34 | } 35 | 36 | public void start(){ 37 | this.busController.start(); 38 | this.mqttServer.start(); 39 | LogUtil.info(log,"Jmqtt start. version:" + brokerConfig.getVersion()); 40 | } 41 | 42 | 43 | public void shutdown(){ 44 | this.mqttServer.shutdown(); 45 | this.busController.shutdown(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /jmqtt-broker/src/main/java/org/jmqtt/broker/BrokerStartup.java: -------------------------------------------------------------------------------- 1 | package org.jmqtt.broker; 2 | 3 | import org.apache.commons.cli.*; 4 | import org.apache.commons.lang3.StringUtils; 5 | import org.apache.logging.log4j.Level; 6 | import org.apache.logging.log4j.LogManager; 7 | import org.apache.logging.log4j.core.LoggerContext; 8 | import org.apache.logging.log4j.core.config.Configuration; 9 | import org.apache.logging.log4j.core.config.LoggerConfig; 10 | import org.jmqtt.support.config.BrokerConfig; 11 | import org.jmqtt.support.config.NettyConfig; 12 | import org.jmqtt.support.helper.MixAll; 13 | 14 | import java.io.*; 15 | import java.util.Map; 16 | import java.util.Objects; 17 | import java.util.Properties; 18 | 19 | /** 20 | * 技术问题,二次开发问题:请加 qq群:578185385 21 | */ 22 | public class BrokerStartup { 23 | 24 | public static void main(String[] args) { 25 | try { 26 | start(args); 27 | } catch (Exception e) { 28 | System.out.println("Jmqtt start failure,cause = " + e); 29 | e.printStackTrace(); 30 | System.exit(-1); 31 | } 32 | } 33 | 34 | public static BrokerController start(String[] args) throws Exception { 35 | Options options = buildOptions(); 36 | CommandLineParser parser = new DefaultParser(); 37 | CommandLine commandLine = parser.parse(options,args); 38 | String jmqttHome = null; 39 | String logLevel = null; 40 | BrokerConfig brokerConfig = new BrokerConfig(); 41 | NettyConfig nettyConfig = new NettyConfig(); 42 | if(commandLine != null){ 43 | jmqttHome = commandLine.getOptionValue("h"); 44 | logLevel = commandLine.getOptionValue("l"); 45 | } 46 | if(StringUtils.isEmpty(jmqttHome)){ 47 | jmqttHome = brokerConfig.getJmqttHome(); 48 | } 49 | if(StringUtils.isEmpty(jmqttHome)){ 50 | throw new Exception("please set JMQTT_HOME."); 51 | } 52 | String jmqttConfigPath = jmqttHome + File.separator + "jmqtt.properties"; 53 | initConfig(jmqttConfigPath,brokerConfig,nettyConfig); 54 | 55 | // 日志配置加载 56 | try { 57 | LoggerContext context = (org.apache.logging.log4j.core.LoggerContext) LogManager.getContext(false); 58 | File file = new File(jmqttHome + File.separator + "log4j2.xml"); 59 | context.setConfigLocation(file.toURI()); 60 | Configuration configuration = context.getConfiguration(); 61 | Map loggerConfigMap = configuration.getLoggers(); 62 | Level newLevel = logLevel == null? null : Level.getLevel(logLevel); 63 | if (newLevel == null) { 64 | newLevel = Level.INFO; 65 | } 66 | for (LoggerConfig value : loggerConfigMap.values()) { 67 | value.setLevel(newLevel); 68 | } 69 | context.updateLoggers(configuration); 70 | } catch (Exception ex) { 71 | System.err.print("Log4j2 load error,ex:" + ex); 72 | } 73 | 74 | // 启动服务,线程等 75 | BrokerController brokerController = new BrokerController(brokerConfig,nettyConfig); 76 | brokerController.start(); 77 | 78 | Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() { 79 | @Override 80 | public void run() { 81 | brokerController.shutdown(); 82 | } 83 | })); 84 | 85 | return brokerController; 86 | } 87 | 88 | private static Options buildOptions(){ 89 | Options options = new Options(); 90 | Option opt = new Option("h",true,"jmqttHome,eg: /wls/xxx"); 91 | opt.setRequired(false); 92 | options.addOption(opt); 93 | 94 | opt = new Option("c",true,"jmqtt.properties path,eg: /wls/xxx/xxx.properties"); 95 | opt.setRequired(false); 96 | options.addOption(opt); 97 | 98 | opt = new Option("l",true,"DEBUG"); 99 | opt.setRequired(false); 100 | options.addOption(opt); 101 | 102 | return options; 103 | } 104 | 105 | /** 106 | * convert properties to java config class 107 | * @param jmqttConfigPath 108 | * @param brokerConfig 109 | * @param nettyConfig 110 | */ 111 | private static void initConfig(String jmqttConfigPath, BrokerConfig brokerConfig, NettyConfig nettyConfig){ 112 | Properties properties = new Properties(); 113 | BufferedReader bufferedReader = null; 114 | try { 115 | bufferedReader = new BufferedReader(new FileReader(jmqttConfigPath)); 116 | properties.load(bufferedReader); 117 | MixAll.properties2POJO(properties,brokerConfig); 118 | MixAll.properties2POJO(properties,nettyConfig); 119 | } catch (FileNotFoundException e) { 120 | System.out.println("jmqtt.properties cannot find,cause + " + e + ",path:"+jmqttConfigPath); 121 | } catch (IOException e) { 122 | System.out.println("Handle jmqttConfig IO exception,cause = " + e); 123 | } finally { 124 | try { 125 | if(Objects.nonNull(bufferedReader)){ 126 | bufferedReader.close(); 127 | } 128 | } catch (IOException e) { 129 | System.out.println("Handle jmqttConfig IO exception,cause = " + e); 130 | } 131 | } 132 | } 133 | 134 | } 135 | -------------------------------------------------------------------------------- /jmqtt-broker/src/main/resources/bin/jmqttshutdown: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | pid=`ps ax | grep -i 'org.jmqtt.broker.BrokerStartup' |grep java | grep -v grep | awk '{print $1}'` 4 | if [ -z "$pid" ] ; then 5 | echo "No jmqttBroker running." 6 | exit -1; 7 | fi 8 | 9 | echo "The jmqttBroker(${pid}) is running..." 10 | 11 | kill ${pid} 12 | 13 | echo "Send shutdown request to jmqttBroker(${pid}) OK" 14 | 15 | -------------------------------------------------------------------------------- /jmqtt-broker/src/main/resources/bin/runbroker.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | 4 | JAVA_OPT="${JAVA_OPT} -server -Xms1280m -Xmx1280m -Xmn640m" 5 | JAVA_OPT="${JAVA_OPT} -XX:+UseG1GC -XX:G1HeapRegionSize=16m -XX:G1ReservePercent=25 -XX:InitiatingHeapOccupancyPercent=30 -XX:SoftRefLRUPolicyMSPerMB=0 -XX:SurvivorRatio=8" 6 | JAVA_OPT="${JAVA_OPT} -verbose:gc -Xloggc:/dev/shm/mq_gc_%p.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCApplicationStoppedTime -XX:+PrintAdaptiveSizePolicy" 7 | JAVA_OPT="${JAVA_OPT} -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=30m" 8 | JAVA_OPT="${JAVA_OPT} -XX:-OmitStackTraceInFastThrow" 9 | JAVA_OPT="${JAVA_OPT} -XX:+AlwaysPreTouch" 10 | JAVA_OPT="${JAVA_OPT} -XX:MaxDirectMemorySize=15g" 11 | JAVA_OPT="${JAVA_OPT} -XX:-UseLargePages -XX:-UseBiasedLocking" 12 | 13 | JAVA_OPT="${JAVA_OPT} -Dcom.sun.management.jmxremote.ssl=false" 14 | JAVA_OPT="${JAVA_OPT} -Dcom.sun.management.jmxremote.authenticate=false" 15 | JAVA_OPT="${JAVA_OPT} -Dcom.sun.management.jmxremote.port=1099" 16 | 17 | #JAVA_OPT="${JAVA_OPT} -Xdebug -Xrunjdwp:transport=dt_socket,address=9555,server=y,suspend=n" 18 | nohup java ${JAVA_OPT} -jar $@ >/dev/null 2>&1 & 19 | echo 'jmqtt start.' 20 | -------------------------------------------------------------------------------- /jmqtt-broker/src/main/resources/conf/client.cer: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICWDCCAcGgAwIBAgIJANmxJ6Gxp3rbMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV 3 | BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX 4 | aWRnaXRzIFB0eSBMdGQwHhcNMTkwNjMwMDk1ODE4WhcNMjkwNjI3MDk1ODE4WjBF 5 | MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50 6 | ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB 7 | gQCgz0+Bb1uY3TZhYx/bdBegTqLLsgmLlwHqdUuWbPEg9UdhLWWAeXpjXr+w1AjH 8 | wQ0aQtDPc6bK6Bu8H11fw4RfrhEAx7TVnka/88gEUx3qivFtIt5fBDKN5dEE1PfM 9 | kXqQV2szrJNRWsQbE8rbT26hNzc16Sdj0InLlJ9Y2pa0VQIDAQABo1AwTjAdBgNV 10 | HQ4EFgQUD4R4hfIOYFFyR3gIi7PGOag7tcwwHwYDVR0jBBgwFoAUD4R4hfIOYFFy 11 | R3gIi7PGOag7tcwwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOBgQBHdaLA 12 | RMg67YI0rEFc24MciGE+xBMSQEGegjIfo/p5frmU0kPBrdcv3XndblMPWDoGXnsE 13 | j1KZHu5Mw6dg8kid4ytvqTgvVjcjwNZ8f6Uj2mhxPJa15K5FF0D3xgCes+WiA+wF 14 | VDmoGEemCmGTIOjN4Nwb+V4/KKhOkoWhQBSLsg== 15 | -----END CERTIFICATE----- 16 | -------------------------------------------------------------------------------- /jmqtt-broker/src/main/resources/conf/jmqtt.properties: -------------------------------------------------------------------------------- 1 | # 是否开启匿名访问 2 | anonymousEnable=true 3 | 4 | # max mqtt message size 5 | maxMsgSize=524288 6 | 7 | 8 | # 是否开启tcp服务 9 | startTcp=true 10 | tcpPort=1883 11 | # 是否开启tcp ssl 服务 12 | startSslTcp=true 13 | sslTcpPort=1884 14 | 15 | #集群模式:1.基于发布订阅,集群主动push消息给Jmqtt; 2.基于poll,jmqtt主动从集群拉消息 16 | clusterMode = 2 17 | # 采用2.poll方式时,一次从集群中最多拉的消息数目和间隔拉取的时间(ms) 18 | maxPollEventNum = 10 19 | pollWaitInterval = 10 20 | 21 | # 是否开启websocket服务,ws协议 22 | startWebsocket=true 23 | websocketPort=8883 24 | 25 | # 是否开启websocket服务,wss协议 26 | startSslWebsocket=true 27 | sslWebsocketPort=8884 28 | 29 | 30 | # db config if jmqtt run with db 31 | driver=com.mysql.cj.jdbc.Driver 32 | url=jdbc:mysql://127.0.0.0:3306/jmqtt?characterEncoding=utf8&autoReconnect=true&failOverReadOnly=false&useSSL=false 33 | username=root 34 | password=CallmeZ2013 35 | -------------------------------------------------------------------------------- /jmqtt-broker/src/main/resources/conf/jmqtt.sql: -------------------------------------------------------------------------------- 1 | DROP INDEX `uk_client_id` ON `jmqtt_session`; 2 | DROP INDEX `uk_client_id_topic` ON `jmqtt_subscription`; 3 | DROP INDEX `idx_client_id` ON `jmqtt_message`; 4 | DROP INDEX `idx_client_id_ack` ON `jmqtt_client_inbox`; 5 | DROP INDEX `uk_topic` ON `jmqtt_retain_message`; 6 | 7 | 8 | DROP TABLE `jmqtt_session`; 9 | DROP TABLE `jmqtt_subscription`; 10 | DROP TABLE `jmqtt_message`; 11 | DROP TABLE `jmqtt_client_inbox`; 12 | DROP TABLE `jmqtt_retain_message`; 13 | DROP TABLE `jmqtt_cluster_event`; 14 | 15 | CREATE TABLE `jmqtt_session` ( 16 | `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键', 17 | `client_id` varchar(64) NOT NULL COMMENT '客户端id', 18 | `online` varchar(12) NOT NULL COMMENT '状态:ONLINE,OFFLINE两种', 19 | `transport_protocol` varchar(20) NOT NULL COMMENT '传输协议:MQTT,TCP,COAP等', 20 | `client_ip` varchar(32) NOT NULL COMMENT '客户端ip', 21 | `server_ip` varchar(32) NOT NULL COMMENT '连接的服务端ip', 22 | `last_offline_time` timestamp NULL COMMENT '上一次离线时间', 23 | `online_time` timestamp NOT NULL COMMENT '最近连接在线时间', 24 | `properties` text NULL COMMENT '扩展信息', 25 | PRIMARY KEY (`id`) , 26 | UNIQUE INDEX `uk_client_id` (`client_id` ASC) 27 | ) 28 | COMMENT = '客户端会话状态'; 29 | CREATE TABLE `jmqtt_subscription` ( 30 | `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键', 31 | `client_id` varchar(64) NOT NULL COMMENT '客户端id', 32 | `topic` varchar(64) NOT NULL COMMENT '订阅的topic', 33 | `subscribe_time` timestamp NOT NULL COMMENT '订阅时间', 34 | `properties` text NULL COMMENT '订阅的额外属性', 35 | PRIMARY KEY (`id`) , 36 | UNIQUE INDEX `uk_client_id_topic` (`client_id` ASC, `topic` ASC) 37 | ) 38 | COMMENT = '客户端订阅关系'; 39 | CREATE TABLE `jmqtt_message` ( 40 | `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键id', 41 | `source` varchar(32) NOT NULL COMMENT '消息来源:DEVICE/PLATFORM', 42 | `content` mediumblob NOT NULL COMMENT '消息体内容:字节', 43 | `topic` varchar(64) NULL COMMENT '发送的目标topic', 44 | `from_client_id` varchar(64) NULL COMMENT '消息来源若是设备,从属设备id', 45 | `stored_time` timestamp NOT NULL COMMENT '消息落库时间', 46 | `properties` text NULL COMMENT '消息额外属性', 47 | PRIMARY KEY (`id`) , 48 | INDEX `idx_client_id` (`source` ASC) 49 | ) 50 | COMMENT = '入栈消息表'; 51 | CREATE TABLE `jmqtt_client_inbox` ( 52 | `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键id', 53 | `client_id` varchar(64) NOT NULL COMMENT '客户端id', 54 | `message_id` bigint(20) NOT NULL COMMENT '消息id', 55 | `ack` tinyint(2) NOT NULL COMMENT '客户端是否收到消息:0未收到,1到达', 56 | `stored_time` timestamp NOT NULL COMMENT '收件箱时间', 57 | `ack_time` timestamp NULL, 58 | PRIMARY KEY (`id`) , 59 | INDEX `idx_client_id_ack` (`client_id` ASC, `ack` ASC) 60 | ) 61 | COMMENT = '设备消息收件箱表'; 62 | CREATE TABLE `jmqtt_retain_message` ( 63 | `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键', 64 | `topic` varchar(64) NOT NULL COMMENT '所属topic', 65 | `content` mediumblob NOT NULL COMMENT '消息体内容', 66 | `from_client_id` varchar(64) NOT NULL COMMENT 'retain消息来源设备id', 67 | `stored_time` timestamp NOT NULL COMMENT '存储事件', 68 | `properties` text NOT NULL COMMENT '额外属性', 69 | PRIMARY KEY (`id`) , 70 | UNIQUE INDEX `uk_topic` (`topic` ASC) 71 | ) 72 | COMMENT = 'mqtt协议的retain 消息表'; 73 | CREATE TABLE `jmqtt_cluster_event` ( 74 | `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键:也是集群节点批量拉消息的offset', 75 | `content` longtext NOT NULL COMMENT '消息体', 76 | `gmt_create` timestamp(6) NOT NULL COMMENT '创建时间', 77 | `node_ip` varchar(32) NOT NULL COMMENT 'jmqtt集群节点ip', 78 | `event_code` varchar(64) NOT NULL COMMENT '事件码:参考代码', 79 | PRIMARY KEY (`id`) 80 | ) 81 | COMMENT = 'jmqtt 集群事件转发表:由发送端将消息发送到该表中,其他节点批量拉取该表中的事件进行处理'; 82 | -------------------------------------------------------------------------------- /jmqtt-broker/src/main/resources/conf/server.pfx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cicizz/jmqtt/032ac7eababf54e072910b49e86c18716abdaab6/jmqtt-broker/src/main/resources/conf/server.pfx -------------------------------------------------------------------------------- /jmqtt-broker/src/test/java/org/jmqtt/broker/test/Runtime.java: -------------------------------------------------------------------------------- 1 | package org.jmqtt.broker.test; 2 | 3 | import com.sun.management.OperatingSystemMXBean; 4 | import org.junit.jupiter.api.Test; 5 | 6 | import java.lang.management.ManagementFactory; 7 | 8 | public class Runtime { 9 | 10 | @Test 11 | public void subscribe() { 12 | // 虚拟机级内存情况查询 13 | long vmFree = 0; 14 | long vmUse = 0; 15 | long vmTotal = 0; 16 | long vmMax = 0; 17 | int byteToMb = 1024 * 1024; 18 | java.lang.Runtime rt = java.lang.Runtime.getRuntime(); 19 | vmTotal = rt.totalMemory() / byteToMb; 20 | vmFree = rt.freeMemory() / byteToMb; 21 | vmMax = rt.maxMemory() / byteToMb; 22 | vmUse = vmTotal - vmFree; 23 | System.out.println("JVM内存已用的空间为:" + vmUse + " MB"); 24 | System.out.println("JVM内存的空闲空间为:" + vmFree + " MB"); 25 | System.out.println("JVM总内存空间为:" + vmTotal + " MB"); 26 | System.out.println("JVM总内存空间为:" + vmMax + " MB"); 27 | 28 | System.out.println("======================================"); 29 | // 操作系统级内存情况查询 30 | OperatingSystemMXBean osmxb = (OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean(); 31 | String os = System.getProperty("os.name"); 32 | long physicalFree = osmxb.getFreePhysicalMemorySize() / byteToMb; 33 | long physicalTotal = osmxb.getTotalPhysicalMemorySize() / byteToMb; 34 | long physicalUse = physicalTotal - physicalFree; 35 | System.out.println("操作系统的版本:" + os); 36 | System.out.println("操作系统物理内存已用的空间为:" + physicalFree + " MB"); 37 | System.out.println("操作系统物理内存的空闲空间为:" + physicalUse + " MB"); 38 | System.out.println("操作系统总物理内存:" + physicalTotal + " MB"); 39 | // 获得线程总数 40 | ThreadGroup parentThread; 41 | int totalThread = 0; 42 | for (parentThread = Thread.currentThread().getThreadGroup(); parentThread 43 | .getParent() != null; parentThread = parentThread.getParent()) { 44 | totalThread = parentThread.activeCount(); 45 | } 46 | System.out.println("获得线程总数:" + totalThread); 47 | 48 | ThreadGroup currentGroup = 49 | Thread.currentThread().getThreadGroup(); 50 | int noThreads = currentGroup.activeCount(); 51 | Thread[] lstThreads = new Thread[noThreads]; 52 | currentGroup.enumerate(lstThreads); 53 | for (int i = 0; i < noThreads; i++) 54 | System.out.println("线程号:" + i + " = " + lstThreads[i].getName()); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /jmqtt-bus/README.md: -------------------------------------------------------------------------------- 1 | iot 消息bus 2 | -------------------------------------------------------------------------------- /jmqtt-bus/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | jmqtt 7 | org.jmqtt 8 | 3.0.0 9 | 10 | 4.0.0 11 | 12 | jmqtt-bus 13 | 14 | jmqtt-bus 15 | 16 | 17 | 18 | 19 | org.assertj 20 | assertj-core 21 | 22 | 23 | 24 | com.google.guava 25 | guava 26 | 27 | 28 | 29 | org.mybatis 30 | mybatis 31 | 32 | 33 | 34 | com.alibaba 35 | druid 36 | 37 | 38 | 39 | mysql 40 | mysql-connector-java 41 | 42 | 43 | org.apache.logging.log4j 44 | log4j-api 45 | 46 | 47 | org.apache.logging.log4j 48 | log4j-core 49 | 50 | 51 | 52 | org.jmqtt 53 | jmqtt-support 54 | 55 | 56 | org.junit.jupiter 57 | junit-jupiter-api 58 | test 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /jmqtt-bus/src/main/java/org/jmqtt/bus/Authenticator.java: -------------------------------------------------------------------------------- 1 | package org.jmqtt.bus; 2 | 3 | 4 | public interface Authenticator { 5 | 6 | 7 | boolean login(String clientId,String userName,byte[] password); 8 | 9 | boolean onBlackList(String clientId,String remoteIpAddress); 10 | 11 | boolean clientIdVerify(String clientId); 12 | 13 | boolean subscribeVerify(String clientId,String topic); 14 | } 15 | -------------------------------------------------------------------------------- /jmqtt-bus/src/main/java/org/jmqtt/bus/BusController.java: -------------------------------------------------------------------------------- 1 | package org.jmqtt.bus; 2 | 3 | import org.jmqtt.bus.impl.*; 4 | import org.jmqtt.bus.store.DBUtils; 5 | import org.jmqtt.bus.subscription.CTrieSubscriptionMatcher; 6 | import org.jmqtt.bus.subscription.SubscriptionMatcher; 7 | import org.jmqtt.support.config.BrokerConfig; 8 | 9 | public class BusController { 10 | 11 | private BrokerConfig brokerConfig; 12 | private Authenticator authenticator; 13 | private DeviceSessionManager deviceSessionManager; 14 | private DeviceMessageManager deviceMessageManager; 15 | private DeviceSubscriptionManager deviceSubscriptionManager; 16 | private SubscriptionMatcher subscriptionMatcher; 17 | private ClusterEventManager clusterEventManager; 18 | 19 | public BusController(BrokerConfig brokerConfig){ 20 | this.brokerConfig = brokerConfig; 21 | this.authenticator = new DefaultAuthenticator(); 22 | this.deviceMessageManager = new DeviceMessageManagerImpl(); 23 | this.deviceSessionManager = new DeviceSessionManagerImpl(); 24 | //this.subscriptionMatcher = new DefaultSubscriptionTreeMatcher(); 25 | this.subscriptionMatcher = new CTrieSubscriptionMatcher(); 26 | this.deviceSubscriptionManager = new DeviceSubscriptionManagerImpl(subscriptionMatcher); 27 | this.clusterEventManager = new ClusterEventManagerImpl(subscriptionMatcher); 28 | } 29 | 30 | 31 | public void start(){ 32 | DBUtils.getInstance().start(brokerConfig); 33 | this.clusterEventManager.start(); 34 | } 35 | 36 | public void shutdown(){ 37 | this.clusterEventManager.shutdown(); 38 | DBUtils.getInstance().shutdown(); 39 | } 40 | 41 | public Authenticator getAuthenticator() { 42 | return authenticator; 43 | } 44 | 45 | public DeviceSessionManager getDeviceSessionManager() { 46 | return deviceSessionManager; 47 | } 48 | 49 | public DeviceMessageManager getDeviceMessageManager() { 50 | return deviceMessageManager; 51 | } 52 | 53 | public DeviceSubscriptionManager getDeviceSubscriptionManager() { 54 | return deviceSubscriptionManager; 55 | } 56 | 57 | public ClusterEventManager getClusterEventManager() { 58 | return clusterEventManager; 59 | } 60 | 61 | public SubscriptionMatcher getSubscriptionMatcher() { 62 | return subscriptionMatcher; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /jmqtt-bus/src/main/java/org/jmqtt/bus/ClusterEventManager.java: -------------------------------------------------------------------------------- 1 | package org.jmqtt.bus; 2 | 3 | import org.jmqtt.bus.event.GatewayListener; 4 | import org.jmqtt.bus.model.ClusterEvent; 5 | 6 | /** 7 | * cluster event send and listen 8 | */ 9 | public interface ClusterEventManager { 10 | 11 | void sendEvent(ClusterEvent clusterEvent); 12 | 13 | void registerEventListener(GatewayListener listener); 14 | 15 | void start(); 16 | 17 | void shutdown(); 18 | } 19 | -------------------------------------------------------------------------------- /jmqtt-bus/src/main/java/org/jmqtt/bus/DeviceMessageManager.java: -------------------------------------------------------------------------------- 1 | 2 | package org.jmqtt.bus; 3 | 4 | import org.jmqtt.bus.enums.MessageAckEnum; 5 | import org.jmqtt.bus.model.DeviceMessage; 6 | 7 | import java.util.List; 8 | 9 | public interface DeviceMessageManager { 10 | 11 | void clearUnAckMessage(String clientId); 12 | 13 | /** 14 | * send message to bus 15 | * @param deviceMessage 16 | */ 17 | void dispatcher(DeviceMessage deviceMessage); 18 | 19 | Long storeMessage(DeviceMessage deviceMessage); 20 | 21 | List queryUnAckMessages(String clientId,int limit); 22 | 23 | List queryByIds(List ids); 24 | 25 | Long addClientInBoxMsg(String clientId,Long messageId, MessageAckEnum ackEnum); 26 | 27 | boolean ackMessage(String clientId,Long messageId); 28 | } 29 | -------------------------------------------------------------------------------- /jmqtt-bus/src/main/java/org/jmqtt/bus/DeviceSessionManager.java: -------------------------------------------------------------------------------- 1 | 2 | package org.jmqtt.bus; 3 | 4 | import org.jmqtt.bus.model.DeviceSession; 5 | 6 | public interface DeviceSessionManager { 7 | 8 | 9 | 10 | DeviceSession getSession(String clientId); 11 | 12 | void storeSession(DeviceSession deviceSession); 13 | 14 | 15 | void offline(String clientId); 16 | } 17 | -------------------------------------------------------------------------------- /jmqtt-bus/src/main/java/org/jmqtt/bus/DeviceSubscriptionManager.java: -------------------------------------------------------------------------------- 1 | package org.jmqtt.bus; 2 | 3 | import org.jmqtt.bus.model.DeviceSubscription; 4 | 5 | import java.util.Set; 6 | 7 | public interface DeviceSubscriptionManager { 8 | 9 | /** 10 | * 1. Add subscription to the sub tree 11 | * 2. Persistence subscription 12 | * @param deviceSubscription 13 | * @return true:sub success false: sub fail 14 | */ 15 | boolean subscribe(DeviceSubscription deviceSubscription); 16 | 17 | /** 18 | * 1. Remove subscription from the sub tree 19 | * 2. Delete subscription from db 20 | */ 21 | boolean unSubscribe(String clientId,String topic); 22 | 23 | boolean isMatch(String pubTopic,String subTopic); 24 | 25 | boolean onlySubscribe2Tree(DeviceSubscription deviceSubscription); 26 | 27 | boolean onlyUnUnSubscribeFromTree(String clientId,String topic); 28 | 29 | Set getAllSubscription(String clientId); 30 | 31 | void deleteAllSubscription(String clientId); 32 | } 33 | -------------------------------------------------------------------------------- /jmqtt-bus/src/main/java/org/jmqtt/bus/enums/ClusterEventCodeEnum.java: -------------------------------------------------------------------------------- 1 | package org.jmqtt.bus.enums; 2 | 3 | /** 4 | * cluster event code 5 | */ 6 | public enum ClusterEventCodeEnum { 7 | 8 | MQTT_CLEAR_SESSION("MQTT_CLEAR_SESSION"), 9 | 10 | DISPATCHER_CLIENT_MESSAGE("DISPATCHER_CLIENT_MESSAGE"), 11 | ; 12 | 13 | private String code; 14 | 15 | ClusterEventCodeEnum(String code) { 16 | this.code = code; 17 | } 18 | 19 | public String getCode() { 20 | return code; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /jmqtt-bus/src/main/java/org/jmqtt/bus/enums/DeviceOnlineStateEnum.java: -------------------------------------------------------------------------------- 1 | 2 | package org.jmqtt.bus.enums; 3 | 4 | public enum DeviceOnlineStateEnum { 5 | 6 | ONLINE("ONLINE"), 7 | 8 | OFFLINE("OFFLINE"), 9 | 10 | ; 11 | 12 | private String code; 13 | 14 | DeviceOnlineStateEnum(String code) { 15 | this.code = code; 16 | } 17 | 18 | public String getCode() { 19 | return code; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /jmqtt-bus/src/main/java/org/jmqtt/bus/enums/MessageAckEnum.java: -------------------------------------------------------------------------------- 1 | 2 | package org.jmqtt.bus.enums; 3 | 4 | public enum MessageAckEnum { 5 | 6 | UN_ACK(0), 7 | 8 | ACK(1), 9 | 10 | ; 11 | 12 | private int code; 13 | 14 | MessageAckEnum(int code) { 15 | this.code = code; 16 | } 17 | 18 | public int getCode() { 19 | return code; 20 | } 21 | 22 | public static MessageAckEnum getByCode(int code) { 23 | for (MessageAckEnum value : MessageAckEnum.values()) { 24 | if (value.code == code) { 25 | return value; 26 | } 27 | } 28 | return null; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /jmqtt-bus/src/main/java/org/jmqtt/bus/enums/MessageFlowEnum.java: -------------------------------------------------------------------------------- 1 | package org.jmqtt.bus.enums; 2 | 3 | 4 | public enum MessageFlowEnum { 5 | 6 | INBOUND("INBOUND"), 7 | 8 | OUTBOUND("OUTBOUND"), 9 | 10 | ; 11 | 12 | private String code; 13 | 14 | MessageFlowEnum(String code) { 15 | this.code = code; 16 | } 17 | 18 | public String getCode() { 19 | return code; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /jmqtt-bus/src/main/java/org/jmqtt/bus/enums/MessageSourceEnum.java: -------------------------------------------------------------------------------- 1 | package org.jmqtt.bus.enums; 2 | 3 | 4 | public enum MessageSourceEnum { 5 | 6 | DEVICE("DEVICE"), 7 | 8 | PLATFORM("PLATFORM"), 9 | 10 | ; 11 | 12 | private String code; 13 | 14 | MessageSourceEnum(String code) { 15 | this.code = code; 16 | } 17 | 18 | public String getCode() { 19 | return code; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /jmqtt-bus/src/main/java/org/jmqtt/bus/enums/TransportProtocolEnum.java: -------------------------------------------------------------------------------- 1 | 2 | package org.jmqtt.bus.enums; 3 | 4 | 5 | public enum TransportProtocolEnum { 6 | 7 | MQTT("MQTT"), 8 | 9 | TCP("TCP"), 10 | 11 | COAP("COAP"), 12 | ; 13 | 14 | private String code; 15 | 16 | TransportProtocolEnum(String code) { 17 | this.code = code; 18 | } 19 | 20 | public String getCode() { 21 | return code; 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /jmqtt-bus/src/main/java/org/jmqtt/bus/event/EventCenter.java: -------------------------------------------------------------------------------- 1 | package org.jmqtt.bus.event; 2 | 3 | import com.google.common.eventbus.EventBus; 4 | import org.jmqtt.bus.model.ClusterEvent; 5 | 6 | public class EventCenter { 7 | 8 | private final EventBus eventBus; 9 | 10 | public EventCenter(){ 11 | this.eventBus = new EventBus(); 12 | } 13 | 14 | /** 15 | * register gateway listener 16 | * @param listener 17 | */ 18 | public void register(GatewayListener listener){ 19 | this.eventBus.register(listener); 20 | } 21 | 22 | /** 23 | * send event to bus,and gateway will consume this event 24 | * @param clusterEvent 25 | */ 26 | public void sendEvent(ClusterEvent clusterEvent){ 27 | this.eventBus.post(clusterEvent); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /jmqtt-bus/src/main/java/org/jmqtt/bus/event/GatewayListener.java: -------------------------------------------------------------------------------- 1 | 2 | package org.jmqtt.bus.event; 3 | 4 | import org.jmqtt.bus.model.ClusterEvent; 5 | 6 | /** 7 | * gateway listener 8 | * iot gateway 实现该接口,接收bus的事件进行客户端触达和处理 9 | */ 10 | public interface GatewayListener { 11 | 12 | void consume(ClusterEvent clusterEvent); 13 | } 14 | -------------------------------------------------------------------------------- /jmqtt-bus/src/main/java/org/jmqtt/bus/impl/ClusterEventManagerImpl.java: -------------------------------------------------------------------------------- 1 | package org.jmqtt.bus.impl; 2 | 3 | import com.alibaba.fastjson.JSONObject; 4 | import org.apache.ibatis.session.SqlSession; 5 | import org.jmqtt.bus.ClusterEventManager; 6 | import org.jmqtt.bus.enums.ClusterEventCodeEnum; 7 | import org.jmqtt.bus.event.EventCenter; 8 | import org.jmqtt.bus.event.GatewayListener; 9 | import org.jmqtt.bus.model.ClusterEvent; 10 | import org.jmqtt.bus.model.DeviceMessage; 11 | import org.jmqtt.bus.store.DBCallback; 12 | import org.jmqtt.bus.store.DBUtils; 13 | import org.jmqtt.bus.store.daoobject.EventDO; 14 | import org.jmqtt.bus.subscription.SubscriptionMatcher; 15 | import org.jmqtt.bus.subscription.model.Subscription; 16 | import org.jmqtt.support.helper.MixAll; 17 | import org.jmqtt.support.log.JmqttLogger; 18 | import org.jmqtt.support.log.LogUtil; 19 | import org.slf4j.Logger; 20 | 21 | import java.util.ArrayList; 22 | import java.util.Collections; 23 | import java.util.List; 24 | import java.util.Set; 25 | import java.util.concurrent.atomic.AtomicBoolean; 26 | import java.util.concurrent.atomic.AtomicLong; 27 | 28 | 29 | /** 30 | * send current node's event to other nodes 集群事件处理,两种方式,根据实际情况,选一个实现即可: 31 | * 1. 发布订阅: a. 发送消息 A -> mq系统转发 b. 第二阶段:消息订阅分发给本节点的设备 jmqtt B,C,D —> jmqtt B的连接设备 32 | * 2. jmqtt服务主动拉取: a. A —> 消息存储服务: b. 第二阶段:主动拉取 jmqtt B,C,D broker 定时批量拉取 —> 从消息存储服务拉取 —> 拉取后推送给 B,C,D上连接的设备 33 | */ 34 | public class ClusterEventManagerImpl implements ClusterEventManager { 35 | 36 | private static final Logger log = JmqttLogger.busLog; 37 | private static final AtomicLong offset = new AtomicLong(); 38 | private AtomicBoolean pollStoped = new AtomicBoolean(false); 39 | private int maxPollNum = 100; 40 | private int pollWaitInterval = 10; // ms 41 | private final EventCenter eventCenter; 42 | private SubscriptionMatcher subscriptionMatcher; 43 | 44 | public ClusterEventManagerImpl(SubscriptionMatcher subscriptionMatcher){ 45 | this.eventCenter = new EventCenter(); 46 | this.subscriptionMatcher = subscriptionMatcher; 47 | } 48 | 49 | @Override 50 | public void start() { 51 | Long maxId = DBUtils.operate(new DBCallback() { 52 | @Override 53 | public Long operate(SqlSession sqlSession) { 54 | return DBUtils.getMapper(sqlSession,DBUtils.eventMapperClass).getMaxOffset(); 55 | } 56 | }); 57 | if (maxId == null) { 58 | offset.set(0); 59 | } else { 60 | offset.set(maxId); 61 | } 62 | new Thread(() -> { 63 | while (!pollStoped.get()) { 64 | try { 65 | List eventList = pollEvent(maxPollNum); 66 | if (!MixAll.isEmpty(eventList)) { 67 | for (ClusterEvent event : eventList) { 68 | // Send event to iot gateway 69 | consumeEvent(event); 70 | } 71 | } 72 | if (MixAll.isEmpty(eventList) || eventList.size() < 5) { 73 | Thread.sleep(pollWaitInterval); 74 | } 75 | } catch (Exception e) { 76 | LogUtil.warn(log, "Poll event from cluster error.", e); 77 | } 78 | } 79 | }).start(); 80 | 81 | LogUtil.info(log,"Cluster event server start."); 82 | } 83 | 84 | @Override 85 | public void shutdown() { 86 | this.pollStoped.compareAndSet(false,true); 87 | } 88 | 89 | /** 90 | * 集群消息往这里投递 91 | */ 92 | private void consumeEvent(ClusterEvent event){ 93 | 94 | // 若是消息,进行订阅树的匹配 95 | if (event.getClusterEventCode() == ClusterEventCodeEnum.DISPATCHER_CLIENT_MESSAGE){ 96 | DeviceMessage deviceMessage = JSONObject.parseObject(event.getContent(),DeviceMessage.class); 97 | if (deviceMessage == null) { 98 | LogUtil.error(log,"[BUS EVENT] event content is empty."); 99 | return; 100 | } 101 | Set subscriptionSet = this.subscriptionMatcher.match(deviceMessage.getTopic()); 102 | deviceMessage.setContent(null); 103 | if (subscriptionSet != null) { 104 | subscriptionSet.forEach(item -> { 105 | ClusterEvent stayEvent = event.clone(); 106 | stayEvent.setSubscription(item); 107 | 108 | eventCenter.sendEvent(stayEvent); 109 | }); 110 | } 111 | return; 112 | } 113 | eventCenter.sendEvent(event); 114 | } 115 | 116 | 117 | private List pollEvent(int maxPollNum) { 118 | // offset: min -> max 119 | long currentOffset = offset.get(); 120 | List eventDOList = DBUtils.operate(new DBCallback() { 121 | @Override 122 | public List operate(SqlSession sqlSession) { 123 | return DBUtils.getMapper(sqlSession,DBUtils.eventMapperClass).consumeEvent(currentOffset,maxPollNum); 124 | } 125 | }); 126 | if (eventDOList == null || eventDOList.size() == 0) { 127 | return Collections.emptyList(); 128 | } 129 | List events = new ArrayList<>(); 130 | for (EventDO eventDO : eventDOList) { 131 | ClusterEvent event = new ClusterEvent(); 132 | event.setNodeIp(eventDO.getNodeIp()); 133 | event.setGmtCreate(eventDO.getGmtCreate()); 134 | event.setContent(eventDO.getContent()); 135 | event.setClusterEventCode(ClusterEventCodeEnum.valueOf(eventDO.getEventCode())); 136 | events.add(event); 137 | } 138 | 139 | // reset offset 140 | EventDO eventDO = eventDOList.get(eventDOList.size()-1); 141 | if (!offset.compareAndSet(currentOffset,eventDO.getId())) { 142 | LogUtil.warn(log,"[RDBClusterEventHandler] pollEvent offset is wrong,expectOffset:{},currentOffset:{},maxOffset:{}", 143 | offset.get(),currentOffset,eventDO.getId()); 144 | offset.set(eventDO.getId()); 145 | } 146 | return events; 147 | } 148 | 149 | @Override 150 | public void sendEvent(ClusterEvent clusterEvent) { 151 | EventDO eventDO = convert(clusterEvent); 152 | DBUtils.operate(new DBCallback() { 153 | @Override 154 | public Object operate(SqlSession sqlSession) { 155 | return DBUtils.getMapper(sqlSession, DBUtils.eventMapperClass).sendEvent(eventDO); 156 | } 157 | }); 158 | } 159 | 160 | 161 | public void registerEventListener(GatewayListener listener){ 162 | this.eventCenter.register(listener); 163 | } 164 | 165 | 166 | private EventDO convert(ClusterEvent clusterEvent) { 167 | EventDO eventDO = new EventDO(); 168 | eventDO.setContent(clusterEvent.getContent()); 169 | eventDO.setNodeIp(clusterEvent.getNodeIp()); 170 | eventDO.setEventCode(clusterEvent.getClusterEventCode().getCode()); 171 | eventDO.setGmtCreate(clusterEvent.getGmtCreate()); 172 | return eventDO; 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /jmqtt-bus/src/main/java/org/jmqtt/bus/impl/DefaultAuthenticator.java: -------------------------------------------------------------------------------- 1 | package org.jmqtt.bus.impl; 2 | 3 | import org.jmqtt.bus.Authenticator; 4 | 5 | 6 | public class DefaultAuthenticator implements Authenticator { 7 | 8 | @Override 9 | public boolean login(String clientId, String userName, byte[] password) { 10 | return true; 11 | } 12 | 13 | @Override 14 | public boolean onBlackList(String clientId, String remoteIpAddress) { 15 | return false; 16 | } 17 | 18 | @Override 19 | public boolean clientIdVerify(String clientId) { 20 | return true; 21 | } 22 | 23 | @Override 24 | public boolean subscribeVerify(String clientId, String topic) { 25 | return true; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /jmqtt-bus/src/main/java/org/jmqtt/bus/impl/DeviceSessionManagerImpl.java: -------------------------------------------------------------------------------- 1 | package org.jmqtt.bus.impl; 2 | 3 | import com.alibaba.fastjson.JSONObject; 4 | import org.apache.ibatis.session.SqlSession; 5 | import org.jmqtt.bus.DeviceSessionManager; 6 | import org.jmqtt.bus.enums.DeviceOnlineStateEnum; 7 | import org.jmqtt.bus.enums.TransportProtocolEnum; 8 | import org.jmqtt.bus.model.DeviceSession; 9 | import org.jmqtt.bus.store.DBCallback; 10 | import org.jmqtt.bus.store.DBUtils; 11 | import org.jmqtt.bus.store.daoobject.SessionDO; 12 | 13 | import java.util.Date; 14 | import java.util.Map; 15 | 16 | public class DeviceSessionManagerImpl implements DeviceSessionManager { 17 | 18 | @Override 19 | public DeviceSession getSession(String clientId) { 20 | 21 | // optimize: add cache 22 | SessionDO sessionDO = (SessionDO) DBUtils.operate(new DBCallback() { 23 | @Override 24 | public Object operate(SqlSession sqlSession) { 25 | return DBUtils.getMapper(sqlSession, DBUtils.sessionMapperClass).getSession(clientId); 26 | } 27 | }); 28 | if (sessionDO == null) { 29 | return null; 30 | } 31 | return convert(sessionDO); 32 | } 33 | 34 | @Override 35 | public void storeSession(DeviceSession deviceSession) { 36 | SessionDO sessionDO = convert(deviceSession); 37 | DBUtils.operate(new DBCallback() { 38 | @Override 39 | public Long operate(SqlSession sqlSession) { 40 | return DBUtils.getMapper(sqlSession, DBUtils.sessionMapperClass).storeSession(sessionDO); 41 | } 42 | }); 43 | } 44 | 45 | @Override 46 | public void offline(String clientId) { 47 | SessionDO sessionDO = new SessionDO(); 48 | sessionDO.setClientId(clientId); 49 | sessionDO.setOnline(DeviceOnlineStateEnum.OFFLINE.getCode()); 50 | sessionDO.setLastOfflineTime(new Date()); 51 | DBUtils.operate(new DBCallback() { 52 | @Override 53 | public Object operate(SqlSession sqlSession) { 54 | return DBUtils.getMapper(sqlSession, DBUtils.sessionMapperClass).offline(sessionDO); 55 | } 56 | }); 57 | } 58 | 59 | private DeviceSession convert(SessionDO sessionDO) { 60 | DeviceSession deviceSession = new DeviceSession(); 61 | deviceSession.setClientId(sessionDO.getClientId()); 62 | deviceSession.setClientIp(sessionDO.getClientIp()); 63 | deviceSession.setLastOfflineTime(sessionDO.getLastOfflineTime()); 64 | deviceSession.setOnline(DeviceOnlineStateEnum.valueOf(sessionDO.getOnline())); 65 | deviceSession.setOnlineTime(sessionDO.getOnlineTime()); 66 | String properties = sessionDO.getProperties(); 67 | if (properties != null) { 68 | deviceSession.setProperties(JSONObject.parseObject(properties, Map.class)); 69 | } 70 | deviceSession.setServerIp(deviceSession.getServerIp()); 71 | deviceSession.setTransportProtocol(TransportProtocolEnum.valueOf(sessionDO.getTransportProtocol())); 72 | return deviceSession; 73 | } 74 | 75 | private SessionDO convert(DeviceSession deviceSession) { 76 | SessionDO sessionDO = new SessionDO(); 77 | sessionDO.setClientId(deviceSession.getClientId()); 78 | sessionDO.setClientIp(deviceSession.getClientIp()); 79 | sessionDO.setLastOfflineTime(deviceSession.getLastOfflineTime()); 80 | sessionDO.setOnline(deviceSession.getOnline().getCode()); 81 | sessionDO.setOnlineTime(deviceSession.getOnlineTime()); 82 | sessionDO.setProperties(deviceSession.getProperties() == null ? null : JSONObject.toJSONString(deviceSession.getProperties())); 83 | sessionDO.setServerIp(deviceSession.getServerIp()); 84 | sessionDO.setTransportProtocol(deviceSession.getTransportProtocol().getCode()); 85 | return sessionDO; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /jmqtt-bus/src/main/java/org/jmqtt/bus/impl/DeviceSubscriptionManagerImpl.java: -------------------------------------------------------------------------------- 1 | package org.jmqtt.bus.impl; 2 | 3 | import com.alibaba.fastjson.JSONObject; 4 | import org.apache.ibatis.session.SqlSession; 5 | import org.jmqtt.bus.DeviceSubscriptionManager; 6 | import org.jmqtt.bus.model.DeviceSubscription; 7 | import org.jmqtt.bus.store.DBCallback; 8 | import org.jmqtt.bus.store.DBUtils; 9 | import org.jmqtt.bus.store.daoobject.SubscriptionDO; 10 | import org.jmqtt.bus.subscription.SubscriptionMatcher; 11 | import org.jmqtt.bus.subscription.Topic; 12 | import org.jmqtt.bus.subscription.model.Subscription; 13 | import org.jmqtt.support.log.JmqttLogger; 14 | import org.jmqtt.support.log.LogUtil; 15 | import org.slf4j.Logger; 16 | 17 | import java.util.HashSet; 18 | import java.util.List; 19 | import java.util.Map; 20 | import java.util.Set; 21 | 22 | 23 | public class DeviceSubscriptionManagerImpl implements DeviceSubscriptionManager { 24 | 25 | private static final Logger log = JmqttLogger.busLog; 26 | 27 | private SubscriptionMatcher subscriptionMatcher; 28 | 29 | public DeviceSubscriptionManagerImpl(SubscriptionMatcher subscriptionMatcher){ 30 | this.subscriptionMatcher = subscriptionMatcher; 31 | } 32 | 33 | @Override 34 | public boolean subscribe(DeviceSubscription deviceSubscription) { 35 | SqlSession sqlSession = null; 36 | try { 37 | sqlSession = DBUtils.getSqlSessionWithTrans(); 38 | SubscriptionDO subscriptionDO = convert(deviceSubscription); 39 | DBUtils.getMapper(sqlSession,DBUtils.subscriptionMapperClass).storeSubscription(subscriptionDO); 40 | if (subscriptionDO.getId() == null || subscriptionDO.getId() <= 0) { 41 | LogUtil.error(log,"[SUBSCRIBE] store subscription fail."); 42 | return false; 43 | } 44 | Subscription subscription = convert2Sub(deviceSubscription); 45 | boolean addSubTree = subscriptionMatcher.subscribe(subscription); 46 | if (!addSubTree) { 47 | LogUtil.error(log,"[SUBSCRIBE] sub tree fail."); 48 | sqlSession.rollback(); 49 | return false; 50 | } 51 | sqlSession.commit(); 52 | return true; 53 | } catch (Exception ex) { 54 | LogUtil.error(log,"[SUBSCRIBE] subscribe failure.ex:{}",ex); 55 | if (sqlSession != null) { 56 | sqlSession.rollback(); 57 | } 58 | } finally { 59 | if (sqlSession != null) { 60 | sqlSession.close(); 61 | } 62 | } 63 | return false; 64 | } 65 | 66 | @Override 67 | public boolean unSubscribe(String clientId,String topic) { 68 | SqlSession sqlSession = null; 69 | try { 70 | sqlSession = DBUtils.getSqlSessionWithTrans(); 71 | Integer effectNum = DBUtils.getMapper(sqlSession,DBUtils.subscriptionMapperClass).delSubscription(clientId,topic); 72 | if (effectNum == null || effectNum <= 0) { 73 | LogUtil.error(log,"[UNSUBSCRIBE] del subscription fail."); 74 | return false; 75 | } 76 | boolean subSub = subscriptionMatcher.unSubscribe(topic,clientId); 77 | if (!subSub) { 78 | LogUtil.error(log,"[SUBSUBSCRIBE] unsub from tree fail."); 79 | sqlSession.rollback(); 80 | return false; 81 | } 82 | sqlSession.commit(); 83 | return true; 84 | } catch (Exception ex) { 85 | LogUtil.error(log,"[SUBSCRIBE] subscribe failure.ex:{}",ex); 86 | if (sqlSession != null) { 87 | sqlSession.rollback(); 88 | } 89 | } finally { 90 | if (sqlSession != null) { 91 | sqlSession.close(); 92 | } 93 | } 94 | return false; 95 | } 96 | 97 | @Override 98 | public boolean isMatch(String pubTopic, String subTopic) { 99 | Topic topic = new Topic(pubTopic); 100 | return topic.match(new Topic(subTopic)); 101 | } 102 | 103 | @Override 104 | public boolean onlySubscribe2Tree(DeviceSubscription deviceSubscription) { 105 | Subscription subscription = convert2Sub(deviceSubscription); 106 | return subscriptionMatcher.subscribe(subscription); 107 | } 108 | 109 | @Override 110 | public boolean onlyUnUnSubscribeFromTree(String clientId, String topic) { 111 | return subscriptionMatcher.unSubscribe(topic,clientId); 112 | } 113 | 114 | @Override 115 | public Set getAllSubscription(String clientId) { 116 | List subscriptionDOS = DBUtils.operate(new DBCallback() { 117 | @Override 118 | public List operate(SqlSession sqlSession) { 119 | return DBUtils.getMapper(sqlSession,DBUtils.subscriptionMapperClass).getAllSubscription(clientId); 120 | } 121 | }); 122 | if (subscriptionDOS == null) { 123 | return null; 124 | } 125 | Set subscriptions = new HashSet<>(subscriptionDOS.size()); 126 | subscriptionDOS.forEach(item -> { 127 | subscriptions.add(convert(item)); 128 | }); 129 | return subscriptions; 130 | } 131 | 132 | @Override 133 | public void deleteAllSubscription(String clientId) { 134 | DBUtils.operate(new DBCallback() { 135 | @Override 136 | public Integer operate(SqlSession sqlSession) { 137 | return DBUtils.getMapper(sqlSession,DBUtils.subscriptionMapperClass).clearSubscription(clientId); 138 | } 139 | }); 140 | } 141 | 142 | private Subscription convert2Sub(DeviceSubscription deviceSubscription){ 143 | return new Subscription(deviceSubscription.getClientId(),deviceSubscription.getTopic(),deviceSubscription.getProperties()); 144 | } 145 | 146 | private DeviceSubscription convert(SubscriptionDO subscriptionDO){ 147 | DeviceSubscription deviceSubscription = new DeviceSubscription(); 148 | deviceSubscription.setClientId(subscriptionDO.getClientId()); 149 | deviceSubscription.setTopic(subscriptionDO.getTopic()); 150 | deviceSubscription.setSubscribeTime(subscriptionDO.getSubscribeTime()); 151 | if (subscriptionDO.getProperties() != null) { 152 | deviceSubscription.setProperties(JSONObject.parseObject(subscriptionDO.getProperties(), Map.class)); 153 | } 154 | return deviceSubscription; 155 | } 156 | 157 | private SubscriptionDO convert(DeviceSubscription deviceSubscription){ 158 | SubscriptionDO subscriptionDO = new SubscriptionDO(); 159 | subscriptionDO.setClientId(deviceSubscription.getClientId()); 160 | subscriptionDO.setTopic(deviceSubscription.getTopic()); 161 | subscriptionDO.setProperties(deviceSubscription.getProperties() != null? JSONObject.toJSONString(deviceSubscription.getProperties()): null); 162 | subscriptionDO.setSubscribeTime(deviceSubscription.getSubscribeTime()); 163 | return subscriptionDO; 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /jmqtt-bus/src/main/java/org/jmqtt/bus/model/ClusterEvent.java: -------------------------------------------------------------------------------- 1 | package org.jmqtt.bus.model; 2 | 3 | import org.jmqtt.bus.enums.ClusterEventCodeEnum; 4 | import org.jmqtt.bus.subscription.model.Subscription; 5 | 6 | import java.util.Date; 7 | 8 | public class ClusterEvent { 9 | 10 | private Long id; 11 | 12 | private String content; 13 | 14 | private Date gmtCreate; 15 | 16 | private String nodeIp; 17 | 18 | private ClusterEventCodeEnum clusterEventCode; 19 | 20 | /** 21 | * 待分发消息时的订阅关系 22 | */ 23 | private Subscription subscription; 24 | 25 | public Long getId() { 26 | return id; 27 | } 28 | 29 | public void setId(Long id) { 30 | this.id = id; 31 | } 32 | 33 | public String getContent() { 34 | return content; 35 | } 36 | 37 | public void setContent(String content) { 38 | this.content = content; 39 | } 40 | 41 | public Date getGmtCreate() { 42 | return gmtCreate; 43 | } 44 | 45 | public void setGmtCreate(Date gmtCreate) { 46 | this.gmtCreate = gmtCreate; 47 | } 48 | 49 | public String getNodeIp() { 50 | return nodeIp; 51 | } 52 | 53 | public void setNodeIp(String nodeIp) { 54 | this.nodeIp = nodeIp; 55 | } 56 | 57 | public ClusterEventCodeEnum getClusterEventCode() { 58 | return clusterEventCode; 59 | } 60 | 61 | public void setClusterEventCode(ClusterEventCodeEnum clusterEventCode) { 62 | this.clusterEventCode = clusterEventCode; 63 | } 64 | 65 | public Subscription getSubscription() { 66 | return subscription; 67 | } 68 | 69 | public void setSubscription(Subscription subscription) { 70 | this.subscription = subscription; 71 | } 72 | 73 | public ClusterEvent clone() { 74 | ClusterEvent event = new ClusterEvent(); 75 | event.setClusterEventCode(this.getClusterEventCode()); 76 | event.setContent(this.getContent()); 77 | event.setGmtCreate(this.getGmtCreate()); 78 | event.setNodeIp(this.getNodeIp()); 79 | event.setId(this.getId()); 80 | event.setSubscription(this.getSubscription()); 81 | return event; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /jmqtt-bus/src/main/java/org/jmqtt/bus/model/DeviceInboxMessage.java: -------------------------------------------------------------------------------- 1 | package org.jmqtt.bus.model; 2 | 3 | import java.util.Date; 4 | 5 | public class DeviceInboxMessage { 6 | 7 | private Long id; 8 | 9 | private String clientId; 10 | 11 | private Long messageId; 12 | 13 | private Integer ack; 14 | 15 | private Date storedTime; 16 | 17 | private Date ackTime; 18 | 19 | public Long getId() { 20 | return id; 21 | } 22 | 23 | public void setId(Long id) { 24 | this.id = id; 25 | } 26 | 27 | public String getClientId() { 28 | return clientId; 29 | } 30 | 31 | public void setClientId(String clientId) { 32 | this.clientId = clientId; 33 | } 34 | 35 | public Long getMessageId() { 36 | return messageId; 37 | } 38 | 39 | public void setMessageId(Long messageId) { 40 | this.messageId = messageId; 41 | } 42 | 43 | public Integer getAck() { 44 | return ack; 45 | } 46 | 47 | public void setAck(Integer ack) { 48 | this.ack = ack; 49 | } 50 | 51 | public Date getStoredTime() { 52 | return storedTime; 53 | } 54 | 55 | public void setStoredTime(Date storedTime) { 56 | this.storedTime = storedTime; 57 | } 58 | 59 | public Date getAckTime() { 60 | return ackTime; 61 | } 62 | 63 | public void setAckTime(Date ackTime) { 64 | this.ackTime = ackTime; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /jmqtt-bus/src/main/java/org/jmqtt/bus/model/DeviceMessage.java: -------------------------------------------------------------------------------- 1 | 2 | package org.jmqtt.bus.model; 3 | 4 | import org.jmqtt.bus.enums.MessageSourceEnum; 5 | 6 | import java.util.Date; 7 | import java.util.Map; 8 | 9 | public class DeviceMessage { 10 | 11 | private Long id; 12 | 13 | private byte[] content; 14 | 15 | private MessageSourceEnum source; 16 | 17 | private String fromClientId; 18 | 19 | private Date storedTime; 20 | 21 | private String topic; 22 | 23 | private Map properties; 24 | 25 | public T getProperty(String key){ 26 | if (properties == null || properties.containsKey(key)) { 27 | return (T) properties.get(key); 28 | } 29 | return null; 30 | } 31 | 32 | public Long getId() { 33 | return id; 34 | } 35 | 36 | public void setId(Long id) { 37 | this.id = id; 38 | } 39 | 40 | public byte[] getContent() { 41 | return content; 42 | } 43 | 44 | public void setContent(byte[] content) { 45 | this.content = content; 46 | } 47 | 48 | public MessageSourceEnum getSource() { 49 | return source; 50 | } 51 | 52 | public void setSource(MessageSourceEnum source) { 53 | this.source = source; 54 | } 55 | 56 | public Date getStoredTime() { 57 | return storedTime; 58 | } 59 | 60 | public void setStoredTime(Date storedTime) { 61 | this.storedTime = storedTime; 62 | } 63 | 64 | public String getTopic() { 65 | return topic; 66 | } 67 | 68 | public void setTopic(String topic) { 69 | this.topic = topic; 70 | } 71 | 72 | public Map getProperties() { 73 | return properties; 74 | } 75 | 76 | public void setProperties(Map properties) { 77 | this.properties = properties; 78 | } 79 | 80 | public String getFromClientId() { 81 | return fromClientId; 82 | } 83 | 84 | public void setFromClientId(String fromClientId) { 85 | this.fromClientId = fromClientId; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /jmqtt-bus/src/main/java/org/jmqtt/bus/model/DeviceSession.java: -------------------------------------------------------------------------------- 1 | 2 | package org.jmqtt.bus.model; 3 | 4 | import org.jmqtt.bus.enums.DeviceOnlineStateEnum; 5 | import org.jmqtt.bus.enums.TransportProtocolEnum; 6 | 7 | import java.util.Date; 8 | import java.util.Map; 9 | 10 | /** 11 | * session 12 | */ 13 | public class DeviceSession { 14 | 15 | private String clientId; 16 | 17 | private TransportProtocolEnum transportProtocol; 18 | 19 | private String clientIp; 20 | 21 | private String serverIp; 22 | 23 | private DeviceOnlineStateEnum online; 24 | 25 | private Date onlineTime; 26 | 27 | private Date lastOfflineTime; 28 | 29 | private Map properties; 30 | 31 | public String getClientId() { 32 | return clientId; 33 | } 34 | 35 | public void setClientId(String clientId) { 36 | this.clientId = clientId; 37 | } 38 | 39 | public TransportProtocolEnum getTransportProtocol() { 40 | return transportProtocol; 41 | } 42 | 43 | public void setTransportProtocol(TransportProtocolEnum transportProtocol) { 44 | this.transportProtocol = transportProtocol; 45 | } 46 | 47 | public String getClientIp() { 48 | return clientIp; 49 | } 50 | 51 | public void setClientIp(String clientIp) { 52 | this.clientIp = clientIp; 53 | } 54 | 55 | public String getServerIp() { 56 | return serverIp; 57 | } 58 | 59 | public void setServerIp(String serverIp) { 60 | this.serverIp = serverIp; 61 | } 62 | 63 | public DeviceOnlineStateEnum getOnline() { 64 | return online; 65 | } 66 | 67 | public void setOnline(DeviceOnlineStateEnum online) { 68 | this.online = online; 69 | } 70 | 71 | public Map getProperties() { 72 | return properties; 73 | } 74 | 75 | public void setProperties(Map properties) { 76 | this.properties = properties; 77 | } 78 | 79 | public Date getOnlineTime() { 80 | return onlineTime; 81 | } 82 | 83 | public void setOnlineTime(Date onlineTime) { 84 | this.onlineTime = onlineTime; 85 | } 86 | 87 | public Date getLastOfflineTime() { 88 | return lastOfflineTime; 89 | } 90 | 91 | public void setLastOfflineTime(Date lastOfflineTime) { 92 | this.lastOfflineTime = lastOfflineTime; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /jmqtt-bus/src/main/java/org/jmqtt/bus/model/DeviceSubscription.java: -------------------------------------------------------------------------------- 1 | package org.jmqtt.bus.model; 2 | 3 | import java.util.Date; 4 | import java.util.Map; 5 | 6 | public class DeviceSubscription { 7 | 8 | private String clientId; 9 | 10 | private String topic; 11 | 12 | private Date subscribeTime; 13 | 14 | private Map properties; 15 | 16 | 17 | public T getProperty(String key){ 18 | if (properties == null) { 19 | return null; 20 | } 21 | return (T) properties.get(key); 22 | } 23 | 24 | public String getClientId() { 25 | return clientId; 26 | } 27 | 28 | public void setClientId(String clientId) { 29 | this.clientId = clientId; 30 | } 31 | 32 | public String getTopic() { 33 | return topic; 34 | } 35 | 36 | public void setTopic(String topic) { 37 | this.topic = topic; 38 | } 39 | 40 | public Date getSubscribeTime() { 41 | return subscribeTime; 42 | } 43 | 44 | public void setSubscribeTime(Date subscribeTime) { 45 | this.subscribeTime = subscribeTime; 46 | } 47 | 48 | public Map getProperties() { 49 | return properties; 50 | } 51 | 52 | public void setProperties(Map properties) { 53 | this.properties = properties; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /jmqtt-bus/src/main/java/org/jmqtt/bus/store/AbstractDBStore.java: -------------------------------------------------------------------------------- 1 | package org.jmqtt.bus.store; 2 | 3 | import org.apache.ibatis.session.SqlSession; 4 | import org.jmqtt.bus.store.mapper.*; 5 | import org.jmqtt.support.config.BrokerConfig; 6 | import org.jmqtt.support.log.JmqttLogger; 7 | import org.slf4j.Logger; 8 | 9 | public abstract class AbstractDBStore { 10 | 11 | 12 | 13 | protected final static Logger log = JmqttLogger.storeLog; 14 | 15 | protected void start(BrokerConfig brokerConfig) { 16 | DBUtils.getInstance().start(brokerConfig); 17 | } 18 | 19 | protected void shutdown() { 20 | DBUtils.getInstance().shutdown(); 21 | } 22 | 23 | protected T getMapper(SqlSession sqlSession,Class clazz) { 24 | return sqlSession.getMapper(clazz); 25 | } 26 | 27 | protected Object operate(DBCallback dbCallback){ 28 | return DBUtils.getInstance().operate(dbCallback); 29 | } 30 | 31 | public SqlSession getSqlSessionWithTrans() { 32 | return DBUtils.getInstance().getSqlSessionWithTrans(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /jmqtt-bus/src/main/java/org/jmqtt/bus/store/DBCallback.java: -------------------------------------------------------------------------------- 1 | package org.jmqtt.bus.store; 2 | 3 | import org.apache.ibatis.session.SqlSession; 4 | 5 | public interface DBCallback { 6 | T operate(SqlSession sqlSession); 7 | } 8 | -------------------------------------------------------------------------------- /jmqtt-bus/src/main/java/org/jmqtt/bus/store/DBUtils.java: -------------------------------------------------------------------------------- 1 | package org.jmqtt.bus.store; 2 | 3 | import com.alibaba.druid.pool.DruidDataSource; 4 | import org.apache.ibatis.datasource.DataSourceFactory; 5 | import org.apache.ibatis.mapping.Environment; 6 | import org.apache.ibatis.session.Configuration; 7 | import org.apache.ibatis.session.SqlSession; 8 | import org.apache.ibatis.session.SqlSessionFactory; 9 | import org.apache.ibatis.session.SqlSessionFactoryBuilder; 10 | import org.apache.ibatis.transaction.TransactionFactory; 11 | import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory; 12 | import org.jmqtt.bus.store.mapper.*; 13 | import org.jmqtt.support.config.BrokerConfig; 14 | import org.jmqtt.support.log.JmqttLogger; 15 | import org.jmqtt.support.log.LogUtil; 16 | import org.slf4j.Logger; 17 | 18 | import javax.sql.DataSource; 19 | import java.sql.SQLException; 20 | import java.util.Properties; 21 | import java.util.concurrent.atomic.AtomicBoolean; 22 | 23 | /** 24 | * db 工具类 25 | */ 26 | public class DBUtils { 27 | 28 | private static final Logger log = JmqttLogger.storeLog; 29 | 30 | public static final Class sessionMapperClass = SessionMapper.class; 31 | public static final Class subscriptionMapperClass = SubscriptionMapper.class; 32 | public static final Class eventMapperClass = EventMapper.class; 33 | public static final Class clientInboxMessageMapperClass = ClientInboxMessageMapper.class; 34 | public static final Class messageMapperClass = MessageMapper.class; 35 | public static final Class retainMessageMapperClass = RetainMessageMapper.class; 36 | 37 | private static final DBUtils dbUtils = new DBUtils(); 38 | 39 | private DBUtils(){} 40 | 41 | private SqlSessionFactory sqlSessionFactory; 42 | 43 | private AtomicBoolean start = new AtomicBoolean(false); 44 | 45 | public static DBUtils getInstance(){ 46 | return dbUtils; 47 | } 48 | 49 | public void start(BrokerConfig brokerConfig){ 50 | if (this.start.compareAndSet(false,true)) { 51 | LogUtil.info(log,"DB store start..."); 52 | DataSource dataSource = new DataSourceFactory() { 53 | @Override 54 | public void setProperties(Properties properties) { 55 | } 56 | 57 | @Override 58 | public DataSource getDataSource() { 59 | DruidDataSource dds = new DruidDataSource(); 60 | dds.setDriverClassName(brokerConfig.getDriver()); 61 | dds.setUrl(brokerConfig.getUrl()); 62 | dds.setUsername(brokerConfig.getUsername()); 63 | dds.setPassword(brokerConfig.getPassword()); 64 | // 其他配置可自行补充 65 | dds.setKeepAlive(true); 66 | dds.setMinEvictableIdleTimeMillis(180000); 67 | dds.setMaxWait(10*1000); 68 | dds.setInitialSize(5); 69 | dds.setMinIdle(5); 70 | try { 71 | dds.init(); 72 | } catch (SQLException e) { 73 | e.printStackTrace(); 74 | System.exit(-1); 75 | } 76 | return dds; 77 | } 78 | }.getDataSource(); 79 | 80 | TransactionFactory transactionFactory = new JdbcTransactionFactory(); 81 | Environment environment = new Environment("development", transactionFactory, dataSource); 82 | Configuration configuration = new Configuration(environment); 83 | 84 | // 初始化所有mapper 85 | configuration.addMapper(SessionMapper.class); 86 | configuration.addMapper(SubscriptionMapper.class); 87 | configuration.addMapper(MessageMapper.class); 88 | configuration.addMapper(EventMapper.class); 89 | configuration.addMapper(ClientInboxMessageMapper.class); 90 | configuration.addMapper(RetainMessageMapper.class); 91 | 92 | configuration.setMapUnderscoreToCamelCase(true); 93 | this.sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration); 94 | LogUtil.info(log,"DB store start success..."); 95 | } 96 | } 97 | 98 | public void shutdown(){} 99 | 100 | 101 | public static final T operate(DBCallback dbCallback) { 102 | try (SqlSession sqlSession = DBUtils.getInstance().sqlSessionFactory.openSession(true)){ 103 | return dbCallback.operate(sqlSession); 104 | } 105 | } 106 | 107 | public static final T getMapper(SqlSession sqlSession,Class clazz) { 108 | return sqlSession.getMapper(clazz); 109 | } 110 | 111 | /** 112 | * 获取关闭事物的session,需要手动提交事物 113 | */ 114 | public static final SqlSession getSqlSessionWithTrans() { 115 | SqlSession sqlSession = DBUtils.getInstance().sqlSessionFactory.openSession(false); 116 | return sqlSession; 117 | } 118 | 119 | 120 | } 121 | -------------------------------------------------------------------------------- /jmqtt-bus/src/main/java/org/jmqtt/bus/store/daoobject/DeviceInboxMessageDO.java: -------------------------------------------------------------------------------- 1 | package org.jmqtt.bus.store.daoobject; 2 | 3 | import java.io.Serializable; 4 | import java.util.Date; 5 | 6 | public class DeviceInboxMessageDO implements Serializable { 7 | 8 | private static final long serialVersionUID = 143413131231231L; 9 | 10 | private Long id; 11 | 12 | private String clientId; 13 | 14 | private Long messageId; 15 | 16 | private Integer ack; 17 | 18 | private Date storedTime; 19 | 20 | private Date ackTime; 21 | 22 | public Long getId() { 23 | return id; 24 | } 25 | 26 | public void setId(Long id) { 27 | this.id = id; 28 | } 29 | 30 | public String getClientId() { 31 | return clientId; 32 | } 33 | 34 | public void setClientId(String clientId) { 35 | this.clientId = clientId; 36 | } 37 | 38 | public Long getMessageId() { 39 | return messageId; 40 | } 41 | 42 | public void setMessageId(Long messageId) { 43 | this.messageId = messageId; 44 | } 45 | 46 | public Integer getAck() { 47 | return ack; 48 | } 49 | 50 | public void setAck(Integer ack) { 51 | this.ack = ack; 52 | } 53 | 54 | public Date getStoredTime() { 55 | return storedTime; 56 | } 57 | 58 | public void setStoredTime(Date storedTime) { 59 | this.storedTime = storedTime; 60 | } 61 | 62 | public Date getAckTime() { 63 | return ackTime; 64 | } 65 | 66 | public void setAckTime(Date ackTime) { 67 | this.ackTime = ackTime; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /jmqtt-bus/src/main/java/org/jmqtt/bus/store/daoobject/EventDO.java: -------------------------------------------------------------------------------- 1 | package org.jmqtt.bus.store.daoobject; 2 | 3 | import java.io.Serializable; 4 | import java.util.Date; 5 | 6 | public class EventDO implements Serializable { 7 | 8 | private static final long serialVersionUID = 12213213131231231L; 9 | 10 | private Long id; 11 | 12 | private String content; 13 | 14 | private Date gmtCreate; 15 | 16 | private String nodeIp; 17 | 18 | private String eventCode; 19 | 20 | public Long getId() { 21 | return id; 22 | } 23 | 24 | public void setId(Long id) { 25 | this.id = id; 26 | } 27 | 28 | public String getContent() { 29 | return content; 30 | } 31 | 32 | public void setContent(String content) { 33 | this.content = content; 34 | } 35 | 36 | public Date getGmtCreate() { 37 | return gmtCreate; 38 | } 39 | 40 | public void setGmtCreate(Date gmtCreate) { 41 | this.gmtCreate = gmtCreate; 42 | } 43 | 44 | public String getNodeIp() { 45 | return nodeIp; 46 | } 47 | 48 | public void setNodeIp(String nodeIp) { 49 | this.nodeIp = nodeIp; 50 | } 51 | 52 | public String getEventCode() { 53 | return eventCode; 54 | } 55 | 56 | public void setEventCode(String eventCode) { 57 | this.eventCode = eventCode; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /jmqtt-bus/src/main/java/org/jmqtt/bus/store/daoobject/MessageDO.java: -------------------------------------------------------------------------------- 1 | package org.jmqtt.bus.store.daoobject; 2 | 3 | import java.io.Serializable; 4 | import java.util.Date; 5 | 6 | public class MessageDO implements Serializable { 7 | 8 | private static final long serialVersionUID = 12313131231231L; 9 | 10 | private Long id; 11 | 12 | private String source; 13 | 14 | private byte[] content; 15 | 16 | private String topic; 17 | 18 | private String fromClientId; 19 | 20 | private Date storedTime; 21 | 22 | private String properties; 23 | 24 | public Long getId() { 25 | return id; 26 | } 27 | 28 | public void setId(Long id) { 29 | this.id = id; 30 | } 31 | 32 | public String getSource() { 33 | return source; 34 | } 35 | 36 | public void setSource(String source) { 37 | this.source = source; 38 | } 39 | 40 | public byte[] getContent() { 41 | return content; 42 | } 43 | 44 | public void setContent(byte[] content) { 45 | this.content = content; 46 | } 47 | 48 | public String getTopic() { 49 | return topic; 50 | } 51 | 52 | public void setTopic(String topic) { 53 | this.topic = topic; 54 | } 55 | 56 | public String getFromClientId() { 57 | return fromClientId; 58 | } 59 | 60 | public void setFromClientId(String fromClientId) { 61 | this.fromClientId = fromClientId; 62 | } 63 | 64 | public Date getStoredTime() { 65 | return storedTime; 66 | } 67 | 68 | public void setStoredTime(Date storedTime) { 69 | this.storedTime = storedTime; 70 | } 71 | 72 | public String getProperties() { 73 | return properties; 74 | } 75 | 76 | public void setProperties(String properties) { 77 | this.properties = properties; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /jmqtt-bus/src/main/java/org/jmqtt/bus/store/daoobject/RetainMessageDO.java: -------------------------------------------------------------------------------- 1 | package org.jmqtt.bus.store.daoobject; 2 | 3 | import java.io.Serializable; 4 | import java.util.Date; 5 | 6 | public class RetainMessageDO implements Serializable { 7 | 8 | private static final long serialVersionUID = 12213131231231L; 9 | 10 | private Long id; 11 | 12 | private String topic; 13 | 14 | private byte[] content; 15 | 16 | private String fromClientId; 17 | 18 | private Date storedTime; 19 | 20 | private String properties; 21 | 22 | public Long getId() { 23 | return id; 24 | } 25 | 26 | public void setId(Long id) { 27 | this.id = id; 28 | } 29 | 30 | public String getTopic() { 31 | return topic; 32 | } 33 | 34 | public void setTopic(String topic) { 35 | this.topic = topic; 36 | } 37 | 38 | public byte[] getContent() { 39 | return content; 40 | } 41 | 42 | public void setContent(byte[] content) { 43 | this.content = content; 44 | } 45 | 46 | public String getFromClientId() { 47 | return fromClientId; 48 | } 49 | 50 | public void setFromClientId(String fromClientId) { 51 | this.fromClientId = fromClientId; 52 | } 53 | 54 | public Date getStoredTime() { 55 | return storedTime; 56 | } 57 | 58 | public void setStoredTime(Date storedTime) { 59 | this.storedTime = storedTime; 60 | } 61 | 62 | public String getProperties() { 63 | return properties; 64 | } 65 | 66 | public void setProperties(String properties) { 67 | this.properties = properties; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /jmqtt-bus/src/main/java/org/jmqtt/bus/store/daoobject/SessionDO.java: -------------------------------------------------------------------------------- 1 | 2 | package org.jmqtt.bus.store.daoobject; 3 | 4 | import java.io.Serializable; 5 | import java.util.Date; 6 | 7 | public class SessionDO implements Serializable { 8 | 9 | private static final long serialVersionUID = 12213131231231L; 10 | 11 | private Long id; 12 | 13 | private String clientId; 14 | 15 | private String online; 16 | 17 | private String transportProtocol; 18 | 19 | private String clientIp; 20 | 21 | private String serverIp; 22 | 23 | private Date lastOfflineTime; 24 | 25 | private Date onlineTime; 26 | 27 | private String properties; 28 | 29 | public Long getId() { 30 | return id; 31 | } 32 | 33 | public void setId(Long id) { 34 | this.id = id; 35 | } 36 | 37 | public String getClientId() { 38 | return clientId; 39 | } 40 | 41 | public void setClientId(String clientId) { 42 | this.clientId = clientId; 43 | } 44 | 45 | public String getOnline() { 46 | return online; 47 | } 48 | 49 | public void setOnline(String online) { 50 | this.online = online; 51 | } 52 | 53 | public String getTransportProtocol() { 54 | return transportProtocol; 55 | } 56 | 57 | public void setTransportProtocol(String transportProtocol) { 58 | this.transportProtocol = transportProtocol; 59 | } 60 | 61 | public String getClientIp() { 62 | return clientIp; 63 | } 64 | 65 | public void setClientIp(String clientIp) { 66 | this.clientIp = clientIp; 67 | } 68 | 69 | public String getServerIp() { 70 | return serverIp; 71 | } 72 | 73 | public void setServerIp(String serverIp) { 74 | this.serverIp = serverIp; 75 | } 76 | 77 | public Date getLastOfflineTime() { 78 | return lastOfflineTime; 79 | } 80 | 81 | public void setLastOfflineTime(Date lastOfflineTime) { 82 | this.lastOfflineTime = lastOfflineTime; 83 | } 84 | 85 | public Date getOnlineTime() { 86 | return onlineTime; 87 | } 88 | 89 | public void setOnlineTime(Date onlineTime) { 90 | this.onlineTime = onlineTime; 91 | } 92 | 93 | public String getProperties() { 94 | return properties; 95 | } 96 | 97 | public void setProperties(String properties) { 98 | this.properties = properties; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /jmqtt-bus/src/main/java/org/jmqtt/bus/store/daoobject/SubscriptionDO.java: -------------------------------------------------------------------------------- 1 | package org.jmqtt.bus.store.daoobject; 2 | 3 | import java.io.Serializable; 4 | import java.util.Date; 5 | 6 | public class SubscriptionDO implements Serializable { 7 | 8 | private static final long serialVersionUID = 12213131231231L; 9 | 10 | private Long id; 11 | 12 | private String clientId; 13 | 14 | private String topic; 15 | 16 | private Date subscribeTime; 17 | 18 | private String properties; 19 | 20 | public Long getId() { 21 | return id; 22 | } 23 | 24 | public void setId(Long id) { 25 | this.id = id; 26 | } 27 | 28 | public String getClientId() { 29 | return clientId; 30 | } 31 | 32 | public void setClientId(String clientId) { 33 | this.clientId = clientId; 34 | } 35 | 36 | public String getTopic() { 37 | return topic; 38 | } 39 | 40 | public void setTopic(String topic) { 41 | this.topic = topic; 42 | } 43 | 44 | public Date getSubscribeTime() { 45 | return subscribeTime; 46 | } 47 | 48 | public void setSubscribeTime(Date subscribeTime) { 49 | this.subscribeTime = subscribeTime; 50 | } 51 | 52 | public String getProperties() { 53 | return properties; 54 | } 55 | 56 | public void setProperties(String properties) { 57 | this.properties = properties; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /jmqtt-bus/src/main/java/org/jmqtt/bus/store/mapper/ClientInboxMessageMapper.java: -------------------------------------------------------------------------------- 1 | package org.jmqtt.bus.store.mapper; 2 | 3 | import org.apache.ibatis.annotations.*; 4 | import org.jmqtt.bus.store.daoobject.DeviceInboxMessageDO; 5 | 6 | import java.util.List; 7 | 8 | public interface ClientInboxMessageMapper { 9 | 10 | @Insert("INSERT INTO jmqtt_client_inbox(client_id,message_id,ack,stored_time) VALUES (#{clientId},#{messageId},#{ack},#{storedTime})") 11 | @Options(useGeneratedKeys=true,keyProperty="id") 12 | Long addInboxMessage(DeviceInboxMessageDO deviceInboxMessageDO); 13 | 14 | @Update("UPDATE jmqtt_client_inbox set ack = 1,ack_time = now()" 15 | + "WHERE client_id = #{clientId} and message_id = #{messageId}") 16 | Integer ack(@Param("clientId") String clientId,@Param("messageId") Long messageId); 17 | 18 | 19 | @Update("DELETE FROM jmqtt_client_inbox WHERE client_id = #{clientId} AND ack = 0") 20 | Integer truncateUnAck(@Param("clientId") String clientId); 21 | 22 | @Select("SELECT * FROM jmqtt_client_inbox WHERE client_id = #{clientId} and ack = 0 ORDER BY stored_time ASC LIMIT #{limit} ") 23 | List getUnAckMessages(@Param("clientId") String clientId, @Param("limit") int limit); 24 | } 25 | -------------------------------------------------------------------------------- /jmqtt-bus/src/main/java/org/jmqtt/bus/store/mapper/EventMapper.java: -------------------------------------------------------------------------------- 1 | package org.jmqtt.bus.store.mapper; 2 | 3 | import org.apache.ibatis.annotations.Insert; 4 | import org.apache.ibatis.annotations.Options; 5 | import org.apache.ibatis.annotations.Param; 6 | import org.apache.ibatis.annotations.Select; 7 | import org.jmqtt.bus.store.daoobject.EventDO; 8 | 9 | import java.util.List; 10 | 11 | public interface EventMapper { 12 | 13 | @Insert("insert into jmqtt_cluster_event (content,gmt_create,node_ip,event_code) values " 14 | + "(#{content},#{gmtCreate},#{nodeIp},#{eventCode})") 15 | @Options(useGeneratedKeys=true,keyProperty="id") 16 | Long sendEvent(EventDO eventDO); 17 | 18 | 19 | @Select("select id,content,gmt_create,node_ip,event_code from jmqtt_cluster_event " 20 | + "where id > #{offset} order by id asc limit #{maxNum}") 21 | List consumeEvent(@Param("offset") long offset, @Param("maxNum") int maxNum); 22 | 23 | @Select("SELECT max(id) FROM jmqtt_cluster_event") 24 | Long getMaxOffset(); 25 | } 26 | -------------------------------------------------------------------------------- /jmqtt-bus/src/main/java/org/jmqtt/bus/store/mapper/MessageMapper.java: -------------------------------------------------------------------------------- 1 | package org.jmqtt.bus.store.mapper; 2 | 3 | import org.apache.ibatis.annotations.Insert; 4 | import org.apache.ibatis.annotations.Options; 5 | import org.apache.ibatis.annotations.Param; 6 | import org.apache.ibatis.annotations.Select; 7 | import org.jmqtt.bus.store.daoobject.MessageDO; 8 | 9 | import java.util.List; 10 | 11 | public interface MessageMapper { 12 | 13 | @Insert("INSERT INTO jmqtt_message(id,source,content,topic,from_client_id,stored_time,properties) VALUES(#{id},#{source},#{content}" 14 | + ",#{topic},#{fromClientId},#{storedTime},#{properties})") 15 | @Options(useGeneratedKeys=true,keyProperty="id") 16 | Long storeMessage(MessageDO messageDO); 17 | 18 | @Select("SELECT id,source,content,topic,from_client_id,stored_time,properties FROM jmqtt_message WHERE id = #{id}") 19 | MessageDO getMessage(@Param("id") long id); 20 | 21 | @Select("") 27 | List queryMessageByIds(@Param("ids")List ids); 28 | } 29 | -------------------------------------------------------------------------------- /jmqtt-bus/src/main/java/org/jmqtt/bus/store/mapper/RetainMessageMapper.java: -------------------------------------------------------------------------------- 1 | package org.jmqtt.bus.store.mapper; 2 | 3 | import org.apache.ibatis.annotations.Delete; 4 | import org.apache.ibatis.annotations.Insert; 5 | import org.apache.ibatis.annotations.Options; 6 | import org.apache.ibatis.annotations.Select; 7 | import org.jmqtt.bus.store.daoobject.RetainMessageDO; 8 | 9 | import java.util.List; 10 | 11 | public interface RetainMessageMapper { 12 | 13 | @Insert("INSERT INTO jmqtt_retain_message(topic,content,from_client_id,stored_time,properties) VALUES" 14 | + "(#{topic},#{content},#{fromClientId},#{storedTime},#{properties})" 15 | + " on DUPLICATE key update content = #{content},from_client_id = #{fromClientId},stored_time=#{storedTime},properties = #{properties}") 16 | @Options(useGeneratedKeys=true,keyProperty="id") 17 | Long storeRetainMessage(RetainMessageDO retainMessageDO); 18 | 19 | @Select("SELECT topic,content,from_client_id,stored_time,properties FROM jmqtt_retain_message") 20 | List getAllRetainMessage(); 21 | 22 | @Delete("DELETE FROM jmqtt_retain_message WHERE topic = #{topic}") 23 | Integer delRetainMessage(String topic); 24 | } 25 | -------------------------------------------------------------------------------- /jmqtt-bus/src/main/java/org/jmqtt/bus/store/mapper/SessionMapper.java: -------------------------------------------------------------------------------- 1 | 2 | package org.jmqtt.bus.store.mapper; 3 | 4 | import org.apache.ibatis.annotations.Insert; 5 | import org.apache.ibatis.annotations.Options; 6 | import org.apache.ibatis.annotations.Select; 7 | import org.apache.ibatis.annotations.Update; 8 | import org.jmqtt.bus.store.daoobject.SessionDO; 9 | 10 | public interface SessionMapper { 11 | 12 | @Select("select client_id,online,transport_protocol,client_ip,server_ip,last_offline_time,online_time,properties from jmqtt_session where client_id = #{clientId}") 13 | SessionDO getSession(String clientId); 14 | 15 | @Insert("insert into jmqtt_session(client_id,online,transport_protocol,client_ip,server_ip,last_offline_time,online_time,properties) values " 16 | + "(#{clientId},#{online},#{transportProtocol},#{clientIp},#{serverIp},#{lastOfflineTime},#{onlineTime},#{properties}) " 17 | + "on DUPLICATE key update online = #{online},last_offline_time = #{lastOfflineTime},online_time = #{onlineTime}" 18 | + ",client_ip=#{clientIp},server_ip=#{serverIp},transport_protocol=#{transportProtocol}") 19 | @Options(useGeneratedKeys=true,keyProperty="id") 20 | Long storeSession(SessionDO sessionDO); 21 | 22 | @Update("update jmqtt_session set last_offline_time = #{lastOfflineTime},online = #{online} where " 23 | + "client_id = #{clientId}") 24 | Long offline(SessionDO sessionDO); 25 | 26 | } 27 | -------------------------------------------------------------------------------- /jmqtt-bus/src/main/java/org/jmqtt/bus/store/mapper/SubscriptionMapper.java: -------------------------------------------------------------------------------- 1 | package org.jmqtt.bus.store.mapper; 2 | 3 | import org.apache.ibatis.annotations.*; 4 | import org.jmqtt.bus.store.daoobject.SubscriptionDO; 5 | 6 | import java.util.List; 7 | 8 | public interface SubscriptionMapper { 9 | 10 | @Insert("INSERT INTO jmqtt_subscription (client_id,topic,properties,subscribe_time) " 11 | + "VALUES (#{clientId},#{topic},#{properties},#{subscribeTime}) on duplicate key update properties = #{properties},subscribe_time=#{subscribeTime}") 12 | @Options(useGeneratedKeys=true,keyProperty="id") 13 | Long storeSubscription(SubscriptionDO subscriptionDO); 14 | 15 | @Delete("DELETE FROM jmqtt_subscription WHERE client_id = #{clientId}") 16 | Integer clearSubscription(String clientId); 17 | 18 | @Delete("DELETE FROM jmqtt_subscription WHERE client_id = #{clientId} AND topic = #{topic}") 19 | Integer delSubscription(@Param("clientId") String clientId, @Param("topic") String topic); 20 | 21 | @Select("SELECT id,client_id,topic,properties,subscribe_time FROM jmqtt_subscription WHERE client_id = #{clientId}") 22 | List getAllSubscription(String clientId); 23 | } 24 | -------------------------------------------------------------------------------- /jmqtt-bus/src/main/java/org/jmqtt/bus/subscription/CNode.java: -------------------------------------------------------------------------------- 1 | package org.jmqtt.bus.subscription; 2 | 3 | import org.jmqtt.bus.subscription.model.Subscription; 4 | 5 | import java.util.*; 6 | 7 | public class CNode { 8 | 9 | private Token token; 10 | private List children; 11 | Set subscriptions; 12 | 13 | CNode() { 14 | this.children = new ArrayList<>(); 15 | this.subscriptions = new HashSet<>(); 16 | } 17 | 18 | //Copy constructor 19 | private CNode(Token token, List children, Set subscriptions) { 20 | this.token = token; // keep reference, root comparison in directory logic relies on it for now. 21 | this.subscriptions = new HashSet<>(subscriptions); 22 | this.children = new ArrayList<>(children); 23 | } 24 | 25 | public Token getToken() { 26 | return token; 27 | } 28 | 29 | public void setToken(Token token) { 30 | this.token = token; 31 | } 32 | 33 | boolean anyChildrenMatch(Token token) { 34 | for (INode iNode : children) { 35 | final CNode child = iNode.mainNode(); 36 | if (child.equalsToken(token)) { 37 | return true; 38 | } 39 | } 40 | return false; 41 | } 42 | 43 | List allChildren() { 44 | return this.children; 45 | } 46 | 47 | INode childOf(Token token) { 48 | for (INode iNode : children) { 49 | final CNode child = iNode.mainNode(); 50 | if (child.equalsToken(token)) { 51 | return iNode; 52 | } 53 | } 54 | throw new IllegalArgumentException("Asked for a token that doesn't exists in any child [" + token + "]"); 55 | } 56 | 57 | private boolean equalsToken(Token token) { 58 | return token != null && this.token != null && this.token.equals(token); 59 | } 60 | 61 | @Override 62 | public int hashCode() { 63 | return Objects.hash(token); 64 | } 65 | 66 | CNode copy() { 67 | return new CNode(this.token, this.children, this.subscriptions); 68 | } 69 | 70 | public void add(INode newINode) { 71 | this.children.add(newINode); 72 | } 73 | 74 | public void remove(INode node) { 75 | this.children.remove(node); 76 | } 77 | 78 | CNode addSubscription(Subscription newSubscription) { 79 | // if already contains one with same topic and same client, keep that with higher QoS 80 | if (subscriptions.contains(newSubscription)) { 81 | final Subscription existing = subscriptions.stream() 82 | .filter(s -> s.equals(newSubscription)) 83 | .findFirst().get(); 84 | subscriptions.remove(existing); 85 | subscriptions.add(new Subscription(newSubscription)); 86 | } else { 87 | this.subscriptions.add(new Subscription(newSubscription)); 88 | } 89 | return this; 90 | } 91 | 92 | boolean containsOnly(String clientId) { 93 | for (Subscription sub : this.subscriptions) { 94 | if (!sub.getClientId().equals(clientId)) { 95 | return false; 96 | } 97 | } 98 | return !this.subscriptions.isEmpty(); 99 | } 100 | 101 | public boolean contains(String clientId) { 102 | for (Subscription sub : this.subscriptions) { 103 | if (sub.getClientId().equals(clientId)) { 104 | return true; 105 | } 106 | } 107 | return false; 108 | } 109 | 110 | void removeSubscriptionsFor(String clientId) { 111 | Set toRemove = new HashSet<>(); 112 | for (Subscription sub : this.subscriptions) { 113 | if (sub.getClientId().equals(clientId)) { 114 | toRemove.add(sub); 115 | } 116 | } 117 | this.subscriptions.removeAll(toRemove); 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /jmqtt-bus/src/main/java/org/jmqtt/bus/subscription/CTrieSubscriptionMatcher.java: -------------------------------------------------------------------------------- 1 | package org.jmqtt.bus.subscription; 2 | 3 | import org.jmqtt.bus.subscription.model.Subscription; 4 | import org.jmqtt.support.log.JmqttLogger; 5 | import org.slf4j.Logger; 6 | 7 | import java.util.Set; 8 | 9 | public class CTrieSubscriptionMatcher implements SubscriptionMatcher { 10 | 11 | private static final Logger log = JmqttLogger.busLog; 12 | 13 | private CTrie ctrie; 14 | 15 | public CTrieSubscriptionMatcher(){ 16 | this.ctrie = new CTrie(); 17 | } 18 | 19 | @Override 20 | public Set match(String topicFilter) { 21 | Topic topic = new Topic(topicFilter); 22 | return ctrie.recursiveMatch(topic); 23 | } 24 | 25 | 26 | @Override 27 | public boolean subscribe(Subscription newSubscription) { 28 | ctrie.addToTree(newSubscription); 29 | return true; 30 | } 31 | 32 | 33 | @Override 34 | public boolean unSubscribe(String topicFilter, String clientID) { 35 | ctrie.removeFromTree(new Topic(topicFilter), clientID); 36 | return true; 37 | } 38 | 39 | @Override 40 | public int size() { 41 | return ctrie.size(); 42 | } 43 | 44 | @Override 45 | public String dumpTree() { 46 | return ctrie.dumpTree(); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /jmqtt-bus/src/main/java/org/jmqtt/bus/subscription/DumpTreeVisitor.java: -------------------------------------------------------------------------------- 1 | package org.jmqtt.bus.subscription; 2 | 3 | import io.netty.util.internal.StringUtil; 4 | import org.jmqtt.bus.subscription.model.Subscription; 5 | 6 | class DumpTreeVisitor implements CTrie.IVisitor { 7 | 8 | String s = ""; 9 | 10 | @Override 11 | public void visit(CNode node, int deep) { 12 | String indentTabs = indentTabs(deep); 13 | s += indentTabs + (node.getToken() == null ? "''" : node.getToken().toString()) + prettySubscriptions(node) + "\n"; 14 | } 15 | 16 | private String prettySubscriptions(CNode node) { 17 | if (node instanceof TNode) { 18 | return "TNode"; 19 | } 20 | if (node.subscriptions.isEmpty()) { 21 | return StringUtil.EMPTY_STRING; 22 | } 23 | StringBuilder subScriptionsStr = new StringBuilder(" ~~["); 24 | int counter = 0; 25 | for (Subscription couple : node.subscriptions) { 26 | subScriptionsStr 27 | .append("{filter=").append(couple.getTopic()).append(", ") 28 | .append("properties=").append(couple.getProperties()).append(", ") 29 | .append("client='").append(couple.getClientId()).append("'}"); 30 | counter++; 31 | if (counter < node.subscriptions.size()) { 32 | subScriptionsStr.append(";"); 33 | } 34 | } 35 | return subScriptionsStr.append("]").toString(); 36 | } 37 | 38 | private String indentTabs(int deep) { 39 | StringBuilder s = new StringBuilder(); 40 | if (deep > 0) { 41 | s.append(" "); 42 | for (int i = 0; i < deep - 1; i++) { 43 | s.append("| "); 44 | } 45 | s.append("|-"); 46 | } 47 | return s.toString(); 48 | } 49 | 50 | @Override 51 | public String getResult() { 52 | return s; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /jmqtt-bus/src/main/java/org/jmqtt/bus/subscription/INode.java: -------------------------------------------------------------------------------- 1 | package org.jmqtt.bus.subscription; 2 | 3 | import java.util.concurrent.atomic.AtomicReference; 4 | 5 | public class INode { 6 | private AtomicReference mainNode = new AtomicReference<>(); 7 | 8 | INode(CNode mainNode) { 9 | this.mainNode.set(mainNode); 10 | if (mainNode instanceof TNode) { // this should never happen 11 | throw new IllegalStateException("TNode should not be set on mainNnode"); 12 | } 13 | } 14 | 15 | boolean compareAndSet(CNode old, CNode newNode) { 16 | return mainNode.compareAndSet(old, newNode); 17 | } 18 | 19 | boolean compareAndSet(CNode old, TNode newNode) { 20 | return mainNode.compareAndSet(old, newNode); 21 | } 22 | 23 | CNode mainNode() { 24 | return this.mainNode.get(); 25 | } 26 | 27 | boolean isTombed() { 28 | return this.mainNode() instanceof TNode; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /jmqtt-bus/src/main/java/org/jmqtt/bus/subscription/SubscriptionCounterVisitor.java: -------------------------------------------------------------------------------- 1 | package org.jmqtt.bus.subscription; 2 | 3 | import java.util.concurrent.atomic.AtomicInteger; 4 | 5 | class SubscriptionCounterVisitor implements CTrie.IVisitor { 6 | 7 | private AtomicInteger accumulator = new AtomicInteger(0); 8 | 9 | @Override 10 | public void visit(CNode node, int deep) { 11 | accumulator.addAndGet(node.subscriptions.size()); 12 | } 13 | 14 | @Override 15 | public Integer getResult() { 16 | return accumulator.get(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /jmqtt-bus/src/main/java/org/jmqtt/bus/subscription/SubscriptionMatcher.java: -------------------------------------------------------------------------------- 1 | package org.jmqtt.bus.subscription; 2 | 3 | import org.jmqtt.bus.subscription.model.Subscription; 4 | 5 | import java.util.Set; 6 | 7 | /** 8 | * Subscription tree 9 | * 订阅树处理 10 | * TODO 1.支持共享订阅 11 | * TODO 2.支持 p2p消息(jmqtt 特有) 12 | * TODO 2.支持系统topic $SYS -> 需要定时采集本机节点等信息下发 13 | */ 14 | public interface SubscriptionMatcher { 15 | 16 | /** 17 | * add subscribe 18 | * @return true:new subscribe,dispatcher retain message 19 | * false:no need to dispatcher retain message 20 | */ 21 | boolean subscribe(Subscription subscription); 22 | 23 | boolean unSubscribe(String topic,String clientId); 24 | 25 | /** 26 | * 获取匹配该topic下的非共享订阅者 27 | */ 28 | Set match(String topic); 29 | 30 | 31 | int size(); 32 | 33 | String dumpTree(); 34 | 35 | } 36 | -------------------------------------------------------------------------------- /jmqtt-bus/src/main/java/org/jmqtt/bus/subscription/TNode.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2018 The original author or authors 3 | * ------------------------------------------------------ 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Apache License v2.0 which accompanies this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * 11 | * The Apache License v2.0 is available at 12 | * http://www.opensource.org/licenses/apache2.0.php 13 | * 14 | * You may elect to redistribute this code under either of these licenses. 15 | */ 16 | package org.jmqtt.bus.subscription; 17 | 18 | import org.jmqtt.bus.subscription.model.Subscription; 19 | 20 | class TNode extends CNode { 21 | 22 | @Override 23 | public Token getToken() { 24 | throw new IllegalStateException("Can't be invoked on TNode"); 25 | } 26 | 27 | @Override 28 | public void setToken(Token token) { 29 | throw new IllegalStateException("Can't be invoked on TNode"); 30 | } 31 | 32 | @Override 33 | INode childOf(Token token) { 34 | throw new IllegalStateException("Can't be invoked on TNode"); 35 | } 36 | 37 | @Override 38 | CNode copy() { 39 | throw new IllegalStateException("Can't be invoked on TNode"); 40 | } 41 | 42 | @Override 43 | public void add(INode newINode) { 44 | throw new IllegalStateException("Can't be invoked on TNode"); 45 | } 46 | 47 | @Override 48 | CNode addSubscription(Subscription newSubscription) { 49 | throw new IllegalStateException("Can't be invoked on TNode"); 50 | } 51 | 52 | @Override 53 | boolean containsOnly(String clientId) { 54 | throw new IllegalStateException("Can't be invoked on TNode"); 55 | } 56 | 57 | @Override 58 | public boolean contains(String clientId) { 59 | throw new IllegalStateException("Can't be invoked on TNode"); 60 | } 61 | 62 | @Override 63 | void removeSubscriptionsFor(String clientId) { 64 | throw new IllegalStateException("Can't be invoked on TNode"); 65 | } 66 | 67 | @Override 68 | boolean anyChildrenMatch(Token token) { 69 | return false; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /jmqtt-bus/src/main/java/org/jmqtt/bus/subscription/Token.java: -------------------------------------------------------------------------------- 1 | package org.jmqtt.bus.subscription; 2 | 3 | 4 | public class Token { 5 | 6 | public static final Token EMPTY = new Token(""); 7 | public static final Token MULTI = new Token("#"); 8 | public static final Token SINGLE = new Token("+"); 9 | final String name; 10 | 11 | public Token(String s) { 12 | name = s; 13 | } 14 | 15 | protected String name() { 16 | return name; 17 | } 18 | 19 | protected boolean match(Token t) { 20 | if (MULTI.equals(t) || SINGLE.equals(t)) { 21 | return false; 22 | } 23 | 24 | if (MULTI.equals(this) || SINGLE.equals(this)) { 25 | return true; 26 | } 27 | 28 | return equals(t); 29 | } 30 | 31 | @Override 32 | public int hashCode() { 33 | int hash = 7; 34 | hash = 29 * hash + (this.name != null ? this.name.hashCode() : 0); 35 | return hash; 36 | } 37 | 38 | @Override 39 | public boolean equals(Object obj) { 40 | if (obj == null) { 41 | return false; 42 | } 43 | if (getClass() != obj.getClass()) { 44 | return false; 45 | } 46 | final Token other = (Token) obj; 47 | if ((this.name == null) ? (other.name != null) : !this.name.equals(other.name)) { 48 | return false; 49 | } 50 | return true; 51 | } 52 | 53 | @Override 54 | public String toString() { 55 | return name; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /jmqtt-bus/src/main/java/org/jmqtt/bus/subscription/Topic.java: -------------------------------------------------------------------------------- 1 | package org.jmqtt.bus.subscription; 2 | 3 | import org.jmqtt.support.log.JmqttLogger; 4 | import org.jmqtt.support.log.LogUtil; 5 | import org.slf4j.Logger; 6 | 7 | import java.io.Serializable; 8 | import java.text.ParseException; 9 | import java.util.ArrayList; 10 | import java.util.Collections; 11 | import java.util.List; 12 | import java.util.Objects; 13 | import java.util.stream.Collectors; 14 | 15 | public class Topic implements Serializable , Comparable { 16 | 17 | private static final Logger log = JmqttLogger.busLog; 18 | 19 | private static final long serialVersionUID = 2438799283749822L; 20 | 21 | private final String topic; 22 | 23 | private transient List tokens; 24 | 25 | private transient boolean valid; 26 | 27 | /** 28 | * Factory method 29 | * 30 | * @param s the topic string (es "/a/b"). 31 | * @return the created Topic instance. 32 | * */ 33 | public static Topic asTopic(String s) { 34 | return new Topic(s); 35 | } 36 | 37 | public Topic(String topic) { 38 | this.topic = topic; 39 | } 40 | 41 | Topic(List tokens) { 42 | this.tokens = tokens; 43 | List strTokens = tokens.stream().map(Token::toString).collect(Collectors.toList()); 44 | this.topic = String.join("/", strTokens); 45 | this.valid = true; 46 | } 47 | 48 | public List getTokens() { 49 | if (tokens == null) { 50 | try { 51 | tokens = parseTopic(topic); 52 | valid = true; 53 | } catch (ParseException e) { 54 | valid = false; 55 | LogUtil.error(log,"Error parsing the topic: {}, message: {}", topic, e.getMessage()); 56 | } 57 | } 58 | 59 | return tokens; 60 | } 61 | 62 | private List parseTopic(String topic) throws ParseException { 63 | if (topic.length() == 0) { 64 | throw new ParseException("Bad format of topic, topic MUST be at least 1 character [MQTT-4.7.3-1] and " + 65 | "this was empty", 0); 66 | } 67 | List res = new ArrayList<>(); 68 | String[] splitted = topic.split("/"); 69 | 70 | if (splitted.length == 0) { 71 | res.add(Token.EMPTY); 72 | } 73 | 74 | if (topic.endsWith("/")) { 75 | // Add a fictious space 76 | String[] newSplitted = new String[splitted.length + 1]; 77 | System.arraycopy(splitted, 0, newSplitted, 0, splitted.length); 78 | newSplitted[splitted.length] = ""; 79 | splitted = newSplitted; 80 | } 81 | 82 | for (int i = 0; i < splitted.length; i++) { 83 | String s = splitted[i]; 84 | if (s.isEmpty()) { 85 | // if (i != 0) { 86 | // throw new ParseException("Bad format of topic, expetec topic name between 87 | // separators", i); 88 | // } 89 | res.add(Token.EMPTY); 90 | } else if (s.equals("#")) { 91 | // check that multi is the last symbol 92 | if (i != splitted.length - 1) { 93 | throw new ParseException( 94 | "Bad format of topic, the multi symbol (#) has to be the last one after a separator", 95 | i); 96 | } 97 | res.add(Token.MULTI); 98 | } else if (s.contains("#")) { 99 | throw new ParseException("Bad format of topic, invalid subtopic name: " + s, i); 100 | } else if (s.equals("+")) { 101 | res.add(Token.SINGLE); 102 | } else if (s.contains("+")) { 103 | throw new ParseException("Bad format of topic, invalid subtopic name: " + s, i); 104 | } else { 105 | res.add(new Token(s)); 106 | } 107 | } 108 | 109 | return res; 110 | } 111 | 112 | public Token headToken() { 113 | final List tokens = getTokens(); 114 | if (tokens.isEmpty()) { 115 | return null; 116 | } 117 | return tokens.get(0); 118 | } 119 | 120 | public boolean isEmpty() { 121 | final List tokens = getTokens(); 122 | return tokens == null || tokens.isEmpty(); 123 | } 124 | 125 | public Topic exceptHeadToken() { 126 | List tokens = getTokens(); 127 | if (tokens.isEmpty()) { 128 | return new Topic(Collections.emptyList()); 129 | } 130 | List tokensCopy = new ArrayList<>(tokens); 131 | tokensCopy.remove(0); 132 | return new Topic(tokensCopy); 133 | } 134 | 135 | public boolean isValid() { 136 | if (tokens == null) 137 | getTokens(); 138 | 139 | return valid; 140 | } 141 | 142 | public boolean match(Topic subscriptionTopic) { 143 | List msgTokens = getTokens(); 144 | List subscriptionTokens = subscriptionTopic.getTokens(); 145 | int i = 0; 146 | for (; i < subscriptionTokens.size(); i++) { 147 | Token subToken = subscriptionTokens.get(i); 148 | if (!Token.MULTI.equals(subToken) && !Token.SINGLE.equals(subToken)) { 149 | if (i >= msgTokens.size()) { 150 | return false; 151 | } 152 | Token msgToken = msgTokens.get(i); 153 | if (!msgToken.equals(subToken)) { 154 | return false; 155 | } 156 | } else { 157 | if (Token.MULTI.equals(subToken)) { 158 | return true; 159 | } 160 | } 161 | } 162 | return i == msgTokens.size(); 163 | } 164 | 165 | @Override 166 | public String toString() { 167 | return topic; 168 | } 169 | 170 | @Override 171 | public boolean equals(Object obj) { 172 | if (obj == null) { 173 | return false; 174 | } 175 | if (getClass() != obj.getClass()) { 176 | return false; 177 | } 178 | Topic other = (Topic) obj; 179 | 180 | return Objects.equals(this.topic, other.topic); 181 | } 182 | 183 | @Override 184 | public int hashCode() { 185 | return topic.hashCode(); 186 | } 187 | 188 | @Override 189 | public int compareTo(Topic o) { 190 | return topic.compareTo(o.topic); 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /jmqtt-bus/src/main/java/org/jmqtt/bus/subscription/model/Subscription.java: -------------------------------------------------------------------------------- 1 | package org.jmqtt.bus.subscription.model; 2 | 3 | import java.util.Map; 4 | import java.util.Objects; 5 | 6 | /** 7 | * 订阅关系 8 | */ 9 | public class Subscription { 10 | private String clientId; 11 | private String topic; 12 | private Map properties; 13 | 14 | public Subscription(String clientId,String topic){ 15 | this.clientId = clientId; 16 | this.topic = topic; 17 | } 18 | 19 | public Subscription(String clientId,String topic,Map properties){ 20 | this.clientId = clientId; 21 | this.topic = topic; 22 | this.properties = properties; 23 | } 24 | 25 | public Subscription(Subscription origin){ 26 | this.clientId = origin.getClientId(); 27 | this.topic = origin.getTopic(); 28 | this.properties = origin.getProperties(); 29 | } 30 | 31 | public T getProperty(String key){ 32 | if (properties == null) { 33 | return null; 34 | } 35 | return (T) properties.get(key); 36 | } 37 | 38 | 39 | public String getClientId() { 40 | return clientId; 41 | } 42 | 43 | public void setClientId(String clientId) { 44 | this.clientId = clientId; 45 | } 46 | 47 | public String getTopic() { 48 | return topic; 49 | } 50 | 51 | public void setTopic(String topic) { 52 | this.topic = topic; 53 | } 54 | 55 | public Map getProperties() { 56 | return properties; 57 | } 58 | 59 | public void setProperties(Map properties) { 60 | this.properties = properties; 61 | } 62 | 63 | @Override 64 | public boolean equals(Object o) { 65 | if (this == o) return true; 66 | if (o == null || getClass() != o.getClass()) return false; 67 | Subscription that = (Subscription) o; 68 | return Objects.equals(clientId, that.clientId) && 69 | Objects.equals(topic, that.topic); 70 | } 71 | 72 | @Override 73 | public int hashCode() { 74 | return Objects.hash(clientId, topic); 75 | } 76 | 77 | @Override 78 | public String toString() { 79 | return "Subscription{" + 80 | "clientId='" + clientId + '\'' + 81 | ", topic='" + topic + '\'' + 82 | ", properties=" + properties + 83 | '}'; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /jmqtt-bus/src/test/java/org/jmqtt/AppTest.java: -------------------------------------------------------------------------------- 1 | package org.jmqtt; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | import static org.junit.jupiter.api.Assertions.assertTrue; 6 | 7 | /** 8 | * Unit test for simple App. 9 | */ 10 | public class AppTest 11 | { 12 | /** 13 | * Rigorous Test :-) 14 | */ 15 | @Test 16 | public void shouldAnswerWithTrue() 17 | { 18 | assertTrue( true ); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /jmqtt-bus/src/test/java/org/jmqtt/bus/subscription/TopicTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2018 The original author or authors 3 | * ------------------------------------------------------ 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Apache License v2.0 which accompanies this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * 11 | * The Apache License v2.0 is available at 12 | * http://www.opensource.org/licenses/apache2.0.php 13 | * 14 | * You may elect to redistribute this code under either of these licenses. 15 | */ 16 | package org.jmqtt.bus.subscription; 17 | 18 | import org.assertj.core.api.AbstractAssert; 19 | import org.assertj.core.api.Assertions; 20 | import org.junit.jupiter.api.Test; 21 | 22 | import static org.junit.jupiter.api.Assertions.assertEquals; 23 | 24 | public class TopicTest { 25 | 26 | @Test 27 | public void testParseTopic() { 28 | assertThat(new Topic("finance/stock/ibm")).containsToken("finance", "stock", "ibm"); 29 | 30 | assertThat(new Topic("/finance/stock/ibm")).containsToken(Token.EMPTY, "finance", "stock", "ibm"); 31 | 32 | assertThat(new Topic("/")).containsToken(Token.EMPTY, Token.EMPTY); 33 | } 34 | 35 | @Test 36 | public void testParseTopicMultiValid() { 37 | assertThat(new Topic("finance/stock/#")).containsToken("finance", "stock", Token.MULTI); 38 | 39 | assertThat(new Topic("#")).containsToken(Token.MULTI); 40 | } 41 | 42 | @Test 43 | public void testValidationProcess() { 44 | // TopicMultiInTheMiddle 45 | assertThat(new Topic("finance/#/closingprice")).isInValid(); 46 | 47 | // MultiNotAfterSeparator 48 | assertThat(new Topic("finance#")).isInValid(); 49 | 50 | // TopicMultiNotAlone 51 | assertThat(new Topic("/finance/#closingprice")).isInValid(); 52 | 53 | // SingleNotAferSeparator 54 | assertThat(new Topic("finance+")).isInValid(); 55 | 56 | assertThat(new Topic("finance/+")).isValid(); 57 | } 58 | 59 | @Test 60 | public void testParseTopicSingleValid() { 61 | assertThat(new Topic("finance/stock/+")).containsToken("finance", "stock", Token.SINGLE); 62 | 63 | assertThat(new Topic("+")).containsToken(Token.SINGLE); 64 | 65 | assertThat(new Topic("finance/+/ibm")).containsToken("finance", Token.SINGLE, "ibm"); 66 | } 67 | 68 | @Test 69 | public void testMatchTopics_simple() { 70 | assertThat(new Topic("/")).matches("/"); 71 | assertThat(new Topic("/finance")).matches("/finance"); 72 | } 73 | 74 | @Test 75 | public void testMatchTopics_multi() { 76 | assertThat(new Topic("finance")).matches("#"); 77 | assertThat(new Topic("finance")).matches("finance/#"); 78 | assertThat(new Topic("finance/stock")).matches("finance/#"); 79 | assertThat(new Topic("finance/stock/ibm")).matches("finance/#"); 80 | } 81 | 82 | @Test 83 | public void testMatchTopics_single() { 84 | assertThat(new Topic("finance")).matches("+"); 85 | assertThat(new Topic("finance/stock")).matches("finance/+"); 86 | assertThat(new Topic("finance")).doesNotMatch("finance/+"); 87 | assertThat(new Topic("/finance")).matches("/+"); 88 | assertThat(new Topic("/finance")).doesNotMatch("+"); 89 | assertThat(new Topic("/finance")).matches("+/+"); 90 | assertThat(new Topic("/finance/stock/ibm")).matches("/finance/+/ibm"); 91 | assertThat(new Topic("/")).matches("+/+"); 92 | assertThat(new Topic("sport/")).matches("sport/+"); 93 | assertThat(new Topic("/finance/stock")).doesNotMatch("+"); 94 | } 95 | 96 | @Test 97 | public void rogerLightMatchTopics() { 98 | assertThat(new Topic("foo/bar")).matches("foo/bar"); 99 | assertThat(new Topic("foo/bar")).matches("foo/+"); 100 | assertThat(new Topic("foo/bar/baz")).matches("foo/+/baz"); 101 | assertThat(new Topic("foo/bar/baz")).matches("foo/+/#"); 102 | assertThat(new Topic("foo/bar/baz")).matches("#"); 103 | 104 | assertThat(new Topic("foo")).doesNotMatch("foo/bar"); 105 | assertThat(new Topic("foo/bar/baz")).doesNotMatch("foo/+"); 106 | assertThat(new Topic("foo/bar/bar")).doesNotMatch("foo/+/baz"); 107 | assertThat(new Topic("fo2/bar/baz")).doesNotMatch("foo/+/#"); 108 | 109 | assertThat(new Topic("/foo/bar")).matches("#"); 110 | assertThat(new Topic("/foo/bar")).matches("/#"); 111 | assertThat(new Topic("foo/bar")).doesNotMatch("/#"); 112 | 113 | assertThat(new Topic("foo//bar")).matches("foo//bar"); 114 | assertThat(new Topic("foo//bar")).matches("foo//+"); 115 | assertThat(new Topic("foo///baz")).matches("foo/+/+/baz"); 116 | assertThat(new Topic("foo/bar/")).matches("foo/bar/+"); 117 | } 118 | 119 | @Test 120 | public void exceptHeadToken() { 121 | assertEquals(Topic.asTopic("token"), Topic.asTopic("/token").exceptHeadToken()); 122 | assertEquals(Topic.asTopic("a/b"), Topic.asTopic("/a/b").exceptHeadToken()); 123 | } 124 | 125 | public static TopicAssert assertThat(Topic topic) { 126 | return new TopicAssert(topic); 127 | } 128 | 129 | static class TopicAssert extends AbstractAssert { 130 | 131 | TopicAssert(Topic actual) { 132 | super(actual, TopicAssert.class); 133 | } 134 | 135 | public TopicAssert matches(String topic) { 136 | Assertions.assertThat(actual.match(new Topic(topic))).isTrue(); 137 | 138 | return myself; 139 | } 140 | 141 | public TopicAssert doesNotMatch(String topic) { 142 | Assertions.assertThat(actual.match(new Topic(topic))).isFalse(); 143 | 144 | return myself; 145 | } 146 | 147 | public TopicAssert containsToken(Object... tokens) { 148 | Assertions.assertThat(actual.getTokens()).containsExactly(asArray(tokens)); 149 | 150 | return myself; 151 | } 152 | 153 | private Token[] asArray(Object... l) { 154 | Token[] tokens = new Token[l.length]; 155 | for (int i = 0; i < l.length; i++) { 156 | Object o = l[i]; 157 | if (o instanceof Token) { 158 | tokens[i] = (Token) o; 159 | } else { 160 | tokens[i] = new Token(o.toString()); 161 | } 162 | } 163 | 164 | return tokens; 165 | } 166 | 167 | public TopicAssert isValid() { 168 | Assertions.assertThat(actual.isValid()).isTrue(); 169 | 170 | return myself; 171 | } 172 | 173 | public TopicAssert isInValid() { 174 | Assertions.assertThat(actual.isValid()).isFalse(); 175 | 176 | return myself; 177 | } 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /jmqtt-doc/README.md: -------------------------------------------------------------------------------- 1 | 官网搭建指导文件 2 | 3 | 1. clone本项目 4 | 2. 安装npm或yarn,具体参考:https://www.vuepress.cn/guide/getting-started.html 5 | 3. 执行:yarn add -D vuepress 6 | 4. 本地启动:yarn dev 7 | 5. 构建:yarn build 8 | 9 | 10 | -------------------------------------------------------------------------------- /jmqtt-doc/docs/README.md: -------------------------------------------------------------------------------- 1 | ## 快速上手 2 | 3 | ### 安装主题 4 | 5 | 1. 下载 [release](https://github.com/Cicizz/jmqtt/releases)(3.x以上版本) 或`clone`本项目: 6 | 2. 在jmqtt根目录执行:: 7 | ```bash 8 | mvn -Ppackage-all -DskipTests clean install -U 9 | ``` 10 | 3. 配置相应的配置文件,初始化db的sql文件:`/jmqtt-broker/resources/conf`目录下 11 | 4. 执行启动命令:`java -jar jmqtt-broker-3.0.0.jar -h ${conf文件目录}` -h后是配置文件目录,里面需要包含jmqtt.properties和log4j2.xml等配置文件 12 | 13 | ### 测试 14 | 下载客户端:[mqtt客户端](https://mqttx.app/cn/) 15 | 或 直接使用websocket测试:`/jmqtt/jmqtt-examples` 16 | 17 | 18 | 19 | 20 |

 

21 | 22 | [我也想为贡献者之一?](https://github.com/Cicizz/jmqtt/pulls) 23 | 24 |

 

25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /jmqtt-doc/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | jmqtt 7 | org.jmqtt 8 | 3.0.0 9 | 10 | 4.0.0 11 | 12 | jmqtt-doc 13 | 14 | jmqtt-doc 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /jmqtt-example/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | jmqtt 7 | org.jmqtt 8 | 3.0.0 9 | 10 | 4.0.0 11 | 12 | jmqtt-example 13 | jmqtt-example 14 | 15 | 16 | UTF-8 17 | 1.8 18 | 1.8 19 | 20 | 21 | 22 | 23 | org.eclipse.paho 24 | org.eclipse.paho.client.mqttv3 25 | 1.2.5 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /jmqtt-example/src/main/java/org/jmqtt/java/Consumer.java: -------------------------------------------------------------------------------- 1 | package org.jmqtt.java; 2 | 3 | import org.eclipse.paho.client.mqttv3.*; 4 | import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence; 5 | 6 | public class Consumer { 7 | private static final String broker = "tcp://127.0.0.1:1883"; 8 | private static final String topic = "MQTT/TOPIC"; 9 | private static final String clientId = "MQTT_SUB_CLIENT"; 10 | 11 | public static void main(String[] args) throws MqttException { 12 | MqttClient subClient = getMqttClient(); 13 | subClient.setCallback(new MqttCallback() { 14 | @Override 15 | public void connectionLost(Throwable throwable) { 16 | System.out.println("Connect lost,do some thing to solve it"); 17 | } 18 | 19 | @Override 20 | public void messageArrived(String s, MqttMessage mqttMessage) { 21 | System.out.println("From topic: " + s); 22 | System.out.println("Message content: " + new String(mqttMessage.getPayload())); 23 | } 24 | 25 | @Override 26 | public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) { 27 | System.out.println("deliveryComplete"); 28 | } 29 | 30 | }); 31 | subClient.subscribe(topic); 32 | } 33 | 34 | 35 | private static MqttClient getMqttClient() { 36 | try { 37 | MqttClient pubClient = new MqttClient(broker, clientId, new MemoryPersistence()); 38 | MqttConnectOptions connectOptions = new MqttConnectOptions(); 39 | connectOptions.setCleanSession(false); 40 | System.out.println("Connecting to broker: " + broker); 41 | pubClient.connect(connectOptions); 42 | return pubClient; 43 | } catch (MqttException e) { 44 | e.printStackTrace(); 45 | } 46 | return null; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /jmqtt-example/src/main/java/org/jmqtt/java/Producer.java: -------------------------------------------------------------------------------- 1 | package org.jmqtt.java; 2 | 3 | import org.eclipse.paho.client.mqttv3.MqttClient; 4 | import org.eclipse.paho.client.mqttv3.MqttConnectOptions; 5 | import org.eclipse.paho.client.mqttv3.MqttException; 6 | import org.eclipse.paho.client.mqttv3.MqttMessage; 7 | import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence; 8 | 9 | public class Producer { 10 | private static final String broker = "tcp://8.142.122.137:1883"; 11 | private static final String content = "Message from MqttProducer"; 12 | private static final int qos = 1; 13 | private static final String topic = "MQTT/TOPIC"; 14 | private static final String clientId = "MQTT_PUB_CLIENT"; 15 | 16 | public static void main(String[] args) throws MqttException, InterruptedException { 17 | MqttClient pubClient = getMqttClient(); 18 | for (int i = 0; i < 3; i++) { 19 | MqttMessage mqttMessage = getMqttMessage(); 20 | pubClient.publish(topic, mqttMessage); 21 | System.out.println("Send message success."); 22 | } 23 | } 24 | 25 | private static MqttMessage getMqttMessage() { 26 | MqttMessage mqttMessage = new MqttMessage(content.getBytes()); 27 | mqttMessage.setQos(qos); 28 | return mqttMessage; 29 | } 30 | 31 | private static MqttClient getMqttClient() { 32 | try { 33 | MqttClient pubClient = new MqttClient(broker, clientId, new MemoryPersistence()); 34 | MqttConnectOptions connectOptions = new MqttConnectOptions(); 35 | connectOptions.setWill("lwt", "this is a will message".getBytes(), 1, false); 36 | connectOptions.setCleanSession(false); 37 | System.out.println("Connecting to broker: " + broker); 38 | pubClient.connect(connectOptions); 39 | return pubClient; 40 | } catch (MqttException e) { 41 | e.printStackTrace(); 42 | } 43 | return null; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /jmqtt-example/src/main/java/org/jmqtt/websocket/webSocket.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | webSocket test 6 | 7 | 8 |

Test WebSocket function

9 |
10 | server ip: 11 | 12 |
13 |
14 | webSocket port: 15 | 16 |
17 |
18 | clientId: 19 | 20 |
21 | 22 |
23 |
24 |
25 | Pub Topic: 26 | 27 |
28 |
29 | Message: 30 | 31 |
32 | 33 |
34 |
35 |
36 | Sub Topic: 37 | 38 |
39 | 40 | 41 |
42 | 43 | 44 | 45 | 46 | 125 | 126 | -------------------------------------------------------------------------------- /jmqtt-manager: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /jmqtt-mqtt/README.md: -------------------------------------------------------------------------------- 1 | Support mqtt gateway 2 | -------------------------------------------------------------------------------- /jmqtt-mqtt/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | jmqtt 6 | org.jmqtt 7 | 3.0.0 8 | 9 | 10 | 4.0.0 11 | 12 | jmqtt-mqtt 13 | jar 14 | 15 | jmqtt-mqtt 16 | 17 | 18 | 19 | 20 | 21 | org.jmqtt 22 | jmqtt-bus 23 | 24 | 25 | 26 | io.netty 27 | netty-all 28 | 29 | 30 | 31 | org.apache.logging.log4j 32 | log4j-api 33 | 34 | 35 | org.apache.logging.log4j 36 | log4j-core 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /jmqtt-mqtt/src/main/java/org/jmqtt/mqtt/ConnectManager.java: -------------------------------------------------------------------------------- 1 | package org.jmqtt.mqtt; 2 | 3 | import java.util.Collection; 4 | import java.util.Map; 5 | import java.util.concurrent.ConcurrentHashMap; 6 | 7 | /** 8 | * 客户端连接管理器 9 | */ 10 | public class ConnectManager { 11 | 12 | private Map clientCache = new ConcurrentHashMap<>(); 13 | 14 | private static final ConnectManager INSTANCE = new ConnectManager(); 15 | 16 | private ConnectManager(){} 17 | 18 | public static ConnectManager getInstance(){ 19 | return INSTANCE; 20 | } 21 | 22 | public Collection getAllConnections(){ 23 | return this.clientCache.values(); 24 | } 25 | 26 | public MQTTConnection getClient(String clientId){ 27 | return this.clientCache.get(clientId); 28 | } 29 | 30 | public MQTTConnection putClient(String clientId,MQTTConnection clientSession){ 31 | return this.clientCache.put(clientId,clientSession); 32 | } 33 | 34 | public boolean containClient(String clientId){ 35 | return this.clientCache.containsKey(clientId); 36 | } 37 | 38 | public MQTTConnection removeClient(String clientId){ 39 | if (clientId != null) { 40 | return this.clientCache.remove(clientId); 41 | } 42 | return null; 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /jmqtt-mqtt/src/main/java/org/jmqtt/mqtt/MQTTConnectionFactory.java: -------------------------------------------------------------------------------- 1 | 2 | package org.jmqtt.mqtt; 3 | 4 | import io.netty.channel.Channel; 5 | import io.netty.handler.codec.mqtt.MqttMessageType; 6 | import org.jmqtt.bus.BusController; 7 | import org.jmqtt.mqtt.protocol.RequestProcessor; 8 | import org.jmqtt.mqtt.protocol.impl.*; 9 | import org.jmqtt.mqtt.retain.RetainMessageHandler; 10 | import org.jmqtt.mqtt.retain.impl.RetainMessageHandlerImpl; 11 | import org.jmqtt.support.config.BrokerConfig; 12 | import org.jmqtt.support.helper.Pair; 13 | import org.jmqtt.support.helper.RejectHandler; 14 | import org.jmqtt.support.helper.ThreadFactoryImpl; 15 | 16 | import java.util.HashMap; 17 | import java.util.Map; 18 | import java.util.concurrent.ExecutorService; 19 | import java.util.concurrent.LinkedBlockingQueue; 20 | import java.util.concurrent.ThreadPoolExecutor; 21 | import java.util.concurrent.TimeUnit; 22 | 23 | /** 24 | * mqtt connection factory 25 | */ 26 | public class MQTTConnectionFactory { 27 | 28 | private BrokerConfig brokerConfig; 29 | 30 | /** mqtt protocol processor */ 31 | private ExecutorService connectExecutor; 32 | private ExecutorService pubExecutor; 33 | private ExecutorService subExecutor; 34 | private ExecutorService pingExecutor; 35 | private LinkedBlockingQueue connectQueue; 36 | private LinkedBlockingQueue pubQueue; 37 | private LinkedBlockingQueue subQueue; 38 | private LinkedBlockingQueue pingQueue; 39 | private Map> processorTable; 40 | 41 | private RetainMessageHandler retainMessageHandler; 42 | 43 | /** bus dependency */ 44 | private BusController busController; 45 | 46 | 47 | 48 | public MQTTConnectionFactory(BrokerConfig brokerConfig, BusController busController) { 49 | 50 | this.brokerConfig = brokerConfig; 51 | this.busController = busController; 52 | this.retainMessageHandler = new RetainMessageHandlerImpl(); 53 | 54 | this.connectQueue = new LinkedBlockingQueue<>(100000); 55 | this.pubQueue = new LinkedBlockingQueue<>(100000); 56 | this.subQueue = new LinkedBlockingQueue<>(100000); 57 | this.pingQueue = new LinkedBlockingQueue<>(10000); 58 | 59 | int coreThreadNum = Runtime.getRuntime().availableProcessors(); 60 | this.connectExecutor = new ThreadPoolExecutor(coreThreadNum * 2, 61 | coreThreadNum * 2, 62 | 60000, 63 | TimeUnit.MILLISECONDS, 64 | connectQueue, 65 | new ThreadFactoryImpl("ConnectThread"), 66 | new RejectHandler("connect", 100000)); 67 | this.pubExecutor = new ThreadPoolExecutor(coreThreadNum * 2, 68 | coreThreadNum * 2, 69 | 60000, 70 | TimeUnit.MILLISECONDS, 71 | pubQueue, 72 | new ThreadFactoryImpl("PubThread"), 73 | new RejectHandler("pub", 100000)); 74 | this.subExecutor = new ThreadPoolExecutor(coreThreadNum * 2, 75 | coreThreadNum * 2, 76 | 60000, 77 | TimeUnit.MILLISECONDS, 78 | subQueue, 79 | new ThreadFactoryImpl("SubThread"), 80 | new RejectHandler("sub", 100000)); 81 | this.pingExecutor = new ThreadPoolExecutor(coreThreadNum, 82 | coreThreadNum, 83 | 60000, 84 | TimeUnit.MILLISECONDS, 85 | pingQueue, 86 | new ThreadFactoryImpl("PingThread"), 87 | new RejectHandler("heartbeat", 100000)); 88 | 89 | RequestProcessor connectProcessor = new ConnectProcessor(); 90 | RequestProcessor disconnectProcessor = new DisconnectProcessor(); 91 | RequestProcessor pingProcessor = new PingProcessor(); 92 | RequestProcessor publishProcessor = new PublishProcessor(); 93 | RequestProcessor pubRelProcessor = new PubRelProcessor(); 94 | RequestProcessor subscribeProcessor = new SubscribeProcessor(); 95 | RequestProcessor unSubscribeProcessor = new UnSubscribeProcessor(); 96 | RequestProcessor pubRecProcessor = new PubRecProcessor(); 97 | RequestProcessor pubAckProcessor = new PubAckProcessor(); 98 | RequestProcessor pubCompProcessor = new PubCompProcessor(); 99 | 100 | processorTable = new HashMap<>(); 101 | registerProcessor(MqttMessageType.CONNECT.value(), connectProcessor, connectExecutor); 102 | registerProcessor(MqttMessageType.DISCONNECT.value(), disconnectProcessor, 103 | connectExecutor); 104 | registerProcessor(MqttMessageType.PINGREQ.value(), pingProcessor, pingExecutor); 105 | registerProcessor(MqttMessageType.PUBLISH.value(), publishProcessor, pubExecutor); 106 | registerProcessor(MqttMessageType.PUBACK.value(), pubAckProcessor, pubExecutor); 107 | registerProcessor(MqttMessageType.PUBREL.value(), pubRelProcessor, pubExecutor); 108 | registerProcessor(MqttMessageType.SUBSCRIBE.value(), subscribeProcessor, subExecutor); 109 | registerProcessor(MqttMessageType.UNSUBSCRIBE.value(), unSubscribeProcessor, subExecutor); 110 | registerProcessor(MqttMessageType.PUBREC.value(), pubRecProcessor, subExecutor); 111 | registerProcessor(MqttMessageType.PUBCOMP.value(), pubCompProcessor, subExecutor); 112 | } 113 | 114 | public void registerProcessor(int mqttMessageType, RequestProcessor requestProcessor, ExecutorService executorService){ 115 | processorTable.put(mqttMessageType,new Pair<>(requestProcessor,executorService)); 116 | } 117 | 118 | public MQTTConnection create(Channel channel){ 119 | MQTTConnection mqttConnection = new MQTTConnection(channel,processorTable,brokerConfig,busController,retainMessageHandler); 120 | return mqttConnection; 121 | } 122 | 123 | 124 | public void shutdown(){ 125 | this.connectExecutor.shutdown(); 126 | this.pubExecutor.shutdown(); 127 | this.subExecutor.shutdown(); 128 | this.pingExecutor.shutdown(); 129 | } 130 | 131 | } 132 | -------------------------------------------------------------------------------- /jmqtt-mqtt/src/main/java/org/jmqtt/mqtt/MQTTServer.java: -------------------------------------------------------------------------------- 1 | 2 | package org.jmqtt.mqtt; 3 | 4 | import io.netty.handler.codec.mqtt.MqttConnectReturnCode; 5 | import org.jmqtt.bus.BusController; 6 | import org.jmqtt.mqtt.event.MqttEventListener; 7 | import org.jmqtt.mqtt.netty.MqttRemotingServer; 8 | import org.jmqtt.support.config.BrokerConfig; 9 | import org.jmqtt.support.config.NettyConfig; 10 | import org.jmqtt.support.helper.MixAll; 11 | import org.jmqtt.support.log.JmqttLogger; 12 | import org.jmqtt.support.log.LogUtil; 13 | import org.slf4j.Logger; 14 | 15 | import java.util.Collection; 16 | 17 | /** 18 | * mqtt服务 19 | */ 20 | public class MQTTServer { 21 | 22 | private static final Logger log = JmqttLogger.mqttLog; 23 | 24 | private BusController busController; 25 | private BrokerConfig brokerConfig; 26 | private NettyConfig nettyConfig; 27 | private MqttRemotingServer mqttRemotingServer; 28 | private MqttEventListener mqttEventListener; 29 | 30 | public MQTTServer(BusController busController, BrokerConfig brokerConfig, NettyConfig nettyConfig){ 31 | this.busController = busController; 32 | this.brokerConfig = brokerConfig; 33 | this.nettyConfig = nettyConfig; 34 | this.mqttRemotingServer = new MqttRemotingServer(brokerConfig,nettyConfig,busController); 35 | this.mqttEventListener = new MqttEventListener(busController.getDeviceMessageManager()); 36 | } 37 | 38 | public void start(){ 39 | this.busController.getClusterEventManager().registerEventListener(mqttEventListener); 40 | this.mqttRemotingServer.start(); 41 | LogUtil.info(log,"MQTT server start success."); 42 | } 43 | 44 | 45 | public void shutdown(){ 46 | this.mqttRemotingServer.shutdown(); 47 | Collection mqttConnections = ConnectManager.getInstance().getAllConnections(); 48 | if (!MixAll.isEmpty(mqttConnections)) { 49 | for (MQTTConnection mqttConnection : mqttConnections) { 50 | mqttConnection.abortConnection(MqttConnectReturnCode.CONNECTION_REFUSED_SERVER_UNAVAILABLE); 51 | } 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /jmqtt-mqtt/src/main/java/org/jmqtt/mqtt/codec/ByteBuf2WebSocketEncoder.java: -------------------------------------------------------------------------------- 1 | package org.jmqtt.mqtt.codec; 2 | 3 | import io.netty.buffer.ByteBuf; 4 | import io.netty.channel.ChannelHandlerContext; 5 | import io.netty.handler.codec.MessageToMessageEncoder; 6 | import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame; 7 | 8 | import java.util.List; 9 | 10 | public class ByteBuf2WebSocketEncoder extends MessageToMessageEncoder { 11 | @Override 12 | protected void encode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List list) throws Exception { 13 | BinaryWebSocketFrame fram = new BinaryWebSocketFrame(); 14 | fram.content().writeBytes(byteBuf); 15 | list.add(fram); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /jmqtt-mqtt/src/main/java/org/jmqtt/mqtt/codec/WebSocket2ByteBufDecoder.java: -------------------------------------------------------------------------------- 1 | package org.jmqtt.mqtt.codec; 2 | 3 | import io.netty.buffer.ByteBuf; 4 | import io.netty.channel.ChannelHandlerContext; 5 | import io.netty.handler.codec.MessageToMessageDecoder; 6 | import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame; 7 | 8 | import java.util.List; 9 | 10 | public class WebSocket2ByteBufDecoder extends MessageToMessageDecoder { 11 | 12 | @Override 13 | protected void decode(ChannelHandlerContext channelHandlerContext, BinaryWebSocketFrame binaryWebSocketFrame, List list) throws Exception { 14 | ByteBuf byteBuf = binaryWebSocketFrame.content(); 15 | byteBuf.retain(); 16 | list.add(byteBuf); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /jmqtt-mqtt/src/main/java/org/jmqtt/mqtt/event/MqttEventListener.java: -------------------------------------------------------------------------------- 1 | package org.jmqtt.mqtt.event; 2 | 3 | import com.alibaba.fastjson.JSONObject; 4 | import com.google.common.eventbus.Subscribe; 5 | import io.netty.handler.codec.mqtt.MqttConnectReturnCode; 6 | import org.jmqtt.bus.DeviceMessageManager; 7 | import org.jmqtt.bus.enums.ClusterEventCodeEnum; 8 | import org.jmqtt.bus.enums.MessageAckEnum; 9 | import org.jmqtt.bus.event.GatewayListener; 10 | import org.jmqtt.bus.model.ClusterEvent; 11 | import org.jmqtt.bus.model.DeviceMessage; 12 | import org.jmqtt.bus.model.DeviceSubscription; 13 | import org.jmqtt.bus.subscription.model.Subscription; 14 | import org.jmqtt.mqtt.ConnectManager; 15 | import org.jmqtt.mqtt.MQTTConnection; 16 | import org.jmqtt.mqtt.utils.MqttMessageUtil; 17 | import org.jmqtt.mqtt.utils.MqttMsgHeader; 18 | import org.jmqtt.support.log.JmqttLogger; 19 | import org.jmqtt.support.log.LogUtil; 20 | import org.jmqtt.support.remoting.RemotingHelper; 21 | import org.slf4j.Logger; 22 | 23 | import java.util.Map; 24 | 25 | public class MqttEventListener implements GatewayListener { 26 | 27 | private static final Logger log = JmqttLogger.mqttLog; 28 | 29 | private static final String currentIp = RemotingHelper.getLocalAddr(); 30 | 31 | private DeviceMessageManager deviceMessageManager; 32 | 33 | public MqttEventListener(DeviceMessageManager deviceMessageManager) { 34 | this.deviceMessageManager = deviceMessageManager; 35 | } 36 | 37 | /** 38 | * 分发到网关的消息,bus上检查设备是在线的,若不在线,说明是断开了 39 | * mqtt网关处理两类事件: 40 | * 1. 集群消息:分发给设备 41 | * 2. 清理session:设备连接到其它服务器了,需要清理当前服务器上该设备的连接session(clientId 保持唯一) 42 | * @param clusterEvent 43 | */ 44 | @Override 45 | @Subscribe 46 | public void consume(ClusterEvent clusterEvent) { 47 | ClusterEventCodeEnum eventCode = clusterEvent.getClusterEventCode(); 48 | switch (eventCode){ 49 | case MQTT_CLEAR_SESSION: 50 | clearCurrentNodeSession(clusterEvent); 51 | break; 52 | case DISPATCHER_CLIENT_MESSAGE: 53 | sendMessage2Client(clusterEvent); 54 | break; 55 | default: 56 | LogUtil.warn(log,"Mqtt gateway not support this event:{}",eventCode); 57 | break; 58 | } 59 | } 60 | 61 | 62 | private void sendMessage2Client(ClusterEvent clusterEvent) { 63 | DeviceMessage deviceMessage = JSONObject.parseObject(clusterEvent.getContent(),DeviceMessage.class); 64 | Subscription subscription = clusterEvent.getSubscription(); 65 | if (deviceMessage == null ||subscription == null) { 66 | LogUtil.error(log,"[EVENT] Send event error, message or subscription is null,message:{},subscription:{}",deviceMessage,subscription); 67 | return; 68 | } 69 | MQTTConnection mqttConnection = ConnectManager.getInstance().getClient(subscription.getClientId()); 70 | 71 | Integer subQos = subscription.getProperty(MqttMsgHeader.QOS); 72 | Integer msgQos = deviceMessage.getProperty(MqttMsgHeader.QOS); 73 | if (subQos == null) { 74 | subQos = 1; 75 | } 76 | if (msgQos == null) { 77 | msgQos = 1; 78 | } 79 | int qos = MqttMessageUtil.getMinQos(subQos,msgQos); 80 | 81 | // add to client inbox 82 | if (qos == 0) { 83 | this.deviceMessageManager.addClientInBoxMsg(subscription.getClientId(),deviceMessage.getId(), MessageAckEnum.ACK); 84 | } else { 85 | this.deviceMessageManager.addClientInBoxMsg(subscription.getClientId(),deviceMessage.getId(),MessageAckEnum.UN_ACK); 86 | } 87 | 88 | // offline can be optimize ,can carry to bus 89 | if (mqttConnection == null) { 90 | return; 91 | } 92 | 93 | mqttConnection.getBindedSession().sendMessage(deviceMessage); 94 | } 95 | 96 | private void clearCurrentNodeSession(ClusterEvent clusterEvent){ 97 | if (currentIp.equals(clusterEvent.getNodeIp())) { 98 | return; 99 | } 100 | 101 | String clientId = clusterEvent.getContent(); 102 | MQTTConnection mqttConnection = ConnectManager.getInstance().getClient(clientId); 103 | if (mqttConnection == null) { 104 | LogUtil.info(log,"MQTT connection is not exist.{}",clientId); 105 | return; 106 | } 107 | // 1. clear subscription tree 108 | Map subscriptionSet = mqttConnection.getBindedSession().getSubscriptions(); 109 | if (subscriptionSet != null && subscriptionSet.size() > 0) { 110 | for (String topic:subscriptionSet.keySet()) { 111 | mqttConnection.getDeviceSubscriptionManager().onlyUnUnSubscribeFromTree(clientId,topic); 112 | LogUtil.debug(log,"[CLUSTER SESSION] clear other node's session.un subscribe clientId:{},topic:{}",clientId,topic); 113 | } 114 | } 115 | 116 | // 2. close channel 117 | mqttConnection.abortConnection(MqttConnectReturnCode.CONNECTION_REFUSED_SERVER_UNAVAILABLE); 118 | 119 | // 3. remove connection in cache 120 | ConnectManager.getInstance().removeClient(clientId); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /jmqtt-mqtt/src/main/java/org/jmqtt/mqtt/model/MqttTopic.java: -------------------------------------------------------------------------------- 1 | package org.jmqtt.mqtt.model; 2 | 3 | public class MqttTopic { 4 | 5 | private String topicName; 6 | private int qos; 7 | 8 | public MqttTopic(String topicName, int qos) { 9 | this.topicName = topicName; 10 | this.qos = qos; 11 | } 12 | 13 | public String getTopicName() { 14 | return topicName; 15 | } 16 | 17 | public void setTopicName(String topicName) { 18 | this.topicName = topicName; 19 | } 20 | 21 | public int getQos() { 22 | return qos; 23 | } 24 | 25 | public void setQos(int qos) { 26 | this.qos = qos; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /jmqtt-mqtt/src/main/java/org/jmqtt/mqtt/netty/MqttNettyUtils.java: -------------------------------------------------------------------------------- 1 | package org.jmqtt.mqtt.netty; 2 | 3 | import io.netty.channel.Channel; 4 | import io.netty.channel.ChannelHandlerContext; 5 | import io.netty.handler.codec.mqtt.MqttMessage; 6 | import io.netty.util.Attribute; 7 | import io.netty.util.AttributeKey; 8 | import org.jmqtt.mqtt.MQTTConnection; 9 | 10 | import java.io.IOException; 11 | 12 | public class MqttNettyUtils { 13 | 14 | public static final String ATTR_USERNAME = "username"; 15 | private static final String ATTR_CLIENTID = "ClientID"; 16 | private static final String CLEAN_SESSION = "removeTemporaryQoS2"; 17 | private static final String KEEP_ALIVE = "keepAlive"; 18 | private static final String CONNECTION = "connection"; 19 | private static final AttributeKey ATTR_KEY_CONNECTION = AttributeKey.valueOf(CONNECTION); 20 | private static final AttributeKey ATTR_KEY_KEEPALIVE = AttributeKey.valueOf(KEEP_ALIVE); 21 | private static final AttributeKey ATTR_KEY_CLEANSESSION = AttributeKey.valueOf(CLEAN_SESSION); 22 | private static final AttributeKey ATTR_KEY_CLIENTID = AttributeKey.valueOf(ATTR_CLIENTID); 23 | private static final AttributeKey ATTR_KEY_USERNAME = AttributeKey.valueOf(ATTR_USERNAME); 24 | 25 | public static Object getAttribute(ChannelHandlerContext ctx, AttributeKey key) { 26 | Attribute attr = ctx.channel().attr(key); 27 | return attr.get(); 28 | } 29 | 30 | public static void keepAlive(Channel channel, int keepAlive) { 31 | channel.attr(ATTR_KEY_KEEPALIVE).set(keepAlive); 32 | } 33 | 34 | public static void cleanSession(Channel channel, boolean cleanSession) { 35 | channel.attr(ATTR_KEY_CLEANSESSION).set(cleanSession); 36 | } 37 | 38 | public static boolean cleanSession(Channel channel) { 39 | return (Boolean) channel.attr(ATTR_KEY_CLEANSESSION).get(); 40 | } 41 | 42 | public static void clientID(Channel channel, String clientID) { 43 | channel.attr(ATTR_KEY_CLIENTID).set(clientID); 44 | } 45 | 46 | public static String clientID(Channel channel) { 47 | return (String) channel.attr(ATTR_KEY_CLIENTID).get(); 48 | } 49 | 50 | 51 | public static void mqttConnection(Channel channel, MQTTConnection mqttConnection) { 52 | channel.attr(ATTR_KEY_CONNECTION).set(mqttConnection); 53 | } 54 | 55 | public static MQTTConnection mqttConnection(Channel channel) { 56 | return (MQTTConnection) channel.attr(ATTR_KEY_CONNECTION).get(); 57 | } 58 | 59 | public static void userName(Channel channel, String username) { 60 | channel.attr(ATTR_KEY_USERNAME).set(username); 61 | } 62 | 63 | public static String userName(Channel channel) { 64 | return (String) channel.attr(ATTR_KEY_USERNAME).get(); 65 | } 66 | 67 | public static MqttMessage validateMessage(Object message) throws IOException, ClassCastException { 68 | MqttMessage msg = (MqttMessage) message; 69 | if (msg.decoderResult() != null && msg.decoderResult().isFailure()) { 70 | throw new IOException("invalid massage", msg.decoderResult().cause()); 71 | } 72 | if (msg.fixedHeader() == null) { 73 | throw new IOException("Unknown packet, no fixedHeader present, no cause provided"); 74 | } 75 | return msg; 76 | } 77 | 78 | private MqttNettyUtils() { 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /jmqtt-mqtt/src/main/java/org/jmqtt/mqtt/netty/NettyMqttHandler.java: -------------------------------------------------------------------------------- 1 | 2 | package org.jmqtt.mqtt.netty; 3 | 4 | import io.netty.channel.ChannelDuplexHandler; 5 | import io.netty.channel.ChannelFuture; 6 | import io.netty.channel.ChannelFutureListener; 7 | import io.netty.channel.ChannelHandlerContext; 8 | import io.netty.handler.codec.mqtt.MqttMessage; 9 | import org.jmqtt.mqtt.MQTTConnection; 10 | import org.jmqtt.mqtt.MQTTConnectionFactory; 11 | import org.jmqtt.support.log.JmqttLogger; 12 | import org.jmqtt.support.log.LogUtil; 13 | import org.slf4j.Logger; 14 | 15 | import static io.netty.channel.ChannelFutureListener.CLOSE_ON_FAILURE; 16 | 17 | public class NettyMqttHandler extends ChannelDuplexHandler { 18 | 19 | private static final Logger log = JmqttLogger.remotingLog; 20 | 21 | private MQTTConnectionFactory connectionFactory; 22 | 23 | NettyMqttHandler(MQTTConnectionFactory connectionFactory) { 24 | this.connectionFactory = connectionFactory; 25 | } 26 | 27 | @Override 28 | public void channelRead(ChannelHandlerContext ctx, Object message) throws Exception { 29 | MqttMessage msg = MqttNettyUtils.validateMessage(message); 30 | final MQTTConnection mqttConnection = MqttNettyUtils.mqttConnection(ctx.channel()); 31 | try { 32 | mqttConnection.processProtocol(ctx,msg); 33 | } catch (Throwable ex) { 34 | LogUtil.error(log,"Error processing protocol message: {}", msg.fixedHeader().messageType(), ex); 35 | ctx.channel().close().addListener(new ChannelFutureListener() { 36 | @Override 37 | public void operationComplete(ChannelFuture future) { 38 | LogUtil.info(log,"Closed client channel due to exception in processing"); 39 | } 40 | }); 41 | } 42 | } 43 | 44 | @Override 45 | public void channelActive(ChannelHandlerContext ctx) { 46 | MQTTConnection connection = connectionFactory.create(ctx.channel()); 47 | MqttNettyUtils.mqttConnection(ctx.channel(), connection); 48 | } 49 | 50 | @Override 51 | public void channelInactive(ChannelHandlerContext ctx) { 52 | final MQTTConnection mqttConnection = MqttNettyUtils.mqttConnection(ctx.channel()); 53 | mqttConnection.handleConnectionLost(); 54 | } 55 | 56 | @Override 57 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { 58 | LogUtil.error(log,"Unexpected exception while processing MQTT message. Closing Netty channel. CId={}", 59 | MqttNettyUtils.clientID(ctx.channel()), cause); 60 | ctx.close().addListener(CLOSE_ON_FAILURE); 61 | } 62 | 63 | @Override 64 | public void channelWritabilityChanged(ChannelHandlerContext ctx) { 65 | ctx.fireChannelWritabilityChanged(); 66 | } 67 | 68 | @Override 69 | public void userEventTriggered(ChannelHandlerContext ctx, Object evt) { 70 | //if (evt instanceof InflightResender.ResendNotAckedPublishes) { 71 | // final MQTTConnection mqttConnection = mqttConnection(ctx.channel()); 72 | // mqttConnection.resendNotAckedPublishes(); 73 | //} 74 | ctx.fireUserEventTriggered(evt); 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /jmqtt-mqtt/src/main/java/org/jmqtt/mqtt/netty/NettySslHandler.java: -------------------------------------------------------------------------------- 1 | package org.jmqtt.mqtt.netty; 2 | 3 | import io.netty.channel.ChannelHandler; 4 | import io.netty.channel.socket.SocketChannel; 5 | import io.netty.handler.ssl.*; 6 | import org.jmqtt.support.log.JmqttLogger; 7 | import org.jmqtt.support.log.LogUtil; 8 | import org.slf4j.Logger; 9 | 10 | import javax.net.ssl.KeyManagerFactory; 11 | import javax.net.ssl.SSLEngine; 12 | import javax.net.ssl.TrustManagerFactory; 13 | import java.io.FileInputStream; 14 | import java.io.InputStream; 15 | import java.security.KeyStore; 16 | 17 | public class NettySslHandler { 18 | private static final Logger log = JmqttLogger.remotingLog; 19 | 20 | public static ChannelHandler getSslHandler(SocketChannel channel, boolean useClientCA, String sslKeyStoreType, String sslKeyFilePath, String sslManagerPwd, String sslStorePwd) { 21 | 22 | SslContext sslContext = createSSLContext(useClientCA, sslKeyStoreType, sslKeyFilePath, sslManagerPwd, sslStorePwd); 23 | SSLEngine sslEngine = sslContext.newEngine( 24 | channel.alloc(), 25 | channel.remoteAddress().getHostString(), 26 | channel.remoteAddress().getPort()); 27 | // server mode 28 | sslEngine.setUseClientMode(false); 29 | if (useClientCA) { 30 | sslEngine.setNeedClientAuth(true); 31 | } 32 | return new SslHandler(sslEngine); 33 | } 34 | 35 | private static SslContext createSSLContext(boolean useClientCA, String sslKeyStoreType, String sslKeyFilePath, String sslManagerPwd, String sslStorePwd) { 36 | try { 37 | InputStream ksInputStream = new FileInputStream(sslKeyFilePath); 38 | KeyStore ks = KeyStore.getInstance(sslKeyStoreType); 39 | ks.load(ksInputStream, sslStorePwd.toCharArray()); 40 | 41 | 42 | final KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); 43 | kmf.init(ks, sslManagerPwd.toCharArray()); 44 | SslContextBuilder contextBuilder = SslContextBuilder.forServer(kmf); 45 | 46 | // whether need client CA(two-way authentication) 47 | if (useClientCA) { 48 | contextBuilder.clientAuth(ClientAuth.REQUIRE); 49 | TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); 50 | tmf.init(ks); 51 | contextBuilder.trustManager(tmf); 52 | } 53 | return contextBuilder.sslProvider(SslProvider.valueOf("JDK")).build(); 54 | } catch (Exception ex) { 55 | LogUtil.error(log,"Create ssl context failure.cause={}", ex); 56 | return null; 57 | } 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /jmqtt-mqtt/src/main/java/org/jmqtt/mqtt/protocol/RequestProcessor.java: -------------------------------------------------------------------------------- 1 | package org.jmqtt.mqtt.protocol; 2 | 3 | import io.netty.channel.ChannelHandlerContext; 4 | import io.netty.handler.codec.mqtt.MqttMessage; 5 | 6 | public interface RequestProcessor { 7 | 8 | /** 9 | * handle mqtt message processor 10 | * ps:阅读mqtt协议代码从这里的实现开始 11 | */ 12 | void processRequest(ChannelHandlerContext ctx, MqttMessage mqttMessage); 13 | } 14 | -------------------------------------------------------------------------------- /jmqtt-mqtt/src/main/java/org/jmqtt/mqtt/protocol/impl/ConnectProcessor.java: -------------------------------------------------------------------------------- 1 | package org.jmqtt.mqtt.protocol.impl; 2 | 3 | import io.netty.channel.ChannelFuture; 4 | import io.netty.channel.ChannelFutureListener; 5 | import io.netty.channel.ChannelHandlerContext; 6 | import io.netty.handler.codec.mqtt.MqttConnAckMessage; 7 | import io.netty.handler.codec.mqtt.MqttConnectMessage; 8 | import io.netty.handler.codec.mqtt.MqttConnectReturnCode; 9 | import io.netty.handler.codec.mqtt.MqttMessage; 10 | import org.jmqtt.mqtt.MQTTConnection; 11 | import org.jmqtt.mqtt.netty.MqttNettyUtils; 12 | import org.jmqtt.mqtt.protocol.RequestProcessor; 13 | import org.jmqtt.mqtt.utils.MqttMessageUtil; 14 | import org.jmqtt.support.log.JmqttLogger; 15 | import org.jmqtt.support.log.LogUtil; 16 | import org.jmqtt.support.remoting.RemotingHelper; 17 | import org.slf4j.Logger; 18 | 19 | /** 20 | * mqtt 客户端连接逻辑处理 强制约束:jmqtt在未返回conAck之前,不接收其它任何mqtt协议报文(mqtt协议可以允许) TODO mqtt5 协议支持 21 | */ 22 | public class ConnectProcessor implements RequestProcessor { 23 | 24 | private static final Logger log = JmqttLogger.mqttLog; 25 | 26 | @Override 27 | public void processRequest(ChannelHandlerContext ctx,MqttMessage mqttMessage) { 28 | MqttConnectMessage connectMessage = (MqttConnectMessage) mqttMessage; 29 | MqttConnectReturnCode returnCode = null; 30 | int mqttVersion = connectMessage.variableHeader().version(); 31 | String clientId = connectMessage.payload().clientIdentifier(); 32 | String userName = connectMessage.payload().userName(); 33 | byte[] password = connectMessage.payload().passwordInBytes(); 34 | boolean cleanSession = connectMessage.variableHeader().isCleanSession(); 35 | MQTTConnection mqttConnection = MqttNettyUtils.mqttConnection(ctx.channel()); 36 | boolean sessionPresent = false; 37 | try { 38 | 39 | if (!versionValid(mqttVersion)) { 40 | returnCode = MqttConnectReturnCode.CONNECTION_REFUSED_UNACCEPTABLE_PROTOCOL_VERSION; 41 | } else if (!mqttConnection.clientIdVerify(clientId)) { 42 | returnCode = MqttConnectReturnCode.CONNECTION_REFUSED_IDENTIFIER_REJECTED; 43 | } else if (mqttConnection.onBlackList(RemotingHelper.getRemoteAddr(ctx.channel()), clientId)) { 44 | returnCode = MqttConnectReturnCode.CONNECTION_REFUSED_NOT_AUTHORIZED; 45 | } else if (!mqttConnection.login(clientId, userName, password)) { 46 | returnCode = MqttConnectReturnCode.CONNECTION_REFUSED_BAD_USER_NAME_OR_PASSWORD; 47 | } else { 48 | // 1. 设置心跳 49 | int heartbeatSec = connectMessage.variableHeader().keepAliveTimeSeconds(); 50 | if (!mqttConnection.keepAlive(heartbeatSec)) { 51 | LogUtil.warn(log,"[CONNECT] -> set heartbeat failure,clientId:{},heartbeatSec:{}", clientId, heartbeatSec); 52 | throw new Exception("set heartbeat failure"); 53 | } 54 | // 2. 处理连接 55 | sessionPresent = mqttConnection.createOrReopenSession(connectMessage); 56 | 57 | returnCode = MqttConnectReturnCode.CONNECTION_ACCEPTED; 58 | MqttNettyUtils.clientID(ctx.channel(), clientId); 59 | } 60 | 61 | if (returnCode != MqttConnectReturnCode.CONNECTION_ACCEPTED) { 62 | ctx.close(); 63 | LogUtil.warn(log,"[CONNECT] -> {} connect failure,returnCode={}", clientId, returnCode); 64 | } 65 | MqttConnAckMessage ackMessage = MqttMessageUtil.getConnectAckMessage(returnCode, sessionPresent); 66 | 67 | final boolean hasOldSession = sessionPresent; 68 | ctx.writeAndFlush(ackMessage).addListener(new ChannelFutureListener() { 69 | @Override 70 | public void operationComplete(ChannelFuture future) throws Exception { 71 | LogUtil.info(log,"[CONNECT] -> {} connect to this mqtt server", clientId); 72 | if (future.isSuccess()) { 73 | // store session 74 | mqttConnection.storeSession(); 75 | // send unAckMessages 76 | if (!cleanSession && hasOldSession) { 77 | mqttConnection.reSendMessage2Client(); 78 | } 79 | } else { 80 | LogUtil.error(log,"[CONNECT] -> send connect ack error."); 81 | mqttConnection.abortConnection(MqttConnectReturnCode.CONNECTION_REFUSED_SERVER_UNAVAILABLE); 82 | } 83 | } 84 | }); 85 | //LogUtil.warn(log,"Connect Cost:{}",(System.currentTimeMillis() - start)); 86 | } catch (Exception ex) { 87 | LogUtil.warn(log,"[CONNECT] -> Service Unavailable: cause={}", ex); 88 | returnCode = MqttConnectReturnCode.CONNECTION_REFUSED_SERVER_UNAVAILABLE; 89 | MqttConnAckMessage ackMessage = MqttMessageUtil.getConnectAckMessage(returnCode, sessionPresent); 90 | ctx.writeAndFlush(ackMessage); 91 | ctx.close(); 92 | } 93 | } 94 | 95 | private boolean versionValid(int mqttVersion) { 96 | if (mqttVersion == 3 || mqttVersion == 4) { 97 | return true; 98 | } 99 | return false; 100 | } 101 | 102 | } 103 | -------------------------------------------------------------------------------- /jmqtt-mqtt/src/main/java/org/jmqtt/mqtt/protocol/impl/DisconnectProcessor.java: -------------------------------------------------------------------------------- 1 | package org.jmqtt.mqtt.protocol.impl; 2 | 3 | import io.netty.channel.ChannelHandlerContext; 4 | import io.netty.handler.codec.mqtt.MqttMessage; 5 | import org.jmqtt.mqtt.MQTTConnection; 6 | import org.jmqtt.mqtt.netty.MqttNettyUtils; 7 | import org.jmqtt.mqtt.protocol.RequestProcessor; 8 | import org.jmqtt.support.log.JmqttLogger; 9 | import org.jmqtt.support.log.LogUtil; 10 | import org.slf4j.Logger; 11 | 12 | 13 | /** 14 | * 客户端主动发起断开连接:正常断连 15 | */ 16 | public class DisconnectProcessor implements RequestProcessor { 17 | 18 | private static final Logger log = JmqttLogger.mqttLog; 19 | 20 | @Override 21 | public void processRequest(ChannelHandlerContext ctx, MqttMessage mqttMessage) { 22 | MQTTConnection mqttConnection = MqttNettyUtils.mqttConnection(ctx.channel()); 23 | String clientId = MqttNettyUtils.clientID(ctx.channel()); 24 | if (mqttConnection == null) { 25 | LogUtil.error(log,"[DISCONNECT] -> {} hasn't connect before",clientId ); 26 | return; 27 | } 28 | mqttConnection.processDisconnect(); 29 | ctx.close(); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /jmqtt-mqtt/src/main/java/org/jmqtt/mqtt/protocol/impl/PingProcessor.java: -------------------------------------------------------------------------------- 1 | package org.jmqtt.mqtt.protocol.impl; 2 | 3 | import io.netty.channel.ChannelHandlerContext; 4 | import io.netty.handler.codec.mqtt.MqttMessage; 5 | import org.jmqtt.mqtt.protocol.RequestProcessor; 6 | import org.jmqtt.mqtt.utils.MqttMessageUtil; 7 | 8 | /** 9 | * 心跳 10 | */ 11 | public class PingProcessor implements RequestProcessor { 12 | 13 | @Override 14 | public void processRequest(ChannelHandlerContext ctx, MqttMessage mqttMessage) { 15 | MqttMessage pingRespMessage = MqttMessageUtil.getPingRespMessage(); 16 | ctx.writeAndFlush(pingRespMessage); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /jmqtt-mqtt/src/main/java/org/jmqtt/mqtt/protocol/impl/PubAckProcessor.java: -------------------------------------------------------------------------------- 1 | package org.jmqtt.mqtt.protocol.impl; 2 | 3 | import io.netty.channel.ChannelHandlerContext; 4 | import io.netty.handler.codec.mqtt.MqttMessage; 5 | import org.jmqtt.mqtt.MQTTConnection; 6 | import org.jmqtt.mqtt.netty.MqttNettyUtils; 7 | import org.jmqtt.mqtt.protocol.RequestProcessor; 8 | import org.jmqtt.support.log.JmqttLogger; 9 | import org.slf4j.Logger; 10 | 11 | /** 12 | * 出栈消息接收到的客户端 pubAck报文:释放缓存的出栈消息 13 | */ 14 | public class PubAckProcessor implements RequestProcessor { 15 | 16 | private static final Logger log = JmqttLogger.messageTraceLog; 17 | 18 | @Override 19 | public void processRequest(ChannelHandlerContext ctx, MqttMessage mqttMessage) { 20 | MQTTConnection mqttConnection = MqttNettyUtils.mqttConnection(ctx.channel()); 21 | mqttConnection.processPubAck(mqttMessage); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /jmqtt-mqtt/src/main/java/org/jmqtt/mqtt/protocol/impl/PubCompProcessor.java: -------------------------------------------------------------------------------- 1 | package org.jmqtt.mqtt.protocol.impl; 2 | 3 | import io.netty.channel.ChannelHandlerContext; 4 | import io.netty.handler.codec.mqtt.MqttMessage; 5 | import org.jmqtt.mqtt.MQTTConnection; 6 | import org.jmqtt.mqtt.netty.MqttNettyUtils; 7 | import org.jmqtt.mqtt.protocol.RequestProcessor; 8 | 9 | /** 10 | * 出栈消息接收到的qos2消息的pubComp报文:清除缓存的出栈消息 11 | */ 12 | public class PubCompProcessor implements RequestProcessor { 13 | 14 | 15 | @Override 16 | public void processRequest(ChannelHandlerContext ctx, MqttMessage mqttMessage) { 17 | MQTTConnection mqttConnection = MqttNettyUtils.mqttConnection(ctx.channel()); 18 | mqttConnection.processPubComp(mqttMessage); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /jmqtt-mqtt/src/main/java/org/jmqtt/mqtt/protocol/impl/PubRecProcessor.java: -------------------------------------------------------------------------------- 1 | package org.jmqtt.mqtt.protocol.impl; 2 | 3 | import io.netty.channel.ChannelHandlerContext; 4 | import io.netty.handler.codec.mqtt.MqttMessage; 5 | import org.jmqtt.mqtt.MQTTConnection; 6 | import org.jmqtt.mqtt.netty.MqttNettyUtils; 7 | import org.jmqtt.mqtt.protocol.RequestProcessor; 8 | 9 | /** 10 | * 出栈消息接收到qos2第一阶段的的pubRec报文: 丢弃消息,存储报文标志符 11 | * 发送pubRel报文 12 | */ 13 | public class PubRecProcessor implements RequestProcessor { 14 | 15 | 16 | @Override 17 | public void processRequest(ChannelHandlerContext ctx, MqttMessage mqttMessage) { 18 | MQTTConnection mqttConnection = MqttNettyUtils.mqttConnection(ctx.channel()); 19 | mqttConnection.processPubRec(mqttMessage); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /jmqtt-mqtt/src/main/java/org/jmqtt/mqtt/protocol/impl/PubRelProcessor.java: -------------------------------------------------------------------------------- 1 | package org.jmqtt.mqtt.protocol.impl; 2 | 3 | import io.netty.channel.ChannelHandlerContext; 4 | import io.netty.handler.codec.mqtt.MqttMessage; 5 | import org.jmqtt.mqtt.MQTTConnection; 6 | import org.jmqtt.mqtt.netty.MqttNettyUtils; 7 | import org.jmqtt.mqtt.protocol.RequestProcessor; 8 | import org.jmqtt.support.log.JmqttLogger; 9 | import org.jmqtt.support.log.LogUtil; 10 | import org.jmqtt.support.remoting.RemotingHelper; 11 | import org.slf4j.Logger; 12 | 13 | /** 14 | * 客户端的pubRel报文:入栈消息qos2的第二阶段: 15 | * 1. 消息所有者转换为broker,开始分发消息 16 | * 2. 返回pubCom报文 17 | */ 18 | public class PubRelProcessor implements RequestProcessor { 19 | 20 | private static final Logger log = JmqttLogger.messageTraceLog; 21 | 22 | @Override 23 | public void processRequest(ChannelHandlerContext ctx, MqttMessage mqttMessage) { 24 | MQTTConnection mqttConnection = MqttNettyUtils.mqttConnection(ctx.channel()); 25 | if(mqttConnection != null){ 26 | mqttConnection.processPubRelMessage(mqttMessage); 27 | }else{ 28 | LogUtil.warn(log,"[PubRelMessage] -> the client:{} disconnect to this server.",mqttConnection.getClientId()); 29 | RemotingHelper.closeChannel(ctx.channel()); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /jmqtt-mqtt/src/main/java/org/jmqtt/mqtt/protocol/impl/PublishProcessor.java: -------------------------------------------------------------------------------- 1 | package org.jmqtt.mqtt.protocol.impl; 2 | 3 | import io.netty.channel.ChannelHandlerContext; 4 | import io.netty.handler.codec.mqtt.MqttMessage; 5 | import io.netty.handler.codec.mqtt.MqttPublishMessage; 6 | import io.netty.util.ReferenceCountUtil; 7 | import org.jmqtt.mqtt.MQTTConnection; 8 | import org.jmqtt.mqtt.netty.MqttNettyUtils; 9 | import org.jmqtt.mqtt.protocol.RequestProcessor; 10 | import org.jmqtt.support.log.JmqttLogger; 11 | import org.jmqtt.support.log.LogUtil; 12 | import org.slf4j.Logger; 13 | 14 | /** 15 | * 客户端publish消息到jmqtt broker TODO mqtt5实现,流控处理 16 | */ 17 | public class PublishProcessor implements RequestProcessor { 18 | 19 | private static final Logger log = JmqttLogger.mqttLog; 20 | 21 | @Override 22 | public void processRequest(ChannelHandlerContext ctx, MqttMessage mqttMessage) { 23 | try { 24 | MqttPublishMessage publishMessage = (MqttPublishMessage) mqttMessage; 25 | MQTTConnection mqttConnection = MqttNettyUtils.mqttConnection(ctx.channel()); 26 | mqttConnection.processPublishMessage(publishMessage); 27 | } catch (Throwable tr) { 28 | LogUtil.warn(log, "[PubMessage] -> Solve mqtt pub message exception:{}", tr); 29 | } finally { 30 | ReferenceCountUtil.release(mqttMessage.payload()); 31 | } 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /jmqtt-mqtt/src/main/java/org/jmqtt/mqtt/protocol/impl/SubscribeProcessor.java: -------------------------------------------------------------------------------- 1 | package org.jmqtt.mqtt.protocol.impl; 2 | 3 | import io.netty.channel.ChannelHandlerContext; 4 | import io.netty.handler.codec.mqtt.MqttMessage; 5 | import io.netty.handler.codec.mqtt.MqttSubscribeMessage; 6 | import org.jmqtt.mqtt.MQTTConnection; 7 | import org.jmqtt.mqtt.netty.MqttNettyUtils; 8 | import org.jmqtt.mqtt.protocol.RequestProcessor; 9 | 10 | /** 11 | * 订阅报文逻辑处理 12 | * TODO mqtt5协议支持 13 | */ 14 | public class SubscribeProcessor implements RequestProcessor { 15 | 16 | @Override 17 | public void processRequest(ChannelHandlerContext ctx, MqttMessage mqttMessage) { 18 | MqttSubscribeMessage subscribeMessage = (MqttSubscribeMessage) mqttMessage; 19 | MQTTConnection mqttConnection = MqttNettyUtils.mqttConnection(ctx.channel()); 20 | mqttConnection.processSubscribe(subscribeMessage); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /jmqtt-mqtt/src/main/java/org/jmqtt/mqtt/protocol/impl/UnSubscribeProcessor.java: -------------------------------------------------------------------------------- 1 | package org.jmqtt.mqtt.protocol.impl; 2 | 3 | import io.netty.channel.ChannelHandlerContext; 4 | import io.netty.handler.codec.mqtt.MqttMessage; 5 | import io.netty.handler.codec.mqtt.MqttUnsubscribeMessage; 6 | import org.jmqtt.mqtt.MQTTConnection; 7 | import org.jmqtt.mqtt.netty.MqttNettyUtils; 8 | import org.jmqtt.mqtt.protocol.RequestProcessor; 9 | 10 | /** 11 | * 取消订阅 12 | */ 13 | public class UnSubscribeProcessor implements RequestProcessor { 14 | 15 | @Override 16 | public void processRequest(ChannelHandlerContext ctx, MqttMessage mqttMessage) { 17 | MqttUnsubscribeMessage unsubscribeMessage = (MqttUnsubscribeMessage) mqttMessage; 18 | MQTTConnection mqttConnection = MqttNettyUtils.mqttConnection(ctx.channel()); 19 | mqttConnection.processUnSubscribe(unsubscribeMessage); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /jmqtt-mqtt/src/main/java/org/jmqtt/mqtt/retain/RetainMessageHandler.java: -------------------------------------------------------------------------------- 1 | package org.jmqtt.mqtt.retain; 2 | 3 | import org.jmqtt.bus.model.DeviceMessage; 4 | 5 | import java.util.List; 6 | 7 | public interface RetainMessageHandler { 8 | 9 | List getAllRetatinMessage(); 10 | 11 | void clearRetainMessage(String topic); 12 | 13 | void storeRetainMessage(DeviceMessage deviceMessage); 14 | } 15 | -------------------------------------------------------------------------------- /jmqtt-mqtt/src/main/java/org/jmqtt/mqtt/retain/impl/RetainMessageHandlerImpl.java: -------------------------------------------------------------------------------- 1 | package org.jmqtt.mqtt.retain.impl; 2 | 3 | import org.apache.ibatis.session.SqlSession; 4 | import org.jmqtt.bus.model.DeviceMessage; 5 | import org.jmqtt.bus.store.DBCallback; 6 | import org.jmqtt.bus.store.DBUtils; 7 | import org.jmqtt.bus.store.daoobject.RetainMessageDO; 8 | import org.jmqtt.mqtt.retain.RetainMessageHandler; 9 | import org.jmqtt.support.helper.MixAll; 10 | import org.jmqtt.support.log.JmqttLogger; 11 | import org.jmqtt.support.log.LogUtil; 12 | import org.slf4j.Logger; 13 | 14 | import java.util.ArrayList; 15 | import java.util.List; 16 | 17 | public class RetainMessageHandlerImpl implements RetainMessageHandler { 18 | 19 | private static final Logger log = JmqttLogger.mqttLog; 20 | 21 | @Override 22 | public List getAllRetatinMessage() { 23 | 24 | // If has too much retain message ,can optimize with paging query 25 | List retainMessageDOList = DBUtils.operate(new DBCallback() { 26 | @Override 27 | public List operate(SqlSession sqlSession) { 28 | return DBUtils.getMapper(sqlSession,DBUtils.retainMessageMapperClass).getAllRetainMessage(); 29 | } 30 | }); 31 | if (retainMessageDOList == null) { 32 | return null; 33 | } 34 | List deviceMessageList = new ArrayList<>(); 35 | retainMessageDOList.forEach(item -> { 36 | DeviceMessage deviceMessage = new DeviceMessage(); 37 | deviceMessage.setId(item.getId()); 38 | deviceMessage.setProperties(MixAll.propToMap(item.getProperties())); 39 | deviceMessage.setContent(item.getContent()); 40 | deviceMessage.setFromClientId(item.getFromClientId()); 41 | deviceMessage.setStoredTime(item.getStoredTime()); 42 | deviceMessage.setTopic(item.getTopic()); 43 | deviceMessageList.add(deviceMessage); 44 | }); 45 | return deviceMessageList; 46 | } 47 | 48 | @Override 49 | public void clearRetainMessage(String topic) { 50 | DBUtils.operate(new DBCallback() { 51 | @Override 52 | public Integer operate(SqlSession sqlSession) { 53 | return DBUtils.getMapper(sqlSession,DBUtils.retainMessageMapperClass).delRetainMessage(topic); 54 | } 55 | }); 56 | } 57 | 58 | @Override 59 | public void storeRetainMessage(DeviceMessage deviceMessage) { 60 | RetainMessageDO retainMessageDO = convert(deviceMessage); 61 | DBUtils.operate(new DBCallback() { 62 | @Override 63 | public Long operate(SqlSession sqlSession) { 64 | return DBUtils.getMapper(sqlSession,DBUtils.retainMessageMapperClass).storeRetainMessage(retainMessageDO); 65 | } 66 | }); 67 | if (retainMessageDO.getId() == null) { 68 | LogUtil.error(log,"[RETAIN MESSAGE] store retain message index error."); 69 | } 70 | } 71 | 72 | private RetainMessageDO convert(DeviceMessage deviceMessage) { 73 | RetainMessageDO retainMessageDO = new RetainMessageDO(); 74 | retainMessageDO.setTopic(deviceMessage.getTopic()); 75 | retainMessageDO.setFromClientId(deviceMessage.getFromClientId()); 76 | retainMessageDO.setContent(deviceMessage.getContent()); 77 | retainMessageDO.setStoredTime(deviceMessage.getStoredTime()); 78 | retainMessageDO.setProperties(MixAll.propToStr(deviceMessage.getProperties())); 79 | return retainMessageDO; 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /jmqtt-mqtt/src/main/java/org/jmqtt/mqtt/session/MqttSession.java: -------------------------------------------------------------------------------- 1 | 2 | package org.jmqtt.mqtt.session; 3 | 4 | import io.netty.handler.codec.mqtt.MqttPublishMessage; 5 | import io.netty.handler.codec.mqtt.MqttQoS; 6 | import org.jmqtt.bus.model.DeviceMessage; 7 | import org.jmqtt.bus.model.DeviceSubscription; 8 | import org.jmqtt.mqtt.MQTTConnection; 9 | import org.jmqtt.mqtt.utils.MqttMessageUtil; 10 | import org.jmqtt.mqtt.utils.MqttMsgHeader; 11 | import org.jmqtt.support.log.JmqttLogger; 12 | import org.jmqtt.support.log.LogUtil; 13 | import org.slf4j.Logger; 14 | 15 | import java.util.HashMap; 16 | import java.util.Map; 17 | import java.util.Set; 18 | import java.util.concurrent.ConcurrentHashMap; 19 | import java.util.concurrent.CopyOnWriteArraySet; 20 | import java.util.concurrent.atomic.AtomicInteger; 21 | 22 | /** 23 | * mqtt session 24 | */ 25 | public class MqttSession { 26 | 27 | private static final Logger log = JmqttLogger.mqttLog; 28 | 29 | /** 30 | * clientId uniqu in a cluseter 31 | */ 32 | private String clientId; 33 | private boolean cleanSession; 34 | private transient AtomicInteger lastPacketId = new AtomicInteger(1); 35 | private int mqttVersion; 36 | private DeviceMessage will; 37 | private final Map qos2Receiving = new ConcurrentHashMap<>(); 38 | private MQTTConnection mqttConnection; 39 | private final Map outboundFlowMessages = new ConcurrentHashMap<>(); 40 | private final Set qos2OutboundFlowPacketIds = new CopyOnWriteArraySet<>(); 41 | private String clientIp; 42 | private String serverIp; 43 | private Map subscriptions = new HashMap<>(); 44 | 45 | 46 | public void addSubscription(DeviceSubscription deviceSubscription) { 47 | this.subscriptions.put(deviceSubscription.getTopic(),deviceSubscription); 48 | } 49 | 50 | public void removeSubscription(String topic) { 51 | this.subscriptions.remove(topic); 52 | } 53 | 54 | public void receivePubRec(int packetId){ 55 | this.qos2OutboundFlowPacketIds.add(packetId); 56 | } 57 | 58 | public boolean releaseQos2SecFlow(int packetId){ 59 | return this.qos2OutboundFlowPacketIds.remove(packetId); 60 | } 61 | 62 | public DeviceMessage releaseOutboundFlowMessage(int packetId){ 63 | DeviceMessage deviceMessage = outboundFlowMessages.remove(packetId); 64 | if (deviceMessage == null) { 65 | LogUtil.error(log,"[PUBACK Or PUBREC] Release cache flow message,message is not success,packetId:{}",packetId); 66 | } 67 | return deviceMessage; 68 | } 69 | 70 | public void sendMessage(DeviceMessage deviceMessage) { 71 | Integer qos = deviceMessage.getProperty(MqttMsgHeader.QOS); 72 | MqttQoS mqttQoS = qos == null ? MqttQoS.AT_LEAST_ONCE : MqttQoS.valueOf(qos); 73 | switch (mqttQoS){ 74 | case AT_MOST_ONCE: 75 | sendQos0Message(deviceMessage); 76 | break; 77 | case AT_LEAST_ONCE: 78 | sendQos1Or2Message(deviceMessage); 79 | break; 80 | case EXACTLY_ONCE: 81 | sendQos1Or2Message(deviceMessage); 82 | break; 83 | default: 84 | LogUtil.error(log,"[Outbound publish] unsupport qos.",qos); 85 | break; 86 | } 87 | } 88 | 89 | private void sendQos0Message(DeviceMessage deviceMessage) { 90 | MqttPublishMessage mqttPublishMessage = MqttMessageUtil.getPubMessage(deviceMessage,0); 91 | mqttConnection.publishMessage(mqttPublishMessage); 92 | } 93 | 94 | private void sendQos1Or2Message(DeviceMessage deviceMessage) { 95 | int packetId = nextPacketId(); 96 | // cache 97 | outboundFlowMessages.put(packetId,deviceMessage); 98 | 99 | MqttPublishMessage mqttPublishMessage = MqttMessageUtil.getPubMessage(deviceMessage,packetId); 100 | mqttConnection.publishMessage(mqttPublishMessage); 101 | } 102 | 103 | 104 | public void receivedPublishQos2(int originPacketId,DeviceMessage deviceMessage) { 105 | this.qos2Receiving.put(originPacketId,deviceMessage); 106 | } 107 | 108 | public DeviceMessage receivedPubRelQos2(int packgetId) { 109 | return this.qos2Receiving.remove(packgetId); 110 | } 111 | 112 | public void clearWill(){ 113 | this.will = null; 114 | } 115 | 116 | public boolean hasWill(){ 117 | return will != null; 118 | } 119 | 120 | public int nextPacketId() { 121 | return lastPacketId.updateAndGet(v -> v == 65535 ? 1 : v + 1); 122 | } 123 | 124 | /** setter getter */ 125 | public String getClientId() { 126 | return clientId; 127 | } 128 | 129 | public void setClientId(String clientId) { 130 | this.clientId = clientId; 131 | } 132 | 133 | public boolean isCleanSession() { 134 | return cleanSession; 135 | } 136 | 137 | public void setCleanSession(boolean cleanSession) { 138 | this.cleanSession = cleanSession; 139 | } 140 | 141 | public int getMqttVersion() { 142 | return mqttVersion; 143 | } 144 | 145 | public void setMqttVersion(int mqttVersion) { 146 | this.mqttVersion = mqttVersion; 147 | } 148 | 149 | public DeviceMessage getWill() { 150 | return will; 151 | } 152 | 153 | public void setWill(DeviceMessage will) { 154 | this.will = will; 155 | } 156 | 157 | 158 | public MQTTConnection getMqttConnection() { 159 | return mqttConnection; 160 | } 161 | 162 | public void setMqttConnection(MQTTConnection mqttConnection) { 163 | this.mqttConnection = mqttConnection; 164 | } 165 | 166 | public String getClientIp() { 167 | return clientIp; 168 | } 169 | 170 | public void setClientIp(String clientIp) { 171 | this.clientIp = clientIp; 172 | } 173 | 174 | public String getServerIp() { 175 | return serverIp; 176 | } 177 | 178 | public void setServerIp(String serverIp) { 179 | this.serverIp = serverIp; 180 | } 181 | 182 | public Map getSubscriptions() { 183 | return subscriptions; 184 | } 185 | 186 | public void setSubscriptions(Map subscriptions) { 187 | this.subscriptions = subscriptions; 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /jmqtt-mqtt/src/main/java/org/jmqtt/mqtt/utils/MqttMessageUtil.java: -------------------------------------------------------------------------------- 1 | package org.jmqtt.mqtt.utils; 2 | 3 | import io.netty.buffer.ByteBuf; 4 | import io.netty.buffer.Unpooled; 5 | import io.netty.handler.codec.mqtt.*; 6 | import org.jmqtt.bus.model.DeviceMessage; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * transfer message from Message and MqttMessage 12 | */ 13 | public class MqttMessageUtil { 14 | 15 | public static byte[] readBytesFromByteBuf(ByteBuf byteBuf){ 16 | byte[] bytes = new byte[byteBuf.readableBytes()]; 17 | byteBuf.readBytes(bytes); 18 | return bytes; 19 | } 20 | 21 | 22 | public static MqttUnsubAckMessage getUnSubAckMessage(int messageId){ 23 | MqttFixedHeader fixedHeader = new MqttFixedHeader(MqttMessageType.UNSUBACK,false,MqttQoS.AT_MOST_ONCE,false,0); 24 | MqttMessageIdVariableHeader idVariableHeader = MqttMessageIdVariableHeader.from(messageId); 25 | return new MqttUnsubAckMessage(fixedHeader,idVariableHeader); 26 | } 27 | 28 | public static int getMessageId(MqttMessage mqttMessage){ 29 | MqttMessageIdVariableHeader idVariableHeader = (MqttMessageIdVariableHeader) mqttMessage.variableHeader(); 30 | return idVariableHeader.messageId(); 31 | } 32 | 33 | public static int getMinQos(int qos1,int qos2){ 34 | if(qos1 < qos2){ 35 | return qos1; 36 | } 37 | return qos2; 38 | } 39 | 40 | public static MqttMessage getPubRelMessage(int messageId){ 41 | MqttFixedHeader fixedHeader = new MqttFixedHeader(MqttMessageType.PUBREL,false,MqttQoS.AT_MOST_ONCE,false,0); 42 | MqttMessageIdVariableHeader idVariableHeader = MqttMessageIdVariableHeader.from(messageId); 43 | return new MqttMessage(fixedHeader,idVariableHeader); 44 | } 45 | 46 | public static MqttPublishMessage getPubMessage(DeviceMessage message,int packetId){ 47 | Integer qos = message.getProperty(MqttMsgHeader.QOS); 48 | qos = qos == null ? 1 : qos; 49 | 50 | Boolean dup = message.getProperty(MqttMsgHeader.DUP); 51 | dup = dup == null ? false : dup; 52 | 53 | MqttFixedHeader fixedHeader = new MqttFixedHeader(MqttMessageType.PUBLISH,dup,MqttQoS.valueOf(qos),false,0); 54 | MqttPublishVariableHeader publishVariableHeader = new MqttPublishVariableHeader(message.getTopic(),packetId); 55 | ByteBuf heapBuf; 56 | if(message.getContent() == null){ 57 | heapBuf = Unpooled.EMPTY_BUFFER; 58 | }else{ 59 | heapBuf = Unpooled.wrappedBuffer(message.getContent()); 60 | } 61 | return new MqttPublishMessage(fixedHeader,publishVariableHeader,heapBuf); 62 | } 63 | 64 | public static MqttMessage getSubAckMessage(int messageId, List qos){ 65 | MqttFixedHeader fixedHeader = new MqttFixedHeader(MqttMessageType.SUBACK,false,MqttQoS.AT_MOST_ONCE,false,0); 66 | MqttMessageIdVariableHeader idVariableHeader = MqttMessageIdVariableHeader.from(messageId); 67 | MqttSubAckPayload subAckPayload = new MqttSubAckPayload(qos); 68 | return new MqttSubAckMessage(fixedHeader,idVariableHeader,subAckPayload); 69 | } 70 | 71 | public static MqttMessage getPingRespMessage(){ 72 | MqttFixedHeader fixedHeader = new MqttFixedHeader(MqttMessageType.PINGRESP,false,MqttQoS.AT_MOST_ONCE,false,0); 73 | MqttMessage mqttMessage = new MqttMessage(fixedHeader); 74 | return mqttMessage; 75 | } 76 | 77 | public static MqttMessage getPubComMessage(int messageId){ 78 | MqttFixedHeader fixedHeader = new MqttFixedHeader(MqttMessageType.PUBCOMP,false,MqttQoS.AT_MOST_ONCE,false,0); 79 | MqttMessage mqttMessage = new MqttMessage(fixedHeader,MqttMessageIdVariableHeader.from(messageId)); 80 | return mqttMessage; 81 | } 82 | 83 | public static MqttMessage getPubRecMessage(int messageId){ 84 | MqttFixedHeader fixedHeader = new MqttFixedHeader(MqttMessageType.PUBREC,false,MqttQoS.AT_MOST_ONCE,false,0); 85 | MqttMessage mqttMessage = new MqttMessage(fixedHeader,MqttMessageIdVariableHeader.from(messageId)); 86 | return mqttMessage; 87 | } 88 | 89 | public static MqttMessage getPubRecMessage(int messageId,boolean isDup){ 90 | MqttFixedHeader fixedHeader = new MqttFixedHeader(MqttMessageType.PUBREC,isDup,MqttQoS.AT_MOST_ONCE,false,0); 91 | MqttMessage mqttMessage = new MqttMessage(fixedHeader,MqttMessageIdVariableHeader.from(messageId)); 92 | return mqttMessage; 93 | } 94 | 95 | public static MqttPubAckMessage getPubAckMessage(int messageId){ 96 | MqttFixedHeader fixedHeader = new MqttFixedHeader(MqttMessageType.PUBACK,false,MqttQoS.AT_MOST_ONCE,false,0); 97 | MqttMessageIdVariableHeader idVariableHeader = MqttMessageIdVariableHeader.from(messageId); 98 | return new MqttPubAckMessage(fixedHeader,idVariableHeader); 99 | } 100 | 101 | public static MqttConnAckMessage getConnectAckMessage(MqttConnectReturnCode returnCode,boolean sessionPresent){ 102 | MqttFixedHeader fixedHeader = new MqttFixedHeader(MqttMessageType.CONNACK, false, MqttQoS.AT_MOST_ONCE, false, 0); 103 | MqttConnAckVariableHeader variableHeade = new MqttConnAckVariableHeader(returnCode,sessionPresent); 104 | return new MqttConnAckMessage(fixedHeader,variableHeade); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /jmqtt-mqtt/src/main/java/org/jmqtt/mqtt/utils/MqttMsgHeader.java: -------------------------------------------------------------------------------- 1 | package org.jmqtt.mqtt.utils; 2 | 3 | import io.netty.handler.codec.mqtt.MqttPublishMessage; 4 | import org.jmqtt.bus.enums.MessageSourceEnum; 5 | import org.jmqtt.bus.model.DeviceMessage; 6 | 7 | import java.util.Date; 8 | import java.util.HashMap; 9 | import java.util.Map; 10 | 11 | public class MqttMsgHeader { 12 | 13 | public static final String QOS = "qos"; 14 | 15 | public static final String RETAIN = "retain"; 16 | 17 | public static final String DUP = "dup"; 18 | 19 | public static final String CLEAN_SESSION = "cleansession"; 20 | 21 | public static final DeviceMessage buildDeviceMessage(boolean retain,int qos,String topic,byte[] content){ 22 | DeviceMessage deviceMessage = new DeviceMessage(); 23 | deviceMessage.setId(null); 24 | deviceMessage.setContent(content); 25 | deviceMessage.setSource(MessageSourceEnum.DEVICE); 26 | deviceMessage.setStoredTime(new Date()); 27 | deviceMessage.setTopic(topic); 28 | 29 | Map properties = new HashMap<>(); 30 | properties.put(QOS,qos); 31 | properties.put(RETAIN,retain); 32 | deviceMessage.setProperties(properties); 33 | return deviceMessage; 34 | } 35 | 36 | public static final DeviceMessage buildDeviceMessage(MqttPublishMessage mqttPublishMessage){ 37 | return buildDeviceMessage(mqttPublishMessage.fixedHeader().isRetain(),mqttPublishMessage.fixedHeader().qosLevel().value(), 38 | mqttPublishMessage.variableHeader().topicName(),MqttMessageUtil.readBytesFromByteBuf(mqttPublishMessage.content())); 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /jmqtt-support/README.md: -------------------------------------------------------------------------------- 1 | 通用支持,支持消息总线模块Bus及各协议网关处理 2 | -------------------------------------------------------------------------------- /jmqtt-support/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | jmqtt 7 | org.jmqtt 8 | 3.0.0 9 | 10 | 4.0.0 11 | 12 | jmqtt-support 13 | 14 | jmqtt-support 15 | 16 | 17 | 18 | 19 | io.netty 20 | netty-all 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /jmqtt-support/src/main/java/org/jmqtt/support/config/BrokerConfig.java: -------------------------------------------------------------------------------- 1 | package org.jmqtt.support.config; 2 | 3 | import java.io.File; 4 | 5 | public class BrokerConfig { 6 | 7 | // 配置conf文件的所在位置,logback,properties文件等所在位置 8 | private String jmqttHome = System.getenv("JMQTT_HOME") != null ? System.getenv("JMQTT_HOME") : System.getProperty("user.dir") + 9 | File.separator + "jmqtt-broker" + File.separator + "src" + File.separator + "main" + File.separator + "resources" + File.separator + "conf"; 10 | private String logLevel = "INFO"; 11 | 12 | private String version = "3.0.2"; 13 | private boolean anonymousEnable = true; 14 | 15 | private int pollThreadNum = Runtime.getRuntime().availableProcessors() * 2; 16 | 17 | // 采用拉消息方式时,一次最多拉的消息数目 18 | private int maxPollEventNum = 10; 19 | private int pollWaitInterval = 10;//ms 20 | 21 | /* db相关配置 */ 22 | private String driver = "com.mysql.jdbc.Driver"; 23 | private String url 24 | = "jdbc:mysql://localhost:3306/jmqtt?characterEncoding=utf8&autoReconnect=true&failOverReadOnly=false" 25 | + "&maxReconnects=10&useSSL=false"; 26 | private String username = "root"; 27 | private String password = "CallmeZ2013"; 28 | 29 | public int getPollThreadNum() { 30 | return pollThreadNum; 31 | } 32 | 33 | public void setPollThreadNum(int pollThreadNum) { 34 | this.pollThreadNum = pollThreadNum; 35 | } 36 | 37 | public String getJmqttHome() { 38 | return jmqttHome; 39 | } 40 | 41 | public void setJmqttHome(String jmqttHome) { 42 | this.jmqttHome = jmqttHome; 43 | } 44 | 45 | public String getVersion() { 46 | return version; 47 | } 48 | 49 | public void setVersion(String version) { 50 | this.version = version; 51 | } 52 | 53 | public boolean getAnonymousEnable() { 54 | return anonymousEnable; 55 | } 56 | 57 | public void setAnonymousEnable(boolean anonymousEnable) { 58 | this.anonymousEnable = anonymousEnable; 59 | } 60 | 61 | public String getDriver() { 62 | return driver; 63 | } 64 | 65 | public void setDriver(String driver) { 66 | this.driver = driver; 67 | } 68 | 69 | public String getUrl() { 70 | return url; 71 | } 72 | 73 | public void setUrl(String url) { 74 | this.url = url; 75 | } 76 | 77 | public String getUsername() { 78 | return username; 79 | } 80 | 81 | public void setUsername(String username) { 82 | this.username = username; 83 | } 84 | 85 | public String getPassword() { 86 | return password; 87 | } 88 | 89 | public void setPassword(String password) { 90 | this.password = password; 91 | } 92 | 93 | public boolean isAnonymousEnable() { 94 | return anonymousEnable; 95 | } 96 | 97 | public int getMaxPollEventNum() { 98 | return maxPollEventNum; 99 | } 100 | 101 | public void setMaxPollEventNum(int maxPollEventNum) { 102 | this.maxPollEventNum = maxPollEventNum; 103 | } 104 | 105 | public int getPollWaitInterval() { 106 | return pollWaitInterval; 107 | } 108 | 109 | public void setPollWaitInterval(int pollWaitInterval) { 110 | this.pollWaitInterval = pollWaitInterval; 111 | } 112 | 113 | public String getLogLevel() { 114 | return logLevel; 115 | } 116 | 117 | public void setLogLevel(String logLevel) { 118 | this.logLevel = logLevel; 119 | } 120 | 121 | } 122 | -------------------------------------------------------------------------------- /jmqtt-support/src/main/java/org/jmqtt/support/config/NettyConfig.java: -------------------------------------------------------------------------------- 1 | package org.jmqtt.support.config; 2 | 3 | public class NettyConfig { 4 | private int tcpBackLog = 1024; 5 | private boolean tcpNoDelay = false; 6 | private boolean tcpReuseAddr = true; 7 | private boolean tcpKeepAlive = false; 8 | private int tcpSndBuf = 65536; 9 | private int tcpRcvBuf = 65536; 10 | private boolean useEpoll = false; 11 | private boolean pooledByteBufAllocatorEnable = false; 12 | 13 | /** 14 | * tcp port default 1883 15 | */ 16 | private boolean startTcp = true; 17 | private int tcpPort = 1883; 18 | 19 | /** 20 | * websocket port default 1884 21 | */ 22 | private boolean startWebsocket = true; 23 | private int websocketPort = 1884; 24 | 25 | /** 26 | * http port default 1881 27 | */ 28 | private boolean startHttp = true; 29 | private int httpPort = 1881; 30 | 31 | /** 32 | * tcp port with ssl default 8883 33 | */ 34 | private boolean startSslTcp = true; 35 | private int sslTcpPort = 8883; 36 | 37 | /** 38 | * websocket port with ssl default 8884 39 | */ 40 | private boolean startSslWebsocket = true; 41 | private int sslWebsocketPort = 8884; 42 | 43 | /** 44 | * SSL setting 45 | */ 46 | private boolean useClientCA = false; 47 | private String sslKeyStoreType = "PKCS12"; 48 | private String sslKeyFilePath = "/conf/server.pfx"; 49 | private String sslManagerPwd = "654321"; 50 | private String sslStorePwd = "654321"; 51 | /** 52 | * max mqtt message size 53 | */ 54 | private int maxMsgSize = 512 * 1024; 55 | 56 | public int getTcpBackLog() { 57 | return tcpBackLog; 58 | } 59 | 60 | public void setTcpBackLog(int tcpBackLog) { 61 | this.tcpBackLog = tcpBackLog; 62 | } 63 | 64 | public boolean getTcpNoDelay() { 65 | return tcpNoDelay; 66 | } 67 | 68 | public void setTcpNoDelay(boolean tcpNoDelay) { 69 | this.tcpNoDelay = tcpNoDelay; 70 | } 71 | 72 | public boolean getTcpReuseAddr() { 73 | return tcpReuseAddr; 74 | } 75 | 76 | public void setTcpReuseAddr(boolean tcpReuseAddr) { 77 | this.tcpReuseAddr = tcpReuseAddr; 78 | } 79 | 80 | public boolean getTcpKeepAlive() { 81 | return tcpKeepAlive; 82 | } 83 | 84 | public void setTcpKeepAlive(boolean tcpKeepAlive) { 85 | this.tcpKeepAlive = tcpKeepAlive; 86 | } 87 | 88 | public int getTcpSndBuf() { 89 | return tcpSndBuf; 90 | } 91 | 92 | public void setTcpSndBuf(int tcpSndBuf) { 93 | this.tcpSndBuf = tcpSndBuf; 94 | } 95 | 96 | public int getTcpRcvBuf() { 97 | return tcpRcvBuf; 98 | } 99 | 100 | public void setTcpRcvBuf(int tcpRcvBuf) { 101 | this.tcpRcvBuf = tcpRcvBuf; 102 | } 103 | 104 | public int getTcpPort() { 105 | return tcpPort; 106 | } 107 | 108 | public void setTcpPort(int tcpPort) { 109 | this.tcpPort = tcpPort; 110 | } 111 | 112 | public int getMaxMsgSize() { 113 | return maxMsgSize; 114 | } 115 | 116 | public void setMaxMsgSize(int maxMsgSize) { 117 | this.maxMsgSize = maxMsgSize; 118 | } 119 | 120 | public boolean getUseEpoll() { 121 | return useEpoll; 122 | } 123 | 124 | public void setUseEpoll(boolean useEpoll) { 125 | this.useEpoll = useEpoll; 126 | } 127 | 128 | public boolean getPooledByteBufAllocatorEnable() { 129 | return pooledByteBufAllocatorEnable; 130 | } 131 | 132 | public void setPooledByteBufAllocatorEnable(boolean pooledByteBufAllocatorEnable) { 133 | this.pooledByteBufAllocatorEnable = pooledByteBufAllocatorEnable; 134 | } 135 | 136 | public boolean getStartWebsocket() { 137 | return startWebsocket; 138 | } 139 | 140 | public void setStartWebsocket(boolean startWebsocket) { 141 | this.startWebsocket = startWebsocket; 142 | } 143 | 144 | public int getWebsocketPort() { 145 | return websocketPort; 146 | } 147 | 148 | public void setWebsocketPort(int websocketPort) { 149 | this.websocketPort = websocketPort; 150 | } 151 | 152 | public boolean getStartTcp() { 153 | return startTcp; 154 | } 155 | 156 | public void setStartTcp(boolean startTcp) { 157 | this.startTcp = startTcp; 158 | } 159 | 160 | 161 | public boolean getStartSslTcp() { 162 | return startSslTcp; 163 | } 164 | 165 | public void setStartSslTcp(boolean startSslTcp) { 166 | this.startSslTcp = startSslTcp; 167 | } 168 | 169 | 170 | public int getSslTcpPort() { 171 | return sslTcpPort; 172 | } 173 | 174 | public void setSslTcpPort(int sslTcpPort) { 175 | this.sslTcpPort = sslTcpPort; 176 | } 177 | 178 | public boolean getStartSslWebsocket() { 179 | return startSslWebsocket; 180 | } 181 | 182 | public void setStartSslWebsocket(boolean startSslWebsocket) { 183 | this.startSslWebsocket = startSslWebsocket; 184 | } 185 | 186 | public int getSslWebsocketPort() { 187 | return sslWebsocketPort; 188 | } 189 | 190 | public void setSslWebsocketPort(int sslWebsocketPort) { 191 | this.sslWebsocketPort = sslWebsocketPort; 192 | } 193 | 194 | public boolean getUseClientCA() { 195 | return useClientCA; 196 | } 197 | 198 | public void setUseClientCA(boolean useClientCA) { 199 | this.useClientCA = useClientCA; 200 | } 201 | 202 | public String getSslKeyStoreType() { 203 | return sslKeyStoreType; 204 | } 205 | 206 | public void setSslKeyStoreType(String sslKeyStoreType) { 207 | this.sslKeyStoreType = sslKeyStoreType; 208 | } 209 | 210 | public String getSslKeyFilePath() { 211 | return sslKeyFilePath; 212 | } 213 | 214 | public void setSslKeyFilePath(String sslKeyFilePath) { 215 | this.sslKeyFilePath = sslKeyFilePath; 216 | } 217 | 218 | public String getSslManagerPwd() { 219 | return sslManagerPwd; 220 | } 221 | 222 | public void setSslManagerPwd(String sslManagerPwd) { 223 | this.sslManagerPwd = sslManagerPwd; 224 | } 225 | 226 | public String getSslStorePwd() { 227 | return sslStorePwd; 228 | } 229 | 230 | public void setSslStorePwd(String sslStorePwd) { 231 | this.sslStorePwd = sslStorePwd; 232 | } 233 | 234 | public boolean getStartHttp() { 235 | return startHttp; 236 | } 237 | 238 | public void setStartHttp(boolean startHttp) { 239 | this.startHttp = startHttp; 240 | } 241 | 242 | public int getHttpPort() { 243 | return httpPort; 244 | } 245 | 246 | public void setHttpPort(int httpPort) { 247 | this.httpPort = httpPort; 248 | } 249 | } 250 | -------------------------------------------------------------------------------- /jmqtt-support/src/main/java/org/jmqtt/support/exception/RemotingConnectException.java: -------------------------------------------------------------------------------- 1 | package org.jmqtt.support.exception; 2 | 3 | import java.io.Serializable; 4 | 5 | public class RemotingConnectException extends RemotingException implements Serializable { 6 | 7 | private static final long serialVersionUID = -412312312370505110L; 8 | 9 | public RemotingConnectException(String addr) { 10 | this(addr,null); 11 | } 12 | 13 | public RemotingConnectException(String addr, Throwable throwable) { 14 | super("connect to <" + addr + "> failed", throwable); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /jmqtt-support/src/main/java/org/jmqtt/support/exception/RemotingException.java: -------------------------------------------------------------------------------- 1 | package org.jmqtt.support.exception; 2 | 3 | import java.io.Serializable; 4 | 5 | public class RemotingException extends Exception implements Serializable { 6 | private static final long serialVersionUID = -46545454570505110L; 7 | 8 | public RemotingException(String message){ 9 | super(message); 10 | } 11 | 12 | public RemotingException(String message,Throwable throwable){ 13 | super(message,throwable); 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /jmqtt-support/src/main/java/org/jmqtt/support/exception/RemotingSendRequestException.java: -------------------------------------------------------------------------------- 1 | package org.jmqtt.support.exception; 2 | 3 | import java.io.Serializable; 4 | 5 | public class RemotingSendRequestException extends RemotingException implements Serializable { 6 | 7 | public RemotingSendRequestException(String message) { 8 | this(message,null); 9 | } 10 | 11 | public RemotingSendRequestException(String message, Throwable throwable) { 12 | super(message, throwable); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /jmqtt-support/src/main/java/org/jmqtt/support/exception/RemotingTimeoutException.java: -------------------------------------------------------------------------------- 1 | package org.jmqtt.support.exception; 2 | 3 | import java.io.Serializable; 4 | 5 | public class RemotingTimeoutException extends RemotingException implements Serializable { 6 | 7 | private static final long serialVersionUID = -56656565470505110L; 8 | 9 | public RemotingTimeoutException(long timeoutMillis) { 10 | this(timeoutMillis,null); 11 | } 12 | 13 | public RemotingTimeoutException(long timeoutMillis, Throwable throwable) { 14 | super("Send timeout time: <" + timeoutMillis + ">", throwable); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /jmqtt-support/src/main/java/org/jmqtt/support/exception/RemotingTooMuchRequestException.java: -------------------------------------------------------------------------------- 1 | package org.jmqtt.support.exception; 2 | 3 | import java.io.Serializable; 4 | 5 | public class RemotingTooMuchRequestException extends RemotingException implements Serializable { 6 | 7 | private static final long serialVersionUID = -865546545670505110L; 8 | 9 | public RemotingTooMuchRequestException(String message) { 10 | super(message); 11 | } 12 | 13 | public RemotingTooMuchRequestException(String message, Throwable throwable) { 14 | super(message, throwable); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /jmqtt-support/src/main/java/org/jmqtt/support/helper/Pair.java: -------------------------------------------------------------------------------- 1 | package org.jmqtt.support.helper; 2 | 3 | public class Pair { 4 | private T object1; 5 | private K object2; 6 | 7 | public Pair(T object1, K object2) { 8 | this.object1 = object1; 9 | this.object2 = object2; 10 | } 11 | 12 | public T getObject1() { 13 | return object1; 14 | } 15 | 16 | public void setObject1(T object1) { 17 | this.object1 = object1; 18 | } 19 | 20 | public K getObject2() { 21 | return object2; 22 | } 23 | 24 | public void setObject2(K object2) { 25 | this.object2 = object2; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /jmqtt-support/src/main/java/org/jmqtt/support/helper/RejectHandler.java: -------------------------------------------------------------------------------- 1 | package org.jmqtt.support.helper; 2 | 3 | import java.util.concurrent.RejectedExecutionException; 4 | import java.util.concurrent.RejectedExecutionHandler; 5 | import java.util.concurrent.ThreadPoolExecutor; 6 | 7 | public class RejectHandler implements RejectedExecutionHandler { 8 | private String task; 9 | private int maxBlockQueueSize; 10 | 11 | public RejectHandler(String task,int maxBlockQueueSize){ 12 | this.task = task; 13 | this.maxBlockQueueSize = maxBlockQueueSize; 14 | }; 15 | 16 | @Override 17 | public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { 18 | throw new RejectedExecutionException("Task:" + task + ",maxBlockQueueSize:" + maxBlockQueueSize 19 | + ",Thread:" + r.toString()); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /jmqtt-support/src/main/java/org/jmqtt/support/helper/ThreadFactoryImpl.java: -------------------------------------------------------------------------------- 1 | package org.jmqtt.support.helper; 2 | 3 | import java.util.concurrent.ThreadFactory; 4 | import java.util.concurrent.atomic.AtomicInteger; 5 | 6 | public class ThreadFactoryImpl implements ThreadFactory { 7 | 8 | private AtomicInteger counter = new AtomicInteger(0); 9 | private String threadName; 10 | 11 | public ThreadFactoryImpl(String threadName){ 12 | this.threadName = threadName; 13 | } 14 | 15 | @Override 16 | public Thread newThread(Runnable r) { 17 | return new Thread(r,threadName + "_" + this.counter.incrementAndGet()); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /jmqtt-support/src/main/java/org/jmqtt/support/log/JmqttLogger.java: -------------------------------------------------------------------------------- 1 | package org.jmqtt.support.log; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | public interface JmqttLogger { 7 | 8 | Logger brokerLog = LoggerFactory.getLogger("brokerLog"); 9 | 10 | Logger mqttLog = LoggerFactory.getLogger("mqttLog"); 11 | 12 | Logger busLog = LoggerFactory.getLogger("busLog"); 13 | 14 | Logger messageTraceLog = LoggerFactory.getLogger("messageTraceLog"); 15 | 16 | Logger remotingLog = LoggerFactory.getLogger("remotingLog"); 17 | 18 | Logger storeLog = LoggerFactory.getLogger("storeLog"); 19 | 20 | Logger otherLog = LoggerFactory.getLogger("otherLog"); 21 | 22 | Logger monitorLog = LoggerFactory.getLogger("monitorLog"); 23 | 24 | } 25 | -------------------------------------------------------------------------------- /jmqtt-support/src/main/java/org/jmqtt/support/log/LogUtil.java: -------------------------------------------------------------------------------- 1 | package org.jmqtt.support.log; 2 | 3 | import org.slf4j.Logger; 4 | 5 | public class LogUtil { 6 | 7 | public static void debug(Logger log,String desc,Object... param){ 8 | if (log != null) { 9 | if (log.isDebugEnabled()) { 10 | log.debug(desc,param); 11 | } 12 | } 13 | } 14 | 15 | public static void info(Logger log,String desc,Object... param){ 16 | if (log != null) { 17 | if (log.isInfoEnabled()) { 18 | log.info(desc,param); 19 | } 20 | } 21 | } 22 | 23 | public static void warn(Logger log,String desc,Object... param){ 24 | if (log != null) { 25 | if (log.isWarnEnabled()) { 26 | log.warn(desc,param); 27 | } 28 | } 29 | } 30 | 31 | public static void error(Logger log,String desc,Object... param){ 32 | if (log != null) { 33 | if (log.isErrorEnabled()) { 34 | log.error(desc,param); 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /jmqtt-support/src/main/java/org/jmqtt/support/remoting/RemotingHelper.java: -------------------------------------------------------------------------------- 1 | package org.jmqtt.support.remoting; 2 | 3 | 4 | import io.netty.channel.Channel; 5 | import io.netty.channel.ChannelFuture; 6 | import io.netty.channel.ChannelFutureListener; 7 | import org.jmqtt.support.log.JmqttLogger; 8 | import org.jmqtt.support.log.LogUtil; 9 | import org.slf4j.Logger; 10 | 11 | import java.net.*; 12 | import java.util.ArrayList; 13 | import java.util.Enumeration; 14 | 15 | public class RemotingHelper { 16 | 17 | private static final Logger log = JmqttLogger.remotingLog; 18 | 19 | public static void closeChannel(Channel channel){ 20 | String remoteAddr = getRemoteAddr(channel); 21 | channel.close().addListener(new ChannelFutureListener() { 22 | @Override 23 | public void operationComplete(ChannelFuture channelFuture) throws Exception { 24 | LogUtil.info(log,"[closeChannel] -> close the connection,addr={},result={}",remoteAddr,channelFuture.isSuccess()); 25 | } 26 | }); 27 | } 28 | 29 | public static SocketAddress string2SocketAddress(final String addr){ 30 | String[] s = addr.split(":"); 31 | InetSocketAddress isa = new InetSocketAddress(s[0],Integer.parseInt(s[1])); 32 | return isa; 33 | } 34 | 35 | public static String getLocalAddr(){ 36 | try { 37 | Enumeration enumeration = NetworkInterface.getNetworkInterfaces(); 38 | ArrayList ipv4Result = new ArrayList(); 39 | ArrayList ipv6Result = new ArrayList(); 40 | while (enumeration.hasMoreElements()) { 41 | final NetworkInterface networkInterface = enumeration.nextElement(); 42 | final Enumeration en = networkInterface.getInetAddresses(); 43 | while (en.hasMoreElements()) { 44 | final InetAddress address = en.nextElement(); 45 | if (!address.isLoopbackAddress()) { 46 | if (address instanceof Inet6Address) { 47 | ipv6Result.add(normalizeHostAddress(address)); 48 | } else { 49 | ipv4Result.add(normalizeHostAddress(address)); 50 | } 51 | } 52 | } 53 | } 54 | 55 | // prefer ipv4 56 | if (!ipv4Result.isEmpty()) { 57 | for (String ip : ipv4Result) { 58 | if (ip.startsWith("127.0") || ip.startsWith("192.168")) { 59 | continue; 60 | } 61 | 62 | return ip; 63 | } 64 | 65 | return ipv4Result.get(ipv4Result.size() - 1); 66 | } else if (!ipv6Result.isEmpty()) { 67 | return ipv6Result.get(0); 68 | } 69 | //If failed to find,fall back to localhost 70 | final InetAddress localHost = InetAddress.getLocalHost(); 71 | return normalizeHostAddress(localHost); 72 | } catch (Exception e) { 73 | LogUtil.error(log,"failed to get local addr",e); 74 | } 75 | return null; 76 | } 77 | 78 | private static String normalizeHostAddress(final InetAddress localHost) { 79 | if (localHost instanceof Inet6Address) { 80 | return "[" + localHost.getHostAddress() + "]"; 81 | } else { 82 | return localHost.getHostAddress(); 83 | } 84 | } 85 | 86 | public static String getRemoteAddr(Channel channel){ 87 | if (null == channel) { 88 | return ""; 89 | } 90 | SocketAddress remote = channel.remoteAddress(); 91 | final String addr = remote != null ? remote.toString() : ""; 92 | if (addr.length() > 0) { 93 | int index = addr.lastIndexOf("/"); 94 | if (index >= 0) { 95 | return addr.substring(index + 1); 96 | } 97 | 98 | return addr; 99 | } 100 | return ""; 101 | } 102 | 103 | 104 | } 105 | -------------------------------------------------------------------------------- /jmqtt-support/src/main/java/org/jmqtt/support/remoting/RemotingService.java: -------------------------------------------------------------------------------- 1 | package org.jmqtt.support.remoting; 2 | 3 | public interface RemotingService { 4 | 5 | /** 6 | * remoting start 7 | */ 8 | void start(); 9 | 10 | /** 11 | * remoting shutdown 12 | */ 13 | void shutdown(); 14 | 15 | } 16 | -------------------------------------------------------------------------------- /jmqtt-tcp/README.md: -------------------------------------------------------------------------------- 1 | 自定义tcp协议网关 2 | 待实现 3 | -------------------------------------------------------------------------------- /jmqtt-tcp/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | jmqtt 7 | org.jmqtt 8 | 3.0.0 9 | 10 | 4.0.0 11 | jmqtt-tcp 12 | jmqtt-tcp 13 | 14 | 15 | -------------------------------------------------------------------------------- /jmqtt-tcp/src/main/java/org/jmqtt/App.java: -------------------------------------------------------------------------------- 1 | package org.jmqtt; 2 | 3 | /** 4 | * Hello world! 5 | * 6 | */ 7 | public class App 8 | { 9 | public static void main( String[] args ) 10 | { 11 | System.out.println( "Hello World!" ); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /jmqtt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cicizz/jmqtt/032ac7eababf54e072910b49e86c18716abdaab6/jmqtt.png -------------------------------------------------------------------------------- /jmqtt_qq2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cicizz/jmqtt/032ac7eababf54e072910b49e86c18716abdaab6/jmqtt_qq2.png -------------------------------------------------------------------------------- /vx_mangdagou.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cicizz/jmqtt/032ac7eababf54e072910b49e86c18716abdaab6/vx_mangdagou.jpg --------------------------------------------------------------------------------