This is an automated email from the ASF dual-hosted git repository.

zhaoqingran pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/hertzbeat.git


The following commit(s) were added to refs/heads/master by this push:
     new 6074c08e1 [feat] support monitor MQTT connections (#2618)
6074c08e1 is described below

commit 6074c08e1a125580e90816b303769bf121b49189
Author: liutianyou <[email protected]>
AuthorDate: Thu Aug 29 11:05:54 2024 +0800

    [feat] support monitor MQTT connections (#2618)
    
    Co-authored-by: shown <[email protected]>
    Co-authored-by: Logic <[email protected]>
---
 collector/pom.xml                                  |   6 +
 .../collector/collect/mqtt/MqttCollectImpl.java    | 231 +++++++++++++++++++++
 .../collector/dispatch/DispatchConstants.java      |   5 +
 ...che.hertzbeat.collector.collect.AbstractCollect |   1 +
 .../hertzbeat/common/entity/job/Metrics.java       |   5 +
 .../common/entity/job/protocol/MqttProtocol.java   |  95 +++++++++
 manager/src/main/resources/define/app-mqtt.yml     | 170 +++++++++++++++
 material/licenses/collector/LICENSE                |   7 +-
 8 files changed, 517 insertions(+), 3 deletions(-)

diff --git a/collector/pom.xml b/collector/pom.xml
index 2d93db597..fea17e384 100644
--- a/collector/pom.xml
+++ b/collector/pom.xml
@@ -206,6 +206,12 @@
                 </exclusion>
             </exclusions>
         </dependency>
+        <!-- mqtt -->
+        <dependency>
+            <groupId>com.hivemq</groupId>
+            <artifactId>hivemq-mqtt-client</artifactId>
+            <version>1.3.3</version>
+        </dependency>
     </dependencies>
 
     <build>
diff --git 
a/collector/src/main/java/org/apache/hertzbeat/collector/collect/mqtt/MqttCollectImpl.java
 
b/collector/src/main/java/org/apache/hertzbeat/collector/collect/mqtt/MqttCollectImpl.java
new file mode 100644
index 000000000..14f8e1814
--- /dev/null
+++ 
b/collector/src/main/java/org/apache/hertzbeat/collector/collect/mqtt/MqttCollectImpl.java
@@ -0,0 +1,231 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hertzbeat.collector.collect.mqtt;
+
+import com.hivemq.client.mqtt.MqttVersion;
+import com.hivemq.client.mqtt.datatypes.MqttQos;
+import com.hivemq.client.mqtt.mqtt3.Mqtt3AsyncClient;
+import com.hivemq.client.mqtt.mqtt3.Mqtt3Client;
+import com.hivemq.client.mqtt.mqtt3.Mqtt3ClientBuilder;
+import com.hivemq.client.mqtt.mqtt3.message.connect.connack.Mqtt3ConnAck;
+import com.hivemq.client.mqtt.mqtt5.Mqtt5AsyncClient;
+import com.hivemq.client.mqtt.mqtt5.Mqtt5Client;
+import com.hivemq.client.mqtt.mqtt5.Mqtt5ClientBuilder;
+import com.hivemq.client.mqtt.mqtt5.message.connect.connack.Mqtt5ConnAck;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.hertzbeat.collector.collect.AbstractCollect;
+import org.apache.hertzbeat.collector.constants.CollectorConstants;
+import org.apache.hertzbeat.collector.dispatch.DispatchConstants;
+import org.apache.hertzbeat.common.constants.CommonConstants;
+import org.apache.hertzbeat.common.entity.job.Metrics;
+import org.apache.hertzbeat.common.entity.job.protocol.MqttProtocol;
+import org.apache.hertzbeat.common.entity.message.CollectRep;
+import 
org.apache.hertzbeat.common.entity.message.CollectRep.MetricsData.Builder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.util.Assert;
+import org.springframework.util.StopWatch;
+
+/**
+ * collect mqtt metrics
+ */
+public class MqttCollectImpl extends AbstractCollect {
+
+    public MqttCollectImpl() {
+    }
+
+    private static final Logger logger = 
LoggerFactory.getLogger(MqttCollectImpl.class);
+
+    @Override
+    public void preCheck(Metrics metrics) throws IllegalArgumentException {
+        MqttProtocol mqttProtocol = metrics.getMqtt();
+        Assert.hasText(mqttProtocol.getHost(), "MQTT protocol host is 
required");
+        Assert.hasText(mqttProtocol.getPort(), "MQTT protocol port is 
required");
+        Assert.hasText(mqttProtocol.getProtocolVersion(), "MQTT protocol 
version is required");
+    }
+
+    @Override
+    public void collect(Builder builder, long monitorId, String app, Metrics 
metrics) {
+        MqttProtocol mqtt = metrics.getMqtt();
+        String protocolVersion = mqtt.getProtocolVersion();
+        MqttVersion mqttVersion = MqttVersion.valueOf(protocolVersion);
+        if (mqttVersion == MqttVersion.MQTT_3_1_1) {
+            collectWithVersion3(metrics, builder);
+        } else if (mqttVersion == MqttVersion.MQTT_5_0) {
+            collectWithVersion5(metrics, builder);
+        }
+    }
+
+    @Override
+    public String supportProtocol() {
+        return DispatchConstants.PROTOCOL_MQTT;
+    }
+
+    /**
+     * collecting data of MQTT 5
+     */
+    private void collectWithVersion5(Metrics metrics, Builder builder) {
+        MqttProtocol mqttProtocol = metrics.getMqtt();
+        Map<Object, String> data = new HashMap<>();
+        Mqtt5AsyncClient client = buildMqtt5Client(mqttProtocol);
+        long responseTime = connectClient(client, mqtt5AsyncClient -> {
+            CompletableFuture<Mqtt5ConnAck> connectFuture = 
mqtt5AsyncClient.connect();
+            try {
+                connectFuture.get(Long.parseLong(mqttProtocol.getTimeout()), 
TimeUnit.MILLISECONDS);
+            } catch (InterruptedException | ExecutionException | 
TimeoutException e) {
+                builder.setCode(CollectRep.Code.FAIL);
+                builder.setMsg(getErrorMessage(e.getMessage()));
+            }
+        });
+        testDescribeAndPublish5(client, mqttProtocol, data);
+        convertToMetricsData(builder, metrics, responseTime, data);
+        client.disconnect();
+    }
+
+    /**
+     * collecting data of MQTT 3.1.1
+     */
+    private void collectWithVersion3(Metrics metrics, Builder builder) {
+        MqttProtocol mqttProtocol = metrics.getMqtt();
+        Map<Object, String> data = new HashMap<>();
+        Mqtt3AsyncClient client = buildMqtt3Client(mqttProtocol);
+        long responseTime = connectClient(client, mqtt3AsyncClient -> {
+            CompletableFuture<Mqtt3ConnAck> connectFuture = 
mqtt3AsyncClient.connect();
+            try {
+                connectFuture.get(Long.parseLong(mqttProtocol.getTimeout()), 
TimeUnit.MILLISECONDS);
+            } catch (InterruptedException | ExecutionException | 
TimeoutException e) {
+                builder.setCode(CollectRep.Code.FAIL);
+                builder.setMsg(getErrorMessage(e.getMessage()));
+            }
+        });
+        testDescribeAndPublish3(client, mqttProtocol, data);
+        convertToMetricsData(builder, metrics, responseTime, data);
+        client.disconnect();
+    }
+
+    private void testDescribeAndPublish3(Mqtt3AsyncClient client, MqttProtocol 
mqttProtocol, Map<Object, String> data) {
+        data.put("canDescribe", test(() -> {
+            
client.subscribeWith().topicFilter(mqttProtocol.getTopic()).qos(MqttQos.AT_LEAST_ONCE).send();
+            
client.unsubscribeWith().topicFilter(mqttProtocol.getTopic()).send();
+        }, "subscribe").toString());
+
+        data.put("canPublish", !mqttProtocol.testPublish() ? 
Boolean.FALSE.toString() : test(() -> {
+            client.publishWith().topic(mqttProtocol.getTopic())
+                
.payload(mqttProtocol.getTestMessage().getBytes(StandardCharsets.UTF_8))
+                .qos(MqttQos.AT_LEAST_ONCE).send();
+            data.put("canPublish", Boolean.TRUE.toString());
+        }, "publish").toString());
+    }
+
+    private void testDescribeAndPublish5(Mqtt5AsyncClient client, MqttProtocol 
mqttProtocol, Map<Object, String> data) {
+        data.put("canDescribe", test(() -> {
+            
client.subscribeWith().topicFilter(mqttProtocol.getTopic()).qos(MqttQos.AT_LEAST_ONCE).send();
+            
client.unsubscribeWith().topicFilter(mqttProtocol.getTopic()).send();
+        }, "subscribe").toString());
+
+        data.put("canPublish", !mqttProtocol.testPublish() ? 
Boolean.FALSE.toString() : test(() -> {
+            client.publishWith().topic(mqttProtocol.getTopic())
+                
.payload(mqttProtocol.getTestMessage().getBytes(StandardCharsets.UTF_8))
+                .qos(MqttQos.AT_LEAST_ONCE).send();
+            data.put("canPublish", Boolean.TRUE.toString());
+        }, "publish").toString());
+    }
+
+    private Mqtt5AsyncClient buildMqtt5Client(MqttProtocol mqttProtocol) {
+        Mqtt5ClientBuilder mqtt5ClientBuilder = Mqtt5Client.builder()
+            .serverHost(mqttProtocol.getHost())
+            .identifier(mqttProtocol.getClientId())
+            .serverPort(Integer.parseInt(mqttProtocol.getPort()));
+
+        if (mqttProtocol.hasAuth()) {
+            
mqtt5ClientBuilder.simpleAuth().username(mqttProtocol.getUsername())
+                
.password(mqttProtocol.getPassword().getBytes(StandardCharsets.UTF_8))
+                .applySimpleAuth();
+        }
+        return mqtt5ClientBuilder.buildAsync();
+    }
+
+    private Mqtt3AsyncClient buildMqtt3Client(MqttProtocol mqttProtocol) {
+
+        Mqtt3ClientBuilder mqtt3ClientBuilder = Mqtt3Client.builder()
+            .serverHost(mqttProtocol.getHost())
+            .identifier(mqttProtocol.getClientId())
+            .serverPort(Integer.parseInt(mqttProtocol.getPort()));
+
+        if (mqttProtocol.hasAuth()) {
+            
mqtt3ClientBuilder.simpleAuth().username(mqttProtocol.getUsername())
+                
.password(mqttProtocol.getPassword().getBytes(StandardCharsets.UTF_8))
+                .applySimpleAuth();
+        }
+        return mqtt3ClientBuilder.buildAsync();
+    }
+
+    public <T> long connectClient(T client, Consumer<T> connect) {
+        StopWatch stopWatch = new StopWatch();
+        stopWatch.start();
+        connect.accept(client);
+        stopWatch.stop();
+        return stopWatch.getTotalTimeMillis();
+    }
+
+    private void convertToMetricsData(Builder builder, Metrics metrics, long 
responseTime, Map<Object, String> data) {
+        CollectRep.ValueRow.Builder valueRowBuilder = 
CollectRep.ValueRow.newBuilder();
+        for (String column : metrics.getAliasFields()) {
+            if (CollectorConstants.RESPONSE_TIME.equals(column)) {
+                valueRowBuilder.addColumns(String.valueOf(responseTime));
+            } else {
+                String value = data.get(column);
+                value = value == null ? CommonConstants.NULL_VALUE : value;
+                valueRowBuilder.addColumns(value);
+            }
+        }
+        builder.addValues(valueRowBuilder.build());
+    }
+
+    private Boolean test(Runnable runnable, String operationName) {
+        try {
+            runnable.run();
+            return true;
+        } catch (Exception e) {
+            logger.error("{} fail", operationName, e);
+        }
+        return false;
+    }
+
+    private String getErrorMessage(String errorMessage) {
+        if (StringUtils.isBlank(errorMessage)) {
+            return "connect failed";
+        }
+        String[] split = errorMessage.split(":");
+        if (split.length > 1) {
+            return 
Arrays.stream(split).skip(1).collect(Collectors.joining(":"));
+        }
+        return errorMessage;
+    }
+
+}
diff --git 
a/collector/src/main/java/org/apache/hertzbeat/collector/dispatch/DispatchConstants.java
 
b/collector/src/main/java/org/apache/hertzbeat/collector/dispatch/DispatchConstants.java
index 0dac9c39a..8be5eab9b 100644
--- 
a/collector/src/main/java/org/apache/hertzbeat/collector/dispatch/DispatchConstants.java
+++ 
b/collector/src/main/java/org/apache/hertzbeat/collector/dispatch/DispatchConstants.java
@@ -137,6 +137,11 @@ public interface DispatchConstants {
      */
     String PROTOCOL_SCRIPT = "script";
 
+    /**
+     * protocol mqtt
+     */
+    String PROTOCOL_MQTT = "mqtt";
+
     // Protocol type related - end
 
     // http protocol related - start should reuse HttpHeaders as much as 
possible
diff --git 
a/collector/src/main/resources/META-INF/services/org.apache.hertzbeat.collector.collect.AbstractCollect
 
b/collector/src/main/resources/META-INF/services/org.apache.hertzbeat.collector.collect.AbstractCollect
index ddf77d2f7..91ee9e1b5 100644
--- 
a/collector/src/main/resources/META-INF/services/org.apache.hertzbeat.collector.collect.AbstractCollect
+++ 
b/collector/src/main/resources/META-INF/services/org.apache.hertzbeat.collector.collect.AbstractCollect
@@ -25,3 +25,4 @@ 
org.apache.hertzbeat.collector.collect.redfish.RedfishCollectImpl
 org.apache.hertzbeat.collector.collect.nebulagraph.NgqlCollectImpl
 org.apache.hertzbeat.collector.collect.imap.ImapCollectImpl
 org.apache.hertzbeat.collector.collect.script.ScriptCollectImpl
+org.apache.hertzbeat.collector.collect.mqtt.MqttCollectImpl
diff --git 
a/common/src/main/java/org/apache/hertzbeat/common/entity/job/Metrics.java 
b/common/src/main/java/org/apache/hertzbeat/common/entity/job/Metrics.java
index 99d25fa6a..37b96db33 100644
--- a/common/src/main/java/org/apache/hertzbeat/common/entity/job/Metrics.java
+++ b/common/src/main/java/org/apache/hertzbeat/common/entity/job/Metrics.java
@@ -38,6 +38,7 @@ import 
org.apache.hertzbeat.common.entity.job.protocol.JdbcProtocol;
 import org.apache.hertzbeat.common.entity.job.protocol.JmxProtocol;
 import org.apache.hertzbeat.common.entity.job.protocol.MemcachedProtocol;
 import org.apache.hertzbeat.common.entity.job.protocol.MongodbProtocol;
+import org.apache.hertzbeat.common.entity.job.protocol.MqttProtocol;
 import org.apache.hertzbeat.common.entity.job.protocol.NebulaGraphProtocol;
 import org.apache.hertzbeat.common.entity.job.protocol.NginxProtocol;
 import org.apache.hertzbeat.common.entity.job.protocol.NgqlProtocol;
@@ -225,6 +226,10 @@ public class Metrics {
      * Monitoring configuration information using the public script protocol
      */
     private ScriptProtocol script;
+    /**
+     * Monitoring configuration information using the public mqtt protocol
+     */
+    private MqttProtocol mqtt;
 
     /**
      * collector use - Temporarily store subTask metrics response data
diff --git 
a/common/src/main/java/org/apache/hertzbeat/common/entity/job/protocol/MqttProtocol.java
 
b/common/src/main/java/org/apache/hertzbeat/common/entity/job/protocol/MqttProtocol.java
new file mode 100644
index 000000000..c14c16221
--- /dev/null
+++ 
b/common/src/main/java/org/apache/hertzbeat/common/entity/job/protocol/MqttProtocol.java
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hertzbeat.common.entity.job.protocol;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.apache.commons.lang3.StringUtils;
+
+/**
+ * mqtt protocol
+ */
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+public class MqttProtocol {
+
+    /**
+     * ip address or domain name of the peer host
+     */
+    private String host;
+
+    /**
+     * peer host port
+     */
+    private String port;
+
+    /**
+     * username
+     */
+    private String username;
+
+    /**
+     * password
+     */
+    private String password;
+
+    /**
+     * time out period
+     */
+    private String timeout;
+
+    /**
+     * client id
+     */
+    private String clientId;
+
+    /**
+     * message used to test whether the mqtt connection can be pushed normally
+     */
+    private String testMessage;
+
+    /**
+     * protocol version of mqtt
+     */
+    private String protocolVersion;
+
+    /**
+     * monitor topic
+     */
+    private String topic;
+
+    /**
+     * Determine whether authentication is required
+     * @return true if it has auth info
+     */
+    public boolean hasAuth() {
+        return StringUtils.isNotBlank(this.username) && 
StringUtils.isNotBlank(this.password);
+    }
+
+    /**
+     * Determine whether you need to test whether messages can be pushed 
normally
+     * @return turn if it has test message
+     */
+    public boolean testPublish(){
+        return StringUtils.isNotBlank(this.testMessage);
+    }
+}
diff --git a/manager/src/main/resources/define/app-mqtt.yml 
b/manager/src/main/resources/define/app-mqtt.yml
new file mode 100644
index 000000000..c8f985327
--- /dev/null
+++ b/manager/src/main/resources/define/app-mqtt.yml
@@ -0,0 +1,170 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# The monitoring type category:service-application service monitoring 
db-database monitoring mid-middleware custom-custom monitoring os-operating 
system monitoring
+category: service
+# The monitoring type eg: linux windows tomcat mysql aws...
+app: mqtt
+# The app api i18n name
+name:
+  zh-CN: MQTT 连接
+  en-US: MQTT Connection
+# The description and help of this monitoring type
+help:
+  zh-CN: HertzBeat 对 MQTT 连接进行监测。<br>您可以点击 “<i>新建 MQTT 连接</i>” 
并进行配置,或者选择“<i>更多操作</i>”,导入已有配置。
+  en-US: HertzBeat monitors MQTT connections. <br>You can click "<i>New MQTT 
connection</i>" and configure it, or select "<i>More actions</i>" to import an 
existing configuration.
+  zh-TW: HertzBeat 對 MQTT 連接進行監測。 <br>您可以點選 “<i>新建 MQTT 連線</i>” 
並進行配置,或選擇“<i>更多操作</i>”,匯入已有配置。
+# Input params define for monitoring(render web ui by the definition)
+params:
+  # field-param field key
+  - field: host
+    # name-param field display i18n name
+    name:
+      zh-CN: MQTT的Host
+      en-US: Target Host
+    # type-param field type(most mapping the html input type)
+    type: host
+    # required-true or false
+    required: true
+  # field-param field key
+  - field: port
+    # name-param field display i18n name
+    name:
+      zh-CN: 端口
+      en-US: Port
+    # type-param field type(most mapping the html input type)
+    type: number
+    # when type is number, range is required
+    range: '[0,65535]'
+    # required-true or false
+    required: true
+    # default value 1883
+    defaultValue: 1883
+  - field: protocolVersion
+    name:
+      zh-CN: 协议版本
+      en-US: Protocol version
+    type: radio
+    options:
+      - label: MQTT 3.1.1
+        value: MQTT_3_1_1
+      - label: MQTT 5.0
+        value: MQTT_5_0
+    required: true
+    defaultValue: MQTT_3_1_1
+  # field-param field key
+  - field: timeout
+    # name-param field display i18n name
+    name:
+      zh-CN: 连接超时时间(ms)
+      en-US: Connect Timeout(ms)
+    # type-param field type(most mapping the html input type)
+    type: number
+    # when type is number, range is required
+    range: '[0,100000]'
+    # required-true or false
+    required: true
+    # default value 6000
+    defaultValue: 6000
+  # field-param field key
+  - field: username
+    name:
+      zh-CN: 用户名
+      en-US: Username
+    type: text
+    hide: true
+    # required-true or false
+    required: false
+  - field: password
+    name:
+      zh-CN: 密码
+      en-US: Password
+    type: text
+    hide: true
+    # required-true or false
+    required: false
+  - field: clientId
+    name:
+      zh-CN: 客户端ID
+      en-US: Client Id
+    type: text
+    defaultValue: hertzbeat-mqtt-client
+    # required-true or false
+    required: true
+
+  - field: topic
+    name:
+      zh-CN: 主题
+      en-US: Topic
+    type: text
+    required: true
+  - field: testMessage
+    name:
+      zh-CN: 测试消息
+      en-US: Test message
+    type: text
+    required: false
+# collect metrics config list
+metrics:
+  # metrics - summary
+  - name: summary
+    i18n:
+      zh-CN: 概要
+      en-US: Summary
+    # metrics scheduling priority(0->127)->(high->low), metrics with the same 
priority will be scheduled in parallel
+    # priority 0's metrics is availability metrics, it will be scheduled 
first, only availability metrics collect success will the scheduling continue
+    priority: 0
+    # field-metric name, type-metric type(0-number,1-string), unit-metric 
unit('%','ms','MB'), label-whether it is a metrics label field
+    fields:
+      - field: responseTime
+        type: 0
+        unit: ms
+        i18n:
+          zh-CN: 响应时间
+          en-US: Response Time
+      - field: canDescribe
+        type: 1
+        i18n:
+          zh-CN: 正常订阅
+          en-US: Normal subscription
+      - field: canPublish
+        type: 1
+        i18n:
+          zh-CN: 正常推送
+          en-US: Normal publish
+    # the protocol used for monitoring, eg: sql, ssh, http, telnet, wmi, snmp, 
sdk
+    protocol: mqtt
+    # Specific collection configuration when protocol is telnet protocol
+    mqtt:
+      # telnet host
+      host: ^_^host^_^
+      # port
+      port: ^_^port^_^
+      # timeout
+      timeout: ^_^timeout^_^
+      # email
+      topic: ^_^topic^_^
+      # clientId
+      clientId: ^_^clientId^_^
+      # protocolVersion
+      protocolVersion: ^_^protocolVersion^_^
+      # username
+      username: ^_^username^_^
+      # password
+      password: ^_^password^_^
+      # testMessage
+      testMessage: ^_^testMessage^_^
+
+
diff --git a/material/licenses/collector/LICENSE 
b/material/licenses/collector/LICENSE
index 1fbaa5618..2620a42bd 100644
--- a/material/licenses/collector/LICENSE
+++ b/material/licenses/collector/LICENSE
@@ -324,6 +324,7 @@ The text of each license is the standard Apache 2.0 license.
     https://mvnrepository.com/artifact/org.springframework/spring-webmvc/6.1.4 
Apache-2.0
     
https://mvnrepository.com/artifact/org.apache.tomcat.embed/tomcat-embed-core/10.1.19
 Apache-2.0
     https://mvnrepository.com/artifact/com.vesoft/client/3.6.0 Apache-2.0
+    https://mvnrepository.com/artifact/com.hivemq/hivemq-mqtt-client/1.3.3 
Apache-2.0
 
 
 ========================================================================
@@ -374,9 +375,9 @@ The text of each license is also included in 
licenses/LICENSE-[project].txt.
     
https://mvnrepository.com/artifact/jakarta.persistence/jakarta.persistence-api/3.1.0
     
https://mvnrepository.com/artifact/jakarta.annotation/jakarta.annotation-api/2.1.1
     https://mvnrepository.com/artifact/org.aspectj/aspectjweaver/1.9.21
-    
https://mvnrepository.com/artifact/org.eclipse.persistence/org.eclipse.persistence.asm/9.5.0
 
-    
https://mvnrepository.com/artifact/org.eclipse.persistence/org.eclipse.persistence.core/4.0.2
 
-    
https://mvnrepository.com/artifact/org.eclipse.persistence/org.eclipse.persistence.jpa/4.0.2
 
+    
https://mvnrepository.com/artifact/org.eclipse.persistence/org.eclipse.persistence.asm/9.5.0
+    
https://mvnrepository.com/artifact/org.eclipse.persistence/org.eclipse.persistence.core/4.0.2
+    
https://mvnrepository.com/artifact/org.eclipse.persistence/org.eclipse.persistence.jpa/4.0.2
     
https://mvnrepository.com/artifact/org.eclipse.persistence/org.eclipse.persistence.jpa.jpql/4.0.2
 
 


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to