thingsboard-developers
Merge pull request #1166 from thingsboard/develop/2.2-transports Develop/2.2 …
Changes
application/pom.xml 19(+10 -9)
application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java 530(+292 -238)
application/src/main/java/org/thingsboard/server/actors/device/PendingSessionMsgData.java 40(+0 -40)
application/src/main/java/org/thingsboard/server/actors/device/ToServerRpcRequestMetadata.java 12(+5 -7)
application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java 40(+12 -28)
application/src/main/java/org/thingsboard/server/actors/session/AbstractSessionActorMsgProcessor.java 122(+0 -122)
application/src/main/java/org/thingsboard/server/actors/session/SessionManagerActor.java 180(+0 -180)
application/src/main/java/org/thingsboard/server/actors/shared/ComponentMsgProcessor.java 16(+0 -16)
application/src/main/java/org/thingsboard/server/service/cluster/discovery/DiscoveryService.java 2(+0 -2)
application/src/main/java/org/thingsboard/server/service/cluster/discovery/DummyDiscoveryService.java 8(+0 -8)
application/src/main/java/org/thingsboard/server/service/cluster/discovery/ZkDiscoveryService.java 8(+0 -8)
application/src/main/java/org/thingsboard/server/service/rpc/ToServerRpcResponseActorMsg.java 3(+0 -3)
application/src/main/java/org/thingsboard/server/service/script/RemoteJsInvokeService.java 11(+5 -6)
application/src/main/java/org/thingsboard/server/service/transport/LocalTransportApiService.java 173(+173 -0)
application/src/main/java/org/thingsboard/server/service/transport/LocalTransportService.java 209(+209 -0)
application/src/main/java/org/thingsboard/server/service/transport/msg/TransportToDeviceActorMsgWrapper.java 26(+20 -6)
application/src/main/java/org/thingsboard/server/service/transport/RemoteRuleEngineTransportService.java 200(+200 -0)
application/src/main/java/org/thingsboard/server/service/transport/RemoteTransportApiService.java 109(+109 -0)
application/src/main/java/org/thingsboard/server/service/transport/RuleEngineTransportService.java 15(+12 -3)
application/src/main/java/org/thingsboard/server/service/transport/ToRuleEngineMsgDecoder.java 17(+11 -6)
application/src/main/java/org/thingsboard/server/service/transport/ToTransportMsgEncoder.java 14(+7 -7)
application/src/main/java/org/thingsboard/server/service/transport/TransportApiRequestDecoder.java 21(+8 -13)
application/src/main/java/org/thingsboard/server/service/transport/TransportApiResponseEncoder.java 16(+8 -8)
application/src/main/java/org/thingsboard/server/service/transport/TransportApiService.java 11(+7 -4)
application/src/main/resources/thingsboard.yml 110(+68 -42)
common/message/pom.xml 4(+4 -0)
common/message/src/main/java/org/thingsboard/server/common/msg/core/AttributesUpdateNotification.java 47(+0 -47)
common/message/src/main/java/org/thingsboard/server/common/msg/core/AttributesUpdateRequest.java 28(+0 -28)
common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicActorSystemToDeviceSessionActorMsg.java 52(+0 -52)
common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicAttributesUpdateRequest.java 63(+0 -63)
common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicCommandAckResponse.java 45(+0 -45)
common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicGetAttributesRequest.java 59(+0 -59)
common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicGetAttributesResponse.java 40(+0 -40)
common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicResponseMsg.java 79(+0 -79)
common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicStatusCodeResponse.java 42(+0 -42)
common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicTelemetryUploadRequest.java 66(+0 -66)
common/message/src/main/java/org/thingsboard/server/common/msg/core/GetAttributesRequest.java 29(+0 -29)
common/message/src/main/java/org/thingsboard/server/common/msg/core/RuleEngineErrorMsg.java 53(+0 -53)
common/message/src/main/java/org/thingsboard/server/common/msg/core/SessionCloseNotification.java 38(+0 -38)
common/message/src/main/java/org/thingsboard/server/common/msg/core/TelemetryUploadRequest.java 29(+0 -29)
common/message/src/main/java/org/thingsboard/server/common/msg/core/ToDeviceRpcRequestMsg.java 41(+0 -41)
common/message/src/main/java/org/thingsboard/server/common/msg/core/ToDeviceRpcResponseMsg.java 36(+0 -36)
common/message/src/main/java/org/thingsboard/server/common/msg/core/ToServerRpcRequestMsg.java 36(+0 -36)
common/message/src/main/java/org/thingsboard/server/common/msg/core/ToServerRpcResponseMsg.java 11(+1 -10)
common/message/src/main/java/org/thingsboard/server/common/msg/device/BasicDeviceToDeviceActorMsg.java 107(+0 -107)
common/message/src/main/java/org/thingsboard/server/common/msg/device/DeviceToDeviceActorMsg.java 41(+0 -41)
common/message/src/main/java/org/thingsboard/server/common/msg/session/BasicAdaptorToSessionActorMsg.java 32(+0 -32)
common/message/src/main/java/org/thingsboard/server/common/msg/session/BasicSessionActorToAdaptorMsg.java 32(+0 -32)
common/message/src/main/java/org/thingsboard/server/common/msg/session/BasicSessionMsg.java 44(+0 -44)
common/message/src/main/java/org/thingsboard/server/common/msg/session/BasicTransportToDeviceSessionActorMsg.java 74(+0 -74)
common/message/src/main/java/org/thingsboard/server/common/msg/session/ctrl/SessionCloseMsg.java 68(+0 -68)
common/message/src/main/java/org/thingsboard/server/common/msg/session/FromDeviceMsg.java 24(+0 -24)
common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionContext.java 16(+3 -13)
common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionCtrlMsg.java 23(+0 -23)
common/message/src/main/java/org/thingsboard/server/common/msg/session/TransportToDeviceSessionActorMsg.java 28(+0 -28)
common/pom.xml 3(+1 -2)
common/queue/pom.xml 6(+5 -1)
common/transport/coap/pom.xml 88(+88 -0)
common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/CoapTransportAdaptor.java 28(+25 -3)
common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/JsonCoapAdaptor.java 155(+155 -0)
common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/client/DeviceEmulator.java 0(+0 -0)
common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportContext.java 39(+30 -9)
common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java 372(+287 -85)
common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportService.java 38(+8 -30)
common/transport/http/pom.xml 81(+81 -0)
common/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java 326(+326 -0)
common/transport/http/src/main/java/org/thingsboard/server/transport/http/HttpTransportContext.java 24(+20 -4)
common/transport/mqtt/pom.xml 98(+98 -0)
common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java 217(+217 -0)
common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/MqttTransportAdaptor.java 62(+62 -0)
common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttSslHandlerProvider.java 59(+44 -15)
common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportContext.java 64(+64 -0)
common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java 382(+242 -140)
common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportServerInitializer.java 38(+8 -30)
common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportService.java 58(+10 -48)
common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/DeviceSessionCtx.java 28(+19 -9)
common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewayDeviceSessionCtx.java 101(+101 -0)
common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionHandler.java 370(+370 -0)
common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/MqttDeviceAwareSessionContext.java 14(+3 -11)
common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/MqttTopicMatcher.java 4(+2 -2)
common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/util/SslUtil.java 2(+1 -1)
common/transport/pom.xml 65(+9 -56)
common/transport/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java 216(+0 -216)
common/transport/src/main/java/org/thingsboard/server/common/transport/session/DeviceAwareSessionContext.java 78(+0 -78)
common/transport/src/main/java/org/thingsboard/server/common/transport/TransportAdaptor.java 32(+0 -32)
common/transport/transport-api/pom.xml 113(+113 -0)
common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/adaptor/AdaptorException.java 0(+0 -0)
common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java 430(+430 -0)
common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/auth/DeviceAuthResult.java 0(+0 -0)
common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/auth/DeviceAuthService.java 0(+0 -0)
common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/AbstractQuotaService.java 0(+0 -0)
common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/Clock.java 0(+0 -0)
common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/host/HostIntervalRegistryCleaner.java 0(+0 -0)
common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/host/HostIntervalRegistryLogger.java 0(+0 -0)
common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/host/HostRequestIntervalRegistry.java 0(+0 -0)
common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/host/HostRequestLimitPolicy.java 0(+0 -0)
common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/host/HostRequestsQuotaService.java 0(+0 -0)
common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/inmemory/IntervalCount.java 0(+0 -0)
common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/inmemory/IntervalRegistryCleaner.java 0(+0 -0)
common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/inmemory/IntervalRegistryLogger.java 0(+0 -0)
common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/inmemory/KeyBasedIntervalRegistry.java 0(+0 -0)
common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/QuotaService.java 0(+0 -0)
common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/RequestLimitPolicy.java 0(+0 -0)
common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantIntervalRegistryCleaner.java 2(+2 -0)
common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantIntervalRegistryLogger.java 2(+2 -0)
common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantMsgsIntervalRegistry.java 2(+2 -0)
common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantQuotaService.java 2(+2 -0)
common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantRequestLimitPolicy.java 2(+2 -0)
common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/AbstractTransportService.java 116(+116 -0)
common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/RemoteTransportService.java 315(+315 -0)
common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/SessionMetaData.java 16(+13 -3)
common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/ToRuleEngineMsgEncoder.java 15(+7 -8)
common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/ToTransportMsgResponseDecoder.java 17(+9 -8)
common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/TransportApiRequestEncoder.java 15(+7 -8)
common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/TransportApiResponseDecoder.java 21(+8 -13)
common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/session/DeviceAwareSessionContext.java 37(+27 -10)
common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/SessionMsgListener.java 22(+18 -4)
common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/SessionMsgProcessor.java 3(+0 -3)
common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportAdaptor.java 6(+4 -2)
common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportContext.java 68(+68 -0)
common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportService.java 68(+68 -0)
common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportServiceCallback.java 10(+7 -3)
common/transport/transport-api/src/test/java/org/thingsboard/server/common/transport/quota/ClockTest.java 0(+0 -0)
common/transport/transport-api/src/test/java/org/thingsboard/server/common/transport/quota/HostRequestLimitPolicyTest.java 0(+0 -0)
common/transport/transport-api/src/test/java/org/thingsboard/server/common/transport/quota/HostRequestsQuotaServiceTest.java 6(+5 -1)
common/transport/transport-api/src/test/java/org/thingsboard/server/common/transport/quota/inmemory/HostRequestIntervalRegistryTest.java 0(+0 -0)
common/transport/transport-api/src/test/java/org/thingsboard/server/common/transport/quota/inmemory/IntervalCountTest.java 0(+0 -0)
common/transport/transport-api/src/test/java/org/thingsboard/server/common/transport/quota/inmemory/IntervalRegistryLoggerTest.java 0(+0 -0)
msa/docker/.env 5(+4 -1)
msa/docker/docker-compose.yml 63(+61 -2)
msa/docker/haproxy/config/haproxy.cfg 33(+30 -3)
msa/docker/tb-coap-transport.env 6(+6 -0)
msa/docker/tb-http-transport.env 6(+6 -0)
msa/docker/tb-mqtt-transport.env 6(+6 -0)
msa/docker/tb-node.env 6(+2 -4)
msa/pom.xml 1(+1 -0)
msa/transport/coap/docker/Dockerfile 31(+31 -0)
msa/transport/coap/docker/logback.xml 50(+50 -0)
msa/transport/coap/pom.xml 137(+137 -0)
msa/transport/http/docker/Dockerfile 31(+31 -0)
msa/transport/http/docker/logback.xml 50(+50 -0)
msa/transport/http/pom.xml 137(+137 -0)
msa/transport/mqtt/docker/Dockerfile 31(+31 -0)
msa/transport/mqtt/docker/logback.xml 50(+50 -0)
msa/transport/mqtt/pom.xml 137(+137 -0)
msa/transport/pom.xml 55(+55 -0)
pom.xml 17(+11 -6)
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCopyAttributesToEntityViewNode.java 3(+1 -2)
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNode.java 2(+1 -1)
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java 7(+3 -4)
transport/coap/build.gradle 140(+140 -0)
transport/coap/pom.xml 299(+278 -21)
transport/coap/src/main/assembly/windows.xml 71(+71 -0)
transport/coap/src/main/conf/logback.xml 43(+43 -0)
transport/coap/src/main/java/org/thingsboard/server/coap/ThingsboardCoapTransportApplication.java 48(+48 -0)
transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/JsonCoapAdaptor.java 265(+0 -265)
transport/coap/src/main/java/org/thingsboard/server/transport/coap/session/CoapExchangeObserverProxy.java 38(+0 -38)
transport/coap/src/main/java/org/thingsboard/server/transport/coap/session/CoapSessionCtx.java 135(+0 -135)
transport/coap/src/main/java/org/thingsboard/server/transport/coap/session/CoapSessionId.java 77(+0 -77)
transport/coap/src/test/java/org/thingsboard/server/transport/coap/CoapServerTestConfiguration.java 35(+0 -35)
transport/http/build.gradle 140(+140 -0)
transport/http/pom.xml 296(+278 -18)
transport/http/src/main/assembly/windows.xml 71(+71 -0)
transport/http/src/main/conf/logback.xml 43(+43 -0)
transport/http/src/main/java/org/thingsboard/server/http/ThingsboardHttpTransportApplication.java 33(+27 -6)
transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java 235(+0 -235)
transport/http/src/main/java/org/thingsboard/server/transport/http/session/HttpSessionCtx.java 164(+0 -164)
transport/mqtt/build.gradle 140(+140 -0)
transport/mqtt/pom.xml 303(+278 -25)
transport/mqtt/src/main/assembly/windows.xml 71(+71 -0)
transport/mqtt/src/main/conf/logback.xml 43(+43 -0)
transport/mqtt/src/main/java/org/thingsboard/server/mqtt/ThingsboardMqttTransportApplication.java 49(+49 -0)
transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java 273(+0 -273)
transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/DeviceSessionCtx.java 119(+0 -119)
transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewayDeviceSessionCtx.java 208(+0 -208)
transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionCtx.java 277(+0 -277)
transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/MqttSessionId.java 63(+0 -63)
transport/pom.xml 18(+1 -17)
Details
application/pom.xml 19(+10 -9)
diff --git a/application/pom.xml b/application/pom.xml
index c4766f2..d99f607 100644
--- a/application/pom.xml
+++ b/application/pom.xml
@@ -57,20 +57,20 @@
<artifactId>rule-engine-components</artifactId>
</dependency>
<dependency>
- <groupId>org.thingsboard.common</groupId>
- <artifactId>transport</artifactId>
+ <groupId>org.thingsboard.common.transport</groupId>
+ <artifactId>transport-api</artifactId>
</dependency>
<dependency>
- <groupId>org.thingsboard.transport</groupId>
- <artifactId>http</artifactId>
+ <groupId>org.thingsboard.common.transport</groupId>
+ <artifactId>mqtt</artifactId>
</dependency>
<dependency>
- <groupId>org.thingsboard.transport</groupId>
- <artifactId>coap</artifactId>
+ <groupId>org.thingsboard.common.transport</groupId>
+ <artifactId>http</artifactId>
</dependency>
<dependency>
- <groupId>org.thingsboard.transport</groupId>
- <artifactId>mqtt</artifactId>
+ <groupId>org.thingsboard.common.transport</groupId>
+ <artifactId>coap</artifactId>
</dependency>
<dependency>
<groupId>org.thingsboard</groupId>
@@ -542,7 +542,8 @@
<args>
<arg>-PprojectBuildDir=${project.build.directory}</arg>
<arg>-PprojectVersion=${project.version}</arg>
- <arg>-PmainJar=${project.build.directory}/${project.build.finalName}-boot.${project.packaging}</arg>
+ <arg>-PmainJar=${project.build.directory}/${project.build.finalName}-boot.${project.packaging}
+ </arg>
<arg>-PpkgName=${pkg.name}</arg>
<arg>-PpkgInstallFolder=${pkg.installFolder}</arg>
<arg>-PpkgLogFolder=${pkg.unixLogFolder}</arg>
diff --git a/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java b/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java
index 8930f84..5e19d69 100644
--- a/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java
+++ b/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java
@@ -31,6 +31,7 @@ import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import org.thingsboard.rule.engine.api.MailService;
import org.thingsboard.server.actors.service.ActorService;
@@ -63,12 +64,12 @@ import org.thingsboard.server.service.encoding.DataDecodingEncodingService;
import org.thingsboard.server.service.executors.DbCallbackExecutorService;
import org.thingsboard.server.service.executors.ExternalCallExecutorService;
import org.thingsboard.server.service.mail.MailExecutorService;
-import org.thingsboard.server.service.queue.MsgQueueService;
import org.thingsboard.server.service.rpc.DeviceRpcService;
import org.thingsboard.server.service.script.JsExecutorService;
import org.thingsboard.server.service.script.JsInvokeService;
import org.thingsboard.server.service.state.DeviceStateService;
import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService;
+import org.thingsboard.server.service.transport.RuleEngineTransportService;
import javax.annotation.Nullable;
import java.io.IOException;
@@ -198,11 +199,12 @@ public class ActorSystemContext {
@Autowired
@Getter
- private MsgQueueService msgQueueService;
+ private DeviceStateService deviceStateService;
+ @Lazy
@Autowired
@Getter
- private DeviceStateService deviceStateService;
+ private RuleEngineTransportService ruleEngineTransportService;
@Value("${cluster.partition_id}")
@Getter
@@ -262,10 +264,6 @@ public class ActorSystemContext {
@Getter
@Setter
- private ActorRef sessionManagerActor;
-
- @Getter
- @Setter
private ActorRef statsActor;
@Getter
diff --git a/application/src/main/java/org/thingsboard/server/actors/app/AppActor.java b/application/src/main/java/org/thingsboard/server/actors/app/AppActor.java
index 6a78f78..f4373db 100644
--- a/application/src/main/java/org/thingsboard/server/actors/app/AppActor.java
+++ b/application/src/main/java/org/thingsboard/server/actors/app/AppActor.java
@@ -38,8 +38,6 @@ import org.thingsboard.server.common.msg.TbActorMsg;
import org.thingsboard.server.common.msg.aware.TenantAwareMsg;
import org.thingsboard.server.common.msg.cluster.SendToClusterMsg;
import org.thingsboard.server.common.msg.cluster.ServerAddress;
-import org.thingsboard.server.common.msg.core.BasicActorSystemToDeviceSessionActorMsg;
-import org.thingsboard.server.common.msg.device.DeviceToDeviceActorMsg;
import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg;
import org.thingsboard.server.dao.model.ModelConstants;
@@ -105,7 +103,7 @@ public class AppActor extends RuleChainManagerActor {
case SERVICE_TO_RULE_ENGINE_MSG:
onServiceToRuleEngineMsg((ServiceToRuleEngineMsg) msg);
break;
- case DEVICE_SESSION_TO_DEVICE_ACTOR_MSG:
+ case TRANSPORT_TO_DEVICE_ACTOR_MSG:
case DEVICE_ATTRIBUTES_UPDATE_TO_DEVICE_ACTOR_MSG:
case DEVICE_CREDENTIALS_UPDATE_TO_DEVICE_ACTOR_MSG:
case DEVICE_NAME_OR_TYPE_UPDATE_TO_DEVICE_ACTOR_MSG:
@@ -114,19 +112,12 @@ public class AppActor extends RuleChainManagerActor {
case REMOTE_TO_RULE_CHAIN_TELL_NEXT_MSG:
onToDeviceActorMsg((TenantAwareMsg) msg);
break;
- case ACTOR_SYSTEM_TO_DEVICE_SESSION_ACTOR_MSG:
- onToDeviceSessionMsg((BasicActorSystemToDeviceSessionActorMsg) msg);
- break;
default:
return false;
}
return true;
}
- private void onToDeviceSessionMsg(BasicActorSystemToDeviceSessionActorMsg msg) {
- systemContext.getSessionManagerActor().tell(msg, self());
- }
-
private void onPossibleClusterMsg(SendToClusterMsg msg) {
Optional<ServerAddress> address = systemContext.getRoutingService().resolveById(msg.getEntityId());
if (address.isPresent()) {
@@ -169,16 +160,6 @@ public class AppActor extends RuleChainManagerActor {
getOrCreateTenantActor(msg.getTenantId()).tell(msg, ActorRef.noSender());
}
- private void processDeviceMsg(DeviceToDeviceActorMsg deviceToDeviceActorMsg) {
- TenantId tenantId = deviceToDeviceActorMsg.getTenantId();
- ActorRef tenantActor = getOrCreateTenantActor(tenantId);
- if (deviceToDeviceActorMsg.getPayload().getMsgType().requiresRulesProcessing()) {
-// tenantActor.tell(new RuleChainDeviceMsg(deviceToDeviceActorMsg, ruleManager.getRuleChain(this.context())), context().self());
- } else {
- tenantActor.tell(deviceToDeviceActorMsg, context().self());
- }
- }
-
private ActorRef getOrCreateTenantActor(TenantId tenantId) {
return tenantActors.computeIfAbsent(tenantId, k -> context().actorOf(Props.create(new TenantActor.ActorCreator(systemContext, tenantId))
.withDispatcher(DefaultActorService.CORE_DISPATCHER_NAME), tenantId.toString()));
diff --git a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActor.java b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActor.java
index 99d0045..bd2a0f4 100644
--- a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActor.java
+++ b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActor.java
@@ -26,12 +26,11 @@ import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.msg.TbActorMsg;
import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;
-import org.thingsboard.server.common.msg.device.DeviceToDeviceActorMsg;
import org.thingsboard.server.common.msg.timeout.DeviceActorClientSideRpcTimeoutMsg;
-import org.thingsboard.server.common.msg.timeout.DeviceActorQueueTimeoutMsg;
import org.thingsboard.server.common.msg.timeout.DeviceActorServerSideRpcTimeoutMsg;
import org.thingsboard.server.service.rpc.ToDeviceRpcRequestActorMsg;
import org.thingsboard.server.service.rpc.ToServerRpcResponseActorMsg;
+import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper;
public class DeviceActor extends ContextAwareActor {
@@ -50,8 +49,8 @@ public class DeviceActor extends ContextAwareActor {
case CLUSTER_EVENT_MSG:
processor.processClusterEventMsg((ClusterEventMsg) msg);
break;
- case DEVICE_SESSION_TO_DEVICE_ACTOR_MSG:
- processor.process(context(), (DeviceToDeviceActorMsg) msg);
+ case TRANSPORT_TO_DEVICE_ACTOR_MSG:
+ processor.process(context(), (TransportToDeviceActorMsgWrapper) msg);
break;
case DEVICE_ATTRIBUTES_UPDATE_TO_DEVICE_ACTOR_MSG:
processor.processAttributesUpdate(context(), (DeviceAttributesEventNotificationMsg) msg);
@@ -74,12 +73,6 @@ public class DeviceActor extends ContextAwareActor {
case DEVICE_ACTOR_CLIENT_SIDE_RPC_TIMEOUT_MSG:
processor.processClientSideRpcTimeout(context(), (DeviceActorClientSideRpcTimeoutMsg) msg);
break;
- case DEVICE_ACTOR_QUEUE_TIMEOUT_MSG:
- processor.processQueueTimeout(context(), (DeviceActorQueueTimeoutMsg) msg);
- break;
- case RULE_ENGINE_QUEUE_PUT_ACK_MSG:
- processor.processQueueAck(context(), (RuleEngineQueuePutAckMsg) msg);
- break;
default:
return false;
}
diff --git a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java
index a2ea048..05902a6 100644
--- a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java
+++ b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java
@@ -16,7 +16,6 @@
package org.thingsboard.server.actors.device;
import akka.actor.ActorContext;
-import akka.actor.ActorRef;
import akka.event.LoggingAdapter;
import com.datastax.driver.core.utils.UUIDs;
import com.google.common.util.concurrent.FutureCallback;
@@ -33,7 +32,6 @@ import org.thingsboard.server.actors.shared.AbstractContextAwareMsgProcessor;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.id.DeviceId;
-import org.thingsboard.server.common.data.id.SessionId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.kv.AttributeKey;
import org.thingsboard.server.common.data.kv.AttributeKvEntry;
@@ -44,36 +42,34 @@ import org.thingsboard.server.common.msg.TbMsgDataType;
import org.thingsboard.server.common.msg.TbMsgMetaData;
import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;
import org.thingsboard.server.common.msg.cluster.ServerAddress;
-import org.thingsboard.server.common.msg.core.ActorSystemToDeviceSessionActorMsg;
-import org.thingsboard.server.common.msg.core.AttributesUpdateNotification;
-import org.thingsboard.server.common.msg.core.AttributesUpdateRequest;
-import org.thingsboard.server.common.msg.core.BasicActorSystemToDeviceSessionActorMsg;
-import org.thingsboard.server.common.msg.core.BasicCommandAckResponse;
-import org.thingsboard.server.common.msg.core.BasicGetAttributesResponse;
-import org.thingsboard.server.common.msg.core.BasicStatusCodeResponse;
-import org.thingsboard.server.common.msg.core.GetAttributesRequest;
-import org.thingsboard.server.common.msg.core.RuleEngineError;
-import org.thingsboard.server.common.msg.core.RuleEngineErrorMsg;
-import org.thingsboard.server.common.msg.core.SessionCloseMsg;
-import org.thingsboard.server.common.msg.core.SessionCloseNotification;
-import org.thingsboard.server.common.msg.core.SessionOpenMsg;
-import org.thingsboard.server.common.msg.core.TelemetryUploadRequest;
-import org.thingsboard.server.common.msg.core.ToDeviceRpcRequestMsg;
-import org.thingsboard.server.common.msg.core.ToDeviceRpcResponseMsg;
-import org.thingsboard.server.common.msg.core.ToServerRpcRequestMsg;
-import org.thingsboard.server.common.msg.device.DeviceToDeviceActorMsg;
-import org.thingsboard.server.common.msg.kv.BasicAttributeKVMsg;
import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest;
-import org.thingsboard.server.common.msg.session.FromDeviceMsg;
import org.thingsboard.server.common.msg.session.SessionMsgType;
-import org.thingsboard.server.common.msg.session.SessionType;
-import org.thingsboard.server.common.msg.session.ToDeviceMsg;
import org.thingsboard.server.common.msg.timeout.DeviceActorClientSideRpcTimeoutMsg;
-import org.thingsboard.server.common.msg.timeout.DeviceActorQueueTimeoutMsg;
import org.thingsboard.server.common.msg.timeout.DeviceActorServerSideRpcTimeoutMsg;
+import org.thingsboard.server.gen.transport.TransportProtos;
+import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.DeviceActorToTransportMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeRequestMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeResponseMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.KeyValueProto;
+import org.thingsboard.server.gen.transport.TransportProtos.KeyValueType;
+import org.thingsboard.server.gen.transport.TransportProtos.PostAttributeMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.PostTelemetryMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.SessionCloseNotificationProto;
+import org.thingsboard.server.gen.transport.TransportProtos.SessionEvent;
+import org.thingsboard.server.gen.transport.TransportProtos.SessionEventMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto;
+import org.thingsboard.server.gen.transport.TransportProtos.SubscribeToAttributeUpdatesMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.SubscribeToRPCMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcRequestMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcResponseMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.TransportToDeviceActorMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.TsKvListProto;
+import org.thingsboard.server.gen.transport.TransportProtos.TsKvProto;
import org.thingsboard.server.service.rpc.FromDeviceRpcResponse;
import org.thingsboard.server.service.rpc.ToDeviceRpcRequestActorMsg;
import org.thingsboard.server.service.rpc.ToServerRpcResponseActorMsg;
+import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper;
import javax.annotation.Nullable;
import java.util.ArrayList;
@@ -87,9 +83,7 @@ import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
-import java.util.concurrent.TimeoutException;
import java.util.function.Consumer;
-import java.util.function.Predicate;
import java.util.stream.Collectors;
/**
@@ -99,12 +93,11 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
private final TenantId tenantId;
private final DeviceId deviceId;
- private final Map<SessionId, SessionInfo> sessions;
- private final Map<SessionId, SessionInfo> attributeSubscriptions;
- private final Map<SessionId, SessionInfo> rpcSubscriptions;
+ private final Map<UUID, SessionInfo> sessions;
+ private final Map<UUID, SessionInfo> attributeSubscriptions;
+ private final Map<UUID, SessionInfo> rpcSubscriptions;
private final Map<Integer, ToDeviceRpcRequestMetadata> toDeviceRpcPendingMap;
private final Map<Integer, ToServerRpcRequestMetadata> toServerRpcPendingMap;
- private final Map<UUID, PendingSessionMsgData> pendingMsgs;
private final Gson gson = new Gson();
private final JsonParser jsonParser = new JsonParser();
@@ -123,7 +116,6 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
this.rpcSubscriptions = new HashMap<>();
this.toDeviceRpcPendingMap = new HashMap<>();
this.toServerRpcPendingMap = new HashMap<>();
- this.pendingMsgs = new HashMap<>();
initAttributes();
}
@@ -139,11 +131,8 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
void processRpcRequest(ActorContext context, ToDeviceRpcRequestActorMsg msg) {
ToDeviceRpcRequest request = msg.getMsg();
ToDeviceRpcRequestBody body = request.getBody();
- ToDeviceRpcRequestMsg rpcRequest = new ToDeviceRpcRequestMsg(
- rpcSeq++,
- body.getMethod(),
- body.getParams()
- );
+ ToDeviceRpcRequestMsg rpcRequest = ToDeviceRpcRequestMsg.newBuilder().setRequestId(
+ rpcSeq++).setMethodName(body.getMethod()).setParams(body.getParams()).build();
long timeout = request.getExpirationTime() - System.currentTimeMillis();
if (timeout <= 0) {
@@ -152,11 +141,10 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
}
boolean sent = rpcSubscriptions.size() > 0;
- Set<SessionId> syncSessionSet = new HashSet<>();
+ Set<UUID> syncSessionSet = new HashSet<>();
rpcSubscriptions.entrySet().forEach(sub -> {
- ActorSystemToDeviceSessionActorMsg response = new BasicActorSystemToDeviceSessionActorMsg(rpcRequest, sub.getKey());
- sendMsgToSessionActor(response, sub.getValue().getServer());
- if (SessionType.SYNC == sub.getValue().getType()) {
+ sendToTransport(rpcRequest, sub.getKey(), sub.getValue().getNodeId());
+ if (TransportProtos.SessionType.SYNC == sub.getValue().getType()) {
syncSessionSet.add(sub.getKey());
}
});
@@ -191,32 +179,11 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
}
}
- void processQueueTimeout(ActorContext context, DeviceActorQueueTimeoutMsg msg) {
- PendingSessionMsgData data = pendingMsgs.remove(msg.getId());
- if (data != null) {
- logger.debug("[{}] Queue put [{}] timeout detected!", deviceId, msg.getId());
- ToDeviceMsg toDeviceMsg = new RuleEngineErrorMsg(data.getSessionMsgType(), RuleEngineError.QUEUE_PUT_TIMEOUT);
- sendMsgToSessionActor(new BasicActorSystemToDeviceSessionActorMsg(toDeviceMsg, data.getSessionId()), data.getServerAddress());
- }
- }
-
- void processQueueAck(ActorContext context, RuleEngineQueuePutAckMsg msg) {
- PendingSessionMsgData data = pendingMsgs.remove(msg.getId());
- if (data != null && data.isReplyOnQueueAck()) {
- int remainingAcks = data.getAckMsgCount() - 1;
- data.setAckMsgCount(remainingAcks);
- logger.debug("[{}] Queue put [{}] ack detected. Remaining acks: {}!", deviceId, msg.getId(), remainingAcks);
- if (remainingAcks == 0) {
- ToDeviceMsg toDeviceMsg = BasicStatusCodeResponse.onSuccess(data.getSessionMsgType(), data.getRequestId());
- sendMsgToSessionActor(new BasicActorSystemToDeviceSessionActorMsg(toDeviceMsg, data.getSessionId()), data.getServerAddress());
- }
- }
- }
-
- private void sendPendingRequests(ActorContext context, SessionId sessionId, SessionType type, Optional<ServerAddress> server) {
+ private void sendPendingRequests(ActorContext context, UUID sessionId, SessionInfoProto sessionInfo) {
+ TransportProtos.SessionType sessionType = getSessionType(sessionId);
if (!toDeviceRpcPendingMap.isEmpty()) {
logger.debug("[{}] Pushing {} pending RPC messages to new async session [{}]", deviceId, toDeviceRpcPendingMap.size(), sessionId);
- if (type == SessionType.SYNC) {
+ if (sessionType == TransportProtos.SessionType.SYNC) {
logger.debug("[{}] Cleanup sync rpc session [{}]", deviceId, sessionId);
rpcSubscriptions.remove(sessionId);
}
@@ -224,16 +191,16 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
logger.debug("[{}] No pending RPC messages for new async session [{}]", deviceId, sessionId);
}
Set<Integer> sentOneWayIds = new HashSet<>();
- if (type == SessionType.ASYNC) {
- toDeviceRpcPendingMap.entrySet().forEach(processPendingRpc(context, sessionId, server, sentOneWayIds));
+ if (sessionType == TransportProtos.SessionType.ASYNC) {
+ toDeviceRpcPendingMap.entrySet().forEach(processPendingRpc(context, sessionId, sessionInfo.getNodeId(), sentOneWayIds));
} else {
- toDeviceRpcPendingMap.entrySet().stream().findFirst().ifPresent(processPendingRpc(context, sessionId, server, sentOneWayIds));
+ toDeviceRpcPendingMap.entrySet().stream().findFirst().ifPresent(processPendingRpc(context, sessionId, sessionInfo.getNodeId(), sentOneWayIds));
}
sentOneWayIds.forEach(toDeviceRpcPendingMap::remove);
}
- private Consumer<Map.Entry<Integer, ToDeviceRpcRequestMetadata>> processPendingRpc(ActorContext context, SessionId sessionId, Optional<ServerAddress> server, Set<Integer> sentOneWayIds) {
+ private Consumer<Map.Entry<Integer, ToDeviceRpcRequestMetadata>> processPendingRpc(ActorContext context, UUID sessionId, String nodeId, Set<Integer> sentOneWayIds) {
return entry -> {
ToDeviceRpcRequestActorMsg requestActorMsg = entry.getValue().getMsg();
ToDeviceRpcRequest request = entry.getValue().getMsg().getMsg();
@@ -242,40 +209,40 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
sentOneWayIds.add(entry.getKey());
systemContext.getDeviceRpcService().processRpcResponseFromDevice(new FromDeviceRpcResponse(request.getId(), requestActorMsg.getServerAddress(), null, null));
}
- ToDeviceRpcRequestMsg rpcRequest = new ToDeviceRpcRequestMsg(
- entry.getKey(),
- body.getMethod(),
- body.getParams()
- );
- ActorSystemToDeviceSessionActorMsg response = new BasicActorSystemToDeviceSessionActorMsg(rpcRequest, sessionId);
- sendMsgToSessionActor(response, server);
+ ToDeviceRpcRequestMsg rpcRequest = ToDeviceRpcRequestMsg.newBuilder().setRequestId(
+ entry.getKey()).setMethodName(body.getMethod()).setParams(body.getParams()).build();
+ sendToTransport(rpcRequest, sessionId, nodeId);
};
}
- void process(ActorContext context, DeviceToDeviceActorMsg msg) {
- processSubscriptionCommands(context, msg);
- processRpcResponses(context, msg);
- processSessionStateMsgs(msg);
-
- SessionMsgType sessionMsgType = msg.getPayload().getMsgType();
- if (sessionMsgType.requiresRulesProcessing()) {
- switch (sessionMsgType) {
- case GET_ATTRIBUTES_REQUEST:
- handleGetAttributesRequest(msg);
- break;
- case POST_ATTRIBUTES_REQUEST:
- handlePostAttributesRequest(context, msg);
- reportActivity();
- break;
- case POST_TELEMETRY_REQUEST:
- handlePostTelemetryRequest(context, msg);
- reportActivity();
- break;
- case TO_SERVER_RPC_REQUEST:
- handleClientSideRPCRequest(context, msg);
- reportActivity();
- break;
- }
+ void process(ActorContext context, TransportToDeviceActorMsgWrapper wrapper) {
+ TransportToDeviceActorMsg msg = wrapper.getMsg();
+ if (msg.hasSessionEvent()) {
+ processSessionStateMsgs(msg.getSessionInfo(), msg.getSessionEvent());
+ }
+ if (msg.hasSubscribeToAttributes()) {
+ processSubscriptionCommands(context, msg.getSessionInfo(), msg.getSubscribeToAttributes());
+ }
+ if (msg.hasSubscribeToRPC()) {
+ processSubscriptionCommands(context, msg.getSessionInfo(), msg.getSubscribeToRPC());
+ }
+ if (msg.hasPostAttributes()) {
+ handlePostAttributesRequest(context, msg.getSessionInfo(), msg.getPostAttributes());
+ reportActivity();
+ }
+ if (msg.hasPostTelemetry()) {
+ handlePostTelemetryRequest(context, msg.getSessionInfo(), msg.getPostTelemetry());
+ reportActivity();
+ }
+ if (msg.hasGetAttributes()) {
+ handleGetAttributesRequest(context, msg.getSessionInfo(), msg.getGetAttributes());
+ }
+ if (msg.hasToDeviceRPCCallResponse()) {
+ processRpcResponses(context, msg.getSessionInfo(), msg.getToDeviceRPCCallResponse());
+ }
+ if (msg.hasToServerRPCCallRequest()) {
+ handleClientSideRPCRequest(context, msg.getSessionInfo(), msg.getToServerRPCCallRequest());
+ reportActivity();
}
}
@@ -291,27 +258,27 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
systemContext.getDeviceStateService().onDeviceDisconnect(deviceId);
}
- private void handleGetAttributesRequest(DeviceToDeviceActorMsg src) {
- GetAttributesRequest request = (GetAttributesRequest) src.getPayload();
- ListenableFuture<List<AttributeKvEntry>> clientAttributesFuture = getAttributeKvEntries(deviceId, DataConstants.CLIENT_SCOPE, request.getClientAttributeNames());
- ListenableFuture<List<AttributeKvEntry>> sharedAttributesFuture = getAttributeKvEntries(deviceId, DataConstants.SHARED_SCOPE, request.getSharedAttributeNames());
-
+ private void handleGetAttributesRequest(ActorContext context, SessionInfoProto sessionInfo, GetAttributeRequestMsg request) {
+ ListenableFuture<List<AttributeKvEntry>> clientAttributesFuture = getAttributeKvEntries(deviceId, DataConstants.CLIENT_SCOPE, toOptionalSet(request.getClientAttributeNamesList()));
+ ListenableFuture<List<AttributeKvEntry>> sharedAttributesFuture = getAttributeKvEntries(deviceId, DataConstants.SHARED_SCOPE, toOptionalSet(request.getSharedAttributeNamesList()));
+ int requestId = request.getRequestId();
Futures.addCallback(Futures.allAsList(Arrays.asList(clientAttributesFuture, sharedAttributesFuture)), new FutureCallback<List<List<AttributeKvEntry>>>() {
@Override
public void onSuccess(@Nullable List<List<AttributeKvEntry>> result) {
- BasicGetAttributesResponse response = BasicGetAttributesResponse.onSuccess(request.getMsgType(),
- request.getRequestId(), BasicAttributeKVMsg.from(result.get(0), result.get(1)));
- sendMsgToSessionActor(new BasicActorSystemToDeviceSessionActorMsg(response, src.getSessionId()), src.getServerAddress());
+ GetAttributeResponseMsg responseMsg = GetAttributeResponseMsg.newBuilder()
+ .setRequestId(requestId)
+ .addAllClientAttributeList(toTsKvProtos(result.get(0)))
+ .addAllSharedAttributeList(toTsKvProtos(result.get(1)))
+ .build();
+ sendToTransport(responseMsg, sessionInfo);
}
@Override
public void onFailure(Throwable t) {
- if (t instanceof Exception) {
- ToDeviceMsg toDeviceMsg = BasicStatusCodeResponse.onError(SessionMsgType.GET_ATTRIBUTES_REQUEST, request.getRequestId(), (Exception) t);
- sendMsgToSessionActor(new BasicActorSystemToDeviceSessionActorMsg(toDeviceMsg, src.getSessionId()), src.getServerAddress());
- } else {
- logger.error("[{}] Failed to process attributes request", deviceId, t);
- }
+ GetAttributeResponseMsg responseMsg = GetAttributeResponseMsg.newBuilder()
+ .setError(t.getMessage())
+ .build();
+ sendToTransport(responseMsg, sessionInfo);
}
});
}
@@ -328,115 +295,98 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
}
}
- private void handlePostAttributesRequest(ActorContext context, DeviceToDeviceActorMsg src) {
- AttributesUpdateRequest request = (AttributesUpdateRequest) src.getPayload();
-
- JsonObject json = new JsonObject();
- for (AttributeKvEntry kv : request.getAttributes()) {
- kv.getBooleanValue().ifPresent(v -> json.addProperty(kv.getKey(), v));
- kv.getLongValue().ifPresent(v -> json.addProperty(kv.getKey(), v));
- kv.getDoubleValue().ifPresent(v -> json.addProperty(kv.getKey(), v));
- kv.getStrValue().ifPresent(v -> json.addProperty(kv.getKey(), v));
- }
-
- TbMsg tbMsg = new TbMsg(UUIDs.timeBased(), SessionMsgType.POST_ATTRIBUTES_REQUEST.name(), deviceId, defaultMetaData.copy(), TbMsgDataType.JSON, gson.toJson(json), null, null, 0L);
- PendingSessionMsgData msgData = new PendingSessionMsgData(src.getSessionId(), src.getServerAddress(),
- SessionMsgType.POST_ATTRIBUTES_REQUEST, request.getRequestId(), true, 1);
- pushToRuleEngineWithTimeout(context, tbMsg, msgData);
+ private void handlePostAttributesRequest(ActorContext context, SessionInfoProto sessionInfo, PostAttributeMsg postAttributes) {
+ JsonObject json = getJsonObject(postAttributes.getKvList());
+ TbMsg tbMsg = new TbMsg(UUIDs.timeBased(), SessionMsgType.POST_ATTRIBUTES_REQUEST.name(), deviceId, defaultMetaData.copy(),
+ TbMsgDataType.JSON, gson.toJson(json), null, null, 0L);
+ pushToRuleEngine(context, tbMsg);
}
- private void handlePostTelemetryRequest(ActorContext context, DeviceToDeviceActorMsg src) {
- TelemetryUploadRequest request = (TelemetryUploadRequest) src.getPayload();
-
- Map<Long, List<KvEntry>> tsData = request.getData();
-
- PendingSessionMsgData msgData = new PendingSessionMsgData(src.getSessionId(), src.getServerAddress(),
- SessionMsgType.POST_TELEMETRY_REQUEST, request.getRequestId(), true, tsData.size());
-
- for (Map.Entry<Long, List<KvEntry>> entry : tsData.entrySet()) {
- JsonObject json = new JsonObject();
- for (KvEntry kv : entry.getValue()) {
- kv.getBooleanValue().ifPresent(v -> json.addProperty(kv.getKey(), v));
- kv.getLongValue().ifPresent(v -> json.addProperty(kv.getKey(), v));
- kv.getDoubleValue().ifPresent(v -> json.addProperty(kv.getKey(), v));
- kv.getStrValue().ifPresent(v -> json.addProperty(kv.getKey(), v));
- }
+ private void handlePostTelemetryRequest(ActorContext context, SessionInfoProto sessionInfo, PostTelemetryMsg postTelemetry) {
+ for (TsKvListProto tsKv : postTelemetry.getTsKvListList()) {
+ JsonObject json = getJsonObject(tsKv.getKvList());
TbMsgMetaData metaData = defaultMetaData.copy();
- metaData.putValue("ts", entry.getKey() + "");
+ metaData.putValue("ts", tsKv.getTs() + "");
TbMsg tbMsg = new TbMsg(UUIDs.timeBased(), SessionMsgType.POST_TELEMETRY_REQUEST.name(), deviceId, metaData, TbMsgDataType.JSON, gson.toJson(json), null, null, 0L);
- pushToRuleEngineWithTimeout(context, tbMsg, msgData);
+ pushToRuleEngine(context, tbMsg);
}
}
- private void handleClientSideRPCRequest(ActorContext context, DeviceToDeviceActorMsg src) {
- ToServerRpcRequestMsg request = (ToServerRpcRequestMsg) src.getPayload();
-
+ private void handleClientSideRPCRequest(ActorContext context, SessionInfoProto sessionInfo, TransportProtos.ToServerRpcRequestMsg request) {
+ UUID sessionId = getSessionId(sessionInfo);
JsonObject json = new JsonObject();
- json.addProperty("method", request.getMethod());
+ json.addProperty("method", request.getMethodName());
json.add("params", jsonParser.parse(request.getParams()));
TbMsgMetaData requestMetaData = defaultMetaData.copy();
requestMetaData.putValue("requestId", Integer.toString(request.getRequestId()));
TbMsg tbMsg = new TbMsg(UUIDs.timeBased(), SessionMsgType.TO_SERVER_RPC_REQUEST.name(), deviceId, requestMetaData, TbMsgDataType.JSON, gson.toJson(json), null, null, 0L);
- PendingSessionMsgData msgData = new PendingSessionMsgData(src.getSessionId(), src.getServerAddress(), SessionMsgType.TO_SERVER_RPC_REQUEST, request.getRequestId(), false, 1);
- pushToRuleEngineWithTimeout(context, tbMsg, msgData);
+ context.parent().tell(new DeviceActorToRuleEngineMsg(context.self(), tbMsg), context.self());
scheduleMsgWithDelay(context, new DeviceActorClientSideRpcTimeoutMsg(request.getRequestId(), systemContext.getClientSideRpcTimeout()), systemContext.getClientSideRpcTimeout());
- toServerRpcPendingMap.put(request.getRequestId(), new ToServerRpcRequestMetadata(src.getSessionId(), src.getSessionType(), src.getServerAddress()));
+ toServerRpcPendingMap.put(request.getRequestId(), new ToServerRpcRequestMetadata(sessionId, getSessionType(sessionId), sessionInfo.getNodeId()));
+ }
+
+ private TransportProtos.SessionType getSessionType(UUID sessionId) {
+ return sessions.containsKey(sessionId) ? TransportProtos.SessionType.ASYNC : TransportProtos.SessionType.SYNC;
}
- public void processClientSideRpcTimeout(ActorContext context, DeviceActorClientSideRpcTimeoutMsg msg) {
+ void processClientSideRpcTimeout(ActorContext context, DeviceActorClientSideRpcTimeoutMsg msg) {
ToServerRpcRequestMetadata data = toServerRpcPendingMap.remove(msg.getId());
if (data != null) {
logger.debug("[{}] Client side RPC request [{}] timeout detected!", deviceId, msg.getId());
- ToDeviceMsg toDeviceMsg = new RuleEngineErrorMsg(SessionMsgType.TO_SERVER_RPC_REQUEST, RuleEngineError.TIMEOUT);
- sendMsgToSessionActor(new BasicActorSystemToDeviceSessionActorMsg(toDeviceMsg, data.getSessionId()), data.getServer());
+ sendToTransport(TransportProtos.ToServerRpcResponseMsg.newBuilder()
+ .setRequestId(msg.getId()).setError("timeout").build()
+ , data.getSessionId(), data.getNodeId());
}
}
void processToServerRPCResponse(ActorContext context, ToServerRpcResponseActorMsg msg) {
- ToServerRpcRequestMetadata data = toServerRpcPendingMap.remove(msg.getMsg().getRequestId());
+ int requestId = msg.getMsg().getRequestId();
+ ToServerRpcRequestMetadata data = toServerRpcPendingMap.remove(requestId);
if (data != null) {
- sendMsgToSessionActor(new BasicActorSystemToDeviceSessionActorMsg(msg.getMsg(), data.getSessionId()), data.getServer());
+ sendToTransport(TransportProtos.ToServerRpcResponseMsg.newBuilder()
+ .setRequestId(requestId).setPayload(msg.getMsg().getData()).build()
+ , data.getSessionId(), data.getNodeId());
}
}
- private void pushToRuleEngineWithTimeout(ActorContext context, TbMsg tbMsg, PendingSessionMsgData pendingMsgData) {
- SessionMsgType sessionMsgType = pendingMsgData.getSessionMsgType();
- int requestId = pendingMsgData.getRequestId();
- if (systemContext.isQueuePersistenceEnabled()) {
- pendingMsgs.put(tbMsg.getId(), pendingMsgData);
- scheduleMsgWithDelay(context, new DeviceActorQueueTimeoutMsg(tbMsg.getId(), systemContext.getQueuePersistenceTimeout()), systemContext.getQueuePersistenceTimeout());
- } else {
- ActorSystemToDeviceSessionActorMsg response = new BasicActorSystemToDeviceSessionActorMsg(BasicStatusCodeResponse.onSuccess(sessionMsgType, requestId), pendingMsgData.getSessionId());
- sendMsgToSessionActor(response, pendingMsgData.getServerAddress());
- }
+ private void pushToRuleEngine(ActorContext context, TbMsg tbMsg) {
context.parent().tell(new DeviceActorToRuleEngineMsg(context.self(), tbMsg), context.self());
}
void processAttributesUpdate(ActorContext context, DeviceAttributesEventNotificationMsg msg) {
if (attributeSubscriptions.size() > 0) {
- ToDeviceMsg notification = null;
+ boolean hasNotificationData = false;
+ AttributeUpdateNotificationMsg.Builder notification = AttributeUpdateNotificationMsg.newBuilder();
if (msg.isDeleted()) {
- List<AttributeKey> sharedKeys = msg.getDeletedKeys().stream()
+ List<String> sharedKeys = msg.getDeletedKeys().stream()
.filter(key -> DataConstants.SHARED_SCOPE.equals(key.getScope()))
+ .map(AttributeKey::getAttributeKey)
.collect(Collectors.toList());
- notification = new AttributesUpdateNotification(BasicAttributeKVMsg.fromDeleted(sharedKeys));
+ if (!sharedKeys.isEmpty()) {
+ notification.addAllSharedDeleted(sharedKeys);
+ hasNotificationData = true;
+ }
} else {
if (DataConstants.SHARED_SCOPE.equals(msg.getScope())) {
List<AttributeKvEntry> attributes = new ArrayList<>(msg.getValues());
if (attributes.size() > 0) {
- notification = new AttributesUpdateNotification(BasicAttributeKVMsg.fromShared(attributes));
+ List<TsKvProto> sharedUpdated = msg.getValues().stream().map(this::toTsKvProto)
+ .collect(Collectors.toList());
+ if (!sharedUpdated.isEmpty()) {
+ notification.addAllSharedUpdated(sharedUpdated);
+ hasNotificationData = true;
+ }
} else {
logger.debug("[{}] No public server side attributes changed!", deviceId);
}
}
}
- if (notification != null) {
- ToDeviceMsg finalNotification = notification;
+ if (hasNotificationData) {
+ AttributeUpdateNotificationMsg finalNotification = notification.build();
attributeSubscriptions.entrySet().forEach(sub -> {
- ActorSystemToDeviceSessionActorMsg response = new BasicActorSystemToDeviceSessionActorMsg(finalNotification, sub.getKey());
- sendMsgToSessionActor(response, sub.getValue().getServer());
+ sendToTransport(finalNotification, sub.getKey(), sub.getValue().getNodeId());
});
}
} else {
@@ -444,75 +394,83 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
}
}
- private void processRpcResponses(ActorContext context, DeviceToDeviceActorMsg msg) {
- SessionId sessionId = msg.getSessionId();
- FromDeviceMsg inMsg = msg.getPayload();
- if (inMsg.getMsgType() == SessionMsgType.TO_DEVICE_RPC_RESPONSE) {
- logger.debug("[{}] Processing rpc command response [{}]", deviceId, sessionId);
- ToDeviceRpcResponseMsg responseMsg = (ToDeviceRpcResponseMsg) inMsg;
- ToDeviceRpcRequestMetadata requestMd = toDeviceRpcPendingMap.remove(responseMsg.getRequestId());
- boolean success = requestMd != null;
- if (success) {
- systemContext.getDeviceRpcService().processRpcResponseFromDevice(new FromDeviceRpcResponse(requestMd.getMsg().getMsg().getId(),
- requestMd.getMsg().getServerAddress(), responseMsg.getData(), null));
- } else {
- logger.debug("[{}] Rpc command response [{}] is stale!", deviceId, responseMsg.getRequestId());
- }
- if (msg.getSessionType() == SessionType.SYNC) {
- BasicCommandAckResponse response = success
- ? BasicCommandAckResponse.onSuccess(SessionMsgType.TO_DEVICE_RPC_REQUEST, responseMsg.getRequestId())
- : BasicCommandAckResponse.onError(SessionMsgType.TO_DEVICE_RPC_REQUEST, responseMsg.getRequestId(), new TimeoutException());
- sendMsgToSessionActor(new BasicActorSystemToDeviceSessionActorMsg(response, msg.getSessionId()), msg.getServerAddress());
- }
+ private void processRpcResponses(ActorContext context, SessionInfoProto sessionInfo, ToDeviceRpcResponseMsg responseMsg) {
+ UUID sessionId = getSessionId(sessionInfo);
+ logger.debug("[{}] Processing rpc command response [{}]", deviceId, sessionId);
+ ToDeviceRpcRequestMetadata requestMd = toDeviceRpcPendingMap.remove(responseMsg.getRequestId());
+ boolean success = requestMd != null;
+ if (success) {
+ systemContext.getDeviceRpcService().processRpcResponseFromDevice(new FromDeviceRpcResponse(requestMd.getMsg().getMsg().getId(),
+ requestMd.getMsg().getServerAddress(), responseMsg.getPayload(), null));
+ } else {
+ logger.debug("[{}] Rpc command response [{}] is stale!", deviceId, responseMsg.getRequestId());
}
}
void processClusterEventMsg(ClusterEventMsg msg) {
- if (!msg.isAdded()) {
- logger.debug("[{}] Clearing attributes/rpc subscription for server [{}]", deviceId, msg.getServerAddress());
- Predicate<Map.Entry<SessionId, SessionInfo>> filter = e -> e.getValue().getServer()
- .map(serverAddress -> serverAddress.equals(msg.getServerAddress())).orElse(false);
- attributeSubscriptions.entrySet().removeIf(filter);
- rpcSubscriptions.entrySet().removeIf(filter);
- }
+// if (!msg.isAdded()) {
+// logger.debug("[{}] Clearing attributes/rpc subscription for server [{}]", deviceId, msg.getServerAddress());
+// Predicate<Map.Entry<SessionId, SessionInfo>> filter = e -> e.getValue().getServer()
+// .map(serverAddress -> serverAddress.equals(msg.getServerAddress())).orElse(false);
+// attributeSubscriptions.entrySet().removeIf(filter);
+// rpcSubscriptions.entrySet().removeIf(filter);
+// }
}
- private void processSubscriptionCommands(ActorContext context, DeviceToDeviceActorMsg msg) {
- SessionId sessionId = msg.getSessionId();
- SessionType sessionType = msg.getSessionType();
- FromDeviceMsg inMsg = msg.getPayload();
- if (inMsg.getMsgType() == SessionMsgType.SUBSCRIBE_ATTRIBUTES_REQUEST) {
- logger.debug("[{}] Registering attributes subscription for session [{}]", deviceId, sessionId);
- attributeSubscriptions.put(sessionId, new SessionInfo(sessionType, msg.getServerAddress()));
- } else if (inMsg.getMsgType() == SessionMsgType.UNSUBSCRIBE_ATTRIBUTES_REQUEST) {
+ private void processSubscriptionCommands(ActorContext context, SessionInfoProto sessionInfo, SubscribeToAttributeUpdatesMsg subscribeCmd) {
+ UUID sessionId = getSessionId(sessionInfo);
+ if (subscribeCmd.getUnsubscribe()) {
logger.debug("[{}] Canceling attributes subscription for session [{}]", deviceId, sessionId);
attributeSubscriptions.remove(sessionId);
- } else if (inMsg.getMsgType() == SessionMsgType.SUBSCRIBE_RPC_COMMANDS_REQUEST) {
- logger.debug("[{}] Registering rpc subscription for session [{}][{}]", deviceId, sessionId, sessionType);
- rpcSubscriptions.put(sessionId, new SessionInfo(sessionType, msg.getServerAddress()));
- sendPendingRequests(context, sessionId, sessionType, msg.getServerAddress());
- } else if (inMsg.getMsgType() == SessionMsgType.UNSUBSCRIBE_RPC_COMMANDS_REQUEST) {
- logger.debug("[{}] Canceling rpc subscription for session [{}][{}]", deviceId, sessionId, sessionType);
+ } else {
+ SessionInfo session = sessions.get(sessionId);
+ if (session == null) {
+ session = new SessionInfo(TransportProtos.SessionType.SYNC, sessionInfo.getNodeId());
+ }
+ logger.debug("[{}] Registering attributes subscription for session [{}]", deviceId, sessionId);
+ attributeSubscriptions.put(sessionId, session);
+ }
+ }
+
+ private UUID getSessionId(SessionInfoProto sessionInfo) {
+ return new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB());
+ }
+
+ private void processSubscriptionCommands(ActorContext context, SessionInfoProto sessionInfo, SubscribeToRPCMsg subscribeCmd) {
+ UUID sessionId = getSessionId(sessionInfo);
+ if (subscribeCmd.getUnsubscribe()) {
+ logger.debug("[{}] Canceling rpc subscription for session [{}]", deviceId, sessionId);
rpcSubscriptions.remove(sessionId);
+ } else {
+ SessionInfo session = sessions.get(sessionId);
+ if (session == null) {
+ session = new SessionInfo(TransportProtos.SessionType.SYNC, sessionInfo.getNodeId());
+ }
+ logger.debug("[{}] Registering rpc subscription for session [{}]", deviceId, sessionId);
+ rpcSubscriptions.put(sessionId, session);
+ sendPendingRequests(context, sessionId, sessionInfo);
}
}
- private void processSessionStateMsgs(DeviceToDeviceActorMsg msg) {
- SessionId sessionId = msg.getSessionId();
- FromDeviceMsg inMsg = msg.getPayload();
- if (inMsg instanceof SessionOpenMsg) {
+ private void processSessionStateMsgs(SessionInfoProto sessionInfo, SessionEventMsg msg) {
+ UUID sessionId = getSessionId(sessionInfo);
+ if (msg.getEvent() == SessionEvent.OPEN) {
+ if(sessions.containsKey(sessionId)){
+ logger.debug("[{}] Received duplicate session open event [{}]", deviceId, sessionId);
+ return;
+ }
logger.debug("[{}] Processing new session [{}]", deviceId, sessionId);
if (sessions.size() >= systemContext.getMaxConcurrentSessionsPerDevice()) {
- SessionId sessionIdToRemove = sessions.keySet().stream().findFirst().orElse(null);
+ UUID sessionIdToRemove = sessions.keySet().stream().findFirst().orElse(null);
if (sessionIdToRemove != null) {
closeSession(sessionIdToRemove, sessions.remove(sessionIdToRemove));
}
}
- sessions.put(sessionId, new SessionInfo(SessionType.ASYNC, msg.getServerAddress()));
+ sessions.put(sessionId, new SessionInfo(TransportProtos.SessionType.ASYNC, sessionInfo.getNodeId()));
if (sessions.size() == 1) {
reportSessionOpen();
}
- } else if (inMsg instanceof SessionCloseMsg) {
+ } else if (msg.getEvent() == SessionEvent.CLOSED) {
logger.debug("[{}] Canceling subscriptions for closed session [{}]", deviceId, sessionId);
sessions.remove(sessionId);
attributeSubscriptions.remove(sessionId);
@@ -523,25 +481,18 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
}
}
- private void sendMsgToSessionActor(ActorSystemToDeviceSessionActorMsg response, Optional<ServerAddress> sessionAddress) {
- if (sessionAddress.isPresent()) {
- ServerAddress address = sessionAddress.get();
- logger.debug("{} Forwarding msg: {}", address, response);
- systemContext.getRpcService().tell(systemContext.getEncodingService()
- .convertToProtoDataMessage(sessionAddress.get(), response));
- } else {
- systemContext.getSessionManagerActor().tell(response, ActorRef.noSender());
- }
- }
-
void processCredentialsUpdate() {
sessions.forEach(this::closeSession);
attributeSubscriptions.clear();
rpcSubscriptions.clear();
}
- private void closeSession(SessionId sessionId, SessionInfo sessionInfo) {
- sendMsgToSessionActor(new BasicActorSystemToDeviceSessionActorMsg(new SessionCloseNotification(), sessionId), sessionInfo.getServer());
+ private void closeSession(UUID sessionId, SessionInfo sessionInfo) {
+ DeviceActorToTransportMsg msg = DeviceActorToTransportMsg.newBuilder()
+ .setSessionIdMSB(sessionId.getMostSignificantBits())
+ .setSessionIdLSB(sessionId.getLeastSignificantBits())
+ .setSessionCloseNotification(SessionCloseNotificationProto.getDefaultInstance()).build();
+ systemContext.getRuleEngineTransportService().process(sessionInfo.getNodeId(), msg);
}
void processNameOrTypeUpdate(DeviceNameOrTypeUpdateMsg msg) {
@@ -552,4 +503,107 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
this.defaultMetaData.putValue("deviceType", deviceType);
}
+ private JsonObject getJsonObject(List<KeyValueProto> tsKv) {
+ JsonObject json = new JsonObject();
+ for (KeyValueProto kv : tsKv) {
+ switch (kv.getType()) {
+ case BOOLEAN_V:
+ json.addProperty(kv.getKey(), kv.getBoolV());
+ break;
+ case LONG_V:
+ json.addProperty(kv.getKey(), kv.getLongV());
+ break;
+ case DOUBLE_V:
+ json.addProperty(kv.getKey(), kv.getDoubleV());
+ break;
+ case STRING_V:
+ json.addProperty(kv.getKey(), kv.getStringV());
+ break;
+ }
+ }
+ return json;
+ }
+
+ private Optional<Set<String>> toOptionalSet(List<String> strings) {
+ if (strings == null || strings.isEmpty()) {
+ return Optional.empty();
+ } else {
+ return Optional.of(new HashSet<>(strings));
+ }
+ }
+
+ private void sendToTransport(GetAttributeResponseMsg responseMsg, SessionInfoProto sessionInfo) {
+ DeviceActorToTransportMsg msg = DeviceActorToTransportMsg.newBuilder()
+ .setSessionIdMSB(sessionInfo.getSessionIdMSB())
+ .setSessionIdLSB(sessionInfo.getSessionIdLSB())
+ .setGetAttributesResponse(responseMsg).build();
+ systemContext.getRuleEngineTransportService().process(sessionInfo.getNodeId(), msg);
+ }
+
+ private void sendToTransport(AttributeUpdateNotificationMsg notificationMsg, UUID sessionId, String nodeId) {
+ DeviceActorToTransportMsg msg = DeviceActorToTransportMsg.newBuilder()
+ .setSessionIdMSB(sessionId.getMostSignificantBits())
+ .setSessionIdLSB(sessionId.getLeastSignificantBits())
+ .setAttributeUpdateNotification(notificationMsg).build();
+ systemContext.getRuleEngineTransportService().process(nodeId, msg);
+ }
+
+ private void sendToTransport(ToDeviceRpcRequestMsg rpcMsg, UUID sessionId, String nodeId) {
+ DeviceActorToTransportMsg msg = DeviceActorToTransportMsg.newBuilder()
+ .setSessionIdMSB(sessionId.getMostSignificantBits())
+ .setSessionIdLSB(sessionId.getLeastSignificantBits())
+ .setToDeviceRequest(rpcMsg).build();
+ systemContext.getRuleEngineTransportService().process(nodeId, msg);
+ }
+
+ private void sendToTransport(TransportProtos.ToServerRpcResponseMsg rpcMsg, UUID sessionId, String nodeId) {
+ DeviceActorToTransportMsg msg = DeviceActorToTransportMsg.newBuilder()
+ .setSessionIdMSB(sessionId.getMostSignificantBits())
+ .setSessionIdLSB(sessionId.getLeastSignificantBits())
+ .setToServerResponse(rpcMsg).build();
+ systemContext.getRuleEngineTransportService().process(nodeId, msg);
+ }
+
+
+ private List<TsKvProto> toTsKvProtos(@Nullable List<AttributeKvEntry> result) {
+ List<TsKvProto> clientAttributes;
+ if (result == null || result.isEmpty()) {
+ clientAttributes = Collections.emptyList();
+ } else {
+ clientAttributes = new ArrayList<>(result.size());
+ for (AttributeKvEntry attrEntry : result) {
+ clientAttributes.add(toTsKvProto(attrEntry));
+ }
+ }
+ return clientAttributes;
+ }
+
+ private TsKvProto toTsKvProto(AttributeKvEntry attrEntry) {
+ return TsKvProto.newBuilder().setTs(attrEntry.getLastUpdateTs())
+ .setKv(toKeyValueProto(attrEntry)).build();
+ }
+
+ private KeyValueProto toKeyValueProto(KvEntry kvEntry) {
+ KeyValueProto.Builder builder = KeyValueProto.newBuilder();
+ builder.setKey(kvEntry.getKey());
+ switch (kvEntry.getDataType()) {
+ case BOOLEAN:
+ builder.setType(KeyValueType.BOOLEAN_V);
+ builder.setBoolV(kvEntry.getBooleanValue().get());
+ break;
+ case DOUBLE:
+ builder.setType(KeyValueType.DOUBLE_V);
+ builder.setDoubleV(kvEntry.getDoubleValue().get());
+ break;
+ case LONG:
+ builder.setType(KeyValueType.LONG_V);
+ builder.setLongV(kvEntry.getLongValue().get());
+ break;
+ case STRING:
+ builder.setType(KeyValueType.STRING_V);
+ builder.setStringV(kvEntry.getStrValue().get());
+ break;
+ }
+ return builder.build();
+ }
}
diff --git a/application/src/main/java/org/thingsboard/server/actors/device/SessionInfo.java b/application/src/main/java/org/thingsboard/server/actors/device/SessionInfo.java
index 04c457c..43ae592 100644
--- a/application/src/main/java/org/thingsboard/server/actors/device/SessionInfo.java
+++ b/application/src/main/java/org/thingsboard/server/actors/device/SessionInfo.java
@@ -16,10 +16,7 @@
package org.thingsboard.server.actors.device;
import lombok.Data;
-import org.thingsboard.server.common.msg.cluster.ServerAddress;
-import org.thingsboard.server.common.msg.session.SessionType;
-
-import java.util.Optional;
+import org.thingsboard.server.gen.transport.TransportProtos.SessionType;
/**
* @author Andrew Shvayka
@@ -27,5 +24,5 @@ import java.util.Optional;
@Data
public class SessionInfo {
private final SessionType type;
- private final Optional<ServerAddress> server;
+ private final String nodeId;
}
diff --git a/application/src/main/java/org/thingsboard/server/actors/device/ToServerRpcRequestMetadata.java b/application/src/main/java/org/thingsboard/server/actors/device/ToServerRpcRequestMetadata.java
index f82a8c2..669d94b 100644
--- a/application/src/main/java/org/thingsboard/server/actors/device/ToServerRpcRequestMetadata.java
+++ b/application/src/main/java/org/thingsboard/server/actors/device/ToServerRpcRequestMetadata.java
@@ -16,18 +16,16 @@
package org.thingsboard.server.actors.device;
import lombok.Data;
-import org.thingsboard.server.common.data.id.SessionId;
-import org.thingsboard.server.common.msg.cluster.ServerAddress;
-import org.thingsboard.server.common.msg.session.SessionType;
+import org.thingsboard.server.gen.transport.TransportProtos;
-import java.util.Optional;
+import java.util.UUID;
/**
* @author Andrew Shvayka
*/
@Data
public class ToServerRpcRequestMetadata {
- private final SessionId sessionId;
- private final SessionType type;
- private final Optional<ServerAddress> server;
+ private final UUID sessionId;
+ private final TransportProtos.SessionType type;
+ private final String nodeId;
}
diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java
index fe02335..3da90d1 100644
--- a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java
+++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java
@@ -25,7 +25,6 @@ import java.util.Optional;
import org.thingsboard.server.actors.ActorSystemContext;
import org.thingsboard.server.actors.device.DeviceActorToRuleEngineMsg;
-import org.thingsboard.server.actors.device.RuleEngineQueuePutAckMsg;
import org.thingsboard.server.actors.service.DefaultActorService;
import org.thingsboard.server.actors.shared.ComponentMsgProcessor;
import org.thingsboard.server.common.data.EntityType;
@@ -90,26 +89,12 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
nodeActors.put(ruleNode.getId(), new RuleNodeCtx(tenantId, self, ruleNodeActor, ruleNode));
}
initRoutes(ruleChain, ruleNodeList);
- reprocess(ruleNodeList);
started = true;
} else {
onUpdate(context);
}
}
- private void reprocess(List<RuleNode> ruleNodeList) {
- for (RuleNode ruleNode : ruleNodeList) {
- for (TbMsg tbMsg : queue.findUnprocessed(tenantId, ruleNode.getId().getId(), systemContext.getQueuePartitionId())) {
- pushMsgToNode(nodeActors.get(ruleNode.getId()), tbMsg, "");
- }
- }
- if (firstNode != null) {
- for (TbMsg tbMsg : queue.findUnprocessed(tenantId, entityId.getId(), systemContext.getQueuePartitionId())) {
- pushMsgToNode(firstNode, tbMsg, "");
- }
- }
- }
-
@Override
public void onUpdate(ActorContext context) throws Exception {
RuleChain ruleChain = service.findRuleChainById(entityId);
@@ -134,7 +119,6 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
});
initRoutes(ruleChain, ruleNodeList);
- reprocess(ruleNodeList);
}
@Override
@@ -188,17 +172,14 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
void onServiceToRuleEngineMsg(ServiceToRuleEngineMsg envelope) {
checkActive();
if (firstNode != null) {
- putToQueue(enrichWithRuleChainId(envelope.getTbMsg()), msg -> pushMsgToNode(firstNode, msg, ""));
+ pushMsgToNode(firstNode, enrichWithRuleChainId(envelope.getTbMsg()), "");
}
}
void onDeviceActorToRuleEngineMsg(DeviceActorToRuleEngineMsg envelope) {
checkActive();
if (firstNode != null) {
- putToQueue(enrichWithRuleChainId(envelope.getTbMsg()), msg -> {
- pushMsgToNode(firstNode, msg, "");
- envelope.getCallbackRef().tell(new RuleEngineQueuePutAckMsg(msg.getId()), self);
- });
+ pushMsgToNode(firstNode, enrichWithRuleChainId(envelope.getTbMsg()), "");
}
}
@@ -206,15 +187,16 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
checkActive();
if (envelope.isEnqueue()) {
if (firstNode != null) {
- putToQueue(enrichWithRuleChainId(envelope.getMsg()), msg -> pushMsgToNode(firstNode, msg, envelope.getFromRelationType()));
+ pushMsgToNode(firstNode, enrichWithRuleChainId(envelope.getMsg()), envelope.getFromRelationType());
}
} else {
if (firstNode != null) {
pushMsgToNode(firstNode, envelope.getMsg(), envelope.getFromRelationType());
} else {
- TbMsg msg = envelope.getMsg();
- EntityId ackId = msg.getRuleNodeId() != null ? msg.getRuleNodeId() : msg.getRuleChainId();
- queue.ack(tenantId, envelope.getMsg(), ackId.getId(), msg.getClusterPartition());
+// TODO: Ack this message in Kafka
+// TbMsg msg = envelope.getMsg();
+// EntityId ackId = msg.getRuleNodeId() != null ? msg.getRuleNodeId() : msg.getRuleChainId();
+// queue.ack(tenantId, envelope.getMsg(), ackId.getId(), msg.getClusterPartition());
}
}
}
@@ -249,7 +231,8 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
EntityId ackId = msg.getRuleNodeId() != null ? msg.getRuleNodeId() : msg.getRuleChainId();
if (relationsCount == 0) {
if (ackId != null) {
- queue.ack(tenantId, msg, ackId.getId(), msg.getClusterPartition());
+// TODO: Ack this message in Kafka
+// queue.ack(tenantId, msg, ackId.getId(), msg.getClusterPartition());
}
} else if (relationsCount == 1) {
for (RuleNodeRelation relation : relations) {
@@ -269,7 +252,8 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
}
//TODO: Ideally this should happen in async way when all targets confirm that the copied messages are successfully written to corresponding target queues.
if (ackId != null) {
- queue.ack(tenantId, msg, ackId.getId(), msg.getClusterPartition());
+// TODO: Ack this message in Kafka
+// queue.ack(tenantId, msg, ackId.getId(), msg.getClusterPartition());
}
}
}
@@ -296,7 +280,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
RuleNodeId targetId = new RuleNodeId(target.getId());
RuleNodeCtx targetNodeCtx = nodeActors.get(targetId);
TbMsg copy = msg.copy(UUIDs.timeBased(), entityId, targetId, DEFAULT_CLUSTER_PARTITION);
- putToQueue(copy, queuedMsg -> pushMsgToNode(targetNodeCtx, queuedMsg, fromRelationType));
+ pushMsgToNode(targetNodeCtx, copy, fromRelationType);
}
private void pushToTarget(TbMsg msg, EntityId target, String fromRelationType) {
diff --git a/application/src/main/java/org/thingsboard/server/actors/service/DefaultActorService.java b/application/src/main/java/org/thingsboard/server/actors/service/DefaultActorService.java
index 1e9e23b..c2e8fd0 100644
--- a/application/src/main/java/org/thingsboard/server/actors/service/DefaultActorService.java
+++ b/application/src/main/java/org/thingsboard/server/actors/service/DefaultActorService.java
@@ -30,7 +30,6 @@ import org.thingsboard.server.actors.app.AppActor;
import org.thingsboard.server.actors.rpc.RpcBroadcastMsg;
import org.thingsboard.server.actors.rpc.RpcManagerActor;
import org.thingsboard.server.actors.rpc.RpcSessionCreateRequestMsg;
-import org.thingsboard.server.actors.session.SessionManagerActor;
import org.thingsboard.server.actors.stats.StatsActor;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.id.DeviceId;
@@ -38,7 +37,6 @@ import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
import org.thingsboard.server.common.msg.TbActorMsg;
-import org.thingsboard.server.common.msg.aware.SessionAwareMsg;
import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;
import org.thingsboard.server.common.msg.cluster.SendToClusterMsg;
import org.thingsboard.server.common.msg.cluster.ServerAddress;
@@ -90,8 +88,6 @@ public class DefaultActorService implements ActorService {
private ActorRef appActor;
- private ActorRef sessionManagerActor;
-
private ActorRef rpcManagerActor;
@PostConstruct
@@ -104,10 +100,6 @@ public class DefaultActorService implements ActorService {
appActor = system.actorOf(Props.create(new AppActor.ActorCreator(actorContext)).withDispatcher(APP_DISPATCHER_NAME), "appActor");
actorContext.setAppActor(appActor);
- sessionManagerActor = system.actorOf(Props.create(new SessionManagerActor.ActorCreator(actorContext)).withDispatcher(CORE_DISPATCHER_NAME),
- "sessionManagerActor");
- actorContext.setSessionManagerActor(sessionManagerActor);
-
rpcManagerActor = system.actorOf(Props.create(new RpcManagerActor.ActorCreator(actorContext)).withDispatcher(CORE_DISPATCHER_NAME),
"rpcManagerActor");
@@ -135,12 +127,6 @@ public class DefaultActorService implements ActorService {
}
@Override
- public void process(SessionAwareMsg msg) {
- log.debug("Processing session aware msg: {}", msg);
- sessionManagerActor.tell(msg, ActorRef.noSender());
- }
-
- @Override
public void onServerAdded(ServerInstance server) {
log.trace("Processing onServerAdded msg: {}", server);
broadcast(new ClusterEventMsg(server.getServerAddress(), true));
@@ -194,7 +180,6 @@ public class DefaultActorService implements ActorService {
private void broadcast(ClusterEventMsg msg) {
this.appActor.tell(msg, ActorRef.noSender());
- this.sessionManagerActor.tell(msg, ActorRef.noSender());
this.rpcManagerActor.tell(msg, ActorRef.noSender());
}
diff --git a/application/src/main/java/org/thingsboard/server/actors/shared/ComponentMsgProcessor.java b/application/src/main/java/org/thingsboard/server/actors/shared/ComponentMsgProcessor.java
index c35b255..2c40be6 100644
--- a/application/src/main/java/org/thingsboard/server/actors/shared/ComponentMsgProcessor.java
+++ b/application/src/main/java/org/thingsboard/server/actors/shared/ComponentMsgProcessor.java
@@ -35,14 +35,12 @@ public abstract class ComponentMsgProcessor<T extends EntityId> extends Abstract
protected final TenantId tenantId;
protected final T entityId;
- protected final MsgQueueService queue;
protected ComponentLifecycleState state;
protected ComponentMsgProcessor(ActorSystemContext systemContext, LoggingAdapter logger, TenantId tenantId, T id) {
super(systemContext, logger);
this.tenantId = tenantId;
this.entityId = id;
- this.queue = systemContext.getMsgQueueService();
}
public abstract void start(ActorContext context) throws Exception;
@@ -87,18 +85,4 @@ public abstract class ComponentMsgProcessor<T extends EntityId> extends Abstract
}
}
- protected void putToQueue(final TbMsg tbMsg, final Consumer<TbMsg> onSuccess) {
- EntityId entityId = tbMsg.getRuleNodeId() != null ? tbMsg.getRuleNodeId() : tbMsg.getRuleChainId();
- Futures.addCallback(queue.put(this.tenantId, tbMsg, entityId.getId(), tbMsg.getClusterPartition()), new FutureCallback<Void>() {
- @Override
- public void onSuccess(@Nullable Void result) {
- onSuccess.accept(tbMsg);
- }
-
- @Override
- public void onFailure(Throwable t) {
- logger.debug("Failed to push message [{}] to queue due to [{}]", tbMsg, t);
- }
- });
- }
}
diff --git a/application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java b/application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java
index 460b64c..347483a 100644
--- a/application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java
+++ b/application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java
@@ -87,7 +87,7 @@ public class TenantActor extends RuleChainManagerActor {
case DEVICE_ACTOR_TO_RULE_ENGINE_MSG:
onDeviceActorToRuleEngineMsg((DeviceActorToRuleEngineMsg) msg);
break;
- case DEVICE_SESSION_TO_DEVICE_ACTOR_MSG:
+ case TRANSPORT_TO_DEVICE_ACTOR_MSG:
case DEVICE_ATTRIBUTES_UPDATE_TO_DEVICE_ACTOR_MSG:
case DEVICE_CREDENTIALS_UPDATE_TO_DEVICE_ACTOR_MSG:
case DEVICE_NAME_OR_TYPE_UPDATE_TO_DEVICE_ACTOR_MSG:
diff --git a/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java b/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java
index 9f92019..6c37614 100644
--- a/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java
@@ -59,7 +59,6 @@ import org.thingsboard.server.common.data.kv.ReadTsKvQuery;
import org.thingsboard.server.common.data.kv.StringDataEntry;
import org.thingsboard.server.common.data.kv.TsKvEntry;
import org.thingsboard.server.common.msg.cluster.SendToClusterMsg;
-import org.thingsboard.server.common.msg.core.TelemetryUploadRequest;
import org.thingsboard.server.common.transport.adaptor.JsonConverter;
import org.thingsboard.server.dao.attributes.AttributesService;
import org.thingsboard.server.dao.timeseries.TimeseriesService;
@@ -346,7 +345,7 @@ public class TelemetryController extends BaseController {
}
private DeferredResult<ResponseEntity> saveTelemetry(EntityId entityIdSrc, String requestBody, long ttl) throws ThingsboardException {
- TelemetryUploadRequest telemetryRequest;
+ Map<Long, List<KvEntry>> telemetryRequest;
JsonElement telemetryJson;
try {
telemetryJson = new JsonParser().parse(requestBody);
@@ -354,12 +353,12 @@ public class TelemetryController extends BaseController {
return getImmediateDeferredResult("Unable to parse timeseries payload: Invalid JSON body!", HttpStatus.BAD_REQUEST);
}
try {
- telemetryRequest = JsonConverter.convertToTelemetry(telemetryJson);
+ telemetryRequest = JsonConverter.convertToTelemetry(telemetryJson, System.currentTimeMillis());
} catch (Exception e) {
return getImmediateDeferredResult("Unable to parse timeseries payload. Invalid JSON body: " + e.getMessage(), HttpStatus.BAD_REQUEST);
}
List<TsKvEntry> entries = new ArrayList<>();
- for (Map.Entry<Long, List<KvEntry>> entry : telemetryRequest.getData().entrySet()) {
+ for (Map.Entry<Long, List<KvEntry>> entry : telemetryRequest.entrySet()) {
for (KvEntry kv : entry.getValue()) {
entries.add(new BasicTsKvEntry(entry.getKey(), kv));
}
diff --git a/application/src/main/java/org/thingsboard/server/service/cluster/discovery/DiscoveryService.java b/application/src/main/java/org/thingsboard/server/service/cluster/discovery/DiscoveryService.java
index 04c4ced..f9caafa 100644
--- a/application/src/main/java/org/thingsboard/server/service/cluster/discovery/DiscoveryService.java
+++ b/application/src/main/java/org/thingsboard/server/service/cluster/discovery/DiscoveryService.java
@@ -26,8 +26,6 @@ public interface DiscoveryService {
void unpublishCurrentServer();
- String getNodeId();
-
ServerInstance getCurrentServer();
List<ServerInstance> getOtherServers();
diff --git a/application/src/main/java/org/thingsboard/server/service/cluster/discovery/DummyDiscoveryService.java b/application/src/main/java/org/thingsboard/server/service/cluster/discovery/DummyDiscoveryService.java
index 859a7af..358c847 100644
--- a/application/src/main/java/org/thingsboard/server/service/cluster/discovery/DummyDiscoveryService.java
+++ b/application/src/main/java/org/thingsboard/server/service/cluster/discovery/DummyDiscoveryService.java
@@ -38,17 +38,9 @@ public class DummyDiscoveryService implements DiscoveryService {
@Autowired
private ServerInstanceService serverInstance;
- private String nodeId;
-
@PostConstruct
public void init() {
log.info("Initializing...");
- this.nodeId = RandomStringUtils.randomAlphabetic(10);
- }
-
- @Override
- public String getNodeId() {
- return nodeId;
}
@Override
diff --git a/application/src/main/java/org/thingsboard/server/service/cluster/discovery/ZkDiscoveryService.java b/application/src/main/java/org/thingsboard/server/service/cluster/discovery/ZkDiscoveryService.java
index b4829f2..bb92642 100644
--- a/application/src/main/java/org/thingsboard/server/service/cluster/discovery/ZkDiscoveryService.java
+++ b/application/src/main/java/org/thingsboard/server/service/cluster/discovery/ZkDiscoveryService.java
@@ -94,13 +94,10 @@ public class ZkDiscoveryService implements DiscoveryService, PathChildrenCacheLi
private CuratorFramework client;
private PathChildrenCache cache;
private String nodePath;
- //TODO: make persistent?
- private String nodeId;
@PostConstruct
public void init() {
log.info("Initializing...");
- this.nodeId = RandomStringUtils.randomAlphabetic(10);
Assert.hasLength(zkUrl, MiscUtils.missingProperty("zk.url"));
Assert.notNull(zkRetryInterval, MiscUtils.missingProperty("zk.retry_interval_ms"));
Assert.notNull(zkConnectionTimeout, MiscUtils.missingProperty("zk.connection_timeout_ms"));
@@ -184,11 +181,6 @@ public class ZkDiscoveryService implements DiscoveryService, PathChildrenCacheLi
}
@Override
- public String getNodeId() {
- return nodeId;
- }
-
- @Override
public ServerInstance getCurrentServer() {
return serverInstance.getSelf();
}
diff --git a/application/src/main/java/org/thingsboard/server/service/queue/DefaultMsgQueueService.java b/application/src/main/java/org/thingsboard/server/service/queue/DefaultMsgQueueService.java
index 9275847..416a4d6 100644
--- a/application/src/main/java/org/thingsboard/server/service/queue/DefaultMsgQueueService.java
+++ b/application/src/main/java/org/thingsboard/server/service/queue/DefaultMsgQueueService.java
@@ -49,7 +49,7 @@ public class DefaultMsgQueueService implements MsgQueueService {
@Autowired
private MsgQueue msgQueue;
- @Autowired
+ @Autowired(required = false)
private TenantQuotaService quotaService;
private ScheduledExecutorService cleanupExecutor;
@@ -74,7 +74,7 @@ public class DefaultMsgQueueService implements MsgQueueService {
@Override
public ListenableFuture<Void> put(TenantId tenantId, TbMsg msg, UUID nodeId, long clusterPartition) {
- if(quotaService.isQuotaExceeded(tenantId.getId().toString())) {
+ if(quotaService != null && quotaService.isQuotaExceeded(tenantId.getId().toString())) {
log.warn("Tenant TbMsg Quota exceeded for [{}:{}] . Reject", tenantId.getId());
return Futures.immediateFailedFuture(new RuntimeException("Tenant TbMsg Quota exceeded"));
}
diff --git a/application/src/main/java/org/thingsboard/server/service/rpc/ToServerRpcResponseActorMsg.java b/application/src/main/java/org/thingsboard/server/service/rpc/ToServerRpcResponseActorMsg.java
index 201f656..d33a338 100644
--- a/application/src/main/java/org/thingsboard/server/service/rpc/ToServerRpcResponseActorMsg.java
+++ b/application/src/main/java/org/thingsboard/server/service/rpc/ToServerRpcResponseActorMsg.java
@@ -22,11 +22,8 @@ import org.thingsboard.rule.engine.api.msg.ToDeviceActorNotificationMsg;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.msg.MsgType;
-import org.thingsboard.server.common.msg.cluster.ServerAddress;
import org.thingsboard.server.common.msg.core.ToServerRpcResponseMsg;
-import java.util.Optional;
-
/**
* Created by ashvayka on 16.04.18.
*/
diff --git a/application/src/main/java/org/thingsboard/server/service/script/RemoteJsInvokeService.java b/application/src/main/java/org/thingsboard/server/service/script/RemoteJsInvokeService.java
index 37a6211..db31bda 100644
--- a/application/src/main/java/org/thingsboard/server/service/script/RemoteJsInvokeService.java
+++ b/application/src/main/java/org/thingsboard/server/service/script/RemoteJsInvokeService.java
@@ -28,6 +28,7 @@ import org.thingsboard.server.kafka.TBKafkaConsumerTemplate;
import org.thingsboard.server.kafka.TBKafkaProducerTemplate;
import org.thingsboard.server.kafka.TbKafkaRequestTemplate;
import org.thingsboard.server.kafka.TbKafkaSettings;
+import org.thingsboard.server.kafka.TbNodeIdProvider;
import org.thingsboard.server.service.cluster.discovery.DiscoveryService;
import javax.annotation.PostConstruct;
@@ -42,7 +43,7 @@ import java.util.concurrent.ConcurrentHashMap;
public class RemoteJsInvokeService extends AbstractJsInvokeService {
@Autowired
- private DiscoveryService discoveryService;
+ private TbNodeIdProvider nodeIdProvider;
@Autowired
private TbKafkaSettings kafkaSettings;
@@ -97,15 +98,13 @@ public class RemoteJsInvokeService extends AbstractJsInvokeService {
TBKafkaConsumerTemplate.TBKafkaConsumerTemplateBuilder<JsInvokeProtos.RemoteJsResponse> responseBuilder = TBKafkaConsumerTemplate.builder();
responseBuilder.settings(kafkaSettings);
- responseBuilder.topic(responseTopicPrefix + "." + discoveryService.getNodeId());
- responseBuilder.clientId(discoveryService.getNodeId());
+ responseBuilder.topic(responseTopicPrefix + "." + nodeIdProvider.getNodeId());
+ responseBuilder.clientId("js-" + nodeIdProvider.getNodeId());
responseBuilder.groupId("rule-engine-node");
responseBuilder.autoCommit(true);
responseBuilder.autoCommitIntervalMs(autoCommitInterval);
responseBuilder.decoder(new RemoteJsResponseDecoder());
- responseBuilder.requestIdExtractor((response) -> {
- return new UUID(response.getRequestIdMSB(), response.getRequestIdLSB());
- });
+ responseBuilder.requestIdExtractor((response) -> new UUID(response.getRequestIdMSB(), response.getRequestIdLSB()));
TbKafkaRequestTemplate.TbKafkaRequestTemplateBuilder
<JsInvokeProtos.RemoteJsRequest, JsInvokeProtos.RemoteJsResponse> builder = TbKafkaRequestTemplate.builder();
diff --git a/application/src/main/java/org/thingsboard/server/service/transport/LocalTransportApiService.java b/application/src/main/java/org/thingsboard/server/service/transport/LocalTransportApiService.java
new file mode 100644
index 0000000..5c39f8a
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/service/transport/LocalTransportApiService.java
@@ -0,0 +1,173 @@
+/**
+ * Copyright © 2016-2018 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.service.transport;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.stereotype.Service;
+import org.thingsboard.server.common.data.Device;
+import org.thingsboard.server.common.data.id.DeviceId;
+import org.thingsboard.server.common.data.relation.EntityRelation;
+import org.thingsboard.server.common.data.security.DeviceCredentials;
+import org.thingsboard.server.common.data.security.DeviceCredentialsType;
+import org.thingsboard.server.dao.device.DeviceCredentialsService;
+import org.thingsboard.server.dao.device.DeviceService;
+import org.thingsboard.server.dao.relation.RelationService;
+import org.thingsboard.server.gen.transport.TransportProtos.DeviceInfoProto;
+import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFromGatewayRequestMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFromGatewayResponseMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.TransportApiRequestMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.TransportApiResponseMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceCredentialsResponseMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceTokenRequestMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceX509CertRequestMsg;
+import org.thingsboard.server.kafka.TBKafkaConsumerTemplate;
+import org.thingsboard.server.kafka.TBKafkaProducerTemplate;
+import org.thingsboard.server.kafka.TbKafkaResponseTemplate;
+import org.thingsboard.server.kafka.TbKafkaSettings;
+import org.thingsboard.server.service.cluster.discovery.DiscoveryService;
+import org.thingsboard.server.service.executors.DbCallbackExecutorService;
+import org.thingsboard.server.service.state.DeviceStateService;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
+import java.util.UUID;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ * Created by ashvayka on 05.10.18.
+ */
+@Slf4j
+@Service
+public class LocalTransportApiService implements TransportApiService {
+
+ private static final ObjectMapper mapper = new ObjectMapper();
+
+ @Autowired
+ private DeviceService deviceService;
+
+ @Autowired
+ private RelationService relationService;
+
+ @Autowired
+ private DeviceCredentialsService deviceCredentialsService;
+
+ @Autowired
+ private DeviceStateService deviceStateService;
+
+ @Autowired
+ private DbCallbackExecutorService dbCallbackExecutorService;
+
+ private ReentrantLock deviceCreationLock = new ReentrantLock();
+
+ @Override
+ public ListenableFuture<TransportApiResponseMsg> handle(TransportApiRequestMsg transportApiRequestMsg) {
+ if (transportApiRequestMsg.hasValidateTokenRequestMsg()) {
+ ValidateDeviceTokenRequestMsg msg = transportApiRequestMsg.getValidateTokenRequestMsg();
+ return validateCredentials(msg.getToken(), DeviceCredentialsType.ACCESS_TOKEN);
+ } else if (transportApiRequestMsg.hasValidateX509CertRequestMsg()) {
+ ValidateDeviceX509CertRequestMsg msg = transportApiRequestMsg.getValidateX509CertRequestMsg();
+ return validateCredentials(msg.getHash(), DeviceCredentialsType.X509_CERTIFICATE);
+ } else if (transportApiRequestMsg.hasGetOrCreateDeviceRequestMsg()) {
+ return handle(transportApiRequestMsg.getGetOrCreateDeviceRequestMsg());
+ }
+ return getEmptyTransportApiResponseFuture();
+ }
+
+ private ListenableFuture<TransportApiResponseMsg> validateCredentials(String credentialsId, DeviceCredentialsType credentialsType) {
+ //TODO: Make async and enable caching
+ DeviceCredentials credentials = deviceCredentialsService.findDeviceCredentialsByCredentialsId(credentialsId);
+ if (credentials != null && credentials.getCredentialsType() == credentialsType) {
+ return getDeviceInfo(credentials.getDeviceId());
+ } else {
+ return getEmptyTransportApiResponseFuture();
+ }
+ }
+
+ private ListenableFuture<TransportApiResponseMsg> handle(GetOrCreateDeviceFromGatewayRequestMsg requestMsg) {
+ DeviceId gatewayId = new DeviceId(new UUID(requestMsg.getGatewayIdMSB(), requestMsg.getGatewayIdLSB()));
+ ListenableFuture<Device> gatewayFuture = deviceService.findDeviceByIdAsync(gatewayId);
+ return Futures.transform(gatewayFuture, gateway -> {
+ deviceCreationLock.lock();
+ try {
+ Device device = deviceService.findDeviceByTenantIdAndName(gateway.getTenantId(), requestMsg.getDeviceName());
+ if (device == null) {
+ device = new Device();
+ device.setTenantId(gateway.getTenantId());
+ device.setName(requestMsg.getDeviceName());
+ device.setType(requestMsg.getDeviceType());
+ device.setCustomerId(gateway.getCustomerId());
+ device = deviceService.saveDevice(device);
+ relationService.saveRelationAsync(new EntityRelation(gateway.getId(), device.getId(), "Created"));
+ deviceStateService.onDeviceAdded(device);
+ }
+ return TransportApiResponseMsg.newBuilder()
+ .setGetOrCreateDeviceResponseMsg(GetOrCreateDeviceFromGatewayResponseMsg.newBuilder().setDeviceInfo(getDeviceInfoProto(device)).build()).build();
+ } catch (JsonProcessingException e) {
+ log.warn("[{}] Failed to lookup device by gateway id and name", gatewayId, requestMsg.getDeviceName(), e);
+ throw new RuntimeException(e);
+ } finally {
+ deviceCreationLock.unlock();
+ }
+ }, dbCallbackExecutorService);
+ }
+
+
+ private ListenableFuture<TransportApiResponseMsg> getDeviceInfo(DeviceId deviceId) {
+ return Futures.transform(deviceService.findDeviceByIdAsync(deviceId), device -> {
+ if (device == null) {
+ log.trace("[{}] Failed to lookup device by id", deviceId);
+ return getEmptyTransportApiResponse();
+ }
+ try {
+ return TransportApiResponseMsg.newBuilder()
+ .setValidateTokenResponseMsg(ValidateDeviceCredentialsResponseMsg.newBuilder().setDeviceInfo(getDeviceInfoProto(device)).build()).build();
+ } catch (JsonProcessingException e) {
+ log.warn("[{}] Failed to lookup device by id", deviceId, e);
+ return getEmptyTransportApiResponse();
+ }
+ });
+ }
+
+ private DeviceInfoProto getDeviceInfoProto(Device device) throws JsonProcessingException {
+ return DeviceInfoProto.newBuilder()
+ .setTenantIdMSB(device.getTenantId().getId().getMostSignificantBits())
+ .setTenantIdLSB(device.getTenantId().getId().getLeastSignificantBits())
+ .setDeviceIdMSB(device.getId().getId().getMostSignificantBits())
+ .setDeviceIdLSB(device.getId().getId().getLeastSignificantBits())
+ .setDeviceName(device.getName())
+ .setDeviceType(device.getType())
+ .setAdditionalInfo(mapper.writeValueAsString(device.getAdditionalInfo()))
+ .build();
+ }
+
+ private ListenableFuture<TransportApiResponseMsg> getEmptyTransportApiResponseFuture() {
+ return Futures.immediateFuture(getEmptyTransportApiResponse());
+ }
+
+ private TransportApiResponseMsg getEmptyTransportApiResponse() {
+ return TransportApiResponseMsg.newBuilder()
+ .setValidateTokenResponseMsg(ValidateDeviceCredentialsResponseMsg.getDefaultInstance()).build();
+ }
+}
diff --git a/application/src/main/java/org/thingsboard/server/service/transport/LocalTransportService.java b/application/src/main/java/org/thingsboard/server/service/transport/LocalTransportService.java
new file mode 100644
index 0000000..d670db3
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/service/transport/LocalTransportService.java
@@ -0,0 +1,209 @@
+/**
+ * Copyright © 2016-2018 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.service.transport;
+
+import akka.actor.ActorRef;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.stereotype.Service;
+import org.thingsboard.rule.engine.api.util.DonAsynchron;
+import org.thingsboard.server.actors.ActorSystemContext;
+import org.thingsboard.server.common.msg.cluster.ServerAddress;
+import org.thingsboard.server.common.transport.SessionMsgListener;
+import org.thingsboard.server.common.transport.TransportService;
+import org.thingsboard.server.common.transport.TransportServiceCallback;
+import org.thingsboard.server.common.transport.service.AbstractTransportService;
+import org.thingsboard.server.gen.transport.TransportProtos;
+import org.thingsboard.server.gen.transport.TransportProtos.DeviceActorToTransportMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeRequestMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFromGatewayRequestMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFromGatewayResponseMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.PostAttributeMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.PostTelemetryMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.SessionEventMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto;
+import org.thingsboard.server.gen.transport.TransportProtos.SubscribeToAttributeUpdatesMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.SubscribeToRPCMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcResponseMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.ToServerRpcRequestMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.TransportApiRequestMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.TransportToDeviceActorMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceCredentialsResponseMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceTokenRequestMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceX509CertRequestMsg;
+import org.thingsboard.server.service.cluster.routing.ClusterRoutingService;
+import org.thingsboard.server.service.cluster.rpc.ClusterRpcService;
+import org.thingsboard.server.service.encoding.DataDecodingEncodingService;
+import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
+import java.util.Optional;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.SynchronousQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
+
+/**
+ * Created by ashvayka on 12.10.18.
+ */
+@Slf4j
+@Service
+@ConditionalOnProperty(prefix = "transport", value = "type", havingValue = "local")
+public class LocalTransportService extends AbstractTransportService implements RuleEngineTransportService {
+
+ @Autowired
+ private TransportApiService transportApiService;
+
+ @Autowired
+ private ActorSystemContext actorContext;
+
+ //TODO: completely replace this routing with the Kafka routing by partition ids.
+ @Autowired
+ private ClusterRoutingService routingService;
+ @Autowired
+ private ClusterRpcService rpcService;
+ @Autowired
+ private DataDecodingEncodingService encodingService;
+
+ @PostConstruct
+ public void init() {
+ super.init();
+ }
+
+ @PreDestroy
+ public void destroy() {
+ super.destroy();
+ }
+
+ @Override
+ public void process(ValidateDeviceTokenRequestMsg msg, TransportServiceCallback<ValidateDeviceCredentialsResponseMsg> callback) {
+ DonAsynchron.withCallback(
+ transportApiService.handle(TransportApiRequestMsg.newBuilder().setValidateTokenRequestMsg(msg).build()),
+ transportApiResponseMsg -> {
+ if (callback != null) {
+ callback.onSuccess(transportApiResponseMsg.getValidateTokenResponseMsg());
+ }
+ },
+ getThrowableConsumer(callback), transportCallbackExecutor);
+ }
+
+ @Override
+ public void process(ValidateDeviceX509CertRequestMsg msg, TransportServiceCallback<ValidateDeviceCredentialsResponseMsg> callback) {
+ DonAsynchron.withCallback(
+ transportApiService.handle(TransportApiRequestMsg.newBuilder().setValidateX509CertRequestMsg(msg).build()),
+ transportApiResponseMsg -> {
+ if (callback != null) {
+ callback.onSuccess(transportApiResponseMsg.getValidateTokenResponseMsg());
+ }
+ },
+ getThrowableConsumer(callback), transportCallbackExecutor);
+ }
+
+ @Override
+ public void process(GetOrCreateDeviceFromGatewayRequestMsg msg, TransportServiceCallback<GetOrCreateDeviceFromGatewayResponseMsg> callback) {
+ DonAsynchron.withCallback(
+ transportApiService.handle(TransportApiRequestMsg.newBuilder().setGetOrCreateDeviceRequestMsg(msg).build()),
+ transportApiResponseMsg -> {
+ if (callback != null) {
+ callback.onSuccess(transportApiResponseMsg.getGetOrCreateDeviceResponseMsg());
+ }
+ },
+ getThrowableConsumer(callback), transportCallbackExecutor);
+ }
+
+ @Override
+ public void process(SessionInfoProto sessionInfo, SessionEventMsg msg, TransportServiceCallback<Void> callback) {
+ forwardToDeviceActor(TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setSessionEvent(msg).build(), callback);
+ }
+
+ @Override
+ public void process(SessionInfoProto sessionInfo, PostTelemetryMsg msg, TransportServiceCallback<Void> callback) {
+ forwardToDeviceActor(TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setPostTelemetry(msg).build(), callback);
+ }
+
+ @Override
+ public void process(SessionInfoProto sessionInfo, PostAttributeMsg msg, TransportServiceCallback<Void> callback) {
+ forwardToDeviceActor(TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setPostAttributes(msg).build(), callback);
+ }
+
+ @Override
+ public void process(SessionInfoProto sessionInfo, GetAttributeRequestMsg msg, TransportServiceCallback<Void> callback) {
+ forwardToDeviceActor(TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setGetAttributes(msg).build(), callback);
+ }
+
+ @Override
+ public void process(SessionInfoProto sessionInfo, SubscribeToAttributeUpdatesMsg msg, TransportServiceCallback<Void> callback) {
+ forwardToDeviceActor(TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setSubscribeToAttributes(msg).build(), callback);
+ }
+
+ @Override
+ public void process(SessionInfoProto sessionInfo, SubscribeToRPCMsg msg, TransportServiceCallback<Void> callback) {
+ forwardToDeviceActor(TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setSubscribeToRPC(msg).build(), callback);
+ }
+
+ @Override
+ public void process(SessionInfoProto sessionInfo, ToDeviceRpcResponseMsg msg, TransportServiceCallback<Void> callback) {
+ forwardToDeviceActor(TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setToDeviceRPCCallResponse(msg).build(), callback);
+ }
+
+ @Override
+ public void process(SessionInfoProto sessionInfo, ToServerRpcRequestMsg msg, TransportServiceCallback<Void> callback) {
+ forwardToDeviceActor(TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setToServerRPCCallRequest(msg).build(), callback);
+ }
+
+ @Override
+ public void process(String nodeId, DeviceActorToTransportMsg msg) {
+ process(nodeId, msg, null, null);
+ }
+
+ @Override
+ public void process(String nodeId, DeviceActorToTransportMsg msg, Runnable onSuccess, Consumer<Throwable> onFailure) {
+ processToTransportMsg(msg);
+ if (onSuccess != null) {
+ onSuccess.run();
+ }
+ }
+
+ private void forwardToDeviceActor(TransportToDeviceActorMsg toDeviceActorMsg, TransportServiceCallback<Void> callback) {
+ TransportToDeviceActorMsgWrapper wrapper = new TransportToDeviceActorMsgWrapper(toDeviceActorMsg);
+ Optional<ServerAddress> address = routingService.resolveById(wrapper.getDeviceId());
+ if (address.isPresent()) {
+ rpcService.tell(encodingService.convertToProtoDataMessage(address.get(), wrapper));
+ } else {
+ actorContext.getAppActor().tell(wrapper, ActorRef.noSender());
+ }
+ if (callback != null) {
+ callback.onSuccess(null);
+ }
+ }
+
+ private <T> Consumer<Throwable> getThrowableConsumer(TransportServiceCallback<T> callback) {
+ return e -> {
+ if (callback != null) {
+ callback.onError(e);
+ }
+ };
+ }
+
+}
diff --git a/application/src/main/java/org/thingsboard/server/service/transport/RemoteRuleEngineTransportService.java b/application/src/main/java/org/thingsboard/server/service/transport/RemoteRuleEngineTransportService.java
new file mode 100644
index 0000000..38db1a4
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/service/transport/RemoteRuleEngineTransportService.java
@@ -0,0 +1,200 @@
+/**
+ * Copyright © 2016-2018 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.service.transport;
+
+import akka.actor.ActorRef;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.kafka.clients.consumer.ConsumerRecords;
+import org.apache.kafka.clients.producer.Callback;
+import org.apache.kafka.clients.producer.RecordMetadata;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.stereotype.Service;
+import org.thingsboard.server.actors.ActorSystemContext;
+import org.thingsboard.server.actors.service.ActorService;
+import org.thingsboard.server.common.msg.cluster.ServerAddress;
+import org.thingsboard.server.gen.transport.TransportProtos.DeviceActorToTransportMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.TransportToDeviceActorMsg;
+import org.thingsboard.server.kafka.TBKafkaConsumerTemplate;
+import org.thingsboard.server.kafka.TBKafkaProducerTemplate;
+import org.thingsboard.server.kafka.TbKafkaSettings;
+import org.thingsboard.server.kafka.TbNodeIdProvider;
+import org.thingsboard.server.service.cluster.discovery.DiscoveryService;
+import org.thingsboard.server.service.cluster.routing.ClusterRoutingService;
+import org.thingsboard.server.service.cluster.rpc.ClusterRpcService;
+import org.thingsboard.server.service.encoding.DataDecodingEncodingService;
+import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
+import java.time.Duration;
+import java.util.Optional;
+import java.util.UUID;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.function.Consumer;
+
+/**
+ * Created by ashvayka on 09.10.18.
+ */
+@Slf4j
+@Service
+@ConditionalOnProperty(prefix = "transport", value = "type", havingValue = "remote")
+public class RemoteRuleEngineTransportService implements RuleEngineTransportService {
+
+ @Value("${transport.remote.rule_engine.topic}")
+ private String ruleEngineTopic;
+ @Value("${transport.remote.notifications.topic}")
+ private String notificationsTopic;
+ @Value("${transport.remote.rule_engine.poll_interval}")
+ private int pollDuration;
+ @Value("${transport.remote.rule_engine.auto_commit_interval}")
+ private int autoCommitInterval;
+
+ @Autowired
+ private TbKafkaSettings kafkaSettings;
+
+ @Autowired
+ private TbNodeIdProvider nodeIdProvider;
+
+ @Autowired
+ private ActorSystemContext actorContext;
+
+ //TODO: completely replace this routing with the Kafka routing by partition ids.
+ @Autowired
+ private ClusterRoutingService routingService;
+ @Autowired
+ private ClusterRpcService rpcService;
+ @Autowired
+ private DataDecodingEncodingService encodingService;
+
+ private TBKafkaConsumerTemplate<ToRuleEngineMsg> ruleEngineConsumer;
+ private TBKafkaProducerTemplate<ToTransportMsg> notificationsProducer;
+
+ private ExecutorService mainConsumerExecutor = Executors.newSingleThreadExecutor();
+
+ private volatile boolean stopped = false;
+
+ @PostConstruct
+ public void init() {
+ TBKafkaProducerTemplate.TBKafkaProducerTemplateBuilder<ToTransportMsg> notificationsProducerBuilder = TBKafkaProducerTemplate.builder();
+ notificationsProducerBuilder.settings(kafkaSettings);
+ notificationsProducerBuilder.defaultTopic(notificationsTopic);
+ notificationsProducerBuilder.encoder(new ToTransportMsgEncoder());
+
+ notificationsProducer = notificationsProducerBuilder.build();
+ notificationsProducer.init();
+
+ TBKafkaConsumerTemplate.TBKafkaConsumerTemplateBuilder<ToRuleEngineMsg> ruleEngineConsumerBuilder = TBKafkaConsumerTemplate.builder();
+ ruleEngineConsumerBuilder.settings(kafkaSettings);
+ ruleEngineConsumerBuilder.topic(ruleEngineTopic);
+ ruleEngineConsumerBuilder.clientId("transport-" + nodeIdProvider.getNodeId());
+ ruleEngineConsumerBuilder.groupId("tb-node");
+ ruleEngineConsumerBuilder.autoCommit(true);
+ ruleEngineConsumerBuilder.autoCommitIntervalMs(autoCommitInterval);
+ ruleEngineConsumerBuilder.decoder(new ToRuleEngineMsgDecoder());
+
+ ruleEngineConsumer = ruleEngineConsumerBuilder.build();
+ ruleEngineConsumer.subscribe();
+
+ mainConsumerExecutor.execute(() -> {
+ while (!stopped) {
+ try {
+ ConsumerRecords<String, byte[]> records = ruleEngineConsumer.poll(Duration.ofMillis(pollDuration));
+ records.forEach(record -> {
+ try {
+ ToRuleEngineMsg toRuleEngineMsg = ruleEngineConsumer.decode(record);
+ if (toRuleEngineMsg.hasToDeviceActorMsg()) {
+ forwardToDeviceActor(toRuleEngineMsg.getToDeviceActorMsg());
+ }
+ } catch (Throwable e) {
+ log.warn("Failed to process the notification.", e);
+ }
+ });
+ } catch (Exception e) {
+ log.warn("Failed to obtain messages from queue.", e);
+ try {
+ Thread.sleep(pollDuration);
+ } catch (InterruptedException e2) {
+ log.trace("Failed to wait until the server has capacity to handle new requests", e2);
+ }
+ }
+ }
+ });
+ }
+
+ @Override
+ public void process(String nodeId, DeviceActorToTransportMsg msg) {
+ process(nodeId, msg, null, null);
+ }
+
+ @Override
+ public void process(String nodeId, DeviceActorToTransportMsg msg, Runnable onSuccess, Consumer<Throwable> onFailure) {
+ notificationsProducer.send(notificationsTopic + "." + nodeId,
+ new UUID(msg.getSessionIdMSB(), msg.getSessionIdLSB()).toString(),
+ ToTransportMsg.newBuilder().setToDeviceSessionMsg(msg).build()
+ , new QueueCallbackAdaptor(onSuccess, onFailure));
+ }
+
+ private void forwardToDeviceActor(TransportToDeviceActorMsg toDeviceActorMsg) {
+ TransportToDeviceActorMsgWrapper wrapper = new TransportToDeviceActorMsgWrapper(toDeviceActorMsg);
+ Optional<ServerAddress> address = routingService.resolveById(wrapper.getDeviceId());
+ if (address.isPresent()) {
+ rpcService.tell(encodingService.convertToProtoDataMessage(address.get(), wrapper));
+ } else {
+ actorContext.getAppActor().tell(wrapper, ActorRef.noSender());
+ }
+ }
+
+ @PreDestroy
+ public void destroy() {
+ stopped = true;
+ if (ruleEngineConsumer != null) {
+ ruleEngineConsumer.unsubscribe();
+ }
+ if (mainConsumerExecutor != null) {
+ mainConsumerExecutor.shutdownNow();
+ }
+ }
+
+ private static class QueueCallbackAdaptor implements Callback {
+ private final Runnable onSuccess;
+ private final Consumer<Throwable> onFailure;
+
+ QueueCallbackAdaptor(Runnable onSuccess, Consumer<Throwable> onFailure) {
+ this.onSuccess = onSuccess;
+ this.onFailure = onFailure;
+ }
+
+ @Override
+ public void onCompletion(RecordMetadata metadata, Exception exception) {
+ if (exception == null) {
+ if (onSuccess != null) {
+ onSuccess.run();
+ }
+ } else {
+ if (onFailure != null) {
+ onFailure.accept(exception);
+ }
+ }
+ }
+ }
+
+}
diff --git a/application/src/main/java/org/thingsboard/server/service/transport/RemoteTransportApiService.java b/application/src/main/java/org/thingsboard/server/service/transport/RemoteTransportApiService.java
new file mode 100644
index 0000000..6d422f4
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/service/transport/RemoteTransportApiService.java
@@ -0,0 +1,109 @@
+/**
+ * Copyright © 2016-2018 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.service.transport;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.stereotype.Component;
+import org.thingsboard.server.gen.transport.TransportProtos.TransportApiRequestMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.TransportApiResponseMsg;
+import org.thingsboard.server.kafka.*;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.SynchronousQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Created by ashvayka on 05.10.18.
+ */
+@Slf4j
+@Component
+@ConditionalOnProperty(prefix = "transport", value = "type", havingValue = "remote")
+public class RemoteTransportApiService {
+
+ @Value("${transport.remote.transport_api.requests_topic}")
+ private String transportApiRequestsTopic;
+ @Value("${transport.remote.transport_api.responses_topic}")
+ private String transportApiResponsesTopic;
+ @Value("${transport.remote.transport_api.max_pending_requests}")
+ private int maxPendingRequests;
+ @Value("${transport.remote.transport_api.request_timeout}")
+ private long requestTimeout;
+ @Value("${transport.remote.transport_api.request_poll_interval}")
+ private int responsePollDuration;
+ @Value("${transport.remote.transport_api.request_auto_commit_interval}")
+ private int autoCommitInterval;
+
+ @Autowired
+ private TbKafkaSettings kafkaSettings;
+
+ @Autowired
+ private TbNodeIdProvider nodeIdProvider;
+
+ @Autowired
+ private TransportApiService transportApiService;
+
+ private ExecutorService transportCallbackExecutor;
+
+ private TbKafkaResponseTemplate<TransportApiRequestMsg, TransportApiResponseMsg> transportApiTemplate;
+
+ @PostConstruct
+ public void init() {
+ this.transportCallbackExecutor = new ThreadPoolExecutor(0, 100, 60L, TimeUnit.SECONDS, new SynchronousQueue<>());
+
+ TBKafkaProducerTemplate.TBKafkaProducerTemplateBuilder<TransportApiResponseMsg> responseBuilder = TBKafkaProducerTemplate.builder();
+ responseBuilder.settings(kafkaSettings);
+ responseBuilder.defaultTopic(transportApiResponsesTopic);
+ responseBuilder.encoder(new TransportApiResponseEncoder());
+
+ TBKafkaConsumerTemplate.TBKafkaConsumerTemplateBuilder<TransportApiRequestMsg> requestBuilder = TBKafkaConsumerTemplate.builder();
+ requestBuilder.settings(kafkaSettings);
+ requestBuilder.topic(transportApiRequestsTopic);
+ requestBuilder.clientId(nodeIdProvider.getNodeId());
+ requestBuilder.groupId("tb-node");
+ requestBuilder.autoCommit(true);
+ requestBuilder.autoCommitIntervalMs(autoCommitInterval);
+ requestBuilder.decoder(new TransportApiRequestDecoder());
+
+ TbKafkaResponseTemplate.TbKafkaResponseTemplateBuilder
+ <TransportApiRequestMsg, TransportApiResponseMsg> builder = TbKafkaResponseTemplate.builder();
+ builder.requestTemplate(requestBuilder.build());
+ builder.responseTemplate(responseBuilder.build());
+ builder.maxPendingRequests(maxPendingRequests);
+ builder.requestTimeout(requestTimeout);
+ builder.pollInterval(responsePollDuration);
+ builder.executor(transportCallbackExecutor);
+ builder.handler(transportApiService);
+ transportApiTemplate = builder.build();
+ transportApiTemplate.init();
+ }
+
+ @PreDestroy
+ public void destroy() {
+ if (transportApiTemplate != null) {
+ transportApiTemplate.stop();
+ }
+ if (transportCallbackExecutor != null) {
+ transportCallbackExecutor.shutdownNow();
+ }
+ }
+
+}
application/src/main/resources/thingsboard.yml 110(+68 -42)
diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml
index 8d75dab..90d1500 100644
--- a/application/src/main/resources/thingsboard.yml
+++ b/application/src/main/resources/thingsboard.yml
@@ -82,47 +82,6 @@ dashboard:
# Maximum allowed datapoints fetched by widgets
max_datapoints_limit: "${DASHBOARD_MAX_DATAPOINTS_LIMIT:50000}"
-# Device communication protocol parameters
-http:
- request_timeout: "${HTTP_REQUEST_TIMEOUT:60000}"
-
-# MQTT server parameters
-mqtt:
- # Enable/disable mqtt transport protocol.
- enabled: "${MQTT_ENABLED:true}"
- bind_address: "${MQTT_BIND_ADDRESS:0.0.0.0}"
- bind_port: "${MQTT_BIND_PORT:1883}"
- adaptor: "${MQTT_ADAPTOR_NAME:JsonMqttAdaptor}"
- timeout: "${MQTT_TIMEOUT:10000}"
- netty:
- leak_detector_level: "${NETTY_LEASK_DETECTOR_LVL:DISABLED}"
- boss_group_thread_count: "${NETTY_BOSS_GROUP_THREADS:1}"
- worker_group_thread_count: "${NETTY_WORKER_GROUP_THREADS:12}"
- max_payload_size: "${NETTY_MAX_PAYLOAD_SIZE:65536}"
- # MQTT SSL configuration
- ssl:
- # Enable/disable SSL support
- enabled: "${MQTT_SSL_ENABLED:false}"
- # SSL protocol: See http://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#SSLContext
- protocol: "${MQTT_SSL_PROTOCOL:TLSv1.2}"
- # Path to the key store that holds the SSL certificate
- key_store: "${MQTT_SSL_KEY_STORE:mqttserver.jks}"
- # Password used to access the key store
- key_store_password: "${MQTT_SSL_KEY_STORE_PASSWORD:server_ks_password}"
- # Password used to access the key
- key_password: "${MQTT_SSL_KEY_PASSWORD:server_key_password}"
- # Type of the key store
- key_store_type: "${MQTT_SSL_KEY_STORE_TYPE:JKS}"
-
-# CoAP server parameters
-coap:
- # Enable/disable coap transport protocol.
- enabled: "${COAP_ENABLED:false}"
- bind_address: "${COAP_BIND_ADDRESS:0.0.0.0}"
- bind_port: "${COAP_BIND_PORT:5683}"
- adaptor: "${COAP_ADAPTOR_NAME:JsonCoapAdaptor}"
- timeout: "${COAP_TIMEOUT:10000}"
-
#Quota parameters
quota:
host:
@@ -154,7 +113,7 @@ quota:
# Interval for scheduled task that cleans expired records. TTL is used for expiring
cleanPeriodMs: "${QUOTA_TENANT_CLEAN_PERIOD_MS:300000}"
# Enable Host API Limits
- enabled: "${QUOTA_TENANT_ENABLED:false}"
+ enabled: "${QUOTA_TENANT_ENABLED:true}"
# Array of whitelist tenants
whitelist: "${QUOTA_TENANT_WHITELIST:}"
# Array of blacklist tenants
@@ -427,6 +386,19 @@ kafka:
batch.size: "${TB_KAFKA_BATCH_SIZE:16384}"
linger.ms: "${TB_KAFKA_LINGER_MS:1}"
buffer.memory: "${TB_BUFFER_MEMORY:33554432}"
+ transport_api:
+ requests_topic: "${TB_TRANSPORT_API_REQUEST_TOPIC:tb.transport.api.requests}"
+ responses_topic: "${TB_TRANSPORT_API_RESPONSE_TOPIC:tb.transport.api.responses}"
+ max_pending_requests: "${TB_TRANSPORT_MAX_PENDING_REQUESTS:10000}"
+ request_timeout: "${TB_TRANSPORT_MAX_REQUEST_TIMEOUT:10000}"
+ request_poll_interval: "${TB_TRANSPORT_REQUEST_POLL_INTERVAL_MS:25}"
+ request_auto_commit_interval: "${TB_TRANSPORT_REQUEST_AUTO_COMMIT_INTERVAL_MS:100}"
+ rule_engine:
+ topic: "${TB_RULE_ENGINE_TOPIC:tb.rule-engine}"
+ poll_interval: "${TB_RULE_ENGINE_POLL_INTERVAL_MS:25}"
+ auto_commit_interval: "${TB_RULE_ENGINE_AUTO_COMMIT_INTERVAL_MS:100}"
+ notifications:
+ topic: "${TB_TRANSPORT_NOTIFICATIONS_TOPIC:tb.transport.notifications}"
js:
evaluator: "${JS_EVALUATOR:local}" # local/remote
@@ -456,3 +428,57 @@ js:
response_auto_commit_interval: "${REMOTE_JS_RESPONSE_AUTO_COMMIT_INTERVAL_MS:100}"
# Maximum allowed JavaScript execution errors before JavaScript will be blacklisted
max_errors: "${REMOTE_JS_SANDBOX_MAX_ERRORS:3}"
+
+transport:
+ type: "${TRANSPORT_TYPE:local}" # local or remote
+ remote:
+ transport_api:
+ requests_topic: "${TB_TRANSPORT_API_REQUEST_TOPIC:tb.transport.api.requests}"
+ responses_topic: "${TB_TRANSPORT_API_RESPONSE_TOPIC:tb.transport.api.responses}"
+ max_pending_requests: "${TB_TRANSPORT_MAX_PENDING_REQUESTS:10000}"
+ request_timeout: "${TB_TRANSPORT_MAX_REQUEST_TIMEOUT:10000}"
+ request_poll_interval: "${TB_TRANSPORT_RESPONSE_POLL_INTERVAL_MS:25}"
+ request_auto_commit_interval: "${TB_TRANSPORT_RESPONSE_AUTO_COMMIT_INTERVAL_MS:1000}"
+ rule_engine:
+ topic: "${TB_RULE_ENGINE_TOPIC:tb.rule-engine}"
+ poll_interval: "${TB_RULE_ENGINE_POLL_INTERVAL_MS:25}"
+ auto_commit_interval: "${TB_RULE_ENGINE_AUTO_COMMIT_INTERVAL_MS:100}"
+ notifications:
+ topic: "${TB_TRANSPORT_NOTIFICATIONS_TOPIC:tb.transport.notifications}"
+ # Local HTTP transport parameters
+ http:
+ enabled: "${HTTP_ENABLED:true}"
+ request_timeout: "${HTTP_REQUEST_TIMEOUT:60000}"
+ # Local MQTT transport parameters
+ mqtt:
+ # Enable/disable mqtt transport protocol.
+ enabled: "${MQTT_ENABLED:true}"
+ bind_address: "${MQTT_BIND_ADDRESS:0.0.0.0}"
+ bind_port: "${MQTT_BIND_PORT:1883}"
+ timeout: "${MQTT_TIMEOUT:10000}"
+ netty:
+ leak_detector_level: "${NETTY_LEASK_DETECTOR_LVL:DISABLED}"
+ boss_group_thread_count: "${NETTY_BOSS_GROUP_THREADS:1}"
+ worker_group_thread_count: "${NETTY_WORKER_GROUP_THREADS:12}"
+ max_payload_size: "${NETTY_MAX_PAYLOAD_SIZE:65536}"
+ # MQTT SSL configuration
+ ssl:
+ # Enable/disable SSL support
+ enabled: "${MQTT_SSL_ENABLED:false}"
+ # SSL protocol: See http://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#SSLContext
+ protocol: "${MQTT_SSL_PROTOCOL:TLSv1.2}"
+ # Path to the key store that holds the SSL certificate
+ key_store: "${MQTT_SSL_KEY_STORE:mqttserver.jks}"
+ # Password used to access the key store
+ key_store_password: "${MQTT_SSL_KEY_STORE_PASSWORD:server_ks_password}"
+ # Password used to access the key
+ key_password: "${MQTT_SSL_KEY_PASSWORD:server_key_password}"
+ # Type of the key store
+ key_store_type: "${MQTT_SSL_KEY_STORE_TYPE:JKS}"
+ # Local CoAP transport parameters
+ coap:
+ # Enable/disable coap transport protocol.
+ enabled: "${COAP_ENABLED:true}"
+ bind_address: "${COAP_BIND_ADDRESS:0.0.0.0}"
+ bind_port: "${COAP_BIND_PORT:5683}"
+ timeout: "${COAP_TIMEOUT:10000}"
common/message/pom.xml 4(+4 -0)
diff --git a/common/message/pom.xml b/common/message/pom.xml
index f4f20b0..d914d4f 100644
--- a/common/message/pom.xml
+++ b/common/message/pom.xml
@@ -41,6 +41,10 @@
<artifactId>data</artifactId>
</dependency>
<dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcprov-jdk15on</artifactId>
+ </dependency>
+ <dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/ToServerRpcResponseMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/core/ToServerRpcResponseMsg.java
index 82f44e9..fa9ef05 100644
--- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/ToServerRpcResponseMsg.java
+++ b/common/message/src/main/java/org/thingsboard/server/common/msg/core/ToServerRpcResponseMsg.java
@@ -16,25 +16,16 @@
package org.thingsboard.server.common.msg.core;
import lombok.Data;
-import org.thingsboard.server.common.msg.session.FromDeviceMsg;
-import org.thingsboard.server.common.msg.session.SessionMsgType;
-import org.thingsboard.server.common.msg.session.SessionMsgType;
-import org.thingsboard.server.common.msg.session.ToDeviceMsg;
/**
* @author Andrew Shvayka
*/
@Data
-public class ToServerRpcResponseMsg implements ToDeviceMsg {
+public class ToServerRpcResponseMsg {
private final int requestId;
private final String data;
- public SessionMsgType getSessionMsgType() {
- return SessionMsgType.TO_SERVER_RPC_RESPONSE;
- }
-
- @Override
public boolean isSuccess() {
return true;
}
diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/MsgType.java b/common/message/src/main/java/org/thingsboard/server/common/msg/MsgType.java
index 60e5469..24758b5 100644
--- a/common/message/src/main/java/org/thingsboard/server/common/msg/MsgType.java
+++ b/common/message/src/main/java/org/thingsboard/server/common/msg/MsgType.java
@@ -77,11 +77,6 @@ public enum MsgType {
*/
RULE_TO_SELF_MSG,
- /**
- * Message that is sent by Session Actor to Device Actor. Represents messages from the device itself.
- */
- DEVICE_SESSION_TO_DEVICE_ACTOR_MSG,
-
DEVICE_ATTRIBUTES_UPDATE_TO_DEVICE_ACTOR_MSG,
DEVICE_CREDENTIALS_UPDATE_TO_DEVICE_ACTOR_MSG,
@@ -96,8 +91,6 @@ public enum MsgType {
DEVICE_ACTOR_CLIENT_SIDE_RPC_TIMEOUT_MSG,
- DEVICE_ACTOR_QUEUE_TIMEOUT_MSG,
-
/**
* Message that is sent from the Device Actor to Rule Engine. Requires acknowledgement
*/
@@ -106,11 +99,16 @@ public enum MsgType {
/**
* Message that is sent from Rule Engine to the Device Actor when message is successfully pushed to queue.
*/
- RULE_ENGINE_QUEUE_PUT_ACK_MSG,
ACTOR_SYSTEM_TO_DEVICE_SESSION_ACTOR_MSG,
TRANSPORT_TO_DEVICE_SESSION_ACTOR_MSG,
SESSION_TIMEOUT_MSG,
SESSION_CTRL_MSG,
- STATS_PERSIST_TICK_MSG;
+ STATS_PERSIST_TICK_MSG,
+
+
+ /**
+ * Message that is sent by TransportRuleEngineService to Device Actor. Represents messages from the device itself.
+ */
+ TRANSPORT_TO_DEVICE_ACTOR_MSG;
}
diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionContext.java b/common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionContext.java
index 2f2d880..7f7a669 100644
--- a/common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionContext.java
+++ b/common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionContext.java
@@ -15,21 +15,11 @@
*/
package org.thingsboard.server.common.msg.session;
-import org.thingsboard.server.common.data.id.SessionId;
-import org.thingsboard.server.common.msg.session.ex.SessionException;
+import java.util.UUID;
public interface SessionContext {
- SessionId getSessionId();
-
- SessionType getSessionType();
-
- void onMsg(SessionActorToAdaptorMsg msg) throws SessionException;
-
- void onMsg(SessionCtrlMsg msg) throws SessionException;
-
- boolean isClosed();
-
- long getTimeout();
+ UUID getSessionId();
+ int nextMsgId();
}
common/pom.xml 3(+1 -2)
diff --git a/common/pom.xml b/common/pom.xml
index e3cdabf..e55d71f 100644
--- a/common/pom.xml
+++ b/common/pom.xml
@@ -23,7 +23,6 @@
<version>2.2.0-SNAPSHOT</version>
<artifactId>thingsboard</artifactId>
</parent>
- <groupId>org.thingsboard</groupId>
<artifactId>common</artifactId>
<packaging>pom</packaging>
@@ -37,8 +36,8 @@
<modules>
<module>data</module>
<module>message</module>
- <module>transport</module>
<module>queue</module>
+ <module>transport</module>
</modules>
</project>
common/queue/pom.xml 6(+5 -1)
diff --git a/common/queue/pom.xml b/common/queue/pom.xml
index 11bb139..765960e 100644
--- a/common/queue/pom.xml
+++ b/common/queue/pom.xml
@@ -16,7 +16,7 @@
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
@@ -65,6 +65,10 @@
<artifactId>gson</artifactId>
</dependency>
<dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-lang3</artifactId>
+ </dependency>
+ <dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
diff --git a/common/queue/src/main/java/org/thingsboard/server/kafka/AsyncCallbackTemplate.java b/common/queue/src/main/java/org/thingsboard/server/kafka/AsyncCallbackTemplate.java
new file mode 100644
index 0000000..b8ad758
--- /dev/null
+++ b/common/queue/src/main/java/org/thingsboard/server/kafka/AsyncCallbackTemplate.java
@@ -0,0 +1,66 @@
+/**
+ * Copyright © 2016-2018 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.kafka;
+
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+
+import java.util.concurrent.Executor;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
+
+/**
+ * Created by ashvayka on 05.10.18.
+ */
+public class AsyncCallbackTemplate {
+
+ public static <T> void withCallbackAndTimeout(ListenableFuture<T> future,
+ Consumer<T> onSuccess,
+ Consumer<Throwable> onFailure,
+ long timeoutInMs,
+ ScheduledExecutorService timeoutExecutor,
+ Executor callbackExecutor) {
+ future = Futures.withTimeout(future, timeoutInMs, TimeUnit.MILLISECONDS, timeoutExecutor);
+ withCallback(future, onSuccess, onFailure, callbackExecutor);
+ }
+
+ public static <T> void withCallback(ListenableFuture<T> future, Consumer<T> onSuccess,
+ Consumer<Throwable> onFailure, Executor executor) {
+ FutureCallback<T> callback = new FutureCallback<T>() {
+ @Override
+ public void onSuccess(T result) {
+ try {
+ onSuccess.accept(result);
+ } catch (Throwable th) {
+ onFailure(th);
+ }
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ onFailure.accept(t);
+ }
+ };
+ if (executor != null) {
+ Futures.addCallback(future, callback, executor);
+ } else {
+ Futures.addCallback(future, callback);
+ }
+ }
+
+}
diff --git a/common/queue/src/main/java/org/thingsboard/server/kafka/TBKafkaConsumerTemplate.java b/common/queue/src/main/java/org/thingsboard/server/kafka/TBKafkaConsumerTemplate.java
index 3972264..86be3a3 100644
--- a/common/queue/src/main/java/org/thingsboard/server/kafka/TBKafkaConsumerTemplate.java
+++ b/common/queue/src/main/java/org/thingsboard/server/kafka/TBKafkaConsumerTemplate.java
@@ -43,12 +43,15 @@ public class TBKafkaConsumerTemplate<T> {
private final String topic;
@Builder
- private TBKafkaConsumerTemplate(TbKafkaSettings settings, TbKafkaDecoder<T> decoder, TbKafkaRequestIdExtractor<T> requestIdExtractor,
+ private TBKafkaConsumerTemplate(TbKafkaSettings settings, TbKafkaDecoder<T> decoder,
+ TbKafkaRequestIdExtractor<T> requestIdExtractor,
String clientId, String groupId, String topic,
boolean autoCommit, int autoCommitIntervalMs) {
Properties props = settings.toProps();
props.put(ConsumerConfig.CLIENT_ID_CONFIG, clientId);
- props.put(ConsumerConfig.GROUP_ID_CONFIG, groupId);
+ if (groupId != null) {
+ props.put(ConsumerConfig.GROUP_ID_CONFIG, groupId);
+ }
props.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, autoCommit);
props.put(ConsumerConfig.AUTO_COMMIT_INTERVAL_MS_CONFIG, autoCommitIntervalMs);
props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringDeserializer");
diff --git a/common/queue/src/main/java/org/thingsboard/server/kafka/TBKafkaProducerTemplate.java b/common/queue/src/main/java/org/thingsboard/server/kafka/TBKafkaProducerTemplate.java
index 7e24ad0..610a490 100644
--- a/common/queue/src/main/java/org/thingsboard/server/kafka/TBKafkaProducerTemplate.java
+++ b/common/queue/src/main/java/org/thingsboard/server/kafka/TBKafkaProducerTemplate.java
@@ -20,6 +20,7 @@ import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.apache.kafka.clients.admin.CreateTopicsResult;
import org.apache.kafka.clients.admin.NewTopic;
+import org.apache.kafka.clients.producer.Callback;
import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.ProducerConfig;
import org.apache.kafka.clients.producer.ProducerRecord;
@@ -27,13 +28,12 @@ import org.apache.kafka.clients.producer.RecordMetadata;
import org.apache.kafka.common.PartitionInfo;
import org.apache.kafka.common.header.Header;
-import java.nio.ByteBuffer;
-import java.util.Arrays;
import java.util.List;
import java.util.Properties;
import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.Future;
-import java.util.function.BiConsumer;
/**
* Created by ashvayka on 24.09.18.
@@ -48,7 +48,7 @@ public class TBKafkaProducerTemplate<T> {
private TbKafkaEnricher<T> enricher = ((value, responseTopic, requestId) -> value);
private final TbKafkaPartitioner<T> partitioner;
- private List<PartitionInfo> partitionInfoList;
+ private ConcurrentMap<String, List<PartitionInfo>> partitionInfoMap;
@Getter
private final String defaultTopic;
@@ -78,38 +78,51 @@ public class TBKafkaProducerTemplate<T> {
log.trace("Failed to create topic: {}", e.getMessage(), e);
}
//Maybe this should not be cached, but we don't plan to change size of partitions
- this.partitionInfoList = producer.partitionsFor(defaultTopic);
+ this.partitionInfoMap = new ConcurrentHashMap<>();
+ this.partitionInfoMap.putIfAbsent(defaultTopic, producer.partitionsFor(defaultTopic));
}
- public T enrich(T value, String responseTopic, UUID requestId) {
- return enricher.enrich(value, responseTopic, requestId);
+ T enrich(T value, String responseTopic, UUID requestId) {
+ if (enricher != null) {
+ return enricher.enrich(value, responseTopic, requestId);
+ } else {
+ return value;
+ }
+ }
+
+ public Future<RecordMetadata> send(String key, T value, Callback callback) {
+ return send(key, value, null, callback);
+ }
+
+ public Future<RecordMetadata> send(String key, T value, Iterable<Header> headers, Callback callback) {
+ return send(key, value, null, headers, callback);
}
- public Future<RecordMetadata> send(String key, T value) {
- return send(key, value, null, null);
+ public Future<RecordMetadata> send(String key, T value, Long timestamp, Iterable<Header> headers, Callback callback) {
+ return send(this.defaultTopic, key, value, timestamp, headers, callback);
}
- public Future<RecordMetadata> send(String key, T value, Iterable<Header> headers) {
- return send(key, value, null, headers);
+ public Future<RecordMetadata> send(String topic, String key, T value, Iterable<Header> headers, Callback callback) {
+ return send(topic, key, value, null, headers, callback);
}
- public Future<RecordMetadata> send(String key, T value, Long timestamp, Iterable<Header> headers) {
- return send(this.defaultTopic, key, value, timestamp, headers);
+ public Future<RecordMetadata> send(String topic, String key, T value, Callback callback) {
+ return send(topic, key, value, null, null, callback);
}
- public Future<RecordMetadata> send(String topic, String key, T value, Long timestamp, Iterable<Header> headers) {
+ public Future<RecordMetadata> send(String topic, String key, T value, Long timestamp, Iterable<Header> headers, Callback callback) {
byte[] data = encoder.encode(value);
ProducerRecord<String, byte[]> record;
Integer partition = getPartition(topic, key, value, data);
- record = new ProducerRecord<>(this.defaultTopic, partition, timestamp, key, data, headers);
- return producer.send(record);
+ record = new ProducerRecord<>(topic, partition, timestamp, key, data, headers);
+ return producer.send(record, callback);
}
private Integer getPartition(String topic, String key, T value, byte[] data) {
if (partitioner == null) {
return null;
} else {
- return partitioner.partition(this.defaultTopic, key, value, data, partitionInfoList);
+ return partitioner.partition(topic, key, value, data, partitionInfoMap.computeIfAbsent(topic, producer::partitionsFor));
}
}
}
diff --git a/common/queue/src/main/java/org/thingsboard/server/kafka/TbKafkaRequestTemplate.java b/common/queue/src/main/java/org/thingsboard/server/kafka/TbKafkaRequestTemplate.java
index 8a0f529..77ad033 100644
--- a/common/queue/src/main/java/org/thingsboard/server/kafka/TbKafkaRequestTemplate.java
+++ b/common/queue/src/main/java/org/thingsboard/server/kafka/TbKafkaRequestTemplate.java
@@ -23,24 +23,25 @@ import lombok.extern.slf4j.Slf4j;
import org.apache.kafka.clients.admin.CreateTopicsResult;
import org.apache.kafka.clients.admin.NewTopic;
import org.apache.kafka.clients.consumer.ConsumerRecords;
-import org.apache.kafka.clients.producer.RecordMetadata;
import org.apache.kafka.common.header.Header;
import org.apache.kafka.common.header.internals.RecordHeader;
import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
-import java.util.concurrent.*;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeoutException;
/**
* Created by ashvayka on 25.09.18.
*/
@Slf4j
-public class TbKafkaRequestTemplate<Request, Response> {
+public class TbKafkaRequestTemplate<Request, Response> extends AbstractTbKafkaTemplate {
private final TBKafkaProducerTemplate<Request> requestTemplate;
private final TBKafkaConsumerTemplate<Response> responseTemplate;
@@ -93,13 +94,12 @@ public class TbKafkaRequestTemplate<Request, Response> {
ConsumerRecords<String, byte[]> responses = responseTemplate.poll(Duration.ofMillis(pollInterval));
responses.forEach(response -> {
Header requestIdHeader = response.headers().lastHeader(TbKafkaSettings.REQUEST_ID_HEADER);
- Response decocedResponse = null;
+ Response decodedResponse = null;
UUID requestId = null;
if (requestIdHeader == null) {
try {
- decocedResponse = responseTemplate.decode(response);
- requestId = responseTemplate.extractRequestId(decocedResponse);
-
+ decodedResponse = responseTemplate.decode(response);
+ requestId = responseTemplate.extractRequestId(decodedResponse);
} catch (IOException e) {
log.error("Failed to decode response", e);
}
@@ -107,17 +107,17 @@ public class TbKafkaRequestTemplate<Request, Response> {
requestId = bytesToUuid(requestIdHeader.value());
}
if (requestId == null) {
- log.error("[{}] Missing requestId in header and response", response);
+ log.error("[{}] Missing requestId in header and body", response);
} else {
ResponseMetaData<Response> expectedResponse = pendingRequests.remove(requestId);
if (expectedResponse == null) {
log.trace("[{}] Invalid or stale request", requestId);
} else {
try {
- if (decocedResponse == null) {
- decocedResponse = responseTemplate.decode(response);
+ if (decodedResponse == null) {
+ decodedResponse = responseTemplate.decode(response);
}
- expectedResponse.future.set(decocedResponse);
+ expectedResponse.future.set(decodedResponse);
} catch (IOException e) {
expectedResponse.future.setException(e);
}
@@ -160,28 +160,10 @@ public class TbKafkaRequestTemplate<Request, Response> {
SettableFuture<Response> future = SettableFuture.create();
pendingRequests.putIfAbsent(requestId, new ResponseMetaData<>(tickTs + maxRequestTimeout, future));
request = requestTemplate.enrich(request, responseTemplate.getTopic(), requestId);
- requestTemplate.send(key, request, headers);
+ requestTemplate.send(key, request, headers, null);
return future;
}
- private byte[] uuidToBytes(UUID uuid) {
- ByteBuffer buf = ByteBuffer.allocate(16);
- buf.putLong(uuid.getMostSignificantBits());
- buf.putLong(uuid.getLeastSignificantBits());
- return buf.array();
- }
-
- private static UUID bytesToUuid(byte[] bytes) {
- ByteBuffer bb = ByteBuffer.wrap(bytes);
- long firstLong = bb.getLong();
- long secondLong = bb.getLong();
- return new UUID(firstLong, secondLong);
- }
-
- private byte[] stringToBytes(String string) {
- return string.getBytes(StandardCharsets.UTF_8);
- }
-
private static class ResponseMetaData<T> {
private final long expTime;
private final SettableFuture<T> future;
diff --git a/common/queue/src/main/java/org/thingsboard/server/kafka/TbKafkaResponseTemplate.java b/common/queue/src/main/java/org/thingsboard/server/kafka/TbKafkaResponseTemplate.java
new file mode 100644
index 0000000..4c23ac2
--- /dev/null
+++ b/common/queue/src/main/java/org/thingsboard/server/kafka/TbKafkaResponseTemplate.java
@@ -0,0 +1,156 @@
+/**
+ * Copyright © 2016-2018 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.kafka;
+
+import lombok.Builder;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.kafka.clients.consumer.ConsumerRecords;
+import org.apache.kafka.common.header.Header;
+import org.apache.kafka.common.header.internals.RecordHeader;
+
+import java.time.Duration;
+import java.util.Collections;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * Created by ashvayka on 25.09.18.
+ */
+@Slf4j
+public class TbKafkaResponseTemplate<Request, Response> extends AbstractTbKafkaTemplate {
+
+ private final TBKafkaConsumerTemplate<Request> requestTemplate;
+ private final TBKafkaProducerTemplate<Response> responseTemplate;
+ private final TbKafkaHandler<Request, Response> handler;
+ private final ConcurrentMap<UUID, String> pendingRequests;
+ private final ExecutorService loopExecutor;
+ private final ScheduledExecutorService timeoutExecutor;
+ private final ExecutorService callbackExecutor;
+ private final int maxPendingRequests;
+ private final long requestTimeout;
+
+ private final long pollInterval;
+ private volatile boolean stopped = false;
+ private final AtomicInteger pendingRequestCount = new AtomicInteger();
+
+ @Builder
+ public TbKafkaResponseTemplate(TBKafkaConsumerTemplate<Request> requestTemplate,
+ TBKafkaProducerTemplate<Response> responseTemplate,
+ TbKafkaHandler<Request, Response> handler,
+ long pollInterval,
+ long requestTimeout,
+ int maxPendingRequests,
+ ExecutorService executor) {
+ this.requestTemplate = requestTemplate;
+ this.responseTemplate = responseTemplate;
+ this.handler = handler;
+ this.pendingRequests = new ConcurrentHashMap<>();
+ this.maxPendingRequests = maxPendingRequests;
+ this.pollInterval = pollInterval;
+ this.requestTimeout = requestTimeout;
+ this.callbackExecutor = executor;
+ this.timeoutExecutor = Executors.newSingleThreadScheduledExecutor();
+ this.loopExecutor = Executors.newSingleThreadExecutor();
+ }
+
+ public void init() {
+ this.responseTemplate.init();
+ requestTemplate.subscribe();
+ loopExecutor.submit(() -> {
+ while (!stopped) {
+ try {
+ while (pendingRequestCount.get() >= maxPendingRequests) {
+ try {
+ Thread.sleep(pollInterval);
+ } catch (InterruptedException e) {
+ log.trace("Failed to wait until the server has capacity to handle new requests", e);
+ }
+ }
+ ConsumerRecords<String, byte[]> requests = requestTemplate.poll(Duration.ofMillis(pollInterval));
+ requests.forEach(request -> {
+ Header requestIdHeader = request.headers().lastHeader(TbKafkaSettings.REQUEST_ID_HEADER);
+ if (requestIdHeader == null) {
+ log.error("[{}] Missing requestId in header", request);
+ return;
+ }
+ UUID requestId = bytesToUuid(requestIdHeader.value());
+ if (requestId == null) {
+ log.error("[{}] Missing requestId in header and body", request);
+ return;
+ }
+ Header responseTopicHeader = request.headers().lastHeader(TbKafkaSettings.RESPONSE_TOPIC_HEADER);
+ if (responseTopicHeader == null) {
+ log.error("[{}] Missing response topic in header", request);
+ return;
+ }
+ String responseTopic = bytesToString(responseTopicHeader.value());
+ try {
+ pendingRequestCount.getAndIncrement();
+ Request decodedRequest = requestTemplate.decode(request);
+ AsyncCallbackTemplate.withCallbackAndTimeout(handler.handle(decodedRequest),
+ response -> {
+ pendingRequestCount.decrementAndGet();
+ reply(requestId, responseTopic, response);
+ },
+ e -> {
+ pendingRequestCount.decrementAndGet();
+ if (e.getCause() != null && e.getCause() instanceof TimeoutException) {
+ log.warn("[{}] Timedout to process the request: {}", requestId, request, e);
+ } else {
+ log.trace("[{}] Failed to process the request: {}", requestId, request, e);
+ }
+ },
+ requestTimeout,
+ timeoutExecutor,
+ callbackExecutor);
+ } catch (Throwable e) {
+ pendingRequestCount.decrementAndGet();
+ log.warn("[{}] Failed to process the request: {}", requestId, request, e);
+ }
+ });
+ } catch (Throwable e) {
+ log.warn("Failed to obtain messages from queue.", e);
+ try {
+ Thread.sleep(pollInterval);
+ } catch (InterruptedException e2) {
+ log.trace("Failed to wait until the server has capacity to handle new requests", e2);
+ }
+ }
+ }
+ });
+ }
+
+ public void stop() {
+ stopped = true;
+ if (timeoutExecutor != null) {
+ timeoutExecutor.shutdownNow();
+ }
+ if (loopExecutor != null) {
+ loopExecutor.shutdownNow();
+ }
+ }
+
+ private void reply(UUID requestId, String topic, Response response) {
+ responseTemplate.send(topic, requestId.toString(), response, Collections.singletonList(new RecordHeader(TbKafkaSettings.REQUEST_ID_HEADER, uuidToBytes(requestId))), null);
+ }
+
+}
common/transport/coap/pom.xml 88(+88 -0)
diff --git a/common/transport/coap/pom.xml b/common/transport/coap/pom.xml
new file mode 100644
index 0000000..d68a6db
--- /dev/null
+++ b/common/transport/coap/pom.xml
@@ -0,0 +1,88 @@
+<!--
+
+ Copyright © 2016-2018 The Thingsboard Authors
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.thingsboard.common</groupId>
+ <version>2.2.0-SNAPSHOT</version>
+ <artifactId>transport</artifactId>
+ </parent>
+ <groupId>org.thingsboard.common.transport</groupId>
+ <artifactId>coap</artifactId>
+ <packaging>jar</packaging>
+
+ <name>Thingsboard CoAP Transport Common</name>
+ <url>https://thingsboard.io</url>
+
+ <properties>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ <main.dir>${basedir}/../../..</main.dir>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.thingsboard.common.transport</groupId>
+ <artifactId>transport-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.californium</groupId>
+ <artifactId>californium-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-context-support</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-context</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>log4j-over-slf4j</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>ch.qos.logback</groupId>
+ <artifactId>logback-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>ch.qos.logback</groupId>
+ <artifactId>logback-classic</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-test</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+</project>
diff --git a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/JsonCoapAdaptor.java b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/JsonCoapAdaptor.java
new file mode 100644
index 0000000..f5bfb49
--- /dev/null
+++ b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/JsonCoapAdaptor.java
@@ -0,0 +1,155 @@
+/**
+ * Copyright © 2016-2018 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.transport.coap.adaptors;
+
+import java.util.*;
+
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import lombok.extern.slf4j.Slf4j;
+import org.eclipse.californium.core.coap.CoAP;
+import org.eclipse.californium.core.coap.Request;
+import org.eclipse.californium.core.coap.Response;
+import org.springframework.util.StringUtils;
+import org.thingsboard.server.common.msg.kv.AttributesKVMsg;
+import org.thingsboard.server.common.msg.session.SessionContext;
+import org.thingsboard.server.common.transport.adaptor.AdaptorException;
+import org.thingsboard.server.common.transport.adaptor.JsonConverter;
+import org.springframework.stereotype.Component;
+
+import com.google.gson.JsonParser;
+import com.google.gson.JsonSyntaxException;
+import org.thingsboard.server.gen.transport.TransportProtos;
+import org.thingsboard.server.transport.coap.CoapTransportResource;
+
+@Component("JsonCoapAdaptor")
+@Slf4j
+public class JsonCoapAdaptor implements CoapTransportAdaptor {
+
+ @Override
+ public TransportProtos.PostTelemetryMsg convertToPostTelemetry(UUID sessionId, Request inbound) throws AdaptorException {
+ String payload = validatePayload(sessionId, inbound);
+ try {
+ return JsonConverter.convertToTelemetryProto(new JsonParser().parse(payload));
+ } catch (IllegalStateException | JsonSyntaxException ex) {
+ throw new AdaptorException(ex);
+ }
+ }
+
+ @Override
+ public TransportProtos.PostAttributeMsg convertToPostAttributes(UUID sessionId, Request inbound) throws AdaptorException {
+ String payload = validatePayload(sessionId, inbound);
+ try {
+ return JsonConverter.convertToAttributesProto(new JsonParser().parse(payload));
+ } catch (IllegalStateException | JsonSyntaxException ex) {
+ throw new AdaptorException(ex);
+ }
+ }
+
+ @Override
+ public TransportProtos.GetAttributeRequestMsg convertToGetAttributes(UUID sessionId, Request inbound) throws AdaptorException {
+ List<String> queryElements = inbound.getOptions().getUriQuery();
+ TransportProtos.GetAttributeRequestMsg.Builder result = TransportProtos.GetAttributeRequestMsg.newBuilder();
+ if (queryElements != null && queryElements.size() > 0) {
+ Set<String> clientKeys = toKeys(queryElements, "clientKeys");
+ Set<String> sharedKeys = toKeys(queryElements, "sharedKeys");
+ if (clientKeys != null) {
+ result.addAllClientAttributeNames(clientKeys);
+ }
+ if (sharedKeys != null) {
+ result.addAllSharedAttributeNames(sharedKeys);
+ }
+ }
+ return result.build();
+ }
+
+ @Override
+ public TransportProtos.ToDeviceRpcResponseMsg convertToDeviceRpcResponse(UUID sessionId, Request inbound) throws AdaptorException {
+ Optional<Integer> requestId = CoapTransportResource.getRequestId(inbound);
+ String payload = validatePayload(sessionId, inbound);
+ JsonObject response = new JsonParser().parse(payload).getAsJsonObject();
+ return TransportProtos.ToDeviceRpcResponseMsg.newBuilder().setRequestId(requestId.orElseThrow(() -> new AdaptorException("Request id is missing!")))
+ .setPayload(response.toString()).build();
+ }
+
+ @Override
+ public TransportProtos.ToServerRpcRequestMsg convertToServerRpcRequest(UUID sessionId, Request inbound) throws AdaptorException {
+ String payload = validatePayload(sessionId, inbound);
+ return JsonConverter.convertToServerRpcRequest(new JsonParser().parse(payload), 0);
+ }
+
+ @Override
+ public Response convertToPublish(CoapTransportResource.CoapSessionListener session, TransportProtos.AttributeUpdateNotificationMsg msg) throws AdaptorException {
+ return getObserveNotification(session.getNextSeqNumber(), JsonConverter.toJson(msg));
+ }
+
+ @Override
+ public Response convertToPublish(CoapTransportResource.CoapSessionListener session, TransportProtos.ToDeviceRpcRequestMsg msg) throws AdaptorException {
+ return getObserveNotification(session.getNextSeqNumber(), JsonConverter.toJson(msg, true));
+ }
+
+ @Override
+ public Response convertToPublish(CoapTransportResource.CoapSessionListener coapSessionListener, TransportProtos.ToServerRpcResponseMsg msg) throws AdaptorException {
+ Response response = new Response(CoAP.ResponseCode.CONTENT);
+ JsonElement result = JsonConverter.toJson(msg);
+ response.setPayload(result.toString());
+ return response;
+ }
+
+ @Override
+ public Response convertToPublish(CoapTransportResource.CoapSessionListener session, TransportProtos.GetAttributeResponseMsg msg) throws AdaptorException {
+ if (msg.getClientAttributeListCount() == 0 && msg.getSharedAttributeListCount() == 0 && msg.getDeletedAttributeKeysCount() == 0) {
+ return new Response(CoAP.ResponseCode.NOT_FOUND);
+ } else {
+ Response response = new Response(CoAP.ResponseCode.CONTENT);
+ JsonObject result = JsonConverter.toJson(msg);
+ response.setPayload(result.toString());
+ return response;
+ }
+ }
+
+ private Response getObserveNotification(int seqNumber, JsonElement json) {
+ Response response = new Response(CoAP.ResponseCode.CONTENT);
+ response.getOptions().setObserve(seqNumber);
+ response.setPayload(json.toString());
+ return response;
+ }
+
+ private String validatePayload(UUID sessionId, Request inbound) throws AdaptorException {
+ String payload = inbound.getPayloadString();
+ if (payload == null) {
+ log.warn("[{}] Payload is empty!", sessionId);
+ throw new AdaptorException(new IllegalArgumentException("Payload is empty!"));
+ }
+ return payload;
+ }
+
+ private Set<String> toKeys(List<String> queryElements, String attributeName) throws AdaptorException {
+ String keys = null;
+ for (String queryElement : queryElements) {
+ String[] queryItem = queryElement.split("=");
+ if (queryItem.length == 2 && queryItem[0].equals(attributeName)) {
+ keys = queryItem[1];
+ }
+ }
+ if (keys != null && !StringUtils.isEmpty(keys)) {
+ return new HashSet<>(Arrays.asList(keys.split(",")));
+ } else {
+ return null;
+ }
+ }
+
+}
common/transport/http/pom.xml 81(+81 -0)
diff --git a/common/transport/http/pom.xml b/common/transport/http/pom.xml
new file mode 100644
index 0000000..5d735c9
--- /dev/null
+++ b/common/transport/http/pom.xml
@@ -0,0 +1,81 @@
+<!--
+
+ Copyright © 2016-2018 The Thingsboard Authors
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.thingsboard.common</groupId>
+ <version>2.2.0-SNAPSHOT</version>
+ <artifactId>transport</artifactId>
+ </parent>
+ <groupId>org.thingsboard.common.transport</groupId>
+ <artifactId>http</artifactId>
+ <packaging>jar</packaging>
+
+ <name>Thingsboard HTTP Transport Common</name>
+ <url>https://thingsboard.io</url>
+
+ <properties>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ <main.dir>${basedir}/../../..</main.dir>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.thingsboard.common.transport</groupId>
+ <artifactId>transport-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-web</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>log4j-over-slf4j</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>ch.qos.logback</groupId>
+ <artifactId>logback-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>ch.qos.logback</groupId>
+ <artifactId>logback-classic</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-test</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+</project>
diff --git a/common/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java b/common/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java
new file mode 100644
index 0000000..b40fbc1
--- /dev/null
+++ b/common/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java
@@ -0,0 +1,326 @@
+/**
+ * Copyright © 2016-2018 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.transport.http;
+
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.util.StringUtils;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.context.request.async.DeferredResult;
+import org.thingsboard.server.common.transport.SessionMsgListener;
+import org.thingsboard.server.common.transport.TransportContext;
+import org.thingsboard.server.common.transport.TransportService;
+import org.thingsboard.server.common.transport.TransportServiceCallback;
+import org.thingsboard.server.common.transport.adaptor.JsonConverter;
+import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.DeviceInfoProto;
+import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeRequestMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeResponseMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.SessionCloseNotificationProto;
+import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto;
+import org.thingsboard.server.gen.transport.TransportProtos.SubscribeToAttributeUpdatesMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.SubscribeToRPCMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcRequestMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcResponseMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.ToServerRpcRequestMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.ToServerRpcResponseMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceCredentialsResponseMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceTokenRequestMsg;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.Arrays;
+import java.util.List;
+import java.util.UUID;
+import java.util.function.Consumer;
+
+/**
+ * @author Andrew Shvayka
+ */
+@RestController
+@ConditionalOnExpression("'${transport.type:null}'=='null' || ('${transport.type}'=='local' && '${transport.http.enabled}'=='true')")
+@RequestMapping("/api/v1")
+@Slf4j
+public class DeviceApiController {
+
+ @Autowired
+ private HttpTransportContext transportContext;
+
+ @RequestMapping(value = "/{deviceToken}/attributes", method = RequestMethod.GET, produces = "application/json")
+ public DeferredResult<ResponseEntity> getDeviceAttributes(@PathVariable("deviceToken") String deviceToken,
+ @RequestParam(value = "clientKeys", required = false, defaultValue = "") String clientKeys,
+ @RequestParam(value = "sharedKeys", required = false, defaultValue = "") String sharedKeys,
+ HttpServletRequest httpRequest) {
+ DeferredResult<ResponseEntity> responseWriter = new DeferredResult<>();
+ if (quotaExceeded(httpRequest, responseWriter)) {
+ return responseWriter;
+ }
+ transportContext.getTransportService().process(ValidateDeviceTokenRequestMsg.newBuilder().setToken(deviceToken).build(),
+ new DeviceAuthCallback(transportContext, responseWriter, sessionInfo -> {
+ GetAttributeRequestMsg.Builder request = GetAttributeRequestMsg.newBuilder().setRequestId(0);
+ List<String> clientKeySet = !StringUtils.isEmpty(clientKeys) ? Arrays.asList(clientKeys.split(",")) : null;
+ List<String> sharedKeySet = !StringUtils.isEmpty(sharedKeys) ? Arrays.asList(sharedKeys.split(",")) : null;
+ if (clientKeySet != null) {
+ request.addAllClientAttributeNames(clientKeySet);
+ }
+ if (sharedKeySet != null) {
+ request.addAllSharedAttributeNames(sharedKeySet);
+ }
+ TransportService transportService = transportContext.getTransportService();
+ transportService.registerSyncSession(sessionInfo, new HttpSessionListener(responseWriter), transportContext.getDefaultTimeout());
+ transportService.process(sessionInfo, request.build(), new SessionCloseOnErrorCallback(transportService, sessionInfo));
+ }));
+ return responseWriter;
+ }
+
+ @RequestMapping(value = "/{deviceToken}/attributes", method = RequestMethod.POST)
+ public DeferredResult<ResponseEntity> postDeviceAttributes(@PathVariable("deviceToken") String deviceToken,
+ @RequestBody String json, HttpServletRequest request) {
+ DeferredResult<ResponseEntity> responseWriter = new DeferredResult<>();
+ if (quotaExceeded(request, responseWriter)) {
+ return responseWriter;
+ }
+ transportContext.getTransportService().process(ValidateDeviceTokenRequestMsg.newBuilder().setToken(deviceToken).build(),
+ new DeviceAuthCallback(transportContext, responseWriter, sessionInfo -> {
+ TransportService transportService = transportContext.getTransportService();
+ transportService.process(sessionInfo, JsonConverter.convertToAttributesProto(new JsonParser().parse(json)),
+ new HttpOkCallback(responseWriter));
+ }));
+ return responseWriter;
+ }
+
+ @RequestMapping(value = "/{deviceToken}/telemetry", method = RequestMethod.POST)
+ public DeferredResult<ResponseEntity> postTelemetry(@PathVariable("deviceToken") String deviceToken,
+ @RequestBody String json, HttpServletRequest request) {
+ DeferredResult<ResponseEntity> responseWriter = new DeferredResult<ResponseEntity>();
+ if (quotaExceeded(request, responseWriter)) {
+ return responseWriter;
+ }
+ transportContext.getTransportService().process(ValidateDeviceTokenRequestMsg.newBuilder().setToken(deviceToken).build(),
+ new DeviceAuthCallback(transportContext, responseWriter, sessionInfo -> {
+ TransportService transportService = transportContext.getTransportService();
+ transportService.process(sessionInfo, JsonConverter.convertToTelemetryProto(new JsonParser().parse(json)),
+ new HttpOkCallback(responseWriter));
+ }));
+ return responseWriter;
+ }
+
+ @RequestMapping(value = "/{deviceToken}/rpc", method = RequestMethod.GET, produces = "application/json")
+ public DeferredResult<ResponseEntity> subscribeToCommands(@PathVariable("deviceToken") String deviceToken,
+ @RequestParam(value = "timeout", required = false, defaultValue = "0") long timeout,
+ HttpServletRequest httpRequest) {
+ DeferredResult<ResponseEntity> responseWriter = new DeferredResult<>();
+ if (quotaExceeded(httpRequest, responseWriter)) {
+ return responseWriter;
+ }
+ transportContext.getTransportService().process(ValidateDeviceTokenRequestMsg.newBuilder().setToken(deviceToken).build(),
+ new DeviceAuthCallback(transportContext, responseWriter, sessionInfo -> {
+ TransportService transportService = transportContext.getTransportService();
+ transportService.registerSyncSession(sessionInfo, new HttpSessionListener(responseWriter),
+ timeout == 0 ? transportContext.getDefaultTimeout() : timeout);
+ transportService.process(sessionInfo, SubscribeToRPCMsg.getDefaultInstance(),
+ new SessionCloseOnErrorCallback(transportService, sessionInfo));
+
+ }));
+ return responseWriter;
+ }
+
+ @RequestMapping(value = "/{deviceToken}/rpc/{requestId}", method = RequestMethod.POST)
+ public DeferredResult<ResponseEntity> replyToCommand(@PathVariable("deviceToken") String deviceToken,
+ @PathVariable("requestId") Integer requestId,
+ @RequestBody String json, HttpServletRequest request) {
+ DeferredResult<ResponseEntity> responseWriter = new DeferredResult<ResponseEntity>();
+ if (quotaExceeded(request, responseWriter)) {
+ return responseWriter;
+ }
+ transportContext.getTransportService().process(ValidateDeviceTokenRequestMsg.newBuilder().setToken(deviceToken).build(),
+ new DeviceAuthCallback(transportContext, responseWriter, sessionInfo -> {
+ TransportService transportService = transportContext.getTransportService();
+ transportService.process(sessionInfo, ToDeviceRpcResponseMsg.newBuilder().setRequestId(requestId).setPayload(json).build(), new HttpOkCallback(responseWriter));
+ }));
+ return responseWriter;
+ }
+
+ @RequestMapping(value = "/{deviceToken}/rpc", method = RequestMethod.POST)
+ public DeferredResult<ResponseEntity> postRpcRequest(@PathVariable("deviceToken") String deviceToken,
+ @RequestBody String json, HttpServletRequest httpRequest) {
+ DeferredResult<ResponseEntity> responseWriter = new DeferredResult<ResponseEntity>();
+ if (quotaExceeded(httpRequest, responseWriter)) {
+ return responseWriter;
+ }
+ transportContext.getTransportService().process(ValidateDeviceTokenRequestMsg.newBuilder().setToken(deviceToken).build(),
+ new DeviceAuthCallback(transportContext, responseWriter, sessionInfo -> {
+ JsonObject request = new JsonParser().parse(json).getAsJsonObject();
+ TransportService transportService = transportContext.getTransportService();
+ transportService.registerSyncSession(sessionInfo, new HttpSessionListener(responseWriter), transportContext.getDefaultTimeout());
+ transportService.process(sessionInfo, ToServerRpcRequestMsg.newBuilder().setRequestId(0)
+ .setMethodName(request.get("method").getAsString())
+ .setParams(request.get("params").toString()).build(),
+ new SessionCloseOnErrorCallback(transportService, sessionInfo));
+ }));
+ return responseWriter;
+ }
+
+ @RequestMapping(value = "/{deviceToken}/attributes/updates", method = RequestMethod.GET, produces = "application/json")
+ public DeferredResult<ResponseEntity> subscribeToAttributes(@PathVariable("deviceToken") String deviceToken,
+ @RequestParam(value = "timeout", required = false, defaultValue = "0") long timeout,
+ HttpServletRequest httpRequest) {
+ DeferredResult<ResponseEntity> responseWriter = new DeferredResult<>();
+ if (quotaExceeded(httpRequest, responseWriter)) {
+ return responseWriter;
+ }
+ transportContext.getTransportService().process(ValidateDeviceTokenRequestMsg.newBuilder().setToken(deviceToken).build(),
+ new DeviceAuthCallback(transportContext, responseWriter, sessionInfo -> {
+ TransportService transportService = transportContext.getTransportService();
+ transportService.registerSyncSession(sessionInfo, new HttpSessionListener(responseWriter),
+ timeout == 0 ? transportContext.getDefaultTimeout() : timeout);
+ transportService.process(sessionInfo, SubscribeToAttributeUpdatesMsg.getDefaultInstance(),
+ new SessionCloseOnErrorCallback(transportService, sessionInfo));
+
+ }));
+ return responseWriter;
+ }
+
+ private boolean quotaExceeded(HttpServletRequest request, DeferredResult<ResponseEntity> responseWriter) {
+ if (transportContext.getQuotaService().isQuotaExceeded(request.getRemoteAddr())) {
+ log.warn("REST Quota exceeded for [{}] . Disconnect", request.getRemoteAddr());
+ responseWriter.setResult(new ResponseEntity<>(HttpStatus.BANDWIDTH_LIMIT_EXCEEDED));
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ private static class DeviceAuthCallback implements TransportServiceCallback<ValidateDeviceCredentialsResponseMsg> {
+ private final TransportContext transportContext;
+ private final DeferredResult<ResponseEntity> responseWriter;
+ private final Consumer<SessionInfoProto> onSuccess;
+
+ DeviceAuthCallback(TransportContext transportContext, DeferredResult<ResponseEntity> responseWriter, Consumer<SessionInfoProto> onSuccess) {
+ this.transportContext = transportContext;
+ this.responseWriter = responseWriter;
+ this.onSuccess = onSuccess;
+ }
+
+ @Override
+ public void onSuccess(ValidateDeviceCredentialsResponseMsg msg) {
+ if (msg.hasDeviceInfo()) {
+ UUID sessionId = UUID.randomUUID();
+ DeviceInfoProto deviceInfoProto = msg.getDeviceInfo();
+ SessionInfoProto sessionInfo = SessionInfoProto.newBuilder()
+ .setNodeId(transportContext.getNodeId())
+ .setTenantIdMSB(deviceInfoProto.getTenantIdMSB())
+ .setTenantIdLSB(deviceInfoProto.getTenantIdLSB())
+ .setDeviceIdMSB(deviceInfoProto.getDeviceIdMSB())
+ .setDeviceIdLSB(deviceInfoProto.getDeviceIdLSB())
+ .setSessionIdMSB(sessionId.getMostSignificantBits())
+ .setSessionIdLSB(sessionId.getLeastSignificantBits())
+ .build();
+ onSuccess.accept(sessionInfo);
+ } else {
+ responseWriter.setResult(new ResponseEntity<>(HttpStatus.UNAUTHORIZED));
+ }
+ }
+
+ @Override
+ public void onError(Throwable e) {
+ log.warn("Failed to process request", e);
+ responseWriter.setResult(new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR));
+ }
+ }
+
+ private static class SessionCloseOnErrorCallback implements TransportServiceCallback<Void> {
+ private final TransportService transportService;
+ private final SessionInfoProto sessionInfo;
+
+ SessionCloseOnErrorCallback(TransportService transportService, SessionInfoProto sessionInfo) {
+ this.transportService = transportService;
+ this.sessionInfo = sessionInfo;
+ }
+
+ @Override
+ public void onSuccess(Void msg) {
+ }
+
+ @Override
+ public void onError(Throwable e) {
+ transportService.deregisterSession(sessionInfo);
+ }
+ }
+
+ private static class HttpOkCallback implements TransportServiceCallback<Void> {
+ private final DeferredResult<ResponseEntity> responseWriter;
+
+ public HttpOkCallback(DeferredResult<ResponseEntity> responseWriter) {
+ this.responseWriter = responseWriter;
+ }
+
+ @Override
+ public void onSuccess(Void msg) {
+ responseWriter.setResult(new ResponseEntity<>(HttpStatus.OK));
+ }
+
+ @Override
+ public void onError(Throwable e) {
+ responseWriter.setResult(new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR));
+ }
+ }
+
+
+ private static class HttpSessionListener implements SessionMsgListener {
+
+ private final DeferredResult<ResponseEntity> responseWriter;
+
+ HttpSessionListener(DeferredResult<ResponseEntity> responseWriter) {
+ this.responseWriter = responseWriter;
+ }
+
+ @Override
+ public void onGetAttributesResponse(GetAttributeResponseMsg msg) {
+ responseWriter.setResult(new ResponseEntity<>(JsonConverter.toJson(msg).toString(), HttpStatus.OK));
+ }
+
+ @Override
+ public void onAttributeUpdate(AttributeUpdateNotificationMsg msg) {
+ responseWriter.setResult(new ResponseEntity<>(JsonConverter.toJson(msg).toString(), HttpStatus.OK));
+ }
+
+ @Override
+ public void onRemoteSessionCloseCommand(SessionCloseNotificationProto sessionCloseNotification) {
+ responseWriter.setResult(new ResponseEntity<>(HttpStatus.REQUEST_TIMEOUT));
+ }
+
+ @Override
+ public void onToDeviceRpcRequest(ToDeviceRpcRequestMsg msg) {
+ responseWriter.setResult(new ResponseEntity<>(JsonConverter.toJson(msg, true).toString(), HttpStatus.OK));
+ }
+
+ @Override
+ public void onToServerRpcResponse(ToServerRpcResponseMsg msg) {
+ responseWriter.setResult(new ResponseEntity<>(JsonConverter.toJson(msg).toString(), HttpStatus.OK));
+ }
+ }
+}
common/transport/mqtt/pom.xml 98(+98 -0)
diff --git a/common/transport/mqtt/pom.xml b/common/transport/mqtt/pom.xml
new file mode 100644
index 0000000..91f30a6
--- /dev/null
+++ b/common/transport/mqtt/pom.xml
@@ -0,0 +1,98 @@
+<!--
+
+ Copyright © 2016-2018 The Thingsboard Authors
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.thingsboard.common</groupId>
+ <version>2.2.0-SNAPSHOT</version>
+ <artifactId>transport</artifactId>
+ </parent>
+ <groupId>org.thingsboard.common.transport</groupId>
+ <artifactId>mqtt</artifactId>
+ <packaging>jar</packaging>
+
+ <name>Thingsboard MQTT Transport Common</name>
+ <url>https://thingsboard.io</url>
+
+ <properties>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ <main.dir>${basedir}/../../..</main.dir>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.thingsboard.common.transport</groupId>
+ <artifactId>transport-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>io.netty</groupId>
+ <artifactId>netty-all</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-context-support</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-context</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>log4j-over-slf4j</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>ch.qos.logback</groupId>
+ <artifactId>logback-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>ch.qos.logback</groupId>
+ <artifactId>logback-classic</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.google.code.findbugs</groupId>
+ <artifactId>jsr305</artifactId>
+ <version>3.0.1</version>
+ <optional>true</optional>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-test</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+</project>
diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java
new file mode 100644
index 0000000..8393ca9
--- /dev/null
+++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java
@@ -0,0 +1,217 @@
+/**
+ * Copyright © 2016-2018 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.transport.mqtt.adaptors;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import com.google.gson.JsonSyntaxException;
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.ByteBufAllocator;
+import io.netty.buffer.UnpooledByteBufAllocator;
+import io.netty.handler.codec.mqtt.MqttFixedHeader;
+import io.netty.handler.codec.mqtt.MqttMessage;
+import io.netty.handler.codec.mqtt.MqttMessageType;
+import io.netty.handler.codec.mqtt.MqttPublishMessage;
+import io.netty.handler.codec.mqtt.MqttPublishVariableHeader;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+import org.springframework.util.StringUtils;
+import org.thingsboard.server.common.transport.adaptor.AdaptorException;
+import org.thingsboard.server.common.transport.adaptor.JsonConverter;
+import org.thingsboard.server.gen.transport.TransportProtos;
+import org.thingsboard.server.transport.mqtt.MqttTopics;
+import org.thingsboard.server.transport.mqtt.session.MqttDeviceAwareSessionContext;
+
+import java.nio.charset.Charset;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Optional;
+import java.util.Set;
+import java.util.UUID;
+
+/**
+ * @author Andrew Shvayka
+ */
+@Component("JsonMqttAdaptor")
+@Slf4j
+public class JsonMqttAdaptor implements MqttTransportAdaptor {
+
+ private static final Gson GSON = new Gson();
+ private static final Charset UTF8 = Charset.forName("UTF-8");
+ private static final ByteBufAllocator ALLOCATOR = new UnpooledByteBufAllocator(false);
+
+ @Override
+ public TransportProtos.PostTelemetryMsg convertToPostTelemetry(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException {
+ String payload = validatePayload(ctx.getSessionId(), inbound.payload());
+ try {
+ return JsonConverter.convertToTelemetryProto(new JsonParser().parse(payload));
+ } catch (IllegalStateException | JsonSyntaxException ex) {
+ throw new AdaptorException(ex);
+ }
+ }
+
+ @Override
+ public TransportProtos.PostAttributeMsg convertToPostAttributes(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException {
+ String payload = validatePayload(ctx.getSessionId(), inbound.payload());
+ try {
+ return JsonConverter.convertToAttributesProto(new JsonParser().parse(payload));
+ } catch (IllegalStateException | JsonSyntaxException ex) {
+ throw new AdaptorException(ex);
+ }
+ }
+
+ @Override
+ public TransportProtos.GetAttributeRequestMsg convertToGetAttributes(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException {
+ String topicName = inbound.variableHeader().topicName();
+ try {
+ TransportProtos.GetAttributeRequestMsg.Builder result = TransportProtos.GetAttributeRequestMsg.newBuilder();
+ result.setRequestId(Integer.valueOf(topicName.substring(MqttTopics.DEVICE_ATTRIBUTES_REQUEST_TOPIC_PREFIX.length())));
+ String payload = inbound.payload().toString(UTF8);
+ JsonElement requestBody = new JsonParser().parse(payload);
+ Set<String> clientKeys = toStringSet(requestBody, "clientKeys");
+ Set<String> sharedKeys = toStringSet(requestBody, "sharedKeys");
+ if (clientKeys != null) {
+ result.addAllClientAttributeNames(clientKeys);
+ }
+ if (sharedKeys != null) {
+ result.addAllSharedAttributeNames(sharedKeys);
+ }
+ return result.build();
+ } catch (RuntimeException e) {
+ log.warn("Failed to decode get attributes request", e);
+ throw new AdaptorException(e);
+ }
+ }
+
+ @Override
+ public TransportProtos.ToDeviceRpcResponseMsg convertToDeviceRpcResponse(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException {
+ String topicName = inbound.variableHeader().topicName();
+ try {
+ Integer requestId = Integer.valueOf(topicName.substring(MqttTopics.DEVICE_RPC_RESPONSE_TOPIC.length()));
+ String payload = inbound.payload().toString(UTF8);
+ return TransportProtos.ToDeviceRpcResponseMsg.newBuilder().setRequestId(requestId).setPayload(payload).build();
+ } catch (RuntimeException e) {
+ log.warn("Failed to decode get attributes request", e);
+ throw new AdaptorException(e);
+ }
+ }
+
+ @Override
+ public TransportProtos.ToServerRpcRequestMsg convertToServerRpcRequest(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException {
+ String topicName = inbound.variableHeader().topicName();
+ String payload = validatePayload(ctx.getSessionId(), inbound.payload());
+ try {
+ Integer requestId = Integer.valueOf(topicName.substring(MqttTopics.DEVICE_RPC_REQUESTS_TOPIC.length()));
+ return JsonConverter.convertToServerRpcRequest(new JsonParser().parse(payload), requestId);
+ } catch (IllegalStateException | JsonSyntaxException ex) {
+ throw new AdaptorException(ex);
+ }
+ }
+
+ @Override
+ public Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, TransportProtos.GetAttributeResponseMsg responseMsg) throws AdaptorException {
+ if (!StringUtils.isEmpty(responseMsg.getError())) {
+ throw new AdaptorException(responseMsg.getError());
+ } else {
+ Integer requestId = responseMsg.getRequestId();
+ if (requestId >= 0) {
+ return Optional.of(createMqttPublishMsg(ctx,
+ MqttTopics.DEVICE_ATTRIBUTES_RESPONSE_TOPIC_PREFIX + requestId,
+ JsonConverter.toJson(responseMsg)));
+ }
+ return Optional.empty();
+ }
+ }
+
+ @Override
+ public Optional<MqttMessage> convertToGatewayPublish(MqttDeviceAwareSessionContext ctx, TransportProtos.GetAttributeResponseMsg responseMsg) throws AdaptorException {
+ if (!StringUtils.isEmpty(responseMsg.getError())) {
+ throw new AdaptorException(responseMsg.getError());
+ } else {
+ JsonObject result = JsonConverter.getJsonObjectForGateway(responseMsg);
+ return Optional.of(createMqttPublishMsg(ctx, MqttTopics.GATEWAY_ATTRIBUTES_RESPONSE_TOPIC, result));
+ }
+ }
+
+ @Override
+ public Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, TransportProtos.AttributeUpdateNotificationMsg notificationMsg) throws AdaptorException {
+ return Optional.of(createMqttPublishMsg(ctx, MqttTopics.DEVICE_ATTRIBUTES_TOPIC, JsonConverter.toJson(notificationMsg)));
+ }
+
+ @Override
+ public Optional<MqttMessage> convertToGatewayPublish(MqttDeviceAwareSessionContext ctx, String deviceName, TransportProtos.AttributeUpdateNotificationMsg notificationMsg) throws AdaptorException {
+ JsonObject result = JsonConverter.getJsonObjectForGateway(deviceName, notificationMsg);
+ return Optional.of(createMqttPublishMsg(ctx, MqttTopics.GATEWAY_ATTRIBUTES_TOPIC, result));
+ }
+
+ @Override
+ public Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, TransportProtos.ToDeviceRpcRequestMsg rpcRequest) throws AdaptorException {
+ return Optional.of(createMqttPublishMsg(ctx, MqttTopics.DEVICE_RPC_REQUESTS_TOPIC + rpcRequest.getRequestId(), JsonConverter.toJson(rpcRequest, false)));
+ }
+
+ @Override
+ public Optional<MqttMessage> convertToGatewayPublish(MqttDeviceAwareSessionContext ctx, String deviceName, TransportProtos.ToDeviceRpcRequestMsg rpcRequest) throws AdaptorException {
+ return Optional.of(createMqttPublishMsg(ctx, MqttTopics.GATEWAY_RPC_TOPIC, JsonConverter.toGatewayJson(deviceName, rpcRequest)));
+ }
+
+ @Override
+ public Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, TransportProtos.ToServerRpcResponseMsg rpcResponse) {
+ return Optional.of(createMqttPublishMsg(ctx, MqttTopics.DEVICE_RPC_RESPONSE_TOPIC + rpcResponse.getRequestId(), JsonConverter.toJson(rpcResponse)));
+ }
+
+ private MqttPublishMessage createMqttPublishMsg(MqttDeviceAwareSessionContext ctx, String topic, JsonElement json) {
+ MqttFixedHeader mqttFixedHeader =
+ new MqttFixedHeader(MqttMessageType.PUBLISH, false, ctx.getQoSForTopic(topic), false, 0);
+ MqttPublishVariableHeader header = new MqttPublishVariableHeader(topic, ctx.nextMsgId());
+ ByteBuf payload = ALLOCATOR.buffer();
+ payload.writeBytes(GSON.toJson(json).getBytes(UTF8));
+ return new MqttPublishMessage(mqttFixedHeader, header, payload);
+ }
+
+ private Set<String> toStringSet(JsonElement requestBody, String name) {
+ JsonElement element = requestBody.getAsJsonObject().get(name);
+ if (element != null) {
+ return new HashSet<>(Arrays.asList(element.getAsString().split(",")));
+ } else {
+ return null;
+ }
+ }
+
+ public static JsonElement validateJsonPayload(UUID sessionId, ByteBuf payloadData) throws AdaptorException {
+ String payload = validatePayload(sessionId, payloadData);
+ try {
+ return new JsonParser().parse(payload);
+ } catch (JsonSyntaxException ex) {
+ throw new AdaptorException(ex);
+ }
+ }
+
+ private static String validatePayload(UUID sessionId, ByteBuf payloadData) throws AdaptorException {
+ try {
+ String payload = payloadData.toString(UTF8);
+ if (payload == null) {
+ log.warn("[{}] Payload is empty!", sessionId);
+ throw new AdaptorException(new IllegalArgumentException("Payload is empty!"));
+ }
+ return payload;
+ } finally {
+ payloadData.release();
+ }
+ }
+
+}
diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/MqttTransportAdaptor.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/MqttTransportAdaptor.java
new file mode 100644
index 0000000..7af0e66
--- /dev/null
+++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/MqttTransportAdaptor.java
@@ -0,0 +1,62 @@
+/**
+ * Copyright © 2016-2018 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.transport.mqtt.adaptors;
+
+import io.netty.handler.codec.mqtt.MqttMessage;
+import io.netty.handler.codec.mqtt.MqttPublishMessage;
+import org.thingsboard.server.common.transport.adaptor.AdaptorException;
+import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeRequestMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeResponseMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.PostAttributeMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.PostTelemetryMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcRequestMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcResponseMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.ToServerRpcRequestMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.ToServerRpcResponseMsg;
+import org.thingsboard.server.transport.mqtt.session.MqttDeviceAwareSessionContext;
+
+import java.util.Optional;
+
+/**
+ * @author Andrew Shvayka
+ */
+public interface MqttTransportAdaptor {
+
+ PostTelemetryMsg convertToPostTelemetry(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException;
+
+ PostAttributeMsg convertToPostAttributes(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException;
+
+ GetAttributeRequestMsg convertToGetAttributes(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException;
+
+ ToDeviceRpcResponseMsg convertToDeviceRpcResponse(MqttDeviceAwareSessionContext ctx, MqttPublishMessage mqttMsg) throws AdaptorException;
+
+ ToServerRpcRequestMsg convertToServerRpcRequest(MqttDeviceAwareSessionContext ctx, MqttPublishMessage mqttMsg) throws AdaptorException;
+
+ Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, GetAttributeResponseMsg responseMsg) throws AdaptorException;
+
+ Optional<MqttMessage> convertToGatewayPublish(MqttDeviceAwareSessionContext ctx, GetAttributeResponseMsg responseMsg) throws AdaptorException;
+
+ Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, AttributeUpdateNotificationMsg notificationMsg) throws AdaptorException;
+
+ Optional<MqttMessage> convertToGatewayPublish(MqttDeviceAwareSessionContext ctx, String deviceName, AttributeUpdateNotificationMsg notificationMsg) throws AdaptorException;
+
+ Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, ToDeviceRpcRequestMsg rpcRequest) throws AdaptorException;
+
+ Optional<MqttMessage> convertToGatewayPublish(MqttDeviceAwareSessionContext ctx, String deviceName, ToDeviceRpcRequestMsg rpcRequest) throws AdaptorException;
+
+ Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, ToServerRpcResponseMsg rpcResponse) throws AdaptorException;
+}
diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportContext.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportContext.java
new file mode 100644
index 0000000..538daa1
--- /dev/null
+++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportContext.java
@@ -0,0 +1,64 @@
+/**
+ * Copyright © 2016-2018 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.transport.mqtt;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import io.netty.handler.ssl.SslHandler;
+import lombok.Data;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.stereotype.Component;
+import org.thingsboard.server.common.transport.TransportContext;
+import org.thingsboard.server.common.transport.TransportService;
+import org.thingsboard.server.common.transport.quota.host.HostRequestsQuotaService;
+import org.thingsboard.server.kafka.TbNodeIdProvider;
+import org.thingsboard.server.transport.mqtt.adaptors.MqttTransportAdaptor;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+/**
+ * Created by ashvayka on 04.10.18.
+ */
+@Slf4j
+@ConditionalOnExpression("'${transport.type:null}'=='null' || ('${transport.type}'=='local' && '${transport.mqtt.enabled}'=='true')")
+@Component
+public class MqttTransportContext extends TransportContext {
+
+ @Getter
+ @Autowired(required = false)
+ private MqttSslHandlerProvider sslHandlerProvider;
+
+ @Getter
+ @Autowired
+ private MqttTransportAdaptor adaptor;
+
+ @Getter
+ @Value("${transport.mqtt.netty.max_payload_size}")
+ private Integer maxPayloadSize;
+
+ @Getter
+ @Setter
+ private SslHandler sslHandler;
+
+}
diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewayDeviceSessionCtx.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewayDeviceSessionCtx.java
new file mode 100644
index 0000000..38c1fff
--- /dev/null
+++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewayDeviceSessionCtx.java
@@ -0,0 +1,101 @@
+/**
+ * Copyright © 2016-2018 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.transport.mqtt.session;
+
+import lombok.extern.slf4j.Slf4j;
+import org.thingsboard.server.common.transport.SessionMsgListener;
+import org.thingsboard.server.gen.transport.TransportProtos;
+import org.thingsboard.server.gen.transport.TransportProtos.DeviceInfoProto;
+import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto;
+
+import java.util.UUID;
+import java.util.concurrent.ConcurrentMap;
+
+/**
+ * Created by ashvayka on 19.01.17.
+ */
+@Slf4j
+public class GatewayDeviceSessionCtx extends MqttDeviceAwareSessionContext implements SessionMsgListener {
+
+ private final GatewaySessionHandler parent;
+ private final SessionInfoProto sessionInfo;
+
+ public GatewayDeviceSessionCtx(GatewaySessionHandler parent, DeviceInfoProto deviceInfo, ConcurrentMap<MqttTopicMatcher, Integer> mqttQoSMap) {
+ super(UUID.randomUUID(), mqttQoSMap);
+ this.parent = parent;
+ this.sessionInfo = SessionInfoProto.newBuilder()
+ .setNodeId(parent.getNodeId())
+ .setSessionIdMSB(sessionId.getMostSignificantBits())
+ .setSessionIdLSB(sessionId.getLeastSignificantBits())
+ .setDeviceIdMSB(deviceInfo.getDeviceIdMSB())
+ .setDeviceIdLSB(deviceInfo.getDeviceIdLSB())
+ .setTenantIdMSB(deviceInfo.getTenantIdMSB())
+ .setTenantIdLSB(deviceInfo.getTenantIdLSB())
+ .build();
+ setDeviceInfo(deviceInfo);
+ }
+
+ @Override
+ public UUID getSessionId() {
+ return sessionId;
+ }
+
+ @Override
+ public int nextMsgId() {
+ return parent.nextMsgId();
+ }
+
+ SessionInfoProto getSessionInfo() {
+ return sessionInfo;
+ }
+
+ @Override
+ public void onGetAttributesResponse(TransportProtos.GetAttributeResponseMsg response) {
+ try {
+ parent.getAdaptor().convertToGatewayPublish(this, response).ifPresent(parent::writeAndFlush);
+ } catch (Exception e) {
+ log.trace("[{}] Failed to convert device attributes response to MQTT msg", sessionId, e);
+ }
+ }
+
+ @Override
+ public void onAttributeUpdate(TransportProtos.AttributeUpdateNotificationMsg notification) {
+ try {
+ parent.getAdaptor().convertToGatewayPublish(this, getDeviceInfo().getDeviceName(), notification).ifPresent(parent::writeAndFlush);
+ } catch (Exception e) {
+ log.trace("[{}] Failed to convert device attributes response to MQTT msg", sessionId, e);
+ }
+ }
+
+ @Override
+ public void onRemoteSessionCloseCommand(TransportProtos.SessionCloseNotificationProto sessionCloseNotification) {
+ parent.deregisterSession(getDeviceInfo().getDeviceName());
+ }
+
+ @Override
+ public void onToDeviceRpcRequest(TransportProtos.ToDeviceRpcRequestMsg request) {
+ try {
+ parent.getAdaptor().convertToGatewayPublish(this, getDeviceInfo().getDeviceName(), request).ifPresent(parent::writeAndFlush);
+ } catch (Exception e) {
+ log.trace("[{}] Failed to convert device attributes response to MQTT msg", sessionId, e);
+ }
+ }
+
+ @Override
+ public void onToServerRpcResponse(TransportProtos.ToServerRpcResponseMsg toServerResponse) {
+ // This feature is not supported in the TB IoT Gateway yet.
+ }
+}
diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionHandler.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionHandler.java
new file mode 100644
index 0000000..d600059
--- /dev/null
+++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionHandler.java
@@ -0,0 +1,370 @@
+/**
+ * Copyright © 2016-2018 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.transport.mqtt.session;
+
+
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.SettableFuture;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonNull;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonSyntaxException;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.handler.codec.mqtt.MqttMessage;
+import io.netty.handler.codec.mqtt.MqttPublishMessage;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.util.StringUtils;
+import org.thingsboard.server.common.transport.TransportService;
+import org.thingsboard.server.common.transport.TransportServiceCallback;
+import org.thingsboard.server.common.transport.adaptor.AdaptorException;
+import org.thingsboard.server.common.transport.adaptor.JsonConverter;
+import org.thingsboard.server.gen.transport.TransportProtos;
+import org.thingsboard.server.gen.transport.TransportProtos.DeviceInfoProto;
+import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFromGatewayRequestMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFromGatewayResponseMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto;
+import org.thingsboard.server.transport.mqtt.MqttTransportContext;
+import org.thingsboard.server.transport.mqtt.MqttTransportHandler;
+import org.thingsboard.server.transport.mqtt.adaptors.JsonMqttAdaptor;
+import org.thingsboard.server.transport.mqtt.adaptors.MqttTransportAdaptor;
+
+import javax.annotation.Nullable;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+/**
+ * Created by ashvayka on 19.01.17.
+ */
+@Slf4j
+public class GatewaySessionHandler {
+
+ private static final String DEFAULT_DEVICE_TYPE = "default";
+ private static final String CAN_T_PARSE_VALUE = "Can't parse value: ";
+ private static final String DEVICE_PROPERTY = "device";
+
+ private final MqttTransportContext context;
+ private final TransportService transportService;
+ private final DeviceInfoProto gateway;
+ private final UUID sessionId;
+ private final Map<String, GatewayDeviceSessionCtx> devices;
+ private final ConcurrentMap<MqttTopicMatcher, Integer> mqttQoSMap;
+ private final ChannelHandlerContext channel;
+ private final DeviceSessionCtx deviceSessionCtx;
+
+ public GatewaySessionHandler(MqttTransportContext context, DeviceSessionCtx deviceSessionCtx, UUID sessionId) {
+ this.context = context;
+ this.transportService = context.getTransportService();
+ this.deviceSessionCtx = deviceSessionCtx;
+ this.gateway = deviceSessionCtx.getDeviceInfo();
+ this.sessionId = sessionId;
+ this.devices = new ConcurrentHashMap<>();
+ this.mqttQoSMap = deviceSessionCtx.getMqttQoSMap();
+ this.channel = deviceSessionCtx.getChannel();
+ }
+
+ public void onDeviceConnect(MqttPublishMessage msg) throws AdaptorException {
+ JsonElement json = getJson(msg);
+ String deviceName = checkDeviceName(getDeviceName(json));
+ String deviceType = getDeviceType(json);
+ log.trace("[{}] onDeviceConnect: {}", sessionId, deviceName);
+ Futures.addCallback(onDeviceConnect(deviceName, deviceType), new FutureCallback<GatewayDeviceSessionCtx>() {
+ @Override
+ public void onSuccess(@Nullable GatewayDeviceSessionCtx result) {
+ ack(msg);
+ log.trace("[{}] onDeviceConnectOk: {}", sessionId, deviceName);
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ log.warn("[{}] Failed to process device connect command: {}", sessionId, deviceName, t);
+
+ }
+ }, context.getExecutor());
+ }
+
+ private ListenableFuture<GatewayDeviceSessionCtx> onDeviceConnect(String deviceName, String deviceType) {
+ SettableFuture<GatewayDeviceSessionCtx> future = SettableFuture.create();
+ GatewayDeviceSessionCtx result = devices.get(deviceName);
+ if (result == null) {
+ transportService.process(GetOrCreateDeviceFromGatewayRequestMsg.newBuilder()
+ .setDeviceName(deviceName)
+ .setDeviceType(deviceType)
+ .setGatewayIdMSB(gateway.getDeviceIdMSB())
+ .setGatewayIdLSB(gateway.getDeviceIdLSB()).build(),
+ new TransportServiceCallback<GetOrCreateDeviceFromGatewayResponseMsg>() {
+ @Override
+ public void onSuccess(GetOrCreateDeviceFromGatewayResponseMsg msg) {
+ GatewayDeviceSessionCtx deviceSessionCtx = new GatewayDeviceSessionCtx(GatewaySessionHandler.this, msg.getDeviceInfo(), mqttQoSMap);
+ if (devices.putIfAbsent(deviceName, deviceSessionCtx) == null) {
+ SessionInfoProto deviceSessionInfo = deviceSessionCtx.getSessionInfo();
+ transportService.process(deviceSessionInfo, MqttTransportHandler.getSessionEventMsg(TransportProtos.SessionEvent.OPEN), null);
+ transportService.process(deviceSessionInfo, TransportProtos.SubscribeToRPCMsg.getDefaultInstance(), null);
+ transportService.process(deviceSessionInfo, TransportProtos.SubscribeToAttributeUpdatesMsg.getDefaultInstance(), null);
+ transportService.registerAsyncSession(deviceSessionInfo, deviceSessionCtx);
+ }
+ future.set(devices.get(deviceName));
+ }
+
+ @Override
+ public void onError(Throwable e) {
+ log.warn("[{}] Failed to process device connect command: {}", sessionId, deviceName, e);
+ future.setException(e);
+ }
+ });
+ } else {
+ future.set(result);
+ }
+ return future;
+ }
+
+ public void onDeviceDisconnect(MqttPublishMessage msg) throws AdaptorException {
+ String deviceName = checkDeviceName(getDeviceName(getJson(msg)));
+ deregisterSession(deviceName);
+ ack(msg);
+ }
+
+ void deregisterSession(String deviceName) {
+ GatewayDeviceSessionCtx deviceSessionCtx = devices.remove(deviceName);
+ if (deviceSessionCtx != null) {
+ deregisterSession(deviceName, deviceSessionCtx);
+ } else {
+ log.debug("[{}] Device [{}] was already removed from the gateway session", sessionId, deviceName);
+ }
+ }
+
+ public void onGatewayDisconnect() {
+ devices.forEach(this::deregisterSession);
+ }
+
+ public void onDeviceTelemetry(MqttPublishMessage mqttMsg) throws AdaptorException {
+ JsonElement json = JsonMqttAdaptor.validateJsonPayload(sessionId, mqttMsg.payload());
+ int msgId = mqttMsg.variableHeader().packetId();
+ if (json.isJsonObject()) {
+ JsonObject jsonObj = json.getAsJsonObject();
+ for (Map.Entry<String, JsonElement> deviceEntry : jsonObj.entrySet()) {
+ String deviceName = deviceEntry.getKey();
+ Futures.addCallback(checkDeviceConnected(deviceName),
+ new FutureCallback<GatewayDeviceSessionCtx>() {
+ @Override
+ public void onSuccess(@Nullable GatewayDeviceSessionCtx deviceCtx) {
+ if (!deviceEntry.getValue().isJsonArray()) {
+ throw new JsonSyntaxException(CAN_T_PARSE_VALUE + json);
+ }
+ TransportProtos.PostTelemetryMsg postTelemetryMsg = JsonConverter.convertToTelemetryProto(deviceEntry.getValue().getAsJsonArray());
+ transportService.process(deviceCtx.getSessionInfo(), postTelemetryMsg, getPubAckCallback(channel, deviceName, msgId, postTelemetryMsg));
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ log.debug("[{}] Failed to process device teleemtry command: {}", sessionId, deviceName, t);
+ }
+ }, context.getExecutor());
+ }
+ } else {
+ throw new JsonSyntaxException(CAN_T_PARSE_VALUE + json);
+ }
+ }
+
+ public void onDeviceAttributes(MqttPublishMessage mqttMsg) throws AdaptorException {
+ JsonElement json = JsonMqttAdaptor.validateJsonPayload(sessionId, mqttMsg.payload());
+ int msgId = mqttMsg.variableHeader().packetId();
+ if (json.isJsonObject()) {
+ JsonObject jsonObj = json.getAsJsonObject();
+ for (Map.Entry<String, JsonElement> deviceEntry : jsonObj.entrySet()) {
+ String deviceName = deviceEntry.getKey();
+ Futures.addCallback(checkDeviceConnected(deviceName),
+ new FutureCallback<GatewayDeviceSessionCtx>() {
+ @Override
+ public void onSuccess(@Nullable GatewayDeviceSessionCtx deviceCtx) {
+ if (!deviceEntry.getValue().isJsonObject()) {
+ throw new JsonSyntaxException(CAN_T_PARSE_VALUE + json);
+ }
+ TransportProtos.PostAttributeMsg postAttributeMsg = JsonConverter.convertToAttributesProto(deviceEntry.getValue().getAsJsonObject());
+ transportService.process(deviceCtx.getSessionInfo(), postAttributeMsg, getPubAckCallback(channel, deviceName, msgId, postAttributeMsg));
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ log.debug("[{}] Failed to process device attributes command: {}", sessionId, deviceName, t);
+ }
+ }, context.getExecutor());
+ }
+ } else {
+ throw new JsonSyntaxException(CAN_T_PARSE_VALUE + json);
+ }
+ }
+
+ public void onDeviceRpcResponse(MqttPublishMessage mqttMsg) throws AdaptorException {
+ JsonElement json = JsonMqttAdaptor.validateJsonPayload(sessionId, mqttMsg.payload());
+ int msgId = mqttMsg.variableHeader().packetId();
+ if (json.isJsonObject()) {
+ JsonObject jsonObj = json.getAsJsonObject();
+ String deviceName = jsonObj.get(DEVICE_PROPERTY).getAsString();
+ Futures.addCallback(checkDeviceConnected(deviceName),
+ new FutureCallback<GatewayDeviceSessionCtx>() {
+ @Override
+ public void onSuccess(@Nullable GatewayDeviceSessionCtx deviceCtx) {
+ Integer requestId = jsonObj.get("id").getAsInt();
+ String data = jsonObj.get("data").toString();
+ TransportProtos.ToDeviceRpcResponseMsg rpcResponseMsg = TransportProtos.ToDeviceRpcResponseMsg.newBuilder()
+ .setRequestId(requestId).setPayload(data).build();
+ transportService.process(deviceCtx.getSessionInfo(), rpcResponseMsg, getPubAckCallback(channel, deviceName, msgId, rpcResponseMsg));
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ log.debug("[{}] Failed to process device teleemtry command: {}", sessionId, deviceName, t);
+ }
+ }, context.getExecutor());
+ } else {
+ throw new JsonSyntaxException(CAN_T_PARSE_VALUE + json);
+ }
+ }
+
+ public void onDeviceAttributesRequest(MqttPublishMessage msg) throws AdaptorException {
+ JsonElement json = JsonMqttAdaptor.validateJsonPayload(sessionId, msg.payload());
+ if (json.isJsonObject()) {
+ JsonObject jsonObj = json.getAsJsonObject();
+ int requestId = jsonObj.get("id").getAsInt();
+ String deviceName = jsonObj.get(DEVICE_PROPERTY).getAsString();
+ boolean clientScope = jsonObj.get("client").getAsBoolean();
+ Set<String> keys;
+ if (jsonObj.has("key")) {
+ keys = Collections.singleton(jsonObj.get("key").getAsString());
+ } else {
+ JsonArray keysArray = jsonObj.get("keys").getAsJsonArray();
+ keys = new HashSet<>();
+ for (JsonElement keyObj : keysArray) {
+ keys.add(keyObj.getAsString());
+ }
+ }
+ TransportProtos.GetAttributeRequestMsg.Builder result = TransportProtos.GetAttributeRequestMsg.newBuilder();
+ result.setRequestId(requestId);
+
+ if (clientScope) {
+ result.addAllClientAttributeNames(keys);
+ } else {
+ result.addAllSharedAttributeNames(keys);
+ }
+ TransportProtos.GetAttributeRequestMsg requestMsg = result.build();
+ int msgId = msg.variableHeader().packetId();
+ Futures.addCallback(checkDeviceConnected(deviceName),
+ new FutureCallback<GatewayDeviceSessionCtx>() {
+ @Override
+ public void onSuccess(@Nullable GatewayDeviceSessionCtx deviceCtx) {
+ transportService.process(deviceCtx.getSessionInfo(), requestMsg, getPubAckCallback(channel, deviceName, msgId, requestMsg));
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ log.debug("[{}] Failed to process device attributes request command: {}", sessionId, deviceName, t);
+ }
+ }, context.getExecutor());
+ ack(msg);
+ } else {
+ throw new JsonSyntaxException(CAN_T_PARSE_VALUE + json);
+ }
+ }
+
+ private ListenableFuture<GatewayDeviceSessionCtx> checkDeviceConnected(String deviceName) {
+ GatewayDeviceSessionCtx ctx = devices.get(deviceName);
+ if (ctx == null) {
+ log.debug("[{}] Missing device [{}] for the gateway session", sessionId, deviceName);
+ return onDeviceConnect(deviceName, DEFAULT_DEVICE_TYPE);
+ } else {
+ return Futures.immediateFuture(ctx);
+ }
+ }
+
+ private String checkDeviceName(String deviceName) {
+ if (StringUtils.isEmpty(deviceName)) {
+ throw new RuntimeException("Device name is empty!");
+ } else {
+ return deviceName;
+ }
+ }
+
+ private String getDeviceName(JsonElement json) throws AdaptorException {
+ return json.getAsJsonObject().get(DEVICE_PROPERTY).getAsString();
+ }
+
+ private String getDeviceType(JsonElement json) throws AdaptorException {
+ JsonElement type = json.getAsJsonObject().get("type");
+ return type == null || type instanceof JsonNull ? DEFAULT_DEVICE_TYPE : type.getAsString();
+ }
+
+ private JsonElement getJson(MqttPublishMessage mqttMsg) throws AdaptorException {
+ return JsonMqttAdaptor.validateJsonPayload(sessionId, mqttMsg.payload());
+ }
+
+ private void ack(MqttPublishMessage msg) {
+ if (msg.variableHeader().packetId() > 0) {
+ writeAndFlush(MqttTransportHandler.createMqttPubAckMsg(msg.variableHeader().packetId()));
+ }
+ }
+
+ void writeAndFlush(MqttMessage mqttMessage) {
+ channel.writeAndFlush(mqttMessage);
+ }
+
+ public String getNodeId() {
+ return context.getNodeId();
+ }
+
+ private void deregisterSession(String deviceName, GatewayDeviceSessionCtx deviceSessionCtx) {
+ transportService.deregisterSession(deviceSessionCtx.getSessionInfo());
+ transportService.process(deviceSessionCtx.getSessionInfo(), MqttTransportHandler.getSessionEventMsg(TransportProtos.SessionEvent.CLOSED), null);
+ log.debug("[{}] Removed device [{}] from the gateway session", sessionId, deviceName);
+ }
+
+ private <T> TransportServiceCallback<Void> getPubAckCallback(final ChannelHandlerContext ctx, final String deviceName, final int msgId, final T msg) {
+ return new TransportServiceCallback<Void>() {
+ @Override
+ public void onSuccess(Void dummy) {
+ log.trace("[{}][{}] Published msg: {}", sessionId, deviceName, msg);
+ if (msgId > 0) {
+ ctx.writeAndFlush(MqttTransportHandler.createMqttPubAckMsg(msgId));
+ }
+ }
+
+ @Override
+ public void onError(Throwable e) {
+ log.trace("[{}] Failed to publish msg: {}", sessionId, deviceName, msg, e);
+ ctx.close();
+ }
+ };
+ }
+
+ public MqttTransportContext getContext() {
+ return context;
+ }
+
+ public MqttTransportAdaptor getAdaptor() {
+ return context.getAdaptor();
+ }
+
+ public int nextMsgId() {
+ return deviceSessionCtx.nextMsgId();
+ }
+}
common/transport/pom.xml 65(+9 -56)
diff --git a/common/transport/pom.xml b/common/transport/pom.xml
index a349012..83e7ba8 100644
--- a/common/transport/pom.xml
+++ b/common/transport/pom.xml
@@ -16,7 +16,7 @@
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
@@ -25,67 +25,20 @@
</parent>
<groupId>org.thingsboard.common</groupId>
<artifactId>transport</artifactId>
- <packaging>jar</packaging>
+ <packaging>pom</packaging>
- <name>Thingsboard Server Common Transport components</name>
+ <name>Thingsboard Server Commons</name>
<url>https://thingsboard.io</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<main.dir>${basedir}/../..</main.dir>
</properties>
-
- <dependencies>
- <dependency>
- <groupId>org.thingsboard.common</groupId>
- <artifactId>data</artifactId>
- </dependency>
- <dependency>
- <groupId>org.thingsboard.common</groupId>
- <artifactId>message</artifactId>
- </dependency>
- <dependency>
- <groupId>com.google.code.gson</groupId>
- <artifactId>gson</artifactId>
- </dependency>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</artifactId>
- </dependency>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>log4j-over-slf4j</artifactId>
- </dependency>
- <dependency>
- <groupId>ch.qos.logback</groupId>
- <artifactId>logback-core</artifactId>
- </dependency>
- <dependency>
- <groupId>ch.qos.logback</groupId>
- <artifactId>logback-classic</artifactId>
- </dependency>
- <dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.mockito</groupId>
- <artifactId>mockito-all</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.springframework</groupId>
- <artifactId>spring-context</artifactId>
- </dependency>
- <dependency>
- <groupId>com.google.guava</groupId>
- <artifactId>guava</artifactId>
- </dependency>
- <dependency>
- <groupId>org.apache.commons</groupId>
- <artifactId>commons-lang3</artifactId>
- </dependency>
- </dependencies>
+ <modules>
+ <module>transport-api</module>
+ <module>mqtt</module>
+ <module>http</module>
+ <module>coap</module>
+ </modules>
</project>
common/transport/transport-api/pom.xml 113(+113 -0)
diff --git a/common/transport/transport-api/pom.xml b/common/transport/transport-api/pom.xml
new file mode 100644
index 0000000..3538e46
--- /dev/null
+++ b/common/transport/transport-api/pom.xml
@@ -0,0 +1,113 @@
+<!--
+
+ Copyright © 2016-2018 The Thingsboard Authors
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.thingsboard.common</groupId>
+ <version>2.2.0-SNAPSHOT</version>
+ <artifactId>transport</artifactId>
+ </parent>
+ <groupId>org.thingsboard.common.transport</groupId>
+ <artifactId>transport-api</artifactId>
+ <packaging>jar</packaging>
+
+ <name>Thingsboard Server Common Transport components</name>
+ <url>https://thingsboard.io</url>
+
+ <properties>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ <main.dir>${basedir}/../../..</main.dir>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.thingsboard.common</groupId>
+ <artifactId>data</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.thingsboard.common</groupId>
+ <artifactId>message</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.thingsboard.common</groupId>
+ <artifactId>queue</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.google.code.gson</groupId>
+ <artifactId>gson</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>log4j-over-slf4j</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>ch.qos.logback</groupId>
+ <artifactId>logback-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>ch.qos.logback</groupId>
+ <artifactId>logback-classic</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-context</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-web</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-lang3</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.google.protobuf</groupId>
+ <artifactId>protobuf-java</artifactId>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.xolstice.maven.plugins</groupId>
+ <artifactId>protobuf-maven-plugin</artifactId>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java
new file mode 100644
index 0000000..7498e8e
--- /dev/null
+++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java
@@ -0,0 +1,430 @@
+/**
+ * Copyright © 2016-2018 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.common.transport.adaptor;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import com.google.gson.JsonPrimitive;
+import com.google.gson.JsonSyntaxException;
+import org.springframework.util.StringUtils;
+import org.thingsboard.server.common.data.kv.AttributeKey;
+import org.thingsboard.server.common.data.kv.AttributeKvEntry;
+import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry;
+import org.thingsboard.server.common.data.kv.BooleanDataEntry;
+import org.thingsboard.server.common.data.kv.DoubleDataEntry;
+import org.thingsboard.server.common.data.kv.KvEntry;
+import org.thingsboard.server.common.data.kv.LongDataEntry;
+import org.thingsboard.server.common.data.kv.StringDataEntry;
+import org.thingsboard.server.common.msg.kv.AttributesKVMsg;
+import org.thingsboard.server.gen.transport.TransportProtos;
+import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeResponseMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.KeyValueProto;
+import org.thingsboard.server.gen.transport.TransportProtos.KeyValueType;
+import org.thingsboard.server.gen.transport.TransportProtos.PostAttributeMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.PostTelemetryMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.TsKvListProto;
+import org.thingsboard.server.gen.transport.TransportProtos.TsKvProto;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+
+public class JsonConverter {
+
+ private static final Gson GSON = new Gson();
+ private static final String CAN_T_PARSE_VALUE = "Can't parse value: ";
+ private static final String DEVICE_PROPERTY = "device";
+
+ public static PostTelemetryMsg convertToTelemetryProto(JsonElement jsonObject) throws JsonSyntaxException {
+ long systemTs = System.currentTimeMillis();
+ PostTelemetryMsg.Builder builder = PostTelemetryMsg.newBuilder();
+ if (jsonObject.isJsonObject()) {
+ parseObject(builder, systemTs, jsonObject);
+ } else if (jsonObject.isJsonArray()) {
+ jsonObject.getAsJsonArray().forEach(je -> {
+ if (je.isJsonObject()) {
+ parseObject(builder, systemTs, je.getAsJsonObject());
+ } else {
+ throw new JsonSyntaxException(CAN_T_PARSE_VALUE + je);
+ }
+ });
+ } else {
+ throw new JsonSyntaxException(CAN_T_PARSE_VALUE + jsonObject);
+ }
+ return builder.build();
+ }
+
+ public static PostAttributeMsg convertToAttributesProto(JsonElement jsonObject) throws JsonSyntaxException {
+ if (jsonObject.isJsonObject()) {
+ PostAttributeMsg.Builder result = PostAttributeMsg.newBuilder();
+ List<KeyValueProto> keyValueList = parseProtoValues(jsonObject.getAsJsonObject());
+ result.addAllKv(keyValueList);
+ return result.build();
+ } else {
+ throw new JsonSyntaxException(CAN_T_PARSE_VALUE + jsonObject);
+ }
+ }
+
+ public static JsonElement toJson(TransportProtos.ToDeviceRpcRequestMsg msg, boolean includeRequestId) {
+ JsonObject result = new JsonObject();
+ if (includeRequestId) {
+ result.addProperty("id", msg.getRequestId());
+ }
+ result.addProperty("method", msg.getMethodName());
+ result.add("params", new JsonParser().parse(msg.getParams()));
+ return result;
+ }
+
+ private static void parseObject(PostTelemetryMsg.Builder builder, long systemTs, JsonElement jsonObject) {
+ JsonObject jo = jsonObject.getAsJsonObject();
+ if (jo.has("ts") && jo.has("values")) {
+ parseWithTs(builder, jo);
+ } else {
+ parseWithoutTs(builder, systemTs, jo);
+ }
+ }
+
+ private static void parseWithoutTs(PostTelemetryMsg.Builder request, long systemTs, JsonObject jo) {
+ TsKvListProto.Builder builder = TsKvListProto.newBuilder();
+ builder.setTs(systemTs);
+ builder.addAllKv(parseProtoValues(jo));
+ request.addTsKvList(builder.build());
+ }
+
+ private static void parseWithTs(PostTelemetryMsg.Builder request, JsonObject jo) {
+ TsKvListProto.Builder builder = TsKvListProto.newBuilder();
+ builder.setTs(jo.get("ts").getAsLong());
+ builder.addAllKv(parseProtoValues(jo.get("values").getAsJsonObject()));
+ request.addTsKvList(builder.build());
+ }
+
+ private static List<KeyValueProto> parseProtoValues(JsonObject valuesObject) {
+ List<KeyValueProto> result = new ArrayList<>();
+ for (Entry<String, JsonElement> valueEntry : valuesObject.entrySet()) {
+ JsonElement element = valueEntry.getValue();
+ if (element.isJsonPrimitive()) {
+ JsonPrimitive value = element.getAsJsonPrimitive();
+ if (value.isString()) {
+ result.add(KeyValueProto.newBuilder().setKey(valueEntry.getKey()).setType(KeyValueType.STRING_V)
+ .setStringV(value.getAsString()).build());
+ } else if (value.isBoolean()) {
+ result.add(KeyValueProto.newBuilder().setKey(valueEntry.getKey()).setType(KeyValueType.BOOLEAN_V)
+ .setBoolV(value.getAsBoolean()).build());
+ } else if (value.isNumber()) {
+ if (value.getAsString().contains(".")) {
+ result.add(KeyValueProto.newBuilder().setKey(valueEntry.getKey()).setType(KeyValueType.DOUBLE_V)
+ .setDoubleV(value.getAsDouble()).build());
+ } else {
+ try {
+ long longValue = Long.parseLong(value.getAsString());
+ result.add(KeyValueProto.newBuilder().setKey(valueEntry.getKey()).setType(KeyValueType.LONG_V)
+ .setLongV(longValue).build());
+ } catch (NumberFormatException e) {
+ throw new JsonSyntaxException("Big integer values are not supported!");
+ }
+ }
+ } else {
+ throw new JsonSyntaxException(CAN_T_PARSE_VALUE + value);
+ }
+ } else {
+ throw new JsonSyntaxException(CAN_T_PARSE_VALUE + element);
+ }
+ }
+ return result;
+ }
+
+ public static TransportProtos.ToServerRpcRequestMsg convertToServerRpcRequest(JsonElement json, int requestId) throws JsonSyntaxException {
+ JsonObject object = json.getAsJsonObject();
+ return TransportProtos.ToServerRpcRequestMsg.newBuilder().setRequestId(requestId).setMethodName(object.get("method").getAsString()).setParams(GSON.toJson(object.get("params"))).build();
+ }
+
+ private static void parseNumericValue(List<KvEntry> result, Entry<String, JsonElement> valueEntry, JsonPrimitive value) {
+ if (value.getAsString().contains(".")) {
+ result.add(new DoubleDataEntry(valueEntry.getKey(), value.getAsDouble()));
+ } else {
+ try {
+ long longValue = Long.parseLong(value.getAsString());
+ result.add(new LongDataEntry(valueEntry.getKey(), longValue));
+ } catch (NumberFormatException e) {
+ throw new JsonSyntaxException("Big integer values are not supported!");
+ }
+ }
+ }
+
+ public static JsonObject toJson(GetAttributeResponseMsg payload) {
+ JsonObject result = new JsonObject();
+ if (payload.getClientAttributeListCount() > 0) {
+ JsonObject attrObject = new JsonObject();
+ payload.getClientAttributeListList().forEach(addToObjectFromProto(attrObject));
+ result.add("client", attrObject);
+ }
+ if (payload.getSharedAttributeListCount() > 0) {
+ JsonObject attrObject = new JsonObject();
+ payload.getSharedAttributeListList().forEach(addToObjectFromProto(attrObject));
+ result.add("shared", attrObject);
+ }
+ if (payload.getDeletedAttributeKeysCount() > 0) {
+ JsonArray attrObject = new JsonArray();
+ payload.getDeletedAttributeKeysList().forEach(attrObject::add);
+ result.add("deleted", attrObject);
+ }
+ return result;
+ }
+
+ public static JsonElement toJson(AttributeUpdateNotificationMsg payload) {
+ JsonObject result = new JsonObject();
+ if (payload.getSharedUpdatedCount() > 0) {
+ payload.getSharedUpdatedList().forEach(addToObjectFromProto(result));
+ }
+ if (payload.getSharedDeletedCount() > 0) {
+ JsonArray attrObject = new JsonArray();
+ payload.getSharedDeletedList().forEach(attrObject::add);
+ result.add("deleted", attrObject);
+ }
+ return result;
+ }
+
+ public static JsonObject toJson(AttributesKVMsg payload, boolean asMap) {
+ JsonObject result = new JsonObject();
+ if (asMap) {
+ if (!payload.getClientAttributes().isEmpty()) {
+ JsonObject attrObject = new JsonObject();
+ payload.getClientAttributes().forEach(addToObject(attrObject));
+ result.add("client", attrObject);
+ }
+ if (!payload.getSharedAttributes().isEmpty()) {
+ JsonObject attrObject = new JsonObject();
+ payload.getSharedAttributes().forEach(addToObject(attrObject));
+ result.add("shared", attrObject);
+ }
+ } else {
+ payload.getClientAttributes().forEach(addToObject(result));
+ payload.getSharedAttributes().forEach(addToObject(result));
+ }
+ if (!payload.getDeletedAttributes().isEmpty()) {
+ JsonArray attrObject = new JsonArray();
+ payload.getDeletedAttributes().forEach(addToObject(attrObject));
+ result.add("deleted", attrObject);
+ }
+ return result;
+ }
+
+ public static JsonObject getJsonObjectForGateway(TransportProtos.GetAttributeResponseMsg responseMsg) {
+ JsonObject result = new JsonObject();
+ result.addProperty("id", responseMsg.getRequestId());
+ if (responseMsg.getClientAttributeListCount() > 0) {
+ addValues(result, responseMsg.getClientAttributeListList());
+ }
+ if (responseMsg.getSharedAttributeListCount() > 0) {
+ addValues(result, responseMsg.getSharedAttributeListList());
+ }
+ return result;
+ }
+
+ public static JsonObject getJsonObjectForGateway(String deviceName, AttributeUpdateNotificationMsg notificationMsg) {
+ JsonObject result = new JsonObject();
+ result.addProperty(DEVICE_PROPERTY, deviceName);
+ result.add("data", toJson(notificationMsg));
+ return result;
+ }
+
+ private static void addValues(JsonObject result, List<TransportProtos.TsKvProto> kvList) {
+ if (kvList.size() == 1) {
+ addValueToJson(result, "value", kvList.get(0).getKv());
+ } else {
+ JsonObject values;
+ if (result.has("values")) {
+ values = result.get("values").getAsJsonObject();
+ } else {
+ values = new JsonObject();
+ result.add("values", values);
+ }
+ kvList.forEach(value -> addValueToJson(values, value.getKv().getKey(), value.getKv()));
+ }
+ }
+
+ private static void addValueToJson(JsonObject json, String name, TransportProtos.KeyValueProto entry) {
+ switch (entry.getType()) {
+ case BOOLEAN_V:
+ json.addProperty(name, entry.getBoolV());
+ break;
+ case STRING_V:
+ json.addProperty(name, entry.getStringV());
+ break;
+ case DOUBLE_V:
+ json.addProperty(name, entry.getDoubleV());
+ break;
+ case LONG_V:
+ json.addProperty(name, entry.getLongV());
+ break;
+ }
+ }
+
+ private static Consumer<AttributeKey> addToObject(JsonArray result) {
+ return key -> result.add(key.getAttributeKey());
+ }
+
+ private static Consumer<TsKvProto> addToObjectFromProto(JsonObject result) {
+ return de -> {
+ JsonPrimitive value;
+ switch (de.getKv().getType()) {
+ case BOOLEAN_V:
+ value = new JsonPrimitive(de.getKv().getBoolV());
+ break;
+ case DOUBLE_V:
+ value = new JsonPrimitive(de.getKv().getDoubleV());
+ break;
+ case LONG_V:
+ value = new JsonPrimitive(de.getKv().getLongV());
+ break;
+ case STRING_V:
+ value = new JsonPrimitive(de.getKv().getStringV());
+ break;
+ default:
+ throw new IllegalArgumentException("Unsupported data type: " + de.getKv().getType());
+ }
+ result.add(de.getKv().getKey(), value);
+ };
+ }
+
+ private static Consumer<AttributeKvEntry> addToObject(JsonObject result) {
+ return de -> {
+ JsonPrimitive value;
+ switch (de.getDataType()) {
+ case BOOLEAN:
+ value = new JsonPrimitive(de.getBooleanValue().get());
+ break;
+ case DOUBLE:
+ value = new JsonPrimitive(de.getDoubleValue().get());
+ break;
+ case LONG:
+ value = new JsonPrimitive(de.getLongValue().get());
+ break;
+ case STRING:
+ value = new JsonPrimitive(de.getStrValue().get());
+ break;
+ default:
+ throw new IllegalArgumentException("Unsupported data type: " + de.getDataType());
+ }
+ result.add(de.getKey(), value);
+ };
+ }
+
+ public static JsonElement toJson(TransportProtos.ToServerRpcResponseMsg msg) {
+ if (StringUtils.isEmpty(msg.getError())) {
+ return new JsonParser().parse(msg.getPayload());
+ } else {
+ JsonObject errorMsg = new JsonObject();
+ errorMsg.addProperty("error", msg.getError());
+ return errorMsg;
+ }
+ }
+
+ public static JsonElement toErrorJson(String errorMsg) {
+ JsonObject error = new JsonObject();
+ error.addProperty("error", errorMsg);
+ return error;
+ }
+
+ public static JsonElement toGatewayJson(String deviceName, TransportProtos.ToDeviceRpcRequestMsg rpcRequest) {
+ JsonObject result = new JsonObject();
+ result.addProperty(DEVICE_PROPERTY, deviceName);
+ result.add("data", JsonConverter.toJson(rpcRequest, true));
+ return result;
+ }
+
+ public static Set<AttributeKvEntry> convertToAttributes(JsonElement element) {
+ Set<AttributeKvEntry> result = new HashSet<>();
+ long ts = System.currentTimeMillis();
+ result.addAll(parseValues(element.getAsJsonObject()).stream().map(kv -> new BaseAttributeKvEntry(kv, ts)).collect(Collectors.toList()));
+ return result;
+ }
+
+ private static List<KvEntry> parseValues(JsonObject valuesObject) {
+ List<KvEntry> result = new ArrayList<>();
+ for (Entry<String, JsonElement> valueEntry : valuesObject.entrySet()) {
+ JsonElement element = valueEntry.getValue();
+ if (element.isJsonPrimitive()) {
+ JsonPrimitive value = element.getAsJsonPrimitive();
+ if (value.isString()) {
+ result.add(new StringDataEntry(valueEntry.getKey(), value.getAsString()));
+ } else if (value.isBoolean()) {
+ result.add(new BooleanDataEntry(valueEntry.getKey(), value.getAsBoolean()));
+ } else if (value.isNumber()) {
+ parseNumericValue(result, valueEntry, value);
+ } else {
+ throw new JsonSyntaxException(CAN_T_PARSE_VALUE + value);
+ }
+ } else {
+ throw new JsonSyntaxException(CAN_T_PARSE_VALUE + element);
+ }
+ }
+ return result;
+ }
+
+ public static Map<Long, List<KvEntry>> convertToTelemetry(JsonElement jsonObject, long systemTs) throws JsonSyntaxException {
+ Map<Long, List<KvEntry>> result = new HashMap<>();
+ if (jsonObject.isJsonObject()) {
+ parseObject(result, systemTs, jsonObject);
+ } else if (jsonObject.isJsonArray()) {
+ jsonObject.getAsJsonArray().forEach(je -> {
+ if (je.isJsonObject()) {
+ parseObject(result, systemTs, je.getAsJsonObject());
+ } else {
+ throw new JsonSyntaxException(CAN_T_PARSE_VALUE + je);
+ }
+ });
+ } else {
+ throw new JsonSyntaxException(CAN_T_PARSE_VALUE + jsonObject);
+ }
+ return result;
+ }
+
+ private static void parseObject(Map<Long, List<KvEntry>> result, long systemTs, JsonElement jsonObject) {
+ JsonObject jo = jsonObject.getAsJsonObject();
+ if (jo.has("ts") && jo.has("values")) {
+ parseWithTs(result, jo);
+ } else {
+ parseWithoutTs(result, systemTs, jo);
+ }
+ }
+
+ private static void parseWithoutTs(Map<Long, List<KvEntry>> result, long systemTs, JsonObject jo) {
+ for (KvEntry entry : parseValues(jo)) {
+ result.computeIfAbsent(systemTs, tmp -> new ArrayList<>()).add(entry);
+ }
+ }
+
+ public static void parseWithTs(Map<Long, List<KvEntry>> result, JsonObject jo) {
+ long ts = jo.get("ts").getAsLong();
+ JsonObject valuesObject = jo.get("values").getAsJsonObject();
+ for (KvEntry entry : parseValues(valuesObject)) {
+ result.computeIfAbsent(ts, tmp -> new ArrayList<>()).add(entry);
+ }
+ }
+
+
+}
diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/AbstractTransportService.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/AbstractTransportService.java
new file mode 100644
index 0000000..e299a02
--- /dev/null
+++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/AbstractTransportService.java
@@ -0,0 +1,116 @@
+/**
+ * Copyright © 2016-2018 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.common.transport.service;
+
+import lombok.extern.slf4j.Slf4j;
+import org.thingsboard.server.common.transport.SessionMsgListener;
+import org.thingsboard.server.common.transport.TransportService;
+import org.thingsboard.server.gen.transport.TransportProtos;
+
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.SynchronousQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Created by ashvayka on 17.10.18.
+ */
+@Slf4j
+public abstract class AbstractTransportService implements TransportService {
+
+ protected ScheduledExecutorService schedulerExecutor;
+ protected ExecutorService transportCallbackExecutor;
+ protected ConcurrentMap<UUID, SessionMetaData> sessions = new ConcurrentHashMap<>();
+
+ @Override
+ public void registerAsyncSession(TransportProtos.SessionInfoProto sessionInfo, SessionMsgListener listener) {
+ sessions.putIfAbsent(toId(sessionInfo), new SessionMetaData(sessionInfo, TransportProtos.SessionType.ASYNC, listener));
+ //TODO: monitor sessions periodically: PING REQ/RESP, etc.
+ }
+
+ @Override
+ public void registerSyncSession(TransportProtos.SessionInfoProto sessionInfo, SessionMsgListener listener, long timeout) {
+ sessions.putIfAbsent(toId(sessionInfo), new SessionMetaData(sessionInfo, TransportProtos.SessionType.SYNC, listener));
+ schedulerExecutor.schedule(() -> {
+ listener.onRemoteSessionCloseCommand(TransportProtos.SessionCloseNotificationProto.getDefaultInstance());
+ deregisterSession(sessionInfo);
+ }, timeout, TimeUnit.MILLISECONDS);
+
+ }
+
+ @Override
+ public void deregisterSession(TransportProtos.SessionInfoProto sessionInfo) {
+ sessions.remove(toId(sessionInfo));
+ }
+
+ protected void processToTransportMsg(TransportProtos.DeviceActorToTransportMsg toSessionMsg) {
+ UUID sessionId = new UUID(toSessionMsg.getSessionIdMSB(), toSessionMsg.getSessionIdLSB());
+ SessionMetaData md = sessions.get(sessionId);
+ if (md != null) {
+ SessionMsgListener listener = md.getListener();
+ transportCallbackExecutor.submit(() -> {
+ if (toSessionMsg.hasGetAttributesResponse()) {
+ listener.onGetAttributesResponse(toSessionMsg.getGetAttributesResponse());
+ }
+ if (toSessionMsg.hasAttributeUpdateNotification()) {
+ listener.onAttributeUpdate(toSessionMsg.getAttributeUpdateNotification());
+ }
+ if (toSessionMsg.hasSessionCloseNotification()) {
+ listener.onRemoteSessionCloseCommand(toSessionMsg.getSessionCloseNotification());
+ }
+ if (toSessionMsg.hasToDeviceRequest()) {
+ listener.onToDeviceRpcRequest(toSessionMsg.getToDeviceRequest());
+ }
+ if (toSessionMsg.hasToServerResponse()) {
+ listener.onToServerRpcResponse(toSessionMsg.getToServerResponse());
+ }
+ });
+ if (md.getSessionType() == TransportProtos.SessionType.SYNC) {
+ deregisterSession(md.getSessionInfo());
+ }
+ } else {
+ //TODO: should we notify the device actor about missed session?
+ log.debug("[{}] Missing session.", sessionId);
+ }
+ }
+
+ protected UUID toId(TransportProtos.SessionInfoProto sessionInfo) {
+ return new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB());
+ }
+
+ String getRoutingKey(TransportProtos.SessionInfoProto sessionInfo) {
+ return new UUID(sessionInfo.getDeviceIdMSB(), sessionInfo.getDeviceIdLSB()).toString();
+ }
+
+ public void init() {
+ this.schedulerExecutor = Executors.newSingleThreadScheduledExecutor();
+ this.transportCallbackExecutor = new ThreadPoolExecutor(0, 20, 60L, TimeUnit.SECONDS, new SynchronousQueue<>());
+ }
+
+ public void destroy() {
+ if (schedulerExecutor != null) {
+ schedulerExecutor.shutdownNow();
+ }
+ if (transportCallbackExecutor != null) {
+ transportCallbackExecutor.shutdownNow();
+ }
+ }
+}
diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/RemoteTransportService.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/RemoteTransportService.java
new file mode 100644
index 0000000..aa3b42b
--- /dev/null
+++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/RemoteTransportService.java
@@ -0,0 +1,315 @@
+/**
+ * Copyright © 2016-2018 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.common.transport.service;
+
+import lombok.extern.slf4j.Slf4j;
+import org.apache.kafka.clients.admin.CreateTopicsResult;
+import org.apache.kafka.clients.admin.NewTopic;
+import org.apache.kafka.clients.consumer.ConsumerRecords;
+import org.apache.kafka.clients.producer.Callback;
+import org.apache.kafka.clients.producer.RecordMetadata;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.stereotype.Service;
+import org.thingsboard.server.common.transport.SessionMsgListener;
+import org.thingsboard.server.common.transport.TransportService;
+import org.thingsboard.server.common.transport.TransportServiceCallback;
+import org.thingsboard.server.gen.transport.TransportProtos;
+import org.thingsboard.server.gen.transport.TransportProtos.*;
+import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFromGatewayRequestMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFromGatewayResponseMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.PostAttributeMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.PostTelemetryMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.SessionEventMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto;
+import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.TransportApiRequestMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.TransportApiResponseMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceCredentialsResponseMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceTokenRequestMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceX509CertRequestMsg;
+import org.thingsboard.server.kafka.*;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
+import java.time.Duration;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+/**
+ * Created by ashvayka on 05.10.18.
+ */
+@ConditionalOnExpression("'${transport.type:null}'=='null'")
+@Service
+@Slf4j
+public class RemoteTransportService extends AbstractTransportService {
+
+ @Value("${kafka.rule_engine.topic}")
+ private String ruleEngineTopic;
+ @Value("${kafka.notifications.topic}")
+ private String notificationsTopic;
+ @Value("${kafka.notifications.poll_interval}")
+ private int notificationsPollDuration;
+ @Value("${kafka.notifications.auto_commit_interval}")
+ private int notificationsAutoCommitInterval;
+ @Value("${kafka.transport_api.requests_topic}")
+ private String transportApiRequestsTopic;
+ @Value("${kafka.transport_api.responses_topic}")
+ private String transportApiResponsesTopic;
+ @Value("${kafka.transport_api.max_pending_requests}")
+ private long maxPendingRequests;
+ @Value("${kafka.transport_api.max_requests_timeout}")
+ private long maxRequestsTimeout;
+ @Value("${kafka.transport_api.response_poll_interval}")
+ private int responsePollDuration;
+ @Value("${kafka.transport_api.response_auto_commit_interval}")
+ private int autoCommitInterval;
+
+ @Autowired
+ private TbKafkaSettings kafkaSettings;
+ //We use this to get the node id. We should replace this with a component that provides the node id.
+ @Autowired
+ private TbNodeIdProvider nodeIdProvider;
+
+ private TbKafkaRequestTemplate<TransportApiRequestMsg, TransportApiResponseMsg> transportApiTemplate;
+ private TBKafkaProducerTemplate<ToRuleEngineMsg> ruleEngineProducer;
+ private TBKafkaConsumerTemplate<ToTransportMsg> mainConsumer;
+
+ private ExecutorService mainConsumerExecutor = Executors.newSingleThreadExecutor();
+
+ private volatile boolean stopped = false;
+
+ @PostConstruct
+ public void init() {
+ super.init();
+
+ TBKafkaProducerTemplate.TBKafkaProducerTemplateBuilder<TransportApiRequestMsg> requestBuilder = TBKafkaProducerTemplate.builder();
+ requestBuilder.settings(kafkaSettings);
+ requestBuilder.defaultTopic(transportApiRequestsTopic);
+ requestBuilder.encoder(new TransportApiRequestEncoder());
+
+ TBKafkaConsumerTemplate.TBKafkaConsumerTemplateBuilder<TransportApiResponseMsg> responseBuilder = TBKafkaConsumerTemplate.builder();
+ responseBuilder.settings(kafkaSettings);
+ responseBuilder.topic(transportApiResponsesTopic + "." + nodeIdProvider.getNodeId());
+ responseBuilder.clientId("transport-api-client-" + nodeIdProvider.getNodeId());
+ responseBuilder.groupId("transport-api-client");
+ responseBuilder.autoCommit(true);
+ responseBuilder.autoCommitIntervalMs(autoCommitInterval);
+ responseBuilder.decoder(new TransportApiResponseDecoder());
+
+ TbKafkaRequestTemplate.TbKafkaRequestTemplateBuilder
+ <TransportApiRequestMsg, TransportApiResponseMsg> builder = TbKafkaRequestTemplate.builder();
+ builder.requestTemplate(requestBuilder.build());
+ builder.responseTemplate(responseBuilder.build());
+ builder.maxPendingRequests(maxPendingRequests);
+ builder.maxRequestTimeout(maxRequestsTimeout);
+ builder.pollInterval(responsePollDuration);
+ transportApiTemplate = builder.build();
+ transportApiTemplate.init();
+
+ TBKafkaProducerTemplate.TBKafkaProducerTemplateBuilder<ToRuleEngineMsg> ruleEngineProducerBuilder = TBKafkaProducerTemplate.builder();
+ ruleEngineProducerBuilder.settings(kafkaSettings);
+ ruleEngineProducerBuilder.defaultTopic(ruleEngineTopic);
+ ruleEngineProducerBuilder.encoder(new ToRuleEngineMsgEncoder());
+ ruleEngineProducer = ruleEngineProducerBuilder.build();
+ ruleEngineProducer.init();
+
+ String notificationsTopicName = notificationsTopic + "." + nodeIdProvider.getNodeId();
+
+ try {
+ TBKafkaAdmin admin = new TBKafkaAdmin(kafkaSettings);
+ CreateTopicsResult result = admin.createTopic(new NewTopic(notificationsTopicName, 1, (short) 1));
+ result.all().get();
+ } catch (Exception e) {
+ log.trace("Failed to create topic: {}", e.getMessage(), e);
+ }
+
+ TBKafkaConsumerTemplate.TBKafkaConsumerTemplateBuilder<ToTransportMsg> mainConsumerBuilder = TBKafkaConsumerTemplate.builder();
+ mainConsumerBuilder.settings(kafkaSettings);
+ mainConsumerBuilder.topic(notificationsTopicName);
+ mainConsumerBuilder.clientId("transport-" + nodeIdProvider.getNodeId());
+ mainConsumerBuilder.groupId("transport");
+ mainConsumerBuilder.autoCommit(true);
+ mainConsumerBuilder.autoCommitIntervalMs(notificationsAutoCommitInterval);
+ mainConsumerBuilder.decoder(new ToTransportMsgResponseDecoder());
+ mainConsumer = mainConsumerBuilder.build();
+ mainConsumer.subscribe();
+
+ mainConsumerExecutor.execute(() -> {
+ while (!stopped) {
+ try {
+ ConsumerRecords<String, byte[]> records = mainConsumer.poll(Duration.ofMillis(notificationsPollDuration));
+ records.forEach(record -> {
+ try {
+ ToTransportMsg toTransportMsg = mainConsumer.decode(record);
+ if (toTransportMsg.hasToDeviceSessionMsg()) {
+ processToTransportMsg(toTransportMsg.getToDeviceSessionMsg());
+ }
+ } catch (Throwable e) {
+ log.warn("Failed to process the notification.", e);
+ }
+ });
+ } catch (Exception e) {
+ log.warn("Failed to obtain messages from queue.", e);
+ try {
+ Thread.sleep(notificationsPollDuration);
+ } catch (InterruptedException e2) {
+ log.trace("Failed to wait until the server has capacity to handle new requests", e2);
+ }
+ }
+ }
+ });
+ }
+
+ @PreDestroy
+ public void destroy() {
+ super.destroy();
+ stopped = true;
+ if (transportApiTemplate != null) {
+ transportApiTemplate.stop();
+ }
+ if (mainConsumer != null) {
+ mainConsumer.unsubscribe();
+ }
+ if (mainConsumerExecutor != null) {
+ mainConsumerExecutor.shutdownNow();
+ }
+ }
+
+ @Override
+ public void process(ValidateDeviceTokenRequestMsg msg, TransportServiceCallback<ValidateDeviceCredentialsResponseMsg> callback) {
+ AsyncCallbackTemplate.withCallback(transportApiTemplate.post(msg.getToken(),
+ TransportApiRequestMsg.newBuilder().setValidateTokenRequestMsg(msg).build()),
+ response -> callback.onSuccess(response.getValidateTokenResponseMsg()), callback::onError, transportCallbackExecutor);
+ }
+
+ @Override
+ public void process(ValidateDeviceX509CertRequestMsg msg, TransportServiceCallback<ValidateDeviceCredentialsResponseMsg> callback) {
+ AsyncCallbackTemplate.withCallback(transportApiTemplate.post(msg.getHash(),
+ TransportApiRequestMsg.newBuilder().setValidateX509CertRequestMsg(msg).build()),
+ response -> callback.onSuccess(response.getValidateTokenResponseMsg()), callback::onError, transportCallbackExecutor);
+ }
+
+ @Override
+ public void process(GetOrCreateDeviceFromGatewayRequestMsg msg, TransportServiceCallback<GetOrCreateDeviceFromGatewayResponseMsg> callback) {
+ AsyncCallbackTemplate.withCallback(transportApiTemplate.post(msg.getDeviceName(),
+ TransportApiRequestMsg.newBuilder().setGetOrCreateDeviceRequestMsg(msg).build()),
+ response -> callback.onSuccess(response.getGetOrCreateDeviceResponseMsg()), callback::onError, transportCallbackExecutor);
+ }
+
+ @Override
+ public void process(SessionInfoProto sessionInfo, SessionEventMsg msg, TransportServiceCallback<Void> callback) {
+ ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg(
+ TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo)
+ .setSessionEvent(msg).build()
+ ).build();
+ send(sessionInfo, toRuleEngineMsg, callback);
+ }
+
+ @Override
+ public void process(SessionInfoProto sessionInfo, PostTelemetryMsg msg, TransportServiceCallback<Void> callback) {
+ ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg(
+ TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo)
+ .setPostTelemetry(msg).build()
+ ).build();
+ send(sessionInfo, toRuleEngineMsg, callback);
+ }
+
+ @Override
+ public void process(SessionInfoProto sessionInfo, PostAttributeMsg msg, TransportServiceCallback<Void> callback) {
+ ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg(
+ TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo)
+ .setPostAttributes(msg).build()
+ ).build();
+ send(sessionInfo, toRuleEngineMsg, callback);
+ }
+
+ @Override
+ public void process(SessionInfoProto sessionInfo, GetAttributeRequestMsg msg, TransportServiceCallback<Void> callback) {
+ ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg(
+ TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo)
+ .setGetAttributes(msg).build()
+ ).build();
+ send(sessionInfo, toRuleEngineMsg, callback);
+ }
+
+ @Override
+ public void process(SessionInfoProto sessionInfo, SubscribeToAttributeUpdatesMsg msg, TransportServiceCallback<Void> callback) {
+ ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg(
+ TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo)
+ .setSubscribeToAttributes(msg).build()
+ ).build();
+ send(sessionInfo, toRuleEngineMsg, callback);
+ }
+
+ @Override
+ public void process(SessionInfoProto sessionInfo, SubscribeToRPCMsg msg, TransportServiceCallback<Void> callback) {
+ ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg(
+ TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo)
+ .setSubscribeToRPC(msg).build()
+ ).build();
+ send(sessionInfo, toRuleEngineMsg, callback);
+ }
+
+ @Override
+ public void process(SessionInfoProto sessionInfo, ToDeviceRpcResponseMsg msg, TransportServiceCallback<Void> callback) {
+ ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg(
+ TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo)
+ .setToDeviceRPCCallResponse(msg).build()
+ ).build();
+ send(sessionInfo, toRuleEngineMsg, callback);
+ }
+
+ @Override
+ public void process(SessionInfoProto sessionInfo, ToServerRpcRequestMsg msg, TransportServiceCallback<Void> callback) {
+ ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg(
+ TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo)
+ .setToServerRPCCallRequest(msg).build()
+ ).build();
+ send(sessionInfo, toRuleEngineMsg, callback);
+ }
+
+ private static class TransportCallbackAdaptor implements Callback {
+ private final TransportServiceCallback<Void> callback;
+
+ TransportCallbackAdaptor(TransportServiceCallback<Void> callback) {
+ this.callback = callback;
+ }
+
+ @Override
+ public void onCompletion(RecordMetadata metadata, Exception exception) {
+ if (exception == null) {
+ if (callback != null) {
+ callback.onSuccess(null);
+ }
+ } else {
+ if (callback != null) {
+ callback.onError(exception);
+ }
+ }
+ }
+ }
+
+ private void send(SessionInfoProto sessionInfo, ToRuleEngineMsg toRuleEngineMsg, TransportServiceCallback<Void> callback) {
+ ruleEngineProducer.send(getRoutingKey(sessionInfo), toRuleEngineMsg, new TransportCallbackAdaptor(callback));
+ }
+}
diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportContext.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportContext.java
new file mode 100644
index 0000000..ab42982
--- /dev/null
+++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportContext.java
@@ -0,0 +1,68 @@
+/**
+ * Copyright © 2016-2018 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.common.transport;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import lombok.Data;
+import lombok.Getter;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.thingsboard.server.common.transport.quota.host.HostRequestsQuotaService;
+import org.thingsboard.server.kafka.TbNodeIdProvider;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+/**
+ * Created by ashvayka on 15.10.18.
+ */
+@Slf4j
+@Data
+public class TransportContext {
+
+ protected final ObjectMapper mapper = new ObjectMapper();
+
+ @Autowired
+ private TransportService transportService;
+
+ @Autowired
+ private TbNodeIdProvider nodeIdProvider;
+
+ @Autowired(required = false)
+ private HostRequestsQuotaService quotaService;
+
+ @Getter
+ private ExecutorService executor;
+
+ @PostConstruct
+ public void init() {
+ executor = Executors.newCachedThreadPool();
+ }
+
+ @PreDestroy
+ public void stop() {
+ if (executor != null) {
+ executor.shutdownNow();
+ }
+ }
+
+ public String getNodeId() {
+ return nodeIdProvider.getNodeId();
+ }
+
+}
diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportService.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportService.java
new file mode 100644
index 0000000..caf178a
--- /dev/null
+++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportService.java
@@ -0,0 +1,68 @@
+/**
+ * Copyright © 2016-2018 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.common.transport;
+
+import org.thingsboard.server.gen.transport.TransportProtos;
+import org.thingsboard.server.gen.transport.TransportProtos.ToServerRpcRequestMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcResponseMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.SubscribeToAttributeUpdatesMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.SubscribeToRPCMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto;
+import org.thingsboard.server.gen.transport.TransportProtos.PostAttributeMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.PostTelemetryMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.SessionEventMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceCredentialsResponseMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceTokenRequestMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceX509CertRequestMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeRequestMsg;
+
+/**
+ * Created by ashvayka on 04.10.18.
+ */
+public interface TransportService {
+
+ void process(ValidateDeviceTokenRequestMsg msg,
+ TransportServiceCallback<ValidateDeviceCredentialsResponseMsg> callback);
+
+ void process(ValidateDeviceX509CertRequestMsg msg,
+ TransportServiceCallback<ValidateDeviceCredentialsResponseMsg> callback);
+
+ void process(TransportProtos.GetOrCreateDeviceFromGatewayRequestMsg msg,
+ TransportServiceCallback<TransportProtos.GetOrCreateDeviceFromGatewayResponseMsg> callback);
+
+ void process(SessionInfoProto sessionInfo, SessionEventMsg msg, TransportServiceCallback<Void> callback);
+
+ void process(SessionInfoProto sessionInfo, PostTelemetryMsg msg, TransportServiceCallback<Void> callback);
+
+ void process(SessionInfoProto sessionInfo, PostAttributeMsg msg, TransportServiceCallback<Void> callback);
+
+ void process(SessionInfoProto sessionInfo, GetAttributeRequestMsg msg, TransportServiceCallback<Void> callback);
+
+ void process(SessionInfoProto sessionInfo, SubscribeToAttributeUpdatesMsg msg, TransportServiceCallback<Void> callback);
+
+ void process(SessionInfoProto sessionInfo, SubscribeToRPCMsg msg, TransportServiceCallback<Void> callback);
+
+ void process(SessionInfoProto sessionInfo, ToDeviceRpcResponseMsg msg, TransportServiceCallback<Void> callback);
+
+ void process(SessionInfoProto sessionInfo, ToServerRpcRequestMsg msg, TransportServiceCallback<Void> callback);
+
+ void registerAsyncSession(SessionInfoProto sessionInfo, SessionMsgListener listener);
+
+ void registerSyncSession(SessionInfoProto sessionInfo, SessionMsgListener listener, long timeout);
+
+ void deregisterSession(SessionInfoProto sessionInfo);
+
+}
diff --git a/common/transport/transport-api/src/main/proto/transport.proto b/common/transport/transport-api/src/main/proto/transport.proto
new file mode 100644
index 0000000..0e36dab
--- /dev/null
+++ b/common/transport/transport-api/src/main/proto/transport.proto
@@ -0,0 +1,217 @@
+/**
+ * Copyright © 2016-2018 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+syntax = "proto3";
+package transport;
+
+option java_package = "org.thingsboard.server.gen.transport";
+option java_outer_classname = "TransportProtos";
+
+/**
+ * Data Structures;
+ */
+message SessionInfoProto {
+ string nodeId = 1;
+ int64 sessionIdMSB = 2;
+ int64 sessionIdLSB = 3;
+ int64 tenantIdMSB = 4;
+ int64 tenantIdLSB = 5;
+ int64 deviceIdMSB = 6;
+ int64 deviceIdLSB = 7;
+}
+
+enum SessionEvent {
+ OPEN = 0;
+ CLOSED = 1;
+}
+
+enum SessionType {
+ SYNC = 0;
+ ASYNC = 1;
+}
+
+enum KeyValueType {
+ BOOLEAN_V = 0;
+ LONG_V = 1;
+ DOUBLE_V = 2;
+ STRING_V = 3;
+}
+
+message KeyValueProto {
+ string key = 1;
+ KeyValueType type = 2;
+ bool bool_v = 3;
+ int64 long_v = 4;
+ double double_v = 5;
+ string string_v = 6;
+}
+
+message TsKvProto {
+ int64 ts = 1;
+ KeyValueProto kv = 2;
+}
+
+message TsKvListProto {
+ int64 ts = 1;
+ repeated KeyValueProto kv = 2;
+}
+
+message DeviceInfoProto {
+ int64 tenantIdMSB = 1;
+ int64 tenantIdLSB = 2;
+ int64 deviceIdMSB = 3;
+ int64 deviceIdLSB = 4;
+ string deviceName = 5;
+ string deviceType = 6;
+ string additionalInfo = 7;
+}
+
+/**
+ * Messages that use Data Structures;
+ */
+message SessionEventMsg {
+ SessionType sessionType = 1;
+ SessionEvent event = 2;
+}
+
+message PostTelemetryMsg {
+ repeated TsKvListProto tsKvList = 1;
+}
+
+message PostAttributeMsg {
+ repeated KeyValueProto kv = 1;
+}
+
+message GetAttributeRequestMsg {
+ int32 requestId = 1;
+ repeated string clientAttributeNames = 2;
+ repeated string sharedAttributeNames = 3;
+}
+
+message GetAttributeResponseMsg {
+ int32 requestId = 1;
+ repeated TsKvProto clientAttributeList = 2;
+ repeated TsKvProto sharedAttributeList = 3;
+ repeated string deletedAttributeKeys = 4;
+ string error = 5;
+}
+
+message AttributeUpdateNotificationMsg {
+ repeated TsKvProto sharedUpdated = 1;
+ repeated string sharedDeleted = 2;
+}
+
+message ValidateDeviceTokenRequestMsg {
+ string token = 1;
+}
+
+message ValidateDeviceX509CertRequestMsg {
+ string hash = 1;
+}
+
+message ValidateDeviceCredentialsResponseMsg {
+ DeviceInfoProto deviceInfo = 1;
+ string credentialsBody = 2;
+}
+
+message GetOrCreateDeviceFromGatewayRequestMsg {
+ int64 gatewayIdMSB = 1;
+ int64 gatewayIdLSB = 2;
+ string deviceName = 3;
+ string deviceType = 4;
+}
+
+message GetOrCreateDeviceFromGatewayResponseMsg {
+ DeviceInfoProto deviceInfo = 1;
+}
+
+message SessionCloseNotificationProto {
+ string message = 1;
+}
+
+message SubscribeToAttributeUpdatesMsg {
+ bool unsubscribe = 1;
+}
+
+message SubscribeToRPCMsg {
+ bool unsubscribe = 1;
+}
+
+message ToDeviceRpcRequestMsg {
+ int32 requestId = 1;
+ string methodName = 2;
+ string params = 3;
+}
+
+message ToDeviceRpcResponseMsg {
+ int32 requestId = 1;
+ string payload = 2;
+}
+
+message ToServerRpcRequestMsg {
+ int32 requestId = 1;
+ string methodName = 2;
+ string params = 3;
+}
+
+message ToServerRpcResponseMsg {
+ int32 requestId = 1;
+ string payload = 2;
+ string error = 3;
+}
+
+message TransportToDeviceActorMsg {
+ SessionInfoProto sessionInfo = 1;
+ SessionEventMsg sessionEvent = 2;
+ PostTelemetryMsg postTelemetry = 3;
+ PostAttributeMsg postAttributes = 4;
+ GetAttributeRequestMsg getAttributes = 5;
+ SubscribeToAttributeUpdatesMsg subscribeToAttributes = 6;
+ SubscribeToRPCMsg subscribeToRPC = 7;
+ ToDeviceRpcResponseMsg toDeviceRPCCallResponse = 8;
+ ToServerRpcRequestMsg toServerRPCCallRequest = 9;
+}
+
+message DeviceActorToTransportMsg {
+ int64 sessionIdMSB = 1;
+ int64 sessionIdLSB = 2;
+ SessionCloseNotificationProto sessionCloseNotification = 3;
+ GetAttributeResponseMsg getAttributesResponse = 4;
+ AttributeUpdateNotificationMsg attributeUpdateNotification = 5;
+ ToDeviceRpcRequestMsg toDeviceRequest = 6;
+ ToServerRpcResponseMsg toServerResponse = 7;
+}
+
+/**
+ * Main messages;
+ */
+message ToRuleEngineMsg {
+ TransportToDeviceActorMsg toDeviceActorMsg = 1;
+}
+
+message ToTransportMsg {
+ DeviceActorToTransportMsg toDeviceSessionMsg = 1;
+}
+
+message TransportApiRequestMsg {
+ ValidateDeviceTokenRequestMsg validateTokenRequestMsg = 1;
+ ValidateDeviceX509CertRequestMsg validateX509CertRequestMsg = 2;
+ GetOrCreateDeviceFromGatewayRequestMsg getOrCreateDeviceRequestMsg = 3;
+}
+
+message TransportApiResponseMsg {
+ ValidateDeviceCredentialsResponseMsg validateTokenResponseMsg = 1;
+ GetOrCreateDeviceFromGatewayResponseMsg getOrCreateDeviceResponseMsg = 2;
+}
\ No newline at end of file
diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsServiceImpl.java
index 4219b06..4b25f2b 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsServiceImpl.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsServiceImpl.java
@@ -26,7 +26,7 @@ import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.common.data.security.DeviceCredentialsType;
-import org.thingsboard.server.dao.EncryptionUtil;
+import org.thingsboard.server.common.msg.EncryptionUtil;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.service.DataValidator;
msa/docker/.env 5(+4 -1)
diff --git a/msa/docker/.env b/msa/docker/.env
index 018d4ac..3d8faeb 100644
--- a/msa/docker/.env
+++ b/msa/docker/.env
@@ -4,7 +4,10 @@ DOCKER_REPO=local-maven-build
JS_EXECUTOR_DOCKER_NAME=tb-js-executor
TB_NODE_DOCKER_NAME=tb-node
WEB_UI_DOCKER_NAME=tb-web-ui
+MQTT_TRANSPORT_DOCKER_NAME=tb-mqtt-transport
+HTTP_TRANSPORT_DOCKER_NAME=tb-http-transport
+COAP_TRANSPORT_DOCKER_NAME=tb-coap-transport
TB_VERSION=2.2.0-SNAPSHOT
-KAFKA_TOPICS=js.eval.requests:100:1:delete --config=retention.ms=60000 --config=retention.bytes=1073741824
+KAFKA_TOPICS=js.eval.requests:100:1:delete --config=retention.ms=60000 --config=retention.bytes=1073741824,tb.transport.api.requests:30:1:delete --config=retention.ms=60000 --config=retention.bytes=1073741824,tb.rule-engine:30:1
msa/docker/docker-compose.yml 63(+61 -2)
diff --git a/msa/docker/docker-compose.yml b/msa/docker/docker-compose.yml
index 91e6286..e3b56ae 100644
--- a/msa/docker/docker-compose.yml
+++ b/msa/docker/docker-compose.yml
@@ -56,8 +56,6 @@ services:
image: "${DOCKER_REPO}/${TB_NODE_DOCKER_NAME}:${TB_VERSION}"
ports:
- "8080"
- - "1883:1883"
- - "5683:5683/udp"
logging:
driver: "json-file"
options:
@@ -75,6 +73,61 @@ services:
- ./tb-node/log:/var/log/thingsboard
depends_on:
- kafka
+ tb-mqtt-transport1:
+ restart: always
+ image: "${DOCKER_REPO}/${MQTT_TRANSPORT_DOCKER_NAME}:${TB_VERSION}"
+ ports:
+ - "1883"
+ environment:
+ TB_KAFKA_SERVERS: kafka:9092
+ env_file:
+ - tb-mqtt-transport.env
+ depends_on:
+ - kafka
+ tb-mqtt-transport2:
+ restart: always
+ image: "${DOCKER_REPO}/${MQTT_TRANSPORT_DOCKER_NAME}:${TB_VERSION}"
+ ports:
+ - "1883"
+ environment:
+ TB_KAFKA_SERVERS: kafka:9092
+ env_file:
+ - tb-mqtt-transport.env
+ depends_on:
+ - kafka
+ tb-http-transport1:
+ restart: always
+ image: "${DOCKER_REPO}/${HTTP_TRANSPORT_DOCKER_NAME}:${TB_VERSION}"
+ ports:
+ - "8081"
+ environment:
+ TB_KAFKA_SERVERS: kafka:9092
+ env_file:
+ - tb-http-transport.env
+ depends_on:
+ - kafka
+ tb-http-transport2:
+ restart: always
+ image: "${DOCKER_REPO}/${HTTP_TRANSPORT_DOCKER_NAME}:${TB_VERSION}"
+ ports:
+ - "8081"
+ environment:
+ TB_KAFKA_SERVERS: kafka:9092
+ env_file:
+ - tb-http-transport.env
+ depends_on:
+ - kafka
+ tb-coap-transport:
+ restart: always
+ image: "${DOCKER_REPO}/${COAP_TRANSPORT_DOCKER_NAME}:${TB_VERSION}"
+ ports:
+ - "5683:5683/udp"
+ environment:
+ TB_KAFKA_SERVERS: kafka:9092
+ env_file:
+ - tb-coap-transport.env
+ depends_on:
+ - kafka
tb-web-ui1:
restart: always
image: "${DOCKER_REPO}/${WEB_UI_DOCKER_NAME}:${TB_VERSION}"
@@ -106,12 +159,18 @@ services:
ports:
- "80:80"
- "443:443"
+ - "1883:1883"
- "9999:9999"
cap_add:
- NET_ADMIN
environment:
HTTP_PORT: 80
HTTPS_PORT: 443
+ MQTT_PORT: 1883
links:
- tb-web-ui1
- tb-web-ui2
+ - tb-mqtt-transport1
+ - tb-mqtt-transport2
+ - tb-http-transport1
+ - tb-http-transport2
msa/docker/haproxy/config/haproxy.cfg 33(+30 -3)
diff --git a/msa/docker/haproxy/config/haproxy.cfg b/msa/docker/haproxy/config/haproxy.cfg
index 5f17465..cb70552 100644
--- a/msa/docker/haproxy/config/haproxy.cfg
+++ b/msa/docker/haproxy/config/haproxy.cfg
@@ -1,6 +1,9 @@
#HA Proxy Config
global
- maxconn 4096
+ ulimit-n 500000
+ maxconn 99999
+ maxpipes 99999
+ tune.maxaccept 500
log 127.0.0.1 local0
log 127.0.0.1 local1 notice
@@ -13,8 +16,6 @@ global
defaults
- option forwardfor
-
log global
mode http
@@ -30,23 +31,42 @@ listen stats
stats uri /stats
stats auth admin:admin@123
+listen mqtt-in
+ bind *:${MQTT_PORT}
+ mode tcp
+ option clitcpka # For TCP keep-alive
+ timeout client 3h
+ timeout server 3h
+ option tcplog
+ balance leastconn
+ server tbMqtt1 tb-mqtt-transport1:1883 check
+ server tbMqtt2 tb-mqtt-transport2:1883 check
+
frontend http-in
bind *:${HTTP_PORT}
+ option forwardfor
+
reqadd X-Forwarded-Proto:\ http
acl transport_http_acl path_beg /api/v1/
acl letsencrypt_http_acl path_beg /.well-known/acme-challenge/
redirect scheme https if !letsencrypt_http_acl !transport_http_acl
use_backend letsencrypt_http if letsencrypt_http_acl
+ use_backend tb-http-backend if transport_http_acl
default_backend tb-web-backend
frontend https_in
bind *:${HTTPS_PORT} ssl crt /usr/local/etc/haproxy/default.pem crt /usr/local/etc/haproxy/certs.d ciphers ECDHE-RSA-AES256-SHA:RC4-SHA:RC4:HIGH:!MD5:!aNULL:!EDH:!AESGCM
+ option forwardfor
+
reqadd X-Forwarded-Proto:\ https
+ acl transport_http_acl path_beg /api/v1/
+ use_backend tb-http-backend if transport_http_acl
+
default_backend tb-web-backend
backend letsencrypt_http
@@ -59,3 +79,10 @@ backend tb-web-backend
server tbWeb1 tb-web-ui1:8080 check
server tbWeb2 tb-web-ui2:8080 check
http-request set-header X-Forwarded-Port %[dst_port]
+
+backend tb-http-backend
+ balance leastconn
+ option tcp-check
+ option log-health-checks
+ server tbHttp1 tb-http-transport1:8081 check
+ server tbHttp2 tb-http-transport2:8081 check
msa/docker/tb-coap-transport.env 6(+6 -0)
diff --git a/msa/docker/tb-coap-transport.env b/msa/docker/tb-coap-transport.env
new file mode 100644
index 0000000..b3331fe
--- /dev/null
+++ b/msa/docker/tb-coap-transport.env
@@ -0,0 +1,6 @@
+
+COAP_BIND_ADDRESS=0.0.0.0
+COAP_BIND_PORT=5683
+COAP_TIMEOUT=10000
+
+TB_KAFKA_SERVERS=localhost:9092
\ No newline at end of file
msa/docker/tb-http-transport.env 6(+6 -0)
diff --git a/msa/docker/tb-http-transport.env b/msa/docker/tb-http-transport.env
new file mode 100644
index 0000000..33c4791
--- /dev/null
+++ b/msa/docker/tb-http-transport.env
@@ -0,0 +1,6 @@
+
+HTTP_BIND_ADDRESS=0.0.0.0
+HTTP_BIND_PORT=8081
+HTTP_REQUEST_TIMEOUT=60000
+
+TB_KAFKA_SERVERS=localhost:9092
\ No newline at end of file
msa/docker/tb-mqtt-transport.env 6(+6 -0)
diff --git a/msa/docker/tb-mqtt-transport.env b/msa/docker/tb-mqtt-transport.env
new file mode 100644
index 0000000..5ada3f7
--- /dev/null
+++ b/msa/docker/tb-mqtt-transport.env
@@ -0,0 +1,6 @@
+
+MQTT_BIND_ADDRESS=0.0.0.0
+MQTT_BIND_PORT=1883
+MQTT_TIMEOUT=10000
+
+TB_KAFKA_SERVERS=localhost:9092
\ No newline at end of file
msa/docker/tb-node.env 6(+2 -4)
diff --git a/msa/docker/tb-node.env b/msa/docker/tb-node.env
index 874bf75..abeccb1 100644
--- a/msa/docker/tb-node.env
+++ b/msa/docker/tb-node.env
@@ -1,8 +1,6 @@
# ThingsBoard server configuration
-MQTT_BIND_ADDRESS=0.0.0.0
-MQTT_BIND_PORT=1883
-COAP_BIND_ADDRESS=0.0.0.0
-COAP_BIND_PORT=5683
+
+TRANSPORT_TYPE=remote
# type of database to use: sql[DEFAULT] or cassandra
DATABASE_TS_TYPE=sql
msa/pom.xml 1(+1 -0)
diff --git a/msa/pom.xml b/msa/pom.xml
index 21ddb4d..59f954a 100644
--- a/msa/pom.xml
+++ b/msa/pom.xml
@@ -40,6 +40,7 @@
<module>js-executor</module>
<module>web-ui</module>
<module>tb-node</module>
+ <module>transport</module>
</modules>
<build>
msa/transport/coap/docker/Dockerfile 31(+31 -0)
diff --git a/msa/transport/coap/docker/Dockerfile b/msa/transport/coap/docker/Dockerfile
new file mode 100644
index 0000000..dcaef3a
--- /dev/null
+++ b/msa/transport/coap/docker/Dockerfile
@@ -0,0 +1,31 @@
+#
+# Copyright © 2016-2018 The Thingsboard Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+FROM openjdk:8-jre
+
+COPY logback.xml ${pkg.name}.conf start-tb-coap-transport.sh ${pkg.name}.deb /tmp/
+
+RUN chmod a+x /tmp/*.sh \
+ && mv /tmp/start-tb-coap-transport.sh /usr/bin
+
+RUN dpkg -i /tmp/${pkg.name}.deb
+
+RUN update-rc.d ${pkg.name} disable
+
+RUN mv /tmp/logback.xml ${pkg.installFolder}/conf \
+ && mv /tmp/${pkg.name}.conf ${pkg.installFolder}/conf
+
+CMD ["start-tb-coap-transport.sh"]
msa/transport/coap/docker/logback.xml 50(+50 -0)
diff --git a/msa/transport/coap/docker/logback.xml b/msa/transport/coap/docker/logback.xml
new file mode 100644
index 0000000..e9d8692
--- /dev/null
+++ b/msa/transport/coap/docker/logback.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+
+ Copyright © 2016-2018 The Thingsboard Authors
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+<!DOCTYPE configuration>
+<configuration scan="true" scanPeriod="10 seconds">
+
+ <appender name="fileLogAppender"
+ class="ch.qos.logback.core.rolling.RollingFileAppender">
+ <file>/var/log/${pkg.name}/${pkg.name}.log</file>
+ <rollingPolicy
+ class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
+ <fileNamePattern>/var/log/${pkg.name}/${pkg.name}.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
+ <maxFileSize>100MB</maxFileSize>
+ <maxHistory>30</maxHistory>
+ <totalSizeCap>3GB</totalSizeCap>
+ </rollingPolicy>
+ <encoder>
+ <pattern>%d{ISO8601} [%thread] %-5level %logger{36} - %msg%n</pattern>
+ </encoder>
+ </appender>
+
+ <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+ <encoder>
+ <pattern>%d{ISO8601} [%thread] %-5level %logger{36} - %msg%n</pattern>
+ </encoder>
+ </appender>
+
+ <logger name="org.thingsboard.server" level="INFO" />
+
+ <root level="INFO">
+ <appender-ref ref="fileLogAppender"/>
+ <appender-ref ref="STDOUT"/>
+ </root>
+
+</configuration>
\ No newline at end of file
diff --git a/msa/transport/coap/docker/start-tb-coap-transport.sh b/msa/transport/coap/docker/start-tb-coap-transport.sh
new file mode 100755
index 0000000..43d4602
--- /dev/null
+++ b/msa/transport/coap/docker/start-tb-coap-transport.sh
@@ -0,0 +1,29 @@
+#!/bin/bash
+#
+# Copyright © 2016-2018 The Thingsboard Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+CONF_FOLDER="${pkg.installFolder}/conf"
+jarfile=${pkg.installFolder}/bin/${pkg.name}.jar
+configfile=${pkg.name}.conf
+
+source "${CONF_FOLDER}/${configfile}"
+
+echo "Starting '${project.name}' ..."
+
+exec java -cp ${jarfile} $JAVA_OPTS -Dloader.main=org.thingsboard.server.coap.ThingsboardCoapTransportApplication \
+ -Dspring.jpa.hibernate.ddl-auto=none \
+ -Dlogging.config=${CONF_FOLDER}/logback.xml \
+ org.springframework.boot.loader.PropertiesLauncher
diff --git a/msa/transport/coap/docker/tb-coap-transport.conf b/msa/transport/coap/docker/tb-coap-transport.conf
new file mode 100644
index 0000000..6d4c54a
--- /dev/null
+++ b/msa/transport/coap/docker/tb-coap-transport.conf
@@ -0,0 +1,23 @@
+#
+# Copyright © 2016-2018 The Thingsboard Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+export JAVA_OPTS="$JAVA_OPTS -Xloggc:@pkg.logFolder@/gc.log -XX:+IgnoreUnrecognizedVMOptions -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDetails -XX:+PrintGCDateStamps"
+export JAVA_OPTS="$JAVA_OPTS -XX:+PrintHeapAtGC -XX:+PrintTenuringDistribution -XX:+PrintGCApplicationStoppedTime -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10"
+export JAVA_OPTS="$JAVA_OPTS -XX:GCLogFileSize=10M -XX:-UseBiasedLocking -XX:+UseTLAB -XX:+ResizeTLAB -XX:+PerfDisableSharedMem -XX:+UseCondCardMark"
+export JAVA_OPTS="$JAVA_OPTS -XX:CMSWaitDuration=10000 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+CMSParallelInitialMarkEnabled"
+export JAVA_OPTS="$JAVA_OPTS -XX:+CMSEdenChunksRecordAlways -XX:CMSInitiatingOccupancyFraction=75 -XX:+UseCMSInitiatingOccupancyOnly -XX:+ExitOnOutOfMemoryError"
+export LOG_FILENAME=${pkg.name}.out
+export LOADER_PATH=${pkg.installFolder}/conf
msa/transport/coap/pom.xml 137(+137 -0)
diff --git a/msa/transport/coap/pom.xml b/msa/transport/coap/pom.xml
new file mode 100644
index 0000000..07c4e63
--- /dev/null
+++ b/msa/transport/coap/pom.xml
@@ -0,0 +1,137 @@
+<!--
+
+ Copyright © 2016-2018 The Thingsboard Authors
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.thingsboard.msa</groupId>
+ <version>2.2.0-SNAPSHOT</version>
+ <artifactId>transport</artifactId>
+ </parent>
+ <groupId>org.thingsboard.msa.transport</groupId>
+ <artifactId>coap</artifactId>
+ <packaging>pom</packaging>
+
+ <name>ThingsBoard COAP Transport Microservice</name>
+ <url>https://thingsboard.io</url>
+ <description>ThingsBoard COAP Transport Microservice</description>
+
+ <properties>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ <main.dir>${basedir}/../../..</main.dir>
+ <pkg.name>tb-coap-transport</pkg.name>
+ <docker.name>tb-coap-transport</docker.name>
+ <pkg.user>thingsboard</pkg.user>
+ <pkg.unixLogFolder>/var/log/${pkg.name}</pkg.unixLogFolder>
+ <pkg.installFolder>/usr/share/${pkg.name}</pkg.installFolder>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.thingsboard.transport</groupId>
+ <artifactId>coap</artifactId>
+ <version>${project.version}</version>
+ <classifier>deb</classifier>
+ <type>deb</type>
+ <scope>provided</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-dependency-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>copy-tb-coap-transport-deb</id>
+ <phase>package</phase>
+ <goals>
+ <goal>copy</goal>
+ </goals>
+ <configuration>
+ <artifactItems>
+ <artifactItem>
+ <groupId>org.thingsboard.transport</groupId>
+ <artifactId>coap</artifactId>
+ <classifier>deb</classifier>
+ <type>deb</type>
+ <destFileName>${pkg.name}.deb</destFileName>
+ <outputDirectory>${project.build.directory}</outputDirectory>
+ </artifactItem>
+ </artifactItems>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-resources-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>copy-docker-config</id>
+ <phase>process-resources</phase>
+ <goals>
+ <goal>copy-resources</goal>
+ </goals>
+ <configuration>
+ <outputDirectory>${project.build.directory}</outputDirectory>
+ <resources>
+ <resource>
+ <directory>docker</directory>
+ <filtering>true</filtering>
+ </resource>
+ </resources>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>com.spotify</groupId>
+ <artifactId>dockerfile-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>build-docker-image</id>
+ <phase>pre-integration-test</phase>
+ <goals>
+ <goal>build</goal>
+ </goals>
+ </execution>
+ </executions>
+ <configuration>
+ <skip>${dockerfile.skip}</skip>
+ <repository>${docker.repo}/${docker.name}</repository>
+ <tag>${project.version}</tag>
+ <verbose>true</verbose>
+ <googleContainerRegistryEnabled>false</googleContainerRegistryEnabled>
+ <contextDirectory>${project.build.directory}</contextDirectory>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ <repositories>
+ <repository>
+ <id>jenkins</id>
+ <name>Jenkins Repository</name>
+ <url>http://repo.jenkins-ci.org/releases</url>
+ <snapshots>
+ <enabled>false</enabled>
+ </snapshots>
+ </repository>
+ </repositories>
+</project>
msa/transport/http/docker/Dockerfile 31(+31 -0)
diff --git a/msa/transport/http/docker/Dockerfile b/msa/transport/http/docker/Dockerfile
new file mode 100644
index 0000000..212047f
--- /dev/null
+++ b/msa/transport/http/docker/Dockerfile
@@ -0,0 +1,31 @@
+#
+# Copyright © 2016-2018 The Thingsboard Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+FROM openjdk:8-jre
+
+COPY logback.xml ${pkg.name}.conf start-tb-http-transport.sh ${pkg.name}.deb /tmp/
+
+RUN chmod a+x /tmp/*.sh \
+ && mv /tmp/start-tb-http-transport.sh /usr/bin
+
+RUN dpkg -i /tmp/${pkg.name}.deb
+
+RUN update-rc.d ${pkg.name} disable
+
+RUN mv /tmp/logback.xml ${pkg.installFolder}/conf \
+ && mv /tmp/${pkg.name}.conf ${pkg.installFolder}/conf
+
+CMD ["start-tb-http-transport.sh"]
msa/transport/http/docker/logback.xml 50(+50 -0)
diff --git a/msa/transport/http/docker/logback.xml b/msa/transport/http/docker/logback.xml
new file mode 100644
index 0000000..e9d8692
--- /dev/null
+++ b/msa/transport/http/docker/logback.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+
+ Copyright © 2016-2018 The Thingsboard Authors
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+<!DOCTYPE configuration>
+<configuration scan="true" scanPeriod="10 seconds">
+
+ <appender name="fileLogAppender"
+ class="ch.qos.logback.core.rolling.RollingFileAppender">
+ <file>/var/log/${pkg.name}/${pkg.name}.log</file>
+ <rollingPolicy
+ class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
+ <fileNamePattern>/var/log/${pkg.name}/${pkg.name}.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
+ <maxFileSize>100MB</maxFileSize>
+ <maxHistory>30</maxHistory>
+ <totalSizeCap>3GB</totalSizeCap>
+ </rollingPolicy>
+ <encoder>
+ <pattern>%d{ISO8601} [%thread] %-5level %logger{36} - %msg%n</pattern>
+ </encoder>
+ </appender>
+
+ <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+ <encoder>
+ <pattern>%d{ISO8601} [%thread] %-5level %logger{36} - %msg%n</pattern>
+ </encoder>
+ </appender>
+
+ <logger name="org.thingsboard.server" level="INFO" />
+
+ <root level="INFO">
+ <appender-ref ref="fileLogAppender"/>
+ <appender-ref ref="STDOUT"/>
+ </root>
+
+</configuration>
\ No newline at end of file
diff --git a/msa/transport/http/docker/start-tb-http-transport.sh b/msa/transport/http/docker/start-tb-http-transport.sh
new file mode 100755
index 0000000..667988f
--- /dev/null
+++ b/msa/transport/http/docker/start-tb-http-transport.sh
@@ -0,0 +1,29 @@
+#!/bin/bash
+#
+# Copyright © 2016-2018 The Thingsboard Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+CONF_FOLDER="${pkg.installFolder}/conf"
+jarfile=${pkg.installFolder}/bin/${pkg.name}.jar
+configfile=${pkg.name}.conf
+
+source "${CONF_FOLDER}/${configfile}"
+
+echo "Starting '${project.name}' ..."
+
+exec java -cp ${jarfile} $JAVA_OPTS -Dloader.main=org.thingsboard.server.http.ThingsboardHttpTransportApplication \
+ -Dspring.jpa.hibernate.ddl-auto=none \
+ -Dlogging.config=${CONF_FOLDER}/logback.xml \
+ org.springframework.boot.loader.PropertiesLauncher
diff --git a/msa/transport/http/docker/tb-http-transport.conf b/msa/transport/http/docker/tb-http-transport.conf
new file mode 100644
index 0000000..6d4c54a
--- /dev/null
+++ b/msa/transport/http/docker/tb-http-transport.conf
@@ -0,0 +1,23 @@
+#
+# Copyright © 2016-2018 The Thingsboard Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+export JAVA_OPTS="$JAVA_OPTS -Xloggc:@pkg.logFolder@/gc.log -XX:+IgnoreUnrecognizedVMOptions -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDetails -XX:+PrintGCDateStamps"
+export JAVA_OPTS="$JAVA_OPTS -XX:+PrintHeapAtGC -XX:+PrintTenuringDistribution -XX:+PrintGCApplicationStoppedTime -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10"
+export JAVA_OPTS="$JAVA_OPTS -XX:GCLogFileSize=10M -XX:-UseBiasedLocking -XX:+UseTLAB -XX:+ResizeTLAB -XX:+PerfDisableSharedMem -XX:+UseCondCardMark"
+export JAVA_OPTS="$JAVA_OPTS -XX:CMSWaitDuration=10000 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+CMSParallelInitialMarkEnabled"
+export JAVA_OPTS="$JAVA_OPTS -XX:+CMSEdenChunksRecordAlways -XX:CMSInitiatingOccupancyFraction=75 -XX:+UseCMSInitiatingOccupancyOnly -XX:+ExitOnOutOfMemoryError"
+export LOG_FILENAME=${pkg.name}.out
+export LOADER_PATH=${pkg.installFolder}/conf
msa/transport/http/pom.xml 137(+137 -0)
diff --git a/msa/transport/http/pom.xml b/msa/transport/http/pom.xml
new file mode 100644
index 0000000..22eb299
--- /dev/null
+++ b/msa/transport/http/pom.xml
@@ -0,0 +1,137 @@
+<!--
+
+ Copyright © 2016-2018 The Thingsboard Authors
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.thingsboard.msa</groupId>
+ <version>2.2.0-SNAPSHOT</version>
+ <artifactId>transport</artifactId>
+ </parent>
+ <groupId>org.thingsboard.msa.transport</groupId>
+ <artifactId>http</artifactId>
+ <packaging>pom</packaging>
+
+ <name>ThingsBoard HTTP Transport Microservice</name>
+ <url>https://thingsboard.io</url>
+ <description>ThingsBoard HTTP Transport Microservice</description>
+
+ <properties>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ <main.dir>${basedir}/../../..</main.dir>
+ <pkg.name>tb-http-transport</pkg.name>
+ <docker.name>tb-http-transport</docker.name>
+ <pkg.user>thingsboard</pkg.user>
+ <pkg.unixLogFolder>/var/log/${pkg.name}</pkg.unixLogFolder>
+ <pkg.installFolder>/usr/share/${pkg.name}</pkg.installFolder>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.thingsboard.transport</groupId>
+ <artifactId>http</artifactId>
+ <version>${project.version}</version>
+ <classifier>deb</classifier>
+ <type>deb</type>
+ <scope>provided</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-dependency-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>copy-tb-http-transport-deb</id>
+ <phase>package</phase>
+ <goals>
+ <goal>copy</goal>
+ </goals>
+ <configuration>
+ <artifactItems>
+ <artifactItem>
+ <groupId>org.thingsboard.transport</groupId>
+ <artifactId>http</artifactId>
+ <classifier>deb</classifier>
+ <type>deb</type>
+ <destFileName>${pkg.name}.deb</destFileName>
+ <outputDirectory>${project.build.directory}</outputDirectory>
+ </artifactItem>
+ </artifactItems>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-resources-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>copy-docker-config</id>
+ <phase>process-resources</phase>
+ <goals>
+ <goal>copy-resources</goal>
+ </goals>
+ <configuration>
+ <outputDirectory>${project.build.directory}</outputDirectory>
+ <resources>
+ <resource>
+ <directory>docker</directory>
+ <filtering>true</filtering>
+ </resource>
+ </resources>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>com.spotify</groupId>
+ <artifactId>dockerfile-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>build-docker-image</id>
+ <phase>pre-integration-test</phase>
+ <goals>
+ <goal>build</goal>
+ </goals>
+ </execution>
+ </executions>
+ <configuration>
+ <skip>${dockerfile.skip}</skip>
+ <repository>${docker.repo}/${docker.name}</repository>
+ <tag>${project.version}</tag>
+ <verbose>true</verbose>
+ <googleContainerRegistryEnabled>false</googleContainerRegistryEnabled>
+ <contextDirectory>${project.build.directory}</contextDirectory>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ <repositories>
+ <repository>
+ <id>jenkins</id>
+ <name>Jenkins Repository</name>
+ <url>http://repo.jenkins-ci.org/releases</url>
+ <snapshots>
+ <enabled>false</enabled>
+ </snapshots>
+ </repository>
+ </repositories>
+</project>
msa/transport/mqtt/docker/Dockerfile 31(+31 -0)
diff --git a/msa/transport/mqtt/docker/Dockerfile b/msa/transport/mqtt/docker/Dockerfile
new file mode 100644
index 0000000..5827c65
--- /dev/null
+++ b/msa/transport/mqtt/docker/Dockerfile
@@ -0,0 +1,31 @@
+#
+# Copyright © 2016-2018 The Thingsboard Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+FROM openjdk:8-jre
+
+COPY logback.xml ${pkg.name}.conf start-tb-mqtt-transport.sh ${pkg.name}.deb /tmp/
+
+RUN chmod a+x /tmp/*.sh \
+ && mv /tmp/start-tb-mqtt-transport.sh /usr/bin
+
+RUN dpkg -i /tmp/${pkg.name}.deb
+
+RUN update-rc.d ${pkg.name} disable
+
+RUN mv /tmp/logback.xml ${pkg.installFolder}/conf \
+ && mv /tmp/${pkg.name}.conf ${pkg.installFolder}/conf
+
+CMD ["start-tb-mqtt-transport.sh"]
msa/transport/mqtt/docker/logback.xml 50(+50 -0)
diff --git a/msa/transport/mqtt/docker/logback.xml b/msa/transport/mqtt/docker/logback.xml
new file mode 100644
index 0000000..e9d8692
--- /dev/null
+++ b/msa/transport/mqtt/docker/logback.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+
+ Copyright © 2016-2018 The Thingsboard Authors
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+<!DOCTYPE configuration>
+<configuration scan="true" scanPeriod="10 seconds">
+
+ <appender name="fileLogAppender"
+ class="ch.qos.logback.core.rolling.RollingFileAppender">
+ <file>/var/log/${pkg.name}/${pkg.name}.log</file>
+ <rollingPolicy
+ class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
+ <fileNamePattern>/var/log/${pkg.name}/${pkg.name}.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
+ <maxFileSize>100MB</maxFileSize>
+ <maxHistory>30</maxHistory>
+ <totalSizeCap>3GB</totalSizeCap>
+ </rollingPolicy>
+ <encoder>
+ <pattern>%d{ISO8601} [%thread] %-5level %logger{36} - %msg%n</pattern>
+ </encoder>
+ </appender>
+
+ <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+ <encoder>
+ <pattern>%d{ISO8601} [%thread] %-5level %logger{36} - %msg%n</pattern>
+ </encoder>
+ </appender>
+
+ <logger name="org.thingsboard.server" level="INFO" />
+
+ <root level="INFO">
+ <appender-ref ref="fileLogAppender"/>
+ <appender-ref ref="STDOUT"/>
+ </root>
+
+</configuration>
\ No newline at end of file
diff --git a/msa/transport/mqtt/docker/start-tb-mqtt-transport.sh b/msa/transport/mqtt/docker/start-tb-mqtt-transport.sh
new file mode 100755
index 0000000..8fe06d2
--- /dev/null
+++ b/msa/transport/mqtt/docker/start-tb-mqtt-transport.sh
@@ -0,0 +1,29 @@
+#!/bin/bash
+#
+# Copyright © 2016-2018 The Thingsboard Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+CONF_FOLDER="${pkg.installFolder}/conf"
+jarfile=${pkg.installFolder}/bin/${pkg.name}.jar
+configfile=${pkg.name}.conf
+
+source "${CONF_FOLDER}/${configfile}"
+
+echo "Starting '${project.name}' ..."
+
+exec java -cp ${jarfile} $JAVA_OPTS -Dloader.main=org.thingsboard.server.mqtt.ThingsboardMqttTransportApplication \
+ -Dspring.jpa.hibernate.ddl-auto=none \
+ -Dlogging.config=${CONF_FOLDER}/logback.xml \
+ org.springframework.boot.loader.PropertiesLauncher
diff --git a/msa/transport/mqtt/docker/tb-mqtt-transport.conf b/msa/transport/mqtt/docker/tb-mqtt-transport.conf
new file mode 100644
index 0000000..6d4c54a
--- /dev/null
+++ b/msa/transport/mqtt/docker/tb-mqtt-transport.conf
@@ -0,0 +1,23 @@
+#
+# Copyright © 2016-2018 The Thingsboard Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+export JAVA_OPTS="$JAVA_OPTS -Xloggc:@pkg.logFolder@/gc.log -XX:+IgnoreUnrecognizedVMOptions -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDetails -XX:+PrintGCDateStamps"
+export JAVA_OPTS="$JAVA_OPTS -XX:+PrintHeapAtGC -XX:+PrintTenuringDistribution -XX:+PrintGCApplicationStoppedTime -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10"
+export JAVA_OPTS="$JAVA_OPTS -XX:GCLogFileSize=10M -XX:-UseBiasedLocking -XX:+UseTLAB -XX:+ResizeTLAB -XX:+PerfDisableSharedMem -XX:+UseCondCardMark"
+export JAVA_OPTS="$JAVA_OPTS -XX:CMSWaitDuration=10000 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+CMSParallelInitialMarkEnabled"
+export JAVA_OPTS="$JAVA_OPTS -XX:+CMSEdenChunksRecordAlways -XX:CMSInitiatingOccupancyFraction=75 -XX:+UseCMSInitiatingOccupancyOnly -XX:+ExitOnOutOfMemoryError"
+export LOG_FILENAME=${pkg.name}.out
+export LOADER_PATH=${pkg.installFolder}/conf
msa/transport/mqtt/pom.xml 137(+137 -0)
diff --git a/msa/transport/mqtt/pom.xml b/msa/transport/mqtt/pom.xml
new file mode 100644
index 0000000..975f9fc
--- /dev/null
+++ b/msa/transport/mqtt/pom.xml
@@ -0,0 +1,137 @@
+<!--
+
+ Copyright © 2016-2018 The Thingsboard Authors
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.thingsboard.msa</groupId>
+ <version>2.2.0-SNAPSHOT</version>
+ <artifactId>transport</artifactId>
+ </parent>
+ <groupId>org.thingsboard.msa.transport</groupId>
+ <artifactId>mqtt</artifactId>
+ <packaging>pom</packaging>
+
+ <name>ThingsBoard MQTT Transport Microservice</name>
+ <url>https://thingsboard.io</url>
+ <description>ThingsBoard MQTT Transport Microservice</description>
+
+ <properties>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ <main.dir>${basedir}/../../..</main.dir>
+ <pkg.name>tb-mqtt-transport</pkg.name>
+ <docker.name>tb-mqtt-transport</docker.name>
+ <pkg.user>thingsboard</pkg.user>
+ <pkg.unixLogFolder>/var/log/${pkg.name}</pkg.unixLogFolder>
+ <pkg.installFolder>/usr/share/${pkg.name}</pkg.installFolder>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.thingsboard.transport</groupId>
+ <artifactId>mqtt</artifactId>
+ <version>${project.version}</version>
+ <classifier>deb</classifier>
+ <type>deb</type>
+ <scope>provided</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-dependency-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>copy-tb-mqtt-transport-deb</id>
+ <phase>package</phase>
+ <goals>
+ <goal>copy</goal>
+ </goals>
+ <configuration>
+ <artifactItems>
+ <artifactItem>
+ <groupId>org.thingsboard.transport</groupId>
+ <artifactId>mqtt</artifactId>
+ <classifier>deb</classifier>
+ <type>deb</type>
+ <destFileName>${pkg.name}.deb</destFileName>
+ <outputDirectory>${project.build.directory}</outputDirectory>
+ </artifactItem>
+ </artifactItems>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-resources-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>copy-docker-config</id>
+ <phase>process-resources</phase>
+ <goals>
+ <goal>copy-resources</goal>
+ </goals>
+ <configuration>
+ <outputDirectory>${project.build.directory}</outputDirectory>
+ <resources>
+ <resource>
+ <directory>docker</directory>
+ <filtering>true</filtering>
+ </resource>
+ </resources>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>com.spotify</groupId>
+ <artifactId>dockerfile-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>build-docker-image</id>
+ <phase>pre-integration-test</phase>
+ <goals>
+ <goal>build</goal>
+ </goals>
+ </execution>
+ </executions>
+ <configuration>
+ <skip>${dockerfile.skip}</skip>
+ <repository>${docker.repo}/${docker.name}</repository>
+ <tag>${project.version}</tag>
+ <verbose>true</verbose>
+ <googleContainerRegistryEnabled>false</googleContainerRegistryEnabled>
+ <contextDirectory>${project.build.directory}</contextDirectory>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ <repositories>
+ <repository>
+ <id>jenkins</id>
+ <name>Jenkins Repository</name>
+ <url>http://repo.jenkins-ci.org/releases</url>
+ <snapshots>
+ <enabled>false</enabled>
+ </snapshots>
+ </repository>
+ </repositories>
+</project>
msa/transport/pom.xml 55(+55 -0)
diff --git a/msa/transport/pom.xml b/msa/transport/pom.xml
new file mode 100644
index 0000000..9f091e3
--- /dev/null
+++ b/msa/transport/pom.xml
@@ -0,0 +1,55 @@
+<!--
+
+ Copyright © 2016-2018 The Thingsboard Authors
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.thingsboard</groupId>
+ <version>2.2.0-SNAPSHOT</version>
+ <artifactId>msa</artifactId>
+ </parent>
+ <groupId>org.thingsboard.msa</groupId>
+ <artifactId>transport</artifactId>
+ <packaging>pom</packaging>
+
+ <name>ThingsBoard Transport Microservices</name>
+ <url>https://thingsboard.io</url>
+
+ <properties>
+ <main.dir>${basedir}/../..</main.dir>
+ </properties>
+
+ <modules>
+ <module>mqtt</module>
+ <module>http</module>
+ <module>coap</module>
+ </modules>
+
+ <build>
+ <pluginManagement>
+ <plugins>
+ <plugin>
+ <groupId>com.spotify</groupId>
+ <artifactId>dockerfile-maven-plugin</artifactId>
+ <version>1.4.5</version>
+ </plugin>
+ </plugins>
+ </pluginManagement>
+ </build>
+
+</project>
pom.xml 17(+11 -6)
diff --git a/pom.xml b/pom.xml
index d6db123..684c3d0 100755
--- a/pom.xml
+++ b/pom.xml
@@ -355,18 +355,23 @@
<version>${project.version}</version>
</dependency>
<dependency>
- <groupId>org.thingsboard.transport</groupId>
- <artifactId>http</artifactId>
+ <groupId>org.thingsboard.common.transport</groupId>
+ <artifactId>transport-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
- <groupId>org.thingsboard.transport</groupId>
- <artifactId>coap</artifactId>
+ <groupId>org.thingsboard.common.transport</groupId>
+ <artifactId>mqtt</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
- <groupId>org.thingsboard.transport</groupId>
- <artifactId>mqtt</artifactId>
+ <groupId>org.thingsboard.common.transport</groupId>
+ <artifactId>http</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.thingsboard.common.transport</groupId>
+ <artifactId>coap</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
diff --git a/rule-engine/rule-engine-components/pom.xml b/rule-engine/rule-engine-components/pom.xml
index 6390929..c22ce78 100644
--- a/rule-engine/rule-engine-components/pom.xml
+++ b/rule-engine/rule-engine-components/pom.xml
@@ -45,8 +45,8 @@
<scope>provided</scope>
</dependency>
<dependency>
- <groupId>org.thingsboard.common</groupId>
- <artifactId>transport</artifactId>
+ <groupId>org.thingsboard.common.transport</groupId>
+ <artifactId>transport-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
@@ -72,6 +72,10 @@
<artifactId>guava</artifactId>
</dependency>
<dependency>
+ <groupId>com.google.code.gson</groupId>
+ <artifactId>gson</artifactId>
+ </dependency>
+ <dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<scope>provided</scope>
diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCopyAttributesToEntityViewNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCopyAttributesToEntityViewNode.java
index fc0ff28..459e35b 100644
--- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCopyAttributesToEntityViewNode.java
+++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCopyAttributesToEntityViewNode.java
@@ -88,8 +88,7 @@ public class TbCopyAttributesToEntityViewNode implements TbNode {
if ((endTime != 0 && endTime > now && startTime < now) || (endTime == 0 && startTime < now)) {
if (DataConstants.ATTRIBUTES_UPDATED.equals(msg.getType()) ||
SessionMsgType.POST_ATTRIBUTES_REQUEST.name().equals(msg.getType())) {
- Set<AttributeKvEntry> attributes =
- JsonConverter.convertToAttributes(new JsonParser().parse(msg.getData())).getAttributes();
+ Set<AttributeKvEntry> attributes = JsonConverter.convertToAttributes(new JsonParser().parse(msg.getData()));
List<AttributeKvEntry> filteredAttributes =
attributes.stream().filter(attr -> attributeContainsInEntityView(scope, attr.getKey(), entityView)).collect(Collectors.toList());
ctx.getTelemetryService().saveAndNotify(entityView.getId(), scope, filteredAttributes,
diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNode.java
index 5cdb04e..12dbc6d 100644
--- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNode.java
+++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNode.java
@@ -63,7 +63,7 @@ public class TbMsgAttributesNode implements TbNode {
}
String src = msg.getData();
- Set<AttributeKvEntry> attributes = JsonConverter.convertToAttributes(new JsonParser().parse(src)).getAttributes();
+ Set<AttributeKvEntry> attributes = JsonConverter.convertToAttributes(new JsonParser().parse(src));
ctx.getTelemetryService().saveAndNotify(msg.getOriginator(), config.getScope(), new ArrayList<>(attributes), new TelemetryNodeCallback(ctx, msg));
if (msg.getOriginator().getEntityType() == EntityType.DEVICE && DataConstants.SHARED_SCOPE.equals(config.getScope())) {
ctx.getTelemetryService().onSharedAttributesUpdate(ctx.getTenantId(), new DeviceId(msg.getOriginator().getId()), attributes);
diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java
index efdf4af..4cb9999 100644
--- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java
+++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java
@@ -29,7 +29,6 @@ import org.thingsboard.server.common.data.kv.KvEntry;
import org.thingsboard.server.common.data.kv.TsKvEntry;
import org.thingsboard.server.common.data.plugin.ComponentType;
import org.thingsboard.server.common.msg.TbMsg;
-import org.thingsboard.server.common.msg.core.TelemetryUploadRequest;
import org.thingsboard.server.common.msg.session.SessionMsgType;
import org.thingsboard.server.common.transport.adaptor.JsonConverter;
@@ -68,13 +67,13 @@ public class TbMsgTimeseriesNode implements TbNode {
if (!StringUtils.isEmpty(tsStr)) {
try {
ts = Long.parseLong(tsStr);
- } catch (NumberFormatException e) {}
+ } catch (NumberFormatException e) {
+ }
} else {
ts = System.currentTimeMillis();
}
String src = msg.getData();
- TelemetryUploadRequest telemetryUploadRequest = JsonConverter.convertToTelemetry(new JsonParser().parse(src), ts);
- Map<Long, List<KvEntry>> tsKvMap = telemetryUploadRequest.getData();
+ Map<Long, List<KvEntry>> tsKvMap = JsonConverter.convertToTelemetry(new JsonParser().parse(src), ts);
if (tsKvMap == null) {
ctx.tellFailure(msg, new IllegalArgumentException("Msg body is empty: " + src));
return;
transport/coap/build.gradle 140(+140 -0)
diff --git a/transport/coap/build.gradle b/transport/coap/build.gradle
new file mode 100644
index 0000000..6d54cb4
--- /dev/null
+++ b/transport/coap/build.gradle
@@ -0,0 +1,140 @@
+/**
+ * Copyright © 2016-2018 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import org.apache.tools.ant.filters.ReplaceTokens
+
+buildscript {
+ ext {
+ osPackageVersion = "3.8.0"
+ }
+ repositories {
+ jcenter()
+ }
+ dependencies {
+ classpath("com.netflix.nebula:gradle-ospackage-plugin:${osPackageVersion}")
+ }
+}
+
+apply plugin: "nebula.ospackage"
+
+buildDir = projectBuildDir
+version = projectVersion
+distsDirName = "./"
+
+// OS Package plugin configuration
+ospackage {
+ packageName = pkgName
+ version = "${project.version}"
+ release = 1
+ os = LINUX
+ type = BINARY
+
+ into pkgInstallFolder
+
+ user pkgName
+ permissionGroup pkgName
+
+ // Copy the actual .jar file
+ from(mainJar) {
+ // Strip the version from the jar filename
+ rename { String fileName ->
+ "${pkgName}.jar"
+ }
+ fileMode 0500
+ into "bin"
+ }
+
+ // Copy the config files
+ from("target/conf") {
+ exclude "${pkgName}.conf"
+ fileType CONFIG | NOREPLACE
+ fileMode 0754
+ into "conf"
+ }
+
+}
+
+// Configure our RPM build task
+buildRpm {
+
+ arch = NOARCH
+
+ version = projectVersion.replace('-', '')
+ archiveName = "${pkgName}.rpm"
+
+ requires("java-1.8.0")
+
+ from("target/conf") {
+ include "${pkgName}.conf"
+ filter(ReplaceTokens, tokens: ['pkg.platform': 'rpm'])
+ fileType CONFIG | NOREPLACE
+ fileMode 0754
+ into "${pkgInstallFolder}/conf"
+ }
+
+ preInstall file("${buildDir}/control/rpm/preinst")
+ postInstall file("${buildDir}/control/rpm/postinst")
+ preUninstall file("${buildDir}/control/rpm/prerm")
+ postUninstall file("${buildDir}/control/rpm/postrm")
+
+ user pkgName
+ permissionGroup pkgName
+
+ // Copy the system unit files
+ from("${buildDir}/control/${pkgName}.service") {
+ addParentDirs = false
+ fileMode 0644
+ into "/usr/lib/systemd/system"
+ }
+
+ directory(pkgLogFolder, 0755)
+ link("${pkgInstallFolder}/bin/${pkgName}.yml", "${pkgInstallFolder}/conf/${pkgName}.yml")
+ link("/etc/${pkgName}/conf", "${pkgInstallFolder}/conf")
+}
+
+// Same as the buildRpm task
+buildDeb {
+
+ arch = "all"
+
+ archiveName = "${pkgName}.deb"
+
+ requires("openjdk-8-jre").or("java8-runtime").or("oracle-java8-installer").or("openjdk-8-jre-headless")
+
+ from("target/conf") {
+ include "${pkgName}.conf"
+ filter(ReplaceTokens, tokens: ['pkg.platform': 'deb'])
+ fileType CONFIG | NOREPLACE
+ fileMode 0754
+ into "${pkgInstallFolder}/conf"
+ }
+
+ configurationFile("${pkgInstallFolder}/conf/${pkgName}.conf")
+ configurationFile("${pkgInstallFolder}/conf/${pkgName}.yml")
+ configurationFile("${pkgInstallFolder}/conf/logback.xml")
+
+ preInstall file("${buildDir}/control/deb/preinst")
+ postInstall file("${buildDir}/control/deb/postinst")
+ preUninstall file("${buildDir}/control/deb/prerm")
+ postUninstall file("${buildDir}/control/deb/postrm")
+
+ user pkgName
+ permissionGroup pkgName
+
+ directory(pkgLogFolder, 0755)
+ link("/etc/init.d/${pkgName}", "${pkgInstallFolder}/bin/${pkgName}.jar")
+ link("${pkgInstallFolder}/bin/${pkgName}.yml", "${pkgInstallFolder}/conf/${pkgName}.yml")
+ link("/etc/${pkgName}/conf", "${pkgInstallFolder}/conf")
+}
transport/coap/pom.xml 299(+278 -21)
diff --git a/transport/coap/pom.xml b/transport/coap/pom.xml
index a7015f0..a5e8107 100644
--- a/transport/coap/pom.xml
+++ b/transport/coap/pom.xml
@@ -27,42 +27,37 @@
<artifactId>coap</artifactId>
<packaging>jar</packaging>
- <name>Thingsboard COAP Transport</name>
+ <name>Thingsboard CoAP Transport Service</name>
<url>https://thingsboard.io</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<main.dir>${basedir}/../..</main.dir>
+ <pkg.name>tb-coap-transport</pkg.name>
+ <pkg.unixLogFolder>/var/log/${pkg.name}</pkg.unixLogFolder>
+ <pkg.installFolder>/usr/share/${pkg.name}</pkg.installFolder>
+ <pkg.win.dist>${project.build.directory}/windows</pkg.win.dist>
</properties>
<dependencies>
<dependency>
- <groupId>org.thingsboard.common</groupId>
- <artifactId>transport</artifactId>
- </dependency>
- <dependency>
- <groupId>org.eclipse.californium</groupId>
- <artifactId>californium-core</artifactId>
- </dependency>
- <dependency>
- <groupId>org.springframework</groupId>
- <artifactId>spring-context</artifactId>
+ <groupId>org.thingsboard.common.transport</groupId>
+ <artifactId>coap</artifactId>
</dependency>
<dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</artifactId>
- </dependency>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>log4j-over-slf4j</artifactId>
+ <groupId>org.thingsboard.common</groupId>
+ <artifactId>queue</artifactId>
</dependency>
<dependency>
- <groupId>ch.qos.logback</groupId>
- <artifactId>logback-core</artifactId>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
- <groupId>ch.qos.logback</groupId>
- <artifactId>logback-classic</artifactId>
+ <groupId>com.sun.winsw</groupId>
+ <artifactId>winsw</artifactId>
+ <classifier>bin</classifier>
+ <type>exe</type>
+ <scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
@@ -81,4 +76,266 @@
</dependency>
</dependencies>
+ <build>
+ <finalName>${pkg.name}-${project.version}</finalName>
+ <resources>
+ <resource>
+ <directory>${project.basedir}/src/main/resources</directory>
+ </resource>
+ </resources>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-resources-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>copy-conf</id>
+ <phase>process-resources</phase>
+ <goals>
+ <goal>copy-resources</goal>
+ </goals>
+ <configuration>
+ <outputDirectory>${project.build.directory}/conf</outputDirectory>
+ <resources>
+ <resource>
+ <directory>src/main/resources</directory>
+ <excludes>
+ <exclude>logback.xml</exclude>
+ </excludes>
+ <filtering>false</filtering>
+ </resource>
+ </resources>
+ </configuration>
+ </execution>
+ <execution>
+ <id>copy-service-conf</id>
+ <phase>process-resources</phase>
+ <goals>
+ <goal>copy-resources</goal>
+ </goals>
+ <configuration>
+ <outputDirectory>${project.build.directory}/conf</outputDirectory>
+ <resources>
+ <resource>
+ <directory>src/main/conf</directory>
+ <filtering>true</filtering>
+ </resource>
+ </resources>
+ <filters>
+ <filter>src/main/filters/unix.properties</filter>
+ </filters>
+ </configuration>
+ </execution>
+ <execution>
+ <id>copy-win-conf</id>
+ <phase>process-resources</phase>
+ <goals>
+ <goal>copy-resources</goal>
+ </goals>
+ <configuration>
+ <outputDirectory>${pkg.win.dist}/conf</outputDirectory>
+ <resources>
+ <resource>
+ <directory>src/main/resources</directory>
+ <excludes>
+ <exclude>logback.xml</exclude>
+ </excludes>
+ <filtering>false</filtering>
+ </resource>
+ <resource>
+ <directory>src/main/conf</directory>
+ <excludes>
+ <exclude>tb-coap-transport.conf</exclude>
+ </excludes>
+ <filtering>true</filtering>
+ </resource>
+ </resources>
+ <filters>
+ <filter>src/main/filters/windows.properties</filter>
+ </filters>
+ </configuration>
+ </execution>
+ <execution>
+ <id>copy-control</id>
+ <phase>process-resources</phase>
+ <goals>
+ <goal>copy-resources</goal>
+ </goals>
+ <configuration>
+ <outputDirectory>${project.build.directory}/control</outputDirectory>
+ <resources>
+ <resource>
+ <directory>src/main/scripts/control</directory>
+ <filtering>true</filtering>
+ </resource>
+ </resources>
+ <filters>
+ <filter>src/main/filters/unix.properties</filter>
+ </filters>
+ </configuration>
+ </execution>
+ <execution>
+ <id>copy-windows-control</id>
+ <phase>process-resources</phase>
+ <goals>
+ <goal>copy-resources</goal>
+ </goals>
+ <configuration>
+ <outputDirectory>${pkg.win.dist}</outputDirectory>
+ <resources>
+ <resource>
+ <directory>src/main/scripts/windows</directory>
+ <filtering>true</filtering>
+ </resource>
+ </resources>
+ <filters>
+ <filter>src/main/filters/windows.properties</filter>
+ </filters>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-dependency-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>copy-winsw-service</id>
+ <phase>package</phase>
+ <goals>
+ <goal>copy</goal>
+ </goals>
+ <configuration>
+ <artifactItems>
+ <artifactItem>
+ <groupId>com.sun.winsw</groupId>
+ <artifactId>winsw</artifactId>
+ <classifier>bin</classifier>
+ <type>exe</type>
+ <destFileName>service.exe</destFileName>
+ </artifactItem>
+ </artifactItems>
+ <outputDirectory>${pkg.win.dist}</outputDirectory>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ <configuration>
+ <excludes>
+ <exclude>**/logback.xml</exclude>
+ </excludes>
+ <archive>
+ <manifestEntries>
+ <Implementation-Title>ThingsBoard CoAP Transport Service</Implementation-Title>
+ <Implementation-Version>${project.version}</Implementation-Version>
+ </manifestEntries>
+ </archive>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-maven-plugin</artifactId>
+ <configuration>
+ <mainClass>org.thingsboard.server.coap.ThingsboardCoapTransportApplication</mainClass>
+ <classifier>boot</classifier>
+ <layout>ZIP</layout>
+ <executable>true</executable>
+ <excludeDevtools>true</excludeDevtools>
+ <embeddedLaunchScriptProperties>
+ <confFolder>${pkg.installFolder}/conf</confFolder>
+ <logFolder>${pkg.unixLogFolder}</logFolder>
+ <logFilename>${pkg.name}.out</logFilename>
+ <initInfoProvides>${pkg.name}</initInfoProvides>
+ </embeddedLaunchScriptProperties>
+ </configuration>
+ <executions>
+ <execution>
+ <goals>
+ <goal>repackage</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.fortasoft</groupId>
+ <artifactId>gradle-maven-plugin</artifactId>
+ <configuration>
+ <tasks>
+ <task>build</task>
+ <task>buildDeb</task>
+ <task>buildRpm</task>
+ </tasks>
+ <args>
+ <arg>-PprojectBuildDir=${project.build.directory}</arg>
+ <arg>-PprojectVersion=${project.version}</arg>
+ <arg>-PmainJar=${project.build.directory}/${project.build.finalName}-boot.${project.packaging}</arg>
+ <arg>-PpkgName=${pkg.name}</arg>
+ <arg>-PpkgInstallFolder=${pkg.installFolder}</arg>
+ <arg>-PpkgLogFolder=${pkg.unixLogFolder}</arg>
+ </args>
+ </configuration>
+ <executions>
+ <execution>
+ <phase>package</phase>
+ <goals>
+ <goal>invoke</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <configuration>
+ <finalName>${pkg.name}</finalName>
+ <descriptors>
+ <descriptor>src/main/assembly/windows.xml</descriptor>
+ </descriptors>
+ </configuration>
+ <executions>
+ <execution>
+ <id>assembly</id>
+ <phase>package</phase>
+ <goals>
+ <goal>single</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-install-plugin</artifactId>
+ <configuration>
+ <file>${project.build.directory}/${pkg.name}.deb</file>
+ <artifactId>${project.artifactId}</artifactId>
+ <groupId>${project.groupId}</groupId>
+ <version>${project.version}</version>
+ <classifier>deb</classifier>
+ <packaging>deb</packaging>
+ </configuration>
+ <executions>
+ <execution>
+ <id>install-deb</id>
+ <phase>package</phase>
+ <goals>
+ <goal>install-file</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ <repositories>
+ <repository>
+ <id>jenkins</id>
+ <name>Jenkins Repository</name>
+ <url>http://repo.jenkins-ci.org/releases</url>
+ <snapshots>
+ <enabled>false</enabled>
+ </snapshots>
+ </repository>
+ </repositories>
</project>
transport/coap/src/main/assembly/windows.xml 71(+71 -0)
diff --git a/transport/coap/src/main/assembly/windows.xml b/transport/coap/src/main/assembly/windows.xml
new file mode 100644
index 0000000..82da34e
--- /dev/null
+++ b/transport/coap/src/main/assembly/windows.xml
@@ -0,0 +1,71 @@
+<!--
+
+ Copyright © 2016-2018 The Thingsboard Authors
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3 http://maven.apache.org/xsd/assembly-1.1.3.xsd">
+ <id>windows</id>
+
+ <formats>
+ <format>zip</format>
+ </formats>
+
+ <!-- Workaround to create logs directory -->
+ <fileSets>
+ <fileSet>
+ <directory>${pkg.win.dist}</directory>
+ <outputDirectory>logs</outputDirectory>
+ <excludes>
+ <exclude>*/**</exclude>
+ </excludes>
+ </fileSet>
+ <fileSet>
+ <directory>${pkg.win.dist}/conf</directory>
+ <outputDirectory>conf</outputDirectory>
+ <lineEnding>windows</lineEnding>
+ </fileSet>
+ </fileSets>
+
+ <files>
+ <file>
+ <source>${project.build.directory}/${project.build.finalName}-boot.${project.packaging}</source>
+ <outputDirectory>lib</outputDirectory>
+ <destName>${pkg.name}.jar</destName>
+ </file>
+ <file>
+ <source>${pkg.win.dist}/service.exe</source>
+ <outputDirectory/>
+ <destName>${pkg.name}.exe</destName>
+ </file>
+ <file>
+ <source>${pkg.win.dist}/service.xml</source>
+ <outputDirectory/>
+ <destName>${pkg.name}.xml</destName>
+ <lineEnding>windows</lineEnding>
+ </file>
+ <file>
+ <source>${pkg.win.dist}/install.bat</source>
+ <outputDirectory/>
+ <lineEnding>windows</lineEnding>
+ </file>
+ <file>
+ <source>${pkg.win.dist}/uninstall.bat</source>
+ <outputDirectory/>
+ <lineEnding>windows</lineEnding>
+ </file>
+ </files>
+</assembly>
transport/coap/src/main/conf/logback.xml 43(+43 -0)
diff --git a/transport/coap/src/main/conf/logback.xml b/transport/coap/src/main/conf/logback.xml
new file mode 100644
index 0000000..f36469d
--- /dev/null
+++ b/transport/coap/src/main/conf/logback.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+
+ Copyright © 2016-2018 The Thingsboard Authors
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+<!DOCTYPE configuration>
+<configuration>
+
+ <appender name="fileLogAppender"
+ class="ch.qos.logback.core.rolling.RollingFileAppender">
+ <file>${pkg.logFolder}/${pkg.name}.log</file>
+ <rollingPolicy
+ class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
+ <fileNamePattern>${pkg.logFolder}/${pkg.name}.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
+ <maxFileSize>100MB</maxFileSize>
+ <maxHistory>30</maxHistory>
+ <totalSizeCap>3GB</totalSizeCap>
+ </rollingPolicy>
+ <encoder>
+ <pattern>%d{ISO8601} [%thread] %-5level %logger{36} - %msg%n</pattern>
+ </encoder>
+ </appender>
+
+ <logger name="org.thingsboard.server" level="INFO" />
+
+ <root level="INFO">
+ <appender-ref ref="fileLogAppender"/>
+ </root>
+
+</configuration>
diff --git a/transport/coap/src/main/conf/tb-coap-transport.conf b/transport/coap/src/main/conf/tb-coap-transport.conf
new file mode 100644
index 0000000..0afa91c
--- /dev/null
+++ b/transport/coap/src/main/conf/tb-coap-transport.conf
@@ -0,0 +1,23 @@
+#
+# Copyright © 2016-2018 The Thingsboard Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+export JAVA_OPTS="$JAVA_OPTS -Xloggc:@pkg.logFolder@/gc.log -XX:+IgnoreUnrecognizedVMOptions -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDetails -XX:+PrintGCDateStamps"
+export JAVA_OPTS="$JAVA_OPTS -XX:+PrintHeapAtGC -XX:+PrintTenuringDistribution -XX:+PrintGCApplicationStoppedTime -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10"
+export JAVA_OPTS="$JAVA_OPTS -XX:GCLogFileSize=10M -XX:-UseBiasedLocking -XX:+UseTLAB -XX:+ResizeTLAB -XX:+PerfDisableSharedMem -XX:+UseCondCardMark"
+export JAVA_OPTS="$JAVA_OPTS -XX:CMSWaitDuration=10000 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+CMSParallelInitialMarkEnabled"
+export JAVA_OPTS="$JAVA_OPTS -XX:+CMSEdenChunksRecordAlways -XX:CMSInitiatingOccupancyFraction=75 -XX:+UseCMSInitiatingOccupancyOnly"
+export LOG_FILENAME=${pkg.name}.out
+export LOADER_PATH=${pkg.installFolder}/conf
diff --git a/transport/coap/src/main/filters/unix.properties b/transport/coap/src/main/filters/unix.properties
new file mode 100644
index 0000000..8967278
--- /dev/null
+++ b/transport/coap/src/main/filters/unix.properties
@@ -0,0 +1 @@
+pkg.logFolder=${pkg.unixLogFolder}
\ No newline at end of file
diff --git a/transport/coap/src/main/filters/windows.properties b/transport/coap/src/main/filters/windows.properties
new file mode 100644
index 0000000..a6e48d9
--- /dev/null
+++ b/transport/coap/src/main/filters/windows.properties
@@ -0,0 +1,2 @@
+pkg.logFolder=${BASE}\\logs
+pkg.winWrapperLogFolder=%BASE%\\logs
diff --git a/transport/coap/src/main/java/org/thingsboard/server/coap/ThingsboardCoapTransportApplication.java b/transport/coap/src/main/java/org/thingsboard/server/coap/ThingsboardCoapTransportApplication.java
new file mode 100644
index 0000000..1ad86dd
--- /dev/null
+++ b/transport/coap/src/main/java/org/thingsboard/server/coap/ThingsboardCoapTransportApplication.java
@@ -0,0 +1,48 @@
+/**
+ * Copyright © 2016-2018 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.coap;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.SpringBootConfiguration;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.scheduling.annotation.EnableAsync;
+import org.springframework.scheduling.annotation.EnableScheduling;
+
+import java.util.Arrays;
+
+@SpringBootConfiguration
+@EnableAsync
+@EnableScheduling
+@ComponentScan({"org.thingsboard.server.coap", "org.thingsboard.server.common", "org.thingsboard.server.transport.coap", "org.thingsboard.server.kafka"})
+public class ThingsboardCoapTransportApplication {
+
+ private static final String SPRING_CONFIG_NAME_KEY = "--spring.config.name";
+ private static final String DEFAULT_SPRING_CONFIG_PARAM = SPRING_CONFIG_NAME_KEY + "=" + "tb-coap-transport";
+
+ public static void main(String[] args) {
+ SpringApplication.run(ThingsboardCoapTransportApplication.class, updateArguments(args));
+ }
+
+ private static String[] updateArguments(String[] args) {
+ if (Arrays.stream(args).noneMatch(arg -> arg.startsWith(SPRING_CONFIG_NAME_KEY))) {
+ String[] modifiedArgs = new String[args.length + 1];
+ System.arraycopy(args, 0, modifiedArgs, 0, args.length);
+ modifiedArgs[args.length] = DEFAULT_SPRING_CONFIG_PARAM;
+ return modifiedArgs;
+ }
+ return args;
+ }
+}
diff --git a/transport/coap/src/main/resources/logback.xml b/transport/coap/src/main/resources/logback.xml
new file mode 100644
index 0000000..18864a9
--- /dev/null
+++ b/transport/coap/src/main/resources/logback.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+
+ Copyright © 2016-2018 The Thingsboard Authors
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+<!DOCTYPE configuration>
+<configuration scan="true" scanPeriod="10 seconds">
+
+ <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+ <encoder>
+ <pattern>%d{ISO8601} [%thread] %-5level %logger{36} - %msg%n</pattern>
+ </encoder>
+ </appender>
+
+ <logger name="org.thingsboard.server" level="TRACE" />
+
+ <root level="INFO">
+ <appender-ref ref="STDOUT"/>
+ </root>
+
+</configuration>
\ No newline at end of file
diff --git a/transport/coap/src/main/resources/tb-coap-transport.yml b/transport/coap/src/main/resources/tb-coap-transport.yml
new file mode 100644
index 0000000..1790d82
--- /dev/null
+++ b/transport/coap/src/main/resources/tb-coap-transport.yml
@@ -0,0 +1,68 @@
+#
+# Copyright © 2016-2018 The Thingsboard Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+spring.main.web-environment: false
+spring.main.web-application-type: none
+
+# MQTT server parameters
+transport:
+ coap:
+ bind_address: "${COAP_BIND_ADDRESS:0.0.0.0}"
+ bind_port: "${COAP_BIND_PORT:5683}"
+ timeout: "${COAP_TIMEOUT:10000}"
+
+#Quota parameters
+quota:
+ host:
+ # Max allowed number of API requests in interval for single host
+ limit: "${QUOTA_HOST_LIMIT:10000}"
+ # Interval duration
+ intervalMs: "${QUOTA_HOST_INTERVAL_MS:60000}"
+ # Maximum silence duration for host after which Host removed from QuotaService. Must be bigger than intervalMs
+ ttlMs: "${QUOTA_HOST_TTL_MS:60000}"
+ # Interval for scheduled task that cleans expired records. TTL is used for expiring
+ cleanPeriodMs: "${QUOTA_HOST_CLEAN_PERIOD_MS:300000}"
+ # Enable Host API Limits
+ enabled: "${QUOTA_HOST_ENABLED:true}"
+ # Array of whitelist hosts
+ whitelist: "${QUOTA_HOST_WHITELIST:localhost,127.0.0.1}"
+ # Array of blacklist hosts
+ blacklist: "${QUOTA_HOST_BLACKLIST:}"
+ log:
+ topSize: 10
+ intervalMin: 2
+
+kafka:
+ enabled: true
+ bootstrap.servers: "${TB_KAFKA_SERVERS:localhost:9092}"
+ acks: "${TB_KAFKA_ACKS:all}"
+ retries: "${TB_KAFKA_RETRIES:1}"
+ batch.size: "${TB_KAFKA_BATCH_SIZE:16384}"
+ linger.ms: "${TB_KAFKA_LINGER_MS:1}"
+ buffer.memory: "${TB_BUFFER_MEMORY:33554432}"
+ transport_api:
+ requests_topic: "${TB_TRANSPORT_API_REQUEST_TOPIC:tb.transport.api.requests}"
+ responses_topic: "${TB_TRANSPORT_API_RESPONSE_TOPIC:tb.transport.api.responses}"
+ max_pending_requests: "${TB_TRANSPORT_MAX_PENDING_REQUESTS:10000}"
+ max_requests_timeout: "${TB_TRANSPORT_MAX_REQUEST_TIMEOUT:10000}"
+ response_poll_interval: "${TB_TRANSPORT_RESPONSE_POLL_INTERVAL_MS:25}"
+ response_auto_commit_interval: "${TB_TRANSPORT_RESPONSE_AUTO_COMMIT_INTERVAL_MS:100}"
+ rule_engine:
+ topic: "${TB_RULE_ENGINE_TOPIC:tb.rule-engine}"
+ notifications:
+ topic: "${TB_TRANSPORT_NOTIFICATIONS_TOPIC:tb.transport.notifications}"
+ poll_interval: "${TB_TRANSPORT_NOTIFICATIONS_POLL_INTERVAL_MS:25}"
+ auto_commit_interval: "${TB_TRANSPORT_NOTIFICATIONS_AUTO_COMMIT_INTERVAL_MS:100}"
diff --git a/transport/coap/src/main/scripts/control/deb/postinst b/transport/coap/src/main/scripts/control/deb/postinst
new file mode 100644
index 0000000..d4066c0
--- /dev/null
+++ b/transport/coap/src/main/scripts/control/deb/postinst
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+chown -R ${pkg.name}: ${pkg.logFolder}
+chown -R ${pkg.name}: ${pkg.installFolder}
+update-rc.d ${pkg.name} defaults
+
diff --git a/transport/coap/src/main/scripts/control/deb/postrm b/transport/coap/src/main/scripts/control/deb/postrm
new file mode 100644
index 0000000..6186580
--- /dev/null
+++ b/transport/coap/src/main/scripts/control/deb/postrm
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+update-rc.d -f ${pkg.name} remove
diff --git a/transport/coap/src/main/scripts/control/deb/preinst b/transport/coap/src/main/scripts/control/deb/preinst
new file mode 100644
index 0000000..6be5959
--- /dev/null
+++ b/transport/coap/src/main/scripts/control/deb/preinst
@@ -0,0 +1,18 @@
+#!/bin/sh
+
+if ! getent group ${pkg.name} >/dev/null; then
+ addgroup --system ${pkg.name}
+fi
+
+if ! getent passwd ${pkg.name} >/dev/null; then
+ adduser --quiet \
+ --system \
+ --ingroup ${pkg.name} \
+ --quiet \
+ --disabled-login \
+ --disabled-password \
+ --home ${pkg.installFolder} \
+ --no-create-home \
+ -gecos "Thingsboard application" \
+ ${pkg.name}
+fi
diff --git a/transport/coap/src/main/scripts/control/deb/prerm b/transport/coap/src/main/scripts/control/deb/prerm
new file mode 100644
index 0000000..898d3ef
--- /dev/null
+++ b/transport/coap/src/main/scripts/control/deb/prerm
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+if [ -e /var/run/${pkg.name}/${pkg.name}.pid ]; then
+ service ${pkg.name} stop
+fi
diff --git a/transport/coap/src/main/scripts/control/rpm/postinst b/transport/coap/src/main/scripts/control/rpm/postinst
new file mode 100644
index 0000000..8a7a88f
--- /dev/null
+++ b/transport/coap/src/main/scripts/control/rpm/postinst
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+chown -R ${pkg.name}: ${pkg.logFolder}
+chown -R ${pkg.name}: ${pkg.installFolder}
+
+if [ $1 -eq 1 ] ; then
+ # Initial installation
+ systemctl --no-reload enable ${pkg.name}.service >/dev/null 2>&1 || :
+fi
diff --git a/transport/coap/src/main/scripts/control/rpm/postrm b/transport/coap/src/main/scripts/control/rpm/postrm
new file mode 100644
index 0000000..8e1f8a2
--- /dev/null
+++ b/transport/coap/src/main/scripts/control/rpm/postrm
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+if [ $1 -ge 1 ] ; then
+ # Package upgrade, not uninstall
+ systemctl try-restart ${pkg.name}.service >/dev/null 2>&1 || :
+fi
diff --git a/transport/coap/src/main/scripts/control/rpm/preinst b/transport/coap/src/main/scripts/control/rpm/preinst
new file mode 100644
index 0000000..e19fc88
--- /dev/null
+++ b/transport/coap/src/main/scripts/control/rpm/preinst
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+getent group ${pkg.name} >/dev/null || groupadd -r ${pkg.name}
+getent passwd ${pkg.name} >/dev/null || \
+useradd -d ${pkg.installFolder} -g ${pkg.name} -M -r ${pkg.name} -s /sbin/nologin \
+-c "Thingsboard application"
diff --git a/transport/coap/src/main/scripts/control/rpm/prerm b/transport/coap/src/main/scripts/control/rpm/prerm
new file mode 100644
index 0000000..accb487
--- /dev/null
+++ b/transport/coap/src/main/scripts/control/rpm/prerm
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+if [ $1 -eq 0 ] ; then
+ # Package removal, not upgrade
+ systemctl --no-reload disable --now ${pkg.name}.service > /dev/null 2>&1 || :
+fi
diff --git a/transport/coap/src/main/scripts/control/tb-coap-transport.service b/transport/coap/src/main/scripts/control/tb-coap-transport.service
new file mode 100644
index 0000000..d456fc0
--- /dev/null
+++ b/transport/coap/src/main/scripts/control/tb-coap-transport.service
@@ -0,0 +1,11 @@
+[Unit]
+Description=${pkg.name}
+After=syslog.target
+
+[Service]
+User=${pkg.name}
+ExecStart=${pkg.installFolder}/bin/${pkg.name}.jar
+SuccessExitStatus=143
+
+[Install]
+WantedBy=multi-user.target
diff --git a/transport/coap/src/main/scripts/windows/install.bat b/transport/coap/src/main/scripts/windows/install.bat
new file mode 100644
index 0000000..dba7736
--- /dev/null
+++ b/transport/coap/src/main/scripts/windows/install.bat
@@ -0,0 +1,87 @@
+@ECHO OFF
+
+setlocal ENABLEEXTENSIONS
+
+@ECHO Detecting Java version installed.
+:CHECK_JAVA_64
+@ECHO Detecting if it is 64 bit machine
+set KEY_NAME="HKEY_LOCAL_MACHINE\Software\Wow6432Node\JavaSoft\Java Runtime Environment"
+set VALUE_NAME=CurrentVersion
+
+FOR /F "usebackq skip=2 tokens=1-3" %%A IN (`REG QUERY %KEY_NAME% /v %VALUE_NAME% 2^>nul`) DO (
+ set ValueName=%%A
+ set ValueType=%%B
+ set ValueValue=%%C
+)
+@ECHO CurrentVersion %ValueValue%
+
+SET KEY_NAME="%KEY_NAME:~1,-1%\%ValueValue%"
+SET VALUE_NAME=JavaHome
+
+if defined ValueName (
+ FOR /F "usebackq skip=2 tokens=1,2*" %%A IN (`REG QUERY %KEY_NAME% /v %VALUE_NAME% 2^>nul`) DO (
+ set ValueName2=%%A
+ set ValueType2=%%B
+ set JRE_PATH2=%%C
+
+ if defined ValueName2 (
+ set ValueName = %ValueName2%
+ set ValueType = %ValueType2%
+ set ValueValue = %JRE_PATH2%
+ )
+ )
+)
+
+IF NOT "%JRE_PATH2%" == "" GOTO JAVA_INSTALLED
+
+:CHECK_JAVA_32
+@ECHO Detecting if it is 32 bit machine
+set KEY_NAME="HKEY_LOCAL_MACHINE\Software\JavaSoft\Java Runtime Environment"
+set VALUE_NAME=CurrentVersion
+
+FOR /F "usebackq skip=2 tokens=1-3" %%A IN (`REG QUERY %KEY_NAME% /v %VALUE_NAME% 2^>nul`) DO (
+ set ValueName=%%A
+ set ValueType=%%B
+ set ValueValue=%%C
+)
+@ECHO CurrentVersion %ValueValue%
+
+SET KEY_NAME="%KEY_NAME:~1,-1%\%ValueValue%"
+SET VALUE_NAME=JavaHome
+
+if defined ValueName (
+ FOR /F "usebackq skip=2 tokens=1,2*" %%A IN (`REG QUERY %KEY_NAME% /v %VALUE_NAME% 2^>nul`) DO (
+ set ValueName2=%%A
+ set ValueType2=%%B
+ set JRE_PATH2=%%C
+
+ if defined ValueName2 (
+ set ValueName = %ValueName2%
+ set ValueType = %ValueType2%
+ set ValueValue = %JRE_PATH2%
+ )
+ )
+)
+
+IF "%JRE_PATH2%" == "" GOTO JAVA_NOT_INSTALLED
+
+:JAVA_INSTALLED
+
+@ECHO Java 1.8 found!
+@ECHO Installing ${pkg.name} ...
+
+%BASE%${pkg.name}.exe install
+
+@ECHO ${pkg.name} installed successfully!
+
+GOTO END
+
+:JAVA_NOT_INSTALLED
+@ECHO Java 1.8 or above is not installed
+@ECHO Please go to https://java.com/ and install Java. Then retry installation.
+PAUSE
+GOTO END
+
+:END
+
+
diff --git a/transport/coap/src/main/scripts/windows/service.xml b/transport/coap/src/main/scripts/windows/service.xml
new file mode 100644
index 0000000..f7b9d30
--- /dev/null
+++ b/transport/coap/src/main/scripts/windows/service.xml
@@ -0,0 +1,36 @@
+<service>
+ <id>${pkg.name}</id>
+ <name>${project.name}</name>
+ <description>${project.description}</description>
+ <workingdirectory>%BASE%\conf</workingdirectory>
+ <logpath>${pkg.winWrapperLogFolder}</logpath>
+ <logmode>rotate</logmode>
+ <env name="LOADER_PATH" value="%BASE%\conf" />
+ <executable>java</executable>
+ <startargument>-Xloggc:%BASE%\logs\gc.log</startargument>
+ <startargument>-XX:+HeapDumpOnOutOfMemoryError</startargument>
+ <startargument>-XX:+PrintGCDetails</startargument>
+ <startargument>-XX:+PrintGCDateStamps</startargument>
+ <startargument>-XX:+PrintHeapAtGC</startargument>
+ <startargument>-XX:+PrintTenuringDistribution</startargument>
+ <startargument>-XX:+PrintGCApplicationStoppedTime</startargument>
+ <startargument>-XX:+UseGCLogFileRotation</startargument>
+ <startargument>-XX:NumberOfGCLogFiles=10</startargument>
+ <startargument>-XX:GCLogFileSize=10M</startargument>
+ <startargument>-XX:-UseBiasedLocking</startargument>
+ <startargument>-XX:+UseTLAB</startargument>
+ <startargument>-XX:+ResizeTLAB</startargument>
+ <startargument>-XX:+PerfDisableSharedMem</startargument>
+ <startargument>-XX:+UseCondCardMark</startargument>
+ <startargument>-XX:CMSWaitDuration=10000</startargument>
+ <startargument>-XX:+UseParNewGC</startargument>
+ <startargument>-XX:+UseConcMarkSweepGC</startargument>
+ <startargument>-XX:+CMSParallelRemarkEnabled</startargument>
+ <startargument>-XX:+CMSParallelInitialMarkEnabled</startargument>
+ <startargument>-XX:+CMSEdenChunksRecordAlways</startargument>
+ <startargument>-XX:CMSInitiatingOccupancyFraction=75</startargument>
+ <startargument>-XX:+UseCMSInitiatingOccupancyOnly</startargument>
+ <startargument>-jar</startargument>
+ <startargument>%BASE%\lib\${pkg.name}.jar</startargument>
+
+</service>
diff --git a/transport/coap/src/main/scripts/windows/uninstall.bat b/transport/coap/src/main/scripts/windows/uninstall.bat
new file mode 100644
index 0000000..921e4c8
--- /dev/null
+++ b/transport/coap/src/main/scripts/windows/uninstall.bat
@@ -0,0 +1,9 @@
+@ECHO OFF
+
+@ECHO Stopping ${pkg.name} ...
+net stop ${pkg.name}
+
+@ECHO Uninstalling ${pkg.name} ...
+%~dp0${pkg.name}.exe uninstall
+
+@ECHO DONE.
\ No newline at end of file
transport/http/build.gradle 140(+140 -0)
diff --git a/transport/http/build.gradle b/transport/http/build.gradle
new file mode 100644
index 0000000..6d54cb4
--- /dev/null
+++ b/transport/http/build.gradle
@@ -0,0 +1,140 @@
+/**
+ * Copyright © 2016-2018 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import org.apache.tools.ant.filters.ReplaceTokens
+
+buildscript {
+ ext {
+ osPackageVersion = "3.8.0"
+ }
+ repositories {
+ jcenter()
+ }
+ dependencies {
+ classpath("com.netflix.nebula:gradle-ospackage-plugin:${osPackageVersion}")
+ }
+}
+
+apply plugin: "nebula.ospackage"
+
+buildDir = projectBuildDir
+version = projectVersion
+distsDirName = "./"
+
+// OS Package plugin configuration
+ospackage {
+ packageName = pkgName
+ version = "${project.version}"
+ release = 1
+ os = LINUX
+ type = BINARY
+
+ into pkgInstallFolder
+
+ user pkgName
+ permissionGroup pkgName
+
+ // Copy the actual .jar file
+ from(mainJar) {
+ // Strip the version from the jar filename
+ rename { String fileName ->
+ "${pkgName}.jar"
+ }
+ fileMode 0500
+ into "bin"
+ }
+
+ // Copy the config files
+ from("target/conf") {
+ exclude "${pkgName}.conf"
+ fileType CONFIG | NOREPLACE
+ fileMode 0754
+ into "conf"
+ }
+
+}
+
+// Configure our RPM build task
+buildRpm {
+
+ arch = NOARCH
+
+ version = projectVersion.replace('-', '')
+ archiveName = "${pkgName}.rpm"
+
+ requires("java-1.8.0")
+
+ from("target/conf") {
+ include "${pkgName}.conf"
+ filter(ReplaceTokens, tokens: ['pkg.platform': 'rpm'])
+ fileType CONFIG | NOREPLACE
+ fileMode 0754
+ into "${pkgInstallFolder}/conf"
+ }
+
+ preInstall file("${buildDir}/control/rpm/preinst")
+ postInstall file("${buildDir}/control/rpm/postinst")
+ preUninstall file("${buildDir}/control/rpm/prerm")
+ postUninstall file("${buildDir}/control/rpm/postrm")
+
+ user pkgName
+ permissionGroup pkgName
+
+ // Copy the system unit files
+ from("${buildDir}/control/${pkgName}.service") {
+ addParentDirs = false
+ fileMode 0644
+ into "/usr/lib/systemd/system"
+ }
+
+ directory(pkgLogFolder, 0755)
+ link("${pkgInstallFolder}/bin/${pkgName}.yml", "${pkgInstallFolder}/conf/${pkgName}.yml")
+ link("/etc/${pkgName}/conf", "${pkgInstallFolder}/conf")
+}
+
+// Same as the buildRpm task
+buildDeb {
+
+ arch = "all"
+
+ archiveName = "${pkgName}.deb"
+
+ requires("openjdk-8-jre").or("java8-runtime").or("oracle-java8-installer").or("openjdk-8-jre-headless")
+
+ from("target/conf") {
+ include "${pkgName}.conf"
+ filter(ReplaceTokens, tokens: ['pkg.platform': 'deb'])
+ fileType CONFIG | NOREPLACE
+ fileMode 0754
+ into "${pkgInstallFolder}/conf"
+ }
+
+ configurationFile("${pkgInstallFolder}/conf/${pkgName}.conf")
+ configurationFile("${pkgInstallFolder}/conf/${pkgName}.yml")
+ configurationFile("${pkgInstallFolder}/conf/logback.xml")
+
+ preInstall file("${buildDir}/control/deb/preinst")
+ postInstall file("${buildDir}/control/deb/postinst")
+ preUninstall file("${buildDir}/control/deb/prerm")
+ postUninstall file("${buildDir}/control/deb/postrm")
+
+ user pkgName
+ permissionGroup pkgName
+
+ directory(pkgLogFolder, 0755)
+ link("/etc/init.d/${pkgName}", "${pkgInstallFolder}/bin/${pkgName}.jar")
+ link("${pkgInstallFolder}/bin/${pkgName}.yml", "${pkgInstallFolder}/conf/${pkgName}.yml")
+ link("/etc/${pkgName}/conf", "${pkgInstallFolder}/conf")
+}
transport/http/pom.xml 296(+278 -18)
diff --git a/transport/http/pom.xml b/transport/http/pom.xml
index 9964708..407d995 100644
--- a/transport/http/pom.xml
+++ b/transport/http/pom.xml
@@ -27,39 +27,37 @@
<artifactId>http</artifactId>
<packaging>jar</packaging>
- <name>Thingsboard HTTP Transport</name>
+ <name>Thingsboard HTTP Transport Service</name>
<url>https://thingsboard.io</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<main.dir>${basedir}/../..</main.dir>
+ <pkg.name>tb-http-transport</pkg.name>
+ <pkg.unixLogFolder>/var/log/${pkg.name}</pkg.unixLogFolder>
+ <pkg.installFolder>/usr/share/${pkg.name}</pkg.installFolder>
+ <pkg.win.dist>${project.build.directory}/windows</pkg.win.dist>
</properties>
<dependencies>
<dependency>
- <groupId>org.thingsboard.common</groupId>
- <artifactId>transport</artifactId>
- </dependency>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-web</artifactId>
- <scope>provided</scope>
+ <groupId>org.thingsboard.common.transport</groupId>
+ <artifactId>http</artifactId>
</dependency>
<dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</artifactId>
- </dependency>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>log4j-over-slf4j</artifactId>
+ <groupId>org.thingsboard.common</groupId>
+ <artifactId>queue</artifactId>
</dependency>
<dependency>
- <groupId>ch.qos.logback</groupId>
- <artifactId>logback-core</artifactId>
+ <groupId>com.sun.winsw</groupId>
+ <artifactId>winsw</artifactId>
+ <classifier>bin</classifier>
+ <type>exe</type>
+ <scope>provided</scope>
</dependency>
<dependency>
- <groupId>ch.qos.logback</groupId>
- <artifactId>logback-classic</artifactId>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
@@ -78,4 +76,266 @@
</dependency>
</dependencies>
+ <build>
+ <finalName>${pkg.name}-${project.version}</finalName>
+ <resources>
+ <resource>
+ <directory>${project.basedir}/src/main/resources</directory>
+ </resource>
+ </resources>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-resources-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>copy-conf</id>
+ <phase>process-resources</phase>
+ <goals>
+ <goal>copy-resources</goal>
+ </goals>
+ <configuration>
+ <outputDirectory>${project.build.directory}/conf</outputDirectory>
+ <resources>
+ <resource>
+ <directory>src/main/resources</directory>
+ <excludes>
+ <exclude>logback.xml</exclude>
+ </excludes>
+ <filtering>false</filtering>
+ </resource>
+ </resources>
+ </configuration>
+ </execution>
+ <execution>
+ <id>copy-service-conf</id>
+ <phase>process-resources</phase>
+ <goals>
+ <goal>copy-resources</goal>
+ </goals>
+ <configuration>
+ <outputDirectory>${project.build.directory}/conf</outputDirectory>
+ <resources>
+ <resource>
+ <directory>src/main/conf</directory>
+ <filtering>true</filtering>
+ </resource>
+ </resources>
+ <filters>
+ <filter>src/main/filters/unix.properties</filter>
+ </filters>
+ </configuration>
+ </execution>
+ <execution>
+ <id>copy-win-conf</id>
+ <phase>process-resources</phase>
+ <goals>
+ <goal>copy-resources</goal>
+ </goals>
+ <configuration>
+ <outputDirectory>${pkg.win.dist}/conf</outputDirectory>
+ <resources>
+ <resource>
+ <directory>src/main/resources</directory>
+ <excludes>
+ <exclude>logback.xml</exclude>
+ </excludes>
+ <filtering>false</filtering>
+ </resource>
+ <resource>
+ <directory>src/main/conf</directory>
+ <excludes>
+ <exclude>tb-http-transport.conf</exclude>
+ </excludes>
+ <filtering>true</filtering>
+ </resource>
+ </resources>
+ <filters>
+ <filter>src/main/filters/windows.properties</filter>
+ </filters>
+ </configuration>
+ </execution>
+ <execution>
+ <id>copy-control</id>
+ <phase>process-resources</phase>
+ <goals>
+ <goal>copy-resources</goal>
+ </goals>
+ <configuration>
+ <outputDirectory>${project.build.directory}/control</outputDirectory>
+ <resources>
+ <resource>
+ <directory>src/main/scripts/control</directory>
+ <filtering>true</filtering>
+ </resource>
+ </resources>
+ <filters>
+ <filter>src/main/filters/unix.properties</filter>
+ </filters>
+ </configuration>
+ </execution>
+ <execution>
+ <id>copy-windows-control</id>
+ <phase>process-resources</phase>
+ <goals>
+ <goal>copy-resources</goal>
+ </goals>
+ <configuration>
+ <outputDirectory>${pkg.win.dist}</outputDirectory>
+ <resources>
+ <resource>
+ <directory>src/main/scripts/windows</directory>
+ <filtering>true</filtering>
+ </resource>
+ </resources>
+ <filters>
+ <filter>src/main/filters/windows.properties</filter>
+ </filters>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-dependency-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>copy-winsw-service</id>
+ <phase>package</phase>
+ <goals>
+ <goal>copy</goal>
+ </goals>
+ <configuration>
+ <artifactItems>
+ <artifactItem>
+ <groupId>com.sun.winsw</groupId>
+ <artifactId>winsw</artifactId>
+ <classifier>bin</classifier>
+ <type>exe</type>
+ <destFileName>service.exe</destFileName>
+ </artifactItem>
+ </artifactItems>
+ <outputDirectory>${pkg.win.dist}</outputDirectory>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ <configuration>
+ <excludes>
+ <exclude>**/logback.xml</exclude>
+ </excludes>
+ <archive>
+ <manifestEntries>
+ <Implementation-Title>ThingsBoard HTTP Transport Service</Implementation-Title>
+ <Implementation-Version>${project.version}</Implementation-Version>
+ </manifestEntries>
+ </archive>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-maven-plugin</artifactId>
+ <configuration>
+ <mainClass>org.thingsboard.server.http.ThingsboardHttpTransportApplication</mainClass>
+ <classifier>boot</classifier>
+ <layout>ZIP</layout>
+ <executable>true</executable>
+ <excludeDevtools>true</excludeDevtools>
+ <embeddedLaunchScriptProperties>
+ <confFolder>${pkg.installFolder}/conf</confFolder>
+ <logFolder>${pkg.unixLogFolder}</logFolder>
+ <logFilename>${pkg.name}.out</logFilename>
+ <initInfoProvides>${pkg.name}</initInfoProvides>
+ </embeddedLaunchScriptProperties>
+ </configuration>
+ <executions>
+ <execution>
+ <goals>
+ <goal>repackage</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.fortasoft</groupId>
+ <artifactId>gradle-maven-plugin</artifactId>
+ <configuration>
+ <tasks>
+ <task>build</task>
+ <task>buildDeb</task>
+ <task>buildRpm</task>
+ </tasks>
+ <args>
+ <arg>-PprojectBuildDir=${project.build.directory}</arg>
+ <arg>-PprojectVersion=${project.version}</arg>
+ <arg>-PmainJar=${project.build.directory}/${project.build.finalName}-boot.${project.packaging}</arg>
+ <arg>-PpkgName=${pkg.name}</arg>
+ <arg>-PpkgInstallFolder=${pkg.installFolder}</arg>
+ <arg>-PpkgLogFolder=${pkg.unixLogFolder}</arg>
+ </args>
+ </configuration>
+ <executions>
+ <execution>
+ <phase>package</phase>
+ <goals>
+ <goal>invoke</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <configuration>
+ <finalName>${pkg.name}</finalName>
+ <descriptors>
+ <descriptor>src/main/assembly/windows.xml</descriptor>
+ </descriptors>
+ </configuration>
+ <executions>
+ <execution>
+ <id>assembly</id>
+ <phase>package</phase>
+ <goals>
+ <goal>single</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-install-plugin</artifactId>
+ <configuration>
+ <file>${project.build.directory}/${pkg.name}.deb</file>
+ <artifactId>${project.artifactId}</artifactId>
+ <groupId>${project.groupId}</groupId>
+ <version>${project.version}</version>
+ <classifier>deb</classifier>
+ <packaging>deb</packaging>
+ </configuration>
+ <executions>
+ <execution>
+ <id>install-deb</id>
+ <phase>package</phase>
+ <goals>
+ <goal>install-file</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ <repositories>
+ <repository>
+ <id>jenkins</id>
+ <name>Jenkins Repository</name>
+ <url>http://repo.jenkins-ci.org/releases</url>
+ <snapshots>
+ <enabled>false</enabled>
+ </snapshots>
+ </repository>
+ </repositories>
</project>
transport/http/src/main/assembly/windows.xml 71(+71 -0)
diff --git a/transport/http/src/main/assembly/windows.xml b/transport/http/src/main/assembly/windows.xml
new file mode 100644
index 0000000..82da34e
--- /dev/null
+++ b/transport/http/src/main/assembly/windows.xml
@@ -0,0 +1,71 @@
+<!--
+
+ Copyright © 2016-2018 The Thingsboard Authors
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3 http://maven.apache.org/xsd/assembly-1.1.3.xsd">
+ <id>windows</id>
+
+ <formats>
+ <format>zip</format>
+ </formats>
+
+ <!-- Workaround to create logs directory -->
+ <fileSets>
+ <fileSet>
+ <directory>${pkg.win.dist}</directory>
+ <outputDirectory>logs</outputDirectory>
+ <excludes>
+ <exclude>*/**</exclude>
+ </excludes>
+ </fileSet>
+ <fileSet>
+ <directory>${pkg.win.dist}/conf</directory>
+ <outputDirectory>conf</outputDirectory>
+ <lineEnding>windows</lineEnding>
+ </fileSet>
+ </fileSets>
+
+ <files>
+ <file>
+ <source>${project.build.directory}/${project.build.finalName}-boot.${project.packaging}</source>
+ <outputDirectory>lib</outputDirectory>
+ <destName>${pkg.name}.jar</destName>
+ </file>
+ <file>
+ <source>${pkg.win.dist}/service.exe</source>
+ <outputDirectory/>
+ <destName>${pkg.name}.exe</destName>
+ </file>
+ <file>
+ <source>${pkg.win.dist}/service.xml</source>
+ <outputDirectory/>
+ <destName>${pkg.name}.xml</destName>
+ <lineEnding>windows</lineEnding>
+ </file>
+ <file>
+ <source>${pkg.win.dist}/install.bat</source>
+ <outputDirectory/>
+ <lineEnding>windows</lineEnding>
+ </file>
+ <file>
+ <source>${pkg.win.dist}/uninstall.bat</source>
+ <outputDirectory/>
+ <lineEnding>windows</lineEnding>
+ </file>
+ </files>
+</assembly>
transport/http/src/main/conf/logback.xml 43(+43 -0)
diff --git a/transport/http/src/main/conf/logback.xml b/transport/http/src/main/conf/logback.xml
new file mode 100644
index 0000000..f36469d
--- /dev/null
+++ b/transport/http/src/main/conf/logback.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+
+ Copyright © 2016-2018 The Thingsboard Authors
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+<!DOCTYPE configuration>
+<configuration>
+
+ <appender name="fileLogAppender"
+ class="ch.qos.logback.core.rolling.RollingFileAppender">
+ <file>${pkg.logFolder}/${pkg.name}.log</file>
+ <rollingPolicy
+ class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
+ <fileNamePattern>${pkg.logFolder}/${pkg.name}.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
+ <maxFileSize>100MB</maxFileSize>
+ <maxHistory>30</maxHistory>
+ <totalSizeCap>3GB</totalSizeCap>
+ </rollingPolicy>
+ <encoder>
+ <pattern>%d{ISO8601} [%thread] %-5level %logger{36} - %msg%n</pattern>
+ </encoder>
+ </appender>
+
+ <logger name="org.thingsboard.server" level="INFO" />
+
+ <root level="INFO">
+ <appender-ref ref="fileLogAppender"/>
+ </root>
+
+</configuration>
diff --git a/transport/http/src/main/conf/tb-http-transport.conf b/transport/http/src/main/conf/tb-http-transport.conf
new file mode 100644
index 0000000..0afa91c
--- /dev/null
+++ b/transport/http/src/main/conf/tb-http-transport.conf
@@ -0,0 +1,23 @@
+#
+# Copyright © 2016-2018 The Thingsboard Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+export JAVA_OPTS="$JAVA_OPTS -Xloggc:@pkg.logFolder@/gc.log -XX:+IgnoreUnrecognizedVMOptions -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDetails -XX:+PrintGCDateStamps"
+export JAVA_OPTS="$JAVA_OPTS -XX:+PrintHeapAtGC -XX:+PrintTenuringDistribution -XX:+PrintGCApplicationStoppedTime -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10"
+export JAVA_OPTS="$JAVA_OPTS -XX:GCLogFileSize=10M -XX:-UseBiasedLocking -XX:+UseTLAB -XX:+ResizeTLAB -XX:+PerfDisableSharedMem -XX:+UseCondCardMark"
+export JAVA_OPTS="$JAVA_OPTS -XX:CMSWaitDuration=10000 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+CMSParallelInitialMarkEnabled"
+export JAVA_OPTS="$JAVA_OPTS -XX:+CMSEdenChunksRecordAlways -XX:CMSInitiatingOccupancyFraction=75 -XX:+UseCMSInitiatingOccupancyOnly"
+export LOG_FILENAME=${pkg.name}.out
+export LOADER_PATH=${pkg.installFolder}/conf
diff --git a/transport/http/src/main/filters/unix.properties b/transport/http/src/main/filters/unix.properties
new file mode 100644
index 0000000..8967278
--- /dev/null
+++ b/transport/http/src/main/filters/unix.properties
@@ -0,0 +1 @@
+pkg.logFolder=${pkg.unixLogFolder}
\ No newline at end of file
diff --git a/transport/http/src/main/filters/windows.properties b/transport/http/src/main/filters/windows.properties
new file mode 100644
index 0000000..a6e48d9
--- /dev/null
+++ b/transport/http/src/main/filters/windows.properties
@@ -0,0 +1,2 @@
+pkg.logFolder=${BASE}\\logs
+pkg.winWrapperLogFolder=%BASE%\\logs
diff --git a/transport/http/src/main/resources/logback.xml b/transport/http/src/main/resources/logback.xml
new file mode 100644
index 0000000..18864a9
--- /dev/null
+++ b/transport/http/src/main/resources/logback.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+
+ Copyright © 2016-2018 The Thingsboard Authors
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+<!DOCTYPE configuration>
+<configuration scan="true" scanPeriod="10 seconds">
+
+ <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+ <encoder>
+ <pattern>%d{ISO8601} [%thread] %-5level %logger{36} - %msg%n</pattern>
+ </encoder>
+ </appender>
+
+ <logger name="org.thingsboard.server" level="TRACE" />
+
+ <root level="INFO">
+ <appender-ref ref="STDOUT"/>
+ </root>
+
+</configuration>
\ No newline at end of file
diff --git a/transport/http/src/main/resources/tb-http-transport.yml b/transport/http/src/main/resources/tb-http-transport.yml
new file mode 100644
index 0000000..65a1ad9
--- /dev/null
+++ b/transport/http/src/main/resources/tb-http-transport.yml
@@ -0,0 +1,69 @@
+#
+# Copyright © 2016-2018 The Thingsboard Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+server:
+ # Server bind address
+ address: "${HTTP_BIND_ADDRESS:0.0.0.0}"
+ # Server bind port
+ port: "${HTTP_BIND_PORT:8081}"
+
+# HTTP server parameters
+transport:
+ http:
+ request_timeout: "${HTTP_REQUEST_TIMEOUT:60000}"
+
+#Quota parameters
+quota:
+ host:
+ # Max allowed number of API requests in interval for single host
+ limit: "${QUOTA_HOST_LIMIT:10000}"
+ # Interval duration
+ intervalMs: "${QUOTA_HOST_INTERVAL_MS:60000}"
+ # Maximum silence duration for host after which Host removed from QuotaService. Must be bigger than intervalMs
+ ttlMs: "${QUOTA_HOST_TTL_MS:60000}"
+ # Interval for scheduled task that cleans expired records. TTL is used for expiring
+ cleanPeriodMs: "${QUOTA_HOST_CLEAN_PERIOD_MS:300000}"
+ # Enable Host API Limits
+ enabled: "${QUOTA_HOST_ENABLED:true}"
+ # Array of whitelist hosts
+ whitelist: "${QUOTA_HOST_WHITELIST:localhost,127.0.0.1}"
+ # Array of blacklist hosts
+ blacklist: "${QUOTA_HOST_BLACKLIST:}"
+ log:
+ topSize: 10
+ intervalMin: 2
+
+kafka:
+ enabled: true
+ bootstrap.servers: "${TB_KAFKA_SERVERS:localhost:9092}"
+ acks: "${TB_KAFKA_ACKS:all}"
+ retries: "${TB_KAFKA_RETRIES:1}"
+ batch.size: "${TB_KAFKA_BATCH_SIZE:16384}"
+ linger.ms: "${TB_KAFKA_LINGER_MS:1}"
+ buffer.memory: "${TB_BUFFER_MEMORY:33554432}"
+ transport_api:
+ requests_topic: "${TB_TRANSPORT_API_REQUEST_TOPIC:tb.transport.api.requests}"
+ responses_topic: "${TB_TRANSPORT_API_RESPONSE_TOPIC:tb.transport.api.responses}"
+ max_pending_requests: "${TB_TRANSPORT_MAX_PENDING_REQUESTS:10000}"
+ max_requests_timeout: "${TB_TRANSPORT_MAX_REQUEST_TIMEOUT:10000}"
+ response_poll_interval: "${TB_TRANSPORT_RESPONSE_POLL_INTERVAL_MS:25}"
+ response_auto_commit_interval: "${TB_TRANSPORT_RESPONSE_AUTO_COMMIT_INTERVAL_MS:100}"
+ rule_engine:
+ topic: "${TB_RULE_ENGINE_TOPIC:tb.rule-engine}"
+ notifications:
+ topic: "${TB_TRANSPORT_NOTIFICATIONS_TOPIC:tb.transport.notifications}"
+ poll_interval: "${TB_TRANSPORT_NOTIFICATIONS_POLL_INTERVAL_MS:25}"
+ auto_commit_interval: "${TB_TRANSPORT_NOTIFICATIONS_AUTO_COMMIT_INTERVAL_MS:100}"
diff --git a/transport/http/src/main/scripts/control/deb/postinst b/transport/http/src/main/scripts/control/deb/postinst
new file mode 100644
index 0000000..d4066c0
--- /dev/null
+++ b/transport/http/src/main/scripts/control/deb/postinst
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+chown -R ${pkg.name}: ${pkg.logFolder}
+chown -R ${pkg.name}: ${pkg.installFolder}
+update-rc.d ${pkg.name} defaults
+
diff --git a/transport/http/src/main/scripts/control/deb/postrm b/transport/http/src/main/scripts/control/deb/postrm
new file mode 100644
index 0000000..6186580
--- /dev/null
+++ b/transport/http/src/main/scripts/control/deb/postrm
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+update-rc.d -f ${pkg.name} remove
diff --git a/transport/http/src/main/scripts/control/deb/preinst b/transport/http/src/main/scripts/control/deb/preinst
new file mode 100644
index 0000000..6be5959
--- /dev/null
+++ b/transport/http/src/main/scripts/control/deb/preinst
@@ -0,0 +1,18 @@
+#!/bin/sh
+
+if ! getent group ${pkg.name} >/dev/null; then
+ addgroup --system ${pkg.name}
+fi
+
+if ! getent passwd ${pkg.name} >/dev/null; then
+ adduser --quiet \
+ --system \
+ --ingroup ${pkg.name} \
+ --quiet \
+ --disabled-login \
+ --disabled-password \
+ --home ${pkg.installFolder} \
+ --no-create-home \
+ -gecos "Thingsboard application" \
+ ${pkg.name}
+fi
diff --git a/transport/http/src/main/scripts/control/deb/prerm b/transport/http/src/main/scripts/control/deb/prerm
new file mode 100644
index 0000000..898d3ef
--- /dev/null
+++ b/transport/http/src/main/scripts/control/deb/prerm
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+if [ -e /var/run/${pkg.name}/${pkg.name}.pid ]; then
+ service ${pkg.name} stop
+fi
diff --git a/transport/http/src/main/scripts/control/rpm/postinst b/transport/http/src/main/scripts/control/rpm/postinst
new file mode 100644
index 0000000..8a7a88f
--- /dev/null
+++ b/transport/http/src/main/scripts/control/rpm/postinst
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+chown -R ${pkg.name}: ${pkg.logFolder}
+chown -R ${pkg.name}: ${pkg.installFolder}
+
+if [ $1 -eq 1 ] ; then
+ # Initial installation
+ systemctl --no-reload enable ${pkg.name}.service >/dev/null 2>&1 || :
+fi
diff --git a/transport/http/src/main/scripts/control/rpm/postrm b/transport/http/src/main/scripts/control/rpm/postrm
new file mode 100644
index 0000000..8e1f8a2
--- /dev/null
+++ b/transport/http/src/main/scripts/control/rpm/postrm
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+if [ $1 -ge 1 ] ; then
+ # Package upgrade, not uninstall
+ systemctl try-restart ${pkg.name}.service >/dev/null 2>&1 || :
+fi
diff --git a/transport/http/src/main/scripts/control/rpm/preinst b/transport/http/src/main/scripts/control/rpm/preinst
new file mode 100644
index 0000000..e19fc88
--- /dev/null
+++ b/transport/http/src/main/scripts/control/rpm/preinst
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+getent group ${pkg.name} >/dev/null || groupadd -r ${pkg.name}
+getent passwd ${pkg.name} >/dev/null || \
+useradd -d ${pkg.installFolder} -g ${pkg.name} -M -r ${pkg.name} -s /sbin/nologin \
+-c "Thingsboard application"
diff --git a/transport/http/src/main/scripts/control/rpm/prerm b/transport/http/src/main/scripts/control/rpm/prerm
new file mode 100644
index 0000000..accb487
--- /dev/null
+++ b/transport/http/src/main/scripts/control/rpm/prerm
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+if [ $1 -eq 0 ] ; then
+ # Package removal, not upgrade
+ systemctl --no-reload disable --now ${pkg.name}.service > /dev/null 2>&1 || :
+fi
diff --git a/transport/http/src/main/scripts/control/tb-http-transport.service b/transport/http/src/main/scripts/control/tb-http-transport.service
new file mode 100644
index 0000000..d456fc0
--- /dev/null
+++ b/transport/http/src/main/scripts/control/tb-http-transport.service
@@ -0,0 +1,11 @@
+[Unit]
+Description=${pkg.name}
+After=syslog.target
+
+[Service]
+User=${pkg.name}
+ExecStart=${pkg.installFolder}/bin/${pkg.name}.jar
+SuccessExitStatus=143
+
+[Install]
+WantedBy=multi-user.target
diff --git a/transport/http/src/main/scripts/windows/install.bat b/transport/http/src/main/scripts/windows/install.bat
new file mode 100644
index 0000000..dba7736
--- /dev/null
+++ b/transport/http/src/main/scripts/windows/install.bat
@@ -0,0 +1,87 @@
+@ECHO OFF
+
+setlocal ENABLEEXTENSIONS
+
+@ECHO Detecting Java version installed.
+:CHECK_JAVA_64
+@ECHO Detecting if it is 64 bit machine
+set KEY_NAME="HKEY_LOCAL_MACHINE\Software\Wow6432Node\JavaSoft\Java Runtime Environment"
+set VALUE_NAME=CurrentVersion
+
+FOR /F "usebackq skip=2 tokens=1-3" %%A IN (`REG QUERY %KEY_NAME% /v %VALUE_NAME% 2^>nul`) DO (
+ set ValueName=%%A
+ set ValueType=%%B
+ set ValueValue=%%C
+)
+@ECHO CurrentVersion %ValueValue%
+
+SET KEY_NAME="%KEY_NAME:~1,-1%\%ValueValue%"
+SET VALUE_NAME=JavaHome
+
+if defined ValueName (
+ FOR /F "usebackq skip=2 tokens=1,2*" %%A IN (`REG QUERY %KEY_NAME% /v %VALUE_NAME% 2^>nul`) DO (
+ set ValueName2=%%A
+ set ValueType2=%%B
+ set JRE_PATH2=%%C
+
+ if defined ValueName2 (
+ set ValueName = %ValueName2%
+ set ValueType = %ValueType2%
+ set ValueValue = %JRE_PATH2%
+ )
+ )
+)
+
+IF NOT "%JRE_PATH2%" == "" GOTO JAVA_INSTALLED
+
+:CHECK_JAVA_32
+@ECHO Detecting if it is 32 bit machine
+set KEY_NAME="HKEY_LOCAL_MACHINE\Software\JavaSoft\Java Runtime Environment"
+set VALUE_NAME=CurrentVersion
+
+FOR /F "usebackq skip=2 tokens=1-3" %%A IN (`REG QUERY %KEY_NAME% /v %VALUE_NAME% 2^>nul`) DO (
+ set ValueName=%%A
+ set ValueType=%%B
+ set ValueValue=%%C
+)
+@ECHO CurrentVersion %ValueValue%
+
+SET KEY_NAME="%KEY_NAME:~1,-1%\%ValueValue%"
+SET VALUE_NAME=JavaHome
+
+if defined ValueName (
+ FOR /F "usebackq skip=2 tokens=1,2*" %%A IN (`REG QUERY %KEY_NAME% /v %VALUE_NAME% 2^>nul`) DO (
+ set ValueName2=%%A
+ set ValueType2=%%B
+ set JRE_PATH2=%%C
+
+ if defined ValueName2 (
+ set ValueName = %ValueName2%
+ set ValueType = %ValueType2%
+ set ValueValue = %JRE_PATH2%
+ )
+ )
+)
+
+IF "%JRE_PATH2%" == "" GOTO JAVA_NOT_INSTALLED
+
+:JAVA_INSTALLED
+
+@ECHO Java 1.8 found!
+@ECHO Installing ${pkg.name} ...
+
+%BASE%${pkg.name}.exe install
+
+@ECHO ${pkg.name} installed successfully!
+
+GOTO END
+
+:JAVA_NOT_INSTALLED
+@ECHO Java 1.8 or above is not installed
+@ECHO Please go to https://java.com/ and install Java. Then retry installation.
+PAUSE
+GOTO END
+
+:END
+
+
diff --git a/transport/http/src/main/scripts/windows/service.xml b/transport/http/src/main/scripts/windows/service.xml
new file mode 100644
index 0000000..f7b9d30
--- /dev/null
+++ b/transport/http/src/main/scripts/windows/service.xml
@@ -0,0 +1,36 @@
+<service>
+ <id>${pkg.name}</id>
+ <name>${project.name}</name>
+ <description>${project.description}</description>
+ <workingdirectory>%BASE%\conf</workingdirectory>
+ <logpath>${pkg.winWrapperLogFolder}</logpath>
+ <logmode>rotate</logmode>
+ <env name="LOADER_PATH" value="%BASE%\conf" />
+ <executable>java</executable>
+ <startargument>-Xloggc:%BASE%\logs\gc.log</startargument>
+ <startargument>-XX:+HeapDumpOnOutOfMemoryError</startargument>
+ <startargument>-XX:+PrintGCDetails</startargument>
+ <startargument>-XX:+PrintGCDateStamps</startargument>
+ <startargument>-XX:+PrintHeapAtGC</startargument>
+ <startargument>-XX:+PrintTenuringDistribution</startargument>
+ <startargument>-XX:+PrintGCApplicationStoppedTime</startargument>
+ <startargument>-XX:+UseGCLogFileRotation</startargument>
+ <startargument>-XX:NumberOfGCLogFiles=10</startargument>
+ <startargument>-XX:GCLogFileSize=10M</startargument>
+ <startargument>-XX:-UseBiasedLocking</startargument>
+ <startargument>-XX:+UseTLAB</startargument>
+ <startargument>-XX:+ResizeTLAB</startargument>
+ <startargument>-XX:+PerfDisableSharedMem</startargument>
+ <startargument>-XX:+UseCondCardMark</startargument>
+ <startargument>-XX:CMSWaitDuration=10000</startargument>
+ <startargument>-XX:+UseParNewGC</startargument>
+ <startargument>-XX:+UseConcMarkSweepGC</startargument>
+ <startargument>-XX:+CMSParallelRemarkEnabled</startargument>
+ <startargument>-XX:+CMSParallelInitialMarkEnabled</startargument>
+ <startargument>-XX:+CMSEdenChunksRecordAlways</startargument>
+ <startargument>-XX:CMSInitiatingOccupancyFraction=75</startargument>
+ <startargument>-XX:+UseCMSInitiatingOccupancyOnly</startargument>
+ <startargument>-jar</startargument>
+ <startargument>%BASE%\lib\${pkg.name}.jar</startargument>
+
+</service>
diff --git a/transport/http/src/main/scripts/windows/uninstall.bat b/transport/http/src/main/scripts/windows/uninstall.bat
new file mode 100644
index 0000000..921e4c8
--- /dev/null
+++ b/transport/http/src/main/scripts/windows/uninstall.bat
@@ -0,0 +1,9 @@
+@ECHO OFF
+
+@ECHO Stopping ${pkg.name} ...
+net stop ${pkg.name}
+
+@ECHO Uninstalling ${pkg.name} ...
+%~dp0${pkg.name}.exe uninstall
+
+@ECHO DONE.
\ No newline at end of file
transport/mqtt/build.gradle 140(+140 -0)
diff --git a/transport/mqtt/build.gradle b/transport/mqtt/build.gradle
new file mode 100644
index 0000000..6d54cb4
--- /dev/null
+++ b/transport/mqtt/build.gradle
@@ -0,0 +1,140 @@
+/**
+ * Copyright © 2016-2018 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import org.apache.tools.ant.filters.ReplaceTokens
+
+buildscript {
+ ext {
+ osPackageVersion = "3.8.0"
+ }
+ repositories {
+ jcenter()
+ }
+ dependencies {
+ classpath("com.netflix.nebula:gradle-ospackage-plugin:${osPackageVersion}")
+ }
+}
+
+apply plugin: "nebula.ospackage"
+
+buildDir = projectBuildDir
+version = projectVersion
+distsDirName = "./"
+
+// OS Package plugin configuration
+ospackage {
+ packageName = pkgName
+ version = "${project.version}"
+ release = 1
+ os = LINUX
+ type = BINARY
+
+ into pkgInstallFolder
+
+ user pkgName
+ permissionGroup pkgName
+
+ // Copy the actual .jar file
+ from(mainJar) {
+ // Strip the version from the jar filename
+ rename { String fileName ->
+ "${pkgName}.jar"
+ }
+ fileMode 0500
+ into "bin"
+ }
+
+ // Copy the config files
+ from("target/conf") {
+ exclude "${pkgName}.conf"
+ fileType CONFIG | NOREPLACE
+ fileMode 0754
+ into "conf"
+ }
+
+}
+
+// Configure our RPM build task
+buildRpm {
+
+ arch = NOARCH
+
+ version = projectVersion.replace('-', '')
+ archiveName = "${pkgName}.rpm"
+
+ requires("java-1.8.0")
+
+ from("target/conf") {
+ include "${pkgName}.conf"
+ filter(ReplaceTokens, tokens: ['pkg.platform': 'rpm'])
+ fileType CONFIG | NOREPLACE
+ fileMode 0754
+ into "${pkgInstallFolder}/conf"
+ }
+
+ preInstall file("${buildDir}/control/rpm/preinst")
+ postInstall file("${buildDir}/control/rpm/postinst")
+ preUninstall file("${buildDir}/control/rpm/prerm")
+ postUninstall file("${buildDir}/control/rpm/postrm")
+
+ user pkgName
+ permissionGroup pkgName
+
+ // Copy the system unit files
+ from("${buildDir}/control/${pkgName}.service") {
+ addParentDirs = false
+ fileMode 0644
+ into "/usr/lib/systemd/system"
+ }
+
+ directory(pkgLogFolder, 0755)
+ link("${pkgInstallFolder}/bin/${pkgName}.yml", "${pkgInstallFolder}/conf/${pkgName}.yml")
+ link("/etc/${pkgName}/conf", "${pkgInstallFolder}/conf")
+}
+
+// Same as the buildRpm task
+buildDeb {
+
+ arch = "all"
+
+ archiveName = "${pkgName}.deb"
+
+ requires("openjdk-8-jre").or("java8-runtime").or("oracle-java8-installer").or("openjdk-8-jre-headless")
+
+ from("target/conf") {
+ include "${pkgName}.conf"
+ filter(ReplaceTokens, tokens: ['pkg.platform': 'deb'])
+ fileType CONFIG | NOREPLACE
+ fileMode 0754
+ into "${pkgInstallFolder}/conf"
+ }
+
+ configurationFile("${pkgInstallFolder}/conf/${pkgName}.conf")
+ configurationFile("${pkgInstallFolder}/conf/${pkgName}.yml")
+ configurationFile("${pkgInstallFolder}/conf/logback.xml")
+
+ preInstall file("${buildDir}/control/deb/preinst")
+ postInstall file("${buildDir}/control/deb/postinst")
+ preUninstall file("${buildDir}/control/deb/prerm")
+ postUninstall file("${buildDir}/control/deb/postrm")
+
+ user pkgName
+ permissionGroup pkgName
+
+ directory(pkgLogFolder, 0755)
+ link("/etc/init.d/${pkgName}", "${pkgInstallFolder}/bin/${pkgName}.jar")
+ link("${pkgInstallFolder}/bin/${pkgName}.yml", "${pkgInstallFolder}/conf/${pkgName}.yml")
+ link("/etc/${pkgName}/conf", "${pkgInstallFolder}/conf")
+}
transport/mqtt/pom.xml 303(+278 -25)
diff --git a/transport/mqtt/pom.xml b/transport/mqtt/pom.xml
index 785e9a5..6395b40 100644
--- a/transport/mqtt/pom.xml
+++ b/transport/mqtt/pom.xml
@@ -27,46 +27,37 @@
<artifactId>mqtt</artifactId>
<packaging>jar</packaging>
- <name>Thingsboard MQTT Transport</name>
+ <name>Thingsboard MQTT Transport Service</name>
<url>https://thingsboard.io</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<main.dir>${basedir}/../..</main.dir>
+ <pkg.name>tb-mqtt-transport</pkg.name>
+ <pkg.unixLogFolder>/var/log/${pkg.name}</pkg.unixLogFolder>
+ <pkg.installFolder>/usr/share/${pkg.name}</pkg.installFolder>
+ <pkg.win.dist>${project.build.directory}/windows</pkg.win.dist>
</properties>
<dependencies>
<dependency>
- <groupId>org.thingsboard.common</groupId>
- <artifactId>transport</artifactId>
- </dependency>
- <dependency>
- <groupId>io.netty</groupId>
- <artifactId>netty-all</artifactId>
+ <groupId>org.thingsboard.common.transport</groupId>
+ <artifactId>mqtt</artifactId>
</dependency>
<dependency>
- <groupId>org.springframework</groupId>
- <artifactId>spring-context</artifactId>
- </dependency>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</artifactId>
- </dependency>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>log4j-over-slf4j</artifactId>
- </dependency>
- <dependency>
- <groupId>ch.qos.logback</groupId>
- <artifactId>logback-core</artifactId>
+ <groupId>org.thingsboard.common</groupId>
+ <artifactId>queue</artifactId>
</dependency>
<dependency>
- <groupId>ch.qos.logback</groupId>
- <artifactId>logback-classic</artifactId>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
- <groupId>com.google.guava</groupId>
- <artifactId>guava</artifactId>
+ <groupId>com.sun.winsw</groupId>
+ <artifactId>winsw</artifactId>
+ <classifier>bin</classifier>
+ <type>exe</type>
+ <scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
@@ -85,4 +76,266 @@
</dependency>
</dependencies>
+ <build>
+ <finalName>${pkg.name}-${project.version}</finalName>
+ <resources>
+ <resource>
+ <directory>${project.basedir}/src/main/resources</directory>
+ </resource>
+ </resources>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-resources-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>copy-conf</id>
+ <phase>process-resources</phase>
+ <goals>
+ <goal>copy-resources</goal>
+ </goals>
+ <configuration>
+ <outputDirectory>${project.build.directory}/conf</outputDirectory>
+ <resources>
+ <resource>
+ <directory>src/main/resources</directory>
+ <excludes>
+ <exclude>logback.xml</exclude>
+ </excludes>
+ <filtering>false</filtering>
+ </resource>
+ </resources>
+ </configuration>
+ </execution>
+ <execution>
+ <id>copy-service-conf</id>
+ <phase>process-resources</phase>
+ <goals>
+ <goal>copy-resources</goal>
+ </goals>
+ <configuration>
+ <outputDirectory>${project.build.directory}/conf</outputDirectory>
+ <resources>
+ <resource>
+ <directory>src/main/conf</directory>
+ <filtering>true</filtering>
+ </resource>
+ </resources>
+ <filters>
+ <filter>src/main/filters/unix.properties</filter>
+ </filters>
+ </configuration>
+ </execution>
+ <execution>
+ <id>copy-win-conf</id>
+ <phase>process-resources</phase>
+ <goals>
+ <goal>copy-resources</goal>
+ </goals>
+ <configuration>
+ <outputDirectory>${pkg.win.dist}/conf</outputDirectory>
+ <resources>
+ <resource>
+ <directory>src/main/resources</directory>
+ <excludes>
+ <exclude>logback.xml</exclude>
+ </excludes>
+ <filtering>false</filtering>
+ </resource>
+ <resource>
+ <directory>src/main/conf</directory>
+ <excludes>
+ <exclude>tb-mqtt-transport.conf</exclude>
+ </excludes>
+ <filtering>true</filtering>
+ </resource>
+ </resources>
+ <filters>
+ <filter>src/main/filters/windows.properties</filter>
+ </filters>
+ </configuration>
+ </execution>
+ <execution>
+ <id>copy-control</id>
+ <phase>process-resources</phase>
+ <goals>
+ <goal>copy-resources</goal>
+ </goals>
+ <configuration>
+ <outputDirectory>${project.build.directory}/control</outputDirectory>
+ <resources>
+ <resource>
+ <directory>src/main/scripts/control</directory>
+ <filtering>true</filtering>
+ </resource>
+ </resources>
+ <filters>
+ <filter>src/main/filters/unix.properties</filter>
+ </filters>
+ </configuration>
+ </execution>
+ <execution>
+ <id>copy-windows-control</id>
+ <phase>process-resources</phase>
+ <goals>
+ <goal>copy-resources</goal>
+ </goals>
+ <configuration>
+ <outputDirectory>${pkg.win.dist}</outputDirectory>
+ <resources>
+ <resource>
+ <directory>src/main/scripts/windows</directory>
+ <filtering>true</filtering>
+ </resource>
+ </resources>
+ <filters>
+ <filter>src/main/filters/windows.properties</filter>
+ </filters>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-dependency-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>copy-winsw-service</id>
+ <phase>package</phase>
+ <goals>
+ <goal>copy</goal>
+ </goals>
+ <configuration>
+ <artifactItems>
+ <artifactItem>
+ <groupId>com.sun.winsw</groupId>
+ <artifactId>winsw</artifactId>
+ <classifier>bin</classifier>
+ <type>exe</type>
+ <destFileName>service.exe</destFileName>
+ </artifactItem>
+ </artifactItems>
+ <outputDirectory>${pkg.win.dist}</outputDirectory>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ <configuration>
+ <excludes>
+ <exclude>**/logback.xml</exclude>
+ </excludes>
+ <archive>
+ <manifestEntries>
+ <Implementation-Title>ThingsBoard MQTT Transport Service</Implementation-Title>
+ <Implementation-Version>${project.version}</Implementation-Version>
+ </manifestEntries>
+ </archive>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-maven-plugin</artifactId>
+ <configuration>
+ <mainClass>org.thingsboard.server.mqtt.ThingsboardMqttTransportApplication</mainClass>
+ <classifier>boot</classifier>
+ <layout>ZIP</layout>
+ <executable>true</executable>
+ <excludeDevtools>true</excludeDevtools>
+ <embeddedLaunchScriptProperties>
+ <confFolder>${pkg.installFolder}/conf</confFolder>
+ <logFolder>${pkg.unixLogFolder}</logFolder>
+ <logFilename>${pkg.name}.out</logFilename>
+ <initInfoProvides>${pkg.name}</initInfoProvides>
+ </embeddedLaunchScriptProperties>
+ </configuration>
+ <executions>
+ <execution>
+ <goals>
+ <goal>repackage</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.fortasoft</groupId>
+ <artifactId>gradle-maven-plugin</artifactId>
+ <configuration>
+ <tasks>
+ <task>build</task>
+ <task>buildDeb</task>
+ <task>buildRpm</task>
+ </tasks>
+ <args>
+ <arg>-PprojectBuildDir=${project.build.directory}</arg>
+ <arg>-PprojectVersion=${project.version}</arg>
+ <arg>-PmainJar=${project.build.directory}/${project.build.finalName}-boot.${project.packaging}</arg>
+ <arg>-PpkgName=${pkg.name}</arg>
+ <arg>-PpkgInstallFolder=${pkg.installFolder}</arg>
+ <arg>-PpkgLogFolder=${pkg.unixLogFolder}</arg>
+ </args>
+ </configuration>
+ <executions>
+ <execution>
+ <phase>package</phase>
+ <goals>
+ <goal>invoke</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <configuration>
+ <finalName>${pkg.name}</finalName>
+ <descriptors>
+ <descriptor>src/main/assembly/windows.xml</descriptor>
+ </descriptors>
+ </configuration>
+ <executions>
+ <execution>
+ <id>assembly</id>
+ <phase>package</phase>
+ <goals>
+ <goal>single</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-install-plugin</artifactId>
+ <configuration>
+ <file>${project.build.directory}/${pkg.name}.deb</file>
+ <artifactId>${project.artifactId}</artifactId>
+ <groupId>${project.groupId}</groupId>
+ <version>${project.version}</version>
+ <classifier>deb</classifier>
+ <packaging>deb</packaging>
+ </configuration>
+ <executions>
+ <execution>
+ <id>install-deb</id>
+ <phase>package</phase>
+ <goals>
+ <goal>install-file</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ <repositories>
+ <repository>
+ <id>jenkins</id>
+ <name>Jenkins Repository</name>
+ <url>http://repo.jenkins-ci.org/releases</url>
+ <snapshots>
+ <enabled>false</enabled>
+ </snapshots>
+ </repository>
+ </repositories>
</project>
transport/mqtt/src/main/assembly/windows.xml 71(+71 -0)
diff --git a/transport/mqtt/src/main/assembly/windows.xml b/transport/mqtt/src/main/assembly/windows.xml
new file mode 100644
index 0000000..82da34e
--- /dev/null
+++ b/transport/mqtt/src/main/assembly/windows.xml
@@ -0,0 +1,71 @@
+<!--
+
+ Copyright © 2016-2018 The Thingsboard Authors
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3 http://maven.apache.org/xsd/assembly-1.1.3.xsd">
+ <id>windows</id>
+
+ <formats>
+ <format>zip</format>
+ </formats>
+
+ <!-- Workaround to create logs directory -->
+ <fileSets>
+ <fileSet>
+ <directory>${pkg.win.dist}</directory>
+ <outputDirectory>logs</outputDirectory>
+ <excludes>
+ <exclude>*/**</exclude>
+ </excludes>
+ </fileSet>
+ <fileSet>
+ <directory>${pkg.win.dist}/conf</directory>
+ <outputDirectory>conf</outputDirectory>
+ <lineEnding>windows</lineEnding>
+ </fileSet>
+ </fileSets>
+
+ <files>
+ <file>
+ <source>${project.build.directory}/${project.build.finalName}-boot.${project.packaging}</source>
+ <outputDirectory>lib</outputDirectory>
+ <destName>${pkg.name}.jar</destName>
+ </file>
+ <file>
+ <source>${pkg.win.dist}/service.exe</source>
+ <outputDirectory/>
+ <destName>${pkg.name}.exe</destName>
+ </file>
+ <file>
+ <source>${pkg.win.dist}/service.xml</source>
+ <outputDirectory/>
+ <destName>${pkg.name}.xml</destName>
+ <lineEnding>windows</lineEnding>
+ </file>
+ <file>
+ <source>${pkg.win.dist}/install.bat</source>
+ <outputDirectory/>
+ <lineEnding>windows</lineEnding>
+ </file>
+ <file>
+ <source>${pkg.win.dist}/uninstall.bat</source>
+ <outputDirectory/>
+ <lineEnding>windows</lineEnding>
+ </file>
+ </files>
+</assembly>
transport/mqtt/src/main/conf/logback.xml 43(+43 -0)
diff --git a/transport/mqtt/src/main/conf/logback.xml b/transport/mqtt/src/main/conf/logback.xml
new file mode 100644
index 0000000..f36469d
--- /dev/null
+++ b/transport/mqtt/src/main/conf/logback.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+
+ Copyright © 2016-2018 The Thingsboard Authors
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+<!DOCTYPE configuration>
+<configuration>
+
+ <appender name="fileLogAppender"
+ class="ch.qos.logback.core.rolling.RollingFileAppender">
+ <file>${pkg.logFolder}/${pkg.name}.log</file>
+ <rollingPolicy
+ class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
+ <fileNamePattern>${pkg.logFolder}/${pkg.name}.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
+ <maxFileSize>100MB</maxFileSize>
+ <maxHistory>30</maxHistory>
+ <totalSizeCap>3GB</totalSizeCap>
+ </rollingPolicy>
+ <encoder>
+ <pattern>%d{ISO8601} [%thread] %-5level %logger{36} - %msg%n</pattern>
+ </encoder>
+ </appender>
+
+ <logger name="org.thingsboard.server" level="INFO" />
+
+ <root level="INFO">
+ <appender-ref ref="fileLogAppender"/>
+ </root>
+
+</configuration>
diff --git a/transport/mqtt/src/main/conf/tb-mqtt-transport.conf b/transport/mqtt/src/main/conf/tb-mqtt-transport.conf
new file mode 100644
index 0000000..0afa91c
--- /dev/null
+++ b/transport/mqtt/src/main/conf/tb-mqtt-transport.conf
@@ -0,0 +1,23 @@
+#
+# Copyright © 2016-2018 The Thingsboard Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+export JAVA_OPTS="$JAVA_OPTS -Xloggc:@pkg.logFolder@/gc.log -XX:+IgnoreUnrecognizedVMOptions -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDetails -XX:+PrintGCDateStamps"
+export JAVA_OPTS="$JAVA_OPTS -XX:+PrintHeapAtGC -XX:+PrintTenuringDistribution -XX:+PrintGCApplicationStoppedTime -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10"
+export JAVA_OPTS="$JAVA_OPTS -XX:GCLogFileSize=10M -XX:-UseBiasedLocking -XX:+UseTLAB -XX:+ResizeTLAB -XX:+PerfDisableSharedMem -XX:+UseCondCardMark"
+export JAVA_OPTS="$JAVA_OPTS -XX:CMSWaitDuration=10000 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+CMSParallelInitialMarkEnabled"
+export JAVA_OPTS="$JAVA_OPTS -XX:+CMSEdenChunksRecordAlways -XX:CMSInitiatingOccupancyFraction=75 -XX:+UseCMSInitiatingOccupancyOnly"
+export LOG_FILENAME=${pkg.name}.out
+export LOADER_PATH=${pkg.installFolder}/conf
diff --git a/transport/mqtt/src/main/filters/unix.properties b/transport/mqtt/src/main/filters/unix.properties
new file mode 100644
index 0000000..8967278
--- /dev/null
+++ b/transport/mqtt/src/main/filters/unix.properties
@@ -0,0 +1 @@
+pkg.logFolder=${pkg.unixLogFolder}
\ No newline at end of file
diff --git a/transport/mqtt/src/main/filters/windows.properties b/transport/mqtt/src/main/filters/windows.properties
new file mode 100644
index 0000000..a6e48d9
--- /dev/null
+++ b/transport/mqtt/src/main/filters/windows.properties
@@ -0,0 +1,2 @@
+pkg.logFolder=${BASE}\\logs
+pkg.winWrapperLogFolder=%BASE%\\logs
diff --git a/transport/mqtt/src/main/java/org/thingsboard/server/mqtt/ThingsboardMqttTransportApplication.java b/transport/mqtt/src/main/java/org/thingsboard/server/mqtt/ThingsboardMqttTransportApplication.java
new file mode 100644
index 0000000..2bae2ff
--- /dev/null
+++ b/transport/mqtt/src/main/java/org/thingsboard/server/mqtt/ThingsboardMqttTransportApplication.java
@@ -0,0 +1,49 @@
+/**
+ * Copyright © 2016-2018 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.thingsboard.server.mqtt;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.SpringBootConfiguration;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.scheduling.annotation.EnableAsync;
+import org.springframework.scheduling.annotation.EnableScheduling;
+
+import java.util.Arrays;
+
+@SpringBootConfiguration
+@EnableAsync
+@EnableScheduling
+@ComponentScan({"org.thingsboard.server.mqtt", "org.thingsboard.server.common", "org.thingsboard.server.transport.mqtt", "org.thingsboard.server.kafka"})
+public class ThingsboardMqttTransportApplication {
+
+ private static final String SPRING_CONFIG_NAME_KEY = "--spring.config.name";
+ private static final String DEFAULT_SPRING_CONFIG_PARAM = SPRING_CONFIG_NAME_KEY + "=" + "tb-mqtt-transport";
+
+ public static void main(String[] args) {
+ SpringApplication.run(ThingsboardMqttTransportApplication.class, updateArguments(args));
+ }
+
+ private static String[] updateArguments(String[] args) {
+ if (Arrays.stream(args).noneMatch(arg -> arg.startsWith(SPRING_CONFIG_NAME_KEY))) {
+ String[] modifiedArgs = new String[args.length + 1];
+ System.arraycopy(args, 0, modifiedArgs, 0, args.length);
+ modifiedArgs[args.length] = DEFAULT_SPRING_CONFIG_PARAM;
+ return modifiedArgs;
+ }
+ return args;
+ }
+}
diff --git a/transport/mqtt/src/main/resources/logback.xml b/transport/mqtt/src/main/resources/logback.xml
new file mode 100644
index 0000000..18864a9
--- /dev/null
+++ b/transport/mqtt/src/main/resources/logback.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+
+ Copyright © 2016-2018 The Thingsboard Authors
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+<!DOCTYPE configuration>
+<configuration scan="true" scanPeriod="10 seconds">
+
+ <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+ <encoder>
+ <pattern>%d{ISO8601} [%thread] %-5level %logger{36} - %msg%n</pattern>
+ </encoder>
+ </appender>
+
+ <logger name="org.thingsboard.server" level="TRACE" />
+
+ <root level="INFO">
+ <appender-ref ref="STDOUT"/>
+ </root>
+
+</configuration>
\ No newline at end of file
diff --git a/transport/mqtt/src/main/resources/tb-mqtt-transport.yml b/transport/mqtt/src/main/resources/tb-mqtt-transport.yml
new file mode 100644
index 0000000..1d425e4
--- /dev/null
+++ b/transport/mqtt/src/main/resources/tb-mqtt-transport.yml
@@ -0,0 +1,88 @@
+#
+# Copyright © 2016-2018 The Thingsboard Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+spring.main.web-environment: false
+spring.main.web-application-type: none
+
+# MQTT server parameters
+transport:
+ mqtt:
+ bind_address: "${MQTT_BIND_ADDRESS:0.0.0.0}"
+ bind_port: "${MQTT_BIND_PORT:1883}"
+ adaptor: "${MQTT_ADAPTOR_NAME:JsonMqttAdaptor}"
+ timeout: "${MQTT_TIMEOUT:10000}"
+ netty:
+ leak_detector_level: "${NETTY_LEASK_DETECTOR_LVL:DISABLED}"
+ boss_group_thread_count: "${NETTY_BOSS_GROUP_THREADS:1}"
+ worker_group_thread_count: "${NETTY_WORKER_GROUP_THREADS:12}"
+ max_payload_size: "${NETTY_MAX_PAYLOAD_SIZE:65536}"
+ # MQTT SSL configuration
+ ssl:
+ # Enable/disable SSL support
+ enabled: "${MQTT_SSL_ENABLED:false}"
+ # SSL protocol: See http://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#SSLContext
+ protocol: "${MQTT_SSL_PROTOCOL:TLSv1.2}"
+ # Path to the key store that holds the SSL certificate
+ key_store: "${MQTT_SSL_KEY_STORE:mqttserver.jks}"
+ # Password used to access the key store
+ key_store_password: "${MQTT_SSL_KEY_STORE_PASSWORD:server_ks_password}"
+ # Password used to access the key
+ key_password: "${MQTT_SSL_KEY_PASSWORD:server_key_password}"
+ # Type of the key store
+ key_store_type: "${MQTT_SSL_KEY_STORE_TYPE:JKS}"
+
+#Quota parameters
+quota:
+ host:
+ # Max allowed number of API requests in interval for single host
+ limit: "${QUOTA_HOST_LIMIT:10000}"
+ # Interval duration
+ intervalMs: "${QUOTA_HOST_INTERVAL_MS:60000}"
+ # Maximum silence duration for host after which Host removed from QuotaService. Must be bigger than intervalMs
+ ttlMs: "${QUOTA_HOST_TTL_MS:60000}"
+ # Interval for scheduled task that cleans expired records. TTL is used for expiring
+ cleanPeriodMs: "${QUOTA_HOST_CLEAN_PERIOD_MS:300000}"
+ # Enable Host API Limits
+ enabled: "${QUOTA_HOST_ENABLED:true}"
+ # Array of whitelist hosts
+ whitelist: "${QUOTA_HOST_WHITELIST:localhost,127.0.0.1}"
+ # Array of blacklist hosts
+ blacklist: "${QUOTA_HOST_BLACKLIST:}"
+ log:
+ topSize: 10
+ intervalMin: 2
+
+kafka:
+ enabled: true
+ bootstrap.servers: "${TB_KAFKA_SERVERS:localhost:9092}"
+ acks: "${TB_KAFKA_ACKS:all}"
+ retries: "${TB_KAFKA_RETRIES:1}"
+ batch.size: "${TB_KAFKA_BATCH_SIZE:16384}"
+ linger.ms: "${TB_KAFKA_LINGER_MS:1}"
+ buffer.memory: "${TB_BUFFER_MEMORY:33554432}"
+ transport_api:
+ requests_topic: "${TB_TRANSPORT_API_REQUEST_TOPIC:tb.transport.api.requests}"
+ responses_topic: "${TB_TRANSPORT_API_RESPONSE_TOPIC:tb.transport.api.responses}"
+ max_pending_requests: "${TB_TRANSPORT_MAX_PENDING_REQUESTS:10000}"
+ max_requests_timeout: "${TB_TRANSPORT_MAX_REQUEST_TIMEOUT:10000}"
+ response_poll_interval: "${TB_TRANSPORT_RESPONSE_POLL_INTERVAL_MS:25}"
+ response_auto_commit_interval: "${TB_TRANSPORT_RESPONSE_AUTO_COMMIT_INTERVAL_MS:100}"
+ rule_engine:
+ topic: "${TB_RULE_ENGINE_TOPIC:tb.rule-engine}"
+ notifications:
+ topic: "${TB_TRANSPORT_NOTIFICATIONS_TOPIC:tb.transport.notifications}"
+ poll_interval: "${TB_TRANSPORT_NOTIFICATIONS_POLL_INTERVAL_MS:25}"
+ auto_commit_interval: "${TB_TRANSPORT_NOTIFICATIONS_AUTO_COMMIT_INTERVAL_MS:100}"
diff --git a/transport/mqtt/src/main/scripts/control/deb/postinst b/transport/mqtt/src/main/scripts/control/deb/postinst
new file mode 100644
index 0000000..d4066c0
--- /dev/null
+++ b/transport/mqtt/src/main/scripts/control/deb/postinst
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+chown -R ${pkg.name}: ${pkg.logFolder}
+chown -R ${pkg.name}: ${pkg.installFolder}
+update-rc.d ${pkg.name} defaults
+
diff --git a/transport/mqtt/src/main/scripts/control/deb/postrm b/transport/mqtt/src/main/scripts/control/deb/postrm
new file mode 100644
index 0000000..6186580
--- /dev/null
+++ b/transport/mqtt/src/main/scripts/control/deb/postrm
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+update-rc.d -f ${pkg.name} remove
diff --git a/transport/mqtt/src/main/scripts/control/deb/preinst b/transport/mqtt/src/main/scripts/control/deb/preinst
new file mode 100644
index 0000000..6be5959
--- /dev/null
+++ b/transport/mqtt/src/main/scripts/control/deb/preinst
@@ -0,0 +1,18 @@
+#!/bin/sh
+
+if ! getent group ${pkg.name} >/dev/null; then
+ addgroup --system ${pkg.name}
+fi
+
+if ! getent passwd ${pkg.name} >/dev/null; then
+ adduser --quiet \
+ --system \
+ --ingroup ${pkg.name} \
+ --quiet \
+ --disabled-login \
+ --disabled-password \
+ --home ${pkg.installFolder} \
+ --no-create-home \
+ -gecos "Thingsboard application" \
+ ${pkg.name}
+fi
diff --git a/transport/mqtt/src/main/scripts/control/deb/prerm b/transport/mqtt/src/main/scripts/control/deb/prerm
new file mode 100644
index 0000000..898d3ef
--- /dev/null
+++ b/transport/mqtt/src/main/scripts/control/deb/prerm
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+if [ -e /var/run/${pkg.name}/${pkg.name}.pid ]; then
+ service ${pkg.name} stop
+fi
diff --git a/transport/mqtt/src/main/scripts/control/rpm/postinst b/transport/mqtt/src/main/scripts/control/rpm/postinst
new file mode 100644
index 0000000..8a7a88f
--- /dev/null
+++ b/transport/mqtt/src/main/scripts/control/rpm/postinst
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+chown -R ${pkg.name}: ${pkg.logFolder}
+chown -R ${pkg.name}: ${pkg.installFolder}
+
+if [ $1 -eq 1 ] ; then
+ # Initial installation
+ systemctl --no-reload enable ${pkg.name}.service >/dev/null 2>&1 || :
+fi
diff --git a/transport/mqtt/src/main/scripts/control/rpm/postrm b/transport/mqtt/src/main/scripts/control/rpm/postrm
new file mode 100644
index 0000000..8e1f8a2
--- /dev/null
+++ b/transport/mqtt/src/main/scripts/control/rpm/postrm
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+if [ $1 -ge 1 ] ; then
+ # Package upgrade, not uninstall
+ systemctl try-restart ${pkg.name}.service >/dev/null 2>&1 || :
+fi
diff --git a/transport/mqtt/src/main/scripts/control/rpm/preinst b/transport/mqtt/src/main/scripts/control/rpm/preinst
new file mode 100644
index 0000000..e19fc88
--- /dev/null
+++ b/transport/mqtt/src/main/scripts/control/rpm/preinst
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+getent group ${pkg.name} >/dev/null || groupadd -r ${pkg.name}
+getent passwd ${pkg.name} >/dev/null || \
+useradd -d ${pkg.installFolder} -g ${pkg.name} -M -r ${pkg.name} -s /sbin/nologin \
+-c "Thingsboard application"
diff --git a/transport/mqtt/src/main/scripts/control/rpm/prerm b/transport/mqtt/src/main/scripts/control/rpm/prerm
new file mode 100644
index 0000000..accb487
--- /dev/null
+++ b/transport/mqtt/src/main/scripts/control/rpm/prerm
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+if [ $1 -eq 0 ] ; then
+ # Package removal, not upgrade
+ systemctl --no-reload disable --now ${pkg.name}.service > /dev/null 2>&1 || :
+fi
diff --git a/transport/mqtt/src/main/scripts/control/tb-mqtt-transport.service b/transport/mqtt/src/main/scripts/control/tb-mqtt-transport.service
new file mode 100644
index 0000000..d456fc0
--- /dev/null
+++ b/transport/mqtt/src/main/scripts/control/tb-mqtt-transport.service
@@ -0,0 +1,11 @@
+[Unit]
+Description=${pkg.name}
+After=syslog.target
+
+[Service]
+User=${pkg.name}
+ExecStart=${pkg.installFolder}/bin/${pkg.name}.jar
+SuccessExitStatus=143
+
+[Install]
+WantedBy=multi-user.target
diff --git a/transport/mqtt/src/main/scripts/windows/install.bat b/transport/mqtt/src/main/scripts/windows/install.bat
new file mode 100644
index 0000000..dba7736
--- /dev/null
+++ b/transport/mqtt/src/main/scripts/windows/install.bat
@@ -0,0 +1,87 @@
+@ECHO OFF
+
+setlocal ENABLEEXTENSIONS
+
+@ECHO Detecting Java version installed.
+:CHECK_JAVA_64
+@ECHO Detecting if it is 64 bit machine
+set KEY_NAME="HKEY_LOCAL_MACHINE\Software\Wow6432Node\JavaSoft\Java Runtime Environment"
+set VALUE_NAME=CurrentVersion
+
+FOR /F "usebackq skip=2 tokens=1-3" %%A IN (`REG QUERY %KEY_NAME% /v %VALUE_NAME% 2^>nul`) DO (
+ set ValueName=%%A
+ set ValueType=%%B
+ set ValueValue=%%C
+)
+@ECHO CurrentVersion %ValueValue%
+
+SET KEY_NAME="%KEY_NAME:~1,-1%\%ValueValue%"
+SET VALUE_NAME=JavaHome
+
+if defined ValueName (
+ FOR /F "usebackq skip=2 tokens=1,2*" %%A IN (`REG QUERY %KEY_NAME% /v %VALUE_NAME% 2^>nul`) DO (
+ set ValueName2=%%A
+ set ValueType2=%%B
+ set JRE_PATH2=%%C
+
+ if defined ValueName2 (
+ set ValueName = %ValueName2%
+ set ValueType = %ValueType2%
+ set ValueValue = %JRE_PATH2%
+ )
+ )
+)
+
+IF NOT "%JRE_PATH2%" == "" GOTO JAVA_INSTALLED
+
+:CHECK_JAVA_32
+@ECHO Detecting if it is 32 bit machine
+set KEY_NAME="HKEY_LOCAL_MACHINE\Software\JavaSoft\Java Runtime Environment"
+set VALUE_NAME=CurrentVersion
+
+FOR /F "usebackq skip=2 tokens=1-3" %%A IN (`REG QUERY %KEY_NAME% /v %VALUE_NAME% 2^>nul`) DO (
+ set ValueName=%%A
+ set ValueType=%%B
+ set ValueValue=%%C
+)
+@ECHO CurrentVersion %ValueValue%
+
+SET KEY_NAME="%KEY_NAME:~1,-1%\%ValueValue%"
+SET VALUE_NAME=JavaHome
+
+if defined ValueName (
+ FOR /F "usebackq skip=2 tokens=1,2*" %%A IN (`REG QUERY %KEY_NAME% /v %VALUE_NAME% 2^>nul`) DO (
+ set ValueName2=%%A
+ set ValueType2=%%B
+ set JRE_PATH2=%%C
+
+ if defined ValueName2 (
+ set ValueName = %ValueName2%
+ set ValueType = %ValueType2%
+ set ValueValue = %JRE_PATH2%
+ )
+ )
+)
+
+IF "%JRE_PATH2%" == "" GOTO JAVA_NOT_INSTALLED
+
+:JAVA_INSTALLED
+
+@ECHO Java 1.8 found!
+@ECHO Installing ${pkg.name} ...
+
+%BASE%${pkg.name}.exe install
+
+@ECHO ${pkg.name} installed successfully!
+
+GOTO END
+
+:JAVA_NOT_INSTALLED
+@ECHO Java 1.8 or above is not installed
+@ECHO Please go to https://java.com/ and install Java. Then retry installation.
+PAUSE
+GOTO END
+
+:END
+
+
diff --git a/transport/mqtt/src/main/scripts/windows/service.xml b/transport/mqtt/src/main/scripts/windows/service.xml
new file mode 100644
index 0000000..f7b9d30
--- /dev/null
+++ b/transport/mqtt/src/main/scripts/windows/service.xml
@@ -0,0 +1,36 @@
+<service>
+ <id>${pkg.name}</id>
+ <name>${project.name}</name>
+ <description>${project.description}</description>
+ <workingdirectory>%BASE%\conf</workingdirectory>
+ <logpath>${pkg.winWrapperLogFolder}</logpath>
+ <logmode>rotate</logmode>
+ <env name="LOADER_PATH" value="%BASE%\conf" />
+ <executable>java</executable>
+ <startargument>-Xloggc:%BASE%\logs\gc.log</startargument>
+ <startargument>-XX:+HeapDumpOnOutOfMemoryError</startargument>
+ <startargument>-XX:+PrintGCDetails</startargument>
+ <startargument>-XX:+PrintGCDateStamps</startargument>
+ <startargument>-XX:+PrintHeapAtGC</startargument>
+ <startargument>-XX:+PrintTenuringDistribution</startargument>
+ <startargument>-XX:+PrintGCApplicationStoppedTime</startargument>
+ <startargument>-XX:+UseGCLogFileRotation</startargument>
+ <startargument>-XX:NumberOfGCLogFiles=10</startargument>
+ <startargument>-XX:GCLogFileSize=10M</startargument>
+ <startargument>-XX:-UseBiasedLocking</startargument>
+ <startargument>-XX:+UseTLAB</startargument>
+ <startargument>-XX:+ResizeTLAB</startargument>
+ <startargument>-XX:+PerfDisableSharedMem</startargument>
+ <startargument>-XX:+UseCondCardMark</startargument>
+ <startargument>-XX:CMSWaitDuration=10000</startargument>
+ <startargument>-XX:+UseParNewGC</startargument>
+ <startargument>-XX:+UseConcMarkSweepGC</startargument>
+ <startargument>-XX:+CMSParallelRemarkEnabled</startargument>
+ <startargument>-XX:+CMSParallelInitialMarkEnabled</startargument>
+ <startargument>-XX:+CMSEdenChunksRecordAlways</startargument>
+ <startargument>-XX:CMSInitiatingOccupancyFraction=75</startargument>
+ <startargument>-XX:+UseCMSInitiatingOccupancyOnly</startargument>
+ <startargument>-jar</startargument>
+ <startargument>%BASE%\lib\${pkg.name}.jar</startargument>
+
+</service>
diff --git a/transport/mqtt/src/main/scripts/windows/uninstall.bat b/transport/mqtt/src/main/scripts/windows/uninstall.bat
new file mode 100644
index 0000000..921e4c8
--- /dev/null
+++ b/transport/mqtt/src/main/scripts/windows/uninstall.bat
@@ -0,0 +1,9 @@
+@ECHO OFF
+
+@ECHO Stopping ${pkg.name} ...
+net stop ${pkg.name}
+
+@ECHO Uninstalling ${pkg.name} ...
+%~dp0${pkg.name}.exe uninstall
+
+@ECHO DONE.
\ No newline at end of file
transport/pom.xml 18(+1 -17)
diff --git a/transport/pom.xml b/transport/pom.xml
index 95b0112..0bb9300 100644
--- a/transport/pom.xml
+++ b/transport/pom.xml
@@ -23,7 +23,6 @@
<version>2.2.0-SNAPSHOT</version>
<artifactId>thingsboard</artifactId>
</parent>
- <groupId>org.thingsboard</groupId>
<artifactId>transport</artifactId>
<packaging>pom</packaging>
@@ -36,23 +35,8 @@
<modules>
<module>http</module>
- <module>coap</module>
<module>mqtt</module>
+ <module>coap</module>
</modules>
- <dependencies>
- <dependency>
- <groupId>org.thingsboard</groupId>
- <artifactId>dao</artifactId>
- </dependency>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-autoconfigure</artifactId>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcprov-jdk15on</artifactId>
- </dependency>
- </dependencies>
-
</project>