diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/DataConstants.java b/common/data/src/main/java/org/thingsboard/server/common/data/DataConstants.java index 02871a59b6..22123a7ea3 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/DataConstants.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/DataConstants.java @@ -43,6 +43,7 @@ public class DataConstants { public static final String RETRIES = "retries"; public static final String EDGE_ID = "edgeId"; public static final String DEVICE_ID = "deviceId"; + public static final String GATEWAY_PARAMETER = "gateway"; public static final String COAP_TRANSPORT_NAME = "COAP"; public static final String LWM2M_TRANSPORT_NAME = "LWM2M"; public static final String MQTT_TRANSPORT_NAME = "MQTT"; diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceConnectivityServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceConnectivityServiceImpl.java index c06103d8f3..1d1c627a66 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceConnectivityServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceConnectivityServiceImpl.java @@ -19,11 +19,13 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.extern.slf4j.Slf4j; +import org.jetbrains.annotations.NotNull; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; import org.springframework.stereotype.Service; import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.DeviceTransportType; @@ -46,10 +48,13 @@ import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.CHECK_DOCUM import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.COAP; import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.COAPS; import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.DOCKER; +import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.GATEWAY; import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.HTTP; import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.HTTPS; +import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.LINUX; import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.MQTT; import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.MQTTS; +import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.WINDOWS; @Service("DeviceConnectivityDaoService") @Slf4j @@ -79,6 +84,12 @@ public class DeviceConnectivityServiceImpl implements DeviceConnectivityService DeviceTransportType transportType = deviceProfile.getTransportType(); ObjectNode commands = JacksonUtil.newObjectNode(); + if (checkIsGateway(device)) { + Optional.ofNullable(getMqttTransportPublishCommands(baseUrl, creds, true)) + .ifPresent(v -> commands.set(MQTT, v)); + return commands; + } + switch (transportType) { case DEFAULT: Optional.ofNullable(getHttpTransportPublishCommands(baseUrl, creds)) @@ -148,10 +159,18 @@ public class DeviceConnectivityServiceImpl implements DeviceConnectivityService } private JsonNode getMqttTransportPublishCommands(String baseUrl, DeviceCredentials deviceCredentials) throws URISyntaxException { - return getMqttTransportPublishCommands(baseUrl, DEFAULT_DEVICE_TELEMETRY_TOPIC, deviceCredentials); + return getMqttTransportPublishCommands(baseUrl, deviceCredentials, false); + } + + private JsonNode getMqttTransportPublishCommands(String baseUrl, DeviceCredentials deviceCredentials, boolean isGateway) throws URISyntaxException { + return getMqttTransportPublishCommands(baseUrl, DEFAULT_DEVICE_TELEMETRY_TOPIC, deviceCredentials, isGateway); } private JsonNode getMqttTransportPublishCommands(String baseUrl, String topic, DeviceCredentials deviceCredentials) throws URISyntaxException { + return getMqttTransportPublishCommands(baseUrl, topic, deviceCredentials, false); + } + + private JsonNode getMqttTransportPublishCommands(String baseUrl, String topic, DeviceCredentials deviceCredentials, boolean isGateway) throws URISyntaxException { ObjectNode mqttCommands = JacksonUtil.newObjectNode(); if (deviceCredentials.getCredentialsType() == DeviceCredentialsType.X509_CERTIFICATE) { @@ -160,6 +179,7 @@ public class DeviceConnectivityServiceImpl implements DeviceConnectivityService } ObjectNode dockerMqttCommands = JacksonUtil.newObjectNode(); + ObjectNode gatewayDockerMqttCommands = JacksonUtil.newObjectNode(); if (deviceConnectivityConfiguration.isEnabled(MQTT)) { Optional.ofNullable(getMqttPublishCommand(baseUrl, topic, deviceCredentials)). @@ -167,6 +187,11 @@ public class DeviceConnectivityServiceImpl implements DeviceConnectivityService Optional.ofNullable(getDockerMqttPublishCommand(MQTT, baseUrl, topic, deviceCredentials)) .ifPresent(v -> dockerMqttCommands.put(MQTT, v)); + + if (isGateway) { + Optional.ofNullable(getGatewayDockerCommands(baseUrl, deviceCredentials, MQTT)) + .ifPresent(v -> gatewayDockerMqttCommands.set(MQTT, v)); + } } if (deviceConnectivityConfiguration.isEnabled(MQTTS)) { @@ -178,11 +203,21 @@ public class DeviceConnectivityServiceImpl implements DeviceConnectivityService Optional.ofNullable(getDockerMqttPublishCommand(MQTTS, baseUrl, topic, deviceCredentials)) .ifPresent(v -> dockerMqttCommands.put(MQTTS, v)); + + if (isGateway) { + Optional.ofNullable(getGatewayDockerCommands(baseUrl, deviceCredentials, MQTTS)) + .ifPresent(v -> gatewayDockerMqttCommands.set(MQTTS, v)); + } } if (!dockerMqttCommands.isEmpty()) { mqttCommands.set(DOCKER, dockerMqttCommands); } + + if (!gatewayDockerMqttCommands.isEmpty()) { + mqttCommands.set(GATEWAY, gatewayDockerMqttCommands); + } + return mqttCommands.isEmpty() ? null : mqttCommands; } @@ -208,6 +243,18 @@ public class DeviceConnectivityServiceImpl implements DeviceConnectivityService return null; } + private JsonNode getGatewayDockerCommands(String baseUrl, DeviceCredentials deviceCredentials, String mqttType) throws URISyntaxException { + ObjectNode dockerLaunchCommands = JacksonUtil.newObjectNode(); + DeviceConnectivityInfo properties = deviceConnectivityConfiguration.getConnectivity().get(mqttType); + String mqttHost = getHost(baseUrl, properties); + String mqttPort = properties.getPort().isEmpty() ? null : properties.getPort(); + Optional.ofNullable(DeviceConnectivityUtil.getGatewayLaunchCommand(LINUX, mqttHost, mqttPort, deviceCredentials)) + .ifPresent(v -> dockerLaunchCommands.put(LINUX, v)); + Optional.ofNullable(DeviceConnectivityUtil.getGatewayLaunchCommand(WINDOWS, mqttHost, mqttPort, deviceCredentials)) + .ifPresent(v -> dockerLaunchCommands.put(WINDOWS, v)); + return dockerLaunchCommands.isEmpty() ? null : dockerLaunchCommands; + } + private String getDockerMqttPublishCommand(String protocol, String baseUrl, String deviceTelemetryTopic, DeviceCredentials deviceCredentials) throws URISyntaxException { DeviceConnectivityInfo properties = deviceConnectivityConfiguration.getConnectivity().get(protocol); String mqttHost = getHost(baseUrl, properties); @@ -265,4 +312,9 @@ public class DeviceConnectivityServiceImpl implements DeviceConnectivityService private String getHost(String baseUrl, DeviceConnectivityInfo properties) throws URISyntaxException { return properties.getHost().isEmpty() ? new URI(baseUrl).getHost() : properties.getHost(); } + + private static boolean checkIsGateway(Device device) { + return device.getAdditionalInfo().has(DataConstants.GATEWAY_PARAMETER) && + device.getAdditionalInfo().get(DataConstants.GATEWAY_PARAMETER).asBoolean(); + } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/util/DeviceConnectivityUtil.java b/dao/src/main/java/org/thingsboard/server/dao/util/DeviceConnectivityUtil.java index 1d20c62d70..36770e9754 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/util/DeviceConnectivityUtil.java +++ b/dao/src/main/java/org/thingsboard/server/dao/util/DeviceConnectivityUtil.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.dao.util; +import org.apache.commons.lang3.StringUtils; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.device.credentials.BasicMqttCredentials; import org.thingsboard.server.common.data.security.DeviceCredentials; @@ -30,6 +31,7 @@ public class DeviceConnectivityUtil { public static final String MQTTS = "mqtts"; public static final String COAP = "coap"; public static final String COAPS = "coaps"; + public static final String GATEWAY = "gateway"; public static final String PEM_CERT_FILE_NAME = "tb-server-chain.pem"; public static final String CHECK_DOCUMENTATION = "Check documentation"; public static final String JSON_EXAMPLE_PAYLOAD = "\"{temperature:25}\""; @@ -78,6 +80,53 @@ public class DeviceConnectivityUtil { return command.toString(); } + public static String getGatewayLaunchCommand(String os, String host, String port, DeviceCredentials deviceCredentials) { + String gatewayVolumePathPrefix = "~/.tb-gateway"; + if (WINDOWS.equals(os)) { + gatewayVolumePathPrefix = "%HOMEPATH%/tb-gateway"; + } + + String gatewayContainerName = "tbGateway" + StringUtils.capitalize(host.replace(".", "")); + + StringBuilder command = new StringBuilder(DOCKER_RUN); + command.append("-v {gatewayVolumePathPrefix}/logs:/thingsboard_gateway/logs".replace("{gatewayVolumePathPrefix}", gatewayVolumePathPrefix)); + command.append(" -v {gatewayVolumePathPrefix}/extensions:/thingsboard_gateway/extensions".replace("{gatewayVolumePathPrefix}", gatewayVolumePathPrefix)); + command.append(" -v {gatewayVolumePathPrefix}/config:/thingsboard_gateway/config".replace("{gatewayVolumePathPrefix}", gatewayVolumePathPrefix)); + command.append(" --name ").append(gatewayContainerName); + command.append(" -e host=").append(host); + command.append(" -e port=").append(port); + + switch(deviceCredentials.getCredentialsType()) { + case ACCESS_TOKEN: + command.append(" -e accessToken=").append(deviceCredentials.getCredentialsId()); + break; + case MQTT_BASIC: + BasicMqttCredentials credentials = JacksonUtil.fromString(deviceCredentials.getCredentialsValue(), + BasicMqttCredentials.class); + if (credentials != null) { + if (credentials.getClientId() != null) { + command.append(" -e clientId=").append(credentials.getClientId()); + } + if (credentials.getUserName() != null) { + command.append(" -e username=").append(credentials.getUserName()); + } + if (credentials.getPassword() != null) { + command.append(" -e password=").append(credentials.getPassword()); + } + } else { + return null; + } + break; + default: + return null; + } + + command.append(" --restart always"); + command.append(" thingsboard/tb-gateway"); + + return command.toString(); + } + public static String getDockerMqttPublishCommand(String protocol, String baseUrl, String host, String port, String deviceTelemetryTopic, DeviceCredentials deviceCredentials) { String mqttCommand = getMqttPublishCommand(protocol, host, port, deviceTelemetryTopic, deviceCredentials);