From bc591ce09fd718222a26ca89fc69f1304c0cada0 Mon Sep 17 00:00:00 2001 From: Andrew Shvayka Date: Thu, 4 Oct 2018 16:36:46 +0300 Subject: [PATCH 01/26] tmp commit to merge with master --- common/transport/pom.xml | 13 +++ .../common/transport/TransportService.java | 16 ++++ .../transport/src/main/proto/transport.proto | 79 +++++++++++++++++++ .../mqtt/MqttSslHandlerProvider.java | 1 - .../transport/mqtt/MqttTransportContext.java | 38 +++++++++ .../mqtt/MqttTransportServerInitializer.java | 33 +++----- .../transport/mqtt/MqttTransportService.java | 36 ++------- 7 files changed, 161 insertions(+), 55 deletions(-) create mode 100644 common/transport/src/main/java/org/thingsboard/server/common/transport/TransportService.java create mode 100644 common/transport/src/main/proto/transport.proto create mode 100644 transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportContext.java diff --git a/common/transport/pom.xml b/common/transport/pom.xml index a349012fce..b8a7ab53f0 100644 --- a/common/transport/pom.xml +++ b/common/transport/pom.xml @@ -86,6 +86,19 @@ org.apache.commons commons-lang3 + + com.google.protobuf + protobuf-java + + + + + org.xolstice.maven.plugins + protobuf-maven-plugin + + + + diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/TransportService.java b/common/transport/src/main/java/org/thingsboard/server/common/transport/TransportService.java new file mode 100644 index 0000000000..9ff19b7111 --- /dev/null +++ b/common/transport/src/main/java/org/thingsboard/server/common/transport/TransportService.java @@ -0,0 +1,16 @@ +package org.thingsboard.server.common.transport; + +import org.thingsboard.server.gen.transport.TransportProtos; + +/** + * Created by ashvayka on 04.10.18. + */ +public interface TransportService { + + void process(TransportProtos.SessionEventMsg msg); + + void process(TransportProtos.PostTelemetryMsg msg); + + void process(TransportProtos.PostAttributeMsg msg); + +} diff --git a/common/transport/src/main/proto/transport.proto b/common/transport/src/main/proto/transport.proto new file mode 100644 index 0000000000..d77e79bbd0 --- /dev/null +++ b/common/transport/src/main/proto/transport.proto @@ -0,0 +1,79 @@ +/** + * 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; +} + +enum SessionEvent { + OPEN = 0; + CLOSED = 1; +} + +message KeyValueProto { + string key = 1; + bool bool_v = 2; + int64 long_v = 3; + double double_v = 4; + string string_v = 5; +} + +message TsKvListProto { + int64 ts = 1; + repeated KeyValueProto kv = 2; +} + +/** + * Messages that use Data Structures; + */ +message SessionEventMsg { + SessionInfoProto sessionInfo = 1; + int64 deviceIdMSB = 2; + int64 deviceIdLSB = 3; +} + +message PostTelemetryMsg { + SessionInfoProto sessionInfo = 1; + repeated TsKvListProto tsKvList = 2; +} + +message PostAttributeMsg { + SessionInfoProto sessionInfo = 1; + repeated TsKvListProto tsKvList = 2; +} + +message GetAttributeRequestMsg { + SessionInfoProto sessionInfo = 1; + repeated string clientAttributeNames = 2; + repeated string sharedAttributeNames = 3; +} + +message GetAttributeResponseMsg { + SessionInfoProto sessionInfo = 1; + repeated TsKvListProto clientAttributeList = 2; + repeated TsKvListProto sharedAttributeList = 3; + repeated string deletedAttributeKeys = 4; +} \ No newline at end of file diff --git a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttSslHandlerProvider.java b/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttSslHandlerProvider.java index 9c458fd39f..70ed5013e4 100644 --- a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttSslHandlerProvider.java +++ b/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttSslHandlerProvider.java @@ -60,7 +60,6 @@ public class MqttSslHandlerProvider { @Autowired private DeviceCredentialsService deviceCredentialsService; - public SslHandler getSslHandler() { try { URL ksUrl = Resources.getResource(keyStoreFile); diff --git a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportContext.java b/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportContext.java new file mode 100644 index 0000000000..43e2a3440e --- /dev/null +++ b/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportContext.java @@ -0,0 +1,38 @@ +package org.thingsboard.server.transport.mqtt; + +import io.netty.handler.ssl.SslHandler; +import lombok.Data; +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.server.common.transport.TransportService; +import org.thingsboard.server.common.transport.quota.host.HostRequestsQuotaService; +import org.thingsboard.server.transport.mqtt.adaptors.MqttTransportAdaptor; + +/** + * Created by ashvayka on 04.10.18. + */ +@Component +@Data +public class MqttTransportContext { + + @Autowired + @Lazy + private TransportService transportService; + + @Autowired(required = false) + private MqttSslHandlerProvider sslHandlerProvider; + + @Autowired(required = false) + private HostRequestsQuotaService quotaService; + + @Autowired + private MqttTransportAdaptor adaptor; + + @Value("${mqtt.netty.max_payload_size}") + private Integer maxPayloadSize; + + private SslHandler sslHandler; + +} diff --git a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportServerInitializer.java b/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportServerInitializer.java index 432966eeff..102a0d9f07 100644 --- a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportServerInitializer.java +++ b/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportServerInitializer.java @@ -22,6 +22,7 @@ import io.netty.handler.codec.mqtt.MqttDecoder; import io.netty.handler.codec.mqtt.MqttEncoder; import io.netty.handler.ssl.SslHandler; import org.thingsboard.server.common.transport.SessionMsgProcessor; +import org.thingsboard.server.common.transport.TransportService; import org.thingsboard.server.common.transport.auth.DeviceAuthService; import org.thingsboard.server.common.transport.quota.QuotaService; import org.thingsboard.server.dao.device.DeviceService; @@ -33,41 +34,25 @@ import org.thingsboard.server.transport.mqtt.adaptors.MqttTransportAdaptor; */ public class MqttTransportServerInitializer extends ChannelInitializer { - private final SessionMsgProcessor processor; - private final DeviceService deviceService; - private final DeviceAuthService authService; - private final RelationService relationService; - private final MqttTransportAdaptor adaptor; - private final MqttSslHandlerProvider sslHandlerProvider; - private final QuotaService quotaService; - private final int maxPayloadSize; + private final MqttTransportContext context; - public MqttTransportServerInitializer(SessionMsgProcessor processor, DeviceService deviceService, DeviceAuthService authService, RelationService relationService, - MqttTransportAdaptor adaptor, MqttSslHandlerProvider sslHandlerProvider, - QuotaService quotaService, int maxPayloadSize) { - this.processor = processor; - this.deviceService = deviceService; - this.authService = authService; - this.relationService = relationService; - this.adaptor = adaptor; - this.sslHandlerProvider = sslHandlerProvider; - this.quotaService = quotaService; - this.maxPayloadSize = maxPayloadSize; + public MqttTransportServerInitializer(MqttTransportContext context) { + this.context = context; } @Override public void initChannel(SocketChannel ch) { ChannelPipeline pipeline = ch.pipeline(); SslHandler sslHandler = null; - if (sslHandlerProvider != null) { - sslHandler = sslHandlerProvider.getSslHandler(); + if (context.getSslHandlerProvider() != null) { + sslHandler = context.getSslHandlerProvider().getSslHandler(); pipeline.addLast(sslHandler); + context.setSslHandler(sslHandler); } - pipeline.addLast("decoder", new MqttDecoder(maxPayloadSize)); + pipeline.addLast("decoder", new MqttDecoder(context.getMaxPayloadSize())); pipeline.addLast("encoder", MqttEncoder.INSTANCE); - MqttTransportHandler handler = new MqttTransportHandler(processor, deviceService, authService, relationService, - adaptor, sslHandler, quotaService); + MqttTransportHandler handler = new MqttTransportHandler(context); pipeline.addLast(handler); ch.closeFuture().addListener(handler); diff --git a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportService.java b/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportService.java index bb8d4ad54c..f435cc2dc5 100644 --- a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportService.java +++ b/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportService.java @@ -23,11 +23,14 @@ import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.util.ResourceLeakDetector; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.thingsboard.server.common.transport.SessionMsgProcessor; +import org.thingsboard.server.common.transport.TransportService; import org.thingsboard.server.common.transport.auth.DeviceAuthService; import org.thingsboard.server.common.transport.quota.host.HostRequestsQuotaService; import org.thingsboard.server.dao.device.DeviceService; @@ -48,27 +51,6 @@ public class MqttTransportService { private static final String V1 = "v1"; private static final String DEVICE = "device"; - @Autowired(required = false) - private ApplicationContext appContext; - - @Autowired(required = false) - private SessionMsgProcessor processor; - - @Autowired(required = false) - private DeviceService deviceService; - - @Autowired(required = false) - private DeviceAuthService authService; - - @Autowired(required = false) - private RelationService relationService; - - @Autowired(required = false) - private MqttSslHandlerProvider sslHandlerProvider; - - @Autowired(required = false) - private HostRequestsQuotaService quotaService; - @Value("${mqtt.bind_address}") private String host; @Value("${mqtt.bind_port}") @@ -82,10 +64,9 @@ public class MqttTransportService { private Integer bossGroupThreadCount; @Value("${mqtt.netty.worker_group_thread_count}") private Integer workerGroupThreadCount; - @Value("${mqtt.netty.max_payload_size}") - private Integer maxPayloadSize; - private MqttTransportAdaptor adaptor; + @Autowired + private MqttTransportContext context; private Channel serverChannel; private EventLoopGroup bossGroup; @@ -97,17 +78,12 @@ public class MqttTransportService { ResourceLeakDetector.setLevel(ResourceLeakDetector.Level.valueOf(leakDetectorLevel.toUpperCase())); log.info("Starting MQTT transport..."); - log.info("Lookup MQTT transport adaptor {}", adaptorName); - this.adaptor = (MqttTransportAdaptor) appContext.getBean(adaptorName); - - log.info("Starting MQTT transport server"); bossGroup = new NioEventLoopGroup(bossGroupThreadCount); workerGroup = new NioEventLoopGroup(workerGroupThreadCount); ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) - .childHandler(new MqttTransportServerInitializer(processor, deviceService, authService, relationService, - adaptor, sslHandlerProvider, quotaService, maxPayloadSize)); + .childHandler(new MqttTransportServerInitializer(context)); serverChannel = b.bind(host, port).sync().channel(); log.info("Mqtt transport started!"); From fd6ad413b721fb1c8eaa1f170c92c17748abf27c Mon Sep 17 00:00:00 2001 From: Andrew Shvayka Date: Thu, 4 Oct 2018 19:18:26 +0300 Subject: [PATCH 02/26] Initial structure --- .../server/ThingsboardServerApplication.java | 8 +- .../common/transport/TransportService.java | 9 +- .../transport/TransportServiceCallback.java | 11 + .../session/DeviceAwareSessionContext.java | 56 +- .../transport/src/main/proto/transport.proto | 20 +- transport/mqtt-transport/pom.xml | 88 +++ .../ThingsboardMqttTransportApplication.java | 48 ++ .../src/main/resources/tb-mqtt-transport.yml | 57 ++ .../transport/mqtt/MqttTransportContext.java | 26 + .../transport/mqtt/MqttTransportHandler.java | 575 ++++++++++-------- .../mqtt/session/DeviceSessionCtx.java | 6 +- .../mqtt/session/GatewaySessionCtx.java | 4 + transport/pom.xml | 1 + 13 files changed, 603 insertions(+), 306 deletions(-) create mode 100644 common/transport/src/main/java/org/thingsboard/server/common/transport/TransportServiceCallback.java create mode 100644 transport/mqtt-transport/pom.xml create mode 100644 transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/ThingsboardMqttTransportApplication.java create mode 100644 transport/mqtt-transport/src/main/resources/tb-mqtt-transport.yml diff --git a/application/src/main/java/org/thingsboard/server/ThingsboardServerApplication.java b/application/src/main/java/org/thingsboard/server/ThingsboardServerApplication.java index 751bde6303..c072311fe2 100644 --- a/application/src/main/java/org/thingsboard/server/ThingsboardServerApplication.java +++ b/application/src/main/java/org/thingsboard/server/ThingsboardServerApplication.java @@ -1,12 +1,12 @@ /** * 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 - * + *

+ * 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. diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/TransportService.java b/common/transport/src/main/java/org/thingsboard/server/common/transport/TransportService.java index 9ff19b7111..ff0debcd4b 100644 --- a/common/transport/src/main/java/org/thingsboard/server/common/transport/TransportService.java +++ b/common/transport/src/main/java/org/thingsboard/server/common/transport/TransportService.java @@ -7,10 +7,13 @@ import org.thingsboard.server.gen.transport.TransportProtos; */ public interface TransportService { - void process(TransportProtos.SessionEventMsg msg); + void process(TransportProtos.ValidateDeviceTokenRequestMsg msg, + TransportServiceCallback callback); - void process(TransportProtos.PostTelemetryMsg msg); + void process(TransportProtos.SessionEventMsg msg, TransportServiceCallback callback); - void process(TransportProtos.PostAttributeMsg msg); + void process(TransportProtos.PostTelemetryMsg msg, TransportServiceCallback callback); + + void process(TransportProtos.PostAttributeMsg msg, TransportServiceCallback callback); } diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/TransportServiceCallback.java b/common/transport/src/main/java/org/thingsboard/server/common/transport/TransportServiceCallback.java new file mode 100644 index 0000000000..bfc7ac890e --- /dev/null +++ b/common/transport/src/main/java/org/thingsboard/server/common/transport/TransportServiceCallback.java @@ -0,0 +1,11 @@ +package org.thingsboard.server.common.transport; + +/** + * Created by ashvayka on 04.10.18. + */ +public interface TransportServiceCallback { + + void onSuccess(T msg); + void onError(Exception e); + +} diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/session/DeviceAwareSessionContext.java b/common/transport/src/main/java/org/thingsboard/server/common/transport/session/DeviceAwareSessionContext.java index 2e7b412c31..28269454c7 100644 --- a/common/transport/src/main/java/org/thingsboard/server/common/transport/session/DeviceAwareSessionContext.java +++ b/common/transport/src/main/java/org/thingsboard/server/common/transport/session/DeviceAwareSessionContext.java @@ -1,12 +1,12 @@ /** * 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 - * + *

+ * 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. @@ -15,6 +15,7 @@ */ package org.thingsboard.server.common.transport.session; +import lombok.Data; import lombok.extern.slf4j.Slf4j; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.security.DeviceCredentialsFilter; @@ -23,56 +24,27 @@ import org.thingsboard.server.common.msg.session.SessionContext; import org.thingsboard.server.common.transport.SessionMsgProcessor; import org.thingsboard.server.common.transport.auth.DeviceAuthResult; import org.thingsboard.server.common.transport.auth.DeviceAuthService; +import org.thingsboard.server.gen.transport.TransportProtos; import java.util.Optional; /** * @author Andrew Shvayka */ -@Slf4j +@Data public abstract class DeviceAwareSessionContext implements SessionContext { - protected final DeviceAuthService authService; - protected final SessionMsgProcessor processor; + private volatile TransportProtos.DeviceInfoProto deviceInfo; - protected volatile Device device; - - public DeviceAwareSessionContext(SessionMsgProcessor processor, DeviceAuthService authService) { - this.processor = processor; - this.authService = authService; - } - - public DeviceAwareSessionContext(SessionMsgProcessor processor, DeviceAuthService authService, Device device) { - this(processor, authService); - this.device = device; - } - - - public boolean login(DeviceCredentialsFilter credentials) { - DeviceAuthResult result = authService.process(credentials); - if (result.isSuccess()) { - Optional deviceOpt = authService.findDeviceById(result.getDeviceId()); - if (deviceOpt.isPresent()) { - device = deviceOpt.get(); - } - return true; - } else { - log.debug("Can't find device using credentials [{}] due to {}", credentials, result.getErrorMsg()); - return false; - } - } - - public DeviceAuthService getAuthService() { - return authService; + public long getDeviceIdMSB() { + return deviceInfo.getDeviceIdMSB(); } - public SessionMsgProcessor getProcessor() { - return processor; + public long getDeviceIdLSB() { + return deviceInfo.getDeviceIdLSB(); } - public Device getDevice() { - return device; + public boolean isConnected() { + return deviceInfo != null; } - - } diff --git a/common/transport/src/main/proto/transport.proto b/common/transport/src/main/proto/transport.proto index d77e79bbd0..65d1468605 100644 --- a/common/transport/src/main/proto/transport.proto +++ b/common/transport/src/main/proto/transport.proto @@ -46,6 +46,14 @@ message TsKvListProto { repeated KeyValueProto kv = 2; } +message DeviceInfoProto { + int64 deviceIdMSB = 1; + int64 deviceIdLSB = 2; + string deviceName = 3; + string deviceType = 4; + string additionalInfo = 5; +} + /** * Messages that use Data Structures; */ @@ -53,6 +61,7 @@ message SessionEventMsg { SessionInfoProto sessionInfo = 1; int64 deviceIdMSB = 2; int64 deviceIdLSB = 3; + SessionEvent event = 4; } message PostTelemetryMsg { @@ -76,4 +85,13 @@ message GetAttributeResponseMsg { repeated TsKvListProto clientAttributeList = 2; repeated TsKvListProto sharedAttributeList = 3; repeated string deletedAttributeKeys = 4; -} \ No newline at end of file +} + +message ValidateDeviceTokenRequestMsg { + string token = 1; +} + +message ValidateDeviceTokenResponseMsg { + DeviceInfoProto deviceInfo = 1; +} + diff --git a/transport/mqtt-transport/pom.xml b/transport/mqtt-transport/pom.xml new file mode 100644 index 0000000000..8d8b24d6c6 --- /dev/null +++ b/transport/mqtt-transport/pom.xml @@ -0,0 +1,88 @@ + + + 4.0.0 + + org.thingsboard + 2.2.0-SNAPSHOT + transport + + org.thingsboard.transport + mqtt-transport + jar + + Thingsboard MQTT Transport Service + https://thingsboard.io + + + UTF-8 + ${basedir}/../.. + + + + + org.thingsboard.common + transport + + + io.netty + netty-all + + + org.springframework + spring-context + + + org.slf4j + slf4j-api + + + org.slf4j + log4j-over-slf4j + + + ch.qos.logback + logback-core + + + ch.qos.logback + logback-classic + + + com.google.guava + guava + + + org.springframework.boot + spring-boot-starter-test + test + + + junit + junit + test + + + org.mockito + mockito-all + test + + + + diff --git a/transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/ThingsboardMqttTransportApplication.java b/transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/ThingsboardMqttTransportApplication.java new file mode 100644 index 0000000000..3c54938a37 --- /dev/null +++ b/transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/ThingsboardMqttTransportApplication.java @@ -0,0 +1,48 @@ +package org.thingsboard.server.mqtt; /** + * 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.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 springfox.documentation.swagger2.annotations.EnableSwagger2; + +import java.util.Arrays; + +@SpringBootConfiguration +@EnableAsync +@EnableScheduling +@ComponentScan({"org.thingsboard.server"}) +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-transport/src/main/resources/tb-mqtt-transport.yml b/transport/mqtt-transport/src/main/resources/tb-mqtt-transport.yml new file mode 100644 index 0000000000..c12f25edcf --- /dev/null +++ b/transport/mqtt-transport/src/main/resources/tb-mqtt-transport.yml @@ -0,0 +1,57 @@ +# +# 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-application-type: none + +# 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}" + +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}" + topic: + telemetry: "${TB_TELEMETRY_TOPIC:tb.transport.telemetry}" + requests: "${TB_TELEMETRY_TOPIC:tb.transport.requests}" \ No newline at end of file diff --git a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportContext.java b/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportContext.java index 43e2a3440e..cc40ee0975 100644 --- a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportContext.java +++ b/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportContext.java @@ -1,22 +1,33 @@ package org.thingsboard.server.transport.mqtt; +import com.fasterxml.jackson.databind.ObjectMapper; import io.netty.handler.ssl.SslHandler; import lombok.Data; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.RandomStringUtils; 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.springframework.util.StringUtils; import org.thingsboard.server.common.transport.TransportService; import org.thingsboard.server.common.transport.quota.host.HostRequestsQuotaService; import org.thingsboard.server.transport.mqtt.adaptors.MqttTransportAdaptor; +import javax.annotation.PostConstruct; +import java.net.InetAddress; +import java.net.UnknownHostException; + /** * Created by ashvayka on 04.10.18. */ +@Slf4j @Component @Data public class MqttTransportContext { + private final ObjectMapper mapper = new ObjectMapper(); + @Autowired @Lazy private TransportService transportService; @@ -33,6 +44,21 @@ public class MqttTransportContext { @Value("${mqtt.netty.max_payload_size}") private Integer maxPayloadSize; + @Value("${cluster.node_id:#{null}}") + private String nodeId; + private SslHandler sslHandler; + @PostConstruct + public void init() { + if (StringUtils.isEmpty(nodeId)) { + try { + nodeId = InetAddress.getLocalHost().getHostName(); + } catch (UnknownHostException e) { + nodeId = RandomStringUtils.randomAlphabetic(10); + } + } + log.info("Current NodeId: {}", nodeId); + } + } diff --git a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java b/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java index ed0c8c642c..fd02d3aa39 100644 --- a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java +++ b/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java @@ -1,12 +1,12 @@ /** * 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 - * + *

+ * 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. @@ -18,9 +18,21 @@ package org.thingsboard.server.transport.mqtt; import com.fasterxml.jackson.databind.JsonNode; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; -import io.netty.handler.codec.mqtt.*; +import io.netty.handler.codec.mqtt.MqttConnAckMessage; +import io.netty.handler.codec.mqtt.MqttConnAckVariableHeader; +import io.netty.handler.codec.mqtt.MqttConnectMessage; import io.netty.handler.codec.mqtt.MqttConnectReturnCode; +import io.netty.handler.codec.mqtt.MqttFixedHeader; +import io.netty.handler.codec.mqtt.MqttMessage; +import io.netty.handler.codec.mqtt.MqttMessageIdVariableHeader; +import io.netty.handler.codec.mqtt.MqttPubAckMessage; +import io.netty.handler.codec.mqtt.MqttPublishMessage; import io.netty.handler.codec.mqtt.MqttQoS; +import io.netty.handler.codec.mqtt.MqttSubAckMessage; +import io.netty.handler.codec.mqtt.MqttSubAckPayload; +import io.netty.handler.codec.mqtt.MqttSubscribeMessage; +import io.netty.handler.codec.mqtt.MqttTopicSubscription; +import io.netty.handler.codec.mqtt.MqttUnsubscribeMessage; import io.netty.handler.ssl.SslHandler; import io.netty.util.concurrent.Future; import io.netty.util.concurrent.GenericFutureListener; @@ -34,13 +46,12 @@ import org.thingsboard.server.common.msg.session.AdaptorToSessionActorMsg; import org.thingsboard.server.common.msg.session.BasicAdaptorToSessionActorMsg; import org.thingsboard.server.common.msg.session.BasicTransportToDeviceSessionActorMsg; import org.thingsboard.server.common.msg.session.ctrl.SessionCloseMsg; -import org.thingsboard.server.common.transport.SessionMsgProcessor; +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.auth.DeviceAuthService; import org.thingsboard.server.common.transport.quota.QuotaService; import org.thingsboard.server.dao.EncryptionUtil; -import org.thingsboard.server.dao.device.DeviceService; -import org.thingsboard.server.dao.relation.RelationService; +import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.transport.mqtt.adaptors.MqttTransportAdaptor; import org.thingsboard.server.transport.mqtt.session.DeviceSessionCtx; import org.thingsboard.server.transport.mqtt.session.GatewaySessionCtx; @@ -48,18 +59,51 @@ import org.thingsboard.server.transport.mqtt.util.SslUtil; import javax.net.ssl.SSLPeerUnverifiedException; import javax.security.cert.X509Certificate; +import java.io.IOException; import java.net.InetSocketAddress; import java.util.ArrayList; import java.util.List; -import java.util.Map; +import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; -import static io.netty.handler.codec.mqtt.MqttConnectReturnCode.*; -import static io.netty.handler.codec.mqtt.MqttMessageType.*; -import static io.netty.handler.codec.mqtt.MqttQoS.*; -import static org.thingsboard.server.common.msg.session.SessionMsgType.*; -import static org.thingsboard.server.transport.mqtt.MqttTopics.*; +import org.thingsboard.server.gen.transport.TransportProtos.*; + +import static io.netty.handler.codec.mqtt.MqttConnectReturnCode.CONNECTION_ACCEPTED; +import static io.netty.handler.codec.mqtt.MqttConnectReturnCode.CONNECTION_REFUSED_BAD_USER_NAME_OR_PASSWORD; +import static io.netty.handler.codec.mqtt.MqttConnectReturnCode.CONNECTION_REFUSED_NOT_AUTHORIZED; +import static io.netty.handler.codec.mqtt.MqttMessageType.CONNACK; +import static io.netty.handler.codec.mqtt.MqttMessageType.PINGRESP; +import static io.netty.handler.codec.mqtt.MqttMessageType.PUBACK; +import static io.netty.handler.codec.mqtt.MqttMessageType.SUBACK; +import static io.netty.handler.codec.mqtt.MqttMessageType.UNSUBACK; +import static io.netty.handler.codec.mqtt.MqttQoS.AT_LEAST_ONCE; +import static io.netty.handler.codec.mqtt.MqttQoS.AT_MOST_ONCE; +import static io.netty.handler.codec.mqtt.MqttQoS.FAILURE; +import static org.thingsboard.server.common.msg.session.SessionMsgType.GET_ATTRIBUTES_REQUEST; +import static org.thingsboard.server.common.msg.session.SessionMsgType.POST_ATTRIBUTES_REQUEST; +import static org.thingsboard.server.common.msg.session.SessionMsgType.POST_TELEMETRY_REQUEST; +import static org.thingsboard.server.common.msg.session.SessionMsgType.SUBSCRIBE_ATTRIBUTES_REQUEST; +import static org.thingsboard.server.common.msg.session.SessionMsgType.SUBSCRIBE_RPC_COMMANDS_REQUEST; +import static org.thingsboard.server.common.msg.session.SessionMsgType.TO_DEVICE_RPC_RESPONSE; +import static org.thingsboard.server.common.msg.session.SessionMsgType.TO_SERVER_RPC_REQUEST; +import static org.thingsboard.server.common.msg.session.SessionMsgType.UNSUBSCRIBE_ATTRIBUTES_REQUEST; +import static org.thingsboard.server.common.msg.session.SessionMsgType.UNSUBSCRIBE_RPC_COMMANDS_REQUEST; +import static org.thingsboard.server.transport.mqtt.MqttTopics.BASE_GATEWAY_API_TOPIC; +import static org.thingsboard.server.transport.mqtt.MqttTopics.DEVICE_ATTRIBUTES_REQUEST_TOPIC_PREFIX; +import static org.thingsboard.server.transport.mqtt.MqttTopics.DEVICE_ATTRIBUTES_RESPONSES_TOPIC; +import static org.thingsboard.server.transport.mqtt.MqttTopics.DEVICE_ATTRIBUTES_TOPIC; +import static org.thingsboard.server.transport.mqtt.MqttTopics.DEVICE_RPC_REQUESTS_SUB_TOPIC; +import static org.thingsboard.server.transport.mqtt.MqttTopics.DEVICE_RPC_REQUESTS_TOPIC; +import static org.thingsboard.server.transport.mqtt.MqttTopics.DEVICE_RPC_RESPONSE_SUB_TOPIC; +import static org.thingsboard.server.transport.mqtt.MqttTopics.DEVICE_RPC_RESPONSE_TOPIC; +import static org.thingsboard.server.transport.mqtt.MqttTopics.DEVICE_TELEMETRY_TOPIC; +import static org.thingsboard.server.transport.mqtt.MqttTopics.GATEWAY_ATTRIBUTES_REQUEST_TOPIC; +import static org.thingsboard.server.transport.mqtt.MqttTopics.GATEWAY_ATTRIBUTES_TOPIC; +import static org.thingsboard.server.transport.mqtt.MqttTopics.GATEWAY_CONNECT_TOPIC; +import static org.thingsboard.server.transport.mqtt.MqttTopics.GATEWAY_DISCONNECT_TOPIC; +import static org.thingsboard.server.transport.mqtt.MqttTopics.GATEWAY_RPC_TOPIC; +import static org.thingsboard.server.transport.mqtt.MqttTopics.GATEWAY_TELEMETRY_TOPIC; /** * @author Andrew Shvayka @@ -69,33 +113,34 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement public static final MqttQoS MAX_SUPPORTED_QOS_LVL = AT_LEAST_ONCE; - private final DeviceSessionCtx deviceSessionCtx; - private final String sessionId; + private final UUID sessionId; + private final MqttTransportContext context; private final MqttTransportAdaptor adaptor; - private final SessionMsgProcessor processor; - private final DeviceService deviceService; - private final DeviceAuthService authService; - private final RelationService relationService; + private final TransportService transportService; private final QuotaService quotaService; private final SslHandler sslHandler; private final ConcurrentMap mqttQoSMap; - private volatile boolean connected; + private final SessionInfoProto sessionInfo; + private volatile InetSocketAddress address; + private volatile DeviceSessionCtx deviceSessionCtx; private volatile GatewaySessionCtx gatewaySessionCtx; - public MqttTransportHandler(SessionMsgProcessor processor, DeviceService deviceService, DeviceAuthService authService, RelationService relationService, - MqttTransportAdaptor adaptor, SslHandler sslHandler, QuotaService quotaService) { - this.processor = processor; - this.deviceService = deviceService; - this.relationService = relationService; - this.authService = authService; - this.adaptor = adaptor; + public MqttTransportHandler(MqttTransportContext context) { + this.sessionId = UUID.randomUUID(); + this.context = context; + this.transportService = context.getTransportService(); + this.adaptor = context.getAdaptor(); + this.quotaService = context.getQuotaService(); + this.sslHandler = context.getSslHandler(); this.mqttQoSMap = new ConcurrentHashMap<>(); - this.deviceSessionCtx = new DeviceSessionCtx(processor, authService, adaptor, mqttQoSMap); - this.sessionId = deviceSessionCtx.getSessionId().toUidStr(); - this.sslHandler = sslHandler; - this.quotaService = quotaService; + this.sessionInfo = SessionInfoProto.newBuilder() + .setNodeId(context.getNodeId()) + .setSessionIdMSB(sessionId.getMostSignificantBits()) + .setSessionIdLSB(sessionId.getLeastSignificantBits()) + .build(); + this.deviceSessionCtx = new DeviceSessionCtx(mqttQoSMap); } @Override @@ -127,196 +172,196 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement case CONNECT: processConnect(ctx, (MqttConnectMessage) msg); break; - case PUBLISH: - processPublish(ctx, (MqttPublishMessage) msg); - break; - case SUBSCRIBE: - processSubscribe(ctx, (MqttSubscribeMessage) msg); - break; - case UNSUBSCRIBE: - processUnsubscribe(ctx, (MqttUnsubscribeMessage) msg); - break; - case PINGREQ: - if (checkConnected(ctx)) { - ctx.writeAndFlush(new MqttMessage(new MqttFixedHeader(PINGRESP, false, AT_MOST_ONCE, false, 0))); - } - break; - case DISCONNECT: - if (checkConnected(ctx)) { - processDisconnect(ctx); - } - break; +// case PUBLISH: +// processPublish(ctx, (MqttPublishMessage) msg); +// break; +// case SUBSCRIBE: +// processSubscribe(ctx, (MqttSubscribeMessage) msg); +// break; +// case UNSUBSCRIBE: +// processUnsubscribe(ctx, (MqttUnsubscribeMessage) msg); +// break; +// case PINGREQ: +// if (checkConnected(ctx)) { +// ctx.writeAndFlush(new MqttMessage(new MqttFixedHeader(PINGRESP, false, AT_MOST_ONCE, false, 0))); +// } +// break; +// case DISCONNECT: +// if (checkConnected(ctx)) { +// processDisconnect(ctx); +// } +// break; default: break; } } - private void processPublish(ChannelHandlerContext ctx, MqttPublishMessage mqttMsg) { - if (!checkConnected(ctx)) { - return; - } - String topicName = mqttMsg.variableHeader().topicName(); - int msgId = mqttMsg.variableHeader().messageId(); - log.trace("[{}] Processing publish msg [{}][{}]!", sessionId, topicName, msgId); - - if (topicName.startsWith(BASE_GATEWAY_API_TOPIC)) { - if (gatewaySessionCtx != null) { - gatewaySessionCtx.setChannel(ctx); - handleMqttPublishMsg(topicName, msgId, mqttMsg); - } - } else { - processDevicePublish(ctx, mqttMsg, topicName, msgId); - } - } - - private void handleMqttPublishMsg(String topicName, int msgId, MqttPublishMessage mqttMsg) { - try { - switch (topicName) { - case GATEWAY_TELEMETRY_TOPIC: - gatewaySessionCtx.onDeviceTelemetry(mqttMsg); - break; - case GATEWAY_ATTRIBUTES_TOPIC: - gatewaySessionCtx.onDeviceAttributes(mqttMsg); - break; - case GATEWAY_ATTRIBUTES_REQUEST_TOPIC: - gatewaySessionCtx.onDeviceAttributesRequest(mqttMsg); - break; - case GATEWAY_RPC_TOPIC: - gatewaySessionCtx.onDeviceRpcResponse(mqttMsg); - break; - case GATEWAY_CONNECT_TOPIC: - gatewaySessionCtx.onDeviceConnect(mqttMsg); - break; - case GATEWAY_DISCONNECT_TOPIC: - gatewaySessionCtx.onDeviceDisconnect(mqttMsg); - break; - } - } catch (RuntimeException | AdaptorException e) { - log.warn("[{}] Failed to process publish msg [{}][{}]", sessionId, topicName, msgId, e); - } - } - - private void processDevicePublish(ChannelHandlerContext ctx, MqttPublishMessage mqttMsg, String topicName, int msgId) { - AdaptorToSessionActorMsg msg = null; - try { - if (topicName.equals(DEVICE_TELEMETRY_TOPIC)) { - msg = adaptor.convertToActorMsg(deviceSessionCtx, POST_TELEMETRY_REQUEST, mqttMsg); - } else if (topicName.equals(DEVICE_ATTRIBUTES_TOPIC)) { - msg = adaptor.convertToActorMsg(deviceSessionCtx, POST_ATTRIBUTES_REQUEST, mqttMsg); - } else if (topicName.startsWith(DEVICE_ATTRIBUTES_REQUEST_TOPIC_PREFIX)) { - msg = adaptor.convertToActorMsg(deviceSessionCtx, GET_ATTRIBUTES_REQUEST, mqttMsg); - if (msgId >= 0) { - ctx.writeAndFlush(createMqttPubAckMsg(msgId)); - } - } else if (topicName.startsWith(DEVICE_RPC_RESPONSE_TOPIC)) { - msg = adaptor.convertToActorMsg(deviceSessionCtx, TO_DEVICE_RPC_RESPONSE, mqttMsg); - if (msgId >= 0) { - ctx.writeAndFlush(createMqttPubAckMsg(msgId)); - } - } else if (topicName.startsWith(DEVICE_RPC_REQUESTS_TOPIC)) { - msg = adaptor.convertToActorMsg(deviceSessionCtx, TO_SERVER_RPC_REQUEST, mqttMsg); - if (msgId >= 0) { - ctx.writeAndFlush(createMqttPubAckMsg(msgId)); - } - } - } catch (AdaptorException e) { - log.warn("[{}] Failed to process publish msg [{}][{}]", sessionId, topicName, msgId, e); - } - if (msg != null) { - processor.process(new BasicTransportToDeviceSessionActorMsg(deviceSessionCtx.getDevice(), msg)); - } else { - log.info("[{}] Closing current session due to invalid publish msg [{}][{}]", sessionId, topicName, msgId); - ctx.close(); - } - } - - private void processSubscribe(ChannelHandlerContext ctx, MqttSubscribeMessage mqttMsg) { - if (!checkConnected(ctx)) { - return; - } - log.trace("[{}] Processing subscription [{}]!", sessionId, mqttMsg.variableHeader().messageId()); - List grantedQoSList = new ArrayList<>(); - for (MqttTopicSubscription subscription : mqttMsg.payload().topicSubscriptions()) { - String topic = subscription.topicName(); - MqttQoS reqQoS = subscription.qualityOfService(); - try { - switch (topic) { - case DEVICE_ATTRIBUTES_TOPIC: { - AdaptorToSessionActorMsg msg = adaptor.convertToActorMsg(deviceSessionCtx, SUBSCRIBE_ATTRIBUTES_REQUEST, mqttMsg); - processor.process(new BasicTransportToDeviceSessionActorMsg(deviceSessionCtx.getDevice(), msg)); - registerSubQoS(topic, grantedQoSList, reqQoS); - break; - } - case DEVICE_RPC_REQUESTS_SUB_TOPIC: { - AdaptorToSessionActorMsg msg = adaptor.convertToActorMsg(deviceSessionCtx, SUBSCRIBE_RPC_COMMANDS_REQUEST, mqttMsg); - processor.process(new BasicTransportToDeviceSessionActorMsg(deviceSessionCtx.getDevice(), msg)); - registerSubQoS(topic, grantedQoSList, reqQoS); - break; - } - case DEVICE_RPC_RESPONSE_SUB_TOPIC: - case GATEWAY_ATTRIBUTES_TOPIC: - case GATEWAY_RPC_TOPIC: - registerSubQoS(topic, grantedQoSList, reqQoS); - break; - case DEVICE_ATTRIBUTES_RESPONSES_TOPIC: - deviceSessionCtx.setAllowAttributeResponses(); - registerSubQoS(topic, grantedQoSList, reqQoS); - break; - default: - log.warn("[{}] Failed to subscribe to [{}][{}]", sessionId, topic, reqQoS); - grantedQoSList.add(FAILURE.value()); - break; - } - } catch (AdaptorException e) { - log.warn("[{}] Failed to subscribe to [{}][{}]", sessionId, topic, reqQoS); - grantedQoSList.add(FAILURE.value()); - } - } - ctx.writeAndFlush(createSubAckMessage(mqttMsg.variableHeader().messageId(), grantedQoSList)); - } - - private void registerSubQoS(String topic, List grantedQoSList, MqttQoS reqQoS) { - grantedQoSList.add(getMinSupportedQos(reqQoS)); - mqttQoSMap.put(topic, getMinSupportedQos(reqQoS)); - } - - private void processUnsubscribe(ChannelHandlerContext ctx, MqttUnsubscribeMessage mqttMsg) { - if (!checkConnected(ctx)) { - return; - } - log.trace("[{}] Processing subscription [{}]!", sessionId, mqttMsg.variableHeader().messageId()); - for (String topicName : mqttMsg.payload().topics()) { - mqttQoSMap.remove(topicName); - try { - switch (topicName) { - case DEVICE_ATTRIBUTES_TOPIC: { - AdaptorToSessionActorMsg msg = adaptor.convertToActorMsg(deviceSessionCtx, UNSUBSCRIBE_ATTRIBUTES_REQUEST, mqttMsg); - processor.process(new BasicTransportToDeviceSessionActorMsg(deviceSessionCtx.getDevice(), msg)); - break; - } - case DEVICE_RPC_REQUESTS_SUB_TOPIC: { - AdaptorToSessionActorMsg msg = adaptor.convertToActorMsg(deviceSessionCtx, UNSUBSCRIBE_RPC_COMMANDS_REQUEST, mqttMsg); - processor.process(new BasicTransportToDeviceSessionActorMsg(deviceSessionCtx.getDevice(), msg)); - break; - } - case DEVICE_ATTRIBUTES_RESPONSES_TOPIC: - deviceSessionCtx.setDisallowAttributeResponses(); - break; - } - } catch (AdaptorException e) { - log.warn("[{}] Failed to process unsubscription [{}] to [{}]", sessionId, mqttMsg.variableHeader().messageId(), topicName); - } - } - ctx.writeAndFlush(createUnSubAckMessage(mqttMsg.variableHeader().messageId())); - } - - private MqttMessage createUnSubAckMessage(int msgId) { - MqttFixedHeader mqttFixedHeader = - new MqttFixedHeader(UNSUBACK, false, AT_LEAST_ONCE, false, 0); - MqttMessageIdVariableHeader mqttMessageIdVariableHeader = MqttMessageIdVariableHeader.from(msgId); - return new MqttMessage(mqttFixedHeader, mqttMessageIdVariableHeader); - } +// private void processPublish(ChannelHandlerContext ctx, MqttPublishMessage mqttMsg) { +// if (!checkConnected(ctx)) { +// return; +// } +// String topicName = mqttMsg.variableHeader().topicName(); +// int msgId = mqttMsg.variableHeader().packetId(); +// log.trace("[{}] Processing publish msg [{}][{}]!", sessionId, topicName, msgId); +// +// if (topicName.startsWith(BASE_GATEWAY_API_TOPIC)) { +// if (gatewaySessionCtx != null) { +// gatewaySessionCtx.setChannel(ctx); +// handleMqttPublishMsg(topicName, msgId, mqttMsg); +// } +// } else { +// processDevicePublish(ctx, mqttMsg, topicName, msgId); +// } +// } +// +// private void handleMqttPublishMsg(String topicName, int msgId, MqttPublishMessage mqttMsg) { +// try { +// switch (topicName) { +// case GATEWAY_TELEMETRY_TOPIC: +// gatewaySessionCtx.onDeviceTelemetry(mqttMsg); +// break; +// case GATEWAY_ATTRIBUTES_TOPIC: +// gatewaySessionCtx.onDeviceAttributes(mqttMsg); +// break; +// case GATEWAY_ATTRIBUTES_REQUEST_TOPIC: +// gatewaySessionCtx.onDeviceAttributesRequest(mqttMsg); +// break; +// case GATEWAY_RPC_TOPIC: +// gatewaySessionCtx.onDeviceRpcResponse(mqttMsg); +// break; +// case GATEWAY_CONNECT_TOPIC: +// gatewaySessionCtx.onDeviceConnect(mqttMsg); +// break; +// case GATEWAY_DISCONNECT_TOPIC: +// gatewaySessionCtx.onDeviceDisconnect(mqttMsg); +// break; +// } +// } catch (RuntimeException | AdaptorException e) { +// log.warn("[{}] Failed to process publish msg [{}][{}]", sessionId, topicName, msgId, e); +// } +// } +// +// private void processDevicePublish(ChannelHandlerContext ctx, MqttPublishMessage mqttMsg, String topicName, int msgId) { +// AdaptorToSessionActorMsg msg = null; +// try { +// if (topicName.equals(DEVICE_TELEMETRY_TOPIC)) { +// msg = adaptor.convertToActorMsg(deviceSessionCtx, POST_TELEMETRY_REQUEST, mqttMsg); +// } else if (topicName.equals(DEVICE_ATTRIBUTES_TOPIC)) { +// msg = adaptor.convertToActorMsg(deviceSessionCtx, POST_ATTRIBUTES_REQUEST, mqttMsg); +// } else if (topicName.startsWith(DEVICE_ATTRIBUTES_REQUEST_TOPIC_PREFIX)) { +// msg = adaptor.convertToActorMsg(deviceSessionCtx, GET_ATTRIBUTES_REQUEST, mqttMsg); +// if (msgId >= 0) { +// ctx.writeAndFlush(createMqttPubAckMsg(msgId)); +// } +// } else if (topicName.startsWith(DEVICE_RPC_RESPONSE_TOPIC)) { +// msg = adaptor.convertToActorMsg(deviceSessionCtx, TO_DEVICE_RPC_RESPONSE, mqttMsg); +// if (msgId >= 0) { +// ctx.writeAndFlush(createMqttPubAckMsg(msgId)); +// } +// } else if (topicName.startsWith(DEVICE_RPC_REQUESTS_TOPIC)) { +// msg = adaptor.convertToActorMsg(deviceSessionCtx, TO_SERVER_RPC_REQUEST, mqttMsg); +// if (msgId >= 0) { +// ctx.writeAndFlush(createMqttPubAckMsg(msgId)); +// } +// } +// } catch (AdaptorException e) { +// log.warn("[{}] Failed to process publish msg [{}][{}]", sessionId, topicName, msgId, e); +// } +// if (msg != null) { +// processor.process(new BasicTransportToDeviceSessionActorMsg(deviceSessionCtx.getDevice(), msg)); +// } else { +// log.info("[{}] Closing current session due to invalid publish msg [{}][{}]", sessionId, topicName, msgId); +// ctx.close(); +// } +// } +// +// private void processSubscribe(ChannelHandlerContext ctx, MqttSubscribeMessage mqttMsg) { +// if (!checkConnected(ctx)) { +// return; +// } +// log.trace("[{}] Processing subscription [{}]!", sessionId, mqttMsg.variableHeader().messageId()); +// List grantedQoSList = new ArrayList<>(); +// for (MqttTopicSubscription subscription : mqttMsg.payload().topicSubscriptions()) { +// String topic = subscription.topicName(); +// MqttQoS reqQoS = subscription.qualityOfService(); +// try { +// switch (topic) { +// case DEVICE_ATTRIBUTES_TOPIC: { +// AdaptorToSessionActorMsg msg = adaptor.convertToActorMsg(deviceSessionCtx, SUBSCRIBE_ATTRIBUTES_REQUEST, mqttMsg); +// processor.process(new BasicTransportToDeviceSessionActorMsg(deviceSessionCtx.getDevice(), msg)); +// registerSubQoS(topic, grantedQoSList, reqQoS); +// break; +// } +// case DEVICE_RPC_REQUESTS_SUB_TOPIC: { +// AdaptorToSessionActorMsg msg = adaptor.convertToActorMsg(deviceSessionCtx, SUBSCRIBE_RPC_COMMANDS_REQUEST, mqttMsg); +// processor.process(new BasicTransportToDeviceSessionActorMsg(deviceSessionCtx.getDevice(), msg)); +// registerSubQoS(topic, grantedQoSList, reqQoS); +// break; +// } +// case DEVICE_RPC_RESPONSE_SUB_TOPIC: +// case GATEWAY_ATTRIBUTES_TOPIC: +// case GATEWAY_RPC_TOPIC: +// registerSubQoS(topic, grantedQoSList, reqQoS); +// break; +// case DEVICE_ATTRIBUTES_RESPONSES_TOPIC: +// deviceSessionCtx.setAllowAttributeResponses(); +// registerSubQoS(topic, grantedQoSList, reqQoS); +// break; +// default: +// log.warn("[{}] Failed to subscribe to [{}][{}]", sessionId, topic, reqQoS); +// grantedQoSList.add(FAILURE.value()); +// break; +// } +// } catch (AdaptorException e) { +// log.warn("[{}] Failed to subscribe to [{}][{}]", sessionId, topic, reqQoS); +// grantedQoSList.add(FAILURE.value()); +// } +// } +// ctx.writeAndFlush(createSubAckMessage(mqttMsg.variableHeader().messageId(), grantedQoSList)); +// } +// +// private void registerSubQoS(String topic, List grantedQoSList, MqttQoS reqQoS) { +// grantedQoSList.add(getMinSupportedQos(reqQoS)); +// mqttQoSMap.put(topic, getMinSupportedQos(reqQoS)); +// } +// +// private void processUnsubscribe(ChannelHandlerContext ctx, MqttUnsubscribeMessage mqttMsg) { +// if (!checkConnected(ctx)) { +// return; +// } +// log.trace("[{}] Processing subscription [{}]!", sessionId, mqttMsg.variableHeader().messageId()); +// for (String topicName : mqttMsg.payload().topics()) { +// mqttQoSMap.remove(topicName); +// try { +// switch (topicName) { +// case DEVICE_ATTRIBUTES_TOPIC: { +// AdaptorToSessionActorMsg msg = adaptor.convertToActorMsg(deviceSessionCtx, UNSUBSCRIBE_ATTRIBUTES_REQUEST, mqttMsg); +// processor.process(new BasicTransportToDeviceSessionActorMsg(deviceSessionCtx.getDevice(), msg)); +// break; +// } +// case DEVICE_RPC_REQUESTS_SUB_TOPIC: { +// AdaptorToSessionActorMsg msg = adaptor.convertToActorMsg(deviceSessionCtx, UNSUBSCRIBE_RPC_COMMANDS_REQUEST, mqttMsg); +// processor.process(new BasicTransportToDeviceSessionActorMsg(deviceSessionCtx.getDevice(), msg)); +// break; +// } +// case DEVICE_ATTRIBUTES_RESPONSES_TOPIC: +// deviceSessionCtx.setDisallowAttributeResponses(); +// break; +// } +// } catch (AdaptorException e) { +// log.warn("[{}] Failed to process unsubscription [{}] to [{}]", sessionId, mqttMsg.variableHeader().messageId(), topicName); +// } +// } +// ctx.writeAndFlush(createUnSubAckMessage(mqttMsg.variableHeader().messageId())); +// } +// +// private MqttMessage createUnSubAckMessage(int msgId) { +// MqttFixedHeader mqttFixedHeader = +// new MqttFixedHeader(UNSUBACK, false, AT_LEAST_ONCE, false, 0); +// MqttMessageIdVariableHeader mqttMessageIdVariableHeader = MqttMessageIdVariableHeader.from(msgId); +// return new MqttMessage(mqttFixedHeader, mqttMessageIdVariableHeader); +// } private void processConnect(ChannelHandlerContext ctx, MqttConnectMessage msg) { log.info("[{}] Processing connect msg for client: {}!", sessionId, msg.payload().clientIdentifier()); @@ -333,36 +378,58 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement if (StringUtils.isEmpty(userName)) { ctx.writeAndFlush(createMqttConnAckMsg(CONNECTION_REFUSED_BAD_USER_NAME_OR_PASSWORD)); ctx.close(); - } else if (!deviceSessionCtx.login(new DeviceTokenCredentials(msg.payload().userName()))) { - ctx.writeAndFlush(createMqttConnAckMsg(CONNECTION_REFUSED_NOT_AUTHORIZED)); - ctx.close(); } else { - ctx.writeAndFlush(createMqttConnAckMsg(CONNECTION_ACCEPTED)); - connected = true; - processor.process(new BasicTransportToDeviceSessionActorMsg(deviceSessionCtx.getDevice(), - new BasicAdaptorToSessionActorMsg(deviceSessionCtx, new SessionOpenMsg()))); - checkGatewaySession(); + transportService.process(ValidateDeviceTokenRequestMsg.newBuilder().setToken(userName).build(), + new TransportServiceCallback() { + @Override + public void onSuccess(ValidateDeviceTokenResponseMsg msg) { + if (!msg.hasDeviceInfo()) { + ctx.writeAndFlush(createMqttConnAckMsg(CONNECTION_REFUSED_NOT_AUTHORIZED)); + ctx.close(); + } else { + ctx.writeAndFlush(createMqttConnAckMsg(CONNECTION_ACCEPTED)); + deviceSessionCtx.setDeviceInfo(deviceSessionCtx.getDeviceInfo()); + transportService.process(getSessionEventMsg(SessionEvent.OPEN), null); + checkGatewaySession(); + } + } + + @Override + public void onError(Exception e) { + log.trace("[{}] Failed to process credentials: {}", address, userName, e); + ctx.writeAndFlush(createMqttConnAckMsg(MqttConnectReturnCode.CONNECTION_REFUSED_SERVER_UNAVAILABLE)); + ctx.close(); + } + }); } } + protected SessionEventMsg getSessionEventMsg(SessionEvent event) { + return SessionEventMsg.newBuilder() + .setSessionInfo(sessionInfo) + .setDeviceIdMSB(deviceSessionCtx.getDeviceIdMSB()) + .setDeviceIdLSB(deviceSessionCtx.getDeviceIdLSB()) + .setEvent(event).build(); + } + private void processX509CertConnect(ChannelHandlerContext ctx, X509Certificate cert) { - try { - String strCert = SslUtil.getX509CertificateString(cert); - String sha3Hash = EncryptionUtil.getSha3Hash(strCert); - if (deviceSessionCtx.login(new DeviceX509Credentials(sha3Hash))) { - ctx.writeAndFlush(createMqttConnAckMsg(CONNECTION_ACCEPTED)); - connected = true; - processor.process(new BasicTransportToDeviceSessionActorMsg(deviceSessionCtx.getDevice(), - new BasicAdaptorToSessionActorMsg(deviceSessionCtx, new SessionOpenMsg()))); - checkGatewaySession(); - } else { - ctx.writeAndFlush(createMqttConnAckMsg(CONNECTION_REFUSED_NOT_AUTHORIZED)); - ctx.close(); - } - } catch (Exception e) { - ctx.writeAndFlush(createMqttConnAckMsg(CONNECTION_REFUSED_NOT_AUTHORIZED)); - ctx.close(); - } +// try { +// String strCert = SslUtil.getX509CertificateString(cert); +// String sha3Hash = EncryptionUtil.getSha3Hash(strCert); +// if (deviceSessionCtx.login(new DeviceX509Credentials(sha3Hash))) { +// ctx.writeAndFlush(createMqttConnAckMsg(CONNECTION_ACCEPTED)); +// connected = true; +// processor.process(new BasicTransportToDeviceSessionActorMsg(deviceSessionCtx.getDevice(), +// new BasicAdaptorToSessionActorMsg(deviceSessionCtx, new SessionOpenMsg()))); +// checkGatewaySession(); +// } else { +// ctx.writeAndFlush(createMqttConnAckMsg(CONNECTION_REFUSED_NOT_AUTHORIZED)); +// ctx.close(); +// } +// } catch (Exception e) { +// ctx.writeAndFlush(createMqttConnAckMsg(CONNECTION_REFUSED_NOT_AUTHORIZED)); +// ctx.close(); +// } } private X509Certificate getX509Certificate() { @@ -380,8 +447,8 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement private void processDisconnect(ChannelHandlerContext ctx) { ctx.close(); - if (connected) { - processor.process(SessionCloseMsg.onDisconnect(deviceSessionCtx.getSessionId())); + if (deviceSessionCtx.isConnected()) { + transportService.process(getSessionEventMsg(SessionEvent.CLOSED), null); if (gatewaySessionCtx != null) { gatewaySessionCtx.onGatewayDisconnect(); } @@ -428,7 +495,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement } private boolean checkConnected(ChannelHandlerContext ctx) { - if (connected) { + if (deviceSessionCtx.isConnected()) { return true; } else { log.info("[{}] Closing current session due to invalid msg order [{}][{}]", sessionId); @@ -438,18 +505,22 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement } private void checkGatewaySession() { - Device device = deviceSessionCtx.getDevice(); - JsonNode infoNode = device.getAdditionalInfo(); - if (infoNode != null) { - JsonNode gatewayNode = infoNode.get("gateway"); - if (gatewayNode != null && gatewayNode.asBoolean()) { - gatewaySessionCtx = new GatewaySessionCtx(processor, deviceService, authService, relationService, deviceSessionCtx); + DeviceInfoProto device = deviceSessionCtx.getDeviceInfo(); + try { + JsonNode infoNode = context.getMapper().readTree(device.getAdditionalInfo()); + if (infoNode != null) { + JsonNode gatewayNode = infoNode.get("gateway"); + if (gatewayNode != null && gatewayNode.asBoolean()) { + gatewaySessionCtx = new GatewaySessionCtx(deviceSessionCtx); + } } + } catch (IOException e) { + log.trace("[{}][{}] Failed to fetch device additional info", sessionId, device.getDeviceName(), e); } } @Override public void operationComplete(Future future) throws Exception { - processor.process(SessionCloseMsg.onError(deviceSessionCtx.getSessionId())); + transportService.process(getSessionEventMsg(SessionEvent.CLOSED), null); } } diff --git a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/DeviceSessionCtx.java b/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/DeviceSessionCtx.java index 3dbb3efe0e..3f29a82fe1 100644 --- a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/DeviceSessionCtx.java +++ b/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/DeviceSessionCtx.java @@ -40,15 +40,13 @@ import java.util.concurrent.atomic.AtomicInteger; @Slf4j public class DeviceSessionCtx extends MqttDeviceAwareSessionContext { - private final MqttTransportAdaptor adaptor; private final MqttSessionId sessionId; private ChannelHandlerContext channel; private volatile boolean allowAttributeResponses; private AtomicInteger msgIdSeq = new AtomicInteger(0); - public DeviceSessionCtx(SessionMsgProcessor processor, DeviceAuthService authService, MqttTransportAdaptor adaptor, ConcurrentMap mqttQoSMap) { - super(processor, authService, mqttQoSMap); - this.adaptor = adaptor; + public DeviceSessionCtx(ConcurrentMap mqttQoSMap) { + super(null, null, null); this.sessionId = new MqttSessionId(); } diff --git a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionCtx.java b/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionCtx.java index 98ad6d2c2c..dec86028b7 100644 --- a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionCtx.java +++ b/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionCtx.java @@ -78,6 +78,10 @@ public class GatewaySessionCtx { this.mqttQoSMap = gatewaySessionCtx.getMqttQoSMap(); } + public GatewaySessionCtx(DeviceSessionCtx deviceSessionCtx) { + + } + public void onDeviceConnect(MqttPublishMessage msg) throws AdaptorException { JsonElement json = getJson(msg); String deviceName = checkDeviceName(getDeviceName(json)); diff --git a/transport/pom.xml b/transport/pom.xml index 95b0112a2a..a01c7ac7ba 100644 --- a/transport/pom.xml +++ b/transport/pom.xml @@ -38,6 +38,7 @@ http coap mqtt + mqtt-transport From caf38f675594a08c66619058c4dfa9d757e7417b Mon Sep 17 00:00:00 2001 From: Andrew Shvayka Date: Fri, 5 Oct 2018 18:52:59 +0300 Subject: [PATCH 03/26] Some refactoring --- application/pom.xml | 2 +- .../server/kafka/TBKafkaConsumerTemplate.java | 3 +- .../server/kafka/TBKafkaProducerTemplate.java | 30 +- .../server/kafka/TbKafkaHandler.java | 12 + .../server/kafka/TbKafkaRequestTemplate.java | 15 +- .../server/kafka/TbKafkaResponseTemplate.java | 173 +++++++++++ common/transport/pom.xml | 5 + .../common/transport/TransportService.java | 15 + .../transport/TransportServiceCallback.java | 17 +- .../tenant/TenantIntervalRegistryCleaner.java | 2 + .../tenant/TenantIntervalRegistryLogger.java | 2 + .../tenant/TenantMsgsIntervalRegistry.java | 2 + .../quota/tenant/TenantQuotaService.java | 2 + .../tenant/TenantRequestLimitPolicy.java | 2 + .../session/DeviceAwareSessionContext.java | 8 +- .../transport/src/main/proto/transport.proto | 14 + .../quota/HostRequestsQuotaServiceTest.java | 6 +- pom.xml | 2 +- transport/{mqtt => mqtt-common}/pom.xml | 4 +- .../mqtt/MqttSslHandlerProvider.java | 0 .../server/transport/mqtt/MqttTopics.java | 0 .../transport/mqtt/MqttTransportContext.java | 0 .../transport/mqtt/MqttTransportHandler.java | 6 +- .../mqtt/MqttTransportServerInitializer.java | 0 .../transport/mqtt/MqttTransportService.java | 0 .../mqtt/adaptors/JsonMqttAdaptor.java | 0 .../mqtt/adaptors/MqttTransportAdaptor.java | 0 .../mqtt/session/DeviceSessionCtx.java | 12 +- .../mqtt/session/GatewayDeviceSessionCtx.java | 6 +- .../mqtt/session/GatewaySessionCtx.java | 284 ++++++++++++++++++ .../MqttDeviceAwareSessionContext.java | 4 +- .../transport/mqtt/session/MqttSessionId.java | 0 .../server/transport/mqtt/util/SslUtil.java | 0 transport/mqtt-transport/pom.xml | 28 +- .../ThingsboardMqttTransportApplication.java | 3 +- .../mqtt/service/AsyncCallbackTemplate.java | 45 +++ .../mqtt/service/MqttTransportService.java | 110 +++++++ .../service/TransportApiRequestEncoder.java | 14 + .../service/TransportApiResponseDecoder.java | 16 + .../src/main/resources/tb-mqtt-transport.yml | 34 ++- .../mqtt/session/GatewaySessionCtx.java | 280 ----------------- transport/pom.xml | 2 +- 42 files changed, 805 insertions(+), 355 deletions(-) create mode 100644 common/queue/src/main/java/org/thingsboard/server/kafka/TbKafkaHandler.java create mode 100644 common/queue/src/main/java/org/thingsboard/server/kafka/TbKafkaResponseTemplate.java rename transport/{mqtt => mqtt-common}/pom.xml (97%) rename transport/{mqtt => mqtt-common}/src/main/java/org/thingsboard/server/transport/mqtt/MqttSslHandlerProvider.java (100%) rename transport/{mqtt => mqtt-common}/src/main/java/org/thingsboard/server/transport/mqtt/MqttTopics.java (100%) rename transport/{mqtt => mqtt-common}/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportContext.java (100%) rename transport/{mqtt => mqtt-common}/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java (99%) rename transport/{mqtt => mqtt-common}/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportServerInitializer.java (100%) rename transport/{mqtt => mqtt-common}/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportService.java (100%) rename transport/{mqtt => mqtt-common}/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java (100%) rename transport/{mqtt => mqtt-common}/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/MqttTransportAdaptor.java (100%) rename transport/{mqtt => mqtt-common}/src/main/java/org/thingsboard/server/transport/mqtt/session/DeviceSessionCtx.java (93%) rename transport/{mqtt => mqtt-common}/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewayDeviceSessionCtx.java (97%) create mode 100644 transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionCtx.java rename transport/{mqtt => mqtt-common}/src/main/java/org/thingsboard/server/transport/mqtt/session/MqttDeviceAwareSessionContext.java (95%) rename transport/{mqtt => mqtt-common}/src/main/java/org/thingsboard/server/transport/mqtt/session/MqttSessionId.java (100%) rename transport/{mqtt => mqtt-common}/src/main/java/org/thingsboard/server/transport/mqtt/util/SslUtil.java (100%) create mode 100644 transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/AsyncCallbackTemplate.java create mode 100644 transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/MqttTransportService.java create mode 100644 transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/TransportApiRequestEncoder.java create mode 100644 transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/TransportApiResponseDecoder.java delete mode 100644 transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionCtx.java diff --git a/application/pom.xml b/application/pom.xml index c4766f2ba8..bff96ec6fd 100644 --- a/application/pom.xml +++ b/application/pom.xml @@ -70,7 +70,7 @@ org.thingsboard.transport - mqtt + mqtt-common org.thingsboard 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 397226479e..3adb1c368d 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,7 +43,8 @@ public class TBKafkaConsumerTemplate { private final String topic; @Builder - private TBKafkaConsumerTemplate(TbKafkaSettings settings, TbKafkaDecoder decoder, TbKafkaRequestIdExtractor requestIdExtractor, + private TBKafkaConsumerTemplate(TbKafkaSettings settings, TbKafkaDecoder decoder, + TbKafkaRequestIdExtractor requestIdExtractor, String clientId, String groupId, String topic, boolean autoCommit, int autoCommitIntervalMs) { Properties props = settings.toProps(); 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 7e24ad00a6..2611e9491a 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 @@ -1,12 +1,12 @@ /** * 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 - * + *

+ * 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. @@ -27,13 +27,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 +47,7 @@ public class TBKafkaProducerTemplate { private TbKafkaEnricher enricher = ((value, responseTopic, requestId) -> value); private final TbKafkaPartitioner partitioner; - private List partitionInfoList; + private ConcurrentMap> partitionInfoMap; @Getter private final String defaultTopic; @@ -78,11 +77,16 @@ public class TBKafkaProducerTemplate { 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 send(String key, T value) { @@ -101,7 +105,7 @@ public class TBKafkaProducerTemplate { byte[] data = encoder.encode(value); ProducerRecord record; Integer partition = getPartition(topic, key, value, data); - record = new ProducerRecord<>(this.defaultTopic, partition, timestamp, key, data, headers); + record = new ProducerRecord<>(topic, partition, timestamp, key, data, headers); return producer.send(record); } @@ -109,7 +113,7 @@ public class TBKafkaProducerTemplate { 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/TbKafkaHandler.java b/common/queue/src/main/java/org/thingsboard/server/kafka/TbKafkaHandler.java new file mode 100644 index 0000000000..66d53c3bde --- /dev/null +++ b/common/queue/src/main/java/org/thingsboard/server/kafka/TbKafkaHandler.java @@ -0,0 +1,12 @@ +package org.thingsboard.server.kafka; + +import java.util.function.Consumer; + +/** + * Created by ashvayka on 05.10.18. + */ +public interface TbKafkaHandler { + + void handle(Request request, Consumer onSuccess, Consumer onFailure); + +} 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 8a0f5293d9..30b20e709d 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 @@ -93,13 +93,12 @@ public class TbKafkaRequestTemplate { ConsumerRecords 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 +106,17 @@ public class TbKafkaRequestTemplate { 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 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); } 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 0000000000..536c4b93b2 --- /dev/null +++ b/common/queue/src/main/java/org/thingsboard/server/kafka/TbKafkaResponseTemplate.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.kafka; + +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.SettableFuture; +import lombok.Builder; +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.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.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 TbKafkaResponseTemplate { + + private final TBKafkaConsumerTemplate requestTemplate; + private final TBKafkaProducerTemplate responseTemplate; + private final TbKafkaHandler handler; + private final ConcurrentMap pendingRequests; + private final ExecutorService executor; + private final long maxPendingRequests; + + private final long pollInterval; + private volatile boolean stopped = false; + + @Builder + public TbKafkaResponseTemplate(TBKafkaConsumerTemplate requestTemplate, + TBKafkaProducerTemplate responseTemplate, + TbKafkaHandler handler, + long pollInterval, + long maxPendingRequests, + ExecutorService executor) { + this.requestTemplate = requestTemplate; + this.responseTemplate = responseTemplate; + this.handler = handler; + this.pendingRequests = new ConcurrentHashMap<>(); + this.maxPendingRequests = maxPendingRequests; + this.pollInterval = pollInterval; + this.executor = executor; + } + + public void init() { + this.responseTemplate.init(); + requestTemplate.subscribe(); + executor.submit(() -> { + long nextCleanupMs = 0L; + while (!stopped) { + ConsumerRecords 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 = bytesToUuid(responseTopicHeader.value()); + if (requestId == null) { + log.error("[{}] Missing requestId in header and body", request); + return; + } + + Request decodedRequest = null; + String responseTopic = null; + + try { + if (decodedRequest == null) { + decodedRequest = requestTemplate.decode(request); + } + executor.submit(() -> { + handler.handle(decodedRequest, ); + }); + } catch (IOException e) { + expectedRequest.future.setException(e); + } + + }); + } + }); + } + + public void stop() { + stopped = true; + } + + public ListenableFuture post(String key, Request request) { + if (tickSize > maxPendingRequests) { + return Futures.immediateFailedFuture(new RuntimeException("Pending request map is full!")); + } + UUID requestId = UUID.randomUUID(); + List

headers = new ArrayList<>(2); + headers.add(new RecordHeader(TbKafkaSettings.REQUEST_ID_HEADER, uuidToBytes(requestId))); + headers.add(new RecordHeader(TbKafkaSettings.RESPONSE_TOPIC_HEADER, stringToBytes(responseTemplate.getTopic()))); + SettableFuture future = SettableFuture.create(); + pendingRequests.putIfAbsent(requestId, new ResponseMetaData<>(tickTs + maxRequestTimeout, future)); + request = requestTemplate.enrich(request, responseTemplate.getTopic(), requestId); + requestTemplate.send(key, request, headers); + 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 String bytesToString(byte[] data) { + return new String(data, StandardCharsets.UTF_8); + } + + private static class ResponseMetaData { + private final long expTime; + private final SettableFuture future; + + ResponseMetaData(long ts, SettableFuture future) { + this.expTime = ts; + this.future = future; + } + } + +} diff --git a/common/transport/pom.xml b/common/transport/pom.xml index b8a7ab53f0..46efbf4dcc 100644 --- a/common/transport/pom.xml +++ b/common/transport/pom.xml @@ -78,6 +78,11 @@ org.springframework spring-context + + org.springframework.boot + spring-boot-starter-web + provided + com.google.guava guava diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/TransportService.java b/common/transport/src/main/java/org/thingsboard/server/common/transport/TransportService.java index ff0debcd4b..e72a626ca1 100644 --- a/common/transport/src/main/java/org/thingsboard/server/common/transport/TransportService.java +++ b/common/transport/src/main/java/org/thingsboard/server/common/transport/TransportService.java @@ -1,3 +1,18 @@ +/** + * 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; diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/TransportServiceCallback.java b/common/transport/src/main/java/org/thingsboard/server/common/transport/TransportServiceCallback.java index bfc7ac890e..5443f7ed29 100644 --- a/common/transport/src/main/java/org/thingsboard/server/common/transport/TransportServiceCallback.java +++ b/common/transport/src/main/java/org/thingsboard/server/common/transport/TransportServiceCallback.java @@ -1,3 +1,18 @@ +/** + * 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; /** @@ -6,6 +21,6 @@ package org.thingsboard.server.common.transport; public interface TransportServiceCallback { void onSuccess(T msg); - void onError(Exception e); + void onError(Throwable e); } diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantIntervalRegistryCleaner.java b/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantIntervalRegistryCleaner.java index c48117069a..47b427840a 100644 --- a/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantIntervalRegistryCleaner.java +++ b/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantIntervalRegistryCleaner.java @@ -16,10 +16,12 @@ package org.thingsboard.server.common.transport.quota.tenant; import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.stereotype.Component; import org.thingsboard.server.common.transport.quota.inmemory.IntervalRegistryCleaner; @Component +@ConditionalOnProperty(prefix = "quota.rule.tenant", value = "enabled", havingValue = "true", matchIfMissing = false) public class TenantIntervalRegistryCleaner extends IntervalRegistryCleaner { public TenantIntervalRegistryCleaner(TenantMsgsIntervalRegistry intervalRegistry, diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantIntervalRegistryLogger.java b/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantIntervalRegistryLogger.java index c56f457423..3bdcf93d07 100644 --- a/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantIntervalRegistryLogger.java +++ b/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantIntervalRegistryLogger.java @@ -17,6 +17,7 @@ package org.thingsboard.server.common.transport.quota.tenant; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.stereotype.Component; import org.thingsboard.server.common.transport.quota.inmemory.IntervalRegistryLogger; @@ -25,6 +26,7 @@ import java.util.concurrent.TimeUnit; @Slf4j @Component +@ConditionalOnProperty(prefix = "quota.rule.tenant", value = "enabled", havingValue = "true", matchIfMissing = false) public class TenantIntervalRegistryLogger extends IntervalRegistryLogger { private final long logIntervalMin; diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantMsgsIntervalRegistry.java b/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantMsgsIntervalRegistry.java index 6e8402c701..3402e62472 100644 --- a/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantMsgsIntervalRegistry.java +++ b/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantMsgsIntervalRegistry.java @@ -16,10 +16,12 @@ package org.thingsboard.server.common.transport.quota.tenant; import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.stereotype.Component; import org.thingsboard.server.common.transport.quota.inmemory.KeyBasedIntervalRegistry; @Component +@ConditionalOnProperty(prefix = "quota.rule.tenant", value = "enabled", havingValue = "true", matchIfMissing = false) public class TenantMsgsIntervalRegistry extends KeyBasedIntervalRegistry { public TenantMsgsIntervalRegistry(@Value("${quota.rule.tenant.intervalMs}") long intervalDurationMs, diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantQuotaService.java b/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantQuotaService.java index a68860a2e0..875a666a28 100644 --- a/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantQuotaService.java +++ b/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantQuotaService.java @@ -16,10 +16,12 @@ package org.thingsboard.server.common.transport.quota.tenant; import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.stereotype.Component; import org.thingsboard.server.common.transport.quota.AbstractQuotaService; @Component +@ConditionalOnProperty(prefix = "quota.rule.tenant", value = "enabled", havingValue = "true", matchIfMissing = false) public class TenantQuotaService extends AbstractQuotaService { public TenantQuotaService(TenantMsgsIntervalRegistry requestRegistry, TenantRequestLimitPolicy requestsPolicy, diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantRequestLimitPolicy.java b/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantRequestLimitPolicy.java index cc32c81ad7..c5dfc40480 100644 --- a/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantRequestLimitPolicy.java +++ b/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantRequestLimitPolicy.java @@ -16,10 +16,12 @@ package org.thingsboard.server.common.transport.quota.tenant; import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.stereotype.Component; import org.thingsboard.server.common.transport.quota.RequestLimitPolicy; @Component +@ConditionalOnProperty(prefix = "quota.rule.tenant", value = "enabled", havingValue = "true", matchIfMissing = false) public class TenantRequestLimitPolicy extends RequestLimitPolicy { public TenantRequestLimitPolicy(@Value("${quota.rule.tenant.limit}") long limit) { diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/session/DeviceAwareSessionContext.java b/common/transport/src/main/java/org/thingsboard/server/common/transport/session/DeviceAwareSessionContext.java index 28269454c7..7b2c05e400 100644 --- a/common/transport/src/main/java/org/thingsboard/server/common/transport/session/DeviceAwareSessionContext.java +++ b/common/transport/src/main/java/org/thingsboard/server/common/transport/session/DeviceAwareSessionContext.java @@ -1,12 +1,12 @@ /** * 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 - *

+ * + * 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. diff --git a/common/transport/src/main/proto/transport.proto b/common/transport/src/main/proto/transport.proto index 65d1468605..f0cc3c75ed 100644 --- a/common/transport/src/main/proto/transport.proto +++ b/common/transport/src/main/proto/transport.proto @@ -95,3 +95,17 @@ message ValidateDeviceTokenResponseMsg { DeviceInfoProto deviceInfo = 1; } +/** + * Main messages; + */ +message TransportToRuleEngineMsg { + +} + +message TransportApiRequestMsg { + ValidateDeviceTokenRequestMsg validateTokenRequestMsg = 1; +} + +message TransportApiResponseMsg { + ValidateDeviceTokenResponseMsg validateTokenResponseMsg = 1; +} \ No newline at end of file diff --git a/common/transport/src/test/java/org/thingsboard/server/common/transport/quota/HostRequestsQuotaServiceTest.java b/common/transport/src/test/java/org/thingsboard/server/common/transport/quota/HostRequestsQuotaServiceTest.java index 20f8a55d0d..37441e9d41 100644 --- a/common/transport/src/test/java/org/thingsboard/server/common/transport/quota/HostRequestsQuotaServiceTest.java +++ b/common/transport/src/test/java/org/thingsboard/server/common/transport/quota/HostRequestsQuotaServiceTest.java @@ -17,7 +17,11 @@ package org.thingsboard.server.common.transport.quota; import org.junit.Before; import org.junit.Test; -import org.thingsboard.server.common.transport.quota.host.*; +import org.thingsboard.server.common.transport.quota.host.HostIntervalRegistryCleaner; +import org.thingsboard.server.common.transport.quota.host.HostIntervalRegistryLogger; +import org.thingsboard.server.common.transport.quota.host.HostRequestIntervalRegistry; +import org.thingsboard.server.common.transport.quota.host.HostRequestLimitPolicy; +import org.thingsboard.server.common.transport.quota.host.HostRequestsQuotaService; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; diff --git a/pom.xml b/pom.xml index d6db123ebe..62fda36056 100755 --- a/pom.xml +++ b/pom.xml @@ -366,7 +366,7 @@ org.thingsboard.transport - mqtt + mqtt-common ${project.version} diff --git a/transport/mqtt/pom.xml b/transport/mqtt-common/pom.xml similarity index 97% rename from transport/mqtt/pom.xml rename to transport/mqtt-common/pom.xml index 785e9a5306..6be5235a8a 100644 --- a/transport/mqtt/pom.xml +++ b/transport/mqtt-common/pom.xml @@ -24,10 +24,10 @@ transport org.thingsboard.transport - mqtt + mqtt-common jar - Thingsboard MQTT Transport + Thingsboard MQTT Transport Common https://thingsboard.io diff --git a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttSslHandlerProvider.java b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttSslHandlerProvider.java similarity index 100% rename from transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttSslHandlerProvider.java rename to transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttSslHandlerProvider.java diff --git a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTopics.java b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTopics.java similarity index 100% rename from transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTopics.java rename to transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTopics.java diff --git a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportContext.java b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportContext.java similarity index 100% rename from transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportContext.java rename to transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportContext.java diff --git a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java similarity index 99% rename from transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java rename to transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java index fd02d3aa39..8c64e7eeb2 100644 --- a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java +++ b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java @@ -395,7 +395,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement } @Override - public void onError(Exception e) { + public void onError(Throwable e) { log.trace("[{}] Failed to process credentials: {}", address, userName, e); ctx.writeAndFlush(createMqttConnAckMsg(MqttConnectReturnCode.CONNECTION_REFUSED_SERVER_UNAVAILABLE)); ctx.close(); @@ -521,6 +521,8 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement @Override public void operationComplete(Future future) throws Exception { - transportService.process(getSessionEventMsg(SessionEvent.CLOSED), null); + if (deviceSessionCtx.isConnected()) { + transportService.process(getSessionEventMsg(SessionEvent.CLOSED), null); + } } } diff --git a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportServerInitializer.java b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportServerInitializer.java similarity index 100% rename from transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportServerInitializer.java rename to transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportServerInitializer.java diff --git a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportService.java b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportService.java similarity index 100% rename from transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportService.java rename to transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportService.java diff --git a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java similarity index 100% rename from transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java rename to transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java diff --git a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/MqttTransportAdaptor.java b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/MqttTransportAdaptor.java similarity index 100% rename from transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/MqttTransportAdaptor.java rename to transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/MqttTransportAdaptor.java diff --git a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/DeviceSessionCtx.java b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/DeviceSessionCtx.java similarity index 93% rename from transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/DeviceSessionCtx.java rename to transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/DeviceSessionCtx.java index 3f29a82fe1..6661cb12f4 100644 --- a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/DeviceSessionCtx.java +++ b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/DeviceSessionCtx.java @@ -57,12 +57,12 @@ public class DeviceSessionCtx extends MqttDeviceAwareSessionContext { @Override public void onMsg(SessionActorToAdaptorMsg msg) throws SessionException { - try { - adaptor.convertToAdaptorMsg(this, msg).ifPresent(this::pushToNetwork); - } catch (AdaptorException e) { - //TODO: close channel with disconnect; - logAndWrap(e); - } +// try { +// adaptor.convertToAdaptorMsg(this, msg).ifPresent(this::pushToNetwork); +// } catch (AdaptorException e) { +// //TODO: close channel with disconnect; +// logAndWrap(e); +// } } private void logAndWrap(AdaptorException e) throws SessionException { diff --git a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewayDeviceSessionCtx.java b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewayDeviceSessionCtx.java similarity index 97% rename from transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewayDeviceSessionCtx.java rename to transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewayDeviceSessionCtx.java index e5be5c71a1..793836d793 100644 --- a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewayDeviceSessionCtx.java +++ b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewayDeviceSessionCtx.java @@ -135,7 +135,7 @@ public class GatewayDeviceSessionCtx extends MqttDeviceAwareSessionContext { private MqttMessage createMqttPublishMsg(String topic, GetAttributesResponse response) { JsonObject result = new JsonObject(); result.addProperty("id", response.getRequestId()); - result.addProperty(DEVICE_PROPERTY, device.getName()); +// result.addProperty(DEVICE_PROPERTY, device.getName()); Optional responseData = response.getData(); if (responseData.isPresent()) { AttributesKVMsg msg = responseData.get(); @@ -183,14 +183,14 @@ public class GatewayDeviceSessionCtx extends MqttDeviceAwareSessionContext { private MqttMessage createMqttPublishMsg(String topic, AttributesKVMsg data) { JsonObject result = new JsonObject(); - result.addProperty(DEVICE_PROPERTY, device.getName()); +// result.addProperty(DEVICE_PROPERTY, device.getName()); result.add("data", JsonConverter.toJson(data, false)); return createMqttPublishMsg(topic, result); } private MqttMessage createMqttPublishMsg(String topic, ToDeviceRpcRequestMsg data) { JsonObject result = new JsonObject(); - result.addProperty(DEVICE_PROPERTY, device.getName()); +// result.addProperty(DEVICE_PROPERTY, device.getName()); result.add("data", JsonConverter.toJson(data, true)); return createMqttPublishMsg(topic, result); } diff --git a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionCtx.java b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionCtx.java new file mode 100644 index 0000000000..fafbc4ec01 --- /dev/null +++ b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionCtx.java @@ -0,0 +1,284 @@ +/** + * 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.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.data.Device; +import org.thingsboard.server.common.data.id.SessionId; +import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; +import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.msg.core.*; +import org.thingsboard.server.common.msg.session.BasicAdaptorToSessionActorMsg; +import org.thingsboard.server.common.msg.session.BasicTransportToDeviceSessionActorMsg; +import org.thingsboard.server.common.msg.session.ctrl.SessionCloseMsg; +import org.thingsboard.server.common.transport.SessionMsgProcessor; +import org.thingsboard.server.common.transport.adaptor.AdaptorException; +import org.thingsboard.server.common.transport.adaptor.JsonConverter; +import org.thingsboard.server.common.transport.auth.DeviceAuthService; +import org.thingsboard.server.dao.device.DeviceService; +import org.thingsboard.server.dao.relation.RelationService; +import org.thingsboard.server.transport.mqtt.MqttTransportHandler; +import org.thingsboard.server.transport.mqtt.adaptors.JsonMqttAdaptor; + +import java.util.*; +import java.util.concurrent.ConcurrentMap; +import java.util.stream.Collectors; + +import static org.thingsboard.server.transport.mqtt.adaptors.JsonMqttAdaptor.validateJsonPayload; + +/** + * Created by ashvayka on 19.01.17. + */ +@Slf4j +public class GatewaySessionCtx { + + private static final String DEFAULT_DEVICE_TYPE = "default"; + public static final String CAN_T_PARSE_VALUE = "Can't parse value: "; + public static final String DEVICE_PROPERTY = "device"; +// private final Device gateway; +// private final SessionId gatewaySessionId; +// private final SessionMsgProcessor processor; +// private final DeviceService deviceService; +// private final DeviceAuthService authService; +// private final RelationService relationService; +// private final Map devices; +// private final ConcurrentMap mqttQoSMap; + private ChannelHandlerContext channel; + +// public GatewaySessionCtx(SessionMsgProcessor processor, DeviceService deviceService, DeviceAuthService authService, RelationService relationService, DeviceSessionCtx gatewaySessionCtx) { +// this.processor = processor; +// this.deviceService = deviceService; +// this.authService = authService; +// this.relationService = relationService; +// this.gateway = gatewaySessionCtx.getDevice(); +// this.gatewaySessionId = gatewaySessionCtx.getSessionId(); +// this.devices = new HashMap<>(); +// this.mqttQoSMap = gatewaySessionCtx.getMqttQoSMap(); +// } + + public GatewaySessionCtx(DeviceSessionCtx deviceSessionCtx) { + + } + + public void onDeviceConnect(MqttPublishMessage msg) throws AdaptorException { + JsonElement json = getJson(msg); + String deviceName = checkDeviceName(getDeviceName(json)); + String deviceType = getDeviceType(json); + onDeviceConnect(deviceName, deviceType); + ack(msg); + } + + private void onDeviceConnect(String deviceName, String deviceType) { +// if (!devices.containsKey(deviceName)) { +// Device device = deviceService.findDeviceByTenantIdAndName(gateway.getTenantId(), deviceName); +// if (device == null) { +// device = new Device(); +// device.setTenantId(gateway.getTenantId()); +// device.setName(deviceName); +// device.setType(deviceType); +// device.setCustomerId(gateway.getCustomerId()); +// device = deviceService.saveDevice(device); +// relationService.saveRelationAsync(new EntityRelation(gateway.getId(), device.getId(), "Created")); +// processor.onDeviceAdded(device); +// } +// GatewayDeviceSessionCtx ctx = new GatewayDeviceSessionCtx(this, device, mqttQoSMap); +// devices.put(deviceName, ctx); +// log.debug("[{}] Added device [{}] to the gateway session", gatewaySessionId, deviceName); +// processor.process(new BasicTransportToDeviceSessionActorMsg(device, new BasicAdaptorToSessionActorMsg(ctx, new AttributesSubscribeMsg()))); +// processor.process(new BasicTransportToDeviceSessionActorMsg(device, new BasicAdaptorToSessionActorMsg(ctx, new RpcSubscribeMsg()))); +// } + } + + public void onDeviceDisconnect(MqttPublishMessage msg) throws AdaptorException { +// String deviceName = checkDeviceName(getDeviceName(getJson(msg))); +// GatewayDeviceSessionCtx deviceSessionCtx = devices.remove(deviceName); +// if (deviceSessionCtx != null) { +// processor.process(SessionCloseMsg.onDisconnect(deviceSessionCtx.getSessionId())); +// deviceSessionCtx.setClosed(true); +// log.debug("[{}] Removed device [{}] from the gateway session", gatewaySessionId, deviceName); +// } else { +// log.debug("[{}] Device [{}] was already removed from the gateway session", gatewaySessionId, deviceName); +// } +// ack(msg); + } + + public void onGatewayDisconnect() { +// devices.forEach((k, v) -> { +// processor.process(SessionCloseMsg.onDisconnect(v.getSessionId())); +// }); + } + + public void onDeviceTelemetry(MqttPublishMessage mqttMsg) throws AdaptorException { +// JsonElement json = validateJsonPayload(gatewaySessionId, mqttMsg.payload()); +// int requestId = mqttMsg.variableHeader().messageId(); +// if (json.isJsonObject()) { +// JsonObject jsonObj = json.getAsJsonObject(); +// for (Map.Entry deviceEntry : jsonObj.entrySet()) { +// String deviceName = checkDeviceConnected(deviceEntry.getKey()); +// if (!deviceEntry.getValue().isJsonArray()) { +// throw new JsonSyntaxException(CAN_T_PARSE_VALUE + json); +// } +// BasicTelemetryUploadRequest request = new BasicTelemetryUploadRequest(requestId); +// JsonArray deviceData = deviceEntry.getValue().getAsJsonArray(); +// for (JsonElement element : deviceData) { +// JsonConverter.parseWithTs(request, element.getAsJsonObject()); +// } +// GatewayDeviceSessionCtx deviceSessionCtx = devices.get(deviceName); +// processor.process(new BasicTransportToDeviceSessionActorMsg(deviceSessionCtx.getDevice(), +// new BasicAdaptorToSessionActorMsg(deviceSessionCtx, request))); +// } +// } else { +// throw new JsonSyntaxException(CAN_T_PARSE_VALUE + json); +// } + } + + public void onDeviceRpcResponse(MqttPublishMessage mqttMsg) throws AdaptorException { +// JsonElement json = validateJsonPayload(gatewaySessionId, mqttMsg.payload()); +// if (json.isJsonObject()) { +// JsonObject jsonObj = json.getAsJsonObject(); +// String deviceName = checkDeviceConnected(jsonObj.get(DEVICE_PROPERTY).getAsString()); +// Integer requestId = jsonObj.get("id").getAsInt(); +// String data = jsonObj.get("data").toString(); +// GatewayDeviceSessionCtx deviceSessionCtx = devices.get(deviceName); +// processor.process(new BasicTransportToDeviceSessionActorMsg(deviceSessionCtx.getDevice(), +// new BasicAdaptorToSessionActorMsg(deviceSessionCtx, new ToDeviceRpcResponseMsg(requestId, data)))); +// ack(mqttMsg); +// } else { +// throw new JsonSyntaxException(CAN_T_PARSE_VALUE + json); +// } + } + + public void onDeviceAttributes(MqttPublishMessage mqttMsg) throws AdaptorException { +// JsonElement json = validateJsonPayload(gatewaySessionId, mqttMsg.payload()); +// int requestId = mqttMsg.variableHeader().messageId(); +// if (json.isJsonObject()) { +// JsonObject jsonObj = json.getAsJsonObject(); +// for (Map.Entry deviceEntry : jsonObj.entrySet()) { +// String deviceName = checkDeviceConnected(deviceEntry.getKey()); +// if (!deviceEntry.getValue().isJsonObject()) { +// throw new JsonSyntaxException(CAN_T_PARSE_VALUE + json); +// } +// long ts = System.currentTimeMillis(); +// BasicAttributesUpdateRequest request = new BasicAttributesUpdateRequest(requestId); +// JsonObject deviceData = deviceEntry.getValue().getAsJsonObject(); +// request.add(JsonConverter.parseValues(deviceData).stream().map(kv -> new BaseAttributeKvEntry(kv, ts)).collect(Collectors.toList())); +// GatewayDeviceSessionCtx deviceSessionCtx = devices.get(deviceName); +// processor.process(new BasicTransportToDeviceSessionActorMsg(deviceSessionCtx.getDevice(), +// new BasicAdaptorToSessionActorMsg(deviceSessionCtx, request))); +// } +// } else { +// throw new JsonSyntaxException(CAN_T_PARSE_VALUE + json); +// } + } + + public void onDeviceAttributesRequest(MqttPublishMessage msg) throws AdaptorException { +// JsonElement json = validateJsonPayload(gatewaySessionId, 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 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()); +// } +// } +// +// BasicGetAttributesRequest request; +// if (clientScope) { +// request = new BasicGetAttributesRequest(requestId, keys, null); +// } else { +// request = new BasicGetAttributesRequest(requestId, null, keys); +// } +// GatewayDeviceSessionCtx deviceSessionCtx = devices.get(deviceName); +// processor.process(new BasicTransportToDeviceSessionActorMsg(deviceSessionCtx.getDevice(), +// new BasicAdaptorToSessionActorMsg(deviceSessionCtx, request))); +// ack(msg); +// } else { +// throw new JsonSyntaxException(CAN_T_PARSE_VALUE + json); +// } + } + + private String checkDeviceConnected(String deviceName) { +// if (!devices.containsKey(deviceName)) { +// log.debug("[{}] Missing device [{}] for the gateway session", gatewaySessionId, deviceName); +// onDeviceConnect(deviceName, DEFAULT_DEVICE_TYPE); +// } +// return deviceName; + return null; + } + + 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(gatewaySessionId, mqttMsg.payload()); + return null; + } + + protected SessionMsgProcessor getProcessor() { +// return processor; + return null; + } + + DeviceAuthService getAuthService() { +// return authService; + return null; + } + + public void setChannel(ChannelHandlerContext channel) { + this.channel = channel; + } + + private void ack(MqttPublishMessage msg) { + if (msg.variableHeader().messageId() > 0) { + writeAndFlush(MqttTransportHandler.createMqttPubAckMsg(msg.variableHeader().messageId())); + } + } + + void writeAndFlush(MqttMessage mqttMessage) { + channel.writeAndFlush(mqttMessage); + } + +} diff --git a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/MqttDeviceAwareSessionContext.java b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/MqttDeviceAwareSessionContext.java similarity index 95% rename from transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/MqttDeviceAwareSessionContext.java rename to transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/MqttDeviceAwareSessionContext.java index f085064016..c01e103889 100644 --- a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/MqttDeviceAwareSessionContext.java +++ b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/MqttDeviceAwareSessionContext.java @@ -32,12 +32,12 @@ public abstract class MqttDeviceAwareSessionContext extends DeviceAwareSessionCo private final ConcurrentMap mqttQoSMap; public MqttDeviceAwareSessionContext(SessionMsgProcessor processor, DeviceAuthService authService, ConcurrentMap mqttQoSMap) { - super(processor, authService); + super(); this.mqttQoSMap = mqttQoSMap; } public MqttDeviceAwareSessionContext(SessionMsgProcessor processor, DeviceAuthService authService, Device device, ConcurrentMap mqttQoSMap) { - super(processor, authService, device); + super(); this.mqttQoSMap = mqttQoSMap; } diff --git a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/MqttSessionId.java b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/MqttSessionId.java similarity index 100% rename from transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/MqttSessionId.java rename to transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/MqttSessionId.java diff --git a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/util/SslUtil.java b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/util/SslUtil.java similarity index 100% rename from transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/util/SslUtil.java rename to transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/util/SslUtil.java diff --git a/transport/mqtt-transport/pom.xml b/transport/mqtt-transport/pom.xml index 8d8b24d6c6..012aabd4e9 100644 --- a/transport/mqtt-transport/pom.xml +++ b/transport/mqtt-transport/pom.xml @@ -41,32 +41,12 @@ transport - io.netty - netty-all + org.thingsboard.transport + mqtt-common - org.springframework - spring-context - - - org.slf4j - slf4j-api - - - org.slf4j - log4j-over-slf4j - - - ch.qos.logback - logback-core - - - ch.qos.logback - logback-classic - - - com.google.guava - guava + org.thingsboard.common + queue org.springframework.boot diff --git a/transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/ThingsboardMqttTransportApplication.java b/transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/ThingsboardMqttTransportApplication.java index 3c54938a37..4740342692 100644 --- a/transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/ThingsboardMqttTransportApplication.java +++ b/transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/ThingsboardMqttTransportApplication.java @@ -19,14 +19,13 @@ import org.springframework.boot.SpringBootConfiguration; import org.springframework.context.annotation.ComponentScan; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.annotation.EnableScheduling; -import springfox.documentation.swagger2.annotations.EnableSwagger2; import java.util.Arrays; @SpringBootConfiguration @EnableAsync @EnableScheduling -@ComponentScan({"org.thingsboard.server"}) +@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"; diff --git a/transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/AsyncCallbackTemplate.java b/transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/AsyncCallbackTemplate.java new file mode 100644 index 0000000000..493c0c8b9f --- /dev/null +++ b/transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/AsyncCallbackTemplate.java @@ -0,0 +1,45 @@ +package org.thingsboard.server.mqtt.service; + +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; + +import javax.annotation.Nullable; +import java.util.concurrent.Executor; +import java.util.function.Consumer; + +/** + * Created by ashvayka on 05.10.18. + */ +public class AsyncCallbackTemplate { + + public static void withCallback(ListenableFuture future, Consumer onSuccess, + Consumer onFailure) { + withCallback(future, onSuccess, onFailure, null); + } + + public static void withCallback(ListenableFuture future, Consumer onSuccess, + Consumer onFailure, Executor executor) { + FutureCallback callback = new FutureCallback() { + @Override + public void onSuccess(@Nullable 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/transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/MqttTransportService.java b/transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/MqttTransportService.java new file mode 100644 index 0000000000..a1641f152f --- /dev/null +++ b/transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/MqttTransportService.java @@ -0,0 +1,110 @@ +package org.thingsboard.server.mqtt.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.thingsboard.server.common.transport.TransportService; +import org.thingsboard.server.common.transport.TransportServiceCallback; +import org.thingsboard.server.kafka.TBKafkaConsumerTemplate; +import org.thingsboard.server.kafka.TBKafkaProducerTemplate; +import org.thingsboard.server.kafka.TbKafkaRequestTemplate; +import org.thingsboard.server.gen.transport.TransportProtos.*; +import org.thingsboard.server.kafka.TbKafkaSettings; +import org.thingsboard.server.transport.mqtt.MqttTransportContext; + +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +/** + * Created by ashvayka on 05.10.18. + */ +@Service +public class MqttTransportService implements TransportService { + + @Value("${kafka.rule-engine.topic}") + private String ruleEngineTopic; + @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 MqttTransportContext transportContext; + + private ExecutorService transportCallbackExecutor; + + private TbKafkaRequestTemplate transportApiTemplate; + + @PostConstruct + public void init() { + this.transportCallbackExecutor = Executors.newCachedThreadPool(); + + TBKafkaProducerTemplate.TBKafkaProducerTemplateBuilder requestBuilder = TBKafkaProducerTemplate.builder(); + requestBuilder.settings(kafkaSettings); + requestBuilder.defaultTopic(transportApiRequestsTopic); + requestBuilder.encoder(new TransportApiRequestEncoder()); + + TBKafkaConsumerTemplate.TBKafkaConsumerTemplateBuilder responseBuilder = TBKafkaConsumerTemplate.builder(); + responseBuilder.settings(kafkaSettings); + responseBuilder.topic(transportApiResponsesTopic + "." + transportContext.getNodeId()); + responseBuilder.clientId(transportContext.getNodeId()); + responseBuilder.groupId("transport-node"); + responseBuilder.autoCommit(true); + responseBuilder.autoCommitIntervalMs(autoCommitInterval); + responseBuilder.decoder(new TransportApiResponseDecoder()); + + TbKafkaRequestTemplate.TbKafkaRequestTemplateBuilder + 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(); + } + + @PreDestroy + public void destroy() { + if (transportApiTemplate != null) { + transportApiTemplate.stop(); + } + if (transportCallbackExecutor != null) { + transportCallbackExecutor.shutdownNow(); + } + } + + @Override + public void process(ValidateDeviceTokenRequestMsg msg, TransportServiceCallback callback) { + AsyncCallbackTemplate.withCallback(transportApiTemplate.post(msg.getToken(), TransportApiRequestMsg.newBuilder().setValidateTokenRequestMsg(msg).build()), + response -> callback.onSuccess(response.getValidateTokenResponseMsg()), callback::onError, transportCallbackExecutor); + } + + @Override + public void process(SessionEventMsg msg, TransportServiceCallback callback) { + + } + + @Override + public void process(PostTelemetryMsg msg, TransportServiceCallback callback) { + + } + + @Override + public void process(PostAttributeMsg msg, TransportServiceCallback callback) { + + } +} diff --git a/transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/TransportApiRequestEncoder.java b/transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/TransportApiRequestEncoder.java new file mode 100644 index 0000000000..f931db6a2e --- /dev/null +++ b/transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/TransportApiRequestEncoder.java @@ -0,0 +1,14 @@ +package org.thingsboard.server.mqtt.service; + +import org.thingsboard.server.gen.transport.TransportProtos.TransportApiRequestMsg; +import org.thingsboard.server.kafka.TbKafkaEncoder; + +/** + * Created by ashvayka on 05.10.18. + */ +public class TransportApiRequestEncoder implements TbKafkaEncoder { + @Override + public byte[] encode(TransportApiRequestMsg value) { + return value.toByteArray(); + } +} diff --git a/transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/TransportApiResponseDecoder.java b/transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/TransportApiResponseDecoder.java new file mode 100644 index 0000000000..22e16478ae --- /dev/null +++ b/transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/TransportApiResponseDecoder.java @@ -0,0 +1,16 @@ +package org.thingsboard.server.mqtt.service; + +import org.thingsboard.server.gen.transport.TransportProtos.TransportApiResponseMsg; +import org.thingsboard.server.kafka.TbKafkaDecoder; + +import java.io.IOException; + +/** + * Created by ashvayka on 05.10.18. + */ +public class TransportApiResponseDecoder implements TbKafkaDecoder { + @Override + public TransportApiResponseMsg decode(byte[] data) throws IOException { + return TransportApiResponseMsg.parseFrom(data); + } +} diff --git a/transport/mqtt-transport/src/main/resources/tb-mqtt-transport.yml b/transport/mqtt-transport/src/main/resources/tb-mqtt-transport.yml index c12f25edcf..a2a0d54adc 100644 --- a/transport/mqtt-transport/src/main/resources/tb-mqtt-transport.yml +++ b/transport/mqtt-transport/src/main/resources/tb-mqtt-transport.yml @@ -44,6 +44,27 @@ mqtt: # 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:false}" + # 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}" @@ -52,6 +73,13 @@ kafka: batch.size: "${TB_KAFKA_BATCH_SIZE:16384}" linger.ms: "${TB_KAFKA_LINGER_MS:1}" buffer.memory: "${TB_BUFFER_MEMORY:33554432}" - topic: - telemetry: "${TB_TELEMETRY_TOPIC:tb.transport.telemetry}" - requests: "${TB_TELEMETRY_TOPIC:tb.transport.requests}" \ No newline at end of file + 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}" + # Maximum allowed JavaScript execution errors before JavaScript will be blacklisted + rule-engine: + topic: "${TB_RULE_ENGINE_TOPIC:tb.rule-engine}" diff --git a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionCtx.java b/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionCtx.java deleted file mode 100644 index dec86028b7..0000000000 --- a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionCtx.java +++ /dev/null @@ -1,280 +0,0 @@ -/** - * 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.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.data.Device; -import org.thingsboard.server.common.data.id.SessionId; -import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; -import org.thingsboard.server.common.data.relation.EntityRelation; -import org.thingsboard.server.common.msg.core.*; -import org.thingsboard.server.common.msg.session.BasicAdaptorToSessionActorMsg; -import org.thingsboard.server.common.msg.session.BasicTransportToDeviceSessionActorMsg; -import org.thingsboard.server.common.msg.session.ctrl.SessionCloseMsg; -import org.thingsboard.server.common.transport.SessionMsgProcessor; -import org.thingsboard.server.common.transport.adaptor.AdaptorException; -import org.thingsboard.server.common.transport.adaptor.JsonConverter; -import org.thingsboard.server.common.transport.auth.DeviceAuthService; -import org.thingsboard.server.dao.device.DeviceService; -import org.thingsboard.server.dao.relation.RelationService; -import org.thingsboard.server.transport.mqtt.MqttTransportHandler; -import org.thingsboard.server.transport.mqtt.adaptors.JsonMqttAdaptor; - -import java.util.*; -import java.util.concurrent.ConcurrentMap; -import java.util.stream.Collectors; - -import static org.thingsboard.server.transport.mqtt.adaptors.JsonMqttAdaptor.validateJsonPayload; - -/** - * Created by ashvayka on 19.01.17. - */ -@Slf4j -public class GatewaySessionCtx { - - private static final String DEFAULT_DEVICE_TYPE = "default"; - public static final String CAN_T_PARSE_VALUE = "Can't parse value: "; - public static final String DEVICE_PROPERTY = "device"; - private final Device gateway; - private final SessionId gatewaySessionId; - private final SessionMsgProcessor processor; - private final DeviceService deviceService; - private final DeviceAuthService authService; - private final RelationService relationService; - private final Map devices; - private final ConcurrentMap mqttQoSMap; - private ChannelHandlerContext channel; - - public GatewaySessionCtx(SessionMsgProcessor processor, DeviceService deviceService, DeviceAuthService authService, RelationService relationService, DeviceSessionCtx gatewaySessionCtx) { - this.processor = processor; - this.deviceService = deviceService; - this.authService = authService; - this.relationService = relationService; - this.gateway = gatewaySessionCtx.getDevice(); - this.gatewaySessionId = gatewaySessionCtx.getSessionId(); - this.devices = new HashMap<>(); - this.mqttQoSMap = gatewaySessionCtx.getMqttQoSMap(); - } - - public GatewaySessionCtx(DeviceSessionCtx deviceSessionCtx) { - - } - - public void onDeviceConnect(MqttPublishMessage msg) throws AdaptorException { - JsonElement json = getJson(msg); - String deviceName = checkDeviceName(getDeviceName(json)); - String deviceType = getDeviceType(json); - onDeviceConnect(deviceName, deviceType); - ack(msg); - } - - private void onDeviceConnect(String deviceName, String deviceType) { - if (!devices.containsKey(deviceName)) { - Device device = deviceService.findDeviceByTenantIdAndName(gateway.getTenantId(), deviceName); - if (device == null) { - device = new Device(); - device.setTenantId(gateway.getTenantId()); - device.setName(deviceName); - device.setType(deviceType); - device.setCustomerId(gateway.getCustomerId()); - device = deviceService.saveDevice(device); - relationService.saveRelationAsync(new EntityRelation(gateway.getId(), device.getId(), "Created")); - processor.onDeviceAdded(device); - } - GatewayDeviceSessionCtx ctx = new GatewayDeviceSessionCtx(this, device, mqttQoSMap); - devices.put(deviceName, ctx); - log.debug("[{}] Added device [{}] to the gateway session", gatewaySessionId, deviceName); - processor.process(new BasicTransportToDeviceSessionActorMsg(device, new BasicAdaptorToSessionActorMsg(ctx, new AttributesSubscribeMsg()))); - processor.process(new BasicTransportToDeviceSessionActorMsg(device, new BasicAdaptorToSessionActorMsg(ctx, new RpcSubscribeMsg()))); - } - } - - public void onDeviceDisconnect(MqttPublishMessage msg) throws AdaptorException { - String deviceName = checkDeviceName(getDeviceName(getJson(msg))); - GatewayDeviceSessionCtx deviceSessionCtx = devices.remove(deviceName); - if (deviceSessionCtx != null) { - processor.process(SessionCloseMsg.onDisconnect(deviceSessionCtx.getSessionId())); - deviceSessionCtx.setClosed(true); - log.debug("[{}] Removed device [{}] from the gateway session", gatewaySessionId, deviceName); - } else { - log.debug("[{}] Device [{}] was already removed from the gateway session", gatewaySessionId, deviceName); - } - ack(msg); - } - - public void onGatewayDisconnect() { - devices.forEach((k, v) -> { - processor.process(SessionCloseMsg.onDisconnect(v.getSessionId())); - }); - } - - public void onDeviceTelemetry(MqttPublishMessage mqttMsg) throws AdaptorException { - JsonElement json = validateJsonPayload(gatewaySessionId, mqttMsg.payload()); - int requestId = mqttMsg.variableHeader().messageId(); - if (json.isJsonObject()) { - JsonObject jsonObj = json.getAsJsonObject(); - for (Map.Entry deviceEntry : jsonObj.entrySet()) { - String deviceName = checkDeviceConnected(deviceEntry.getKey()); - if (!deviceEntry.getValue().isJsonArray()) { - throw new JsonSyntaxException(CAN_T_PARSE_VALUE + json); - } - BasicTelemetryUploadRequest request = new BasicTelemetryUploadRequest(requestId); - JsonArray deviceData = deviceEntry.getValue().getAsJsonArray(); - for (JsonElement element : deviceData) { - JsonConverter.parseWithTs(request, element.getAsJsonObject()); - } - GatewayDeviceSessionCtx deviceSessionCtx = devices.get(deviceName); - processor.process(new BasicTransportToDeviceSessionActorMsg(deviceSessionCtx.getDevice(), - new BasicAdaptorToSessionActorMsg(deviceSessionCtx, request))); - } - } else { - throw new JsonSyntaxException(CAN_T_PARSE_VALUE + json); - } - } - - public void onDeviceRpcResponse(MqttPublishMessage mqttMsg) throws AdaptorException { - JsonElement json = validateJsonPayload(gatewaySessionId, mqttMsg.payload()); - if (json.isJsonObject()) { - JsonObject jsonObj = json.getAsJsonObject(); - String deviceName = checkDeviceConnected(jsonObj.get(DEVICE_PROPERTY).getAsString()); - Integer requestId = jsonObj.get("id").getAsInt(); - String data = jsonObj.get("data").toString(); - GatewayDeviceSessionCtx deviceSessionCtx = devices.get(deviceName); - processor.process(new BasicTransportToDeviceSessionActorMsg(deviceSessionCtx.getDevice(), - new BasicAdaptorToSessionActorMsg(deviceSessionCtx, new ToDeviceRpcResponseMsg(requestId, data)))); - ack(mqttMsg); - } else { - throw new JsonSyntaxException(CAN_T_PARSE_VALUE + json); - } - } - - public void onDeviceAttributes(MqttPublishMessage mqttMsg) throws AdaptorException { - JsonElement json = validateJsonPayload(gatewaySessionId, mqttMsg.payload()); - int requestId = mqttMsg.variableHeader().messageId(); - if (json.isJsonObject()) { - JsonObject jsonObj = json.getAsJsonObject(); - for (Map.Entry deviceEntry : jsonObj.entrySet()) { - String deviceName = checkDeviceConnected(deviceEntry.getKey()); - if (!deviceEntry.getValue().isJsonObject()) { - throw new JsonSyntaxException(CAN_T_PARSE_VALUE + json); - } - long ts = System.currentTimeMillis(); - BasicAttributesUpdateRequest request = new BasicAttributesUpdateRequest(requestId); - JsonObject deviceData = deviceEntry.getValue().getAsJsonObject(); - request.add(JsonConverter.parseValues(deviceData).stream().map(kv -> new BaseAttributeKvEntry(kv, ts)).collect(Collectors.toList())); - GatewayDeviceSessionCtx deviceSessionCtx = devices.get(deviceName); - processor.process(new BasicTransportToDeviceSessionActorMsg(deviceSessionCtx.getDevice(), - new BasicAdaptorToSessionActorMsg(deviceSessionCtx, request))); - } - } else { - throw new JsonSyntaxException(CAN_T_PARSE_VALUE + json); - } - } - - public void onDeviceAttributesRequest(MqttPublishMessage msg) throws AdaptorException { - JsonElement json = validateJsonPayload(gatewaySessionId, 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 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()); - } - } - - BasicGetAttributesRequest request; - if (clientScope) { - request = new BasicGetAttributesRequest(requestId, keys, null); - } else { - request = new BasicGetAttributesRequest(requestId, null, keys); - } - GatewayDeviceSessionCtx deviceSessionCtx = devices.get(deviceName); - processor.process(new BasicTransportToDeviceSessionActorMsg(deviceSessionCtx.getDevice(), - new BasicAdaptorToSessionActorMsg(deviceSessionCtx, request))); - ack(msg); - } else { - throw new JsonSyntaxException(CAN_T_PARSE_VALUE + json); - } - } - - private String checkDeviceConnected(String deviceName) { - if (!devices.containsKey(deviceName)) { - log.debug("[{}] Missing device [{}] for the gateway session", gatewaySessionId, deviceName); - onDeviceConnect(deviceName, DEFAULT_DEVICE_TYPE); - } - return deviceName; - } - - 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(gatewaySessionId, mqttMsg.payload()); - } - - protected SessionMsgProcessor getProcessor() { - return processor; - } - - DeviceAuthService getAuthService() { - return authService; - } - - public void setChannel(ChannelHandlerContext channel) { - this.channel = channel; - } - - private void ack(MqttPublishMessage msg) { - if (msg.variableHeader().messageId() > 0) { - writeAndFlush(MqttTransportHandler.createMqttPubAckMsg(msg.variableHeader().messageId())); - } - } - - void writeAndFlush(MqttMessage mqttMessage) { - channel.writeAndFlush(mqttMessage); - } - -} diff --git a/transport/pom.xml b/transport/pom.xml index a01c7ac7ba..2401d3f845 100644 --- a/transport/pom.xml +++ b/transport/pom.xml @@ -37,7 +37,7 @@ http coap - mqtt + mqtt-common mqtt-transport From b190a4bd409eb4943b8f774db995c0a1a3b10753 Mon Sep 17 00:00:00 2001 From: Andrew Shvayka Date: Fri, 5 Oct 2018 19:45:10 +0300 Subject: [PATCH 04/26] Transport --- .../transport/RemoteTransportApiService.java | 14 +++ .../transport/TransportApiService.java | 7 ++ .../server/kafka/AbstractTbKafkaTemplate.java | 50 ++++++++++ .../server/kafka/TbKafkaRequestTemplate.java | 29 ++---- .../server/kafka/TbKafkaResponseTemplate.java | 98 ++++--------------- 5 files changed, 97 insertions(+), 101 deletions(-) create mode 100644 application/src/main/java/org/thingsboard/server/service/transport/RemoteTransportApiService.java create mode 100644 application/src/main/java/org/thingsboard/server/service/transport/TransportApiService.java create mode 100644 common/queue/src/main/java/org/thingsboard/server/kafka/AbstractTbKafkaTemplate.java 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 0000000000..7831b340dc --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/transport/RemoteTransportApiService.java @@ -0,0 +1,14 @@ +package org.thingsboard.server.service.transport; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.stereotype.Service; + +/** + * Created by ashvayka on 05.10.18. + */ +@Slf4j +@Service +@ConditionalOnProperty(prefix = "quota.rule.tenant", value = "enabled", havingValue = "true", matchIfMissing = false) +public class RemoteTransportApiService implements TransportApiService { +} diff --git a/application/src/main/java/org/thingsboard/server/service/transport/TransportApiService.java b/application/src/main/java/org/thingsboard/server/service/transport/TransportApiService.java new file mode 100644 index 0000000000..28a769a832 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/transport/TransportApiService.java @@ -0,0 +1,7 @@ +package org.thingsboard.server.service.transport; + +/** + * Created by ashvayka on 05.10.18. + */ +public interface TransportApiService { +} diff --git a/common/queue/src/main/java/org/thingsboard/server/kafka/AbstractTbKafkaTemplate.java b/common/queue/src/main/java/org/thingsboard/server/kafka/AbstractTbKafkaTemplate.java new file mode 100644 index 0000000000..8d851bca83 --- /dev/null +++ b/common/queue/src/main/java/org/thingsboard/server/kafka/AbstractTbKafkaTemplate.java @@ -0,0 +1,50 @@ +/** + * 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.extern.slf4j.Slf4j; + +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.UUID; + +/** + * Created by ashvayka on 25.09.18. + */ +@Slf4j +public abstract class AbstractTbKafkaTemplate { + protected byte[] uuidToBytes(UUID uuid) { + ByteBuffer buf = ByteBuffer.allocate(16); + buf.putLong(uuid.getMostSignificantBits()); + buf.putLong(uuid.getLeastSignificantBits()); + return buf.array(); + } + + protected static UUID bytesToUuid(byte[] bytes) { + ByteBuffer bb = ByteBuffer.wrap(bytes); + long firstLong = bb.getLong(); + long secondLong = bb.getLong(); + return new UUID(firstLong, secondLong); + } + + protected byte[] stringToBytes(String string) { + return string.getBytes(StandardCharsets.UTF_8); + } + + protected String bytesToString(byte[] data) { + return new String(data, StandardCharsets.UTF_8); + } +} 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 30b20e709d..ba08bf8946 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 { +public class TbKafkaRequestTemplate extends AbstractTbKafkaTemplate { private final TBKafkaProducerTemplate requestTemplate; private final TBKafkaConsumerTemplate responseTemplate; @@ -163,24 +164,6 @@ public class TbKafkaRequestTemplate { 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 { private final long expTime; private final SettableFuture 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 index 536c4b93b2..1f533873cd 100644 --- a/common/queue/src/main/java/org/thingsboard/server/kafka/TbKafkaResponseTemplate.java +++ b/common/queue/src/main/java/org/thingsboard/server/kafka/TbKafkaResponseTemplate.java @@ -15,52 +15,45 @@ */ package org.thingsboard.server.kafka; -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.SettableFuture; import lombok.Builder; 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.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.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.TimeoutException; +import java.util.concurrent.atomic.AtomicInteger; /** * Created by ashvayka on 25.09.18. */ @Slf4j -public class TbKafkaResponseTemplate { +public class TbKafkaResponseTemplate extends AbstractTbKafkaTemplate { private final TBKafkaConsumerTemplate requestTemplate; private final TBKafkaProducerTemplate responseTemplate; private final TbKafkaHandler handler; private final ConcurrentMap pendingRequests; private final ExecutorService executor; - private final long maxPendingRequests; + private final int maxPendingRequests; private final long pollInterval; private volatile boolean stopped = false; + //TODO: + private final AtomicInteger pendingRequestCount = new AtomicInteger(); @Builder public TbKafkaResponseTemplate(TBKafkaConsumerTemplate requestTemplate, TBKafkaProducerTemplate responseTemplate, TbKafkaHandler handler, long pollInterval, - long maxPendingRequests, + int maxPendingRequests, ExecutorService executor) { this.requestTemplate = requestTemplate; this.responseTemplate = responseTemplate; @@ -75,8 +68,11 @@ public class TbKafkaResponseTemplate { this.responseTemplate.init(); requestTemplate.subscribe(); executor.submit(() -> { - long nextCleanupMs = 0L; while (!stopped) { + if(pendingRequestCount.get() > maxPendingRequests){ + + } + //TODO: we need to protect from reading too much requests. ConsumerRecords requests = requestTemplate.poll(Duration.ofMillis(pollInterval)); requests.forEach(request -> { Header requestIdHeader = request.headers().lastHeader(TbKafkaSettings.REQUEST_ID_HEADER); @@ -94,26 +90,15 @@ public class TbKafkaResponseTemplate { log.error("[{}] Missing response topic in header", request); return; } - String responseTopic = bytesToUuid(responseTopicHeader.value()); - if (requestId == null) { - log.error("[{}] Missing requestId in header and body", request); - return; - } - - Request decodedRequest = null; - String responseTopic = null; - + String responseTopic = bytesToString(responseTopicHeader.value()); try { - if (decodedRequest == null) { - decodedRequest = requestTemplate.decode(request); - } - executor.submit(() -> { - handler.handle(decodedRequest, ); - }); - } catch (IOException e) { - expectedRequest.future.setException(e); + Request decodedRequest = requestTemplate.decode(request); + executor.submit(() -> handler.handle(decodedRequest, + response -> reply(requestId, responseTopic, response), + e -> log.error("[{}] Failed to process the request: {}", requestId, request, e))); + } catch (Throwable e) { + log.error("[{}] Failed to process the request: {}", requestId, request, e); } - }); } }); @@ -123,51 +108,8 @@ public class TbKafkaResponseTemplate { stopped = true; } - public ListenableFuture post(String key, Request request) { - if (tickSize > maxPendingRequests) { - return Futures.immediateFailedFuture(new RuntimeException("Pending request map is full!")); - } - UUID requestId = UUID.randomUUID(); - List

headers = new ArrayList<>(2); - headers.add(new RecordHeader(TbKafkaSettings.REQUEST_ID_HEADER, uuidToBytes(requestId))); - headers.add(new RecordHeader(TbKafkaSettings.RESPONSE_TOPIC_HEADER, stringToBytes(responseTemplate.getTopic()))); - SettableFuture future = SettableFuture.create(); - pendingRequests.putIfAbsent(requestId, new ResponseMetaData<>(tickTs + maxRequestTimeout, future)); - request = requestTemplate.enrich(request, responseTemplate.getTopic(), requestId); - requestTemplate.send(key, request, headers); - 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 String bytesToString(byte[] data) { - return new String(data, StandardCharsets.UTF_8); - } - - private static class ResponseMetaData { - private final long expTime; - private final SettableFuture future; - - ResponseMetaData(long ts, SettableFuture future) { - this.expTime = ts; - this.future = future; - } + private void reply(UUID requestId, String topic, Response response) { + responseTemplate.send(topic, response, Collections.singletonList(new RecordHeader(TbKafkaSettings.REQUEST_ID_HEADER, uuidToBytes(requestId)))); } } From 1ac0ed40baa62623aef1ef2dff2c6cb83edd3cf3 Mon Sep 17 00:00:00 2001 From: Andrew Shvayka Date: Mon, 8 Oct 2018 14:27:02 +0300 Subject: [PATCH 05/26] Handling Connect/Connack with new architecture --- .../server/ThingsboardServerApplication.java | 8 +- .../service/queue/DefaultMsgQueueService.java | 4 +- .../service/script/RemoteJsInvokeService.java | 4 +- .../transport/RemoteTransportApiService.java | 153 +++++++++++++++++- .../transport/TransportApiRequestDecoder.java | 31 ++++ .../TransportApiResponseEncoder.java | 30 ++++ .../transport/TransportApiService.java | 20 ++- .../src/main/resources/thingsboard.yml | 17 +- .../server/kafka/AbstractTbKafkaTemplate.java | 8 +- .../server/kafka/AsyncCallbackTemplate.java | 66 ++++++++ .../server/kafka/TBKafkaProducerTemplate.java | 12 +- .../server/kafka/TbKafkaHandler.java | 19 ++- .../server/kafka/TbKafkaResponseTemplate.java | 66 ++++++-- .../transport/src/main/proto/transport.proto | 12 +- .../transport/coap/CoapTransportResource.java | 10 +- .../coap/session/CoapSessionCtx.java | 4 +- .../transport/http/DeviceApiController.java | 134 +++++++-------- .../http/session/HttpSessionCtx.java | 2 +- .../transport/mqtt/MqttTransportContext.java | 15 ++ .../transport/mqtt/MqttTransportHandler.java | 10 +- .../mqtt/service/AsyncCallbackTemplate.java | 45 ------ .../mqtt/service/MqttTransportService.java | 30 +++- .../service/TransportApiRequestEncoder.java | 15 ++ .../service/TransportApiResponseDecoder.java | 15 ++ .../src/main/resources/tb-mqtt-transport.yml | 9 +- 25 files changed, 557 insertions(+), 182 deletions(-) create mode 100644 application/src/main/java/org/thingsboard/server/service/transport/TransportApiRequestDecoder.java create mode 100644 application/src/main/java/org/thingsboard/server/service/transport/TransportApiResponseEncoder.java create mode 100644 common/queue/src/main/java/org/thingsboard/server/kafka/AsyncCallbackTemplate.java delete mode 100644 transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/AsyncCallbackTemplate.java diff --git a/application/src/main/java/org/thingsboard/server/ThingsboardServerApplication.java b/application/src/main/java/org/thingsboard/server/ThingsboardServerApplication.java index c072311fe2..751bde6303 100644 --- a/application/src/main/java/org/thingsboard/server/ThingsboardServerApplication.java +++ b/application/src/main/java/org/thingsboard/server/ThingsboardServerApplication.java @@ -1,12 +1,12 @@ /** * 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 - *

+ * + * 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. 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 927584789d..416a4d6b0f 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 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/script/RemoteJsInvokeService.java b/application/src/main/java/org/thingsboard/server/service/script/RemoteJsInvokeService.java index 37a62117be..b334ff15ae 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 @@ -103,9 +103,7 @@ public class RemoteJsInvokeService extends AbstractJsInvokeService { 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 builder = TbKafkaRequestTemplate.builder(); 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 index 7831b340dc..0c7b5f4327 100644 --- a/application/src/main/java/org/thingsboard/server/service/transport/RemoteTransportApiService.java +++ b/application/src/main/java/org/thingsboard/server/service/transport/RemoteTransportApiService.java @@ -1,14 +1,165 @@ +/** + * 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.id.DeviceId; +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.gen.transport.TransportProtos.DeviceInfoProto; +import org.thingsboard.server.gen.transport.TransportProtos.TransportApiRequestMsg; +import org.thingsboard.server.gen.transport.TransportProtos.TransportApiResponseMsg; +import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceTokenRequestMsg; +import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceTokenResponseMsg; +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 javax.annotation.PostConstruct; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; /** * Created by ashvayka on 05.10.18. */ @Slf4j @Service -@ConditionalOnProperty(prefix = "quota.rule.tenant", value = "enabled", havingValue = "true", matchIfMissing = false) +@ConditionalOnProperty(prefix = "transport.remote", value = "enabled", havingValue = "true") public class RemoteTransportApiService implements TransportApiService { + + private static final ObjectMapper mapper = new ObjectMapper(); + + @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 DiscoveryService discoveryService; + + @Autowired + private DeviceService deviceService; + + @Autowired + private DeviceCredentialsService deviceCredentialsService; + + private ExecutorService transportCallbackExecutor; + + private TbKafkaResponseTemplate transportApiTemplate; + + @PostConstruct + public void init() { + this.transportCallbackExecutor = Executors.newCachedThreadPool(); + + TBKafkaProducerTemplate.TBKafkaProducerTemplateBuilder responseBuilder = TBKafkaProducerTemplate.builder(); + responseBuilder.settings(kafkaSettings); + responseBuilder.defaultTopic(transportApiResponsesTopic); + responseBuilder.encoder(new TransportApiResponseEncoder()); + + TBKafkaConsumerTemplate.TBKafkaConsumerTemplateBuilder requestBuilder = TBKafkaConsumerTemplate.builder(); + requestBuilder.settings(kafkaSettings); + requestBuilder.topic(transportApiRequestsTopic); + requestBuilder.clientId(discoveryService.getNodeId()); + requestBuilder.groupId("tb-node"); + requestBuilder.autoCommit(true); + requestBuilder.autoCommitIntervalMs(autoCommitInterval); + requestBuilder.decoder(new TransportApiRequestDecoder()); + + TbKafkaResponseTemplate.TbKafkaResponseTemplateBuilder + 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(this); + transportApiTemplate = builder.build(); + transportApiTemplate.init(); + } + + @Override + public ListenableFuture handle(TransportApiRequestMsg transportApiRequestMsg) throws Exception { + if (transportApiRequestMsg.hasValidateTokenRequestMsg()) { + ValidateDeviceTokenRequestMsg msg = transportApiRequestMsg.getValidateTokenRequestMsg(); + //TODO: Make async and enable caching + DeviceCredentials credentials = deviceCredentialsService.findDeviceCredentialsByCredentialsId(msg.getToken()); + if (credentials != null && credentials.getCredentialsType() == DeviceCredentialsType.ACCESS_TOKEN) { + return getDeviceInfo(credentials.getDeviceId()); + } else { + return getEmptyTransportApiResponseFuture(); + } + } + return getEmptyTransportApiResponseFuture(); + } + + private ListenableFuture 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 { + DeviceInfoProto deviceInfoProto = 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(); + return TransportApiResponseMsg.newBuilder() + .setValidateTokenResponseMsg(ValidateDeviceTokenResponseMsg.newBuilder().setDeviceInfo(deviceInfoProto).build()).build(); + } catch (JsonProcessingException e) { + log.warn("[{}] Failed to lookup device by id", deviceId, e); + return getEmptyTransportApiResponse(); + } + }); + } + + private ListenableFuture getEmptyTransportApiResponseFuture() { + return Futures.immediateFuture(getEmptyTransportApiResponse()); + } + + private TransportApiResponseMsg getEmptyTransportApiResponse() { + return TransportApiResponseMsg.newBuilder() + .setValidateTokenResponseMsg(ValidateDeviceTokenResponseMsg.getDefaultInstance()).build(); + } } diff --git a/application/src/main/java/org/thingsboard/server/service/transport/TransportApiRequestDecoder.java b/application/src/main/java/org/thingsboard/server/service/transport/TransportApiRequestDecoder.java new file mode 100644 index 0000000000..c54f1a0303 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/transport/TransportApiRequestDecoder.java @@ -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. + */ +package org.thingsboard.server.service.transport; + +import org.thingsboard.server.gen.transport.TransportProtos.TransportApiRequestMsg; +import org.thingsboard.server.kafka.TbKafkaDecoder; + +import java.io.IOException; + +/** + * Created by ashvayka on 05.10.18. + */ +public class TransportApiRequestDecoder implements TbKafkaDecoder { + @Override + public TransportApiRequestMsg decode(byte[] data) throws IOException { + return TransportApiRequestMsg.parseFrom(data); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/transport/TransportApiResponseEncoder.java b/application/src/main/java/org/thingsboard/server/service/transport/TransportApiResponseEncoder.java new file mode 100644 index 0000000000..175a92df96 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/transport/TransportApiResponseEncoder.java @@ -0,0 +1,30 @@ +/** + * 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 org.thingsboard.server.kafka.TbKafkaEncoder; + +import org.thingsboard.server.gen.transport.TransportProtos.TransportApiResponseMsg; + +/** + * Created by ashvayka on 05.10.18. + */ +public class TransportApiResponseEncoder implements TbKafkaEncoder { + @Override + public byte[] encode(TransportApiResponseMsg value) { + return value.toByteArray(); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/transport/TransportApiService.java b/application/src/main/java/org/thingsboard/server/service/transport/TransportApiService.java index 28a769a832..cc6f5b9f15 100644 --- a/application/src/main/java/org/thingsboard/server/service/transport/TransportApiService.java +++ b/application/src/main/java/org/thingsboard/server/service/transport/TransportApiService.java @@ -1,7 +1,25 @@ +/** + * 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 org.thingsboard.server.gen.transport.TransportProtos; +import org.thingsboard.server.kafka.TbKafkaHandler; + /** * Created by ashvayka on 05.10.18. */ -public interface TransportApiService { +public interface TransportApiService extends TbKafkaHandler { } diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index f35df67265..c58f3a1a38 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -84,7 +84,7 @@ http: # MQTT server parameters mqtt: # Enable/disable mqtt transport protocol. - enabled: "${MQTT_ENABLED:true}" + enabled: "${MQTT_ENABLED:false}" bind_address: "${MQTT_BIND_ADDRESS:0.0.0.0}" bind_port: "${MQTT_BIND_PORT:1883}" adaptor: "${MQTT_ADAPTOR_NAME:JsonMqttAdaptor}" @@ -149,7 +149,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 @@ -450,3 +450,16 @@ 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: + remote: + enabled: "${REMOTE_TRANSPORT_ENABLED:true}" + 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}" \ No newline at end of file diff --git a/common/queue/src/main/java/org/thingsboard/server/kafka/AbstractTbKafkaTemplate.java b/common/queue/src/main/java/org/thingsboard/server/kafka/AbstractTbKafkaTemplate.java index 8d851bca83..386bfc574e 100644 --- a/common/queue/src/main/java/org/thingsboard/server/kafka/AbstractTbKafkaTemplate.java +++ b/common/queue/src/main/java/org/thingsboard/server/kafka/AbstractTbKafkaTemplate.java @@ -1,12 +1,12 @@ /** * 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 - *

+ * + * 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. 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 0000000000..b8ad758ef9 --- /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 void withCallbackAndTimeout(ListenableFuture future, + Consumer onSuccess, + Consumer onFailure, + long timeoutInMs, + ScheduledExecutorService timeoutExecutor, + Executor callbackExecutor) { + future = Futures.withTimeout(future, timeoutInMs, TimeUnit.MILLISECONDS, timeoutExecutor); + withCallback(future, onSuccess, onFailure, callbackExecutor); + } + + public static void withCallback(ListenableFuture future, Consumer onSuccess, + Consumer onFailure, Executor executor) { + FutureCallback callback = new FutureCallback() { + @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/TBKafkaProducerTemplate.java b/common/queue/src/main/java/org/thingsboard/server/kafka/TBKafkaProducerTemplate.java index 2611e9491a..1e109d2877 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 @@ -1,12 +1,12 @@ /** * 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 - *

+ * + * 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. @@ -101,6 +101,10 @@ public class TBKafkaProducerTemplate { return send(this.defaultTopic, key, value, timestamp, headers); } + public Future send(String topic, String key, T value, Iterable

headers) { + return send(topic, key, value, null, headers); + } + public Future send(String topic, String key, T value, Long timestamp, Iterable
headers) { byte[] data = encoder.encode(value); ProducerRecord record; diff --git a/common/queue/src/main/java/org/thingsboard/server/kafka/TbKafkaHandler.java b/common/queue/src/main/java/org/thingsboard/server/kafka/TbKafkaHandler.java index 66d53c3bde..a64d28e173 100644 --- a/common/queue/src/main/java/org/thingsboard/server/kafka/TbKafkaHandler.java +++ b/common/queue/src/main/java/org/thingsboard/server/kafka/TbKafkaHandler.java @@ -1,12 +1,27 @@ +/** + * 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 java.util.function.Consumer; +import com.google.common.util.concurrent.ListenableFuture; /** * Created by ashvayka on 05.10.18. */ public interface TbKafkaHandler { - void handle(Request request, Consumer onSuccess, Consumer onFailure); + ListenableFuture handle(Request request) throws Exception; } 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 index 1f533873cd..0dbf45db14 100644 --- a/common/queue/src/main/java/org/thingsboard/server/kafka/TbKafkaResponseTemplate.java +++ b/common/queue/src/main/java/org/thingsboard/server/kafka/TbKafkaResponseTemplate.java @@ -1,12 +1,12 @@ /** * 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 - *

+ * + * 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. @@ -21,13 +21,15 @@ import org.apache.kafka.clients.consumer.ConsumerRecords; import org.apache.kafka.common.header.Header; import org.apache.kafka.common.header.internals.RecordHeader; -import java.io.IOException; 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; /** @@ -40,12 +42,14 @@ public class TbKafkaResponseTemplate extends AbstractTbKafkaT private final TBKafkaProducerTemplate responseTemplate; private final TbKafkaHandler handler; private final ConcurrentMap pendingRequests; - private final ExecutorService executor; + 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; - //TODO: private final AtomicInteger pendingRequestCount = new AtomicInteger(); @Builder @@ -53,6 +57,7 @@ public class TbKafkaResponseTemplate extends AbstractTbKafkaT TBKafkaProducerTemplate responseTemplate, TbKafkaHandler handler, long pollInterval, + long requestTimeout, int maxPendingRequests, ExecutorService executor) { this.requestTemplate = requestTemplate; @@ -61,18 +66,24 @@ public class TbKafkaResponseTemplate extends AbstractTbKafkaT this.pendingRequests = new ConcurrentHashMap<>(); this.maxPendingRequests = maxPendingRequests; this.pollInterval = pollInterval; - this.executor = executor; + this.requestTimeout = requestTimeout; + this.callbackExecutor = executor; + this.timeoutExecutor = Executors.newSingleThreadScheduledExecutor(); + this.loopExecutor = Executors.newSingleThreadExecutor(); } public void init() { this.responseTemplate.init(); requestTemplate.subscribe(); - executor.submit(() -> { + loopExecutor.submit(() -> { while (!stopped) { - if(pendingRequestCount.get() > maxPendingRequests){ - + 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); + } } - //TODO: we need to protect from reading too much requests. ConsumerRecords requests = requestTemplate.poll(Duration.ofMillis(pollInterval)); requests.forEach(request -> { Header requestIdHeader = request.headers().lastHeader(TbKafkaSettings.REQUEST_ID_HEADER); @@ -92,12 +103,27 @@ public class TbKafkaResponseTemplate extends AbstractTbKafkaT } String responseTopic = bytesToString(responseTopicHeader.value()); try { + pendingRequestCount.getAndIncrement(); Request decodedRequest = requestTemplate.decode(request); - executor.submit(() -> handler.handle(decodedRequest, - response -> reply(requestId, responseTopic, response), - e -> log.error("[{}] Failed to process the request: {}", requestId, request, e))); + 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) { - log.error("[{}] Failed to process the request: {}", requestId, request, e); + pendingRequestCount.decrementAndGet(); + log.warn("[{}] Failed to process the request: {}", requestId, request, e); } }); } @@ -106,10 +132,16 @@ public class TbKafkaResponseTemplate extends AbstractTbKafkaT 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, response, Collections.singletonList(new RecordHeader(TbKafkaSettings.REQUEST_ID_HEADER, uuidToBytes(requestId)))); + responseTemplate.send(topic, requestId.toString(), response, Collections.singletonList(new RecordHeader(TbKafkaSettings.REQUEST_ID_HEADER, uuidToBytes(requestId)))); } } diff --git a/common/transport/src/main/proto/transport.proto b/common/transport/src/main/proto/transport.proto index f0cc3c75ed..029bb14ebf 100644 --- a/common/transport/src/main/proto/transport.proto +++ b/common/transport/src/main/proto/transport.proto @@ -47,11 +47,13 @@ message TsKvListProto { } message DeviceInfoProto { - int64 deviceIdMSB = 1; - int64 deviceIdLSB = 2; - string deviceName = 3; - string deviceType = 4; - string additionalInfo = 5; + int64 tenantIdMSB = 1; + int64 tenantIdLSB = 2; + int64 deviceIdMSB = 3; + int64 deviceIdLSB = 4; + string deviceName = 5; + string deviceType = 6; + string additionalInfo = 7; } /** diff --git a/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java b/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java index 8a596d7119..0853124acb 100644 --- a/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java +++ b/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java @@ -157,10 +157,10 @@ public class CoapTransportResource extends CoapResource { CoapSessionCtx ctx = new CoapSessionCtx(exchange, adaptor, processor, authService, timeout); - if (!ctx.login(credentials.get())) { - exchange.respond(ResponseCode.UNAUTHORIZED); - return Optional.empty(); - } +// if (!ctx.login(credentials.get())) { +// exchange.respond(ResponseCode.UNAUTHORIZED); +// return Optional.empty(); +// } AdaptorToSessionActorMsg msg; try { @@ -190,7 +190,7 @@ public class CoapTransportResource extends CoapResource { throw new IllegalArgumentException("Unsupported msg type: " + type); } log.trace("Processing msg: {}", msg); - processor.process(new BasicTransportToDeviceSessionActorMsg(ctx.getDevice(), msg)); +// processor.process(new BasicTransportToDeviceSessionActorMsg(ctx.getDevice(), msg)); } catch (AdaptorException e) { log.debug("Failed to decode payload {}", e); exchange.respond(ResponseCode.BAD_REQUEST, e.getMessage()); diff --git a/transport/coap/src/main/java/org/thingsboard/server/transport/coap/session/CoapSessionCtx.java b/transport/coap/src/main/java/org/thingsboard/server/transport/coap/session/CoapSessionCtx.java index 7a703b2be3..c43a4ded7a 100644 --- a/transport/coap/src/main/java/org/thingsboard/server/transport/coap/session/CoapSessionCtx.java +++ b/transport/coap/src/main/java/org/thingsboard/server/transport/coap/session/CoapSessionCtx.java @@ -49,7 +49,7 @@ public class CoapSessionCtx extends DeviceAwareSessionContext { private final AtomicInteger seqNumber = new AtomicInteger(2); public CoapSessionCtx(CoapExchange exchange, CoapTransportAdaptor adaptor, SessionMsgProcessor processor, DeviceAuthService authService, long timeout) { - super(processor, authService); + super(); Request request = exchange.advanced().getRequest(); this.token = request.getTokenString(); this.sessionId = new CoapSessionId(request.getSource().getHostAddress(), request.getSourcePort(), this.token); @@ -112,7 +112,7 @@ public class CoapSessionCtx extends DeviceAwareSessionContext { public void close() { log.info("[{}] Closing processing context. Timeout: {}", sessionId, exchange.advanced().isTimedOut()); - processor.process(exchange.advanced().isTimedOut() ? SessionCloseMsg.onTimeout(sessionId) : SessionCloseMsg.onError(sessionId)); +// processor.process(exchange.advanced().isTimedOut() ? SessionCloseMsg.onTimeout(sessionId) : SessionCloseMsg.onError(sessionId)); } @Override diff --git a/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java b/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java index d26d076b21..add89b1e1a 100644 --- a/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java +++ b/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java @@ -73,20 +73,20 @@ public class DeviceApiController { if (quotaExceeded(httpRequest, responseWriter)) { return responseWriter; } - HttpSessionCtx ctx = getHttpSessionCtx(responseWriter); - if (ctx.login(new DeviceTokenCredentials(deviceToken))) { - GetAttributesRequest request; - if (StringUtils.isEmpty(clientKeys) && StringUtils.isEmpty(sharedKeys)) { - request = new BasicGetAttributesRequest(0); - } else { - Set clientKeySet = !StringUtils.isEmpty(clientKeys) ? new HashSet<>(Arrays.asList(clientKeys.split(","))) : null; - Set sharedKeySet = !StringUtils.isEmpty(sharedKeys) ? new HashSet<>(Arrays.asList(sharedKeys.split(","))) : null; - request = new BasicGetAttributesRequest(0, clientKeySet, sharedKeySet); - } - process(ctx, request); - } else { - responseWriter.setResult(new ResponseEntity<>(HttpStatus.UNAUTHORIZED)); - } +// HttpSessionCtx ctx = getHttpSessionCtx(responseWriter); +// if (ctx.login(new DeviceTokenCredentials(deviceToken))) { +// GetAttributesRequest request; +// if (StringUtils.isEmpty(clientKeys) && StringUtils.isEmpty(sharedKeys)) { +// request = new BasicGetAttributesRequest(0); +// } else { +// Set clientKeySet = !StringUtils.isEmpty(clientKeys) ? new HashSet<>(Arrays.asList(clientKeys.split(","))) : null; +// Set sharedKeySet = !StringUtils.isEmpty(sharedKeys) ? new HashSet<>(Arrays.asList(sharedKeys.split(","))) : null; +// request = new BasicGetAttributesRequest(0, clientKeySet, sharedKeySet); +// } +// process(ctx, request); +// } else { +// responseWriter.setResult(new ResponseEntity<>(HttpStatus.UNAUTHORIZED)); +// } return responseWriter; } @@ -98,16 +98,16 @@ public class DeviceApiController { if (quotaExceeded(request, responseWriter)) { return responseWriter; } - HttpSessionCtx ctx = getHttpSessionCtx(responseWriter); - if (ctx.login(new DeviceTokenCredentials(deviceToken))) { - try { - process(ctx, JsonConverter.convertToAttributes(new JsonParser().parse(json))); - } catch (IllegalStateException | JsonSyntaxException ex) { - responseWriter.setResult(new ResponseEntity<>(HttpStatus.BAD_REQUEST)); - } - } else { - responseWriter.setResult(new ResponseEntity<>(HttpStatus.UNAUTHORIZED)); - } +// HttpSessionCtx ctx = getHttpSessionCtx(responseWriter); +// if (ctx.login(new DeviceTokenCredentials(deviceToken))) { +// try { +// process(ctx, JsonConverter.convertToAttributes(new JsonParser().parse(json))); +// } catch (IllegalStateException | JsonSyntaxException ex) { +// responseWriter.setResult(new ResponseEntity<>(HttpStatus.BAD_REQUEST)); +// } +// } else { +// responseWriter.setResult(new ResponseEntity<>(HttpStatus.UNAUTHORIZED)); +// } return responseWriter; } @@ -119,15 +119,15 @@ public class DeviceApiController { return responseWriter; } HttpSessionCtx ctx = getHttpSessionCtx(responseWriter); - if (ctx.login(new DeviceTokenCredentials(deviceToken))) { - try { - process(ctx, JsonConverter.convertToTelemetry(new JsonParser().parse(json))); - } catch (IllegalStateException | JsonSyntaxException ex) { - responseWriter.setResult(new ResponseEntity<>(HttpStatus.BAD_REQUEST)); - } - } else { - responseWriter.setResult(new ResponseEntity<>(HttpStatus.UNAUTHORIZED)); - } +// if (ctx.login(new DeviceTokenCredentials(deviceToken))) { +// try { +// process(ctx, JsonConverter.convertToTelemetry(new JsonParser().parse(json))); +// } catch (IllegalStateException | JsonSyntaxException ex) { +// responseWriter.setResult(new ResponseEntity<>(HttpStatus.BAD_REQUEST)); +// } +// } else { +// responseWriter.setResult(new ResponseEntity<>(HttpStatus.UNAUTHORIZED)); +// } return responseWriter; } @@ -147,17 +147,17 @@ public class DeviceApiController { if (quotaExceeded(request, responseWriter)) { return responseWriter; } - HttpSessionCtx ctx = getHttpSessionCtx(responseWriter); - if (ctx.login(new DeviceTokenCredentials(deviceToken))) { - try { - JsonObject response = new JsonParser().parse(json).getAsJsonObject(); - process(ctx, new ToDeviceRpcResponseMsg(requestId, response.toString())); - } catch (IllegalStateException | JsonSyntaxException ex) { - responseWriter.setResult(new ResponseEntity<>(HttpStatus.BAD_REQUEST)); - } - } else { - responseWriter.setResult(new ResponseEntity<>(HttpStatus.UNAUTHORIZED)); - } +// HttpSessionCtx ctx = getHttpSessionCtx(responseWriter); +// if (ctx.login(new DeviceTokenCredentials(deviceToken))) { +// try { +// JsonObject response = new JsonParser().parse(json).getAsJsonObject(); +// process(ctx, new ToDeviceRpcResponseMsg(requestId, response.toString())); +// } catch (IllegalStateException | JsonSyntaxException ex) { +// responseWriter.setResult(new ResponseEntity<>(HttpStatus.BAD_REQUEST)); +// } +// } else { +// responseWriter.setResult(new ResponseEntity<>(HttpStatus.UNAUTHORIZED)); +// } return responseWriter; } @@ -169,18 +169,18 @@ public class DeviceApiController { return responseWriter; } HttpSessionCtx ctx = getHttpSessionCtx(responseWriter); - if (ctx.login(new DeviceTokenCredentials(deviceToken))) { - try { - JsonObject request = new JsonParser().parse(json).getAsJsonObject(); - process(ctx, new ToServerRpcRequestMsg(0, - request.get("method").getAsString(), - request.get("params").toString())); - } catch (IllegalStateException | JsonSyntaxException ex) { - responseWriter.setResult(new ResponseEntity<>(HttpStatus.BAD_REQUEST)); - } - } else { - responseWriter.setResult(new ResponseEntity<>(HttpStatus.UNAUTHORIZED)); - } +// if (ctx.login(new DeviceTokenCredentials(deviceToken))) { +// try { +// JsonObject request = new JsonParser().parse(json).getAsJsonObject(); +// process(ctx, new ToServerRpcRequestMsg(0, +// request.get("method").getAsString(), +// request.get("params").toString())); +// } catch (IllegalStateException | JsonSyntaxException ex) { +// responseWriter.setResult(new ResponseEntity<>(HttpStatus.BAD_REQUEST)); +// } +// } else { +// responseWriter.setResult(new ResponseEntity<>(HttpStatus.UNAUTHORIZED)); +// } return responseWriter; } @@ -197,16 +197,16 @@ public class DeviceApiController { if (quotaExceeded(httpRequest, responseWriter)) { return responseWriter; } - HttpSessionCtx ctx = getHttpSessionCtx(responseWriter, timeout); - if (ctx.login(new DeviceTokenCredentials(deviceToken))) { - try { - process(ctx, msg); - } catch (IllegalStateException | JsonSyntaxException ex) { - responseWriter.setResult(new ResponseEntity<>(HttpStatus.BAD_REQUEST)); - } - } else { - responseWriter.setResult(new ResponseEntity<>(HttpStatus.UNAUTHORIZED)); - } +// HttpSessionCtx ctx = getHttpSessionCtx(responseWriter, timeout); +// if (ctx.login(new DeviceTokenCredentials(deviceToken))) { +// try { +// process(ctx, msg); +// } catch (IllegalStateException | JsonSyntaxException ex) { +// responseWriter.setResult(new ResponseEntity<>(HttpStatus.BAD_REQUEST)); +// } +// } else { +// responseWriter.setResult(new ResponseEntity<>(HttpStatus.UNAUTHORIZED)); +// } return responseWriter; } @@ -220,7 +220,7 @@ public class DeviceApiController { private void process(HttpSessionCtx ctx, FromDeviceMsg request) { AdaptorToSessionActorMsg msg = new BasicAdaptorToSessionActorMsg(ctx, request); - processor.process(new BasicTransportToDeviceSessionActorMsg(ctx.getDevice(), msg)); +// processor.process(new BasicTransportToDeviceSessionActorMsg(ctx.getDevice(), msg)); } private boolean quotaExceeded(HttpServletRequest request, DeferredResult responseWriter) { diff --git a/transport/http/src/main/java/org/thingsboard/server/transport/http/session/HttpSessionCtx.java b/transport/http/src/main/java/org/thingsboard/server/transport/http/session/HttpSessionCtx.java index 4732785de2..e503409b23 100644 --- a/transport/http/src/main/java/org/thingsboard/server/transport/http/session/HttpSessionCtx.java +++ b/transport/http/src/main/java/org/thingsboard/server/transport/http/session/HttpSessionCtx.java @@ -43,7 +43,7 @@ public class HttpSessionCtx extends DeviceAwareSessionContext { private final DeferredResult responseWriter; public HttpSessionCtx(SessionMsgProcessor processor, DeviceAuthService authService, DeferredResult responseWriter, long timeout) { - super(processor, authService); + super(); this.sessionId = new HttpSessionId(); this.responseWriter = responseWriter; this.timeout = timeout; diff --git a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportContext.java b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportContext.java index cc40ee0975..b500a31e69 100644 --- a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportContext.java +++ b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportContext.java @@ -1,3 +1,18 @@ +/** + * 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; diff --git a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java index 8c64e7eeb2..a318b30e66 100644 --- a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java +++ b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java @@ -1,12 +1,12 @@ /** * 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 - *

+ * + * 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. @@ -388,7 +388,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement ctx.close(); } else { ctx.writeAndFlush(createMqttConnAckMsg(CONNECTION_ACCEPTED)); - deviceSessionCtx.setDeviceInfo(deviceSessionCtx.getDeviceInfo()); + deviceSessionCtx.setDeviceInfo(msg.getDeviceInfo()); transportService.process(getSessionEventMsg(SessionEvent.OPEN), null); checkGatewaySession(); } diff --git a/transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/AsyncCallbackTemplate.java b/transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/AsyncCallbackTemplate.java deleted file mode 100644 index 493c0c8b9f..0000000000 --- a/transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/AsyncCallbackTemplate.java +++ /dev/null @@ -1,45 +0,0 @@ -package org.thingsboard.server.mqtt.service; - -import com.google.common.util.concurrent.FutureCallback; -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; - -import javax.annotation.Nullable; -import java.util.concurrent.Executor; -import java.util.function.Consumer; - -/** - * Created by ashvayka on 05.10.18. - */ -public class AsyncCallbackTemplate { - - public static void withCallback(ListenableFuture future, Consumer onSuccess, - Consumer onFailure) { - withCallback(future, onSuccess, onFailure, null); - } - - public static void withCallback(ListenableFuture future, Consumer onSuccess, - Consumer onFailure, Executor executor) { - FutureCallback callback = new FutureCallback() { - @Override - public void onSuccess(@Nullable 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/transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/MqttTransportService.java b/transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/MqttTransportService.java index a1641f152f..f5fd690170 100644 --- a/transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/MqttTransportService.java +++ b/transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/MqttTransportService.java @@ -1,3 +1,18 @@ +/** + * 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.service; import org.springframework.beans.factory.annotation.Autowired; @@ -5,6 +20,7 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.thingsboard.server.common.transport.TransportService; import org.thingsboard.server.common.transport.TransportServiceCallback; +import org.thingsboard.server.kafka.AsyncCallbackTemplate; import org.thingsboard.server.kafka.TBKafkaConsumerTemplate; import org.thingsboard.server.kafka.TBKafkaProducerTemplate; import org.thingsboard.server.kafka.TbKafkaRequestTemplate; @@ -23,19 +39,19 @@ import java.util.concurrent.Executors; @Service public class MqttTransportService implements TransportService { - @Value("${kafka.rule-engine.topic}") + @Value("${kafka.rule_engine.topic}") private String ruleEngineTopic; - @Value("${kafka.transport-api.requests-topic}") + @Value("${kafka.transport_api.requests_topic}") private String transportApiRequestsTopic; - @Value("${kafka.transport-api.responses-topic}") + @Value("${kafka.transport_api.responses_topic}") private String transportApiResponsesTopic; - @Value("${kafka.transport-api.max_pending_requests}") + @Value("${kafka.transport_api.max_pending_requests}") private long maxPendingRequests; - @Value("${kafka.transport-api.max_requests_timeout}") + @Value("${kafka.transport_api.max_requests_timeout}") private long maxRequestsTimeout; - @Value("${kafka.transport-api.response_poll_interval}") + @Value("${kafka.transport_api.response_poll_interval}") private int responsePollDuration; - @Value("${kafka.transport-api.response_auto_commit_interval}") + @Value("${kafka.transport_api.response_auto_commit_interval}") private int autoCommitInterval; @Autowired diff --git a/transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/TransportApiRequestEncoder.java b/transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/TransportApiRequestEncoder.java index f931db6a2e..8ae4d1711f 100644 --- a/transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/TransportApiRequestEncoder.java +++ b/transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/TransportApiRequestEncoder.java @@ -1,3 +1,18 @@ +/** + * 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.service; import org.thingsboard.server.gen.transport.TransportProtos.TransportApiRequestMsg; diff --git a/transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/TransportApiResponseDecoder.java b/transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/TransportApiResponseDecoder.java index 22e16478ae..66e64b56e6 100644 --- a/transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/TransportApiResponseDecoder.java +++ b/transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/TransportApiResponseDecoder.java @@ -1,3 +1,18 @@ +/** + * 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.service; import org.thingsboard.server.gen.transport.TransportProtos.TransportApiResponseMsg; diff --git a/transport/mqtt-transport/src/main/resources/tb-mqtt-transport.yml b/transport/mqtt-transport/src/main/resources/tb-mqtt-transport.yml index a2a0d54adc..707fb4bcc1 100644 --- a/transport/mqtt-transport/src/main/resources/tb-mqtt-transport.yml +++ b/transport/mqtt-transport/src/main/resources/tb-mqtt-transport.yml @@ -73,13 +73,12 @@ 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}" + 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}" - # Maximum allowed JavaScript execution errors before JavaScript will be blacklisted - rule-engine: + rule_engine: topic: "${TB_RULE_ENGINE_TOPIC:tb.rule-engine}" From 6c61169452092e18611f968e0b32da68309ba7db Mon Sep 17 00:00:00 2001 From: Andrew Shvayka Date: Mon, 8 Oct 2018 17:33:58 +0300 Subject: [PATCH 06/26] Ability to handle X509 certificate login via MQTT --- .../transport/RemoteTransportApiService.java | 28 +++++--- .../common/transport/TransportService.java | 5 +- .../transport/src/main/proto/transport.proto | 9 ++- .../transport/mqtt/MqttTransportHandler.java | 66 +++++++++++-------- .../mqtt/service/MqttTransportService.java | 12 +++- 5 files changed, 78 insertions(+), 42 deletions(-) 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 index 0c7b5f4327..6e9b2efa0a 100644 --- a/application/src/main/java/org/thingsboard/server/service/transport/RemoteTransportApiService.java +++ b/application/src/main/java/org/thingsboard/server/service/transport/RemoteTransportApiService.java @@ -33,7 +33,8 @@ import org.thingsboard.server.gen.transport.TransportProtos.DeviceInfoProto; import org.thingsboard.server.gen.transport.TransportProtos.TransportApiRequestMsg; import org.thingsboard.server.gen.transport.TransportProtos.TransportApiResponseMsg; import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceTokenRequestMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceTokenResponseMsg; +import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceX509CertRequestMsg; +import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceCredentialsResponseMsg; import org.thingsboard.server.kafka.TBKafkaConsumerTemplate; import org.thingsboard.server.kafka.TBKafkaProducerTemplate; import org.thingsboard.server.kafka.TbKafkaResponseTemplate; @@ -118,17 +119,24 @@ public class RemoteTransportApiService implements TransportApiService { public ListenableFuture handle(TransportApiRequestMsg transportApiRequestMsg) throws Exception { if (transportApiRequestMsg.hasValidateTokenRequestMsg()) { ValidateDeviceTokenRequestMsg msg = transportApiRequestMsg.getValidateTokenRequestMsg(); - //TODO: Make async and enable caching - DeviceCredentials credentials = deviceCredentialsService.findDeviceCredentialsByCredentialsId(msg.getToken()); - if (credentials != null && credentials.getCredentialsType() == DeviceCredentialsType.ACCESS_TOKEN) { - return getDeviceInfo(credentials.getDeviceId()); - } else { - return getEmptyTransportApiResponseFuture(); - } + return validateCredentials(msg.getToken(), DeviceCredentialsType.ACCESS_TOKEN); + } else if (transportApiRequestMsg.hasValidateX509CertRequestMsg()) { + ValidateDeviceX509CertRequestMsg msg = transportApiRequestMsg.getValidateX509CertRequestMsg(); + return validateCredentials(msg.getHash(), DeviceCredentialsType.X509_CERTIFICATE); } return getEmptyTransportApiResponseFuture(); } + private ListenableFuture 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 getDeviceInfo(DeviceId deviceId) { return Futures.transform(deviceService.findDeviceByIdAsync(deviceId), device -> { if (device == null) { @@ -146,7 +154,7 @@ public class RemoteTransportApiService implements TransportApiService { .setAdditionalInfo(mapper.writeValueAsString(device.getAdditionalInfo())) .build(); return TransportApiResponseMsg.newBuilder() - .setValidateTokenResponseMsg(ValidateDeviceTokenResponseMsg.newBuilder().setDeviceInfo(deviceInfoProto).build()).build(); + .setValidateTokenResponseMsg(ValidateDeviceCredentialsResponseMsg.newBuilder().setDeviceInfo(deviceInfoProto).build()).build(); } catch (JsonProcessingException e) { log.warn("[{}] Failed to lookup device by id", deviceId, e); return getEmptyTransportApiResponse(); @@ -160,6 +168,6 @@ public class RemoteTransportApiService implements TransportApiService { private TransportApiResponseMsg getEmptyTransportApiResponse() { return TransportApiResponseMsg.newBuilder() - .setValidateTokenResponseMsg(ValidateDeviceTokenResponseMsg.getDefaultInstance()).build(); + .setValidateTokenResponseMsg(ValidateDeviceCredentialsResponseMsg.getDefaultInstance()).build(); } } diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/TransportService.java b/common/transport/src/main/java/org/thingsboard/server/common/transport/TransportService.java index e72a626ca1..84d34e12cb 100644 --- a/common/transport/src/main/java/org/thingsboard/server/common/transport/TransportService.java +++ b/common/transport/src/main/java/org/thingsboard/server/common/transport/TransportService.java @@ -23,7 +23,10 @@ import org.thingsboard.server.gen.transport.TransportProtos; public interface TransportService { void process(TransportProtos.ValidateDeviceTokenRequestMsg msg, - TransportServiceCallback callback); + TransportServiceCallback callback); + + void process(TransportProtos.ValidateDeviceX509CertRequestMsg msg, + TransportServiceCallback callback); void process(TransportProtos.SessionEventMsg msg, TransportServiceCallback callback); diff --git a/common/transport/src/main/proto/transport.proto b/common/transport/src/main/proto/transport.proto index 029bb14ebf..e78f87351e 100644 --- a/common/transport/src/main/proto/transport.proto +++ b/common/transport/src/main/proto/transport.proto @@ -93,7 +93,11 @@ message ValidateDeviceTokenRequestMsg { string token = 1; } -message ValidateDeviceTokenResponseMsg { +message ValidateDeviceX509CertRequestMsg { + string hash = 1; +} + +message ValidateDeviceCredentialsResponseMsg { DeviceInfoProto deviceInfo = 1; } @@ -106,8 +110,9 @@ message TransportToRuleEngineMsg { message TransportApiRequestMsg { ValidateDeviceTokenRequestMsg validateTokenRequestMsg = 1; + ValidateDeviceX509CertRequestMsg validateX509CertRequestMsg = 2; } message TransportApiResponseMsg { - ValidateDeviceTokenResponseMsg validateTokenResponseMsg = 1; + ValidateDeviceCredentialsResponseMsg validateTokenResponseMsg = 1; } \ No newline at end of file diff --git a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java index a318b30e66..23bc4cc3bd 100644 --- a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java +++ b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java @@ -380,9 +380,9 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement ctx.close(); } else { transportService.process(ValidateDeviceTokenRequestMsg.newBuilder().setToken(userName).build(), - new TransportServiceCallback() { + new TransportServiceCallback() { @Override - public void onSuccess(ValidateDeviceTokenResponseMsg msg) { + public void onSuccess(ValidateDeviceCredentialsResponseMsg msg) { if (!msg.hasDeviceInfo()) { ctx.writeAndFlush(createMqttConnAckMsg(CONNECTION_REFUSED_NOT_AUTHORIZED)); ctx.close(); @@ -404,32 +404,36 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement } } - protected SessionEventMsg getSessionEventMsg(SessionEvent event) { - return SessionEventMsg.newBuilder() - .setSessionInfo(sessionInfo) - .setDeviceIdMSB(deviceSessionCtx.getDeviceIdMSB()) - .setDeviceIdLSB(deviceSessionCtx.getDeviceIdLSB()) - .setEvent(event).build(); - } - private void processX509CertConnect(ChannelHandlerContext ctx, X509Certificate cert) { -// try { -// String strCert = SslUtil.getX509CertificateString(cert); -// String sha3Hash = EncryptionUtil.getSha3Hash(strCert); -// if (deviceSessionCtx.login(new DeviceX509Credentials(sha3Hash))) { -// ctx.writeAndFlush(createMqttConnAckMsg(CONNECTION_ACCEPTED)); -// connected = true; -// processor.process(new BasicTransportToDeviceSessionActorMsg(deviceSessionCtx.getDevice(), -// new BasicAdaptorToSessionActorMsg(deviceSessionCtx, new SessionOpenMsg()))); -// checkGatewaySession(); -// } else { -// ctx.writeAndFlush(createMqttConnAckMsg(CONNECTION_REFUSED_NOT_AUTHORIZED)); -// ctx.close(); -// } -// } catch (Exception e) { -// ctx.writeAndFlush(createMqttConnAckMsg(CONNECTION_REFUSED_NOT_AUTHORIZED)); -// ctx.close(); -// } + try { + String strCert = SslUtil.getX509CertificateString(cert); + String sha3Hash = EncryptionUtil.getSha3Hash(strCert); + transportService.process(ValidateDeviceX509CertRequestMsg.newBuilder().setHash(sha3Hash).build(), + new TransportServiceCallback() { + @Override + public void onSuccess(ValidateDeviceCredentialsResponseMsg msg) { + if (!msg.hasDeviceInfo()) { + ctx.writeAndFlush(createMqttConnAckMsg(CONNECTION_REFUSED_NOT_AUTHORIZED)); + ctx.close(); + } else { + ctx.writeAndFlush(createMqttConnAckMsg(CONNECTION_ACCEPTED)); + deviceSessionCtx.setDeviceInfo(msg.getDeviceInfo()); + transportService.process(getSessionEventMsg(SessionEvent.OPEN), null); + checkGatewaySession(); + } + } + + @Override + public void onError(Throwable e) { + log.trace("[{}] Failed to process credentials: {}", address, sha3Hash, e); + ctx.writeAndFlush(createMqttConnAckMsg(MqttConnectReturnCode.CONNECTION_REFUSED_SERVER_UNAVAILABLE)); + ctx.close(); + } + }); + } catch (Exception e) { + ctx.writeAndFlush(createMqttConnAckMsg(CONNECTION_REFUSED_NOT_AUTHORIZED)); + ctx.close(); + } } private X509Certificate getX509Certificate() { @@ -519,6 +523,14 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement } } + private SessionEventMsg getSessionEventMsg(SessionEvent event) { + return SessionEventMsg.newBuilder() + .setSessionInfo(sessionInfo) + .setDeviceIdMSB(deviceSessionCtx.getDeviceIdMSB()) + .setDeviceIdLSB(deviceSessionCtx.getDeviceIdLSB()) + .setEvent(event).build(); + } + @Override public void operationComplete(Future future) throws Exception { if (deviceSessionCtx.isConnected()) { diff --git a/transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/MqttTransportService.java b/transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/MqttTransportService.java index f5fd690170..4d945de394 100644 --- a/transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/MqttTransportService.java +++ b/transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/MqttTransportService.java @@ -104,8 +104,16 @@ public class MqttTransportService implements TransportService { } @Override - public void process(ValidateDeviceTokenRequestMsg msg, TransportServiceCallback callback) { - AsyncCallbackTemplate.withCallback(transportApiTemplate.post(msg.getToken(), TransportApiRequestMsg.newBuilder().setValidateTokenRequestMsg(msg).build()), + public void process(ValidateDeviceTokenRequestMsg msg, TransportServiceCallback 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 callback) { + AsyncCallbackTemplate.withCallback(transportApiTemplate.post(msg.getHash(), + TransportApiRequestMsg.newBuilder().setValidateX509CertRequestMsg(msg).build()), response -> callback.onSuccess(response.getValidateTokenResponseMsg()), callback::onError, transportCallbackExecutor); } From f0bccc7cfdfd92b35c586be9e6b364a1e20678d1 Mon Sep 17 00:00:00 2001 From: Andrew Shvayka Date: Tue, 9 Oct 2018 14:16:08 +0300 Subject: [PATCH 07/26] WIP: Implementation --- .../server/actors/ActorSystemContext.java | 7 + .../server/actors/app/AppActor.java | 13 +- .../server/actors/device/DeviceActor.java | 6 +- .../device/DeviceActorMessageProcessor.java | 220 ++++++++++-------- .../actors/device/PendingSessionMsgData.java | 3 +- .../server/actors/device/SessionInfo.java | 16 +- .../actors/session/ASyncMsgProcessor.java | 156 ------------- .../AbstractSessionActorMsgProcessor.java | 122 ---------- .../server/actors/session/SessionActor.java | 143 ------------ .../actors/session/SessionManagerActor.java | 180 -------------- .../actors/session/SyncMsgProcessor.java | 95 -------- .../server/actors/tenant/TenantActor.java | 2 +- .../RemoteRuleEngineTransportService.java | 187 +++++++++++++++ .../transport/RuleEngineTransportService.java | 32 +++ .../transport/ToRuleEngineMsgDecoder.java | 31 +++ .../transport/ToTransportMsgEncoder.java | 29 +++ .../msg/TransportToDeviceActorMsgWrapper.java | 36 +++ .../src/main/resources/thingsboard.yml | 6 +- .../server/common/msg/MsgType.java | 13 +- .../device/BasicDeviceToDeviceActorMsg.java | 107 --------- .../msg/device/DeviceToDeviceActorMsg.java | 41 ---- .../server/kafka/TBKafkaConsumerTemplate.java | 12 +- .../server/kafka/TBKafkaProducerTemplate.java | 29 +-- .../server/kafka/TbKafkaResponseTemplate.java | 109 +++++---- .../common/transport/SessionMsgListener.java | 22 +- .../common/transport/TransportService.java | 34 ++- .../session/DeviceAwareSessionContext.java | 35 +-- .../transport/src/main/proto/transport.proto | 80 +++++-- .../transport/mqtt/MqttTransportHandler.java | 67 ++---- .../mqtt/service/MqttTransportService.java | 168 ++++++++++++- .../mqtt/service/ToRuleEngineMsgEncoder.java | 29 +++ .../ToTransportMsgResponseDecoder.java | 31 +++ .../src/main/resources/tb-mqtt-transport.yml | 4 + 33 files changed, 901 insertions(+), 1164 deletions(-) delete mode 100644 application/src/main/java/org/thingsboard/server/actors/session/ASyncMsgProcessor.java delete mode 100644 application/src/main/java/org/thingsboard/server/actors/session/AbstractSessionActorMsgProcessor.java delete mode 100644 application/src/main/java/org/thingsboard/server/actors/session/SessionActor.java delete mode 100644 application/src/main/java/org/thingsboard/server/actors/session/SessionManagerActor.java delete mode 100644 application/src/main/java/org/thingsboard/server/actors/session/SyncMsgProcessor.java create mode 100644 application/src/main/java/org/thingsboard/server/service/transport/RemoteRuleEngineTransportService.java create mode 100644 application/src/main/java/org/thingsboard/server/service/transport/RuleEngineTransportService.java create mode 100644 application/src/main/java/org/thingsboard/server/service/transport/ToRuleEngineMsgDecoder.java create mode 100644 application/src/main/java/org/thingsboard/server/service/transport/ToTransportMsgEncoder.java create mode 100644 application/src/main/java/org/thingsboard/server/service/transport/msg/TransportToDeviceActorMsgWrapper.java delete mode 100644 common/message/src/main/java/org/thingsboard/server/common/msg/device/BasicDeviceToDeviceActorMsg.java delete mode 100644 common/message/src/main/java/org/thingsboard/server/common/msg/device/DeviceToDeviceActorMsg.java rename application/src/main/java/org/thingsboard/server/actors/session/SessionTerminationMsg.java => common/transport/src/main/java/org/thingsboard/server/common/transport/SessionMsgListener.java (59%) create mode 100644 transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/ToRuleEngineMsgEncoder.java create mode 100644 transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/ToTransportMsgResponseDecoder.java 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 8930f8476e..8ff61aca46 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; @@ -69,6 +70,7 @@ 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; @@ -204,6 +206,11 @@ public class ActorSystemContext { @Getter private DeviceStateService deviceStateService; + @Lazy + @Autowired + @Getter + private RuleEngineTransportService ruleEngineTransportService; + @Value("${cluster.partition_id}") @Getter private long queuePartitionId; 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 6a78f78d1b..14ca58610d 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 @@ -39,7 +39,6 @@ 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 +104,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: @@ -169,16 +168,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 99d004574e..7fabe493dd 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,12 @@ 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 +50,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); 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 a2ea0487cf..1313f60be5 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 @@ -1,12 +1,12 @@ /** * 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 - * + *

+ * 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. @@ -61,7 +61,6 @@ 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; @@ -71,9 +70,11 @@ 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.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; @@ -92,6 +93,8 @@ import java.util.function.Consumer; import java.util.function.Predicate; import java.util.stream.Collectors; +import org.thingsboard.server.gen.transport.TransportProtos.*; + /** * @author Andrew Shvayka */ @@ -99,12 +102,11 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso private final TenantId tenantId; private final DeviceId deviceId; - private final Map sessions; - private final Map attributeSubscriptions; - private final Map rpcSubscriptions; + private final Map sessions; + private final Map attributeSubscriptions; + private final Map rpcSubscriptions; private final Map toDeviceRpcPendingMap; private final Map toServerRpcPendingMap; - private final Map pendingMsgs; private final Gson gson = new Gson(); private final JsonParser jsonParser = new JsonParser(); @@ -123,7 +125,6 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso this.rpcSubscriptions = new HashMap<>(); this.toDeviceRpcPendingMap = new HashMap<>(); this.toServerRpcPendingMap = new HashMap<>(); - this.pendingMsgs = new HashMap<>(); initAttributes(); } @@ -154,11 +155,11 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso boolean sent = rpcSubscriptions.size() > 0; Set 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()) { - syncSessionSet.add(sub.getKey()); - } +// ActorSystemToDeviceSessionActorMsg response = new BasicActorSystemToDeviceSessionActorMsg(rpcRequest, sub.getKey()); +// sendMsgToSessionActor(response, sub.getValue().getServer()); +// if (SessionType.SYNC == sub.getValue().getType()) { +// syncSessionSet.add(sub.getKey()); +// } }); syncSessionSet.forEach(rpcSubscriptions::remove); @@ -191,15 +192,6 @@ 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()) { @@ -252,31 +244,37 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso }; } - void process(ActorContext context, DeviceToDeviceActorMsg msg) { - processSubscriptionCommands(context, msg); - processRpcResponses(context, msg); - processSessionStateMsgs(msg); + void process(ActorContext context, TransportToDeviceActorMsgWrapper wrapper) { + TransportToDeviceActorMsg msg = wrapper.getMsg(); +// processSubscriptionCommands(context, msg); +// processRpcResponses(context, msg); + if (msg.hasSessionEvent()) { + processSessionStateMsgs(msg.getSessionInfo(), msg.getSessionEvent()); + } - 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; - } + 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()); + } +// SessionMsgType sessionMsgType = msg.getPayload().getMsgType(); +// if (sessionMsgType.requiresRulesProcessing()) { +// switch (sessionMsgType) { +// case GET_ATTRIBUTES_REQUEST: +// handleGetAttributesRequest(msg); +// break; +// case TO_SERVER_RPC_REQUEST: +// handleClientSideRPCRequest(context, msg); +// reportActivity(); +// break; +// } +// } } private void reportActivity() { @@ -291,6 +289,39 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso systemContext.getDeviceStateService().onDeviceDisconnect(deviceId); } + private void handleGetAttributesRequest(ActorContext context, SessionInfoProto sessionInfo, GetAttributeRequestMsg request) { + ListenableFuture> clientAttributesFuture = getAttributeKvEntries(deviceId, DataConstants.CLIENT_SCOPE, toOptionalSet(request.getClientAttributeNamesList())); + ListenableFuture> sharedAttributesFuture = getAttributeKvEntries(deviceId, DataConstants.SHARED_SCOPE, toOptionalSet(request.getSharedAttributeNamesList())); + + Futures.addCallback(Futures.allAsList(Arrays.asList(clientAttributesFuture, sharedAttributesFuture)), new FutureCallback>>() { + @Override + public void onSuccess(@Nullable List> result) { + systemContext.getRuleEngineTransportService().process(); + BasicGetAttributesResponse response = BasicGetAttributesResponse.onSuccess(request.getMsgType(), + request.getRequestId(), BasicAttributeKVMsg.from(result.get(0), result.get(1))); + sendMsgToSessionActor(new BasicActorSystemToDeviceSessionActorMsg(response, src.getSessionId()), src.getServerAddress()); + } + + @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); + } + } + }); + } + + private Optional> toOptionalSet(List strings) { + if (strings == null || strings.isEmpty()) { + return Optional.empty(); + } else { + return Optional.of(new HashSet<>(strings)); + } + } + private void handleGetAttributesRequest(DeviceToDeviceActorMsg src) { GetAttributesRequest request = (GetAttributesRequest) src.getPayload(); ListenableFuture> clientAttributesFuture = getAttributeKvEntries(deviceId, DataConstants.CLIENT_SCOPE, request.getClientAttributeNames()); @@ -328,43 +359,20 @@ 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> tsData = request.getData(); - - PendingSessionMsgData msgData = new PendingSessionMsgData(src.getSessionId(), src.getServerAddress(), - SessionMsgType.POST_TELEMETRY_REQUEST, request.getRequestId(), true, tsData.size()); - - for (Map.Entry> 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); } } @@ -401,16 +409,7 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso } } - 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()); } @@ -497,13 +496,12 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso } } - 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 = new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB()); + if (msg.getEvent() == SessionEvent.OPEN) { 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)); } @@ -512,6 +510,10 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso if (sessions.size() == 1) { reportSessionOpen(); } + } + FromDeviceMsg inMsg = msg.getPayload(); + if (inMsg instanceof SessionOpenMsg) { + logger.debug("[{}] Processing new session [{}]", deviceId, sessionId); } else if (inMsg instanceof SessionCloseMsg) { logger.debug("[{}] Canceling subscriptions for closed session [{}]", deviceId, sessionId); sessions.remove(sessionId); @@ -540,8 +542,12 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso 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 +558,24 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso this.defaultMetaData.putValue("deviceType", deviceType); } + private JsonObject getJsonObject(List 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; + } } diff --git a/application/src/main/java/org/thingsboard/server/actors/device/PendingSessionMsgData.java b/application/src/main/java/org/thingsboard/server/actors/device/PendingSessionMsgData.java index 23ad966049..dfa07cf065 100644 --- a/application/src/main/java/org/thingsboard/server/actors/device/PendingSessionMsgData.java +++ b/application/src/main/java/org/thingsboard/server/actors/device/PendingSessionMsgData.java @@ -22,6 +22,7 @@ import org.thingsboard.server.common.msg.cluster.ServerAddress; import org.thingsboard.server.common.msg.session.SessionMsgType; import java.util.Optional; +import java.util.UUID; /** * Created by ashvayka on 17.04.18. @@ -30,7 +31,7 @@ import java.util.Optional; @AllArgsConstructor public final class PendingSessionMsgData { - private final SessionId sessionId; + private final UUID sessionId; private final Optional serverAddress; private final SessionMsgType sessionMsgType; private final int requestId; 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 04c457c92e..9faaade208 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 @@ -1,12 +1,12 @@ /** * 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 - * + *

+ * 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. @@ -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,6 @@ import java.util.Optional; @Data public class SessionInfo { private final SessionType type; - private final Optional server; + private final String nodeId; + } diff --git a/application/src/main/java/org/thingsboard/server/actors/session/ASyncMsgProcessor.java b/application/src/main/java/org/thingsboard/server/actors/session/ASyncMsgProcessor.java deleted file mode 100644 index a8f14fedf5..0000000000 --- a/application/src/main/java/org/thingsboard/server/actors/session/ASyncMsgProcessor.java +++ /dev/null @@ -1,156 +0,0 @@ -/** - * 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.actors.session; - -import akka.actor.ActorContext; -import akka.event.LoggingAdapter; -import org.thingsboard.server.actors.ActorSystemContext; -import org.thingsboard.server.actors.shared.SessionTimeoutMsg; -import org.thingsboard.server.common.data.id.SessionId; -import org.thingsboard.server.common.msg.cluster.ClusterEventMsg; -import org.thingsboard.server.common.msg.cluster.ServerAddress; -import org.thingsboard.server.common.msg.core.AttributesSubscribeMsg; -import org.thingsboard.server.common.msg.core.ResponseMsg; -import org.thingsboard.server.common.msg.core.RpcSubscribeMsg; -import org.thingsboard.server.common.msg.core.SessionCloseMsg; -import org.thingsboard.server.common.msg.core.SessionOpenMsg; -import org.thingsboard.server.common.msg.device.DeviceToDeviceActorMsg; -import org.thingsboard.server.common.msg.session.BasicSessionActorToAdaptorMsg; -import org.thingsboard.server.common.msg.session.FromDeviceMsg; -import org.thingsboard.server.common.msg.session.FromDeviceRequestMsg; -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.session.TransportToDeviceSessionActorMsg; -import org.thingsboard.server.common.msg.session.ex.SessionException; - -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; - -class ASyncMsgProcessor extends AbstractSessionActorMsgProcessor { - - private boolean firstMsg = true; - private Map pendingMap = new HashMap<>(); - private Optional currentTargetServer; - private boolean subscribedToAttributeUpdates; - private boolean subscribedToRpcCommands; - - public ASyncMsgProcessor(ActorSystemContext ctx, LoggingAdapter logger, SessionId sessionId) { - super(ctx, logger, sessionId); - } - - @Override - protected void processToDeviceActorMsg(ActorContext ctx, TransportToDeviceSessionActorMsg msg) { - updateSessionCtx(msg, SessionType.ASYNC); - DeviceToDeviceActorMsg pendingMsg = toDeviceMsg(msg); - FromDeviceMsg fromDeviceMsg = pendingMsg.getPayload(); - if (firstMsg) { - if (fromDeviceMsg.getMsgType() != SessionMsgType.SESSION_OPEN) { - toDeviceMsg(new SessionOpenMsg()).ifPresent(m -> forwardToAppActor(ctx, m)); - } - firstMsg = false; - } - switch (fromDeviceMsg.getMsgType()) { - case POST_TELEMETRY_REQUEST: - case POST_ATTRIBUTES_REQUEST: - FromDeviceRequestMsg requestMsg = (FromDeviceRequestMsg) fromDeviceMsg; - if (requestMsg.getRequestId() >= 0) { - logger.debug("[{}] Pending request {} registered", requestMsg.getRequestId(), requestMsg.getMsgType()); - //TODO: handle duplicates. - pendingMap.put(requestMsg.getRequestId(), pendingMsg); - } - break; - case SUBSCRIBE_ATTRIBUTES_REQUEST: - subscribedToAttributeUpdates = true; - break; - case UNSUBSCRIBE_ATTRIBUTES_REQUEST: - subscribedToAttributeUpdates = false; - break; - case SUBSCRIBE_RPC_COMMANDS_REQUEST: - subscribedToRpcCommands = true; - break; - case UNSUBSCRIBE_RPC_COMMANDS_REQUEST: - subscribedToRpcCommands = false; - break; - default: - break; - } - currentTargetServer = forwardToAppActor(ctx, pendingMsg); - } - - @Override - public void processToDeviceMsg(ActorContext context, ToDeviceMsg msg) { - try { - if (msg.getSessionMsgType() != SessionMsgType.SESSION_CLOSE) { - switch (msg.getSessionMsgType()) { - case STATUS_CODE_RESPONSE: - case GET_ATTRIBUTES_RESPONSE: - ResponseMsg responseMsg = (ResponseMsg) msg; - if (responseMsg.getRequestId() >= 0) { - logger.debug("[{}] Pending request processed: {}", responseMsg.getRequestId(), responseMsg); - pendingMap.remove(responseMsg.getRequestId()); - } - break; - default: - break; - } - sessionCtx.onMsg(new BasicSessionActorToAdaptorMsg(this.sessionCtx, msg)); - } else { - sessionCtx.onMsg(org.thingsboard.server.common.msg.session.ctrl.SessionCloseMsg.onCredentialsRevoked(sessionCtx.getSessionId())); - } - } catch (SessionException e) { - logger.warning("Failed to push session response msg", e); - } - } - - @Override - public void processTimeoutMsg(ActorContext context, SessionTimeoutMsg msg) { - // TODO Auto-generated method stub - } - - @Override - protected void cleanupSession(ActorContext ctx) { - toDeviceMsg(new SessionCloseMsg()).ifPresent(m -> forwardToAppActor(ctx, m)); - } - - @Override - public void processClusterEvent(ActorContext context, ClusterEventMsg msg) { - if (pendingMap.size() > 0 || subscribedToAttributeUpdates || subscribedToRpcCommands) { - Optional newTargetServer = systemContext.getRoutingService().resolveById(getDeviceId()); - if (!newTargetServer.equals(currentTargetServer)) { - firstMsg = true; - currentTargetServer = newTargetServer; - pendingMap.values().forEach(v -> { - forwardToAppActor(context, v, currentTargetServer); - if (currentTargetServer.isPresent()) { - logger.debug("[{}] Forwarded msg to new server: {}", sessionId, currentTargetServer.get()); - } else { - logger.debug("[{}] Forwarded msg to local server.", sessionId); - } - }); - if (subscribedToAttributeUpdates) { - toDeviceMsg(new AttributesSubscribeMsg()).ifPresent(m -> forwardToAppActor(context, m, currentTargetServer)); - logger.debug("[{}] Forwarded attributes subscription.", sessionId); - } - if (subscribedToRpcCommands) { - toDeviceMsg(new RpcSubscribeMsg()).ifPresent(m -> forwardToAppActor(context, m, currentTargetServer)); - logger.debug("[{}] Forwarded rpc commands subscription.", sessionId); - } - } - } - } -} diff --git a/application/src/main/java/org/thingsboard/server/actors/session/AbstractSessionActorMsgProcessor.java b/application/src/main/java/org/thingsboard/server/actors/session/AbstractSessionActorMsgProcessor.java deleted file mode 100644 index 469cda9c5f..0000000000 --- a/application/src/main/java/org/thingsboard/server/actors/session/AbstractSessionActorMsgProcessor.java +++ /dev/null @@ -1,122 +0,0 @@ -/** - * 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.actors.session; - -import akka.actor.ActorContext; -import akka.actor.ActorRef; -import akka.event.LoggingAdapter; -import org.thingsboard.server.actors.ActorSystemContext; -import org.thingsboard.server.actors.shared.AbstractContextAwareMsgProcessor; -import org.thingsboard.server.actors.shared.SessionTimeoutMsg; -import org.thingsboard.server.common.data.id.DeviceId; -import org.thingsboard.server.common.data.id.SessionId; -import org.thingsboard.server.common.msg.cluster.ClusterEventMsg; -import org.thingsboard.server.common.msg.cluster.SendToClusterMsg; -import org.thingsboard.server.common.msg.cluster.ServerAddress; -import org.thingsboard.server.common.msg.device.BasicDeviceToDeviceActorMsg; -import org.thingsboard.server.common.msg.device.DeviceToDeviceActorMsg; -import org.thingsboard.server.common.msg.session.AdaptorToSessionActorMsg; -import org.thingsboard.server.common.msg.session.FromDeviceMsg; -import org.thingsboard.server.common.msg.session.SessionContext; -import org.thingsboard.server.common.msg.session.SessionCtrlMsg; -import org.thingsboard.server.common.msg.session.SessionType; -import org.thingsboard.server.common.msg.session.ToDeviceMsg; -import org.thingsboard.server.common.msg.session.TransportToDeviceSessionActorMsg; -import org.thingsboard.server.common.msg.session.ctrl.SessionCloseMsg; - -import java.util.Optional; - -abstract class AbstractSessionActorMsgProcessor extends AbstractContextAwareMsgProcessor { - - protected final SessionId sessionId; - protected SessionContext sessionCtx; - protected DeviceToDeviceActorMsg deviceToDeviceActorMsgPrototype; - - protected AbstractSessionActorMsgProcessor(ActorSystemContext ctx, LoggingAdapter logger, SessionId sessionId) { - super(ctx, logger); - this.sessionId = sessionId; - } - - protected abstract void processToDeviceActorMsg(ActorContext ctx, TransportToDeviceSessionActorMsg msg); - - protected abstract void processTimeoutMsg(ActorContext context, SessionTimeoutMsg msg); - - protected abstract void processToDeviceMsg(ActorContext context, ToDeviceMsg msg); - - public abstract void processClusterEvent(ActorContext context, ClusterEventMsg msg); - - protected void processSessionCtrlMsg(ActorContext ctx, SessionCtrlMsg msg) { - if (msg instanceof SessionCloseMsg) { - cleanupSession(ctx); - terminateSession(ctx, sessionId); - } - } - - protected void cleanupSession(ActorContext ctx) { - } - - protected void updateSessionCtx(TransportToDeviceSessionActorMsg msg, SessionType type) { - sessionCtx = msg.getSessionMsg().getSessionContext(); - deviceToDeviceActorMsgPrototype = new BasicDeviceToDeviceActorMsg(msg, type); - } - - protected DeviceToDeviceActorMsg toDeviceMsg(TransportToDeviceSessionActorMsg msg) { - AdaptorToSessionActorMsg adaptorMsg = msg.getSessionMsg(); - return new BasicDeviceToDeviceActorMsg(deviceToDeviceActorMsgPrototype, adaptorMsg.getMsg()); - } - - protected Optional toDeviceMsg(FromDeviceMsg msg) { - if (deviceToDeviceActorMsgPrototype != null) { - return Optional.of(new BasicDeviceToDeviceActorMsg(deviceToDeviceActorMsgPrototype, msg)); - } else { - return Optional.empty(); - } - } - - protected Optional forwardToAppActor(ActorContext ctx, DeviceToDeviceActorMsg toForward) { - Optional address = systemContext.getRoutingService().resolveById(toForward.getDeviceId()); - forwardToAppActor(ctx, toForward, address); - return address; - } - - protected Optional forwardToAppActorIfAddressChanged(ActorContext ctx, DeviceToDeviceActorMsg toForward, Optional oldAddress) { - - Optional newAddress = systemContext.getRoutingService().resolveById(toForward.getDeviceId()); - if (!newAddress.equals(oldAddress)) { - getAppActor().tell(new SendToClusterMsg(toForward.getDeviceId(), toForward - .toOtherAddress(systemContext.getRoutingService().getCurrentServer())), ctx.self()); - } - return newAddress; - } - - protected void forwardToAppActor(ActorContext ctx, DeviceToDeviceActorMsg toForward, Optional address) { - if (address.isPresent()) { - systemContext.getRpcService().tell(systemContext.getEncodingService().convertToProtoDataMessage(address.get(), - toForward.toOtherAddress(systemContext.getRoutingService().getCurrentServer()))); - } else { - getAppActor().tell(toForward, ctx.self()); - } - } - - public static void terminateSession(ActorContext ctx, SessionId sessionId) { - ctx.parent().tell(new SessionTerminationMsg(sessionId), ActorRef.noSender()); - ctx.stop(ctx.self()); - } - - public DeviceId getDeviceId() { - return deviceToDeviceActorMsgPrototype.getDeviceId(); - } -} diff --git a/application/src/main/java/org/thingsboard/server/actors/session/SessionActor.java b/application/src/main/java/org/thingsboard/server/actors/session/SessionActor.java deleted file mode 100644 index 05926c1825..0000000000 --- a/application/src/main/java/org/thingsboard/server/actors/session/SessionActor.java +++ /dev/null @@ -1,143 +0,0 @@ -/** - * 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.actors.session; - -import akka.actor.OneForOneStrategy; -import akka.actor.SupervisorStrategy; -import akka.event.Logging; -import akka.event.LoggingAdapter; -import org.thingsboard.server.actors.ActorSystemContext; -import org.thingsboard.server.actors.service.ContextAwareActor; -import org.thingsboard.server.actors.service.ContextBasedCreator; -import org.thingsboard.server.actors.shared.SessionTimeoutMsg; -import org.thingsboard.server.common.data.id.SessionId; -import org.thingsboard.server.common.msg.TbActorMsg; -import org.thingsboard.server.common.msg.cluster.ClusterEventMsg; -import org.thingsboard.server.common.msg.core.ActorSystemToDeviceSessionActorMsg; -import org.thingsboard.server.common.msg.session.SessionCtrlMsg; -import org.thingsboard.server.common.msg.session.SessionMsg; -import org.thingsboard.server.common.msg.session.SessionType; -import org.thingsboard.server.common.msg.session.TransportToDeviceSessionActorMsg; -import org.thingsboard.server.common.msg.session.ctrl.SessionCloseMsg; -import scala.concurrent.duration.Duration; - -public class SessionActor extends ContextAwareActor { - - private final LoggingAdapter logger = Logging.getLogger(getContext().system(), this); - - private final SessionId sessionId; - private AbstractSessionActorMsgProcessor processor; - - private SessionActor(ActorSystemContext systemContext, SessionId sessionId) { - super(systemContext); - this.sessionId = sessionId; - } - - @Override - public SupervisorStrategy supervisorStrategy() { - return new OneForOneStrategy(-1, Duration.Inf(), - throwable -> { - logger.error(throwable, "Unknown session error"); - if (throwable instanceof Error) { - return OneForOneStrategy.escalate(); - } else { - return OneForOneStrategy.resume(); - } - }); - } - - @Override - protected boolean process(TbActorMsg msg) { - switch (msg.getMsgType()) { - case TRANSPORT_TO_DEVICE_SESSION_ACTOR_MSG: - processTransportToSessionMsg((TransportToDeviceSessionActorMsg) msg); - break; - case ACTOR_SYSTEM_TO_DEVICE_SESSION_ACTOR_MSG: - processActorsToSessionMsg((ActorSystemToDeviceSessionActorMsg) msg); - break; - case SESSION_TIMEOUT_MSG: - processTimeoutMsg((SessionTimeoutMsg) msg); - break; - case SESSION_CTRL_MSG: - processSessionCloseMsg((SessionCtrlMsg) msg); - break; - case CLUSTER_EVENT_MSG: - processClusterEvent((ClusterEventMsg) msg); - break; - default: return false; - } - return true; - } - - private void processClusterEvent(ClusterEventMsg msg) { - processor.processClusterEvent(context(), msg); - } - - private void processTransportToSessionMsg(TransportToDeviceSessionActorMsg msg) { - initProcessor(msg); - processor.processToDeviceActorMsg(context(), msg); - } - - private void processActorsToSessionMsg(ActorSystemToDeviceSessionActorMsg msg) { - processor.processToDeviceMsg(context(), msg.getMsg()); - } - - private void processTimeoutMsg(SessionTimeoutMsg msg) { - if (processor != null) { - processor.processTimeoutMsg(context(), msg); - } else { - logger.warning("[{}] Can't process timeout msg: {} without processor", sessionId, msg); - } - } - - private void processSessionCloseMsg(SessionCtrlMsg msg) { - if (processor != null) { - processor.processSessionCtrlMsg(context(), msg); - } else if (msg instanceof SessionCloseMsg) { - AbstractSessionActorMsgProcessor.terminateSession(context(), sessionId); - } else { - logger.warning("[{}] Can't process session ctrl msg: {} without processor", sessionId, msg); - } - } - - private void initProcessor(TransportToDeviceSessionActorMsg msg) { - if (processor == null) { - SessionMsg sessionMsg = (SessionMsg) msg.getSessionMsg(); - if (sessionMsg.getSessionContext().getSessionType() == SessionType.SYNC) { - processor = new SyncMsgProcessor(systemContext, logger, sessionId); - } else { - processor = new ASyncMsgProcessor(systemContext, logger, sessionId); - } - } - } - - public static class ActorCreator extends ContextBasedCreator { - private static final long serialVersionUID = 1L; - - private final SessionId sessionId; - - public ActorCreator(ActorSystemContext context, SessionId sessionId) { - super(context); - this.sessionId = sessionId; - } - - @Override - public SessionActor create() throws Exception { - return new SessionActor(context, sessionId); - } - } - -} diff --git a/application/src/main/java/org/thingsboard/server/actors/session/SessionManagerActor.java b/application/src/main/java/org/thingsboard/server/actors/session/SessionManagerActor.java deleted file mode 100644 index 9039d302e3..0000000000 --- a/application/src/main/java/org/thingsboard/server/actors/session/SessionManagerActor.java +++ /dev/null @@ -1,180 +0,0 @@ -/** - * 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.actors.session; - -import akka.actor.ActorInitializationException; -import akka.actor.ActorRef; -import akka.actor.InvalidActorNameException; -import akka.actor.LocalActorRef; -import akka.actor.OneForOneStrategy; -import akka.actor.Props; -import akka.actor.SupervisorStrategy; -import akka.actor.Terminated; -import akka.event.Logging; -import akka.event.LoggingAdapter; -import akka.japi.Function; -import org.thingsboard.server.actors.ActorSystemContext; -import org.thingsboard.server.actors.service.ContextAwareActor; -import org.thingsboard.server.actors.service.ContextBasedCreator; -import org.thingsboard.server.actors.service.DefaultActorService; -import org.thingsboard.server.actors.shared.SessionTimeoutMsg; -import org.thingsboard.server.common.data.id.SessionId; -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.core.ActorSystemToDeviceSessionActorMsg; -import org.thingsboard.server.common.msg.core.SessionCloseMsg; -import org.thingsboard.server.common.msg.session.SessionCtrlMsg; -import scala.concurrent.duration.Duration; - -import java.util.HashMap; -import java.util.Map; - -public class SessionManagerActor extends ContextAwareActor { - - private static final int INITIAL_SESSION_MAP_SIZE = 1024; - - private final LoggingAdapter log = Logging.getLogger(getContext().system(), this); - - private final Map sessionActors; - - SessionManagerActor(ActorSystemContext systemContext) { - super(systemContext); - this.sessionActors = new HashMap<>(INITIAL_SESSION_MAP_SIZE); - } - - @Override - public SupervisorStrategy supervisorStrategy() { - return strategy; - } - - @Override - protected boolean process(TbActorMsg msg) { - //TODO Move everything here, to work with TbActorMsg - return false; - } - - @Override - public void onReceive(Object msg) throws Exception { - if (msg instanceof SessionCtrlMsg) { - onSessionCtrlMsg((SessionCtrlMsg) msg); - } else if (msg instanceof SessionAwareMsg) { - forwardToSessionActor((SessionAwareMsg) msg); - } else if (msg instanceof SessionTerminationMsg) { - onSessionTermination((SessionTerminationMsg) msg); - } else if (msg instanceof Terminated) { - onTermination((Terminated) msg); - } else if (msg instanceof SessionTimeoutMsg) { - onSessionTimeout((SessionTimeoutMsg) msg); - } else if (msg instanceof ClusterEventMsg) { - broadcast(msg); - } - } - - private void broadcast(Object msg) { - sessionActors.values().forEach(actorRef -> actorRef.tell(msg, ActorRef.noSender())); - } - - private void onSessionTimeout(SessionTimeoutMsg msg) { - String sessionIdStr = msg.getSessionId().toUidStr(); - ActorRef sessionActor = sessionActors.get(sessionIdStr); - if (sessionActor != null) { - sessionActor.tell(msg, ActorRef.noSender()); - } - } - - private void onSessionCtrlMsg(SessionCtrlMsg msg) { - String sessionIdStr = msg.getSessionId().toUidStr(); - ActorRef sessionActor = sessionActors.get(sessionIdStr); - if (sessionActor != null) { - sessionActor.tell(msg, ActorRef.noSender()); - } - } - - private void onSessionTermination(SessionTerminationMsg msg) { - String sessionIdStr = msg.getId().toUidStr(); - ActorRef sessionActor = sessionActors.remove(sessionIdStr); - if (sessionActor != null) { - log.debug("[{}] Removed session actor.", sessionIdStr); - //TODO: onSubscriptionUpdate device actor about session close; - } else { - log.debug("[{}] Session actor was already removed.", sessionIdStr); - } - } - - private void forwardToSessionActor(SessionAwareMsg msg) { - if (msg instanceof ActorSystemToDeviceSessionActorMsg || msg instanceof SessionCloseMsg) { - String sessionIdStr = msg.getSessionId().toUidStr(); - ActorRef sessionActor = sessionActors.get(sessionIdStr); - if (sessionActor != null) { - sessionActor.tell(msg, ActorRef.noSender()); - } else { - log.debug("[{}] Session actor was already removed.", sessionIdStr); - } - } else { - try { - getOrCreateSessionActor(msg.getSessionId()).tell(msg, self()); - } catch (InvalidActorNameException e) { - log.info("Invalid msg : {}", msg); - } - } - } - - private ActorRef getOrCreateSessionActor(SessionId sessionId) { - String sessionIdStr = sessionId.toUidStr(); - ActorRef sessionActor = sessionActors.get(sessionIdStr); - if (sessionActor == null) { - log.debug("[{}] Creating session actor.", sessionIdStr); - sessionActor = context().actorOf( - Props.create(new SessionActor.ActorCreator(systemContext, sessionId)).withDispatcher(DefaultActorService.SESSION_DISPATCHER_NAME), - sessionIdStr); - sessionActors.put(sessionIdStr, sessionActor); - log.debug("[{}] Created session actor.", sessionIdStr); - } - return sessionActor; - } - - private void onTermination(Terminated message) { - ActorRef terminated = message.actor(); - if (terminated instanceof LocalActorRef) { - log.info("Removed actor: {}.", terminated); - //TODO: cleanup session actors map - } else { - throw new IllegalStateException("Remote actors are not supported!"); - } - } - - public static class ActorCreator extends ContextBasedCreator { - private static final long serialVersionUID = 1L; - - public ActorCreator(ActorSystemContext context) { - super(context); - } - - @Override - public SessionManagerActor create() throws Exception { - return new SessionManagerActor(context); - } - } - - private final SupervisorStrategy strategy = new OneForOneStrategy(3, Duration.create("1 minute"), new Function() { - @Override - public SupervisorStrategy.Directive apply(Throwable t) { - logger.error(t, "Unknown failure"); - return SupervisorStrategy.stop(); - } - }); -} diff --git a/application/src/main/java/org/thingsboard/server/actors/session/SyncMsgProcessor.java b/application/src/main/java/org/thingsboard/server/actors/session/SyncMsgProcessor.java deleted file mode 100644 index cf8df1385f..0000000000 --- a/application/src/main/java/org/thingsboard/server/actors/session/SyncMsgProcessor.java +++ /dev/null @@ -1,95 +0,0 @@ -/** - * 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.actors.session; - -import akka.actor.ActorContext; -import akka.event.LoggingAdapter; -import org.thingsboard.server.actors.ActorSystemContext; -import org.thingsboard.server.actors.shared.SessionTimeoutMsg; -import org.thingsboard.server.common.data.id.SessionId; -import org.thingsboard.server.common.msg.cluster.ClusterEventMsg; -import org.thingsboard.server.common.msg.cluster.ServerAddress; -import org.thingsboard.server.common.msg.device.DeviceToDeviceActorMsg; -import org.thingsboard.server.common.msg.session.BasicSessionActorToAdaptorMsg; -import org.thingsboard.server.common.msg.session.SessionContext; -import org.thingsboard.server.common.msg.session.SessionType; -import org.thingsboard.server.common.msg.session.ToDeviceMsg; -import org.thingsboard.server.common.msg.session.TransportToDeviceSessionActorMsg; -import org.thingsboard.server.common.msg.session.ctrl.SessionCloseMsg; -import org.thingsboard.server.common.msg.session.ex.SessionException; - -import java.util.Optional; - -class SyncMsgProcessor extends AbstractSessionActorMsgProcessor { - private DeviceToDeviceActorMsg pendingMsg; - private Optional currentTargetServer; - private boolean pendingResponse; - - public SyncMsgProcessor(ActorSystemContext ctx, LoggingAdapter logger, SessionId sessionId) { - super(ctx, logger, sessionId); - } - - @Override - protected void processToDeviceActorMsg(ActorContext ctx, TransportToDeviceSessionActorMsg msg) { - updateSessionCtx(msg, SessionType.SYNC); - pendingMsg = toDeviceMsg(msg); - pendingResponse = true; - currentTargetServer = forwardToAppActor(ctx, pendingMsg); - scheduleMsgWithDelay(ctx, new SessionTimeoutMsg(sessionId), getTimeout(systemContext, msg.getSessionMsg().getSessionContext()), ctx.parent()); - } - - public void processTimeoutMsg(ActorContext context, SessionTimeoutMsg msg) { - if (pendingResponse) { - try { - sessionCtx.onMsg(SessionCloseMsg.onTimeout(sessionId)); - } catch (SessionException e) { - logger.warning("Failed to push session close msg", e); - } - terminateSession(context, this.sessionId); - } - } - - public void processToDeviceMsg(ActorContext context, ToDeviceMsg msg) { - try { - sessionCtx.onMsg(new BasicSessionActorToAdaptorMsg(this.sessionCtx, msg)); - pendingResponse = false; - } catch (SessionException e) { - logger.warning("Failed to push session response msg", e); - } - terminateSession(context, this.sessionId); - } - - @Override - public void processClusterEvent(ActorContext context, ClusterEventMsg msg) { - if (pendingResponse) { - Optional newTargetServer = forwardToAppActorIfAddressChanged(context, pendingMsg, currentTargetServer); - if (logger.isDebugEnabled()) { - if (!newTargetServer.equals(currentTargetServer)) { - if (newTargetServer.isPresent()) { - logger.debug("[{}] Forwarded msg to new server: {}", sessionId, newTargetServer.get()); - } else { - logger.debug("[{}] Forwarded msg to local server.", sessionId); - } - } - } - currentTargetServer = newTargetServer; - } - } - - private long getTimeout(ActorSystemContext ctx, SessionContext sessionCtx) { - return sessionCtx.getTimeout() > 0 ? sessionCtx.getTimeout() : ctx.getSyncSessionTimeout(); - } -} 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 460b64c772..347483a185 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/service/transport/RemoteRuleEngineTransportService.java b/application/src/main/java/org/thingsboard/server/service/transport/RemoteRuleEngineTransportService.java new file mode 100644 index 0000000000..6b827921cd --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/transport/RemoteRuleEngineTransportService.java @@ -0,0 +1,187 @@ +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.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.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.function.Consumer; + +/** + * Created by ashvayka on 09.10.18. + */ +@Slf4j +@Service +@ConditionalOnProperty(prefix = "transport.remote", value = "enabled", havingValue = "true") +public class RemoteRuleEngineTransportService implements RuleEngineTransportService { + + private static final ObjectMapper mapper = new ObjectMapper(); + + @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 DiscoveryService discoveryService; + + @Autowired + private ActorSystemContext actorContext; + + @Autowired + private ActorService actorService; + + //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 ruleEngineConsumer; + private TBKafkaProducerTemplate notificationsProducer; + + private ExecutorService mainConsumerExecutor = Executors.newSingleThreadExecutor(); + + private volatile boolean stopped = false; + + @PostConstruct + public void init() { + TBKafkaProducerTemplate.TBKafkaProducerTemplateBuilder notificationsProducerBuilder = TBKafkaProducerTemplate.builder(); + notificationsProducerBuilder.settings(kafkaSettings); + notificationsProducerBuilder.defaultTopic(notificationsTopic); + notificationsProducerBuilder.encoder(new ToTransportMsgEncoder()); + + notificationsProducer = notificationsProducerBuilder.build(); + notificationsProducer.init(); + + TBKafkaConsumerTemplate.TBKafkaConsumerTemplateBuilder ruleEngineConsumerBuilder = TBKafkaConsumerTemplate.builder(); + ruleEngineConsumerBuilder.settings(kafkaSettings); + ruleEngineConsumerBuilder.topic(ruleEngineTopic); + ruleEngineConsumerBuilder.clientId(discoveryService.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 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 onFailure) { + notificationsProducer.send(notificationsTopic + "." + nodeId, + ToTransportMsg.newBuilder().setToDeviceSessionMsg(msg).build() + , new QueueCallbackAdaptor(onSuccess, onFailure)); + } + + private void forwardToDeviceActor(TransportToDeviceActorMsg toDeviceActorMsg) { + TransportToDeviceActorMsgWrapper wrapper = new TransportToDeviceActorMsgWrapper(toDeviceActorMsg); + Optional 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 onFailure; + + QueueCallbackAdaptor(Runnable onSuccess, Consumer 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/RuleEngineTransportService.java b/application/src/main/java/org/thingsboard/server/service/transport/RuleEngineTransportService.java new file mode 100644 index 0000000000..e6b3dd3369 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/transport/RuleEngineTransportService.java @@ -0,0 +1,32 @@ +/** + * 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 org.thingsboard.server.gen.transport.TransportProtos.DeviceActorToTransportMsg; +import org.thingsboard.server.gen.transport.TransportProtos.; + +import java.util.function.Consumer; + +/** + * Created by ashvayka on 05.10.18. + */ +public interface RuleEngineTransportService { + + void process(String nodeId, DeviceActorToTransportMsg msg); + + void process(String nodeId, DeviceActorToTransportMsg msg, Runnable onSuccess, Consumer onFailure); + +} diff --git a/application/src/main/java/org/thingsboard/server/service/transport/ToRuleEngineMsgDecoder.java b/application/src/main/java/org/thingsboard/server/service/transport/ToRuleEngineMsgDecoder.java new file mode 100644 index 0000000000..16954eea10 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/transport/ToRuleEngineMsgDecoder.java @@ -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. + */ +package org.thingsboard.server.service.transport; + +import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; +import org.thingsboard.server.kafka.TbKafkaDecoder; + +import java.io.IOException; + +/** + * Created by ashvayka on 05.10.18. + */ +public class ToRuleEngineMsgDecoder implements TbKafkaDecoder { + @Override + public ToRuleEngineMsg decode(byte[] data) throws IOException { + return ToRuleEngineMsg.parseFrom(data); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/transport/ToTransportMsgEncoder.java b/application/src/main/java/org/thingsboard/server/service/transport/ToTransportMsgEncoder.java new file mode 100644 index 0000000000..5f3c026249 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/transport/ToTransportMsgEncoder.java @@ -0,0 +1,29 @@ +/** + * 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 org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg; +import org.thingsboard.server.kafka.TbKafkaEncoder; + +/** + * Created by ashvayka on 05.10.18. + */ +public class ToTransportMsgEncoder implements TbKafkaEncoder { + @Override + public byte[] encode(ToTransportMsg value) { + return value.toByteArray(); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/transport/msg/TransportToDeviceActorMsgWrapper.java b/application/src/main/java/org/thingsboard/server/service/transport/msg/TransportToDeviceActorMsgWrapper.java new file mode 100644 index 0000000000..3ee185893a --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/transport/msg/TransportToDeviceActorMsgWrapper.java @@ -0,0 +1,36 @@ +package org.thingsboard.server.service.transport.msg; + +import lombok.Data; +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.TbActorMsg; +import org.thingsboard.server.common.msg.aware.DeviceAwareMsg; +import org.thingsboard.server.common.msg.aware.TenantAwareMsg; +import org.thingsboard.server.common.msg.cluster.ServerAddress; +import org.thingsboard.server.gen.transport.TransportProtos.TransportToDeviceActorMsg; + +import java.io.Serializable; +import java.util.UUID; + +/** + * Created by ashvayka on 09.10.18. + */ +@Data +public class TransportToDeviceActorMsgWrapper implements TbActorMsg, DeviceAwareMsg, TenantAwareMsg, Serializable { + + private final TenantId tenantId; + private final DeviceId deviceId; + private final TransportToDeviceActorMsg msg; + + public TransportToDeviceActorMsgWrapper(TransportToDeviceActorMsg msg) { + this.msg = msg; + this.tenantId = new TenantId(new UUID(msg.getSessionInfo().getTenantIdMSB(), msg.getSessionInfo().getTenantIdLSB())); + this.deviceId = new DeviceId(new UUID(msg.getSessionInfo().getDeviceIdMSB(), msg.getSessionInfo().getDeviceIdLSB())); + } + + @Override + public MsgType getMsgType() { + return MsgType.TRANSPORT_TO_DEVICE_ACTOR_MSG; + } +} diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index c58f3a1a38..923976c302 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -462,4 +462,8 @@ transport: 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}" \ No newline at end of file + 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}" \ No newline at end of file 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 60e5469fa5..dfd8f98ec3 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, @@ -111,6 +106,12 @@ public enum MsgType { 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/device/BasicDeviceToDeviceActorMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/device/BasicDeviceToDeviceActorMsg.java deleted file mode 100644 index 92c5105673..0000000000 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/device/BasicDeviceToDeviceActorMsg.java +++ /dev/null @@ -1,107 +0,0 @@ -/** - * 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.msg.device; - -import lombok.ToString; -import org.thingsboard.server.common.data.id.CustomerId; -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.msg.MsgType; -import org.thingsboard.server.common.msg.cluster.ServerAddress; -import org.thingsboard.server.common.msg.session.FromDeviceMsg; -import org.thingsboard.server.common.msg.session.SessionType; -import org.thingsboard.server.common.msg.session.TransportToDeviceSessionActorMsg; - -import java.util.Optional; - -@ToString -public class BasicDeviceToDeviceActorMsg implements DeviceToDeviceActorMsg { - - private static final long serialVersionUID = -1866795134993115408L; - - private final TenantId tenantId; - private final CustomerId customerId; - private final DeviceId deviceId; - private final SessionId sessionId; - private final SessionType sessionType; - private final ServerAddress serverAddress; - private final FromDeviceMsg msg; - - public BasicDeviceToDeviceActorMsg(DeviceToDeviceActorMsg other, FromDeviceMsg msg) { - this(null, other.getTenantId(), other.getCustomerId(), other.getDeviceId(), other.getSessionId(), other.getSessionType(), msg); - } - - public BasicDeviceToDeviceActorMsg(TransportToDeviceSessionActorMsg msg, SessionType sessionType) { - this(null, msg.getTenantId(), msg.getCustomerId(), msg.getDeviceId(), msg.getSessionId(), sessionType, msg.getSessionMsg().getMsg()); - } - - private BasicDeviceToDeviceActorMsg(ServerAddress serverAddress, TenantId tenantId, CustomerId customerId, DeviceId deviceId, SessionId sessionId, SessionType sessionType, - FromDeviceMsg msg) { - super(); - this.serverAddress = serverAddress; - this.tenantId = tenantId; - this.customerId = customerId; - this.deviceId = deviceId; - this.sessionId = sessionId; - this.sessionType = sessionType; - this.msg = msg; - } - - @Override - public DeviceId getDeviceId() { - return deviceId; - } - - @Override - public CustomerId getCustomerId() { - return customerId; - } - - public TenantId getTenantId() { - return tenantId; - } - - @Override - public SessionId getSessionId() { - return sessionId; - } - - @Override - public SessionType getSessionType() { - return sessionType; - } - - @Override - public Optional getServerAddress() { - return Optional.ofNullable(serverAddress); - } - - @Override - public FromDeviceMsg getPayload() { - return msg; - } - - @Override - public DeviceToDeviceActorMsg toOtherAddress(ServerAddress otherAddress) { - return new BasicDeviceToDeviceActorMsg(otherAddress, tenantId, customerId, deviceId, sessionId, sessionType, msg); - } - - @Override - public MsgType getMsgType() { - return MsgType.DEVICE_SESSION_TO_DEVICE_ACTOR_MSG; - } -} diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/device/DeviceToDeviceActorMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/device/DeviceToDeviceActorMsg.java deleted file mode 100644 index fe8d9f2c21..0000000000 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/device/DeviceToDeviceActorMsg.java +++ /dev/null @@ -1,41 +0,0 @@ -/** - * 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.msg.device; - -import java.io.Serializable; -import java.util.Optional; - -import org.thingsboard.server.common.data.id.SessionId; -import org.thingsboard.server.common.msg.TbActorMsg; -import org.thingsboard.server.common.msg.aware.CustomerAwareMsg; -import org.thingsboard.server.common.msg.aware.DeviceAwareMsg; -import org.thingsboard.server.common.msg.aware.TenantAwareMsg; -import org.thingsboard.server.common.msg.cluster.ServerAddress; -import org.thingsboard.server.common.msg.session.FromDeviceMsg; -import org.thingsboard.server.common.msg.session.SessionType; - -public interface DeviceToDeviceActorMsg extends TbActorMsg, DeviceAwareMsg, CustomerAwareMsg, TenantAwareMsg, Serializable { - - SessionId getSessionId(); - - SessionType getSessionType(); - - Optional getServerAddress(); - - FromDeviceMsg getPayload(); - - DeviceToDeviceActorMsg toOtherAddress(ServerAddress otherAddress); -} 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 3adb1c368d..90f15a4892 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 @@ -1,12 +1,12 @@ /** * 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 - * + *

+ * 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. @@ -49,7 +49,9 @@ public class TBKafkaConsumerTemplate { 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 1e109d2877..8f4c095ed7 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 @@ -1,12 +1,12 @@ /** * 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 - * + *

+ * 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. @@ -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; @@ -89,28 +90,28 @@ public class TBKafkaProducerTemplate { } } - public Future send(String key, T value) { - return send(key, value, null, null); + public Future send(String key, T value, Callback callback) { + return send(key, value, null, callback); } - public Future send(String key, T value, Iterable

headers) { - return send(key, value, null, headers); + public Future send(String key, T value, Iterable
headers, Callback callback) { + return send(key, value, null, headers, callback); } - public Future send(String key, T value, Long timestamp, Iterable
headers) { - return send(this.defaultTopic, key, value, timestamp, headers); + public Future send(String key, T value, Long timestamp, Iterable
headers, Callback callback) { + return send(this.defaultTopic, key, value, timestamp, headers, callback); } - public Future send(String topic, String key, T value, Iterable
headers) { - return send(topic, key, value, null, headers); + public Future send(String topic, String key, T value, Iterable
headers, Callback callback) { + return send(topic, key, value, null, headers, callback); } - public Future send(String topic, String key, T value, Long timestamp, Iterable
headers) { + public Future send(String topic, String key, T value, Long timestamp, Iterable
headers, Callback callback) { byte[] data = encoder.encode(value); ProducerRecord record; Integer partition = getPartition(topic, key, value, data); record = new ProducerRecord<>(topic, partition, timestamp, key, data, headers); - return producer.send(record); + return producer.send(record, callback); } private Integer getPartition(String topic, String key, T value, byte[] data) { 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 index 0dbf45db14..6f9e8a791b 100644 --- a/common/queue/src/main/java/org/thingsboard/server/kafka/TbKafkaResponseTemplate.java +++ b/common/queue/src/main/java/org/thingsboard/server/kafka/TbKafkaResponseTemplate.java @@ -1,12 +1,12 @@ /** * 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 - * + *

+ * 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. @@ -77,55 +77,64 @@ public class TbKafkaResponseTemplate extends AbstractTbKafkaT requestTemplate.subscribe(); loopExecutor.submit(() -> { while (!stopped) { - while (pendingRequestCount.get() >= maxPendingRequests) { + 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 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 e) { - log.trace("Failed to wait until the server has capacity to handle new requests", e); + } catch (InterruptedException e2) { + log.trace("Failed to wait until the server has capacity to handle new requests", e2); } } - ConsumerRecords 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); - } - }); } }); } @@ -141,7 +150,7 @@ public class TbKafkaResponseTemplate extends AbstractTbKafkaT } 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)))); + responseTemplate.send(topic, requestId.toString(), response, Collections.singletonList(new RecordHeader(TbKafkaSettings.REQUEST_ID_HEADER, uuidToBytes(requestId))), null); } } diff --git a/application/src/main/java/org/thingsboard/server/actors/session/SessionTerminationMsg.java b/common/transport/src/main/java/org/thingsboard/server/common/transport/SessionMsgListener.java similarity index 59% rename from application/src/main/java/org/thingsboard/server/actors/session/SessionTerminationMsg.java rename to common/transport/src/main/java/org/thingsboard/server/common/transport/SessionMsgListener.java index 38cf878fa7..d0e6d18de2 100644 --- a/application/src/main/java/org/thingsboard/server/actors/session/SessionTerminationMsg.java +++ b/common/transport/src/main/java/org/thingsboard/server/common/transport/SessionMsgListener.java @@ -1,26 +1,26 @@ /** * 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 - * + *

+ * 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.actors.session; +package org.thingsboard.server.common.transport; -import org.thingsboard.server.actors.shared.ActorTerminationMsg; -import org.thingsboard.server.common.data.id.SessionId; +import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeResponseMsg; -public class SessionTerminationMsg extends ActorTerminationMsg { +/** + * Created by ashvayka on 04.10.18. + */ +public interface SessionMsgListener { - public SessionTerminationMsg(SessionId id) { - super(id); - } + void onGetAttributesResponse(GetAttributeResponseMsg getAttributesResponse); } diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/TransportService.java b/common/transport/src/main/java/org/thingsboard/server/common/transport/TransportService.java index 84d34e12cb..dc2e3062f2 100644 --- a/common/transport/src/main/java/org/thingsboard/server/common/transport/TransportService.java +++ b/common/transport/src/main/java/org/thingsboard/server/common/transport/TransportService.java @@ -1,12 +1,12 @@ /** * 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 - * + *

+ * 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. @@ -15,23 +15,33 @@ */ package org.thingsboard.server.common.transport; -import org.thingsboard.server.gen.transport.TransportProtos; +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; /** * Created by ashvayka on 04.10.18. */ public interface TransportService { - void process(TransportProtos.ValidateDeviceTokenRequestMsg msg, - TransportServiceCallback callback); + void process(ValidateDeviceTokenRequestMsg msg, + TransportServiceCallback callback); - void process(TransportProtos.ValidateDeviceX509CertRequestMsg msg, - TransportServiceCallback callback); + void process(ValidateDeviceX509CertRequestMsg msg, + TransportServiceCallback callback); - void process(TransportProtos.SessionEventMsg msg, TransportServiceCallback callback); + void process(SessionInfoProto sessionInfo, SessionEventMsg msg, TransportServiceCallback callback); - void process(TransportProtos.PostTelemetryMsg msg, TransportServiceCallback callback); + void process(SessionInfoProto sessionInfo, PostTelemetryMsg msg, TransportServiceCallback callback); - void process(TransportProtos.PostAttributeMsg msg, TransportServiceCallback callback); + void process(SessionInfoProto sessionInfo, PostAttributeMsg msg, TransportServiceCallback callback); + + void registerSession(SessionInfoProto sessionInfo, SessionMsgListener listener); + + void deregisterSession(SessionInfoProto sessionInfo); } diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/session/DeviceAwareSessionContext.java b/common/transport/src/main/java/org/thingsboard/server/common/transport/session/DeviceAwareSessionContext.java index 7b2c05e400..30689748c5 100644 --- a/common/transport/src/main/java/org/thingsboard/server/common/transport/session/DeviceAwareSessionContext.java +++ b/common/transport/src/main/java/org/thingsboard/server/common/transport/session/DeviceAwareSessionContext.java @@ -1,12 +1,12 @@ /** * 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 - * + *

+ * 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. @@ -16,17 +16,12 @@ package org.thingsboard.server.common.transport.session; import lombok.Data; -import lombok.extern.slf4j.Slf4j; -import org.thingsboard.server.common.data.Device; -import org.thingsboard.server.common.data.security.DeviceCredentialsFilter; -import org.thingsboard.server.common.data.security.DeviceTokenCredentials; +import lombok.Getter; +import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.msg.session.SessionContext; -import org.thingsboard.server.common.transport.SessionMsgProcessor; -import org.thingsboard.server.common.transport.auth.DeviceAuthResult; -import org.thingsboard.server.common.transport.auth.DeviceAuthService; -import org.thingsboard.server.gen.transport.TransportProtos; +import org.thingsboard.server.gen.transport.TransportProtos.DeviceInfoProto; -import java.util.Optional; +import java.util.UUID; /** * @author Andrew Shvayka @@ -34,7 +29,9 @@ import java.util.Optional; @Data public abstract class DeviceAwareSessionContext implements SessionContext { - private volatile TransportProtos.DeviceInfoProto deviceInfo; + @Getter + private volatile DeviceId deviceId; + private volatile DeviceInfoProto deviceInfo; public long getDeviceIdMSB() { return deviceInfo.getDeviceIdMSB(); @@ -44,6 +41,16 @@ public abstract class DeviceAwareSessionContext implements SessionContext { return deviceInfo.getDeviceIdLSB(); } + public DeviceId getDeviceId() { + return deviceId; + } + + public void setDeviceInfo(DeviceInfoProto deviceInfo) { + this.deviceInfo = deviceInfo; + this.deviceId = new DeviceId(new UUID(deviceInfo.getDeviceIdMSB(), deviceInfo.getDeviceIdLSB())); + } + + public boolean isConnected() { return deviceInfo != null; } diff --git a/common/transport/src/main/proto/transport.proto b/common/transport/src/main/proto/transport.proto index e78f87351e..f3b3ae87a4 100644 --- a/common/transport/src/main/proto/transport.proto +++ b/common/transport/src/main/proto/transport.proto @@ -23,9 +23,12 @@ option java_outer_classname = "TransportProtos"; * Data Structures; */ message SessionInfoProto { - string nodeId = 1; - int64 sessionIdMSB = 2; - int64 sessionIdLSB = 3; + int64 sessionIdMSB = 1; + int64 sessionIdLSB = 2; + int64 tenantIdMSB = 3; + int64 tenantIdLSB = 4; + int64 deviceIdMSB = 5; + int64 deviceIdLSB = 6; } enum SessionEvent { @@ -33,12 +36,25 @@ enum SessionEvent { 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; - bool bool_v = 2; - int64 long_v = 3; - double double_v = 4; - string string_v = 5; + KeyValueType type = 2; + bool bool_v = 3; + int64 long_v = 4; + double double_v = 5; + string string_v = 6; } message TsKvListProto { @@ -60,33 +76,28 @@ message DeviceInfoProto { * Messages that use Data Structures; */ message SessionEventMsg { - SessionInfoProto sessionInfo = 1; - int64 deviceIdMSB = 2; - int64 deviceIdLSB = 3; - SessionEvent event = 4; + string nodeId = 1; + SessionType sessionType = 2; + SessionEvent event = 3; } message PostTelemetryMsg { - SessionInfoProto sessionInfo = 1; - repeated TsKvListProto tsKvList = 2; + repeated TsKvListProto tsKvList = 1; } message PostAttributeMsg { - SessionInfoProto sessionInfo = 1; - repeated TsKvListProto tsKvList = 2; + repeated KeyValueProto kv = 1; } message GetAttributeRequestMsg { - SessionInfoProto sessionInfo = 1; - repeated string clientAttributeNames = 2; - repeated string sharedAttributeNames = 3; + repeated string clientAttributeNames = 1; + repeated string sharedAttributeNames = 2; } message GetAttributeResponseMsg { - SessionInfoProto sessionInfo = 1; - repeated TsKvListProto clientAttributeList = 2; - repeated TsKvListProto sharedAttributeList = 3; - repeated string deletedAttributeKeys = 4; + repeated TsKvListProto clientAttributeList = 1; + repeated TsKvListProto sharedAttributeList = 2; + repeated string deletedAttributeKeys = 3; } message ValidateDeviceTokenRequestMsg { @@ -101,11 +112,34 @@ message ValidateDeviceCredentialsResponseMsg { DeviceInfoProto deviceInfo = 1; } +message SessionCloseNotificationProto { + string message = 1; +} + +message TransportToDeviceActorMsg { + SessionInfoProto sessionInfo = 1; + SessionEventMsg sessionEvent = 2; + PostTelemetryMsg postTelemetry = 3; + PostAttributeMsg postAttributes = 4; + GetAttributeRequestMsg getAttributes = 5; +} + +message DeviceActorToTransportMsg { + int64 sessionIdMSB = 1; + int64 sessionIdLSB = 2; + SessionCloseNotificationProto sessionCloseNotification = 3; + GetAttributeResponseMsg getAttributesResponse = 4; +} + /** * Main messages; */ -message TransportToRuleEngineMsg { +message ToRuleEngineMsg { + TransportToDeviceActorMsg toDeviceActorMsg = 1; +} +message ToTransportMsg { + DeviceActorToTransportMsg toDeviceSessionMsg = 1; } message TransportApiRequestMsg { diff --git a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java index 23bc4cc3bd..d8f1d10b11 100644 --- a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java +++ b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java @@ -1,12 +1,12 @@ /** * 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 - * + *

+ * 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. @@ -26,32 +26,25 @@ import io.netty.handler.codec.mqtt.MqttFixedHeader; import io.netty.handler.codec.mqtt.MqttMessage; import io.netty.handler.codec.mqtt.MqttMessageIdVariableHeader; import io.netty.handler.codec.mqtt.MqttPubAckMessage; -import io.netty.handler.codec.mqtt.MqttPublishMessage; import io.netty.handler.codec.mqtt.MqttQoS; import io.netty.handler.codec.mqtt.MqttSubAckMessage; import io.netty.handler.codec.mqtt.MqttSubAckPayload; -import io.netty.handler.codec.mqtt.MqttSubscribeMessage; -import io.netty.handler.codec.mqtt.MqttTopicSubscription; -import io.netty.handler.codec.mqtt.MqttUnsubscribeMessage; import io.netty.handler.ssl.SslHandler; import io.netty.util.concurrent.Future; import io.netty.util.concurrent.GenericFutureListener; import lombok.extern.slf4j.Slf4j; import org.springframework.util.StringUtils; -import org.thingsboard.server.common.data.Device; -import org.thingsboard.server.common.data.security.DeviceTokenCredentials; -import org.thingsboard.server.common.data.security.DeviceX509Credentials; -import org.thingsboard.server.common.msg.core.SessionOpenMsg; -import org.thingsboard.server.common.msg.session.AdaptorToSessionActorMsg; -import org.thingsboard.server.common.msg.session.BasicAdaptorToSessionActorMsg; -import org.thingsboard.server.common.msg.session.BasicTransportToDeviceSessionActorMsg; -import org.thingsboard.server.common.msg.session.ctrl.SessionCloseMsg; 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.quota.QuotaService; import org.thingsboard.server.dao.EncryptionUtil; -import org.thingsboard.server.gen.transport.TransportProtos; +import org.thingsboard.server.gen.transport.TransportProtos.DeviceInfoProto; +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.ValidateDeviceCredentialsResponseMsg; +import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceTokenRequestMsg; +import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceX509CertRequestMsg; import org.thingsboard.server.transport.mqtt.adaptors.MqttTransportAdaptor; import org.thingsboard.server.transport.mqtt.session.DeviceSessionCtx; import org.thingsboard.server.transport.mqtt.session.GatewaySessionCtx; @@ -61,49 +54,19 @@ import javax.net.ssl.SSLPeerUnverifiedException; import javax.security.cert.X509Certificate; import java.io.IOException; import java.net.InetSocketAddress; -import java.util.ArrayList; import java.util.List; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; -import org.thingsboard.server.gen.transport.TransportProtos.*; - import static io.netty.handler.codec.mqtt.MqttConnectReturnCode.CONNECTION_ACCEPTED; import static io.netty.handler.codec.mqtt.MqttConnectReturnCode.CONNECTION_REFUSED_BAD_USER_NAME_OR_PASSWORD; import static io.netty.handler.codec.mqtt.MqttConnectReturnCode.CONNECTION_REFUSED_NOT_AUTHORIZED; import static io.netty.handler.codec.mqtt.MqttMessageType.CONNACK; -import static io.netty.handler.codec.mqtt.MqttMessageType.PINGRESP; import static io.netty.handler.codec.mqtt.MqttMessageType.PUBACK; import static io.netty.handler.codec.mqtt.MqttMessageType.SUBACK; -import static io.netty.handler.codec.mqtt.MqttMessageType.UNSUBACK; import static io.netty.handler.codec.mqtt.MqttQoS.AT_LEAST_ONCE; import static io.netty.handler.codec.mqtt.MqttQoS.AT_MOST_ONCE; -import static io.netty.handler.codec.mqtt.MqttQoS.FAILURE; -import static org.thingsboard.server.common.msg.session.SessionMsgType.GET_ATTRIBUTES_REQUEST; -import static org.thingsboard.server.common.msg.session.SessionMsgType.POST_ATTRIBUTES_REQUEST; -import static org.thingsboard.server.common.msg.session.SessionMsgType.POST_TELEMETRY_REQUEST; -import static org.thingsboard.server.common.msg.session.SessionMsgType.SUBSCRIBE_ATTRIBUTES_REQUEST; -import static org.thingsboard.server.common.msg.session.SessionMsgType.SUBSCRIBE_RPC_COMMANDS_REQUEST; -import static org.thingsboard.server.common.msg.session.SessionMsgType.TO_DEVICE_RPC_RESPONSE; -import static org.thingsboard.server.common.msg.session.SessionMsgType.TO_SERVER_RPC_REQUEST; -import static org.thingsboard.server.common.msg.session.SessionMsgType.UNSUBSCRIBE_ATTRIBUTES_REQUEST; -import static org.thingsboard.server.common.msg.session.SessionMsgType.UNSUBSCRIBE_RPC_COMMANDS_REQUEST; -import static org.thingsboard.server.transport.mqtt.MqttTopics.BASE_GATEWAY_API_TOPIC; -import static org.thingsboard.server.transport.mqtt.MqttTopics.DEVICE_ATTRIBUTES_REQUEST_TOPIC_PREFIX; -import static org.thingsboard.server.transport.mqtt.MqttTopics.DEVICE_ATTRIBUTES_RESPONSES_TOPIC; -import static org.thingsboard.server.transport.mqtt.MqttTopics.DEVICE_ATTRIBUTES_TOPIC; -import static org.thingsboard.server.transport.mqtt.MqttTopics.DEVICE_RPC_REQUESTS_SUB_TOPIC; -import static org.thingsboard.server.transport.mqtt.MqttTopics.DEVICE_RPC_REQUESTS_TOPIC; -import static org.thingsboard.server.transport.mqtt.MqttTopics.DEVICE_RPC_RESPONSE_SUB_TOPIC; -import static org.thingsboard.server.transport.mqtt.MqttTopics.DEVICE_RPC_RESPONSE_TOPIC; -import static org.thingsboard.server.transport.mqtt.MqttTopics.DEVICE_TELEMETRY_TOPIC; -import static org.thingsboard.server.transport.mqtt.MqttTopics.GATEWAY_ATTRIBUTES_REQUEST_TOPIC; -import static org.thingsboard.server.transport.mqtt.MqttTopics.GATEWAY_ATTRIBUTES_TOPIC; -import static org.thingsboard.server.transport.mqtt.MqttTopics.GATEWAY_CONNECT_TOPIC; -import static org.thingsboard.server.transport.mqtt.MqttTopics.GATEWAY_DISCONNECT_TOPIC; -import static org.thingsboard.server.transport.mqtt.MqttTopics.GATEWAY_RPC_TOPIC; -import static org.thingsboard.server.transport.mqtt.MqttTopics.GATEWAY_TELEMETRY_TOPIC; /** * @author Andrew Shvayka @@ -389,7 +352,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement } else { ctx.writeAndFlush(createMqttConnAckMsg(CONNECTION_ACCEPTED)); deviceSessionCtx.setDeviceInfo(msg.getDeviceInfo()); - transportService.process(getSessionEventMsg(SessionEvent.OPEN), null); + transportService.process(deviceSessionCtx, getSessionEventMsg(SessionEvent.OPEN), null); checkGatewaySession(); } } @@ -418,7 +381,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement } else { ctx.writeAndFlush(createMqttConnAckMsg(CONNECTION_ACCEPTED)); deviceSessionCtx.setDeviceInfo(msg.getDeviceInfo()); - transportService.process(getSessionEventMsg(SessionEvent.OPEN), null); + transportService.process(deviceSessionCtx, getSessionEventMsg(SessionEvent.OPEN), null); checkGatewaySession(); } } @@ -452,7 +415,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement private void processDisconnect(ChannelHandlerContext ctx) { ctx.close(); if (deviceSessionCtx.isConnected()) { - transportService.process(getSessionEventMsg(SessionEvent.CLOSED), null); + transportService.process(deviceSessionCtx, getSessionEventMsg(SessionEvent.CLOSED), null); if (gatewaySessionCtx != null) { gatewaySessionCtx.onGatewayDisconnect(); } @@ -534,7 +497,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement @Override public void operationComplete(Future future) throws Exception { if (deviceSessionCtx.isConnected()) { - transportService.process(getSessionEventMsg(SessionEvent.CLOSED), null); + transportService.process(deviceSessionCtx, getSessionEventMsg(SessionEvent.CLOSED), null); } } } diff --git a/transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/MqttTransportService.java b/transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/MqttTransportService.java index 4d945de394..76c1c1b8c1 100644 --- a/transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/MqttTransportService.java +++ b/transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/MqttTransportService.java @@ -1,12 +1,12 @@ /** * 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 - * + *

+ * 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. @@ -15,21 +15,41 @@ */ package org.thingsboard.server.mqtt.service; +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.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.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.AsyncCallbackTemplate; import org.thingsboard.server.kafka.TBKafkaConsumerTemplate; import org.thingsboard.server.kafka.TBKafkaProducerTemplate; import org.thingsboard.server.kafka.TbKafkaRequestTemplate; -import org.thingsboard.server.gen.transport.TransportProtos.*; import org.thingsboard.server.kafka.TbKafkaSettings; import org.thingsboard.server.transport.mqtt.MqttTransportContext; 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; @@ -37,10 +57,17 @@ import java.util.concurrent.Executors; * Created by ashvayka on 05.10.18. */ @Service +@Slf4j public class MqttTransportService implements TransportService { @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}") @@ -54,6 +81,8 @@ public class MqttTransportService implements TransportService { @Value("${kafka.transport_api.response_auto_commit_interval}") private int autoCommitInterval; + private ConcurrentMap sessions = new ConcurrentHashMap<>(); + @Autowired private TbKafkaSettings kafkaSettings; //We use this to get the node id. We should replace this with a component that provides the node id. @@ -63,6 +92,12 @@ public class MqttTransportService implements TransportService { private ExecutorService transportCallbackExecutor; private TbKafkaRequestTemplate transportApiTemplate; + private TBKafkaProducerTemplate ruleEngineProducer; + private TBKafkaConsumerTemplate mainConsumer; + + private ExecutorService mainConsumerExecutor = Executors.newSingleThreadExecutor(); + + private volatile boolean stopped = false; @PostConstruct public void init() { @@ -77,7 +112,7 @@ public class MqttTransportService implements TransportService { responseBuilder.settings(kafkaSettings); responseBuilder.topic(transportApiResponsesTopic + "." + transportContext.getNodeId()); responseBuilder.clientId(transportContext.getNodeId()); - responseBuilder.groupId("transport-node"); + responseBuilder.groupId(null); responseBuilder.autoCommit(true); responseBuilder.autoCommitIntervalMs(autoCommitInterval); responseBuilder.decoder(new TransportApiResponseDecoder()); @@ -91,16 +126,79 @@ public class MqttTransportService implements TransportService { builder.pollInterval(responsePollDuration); transportApiTemplate = builder.build(); transportApiTemplate.init(); + + TBKafkaProducerTemplate.TBKafkaProducerTemplateBuilder ruleEngineProducerBuilder = TBKafkaProducerTemplate.builder(); + ruleEngineProducerBuilder.settings(kafkaSettings); + ruleEngineProducerBuilder.defaultTopic(ruleEngineTopic); + ruleEngineProducerBuilder.encoder(new ToRuleEngineMsgEncoder()); + ruleEngineProducer = ruleEngineProducerBuilder.build(); + ruleEngineProducer.init(); + + TBKafkaConsumerTemplate.TBKafkaConsumerTemplateBuilder mainConsumerBuilder = TBKafkaConsumerTemplate.builder(); + mainConsumerBuilder.settings(kafkaSettings); + mainConsumerBuilder.topic(notificationsTopic + "." + transportContext.getNodeId()); + mainConsumerBuilder.clientId(transportContext.getNodeId()); + mainConsumerBuilder.groupId(null); + mainConsumerBuilder.autoCommit(true); + mainConsumerBuilder.autoCommitIntervalMs(notificationsAutoCommitInterval); + mainConsumerBuilder.decoder(new ToTransportMsgResponseDecoder()); + mainConsumer = mainConsumerBuilder.build(); + mainConsumer.subscribe(); + + mainConsumerExecutor.execute(() -> { + while (!stopped) { + try { + ConsumerRecords records = mainConsumer.poll(Duration.ofMillis(notificationsPollDuration)); + records.forEach(record -> { + try { + ToTransportMsg toTransportMsg = mainConsumer.decode(record); + if (toTransportMsg.hasToDeviceSessionMsg()) { + TransportProtos.DeviceActorToTransportMsg toSessionMsg = toTransportMsg.getToDeviceSessionMsg(); + UUID sessionId = new UUID(toSessionMsg.getSessionIdMSB(), toSessionMsg.getSessionIdLSB()); + SessionMsgListener listener = sessions.get(sessionId); + if (listener != null) { + transportCallbackExecutor.submit(() -> { + if (toSessionMsg.hasGetAttributesResponse()) { + listener.onGetAttributesResponse(toSessionMsg.getGetAttributesResponse()); + } + }); + } else { + //TODO: should we notify the device actor about missed session? + log.debug("[{}] Missing session.", sessionId); + } + + } + } 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() { + stopped = true; if (transportApiTemplate != null) { transportApiTemplate.stop(); } if (transportCallbackExecutor != null) { transportCallbackExecutor.shutdownNow(); } + if (mainConsumer != null) { + mainConsumer.unsubscribe(); + } + if (mainConsumerExecutor != null) { + mainConsumerExecutor.shutdownNow(); + } } @Override @@ -118,17 +216,69 @@ public class MqttTransportService implements TransportService { } @Override - public void process(SessionEventMsg msg, TransportServiceCallback callback) { + public void process(SessionInfoProto sessionInfo, SessionEventMsg msg, TransportServiceCallback callback) { + ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg( + TransportProtos.TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) + .setSessionEvent(msg).build() + ).build(); + send(sessionInfo, toRuleEngineMsg, callback); + } + @Override + public void process(SessionInfoProto sessionInfo, PostTelemetryMsg msg, TransportServiceCallback callback) { + ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg( + TransportProtos.TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) + .setPostTelemetry(msg).build() + ).build(); + send(sessionInfo, toRuleEngineMsg, callback); } @Override - public void process(PostTelemetryMsg msg, TransportServiceCallback callback) { + public void process(SessionInfoProto sessionInfo, PostAttributeMsg msg, TransportServiceCallback callback) { + ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg( + TransportProtos.TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) + .setPostAttributes(msg).build() + ).build(); + send(sessionInfo, toRuleEngineMsg, callback); + } + @Override + public void registerSession(SessionInfoProto sessionInfo, SessionMsgListener listener) { + sessions.putIfAbsent(toId(sessionInfo), listener); + //TODO: monitor sessions periodically: PING REQ/RESP, etc. } @Override - public void process(PostAttributeMsg msg, TransportServiceCallback callback) { + public void deregisterSession(SessionInfoProto sessionInfo) { + sessions.remove(toId(sessionInfo)); + } + + private UUID toId(SessionInfoProto sessionInfo) { + return new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB()); + } + + private String getRoutingKey(SessionInfoProto sessionInfo) { + return new UUID(sessionInfo.getDeviceIdMSB(), sessionInfo.getDeviceIdLSB()).toString(); + } + + private static class TransportCallbackAdaptor implements Callback { + private final TransportServiceCallback callback; + + TransportCallbackAdaptor(TransportServiceCallback callback) { + this.callback = callback; + } + + @Override + public void onCompletion(RecordMetadata metadata, Exception exception) { + if (exception == null) { + callback.onSuccess(null); + } else { + callback.onError(exception); + } + } + } + private void send(SessionInfoProto sessionInfo, ToRuleEngineMsg toRuleEngineMsg, TransportServiceCallback callback) { + ruleEngineProducer.send(getRoutingKey(sessionInfo), toRuleEngineMsg, new TransportCallbackAdaptor(callback)); } } diff --git a/transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/ToRuleEngineMsgEncoder.java b/transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/ToRuleEngineMsgEncoder.java new file mode 100644 index 0000000000..ee929ceb6f --- /dev/null +++ b/transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/ToRuleEngineMsgEncoder.java @@ -0,0 +1,29 @@ +/** + * 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.service; + +import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; +import org.thingsboard.server.kafka.TbKafkaEncoder; + +/** + * Created by ashvayka on 05.10.18. + */ +public class ToRuleEngineMsgEncoder implements TbKafkaEncoder { + @Override + public byte[] encode(ToRuleEngineMsg value) { + return value.toByteArray(); + } +} diff --git a/transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/ToTransportMsgResponseDecoder.java b/transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/ToTransportMsgResponseDecoder.java new file mode 100644 index 0000000000..b8d42049de --- /dev/null +++ b/transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/ToTransportMsgResponseDecoder.java @@ -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. + */ +package org.thingsboard.server.mqtt.service; + +import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg; +import org.thingsboard.server.kafka.TbKafkaDecoder; + +import java.io.IOException; + +/** + * Created by ashvayka on 05.10.18. + */ +public class ToTransportMsgResponseDecoder implements TbKafkaDecoder { + @Override + public ToTransportMsg decode(byte[] data) throws IOException { + return ToTransportMsg.parseFrom(data); + } +} diff --git a/transport/mqtt-transport/src/main/resources/tb-mqtt-transport.yml b/transport/mqtt-transport/src/main/resources/tb-mqtt-transport.yml index 707fb4bcc1..735eee03ed 100644 --- a/transport/mqtt-transport/src/main/resources/tb-mqtt-transport.yml +++ b/transport/mqtt-transport/src/main/resources/tb-mqtt-transport.yml @@ -82,3 +82,7 @@ kafka: 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}" From 2637babfe3e06ee31013c08c7b83b6475d16dbce Mon Sep 17 00:00:00 2001 From: Andrew Shvayka Date: Wed, 10 Oct 2018 13:06:07 +0300 Subject: [PATCH 08/26] Mqtt transport implementation: POST telemetry, attributes --- .../server/actors/ActorSystemContext.java | 8 - .../server/actors/app/AppActor.java | 8 - .../server/actors/device/DeviceActor.java | 7 - .../device/DeviceActorMessageProcessor.java | 306 +++++++++--------- .../device/RuleEngineQueuePutAckMsg.java | 36 --- .../server/actors/device/SessionInfo.java | 8 +- .../RuleChainActorMessageProcessor.java | 40 +-- .../actors/service/DefaultActorService.java | 14 - .../actors/shared/ComponentMsgProcessor.java | 16 - .../RemoteRuleEngineTransportService.java | 17 + .../transport/RuleEngineTransportService.java | 9 +- .../transport/ToTransportMsgEncoder.java | 8 +- .../msg/TransportToDeviceActorMsgWrapper.java | 16 +- .../server/common/msg/MsgType.java | 3 - ...sicActorSystemToDeviceSessionActorMsg.java | 52 --- .../timeout/DeviceActorQueueTimeoutMsg.java | 36 --- .../server/kafka/TBKafkaConsumerTemplate.java | 8 +- .../server/kafka/TBKafkaProducerTemplate.java | 12 +- .../server/kafka/TbKafkaRequestTemplate.java | 2 +- .../server/kafka/TbKafkaResponseTemplate.java | 8 +- .../common/transport/SessionMsgListener.java | 8 +- .../common/transport/SessionMsgProcessor.java | 2 - .../common/transport/TransportAdaptor.java | 1 + .../common/transport/TransportService.java | 11 +- .../transport/adaptor/JsonConverter.java | 180 ++++++++++- .../session/DeviceAwareSessionContext.java | 8 +- .../transport/src/main/proto/transport.proto | 36 ++- .../server/transport/coap/CoapServerTest.java | 48 +-- .../transport/mqtt/MqttTransportHandler.java | 302 ++++++++++------- .../mqtt/adaptors/JsonMqttAdaptor.java | 61 ++++ .../mqtt/adaptors/MqttTransportAdaptor.java | 13 + .../mqtt/session/DeviceSessionCtx.java | 4 +- .../mqtt/service/MqttTransportService.java | 33 +- .../mqtt/service/ToRuleEngineMsgEncoder.java | 8 +- 34 files changed, 738 insertions(+), 591 deletions(-) delete mode 100644 application/src/main/java/org/thingsboard/server/actors/device/RuleEngineQueuePutAckMsg.java delete mode 100644 common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicActorSystemToDeviceSessionActorMsg.java delete mode 100644 common/message/src/main/java/org/thingsboard/server/common/msg/timeout/DeviceActorQueueTimeoutMsg.java 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 8ff61aca46..1081ab2aec 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java +++ b/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java @@ -198,10 +198,6 @@ public class ActorSystemContext { @Getter private MailService mailService; - @Autowired - @Getter - private MsgQueueService msgQueueService; - @Autowired @Getter private DeviceStateService deviceStateService; @@ -267,10 +263,6 @@ public class ActorSystemContext { @Setter private ActorRef appActor; - @Getter - @Setter - private ActorRef sessionManagerActor; - @Getter @Setter private ActorRef statsActor; 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 14ca58610d..f4373db9be 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,7 +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.plugin.ComponentLifecycleMsg; import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg; import org.thingsboard.server.dao.model.ModelConstants; @@ -113,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 address = systemContext.getRoutingService().resolveById(msg.getEntityId()); if (address.isPresent()) { 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 7fabe493dd..bd2a0f4486 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 @@ -27,7 +27,6 @@ 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.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; @@ -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 1313f60be5..0822766b4b 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 @@ -1,12 +1,12 @@ /** * 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 - *

+ * + * 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. @@ -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; @@ -46,29 +45,15 @@ 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.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.service.rpc.FromDeviceRpcResponse; @@ -88,9 +73,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; import org.thingsboard.server.gen.transport.TransportProtos.*; @@ -192,19 +175,6 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso } } - 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 server) { if (!toDeviceRpcPendingMap.isEmpty()) { logger.debug("[{}] Pushing {} pending RPC messages to new async session [{}]", deviceId, toDeviceRpcPendingMap.size(), sessionId); @@ -239,8 +209,8 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso body.getMethod(), body.getParams() ); - ActorSystemToDeviceSessionActorMsg response = new BasicActorSystemToDeviceSessionActorMsg(rpcRequest, sessionId); - sendMsgToSessionActor(response, server); +// ActorSystemToDeviceSessionActorMsg response = new BasicActorSystemToDeviceSessionActorMsg(rpcRequest, sessionId); +// sendMsgToSessionActor(response, server); }; } @@ -292,57 +262,25 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso private void handleGetAttributesRequest(ActorContext context, SessionInfoProto sessionInfo, GetAttributeRequestMsg request) { ListenableFuture> clientAttributesFuture = getAttributeKvEntries(deviceId, DataConstants.CLIENT_SCOPE, toOptionalSet(request.getClientAttributeNamesList())); ListenableFuture> sharedAttributesFuture = getAttributeKvEntries(deviceId, DataConstants.SHARED_SCOPE, toOptionalSet(request.getSharedAttributeNamesList())); - - Futures.addCallback(Futures.allAsList(Arrays.asList(clientAttributesFuture, sharedAttributesFuture)), new FutureCallback>>() { - @Override - public void onSuccess(@Nullable List> result) { - systemContext.getRuleEngineTransportService().process(); - BasicGetAttributesResponse response = BasicGetAttributesResponse.onSuccess(request.getMsgType(), - request.getRequestId(), BasicAttributeKVMsg.from(result.get(0), result.get(1))); - sendMsgToSessionActor(new BasicActorSystemToDeviceSessionActorMsg(response, src.getSessionId()), src.getServerAddress()); - } - - @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); - } - } - }); - } - - private Optional> toOptionalSet(List strings) { - if (strings == null || strings.isEmpty()) { - return Optional.empty(); - } else { - return Optional.of(new HashSet<>(strings)); - } - } - - private void handleGetAttributesRequest(DeviceToDeviceActorMsg src) { - GetAttributesRequest request = (GetAttributesRequest) src.getPayload(); - ListenableFuture> clientAttributesFuture = getAttributeKvEntries(deviceId, DataConstants.CLIENT_SCOPE, request.getClientAttributeNames()); - ListenableFuture> sharedAttributesFuture = getAttributeKvEntries(deviceId, DataConstants.SHARED_SCOPE, request.getSharedAttributeNames()); - + UUID sessionId = new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB()); + int requestId = request.getRequestId(); Futures.addCallback(Futures.allAsList(Arrays.asList(clientAttributesFuture, sharedAttributesFuture)), new FutureCallback>>() { @Override public void onSuccess(@Nullable List> 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, sessionId, 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, sessionId, sessionInfo); } }); } @@ -376,36 +314,36 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso } } - private void handleClientSideRPCRequest(ActorContext context, DeviceToDeviceActorMsg src) { - ToServerRpcRequestMsg request = (ToServerRpcRequestMsg) src.getPayload(); - - JsonObject json = new JsonObject(); - json.addProperty("method", request.getMethod()); - 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); - - scheduleMsgWithDelay(context, new DeviceActorClientSideRpcTimeoutMsg(request.getRequestId(), systemContext.getClientSideRpcTimeout()), systemContext.getClientSideRpcTimeout()); - toServerRpcPendingMap.put(request.getRequestId(), new ToServerRpcRequestMetadata(src.getSessionId(), src.getSessionType(), src.getServerAddress())); - } +// private void handleClientSideRPCRequest(ActorContext context, DeviceToDeviceActorMsg src) { +// ToServerRpcRequestMsg request = (ToServerRpcRequestMsg) src.getPayload(); +// +// JsonObject json = new JsonObject(); +// json.addProperty("method", request.getMethod()); +// 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); +// +// scheduleMsgWithDelay(context, new DeviceActorClientSideRpcTimeoutMsg(request.getRequestId(), systemContext.getClientSideRpcTimeout()), systemContext.getClientSideRpcTimeout()); +// toServerRpcPendingMap.put(request.getRequestId(), new ToServerRpcRequestMetadata(src.getSessionId(), src.getSessionType(), src.getServerAddress())); +// } public 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()); +// sendMsgToSessionActor(new BasicActorSystemToDeviceSessionActorMsg(toDeviceMsg, data.getSessionId()), data.getServer()); } } void processToServerRPCResponse(ActorContext context, ToServerRpcResponseActorMsg msg) { ToServerRpcRequestMetadata data = toServerRpcPendingMap.remove(msg.getMsg().getRequestId()); if (data != null) { - sendMsgToSessionActor(new BasicActorSystemToDeviceSessionActorMsg(msg.getMsg(), data.getSessionId()), data.getServer()); +// sendMsgToSessionActor(new BasicActorSystemToDeviceSessionActorMsg(msg.getMsg(), data.getSessionId()), data.getServer()); } } @@ -433,68 +371,68 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso } if (notification != null) { ToDeviceMsg finalNotification = notification; - attributeSubscriptions.entrySet().forEach(sub -> { - ActorSystemToDeviceSessionActorMsg response = new BasicActorSystemToDeviceSessionActorMsg(finalNotification, sub.getKey()); - sendMsgToSessionActor(response, sub.getValue().getServer()); - }); +// attributeSubscriptions.entrySet().forEach(sub -> { +// ActorSystemToDeviceSessionActorMsg response = new BasicActorSystemToDeviceSessionActorMsg(finalNotification, sub.getKey()); +// sendMsgToSessionActor(response, sub.getValue().getServer()); +// }); } } else { logger.debug("[{}] No registered attributes subscriptions to process!", deviceId); } } - 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, 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()); +// } +// } +// } void processClusterEventMsg(ClusterEventMsg msg) { - if (!msg.isAdded()) { - logger.debug("[{}] Clearing attributes/rpc subscription for server [{}]", deviceId, msg.getServerAddress()); - Predicate> 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> 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) { - 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); - rpcSubscriptions.remove(sessionId); - } - } +// 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) { +// 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); +// rpcSubscriptions.remove(sessionId); +// } +// } private void processSessionStateMsgs(SessionInfoProto sessionInfo, SessionEventMsg msg) { UUID sessionId = new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB()); @@ -506,15 +444,11 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso 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(); } - } - FromDeviceMsg inMsg = msg.getPayload(); - if (inMsg instanceof SessionOpenMsg) { - logger.debug("[{}] Processing new session [{}]", deviceId, sessionId); - } 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); @@ -532,7 +466,7 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso systemContext.getRpcService().tell(systemContext.getEncodingService() .convertToProtoDataMessage(sessionAddress.get(), response)); } else { - systemContext.getSessionManagerActor().tell(response, ActorRef.noSender()); +// systemContext.getSessionManagerActor().tell(response, ActorRef.noSender()); } } @@ -578,4 +512,62 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso } return json; } + + private Optional> toOptionalSet(List strings) { + if (strings == null || strings.isEmpty()) { + return Optional.empty(); + } else { + return Optional.of(new HashSet<>(strings)); + } + } + + private void sendToTransport(GetAttributeResponseMsg responseMsg, UUID sessionId, SessionInfoProto sessionInfo) { + DeviceActorToTransportMsg msg = DeviceActorToTransportMsg.newBuilder() + .setSessionIdMSB(sessionId.getMostSignificantBits()) + .setSessionIdLSB(sessionId.getLeastSignificantBits()) + .setGetAttributesResponse(responseMsg).build(); + systemContext.getRuleEngineTransportService().process(sessionInfo.getNodeId(), msg); + } + + private List toTsKvProtos(@Nullable List result) { + List 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/RuleEngineQueuePutAckMsg.java b/application/src/main/java/org/thingsboard/server/actors/device/RuleEngineQueuePutAckMsg.java deleted file mode 100644 index 0b6fe54c4a..0000000000 --- a/application/src/main/java/org/thingsboard/server/actors/device/RuleEngineQueuePutAckMsg.java +++ /dev/null @@ -1,36 +0,0 @@ -/** - * 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.actors.device; - -import lombok.Data; -import org.thingsboard.server.common.msg.MsgType; -import org.thingsboard.server.common.msg.TbActorMsg; - -import java.util.UUID; - -/** - * Created by ashvayka on 15.03.18. - */ -@Data -public final class RuleEngineQueuePutAckMsg implements TbActorMsg { - - private final UUID id; - - @Override - public MsgType getMsgType() { - return MsgType.RULE_ENGINE_QUEUE_PUT_ACK_MSG; - } -} 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 9faaade208..32cb60da5f 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 @@ -1,12 +1,12 @@ /** * 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 - *

+ * + * 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. 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 fe02335223..3da90d1e74 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 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 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 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 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 1e9e23bae3..b70d1ed480 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; @@ -90,8 +89,6 @@ public class DefaultActorService implements ActorService { private ActorRef appActor; - private ActorRef sessionManagerActor; - private ActorRef rpcManagerActor; @PostConstruct @@ -104,10 +101,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"); @@ -134,12 +127,6 @@ public class DefaultActorService implements ActorService { appActor.tell(msg, ActorRef.noSender()); } - @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); @@ -194,7 +181,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 1cf83397fc..3441abc8a6 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 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; @@ -86,18 +84,4 @@ public abstract class ComponentMsgProcessor extends Abstract } } - protected void putToQueue(final TbMsg tbMsg, final Consumer onSuccess) { - EntityId entityId = tbMsg.getRuleNodeId() != null ? tbMsg.getRuleNodeId() : tbMsg.getRuleChainId(); - Futures.addCallback(queue.put(this.tenantId, tbMsg, entityId.getId(), tbMsg.getClusterPartition()), new FutureCallback() { - @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/service/transport/RemoteRuleEngineTransportService.java b/application/src/main/java/org/thingsboard/server/service/transport/RemoteRuleEngineTransportService.java index 6b827921cd..aa36b9b2cb 100644 --- a/application/src/main/java/org/thingsboard/server/service/transport/RemoteRuleEngineTransportService.java +++ b/application/src/main/java/org/thingsboard/server/service/transport/RemoteRuleEngineTransportService.java @@ -1,3 +1,18 @@ +/** + * 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; @@ -30,6 +45,7 @@ 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; @@ -136,6 +152,7 @@ public class RemoteRuleEngineTransportService implements RuleEngineTransportServ @Override public void process(String nodeId, DeviceActorToTransportMsg msg, Runnable onSuccess, Consumer onFailure) { notificationsProducer.send(notificationsTopic + "." + nodeId, + new UUID(msg.getSessionIdMSB(), msg.getSessionIdLSB()).toString(), ToTransportMsg.newBuilder().setToDeviceSessionMsg(msg).build() , new QueueCallbackAdaptor(onSuccess, onFailure)); } diff --git a/application/src/main/java/org/thingsboard/server/service/transport/RuleEngineTransportService.java b/application/src/main/java/org/thingsboard/server/service/transport/RuleEngineTransportService.java index e6b3dd3369..8ca484ffcf 100644 --- a/application/src/main/java/org/thingsboard/server/service/transport/RuleEngineTransportService.java +++ b/application/src/main/java/org/thingsboard/server/service/transport/RuleEngineTransportService.java @@ -1,12 +1,12 @@ /** * 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 - *

+ * + * 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. @@ -16,7 +16,6 @@ package org.thingsboard.server.service.transport; import org.thingsboard.server.gen.transport.TransportProtos.DeviceActorToTransportMsg; -import org.thingsboard.server.gen.transport.TransportProtos.; import java.util.function.Consumer; diff --git a/application/src/main/java/org/thingsboard/server/service/transport/ToTransportMsgEncoder.java b/application/src/main/java/org/thingsboard/server/service/transport/ToTransportMsgEncoder.java index 5f3c026249..bb0f5b620e 100644 --- a/application/src/main/java/org/thingsboard/server/service/transport/ToTransportMsgEncoder.java +++ b/application/src/main/java/org/thingsboard/server/service/transport/ToTransportMsgEncoder.java @@ -1,12 +1,12 @@ /** * 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 - *

+ * + * 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. diff --git a/application/src/main/java/org/thingsboard/server/service/transport/msg/TransportToDeviceActorMsgWrapper.java b/application/src/main/java/org/thingsboard/server/service/transport/msg/TransportToDeviceActorMsgWrapper.java index 3ee185893a..570082d542 100644 --- a/application/src/main/java/org/thingsboard/server/service/transport/msg/TransportToDeviceActorMsgWrapper.java +++ b/application/src/main/java/org/thingsboard/server/service/transport/msg/TransportToDeviceActorMsgWrapper.java @@ -1,3 +1,18 @@ +/** + * 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.msg; import lombok.Data; @@ -7,7 +22,6 @@ import org.thingsboard.server.common.msg.MsgType; import org.thingsboard.server.common.msg.TbActorMsg; import org.thingsboard.server.common.msg.aware.DeviceAwareMsg; import org.thingsboard.server.common.msg.aware.TenantAwareMsg; -import org.thingsboard.server.common.msg.cluster.ServerAddress; import org.thingsboard.server.gen.transport.TransportProtos.TransportToDeviceActorMsg; import java.io.Serializable; 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 dfd8f98ec3..24758b5b36 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 @@ -91,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 */ @@ -101,7 +99,6 @@ 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, diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicActorSystemToDeviceSessionActorMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicActorSystemToDeviceSessionActorMsg.java deleted file mode 100644 index cc298915c8..0000000000 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicActorSystemToDeviceSessionActorMsg.java +++ /dev/null @@ -1,52 +0,0 @@ -/** - * 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.msg.core; - -import org.thingsboard.server.common.data.id.SessionId; -import org.thingsboard.server.common.msg.MsgType; -import org.thingsboard.server.common.msg.session.ToDeviceMsg; - -public class BasicActorSystemToDeviceSessionActorMsg implements ActorSystemToDeviceSessionActorMsg { - - private final ToDeviceMsg msg; - private final SessionId sessionId; - - public BasicActorSystemToDeviceSessionActorMsg(ToDeviceMsg msg, SessionId sessionId) { - super(); - this.msg = msg; - this.sessionId = sessionId; - } - - @Override - public SessionId getSessionId() { - return sessionId; - } - - @Override - public ToDeviceMsg getMsg() { - return msg; - } - - @Override - public String toString() { - return "BasicActorSystemToDeviceSessionActorMsg [msg=" + msg + ", sessionId=" + sessionId + "]"; - } - - @Override - public MsgType getMsgType() { - return MsgType.ACTOR_SYSTEM_TO_DEVICE_SESSION_ACTOR_MSG; - } -} diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/timeout/DeviceActorQueueTimeoutMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/timeout/DeviceActorQueueTimeoutMsg.java deleted file mode 100644 index 0277983d8b..0000000000 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/timeout/DeviceActorQueueTimeoutMsg.java +++ /dev/null @@ -1,36 +0,0 @@ -/** - * 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.msg.timeout; - -import org.thingsboard.server.common.msg.MsgType; -import org.thingsboard.server.common.msg.timeout.TimeoutMsg; - -import java.util.UUID; - -/** - * @author Andrew Shvayka - */ -public final class DeviceActorQueueTimeoutMsg extends TimeoutMsg { - - public DeviceActorQueueTimeoutMsg(UUID id, long timeout) { - super(id, timeout); - } - - @Override - public MsgType getMsgType() { - return MsgType.DEVICE_ACTOR_QUEUE_TIMEOUT_MSG; - } -} 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 90f15a4892..86be3a3c38 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 @@ -1,12 +1,12 @@ /** * 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 - *

+ * + * 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. 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 8f4c095ed7..610a49007d 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 @@ -1,12 +1,12 @@ /** * 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 - *

+ * + * 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. @@ -106,6 +106,10 @@ public class TBKafkaProducerTemplate { return send(topic, key, value, null, headers, callback); } + public Future send(String topic, String key, T value, Callback callback) { + return send(topic, key, value, null, null, callback); + } + public Future send(String topic, String key, T value, Long timestamp, Iterable

headers, Callback callback) { byte[] data = encoder.encode(value); ProducerRecord record; 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 ba08bf8946..77ad0331ab 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 @@ -160,7 +160,7 @@ public class TbKafkaRequestTemplate extends AbstractTbKafkaTe SettableFuture 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; } 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 index 6f9e8a791b..4c23ac2211 100644 --- a/common/queue/src/main/java/org/thingsboard/server/kafka/TbKafkaResponseTemplate.java +++ b/common/queue/src/main/java/org/thingsboard/server/kafka/TbKafkaResponseTemplate.java @@ -1,12 +1,12 @@ /** * 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 - *

+ * + * 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. diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/SessionMsgListener.java b/common/transport/src/main/java/org/thingsboard/server/common/transport/SessionMsgListener.java index d0e6d18de2..44fcd2a39d 100644 --- a/common/transport/src/main/java/org/thingsboard/server/common/transport/SessionMsgListener.java +++ b/common/transport/src/main/java/org/thingsboard/server/common/transport/SessionMsgListener.java @@ -1,12 +1,12 @@ /** * 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 - *

+ * + * 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. diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/SessionMsgProcessor.java b/common/transport/src/main/java/org/thingsboard/server/common/transport/SessionMsgProcessor.java index 4cff643a5a..80b2690b21 100644 --- a/common/transport/src/main/java/org/thingsboard/server/common/transport/SessionMsgProcessor.java +++ b/common/transport/src/main/java/org/thingsboard/server/common/transport/SessionMsgProcessor.java @@ -20,8 +20,6 @@ import org.thingsboard.server.common.msg.aware.SessionAwareMsg; public interface SessionMsgProcessor { - void process(SessionAwareMsg msg); - void onDeviceAdded(Device device); } diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/TransportAdaptor.java b/common/transport/src/main/java/org/thingsboard/server/common/transport/TransportAdaptor.java index 080f874efb..86067d536e 100644 --- a/common/transport/src/main/java/org/thingsboard/server/common/transport/TransportAdaptor.java +++ b/common/transport/src/main/java/org/thingsboard/server/common/transport/TransportAdaptor.java @@ -20,6 +20,7 @@ import org.thingsboard.server.common.msg.session.SessionMsgType; import org.thingsboard.server.common.msg.session.SessionActorToAdaptorMsg; import org.thingsboard.server.common.msg.session.SessionContext; import org.thingsboard.server.common.transport.adaptor.AdaptorException; +import org.thingsboard.server.gen.transport.TransportProtos; import java.util.Optional; diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/TransportService.java b/common/transport/src/main/java/org/thingsboard/server/common/transport/TransportService.java index dc2e3062f2..c3817a9ffa 100644 --- a/common/transport/src/main/java/org/thingsboard/server/common/transport/TransportService.java +++ b/common/transport/src/main/java/org/thingsboard/server/common/transport/TransportService.java @@ -1,12 +1,12 @@ /** * 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 - *

+ * + * 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. @@ -22,6 +22,7 @@ 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. @@ -40,6 +41,8 @@ public interface TransportService { void process(SessionInfoProto sessionInfo, PostAttributeMsg msg, TransportServiceCallback callback); + void process(SessionInfoProto sessionInfo, GetAttributeRequestMsg msg, TransportServiceCallback callback); + void registerSession(SessionInfoProto sessionInfo, SessionMsgListener listener); void deregisterSession(SessionInfoProto sessionInfo); diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java b/common/transport/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java index e7c173485c..6bb8eff1be 100644 --- a/common/transport/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java +++ b/common/transport/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java @@ -15,18 +15,38 @@ */ 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.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.core.AttributesUpdateRequest; +import org.thingsboard.server.common.msg.core.BasicAttributesUpdateRequest; +import org.thingsboard.server.common.msg.core.BasicRequest; +import org.thingsboard.server.common.msg.core.BasicTelemetryUploadRequest; +import org.thingsboard.server.common.msg.core.TelemetryUploadRequest; +import org.thingsboard.server.common.msg.core.ToDeviceRpcRequestMsg; +import org.thingsboard.server.common.msg.core.ToServerRpcRequestMsg; +import org.thingsboard.server.common.msg.core.ToServerRpcResponseMsg; +import org.thingsboard.server.common.msg.kv.AttributesKVMsg; +import org.thingsboard.server.gen.transport.TransportProtos.*; + import java.util.ArrayList; import java.util.List; import java.util.Map.Entry; import java.util.function.Consumer; import java.util.stream.Collectors; -import com.google.gson.*; -import org.thingsboard.server.common.msg.core.*; - -import org.thingsboard.server.common.data.kv.*; -import org.thingsboard.server.common.msg.kv.AttributesKVMsg; - public class JsonConverter { private static final Gson GSON = new Gson(); @@ -44,6 +64,109 @@ public class JsonConverter { return convertToTelemetry(jsonObject, System.currentTimeMillis(), requestId); } + 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 keyValueList = parseProtoValues(jsonObject.getAsJsonObject()); + result.addAllKv(keyValueList); + return result.build(); + } else { + throw new JsonSyntaxException(CAN_T_PARSE_VALUE + jsonObject); + } + } + + + 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()); + } + + public 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()); + } + + public static List parseProtoValues(JsonObject valuesObject) { + List result = new ArrayList<>(); + for (Entry 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; + } + + private static void parseNumericProto(List result, Entry 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!"); + } + } + } + + private static TelemetryUploadRequest convertToTelemetry(JsonElement jsonObject, long systemTs, int requestId) throws JsonSyntaxException { BasicTelemetryUploadRequest request = new BasicTelemetryUploadRequest(requestId); if (jsonObject.isJsonObject()) { @@ -140,6 +263,26 @@ public class JsonConverter { } } + 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 JsonObject toJson(AttributesKVMsg payload, boolean asMap) { JsonObject result = new JsonObject(); if (asMap) { @@ -166,8 +309,29 @@ public class JsonConverter { } private static Consumer addToObject(JsonArray result) { - return key -> { - result.add(key.getAttributeKey()); + return key -> result.add(key.getAttributeKey()); + } + + private static Consumer 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); }; } diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/session/DeviceAwareSessionContext.java b/common/transport/src/main/java/org/thingsboard/server/common/transport/session/DeviceAwareSessionContext.java index 30689748c5..b46cfb81df 100644 --- a/common/transport/src/main/java/org/thingsboard/server/common/transport/session/DeviceAwareSessionContext.java +++ b/common/transport/src/main/java/org/thingsboard/server/common/transport/session/DeviceAwareSessionContext.java @@ -1,12 +1,12 @@ /** * 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 - *

+ * + * 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. diff --git a/common/transport/src/main/proto/transport.proto b/common/transport/src/main/proto/transport.proto index f3b3ae87a4..d32bac99a7 100644 --- a/common/transport/src/main/proto/transport.proto +++ b/common/transport/src/main/proto/transport.proto @@ -23,12 +23,13 @@ option java_outer_classname = "TransportProtos"; * Data Structures; */ message SessionInfoProto { - int64 sessionIdMSB = 1; - int64 sessionIdLSB = 2; - int64 tenantIdMSB = 3; - int64 tenantIdLSB = 4; - int64 deviceIdMSB = 5; - int64 deviceIdLSB = 6; + string nodeId = 1; + int64 sessionIdMSB = 2; + int64 sessionIdLSB = 3; + int64 tenantIdMSB = 4; + int64 tenantIdLSB = 5; + int64 deviceIdMSB = 6; + int64 deviceIdLSB = 7; } enum SessionEvent { @@ -57,6 +58,11 @@ message KeyValueProto { string string_v = 6; } +message TsKvProto { + int64 ts = 1; + KeyValueProto kv = 2; +} + message TsKvListProto { int64 ts = 1; repeated KeyValueProto kv = 2; @@ -76,9 +82,8 @@ message DeviceInfoProto { * Messages that use Data Structures; */ message SessionEventMsg { - string nodeId = 1; - SessionType sessionType = 2; - SessionEvent event = 3; + SessionType sessionType = 1; + SessionEvent event = 2; } message PostTelemetryMsg { @@ -90,14 +95,17 @@ message PostAttributeMsg { } message GetAttributeRequestMsg { - repeated string clientAttributeNames = 1; - repeated string sharedAttributeNames = 2; + int32 requestId = 1; + repeated string clientAttributeNames = 2; + repeated string sharedAttributeNames = 3; } message GetAttributeResponseMsg { - repeated TsKvListProto clientAttributeList = 1; - repeated TsKvListProto sharedAttributeList = 2; - repeated string deletedAttributeKeys = 3; + int32 requestId = 1; + repeated TsKvProto clientAttributeList = 2; + repeated TsKvProto sharedAttributeList = 3; + repeated string deletedAttributeKeys = 4; + string error = 5; } message ValidateDeviceTokenRequestMsg { diff --git a/transport/coap/src/test/java/org/thingsboard/server/transport/coap/CoapServerTest.java b/transport/coap/src/test/java/org/thingsboard/server/transport/coap/CoapServerTest.java index e2fd7cd6c4..ec69cec673 100644 --- a/transport/coap/src/test/java/org/thingsboard/server/transport/coap/CoapServerTest.java +++ b/transport/coap/src/test/java/org/thingsboard/server/transport/coap/CoapServerTest.java @@ -106,30 +106,30 @@ public class CoapServerTest { public static SessionMsgProcessor sessionMsgProcessor() { return new SessionMsgProcessor() { - @Override - public void process(SessionAwareMsg toActorMsg) { - if (toActorMsg instanceof TransportToDeviceSessionActorMsg) { - AdaptorToSessionActorMsg sessionMsg = ((TransportToDeviceSessionActorMsg) toActorMsg).getSessionMsg(); - try { - FromDeviceMsg deviceMsg = sessionMsg.getMsg(); - ToDeviceMsg toDeviceMsg = null; - if (deviceMsg.getMsgType() == SessionMsgType.POST_TELEMETRY_REQUEST) { - toDeviceMsg = BasicStatusCodeResponse.onSuccess(deviceMsg.getMsgType(), BasicRequest.DEFAULT_REQUEST_ID); - } else if (deviceMsg.getMsgType() == SessionMsgType.GET_ATTRIBUTES_REQUEST) { - List data = new ArrayList<>(); - data.add(new BaseAttributeKvEntry(new StringDataEntry("key1", "value1"), System.currentTimeMillis())); - data.add(new BaseAttributeKvEntry(new LongDataEntry("key2", 42L), System.currentTimeMillis())); - BasicAttributeKVMsg kv = BasicAttributeKVMsg.fromClient(data); - toDeviceMsg = BasicGetAttributesResponse.onSuccess(deviceMsg.getMsgType(), BasicRequest.DEFAULT_REQUEST_ID, kv); - } - if (toDeviceMsg != null) { - sessionMsg.getSessionContext().onMsg(new BasicSessionActorToAdaptorMsg(sessionMsg.getSessionContext(), toDeviceMsg)); - } - } catch (Exception e) { - e.printStackTrace(); - } - } - } +// @Override +// public void process(SessionAwareMsg toActorMsg) { +// if (toActorMsg instanceof TransportToDeviceSessionActorMsg) { +// AdaptorToSessionActorMsg sessionMsg = ((TransportToDeviceSessionActorMsg) toActorMsg).getSessionMsg(); +// try { +// FromDeviceMsg deviceMsg = sessionMsg.getMsg(); +// ToDeviceMsg toDeviceMsg = null; +// if (deviceMsg.getMsgType() == SessionMsgType.POST_TELEMETRY_REQUEST) { +// toDeviceMsg = BasicStatusCodeResponse.onSuccess(deviceMsg.getMsgType(), BasicRequest.DEFAULT_REQUEST_ID); +// } else if (deviceMsg.getMsgType() == SessionMsgType.GET_ATTRIBUTES_REQUEST) { +// List data = new ArrayList<>(); +// data.add(new BaseAttributeKvEntry(new StringDataEntry("key1", "value1"), System.currentTimeMillis())); +// data.add(new BaseAttributeKvEntry(new LongDataEntry("key2", 42L), System.currentTimeMillis())); +// BasicAttributeKVMsg kv = BasicAttributeKVMsg.fromClient(data); +// toDeviceMsg = BasicGetAttributesResponse.onSuccess(deviceMsg.getMsgType(), BasicRequest.DEFAULT_REQUEST_ID, kv); +// } +// if (toDeviceMsg != null) { +// sessionMsg.getSessionContext().onMsg(new BasicSessionActorToAdaptorMsg(sessionMsg.getSessionContext(), toDeviceMsg)); +// } +// } catch (Exception e) { +// e.printStackTrace(); +// } +// } +// } @Override public void onDeviceAdded(Device device) { diff --git a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java index d8f1d10b11..c64ab5d559 100644 --- a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java +++ b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java @@ -1,12 +1,12 @@ /** * 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 - *

+ * + * 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. @@ -26,18 +26,25 @@ import io.netty.handler.codec.mqtt.MqttFixedHeader; import io.netty.handler.codec.mqtt.MqttMessage; import io.netty.handler.codec.mqtt.MqttMessageIdVariableHeader; import io.netty.handler.codec.mqtt.MqttPubAckMessage; +import io.netty.handler.codec.mqtt.MqttPublishMessage; import io.netty.handler.codec.mqtt.MqttQoS; import io.netty.handler.codec.mqtt.MqttSubAckMessage; import io.netty.handler.codec.mqtt.MqttSubAckPayload; +import io.netty.handler.codec.mqtt.MqttSubscribeMessage; +import io.netty.handler.codec.mqtt.MqttTopicSubscription; +import io.netty.handler.codec.mqtt.MqttUnsubscribeMessage; import io.netty.handler.ssl.SslHandler; import io.netty.util.concurrent.Future; import io.netty.util.concurrent.GenericFutureListener; import lombok.extern.slf4j.Slf4j; import org.springframework.util.StringUtils; +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.adaptor.AdaptorException; import org.thingsboard.server.common.transport.quota.QuotaService; import org.thingsboard.server.dao.EncryptionUtil; +import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos.DeviceInfoProto; import org.thingsboard.server.gen.transport.TransportProtos.SessionEvent; import org.thingsboard.server.gen.transport.TransportProtos.SessionEventMsg; @@ -54,6 +61,7 @@ import javax.net.ssl.SSLPeerUnverifiedException; import javax.security.cert.X509Certificate; import java.io.IOException; import java.net.InetSocketAddress; +import java.util.ArrayList; import java.util.List; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; @@ -65,14 +73,16 @@ import static io.netty.handler.codec.mqtt.MqttConnectReturnCode.CONNECTION_REFUS import static io.netty.handler.codec.mqtt.MqttMessageType.CONNACK; import static io.netty.handler.codec.mqtt.MqttMessageType.PUBACK; import static io.netty.handler.codec.mqtt.MqttMessageType.SUBACK; +import static io.netty.handler.codec.mqtt.MqttMessageType.UNSUBACK; import static io.netty.handler.codec.mqtt.MqttQoS.AT_LEAST_ONCE; import static io.netty.handler.codec.mqtt.MqttQoS.AT_MOST_ONCE; +import static io.netty.handler.codec.mqtt.MqttQoS.FAILURE; /** * @author Andrew Shvayka */ @Slf4j -public class MqttTransportHandler extends ChannelInboundHandlerAdapter implements GenericFutureListener> { +public class MqttTransportHandler extends ChannelInboundHandlerAdapter implements GenericFutureListener>, SessionMsgListener { public static final MqttQoS MAX_SUPPORTED_QOS_LVL = AT_LEAST_ONCE; @@ -84,8 +94,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement private final SslHandler sslHandler; private final ConcurrentMap mqttQoSMap; - private final SessionInfoProto sessionInfo; - + private volatile SessionInfoProto sessionInfo; private volatile InetSocketAddress address; private volatile DeviceSessionCtx deviceSessionCtx; private volatile GatewaySessionCtx gatewaySessionCtx; @@ -98,11 +107,6 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement this.quotaService = context.getQuotaService(); this.sslHandler = context.getSslHandler(); this.mqttQoSMap = new ConcurrentHashMap<>(); - this.sessionInfo = SessionInfoProto.newBuilder() - .setNodeId(context.getNodeId()) - .setSessionIdMSB(sessionId.getMostSignificantBits()) - .setSessionIdLSB(sessionId.getLeastSignificantBits()) - .build(); this.deviceSessionCtx = new DeviceSessionCtx(mqttQoSMap); } @@ -135,15 +139,15 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement case CONNECT: processConnect(ctx, (MqttConnectMessage) msg); break; -// case PUBLISH: -// processPublish(ctx, (MqttPublishMessage) msg); -// break; -// case SUBSCRIBE: -// processSubscribe(ctx, (MqttSubscribeMessage) msg); -// break; -// case UNSUBSCRIBE: -// processUnsubscribe(ctx, (MqttUnsubscribeMessage) msg); -// break; + case PUBLISH: + processPublish(ctx, (MqttPublishMessage) msg); + break; + case SUBSCRIBE: + processSubscribe(ctx, (MqttSubscribeMessage) msg); + break; + case UNSUBSCRIBE: + processUnsubscribe(ctx, (MqttUnsubscribeMessage) msg); + break; // case PINGREQ: // if (checkConnected(ctx)) { // ctx.writeAndFlush(new MqttMessage(new MqttFixedHeader(PINGRESP, false, AT_MOST_ONCE, false, 0))); @@ -160,24 +164,25 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement } -// private void processPublish(ChannelHandlerContext ctx, MqttPublishMessage mqttMsg) { -// if (!checkConnected(ctx)) { -// return; -// } -// String topicName = mqttMsg.variableHeader().topicName(); -// int msgId = mqttMsg.variableHeader().packetId(); -// log.trace("[{}] Processing publish msg [{}][{}]!", sessionId, topicName, msgId); -// -// if (topicName.startsWith(BASE_GATEWAY_API_TOPIC)) { -// if (gatewaySessionCtx != null) { -// gatewaySessionCtx.setChannel(ctx); + private void processPublish(ChannelHandlerContext ctx, MqttPublishMessage mqttMsg) { + if (!checkConnected(ctx)) { + return; + } + String topicName = mqttMsg.variableHeader().topicName(); + int msgId = mqttMsg.variableHeader().packetId(); + log.trace("[{}] Processing publish msg [{}][{}]!", sessionId, topicName, msgId); + + if (topicName.startsWith(MqttTopics.BASE_GATEWAY_API_TOPIC)) { + if (gatewaySessionCtx != null) { + gatewaySessionCtx.setChannel(ctx); // handleMqttPublishMsg(topicName, msgId, mqttMsg); -// } -// } else { -// processDevicePublish(ctx, mqttMsg, topicName, msgId); -// } -// } -// + } + } else { + processDevicePublish(ctx, mqttMsg, topicName, msgId); + } + } + + // // private void handleMqttPublishMsg(String topicName, int msgId, MqttPublishMessage mqttMsg) { // try { // switch (topicName) { @@ -205,7 +210,23 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement // } // } // -// private void processDevicePublish(ChannelHandlerContext ctx, MqttPublishMessage mqttMsg, String topicName, int msgId) { + private void processDevicePublish(ChannelHandlerContext ctx, MqttPublishMessage mqttMsg, String topicName, int msgId) { + try { + if (topicName.equals(MqttTopics.DEVICE_TELEMETRY_TOPIC)) { + TransportProtos.PostTelemetryMsg postTelemetryMsg = adaptor.convertToPostTelemetry(deviceSessionCtx, mqttMsg); + transportService.process(sessionInfo, postTelemetryMsg, getPubAckCallback(ctx, msgId, postTelemetryMsg)); + } else if (topicName.equals(MqttTopics.DEVICE_ATTRIBUTES_TOPIC)) { + TransportProtos.PostAttributeMsg postAttributeMsg = adaptor.convertToPostAttributes(deviceSessionCtx, mqttMsg); + transportService.process(sessionInfo, postAttributeMsg, getPubAckCallback(ctx, msgId, postAttributeMsg)); + } else if (topicName.startsWith(MqttTopics.DEVICE_ATTRIBUTES_REQUEST_TOPIC_PREFIX)) { + TransportProtos.GetAttributeRequestMsg getAttributeMsg = adaptor.convertToGetAttributes(deviceSessionCtx, mqttMsg); + transportService.process(sessionInfo, getAttributeMsg, getPubAckCallback(ctx, msgId, getAttributeMsg)); + } + } catch (AdaptorException e) { + log.warn("[{}] Failed to process publish msg [{}][{}]", sessionId, topicName, msgId, e); + log.info("[{}] Closing current session due to invalid publish msg [{}][{}]", sessionId, topicName, msgId); + ctx.close(); + } // AdaptorToSessionActorMsg msg = null; // try { // if (topicName.equals(DEVICE_TELEMETRY_TOPIC)) { @@ -237,20 +258,38 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement // log.info("[{}] Closing current session due to invalid publish msg [{}][{}]", sessionId, topicName, msgId); // ctx.close(); // } -// } -// -// private void processSubscribe(ChannelHandlerContext ctx, MqttSubscribeMessage mqttMsg) { -// if (!checkConnected(ctx)) { -// return; -// } -// log.trace("[{}] Processing subscription [{}]!", sessionId, mqttMsg.variableHeader().messageId()); -// List grantedQoSList = new ArrayList<>(); -// for (MqttTopicSubscription subscription : mqttMsg.payload().topicSubscriptions()) { -// String topic = subscription.topicName(); -// MqttQoS reqQoS = subscription.qualityOfService(); -// try { -// switch (topic) { -// case DEVICE_ATTRIBUTES_TOPIC: { + } + + private TransportServiceCallback getPubAckCallback(final ChannelHandlerContext ctx, final int msgId, final T msg) { + return new TransportServiceCallback() { + @Override + public void onSuccess(Void dummy) { + log.trace("[{}] Published msg: {}", sessionId, msg); + if (msgId > 0) { + ctx.writeAndFlush(createMqttPubAckMsg(msgId)); + } + } + + @Override + public void onError(Throwable e) { + log.trace("[{}] Failed to publish msg: {}", sessionId, msg, e); + ctx.close(); + } + }; + } + + private void processSubscribe(ChannelHandlerContext ctx, MqttSubscribeMessage mqttMsg) { + if (!checkConnected(ctx)) { + return; + } + log.trace("[{}] Processing subscription [{}]!", sessionId, mqttMsg.variableHeader().messageId()); + List grantedQoSList = new ArrayList<>(); + for (MqttTopicSubscription subscription : mqttMsg.payload().topicSubscriptions()) { + String topic = subscription.topicName(); + MqttQoS reqQoS = subscription.qualityOfService(); + try { + switch (topic) { +// case MqttTopics.DEVICE_ATTRIBUTES_TOPIC: { // AdaptorToSessionActorMsg msg = adaptor.convertToActorMsg(deviceSessionCtx, SUBSCRIBE_ATTRIBUTES_REQUEST, mqttMsg); // processor.process(new BasicTransportToDeviceSessionActorMsg(deviceSessionCtx.getDevice(), msg)); // registerSubQoS(topic, grantedQoSList, reqQoS); @@ -267,37 +306,37 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement // case GATEWAY_RPC_TOPIC: // registerSubQoS(topic, grantedQoSList, reqQoS); // break; -// case DEVICE_ATTRIBUTES_RESPONSES_TOPIC: -// deviceSessionCtx.setAllowAttributeResponses(); -// registerSubQoS(topic, grantedQoSList, reqQoS); -// break; -// default: -// log.warn("[{}] Failed to subscribe to [{}][{}]", sessionId, topic, reqQoS); -// grantedQoSList.add(FAILURE.value()); -// break; -// } -// } catch (AdaptorException e) { -// log.warn("[{}] Failed to subscribe to [{}][{}]", sessionId, topic, reqQoS); -// grantedQoSList.add(FAILURE.value()); -// } -// } -// ctx.writeAndFlush(createSubAckMessage(mqttMsg.variableHeader().messageId(), grantedQoSList)); -// } -// -// private void registerSubQoS(String topic, List grantedQoSList, MqttQoS reqQoS) { -// grantedQoSList.add(getMinSupportedQos(reqQoS)); -// mqttQoSMap.put(topic, getMinSupportedQos(reqQoS)); -// } -// -// private void processUnsubscribe(ChannelHandlerContext ctx, MqttUnsubscribeMessage mqttMsg) { -// if (!checkConnected(ctx)) { -// return; -// } -// log.trace("[{}] Processing subscription [{}]!", sessionId, mqttMsg.variableHeader().messageId()); -// for (String topicName : mqttMsg.payload().topics()) { -// mqttQoSMap.remove(topicName); -// try { -// switch (topicName) { + case MqttTopics.DEVICE_ATTRIBUTES_RESPONSES_TOPIC: + deviceSessionCtx.setAllowAttributeResponses(); + registerSubQoS(topic, grantedQoSList, reqQoS); + break; + default: + log.warn("[{}] Failed to subscribe to [{}][{}]", sessionId, topic, reqQoS); + grantedQoSList.add(FAILURE.value()); + break; + } + } catch (Exception e) { + log.warn("[{}] Failed to subscribe to [{}][{}]", sessionId, topic, reqQoS); + grantedQoSList.add(FAILURE.value()); + } + } + ctx.writeAndFlush(createSubAckMessage(mqttMsg.variableHeader().messageId(), grantedQoSList)); + } + + private void registerSubQoS(String topic, List grantedQoSList, MqttQoS reqQoS) { + grantedQoSList.add(getMinSupportedQos(reqQoS)); + mqttQoSMap.put(topic, getMinSupportedQos(reqQoS)); + } + + private void processUnsubscribe(ChannelHandlerContext ctx, MqttUnsubscribeMessage mqttMsg) { + if (!checkConnected(ctx)) { + return; + } + log.trace("[{}] Processing subscription [{}]!", sessionId, mqttMsg.variableHeader().messageId()); + for (String topicName : mqttMsg.payload().topics()) { + mqttQoSMap.remove(topicName); + try { + switch (topicName) { // case DEVICE_ATTRIBUTES_TOPIC: { // AdaptorToSessionActorMsg msg = adaptor.convertToActorMsg(deviceSessionCtx, UNSUBSCRIBE_ATTRIBUTES_REQUEST, mqttMsg); // processor.process(new BasicTransportToDeviceSessionActorMsg(deviceSessionCtx.getDevice(), msg)); @@ -308,23 +347,23 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement // processor.process(new BasicTransportToDeviceSessionActorMsg(deviceSessionCtx.getDevice(), msg)); // break; // } -// case DEVICE_ATTRIBUTES_RESPONSES_TOPIC: -// deviceSessionCtx.setDisallowAttributeResponses(); -// break; -// } -// } catch (AdaptorException e) { -// log.warn("[{}] Failed to process unsubscription [{}] to [{}]", sessionId, mqttMsg.variableHeader().messageId(), topicName); -// } -// } -// ctx.writeAndFlush(createUnSubAckMessage(mqttMsg.variableHeader().messageId())); -// } -// -// private MqttMessage createUnSubAckMessage(int msgId) { -// MqttFixedHeader mqttFixedHeader = -// new MqttFixedHeader(UNSUBACK, false, AT_LEAST_ONCE, false, 0); -// MqttMessageIdVariableHeader mqttMessageIdVariableHeader = MqttMessageIdVariableHeader.from(msgId); -// return new MqttMessage(mqttFixedHeader, mqttMessageIdVariableHeader); -// } + case MqttTopics.DEVICE_ATTRIBUTES_RESPONSES_TOPIC: + deviceSessionCtx.setDisallowAttributeResponses(); + break; + } + } catch (Exception e) { + log.warn("[{}] Failed to process unsubscription [{}] to [{}]", sessionId, mqttMsg.variableHeader().messageId(), topicName); + } + } + ctx.writeAndFlush(createUnSubAckMessage(mqttMsg.variableHeader().messageId())); + } + + private MqttMessage createUnSubAckMessage(int msgId) { + MqttFixedHeader mqttFixedHeader = + new MqttFixedHeader(UNSUBACK, false, AT_LEAST_ONCE, false, 0); + MqttMessageIdVariableHeader mqttMessageIdVariableHeader = MqttMessageIdVariableHeader.from(msgId); + return new MqttMessage(mqttFixedHeader, mqttMessageIdVariableHeader); + } private void processConnect(ChannelHandlerContext ctx, MqttConnectMessage msg) { log.info("[{}] Processing connect msg for client: {}!", sessionId, msg.payload().clientIdentifier()); @@ -346,15 +385,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement new TransportServiceCallback() { @Override public void onSuccess(ValidateDeviceCredentialsResponseMsg msg) { - if (!msg.hasDeviceInfo()) { - ctx.writeAndFlush(createMqttConnAckMsg(CONNECTION_REFUSED_NOT_AUTHORIZED)); - ctx.close(); - } else { - ctx.writeAndFlush(createMqttConnAckMsg(CONNECTION_ACCEPTED)); - deviceSessionCtx.setDeviceInfo(msg.getDeviceInfo()); - transportService.process(deviceSessionCtx, getSessionEventMsg(SessionEvent.OPEN), null); - checkGatewaySession(); - } + onValidateDeviceResponse(msg, ctx); } @Override @@ -375,15 +406,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement new TransportServiceCallback() { @Override public void onSuccess(ValidateDeviceCredentialsResponseMsg msg) { - if (!msg.hasDeviceInfo()) { - ctx.writeAndFlush(createMqttConnAckMsg(CONNECTION_REFUSED_NOT_AUTHORIZED)); - ctx.close(); - } else { - ctx.writeAndFlush(createMqttConnAckMsg(CONNECTION_ACCEPTED)); - deviceSessionCtx.setDeviceInfo(msg.getDeviceInfo()); - transportService.process(deviceSessionCtx, getSessionEventMsg(SessionEvent.OPEN), null); - checkGatewaySession(); - } + onValidateDeviceResponse(msg, ctx); } @Override @@ -415,7 +438,8 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement private void processDisconnect(ChannelHandlerContext ctx) { ctx.close(); if (deviceSessionCtx.isConnected()) { - transportService.process(deviceSessionCtx, getSessionEventMsg(SessionEvent.CLOSED), null); + transportService.process(sessionInfo, getSessionEventMsg(SessionEvent.CLOSED), null); + transportService.deregisterSession(sessionInfo); if (gatewaySessionCtx != null) { gatewaySessionCtx.onGatewayDisconnect(); } @@ -488,16 +512,46 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement private SessionEventMsg getSessionEventMsg(SessionEvent event) { return SessionEventMsg.newBuilder() - .setSessionInfo(sessionInfo) - .setDeviceIdMSB(deviceSessionCtx.getDeviceIdMSB()) - .setDeviceIdLSB(deviceSessionCtx.getDeviceIdLSB()) + .setSessionType(TransportProtos.SessionType.ASYNC) .setEvent(event).build(); } @Override public void operationComplete(Future future) throws Exception { if (deviceSessionCtx.isConnected()) { - transportService.process(deviceSessionCtx, getSessionEventMsg(SessionEvent.CLOSED), null); + transportService.process(sessionInfo, getSessionEventMsg(SessionEvent.CLOSED), null); + transportService.deregisterSession(sessionInfo); + } + } + + private void onValidateDeviceResponse(ValidateDeviceCredentialsResponseMsg msg, ChannelHandlerContext ctx) { + if (!msg.hasDeviceInfo()) { + ctx.writeAndFlush(createMqttConnAckMsg(CONNECTION_REFUSED_NOT_AUTHORIZED)); + ctx.close(); + } else { + ctx.writeAndFlush(createMqttConnAckMsg(CONNECTION_ACCEPTED)); + deviceSessionCtx.setDeviceInfo(msg.getDeviceInfo()); + sessionInfo = SessionInfoProto.newBuilder() + .setNodeId(context.getNodeId()) + .setSessionIdMSB(sessionId.getMostSignificantBits()) + .setSessionIdLSB(sessionId.getLeastSignificantBits()) + .setDeviceIdMSB(msg.getDeviceInfo().getDeviceIdMSB()) + .setDeviceIdLSB(msg.getDeviceInfo().getDeviceIdLSB()) + .setTenantIdMSB(msg.getDeviceInfo().getTenantIdMSB()) + .setTenantIdLSB(msg.getDeviceInfo().getTenantIdLSB()) + .build(); + transportService.process(sessionInfo, getSessionEventMsg(SessionEvent.OPEN), null); + transportService.registerSession(sessionInfo, this); + checkGatewaySession(); + } + } + + @Override + public void onGetAttributesResponse(TransportProtos.GetAttributeResponseMsg response) { + try { + adaptor.convertToPublish(deviceSessionCtx, response).ifPresent(deviceSessionCtx.getChannel()::writeAndFlush); + } catch (Exception e) { + log.trace("[{}] Failed to convert device attributes to MQTT msg", sessionId, e); } } } diff --git a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java index c8baaf7e53..ee41d2c3d6 100644 --- a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java +++ b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java @@ -17,6 +17,7 @@ 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; @@ -25,12 +26,14 @@ import io.netty.buffer.UnpooledByteBufAllocator; import io.netty.handler.codec.mqtt.*; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; import org.thingsboard.server.common.data.id.SessionId; import org.thingsboard.server.common.msg.core.*; import org.thingsboard.server.common.msg.kv.AttributesKVMsg; import org.thingsboard.server.common.msg.session.*; 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.DeviceSessionCtx; import org.thingsboard.server.transport.mqtt.MqttTransportHandler; @@ -52,6 +55,64 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor { private static final Charset UTF8 = Charset.forName("UTF-8"); private static final ByteBufAllocator ALLOCATOR = new UnpooledByteBufAllocator(false); + @Override + public TransportProtos.PostTelemetryMsg convertToPostTelemetry(DeviceSessionCtx 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(DeviceSessionCtx 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(DeviceSessionCtx 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 clientKeys = toStringSet(requestBody, "clientKeys"); + Set 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 Optional convertToPublish(DeviceSessionCtx 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 AdaptorToSessionActorMsg convertToActorMsg(DeviceSessionCtx ctx, SessionMsgType type, MqttMessage inbound) throws AdaptorException { FromDeviceMsg msg; diff --git a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/MqttTransportAdaptor.java b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/MqttTransportAdaptor.java index bf83a1fd98..54e7c5ca3c 100644 --- a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/MqttTransportAdaptor.java +++ b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/MqttTransportAdaptor.java @@ -16,11 +16,24 @@ 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.TransportAdaptor; +import org.thingsboard.server.common.transport.adaptor.AdaptorException; +import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.transport.mqtt.session.DeviceSessionCtx; +import java.util.Optional; + /** * @author Andrew Shvayka */ public interface MqttTransportAdaptor extends TransportAdaptor { + + TransportProtos.PostTelemetryMsg convertToPostTelemetry(DeviceSessionCtx ctx, MqttPublishMessage inbound) throws AdaptorException; + + TransportProtos.PostAttributeMsg convertToPostAttributes(DeviceSessionCtx ctx, MqttPublishMessage inbound) throws AdaptorException; + + TransportProtos.GetAttributeRequestMsg convertToGetAttributes(DeviceSessionCtx ctx, MqttPublishMessage inbound) throws AdaptorException; + + Optional convertToPublish(DeviceSessionCtx ctx, TransportProtos.GetAttributeResponseMsg responseMsg) throws AdaptorException; } diff --git a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/DeviceSessionCtx.java b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/DeviceSessionCtx.java index 6661cb12f4..d704634ded 100644 --- a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/DeviceSessionCtx.java +++ b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/DeviceSessionCtx.java @@ -17,6 +17,7 @@ package org.thingsboard.server.transport.mqtt.session; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.mqtt.*; +import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.thingsboard.server.common.data.id.SessionId; import org.thingsboard.server.common.msg.session.SessionActorToAdaptorMsg; @@ -41,12 +42,13 @@ import java.util.concurrent.atomic.AtomicInteger; public class DeviceSessionCtx extends MqttDeviceAwareSessionContext { private final MqttSessionId sessionId; + @Getter private ChannelHandlerContext channel; private volatile boolean allowAttributeResponses; private AtomicInteger msgIdSeq = new AtomicInteger(0); public DeviceSessionCtx(ConcurrentMap mqttQoSMap) { - super(null, null, null); + super(null, null, mqttQoSMap); this.sessionId = new MqttSessionId(); } diff --git a/transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/MqttTransportService.java b/transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/MqttTransportService.java index 76c1c1b8c1..7a2f698647 100644 --- a/transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/MqttTransportService.java +++ b/transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/MqttTransportService.java @@ -1,12 +1,12 @@ /** * 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 - *

+ * + * 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. @@ -111,8 +111,8 @@ public class MqttTransportService implements TransportService { TBKafkaConsumerTemplate.TBKafkaConsumerTemplateBuilder responseBuilder = TBKafkaConsumerTemplate.builder(); responseBuilder.settings(kafkaSettings); responseBuilder.topic(transportApiResponsesTopic + "." + transportContext.getNodeId()); - responseBuilder.clientId(transportContext.getNodeId()); - responseBuilder.groupId(null); + responseBuilder.clientId("transport-api-client-" + transportContext.getNodeId()); + responseBuilder.groupId("transport-api-client"); responseBuilder.autoCommit(true); responseBuilder.autoCommitIntervalMs(autoCommitInterval); responseBuilder.decoder(new TransportApiResponseDecoder()); @@ -137,8 +137,8 @@ public class MqttTransportService implements TransportService { TBKafkaConsumerTemplate.TBKafkaConsumerTemplateBuilder mainConsumerBuilder = TBKafkaConsumerTemplate.builder(); mainConsumerBuilder.settings(kafkaSettings); mainConsumerBuilder.topic(notificationsTopic + "." + transportContext.getNodeId()); - mainConsumerBuilder.clientId(transportContext.getNodeId()); - mainConsumerBuilder.groupId(null); + mainConsumerBuilder.clientId("transport-" + transportContext.getNodeId()); + mainConsumerBuilder.groupId("transport"); mainConsumerBuilder.autoCommit(true); mainConsumerBuilder.autoCommitIntervalMs(notificationsAutoCommitInterval); mainConsumerBuilder.decoder(new ToTransportMsgResponseDecoder()); @@ -242,6 +242,15 @@ public class MqttTransportService implements TransportService { send(sessionInfo, toRuleEngineMsg, callback); } + @Override + public void process(SessionInfoProto sessionInfo, TransportProtos.GetAttributeRequestMsg msg, TransportServiceCallback callback) { + ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg( + TransportProtos.TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) + .setGetAttributes(msg).build() + ).build(); + send(sessionInfo, toRuleEngineMsg, callback); + } + @Override public void registerSession(SessionInfoProto sessionInfo, SessionMsgListener listener) { sessions.putIfAbsent(toId(sessionInfo), listener); @@ -271,9 +280,13 @@ public class MqttTransportService implements TransportService { @Override public void onCompletion(RecordMetadata metadata, Exception exception) { if (exception == null) { - callback.onSuccess(null); + if (callback != null) { + callback.onSuccess(null); + } } else { - callback.onError(exception); + if (callback != null) { + callback.onError(exception); + } } } } diff --git a/transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/ToRuleEngineMsgEncoder.java b/transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/ToRuleEngineMsgEncoder.java index ee929ceb6f..179a2a13cf 100644 --- a/transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/ToRuleEngineMsgEncoder.java +++ b/transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/ToRuleEngineMsgEncoder.java @@ -1,12 +1,12 @@ /** * 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 - *

+ * + * 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. From 5a80d75584d09b33a88b25fd63d9e41e95e2eafc Mon Sep 17 00:00:00 2001 From: Andrew Shvayka Date: Wed, 10 Oct 2018 15:20:08 +0300 Subject: [PATCH 09/26] Added support of attribute update notifications --- .../device/DeviceActorMessageProcessor.java | 113 +++++++++++------- .../server/actors/device/SessionInfo.java | 1 - .../common/transport/SessionMsgListener.java | 11 +- .../common/transport/TransportService.java | 6 + .../transport/adaptor/JsonConverter.java | 37 +++--- .../transport/src/main/proto/transport.proto | 16 +++ .../transport/mqtt/MqttTransportHandler.java | 91 +++++++------- .../mqtt/adaptors/JsonMqttAdaptor.java | 15 ++- .../mqtt/adaptors/MqttTransportAdaptor.java | 4 + .../mqtt/session/DeviceSessionCtx.java | 9 -- .../mqtt/service/MqttTransportService.java | 29 ++++- 11 files changed, 204 insertions(+), 128 deletions(-) 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 0822766b4b..7abcca2cab 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 @@ -1,12 +1,12 @@ /** * 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 - * + *

+ * 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. @@ -216,12 +216,16 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso void process(ActorContext context, TransportToDeviceActorMsgWrapper wrapper) { TransportToDeviceActorMsg msg = wrapper.getMsg(); -// processSubscriptionCommands(context, msg); // processRpcResponses(context, msg); 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(); @@ -236,9 +240,6 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso // SessionMsgType sessionMsgType = msg.getPayload().getMsgType(); // if (sessionMsgType.requiresRulesProcessing()) { // switch (sessionMsgType) { -// case GET_ATTRIBUTES_REQUEST: -// handleGetAttributesRequest(msg); -// break; // case TO_SERVER_RPC_REQUEST: // handleClientSideRPCRequest(context, msg); // reportActivity(); @@ -262,7 +263,6 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso private void handleGetAttributesRequest(ActorContext context, SessionInfoProto sessionInfo, GetAttributeRequestMsg request) { ListenableFuture> clientAttributesFuture = getAttributeKvEntries(deviceId, DataConstants.CLIENT_SCOPE, toOptionalSet(request.getClientAttributeNamesList())); ListenableFuture> sharedAttributesFuture = getAttributeKvEntries(deviceId, DataConstants.SHARED_SCOPE, toOptionalSet(request.getSharedAttributeNamesList())); - UUID sessionId = new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB()); int requestId = request.getRequestId(); Futures.addCallback(Futures.allAsList(Arrays.asList(clientAttributesFuture, sharedAttributesFuture)), new FutureCallback>>() { @Override @@ -272,7 +272,7 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso .addAllClientAttributeList(toTsKvProtos(result.get(0))) .addAllSharedAttributeList(toTsKvProtos(result.get(1))) .build(); - sendToTransport(responseMsg, sessionId, sessionInfo); + sendToTransport(responseMsg, sessionInfo); } @Override @@ -280,7 +280,7 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso GetAttributeResponseMsg responseMsg = GetAttributeResponseMsg.newBuilder() .setError(t.getMessage()) .build(); - sendToTransport(responseMsg, sessionId, sessionInfo); + sendToTransport(responseMsg, sessionInfo); } }); } @@ -353,28 +353,37 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso 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 sharedKeys = msg.getDeletedKeys().stream() + List 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 attributes = new ArrayList<>(msg.getValues()); if (attributes.size() > 0) { - notification = new AttributesUpdateNotification(BasicAttributeKVMsg.fromShared(attributes)); + List 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; -// attributeSubscriptions.entrySet().forEach(sub -> { -// ActorSystemToDeviceSessionActorMsg response = new BasicActorSystemToDeviceSessionActorMsg(finalNotification, sub.getKey()); -// sendMsgToSessionActor(response, sub.getValue().getServer()); -// }); + if (hasNotificationData) { + AttributeUpdateNotificationMsg finalNotification = notification.build(); + attributeSubscriptions.entrySet().forEach(sub -> { + sendToTransport(finalNotification, sub.getKey(), sub.getValue()); + }); } } else { logger.debug("[{}] No registered attributes subscriptions to process!", deviceId); @@ -414,25 +423,35 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso // } } -// 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) { -// 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); -// rpcSubscriptions.remove(sessionId); -// } -// } + private void processSubscriptionCommands(ActorContext context, SessionInfoProto sessionInfo, SubscribeToAttributeUpdatesMsg subscribeCmd) { + UUID sessionId = new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB()); + if (subscribeCmd.getUnsubscribe()) { + logger.debug("[{}] Canceling attributes subscription for session [{}]", deviceId, sessionId); + attributeSubscriptions.remove(sessionId); + } 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 void processSubscriptionCommands(ActorContext context, SessionInfoProto sessionInfo, SubscribeToRPCMsg subscribeCmd) { + UUID sessionId = new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB()); + 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); + } + } private void processSessionStateMsgs(SessionInfoProto sessionInfo, SessionEventMsg msg) { UUID sessionId = new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB()); @@ -521,11 +540,19 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso } } - private void sendToTransport(GetAttributeResponseMsg responseMsg, UUID sessionId, SessionInfoProto sessionInfo) { + 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, SessionInfo sessionInfo) { DeviceActorToTransportMsg msg = DeviceActorToTransportMsg.newBuilder() .setSessionIdMSB(sessionId.getMostSignificantBits()) .setSessionIdLSB(sessionId.getLeastSignificantBits()) - .setGetAttributesResponse(responseMsg).build(); + .setAttributeUpdateNotification(notificationMsg).build(); systemContext.getRuleEngineTransportService().process(sessionInfo.getNodeId(), msg); } 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 32cb60da5f..43ae592cee 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 @@ -25,5 +25,4 @@ import org.thingsboard.server.gen.transport.TransportProtos.SessionType; public class SessionInfo { private final SessionType type; private final String nodeId; - } diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/SessionMsgListener.java b/common/transport/src/main/java/org/thingsboard/server/common/transport/SessionMsgListener.java index 44fcd2a39d..17d4a1f42c 100644 --- a/common/transport/src/main/java/org/thingsboard/server/common/transport/SessionMsgListener.java +++ b/common/transport/src/main/java/org/thingsboard/server/common/transport/SessionMsgListener.java @@ -1,12 +1,12 @@ /** * 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 - * + *

+ * 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. @@ -15,6 +15,7 @@ */ package org.thingsboard.server.common.transport; +import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg; import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeResponseMsg; /** @@ -23,4 +24,6 @@ import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeResponse public interface SessionMsgListener { void onGetAttributesResponse(GetAttributeResponseMsg getAttributesResponse); + + void onAttributeUpdate(AttributeUpdateNotificationMsg attributeUpdateNotification); } diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/TransportService.java b/common/transport/src/main/java/org/thingsboard/server/common/transport/TransportService.java index c3817a9ffa..bf33b1d8a5 100644 --- a/common/transport/src/main/java/org/thingsboard/server/common/transport/TransportService.java +++ b/common/transport/src/main/java/org/thingsboard/server/common/transport/TransportService.java @@ -15,6 +15,8 @@ */ package org.thingsboard.server.common.transport; +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; @@ -43,6 +45,10 @@ public interface TransportService { void process(SessionInfoProto sessionInfo, GetAttributeRequestMsg msg, TransportServiceCallback callback); + void process(SessionInfoProto sessionInfo, SubscribeToAttributeUpdatesMsg msg, TransportServiceCallback callback); + + void process(SessionInfoProto sessionInfo, SubscribeToRPCMsg msg, TransportServiceCallback callback); + void registerSession(SessionInfoProto sessionInfo, SessionMsgListener listener); void deregisterSession(SessionInfoProto sessionInfo); diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java b/common/transport/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java index 6bb8eff1be..872dfaa03e 100644 --- a/common/transport/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java +++ b/common/transport/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java @@ -1,12 +1,12 @@ /** * 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 - * + *

+ * 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. @@ -39,6 +39,7 @@ import org.thingsboard.server.common.msg.core.ToDeviceRpcRequestMsg; import org.thingsboard.server.common.msg.core.ToServerRpcRequestMsg; import org.thingsboard.server.common.msg.core.ToServerRpcResponseMsg; import org.thingsboard.server.common.msg.kv.AttributesKVMsg; +import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos.*; import java.util.ArrayList; @@ -153,20 +154,6 @@ public class JsonConverter { return result; } - private static void parseNumericProto(List result, Entry 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!"); - } - } - } - - private static TelemetryUploadRequest convertToTelemetry(JsonElement jsonObject, long systemTs, int requestId) throws JsonSyntaxException { BasicTelemetryUploadRequest request = new BasicTelemetryUploadRequest(requestId); if (jsonObject.isJsonObject()) { @@ -283,6 +270,19 @@ public class JsonConverter { 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) { @@ -377,4 +377,5 @@ public class JsonConverter { error.addProperty("error", errorMsg); return error; } + } diff --git a/common/transport/src/main/proto/transport.proto b/common/transport/src/main/proto/transport.proto index d32bac99a7..66c0d8183a 100644 --- a/common/transport/src/main/proto/transport.proto +++ b/common/transport/src/main/proto/transport.proto @@ -108,6 +108,11 @@ message GetAttributeResponseMsg { string error = 5; } +message AttributeUpdateNotificationMsg { + repeated TsKvProto sharedUpdated = 1; + repeated string sharedDeleted = 2; +} + message ValidateDeviceTokenRequestMsg { string token = 1; } @@ -124,12 +129,22 @@ message SessionCloseNotificationProto { string message = 1; } +message SubscribeToAttributeUpdatesMsg { + bool unsubscribe = 1; +} + +message SubscribeToRPCMsg { + bool unsubscribe = 1; +} + message TransportToDeviceActorMsg { SessionInfoProto sessionInfo = 1; SessionEventMsg sessionEvent = 2; PostTelemetryMsg postTelemetry = 3; PostAttributeMsg postAttributes = 4; GetAttributeRequestMsg getAttributes = 5; + SubscribeToAttributeUpdatesMsg subscribeToAttributes = 6; + SubscribeToRPCMsg subscribeToRPC= 7; } message DeviceActorToTransportMsg { @@ -137,6 +152,7 @@ message DeviceActorToTransportMsg { int64 sessionIdLSB = 2; SessionCloseNotificationProto sessionCloseNotification = 3; GetAttributeResponseMsg getAttributesResponse = 4; + AttributeUpdateNotificationMsg attributeUpdateNotification = 5; } /** diff --git a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java index c64ab5d559..b1ee037dc3 100644 --- a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java +++ b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java @@ -1,12 +1,12 @@ /** * 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 - * + *

+ * 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. @@ -71,6 +71,7 @@ import static io.netty.handler.codec.mqtt.MqttConnectReturnCode.CONNECTION_ACCEP import static io.netty.handler.codec.mqtt.MqttConnectReturnCode.CONNECTION_REFUSED_BAD_USER_NAME_OR_PASSWORD; import static io.netty.handler.codec.mqtt.MqttConnectReturnCode.CONNECTION_REFUSED_NOT_AUTHORIZED; import static io.netty.handler.codec.mqtt.MqttMessageType.CONNACK; +import static io.netty.handler.codec.mqtt.MqttMessageType.PINGRESP; import static io.netty.handler.codec.mqtt.MqttMessageType.PUBACK; import static io.netty.handler.codec.mqtt.MqttMessageType.SUBACK; import static io.netty.handler.codec.mqtt.MqttMessageType.UNSUBACK; @@ -148,16 +149,17 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement case UNSUBSCRIBE: processUnsubscribe(ctx, (MqttUnsubscribeMessage) msg); break; -// case PINGREQ: -// if (checkConnected(ctx)) { -// ctx.writeAndFlush(new MqttMessage(new MqttFixedHeader(PINGRESP, false, AT_MOST_ONCE, false, 0))); -// } -// break; -// case DISCONNECT: -// if (checkConnected(ctx)) { -// processDisconnect(ctx); -// } -// break; + case PINGREQ: + //TODO: should we push the notification to the rule engine? + if (checkConnected(ctx)) { + ctx.writeAndFlush(new MqttMessage(new MqttFixedHeader(PINGRESP, false, AT_MOST_ONCE, false, 0))); + } + break; + case DISCONNECT: + if (checkConnected(ctx)) { + processDisconnect(ctx); + } + break; default: break; } @@ -289,25 +291,20 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement MqttQoS reqQoS = subscription.qualityOfService(); try { switch (topic) { -// case MqttTopics.DEVICE_ATTRIBUTES_TOPIC: { -// AdaptorToSessionActorMsg msg = adaptor.convertToActorMsg(deviceSessionCtx, SUBSCRIBE_ATTRIBUTES_REQUEST, mqttMsg); -// processor.process(new BasicTransportToDeviceSessionActorMsg(deviceSessionCtx.getDevice(), msg)); -// registerSubQoS(topic, grantedQoSList, reqQoS); -// break; -// } -// case DEVICE_RPC_REQUESTS_SUB_TOPIC: { -// AdaptorToSessionActorMsg msg = adaptor.convertToActorMsg(deviceSessionCtx, SUBSCRIBE_RPC_COMMANDS_REQUEST, mqttMsg); -// processor.process(new BasicTransportToDeviceSessionActorMsg(deviceSessionCtx.getDevice(), msg)); -// registerSubQoS(topic, grantedQoSList, reqQoS); -// break; -// } -// case DEVICE_RPC_RESPONSE_SUB_TOPIC: -// case GATEWAY_ATTRIBUTES_TOPIC: -// case GATEWAY_RPC_TOPIC: -// registerSubQoS(topic, grantedQoSList, reqQoS); -// break; + case MqttTopics.DEVICE_ATTRIBUTES_TOPIC: { + transportService.process(sessionInfo, TransportProtos.SubscribeToAttributeUpdatesMsg.newBuilder().build(), null); + registerSubQoS(topic, grantedQoSList, reqQoS); + break; + } + case MqttTopics.DEVICE_RPC_REQUESTS_SUB_TOPIC: { + transportService.process(sessionInfo, TransportProtos.SubscribeToRPCMsg.newBuilder().build(), null); + registerSubQoS(topic, grantedQoSList, reqQoS); + break; + } + case MqttTopics.DEVICE_RPC_RESPONSE_SUB_TOPIC: + case MqttTopics.GATEWAY_ATTRIBUTES_TOPIC: + case MqttTopics.GATEWAY_RPC_TOPIC: case MqttTopics.DEVICE_ATTRIBUTES_RESPONSES_TOPIC: - deviceSessionCtx.setAllowAttributeResponses(); registerSubQoS(topic, grantedQoSList, reqQoS); break; default: @@ -337,19 +334,14 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement mqttQoSMap.remove(topicName); try { switch (topicName) { -// case DEVICE_ATTRIBUTES_TOPIC: { -// AdaptorToSessionActorMsg msg = adaptor.convertToActorMsg(deviceSessionCtx, UNSUBSCRIBE_ATTRIBUTES_REQUEST, mqttMsg); -// processor.process(new BasicTransportToDeviceSessionActorMsg(deviceSessionCtx.getDevice(), msg)); -// break; -// } -// case DEVICE_RPC_REQUESTS_SUB_TOPIC: { -// AdaptorToSessionActorMsg msg = adaptor.convertToActorMsg(deviceSessionCtx, UNSUBSCRIBE_RPC_COMMANDS_REQUEST, mqttMsg); -// processor.process(new BasicTransportToDeviceSessionActorMsg(deviceSessionCtx.getDevice(), msg)); -// break; -// } - case MqttTopics.DEVICE_ATTRIBUTES_RESPONSES_TOPIC: - deviceSessionCtx.setDisallowAttributeResponses(); + case MqttTopics.DEVICE_ATTRIBUTES_TOPIC: { + transportService.process(sessionInfo, TransportProtos.SubscribeToAttributeUpdatesMsg.newBuilder().setUnsubscribe(true).build(), null); + break; + } + case MqttTopics.DEVICE_RPC_REQUESTS_SUB_TOPIC: { + transportService.process(sessionInfo, TransportProtos.SubscribeToRPCMsg.newBuilder().setUnsubscribe(true).build(), null); break; + } } } catch (Exception e) { log.warn("[{}] Failed to process unsubscription [{}] to [{}]", sessionId, mqttMsg.variableHeader().messageId(), topicName); @@ -551,7 +543,16 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement try { adaptor.convertToPublish(deviceSessionCtx, response).ifPresent(deviceSessionCtx.getChannel()::writeAndFlush); } catch (Exception e) { - log.trace("[{}] Failed to convert device attributes to MQTT msg", sessionId, e); + log.trace("[{}] Failed to convert device attributes response to MQTT msg", sessionId, e); + } + } + + @Override + public void onAttributeUpdate(TransportProtos.AttributeUpdateNotificationMsg notification) { + try { + adaptor.convertToPublish(deviceSessionCtx, notification).ifPresent(deviceSessionCtx.getChannel()::writeAndFlush); + } catch (Exception e) { + log.trace("[{}] Failed to convert device attributes update to MQTT msg", sessionId, e); } } } diff --git a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java index ee41d2c3d6..d4ab033cee 100644 --- a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java +++ b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java @@ -1,12 +1,12 @@ /** * 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 - * + *

+ * 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. @@ -113,6 +113,13 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor { } } + @Override + public Optional convertToPublish(DeviceSessionCtx ctx, TransportProtos.AttributeUpdateNotificationMsg notificationMsg) throws AdaptorException { + return Optional.of(createMqttPublishMsg(ctx, + MqttTopics.DEVICE_ATTRIBUTES_TOPIC, + JsonConverter.toJson(notificationMsg))); + } + @Override public AdaptorToSessionActorMsg convertToActorMsg(DeviceSessionCtx ctx, SessionMsgType type, MqttMessage inbound) throws AdaptorException { FromDeviceMsg msg; diff --git a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/MqttTransportAdaptor.java b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/MqttTransportAdaptor.java index 54e7c5ca3c..602fde12f6 100644 --- a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/MqttTransportAdaptor.java +++ b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/MqttTransportAdaptor.java @@ -36,4 +36,8 @@ public interface MqttTransportAdaptor extends TransportAdaptor convertToPublish(DeviceSessionCtx ctx, TransportProtos.GetAttributeResponseMsg responseMsg) throws AdaptorException; + + Optional convertToPublish(DeviceSessionCtx ctx, TransportProtos.AttributeUpdateNotificationMsg notificationMsg) throws AdaptorException; + + } diff --git a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/DeviceSessionCtx.java b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/DeviceSessionCtx.java index d704634ded..6bbb9fa6fd 100644 --- a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/DeviceSessionCtx.java +++ b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/DeviceSessionCtx.java @@ -44,7 +44,6 @@ public class DeviceSessionCtx extends MqttDeviceAwareSessionContext { private final MqttSessionId sessionId; @Getter private ChannelHandlerContext channel; - private volatile boolean allowAttributeResponses; private AtomicInteger msgIdSeq = new AtomicInteger(0); public DeviceSessionCtx(ConcurrentMap mqttQoSMap) { @@ -103,14 +102,6 @@ public class DeviceSessionCtx extends MqttDeviceAwareSessionContext { this.channel = channel; } - public void setAllowAttributeResponses() { - allowAttributeResponses = true; - } - - public void setDisallowAttributeResponses() { - allowAttributeResponses = false; - } - public int nextMsgId() { return msgIdSeq.incrementAndGet(); } diff --git a/transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/MqttTransportService.java b/transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/MqttTransportService.java index 7a2f698647..648b01c9c3 100644 --- a/transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/MqttTransportService.java +++ b/transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/MqttTransportService.java @@ -1,12 +1,12 @@ /** * 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 - * + *

+ * 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. @@ -161,6 +161,9 @@ public class MqttTransportService implements TransportService { if (toSessionMsg.hasGetAttributesResponse()) { listener.onGetAttributesResponse(toSessionMsg.getGetAttributesResponse()); } + if (toSessionMsg.hasAttributeUpdateNotification()) { + listener.onAttributeUpdate(toSessionMsg.getAttributeUpdateNotification()); + } }); } else { //TODO: should we notify the device actor about missed session? @@ -251,6 +254,24 @@ public class MqttTransportService implements TransportService { send(sessionInfo, toRuleEngineMsg, callback); } + @Override + public void process(SessionInfoProto sessionInfo, TransportProtos.SubscribeToAttributeUpdatesMsg msg, TransportServiceCallback callback) { + ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg( + TransportProtos.TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) + .setSubscribeToAttributes(msg).build() + ).build(); + send(sessionInfo, toRuleEngineMsg, callback); + } + + @Override + public void process(SessionInfoProto sessionInfo, TransportProtos.SubscribeToRPCMsg msg, TransportServiceCallback callback) { + ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg( + TransportProtos.TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) + .setSubscribeToRPC(msg).build() + ).build(); + send(sessionInfo, toRuleEngineMsg, callback); + } + @Override public void registerSession(SessionInfoProto sessionInfo, SessionMsgListener listener) { sessions.putIfAbsent(toId(sessionInfo), listener); From 950ec8d622cd5c0c851de63f715959e1a3a4e168 Mon Sep 17 00:00:00 2001 From: Andrew Shvayka Date: Thu, 11 Oct 2018 14:04:16 +0300 Subject: [PATCH 10/26] MQTT API implementation --- .../device/DeviceActorMessageProcessor.java | 187 +++++++++++------- .../actors/device/PendingSessionMsgData.java | 41 ---- .../device/ToServerRpcRequestMetadata.java | 12 +- .../common/transport/SessionMsgListener.java | 17 +- .../common/transport/TransportService.java | 6 + .../transport/adaptor/JsonConverter.java | 36 +++- .../transport/src/main/proto/transport.proto | 29 ++- .../coap/adaptors/JsonCoapAdaptor.java | 7 +- .../http/session/HttpSessionCtx.java | 2 +- .../transport/mqtt/MqttTransportHandler.java | 71 +++---- .../mqtt/adaptors/JsonMqttAdaptor.java | 76 ++++--- .../mqtt/adaptors/MqttTransportAdaptor.java | 26 ++- .../mqtt/service/MqttTransportService.java | 35 +++- 13 files changed, 333 insertions(+), 212 deletions(-) delete mode 100644 application/src/main/java/org/thingsboard/server/actors/device/PendingSessionMsgData.java 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 7abcca2cab..c590104665 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 @@ -1,12 +1,12 @@ /** * 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 - *

+ * + * 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. @@ -32,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,18 +43,33 @@ 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.RuleEngineError; import org.thingsboard.server.common.msg.core.RuleEngineErrorMsg; -import org.thingsboard.server.common.msg.core.ToDeviceRpcRequestMsg; -import org.thingsboard.server.common.msg.kv.BasicAttributeKVMsg; import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest; 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.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; @@ -76,8 +90,6 @@ import java.util.UUID; import java.util.function.Consumer; import java.util.stream.Collectors; -import org.thingsboard.server.gen.transport.TransportProtos.*; - /** * @author Andrew Shvayka */ @@ -123,11 +135,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) { @@ -136,13 +145,12 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso } boolean sent = rpcSubscriptions.size() > 0; - Set syncSessionSet = new HashSet<>(); + Set 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()) { -// syncSessionSet.add(sub.getKey()); -// } + sendToTransport(rpcRequest, sub.getKey(), sub.getValue().getNodeId()); + if (TransportProtos.SessionType.SYNC == sub.getValue().getType()) { + syncSessionSet.add(sub.getKey()); + } }); syncSessionSet.forEach(rpcSubscriptions::remove); @@ -175,10 +183,11 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso } } - private void sendPendingRequests(ActorContext context, SessionId sessionId, SessionType type, Optional 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); } @@ -186,16 +195,16 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso logger.debug("[{}] No pending RPC messages for new async session [{}]", deviceId, sessionId); } Set 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> processPendingRpc(ActorContext context, SessionId sessionId, Optional server, Set sentOneWayIds) { + private Consumer> processPendingRpc(ActorContext context, UUID sessionId, String nodeId, Set sentOneWayIds) { return entry -> { ToDeviceRpcRequestActorMsg requestActorMsg = entry.getValue().getMsg(); ToDeviceRpcRequest request = entry.getValue().getMsg().getMsg(); @@ -204,19 +213,14 @@ 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, TransportToDeviceActorMsgWrapper wrapper) { TransportToDeviceActorMsg msg = wrapper.getMsg(); -// processRpcResponses(context, msg); if (msg.hasSessionEvent()) { processSessionStateMsgs(msg.getSessionInfo(), msg.getSessionEvent()); } @@ -237,15 +241,13 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso if (msg.hasGetAttributes()) { handleGetAttributesRequest(context, msg.getSessionInfo(), msg.getGetAttributes()); } -// SessionMsgType sessionMsgType = msg.getPayload().getMsgType(); -// if (sessionMsgType.requiresRulesProcessing()) { -// switch (sessionMsgType) { -// case TO_SERVER_RPC_REQUEST: -// handleClientSideRPCRequest(context, msg); -// reportActivity(); -// break; -// } -// } + if (msg.hasToDeviceRPCCallResponse()) { + processRpcResponses(context, msg.getSessionInfo(), msg.getToDeviceRPCCallResponse()); + } + if (msg.hasToServerRPCCallRequest()) { + handleClientSideRPCRequest(context, msg.getSessionInfo(), msg.getToServerRPCCallRequest()); + reportActivity(); + } } private void reportActivity() { @@ -314,36 +316,42 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso } } -// private void handleClientSideRPCRequest(ActorContext context, DeviceToDeviceActorMsg src) { -// ToServerRpcRequestMsg request = (ToServerRpcRequestMsg) src.getPayload(); -// -// JsonObject json = new JsonObject(); -// json.addProperty("method", request.getMethod()); -// 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); -// -// scheduleMsgWithDelay(context, new DeviceActorClientSideRpcTimeoutMsg(request.getRequestId(), systemContext.getClientSideRpcTimeout()), systemContext.getClientSideRpcTimeout()); -// toServerRpcPendingMap.put(request.getRequestId(), new ToServerRpcRequestMetadata(src.getSessionId(), src.getSessionType(), src.getServerAddress())); -// } + private void handleClientSideRPCRequest(ActorContext context, SessionInfoProto sessionInfo, TransportProtos.ToServerRpcRequestMsg request) { + UUID sessionId = getSessionId(sessionInfo); + JsonObject json = new JsonObject(); + 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); + 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(sessionId, getSessionType(sessionId), sessionInfo.getNodeId())); + } - public void processClientSideRpcTimeout(ActorContext context, DeviceActorClientSideRpcTimeoutMsg msg) { + private TransportProtos.SessionType getSessionType(UUID sessionId) { + return sessions.containsKey(sessionId) ? TransportProtos.SessionType.ASYNC : TransportProtos.SessionType.SYNC; + } + + 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()); } } @@ -382,7 +390,7 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso if (hasNotificationData) { AttributeUpdateNotificationMsg finalNotification = notification.build(); attributeSubscriptions.entrySet().forEach(sub -> { - sendToTransport(finalNotification, sub.getKey(), sub.getValue()); + sendToTransport(finalNotification, sub.getKey(), sub.getValue().getNodeId()); }); } } else { @@ -390,6 +398,19 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso } } + 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()); + } + } + // private void processRpcResponses(ActorContext context, DeviceToDeviceActorMsg msg) { // SessionId sessionId = msg.getSessionId(); // FromDeviceMsg inMsg = msg.getPayload(); @@ -424,7 +445,7 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso } private void processSubscriptionCommands(ActorContext context, SessionInfoProto sessionInfo, SubscribeToAttributeUpdatesMsg subscribeCmd) { - UUID sessionId = new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB()); + UUID sessionId = getSessionId(sessionInfo); if (subscribeCmd.getUnsubscribe()) { logger.debug("[{}] Canceling attributes subscription for session [{}]", deviceId, sessionId); attributeSubscriptions.remove(sessionId); @@ -438,8 +459,12 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso } } + private UUID getSessionId(SessionInfoProto sessionInfo) { + return new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB()); + } + private void processSubscriptionCommands(ActorContext context, SessionInfoProto sessionInfo, SubscribeToRPCMsg subscribeCmd) { - UUID sessionId = new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB()); + UUID sessionId = getSessionId(sessionInfo); if (subscribeCmd.getUnsubscribe()) { logger.debug("[{}] Canceling rpc subscription for session [{}]", deviceId, sessionId); rpcSubscriptions.remove(sessionId); @@ -450,11 +475,12 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso } logger.debug("[{}] Registering rpc subscription for session [{}]", deviceId, sessionId); rpcSubscriptions.put(sessionId, session); + sendPendingRequests(context, sessionId, sessionInfo); } } private void processSessionStateMsgs(SessionInfoProto sessionInfo, SessionEventMsg msg) { - UUID sessionId = new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB()); + UUID sessionId = getSessionId(sessionInfo); if (msg.getEvent() == SessionEvent.OPEN) { logger.debug("[{}] Processing new session [{}]", deviceId, sessionId); if (sessions.size() >= systemContext.getMaxConcurrentSessionsPerDevice()) { @@ -548,14 +574,31 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso systemContext.getRuleEngineTransportService().process(sessionInfo.getNodeId(), msg); } - private void sendToTransport(AttributeUpdateNotificationMsg notificationMsg, UUID sessionId, SessionInfo sessionInfo) { + 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(sessionInfo.getNodeId(), msg); + 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 toTsKvProtos(@Nullable List result) { List clientAttributes; if (result == null || result.isEmpty()) { diff --git a/application/src/main/java/org/thingsboard/server/actors/device/PendingSessionMsgData.java b/application/src/main/java/org/thingsboard/server/actors/device/PendingSessionMsgData.java deleted file mode 100644 index dfa07cf065..0000000000 --- a/application/src/main/java/org/thingsboard/server/actors/device/PendingSessionMsgData.java +++ /dev/null @@ -1,41 +0,0 @@ -/** - * 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.actors.device; - -import lombok.AllArgsConstructor; -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.SessionMsgType; - -import java.util.Optional; -import java.util.UUID; - -/** - * Created by ashvayka on 17.04.18. - */ -@Data -@AllArgsConstructor -public final class PendingSessionMsgData { - - private final UUID sessionId; - private final Optional serverAddress; - private final SessionMsgType sessionMsgType; - private final int requestId; - private final boolean replyOnQueueAck; - private int ackMsgCount; - -} 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 f82a8c2481..669d94b17d 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 server; + private final UUID sessionId; + private final TransportProtos.SessionType type; + private final String nodeId; } diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/SessionMsgListener.java b/common/transport/src/main/java/org/thingsboard/server/common/transport/SessionMsgListener.java index 17d4a1f42c..09d1bcb08f 100644 --- a/common/transport/src/main/java/org/thingsboard/server/common/transport/SessionMsgListener.java +++ b/common/transport/src/main/java/org/thingsboard/server/common/transport/SessionMsgListener.java @@ -1,12 +1,12 @@ /** * 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 - *

+ * + * 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. @@ -15,8 +15,11 @@ */ package org.thingsboard.server.common.transport; +import org.thingsboard.server.gen.transport.TransportProtos.ToServerRpcResponseMsg; import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg; import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeResponseMsg; +import org.thingsboard.server.gen.transport.TransportProtos.SessionCloseNotificationProto; +import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcRequestMsg; /** * Created by ashvayka on 04.10.18. @@ -26,4 +29,10 @@ public interface SessionMsgListener { void onGetAttributesResponse(GetAttributeResponseMsg getAttributesResponse); void onAttributeUpdate(AttributeUpdateNotificationMsg attributeUpdateNotification); + + void onRemoteSessionCloseCommand(SessionCloseNotificationProto sessionCloseNotification); + + void onToDeviceRpcRequest(ToDeviceRpcRequestMsg toDeviceRequest); + + void onToServerRpcResponse(ToServerRpcResponseMsg toServerResponse); } diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/TransportService.java b/common/transport/src/main/java/org/thingsboard/server/common/transport/TransportService.java index bf33b1d8a5..cd3041939b 100644 --- a/common/transport/src/main/java/org/thingsboard/server/common/transport/TransportService.java +++ b/common/transport/src/main/java/org/thingsboard/server/common/transport/TransportService.java @@ -15,6 +15,8 @@ */ package org.thingsboard.server.common.transport; +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; @@ -49,6 +51,10 @@ public interface TransportService { void process(SessionInfoProto sessionInfo, SubscribeToRPCMsg msg, TransportServiceCallback callback); + void process(SessionInfoProto sessionInfo, ToDeviceRpcResponseMsg msg, TransportServiceCallback callback); + + void process(SessionInfoProto sessionInfo, ToServerRpcRequestMsg msg, TransportServiceCallback callback); + void registerSession(SessionInfoProto sessionInfo, SessionMsgListener listener); void deregisterSession(SessionInfoProto sessionInfo); diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java b/common/transport/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java index 872dfaa03e..db7ee884dc 100644 --- a/common/transport/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java +++ b/common/transport/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java @@ -1,12 +1,12 @@ /** * 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 - *

+ * + * 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. @@ -22,6 +22,7 @@ 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; @@ -95,6 +96,15 @@ public class JsonConverter { } } + 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(); @@ -112,14 +122,14 @@ public class JsonConverter { request.addTsKvList(builder.build()); } - public static void parseWithTs(PostTelemetryMsg.Builder request, JsonObject jo) { + 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()); } - public static List parseProtoValues(JsonObject valuesObject) { + private static List parseProtoValues(JsonObject valuesObject) { List result = new ArrayList<>(); for (Entry valueEntry : valuesObject.entrySet()) { JsonElement element = valueEntry.getValue(); @@ -172,9 +182,9 @@ public class JsonConverter { return request; } - public static ToServerRpcRequestMsg convertToServerRpcRequest(JsonElement json, int requestId) throws JsonSyntaxException { + public static TransportProtos.ToServerRpcRequestMsg convertToServerRpcRequest(JsonElement json, int requestId) throws JsonSyntaxException { JsonObject object = json.getAsJsonObject(); - return new ToServerRpcRequestMsg(requestId, object.get("method").getAsString(), GSON.toJson(object.get("params"))); + return TransportProtos.ToServerRpcRequestMsg.newBuilder().setRequestId(requestId).setMethodName(object.get("method").getAsString()).setParams(GSON.toJson(object.get("params"))).build(); } private static void parseObject(BasicTelemetryUploadRequest request, long systemTs, JsonElement jsonObject) { @@ -368,8 +378,14 @@ public class JsonConverter { return result; } - public static JsonElement toJson(ToServerRpcResponseMsg msg) { - return new JsonParser().parse(msg.getData()); + 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) { diff --git a/common/transport/src/main/proto/transport.proto b/common/transport/src/main/proto/transport.proto index 66c0d8183a..bcf5f529b9 100644 --- a/common/transport/src/main/proto/transport.proto +++ b/common/transport/src/main/proto/transport.proto @@ -137,6 +137,29 @@ 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; @@ -144,7 +167,9 @@ message TransportToDeviceActorMsg { PostAttributeMsg postAttributes = 4; GetAttributeRequestMsg getAttributes = 5; SubscribeToAttributeUpdatesMsg subscribeToAttributes = 6; - SubscribeToRPCMsg subscribeToRPC= 7; + SubscribeToRPCMsg subscribeToRPC = 7; + ToDeviceRpcResponseMsg toDeviceRPCCallResponse = 8; + ToServerRpcRequestMsg toServerRPCCallRequest = 9; } message DeviceActorToTransportMsg { @@ -153,6 +178,8 @@ message DeviceActorToTransportMsg { SessionCloseNotificationProto sessionCloseNotification = 3; GetAttributeResponseMsg getAttributesResponse = 4; AttributeUpdateNotificationMsg attributeUpdateNotification = 5; + ToDeviceRpcRequestMsg toDeviceRequest = 6; + ToServerRpcResponseMsg toServerResponse = 7; } /** diff --git a/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/JsonCoapAdaptor.java b/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/JsonCoapAdaptor.java index 1c9631129f..3cd414289c 100644 --- a/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/JsonCoapAdaptor.java +++ b/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/JsonCoapAdaptor.java @@ -98,7 +98,8 @@ public class JsonCoapAdaptor implements CoapTransportAdaptor { String payload = validatePayload(ctx, inbound); - return JsonConverter.convertToServerRpcRequest(new JsonParser().parse(payload), 0); +// return JsonConverter.convertToServerRpcRequest(new JsonParser().parse(payload), 0); + return null; } @Override @@ -225,8 +226,8 @@ public class JsonCoapAdaptor implements CoapTransportAdaptor { private Response convertToServerRpcResponse(SessionContext ctx, ToServerRpcResponseMsg msg) { if (msg.isSuccess()) { Response response = new Response(ResponseCode.CONTENT); - JsonElement result = JsonConverter.toJson(msg); - response.setPayload(result.toString()); +// JsonElement result = JsonConverter.toJson(msg); +// response.setPayload(result.toString()); return response; } else { return convertError(Optional.of(new RuntimeException("Server RPC response is empty!"))); diff --git a/transport/http/src/main/java/org/thingsboard/server/transport/http/session/HttpSessionCtx.java b/transport/http/src/main/java/org/thingsboard/server/transport/http/session/HttpSessionCtx.java index e503409b23..5269285d25 100644 --- a/transport/http/src/main/java/org/thingsboard/server/transport/http/session/HttpSessionCtx.java +++ b/transport/http/src/main/java/org/thingsboard/server/transport/http/session/HttpSessionCtx.java @@ -114,7 +114,7 @@ public class HttpSessionCtx extends DeviceAwareSessionContext { } private void reply(ToServerRpcResponseMsg msg) { - responseWriter.setResult(new ResponseEntity<>(JsonConverter.toJson(msg).toString(), HttpStatus.OK)); +// responseWriter.setResult(new ResponseEntity<>(JsonConverter.toJson(msg).toString(), HttpStatus.OK)); } private void reply(AttributesUpdateNotification msg) { diff --git a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java index b1ee037dc3..3099b24b87 100644 --- a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java +++ b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java @@ -1,12 +1,12 @@ /** * 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 - *

+ * + * 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. @@ -223,43 +223,18 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement } else if (topicName.startsWith(MqttTopics.DEVICE_ATTRIBUTES_REQUEST_TOPIC_PREFIX)) { TransportProtos.GetAttributeRequestMsg getAttributeMsg = adaptor.convertToGetAttributes(deviceSessionCtx, mqttMsg); transportService.process(sessionInfo, getAttributeMsg, getPubAckCallback(ctx, msgId, getAttributeMsg)); + } else if (topicName.startsWith(MqttTopics.DEVICE_RPC_RESPONSE_TOPIC)){ + TransportProtos.ToDeviceRpcResponseMsg rpcResponseMsg = adaptor.convertToDeviceRpcResponse(deviceSessionCtx, mqttMsg); + transportService.process(sessionInfo, rpcResponseMsg, getPubAckCallback(ctx, msgId, rpcResponseMsg)); + } else if (topicName.startsWith(MqttTopics.DEVICE_RPC_REQUESTS_TOPIC)){ + TransportProtos.ToServerRpcRequestMsg rpcRequestMsg = adaptor.convertToServerRpcRequest(deviceSessionCtx, mqttMsg); + transportService.process(sessionInfo, rpcRequestMsg, getPubAckCallback(ctx, msgId, rpcRequestMsg)); } } catch (AdaptorException e) { log.warn("[{}] Failed to process publish msg [{}][{}]", sessionId, topicName, msgId, e); log.info("[{}] Closing current session due to invalid publish msg [{}][{}]", sessionId, topicName, msgId); ctx.close(); } -// AdaptorToSessionActorMsg msg = null; -// try { -// if (topicName.equals(DEVICE_TELEMETRY_TOPIC)) { -// msg = adaptor.convertToActorMsg(deviceSessionCtx, POST_TELEMETRY_REQUEST, mqttMsg); -// } else if (topicName.equals(DEVICE_ATTRIBUTES_TOPIC)) { -// msg = adaptor.convertToActorMsg(deviceSessionCtx, POST_ATTRIBUTES_REQUEST, mqttMsg); -// } else if (topicName.startsWith(DEVICE_ATTRIBUTES_REQUEST_TOPIC_PREFIX)) { -// msg = adaptor.convertToActorMsg(deviceSessionCtx, GET_ATTRIBUTES_REQUEST, mqttMsg); -// if (msgId >= 0) { -// ctx.writeAndFlush(createMqttPubAckMsg(msgId)); -// } -// } else if (topicName.startsWith(DEVICE_RPC_RESPONSE_TOPIC)) { -// msg = adaptor.convertToActorMsg(deviceSessionCtx, TO_DEVICE_RPC_RESPONSE, mqttMsg); -// if (msgId >= 0) { -// ctx.writeAndFlush(createMqttPubAckMsg(msgId)); -// } -// } else if (topicName.startsWith(DEVICE_RPC_REQUESTS_TOPIC)) { -// msg = adaptor.convertToActorMsg(deviceSessionCtx, TO_SERVER_RPC_REQUEST, mqttMsg); -// if (msgId >= 0) { -// ctx.writeAndFlush(createMqttPubAckMsg(msgId)); -// } -// } -// } catch (AdaptorException e) { -// log.warn("[{}] Failed to process publish msg [{}][{}]", sessionId, topicName, msgId, e); -// } -// if (msg != null) { -// processor.process(new BasicTransportToDeviceSessionActorMsg(deviceSessionCtx.getDevice(), msg)); -// } else { -// log.info("[{}] Closing current session due to invalid publish msg [{}][{}]", sessionId, topicName, msgId); -// ctx.close(); -// } } private TransportServiceCallback getPubAckCallback(final ChannelHandlerContext ctx, final int msgId, final T msg) { @@ -555,4 +530,30 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement log.trace("[{}] Failed to convert device attributes update to MQTT msg", sessionId, e); } } + + @Override + public void onRemoteSessionCloseCommand(TransportProtos.SessionCloseNotificationProto sessionCloseNotification) { + log.trace("[{}] Received the remote command to close the session", sessionId); + processDisconnect(deviceSessionCtx.getChannel()); + } + + @Override + public void onToDeviceRpcRequest(TransportProtos.ToDeviceRpcRequestMsg rpcRequest) { + log.trace("[{}] Received RPC command to device", sessionId); + try { + adaptor.convertToPublish(deviceSessionCtx, rpcRequest).ifPresent(deviceSessionCtx.getChannel()::writeAndFlush); + } catch (Exception e) { + log.trace("[{}] Failed to convert device RPC commandto MQTT msg", sessionId, e); + } + } + + @Override + public void onToServerRpcResponse(TransportProtos.ToServerRpcResponseMsg rpcResponse) { + log.trace("[{}] Received RPC command to device", sessionId); + try { + adaptor.convertToPublish(deviceSessionCtx, rpcResponse).ifPresent(deviceSessionCtx.getChannel()::writeAndFlush); + } catch (Exception e) { + log.trace("[{}] Failed to convert device RPC commandto MQTT msg", sessionId, e); + } + } } diff --git a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java index d4ab033cee..a2be169656 100644 --- a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java +++ b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java @@ -1,12 +1,12 @@ /** * 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 - *

+ * + * 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. @@ -17,7 +17,6 @@ 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; @@ -98,6 +97,31 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor { } } + @Override + public TransportProtos.ToDeviceRpcResponseMsg convertToDeviceRpcResponse(DeviceSessionCtx 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(DeviceSessionCtx 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 convertToPublish(DeviceSessionCtx ctx, TransportProtos.GetAttributeResponseMsg responseMsg) throws AdaptorException { if (!StringUtils.isEmpty(responseMsg.getError())) { @@ -115,9 +139,17 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor { @Override public Optional convertToPublish(DeviceSessionCtx ctx, TransportProtos.AttributeUpdateNotificationMsg notificationMsg) throws AdaptorException { - return Optional.of(createMqttPublishMsg(ctx, - MqttTopics.DEVICE_ATTRIBUTES_TOPIC, - JsonConverter.toJson(notificationMsg))); + return Optional.of(createMqttPublishMsg(ctx, MqttTopics.DEVICE_ATTRIBUTES_TOPIC, JsonConverter.toJson(notificationMsg))); + } + + @Override + public Optional convertToPublish(DeviceSessionCtx 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 convertToPublish(DeviceSessionCtx ctx, TransportProtos.ToServerRpcResponseMsg rpcResponse) { + return Optional.of(createMqttPublishMsg(ctx, MqttTopics.DEVICE_RPC_RESPONSE_TOPIC + rpcResponse.getRequestId(), JsonConverter.toJson(rpcResponse))); } @Override @@ -149,7 +181,7 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor { msg = convertToRpcCommandResponse(ctx, (MqttPublishMessage) inbound); break; case TO_SERVER_RPC_REQUEST: - msg = convertToServerRpcRequest(ctx, (MqttPublishMessage) inbound); + msg = null;//convertToServerRpcRequest(ctx, (MqttPublishMessage) inbound); break; default: log.warn("[{}] Unsupported msg type: {}!", ctx.getSessionId(), type); @@ -181,13 +213,12 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor { break; case TO_DEVICE_RPC_REQUEST: ToDeviceRpcRequestMsg rpcRequest = (ToDeviceRpcRequestMsg) msg; - result = createMqttPublishMsg(ctx, MqttTopics.DEVICE_RPC_REQUESTS_TOPIC + rpcRequest.getRequestId(), - rpcRequest); + result = createMqttPublishMsg(ctx, MqttTopics.DEVICE_RPC_REQUESTS_TOPIC + rpcRequest.getRequestId(), rpcRequest); break; case TO_SERVER_RPC_RESPONSE: - ToServerRpcResponseMsg rpcResponse = (ToServerRpcResponseMsg) msg; - result = createMqttPublishMsg(ctx, MqttTopics.DEVICE_RPC_RESPONSE_TOPIC + rpcResponse.getRequestId(), - rpcResponse); +// ToServerRpcResponseMsg rpcResponse = (ToServerRpcResponseMsg) msg; +// result = createMqttPublishMsg(ctx, MqttTopics.DEVICE_RPC_RESPONSE_TOPIC + rpcResponse.getRequestId(), +// rpcResponse); break; case RULE_ENGINE_ERROR: RuleEngineErrorMsg errorMsg = (RuleEngineErrorMsg) msg; @@ -232,7 +263,7 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor { return createMqttPublishMsg(ctx, topic, JsonConverter.toJson(msg, false)); } - private MqttPublishMessage createMqttPublishMsg(DeviceSessionCtx ctx, String topic, ToServerRpcResponseMsg msg) { + private MqttPublishMessage createMqttPublishMsg(DeviceSessionCtx ctx, String topic, TransportProtos.ToServerRpcResponseMsg msg) { return createMqttPublishMsg(ctx, topic, JsonConverter.toJson(msg)); } @@ -290,7 +321,7 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor { private AttributesUpdateRequest convertToUpdateAttributesRequest(SessionContext ctx, MqttPublishMessage inbound) throws AdaptorException { String payload = validatePayload(ctx.getSessionId(), inbound.payload()); try { - return JsonConverter.convertToAttributes(new JsonParser().parse(payload), inbound.variableHeader().messageId()); + return JsonConverter.convertToAttributes(new JsonParser().parse(payload), inbound.variableHeader().packetId()); } catch (IllegalStateException | JsonSyntaxException ex) { throw new AdaptorException(ex); } @@ -299,18 +330,7 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor { private TelemetryUploadRequest convertToTelemetryUploadRequest(SessionContext ctx, MqttPublishMessage inbound) throws AdaptorException { String payload = validatePayload(ctx.getSessionId(), inbound.payload()); try { - return JsonConverter.convertToTelemetry(new JsonParser().parse(payload), inbound.variableHeader().messageId()); - } catch (IllegalStateException | JsonSyntaxException ex) { - throw new AdaptorException(ex); - } - } - - private FromDeviceMsg convertToServerRpcRequest(DeviceSessionCtx 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); + return JsonConverter.convertToTelemetry(new JsonParser().parse(payload), inbound.variableHeader().packetId()); } catch (IllegalStateException | JsonSyntaxException ex) { throw new AdaptorException(ex); } diff --git a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/MqttTransportAdaptor.java b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/MqttTransportAdaptor.java index 602fde12f6..48173f968a 100644 --- a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/MqttTransportAdaptor.java +++ b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/MqttTransportAdaptor.java @@ -19,7 +19,15 @@ import io.netty.handler.codec.mqtt.MqttMessage; import io.netty.handler.codec.mqtt.MqttPublishMessage; import org.thingsboard.server.common.transport.TransportAdaptor; import org.thingsboard.server.common.transport.adaptor.AdaptorException; -import org.thingsboard.server.gen.transport.TransportProtos; +import org.thingsboard.server.gen.transport.TransportProtos.ToServerRpcResponseMsg; +import org.thingsboard.server.gen.transport.TransportProtos.ToServerRpcRequestMsg; +import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcResponseMsg; +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.transport.mqtt.session.DeviceSessionCtx; import java.util.Optional; @@ -29,15 +37,21 @@ import java.util.Optional; */ public interface MqttTransportAdaptor extends TransportAdaptor { - TransportProtos.PostTelemetryMsg convertToPostTelemetry(DeviceSessionCtx ctx, MqttPublishMessage inbound) throws AdaptorException; + PostTelemetryMsg convertToPostTelemetry(DeviceSessionCtx ctx, MqttPublishMessage inbound) throws AdaptorException; - TransportProtos.PostAttributeMsg convertToPostAttributes(DeviceSessionCtx ctx, MqttPublishMessage inbound) throws AdaptorException; + PostAttributeMsg convertToPostAttributes(DeviceSessionCtx ctx, MqttPublishMessage inbound) throws AdaptorException; - TransportProtos.GetAttributeRequestMsg convertToGetAttributes(DeviceSessionCtx ctx, MqttPublishMessage inbound) throws AdaptorException; + GetAttributeRequestMsg convertToGetAttributes(DeviceSessionCtx ctx, MqttPublishMessage inbound) throws AdaptorException; - Optional convertToPublish(DeviceSessionCtx ctx, TransportProtos.GetAttributeResponseMsg responseMsg) throws AdaptorException; + ToDeviceRpcResponseMsg convertToDeviceRpcResponse(DeviceSessionCtx ctx, MqttPublishMessage mqttMsg) throws AdaptorException; - Optional convertToPublish(DeviceSessionCtx ctx, TransportProtos.AttributeUpdateNotificationMsg notificationMsg) throws AdaptorException; + ToServerRpcRequestMsg convertToServerRpcRequest(DeviceSessionCtx ctx, MqttPublishMessage mqttMsg) throws AdaptorException; + Optional convertToPublish(DeviceSessionCtx ctx, GetAttributeResponseMsg responseMsg) throws AdaptorException; + Optional convertToPublish(DeviceSessionCtx ctx, AttributeUpdateNotificationMsg notificationMsg) throws AdaptorException; + + Optional convertToPublish(DeviceSessionCtx ctx, ToDeviceRpcRequestMsg rpcRequest) throws AdaptorException; + + Optional convertToPublish(DeviceSessionCtx ctx, ToServerRpcResponseMsg rpcResponse); } diff --git a/transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/MqttTransportService.java b/transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/MqttTransportService.java index 648b01c9c3..15392386c4 100644 --- a/transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/MqttTransportService.java +++ b/transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/MqttTransportService.java @@ -1,12 +1,12 @@ /** * 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 - *

+ * + * 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. @@ -164,6 +164,15 @@ public class MqttTransportService implements TransportService { 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()); + } }); } else { //TODO: should we notify the device actor about missed session? @@ -272,6 +281,24 @@ public class MqttTransportService implements TransportService { send(sessionInfo, toRuleEngineMsg, callback); } + @Override + public void process(SessionInfoProto sessionInfo, TransportProtos.ToDeviceRpcResponseMsg msg, TransportServiceCallback callback) { + ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg( + TransportProtos.TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) + .setToDeviceRPCCallResponse(msg).build() + ).build(); + send(sessionInfo, toRuleEngineMsg, callback); + } + + @Override + public void process(SessionInfoProto sessionInfo, TransportProtos.ToServerRpcRequestMsg msg, TransportServiceCallback callback) { + ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg( + TransportProtos.TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) + .setToServerRPCCallRequest(msg).build() + ).build(); + send(sessionInfo, toRuleEngineMsg, callback); + } + @Override public void registerSession(SessionInfoProto sessionInfo, SessionMsgListener listener) { sessions.putIfAbsent(toId(sessionInfo), listener); From e5a1fb58654cb5c6b03e8007eae34f5422625a7e Mon Sep 17 00:00:00 2001 From: Andrew Shvayka Date: Thu, 11 Oct 2018 19:29:21 +0300 Subject: [PATCH 11/26] Refactoring of Gateway API --- .../device/DeviceActorMessageProcessor.java | 23 - .../actors/shared/SessionTimeoutMsg.java | 1 - .../transport/RemoteTransportApiService.java | 72 ++- .../server/common/data/id/SessionId.java | 24 - .../common/msg/aware/SessionAwareMsg.java | 2 - .../common/msg/core/SessionOpenMsg.java | 3 - .../common/msg/session/BasicSessionMsg.java | 2 - ...BasicTransportToDeviceSessionActorMsg.java | 1 - .../common/msg/session/SessionContext.java | 24 +- .../msg/session/ctrl/SessionCloseMsg.java | 1 - .../common/transport/TransportAdaptor.java | 11 - .../common/transport/TransportService.java | 4 + .../transport/adaptor/JsonConverter.java | 62 ++- .../session/DeviceAwareSessionContext.java | 9 +- .../transport/src/main/proto/transport.proto | 13 + .../transport/coap/CoapTransportResource.java | 1 - .../coap/session/CoapSessionCtx.java | 4 - .../transport/coap/session/CoapSessionId.java | 2 - .../http/session/HttpSessionCtx.java | 1 - .../transport/http/session/HttpSessionId.java | 2 - .../transport/mqtt/MqttTransportContext.java | 23 +- .../transport/mqtt/MqttTransportHandler.java | 76 ++- .../mqtt/adaptors/JsonMqttAdaptor.java | 141 ++---- .../mqtt/adaptors/MqttTransportAdaptor.java | 33 +- .../mqtt/session/DeviceSessionCtx.java | 23 +- .../mqtt/session/GatewayDeviceSessionCtx.java | 129 +++-- .../mqtt/session/GatewaySessionCtx.java | 450 ++++++++++-------- .../MqttDeviceAwareSessionContext.java | 7 +- .../transport/mqtt/session/MqttSessionId.java | 3 - .../mqtt/service/MqttTransportService.java | 47 +- 30 files changed, 635 insertions(+), 559 deletions(-) delete mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/id/SessionId.java 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 c590104665..1cfdcd202a 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 @@ -411,29 +411,6 @@ 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()); -// } -// } -// } - void processClusterEventMsg(ClusterEventMsg msg) { // if (!msg.isAdded()) { // logger.debug("[{}] Clearing attributes/rpc subscription for server [{}]", deviceId, msg.getServerAddress()); diff --git a/application/src/main/java/org/thingsboard/server/actors/shared/SessionTimeoutMsg.java b/application/src/main/java/org/thingsboard/server/actors/shared/SessionTimeoutMsg.java index d015fe01de..d89848b8a8 100644 --- a/application/src/main/java/org/thingsboard/server/actors/shared/SessionTimeoutMsg.java +++ b/application/src/main/java/org/thingsboard/server/actors/shared/SessionTimeoutMsg.java @@ -16,7 +16,6 @@ package org.thingsboard.server.actors.shared; import lombok.Data; -import org.thingsboard.server.common.data.id.SessionId; import org.thingsboard.server.common.msg.MsgType; import org.thingsboard.server.common.msg.TbActorMsg; 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 index 6e9b2efa0a..5bcd8ac888 100644 --- a/application/src/main/java/org/thingsboard/server/service/transport/RemoteTransportApiService.java +++ b/application/src/main/java/org/thingsboard/server/service/transport/RemoteTransportApiService.java @@ -1,12 +1,12 @@ /** * 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 - * + *

+ * 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. @@ -24,11 +24,16 @@ 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.GetOrCreateDeviceFromGatewayRequestMsg; +import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFromGatewayResponseMsg; import org.thingsboard.server.gen.transport.TransportProtos.DeviceInfoProto; import org.thingsboard.server.gen.transport.TransportProtos.TransportApiRequestMsg; import org.thingsboard.server.gen.transport.TransportProtos.TransportApiResponseMsg; @@ -40,8 +45,10 @@ 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.state.DeviceStateService; import javax.annotation.PostConstruct; +import java.util.UUID; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -77,9 +84,15 @@ public class RemoteTransportApiService implements TransportApiService { @Autowired private DeviceService deviceService; + @Autowired + private RelationService relationService; + @Autowired private DeviceCredentialsService deviceCredentialsService; + @Autowired + private DeviceStateService deviceStateService; + private ExecutorService transportCallbackExecutor; private TbKafkaResponseTemplate transportApiTemplate; @@ -123,6 +136,8 @@ public class RemoteTransportApiService implements TransportApiService { } 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(); } @@ -137,6 +152,32 @@ public class RemoteTransportApiService implements TransportApiService { } } + private ListenableFuture handle(GetOrCreateDeviceFromGatewayRequestMsg requestMsg) { + DeviceId gatewayId = new DeviceId(new UUID(requestMsg.getGatewayIdMSB(), requestMsg.getGatewayIdLSB())); + ListenableFuture gatewayFuture = deviceService.findDeviceByIdAsync(gatewayId); + return Futures.transform(gatewayFuture, gateway -> { + Device device = deviceService.findDeviceByTenantIdAndName(gateway.getTenantId(), gateway.getName()); + 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); + } + try { + 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); + } + }, transportCallbackExecutor); + } + + private ListenableFuture getDeviceInfo(DeviceId deviceId) { return Futures.transform(deviceService.findDeviceByIdAsync(deviceId), device -> { if (device == null) { @@ -144,17 +185,8 @@ public class RemoteTransportApiService implements TransportApiService { return getEmptyTransportApiResponse(); } try { - DeviceInfoProto deviceInfoProto = 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(); return TransportApiResponseMsg.newBuilder() - .setValidateTokenResponseMsg(ValidateDeviceCredentialsResponseMsg.newBuilder().setDeviceInfo(deviceInfoProto).build()).build(); + .setValidateTokenResponseMsg(ValidateDeviceCredentialsResponseMsg.newBuilder().setDeviceInfo(getDeviceInfoProto(device)).build()).build(); } catch (JsonProcessingException e) { log.warn("[{}] Failed to lookup device by id", deviceId, e); return getEmptyTransportApiResponse(); @@ -162,6 +194,18 @@ public class RemoteTransportApiService implements TransportApiService { }); } + 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 getEmptyTransportApiResponseFuture() { return Futures.immediateFuture(getEmptyTransportApiResponse()); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/SessionId.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/SessionId.java deleted file mode 100644 index 1b4772768f..0000000000 --- a/common/data/src/main/java/org/thingsboard/server/common/data/id/SessionId.java +++ /dev/null @@ -1,24 +0,0 @@ -/** - * 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.data.id; - -import java.io.Serializable; - -public interface SessionId extends Serializable { - - String toUidStr(); - -} diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/aware/SessionAwareMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/aware/SessionAwareMsg.java index db6ff6f632..1c258239d4 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/aware/SessionAwareMsg.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/aware/SessionAwareMsg.java @@ -15,8 +15,6 @@ */ package org.thingsboard.server.common.msg.aware; -import org.thingsboard.server.common.data.id.SessionId; - public interface SessionAwareMsg { SessionId getSessionId(); diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/SessionOpenMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/core/SessionOpenMsg.java index 28e319e525..d8af5a953c 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/SessionOpenMsg.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/core/SessionOpenMsg.java @@ -16,11 +16,8 @@ package org.thingsboard.server.common.msg.core; import lombok.Data; -import org.thingsboard.server.common.data.id.SessionId; -import org.thingsboard.server.common.msg.aware.SessionAwareMsg; import org.thingsboard.server.common.msg.session.FromDeviceMsg; import org.thingsboard.server.common.msg.session.SessionMsgType; -import org.thingsboard.server.common.msg.session.SessionMsgType; /** * @author Andrew Shvayka diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/session/BasicSessionMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/session/BasicSessionMsg.java index 855cce53d7..84c5052662 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/session/BasicSessionMsg.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/session/BasicSessionMsg.java @@ -15,8 +15,6 @@ */ package org.thingsboard.server.common.msg.session; -import org.thingsboard.server.common.data.id.SessionId; - public class BasicSessionMsg implements SessionMsg { private final SessionContext ctx; diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/session/BasicTransportToDeviceSessionActorMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/session/BasicTransportToDeviceSessionActorMsg.java index 0704e6e1bf..4a81fd4578 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/session/BasicTransportToDeviceSessionActorMsg.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/session/BasicTransportToDeviceSessionActorMsg.java @@ -18,7 +18,6 @@ package org.thingsboard.server.common.msg.session; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.id.CustomerId; 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.msg.MsgType; 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 2f2d88047e..6607295ee3 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 @@ -1,12 +1,12 @@ /** * 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 - * + *

+ * 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. @@ -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(); } diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/session/ctrl/SessionCloseMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/session/ctrl/SessionCloseMsg.java index 49fad13236..22a13d03b5 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/session/ctrl/SessionCloseMsg.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/session/ctrl/SessionCloseMsg.java @@ -15,7 +15,6 @@ */ package org.thingsboard.server.common.msg.session.ctrl; -import org.thingsboard.server.common.data.id.SessionId; import org.thingsboard.server.common.msg.MsgType; import org.thingsboard.server.common.msg.session.SessionCtrlMsg; diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/TransportAdaptor.java b/common/transport/src/main/java/org/thingsboard/server/common/transport/TransportAdaptor.java index 86067d536e..b59d49362b 100644 --- a/common/transport/src/main/java/org/thingsboard/server/common/transport/TransportAdaptor.java +++ b/common/transport/src/main/java/org/thingsboard/server/common/transport/TransportAdaptor.java @@ -15,19 +15,8 @@ */ package org.thingsboard.server.common.transport; -import org.thingsboard.server.common.msg.session.AdaptorToSessionActorMsg; -import org.thingsboard.server.common.msg.session.SessionMsgType; -import org.thingsboard.server.common.msg.session.SessionActorToAdaptorMsg; import org.thingsboard.server.common.msg.session.SessionContext; -import org.thingsboard.server.common.transport.adaptor.AdaptorException; -import org.thingsboard.server.gen.transport.TransportProtos; - -import java.util.Optional; public interface TransportAdaptor { - AdaptorToSessionActorMsg convertToActorMsg(C ctx, SessionMsgType type, T inbound) throws AdaptorException; - - Optional convertToAdaptorMsg(C ctx, SessionActorToAdaptorMsg msg) throws AdaptorException; - } diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/TransportService.java b/common/transport/src/main/java/org/thingsboard/server/common/transport/TransportService.java index cd3041939b..c9a04c4daf 100644 --- a/common/transport/src/main/java/org/thingsboard/server/common/transport/TransportService.java +++ b/common/transport/src/main/java/org/thingsboard/server/common/transport/TransportService.java @@ -15,6 +15,7 @@ */ 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; @@ -39,6 +40,9 @@ public interface TransportService { void process(ValidateDeviceX509CertRequestMsg msg, TransportServiceCallback callback); + void process(TransportProtos.GetOrCreateDeviceFromGatewayRequestMsg msg, + TransportServiceCallback callback); + void process(SessionInfoProto sessionInfo, SessionEventMsg msg, TransportServiceCallback callback); void process(SessionInfoProto sessionInfo, PostTelemetryMsg msg, TransportServiceCallback callback); diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java b/common/transport/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java index db7ee884dc..e02bb4bf34 100644 --- a/common/transport/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java +++ b/common/transport/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java @@ -1,12 +1,12 @@ /** * 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 - * + *

+ * 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. @@ -52,7 +52,8 @@ import java.util.stream.Collectors; public class JsonConverter { private static final Gson GSON = new Gson(); - public static final String CAN_T_PARSE_VALUE = "Can't parse value: "; + private static final String CAN_T_PARSE_VALUE = "Can't parse value: "; + private static final String DEVICE_PROPERTY = "device"; public static TelemetryUploadRequest convertToTelemetry(JsonElement jsonObject) throws JsonSyntaxException { return convertToTelemetry(jsonObject, BasicRequest.DEFAULT_REQUEST_ID); @@ -318,6 +319,57 @@ public class JsonConverter { 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 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 addToObject(JsonArray result) { return key -> result.add(key.getAttributeKey()); } diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/session/DeviceAwareSessionContext.java b/common/transport/src/main/java/org/thingsboard/server/common/transport/session/DeviceAwareSessionContext.java index b46cfb81df..93921d347f 100644 --- a/common/transport/src/main/java/org/thingsboard/server/common/transport/session/DeviceAwareSessionContext.java +++ b/common/transport/src/main/java/org/thingsboard/server/common/transport/session/DeviceAwareSessionContext.java @@ -31,16 +31,9 @@ public abstract class DeviceAwareSessionContext implements SessionContext { @Getter private volatile DeviceId deviceId; + @Getter private volatile DeviceInfoProto deviceInfo; - public long getDeviceIdMSB() { - return deviceInfo.getDeviceIdMSB(); - } - - public long getDeviceIdLSB() { - return deviceInfo.getDeviceIdLSB(); - } - public DeviceId getDeviceId() { return deviceId; } diff --git a/common/transport/src/main/proto/transport.proto b/common/transport/src/main/proto/transport.proto index bcf5f529b9..3099c5c7f6 100644 --- a/common/transport/src/main/proto/transport.proto +++ b/common/transport/src/main/proto/transport.proto @@ -125,6 +125,17 @@ message ValidateDeviceCredentialsResponseMsg { DeviceInfoProto deviceInfo = 1; } +message GetOrCreateDeviceFromGatewayRequestMsg { + int64 gatewayIdMSB = 1; + int64 gatewayIdLSB = 2; + string deviceName = 3; + string deviceType = 4; +} + +message GetOrCreateDeviceFromGatewayResponseMsg { + DeviceInfoProto deviceInfo = 1; +} + message SessionCloseNotificationProto { string message = 1; } @@ -196,8 +207,10 @@ message ToTransportMsg { 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/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java b/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java index 0853124acb..88fb69ecd0 100644 --- a/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java +++ b/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java @@ -27,7 +27,6 @@ import org.eclipse.californium.core.network.Exchange; import org.eclipse.californium.core.network.ExchangeObserver; import org.eclipse.californium.core.server.resources.CoapExchange; import org.eclipse.californium.core.server.resources.Resource; -import org.thingsboard.server.common.data.id.SessionId; import org.thingsboard.server.common.data.security.DeviceCredentialsFilter; import org.thingsboard.server.common.data.security.DeviceTokenCredentials; import org.thingsboard.server.common.msg.session.*; diff --git a/transport/coap/src/main/java/org/thingsboard/server/transport/coap/session/CoapSessionCtx.java b/transport/coap/src/main/java/org/thingsboard/server/transport/coap/session/CoapSessionCtx.java index c43a4ded7a..efabc4eac2 100644 --- a/transport/coap/src/main/java/org/thingsboard/server/transport/coap/session/CoapSessionCtx.java +++ b/transport/coap/src/main/java/org/thingsboard/server/transport/coap/session/CoapSessionCtx.java @@ -20,20 +20,16 @@ import org.eclipse.californium.core.coap.CoAP.ResponseCode; import org.eclipse.californium.core.coap.Request; import org.eclipse.californium.core.coap.Response; import org.eclipse.californium.core.server.resources.CoapExchange; -import org.thingsboard.server.common.data.id.SessionId; import org.thingsboard.server.common.msg.session.SessionActorToAdaptorMsg; import org.thingsboard.server.common.msg.session.SessionCtrlMsg; import org.thingsboard.server.common.msg.session.SessionType; import org.thingsboard.server.common.msg.session.ctrl.SessionCloseMsg; -import org.thingsboard.server.common.msg.session.ex.SessionAuthException; import org.thingsboard.server.common.msg.session.ex.SessionException; import org.thingsboard.server.common.transport.SessionMsgProcessor; import org.thingsboard.server.common.transport.adaptor.AdaptorException; import org.thingsboard.server.common.transport.auth.DeviceAuthService; import org.thingsboard.server.common.transport.session.DeviceAwareSessionContext; import org.thingsboard.server.transport.coap.adaptors.CoapTransportAdaptor; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import java.util.concurrent.atomic.AtomicInteger; diff --git a/transport/coap/src/main/java/org/thingsboard/server/transport/coap/session/CoapSessionId.java b/transport/coap/src/main/java/org/thingsboard/server/transport/coap/session/CoapSessionId.java index 888fb2fc30..2bfcf1d2c6 100644 --- a/transport/coap/src/main/java/org/thingsboard/server/transport/coap/session/CoapSessionId.java +++ b/transport/coap/src/main/java/org/thingsboard/server/transport/coap/session/CoapSessionId.java @@ -15,8 +15,6 @@ */ package org.thingsboard.server.transport.coap.session; -import org.thingsboard.server.common.data.id.SessionId; - public final class CoapSessionId implements SessionId { private final String clientAddress; diff --git a/transport/http/src/main/java/org/thingsboard/server/transport/http/session/HttpSessionCtx.java b/transport/http/src/main/java/org/thingsboard/server/transport/http/session/HttpSessionCtx.java index 5269285d25..62999cc64b 100644 --- a/transport/http/src/main/java/org/thingsboard/server/transport/http/session/HttpSessionCtx.java +++ b/transport/http/src/main/java/org/thingsboard/server/transport/http/session/HttpSessionCtx.java @@ -20,7 +20,6 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.context.request.async.DeferredResult; -import org.thingsboard.server.common.data.id.SessionId; import org.thingsboard.server.common.msg.core.*; import org.thingsboard.server.common.msg.session.*; import org.thingsboard.server.common.msg.session.ex.SessionException; diff --git a/transport/http/src/main/java/org/thingsboard/server/transport/http/session/HttpSessionId.java b/transport/http/src/main/java/org/thingsboard/server/transport/http/session/HttpSessionId.java index 3211765334..a5426ca051 100644 --- a/transport/http/src/main/java/org/thingsboard/server/transport/http/session/HttpSessionId.java +++ b/transport/http/src/main/java/org/thingsboard/server/transport/http/session/HttpSessionId.java @@ -15,8 +15,6 @@ */ package org.thingsboard.server.transport.http.session; -import org.thingsboard.server.common.data.id.SessionId; - import java.util.UUID; /** diff --git a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportContext.java b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportContext.java index b500a31e69..2c8ff59f1f 100644 --- a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportContext.java +++ b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportContext.java @@ -1,12 +1,12 @@ /** * 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 - * + *

+ * 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. @@ -18,6 +18,7 @@ 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.extern.slf4j.Slf4j; import org.apache.commons.lang3.RandomStringUtils; import org.springframework.beans.factory.annotation.Autowired; @@ -30,8 +31,11 @@ import org.thingsboard.server.common.transport.quota.host.HostRequestsQuotaServi import org.thingsboard.server.transport.mqtt.adaptors.MqttTransportAdaptor; import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; import java.net.InetAddress; import java.net.UnknownHostException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; /** * Created by ashvayka on 04.10.18. @@ -64,6 +68,9 @@ public class MqttTransportContext { private SslHandler sslHandler; + @Getter + private ExecutorService executor; + @PostConstruct public void init() { if (StringUtils.isEmpty(nodeId)) { @@ -74,6 +81,14 @@ public class MqttTransportContext { } } log.info("Current NodeId: {}", nodeId); + executor = Executors.newCachedThreadPool(); + } + + @PreDestroy + public void stop() { + if (executor != null) { + executor.shutdownNow(); + } } } diff --git a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java index 3099b24b87..21d33354cd 100644 --- a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java +++ b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java @@ -1,12 +1,12 @@ /** * 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 - * + *

+ * 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. @@ -108,7 +108,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement this.quotaService = context.getQuotaService(); this.sslHandler = context.getSslHandler(); this.mqttQoSMap = new ConcurrentHashMap<>(); - this.deviceSessionCtx = new DeviceSessionCtx(mqttQoSMap); + this.deviceSessionCtx = new DeviceSessionCtx(sessionId, mqttQoSMap); } @Override @@ -176,42 +176,40 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement if (topicName.startsWith(MqttTopics.BASE_GATEWAY_API_TOPIC)) { if (gatewaySessionCtx != null) { - gatewaySessionCtx.setChannel(ctx); -// handleMqttPublishMsg(topicName, msgId, mqttMsg); + handleGatewayPublishMsg(topicName, msgId, mqttMsg); } } else { processDevicePublish(ctx, mqttMsg, topicName, msgId); } } - // -// private void handleMqttPublishMsg(String topicName, int msgId, MqttPublishMessage mqttMsg) { -// try { -// switch (topicName) { -// case GATEWAY_TELEMETRY_TOPIC: -// gatewaySessionCtx.onDeviceTelemetry(mqttMsg); -// break; -// case GATEWAY_ATTRIBUTES_TOPIC: -// gatewaySessionCtx.onDeviceAttributes(mqttMsg); -// break; -// case GATEWAY_ATTRIBUTES_REQUEST_TOPIC: -// gatewaySessionCtx.onDeviceAttributesRequest(mqttMsg); -// break; -// case GATEWAY_RPC_TOPIC: -// gatewaySessionCtx.onDeviceRpcResponse(mqttMsg); -// break; -// case GATEWAY_CONNECT_TOPIC: -// gatewaySessionCtx.onDeviceConnect(mqttMsg); -// break; -// case GATEWAY_DISCONNECT_TOPIC: -// gatewaySessionCtx.onDeviceDisconnect(mqttMsg); -// break; -// } -// } catch (RuntimeException | AdaptorException e) { -// log.warn("[{}] Failed to process publish msg [{}][{}]", sessionId, topicName, msgId, e); -// } -// } -// + private void handleGatewayPublishMsg(String topicName, int msgId, MqttPublishMessage mqttMsg) { + try { + switch (topicName) { + case MqttTopics.GATEWAY_TELEMETRY_TOPIC: + gatewaySessionCtx.onDeviceTelemetry(mqttMsg); + break; + case MqttTopics.GATEWAY_ATTRIBUTES_TOPIC: + gatewaySessionCtx.onDeviceAttributes(mqttMsg); + break; + case MqttTopics.GATEWAY_ATTRIBUTES_REQUEST_TOPIC: + gatewaySessionCtx.onDeviceAttributesRequest(mqttMsg); + break; + case MqttTopics.GATEWAY_RPC_TOPIC: + gatewaySessionCtx.onDeviceRpcResponse(mqttMsg); + break; + case MqttTopics.GATEWAY_CONNECT_TOPIC: + gatewaySessionCtx.onDeviceConnect(mqttMsg); + break; + case MqttTopics.GATEWAY_DISCONNECT_TOPIC: + gatewaySessionCtx.onDeviceDisconnect(mqttMsg); + break; + } + } catch (RuntimeException | AdaptorException e) { + log.warn("[{}] Failed to process publish msg [{}][{}]", sessionId, topicName, msgId, e); + } + } + private void processDevicePublish(ChannelHandlerContext ctx, MqttPublishMessage mqttMsg, String topicName, int msgId) { try { if (topicName.equals(MqttTopics.DEVICE_TELEMETRY_TOPIC)) { @@ -223,10 +221,10 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement } else if (topicName.startsWith(MqttTopics.DEVICE_ATTRIBUTES_REQUEST_TOPIC_PREFIX)) { TransportProtos.GetAttributeRequestMsg getAttributeMsg = adaptor.convertToGetAttributes(deviceSessionCtx, mqttMsg); transportService.process(sessionInfo, getAttributeMsg, getPubAckCallback(ctx, msgId, getAttributeMsg)); - } else if (topicName.startsWith(MqttTopics.DEVICE_RPC_RESPONSE_TOPIC)){ + } else if (topicName.startsWith(MqttTopics.DEVICE_RPC_RESPONSE_TOPIC)) { TransportProtos.ToDeviceRpcResponseMsg rpcResponseMsg = adaptor.convertToDeviceRpcResponse(deviceSessionCtx, mqttMsg); transportService.process(sessionInfo, rpcResponseMsg, getPubAckCallback(ctx, msgId, rpcResponseMsg)); - } else if (topicName.startsWith(MqttTopics.DEVICE_RPC_REQUESTS_TOPIC)){ + } else if (topicName.startsWith(MqttTopics.DEVICE_RPC_REQUESTS_TOPIC)) { TransportProtos.ToServerRpcRequestMsg rpcRequestMsg = adaptor.convertToServerRpcRequest(deviceSessionCtx, mqttMsg); transportService.process(sessionInfo, rpcRequestMsg, getPubAckCallback(ctx, msgId, rpcRequestMsg)); } @@ -469,7 +467,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement if (infoNode != null) { JsonNode gatewayNode = infoNode.get("gateway"); if (gatewayNode != null && gatewayNode.asBoolean()) { - gatewaySessionCtx = new GatewaySessionCtx(deviceSessionCtx); + gatewaySessionCtx = new GatewaySessionCtx(context, deviceSessionCtx, sessionId); } } } catch (IOException e) { @@ -477,7 +475,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement } } - private SessionEventMsg getSessionEventMsg(SessionEvent event) { + public static SessionEventMsg getSessionEventMsg(SessionEvent event) { return SessionEventMsg.newBuilder() .setSessionType(TransportProtos.SessionType.ASYNC) .setEvent(event).build(); diff --git a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java index a2be169656..85dc813428 100644 --- a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java +++ b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java @@ -1,12 +1,12 @@ /** * 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 - * + *

+ * 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. @@ -17,6 +17,7 @@ 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; @@ -26,7 +27,8 @@ import io.netty.handler.codec.mqtt.*; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; -import org.thingsboard.server.common.data.id.SessionId; +import org.thingsboard.server.common.data.kv.AttributeKvEntry; +import org.thingsboard.server.common.data.kv.KvEntry; import org.thingsboard.server.common.msg.core.*; import org.thingsboard.server.common.msg.kv.AttributesKVMsg; import org.thingsboard.server.common.msg.session.*; @@ -36,12 +38,15 @@ import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.transport.mqtt.MqttTopics; import org.thingsboard.server.transport.mqtt.session.DeviceSessionCtx; import org.thingsboard.server.transport.mqtt.MqttTransportHandler; +import org.thingsboard.server.transport.mqtt.session.MqttDeviceAwareSessionContext; import java.nio.charset.Charset; import java.util.Arrays; import java.util.HashSet; +import java.util.List; import java.util.Optional; import java.util.Set; +import java.util.UUID; /** * @author Andrew Shvayka @@ -55,7 +60,7 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor { private static final ByteBufAllocator ALLOCATOR = new UnpooledByteBufAllocator(false); @Override - public TransportProtos.PostTelemetryMsg convertToPostTelemetry(DeviceSessionCtx ctx, MqttPublishMessage inbound) throws AdaptorException { + 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)); @@ -65,7 +70,7 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor { } @Override - public TransportProtos.PostAttributeMsg convertToPostAttributes(DeviceSessionCtx ctx, MqttPublishMessage inbound) throws AdaptorException { + 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)); @@ -75,7 +80,7 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor { } @Override - public TransportProtos.GetAttributeRequestMsg convertToGetAttributes(DeviceSessionCtx ctx, MqttPublishMessage inbound) throws AdaptorException { + public TransportProtos.GetAttributeRequestMsg convertToGetAttributes(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException { String topicName = inbound.variableHeader().topicName(); try { TransportProtos.GetAttributeRequestMsg.Builder result = TransportProtos.GetAttributeRequestMsg.newBuilder(); @@ -98,7 +103,7 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor { } @Override - public TransportProtos.ToDeviceRpcResponseMsg convertToDeviceRpcResponse(DeviceSessionCtx ctx, MqttPublishMessage inbound) throws AdaptorException { + 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())); @@ -111,7 +116,7 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor { } @Override - public TransportProtos.ToServerRpcRequestMsg convertToServerRpcRequest(DeviceSessionCtx ctx, MqttPublishMessage inbound) throws AdaptorException { + public TransportProtos.ToServerRpcRequestMsg convertToServerRpcRequest(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException { String topicName = inbound.variableHeader().topicName(); String payload = validatePayload(ctx.getSessionId(), inbound.payload()); try { @@ -123,7 +128,7 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor { } @Override - public Optional convertToPublish(DeviceSessionCtx ctx, TransportProtos.GetAttributeResponseMsg responseMsg) throws AdaptorException { + public Optional convertToPublish(MqttDeviceAwareSessionContext ctx, TransportProtos.GetAttributeResponseMsg responseMsg) throws AdaptorException { if (!StringUtils.isEmpty(responseMsg.getError())) { throw new AdaptorException(responseMsg.getError()); } else { @@ -138,99 +143,37 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor { } @Override - public Optional convertToPublish(DeviceSessionCtx ctx, TransportProtos.AttributeUpdateNotificationMsg notificationMsg) throws AdaptorException { - return Optional.of(createMqttPublishMsg(ctx, MqttTopics.DEVICE_ATTRIBUTES_TOPIC, JsonConverter.toJson(notificationMsg))); + public Optional 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 convertToPublish(DeviceSessionCtx ctx, TransportProtos.ToDeviceRpcRequestMsg rpcRequest) throws AdaptorException { - return Optional.of(createMqttPublishMsg(ctx, MqttTopics.DEVICE_RPC_REQUESTS_TOPIC + rpcRequest.getRequestId(), JsonConverter.toJson(rpcRequest, false))); + public Optional convertToPublish(MqttDeviceAwareSessionContext ctx, TransportProtos.AttributeUpdateNotificationMsg notificationMsg) throws AdaptorException { + return Optional.of(createMqttPublishMsg(ctx, MqttTopics.DEVICE_ATTRIBUTES_TOPIC, JsonConverter.toJson(notificationMsg))); } @Override - public Optional convertToPublish(DeviceSessionCtx ctx, TransportProtos.ToServerRpcResponseMsg rpcResponse) { - return Optional.of(createMqttPublishMsg(ctx, MqttTopics.DEVICE_RPC_RESPONSE_TOPIC + rpcResponse.getRequestId(), JsonConverter.toJson(rpcResponse))); + public Optional 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 AdaptorToSessionActorMsg convertToActorMsg(DeviceSessionCtx ctx, SessionMsgType type, MqttMessage inbound) throws AdaptorException { - FromDeviceMsg msg; - switch (type) { - case POST_TELEMETRY_REQUEST: - msg = convertToTelemetryUploadRequest(ctx, (MqttPublishMessage) inbound); - break; - case POST_ATTRIBUTES_REQUEST: - msg = convertToUpdateAttributesRequest(ctx, (MqttPublishMessage) inbound); - break; - case SUBSCRIBE_ATTRIBUTES_REQUEST: - msg = new AttributesSubscribeMsg(); - break; - case UNSUBSCRIBE_ATTRIBUTES_REQUEST: - msg = new AttributesUnsubscribeMsg(); - break; - case SUBSCRIBE_RPC_COMMANDS_REQUEST: - msg = new RpcSubscribeMsg(); - break; - case UNSUBSCRIBE_RPC_COMMANDS_REQUEST: - msg = new RpcUnsubscribeMsg(); - break; - case GET_ATTRIBUTES_REQUEST: - msg = convertToGetAttributesRequest(ctx, (MqttPublishMessage) inbound); - break; - case TO_DEVICE_RPC_RESPONSE: - msg = convertToRpcCommandResponse(ctx, (MqttPublishMessage) inbound); - break; - case TO_SERVER_RPC_REQUEST: - msg = null;//convertToServerRpcRequest(ctx, (MqttPublishMessage) inbound); - break; - default: - log.warn("[{}] Unsupported msg type: {}!", ctx.getSessionId(), type); - throw new AdaptorException(new IllegalArgumentException("Unsupported msg type: " + type + "!")); - } - return new BasicAdaptorToSessionActorMsg(ctx, msg); + public Optional 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 convertToAdaptorMsg(DeviceSessionCtx ctx, SessionActorToAdaptorMsg sessionMsg) throws AdaptorException { - MqttMessage result = null; - ToDeviceMsg msg = sessionMsg.getMsg(); - switch (msg.getSessionMsgType()) { - case STATUS_CODE_RESPONSE: - case GET_ATTRIBUTES_RESPONSE: - ResponseMsg responseMsg = (ResponseMsg) msg; - Optional responseError = responseMsg.getError(); - if (responseMsg.isSuccess()) { - result = convertResponseMsg(ctx, msg, responseMsg, responseError); - } else { - if (responseError.isPresent()) { - throw new AdaptorException(responseError.get()); - } - } - break; - case ATTRIBUTES_UPDATE_NOTIFICATION: - AttributesUpdateNotification notification = (AttributesUpdateNotification) msg; - result = createMqttPublishMsg(ctx, MqttTopics.DEVICE_ATTRIBUTES_TOPIC, notification.getData(), false); - break; - case TO_DEVICE_RPC_REQUEST: - ToDeviceRpcRequestMsg rpcRequest = (ToDeviceRpcRequestMsg) msg; - result = createMqttPublishMsg(ctx, MqttTopics.DEVICE_RPC_REQUESTS_TOPIC + rpcRequest.getRequestId(), rpcRequest); - break; - case TO_SERVER_RPC_RESPONSE: -// ToServerRpcResponseMsg rpcResponse = (ToServerRpcResponseMsg) msg; -// result = createMqttPublishMsg(ctx, MqttTopics.DEVICE_RPC_RESPONSE_TOPIC + rpcResponse.getRequestId(), -// rpcResponse); - break; - case RULE_ENGINE_ERROR: - RuleEngineErrorMsg errorMsg = (RuleEngineErrorMsg) msg; - result = createMqttPublishMsg(ctx, "errors", JsonConverter.toErrorJson(errorMsg.getErrorMsg())); - break; - default: - break; - } - return Optional.ofNullable(result); + public Optional convertToPublish(MqttDeviceAwareSessionContext ctx, TransportProtos.ToServerRpcResponseMsg rpcResponse) { + return Optional.of(createMqttPublishMsg(ctx, MqttTopics.DEVICE_RPC_RESPONSE_TOPIC + rpcResponse.getRequestId(), JsonConverter.toJson(rpcResponse))); } - private MqttMessage convertResponseMsg(DeviceSessionCtx ctx, ToDeviceMsg msg, + private MqttMessage convertResponseMsg(MqttDeviceAwareSessionContext ctx, ToDeviceMsg msg, ResponseMsg responseMsg, Optional responseError) throws AdaptorException { MqttMessage result = null; SessionMsgType requestMsgType = responseMsg.getRequestMsgType(); @@ -255,19 +198,19 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor { return result; } - private MqttPublishMessage createMqttPublishMsg(DeviceSessionCtx ctx, String topic, AttributesKVMsg msg, boolean asMap) { + private MqttPublishMessage createMqttPublishMsg(MqttDeviceAwareSessionContext ctx, String topic, AttributesKVMsg msg, boolean asMap) { return createMqttPublishMsg(ctx, topic, JsonConverter.toJson(msg, asMap)); } - private MqttPublishMessage createMqttPublishMsg(DeviceSessionCtx ctx, String topic, ToDeviceRpcRequestMsg msg) { + private MqttPublishMessage createMqttPublishMsg(MqttDeviceAwareSessionContext ctx, String topic, ToDeviceRpcRequestMsg msg) { return createMqttPublishMsg(ctx, topic, JsonConverter.toJson(msg, false)); } - private MqttPublishMessage createMqttPublishMsg(DeviceSessionCtx ctx, String topic, TransportProtos.ToServerRpcResponseMsg msg) { + private MqttPublishMessage createMqttPublishMsg(MqttDeviceAwareSessionContext ctx, String topic, TransportProtos.ToServerRpcResponseMsg msg) { return createMqttPublishMsg(ctx, topic, JsonConverter.toJson(msg)); } - private MqttPublishMessage createMqttPublishMsg(DeviceSessionCtx ctx, String topic, JsonElement json) { + 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()); @@ -276,7 +219,7 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor { return new MqttPublishMessage(mqttFixedHeader, header, payload); } - private FromDeviceMsg convertToGetAttributesRequest(DeviceSessionCtx ctx, MqttPublishMessage inbound) throws AdaptorException { + private FromDeviceMsg convertToGetAttributesRequest(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException { String topicName = inbound.variableHeader().topicName(); try { Integer requestId = Integer.valueOf(topicName.substring(MqttTopics.DEVICE_ATTRIBUTES_REQUEST_TOPIC_PREFIX.length())); @@ -295,7 +238,7 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor { } } - private FromDeviceMsg convertToRpcCommandResponse(DeviceSessionCtx ctx, MqttPublishMessage inbound) throws AdaptorException { + private FromDeviceMsg convertToRpcCommandResponse(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException { String topicName = inbound.variableHeader().topicName(); try { Integer requestId = Integer.valueOf(topicName.substring(MqttTopics.DEVICE_RPC_RESPONSE_TOPIC.length())); @@ -336,7 +279,7 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor { } } - public static JsonElement validateJsonPayload(SessionId sessionId, ByteBuf payloadData) throws AdaptorException { + public static JsonElement validateJsonPayload(UUID sessionId, ByteBuf payloadData) throws AdaptorException { String payload = validatePayload(sessionId, payloadData); try { return new JsonParser().parse(payload); @@ -345,11 +288,11 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor { } } - public static String validatePayload(SessionId sessionId, ByteBuf payloadData) throws AdaptorException { + public static String validatePayload(UUID sessionId, ByteBuf payloadData) throws AdaptorException { try { String payload = payloadData.toString(UTF8); if (payload == null) { - log.warn("[{}] Payload is empty!", sessionId.toUidStr()); + log.warn("[{}] Payload is empty!", sessionId); throw new AdaptorException(new IllegalArgumentException("Payload is empty!")); } return payload; diff --git a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/MqttTransportAdaptor.java b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/MqttTransportAdaptor.java index 48173f968a..37b5395b69 100644 --- a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/MqttTransportAdaptor.java +++ b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/MqttTransportAdaptor.java @@ -17,41 +17,44 @@ 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.TransportAdaptor; import org.thingsboard.server.common.transport.adaptor.AdaptorException; -import org.thingsboard.server.gen.transport.TransportProtos.ToServerRpcResponseMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToServerRpcRequestMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcResponseMsg; 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.transport.mqtt.session.DeviceSessionCtx; +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 extends TransportAdaptor { +public interface MqttTransportAdaptor { + + PostTelemetryMsg convertToPostTelemetry(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException; + + PostAttributeMsg convertToPostAttributes(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException; - PostTelemetryMsg convertToPostTelemetry(DeviceSessionCtx ctx, MqttPublishMessage inbound) throws AdaptorException; + GetAttributeRequestMsg convertToGetAttributes(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException; - PostAttributeMsg convertToPostAttributes(DeviceSessionCtx ctx, MqttPublishMessage inbound) throws AdaptorException; + ToDeviceRpcResponseMsg convertToDeviceRpcResponse(MqttDeviceAwareSessionContext ctx, MqttPublishMessage mqttMsg) throws AdaptorException; - GetAttributeRequestMsg convertToGetAttributes(DeviceSessionCtx ctx, MqttPublishMessage inbound) throws AdaptorException; + ToServerRpcRequestMsg convertToServerRpcRequest(MqttDeviceAwareSessionContext ctx, MqttPublishMessage mqttMsg) throws AdaptorException; - ToDeviceRpcResponseMsg convertToDeviceRpcResponse(DeviceSessionCtx ctx, MqttPublishMessage mqttMsg) throws AdaptorException; + Optional convertToPublish(MqttDeviceAwareSessionContext ctx, GetAttributeResponseMsg responseMsg) throws AdaptorException; - ToServerRpcRequestMsg convertToServerRpcRequest(DeviceSessionCtx ctx, MqttPublishMessage mqttMsg) throws AdaptorException; + Optional convertToGatewayPublish(MqttDeviceAwareSessionContext ctx, GetAttributeResponseMsg responseMsg) throws AdaptorException; - Optional convertToPublish(DeviceSessionCtx ctx, GetAttributeResponseMsg responseMsg) throws AdaptorException; + Optional convertToPublish(MqttDeviceAwareSessionContext ctx, AttributeUpdateNotificationMsg notificationMsg) throws AdaptorException; - Optional convertToPublish(DeviceSessionCtx ctx, AttributeUpdateNotificationMsg notificationMsg) throws AdaptorException; + Optional convertToGatewayPublish(MqttDeviceAwareSessionContext ctx, String deviceName, AttributeUpdateNotificationMsg notificationMsg) throws AdaptorException; - Optional convertToPublish(DeviceSessionCtx ctx, ToDeviceRpcRequestMsg rpcRequest) throws AdaptorException; + Optional convertToPublish(MqttDeviceAwareSessionContext ctx, ToDeviceRpcRequestMsg rpcRequest) throws AdaptorException; - Optional convertToPublish(DeviceSessionCtx ctx, ToServerRpcResponseMsg rpcResponse); + Optional convertToPublish(MqttDeviceAwareSessionContext ctx, ToServerRpcResponseMsg rpcResponse) throws AdaptorException; } diff --git a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/DeviceSessionCtx.java b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/DeviceSessionCtx.java index 6bbb9fa6fd..6195fdd440 100644 --- a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/DeviceSessionCtx.java +++ b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/DeviceSessionCtx.java @@ -1,12 +1,12 @@ /** * 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 - * + *

+ * 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. @@ -19,19 +19,14 @@ import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.mqtt.*; import lombok.Getter; import lombok.extern.slf4j.Slf4j; -import org.thingsboard.server.common.data.id.SessionId; import org.thingsboard.server.common.msg.session.SessionActorToAdaptorMsg; import org.thingsboard.server.common.msg.session.SessionCtrlMsg; import org.thingsboard.server.common.msg.session.SessionType; import org.thingsboard.server.common.msg.session.ctrl.SessionCloseMsg; import org.thingsboard.server.common.msg.session.ex.SessionException; -import org.thingsboard.server.common.transport.SessionMsgProcessor; import org.thingsboard.server.common.transport.adaptor.AdaptorException; -import org.thingsboard.server.common.transport.auth.DeviceAuthService; -import org.thingsboard.server.common.transport.session.DeviceAwareSessionContext; -import org.thingsboard.server.transport.mqtt.adaptors.MqttTransportAdaptor; -import java.util.Map; +import java.util.UUID; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicInteger; @@ -41,14 +36,14 @@ import java.util.concurrent.atomic.AtomicInteger; @Slf4j public class DeviceSessionCtx extends MqttDeviceAwareSessionContext { - private final MqttSessionId sessionId; + private final UUID sessionId; @Getter private ChannelHandlerContext channel; private AtomicInteger msgIdSeq = new AtomicInteger(0); - public DeviceSessionCtx(ConcurrentMap mqttQoSMap) { + public DeviceSessionCtx(UUID sessionId, ConcurrentMap mqttQoSMap) { super(null, null, mqttQoSMap); - this.sessionId = new MqttSessionId(); + this.sessionId = sessionId; } @Override @@ -94,7 +89,7 @@ public class DeviceSessionCtx extends MqttDeviceAwareSessionContext { } @Override - public SessionId getSessionId() { + public UUID getSessionId() { return sessionId; } diff --git a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewayDeviceSessionCtx.java b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewayDeviceSessionCtx.java index 793836d793..387d9cf439 100644 --- a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewayDeviceSessionCtx.java +++ b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewayDeviceSessionCtx.java @@ -1,12 +1,12 @@ /** * 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 - * + *

+ * 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. @@ -16,70 +16,80 @@ package org.thingsboard.server.transport.mqtt.session; import com.google.gson.Gson; -import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; +import com.sun.xml.internal.bind.v2.TODO; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufAllocator; import io.netty.buffer.UnpooledByteBufAllocator; -import io.netty.handler.codec.mqtt.*; -import org.thingsboard.server.common.data.Device; -import org.thingsboard.server.common.data.id.SessionId; +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.thingsboard.server.common.data.kv.AttributeKvEntry; import org.thingsboard.server.common.data.kv.KvEntry; -import org.thingsboard.server.common.msg.core.*; +import org.thingsboard.server.common.msg.core.AttributesUpdateNotification; +import org.thingsboard.server.common.msg.core.GetAttributesResponse; +import org.thingsboard.server.common.msg.core.ResponseMsg; +import org.thingsboard.server.common.msg.core.ToDeviceRpcRequestMsg; import org.thingsboard.server.common.msg.kv.AttributesKVMsg; -import org.thingsboard.server.common.msg.session.*; -import org.thingsboard.server.common.msg.session.ex.SessionException; +import org.thingsboard.server.common.msg.session.SessionActorToAdaptorMsg; +import org.thingsboard.server.common.msg.session.SessionMsgType; +import org.thingsboard.server.common.msg.session.ToDeviceMsg; +import org.thingsboard.server.common.transport.SessionMsgListener; import org.thingsboard.server.common.transport.adaptor.JsonConverter; -import org.thingsboard.server.common.transport.session.DeviceAwareSessionContext; +import org.thingsboard.server.gen.transport.TransportProtos; +import org.thingsboard.server.gen.transport.TransportProtos.DeviceInfoProto; +import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto; import org.thingsboard.server.transport.mqtt.MqttTopics; import org.thingsboard.server.transport.mqtt.MqttTransportHandler; import java.nio.charset.Charset; import java.util.List; -import java.util.Map; import java.util.Optional; +import java.util.UUID; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicInteger; /** * Created by ashvayka on 19.01.17. */ -public class GatewayDeviceSessionCtx extends MqttDeviceAwareSessionContext { +@Slf4j +public class GatewayDeviceSessionCtx extends MqttDeviceAwareSessionContext implements SessionMsgListener { private static final Gson GSON = new Gson(); private static final Charset UTF8 = Charset.forName("UTF-8"); private static final ByteBufAllocator ALLOCATOR = new UnpooledByteBufAllocator(false); - public static final String DEVICE_PROPERTY = "device"; - private GatewaySessionCtx parent; - private final MqttSessionId sessionId; + private final GatewaySessionCtx parent; + private final UUID sessionId; + private final SessionInfoProto sessionInfo; private volatile boolean closed; private AtomicInteger msgIdSeq = new AtomicInteger(0); - public GatewayDeviceSessionCtx(GatewaySessionCtx parent, Device device, ConcurrentMap mqttQoSMap) { - super(parent.getProcessor(), parent.getAuthService(), device, mqttQoSMap); + public GatewayDeviceSessionCtx(GatewaySessionCtx parent, DeviceInfoProto deviceInfo, ConcurrentMap mqttQoSMap) { + super(mqttQoSMap); this.parent = parent; - this.sessionId = new MqttSessionId(); + this.sessionId = UUID.randomUUID(); + 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 SessionId getSessionId() { + public UUID getSessionId() { return sessionId; } - @Override - public SessionType getSessionType() { - return SessionType.ASYNC; - } - - @Override - public void onMsg(SessionActorToAdaptorMsg sessionMsg) throws SessionException { - Optional message = getToDeviceMsg(sessionMsg); - message.ifPresent(parent::writeAndFlush); - } - private Optional getToDeviceMsg(SessionActorToAdaptorMsg sessionMsg) { ToDeviceMsg msg = sessionMsg.getMsg(); switch (msg.getSessionMsgType()) { @@ -113,25 +123,6 @@ public class GatewayDeviceSessionCtx extends MqttDeviceAwareSessionContext { return Optional.empty(); } - @Override - public void onMsg(SessionCtrlMsg msg) throws SessionException { - //Do nothing - } - - @Override - public boolean isClosed() { - return closed; - } - - public void setClosed(boolean closed) { - this.closed = closed; - } - - @Override - public long getTimeout() { - return 0; - } - private MqttMessage createMqttPublishMsg(String topic, GetAttributesResponse response) { JsonObject result = new JsonObject(); result.addProperty("id", response.getRequestId()); @@ -204,4 +195,40 @@ public class GatewayDeviceSessionCtx extends MqttDeviceAwareSessionContext { return new MqttPublishMessage(mqttFixedHeader, header, payload); } + 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 toDeviceRequest) { + + } + + @Override + public void onToServerRpcResponse(TransportProtos.ToServerRpcResponseMsg toServerResponse) { + TODO + } } diff --git a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionCtx.java b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionCtx.java index fafbc4ec01..3788d57e54 100644 --- a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionCtx.java +++ b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionCtx.java @@ -1,12 +1,12 @@ /** * 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 - * + *

+ * 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. @@ -15,6 +15,11 @@ */ 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; @@ -25,28 +30,28 @@ 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.data.Device; -import org.thingsboard.server.common.data.id.SessionId; -import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; -import org.thingsboard.server.common.data.relation.EntityRelation; -import org.thingsboard.server.common.msg.core.*; -import org.thingsboard.server.common.msg.session.BasicAdaptorToSessionActorMsg; -import org.thingsboard.server.common.msg.session.BasicTransportToDeviceSessionActorMsg; -import org.thingsboard.server.common.msg.session.ctrl.SessionCloseMsg; -import org.thingsboard.server.common.transport.SessionMsgProcessor; +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.common.transport.auth.DeviceAuthService; -import org.thingsboard.server.dao.device.DeviceService; -import org.thingsboard.server.dao.relation.RelationService; +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 java.util.*; +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; -import java.util.stream.Collectors; - -import static org.thingsboard.server.transport.mqtt.adaptors.JsonMqttAdaptor.validateJsonPayload; /** * Created by ashvayka on 19.01.17. @@ -55,184 +60,235 @@ import static org.thingsboard.server.transport.mqtt.adaptors.JsonMqttAdaptor.val public class GatewaySessionCtx { private static final String DEFAULT_DEVICE_TYPE = "default"; - public static final String CAN_T_PARSE_VALUE = "Can't parse value: "; - public static final String DEVICE_PROPERTY = "device"; -// private final Device gateway; -// private final SessionId gatewaySessionId; -// private final SessionMsgProcessor processor; -// private final DeviceService deviceService; -// private final DeviceAuthService authService; -// private final RelationService relationService; -// private final Map devices; -// private final ConcurrentMap mqttQoSMap; - private ChannelHandlerContext channel; - -// public GatewaySessionCtx(SessionMsgProcessor processor, DeviceService deviceService, DeviceAuthService authService, RelationService relationService, DeviceSessionCtx gatewaySessionCtx) { -// this.processor = processor; -// this.deviceService = deviceService; -// this.authService = authService; -// this.relationService = relationService; -// this.gateway = gatewaySessionCtx.getDevice(); -// this.gatewaySessionId = gatewaySessionCtx.getSessionId(); -// this.devices = new HashMap<>(); -// this.mqttQoSMap = gatewaySessionCtx.getMqttQoSMap(); -// } - - public GatewaySessionCtx(DeviceSessionCtx deviceSessionCtx) { + 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 devices; + private final ConcurrentMap mqttQoSMap; + private final ChannelHandlerContext channel; + public GatewaySessionCtx(MqttTransportContext context, DeviceSessionCtx deviceSessionCtx, UUID sessionId) { + this.context = context; + this.transportService = context.getTransportService(); + 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); - onDeviceConnect(deviceName, deviceType); - ack(msg); + Futures.addCallback(onDeviceConnect(deviceName, deviceType), new FutureCallback() { + @Override + public void onSuccess(@Nullable GatewayDeviceSessionCtx result) { + ack(msg); + } + + @Override + public void onFailure(Throwable t) { + log.warn("[{}] Failed to process device connect command: {}", sessionId, deviceName, t); + + } + }, context.getExecutor()); } - private void onDeviceConnect(String deviceName, String deviceType) { -// if (!devices.containsKey(deviceName)) { -// Device device = deviceService.findDeviceByTenantIdAndName(gateway.getTenantId(), deviceName); -// if (device == null) { -// device = new Device(); -// device.setTenantId(gateway.getTenantId()); -// device.setName(deviceName); -// device.setType(deviceType); -// device.setCustomerId(gateway.getCustomerId()); -// device = deviceService.saveDevice(device); -// relationService.saveRelationAsync(new EntityRelation(gateway.getId(), device.getId(), "Created")); -// processor.onDeviceAdded(device); -// } -// GatewayDeviceSessionCtx ctx = new GatewayDeviceSessionCtx(this, device, mqttQoSMap); -// devices.put(deviceName, ctx); -// log.debug("[{}] Added device [{}] to the gateway session", gatewaySessionId, deviceName); -// processor.process(new BasicTransportToDeviceSessionActorMsg(device, new BasicAdaptorToSessionActorMsg(ctx, new AttributesSubscribeMsg()))); -// processor.process(new BasicTransportToDeviceSessionActorMsg(device, new BasicAdaptorToSessionActorMsg(ctx, new RpcSubscribeMsg()))); -// } + private ListenableFuture onDeviceConnect(String deviceName, String deviceType) { + SettableFuture 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() { + @Override + public void onSuccess(GetOrCreateDeviceFromGatewayResponseMsg msg) { + GatewayDeviceSessionCtx deviceSessionCtx = new GatewayDeviceSessionCtx(GatewaySessionCtx.this, msg.getDeviceInfo(), mqttQoSMap); + if (devices.putIfAbsent(deviceName, deviceSessionCtx) == null) { + SessionInfoProto deviceSessionInfo = deviceSessionCtx.getSessionInfo(); + transportService.process(deviceSessionInfo, MqttTransportHandler.getSessionEventMsg(TransportProtos.SessionEvent.OPEN), null); + transportService.registerSession(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))); -// GatewayDeviceSessionCtx deviceSessionCtx = devices.remove(deviceName); -// if (deviceSessionCtx != null) { -// processor.process(SessionCloseMsg.onDisconnect(deviceSessionCtx.getSessionId())); -// deviceSessionCtx.setClosed(true); -// log.debug("[{}] Removed device [{}] from the gateway session", gatewaySessionId, deviceName); -// } else { -// log.debug("[{}] Device [{}] was already removed from the gateway session", gatewaySessionId, deviceName); -// } -// ack(msg); + 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((k, v) -> { -// processor.process(SessionCloseMsg.onDisconnect(v.getSessionId())); -// }); + devices.forEach(this::deregisterSession); } public void onDeviceTelemetry(MqttPublishMessage mqttMsg) throws AdaptorException { -// JsonElement json = validateJsonPayload(gatewaySessionId, mqttMsg.payload()); -// int requestId = mqttMsg.variableHeader().messageId(); -// if (json.isJsonObject()) { -// JsonObject jsonObj = json.getAsJsonObject(); -// for (Map.Entry deviceEntry : jsonObj.entrySet()) { -// String deviceName = checkDeviceConnected(deviceEntry.getKey()); -// if (!deviceEntry.getValue().isJsonArray()) { -// throw new JsonSyntaxException(CAN_T_PARSE_VALUE + json); -// } -// BasicTelemetryUploadRequest request = new BasicTelemetryUploadRequest(requestId); -// JsonArray deviceData = deviceEntry.getValue().getAsJsonArray(); -// for (JsonElement element : deviceData) { -// JsonConverter.parseWithTs(request, element.getAsJsonObject()); -// } -// GatewayDeviceSessionCtx deviceSessionCtx = devices.get(deviceName); -// processor.process(new BasicTransportToDeviceSessionActorMsg(deviceSessionCtx.getDevice(), -// new BasicAdaptorToSessionActorMsg(deviceSessionCtx, request))); -// } -// } else { -// throw new JsonSyntaxException(CAN_T_PARSE_VALUE + json); -// } - } + JsonElement json = JsonMqttAdaptor.validateJsonPayload(sessionId, mqttMsg.payload()); + int msgId = mqttMsg.variableHeader().packetId(); + if (json.isJsonObject()) { + JsonObject jsonObj = json.getAsJsonObject(); + for (Map.Entry deviceEntry : jsonObj.entrySet()) { + String deviceName = deviceEntry.getKey(); + Futures.addCallback(checkDeviceConnected(deviceName), + new FutureCallback() { + @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)); + } - public void onDeviceRpcResponse(MqttPublishMessage mqttMsg) throws AdaptorException { -// JsonElement json = validateJsonPayload(gatewaySessionId, mqttMsg.payload()); -// if (json.isJsonObject()) { -// JsonObject jsonObj = json.getAsJsonObject(); -// String deviceName = checkDeviceConnected(jsonObj.get(DEVICE_PROPERTY).getAsString()); -// Integer requestId = jsonObj.get("id").getAsInt(); -// String data = jsonObj.get("data").toString(); -// GatewayDeviceSessionCtx deviceSessionCtx = devices.get(deviceName); -// processor.process(new BasicTransportToDeviceSessionActorMsg(deviceSessionCtx.getDevice(), -// new BasicAdaptorToSessionActorMsg(deviceSessionCtx, new ToDeviceRpcResponseMsg(requestId, data)))); -// ack(mqttMsg); -// } else { -// throw new JsonSyntaxException(CAN_T_PARSE_VALUE + json); -// } + @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 = validateJsonPayload(gatewaySessionId, mqttMsg.payload()); -// int requestId = mqttMsg.variableHeader().messageId(); -// if (json.isJsonObject()) { -// JsonObject jsonObj = json.getAsJsonObject(); -// for (Map.Entry deviceEntry : jsonObj.entrySet()) { -// String deviceName = checkDeviceConnected(deviceEntry.getKey()); -// if (!deviceEntry.getValue().isJsonObject()) { -// throw new JsonSyntaxException(CAN_T_PARSE_VALUE + json); -// } -// long ts = System.currentTimeMillis(); -// BasicAttributesUpdateRequest request = new BasicAttributesUpdateRequest(requestId); -// JsonObject deviceData = deviceEntry.getValue().getAsJsonObject(); -// request.add(JsonConverter.parseValues(deviceData).stream().map(kv -> new BaseAttributeKvEntry(kv, ts)).collect(Collectors.toList())); -// GatewayDeviceSessionCtx deviceSessionCtx = devices.get(deviceName); -// processor.process(new BasicTransportToDeviceSessionActorMsg(deviceSessionCtx.getDevice(), -// new BasicAdaptorToSessionActorMsg(deviceSessionCtx, request))); -// } -// } else { -// throw new JsonSyntaxException(CAN_T_PARSE_VALUE + json); -// } + JsonElement json = JsonMqttAdaptor.validateJsonPayload(sessionId, mqttMsg.payload()); + int msgId = mqttMsg.variableHeader().packetId(); + if (json.isJsonObject()) { + JsonObject jsonObj = json.getAsJsonObject(); + for (Map.Entry deviceEntry : jsonObj.entrySet()) { + String deviceName = deviceEntry.getKey(); + Futures.addCallback(checkDeviceConnected(deviceName), + new FutureCallback() { + @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 teleemtry 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() { + @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 = validateJsonPayload(gatewaySessionId, 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 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()); -// } -// } -// -// BasicGetAttributesRequest request; -// if (clientScope) { -// request = new BasicGetAttributesRequest(requestId, keys, null); -// } else { -// request = new BasicGetAttributesRequest(requestId, null, keys); -// } -// GatewayDeviceSessionCtx deviceSessionCtx = devices.get(deviceName); -// processor.process(new BasicTransportToDeviceSessionActorMsg(deviceSessionCtx.getDevice(), -// new BasicAdaptorToSessionActorMsg(deviceSessionCtx, request))); -// ack(msg); -// } else { -// throw new JsonSyntaxException(CAN_T_PARSE_VALUE + json); -// } + 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 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); + } + int msgId = msg.variableHeader().packetId(); + TransportProtos.GetAttributeRequestMsg requestMsg = result.build(); + Futures.addCallback(checkDeviceConnected(deviceName), + new FutureCallback() { + @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 teleemtry command: {}", sessionId, deviceName, t); + } + }, context.getExecutor()); + ack(msg); + } else { + throw new JsonSyntaxException(CAN_T_PARSE_VALUE + json); + } } - private String checkDeviceConnected(String deviceName) { -// if (!devices.containsKey(deviceName)) { -// log.debug("[{}] Missing device [{}] for the gateway session", gatewaySessionId, deviceName); -// onDeviceConnect(deviceName, DEFAULT_DEVICE_TYPE); -// } -// return deviceName; - return null; + private ListenableFuture 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) { @@ -253,32 +309,52 @@ public class GatewaySessionCtx { } private JsonElement getJson(MqttPublishMessage mqttMsg) throws AdaptorException { -// return JsonMqttAdaptor.validateJsonPayload(gatewaySessionId, mqttMsg.payload()); - return null; + return JsonMqttAdaptor.validateJsonPayload(sessionId, mqttMsg.payload()); + } + + private void ack(MqttPublishMessage msg) { + if (msg.variableHeader().packetId() > 0) { + writeAndFlush(MqttTransportHandler.createMqttPubAckMsg(msg.variableHeader().packetId())); + } } - protected SessionMsgProcessor getProcessor() { -// return processor; - return null; + void writeAndFlush(MqttMessage mqttMessage) { + channel.writeAndFlush(mqttMessage); } - DeviceAuthService getAuthService() { -// return authService; - return null; + public String getNodeId() { + return context.getNodeId(); } - public void setChannel(ChannelHandlerContext channel) { - this.channel = channel; + 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 void ack(MqttPublishMessage msg) { - if (msg.variableHeader().messageId() > 0) { - writeAndFlush(MqttTransportHandler.createMqttPubAckMsg(msg.variableHeader().messageId())); - } + private TransportServiceCallback getPubAckCallback(final ChannelHandlerContext ctx, final String deviceName, final int msgId, final T msg) { + return new TransportServiceCallback() { + @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(); + } + }; } - void writeAndFlush(MqttMessage mqttMessage) { - channel.writeAndFlush(mqttMessage); + public MqttTransportContext getContext() { + return context; } + public MqttTransportAdaptor getAdaptor() { + return context.getAdaptor(); + } } diff --git a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/MqttDeviceAwareSessionContext.java b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/MqttDeviceAwareSessionContext.java index c01e103889..9e66566383 100644 --- a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/MqttDeviceAwareSessionContext.java +++ b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/MqttDeviceAwareSessionContext.java @@ -31,12 +31,7 @@ public abstract class MqttDeviceAwareSessionContext extends DeviceAwareSessionCo private final ConcurrentMap mqttQoSMap; - public MqttDeviceAwareSessionContext(SessionMsgProcessor processor, DeviceAuthService authService, ConcurrentMap mqttQoSMap) { - super(); - this.mqttQoSMap = mqttQoSMap; - } - - public MqttDeviceAwareSessionContext(SessionMsgProcessor processor, DeviceAuthService authService, Device device, ConcurrentMap mqttQoSMap) { + public MqttDeviceAwareSessionContext(ConcurrentMap mqttQoSMap) { super(); this.mqttQoSMap = mqttQoSMap; } diff --git a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/MqttSessionId.java b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/MqttSessionId.java index 03006128bf..9256e5d8c0 100644 --- a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/MqttSessionId.java +++ b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/MqttSessionId.java @@ -15,9 +15,6 @@ */ package org.thingsboard.server.transport.mqtt.session; -import org.thingsboard.server.common.data.id.SessionId; - -import java.util.UUID; import java.util.concurrent.atomic.AtomicLong; /** diff --git a/transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/MqttTransportService.java b/transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/MqttTransportService.java index 15392386c4..bfc69efdc4 100644 --- a/transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/MqttTransportService.java +++ b/transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/MqttTransportService.java @@ -1,12 +1,12 @@ /** * 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 - * + *

+ * 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. @@ -25,7 +25,9 @@ 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; @@ -153,7 +155,7 @@ public class MqttTransportService implements TransportService { try { ToTransportMsg toTransportMsg = mainConsumer.decode(record); if (toTransportMsg.hasToDeviceSessionMsg()) { - TransportProtos.DeviceActorToTransportMsg toSessionMsg = toTransportMsg.getToDeviceSessionMsg(); + DeviceActorToTransportMsg toSessionMsg = toTransportMsg.getToDeviceSessionMsg(); UUID sessionId = new UUID(toSessionMsg.getSessionIdMSB(), toSessionMsg.getSessionIdLSB()); SessionMsgListener listener = sessions.get(sessionId); if (listener != null) { @@ -227,10 +229,17 @@ public class MqttTransportService implements TransportService { response -> callback.onSuccess(response.getValidateTokenResponseMsg()), callback::onError, transportCallbackExecutor); } + @Override + public void process(GetOrCreateDeviceFromGatewayRequestMsg msg, TransportServiceCallback callback) { + AsyncCallbackTemplate.withCallback(transportApiTemplate.post(msg.getTenantIdMSB() + msg.getTenantIdLSB() + msg.getDeviceName(), + TransportApiRequestMsg.newBuilder().setGetOrCreateDeviceRequestMsg(msg).build()), + response -> callback.onSuccess(response.getGetOrCreateDeviceResponseMsg()), callback::onError, transportCallbackExecutor); + } + @Override public void process(SessionInfoProto sessionInfo, SessionEventMsg msg, TransportServiceCallback callback) { ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg( - TransportProtos.TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) + TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) .setSessionEvent(msg).build() ).build(); send(sessionInfo, toRuleEngineMsg, callback); @@ -239,7 +248,7 @@ public class MqttTransportService implements TransportService { @Override public void process(SessionInfoProto sessionInfo, PostTelemetryMsg msg, TransportServiceCallback callback) { ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg( - TransportProtos.TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) + TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) .setPostTelemetry(msg).build() ).build(); send(sessionInfo, toRuleEngineMsg, callback); @@ -248,52 +257,52 @@ public class MqttTransportService implements TransportService { @Override public void process(SessionInfoProto sessionInfo, PostAttributeMsg msg, TransportServiceCallback callback) { ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg( - TransportProtos.TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) + TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) .setPostAttributes(msg).build() ).build(); send(sessionInfo, toRuleEngineMsg, callback); } @Override - public void process(SessionInfoProto sessionInfo, TransportProtos.GetAttributeRequestMsg msg, TransportServiceCallback callback) { + public void process(SessionInfoProto sessionInfo, GetAttributeRequestMsg msg, TransportServiceCallback callback) { ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg( - TransportProtos.TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) + TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) .setGetAttributes(msg).build() ).build(); send(sessionInfo, toRuleEngineMsg, callback); } @Override - public void process(SessionInfoProto sessionInfo, TransportProtos.SubscribeToAttributeUpdatesMsg msg, TransportServiceCallback callback) { + public void process(SessionInfoProto sessionInfo, SubscribeToAttributeUpdatesMsg msg, TransportServiceCallback callback) { ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg( - TransportProtos.TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) + TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) .setSubscribeToAttributes(msg).build() ).build(); send(sessionInfo, toRuleEngineMsg, callback); } @Override - public void process(SessionInfoProto sessionInfo, TransportProtos.SubscribeToRPCMsg msg, TransportServiceCallback callback) { + public void process(SessionInfoProto sessionInfo, SubscribeToRPCMsg msg, TransportServiceCallback callback) { ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg( - TransportProtos.TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) + TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) .setSubscribeToRPC(msg).build() ).build(); send(sessionInfo, toRuleEngineMsg, callback); } @Override - public void process(SessionInfoProto sessionInfo, TransportProtos.ToDeviceRpcResponseMsg msg, TransportServiceCallback callback) { + public void process(SessionInfoProto sessionInfo, ToDeviceRpcResponseMsg msg, TransportServiceCallback callback) { ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg( - TransportProtos.TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) + TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) .setToDeviceRPCCallResponse(msg).build() ).build(); send(sessionInfo, toRuleEngineMsg, callback); } @Override - public void process(SessionInfoProto sessionInfo, TransportProtos.ToServerRpcRequestMsg msg, TransportServiceCallback callback) { + public void process(SessionInfoProto sessionInfo, ToServerRpcRequestMsg msg, TransportServiceCallback callback) { ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg( - TransportProtos.TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) + TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) .setToServerRPCCallRequest(msg).build() ).build(); send(sessionInfo, toRuleEngineMsg, callback); From cc5b18f30e51e983d6bd7292875b553f26f0a4b5 Mon Sep 17 00:00:00 2001 From: Andrew Shvayka Date: Fri, 12 Oct 2018 11:18:41 +0300 Subject: [PATCH 12/26] MQTT Gateway API Implementation --- .../transport/adaptor/JsonConverter.java | 17 +- .../transport/mqtt/MqttTransportHandler.java | 4 +- .../mqtt/adaptors/JsonMqttAdaptor.java | 109 ++----------- .../mqtt/adaptors/MqttTransportAdaptor.java | 3 + .../mqtt/session/GatewayDeviceSessionCtx.java | 151 ++---------------- .../mqtt/session/GatewaySessionCtx.java | 7 + 6 files changed, 47 insertions(+), 244 deletions(-) diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java b/common/transport/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java index e02bb4bf34..1fcd60730c 100644 --- a/common/transport/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java +++ b/common/transport/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java @@ -37,11 +37,16 @@ import org.thingsboard.server.common.msg.core.BasicRequest; import org.thingsboard.server.common.msg.core.BasicTelemetryUploadRequest; import org.thingsboard.server.common.msg.core.TelemetryUploadRequest; import org.thingsboard.server.common.msg.core.ToDeviceRpcRequestMsg; -import org.thingsboard.server.common.msg.core.ToServerRpcRequestMsg; -import org.thingsboard.server.common.msg.core.ToServerRpcResponseMsg; import org.thingsboard.server.common.msg.kv.AttributesKVMsg; import org.thingsboard.server.gen.transport.TransportProtos; -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.List; @@ -446,4 +451,10 @@ public class JsonConverter { 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; + } } diff --git a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java index 21d33354cd..1f1288c6da 100644 --- a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java +++ b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java @@ -85,7 +85,7 @@ import static io.netty.handler.codec.mqtt.MqttQoS.FAILURE; @Slf4j public class MqttTransportHandler extends ChannelInboundHandlerAdapter implements GenericFutureListener>, SessionMsgListener { - public static final MqttQoS MAX_SUPPORTED_QOS_LVL = AT_LEAST_ONCE; + private static final MqttQoS MAX_SUPPORTED_QOS_LVL = AT_LEAST_ONCE; private final UUID sessionId; private final MqttTransportContext context; @@ -100,7 +100,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement private volatile DeviceSessionCtx deviceSessionCtx; private volatile GatewaySessionCtx gatewaySessionCtx; - public MqttTransportHandler(MqttTransportContext context) { + MqttTransportHandler(MqttTransportContext context) { this.sessionId = UUID.randomUUID(); this.context = context; this.transportService = context.getTransportService(); diff --git a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java index 85dc813428..4bde5a35fe 100644 --- a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java +++ b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java @@ -23,27 +23,23 @@ 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.*; +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.data.kv.AttributeKvEntry; -import org.thingsboard.server.common.data.kv.KvEntry; -import org.thingsboard.server.common.msg.core.*; -import org.thingsboard.server.common.msg.kv.AttributesKVMsg; -import org.thingsboard.server.common.msg.session.*; 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.DeviceSessionCtx; -import org.thingsboard.server.transport.mqtt.MqttTransportHandler; import org.thingsboard.server.transport.mqtt.session.MqttDeviceAwareSessionContext; import java.nio.charset.Charset; import java.util.Arrays; import java.util.HashSet; -import java.util.List; import java.util.Optional; import java.util.Set; import java.util.UUID; @@ -169,45 +165,13 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor { } @Override - public Optional convertToPublish(MqttDeviceAwareSessionContext ctx, TransportProtos.ToServerRpcResponseMsg rpcResponse) { - return Optional.of(createMqttPublishMsg(ctx, MqttTopics.DEVICE_RPC_RESPONSE_TOPIC + rpcResponse.getRequestId(), JsonConverter.toJson(rpcResponse))); + public Optional convertToGatewayPublish(MqttDeviceAwareSessionContext ctx, String deviceName, TransportProtos.ToDeviceRpcRequestMsg rpcRequest) throws AdaptorException { + return Optional.of(createMqttPublishMsg(ctx, MqttTopics.GATEWAY_RPC_TOPIC, JsonConverter.toGatewayJson(deviceName, rpcRequest))); } - private MqttMessage convertResponseMsg(MqttDeviceAwareSessionContext ctx, ToDeviceMsg msg, - ResponseMsg responseMsg, Optional responseError) throws AdaptorException { - MqttMessage result = null; - SessionMsgType requestMsgType = responseMsg.getRequestMsgType(); - Integer requestId = responseMsg.getRequestId(); - if (requestId >= 0) { - if (requestMsgType == SessionMsgType.POST_ATTRIBUTES_REQUEST || requestMsgType == SessionMsgType.POST_TELEMETRY_REQUEST) { - result = MqttTransportHandler.createMqttPubAckMsg(requestId); - } else if (requestMsgType == SessionMsgType.GET_ATTRIBUTES_REQUEST) { - GetAttributesResponse response = (GetAttributesResponse) msg; - Optional responseData = response.getData(); - if (response.isSuccess() && responseData.isPresent()) { - result = createMqttPublishMsg(ctx, - MqttTopics.DEVICE_ATTRIBUTES_RESPONSE_TOPIC_PREFIX + requestId, - responseData.get(), true); - } else { - if (responseError.isPresent()) { - throw new AdaptorException(responseError.get()); - } - } - } - } - return result; - } - - private MqttPublishMessage createMqttPublishMsg(MqttDeviceAwareSessionContext ctx, String topic, AttributesKVMsg msg, boolean asMap) { - return createMqttPublishMsg(ctx, topic, JsonConverter.toJson(msg, asMap)); - } - - private MqttPublishMessage createMqttPublishMsg(MqttDeviceAwareSessionContext ctx, String topic, ToDeviceRpcRequestMsg msg) { - return createMqttPublishMsg(ctx, topic, JsonConverter.toJson(msg, false)); - } - - private MqttPublishMessage createMqttPublishMsg(MqttDeviceAwareSessionContext ctx, String topic, TransportProtos.ToServerRpcResponseMsg msg) { - return createMqttPublishMsg(ctx, topic, JsonConverter.toJson(msg)); + @Override + public Optional 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) { @@ -219,39 +183,6 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor { return new MqttPublishMessage(mqttFixedHeader, header, payload); } - private FromDeviceMsg convertToGetAttributesRequest(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException { - String topicName = inbound.variableHeader().topicName(); - try { - Integer requestId = Integer.valueOf(topicName.substring(MqttTopics.DEVICE_ATTRIBUTES_REQUEST_TOPIC_PREFIX.length())); - String payload = inbound.payload().toString(UTF8); - JsonElement requestBody = new JsonParser().parse(payload); - Set clientKeys = toStringSet(requestBody, "clientKeys"); - Set sharedKeys = toStringSet(requestBody, "sharedKeys"); - if (clientKeys == null && sharedKeys == null) { - return new BasicGetAttributesRequest(requestId); - } else { - return new BasicGetAttributesRequest(requestId, clientKeys, sharedKeys); - } - } catch (RuntimeException e) { - log.warn("Failed to decode get attributes request", e); - throw new AdaptorException(e); - } - } - - private FromDeviceMsg convertToRpcCommandResponse(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 new ToDeviceRpcResponseMsg( - requestId, - payload); - } catch (RuntimeException e) { - log.warn("Failed to decode get attributes request", e); - throw new AdaptorException(e); - } - } - private Set toStringSet(JsonElement requestBody, String name) { JsonElement element = requestBody.getAsJsonObject().get(name); if (element != null) { @@ -261,24 +192,6 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor { } } - private AttributesUpdateRequest convertToUpdateAttributesRequest(SessionContext ctx, MqttPublishMessage inbound) throws AdaptorException { - String payload = validatePayload(ctx.getSessionId(), inbound.payload()); - try { - return JsonConverter.convertToAttributes(new JsonParser().parse(payload), inbound.variableHeader().packetId()); - } catch (IllegalStateException | JsonSyntaxException ex) { - throw new AdaptorException(ex); - } - } - - private TelemetryUploadRequest convertToTelemetryUploadRequest(SessionContext ctx, MqttPublishMessage inbound) throws AdaptorException { - String payload = validatePayload(ctx.getSessionId(), inbound.payload()); - try { - return JsonConverter.convertToTelemetry(new JsonParser().parse(payload), inbound.variableHeader().packetId()); - } catch (IllegalStateException | JsonSyntaxException ex) { - throw new AdaptorException(ex); - } - } - public static JsonElement validateJsonPayload(UUID sessionId, ByteBuf payloadData) throws AdaptorException { String payload = validatePayload(sessionId, payloadData); try { @@ -288,7 +201,7 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor { } } - public static String validatePayload(UUID sessionId, ByteBuf payloadData) throws AdaptorException { + private static String validatePayload(UUID sessionId, ByteBuf payloadData) throws AdaptorException { try { String payload = payloadData.toString(UTF8); if (payload == null) { diff --git a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/MqttTransportAdaptor.java b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/MqttTransportAdaptor.java index 37b5395b69..a653e4c93e 100644 --- a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/MqttTransportAdaptor.java +++ b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/MqttTransportAdaptor.java @@ -18,6 +18,7 @@ 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; import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg; import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeRequestMsg; import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeResponseMsg; @@ -56,5 +57,7 @@ public interface MqttTransportAdaptor { Optional convertToPublish(MqttDeviceAwareSessionContext ctx, ToDeviceRpcRequestMsg rpcRequest) throws AdaptorException; + Optional convertToGatewayPublish(MqttDeviceAwareSessionContext ctx, String deviceName, ToDeviceRpcRequestMsg rpcRequest) throws AdaptorException; + Optional convertToPublish(MqttDeviceAwareSessionContext ctx, ToServerRpcResponseMsg rpcResponse) throws AdaptorException; } diff --git a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewayDeviceSessionCtx.java b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewayDeviceSessionCtx.java index 387d9cf439..32d9e55832 100644 --- a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewayDeviceSessionCtx.java +++ b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewayDeviceSessionCtx.java @@ -15,43 +15,14 @@ */ package org.thingsboard.server.transport.mqtt.session; -import com.google.gson.Gson; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.sun.xml.internal.bind.v2.TODO; -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.thingsboard.server.common.data.kv.AttributeKvEntry; -import org.thingsboard.server.common.data.kv.KvEntry; -import org.thingsboard.server.common.msg.core.AttributesUpdateNotification; -import org.thingsboard.server.common.msg.core.GetAttributesResponse; -import org.thingsboard.server.common.msg.core.ResponseMsg; -import org.thingsboard.server.common.msg.core.ToDeviceRpcRequestMsg; -import org.thingsboard.server.common.msg.kv.AttributesKVMsg; -import org.thingsboard.server.common.msg.session.SessionActorToAdaptorMsg; -import org.thingsboard.server.common.msg.session.SessionMsgType; -import org.thingsboard.server.common.msg.session.ToDeviceMsg; import org.thingsboard.server.common.transport.SessionMsgListener; -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.SessionInfoProto; -import org.thingsboard.server.transport.mqtt.MqttTopics; -import org.thingsboard.server.transport.mqtt.MqttTransportHandler; -import java.nio.charset.Charset; -import java.util.List; -import java.util.Optional; import java.util.UUID; import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.atomic.AtomicInteger; /** * Created by ashvayka on 19.01.17. @@ -59,15 +30,9 @@ import java.util.concurrent.atomic.AtomicInteger; @Slf4j public class GatewayDeviceSessionCtx extends MqttDeviceAwareSessionContext implements SessionMsgListener { - private static final Gson GSON = new Gson(); - private static final Charset UTF8 = Charset.forName("UTF-8"); - private static final ByteBufAllocator ALLOCATOR = new UnpooledByteBufAllocator(false); - private final GatewaySessionCtx parent; private final UUID sessionId; private final SessionInfoProto sessionInfo; - private volatile boolean closed; - private AtomicInteger msgIdSeq = new AtomicInteger(0); public GatewayDeviceSessionCtx(GatewaySessionCtx parent, DeviceInfoProto deviceInfo, ConcurrentMap mqttQoSMap) { super(mqttQoSMap); @@ -90,109 +55,9 @@ public class GatewayDeviceSessionCtx extends MqttDeviceAwareSessionContext imple return sessionId; } - private Optional getToDeviceMsg(SessionActorToAdaptorMsg sessionMsg) { - ToDeviceMsg msg = sessionMsg.getMsg(); - switch (msg.getSessionMsgType()) { - case STATUS_CODE_RESPONSE: - ResponseMsg responseMsg = (ResponseMsg) msg; - if (responseMsg.isSuccess()) { - SessionMsgType requestMsgType = responseMsg.getRequestMsgType(); - Integer requestId = responseMsg.getRequestId(); - if (requestId >= 0 && (requestMsgType == SessionMsgType.POST_ATTRIBUTES_REQUEST || requestMsgType == SessionMsgType.POST_TELEMETRY_REQUEST)) { - return Optional.of(MqttTransportHandler.createMqttPubAckMsg(requestId)); - } - } - break; - case GET_ATTRIBUTES_RESPONSE: - GetAttributesResponse response = (GetAttributesResponse) msg; - if (response.isSuccess()) { - return Optional.of(createMqttPublishMsg(MqttTopics.GATEWAY_ATTRIBUTES_RESPONSE_TOPIC, response)); - } else { - //TODO: push error handling to the gateway - } - break; - case ATTRIBUTES_UPDATE_NOTIFICATION: - AttributesUpdateNotification notification = (AttributesUpdateNotification) msg; - return Optional.of(createMqttPublishMsg(MqttTopics.GATEWAY_ATTRIBUTES_TOPIC, notification.getData())); - case TO_DEVICE_RPC_REQUEST: - ToDeviceRpcRequestMsg rpcRequest = (ToDeviceRpcRequestMsg) msg; - return Optional.of(createMqttPublishMsg(MqttTopics.GATEWAY_RPC_TOPIC, rpcRequest)); - default: - break; - } - return Optional.empty(); - } - - private MqttMessage createMqttPublishMsg(String topic, GetAttributesResponse response) { - JsonObject result = new JsonObject(); - result.addProperty("id", response.getRequestId()); -// result.addProperty(DEVICE_PROPERTY, device.getName()); - Optional responseData = response.getData(); - if (responseData.isPresent()) { - AttributesKVMsg msg = responseData.get(); - if (msg.getClientAttributes() != null) { - addValues(result, msg.getClientAttributes()); - } - if (msg.getSharedAttributes() != null) { - addValues(result, msg.getSharedAttributes()); - } - } - return createMqttPublishMsg(topic, result); - } - - private void addValues(JsonObject result, List kvList) { - if (kvList.size() == 1) { - addValueToJson(result, "value", kvList.get(0)); - } 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.getKey(), value)); - } - } - - private void addValueToJson(JsonObject json, String name, KvEntry entry) { - switch (entry.getDataType()) { - case BOOLEAN: - entry.getBooleanValue().ifPresent(aBoolean -> json.addProperty(name, aBoolean)); - break; - case STRING: - entry.getStrValue().ifPresent(aString -> json.addProperty(name, aString)); - break; - case DOUBLE: - entry.getDoubleValue().ifPresent(aDouble -> json.addProperty(name, aDouble)); - break; - case LONG: - entry.getLongValue().ifPresent(aLong -> json.addProperty(name, aLong)); - break; - } - } - - private MqttMessage createMqttPublishMsg(String topic, AttributesKVMsg data) { - JsonObject result = new JsonObject(); -// result.addProperty(DEVICE_PROPERTY, device.getName()); - result.add("data", JsonConverter.toJson(data, false)); - return createMqttPublishMsg(topic, result); - } - - private MqttMessage createMqttPublishMsg(String topic, ToDeviceRpcRequestMsg data) { - JsonObject result = new JsonObject(); -// result.addProperty(DEVICE_PROPERTY, device.getName()); - result.add("data", JsonConverter.toJson(data, true)); - return createMqttPublishMsg(topic, result); - } - - private MqttPublishMessage createMqttPublishMsg(String topic, JsonElement json) { - MqttFixedHeader mqttFixedHeader = - new MqttFixedHeader(MqttMessageType.PUBLISH, false, getQoSForTopic(topic), false, 0); - MqttPublishVariableHeader header = new MqttPublishVariableHeader(topic, msgIdSeq.incrementAndGet()); - ByteBuf payload = ALLOCATOR.buffer(); - payload.writeBytes(GSON.toJson(json).getBytes(UTF8)); - return new MqttPublishMessage(mqttFixedHeader, header, payload); + @Override + public int nextMsgId() { + return parent.nextMsgId(); } SessionInfoProto getSessionInfo() { @@ -223,12 +88,16 @@ public class GatewayDeviceSessionCtx extends MqttDeviceAwareSessionContext imple } @Override - public void onToDeviceRpcRequest(TransportProtos.ToDeviceRpcRequestMsg toDeviceRequest) { - + 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) { - TODO + // This feature is not supported in the TB IoT Gateway yet. } } diff --git a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionCtx.java b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionCtx.java index 3788d57e54..e038a4d269 100644 --- a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionCtx.java +++ b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionCtx.java @@ -52,6 +52,7 @@ import java.util.Set; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.atomic.AtomicInteger; /** * Created by ashvayka on 19.01.17. @@ -70,10 +71,12 @@ public class GatewaySessionCtx { private final Map devices; private final ConcurrentMap mqttQoSMap; private final ChannelHandlerContext channel; + private final DeviceSessionCtx deviceSessionCtx; public GatewaySessionCtx(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<>(); @@ -357,4 +360,8 @@ public class GatewaySessionCtx { public MqttTransportAdaptor getAdaptor() { return context.getAdaptor(); } + + public int nextMsgId() { + return deviceSessionCtx.nextMsgId(); + } } From 52ef95ac1be6988d5be7ef8e93fd98ff7911d53e Mon Sep 17 00:00:00 2001 From: Andrew Shvayka Date: Fri, 12 Oct 2018 12:20:22 +0300 Subject: [PATCH 13/26] Refactoring of MQTT Transport and removing data structures --- .../device/DeviceActorMessageProcessor.java | 15 -- .../actors/service/DefaultActorService.java | 1 - .../actors/shared/SessionTimeoutMsg.java | 35 ---- .../controller/TelemetryController.java | 7 +- .../rpc/ToServerRpcResponseActorMsg.java | 3 - .../transport/RemoteTransportApiService.java | 8 +- .../common/msg/aware/SessionAwareMsg.java | 22 -- .../ActorSystemToDeviceSessionActorMsg.java | 30 --- .../msg/core/AttributesSubscribeMsg.java | 29 --- .../msg/core/AttributesUnsubscribeMsg.java | 30 --- .../core/AttributesUpdateNotification.java | 47 ----- .../msg/core/AttributesUpdateRequest.java | 28 --- .../core/BasicAttributesUpdateRequest.java | 63 ------ .../msg/core/BasicCommandAckResponse.java | 45 ----- .../msg/core/BasicGetAttributesRequest.java | 59 ------ .../msg/core/BasicGetAttributesResponse.java | 40 ---- .../server/common/msg/core/BasicRequest.java | 36 ---- .../common/msg/core/BasicResponseMsg.java | 79 -------- .../msg/core/BasicStatusCodeResponse.java | 42 ---- .../msg/core/BasicTelemetryUploadRequest.java | 66 ------ .../common/msg/core/GetAttributesRequest.java | 29 --- .../msg/core/GetAttributesResponse.java | 22 -- .../server/common/msg/core/ResponseMsg.java | 33 --- .../common/msg/core/RpcSubscribeMsg.java | 30 --- .../common/msg/core/RpcUnsubscribeMsg.java | 30 --- .../common/msg/core/RuleEngineError.java | 43 ---- .../common/msg/core/RuleEngineErrorMsg.java | 53 ----- .../common/msg/core/SessionCloseMsg.java | 30 --- .../msg/core/SessionCloseNotification.java | 38 ---- .../common/msg/core/SessionOpenMsg.java | 32 --- .../common/msg/core/StatusCodeResponse.java | 20 -- .../msg/core/TelemetryUploadRequest.java | 29 --- .../msg/core/ToDeviceRpcRequestMsg.java | 41 ---- .../msg/core/ToDeviceRpcResponseMsg.java | 36 ---- .../msg/core/ToServerRpcRequestMsg.java | 36 ---- .../msg/core/ToServerRpcResponseMsg.java | 11 +- .../msg/session/AdaptorToSessionActorMsg.java | 22 -- .../BasicAdaptorToSessionActorMsg.java | 32 --- .../BasicSessionActorToAdaptorMsg.java | 32 --- .../common/msg/session/BasicSessionMsg.java | 42 ---- ...BasicTransportToDeviceSessionActorMsg.java | 73 ------- .../common/msg/session/FromDeviceMsg.java | 24 --- .../msg/session/FromDeviceRequestMsg.java | 25 --- .../msg/session/SessionActorToAdaptorMsg.java | 22 -- .../common/msg/session/SessionContext.java | 8 +- .../common/msg/session/SessionCtrlMsg.java | 23 --- .../server/common/msg/session/SessionMsg.java | 24 --- .../common/msg/session/SessionType.java | 22 -- .../common/msg/session/ToDeviceMsg.java | 26 --- .../TransportToDeviceSessionActorMsg.java | 28 --- .../msg/session/ctrl/SessionCloseMsg.java | 67 ------ .../common/transport/SessionMsgProcessor.java | 1 - .../transport/adaptor/JsonConverter.java | 190 ++++++++---------- .../session/DeviceAwareSessionContext.java | 2 + .../TbCopyAttributesToEntityViewNode.java | 2 +- .../engine/telemetry/TbMsgAttributesNode.java | 2 +- .../engine/telemetry/TbMsgTimeseriesNode.java | 7 +- .../coap/adaptors/JsonCoapAdaptor.java | 6 - .../coap/session/CoapSessionCtx.java | 4 - .../server/transport/coap/CoapServerTest.java | 11 - .../transport/http/DeviceApiController.java | 14 -- .../transport/mqtt/MqttTransportContext.java | 8 +- .../transport/mqtt/MqttTransportHandler.java | 8 +- .../mqtt/adaptors/JsonMqttAdaptor.java | 8 +- .../mqtt/session/DeviceSessionCtx.java | 56 +----- .../mqtt/session/GatewayDeviceSessionCtx.java | 12 +- .../mqtt/session/GatewaySessionCtx.java | 8 +- .../MqttDeviceAwareSessionContext.java | 5 +- .../transport/mqtt/session/MqttSessionId.java | 60 ------ .../mqtt/service/MqttTransportService.java | 10 +- transport/pom.xml | 4 +- 71 files changed, 136 insertions(+), 1950 deletions(-) delete mode 100644 application/src/main/java/org/thingsboard/server/actors/shared/SessionTimeoutMsg.java delete mode 100644 common/message/src/main/java/org/thingsboard/server/common/msg/aware/SessionAwareMsg.java delete mode 100644 common/message/src/main/java/org/thingsboard/server/common/msg/core/ActorSystemToDeviceSessionActorMsg.java delete mode 100644 common/message/src/main/java/org/thingsboard/server/common/msg/core/AttributesSubscribeMsg.java delete mode 100644 common/message/src/main/java/org/thingsboard/server/common/msg/core/AttributesUnsubscribeMsg.java delete mode 100644 common/message/src/main/java/org/thingsboard/server/common/msg/core/AttributesUpdateNotification.java delete mode 100644 common/message/src/main/java/org/thingsboard/server/common/msg/core/AttributesUpdateRequest.java delete mode 100644 common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicAttributesUpdateRequest.java delete mode 100644 common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicCommandAckResponse.java delete mode 100644 common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicGetAttributesRequest.java delete mode 100644 common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicGetAttributesResponse.java delete mode 100644 common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicRequest.java delete mode 100644 common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicResponseMsg.java delete mode 100644 common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicStatusCodeResponse.java delete mode 100644 common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicTelemetryUploadRequest.java delete mode 100644 common/message/src/main/java/org/thingsboard/server/common/msg/core/GetAttributesRequest.java delete mode 100644 common/message/src/main/java/org/thingsboard/server/common/msg/core/GetAttributesResponse.java delete mode 100644 common/message/src/main/java/org/thingsboard/server/common/msg/core/ResponseMsg.java delete mode 100644 common/message/src/main/java/org/thingsboard/server/common/msg/core/RpcSubscribeMsg.java delete mode 100644 common/message/src/main/java/org/thingsboard/server/common/msg/core/RpcUnsubscribeMsg.java delete mode 100644 common/message/src/main/java/org/thingsboard/server/common/msg/core/RuleEngineError.java delete mode 100644 common/message/src/main/java/org/thingsboard/server/common/msg/core/RuleEngineErrorMsg.java delete mode 100644 common/message/src/main/java/org/thingsboard/server/common/msg/core/SessionCloseMsg.java delete mode 100644 common/message/src/main/java/org/thingsboard/server/common/msg/core/SessionCloseNotification.java delete mode 100644 common/message/src/main/java/org/thingsboard/server/common/msg/core/SessionOpenMsg.java delete mode 100644 common/message/src/main/java/org/thingsboard/server/common/msg/core/StatusCodeResponse.java delete mode 100644 common/message/src/main/java/org/thingsboard/server/common/msg/core/TelemetryUploadRequest.java delete mode 100644 common/message/src/main/java/org/thingsboard/server/common/msg/core/ToDeviceRpcRequestMsg.java delete mode 100644 common/message/src/main/java/org/thingsboard/server/common/msg/core/ToDeviceRpcResponseMsg.java delete mode 100644 common/message/src/main/java/org/thingsboard/server/common/msg/core/ToServerRpcRequestMsg.java delete mode 100644 common/message/src/main/java/org/thingsboard/server/common/msg/session/AdaptorToSessionActorMsg.java delete mode 100644 common/message/src/main/java/org/thingsboard/server/common/msg/session/BasicAdaptorToSessionActorMsg.java delete mode 100644 common/message/src/main/java/org/thingsboard/server/common/msg/session/BasicSessionActorToAdaptorMsg.java delete mode 100644 common/message/src/main/java/org/thingsboard/server/common/msg/session/BasicSessionMsg.java delete mode 100644 common/message/src/main/java/org/thingsboard/server/common/msg/session/BasicTransportToDeviceSessionActorMsg.java delete mode 100644 common/message/src/main/java/org/thingsboard/server/common/msg/session/FromDeviceMsg.java delete mode 100644 common/message/src/main/java/org/thingsboard/server/common/msg/session/FromDeviceRequestMsg.java delete mode 100644 common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionActorToAdaptorMsg.java delete mode 100644 common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionCtrlMsg.java delete mode 100644 common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionMsg.java delete mode 100644 common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionType.java delete mode 100644 common/message/src/main/java/org/thingsboard/server/common/msg/session/ToDeviceMsg.java delete mode 100644 common/message/src/main/java/org/thingsboard/server/common/msg/session/TransportToDeviceSessionActorMsg.java delete mode 100644 common/message/src/main/java/org/thingsboard/server/common/msg/session/ctrl/SessionCloseMsg.java delete mode 100644 transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/MqttSessionId.java 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 1cfdcd202a..64480863f5 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 @@ -42,12 +42,8 @@ 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.RuleEngineError; -import org.thingsboard.server.common.msg.core.RuleEngineErrorMsg; import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest; import org.thingsboard.server.common.msg.session.SessionMsgType; -import org.thingsboard.server.common.msg.session.ToDeviceMsg; import org.thingsboard.server.common.msg.timeout.DeviceActorClientSideRpcTimeoutMsg; import org.thingsboard.server.common.msg.timeout.DeviceActorServerSideRpcTimeoutMsg; import org.thingsboard.server.gen.transport.TransportProtos; @@ -481,17 +477,6 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso } } - private void sendMsgToSessionActor(ActorSystemToDeviceSessionActorMsg response, Optional 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(); 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 b70d1ed480..c2e8fd048a 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 @@ -37,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; diff --git a/application/src/main/java/org/thingsboard/server/actors/shared/SessionTimeoutMsg.java b/application/src/main/java/org/thingsboard/server/actors/shared/SessionTimeoutMsg.java deleted file mode 100644 index d89848b8a8..0000000000 --- a/application/src/main/java/org/thingsboard/server/actors/shared/SessionTimeoutMsg.java +++ /dev/null @@ -1,35 +0,0 @@ -/** - * 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.actors.shared; - -import lombok.Data; -import org.thingsboard.server.common.msg.MsgType; -import org.thingsboard.server.common.msg.TbActorMsg; - -import java.io.Serializable; - -@Data -public class SessionTimeoutMsg implements Serializable, TbActorMsg { - - private static final long serialVersionUID = 1L; - - private final SessionId sessionId; - - @Override - public MsgType getMsgType() { - return MsgType.SESSION_TIMEOUT_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 7062ca97f9..b19d782805 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; @@ -352,7 +351,7 @@ public class TelemetryController extends BaseController { } private DeferredResult saveTelemetry(EntityId entityIdSrc, String requestBody, long ttl) throws ThingsboardException { - TelemetryUploadRequest telemetryRequest; + Map> telemetryRequest; JsonElement telemetryJson; try { telemetryJson = new JsonParser().parse(requestBody); @@ -360,12 +359,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 entries = new ArrayList<>(); - for (Map.Entry> entry : telemetryRequest.getData().entrySet()) { + for (Map.Entry> 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/rpc/ToServerRpcResponseActorMsg.java b/application/src/main/java/org/thingsboard/server/service/rpc/ToServerRpcResponseActorMsg.java index 201f65613a..d33a3380f4 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/transport/RemoteTransportApiService.java b/application/src/main/java/org/thingsboard/server/service/transport/RemoteTransportApiService.java index 5bcd8ac888..945317f768 100644 --- a/application/src/main/java/org/thingsboard/server/service/transport/RemoteTransportApiService.java +++ b/application/src/main/java/org/thingsboard/server/service/transport/RemoteTransportApiService.java @@ -1,12 +1,12 @@ /** * 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 - *

+ * + * 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. diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/aware/SessionAwareMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/aware/SessionAwareMsg.java deleted file mode 100644 index 1c258239d4..0000000000 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/aware/SessionAwareMsg.java +++ /dev/null @@ -1,22 +0,0 @@ -/** - * 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.msg.aware; - -public interface SessionAwareMsg { - - SessionId getSessionId(); - -} diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/ActorSystemToDeviceSessionActorMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/core/ActorSystemToDeviceSessionActorMsg.java deleted file mode 100644 index 6bb5c9911d..0000000000 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/ActorSystemToDeviceSessionActorMsg.java +++ /dev/null @@ -1,30 +0,0 @@ -/** - * 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.msg.core; - -import org.thingsboard.server.common.msg.TbActorMsg; -import org.thingsboard.server.common.msg.aware.SessionAwareMsg; -import org.thingsboard.server.common.msg.session.ToDeviceMsg; - -import java.io.Serializable; - -/** - * @author Andrew Shvayka - */ -public interface ActorSystemToDeviceSessionActorMsg extends SessionAwareMsg, Serializable, TbActorMsg { - - ToDeviceMsg getMsg(); -} diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/AttributesSubscribeMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/core/AttributesSubscribeMsg.java deleted file mode 100644 index cadaf3ce56..0000000000 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/AttributesSubscribeMsg.java +++ /dev/null @@ -1,29 +0,0 @@ -/** - * 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.msg.core; - -import org.thingsboard.server.common.msg.session.FromDeviceMsg; -import org.thingsboard.server.common.msg.session.SessionMsgType; - -/** - * @author Andrew Shvayka - */ -public class AttributesSubscribeMsg implements FromDeviceMsg { - @Override - public SessionMsgType getMsgType() { - return SessionMsgType.SUBSCRIBE_ATTRIBUTES_REQUEST; - } -} diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/AttributesUnsubscribeMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/core/AttributesUnsubscribeMsg.java deleted file mode 100644 index c98ad2ae26..0000000000 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/AttributesUnsubscribeMsg.java +++ /dev/null @@ -1,30 +0,0 @@ -/** - * 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.msg.core; - -import org.thingsboard.server.common.msg.session.FromDeviceMsg; -import org.thingsboard.server.common.msg.session.SessionMsgType; -import org.thingsboard.server.common.msg.session.SessionMsgType; - -/** - * @author Andrew Shvayka - */ -public class AttributesUnsubscribeMsg implements FromDeviceMsg { - @Override - public SessionMsgType getMsgType() { - return SessionMsgType.UNSUBSCRIBE_ATTRIBUTES_REQUEST; - } -} diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/AttributesUpdateNotification.java b/common/message/src/main/java/org/thingsboard/server/common/msg/core/AttributesUpdateNotification.java deleted file mode 100644 index 1f0f9ddc99..0000000000 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/AttributesUpdateNotification.java +++ /dev/null @@ -1,47 +0,0 @@ -/** - * 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.msg.core; - -import lombok.ToString; -import org.thingsboard.server.common.msg.kv.AttributesKVMsg; -import org.thingsboard.server.common.msg.session.SessionMsgType; -import org.thingsboard.server.common.msg.session.SessionMsgType; -import org.thingsboard.server.common.msg.session.ToDeviceMsg; - -@ToString -public class AttributesUpdateNotification implements ToDeviceMsg { - - private static final long serialVersionUID = 1L; - - private AttributesKVMsg data; - - public AttributesUpdateNotification(AttributesKVMsg data) { - this.data = data; - } - - @Override - public boolean isSuccess() { - return true; - } - - public SessionMsgType getSessionMsgType() { - return SessionMsgType.ATTRIBUTES_UPDATE_NOTIFICATION; - } - - public AttributesKVMsg getData() { - return data; - } -} diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/AttributesUpdateRequest.java b/common/message/src/main/java/org/thingsboard/server/common/msg/core/AttributesUpdateRequest.java deleted file mode 100644 index d4cb4b1ae6..0000000000 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/AttributesUpdateRequest.java +++ /dev/null @@ -1,28 +0,0 @@ -/** - * 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.msg.core; - -import java.util.Set; - -import org.thingsboard.server.common.data.kv.AttributeKvEntry; -import org.thingsboard.server.common.msg.session.FromDeviceMsg; -import org.thingsboard.server.common.msg.session.FromDeviceRequestMsg; - -public interface AttributesUpdateRequest extends FromDeviceRequestMsg { - - Set getAttributes(); - -} diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicAttributesUpdateRequest.java b/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicAttributesUpdateRequest.java deleted file mode 100644 index 76efca0f07..0000000000 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicAttributesUpdateRequest.java +++ /dev/null @@ -1,63 +0,0 @@ -/** - * 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.msg.core; - -import java.util.Collection; -import java.util.LinkedHashSet; -import java.util.Set; - -import org.thingsboard.server.common.data.kv.AttributeKvEntry; -import org.thingsboard.server.common.msg.session.SessionMsgType; - -public class BasicAttributesUpdateRequest extends BasicRequest implements AttributesUpdateRequest { - - private static final long serialVersionUID = 1L; - - private final Set data; - - public BasicAttributesUpdateRequest() { - this(DEFAULT_REQUEST_ID); - } - - public BasicAttributesUpdateRequest(Integer requestId) { - super(requestId); - this.data = new LinkedHashSet<>(); - } - - public void add(AttributeKvEntry entry) { - this.data.add(entry); - } - - public void add(Collection entries) { - this.data.addAll(entries); - } - - @Override - public SessionMsgType getMsgType() { - return SessionMsgType.POST_ATTRIBUTES_REQUEST; - } - - @Override - public Set getAttributes() { - return data; - } - - @Override - public String toString() { - return "BasicAttributesUpdateRequest [data=" + data + "]"; - } - -} diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicCommandAckResponse.java b/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicCommandAckResponse.java deleted file mode 100644 index 3ee04ba216..0000000000 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicCommandAckResponse.java +++ /dev/null @@ -1,45 +0,0 @@ -/** - * 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.msg.core; - -import org.thingsboard.server.common.msg.session.SessionMsgType; -import org.thingsboard.server.common.msg.session.SessionMsgType; - -public class BasicCommandAckResponse extends BasicResponseMsg implements StatusCodeResponse { - - private static final long serialVersionUID = 1L; - - public static BasicCommandAckResponse onSuccess(SessionMsgType requestMsgType, Integer requestId) { - return BasicCommandAckResponse.onSuccess(requestMsgType, requestId, 200); - } - - public static BasicCommandAckResponse onSuccess(SessionMsgType requestMsgType, Integer requestId, Integer code) { - return new BasicCommandAckResponse(requestMsgType, requestId, true, null, code); - } - - public static BasicCommandAckResponse onError(SessionMsgType requestMsgType, Integer requestId, Exception error) { - return new BasicCommandAckResponse(requestMsgType, requestId, false, error, null); - } - - private BasicCommandAckResponse(SessionMsgType requestMsgType, Integer requestId, boolean success, Exception error, Integer code) { - super(requestMsgType, requestId, SessionMsgType.TO_DEVICE_RPC_RESPONSE_ACK, success, error, code); - } - - @Override - public String toString() { - return "BasicStatusCodeResponse []"; - } -} diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicGetAttributesRequest.java b/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicGetAttributesRequest.java deleted file mode 100644 index e0f6d7ecc4..0000000000 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicGetAttributesRequest.java +++ /dev/null @@ -1,59 +0,0 @@ -/** - * 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.msg.core; - -import lombok.ToString; -import org.thingsboard.server.common.msg.session.SessionMsgType; -import org.thingsboard.server.common.msg.session.SessionMsgType; - -import java.util.Collections; -import java.util.Optional; -import java.util.Set; - -@ToString -public class BasicGetAttributesRequest extends BasicRequest implements GetAttributesRequest { - - private static final long serialVersionUID = 1L; - - private final Set clientKeys; - private final Set sharedKeys; - - public BasicGetAttributesRequest(Integer requestId) { - this(requestId, Collections.emptySet(), Collections.emptySet()); - } - - public BasicGetAttributesRequest(Integer requestId, Set clientKeys, Set sharedKeys) { - super(requestId); - this.clientKeys = clientKeys; - this.sharedKeys = sharedKeys; - } - - @Override - public SessionMsgType getMsgType() { - return SessionMsgType.GET_ATTRIBUTES_REQUEST; - } - - @Override - public Optional> getClientAttributeNames() { - return Optional.ofNullable(clientKeys); - } - - @Override - public Optional> getSharedAttributeNames() { - return Optional.ofNullable(sharedKeys); - } - -} diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicGetAttributesResponse.java b/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicGetAttributesResponse.java deleted file mode 100644 index e3eb15d807..0000000000 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicGetAttributesResponse.java +++ /dev/null @@ -1,40 +0,0 @@ -/** - * 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.msg.core; - -import lombok.ToString; -import org.thingsboard.server.common.msg.kv.AttributesKVMsg; -import org.thingsboard.server.common.msg.session.SessionMsgType; -import org.thingsboard.server.common.msg.session.SessionMsgType; - -@ToString -public class BasicGetAttributesResponse extends BasicResponseMsg implements GetAttributesResponse { - - private static final long serialVersionUID = 1L; - - public static BasicGetAttributesResponse onSuccess(SessionMsgType requestMsgType, int requestId, AttributesKVMsg code) { - return new BasicGetAttributesResponse(requestMsgType, requestId, true, null, code); - } - - public static BasicGetAttributesResponse onError(SessionMsgType requestMsgType, int requestId, Exception error) { - return new BasicGetAttributesResponse(requestMsgType, requestId, false, error, null); - } - - private BasicGetAttributesResponse(SessionMsgType requestMsgType, int requestId, boolean success, Exception error, AttributesKVMsg code) { - super(requestMsgType, requestId, SessionMsgType.GET_ATTRIBUTES_RESPONSE, success, error, code); - } - -} diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicRequest.java b/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicRequest.java deleted file mode 100644 index 4fcde4456b..0000000000 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicRequest.java +++ /dev/null @@ -1,36 +0,0 @@ -/** - * 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.msg.core; - -import java.io.Serializable; - -/** - * @author Andrew Shvayka - */ -public class BasicRequest implements Serializable { - - public static final Integer DEFAULT_REQUEST_ID = 0; - - private final Integer requestId; - - public BasicRequest(Integer requestId) { - this.requestId = requestId; - } - - public Integer getRequestId() { - return requestId; - } -} diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicResponseMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicResponseMsg.java deleted file mode 100644 index c61d8e5e03..0000000000 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicResponseMsg.java +++ /dev/null @@ -1,79 +0,0 @@ -/** - * 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.msg.core; - -import java.io.Serializable; -import java.util.Optional; - -import org.thingsboard.server.common.msg.session.SessionMsgType; -import org.thingsboard.server.common.msg.session.SessionMsgType; - - -public class BasicResponseMsg implements ResponseMsg { - - private static final long serialVersionUID = 1L; - - private final SessionMsgType requestMsgType; - private final Integer requestId; - private final SessionMsgType sessionMsgType; - private final boolean success; - private final T data; - private final Exception error; - - protected BasicResponseMsg(SessionMsgType requestMsgType, Integer requestId, SessionMsgType sessionMsgType, boolean success, Exception error, T data) { - super(); - this.requestMsgType = requestMsgType; - this.requestId = requestId; - this.sessionMsgType = sessionMsgType; - this.success = success; - this.error = error; - this.data = data; - } - - @Override - public SessionMsgType getRequestMsgType() { - return requestMsgType; - } - - @Override - public Integer getRequestId() { - return requestId; - } - - @Override - public boolean isSuccess() { - return success; - } - - @Override - public Optional getError() { - return Optional.ofNullable(error); - } - - @Override - public Optional getData() { - return Optional.ofNullable(data); - } - - @Override - public String toString() { - return "BasicResponseMsg [success=" + success + ", data=" + data + ", error=" + error + "]"; - } - - public SessionMsgType getSessionMsgType() { - return sessionMsgType; - } -} diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicStatusCodeResponse.java b/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicStatusCodeResponse.java deleted file mode 100644 index f21aa857bf..0000000000 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicStatusCodeResponse.java +++ /dev/null @@ -1,42 +0,0 @@ -/** - * 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.msg.core; - -import lombok.ToString; -import org.thingsboard.server.common.msg.session.SessionMsgType; -import org.thingsboard.server.common.msg.session.SessionMsgType; - -@ToString -public class BasicStatusCodeResponse extends BasicResponseMsg implements StatusCodeResponse { - - private static final long serialVersionUID = 1L; - - public static BasicStatusCodeResponse onSuccess(SessionMsgType requestMsgType, Integer requestId) { - return BasicStatusCodeResponse.onSuccess(requestMsgType, requestId, 0); - } - - public static BasicStatusCodeResponse onSuccess(SessionMsgType requestMsgType, Integer requestId, Integer code) { - return new BasicStatusCodeResponse(requestMsgType, requestId, true, null, code); - } - - public static BasicStatusCodeResponse onError(SessionMsgType requestMsgType, Integer requestId, Exception error) { - return new BasicStatusCodeResponse(requestMsgType, requestId, false, error, null); - } - - private BasicStatusCodeResponse(SessionMsgType requestMsgType, Integer requestId, boolean success, Exception error, Integer code) { - super(requestMsgType, requestId, SessionMsgType.STATUS_CODE_RESPONSE, success, error, code); - } -} diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicTelemetryUploadRequest.java b/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicTelemetryUploadRequest.java deleted file mode 100644 index 60faeb1829..0000000000 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicTelemetryUploadRequest.java +++ /dev/null @@ -1,66 +0,0 @@ -/** - * 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.msg.core; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.thingsboard.server.common.data.kv.KvEntry; -import org.thingsboard.server.common.msg.session.SessionMsgType; -import org.thingsboard.server.common.msg.session.SessionMsgType; - -public class BasicTelemetryUploadRequest extends BasicRequest implements TelemetryUploadRequest { - - private static final long serialVersionUID = 1L; - - private final Map> data; - - public BasicTelemetryUploadRequest() { - this(DEFAULT_REQUEST_ID); - } - - public BasicTelemetryUploadRequest(Integer requestId) { - super(requestId); - this.data = new HashMap<>(); - } - - public void add(long ts, KvEntry entry) { - List tsEntries = data.get(ts); - if (tsEntries == null) { - tsEntries = new ArrayList<>(); - data.put(ts, tsEntries); - } - tsEntries.add(entry); - } - - @Override - public SessionMsgType getMsgType() { - return SessionMsgType.POST_TELEMETRY_REQUEST; - } - - @Override - public Map> getData() { - return data; - } - - @Override - public String toString() { - return "BasicTelemetryUploadRequest [data=" + data + "]"; - } - -} diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/GetAttributesRequest.java b/common/message/src/main/java/org/thingsboard/server/common/msg/core/GetAttributesRequest.java deleted file mode 100644 index 241bb6ba1e..0000000000 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/GetAttributesRequest.java +++ /dev/null @@ -1,29 +0,0 @@ -/** - * 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.msg.core; - -import java.util.Optional; -import java.util.Set; - -import org.thingsboard.server.common.msg.session.FromDeviceMsg; -import org.thingsboard.server.common.msg.session.FromDeviceRequestMsg; - -public interface GetAttributesRequest extends FromDeviceRequestMsg { - - Optional> getClientAttributeNames(); - Optional> getSharedAttributeNames(); - -} diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/GetAttributesResponse.java b/common/message/src/main/java/org/thingsboard/server/common/msg/core/GetAttributesResponse.java deleted file mode 100644 index 6242d46e8f..0000000000 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/GetAttributesResponse.java +++ /dev/null @@ -1,22 +0,0 @@ -/** - * 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.msg.core; - -import org.thingsboard.server.common.msg.kv.AttributesKVMsg; - -public interface GetAttributesResponse extends ResponseMsg { - -} diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/ResponseMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/core/ResponseMsg.java deleted file mode 100644 index 3e70460dcb..0000000000 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/ResponseMsg.java +++ /dev/null @@ -1,33 +0,0 @@ -/** - * 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.msg.core; - -import java.io.Serializable; -import java.util.Optional; - -import org.thingsboard.server.common.msg.session.SessionMsgType; -import org.thingsboard.server.common.msg.session.ToDeviceMsg; - -public interface ResponseMsg extends ToDeviceMsg { - - SessionMsgType getRequestMsgType(); - - Integer getRequestId(); - - Optional getError(); - - Optional getData(); -} diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/RpcSubscribeMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/core/RpcSubscribeMsg.java deleted file mode 100644 index f8f24e8d20..0000000000 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/RpcSubscribeMsg.java +++ /dev/null @@ -1,30 +0,0 @@ -/** - * 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.msg.core; - -import org.thingsboard.server.common.msg.session.FromDeviceMsg; -import org.thingsboard.server.common.msg.session.SessionMsgType; -import org.thingsboard.server.common.msg.session.SessionMsgType; - -/** - * @author Andrew Shvayka - */ -public class RpcSubscribeMsg implements FromDeviceMsg { - @Override - public SessionMsgType getMsgType() { - return SessionMsgType.SUBSCRIBE_RPC_COMMANDS_REQUEST; - } -} diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/RpcUnsubscribeMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/core/RpcUnsubscribeMsg.java deleted file mode 100644 index 23eb238d79..0000000000 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/RpcUnsubscribeMsg.java +++ /dev/null @@ -1,30 +0,0 @@ -/** - * 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.msg.core; - -import org.thingsboard.server.common.msg.session.FromDeviceMsg; -import org.thingsboard.server.common.msg.session.SessionMsgType; -import org.thingsboard.server.common.msg.session.SessionMsgType; - -/** - * @author Andrew Shvayka - */ -public class RpcUnsubscribeMsg implements FromDeviceMsg { - @Override - public SessionMsgType getMsgType() { - return SessionMsgType.UNSUBSCRIBE_RPC_COMMANDS_REQUEST; - } -} diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/RuleEngineError.java b/common/message/src/main/java/org/thingsboard/server/common/msg/core/RuleEngineError.java deleted file mode 100644 index dcfde0fb52..0000000000 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/RuleEngineError.java +++ /dev/null @@ -1,43 +0,0 @@ -/** - * 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.msg.core; - -/** - * @author Andrew Shvayka - */ - -public enum RuleEngineError { - - QUEUE_PUT_TIMEOUT(true), SERVER_ERROR(true), TIMEOUT; - - private final boolean critical; - - RuleEngineError() { - this(false); - } - - RuleEngineError(boolean critical) { - this.critical = critical; - } - - public boolean isCritical() { - return critical; - } - - public int getPriority() { - return ordinal(); - } -} diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/RuleEngineErrorMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/core/RuleEngineErrorMsg.java deleted file mode 100644 index e0ff23b379..0000000000 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/RuleEngineErrorMsg.java +++ /dev/null @@ -1,53 +0,0 @@ -/** - * 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.msg.core; - -import lombok.Data; -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 RuleEngineErrorMsg implements ToDeviceMsg { - - private final SessionMsgType inSessionMsgType; - private final RuleEngineError error; - - @Override - public boolean isSuccess() { - return false; - } - - public SessionMsgType getSessionMsgType() { - return SessionMsgType.RULE_ENGINE_ERROR; - } - - public String getErrorMsg() { - switch (error) { - case QUEUE_PUT_TIMEOUT: - return "Timeout during persistence of the message to the queue!"; - case SERVER_ERROR: - return "Error during processing of message by the server!"; - case TIMEOUT: - return "Timeout during processing of message by the server!"; - default: - throw new RuntimeException("Error " + error + " is not supported!"); - } - } -} diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/SessionCloseMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/core/SessionCloseMsg.java deleted file mode 100644 index 6d3ad5e8be..0000000000 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/SessionCloseMsg.java +++ /dev/null @@ -1,30 +0,0 @@ -/** - * 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.msg.core; - -import org.thingsboard.server.common.msg.session.FromDeviceMsg; -import org.thingsboard.server.common.msg.session.SessionMsgType; -import org.thingsboard.server.common.msg.session.SessionMsgType; - -/** - * @author Andrew Shvayka - */ -public class SessionCloseMsg implements FromDeviceMsg { - @Override - public SessionMsgType getMsgType() { - return SessionMsgType.SESSION_CLOSE; - } -} diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/SessionCloseNotification.java b/common/message/src/main/java/org/thingsboard/server/common/msg/core/SessionCloseNotification.java deleted file mode 100644 index bf36a9b3c3..0000000000 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/SessionCloseNotification.java +++ /dev/null @@ -1,38 +0,0 @@ -/** - * 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.msg.core; - -import lombok.ToString; -import org.thingsboard.server.common.msg.kv.AttributesKVMsg; -import org.thingsboard.server.common.msg.session.SessionMsgType; -import org.thingsboard.server.common.msg.session.SessionMsgType; -import org.thingsboard.server.common.msg.session.ToDeviceMsg; - -@ToString -public class SessionCloseNotification implements ToDeviceMsg { - - private static final long serialVersionUID = 1L; - - @Override - public boolean isSuccess() { - return true; - } - - public SessionMsgType getSessionMsgType() { - return SessionMsgType.SESSION_CLOSE; - } - -} diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/SessionOpenMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/core/SessionOpenMsg.java deleted file mode 100644 index d8af5a953c..0000000000 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/SessionOpenMsg.java +++ /dev/null @@ -1,32 +0,0 @@ -/** - * 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.msg.core; - -import lombok.Data; -import org.thingsboard.server.common.msg.session.FromDeviceMsg; -import org.thingsboard.server.common.msg.session.SessionMsgType; - -/** - * @author Andrew Shvayka - */ -@Data -public class SessionOpenMsg implements FromDeviceMsg { - - @Override - public SessionMsgType getMsgType() { - return SessionMsgType.SESSION_OPEN; - } -} diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/StatusCodeResponse.java b/common/message/src/main/java/org/thingsboard/server/common/msg/core/StatusCodeResponse.java deleted file mode 100644 index b2d8611d1f..0000000000 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/StatusCodeResponse.java +++ /dev/null @@ -1,20 +0,0 @@ -/** - * 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.msg.core; - -public interface StatusCodeResponse extends ResponseMsg{ - -} diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/TelemetryUploadRequest.java b/common/message/src/main/java/org/thingsboard/server/common/msg/core/TelemetryUploadRequest.java deleted file mode 100644 index 52c2f9e161..0000000000 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/TelemetryUploadRequest.java +++ /dev/null @@ -1,29 +0,0 @@ -/** - * 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.msg.core; - -import java.util.List; -import java.util.Map; - -import org.thingsboard.server.common.data.kv.KvEntry; -import org.thingsboard.server.common.msg.session.FromDeviceMsg; -import org.thingsboard.server.common.msg.session.FromDeviceRequestMsg; - -public interface TelemetryUploadRequest extends FromDeviceRequestMsg { - - Map> getData(); - -} diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/ToDeviceRpcRequestMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/core/ToDeviceRpcRequestMsg.java deleted file mode 100644 index d1a1f96451..0000000000 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/ToDeviceRpcRequestMsg.java +++ /dev/null @@ -1,41 +0,0 @@ -/** - * 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.msg.core; - -import lombok.Data; -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 ToDeviceRpcRequestMsg implements ToDeviceMsg { - - private final int requestId; - private final String method; - private final String params; - - public SessionMsgType getSessionMsgType() { - return SessionMsgType.TO_DEVICE_RPC_REQUEST; - } - - @Override - public boolean isSuccess() { - return true; - } -} diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/ToDeviceRpcResponseMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/core/ToDeviceRpcResponseMsg.java deleted file mode 100644 index 4fa3024947..0000000000 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/ToDeviceRpcResponseMsg.java +++ /dev/null @@ -1,36 +0,0 @@ -/** - * 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.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; - -/** - * @author Andrew Shvayka - */ -@Data -public class ToDeviceRpcResponseMsg implements FromDeviceMsg { - - private final int requestId; - private final String data; - - @Override - public SessionMsgType getMsgType() { - return SessionMsgType.TO_DEVICE_RPC_RESPONSE; - } -} diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/ToServerRpcRequestMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/core/ToServerRpcRequestMsg.java deleted file mode 100644 index 3823acac70..0000000000 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/ToServerRpcRequestMsg.java +++ /dev/null @@ -1,36 +0,0 @@ -/** - * 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.msg.core; - -import lombok.Data; -import org.thingsboard.server.common.msg.session.FromDeviceRequestMsg; -import org.thingsboard.server.common.msg.session.SessionMsgType; - -/** - * @author Andrew Shvayka - */ -@Data -public class ToServerRpcRequestMsg implements FromDeviceRequestMsg { - - private final Integer requestId; - private final String method; - private final String params; - - @Override - public SessionMsgType getMsgType() { - return SessionMsgType.TO_SERVER_RPC_REQUEST; - } -} 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 82f44e9113..fa9ef05fe8 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/session/AdaptorToSessionActorMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/session/AdaptorToSessionActorMsg.java deleted file mode 100644 index 5383a5253e..0000000000 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/session/AdaptorToSessionActorMsg.java +++ /dev/null @@ -1,22 +0,0 @@ -/** - * 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.msg.session; - -public interface AdaptorToSessionActorMsg extends SessionMsg { - - FromDeviceMsg getMsg(); - -} diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/session/BasicAdaptorToSessionActorMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/session/BasicAdaptorToSessionActorMsg.java deleted file mode 100644 index 21c28d25be..0000000000 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/session/BasicAdaptorToSessionActorMsg.java +++ /dev/null @@ -1,32 +0,0 @@ -/** - * 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.msg.session; - -public class BasicAdaptorToSessionActorMsg extends BasicSessionMsg implements AdaptorToSessionActorMsg { - - private final FromDeviceMsg msg; - - public BasicAdaptorToSessionActorMsg(SessionContext ctx, FromDeviceMsg msg) { - super(ctx); - this.msg = msg; - } - - @Override - public FromDeviceMsg getMsg() { - return msg; - } - -} diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/session/BasicSessionActorToAdaptorMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/session/BasicSessionActorToAdaptorMsg.java deleted file mode 100644 index 0e1a087d6e..0000000000 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/session/BasicSessionActorToAdaptorMsg.java +++ /dev/null @@ -1,32 +0,0 @@ -/** - * 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.msg.session; - -public class BasicSessionActorToAdaptorMsg extends BasicSessionMsg implements SessionActorToAdaptorMsg { - - private final ToDeviceMsg msg; - - public BasicSessionActorToAdaptorMsg(SessionContext ctx, ToDeviceMsg msg) { - super(ctx); - this.msg = msg; - } - - @Override - public ToDeviceMsg getMsg() { - return msg; - } - -} diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/session/BasicSessionMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/session/BasicSessionMsg.java deleted file mode 100644 index 84c5052662..0000000000 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/session/BasicSessionMsg.java +++ /dev/null @@ -1,42 +0,0 @@ -/** - * 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.msg.session; - -public class BasicSessionMsg implements SessionMsg { - - private final SessionContext ctx; - - public BasicSessionMsg(SessionContext ctx) { - super(); - this.ctx = ctx; - } - - @Override - public SessionId getSessionId() { - return ctx.getSessionId(); - } - - @Override - public SessionContext getSessionContext() { - return ctx; - } - - @Override - public String toString() { - return "BasicSessionMsg [ctx=" + ctx + "]"; - } - -} diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/session/BasicTransportToDeviceSessionActorMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/session/BasicTransportToDeviceSessionActorMsg.java deleted file mode 100644 index 4a81fd4578..0000000000 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/session/BasicTransportToDeviceSessionActorMsg.java +++ /dev/null @@ -1,73 +0,0 @@ -/** - * 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.msg.session; - -import org.thingsboard.server.common.data.Device; -import org.thingsboard.server.common.data.id.CustomerId; -import org.thingsboard.server.common.data.id.DeviceId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.msg.MsgType; - -public class BasicTransportToDeviceSessionActorMsg implements TransportToDeviceSessionActorMsg { - - private final TenantId tenantId; - private final CustomerId customerId; - private final DeviceId deviceId; - private final AdaptorToSessionActorMsg msg; - - public BasicTransportToDeviceSessionActorMsg(Device device, AdaptorToSessionActorMsg msg) { - super(); - this.tenantId = device.getTenantId(); - this.customerId = device.getCustomerId(); - this.deviceId = device.getId(); - this.msg = msg; - } - - @Override - public DeviceId getDeviceId() { - return deviceId; - } - - @Override - public CustomerId getCustomerId() { - return customerId; - } - - public TenantId getTenantId() { - return tenantId; - } - - @Override - public SessionId getSessionId() { - return msg.getSessionId(); - } - - @Override - public AdaptorToSessionActorMsg getSessionMsg() { - return msg; - } - - @Override - public String toString() { - return "BasicTransportToDeviceSessionActorMsg [tenantId=" + tenantId + ", customerId=" + customerId + ", deviceId=" + deviceId + ", msg=" + msg - + "]"; - } - - @Override - public MsgType getMsgType() { - return MsgType.TRANSPORT_TO_DEVICE_SESSION_ACTOR_MSG; - } -} diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/session/FromDeviceMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/session/FromDeviceMsg.java deleted file mode 100644 index 71b605712f..0000000000 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/session/FromDeviceMsg.java +++ /dev/null @@ -1,24 +0,0 @@ -/** - * 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.msg.session; - -import java.io.Serializable; - -public interface FromDeviceMsg extends Serializable { - - SessionMsgType getMsgType(); - -} diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/session/FromDeviceRequestMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/session/FromDeviceRequestMsg.java deleted file mode 100644 index 1ebeb671a6..0000000000 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/session/FromDeviceRequestMsg.java +++ /dev/null @@ -1,25 +0,0 @@ -/** - * 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.msg.session; - -/** - * @author Andrew Shvayka - */ -public interface FromDeviceRequestMsg extends FromDeviceMsg { - - Integer getRequestId(); - -} diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionActorToAdaptorMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionActorToAdaptorMsg.java deleted file mode 100644 index fa1f6d5e8f..0000000000 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionActorToAdaptorMsg.java +++ /dev/null @@ -1,22 +0,0 @@ -/** - * 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.msg.session; - -public interface SessionActorToAdaptorMsg extends SessionMsg { - - ToDeviceMsg getMsg(); - -} 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 6607295ee3..7f7a669100 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 @@ -1,12 +1,12 @@ /** * 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 - *

+ * + * 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. diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionCtrlMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionCtrlMsg.java deleted file mode 100644 index 8082f72184..0000000000 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionCtrlMsg.java +++ /dev/null @@ -1,23 +0,0 @@ -/** - * 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.msg.session; - -import org.thingsboard.server.common.msg.TbActorMsg; -import org.thingsboard.server.common.msg.aware.SessionAwareMsg; - -public interface SessionCtrlMsg extends SessionAwareMsg, TbActorMsg { - -} diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionMsg.java deleted file mode 100644 index 6b0b0af15d..0000000000 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionMsg.java +++ /dev/null @@ -1,24 +0,0 @@ -/** - * 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.msg.session; - -import org.thingsboard.server.common.msg.aware.SessionAwareMsg; - -public interface SessionMsg extends SessionAwareMsg { - - SessionContext getSessionContext(); - -} diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionType.java b/common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionType.java deleted file mode 100644 index 831682d165..0000000000 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionType.java +++ /dev/null @@ -1,22 +0,0 @@ -/** - * 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.msg.session; - -public enum SessionType { - - SYNC, ASYNC; - -} diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/session/ToDeviceMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/session/ToDeviceMsg.java deleted file mode 100644 index 705e86420b..0000000000 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/session/ToDeviceMsg.java +++ /dev/null @@ -1,26 +0,0 @@ -/** - * 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.msg.session; - -import java.io.Serializable; - -public interface ToDeviceMsg extends Serializable { - - boolean isSuccess(); - - SessionMsgType getSessionMsgType(); - -} diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/session/TransportToDeviceSessionActorMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/session/TransportToDeviceSessionActorMsg.java deleted file mode 100644 index deaef3d3e5..0000000000 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/session/TransportToDeviceSessionActorMsg.java +++ /dev/null @@ -1,28 +0,0 @@ -/** - * 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.msg.session; - -import org.thingsboard.server.common.msg.TbActorMsg; -import org.thingsboard.server.common.msg.aware.CustomerAwareMsg; -import org.thingsboard.server.common.msg.aware.DeviceAwareMsg; -import org.thingsboard.server.common.msg.aware.SessionAwareMsg; -import org.thingsboard.server.common.msg.aware.TenantAwareMsg; - -public interface TransportToDeviceSessionActorMsg extends DeviceAwareMsg, CustomerAwareMsg, TenantAwareMsg, SessionAwareMsg, TbActorMsg { - - AdaptorToSessionActorMsg getSessionMsg(); - -} diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/session/ctrl/SessionCloseMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/session/ctrl/SessionCloseMsg.java deleted file mode 100644 index 22a13d03b5..0000000000 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/session/ctrl/SessionCloseMsg.java +++ /dev/null @@ -1,67 +0,0 @@ -/** - * 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.msg.session.ctrl; - -import org.thingsboard.server.common.msg.MsgType; -import org.thingsboard.server.common.msg.session.SessionCtrlMsg; - -public class SessionCloseMsg implements SessionCtrlMsg { - - private final SessionId sessionId; - private final boolean revoked; - private final boolean timeout; - - public static SessionCloseMsg onDisconnect(SessionId sessionId) { - return new SessionCloseMsg(sessionId, false, false); - } - - public static SessionCloseMsg onError(SessionId sessionId) { - return new SessionCloseMsg(sessionId, false, false); - } - - public static SessionCloseMsg onTimeout(SessionId sessionId) { - return new SessionCloseMsg(sessionId, false, true); - } - - public static SessionCloseMsg onCredentialsRevoked(SessionId sessionId) { - return new SessionCloseMsg(sessionId, true, false); - } - - private SessionCloseMsg(SessionId sessionId, boolean unauthorized, boolean timeout) { - super(); - this.sessionId = sessionId; - this.revoked = unauthorized; - this.timeout = timeout; - } - - @Override - public SessionId getSessionId() { - return sessionId; - } - - public boolean isCredentialsRevoked() { - return revoked; - } - - public boolean isTimeout() { - return timeout; - } - - @Override - public MsgType getMsgType() { - return MsgType.SESSION_CTRL_MSG; - } -} diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/SessionMsgProcessor.java b/common/transport/src/main/java/org/thingsboard/server/common/transport/SessionMsgProcessor.java index 80b2690b21..992492f7d6 100644 --- a/common/transport/src/main/java/org/thingsboard/server/common/transport/SessionMsgProcessor.java +++ b/common/transport/src/main/java/org/thingsboard/server/common/transport/SessionMsgProcessor.java @@ -16,7 +16,6 @@ package org.thingsboard.server.common.transport; import org.thingsboard.server.common.data.Device; -import org.thingsboard.server.common.msg.aware.SessionAwareMsg; public interface SessionMsgProcessor { diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java b/common/transport/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java index 1fcd60730c..7498e8ee2a 100644 --- a/common/transport/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java +++ b/common/transport/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java @@ -1,12 +1,12 @@ /** * 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 - *

+ * + * 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. @@ -31,12 +31,6 @@ 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.core.AttributesUpdateRequest; -import org.thingsboard.server.common.msg.core.BasicAttributesUpdateRequest; -import org.thingsboard.server.common.msg.core.BasicRequest; -import org.thingsboard.server.common.msg.core.BasicTelemetryUploadRequest; -import org.thingsboard.server.common.msg.core.TelemetryUploadRequest; -import org.thingsboard.server.common.msg.core.ToDeviceRpcRequestMsg; import org.thingsboard.server.common.msg.kv.AttributesKVMsg; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg; @@ -49,8 +43,12 @@ 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; @@ -60,18 +58,6 @@ public class JsonConverter { private static final String CAN_T_PARSE_VALUE = "Can't parse value: "; private static final String DEVICE_PROPERTY = "device"; - public static TelemetryUploadRequest convertToTelemetry(JsonElement jsonObject) throws JsonSyntaxException { - return convertToTelemetry(jsonObject, BasicRequest.DEFAULT_REQUEST_ID); - } - - public static TelemetryUploadRequest convertToTelemetry(JsonElement jsonObject, long ts) throws JsonSyntaxException { - return convertToTelemetry(jsonObject, ts, BasicRequest.DEFAULT_REQUEST_ID); - } - - public static TelemetryUploadRequest convertToTelemetry(JsonElement jsonObject, int requestId) throws JsonSyntaxException { - return convertToTelemetry(jsonObject, System.currentTimeMillis(), requestId); - } - public static PostTelemetryMsg convertToTelemetryProto(JsonElement jsonObject) throws JsonSyntaxException { long systemTs = System.currentTimeMillis(); PostTelemetryMsg.Builder builder = PostTelemetryMsg.newBuilder(); @@ -170,74 +156,11 @@ public class JsonConverter { return result; } - private static TelemetryUploadRequest convertToTelemetry(JsonElement jsonObject, long systemTs, int requestId) throws JsonSyntaxException { - BasicTelemetryUploadRequest request = new BasicTelemetryUploadRequest(requestId); - if (jsonObject.isJsonObject()) { - parseObject(request, systemTs, jsonObject); - } else if (jsonObject.isJsonArray()) { - jsonObject.getAsJsonArray().forEach(je -> { - if (je.isJsonObject()) { - parseObject(request, systemTs, je.getAsJsonObject()); - } else { - throw new JsonSyntaxException(CAN_T_PARSE_VALUE + je); - } - }); - } else { - throw new JsonSyntaxException(CAN_T_PARSE_VALUE + jsonObject); - } - return request; - } - 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 parseObject(BasicTelemetryUploadRequest request, long systemTs, JsonElement jsonObject) { - JsonObject jo = jsonObject.getAsJsonObject(); - if (jo.has("ts") && jo.has("values")) { - parseWithTs(request, jo); - } else { - parseWithoutTs(request, systemTs, jo); - } - } - - private static void parseWithoutTs(BasicTelemetryUploadRequest request, long systemTs, JsonObject jo) { - for (KvEntry entry : parseValues(jo)) { - request.add(systemTs, entry); - } - } - - public static void parseWithTs(BasicTelemetryUploadRequest request, JsonObject jo) { - long ts = jo.get("ts").getAsLong(); - JsonObject valuesObject = jo.get("values").getAsJsonObject(); - for (KvEntry entry : parseValues(valuesObject)) { - request.add(ts, entry); - } - } - - public static List parseValues(JsonObject valuesObject) { - List result = new ArrayList<>(); - for (Entry 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; - } - private static void parseNumericValue(List result, Entry valueEntry, JsonPrimitive value) { if (value.getAsString().contains(".")) { result.add(new DoubleDataEntry(valueEntry.getKey(), value.getAsDouble())); @@ -251,21 +174,6 @@ public class JsonConverter { } } - public static AttributesUpdateRequest convertToAttributes(JsonElement element) { - return convertToAttributes(element, BasicRequest.DEFAULT_REQUEST_ID); - } - - public static AttributesUpdateRequest convertToAttributes(JsonElement element, int requestId) { - if (element.isJsonObject()) { - BasicAttributesUpdateRequest request = new BasicAttributesUpdateRequest(requestId); - long ts = System.currentTimeMillis(); - request.add(parseValues(element.getAsJsonObject()).stream().map(kv -> new BaseAttributeKvEntry(kv, ts)).collect(Collectors.toList())); - return request; - } else { - throw new JsonSyntaxException(CAN_T_PARSE_VALUE + element); - } - } - public static JsonObject toJson(GetAttributeResponseMsg payload) { JsonObject result = new JsonObject(); if (payload.getClientAttributeListCount() > 0) { @@ -425,16 +333,6 @@ public class JsonConverter { }; } - public static JsonObject toJson(ToDeviceRpcRequestMsg msg, boolean includeRequestId) { - JsonObject result = new JsonObject(); - if (includeRequestId) { - result.addProperty("id", msg.getRequestId()); - } - result.addProperty("method", msg.getMethod()); - result.add("params", new JsonParser().parse(msg.getParams())); - return result; - } - public static JsonElement toJson(TransportProtos.ToServerRpcResponseMsg msg) { if (StringUtils.isEmpty(msg.getError())) { return new JsonParser().parse(msg.getPayload()); @@ -457,4 +355,76 @@ public class JsonConverter { result.add("data", JsonConverter.toJson(rpcRequest, true)); return result; } + + public static Set convertToAttributes(JsonElement element) { + Set 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 parseValues(JsonObject valuesObject) { + List result = new ArrayList<>(); + for (Entry 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> convertToTelemetry(JsonElement jsonObject, long systemTs) throws JsonSyntaxException { + Map> 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> 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> result, long systemTs, JsonObject jo) { + for (KvEntry entry : parseValues(jo)) { + result.computeIfAbsent(systemTs, tmp -> new ArrayList<>()).add(entry); + } + } + + public static void parseWithTs(Map> 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/src/main/java/org/thingsboard/server/common/transport/session/DeviceAwareSessionContext.java b/common/transport/src/main/java/org/thingsboard/server/common/transport/session/DeviceAwareSessionContext.java index 93921d347f..93c0f41ddd 100644 --- a/common/transport/src/main/java/org/thingsboard/server/common/transport/session/DeviceAwareSessionContext.java +++ b/common/transport/src/main/java/org/thingsboard/server/common/transport/session/DeviceAwareSessionContext.java @@ -29,6 +29,8 @@ import java.util.UUID; @Data public abstract class DeviceAwareSessionContext implements SessionContext { + @Getter + protected final UUID sessionId; @Getter private volatile DeviceId deviceId; @Getter 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 40e00ec009..9adc79b79e 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 @@ -84,7 +84,7 @@ public class TbCopyAttributesToEntityViewNode implements TbNode { long endTime = entityView.getEndTimeMs(); if ((endTime != 0 && endTime > now && startTime < now) || (endTime == 0 && startTime < now)) { Set attributes = - JsonConverter.convertToAttributes(new JsonParser().parse(msg.getData())).getAttributes(); + JsonConverter.convertToAttributes(new JsonParser().parse(msg.getData())); List filteredAttributes = attributes.stream() .filter(attr -> { 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 5cdb04ed5b..12dbc6dd89 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 attributes = JsonConverter.convertToAttributes(new JsonParser().parse(src)).getAttributes(); + Set 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 efdf4af3f2..4cb9999969 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> tsKvMap = telemetryUploadRequest.getData(); + Map> tsKvMap = JsonConverter.convertToTelemetry(new JsonParser().parse(src), ts); if (tsKvMap == null) { ctx.tellFailure(msg, new IllegalArgumentException("Msg body is empty: " + src)); return; diff --git a/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/JsonCoapAdaptor.java b/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/JsonCoapAdaptor.java index 3cd414289c..1c587f0fae 100644 --- a/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/JsonCoapAdaptor.java +++ b/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/JsonCoapAdaptor.java @@ -17,7 +17,6 @@ package org.thingsboard.server.transport.coap.adaptors; import java.util.*; -import com.google.gson.JsonElement; import lombok.extern.slf4j.Slf4j; import org.eclipse.californium.core.coap.CoAP.ResponseCode; import org.eclipse.californium.core.coap.Request; @@ -25,13 +24,8 @@ import org.eclipse.californium.core.coap.Response; import org.springframework.util.StringUtils; import org.thingsboard.server.common.msg.core.*; import org.thingsboard.server.common.msg.kv.AttributesKVMsg; -import org.thingsboard.server.common.msg.session.AdaptorToSessionActorMsg; -import org.thingsboard.server.common.msg.session.BasicAdaptorToSessionActorMsg; -import org.thingsboard.server.common.msg.session.FromDeviceMsg; import org.thingsboard.server.common.msg.session.SessionMsgType; -import org.thingsboard.server.common.msg.session.SessionActorToAdaptorMsg; import org.thingsboard.server.common.msg.session.SessionContext; -import org.thingsboard.server.common.msg.session.ToDeviceMsg; import org.thingsboard.server.common.msg.session.ex.ProcessingTimeoutException; import org.thingsboard.server.common.transport.adaptor.AdaptorException; import org.thingsboard.server.common.transport.adaptor.JsonConverter; diff --git a/transport/coap/src/main/java/org/thingsboard/server/transport/coap/session/CoapSessionCtx.java b/transport/coap/src/main/java/org/thingsboard/server/transport/coap/session/CoapSessionCtx.java index efabc4eac2..60d54087bf 100644 --- a/transport/coap/src/main/java/org/thingsboard/server/transport/coap/session/CoapSessionCtx.java +++ b/transport/coap/src/main/java/org/thingsboard/server/transport/coap/session/CoapSessionCtx.java @@ -20,10 +20,6 @@ import org.eclipse.californium.core.coap.CoAP.ResponseCode; import org.eclipse.californium.core.coap.Request; import org.eclipse.californium.core.coap.Response; import org.eclipse.californium.core.server.resources.CoapExchange; -import org.thingsboard.server.common.msg.session.SessionActorToAdaptorMsg; -import org.thingsboard.server.common.msg.session.SessionCtrlMsg; -import org.thingsboard.server.common.msg.session.SessionType; -import org.thingsboard.server.common.msg.session.ctrl.SessionCloseMsg; import org.thingsboard.server.common.msg.session.ex.SessionException; import org.thingsboard.server.common.transport.SessionMsgProcessor; import org.thingsboard.server.common.transport.adaptor.AdaptorException; diff --git a/transport/coap/src/test/java/org/thingsboard/server/transport/coap/CoapServerTest.java b/transport/coap/src/test/java/org/thingsboard/server/transport/coap/CoapServerTest.java index ec69cec673..84aeb848c8 100644 --- a/transport/coap/src/test/java/org/thingsboard/server/transport/coap/CoapServerTest.java +++ b/transport/coap/src/test/java/org/thingsboard/server/transport/coap/CoapServerTest.java @@ -34,26 +34,15 @@ import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.kv.AttributeKvEntry; -import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; -import org.thingsboard.server.common.data.kv.LongDataEntry; -import org.thingsboard.server.common.data.kv.StringDataEntry; import org.thingsboard.server.common.data.security.DeviceCredentialsFilter; import org.thingsboard.server.common.data.security.DeviceCredentialsType; import org.thingsboard.server.common.data.security.DeviceTokenCredentials; -import org.thingsboard.server.common.msg.aware.SessionAwareMsg; -import org.thingsboard.server.common.msg.core.BasicGetAttributesResponse; -import org.thingsboard.server.common.msg.core.BasicRequest; -import org.thingsboard.server.common.msg.core.BasicStatusCodeResponse; -import org.thingsboard.server.common.msg.kv.BasicAttributeKVMsg; import org.thingsboard.server.common.msg.session.*; import org.thingsboard.server.common.transport.SessionMsgProcessor; import org.thingsboard.server.common.transport.auth.DeviceAuthResult; import org.thingsboard.server.common.transport.auth.DeviceAuthService; import org.thingsboard.server.common.transport.quota.host.HostRequestsQuotaService; -import java.util.ArrayList; -import java.util.List; import java.util.Optional; import java.util.UUID; diff --git a/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java b/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java index add89b1e1a..bc7eeff6f7 100644 --- a/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java +++ b/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java @@ -15,34 +15,20 @@ */ package org.thingsboard.server.transport.http; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; -import com.google.gson.JsonSyntaxException; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; -import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.*; import org.springframework.web.context.request.async.DeferredResult; -import org.thingsboard.server.common.data.security.DeviceTokenCredentials; import org.thingsboard.server.common.msg.core.*; -import org.thingsboard.server.common.msg.session.AdaptorToSessionActorMsg; -import org.thingsboard.server.common.msg.session.BasicAdaptorToSessionActorMsg; -import org.thingsboard.server.common.msg.session.BasicTransportToDeviceSessionActorMsg; -import org.thingsboard.server.common.msg.session.FromDeviceMsg; import org.thingsboard.server.common.transport.SessionMsgProcessor; -import org.thingsboard.server.common.transport.adaptor.JsonConverter; import org.thingsboard.server.common.transport.auth.DeviceAuthService; -import org.thingsboard.server.common.transport.quota.QuotaService; import org.thingsboard.server.common.transport.quota.host.HostRequestsQuotaService; import org.thingsboard.server.transport.http.session.HttpSessionCtx; import javax.servlet.http.HttpServletRequest; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; /** * @author Andrew Shvayka diff --git a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportContext.java b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportContext.java index 2c8ff59f1f..4742b3b520 100644 --- a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportContext.java +++ b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportContext.java @@ -1,12 +1,12 @@ /** * 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 - *

+ * + * 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. diff --git a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java index 1f1288c6da..b570c74ec4 100644 --- a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java +++ b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java @@ -1,12 +1,12 @@ /** * 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 - *

+ * + * 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. diff --git a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java index 4bde5a35fe..8393ca9e0c 100644 --- a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java +++ b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java @@ -1,12 +1,12 @@ /** * 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 - *

+ * + * 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. diff --git a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/DeviceSessionCtx.java b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/DeviceSessionCtx.java index 6195fdd440..5547071809 100644 --- a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/DeviceSessionCtx.java +++ b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/DeviceSessionCtx.java @@ -1,12 +1,12 @@ /** * 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 - *

+ * + * 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. @@ -16,13 +16,9 @@ package org.thingsboard.server.transport.mqtt.session; import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.mqtt.*; +import io.netty.handler.codec.mqtt.MqttMessage; import lombok.Getter; import lombok.extern.slf4j.Slf4j; -import org.thingsboard.server.common.msg.session.SessionActorToAdaptorMsg; -import org.thingsboard.server.common.msg.session.SessionCtrlMsg; -import org.thingsboard.server.common.msg.session.SessionType; -import org.thingsboard.server.common.msg.session.ctrl.SessionCloseMsg; import org.thingsboard.server.common.msg.session.ex.SessionException; import org.thingsboard.server.common.transport.adaptor.AdaptorException; @@ -36,29 +32,12 @@ import java.util.concurrent.atomic.AtomicInteger; @Slf4j public class DeviceSessionCtx extends MqttDeviceAwareSessionContext { - private final UUID sessionId; @Getter private ChannelHandlerContext channel; private AtomicInteger msgIdSeq = new AtomicInteger(0); public DeviceSessionCtx(UUID sessionId, ConcurrentMap mqttQoSMap) { - super(null, null, mqttQoSMap); - this.sessionId = sessionId; - } - - @Override - public SessionType getSessionType() { - return SessionType.ASYNC; - } - - @Override - public void onMsg(SessionActorToAdaptorMsg msg) throws SessionException { -// try { -// adaptor.convertToAdaptorMsg(this, msg).ifPresent(this::pushToNetwork); -// } catch (AdaptorException e) { -// //TODO: close channel with disconnect; -// logAndWrap(e); -// } + super(sessionId, mqttQoSMap); } private void logAndWrap(AdaptorException e) throws SessionException { @@ -70,29 +49,6 @@ public class DeviceSessionCtx extends MqttDeviceAwareSessionContext { channel.writeAndFlush(msg); } - @Override - public void onMsg(SessionCtrlMsg msg) throws SessionException { - if (msg instanceof SessionCloseMsg) { - pushToNetwork(new MqttMessage(new MqttFixedHeader(MqttMessageType.DISCONNECT, false, MqttQoS.AT_MOST_ONCE, false, 0))); - channel.close(); - } - } - - @Override - public boolean isClosed() { - return false; - } - - @Override - public long getTimeout() { - return 0; - } - - @Override - public UUID getSessionId() { - return sessionId; - } - public void setChannel(ChannelHandlerContext channel) { this.channel = channel; } diff --git a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewayDeviceSessionCtx.java b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewayDeviceSessionCtx.java index 32d9e55832..8560b35acf 100644 --- a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewayDeviceSessionCtx.java +++ b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewayDeviceSessionCtx.java @@ -1,12 +1,12 @@ /** * 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 - *

+ * + * 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. @@ -31,13 +31,11 @@ import java.util.concurrent.ConcurrentMap; public class GatewayDeviceSessionCtx extends MqttDeviceAwareSessionContext implements SessionMsgListener { private final GatewaySessionCtx parent; - private final UUID sessionId; private final SessionInfoProto sessionInfo; public GatewayDeviceSessionCtx(GatewaySessionCtx parent, DeviceInfoProto deviceInfo, ConcurrentMap mqttQoSMap) { - super(mqttQoSMap); + super(UUID.randomUUID(), mqttQoSMap); this.parent = parent; - this.sessionId = UUID.randomUUID(); this.sessionInfo = SessionInfoProto.newBuilder() .setNodeId(parent.getNodeId()) .setSessionIdMSB(sessionId.getMostSignificantBits()) diff --git a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionCtx.java b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionCtx.java index e038a4d269..fd5eeb7876 100644 --- a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionCtx.java +++ b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionCtx.java @@ -1,12 +1,12 @@ /** * 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 - *

+ * + * 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. diff --git a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/MqttDeviceAwareSessionContext.java b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/MqttDeviceAwareSessionContext.java index 9e66566383..3409a0d335 100644 --- a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/MqttDeviceAwareSessionContext.java +++ b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/MqttDeviceAwareSessionContext.java @@ -22,6 +22,7 @@ import org.thingsboard.server.common.transport.auth.DeviceAuthService; import org.thingsboard.server.common.transport.session.DeviceAwareSessionContext; import java.util.Map; +import java.util.UUID; import java.util.concurrent.ConcurrentMap; /** @@ -31,8 +32,8 @@ public abstract class MqttDeviceAwareSessionContext extends DeviceAwareSessionCo private final ConcurrentMap mqttQoSMap; - public MqttDeviceAwareSessionContext(ConcurrentMap mqttQoSMap) { - super(); + public MqttDeviceAwareSessionContext(UUID sessionId, ConcurrentMap mqttQoSMap) { + super(sessionId); this.mqttQoSMap = mqttQoSMap; } diff --git a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/MqttSessionId.java b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/MqttSessionId.java deleted file mode 100644 index 9256e5d8c0..0000000000 --- a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/MqttSessionId.java +++ /dev/null @@ -1,60 +0,0 @@ -/** - * 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 java.util.concurrent.atomic.AtomicLong; - -/** - * @author Andrew Shvayka - */ -public class MqttSessionId implements SessionId { - - private static final AtomicLong idSeq = new AtomicLong(); - - private final long id; - - public MqttSessionId() { - this.id = idSeq.incrementAndGet(); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - MqttSessionId that = (MqttSessionId) o; - - return id == that.id; - - } - - @Override - public String toString() { - return "MqttSessionId{" + - "id=" + id + - '}'; - } - - @Override - public int hashCode() { - return (int) (id ^ (id >>> 32)); - } - - @Override - public String toUidStr() { - return "mqtt" + id; - } -} diff --git a/transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/MqttTransportService.java b/transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/MqttTransportService.java index bfc69efdc4..4ce6b081aa 100644 --- a/transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/MqttTransportService.java +++ b/transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/MqttTransportService.java @@ -1,12 +1,12 @@ /** * 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 - *

+ * + * 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. @@ -231,7 +231,7 @@ public class MqttTransportService implements TransportService { @Override public void process(GetOrCreateDeviceFromGatewayRequestMsg msg, TransportServiceCallback callback) { - AsyncCallbackTemplate.withCallback(transportApiTemplate.post(msg.getTenantIdMSB() + msg.getTenantIdLSB() + msg.getDeviceName(), + AsyncCallbackTemplate.withCallback(transportApiTemplate.post(msg.getDeviceName(), TransportApiRequestMsg.newBuilder().setGetOrCreateDeviceRequestMsg(msg).build()), response -> callback.onSuccess(response.getGetOrCreateDeviceResponseMsg()), callback::onError, transportCallbackExecutor); } diff --git a/transport/pom.xml b/transport/pom.xml index 2401d3f845..671e3c1d63 100644 --- a/transport/pom.xml +++ b/transport/pom.xml @@ -35,8 +35,8 @@ - http - coap + + mqtt-common mqtt-transport From c6800dbd032b3666ae6eb2ab5c061dc6e5a2e39c Mon Sep 17 00:00:00 2001 From: Andrew Shvayka Date: Fri, 12 Oct 2018 13:35:26 +0300 Subject: [PATCH 14/26] BugFix for Gateway API and concurrent device creation --- application/pom.xml | 16 +- .../device/DeviceActorMessageProcessor.java | 4 + .../transport/RemoteTransportApiService.java | 28 +- .../transport/http/DeviceApiController.java | 32 +-- .../http/session/HttpSessionCtx.java | 246 +++++++++--------- .../transport/http/session/HttpSessionId.java | 35 --- .../transport/mqtt/MqttTransportHandler.java | 24 +- .../mqtt/session/GatewayDeviceSessionCtx.java | 4 +- ...ionCtx.java => GatewaySessionHandler.java} | 15 +- transport/pom.xml | 2 +- 10 files changed, 197 insertions(+), 209 deletions(-) delete mode 100644 transport/http/src/main/java/org/thingsboard/server/transport/http/session/HttpSessionId.java rename transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/{GatewaySessionCtx.java => GatewaySessionHandler.java} (96%) diff --git a/application/pom.xml b/application/pom.xml index bff96ec6fd..dd1bade336 100644 --- a/application/pom.xml +++ b/application/pom.xml @@ -60,14 +60,14 @@ org.thingsboard.common transport - - org.thingsboard.transport - http - - - org.thingsboard.transport - coap - + + + + + + + + org.thingsboard.transport mqtt-common 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 64480863f5..05902a6146 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 @@ -455,6 +455,10 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso 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()) { UUID sessionIdToRemove = sessions.keySet().stream().findFirst().orElse(null); 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 index 945317f768..66a8226a98 100644 --- a/application/src/main/java/org/thingsboard/server/service/transport/RemoteTransportApiService.java +++ b/application/src/main/java/org/thingsboard/server/service/transport/RemoteTransportApiService.java @@ -51,6 +51,7 @@ import javax.annotation.PostConstruct; 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. @@ -97,6 +98,8 @@ public class RemoteTransportApiService implements TransportApiService { private TbKafkaResponseTemplate transportApiTemplate; + private ReentrantLock deviceCreationLock = new ReentrantLock(); + @PostConstruct public void init() { this.transportCallbackExecutor = Executors.newCachedThreadPool(); @@ -156,23 +159,26 @@ public class RemoteTransportApiService implements TransportApiService { DeviceId gatewayId = new DeviceId(new UUID(requestMsg.getGatewayIdMSB(), requestMsg.getGatewayIdLSB())); ListenableFuture gatewayFuture = deviceService.findDeviceByIdAsync(gatewayId); return Futures.transform(gatewayFuture, gateway -> { - Device device = deviceService.findDeviceByTenantIdAndName(gateway.getTenantId(), gateway.getName()); - 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); - } + 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(); } }, transportCallbackExecutor); } diff --git a/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java b/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java index bc7eeff6f7..3c7c521b55 100644 --- a/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java +++ b/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java @@ -122,7 +122,8 @@ public class DeviceApiController { @RequestParam(value = "timeout", required = false, defaultValue = "0") long timeout, HttpServletRequest request) { - return subscribe(deviceToken, timeout, new RpcSubscribeMsg(), request); +// return subscribe(deviceToken, timeout, new RpcSubscribeMsg(), request); + return null; } @RequestMapping(value = "/{deviceToken}/rpc/{requestId}", method = RequestMethod.POST) @@ -174,15 +175,15 @@ public class DeviceApiController { public DeferredResult subscribeToAttributes(@PathVariable("deviceToken") String deviceToken, @RequestParam(value = "timeout", required = false, defaultValue = "0") long timeout, HttpServletRequest httpRequest) { - - return subscribe(deviceToken, timeout, new AttributesSubscribeMsg(), httpRequest); + return null; +// return subscribe(deviceToken, timeout, new AttributesSubscribeMsg(), httpRequest); } - private DeferredResult subscribe(String deviceToken, long timeout, FromDeviceMsg msg, HttpServletRequest httpRequest) { - DeferredResult responseWriter = new DeferredResult(); - if (quotaExceeded(httpRequest, responseWriter)) { - return responseWriter; - } +// private DeferredResult subscribe(String deviceToken, long timeout, FromDeviceMsg msg, HttpServletRequest httpRequest) { +// DeferredResult responseWriter = new DeferredResult(); +// if (quotaExceeded(httpRequest, responseWriter)) { +// return responseWriter; +// } // HttpSessionCtx ctx = getHttpSessionCtx(responseWriter, timeout); // if (ctx.login(new DeviceTokenCredentials(deviceToken))) { // try { @@ -193,21 +194,22 @@ public class DeviceApiController { // } else { // responseWriter.setResult(new ResponseEntity<>(HttpStatus.UNAUTHORIZED)); // } - return responseWriter; - } +// return responseWriter; +// } private HttpSessionCtx getHttpSessionCtx(DeferredResult responseWriter) { return getHttpSessionCtx(responseWriter, defaultTimeout); } private HttpSessionCtx getHttpSessionCtx(DeferredResult responseWriter, long timeout) { - return new HttpSessionCtx(processor, authService, responseWriter, timeout != 0 ? timeout : defaultTimeout); + return null; +// return new HttpSessionCtx(processor, authService, responseWriter, timeout != 0 ? timeout : defaultTimeout); } - private void process(HttpSessionCtx ctx, FromDeviceMsg request) { - AdaptorToSessionActorMsg msg = new BasicAdaptorToSessionActorMsg(ctx, request); -// processor.process(new BasicTransportToDeviceSessionActorMsg(ctx.getDevice(), msg)); - } +// private void process(HttpSessionCtx ctx, FromDeviceMsg request) { +// AdaptorToSessionActorMsg msg = new BasicAdaptorToSessionActorMsg(ctx, request); +//// processor.process(new BasicTransportToDeviceSessionActorMsg(ctx.getDevice(), msg)); +// } private boolean quotaExceeded(HttpServletRequest request, DeferredResult responseWriter) { if (quotaService.isQuotaExceeded(request.getRemoteAddr())) { diff --git a/transport/http/src/main/java/org/thingsboard/server/transport/http/session/HttpSessionCtx.java b/transport/http/src/main/java/org/thingsboard/server/transport/http/session/HttpSessionCtx.java index 62999cc64b..0e087d4966 100644 --- a/transport/http/src/main/java/org/thingsboard/server/transport/http/session/HttpSessionCtx.java +++ b/transport/http/src/main/java/org/thingsboard/server/transport/http/session/HttpSessionCtx.java @@ -29,6 +29,7 @@ import org.thingsboard.server.common.transport.auth.DeviceAuthService; import org.thingsboard.server.common.transport.session.DeviceAwareSessionContext; import java.util.Optional; +import java.util.UUID; import java.util.function.Consumer; /** @@ -37,127 +38,136 @@ import java.util.function.Consumer; @Slf4j public class HttpSessionCtx extends DeviceAwareSessionContext { - private final SessionId sessionId; - private final long timeout; - private final DeferredResult responseWriter; - - public HttpSessionCtx(SessionMsgProcessor processor, DeviceAuthService authService, DeferredResult responseWriter, long timeout) { - super(); - this.sessionId = new HttpSessionId(); - this.responseWriter = responseWriter; - this.timeout = timeout; - } - - @Override - public SessionType getSessionType() { - return SessionType.SYNC; + public HttpSessionCtx(UUID sessionId) { + super(sessionId); } @Override - public void onMsg(SessionActorToAdaptorMsg source) throws SessionException { - ToDeviceMsg msg = source.getMsg(); - switch (msg.getSessionMsgType()) { - case GET_ATTRIBUTES_RESPONSE: - reply((GetAttributesResponse) msg); - return; - case STATUS_CODE_RESPONSE: - reply((StatusCodeResponse) msg); - return; - case ATTRIBUTES_UPDATE_NOTIFICATION: - reply((AttributesUpdateNotification) msg); - return; - case TO_DEVICE_RPC_REQUEST: - reply((ToDeviceRpcRequestMsg) msg); - return; - case TO_SERVER_RPC_RESPONSE: - reply((ToServerRpcResponseMsg) msg); - return; - case RULE_ENGINE_ERROR: - reply((RuleEngineErrorMsg) msg); - return; - default: - break; - } - } - - private void reply(RuleEngineErrorMsg msg) { - HttpStatus status = HttpStatus.INTERNAL_SERVER_ERROR; - switch (msg.getError()) { - case QUEUE_PUT_TIMEOUT: - status = HttpStatus.REQUEST_TIMEOUT; - break; - default: - if (msg.getInSessionMsgType() == SessionMsgType.TO_SERVER_RPC_REQUEST) { - status = HttpStatus.BAD_REQUEST; - } - break; - } - responseWriter.setResult(new ResponseEntity<>(JsonConverter.toErrorJson(msg.getErrorMsg()).toString(), status)); - } - - private void reply(ResponseMsg msg, Consumer f) { - Optional msgError = msg.getError(); - if (!msgError.isPresent()) { - Optional msgData = msg.getData(); - if (msgData.isPresent()) { - f.accept(msgData.get()); - } - } else { - Exception e = msgError.get(); - responseWriter.setResult(new ResponseEntity<>(e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR)); - } + public int nextMsgId() { + return 0; } - private void reply(ToDeviceRpcRequestMsg msg) { - responseWriter.setResult(new ResponseEntity<>(JsonConverter.toJson(msg, true).toString(), HttpStatus.OK)); - } - - private void reply(ToServerRpcResponseMsg msg) { -// responseWriter.setResult(new ResponseEntity<>(JsonConverter.toJson(msg).toString(), HttpStatus.OK)); - } - - private void reply(AttributesUpdateNotification msg) { - responseWriter.setResult(new ResponseEntity<>(JsonConverter.toJson(msg.getData(), false).toString(), HttpStatus.OK)); - } - - private void reply(GetAttributesResponse msg) { - reply(msg, payload -> { - if (payload.getClientAttributes().isEmpty() && payload.getSharedAttributes().isEmpty()) { - responseWriter.setResult(new ResponseEntity<>(HttpStatus.NOT_FOUND)); - } else { - JsonObject result = JsonConverter.toJson(payload, false); - responseWriter.setResult(new ResponseEntity<>(result.toString(), HttpStatus.OK)); - } - }); - } - - private void reply(StatusCodeResponse msg) { - reply(msg, payload -> { - if (payload == 0) { - responseWriter.setResult(new ResponseEntity<>(HttpStatus.OK)); - } else { - responseWriter.setResult(new ResponseEntity<>(HttpStatus.valueOf(payload))); - } - }); - } - - @Override - public void onMsg(SessionCtrlMsg msg) throws SessionException { - //Do nothing - } - - @Override - public boolean isClosed() { - return false; - } - - @Override - public long getTimeout() { - return timeout; - } - - @Override - public SessionId getSessionId() { - return sessionId; - } + // private final SessionId sessionId; +// private final long timeout; +// private final DeferredResult responseWriter; +// +// public HttpSessionCtx(SessionMsgProcessor processor, DeviceAuthService authService, DeferredResult responseWriter, long timeout) { +// super(); +// this.sessionId = new HttpSessionId(); +// this.responseWriter = responseWriter; +// this.timeout = timeout; +// } +// +// @Override +// public SessionType getSessionType() { +// return SessionType.SYNC; +// } +// +// @Override +// public void onMsg(SessionActorToAdaptorMsg source) throws SessionException { +// ToDeviceMsg msg = source.getMsg(); +// switch (msg.getSessionMsgType()) { +// case GET_ATTRIBUTES_RESPONSE: +// reply((GetAttributesResponse) msg); +// return; +// case STATUS_CODE_RESPONSE: +// reply((StatusCodeResponse) msg); +// return; +// case ATTRIBUTES_UPDATE_NOTIFICATION: +// reply((AttributesUpdateNotification) msg); +// return; +// case TO_DEVICE_RPC_REQUEST: +// reply((ToDeviceRpcRequestMsg) msg); +// return; +// case TO_SERVER_RPC_RESPONSE: +// reply((ToServerRpcResponseMsg) msg); +// return; +// case RULE_ENGINE_ERROR: +// reply((RuleEngineErrorMsg) msg); +// return; +// default: +// break; +// } +// } +// +// private void reply(RuleEngineErrorMsg msg) { +// HttpStatus status = HttpStatus.INTERNAL_SERVER_ERROR; +// switch (msg.getError()) { +// case QUEUE_PUT_TIMEOUT: +// status = HttpStatus.REQUEST_TIMEOUT; +// break; +// default: +// if (msg.getInSessionMsgType() == SessionMsgType.TO_SERVER_RPC_REQUEST) { +// status = HttpStatus.BAD_REQUEST; +// } +// break; +// } +// responseWriter.setResult(new ResponseEntity<>(JsonConverter.toErrorJson(msg.getErrorMsg()).toString(), status)); +// } +// +// private void reply(ResponseMsg msg, Consumer f) { +// Optional msgError = msg.getError(); +// if (!msgError.isPresent()) { +// Optional msgData = msg.getData(); +// if (msgData.isPresent()) { +// f.accept(msgData.get()); +// } +// } else { +// Exception e = msgError.get(); +// responseWriter.setResult(new ResponseEntity<>(e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR)); +// } +// } +// +// private void reply(ToDeviceRpcRequestMsg msg) { +// responseWriter.setResult(new ResponseEntity<>(JsonConverter.toJson(msg, true).toString(), HttpStatus.OK)); +// } +// +// private void reply(ToServerRpcResponseMsg msg) { +//// responseWriter.setResult(new ResponseEntity<>(JsonConverter.toJson(msg).toString(), HttpStatus.OK)); +// } +// +// private void reply(AttributesUpdateNotification msg) { +// responseWriter.setResult(new ResponseEntity<>(JsonConverter.toJson(msg.getData(), false).toString(), HttpStatus.OK)); +// } +// +// private void reply(GetAttributesResponse msg) { +// reply(msg, payload -> { +// if (payload.getClientAttributes().isEmpty() && payload.getSharedAttributes().isEmpty()) { +// responseWriter.setResult(new ResponseEntity<>(HttpStatus.NOT_FOUND)); +// } else { +// JsonObject result = JsonConverter.toJson(payload, false); +// responseWriter.setResult(new ResponseEntity<>(result.toString(), HttpStatus.OK)); +// } +// }); +// } +// +// private void reply(StatusCodeResponse msg) { +// reply(msg, payload -> { +// if (payload == 0) { +// responseWriter.setResult(new ResponseEntity<>(HttpStatus.OK)); +// } else { +// responseWriter.setResult(new ResponseEntity<>(HttpStatus.valueOf(payload))); +// } +// }); +// } +// +// @Override +// public void onMsg(SessionCtrlMsg msg) throws SessionException { +// //Do nothing +// } +// +// @Override +// public boolean isClosed() { +// return false; +// } +// +// @Override +// public long getTimeout() { +// return timeout; +// } +// +// @Override +// public SessionId getSessionId() { +// return sessionId; +// } } diff --git a/transport/http/src/main/java/org/thingsboard/server/transport/http/session/HttpSessionId.java b/transport/http/src/main/java/org/thingsboard/server/transport/http/session/HttpSessionId.java deleted file mode 100644 index a5426ca051..0000000000 --- a/transport/http/src/main/java/org/thingsboard/server/transport/http/session/HttpSessionId.java +++ /dev/null @@ -1,35 +0,0 @@ -/** - * 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.session; - -import java.util.UUID; - -/** - * @author Andrew Shvayka - */ -public class HttpSessionId implements SessionId { - - private final UUID id; - - public HttpSessionId() { - this.id = UUID.randomUUID(); - } - - @Override - public String toUidStr() { - return id.toString(); - } -} diff --git a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java index b570c74ec4..b1b176d032 100644 --- a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java +++ b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java @@ -54,7 +54,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceTokenR import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceX509CertRequestMsg; import org.thingsboard.server.transport.mqtt.adaptors.MqttTransportAdaptor; import org.thingsboard.server.transport.mqtt.session.DeviceSessionCtx; -import org.thingsboard.server.transport.mqtt.session.GatewaySessionCtx; +import org.thingsboard.server.transport.mqtt.session.GatewaySessionHandler; import org.thingsboard.server.transport.mqtt.util.SslUtil; import javax.net.ssl.SSLPeerUnverifiedException; @@ -98,7 +98,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement private volatile SessionInfoProto sessionInfo; private volatile InetSocketAddress address; private volatile DeviceSessionCtx deviceSessionCtx; - private volatile GatewaySessionCtx gatewaySessionCtx; + private volatile GatewaySessionHandler gatewaySessionHandler; MqttTransportHandler(MqttTransportContext context) { this.sessionId = UUID.randomUUID(); @@ -175,7 +175,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement log.trace("[{}] Processing publish msg [{}][{}]!", sessionId, topicName, msgId); if (topicName.startsWith(MqttTopics.BASE_GATEWAY_API_TOPIC)) { - if (gatewaySessionCtx != null) { + if (gatewaySessionHandler != null) { handleGatewayPublishMsg(topicName, msgId, mqttMsg); } } else { @@ -187,22 +187,22 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement try { switch (topicName) { case MqttTopics.GATEWAY_TELEMETRY_TOPIC: - gatewaySessionCtx.onDeviceTelemetry(mqttMsg); + gatewaySessionHandler.onDeviceTelemetry(mqttMsg); break; case MqttTopics.GATEWAY_ATTRIBUTES_TOPIC: - gatewaySessionCtx.onDeviceAttributes(mqttMsg); + gatewaySessionHandler.onDeviceAttributes(mqttMsg); break; case MqttTopics.GATEWAY_ATTRIBUTES_REQUEST_TOPIC: - gatewaySessionCtx.onDeviceAttributesRequest(mqttMsg); + gatewaySessionHandler.onDeviceAttributesRequest(mqttMsg); break; case MqttTopics.GATEWAY_RPC_TOPIC: - gatewaySessionCtx.onDeviceRpcResponse(mqttMsg); + gatewaySessionHandler.onDeviceRpcResponse(mqttMsg); break; case MqttTopics.GATEWAY_CONNECT_TOPIC: - gatewaySessionCtx.onDeviceConnect(mqttMsg); + gatewaySessionHandler.onDeviceConnect(mqttMsg); break; case MqttTopics.GATEWAY_DISCONNECT_TOPIC: - gatewaySessionCtx.onDeviceDisconnect(mqttMsg); + gatewaySessionHandler.onDeviceDisconnect(mqttMsg); break; } } catch (RuntimeException | AdaptorException e) { @@ -405,8 +405,8 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement if (deviceSessionCtx.isConnected()) { transportService.process(sessionInfo, getSessionEventMsg(SessionEvent.CLOSED), null); transportService.deregisterSession(sessionInfo); - if (gatewaySessionCtx != null) { - gatewaySessionCtx.onGatewayDisconnect(); + if (gatewaySessionHandler != null) { + gatewaySessionHandler.onGatewayDisconnect(); } } } @@ -467,7 +467,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement if (infoNode != null) { JsonNode gatewayNode = infoNode.get("gateway"); if (gatewayNode != null && gatewayNode.asBoolean()) { - gatewaySessionCtx = new GatewaySessionCtx(context, deviceSessionCtx, sessionId); + gatewaySessionHandler = new GatewaySessionHandler(context, deviceSessionCtx, sessionId); } } } catch (IOException e) { diff --git a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewayDeviceSessionCtx.java b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewayDeviceSessionCtx.java index 8560b35acf..1e2c2ca71d 100644 --- a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewayDeviceSessionCtx.java +++ b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewayDeviceSessionCtx.java @@ -30,10 +30,10 @@ import java.util.concurrent.ConcurrentMap; @Slf4j public class GatewayDeviceSessionCtx extends MqttDeviceAwareSessionContext implements SessionMsgListener { - private final GatewaySessionCtx parent; + private final GatewaySessionHandler parent; private final SessionInfoProto sessionInfo; - public GatewayDeviceSessionCtx(GatewaySessionCtx parent, DeviceInfoProto deviceInfo, ConcurrentMap mqttQoSMap) { + public GatewayDeviceSessionCtx(GatewaySessionHandler parent, DeviceInfoProto deviceInfo, ConcurrentMap mqttQoSMap) { super(UUID.randomUUID(), mqttQoSMap); this.parent = parent; this.sessionInfo = SessionInfoProto.newBuilder() diff --git a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionCtx.java b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionHandler.java similarity index 96% rename from transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionCtx.java rename to transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionHandler.java index fd5eeb7876..b1504b1246 100644 --- a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionCtx.java +++ b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionHandler.java @@ -52,13 +52,12 @@ import java.util.Set; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.atomic.AtomicInteger; /** * Created by ashvayka on 19.01.17. */ @Slf4j -public class GatewaySessionCtx { +public class GatewaySessionHandler { private static final String DEFAULT_DEVICE_TYPE = "default"; private static final String CAN_T_PARSE_VALUE = "Can't parse value: "; @@ -73,7 +72,7 @@ public class GatewaySessionCtx { private final ChannelHandlerContext channel; private final DeviceSessionCtx deviceSessionCtx; - public GatewaySessionCtx(MqttTransportContext context, DeviceSessionCtx deviceSessionCtx, UUID sessionId) { + public GatewaySessionHandler(MqttTransportContext context, DeviceSessionCtx deviceSessionCtx, UUID sessionId) { this.context = context; this.transportService = context.getTransportService(); this.deviceSessionCtx = deviceSessionCtx; @@ -114,10 +113,12 @@ public class GatewaySessionCtx { new TransportServiceCallback() { @Override public void onSuccess(GetOrCreateDeviceFromGatewayResponseMsg msg) { - GatewayDeviceSessionCtx deviceSessionCtx = new GatewayDeviceSessionCtx(GatewaySessionCtx.this, msg.getDeviceInfo(), mqttQoSMap); + 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.registerSession(deviceSessionInfo, deviceSessionCtx); } future.set(devices.get(deviceName)); @@ -203,7 +204,7 @@ public class GatewaySessionCtx { @Override public void onFailure(Throwable t) { - log.debug("[{}] Failed to process device teleemtry command: {}", sessionId, deviceName, t); + log.debug("[{}] Failed to process device attributes command: {}", sessionId, deviceName, t); } }, context.getExecutor()); } @@ -264,8 +265,8 @@ public class GatewaySessionCtx { } else { result.addAllSharedAttributeNames(keys); } - int msgId = msg.variableHeader().packetId(); TransportProtos.GetAttributeRequestMsg requestMsg = result.build(); + int msgId = msg.variableHeader().packetId(); Futures.addCallback(checkDeviceConnected(deviceName), new FutureCallback() { @Override @@ -275,7 +276,7 @@ public class GatewaySessionCtx { @Override public void onFailure(Throwable t) { - log.debug("[{}] Failed to process device teleemtry command: {}", sessionId, deviceName, t); + log.debug("[{}] Failed to process device attributes request command: {}", sessionId, deviceName, t); } }, context.getExecutor()); ack(msg); diff --git a/transport/pom.xml b/transport/pom.xml index 671e3c1d63..79159885d2 100644 --- a/transport/pom.xml +++ b/transport/pom.xml @@ -35,7 +35,7 @@ - + http mqtt-common mqtt-transport From 2590f04ac0fd9882ecbefa98111ce57dd9264ab1 Mon Sep 17 00:00:00 2001 From: Andrew Shvayka Date: Fri, 12 Oct 2018 15:41:44 +0300 Subject: [PATCH 15/26] Local Transport Service implementation --- .../transport/LocalTransportApiService.java | 173 ++++++++++++ .../transport/LocalTransportService.java | 249 ++++++++++++++++++ .../RemoteRuleEngineTransportService.java | 5 +- .../transport/RemoteTransportApiService.java | 121 ++------- .../src/main/resources/thingsboard.yml | 2 +- .../server/kafka/TbKafkaHandler.java | 2 +- .../transport/mqtt/MqttTransportContext.java | 1 - .../transport/mqtt/MqttTransportHandler.java | 2 +- .../mqtt/session/GatewaySessionHandler.java | 2 + .../src/main/resources/logback.xml | 34 +++ 10 files changed, 479 insertions(+), 112 deletions(-) create mode 100644 application/src/main/java/org/thingsboard/server/service/transport/LocalTransportApiService.java create mode 100644 application/src/main/java/org/thingsboard/server/service/transport/LocalTransportService.java create mode 100644 transport/mqtt-transport/src/main/resources/logback.xml 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 0000000000..5c39f8af4d --- /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 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 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 handle(GetOrCreateDeviceFromGatewayRequestMsg requestMsg) { + DeviceId gatewayId = new DeviceId(new UUID(requestMsg.getGatewayIdMSB(), requestMsg.getGatewayIdLSB())); + ListenableFuture 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 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 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 0000000000..ba5cc074d4 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/transport/LocalTransportService.java @@ -0,0 +1,249 @@ +/** + * 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.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.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 implements TransportService, RuleEngineTransportService { + + private ConcurrentMap sessions = new ConcurrentHashMap<>(); + + private ExecutorService transportCallbackExecutor; + + @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() { + this.transportCallbackExecutor = new ThreadPoolExecutor(0, 100, 60L, TimeUnit.SECONDS, new SynchronousQueue<>()); + } + + @PreDestroy + public void destroy() { + if (transportCallbackExecutor != null) { + transportCallbackExecutor.shutdownNow(); + } + } + + @Override + public void process(ValidateDeviceTokenRequestMsg msg, TransportServiceCallback 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 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 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 callback) { + forwardToDeviceActor(TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setSessionEvent(msg).build(), callback); + } + + @Override + public void process(SessionInfoProto sessionInfo, PostTelemetryMsg msg, TransportServiceCallback callback) { + forwardToDeviceActor(TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setPostTelemetry(msg).build(), callback); + } + + @Override + public void process(SessionInfoProto sessionInfo, PostAttributeMsg msg, TransportServiceCallback callback) { + forwardToDeviceActor(TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setPostAttributes(msg).build(), callback); + } + + @Override + public void process(SessionInfoProto sessionInfo, GetAttributeRequestMsg msg, TransportServiceCallback callback) { + forwardToDeviceActor(TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setGetAttributes(msg).build(), callback); + } + + @Override + public void process(SessionInfoProto sessionInfo, SubscribeToAttributeUpdatesMsg msg, TransportServiceCallback callback) { + forwardToDeviceActor(TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setSubscribeToAttributes(msg).build(), callback); + } + + @Override + public void process(SessionInfoProto sessionInfo, SubscribeToRPCMsg msg, TransportServiceCallback callback) { + forwardToDeviceActor(TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setSubscribeToRPC(msg).build(), callback); + } + + @Override + public void process(SessionInfoProto sessionInfo, ToDeviceRpcResponseMsg msg, TransportServiceCallback callback) { + forwardToDeviceActor(TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setToDeviceRPCCallResponse(msg).build(), callback); + } + + @Override + public void process(SessionInfoProto sessionInfo, ToServerRpcRequestMsg msg, TransportServiceCallback callback) { + forwardToDeviceActor(TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setToServerRPCCallRequest(msg).build(), callback); + } + + @Override + public void registerSession(SessionInfoProto sessionInfo, SessionMsgListener listener) { + sessions.putIfAbsent(toId(sessionInfo), listener); + //TODO: monitor sessions periodically: PING REQ/RESP, etc. + } + + @Override + public void deregisterSession(SessionInfoProto sessionInfo) { + sessions.remove(toId(sessionInfo)); + } + + private UUID toId(SessionInfoProto sessionInfo) { + return new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB()); + } + + @Override + public void process(String nodeId, DeviceActorToTransportMsg msg) { + process(nodeId, msg, null, null); + } + + @Override + public void process(String nodeId, DeviceActorToTransportMsg msg, Runnable onSuccess, Consumer onFailure) { + UUID sessionId = new UUID(msg.getSessionIdMSB(), msg.getSessionIdLSB()); + SessionMsgListener listener = sessions.get(sessionId); + if (listener != null) { + transportCallbackExecutor.submit(() -> { + if (msg.hasGetAttributesResponse()) { + listener.onGetAttributesResponse(msg.getGetAttributesResponse()); + } + if (msg.hasAttributeUpdateNotification()) { + listener.onAttributeUpdate(msg.getAttributeUpdateNotification()); + } + if (msg.hasSessionCloseNotification()) { + listener.onRemoteSessionCloseCommand(msg.getSessionCloseNotification()); + } + if (msg.hasToDeviceRequest()) { + listener.onToDeviceRpcRequest(msg.getToDeviceRequest()); + } + if (msg.hasToServerResponse()) { + listener.onToServerRpcResponse(msg.getToServerResponse()); + } + }); + } else { + //TODO: should we notify the device actor about missed session? + log.debug("[{}] Missing session.", sessionId); + } + if (onSuccess != null) { + onSuccess.run(); + } + } + + private void forwardToDeviceActor(TransportToDeviceActorMsg toDeviceActorMsg, TransportServiceCallback callback) { + TransportToDeviceActorMsgWrapper wrapper = new TransportToDeviceActorMsgWrapper(toDeviceActorMsg); + Optional 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 Consumer getThrowableConsumer(TransportServiceCallback 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 index aa36b9b2cb..a0791adddf 100644 --- a/application/src/main/java/org/thingsboard/server/service/transport/RemoteRuleEngineTransportService.java +++ b/application/src/main/java/org/thingsboard/server/service/transport/RemoteRuleEngineTransportService.java @@ -55,7 +55,7 @@ import java.util.function.Consumer; */ @Slf4j @Service -@ConditionalOnProperty(prefix = "transport.remote", value = "enabled", havingValue = "true") +@ConditionalOnProperty(prefix = "transport", value = "type", havingValue = "remote") public class RemoteRuleEngineTransportService implements RuleEngineTransportService { private static final ObjectMapper mapper = new ObjectMapper(); @@ -78,9 +78,6 @@ public class RemoteRuleEngineTransportService implements RuleEngineTransportServ @Autowired private ActorSystemContext actorContext; - @Autowired - private ActorService actorService; - //TODO: completely replace this routing with the Kafka routing by partition ids. @Autowired private ClusterRoutingService routingService; 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 index 66a8226a98..ebd35a414e 100644 --- a/application/src/main/java/org/thingsboard/server/service/transport/RemoteTransportApiService.java +++ b/application/src/main/java/org/thingsboard/server/service/transport/RemoteTransportApiService.java @@ -23,6 +23,7 @@ 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.springframework.stereotype.Service; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.id.DeviceId; @@ -48,20 +49,22 @@ import org.thingsboard.server.service.cluster.discovery.DiscoveryService; 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.SynchronousQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantLock; /** * Created by ashvayka on 05.10.18. */ @Slf4j -@Service -@ConditionalOnProperty(prefix = "transport.remote", value = "enabled", havingValue = "true") -public class RemoteTransportApiService implements TransportApiService { - - private static final ObjectMapper mapper = new ObjectMapper(); +@Component +@ConditionalOnProperty(prefix = "transport", value = "type", havingValue = "remote") +public class RemoteTransportApiService { @Value("${transport.remote.transport_api.requests_topic}") private String transportApiRequestsTopic; @@ -83,26 +86,15 @@ public class RemoteTransportApiService implements TransportApiService { private DiscoveryService discoveryService; @Autowired - private DeviceService deviceService; - - @Autowired - private RelationService relationService; - - @Autowired - private DeviceCredentialsService deviceCredentialsService; - - @Autowired - private DeviceStateService deviceStateService; + private TransportApiService transportApiService; private ExecutorService transportCallbackExecutor; private TbKafkaResponseTemplate transportApiTemplate; - private ReentrantLock deviceCreationLock = new ReentrantLock(); - @PostConstruct public void init() { - this.transportCallbackExecutor = Executors.newCachedThreadPool(); + this.transportCallbackExecutor = new ThreadPoolExecutor(0, 100, 60L, TimeUnit.SECONDS, new SynchronousQueue<>()); TBKafkaProducerTemplate.TBKafkaProducerTemplateBuilder responseBuilder = TBKafkaProducerTemplate.builder(); responseBuilder.settings(kafkaSettings); @@ -126,98 +118,19 @@ public class RemoteTransportApiService implements TransportApiService { builder.requestTimeout(requestTimeout); builder.pollInterval(responsePollDuration); builder.executor(transportCallbackExecutor); - builder.handler(this); + builder.handler(transportApiService); transportApiTemplate = builder.build(); transportApiTemplate.init(); } - @Override - public ListenableFuture handle(TransportApiRequestMsg transportApiRequestMsg) throws Exception { - 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()); + @PreDestroy + public void destroy() { + if (transportApiTemplate != null) { + transportApiTemplate.stop(); } - return getEmptyTransportApiResponseFuture(); - } - - private ListenableFuture 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(); + if (transportCallbackExecutor != null) { + transportCallbackExecutor.shutdownNow(); } } - private ListenableFuture handle(GetOrCreateDeviceFromGatewayRequestMsg requestMsg) { - DeviceId gatewayId = new DeviceId(new UUID(requestMsg.getGatewayIdMSB(), requestMsg.getGatewayIdLSB())); - ListenableFuture 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(); - } - }, transportCallbackExecutor); - } - - - private ListenableFuture 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 getEmptyTransportApiResponseFuture() { - return Futures.immediateFuture(getEmptyTransportApiResponse()); - } - - private TransportApiResponseMsg getEmptyTransportApiResponse() { - return TransportApiResponseMsg.newBuilder() - .setValidateTokenResponseMsg(ValidateDeviceCredentialsResponseMsg.getDefaultInstance()).build(); - } } diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 923976c302..529ab25040 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -452,8 +452,8 @@ js: max_errors: "${REMOTE_JS_SANDBOX_MAX_ERRORS:3}" transport: + type: "${TRANSPORT_TYPE:remote}" # local or remote remote: - enabled: "${REMOTE_TRANSPORT_ENABLED:true}" transport_api: requests_topic: "${TB_TRANSPORT_API_REQUEST_TOPIC:tb.transport.api.requests}" responses_topic: "${TB_TRANSPORT_API_RESPONSE_TOPIC:tb.transport.api.responses}" diff --git a/common/queue/src/main/java/org/thingsboard/server/kafka/TbKafkaHandler.java b/common/queue/src/main/java/org/thingsboard/server/kafka/TbKafkaHandler.java index a64d28e173..9f86155611 100644 --- a/common/queue/src/main/java/org/thingsboard/server/kafka/TbKafkaHandler.java +++ b/common/queue/src/main/java/org/thingsboard/server/kafka/TbKafkaHandler.java @@ -22,6 +22,6 @@ import com.google.common.util.concurrent.ListenableFuture; */ public interface TbKafkaHandler { - ListenableFuture handle(Request request) throws Exception; + ListenableFuture handle(Request request); } diff --git a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportContext.java b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportContext.java index 4742b3b520..173fe8aef1 100644 --- a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportContext.java +++ b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportContext.java @@ -48,7 +48,6 @@ public class MqttTransportContext { private final ObjectMapper mapper = new ObjectMapper(); @Autowired - @Lazy private TransportService transportService; @Autowired(required = false) diff --git a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java index b1b176d032..f9427a846e 100644 --- a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java +++ b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java @@ -494,7 +494,6 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement ctx.writeAndFlush(createMqttConnAckMsg(CONNECTION_REFUSED_NOT_AUTHORIZED)); ctx.close(); } else { - ctx.writeAndFlush(createMqttConnAckMsg(CONNECTION_ACCEPTED)); deviceSessionCtx.setDeviceInfo(msg.getDeviceInfo()); sessionInfo = SessionInfoProto.newBuilder() .setNodeId(context.getNodeId()) @@ -508,6 +507,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement transportService.process(sessionInfo, getSessionEventMsg(SessionEvent.OPEN), null); transportService.registerSession(sessionInfo, this); checkGatewaySession(); + ctx.writeAndFlush(createMqttConnAckMsg(CONNECTION_ACCEPTED)); } } diff --git a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionHandler.java b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionHandler.java index b1504b1246..7ef832f705 100644 --- a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionHandler.java +++ b/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionHandler.java @@ -87,10 +87,12 @@ public class GatewaySessionHandler { 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() { @Override public void onSuccess(@Nullable GatewayDeviceSessionCtx result) { ack(msg); + log.trace("[{}] onDeviceConnectOk: {}", sessionId, deviceName); } @Override diff --git a/transport/mqtt-transport/src/main/resources/logback.xml b/transport/mqtt-transport/src/main/resources/logback.xml new file mode 100644 index 0000000000..18864a9fab --- /dev/null +++ b/transport/mqtt-transport/src/main/resources/logback.xml @@ -0,0 +1,34 @@ + + + + + + + + %d{ISO8601} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + + + \ No newline at end of file From 67431a044a55c241ac99d7a9fa4d0c21102489a1 Mon Sep 17 00:00:00 2001 From: Andrew Shvayka Date: Mon, 15 Oct 2018 15:53:06 +0300 Subject: [PATCH 16/26] Refactoring of packages --- application/pom.xml | 4 +- .../server/actors/ActorSystemContext.java | 1 - .../src/main/resources/thingsboard.yml | 7 +- common/message/pom.xml | 4 + .../server/common/msg}/EncryptionUtil.java | 2 +- common/pom.xml | 1 - common/queue/pom.xml | 6 +- .../server/kafka/TbNodeIdProvider.java | 51 ++++++++ .../transport/mqtt}/pom.xml | 22 +++- .../mqtt/MqttSslHandlerProvider.java | 56 ++++++--- .../server/transport/mqtt/MqttTopics.java | 0 .../transport/mqtt/MqttTransportContext.java | 22 ++-- .../transport/mqtt/MqttTransportHandler.java | 2 +- .../mqtt/MqttTransportServerInitializer.java | 7 -- .../transport/mqtt/MqttTransportService.java | 10 -- .../mqtt/adaptors/JsonMqttAdaptor.java | 0 .../mqtt/adaptors/MqttTransportAdaptor.java | 1 - .../mqtt/session/DeviceSessionCtx.java | 3 - .../mqtt/session/GatewayDeviceSessionCtx.java | 0 .../mqtt/session/GatewaySessionHandler.java | 0 .../MqttDeviceAwareSessionContext.java | 0 .../mqtt/session/MqttTopicMatcher.java | 0 .../server/transport/mqtt/util/SslUtil.java | 2 +- common/transport/pom.xml | 83 ++----------- common/transport/transport-api/pom.xml | 113 ++++++++++++++++++ .../common/transport/SessionMsgListener.java | 0 .../common/transport/SessionMsgProcessor.java | 0 .../common/transport/TransportAdaptor.java | 0 .../common/transport/TransportService.java | 0 .../transport/TransportServiceCallback.java | 0 .../transport/adaptor/AdaptorException.java | 0 .../transport/adaptor/JsonConverter.java | 0 .../transport/auth/DeviceAuthResult.java | 0 .../transport/auth/DeviceAuthService.java | 0 .../transport/quota/AbstractQuotaService.java | 0 .../server/common/transport/quota/Clock.java | 0 .../common/transport/quota/QuotaService.java | 0 .../transport/quota/RequestLimitPolicy.java | 0 .../host/HostIntervalRegistryCleaner.java | 0 .../host/HostIntervalRegistryLogger.java | 0 .../host/HostRequestIntervalRegistry.java | 0 .../quota/host/HostRequestLimitPolicy.java | 0 .../quota/host/HostRequestsQuotaService.java | 0 .../quota/inmemory/IntervalCount.java | 0 .../inmemory/IntervalRegistryCleaner.java | 0 .../inmemory/IntervalRegistryLogger.java | 0 .../inmemory/KeyBasedIntervalRegistry.java | 0 .../tenant/TenantIntervalRegistryCleaner.java | 0 .../tenant/TenantIntervalRegistryLogger.java | 0 .../tenant/TenantMsgsIntervalRegistry.java | 0 .../quota/tenant/TenantQuotaService.java | 0 .../tenant/TenantRequestLimitPolicy.java | 0 .../service/RemoteTransportService.java | 18 +-- .../service/ToRuleEngineMsgEncoder.java | 2 +- .../ToTransportMsgResponseDecoder.java | 2 +- .../service/TransportApiRequestEncoder.java | 2 +- .../service/TransportApiResponseDecoder.java | 2 +- .../session/DeviceAwareSessionContext.java | 0 .../src/main/proto/transport.proto | 1 + .../common/transport/quota/ClockTest.java | 0 .../quota/HostRequestLimitPolicyTest.java | 0 .../quota/HostRequestsQuotaServiceTest.java | 0 .../HostRequestIntervalRegistryTest.java | 0 .../quota/inmemory/IntervalCountTest.java | 0 .../inmemory/IntervalRegistryLoggerTest.java | 0 .../device/DeviceCredentialsServiceImpl.java | 2 +- pom.xml | 13 +- rule-engine/rule-engine-components/pom.xml | 4 + .../TbCopyAttributesToEntityViewNode.java | 1 - .../transport/http/DeviceApiController.java | 1 - .../http/session/HttpSessionCtx.java | 12 -- transport/mqtt-transport/pom.xml | 8 +- transport/pom.xml | 2 - 73 files changed, 287 insertions(+), 180 deletions(-) rename {dao/src/main/java/org/thingsboard/server/dao => common/message/src/main/java/org/thingsboard/server/common/msg}/EncryptionUtil.java (97%) create mode 100644 common/queue/src/main/java/org/thingsboard/server/kafka/TbNodeIdProvider.java rename {transport/mqtt-common => common/transport/mqtt}/pom.xml (80%) rename {transport/mqtt-common => common/transport/mqtt}/src/main/java/org/thingsboard/server/transport/mqtt/MqttSslHandlerProvider.java (70%) rename {transport/mqtt-common => common/transport/mqtt}/src/main/java/org/thingsboard/server/transport/mqtt/MqttTopics.java (100%) rename {transport/mqtt-common => common/transport/mqtt}/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportContext.java (80%) rename {transport/mqtt-common => common/transport/mqtt}/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java (99%) rename {transport/mqtt-common => common/transport/mqtt}/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportServerInitializer.java (80%) rename {transport/mqtt-common => common/transport/mqtt}/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportService.java (83%) rename {transport/mqtt-common => common/transport/mqtt}/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java (100%) rename {transport/mqtt-common => common/transport/mqtt}/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/MqttTransportAdaptor.java (98%) rename {transport/mqtt-common => common/transport/mqtt}/src/main/java/org/thingsboard/server/transport/mqtt/session/DeviceSessionCtx.java (88%) rename {transport/mqtt-common => common/transport/mqtt}/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewayDeviceSessionCtx.java (100%) rename {transport/mqtt-common => common/transport/mqtt}/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionHandler.java (100%) rename {transport/mqtt-common => common/transport/mqtt}/src/main/java/org/thingsboard/server/transport/mqtt/session/MqttDeviceAwareSessionContext.java (100%) rename {transport/mqtt-common => common/transport/mqtt}/src/main/java/org/thingsboard/server/transport/mqtt/session/MqttTopicMatcher.java (100%) rename {transport/mqtt-common => common/transport/mqtt}/src/main/java/org/thingsboard/server/transport/mqtt/util/SslUtil.java (96%) create mode 100644 common/transport/transport-api/pom.xml rename common/transport/{ => transport-api}/src/main/java/org/thingsboard/server/common/transport/SessionMsgListener.java (100%) rename common/transport/{ => transport-api}/src/main/java/org/thingsboard/server/common/transport/SessionMsgProcessor.java (100%) rename common/transport/{ => transport-api}/src/main/java/org/thingsboard/server/common/transport/TransportAdaptor.java (100%) rename common/transport/{ => transport-api}/src/main/java/org/thingsboard/server/common/transport/TransportService.java (100%) rename common/transport/{ => transport-api}/src/main/java/org/thingsboard/server/common/transport/TransportServiceCallback.java (100%) rename common/transport/{ => transport-api}/src/main/java/org/thingsboard/server/common/transport/adaptor/AdaptorException.java (100%) rename common/transport/{ => transport-api}/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java (100%) rename common/transport/{ => transport-api}/src/main/java/org/thingsboard/server/common/transport/auth/DeviceAuthResult.java (100%) rename common/transport/{ => transport-api}/src/main/java/org/thingsboard/server/common/transport/auth/DeviceAuthService.java (100%) rename common/transport/{ => transport-api}/src/main/java/org/thingsboard/server/common/transport/quota/AbstractQuotaService.java (100%) rename common/transport/{ => transport-api}/src/main/java/org/thingsboard/server/common/transport/quota/Clock.java (100%) rename common/transport/{ => transport-api}/src/main/java/org/thingsboard/server/common/transport/quota/QuotaService.java (100%) rename common/transport/{ => transport-api}/src/main/java/org/thingsboard/server/common/transport/quota/RequestLimitPolicy.java (100%) rename common/transport/{ => transport-api}/src/main/java/org/thingsboard/server/common/transport/quota/host/HostIntervalRegistryCleaner.java (100%) rename common/transport/{ => transport-api}/src/main/java/org/thingsboard/server/common/transport/quota/host/HostIntervalRegistryLogger.java (100%) rename common/transport/{ => transport-api}/src/main/java/org/thingsboard/server/common/transport/quota/host/HostRequestIntervalRegistry.java (100%) rename common/transport/{ => transport-api}/src/main/java/org/thingsboard/server/common/transport/quota/host/HostRequestLimitPolicy.java (100%) rename common/transport/{ => transport-api}/src/main/java/org/thingsboard/server/common/transport/quota/host/HostRequestsQuotaService.java (100%) rename common/transport/{ => transport-api}/src/main/java/org/thingsboard/server/common/transport/quota/inmemory/IntervalCount.java (100%) rename common/transport/{ => transport-api}/src/main/java/org/thingsboard/server/common/transport/quota/inmemory/IntervalRegistryCleaner.java (100%) rename common/transport/{ => transport-api}/src/main/java/org/thingsboard/server/common/transport/quota/inmemory/IntervalRegistryLogger.java (100%) rename common/transport/{ => transport-api}/src/main/java/org/thingsboard/server/common/transport/quota/inmemory/KeyBasedIntervalRegistry.java (100%) rename common/transport/{ => transport-api}/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantIntervalRegistryCleaner.java (100%) rename common/transport/{ => transport-api}/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantIntervalRegistryLogger.java (100%) rename common/transport/{ => transport-api}/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantMsgsIntervalRegistry.java (100%) rename common/transport/{ => transport-api}/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantQuotaService.java (100%) rename common/transport/{ => transport-api}/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantRequestLimitPolicy.java (100%) rename transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/MqttTransportService.java => common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/RemoteTransportService.java (96%) rename {transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt => common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport}/service/ToRuleEngineMsgEncoder.java (94%) rename {transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt => common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport}/service/ToTransportMsgResponseDecoder.java (94%) rename {transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt => common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport}/service/TransportApiRequestEncoder.java (94%) rename {transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt => common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport}/service/TransportApiResponseDecoder.java (94%) rename common/transport/{ => transport-api}/src/main/java/org/thingsboard/server/common/transport/session/DeviceAwareSessionContext.java (100%) rename common/transport/{ => transport-api}/src/main/proto/transport.proto (99%) rename common/transport/{ => transport-api}/src/test/java/org/thingsboard/server/common/transport/quota/ClockTest.java (100%) rename common/transport/{ => transport-api}/src/test/java/org/thingsboard/server/common/transport/quota/HostRequestLimitPolicyTest.java (100%) rename common/transport/{ => transport-api}/src/test/java/org/thingsboard/server/common/transport/quota/HostRequestsQuotaServiceTest.java (100%) rename common/transport/{ => transport-api}/src/test/java/org/thingsboard/server/common/transport/quota/inmemory/HostRequestIntervalRegistryTest.java (100%) rename common/transport/{ => transport-api}/src/test/java/org/thingsboard/server/common/transport/quota/inmemory/IntervalCountTest.java (100%) rename common/transport/{ => transport-api}/src/test/java/org/thingsboard/server/common/transport/quota/inmemory/IntervalRegistryLoggerTest.java (100%) diff --git a/application/pom.xml b/application/pom.xml index dd1bade336..915a4ab5e1 100644 --- a/application/pom.xml +++ b/application/pom.xml @@ -69,8 +69,8 @@ - org.thingsboard.transport - mqtt-common + org.thingsboard.common.transport + mqtt org.thingsboard 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 1081ab2aec..5e19d690c4 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java +++ b/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java @@ -64,7 +64,6 @@ 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; diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 0693d5ddd2..ca509443bf 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -417,7 +417,7 @@ js: max_errors: "${REMOTE_JS_SANDBOX_MAX_ERRORS:3}" transport: - type: "${TRANSPORT_TYPE:remote}" # local or remote + type: "${TRANSPORT_TYPE:local}" # local or remote remote: transport_api: requests_topic: "${TB_TRANSPORT_API_REQUEST_TOPIC:tb.transport.api.requests}" @@ -434,11 +434,12 @@ transport: topic: "${TB_TRANSPORT_NOTIFICATIONS_TOPIC:tb.transport.notifications}" # Local HTTP transport parameters http: + enabled: "${MQTT_ENABLED:true}" request_timeout: "${HTTP_REQUEST_TIMEOUT:60000}" # Local MQTT transport parameters mqtt: # Enable/disable mqtt transport protocol. - enabled: "${MQTT_ENABLED:false}" + enabled: "${MQTT_ENABLED:true}" bind_address: "${MQTT_BIND_ADDRESS:0.0.0.0}" bind_port: "${MQTT_BIND_PORT:1883}" adaptor: "${MQTT_ADAPTOR_NAME:JsonMqttAdaptor}" @@ -465,7 +466,7 @@ transport: # Local CoAP transport parameters coap: # Enable/disable coap transport protocol. - enabled: "${COAP_ENABLED:false}" + enabled: "${COAP_ENABLED:true}" bind_address: "${COAP_BIND_ADDRESS:0.0.0.0}" bind_port: "${COAP_BIND_PORT:5683}" adaptor: "${COAP_ADAPTOR_NAME:JsonCoapAdaptor}" diff --git a/common/message/pom.xml b/common/message/pom.xml index f4f20b08c6..d914d4f21d 100644 --- a/common/message/pom.xml +++ b/common/message/pom.xml @@ -40,6 +40,10 @@ org.thingsboard.common data + + org.bouncycastle + bcprov-jdk15on + org.slf4j slf4j-api diff --git a/dao/src/main/java/org/thingsboard/server/dao/EncryptionUtil.java b/common/message/src/main/java/org/thingsboard/server/common/msg/EncryptionUtil.java similarity index 97% rename from dao/src/main/java/org/thingsboard/server/dao/EncryptionUtil.java rename to common/message/src/main/java/org/thingsboard/server/common/msg/EncryptionUtil.java index a44ad57e19..41a608763a 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/EncryptionUtil.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/EncryptionUtil.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.dao; +package org.thingsboard.server.common.msg; import lombok.extern.slf4j.Slf4j; import org.bouncycastle.crypto.digests.SHA3Digest; diff --git a/common/pom.xml b/common/pom.xml index e3cdabff98..84481334e9 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -23,7 +23,6 @@ 2.2.0-SNAPSHOT thingsboard - org.thingsboard common pom diff --git a/common/queue/pom.xml b/common/queue/pom.xml index 11bb13981f..765960e6a7 100644 --- a/common/queue/pom.xml +++ b/common/queue/pom.xml @@ -16,7 +16,7 @@ --> + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 org.thingsboard @@ -64,6 +64,10 @@ com.google.code.gson gson + + org.apache.commons + commons-lang3 + org.slf4j slf4j-api diff --git a/common/queue/src/main/java/org/thingsboard/server/kafka/TbNodeIdProvider.java b/common/queue/src/main/java/org/thingsboard/server/kafka/TbNodeIdProvider.java new file mode 100644 index 0000000000..47dd725682 --- /dev/null +++ b/common/queue/src/main/java/org/thingsboard/server/kafka/TbNodeIdProvider.java @@ -0,0 +1,51 @@ +/** + * 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.Getter; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; + +import javax.annotation.PostConstruct; +import java.net.InetAddress; +import java.net.UnknownHostException; + +/** + * Created by ashvayka on 12.10.18. + */ +@Slf4j +@Component +public class TbNodeIdProvider { + + @Getter + @Value("${cluster.node_id:#{null}}") + private String nodeId; + + @PostConstruct + public void init() { + if (StringUtils.isEmpty(nodeId)) { + try { + nodeId = InetAddress.getLocalHost().getHostName(); + } catch (UnknownHostException e) { + nodeId = org.apache.commons.lang3.RandomStringUtils.randomAlphabetic(10); + } + } + log.info("Current NodeId: {}", nodeId); + } + +} diff --git a/transport/mqtt-common/pom.xml b/common/transport/mqtt/pom.xml similarity index 80% rename from transport/mqtt-common/pom.xml rename to common/transport/mqtt/pom.xml index 6be5235a8a..91f30a6f3d 100644 --- a/transport/mqtt-common/pom.xml +++ b/common/transport/mqtt/pom.xml @@ -19,12 +19,12 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 - org.thingsboard + org.thingsboard.common 2.2.0-SNAPSHOT transport - org.thingsboard.transport - mqtt-common + org.thingsboard.common.transport + mqtt jar Thingsboard MQTT Transport Common @@ -32,18 +32,22 @@ UTF-8 - ${basedir}/../.. + ${basedir}/../../.. - org.thingsboard.common - transport + org.thingsboard.common.transport + transport-api io.netty netty-all + + org.springframework + spring-context-support + org.springframework spring-context @@ -68,6 +72,12 @@ com.google.guava guava + + com.google.code.findbugs + jsr305 + 3.0.1 + true + org.springframework.boot spring-boot-starter-test diff --git a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttSslHandlerProvider.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttSslHandlerProvider.java similarity index 70% rename from transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttSslHandlerProvider.java rename to common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttSslHandlerProvider.java index 70ed5013e4..ee28738a1e 100644 --- a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttSslHandlerProvider.java +++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttSslHandlerProvider.java @@ -23,12 +23,19 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; -import org.thingsboard.server.common.data.security.DeviceCredentials; -import org.thingsboard.server.dao.EncryptionUtil; -import org.thingsboard.server.dao.device.DeviceCredentialsService; +import org.thingsboard.server.common.msg.EncryptionUtil; +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.transport.mqtt.util.SslUtil; -import javax.net.ssl.*; +import javax.net.ssl.KeyManager; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactory; +import javax.net.ssl.X509TrustManager; import java.io.File; import java.io.FileInputStream; import java.io.IOException; @@ -37,6 +44,8 @@ import java.net.URL; import java.security.KeyStore; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; /** * Created by valerii.sosliuk on 11/6/16. @@ -58,7 +67,7 @@ public class MqttSslHandlerProvider { private String keyStoreType; @Autowired - private DeviceCredentialsService deviceCredentialsService; + private TransportService transportService; public SslHandler getSslHandler() { try { @@ -111,17 +120,17 @@ public class MqttSslHandlerProvider { break; } } - return new ThingsboardMqttX509TrustManager(x509Tm, deviceCredentialsService); + return new ThingsboardMqttX509TrustManager(x509Tm, transportService); } static class ThingsboardMqttX509TrustManager implements X509TrustManager { private final X509TrustManager trustManager; - private DeviceCredentialsService deviceCredentialsService; + private TransportService transportService; - ThingsboardMqttX509TrustManager(X509TrustManager trustManager, DeviceCredentialsService deviceCredentialsService) { + ThingsboardMqttX509TrustManager(X509TrustManager trustManager, TransportService transportService) { this.trustManager = trustManager; - this.deviceCredentialsService = deviceCredentialsService; + this.transportService = transportService; } @Override @@ -138,20 +147,39 @@ public class MqttSslHandlerProvider { @Override public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { - DeviceCredentials deviceCredentials = null; + String credentialsBody = null; for (X509Certificate cert : chain) { try { String strCert = SslUtil.getX509CertificateString(cert); String sha3Hash = EncryptionUtil.getSha3Hash(strCert); - deviceCredentials = deviceCredentialsService.findDeviceCredentialsByCredentialsId(sha3Hash); - if (deviceCredentials != null && strCert.equals(deviceCredentials.getCredentialsValue())) { + final String[] credentialsBodyHolder = new String[1]; + CountDownLatch latch = new CountDownLatch(1); + transportService.process(TransportProtos.ValidateDeviceX509CertRequestMsg.newBuilder().setHash(sha3Hash).build(), + new TransportServiceCallback() { + @Override + public void onSuccess(TransportProtos.ValidateDeviceCredentialsResponseMsg msg) { + if (!StringUtils.isEmpty(msg.getCredentialsBody())) { + credentialsBodyHolder[0] = msg.getCredentialsBody(); + } + latch.countDown(); + } + + @Override + public void onError(Throwable e) { + log.error(e.getMessage(), e); + latch.countDown(); + } + }); + latch.await(10, TimeUnit.SECONDS); + if (strCert.equals(credentialsBodyHolder[0])) { + credentialsBody = credentialsBodyHolder[0]; break; } - } catch (IOException e) { + } catch (InterruptedException | IOException e) { log.error(e.getMessage(), e); } } - if (deviceCredentials == null) { + if (credentialsBody == null) { throw new CertificateException("Invalid Device Certificate"); } } diff --git a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTopics.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTopics.java similarity index 100% rename from transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTopics.java rename to common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTopics.java diff --git a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportContext.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportContext.java similarity index 80% rename from transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportContext.java rename to common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportContext.java index 9d3d45f92b..9ae42e8ca3 100644 --- a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportContext.java +++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportContext.java @@ -20,21 +20,17 @@ import io.netty.handler.ssl.SslHandler; import lombok.Data; import lombok.Getter; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.RandomStringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; -import org.springframework.util.StringUtils; 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.net.InetAddress; -import java.net.UnknownHostException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -61,11 +57,12 @@ public class MqttTransportContext { @Autowired private MqttTransportAdaptor adaptor; + @Autowired + private TbNodeIdProvider nodeIdProvider; + @Value("${transport.mqtt.netty.max_payload_size}") private Integer maxPayloadSize; - @Value("${cluster.node_id:#{null}}") - private String nodeId; private SslHandler sslHandler; @@ -74,14 +71,6 @@ public class MqttTransportContext { @PostConstruct public void init() { - if (StringUtils.isEmpty(nodeId)) { - try { - nodeId = InetAddress.getLocalHost().getHostName(); - } catch (UnknownHostException e) { - nodeId = RandomStringUtils.randomAlphabetic(10); - } - } - log.info("Current NodeId: {}", nodeId); executor = Executors.newCachedThreadPool(); } @@ -92,4 +81,7 @@ public class MqttTransportContext { } } + public String getNodeId() { + return nodeIdProvider.getNodeId(); + } } diff --git a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java similarity index 99% rename from transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java rename to common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java index c09b9193e4..6a5597bb11 100644 --- a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java +++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java @@ -43,7 +43,7 @@ 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.quota.QuotaService; -import org.thingsboard.server.dao.EncryptionUtil; +import org.thingsboard.server.common.msg.EncryptionUtil; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos.DeviceInfoProto; import org.thingsboard.server.gen.transport.TransportProtos.SessionEvent; diff --git a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportServerInitializer.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportServerInitializer.java similarity index 80% rename from transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportServerInitializer.java rename to common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportServerInitializer.java index 102a0d9f07..550837842f 100644 --- a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportServerInitializer.java +++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportServerInitializer.java @@ -21,13 +21,6 @@ import io.netty.channel.socket.SocketChannel; import io.netty.handler.codec.mqtt.MqttDecoder; import io.netty.handler.codec.mqtt.MqttEncoder; import io.netty.handler.ssl.SslHandler; -import org.thingsboard.server.common.transport.SessionMsgProcessor; -import org.thingsboard.server.common.transport.TransportService; -import org.thingsboard.server.common.transport.auth.DeviceAuthService; -import org.thingsboard.server.common.transport.quota.QuotaService; -import org.thingsboard.server.dao.device.DeviceService; -import org.thingsboard.server.dao.relation.RelationService; -import org.thingsboard.server.transport.mqtt.adaptors.MqttTransportAdaptor; /** * @author Andrew Shvayka diff --git a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportService.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportService.java similarity index 83% rename from transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportService.java rename to common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportService.java index 41026784df..199b567430 100644 --- a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportService.java +++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportService.java @@ -23,19 +23,9 @@ import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.util.ResourceLeakDetector; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.context.ApplicationContext; -import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; -import org.thingsboard.server.common.transport.SessionMsgProcessor; -import org.thingsboard.server.common.transport.TransportService; -import org.thingsboard.server.common.transport.auth.DeviceAuthService; -import org.thingsboard.server.common.transport.quota.host.HostRequestsQuotaService; -import org.thingsboard.server.dao.device.DeviceService; -import org.thingsboard.server.dao.relation.RelationService; -import org.thingsboard.server.transport.mqtt.adaptors.MqttTransportAdaptor; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; diff --git a/transport/mqtt-common/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 similarity index 100% rename from transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java rename to common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java diff --git a/transport/mqtt-common/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 similarity index 98% rename from transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/MqttTransportAdaptor.java rename to common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/MqttTransportAdaptor.java index a653e4c93e..7af0e66e34 100644 --- a/transport/mqtt-common/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 @@ -18,7 +18,6 @@ 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; import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg; import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeRequestMsg; import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeResponseMsg; diff --git a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/DeviceSessionCtx.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/DeviceSessionCtx.java similarity index 88% rename from transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/DeviceSessionCtx.java rename to common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/DeviceSessionCtx.java index dc0e9a44c2..fb05e0a624 100644 --- a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/DeviceSessionCtx.java +++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/DeviceSessionCtx.java @@ -16,11 +16,8 @@ package org.thingsboard.server.transport.mqtt.session; import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.mqtt.MqttMessage; import lombok.Getter; import lombok.extern.slf4j.Slf4j; -import org.thingsboard.server.common.msg.session.ex.SessionException; -import org.thingsboard.server.common.transport.adaptor.AdaptorException; import java.util.UUID; import java.util.concurrent.ConcurrentMap; diff --git a/transport/mqtt-common/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 similarity index 100% rename from transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewayDeviceSessionCtx.java rename to common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewayDeviceSessionCtx.java diff --git a/transport/mqtt-common/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 similarity index 100% rename from transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionHandler.java rename to common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionHandler.java diff --git a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/MqttDeviceAwareSessionContext.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/MqttDeviceAwareSessionContext.java similarity index 100% rename from transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/MqttDeviceAwareSessionContext.java rename to common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/MqttDeviceAwareSessionContext.java diff --git a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/MqttTopicMatcher.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/MqttTopicMatcher.java similarity index 100% rename from transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/session/MqttTopicMatcher.java rename to common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/MqttTopicMatcher.java diff --git a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/util/SslUtil.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/util/SslUtil.java similarity index 96% rename from transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/util/SslUtil.java rename to common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/util/SslUtil.java index 96cdf142a6..d98d1b91bc 100644 --- a/transport/mqtt-common/src/main/java/org/thingsboard/server/transport/mqtt/util/SslUtil.java +++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/util/SslUtil.java @@ -17,7 +17,7 @@ package org.thingsboard.server.transport.mqtt.util; import lombok.extern.slf4j.Slf4j; import org.springframework.util.Base64Utils; -import org.thingsboard.server.dao.EncryptionUtil; +import org.thingsboard.server.common.msg.EncryptionUtil; import java.io.IOException; import java.security.cert.CertificateEncodingException; diff --git a/common/transport/pom.xml b/common/transport/pom.xml index 46efbf4dcc..69f64e6307 100644 --- a/common/transport/pom.xml +++ b/common/transport/pom.xml @@ -16,7 +16,7 @@ --> + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 org.thingsboard @@ -25,85 +25,20 @@ org.thingsboard.common transport - jar + pom - Thingsboard Server Common Transport components + Thingsboard Server Commons https://thingsboard.io UTF-8 ${basedir}/../.. - - - - org.thingsboard.common - data - - - org.thingsboard.common - message - - - com.google.code.gson - gson - - - org.slf4j - slf4j-api - - - org.slf4j - log4j-over-slf4j - - - ch.qos.logback - logback-core - - - ch.qos.logback - logback-classic - - - junit - junit - test - - - org.mockito - mockito-all - test - - - org.springframework - spring-context - - - org.springframework.boot - spring-boot-starter-web - provided - - - com.google.guava - guava - - - org.apache.commons - commons-lang3 - - - com.google.protobuf - protobuf-java - - - - - - - org.xolstice.maven.plugins - protobuf-maven-plugin - - - + + transport-api + mqtt + + + diff --git a/common/transport/transport-api/pom.xml b/common/transport/transport-api/pom.xml new file mode 100644 index 0000000000..3538e468d4 --- /dev/null +++ b/common/transport/transport-api/pom.xml @@ -0,0 +1,113 @@ + + + 4.0.0 + + org.thingsboard.common + 2.2.0-SNAPSHOT + transport + + org.thingsboard.common.transport + transport-api + jar + + Thingsboard Server Common Transport components + https://thingsboard.io + + + UTF-8 + ${basedir}/../../.. + + + + + org.thingsboard.common + data + + + org.thingsboard.common + message + + + org.thingsboard.common + queue + + + com.google.code.gson + gson + + + org.slf4j + slf4j-api + + + org.slf4j + log4j-over-slf4j + + + ch.qos.logback + logback-core + + + ch.qos.logback + logback-classic + + + junit + junit + test + + + org.mockito + mockito-all + test + + + org.springframework + spring-context + + + org.springframework.boot + spring-boot-starter-web + provided + + + com.google.guava + guava + + + org.apache.commons + commons-lang3 + + + com.google.protobuf + protobuf-java + + + + + + + org.xolstice.maven.plugins + protobuf-maven-plugin + + + + + diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/SessionMsgListener.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/SessionMsgListener.java similarity index 100% rename from common/transport/src/main/java/org/thingsboard/server/common/transport/SessionMsgListener.java rename to common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/SessionMsgListener.java diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/SessionMsgProcessor.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/SessionMsgProcessor.java similarity index 100% rename from common/transport/src/main/java/org/thingsboard/server/common/transport/SessionMsgProcessor.java rename to common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/SessionMsgProcessor.java diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/TransportAdaptor.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportAdaptor.java similarity index 100% rename from common/transport/src/main/java/org/thingsboard/server/common/transport/TransportAdaptor.java rename to common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportAdaptor.java diff --git a/common/transport/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 similarity index 100% rename from common/transport/src/main/java/org/thingsboard/server/common/transport/TransportService.java rename to common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportService.java diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/TransportServiceCallback.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportServiceCallback.java similarity index 100% rename from common/transport/src/main/java/org/thingsboard/server/common/transport/TransportServiceCallback.java rename to common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportServiceCallback.java diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/adaptor/AdaptorException.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/adaptor/AdaptorException.java similarity index 100% rename from common/transport/src/main/java/org/thingsboard/server/common/transport/adaptor/AdaptorException.java rename to common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/adaptor/AdaptorException.java diff --git a/common/transport/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 similarity index 100% rename from common/transport/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java rename to common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/auth/DeviceAuthResult.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/auth/DeviceAuthResult.java similarity index 100% rename from common/transport/src/main/java/org/thingsboard/server/common/transport/auth/DeviceAuthResult.java rename to common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/auth/DeviceAuthResult.java diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/auth/DeviceAuthService.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/auth/DeviceAuthService.java similarity index 100% rename from common/transport/src/main/java/org/thingsboard/server/common/transport/auth/DeviceAuthService.java rename to common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/auth/DeviceAuthService.java diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/AbstractQuotaService.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/AbstractQuotaService.java similarity index 100% rename from common/transport/src/main/java/org/thingsboard/server/common/transport/quota/AbstractQuotaService.java rename to common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/AbstractQuotaService.java diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/Clock.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/Clock.java similarity index 100% rename from common/transport/src/main/java/org/thingsboard/server/common/transport/quota/Clock.java rename to common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/Clock.java diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/QuotaService.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/QuotaService.java similarity index 100% rename from common/transport/src/main/java/org/thingsboard/server/common/transport/quota/QuotaService.java rename to common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/QuotaService.java diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/RequestLimitPolicy.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/RequestLimitPolicy.java similarity index 100% rename from common/transport/src/main/java/org/thingsboard/server/common/transport/quota/RequestLimitPolicy.java rename to common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/RequestLimitPolicy.java diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/host/HostIntervalRegistryCleaner.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/host/HostIntervalRegistryCleaner.java similarity index 100% rename from common/transport/src/main/java/org/thingsboard/server/common/transport/quota/host/HostIntervalRegistryCleaner.java rename to common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/host/HostIntervalRegistryCleaner.java diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/host/HostIntervalRegistryLogger.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/host/HostIntervalRegistryLogger.java similarity index 100% rename from common/transport/src/main/java/org/thingsboard/server/common/transport/quota/host/HostIntervalRegistryLogger.java rename to common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/host/HostIntervalRegistryLogger.java diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/host/HostRequestIntervalRegistry.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/host/HostRequestIntervalRegistry.java similarity index 100% rename from common/transport/src/main/java/org/thingsboard/server/common/transport/quota/host/HostRequestIntervalRegistry.java rename to common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/host/HostRequestIntervalRegistry.java diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/host/HostRequestLimitPolicy.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/host/HostRequestLimitPolicy.java similarity index 100% rename from common/transport/src/main/java/org/thingsboard/server/common/transport/quota/host/HostRequestLimitPolicy.java rename to common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/host/HostRequestLimitPolicy.java diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/host/HostRequestsQuotaService.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/host/HostRequestsQuotaService.java similarity index 100% rename from common/transport/src/main/java/org/thingsboard/server/common/transport/quota/host/HostRequestsQuotaService.java rename to common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/host/HostRequestsQuotaService.java diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/inmemory/IntervalCount.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/inmemory/IntervalCount.java similarity index 100% rename from common/transport/src/main/java/org/thingsboard/server/common/transport/quota/inmemory/IntervalCount.java rename to common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/inmemory/IntervalCount.java diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/inmemory/IntervalRegistryCleaner.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/inmemory/IntervalRegistryCleaner.java similarity index 100% rename from common/transport/src/main/java/org/thingsboard/server/common/transport/quota/inmemory/IntervalRegistryCleaner.java rename to common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/inmemory/IntervalRegistryCleaner.java diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/inmemory/IntervalRegistryLogger.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/inmemory/IntervalRegistryLogger.java similarity index 100% rename from common/transport/src/main/java/org/thingsboard/server/common/transport/quota/inmemory/IntervalRegistryLogger.java rename to common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/inmemory/IntervalRegistryLogger.java diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/inmemory/KeyBasedIntervalRegistry.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/inmemory/KeyBasedIntervalRegistry.java similarity index 100% rename from common/transport/src/main/java/org/thingsboard/server/common/transport/quota/inmemory/KeyBasedIntervalRegistry.java rename to common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/inmemory/KeyBasedIntervalRegistry.java diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantIntervalRegistryCleaner.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantIntervalRegistryCleaner.java similarity index 100% rename from common/transport/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantIntervalRegistryCleaner.java rename to common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantIntervalRegistryCleaner.java diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantIntervalRegistryLogger.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantIntervalRegistryLogger.java similarity index 100% rename from common/transport/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantIntervalRegistryLogger.java rename to common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantIntervalRegistryLogger.java diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantMsgsIntervalRegistry.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantMsgsIntervalRegistry.java similarity index 100% rename from common/transport/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantMsgsIntervalRegistry.java rename to common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantMsgsIntervalRegistry.java diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantQuotaService.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantQuotaService.java similarity index 100% rename from common/transport/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantQuotaService.java rename to common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantQuotaService.java diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantRequestLimitPolicy.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantRequestLimitPolicy.java similarity index 100% rename from common/transport/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantRequestLimitPolicy.java rename to common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantRequestLimitPolicy.java diff --git a/transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/MqttTransportService.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/RemoteTransportService.java similarity index 96% rename from transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/MqttTransportService.java rename to common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/RemoteTransportService.java index 4ce6b081aa..ab52d41366 100644 --- a/transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/MqttTransportService.java +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/RemoteTransportService.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.mqtt.service; +package org.thingsboard.server.common.transport.service; import lombok.extern.slf4j.Slf4j; import org.apache.kafka.clients.consumer.ConsumerRecords; @@ -21,6 +21,7 @@ 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.common.transport.SessionMsgListener; import org.thingsboard.server.common.transport.TransportService; @@ -44,7 +45,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.transport.mqtt.MqttTransportContext; +import org.thingsboard.server.kafka.TbNodeIdProvider; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; @@ -58,9 +59,10 @@ import java.util.concurrent.Executors; /** * Created by ashvayka on 05.10.18. */ +@ConditionalOnProperty(prefix = "transport", value = "type", havingValue = "remote", matchIfMissing = true) @Service @Slf4j -public class MqttTransportService implements TransportService { +public class RemoteTransportService implements TransportService { @Value("${kafka.rule_engine.topic}") private String ruleEngineTopic; @@ -89,7 +91,7 @@ public class MqttTransportService implements TransportService { 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 MqttTransportContext transportContext; + private TbNodeIdProvider nodeIdProvider; private ExecutorService transportCallbackExecutor; @@ -112,8 +114,8 @@ public class MqttTransportService implements TransportService { TBKafkaConsumerTemplate.TBKafkaConsumerTemplateBuilder responseBuilder = TBKafkaConsumerTemplate.builder(); responseBuilder.settings(kafkaSettings); - responseBuilder.topic(transportApiResponsesTopic + "." + transportContext.getNodeId()); - responseBuilder.clientId("transport-api-client-" + transportContext.getNodeId()); + responseBuilder.topic(transportApiResponsesTopic + "." + nodeIdProvider.getNodeId()); + responseBuilder.clientId("transport-api-client-" + nodeIdProvider.getNodeId()); responseBuilder.groupId("transport-api-client"); responseBuilder.autoCommit(true); responseBuilder.autoCommitIntervalMs(autoCommitInterval); @@ -138,8 +140,8 @@ public class MqttTransportService implements TransportService { TBKafkaConsumerTemplate.TBKafkaConsumerTemplateBuilder mainConsumerBuilder = TBKafkaConsumerTemplate.builder(); mainConsumerBuilder.settings(kafkaSettings); - mainConsumerBuilder.topic(notificationsTopic + "." + transportContext.getNodeId()); - mainConsumerBuilder.clientId("transport-" + transportContext.getNodeId()); + mainConsumerBuilder.topic(notificationsTopic + "." + nodeIdProvider.getNodeId()); + mainConsumerBuilder.clientId("transport-" + nodeIdProvider.getNodeId()); mainConsumerBuilder.groupId("transport"); mainConsumerBuilder.autoCommit(true); mainConsumerBuilder.autoCommitIntervalMs(notificationsAutoCommitInterval); diff --git a/transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/ToRuleEngineMsgEncoder.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/ToRuleEngineMsgEncoder.java similarity index 94% rename from transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/ToRuleEngineMsgEncoder.java rename to common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/ToRuleEngineMsgEncoder.java index 179a2a13cf..2842d7ef66 100644 --- a/transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/ToRuleEngineMsgEncoder.java +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/ToRuleEngineMsgEncoder.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.mqtt.service; +package org.thingsboard.server.common.transport.service; import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; import org.thingsboard.server.kafka.TbKafkaEncoder; diff --git a/transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/ToTransportMsgResponseDecoder.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/ToTransportMsgResponseDecoder.java similarity index 94% rename from transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/ToTransportMsgResponseDecoder.java rename to common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/ToTransportMsgResponseDecoder.java index b8d42049de..a905f76ace 100644 --- a/transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/ToTransportMsgResponseDecoder.java +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/ToTransportMsgResponseDecoder.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.mqtt.service; +package org.thingsboard.server.common.transport.service; import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg; import org.thingsboard.server.kafka.TbKafkaDecoder; diff --git a/transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/TransportApiRequestEncoder.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/TransportApiRequestEncoder.java similarity index 94% rename from transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/TransportApiRequestEncoder.java rename to common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/TransportApiRequestEncoder.java index 8ae4d1711f..a8f27aad8f 100644 --- a/transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/TransportApiRequestEncoder.java +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/TransportApiRequestEncoder.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.mqtt.service; +package org.thingsboard.server.common.transport.service; import org.thingsboard.server.gen.transport.TransportProtos.TransportApiRequestMsg; import org.thingsboard.server.kafka.TbKafkaEncoder; diff --git a/transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/TransportApiResponseDecoder.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/TransportApiResponseDecoder.java similarity index 94% rename from transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/TransportApiResponseDecoder.java rename to common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/TransportApiResponseDecoder.java index 66e64b56e6..3b5c9ce579 100644 --- a/transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/service/TransportApiResponseDecoder.java +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/TransportApiResponseDecoder.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.mqtt.service; +package org.thingsboard.server.common.transport.service; import org.thingsboard.server.gen.transport.TransportProtos.TransportApiResponseMsg; import org.thingsboard.server.kafka.TbKafkaDecoder; diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/session/DeviceAwareSessionContext.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/session/DeviceAwareSessionContext.java similarity index 100% rename from common/transport/src/main/java/org/thingsboard/server/common/transport/session/DeviceAwareSessionContext.java rename to common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/session/DeviceAwareSessionContext.java diff --git a/common/transport/src/main/proto/transport.proto b/common/transport/transport-api/src/main/proto/transport.proto similarity index 99% rename from common/transport/src/main/proto/transport.proto rename to common/transport/transport-api/src/main/proto/transport.proto index 3099c5c7f6..0e36dabbf3 100644 --- a/common/transport/src/main/proto/transport.proto +++ b/common/transport/transport-api/src/main/proto/transport.proto @@ -123,6 +123,7 @@ message ValidateDeviceX509CertRequestMsg { message ValidateDeviceCredentialsResponseMsg { DeviceInfoProto deviceInfo = 1; + string credentialsBody = 2; } message GetOrCreateDeviceFromGatewayRequestMsg { diff --git a/common/transport/src/test/java/org/thingsboard/server/common/transport/quota/ClockTest.java b/common/transport/transport-api/src/test/java/org/thingsboard/server/common/transport/quota/ClockTest.java similarity index 100% rename from common/transport/src/test/java/org/thingsboard/server/common/transport/quota/ClockTest.java rename to common/transport/transport-api/src/test/java/org/thingsboard/server/common/transport/quota/ClockTest.java diff --git a/common/transport/src/test/java/org/thingsboard/server/common/transport/quota/HostRequestLimitPolicyTest.java b/common/transport/transport-api/src/test/java/org/thingsboard/server/common/transport/quota/HostRequestLimitPolicyTest.java similarity index 100% rename from common/transport/src/test/java/org/thingsboard/server/common/transport/quota/HostRequestLimitPolicyTest.java rename to common/transport/transport-api/src/test/java/org/thingsboard/server/common/transport/quota/HostRequestLimitPolicyTest.java diff --git a/common/transport/src/test/java/org/thingsboard/server/common/transport/quota/HostRequestsQuotaServiceTest.java b/common/transport/transport-api/src/test/java/org/thingsboard/server/common/transport/quota/HostRequestsQuotaServiceTest.java similarity index 100% rename from common/transport/src/test/java/org/thingsboard/server/common/transport/quota/HostRequestsQuotaServiceTest.java rename to common/transport/transport-api/src/test/java/org/thingsboard/server/common/transport/quota/HostRequestsQuotaServiceTest.java diff --git a/common/transport/src/test/java/org/thingsboard/server/common/transport/quota/inmemory/HostRequestIntervalRegistryTest.java b/common/transport/transport-api/src/test/java/org/thingsboard/server/common/transport/quota/inmemory/HostRequestIntervalRegistryTest.java similarity index 100% rename from common/transport/src/test/java/org/thingsboard/server/common/transport/quota/inmemory/HostRequestIntervalRegistryTest.java rename to common/transport/transport-api/src/test/java/org/thingsboard/server/common/transport/quota/inmemory/HostRequestIntervalRegistryTest.java diff --git a/common/transport/src/test/java/org/thingsboard/server/common/transport/quota/inmemory/IntervalCountTest.java b/common/transport/transport-api/src/test/java/org/thingsboard/server/common/transport/quota/inmemory/IntervalCountTest.java similarity index 100% rename from common/transport/src/test/java/org/thingsboard/server/common/transport/quota/inmemory/IntervalCountTest.java rename to common/transport/transport-api/src/test/java/org/thingsboard/server/common/transport/quota/inmemory/IntervalCountTest.java diff --git a/common/transport/src/test/java/org/thingsboard/server/common/transport/quota/inmemory/IntervalRegistryLoggerTest.java b/common/transport/transport-api/src/test/java/org/thingsboard/server/common/transport/quota/inmemory/IntervalRegistryLoggerTest.java similarity index 100% rename from common/transport/src/test/java/org/thingsboard/server/common/transport/quota/inmemory/IntervalRegistryLoggerTest.java rename to common/transport/transport-api/src/test/java/org/thingsboard/server/common/transport/quota/inmemory/IntervalRegistryLoggerTest.java 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 4219b068f1..4b25f2b38a 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; diff --git a/pom.xml b/pom.xml index 62fda36056..e999a4c3f8 100755 --- a/pom.xml +++ b/pom.xml @@ -355,18 +355,23 @@ ${project.version} - org.thingsboard.transport - http + org.thingsboard.common.transport + transport-api + ${project.version} + + + org.thingsboard.common.transport + mqtt ${project.version} org.thingsboard.transport - coap + http ${project.version} org.thingsboard.transport - mqtt-common + coap ${project.version} diff --git a/rule-engine/rule-engine-components/pom.xml b/rule-engine/rule-engine-components/pom.xml index 6390929911..0dc64f0245 100644 --- a/rule-engine/rule-engine-components/pom.xml +++ b/rule-engine/rule-engine-components/pom.xml @@ -71,6 +71,10 @@ com.google.guava guava + + com.google.code.gson + gson + org.springframework spring-web 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 8b4411eb6a..1aa9e67384 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 @@ -25,7 +25,6 @@ import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.TbNode; import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeException; -import org.thingsboard.rule.engine.api.TbRelationTypes; import org.thingsboard.rule.engine.api.util.DonAsynchron; import org.thingsboard.rule.engine.api.util.TbNodeUtils; import org.thingsboard.server.common.data.DataConstants; diff --git a/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java b/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java index aedab28dcb..cd6d89998c 100644 --- a/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java +++ b/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java @@ -23,7 +23,6 @@ import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import org.springframework.web.context.request.async.DeferredResult; -import org.thingsboard.server.common.msg.core.*; import org.thingsboard.server.common.transport.SessionMsgProcessor; import org.thingsboard.server.common.transport.auth.DeviceAuthService; import org.thingsboard.server.common.transport.quota.host.HostRequestsQuotaService; diff --git a/transport/http/src/main/java/org/thingsboard/server/transport/http/session/HttpSessionCtx.java b/transport/http/src/main/java/org/thingsboard/server/transport/http/session/HttpSessionCtx.java index 0e087d4966..27cd4f1dd4 100644 --- a/transport/http/src/main/java/org/thingsboard/server/transport/http/session/HttpSessionCtx.java +++ b/transport/http/src/main/java/org/thingsboard/server/transport/http/session/HttpSessionCtx.java @@ -15,22 +15,10 @@ */ package org.thingsboard.server.transport.http.session; -import com.google.gson.JsonObject; import lombok.extern.slf4j.Slf4j; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.web.context.request.async.DeferredResult; -import org.thingsboard.server.common.msg.core.*; -import org.thingsboard.server.common.msg.session.*; -import org.thingsboard.server.common.msg.session.ex.SessionException; -import org.thingsboard.server.common.transport.SessionMsgProcessor; -import org.thingsboard.server.common.transport.adaptor.JsonConverter; -import org.thingsboard.server.common.transport.auth.DeviceAuthService; import org.thingsboard.server.common.transport.session.DeviceAwareSessionContext; -import java.util.Optional; import java.util.UUID; -import java.util.function.Consumer; /** * @author Andrew Shvayka diff --git a/transport/mqtt-transport/pom.xml b/transport/mqtt-transport/pom.xml index 012aabd4e9..56814a68be 100644 --- a/transport/mqtt-transport/pom.xml +++ b/transport/mqtt-transport/pom.xml @@ -37,12 +37,8 @@ - org.thingsboard.common - transport - - - org.thingsboard.transport - mqtt-common + org.thingsboard.common.transport + mqtt org.thingsboard.common diff --git a/transport/pom.xml b/transport/pom.xml index 79159885d2..9a7c2e4690 100644 --- a/transport/pom.xml +++ b/transport/pom.xml @@ -23,7 +23,6 @@ 2.2.0-SNAPSHOT thingsboard - org.thingsboard transport pom @@ -37,7 +36,6 @@ http - mqtt-common mqtt-transport From 6678891c5b839a800e71d1f601faa10144f2b077 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Mon, 15 Oct 2018 16:42:08 +0300 Subject: [PATCH 17/26] MQTT Transport packaging --- application/pom.xml | 4 +- rule-engine/rule-engine-components/pom.xml | 4 +- transport/mqtt-transport/pom.xml | 64 ---- transport/mqtt/build.gradle | 140 ++++++++ transport/mqtt/pom.xml | 337 ++++++++++++++++++ transport/mqtt/src/main/assembly/windows.xml | 71 ++++ transport/mqtt/src/main/conf/logback.xml | 43 +++ .../mqtt/src/main/conf/tb-mqtt-transport.conf | 23 ++ .../mqtt/src/main/filters/unix.properties | 1 + .../mqtt/src/main/filters/windows.properties | 2 + .../ThingsboardMqttTransportApplication.java | 0 .../src/main/resources/logback.xml | 0 .../src/main/resources/tb-mqtt-transport.yml | 0 .../src/main/scripts/control/deb/postinst | 6 + .../mqtt/src/main/scripts/control/deb/postrm | 3 + .../mqtt/src/main/scripts/control/deb/preinst | 18 + .../mqtt/src/main/scripts/control/deb/prerm | 5 + .../src/main/scripts/control/rpm/postinst | 9 + .../mqtt/src/main/scripts/control/rpm/postrm | 6 + .../mqtt/src/main/scripts/control/rpm/preinst | 6 + .../mqtt/src/main/scripts/control/rpm/prerm | 6 + .../scripts/control/tb-mqtt-transport.service | 11 + .../mqtt/src/main/scripts/windows/install.bat | 87 +++++ .../mqtt/src/main/scripts/windows/service.xml | 36 ++ .../src/main/scripts/windows/uninstall.bat | 9 + transport/pom.xml | 4 +- 26 files changed, 825 insertions(+), 70 deletions(-) delete mode 100644 transport/mqtt-transport/pom.xml create mode 100644 transport/mqtt/build.gradle create mode 100644 transport/mqtt/pom.xml create mode 100644 transport/mqtt/src/main/assembly/windows.xml create mode 100644 transport/mqtt/src/main/conf/logback.xml create mode 100644 transport/mqtt/src/main/conf/tb-mqtt-transport.conf create mode 100644 transport/mqtt/src/main/filters/unix.properties create mode 100644 transport/mqtt/src/main/filters/windows.properties rename transport/{mqtt-transport => mqtt}/src/main/java/org/thingsboard/server/mqtt/ThingsboardMqttTransportApplication.java (100%) rename transport/{mqtt-transport => mqtt}/src/main/resources/logback.xml (100%) rename transport/{mqtt-transport => mqtt}/src/main/resources/tb-mqtt-transport.yml (100%) create mode 100644 transport/mqtt/src/main/scripts/control/deb/postinst create mode 100644 transport/mqtt/src/main/scripts/control/deb/postrm create mode 100644 transport/mqtt/src/main/scripts/control/deb/preinst create mode 100644 transport/mqtt/src/main/scripts/control/deb/prerm create mode 100644 transport/mqtt/src/main/scripts/control/rpm/postinst create mode 100644 transport/mqtt/src/main/scripts/control/rpm/postrm create mode 100644 transport/mqtt/src/main/scripts/control/rpm/preinst create mode 100644 transport/mqtt/src/main/scripts/control/rpm/prerm create mode 100644 transport/mqtt/src/main/scripts/control/tb-mqtt-transport.service create mode 100644 transport/mqtt/src/main/scripts/windows/install.bat create mode 100644 transport/mqtt/src/main/scripts/windows/service.xml create mode 100644 transport/mqtt/src/main/scripts/windows/uninstall.bat diff --git a/application/pom.xml b/application/pom.xml index 915a4ab5e1..45318b3bb2 100644 --- a/application/pom.xml +++ b/application/pom.xml @@ -57,8 +57,8 @@ rule-engine-components - org.thingsboard.common - transport + org.thingsboard.common.transport + transport-api diff --git a/rule-engine/rule-engine-components/pom.xml b/rule-engine/rule-engine-components/pom.xml index 0dc64f0245..c22ce784fa 100644 --- a/rule-engine/rule-engine-components/pom.xml +++ b/rule-engine/rule-engine-components/pom.xml @@ -45,8 +45,8 @@ provided - org.thingsboard.common - transport + org.thingsboard.common.transport + transport-api provided diff --git a/transport/mqtt-transport/pom.xml b/transport/mqtt-transport/pom.xml deleted file mode 100644 index 56814a68be..0000000000 --- a/transport/mqtt-transport/pom.xml +++ /dev/null @@ -1,64 +0,0 @@ - - - 4.0.0 - - org.thingsboard - 2.2.0-SNAPSHOT - transport - - org.thingsboard.transport - mqtt-transport - jar - - Thingsboard MQTT Transport Service - https://thingsboard.io - - - UTF-8 - ${basedir}/../.. - - - - - org.thingsboard.common.transport - mqtt - - - org.thingsboard.common - queue - - - org.springframework.boot - spring-boot-starter-test - test - - - junit - junit - test - - - org.mockito - mockito-all - test - - - - diff --git a/transport/mqtt/build.gradle b/transport/mqtt/build.gradle new file mode 100644 index 0000000000..6d54cb4789 --- /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") +} diff --git a/transport/mqtt/pom.xml b/transport/mqtt/pom.xml new file mode 100644 index 0000000000..2750e1e511 --- /dev/null +++ b/transport/mqtt/pom.xml @@ -0,0 +1,337 @@ + + + 4.0.0 + + org.thingsboard + 2.2.0-SNAPSHOT + transport + + org.thingsboard.transport + mqtt + jar + + Thingsboard MQTT Transport Service + https://thingsboard.io + + + UTF-8 + ${basedir}/../.. + tb-mqtt-transport + /var/log/${pkg.name} + /usr/share/${pkg.name} + ${project.build.directory}/windows + + + + + org.thingsboard.common.transport + mqtt + + + org.thingsboard.common + queue + + + com.sun.winsw + winsw + bin + exe + provided + + + org.springframework.boot + spring-boot-starter-test + test + + + junit + junit + test + + + org.mockito + mockito-all + test + + + + + ${pkg.name}-${project.version} + + + ${project.basedir}/src/main/resources + + + + + org.apache.maven.plugins + maven-resources-plugin + + + copy-conf + process-resources + + copy-resources + + + ${project.build.directory}/conf + + + src/main/resources + + logback.xml + + false + + + + + + copy-service-conf + process-resources + + copy-resources + + + ${project.build.directory}/conf + + + src/main/conf + true + + + + src/main/filters/unix.properties + + + + + copy-win-conf + process-resources + + copy-resources + + + ${pkg.win.dist}/conf + + + src/main/resources + + logback.xml + + false + + + src/main/conf + + tb-mqtt-transport.conf + + true + + + + src/main/filters/windows.properties + + + + + copy-control + process-resources + + copy-resources + + + ${project.build.directory}/control + + + src/main/scripts/control + true + + + + src/main/filters/unix.properties + + + + + copy-windows-control + process-resources + + copy-resources + + + ${pkg.win.dist} + + + src/main/scripts/windows + true + + + + src/main/filters/windows.properties + + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + copy-winsw-service + package + + copy + + + + + com.sun.winsw + winsw + bin + exe + service.exe + + + ${pkg.win.dist} + + + + + + org.apache.maven.plugins + maven-jar-plugin + + + **/logback.xml + + + + ThingsBoard MQTT Transport Service + ${project.version} + + + + + + org.springframework.boot + spring-boot-maven-plugin + + org.thingsboard.server.mqtt.ThingsboardMqttTransportApplication + boot + ZIP + true + true + + ${pkg.installFolder}/conf + ${pkg.unixLogFolder} + ${pkg.name}.out + ${pkg.name} + + + + + + repackage + + + + + + org.fortasoft + gradle-maven-plugin + + + build + buildDeb + buildRpm + + + -PprojectBuildDir=${project.build.directory} + -PprojectVersion=${project.version} + -PmainJar=${project.build.directory}/${project.build.finalName}-boot.${project.packaging} + -PpkgName=${pkg.name} + -PpkgInstallFolder=${pkg.installFolder} + -PpkgLogFolder=${pkg.unixLogFolder} + + + + + package + + invoke + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + ${pkg.name} + + src/main/assembly/windows.xml + + + + + assembly + package + + single + + + + + + org.apache.maven.plugins + maven-install-plugin + + ${project.build.directory}/${pkg.name}.deb + ${project.artifactId} + ${project.groupId} + ${project.version} + deb + deb + + + + install-deb + package + + install-file + + + + + + + + + jenkins + Jenkins Repository + http://repo.jenkins-ci.org/releases + + false + + + + diff --git a/transport/mqtt/src/main/assembly/windows.xml b/transport/mqtt/src/main/assembly/windows.xml new file mode 100644 index 0000000000..82da34ed13 --- /dev/null +++ b/transport/mqtt/src/main/assembly/windows.xml @@ -0,0 +1,71 @@ + + + windows + + + zip + + + + + + ${pkg.win.dist} + logs + + */** + + + + ${pkg.win.dist}/conf + conf + windows + + + + + + ${project.build.directory}/${project.build.finalName}-boot.${project.packaging} + lib + ${pkg.name}.jar + + + ${pkg.win.dist}/service.exe + + ${pkg.name}.exe + + + ${pkg.win.dist}/service.xml + + ${pkg.name}.xml + windows + + + ${pkg.win.dist}/install.bat + + windows + + + ${pkg.win.dist}/uninstall.bat + + windows + + + diff --git a/transport/mqtt/src/main/conf/logback.xml b/transport/mqtt/src/main/conf/logback.xml new file mode 100644 index 0000000000..f36469d5f2 --- /dev/null +++ b/transport/mqtt/src/main/conf/logback.xml @@ -0,0 +1,43 @@ + + + + + + + ${pkg.logFolder}/${pkg.name}.log + + ${pkg.logFolder}/${pkg.name}.%d{yyyy-MM-dd}.%i.log + 100MB + 30 + 3GB + + + %d{ISO8601} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + + + 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 0000000000..0afa91c18e --- /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 0000000000..8967278673 --- /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 0000000000..a6e48d91ba --- /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-transport/src/main/java/org/thingsboard/server/mqtt/ThingsboardMqttTransportApplication.java b/transport/mqtt/src/main/java/org/thingsboard/server/mqtt/ThingsboardMqttTransportApplication.java similarity index 100% rename from transport/mqtt-transport/src/main/java/org/thingsboard/server/mqtt/ThingsboardMqttTransportApplication.java rename to transport/mqtt/src/main/java/org/thingsboard/server/mqtt/ThingsboardMqttTransportApplication.java diff --git a/transport/mqtt-transport/src/main/resources/logback.xml b/transport/mqtt/src/main/resources/logback.xml similarity index 100% rename from transport/mqtt-transport/src/main/resources/logback.xml rename to transport/mqtt/src/main/resources/logback.xml diff --git a/transport/mqtt-transport/src/main/resources/tb-mqtt-transport.yml b/transport/mqtt/src/main/resources/tb-mqtt-transport.yml similarity index 100% rename from transport/mqtt-transport/src/main/resources/tb-mqtt-transport.yml rename to transport/mqtt/src/main/resources/tb-mqtt-transport.yml 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 0000000000..d4066c027b --- /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 0000000000..61865803c3 --- /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 0000000000..6be5959285 --- /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 0000000000..898d3efd5c --- /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 0000000000..8a7a88f7e0 --- /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 0000000000..8e1f8a2048 --- /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 0000000000..e19fc884c8 --- /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 0000000000..accb487b8e --- /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 0000000000..d456fc03c0 --- /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 0000000000..dba773663c --- /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 0000000000..f7b9d307c8 --- /dev/null +++ b/transport/mqtt/src/main/scripts/windows/service.xml @@ -0,0 +1,36 @@ + + ${pkg.name} + ${project.name} + ${project.description} + %BASE%\conf + ${pkg.winWrapperLogFolder} + rotate + + java + -Xloggc:%BASE%\logs\gc.log + -XX:+HeapDumpOnOutOfMemoryError + -XX:+PrintGCDetails + -XX:+PrintGCDateStamps + -XX:+PrintHeapAtGC + -XX:+PrintTenuringDistribution + -XX:+PrintGCApplicationStoppedTime + -XX:+UseGCLogFileRotation + -XX:NumberOfGCLogFiles=10 + -XX:GCLogFileSize=10M + -XX:-UseBiasedLocking + -XX:+UseTLAB + -XX:+ResizeTLAB + -XX:+PerfDisableSharedMem + -XX:+UseCondCardMark + -XX:CMSWaitDuration=10000 + -XX:+UseParNewGC + -XX:+UseConcMarkSweepGC + -XX:+CMSParallelRemarkEnabled + -XX:+CMSParallelInitialMarkEnabled + -XX:+CMSEdenChunksRecordAlways + -XX:CMSInitiatingOccupancyFraction=75 + -XX:+UseCMSInitiatingOccupancyOnly + -jar + %BASE%\lib\${pkg.name}.jar + + 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 0000000000..921e4c8395 --- /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 diff --git a/transport/pom.xml b/transport/pom.xml index 9a7c2e4690..b0790262b1 100644 --- a/transport/pom.xml +++ b/transport/pom.xml @@ -34,9 +34,9 @@ - http + - mqtt-transport + mqtt From 6fa86eeacabde2b2edaae31336daf0aa404e0cee Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Mon, 15 Oct 2018 18:24:51 +0300 Subject: [PATCH 18/26] Add MQTT transport Docker image. --- msa/docker/.env | 3 +- msa/docker/docker-compose.yml | 27 +++- msa/docker/haproxy/config/haproxy.cfg | 13 ++ msa/docker/tb-mqtt-transport.env | 6 + msa/docker/tb-node.env | 6 +- msa/pom.xml | 1 + msa/transport/mqtt/docker/Dockerfile | 31 ++++ msa/transport/mqtt/docker/logback.xml | 50 +++++++ .../mqtt/docker/start-tb-mqtt-transport.sh | 29 ++++ .../mqtt/docker/tb-mqtt-transport.conf | 23 +++ msa/transport/mqtt/pom.xml | 137 ++++++++++++++++++ msa/transport/pom.xml | 53 +++++++ 12 files changed, 373 insertions(+), 6 deletions(-) create mode 100644 msa/docker/tb-mqtt-transport.env create mode 100644 msa/transport/mqtt/docker/Dockerfile create mode 100644 msa/transport/mqtt/docker/logback.xml create mode 100755 msa/transport/mqtt/docker/start-tb-mqtt-transport.sh create mode 100644 msa/transport/mqtt/docker/tb-mqtt-transport.conf create mode 100644 msa/transport/mqtt/pom.xml create mode 100644 msa/transport/pom.xml diff --git a/msa/docker/.env b/msa/docker/.env index 111ff1e2b4..029503ac8e 100644 --- a/msa/docker/.env +++ b/msa/docker/.env @@ -4,7 +4,8 @@ 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 TB_VERSION=2.2.0-SNAPSHOT -KAFKA_TOPICS=js.eval.requests:100:1 +KAFKA_TOPICS=js.eval.requests:100:1,tb.transport.api.requests:30:1,tb.rule-engine:30:1 diff --git a/msa/docker/docker-compose.yml b/msa/docker/docker-compose.yml index 93c2ab08cd..f2ca2abd35 100644 --- a/msa/docker/docker-compose.yml +++ b/msa/docker/docker-compose.yml @@ -53,7 +53,6 @@ services: image: "${DOCKER_REPO}/${TB_NODE_DOCKER_NAME}:${TB_VERSION}" ports: - "8080" - - "1883:1883" - "5683:5683/udp" logging: driver: "json-file" @@ -72,6 +71,28 @@ 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-web-ui1: restart: always image: "${DOCKER_REPO}/${WEB_UI_DOCKER_NAME}:${TB_VERSION}" @@ -103,12 +124,16 @@ 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 diff --git a/msa/docker/haproxy/config/haproxy.cfg b/msa/docker/haproxy/config/haproxy.cfg index 5f174653f9..d334c9272b 100644 --- a/msa/docker/haproxy/config/haproxy.cfg +++ b/msa/docker/haproxy/config/haproxy.cfg @@ -49,6 +49,12 @@ frontend https_in default_backend tb-web-backend +frontend mqtt-in + mode tcp + bind *:${MQTT_PORT} + + default_backend tb-mqtt-backend + backend letsencrypt_http server letsencrypt_http_srv 127.0.0.1:8080 @@ -59,3 +65,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-mqtt-backend + balance leastconn + option tcp-check + option log-health-checks + server tbMqtt1 tb-mqtt-transport1:1883 check + server tbMqtt2 tb-mqtt-transport2:1883 check diff --git a/msa/docker/tb-mqtt-transport.env b/msa/docker/tb-mqtt-transport.env new file mode 100644 index 0000000000..5ada3f786a --- /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 diff --git a/msa/docker/tb-node.env b/msa/docker/tb-node.env index 874bf75d55..abeccb12eb 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 diff --git a/msa/pom.xml b/msa/pom.xml index 21ddb4dafe..59f954a1ed 100644 --- a/msa/pom.xml +++ b/msa/pom.xml @@ -40,6 +40,7 @@ js-executor web-ui tb-node + transport diff --git a/msa/transport/mqtt/docker/Dockerfile b/msa/transport/mqtt/docker/Dockerfile new file mode 100644 index 0000000000..5827c65229 --- /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"] diff --git a/msa/transport/mqtt/docker/logback.xml b/msa/transport/mqtt/docker/logback.xml new file mode 100644 index 0000000000..e9d86928bd --- /dev/null +++ b/msa/transport/mqtt/docker/logback.xml @@ -0,0 +1,50 @@ + + + + + + + /var/log/${pkg.name}/${pkg.name}.log + + /var/log/${pkg.name}/${pkg.name}.%d{yyyy-MM-dd}.%i.log + 100MB + 30 + 3GB + + + %d{ISO8601} [%thread] %-5level %logger{36} - %msg%n + + + + + + %d{ISO8601} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + + + + \ 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 0000000000..8fe06d272e --- /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 0000000000..6d4c54aad1 --- /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 diff --git a/msa/transport/mqtt/pom.xml b/msa/transport/mqtt/pom.xml new file mode 100644 index 0000000000..975f9fcdd0 --- /dev/null +++ b/msa/transport/mqtt/pom.xml @@ -0,0 +1,137 @@ + + + 4.0.0 + + org.thingsboard.msa + 2.2.0-SNAPSHOT + transport + + org.thingsboard.msa.transport + mqtt + pom + + ThingsBoard MQTT Transport Microservice + https://thingsboard.io + ThingsBoard MQTT Transport Microservice + + + UTF-8 + ${basedir}/../../.. + tb-mqtt-transport + tb-mqtt-transport + thingsboard + /var/log/${pkg.name} + /usr/share/${pkg.name} + + + + + org.thingsboard.transport + mqtt + ${project.version} + deb + deb + provided + + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + copy-tb-mqtt-transport-deb + package + + copy + + + + + org.thingsboard.transport + mqtt + deb + deb + ${pkg.name}.deb + ${project.build.directory} + + + + + + + + org.apache.maven.plugins + maven-resources-plugin + + + copy-docker-config + process-resources + + copy-resources + + + ${project.build.directory} + + + docker + true + + + + + + + + com.spotify + dockerfile-maven-plugin + + + build-docker-image + pre-integration-test + + build + + + + + ${dockerfile.skip} + ${docker.repo}/${docker.name} + ${project.version} + true + false + ${project.build.directory} + + + + + + + jenkins + Jenkins Repository + http://repo.jenkins-ci.org/releases + + false + + + + diff --git a/msa/transport/pom.xml b/msa/transport/pom.xml new file mode 100644 index 0000000000..7fc7b34c0d --- /dev/null +++ b/msa/transport/pom.xml @@ -0,0 +1,53 @@ + + + 4.0.0 + + org.thingsboard + 2.2.0-SNAPSHOT + msa + + org.thingsboard.msa + transport + pom + + ThingsBoard Transport Microservices + https://thingsboard.io + + + ${basedir}/../.. + + + + mqtt + + + + + + + com.spotify + dockerfile-maven-plugin + 1.4.5 + + + + + + From 68eabb9ec20c9e0abae1398e5998b6ccd7e88a40 Mon Sep 17 00:00:00 2001 From: Andrew Shvayka Date: Tue, 16 Oct 2018 13:20:40 +0300 Subject: [PATCH 19/26] TMP commit for HTTP transport --- application/pom.xml | 15 +- .../transport/LocalTransportService.java | 3 +- {transport => common/transport}/http/pom.xml | 10 +- .../transport/http/DeviceApiController.java | 174 ++++++++++++++---- .../transport/http/HttpTransportContext.java | 37 ++++ .../http/session/HttpSessionCtx.java | 0 .../transport/mqtt/MqttTransportContext.java | 38 +--- .../transport/mqtt/MqttTransportHandler.java | 2 +- .../mqtt/session/GatewaySessionHandler.java | 2 +- common/transport/pom.xml | 2 +- .../common/transport/TransportContext.java | 53 ++++++ .../common/transport/TransportService.java | 2 +- .../service/RemoteTransportService.java | 22 ++- .../transport/service/SessionMetaData.java | 17 ++ pom.xml | 2 +- transport/pom.xml | 1 - 16 files changed, 281 insertions(+), 99 deletions(-) rename {transport => common/transport}/http/pom.xml (90%) rename {transport => common/transport}/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java (60%) create mode 100644 common/transport/http/src/main/java/org/thingsboard/server/transport/http/HttpTransportContext.java rename {transport => common/transport}/http/src/main/java/org/thingsboard/server/transport/http/session/HttpSessionCtx.java (100%) create mode 100644 common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportContext.java create mode 100644 common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/SessionMetaData.java diff --git a/application/pom.xml b/application/pom.xml index 915a4ab5e1..f8c3219559 100644 --- a/application/pom.xml +++ b/application/pom.xml @@ -61,17 +61,17 @@ transport - - - - - - + + org.thingsboard.common.transport mqtt + + org.thingsboard.common.transport + http + org.thingsboard dao @@ -542,7 +542,8 @@ -PprojectBuildDir=${project.build.directory} -PprojectVersion=${project.version} - -PmainJar=${project.build.directory}/${project.build.finalName}-boot.${project.packaging} + -PmainJar=${project.build.directory}/${project.build.finalName}-boot.${project.packaging} + -PpkgName=${pkg.name} -PpkgInstallFolder=${pkg.installFolder} -PpkgLogFolder=${pkg.unixLogFolder} 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 index ba5cc074d4..a7590c9610 100644 --- a/application/src/main/java/org/thingsboard/server/service/transport/LocalTransportService.java +++ b/application/src/main/java/org/thingsboard/server/service/transport/LocalTransportService.java @@ -26,6 +26,7 @@ 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.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; @@ -175,7 +176,7 @@ public class LocalTransportService implements TransportService, RuleEngineTransp } @Override - public void registerSession(SessionInfoProto sessionInfo, SessionMsgListener listener) { + public void registerSession(SessionInfoProto sessionInfo, TransportProtos.SessionType sessionType, SessionMsgListener listener) { sessions.putIfAbsent(toId(sessionInfo), listener); //TODO: monitor sessions periodically: PING REQ/RESP, etc. } diff --git a/transport/http/pom.xml b/common/transport/http/pom.xml similarity index 90% rename from transport/http/pom.xml rename to common/transport/http/pom.xml index 996470832c..2ee11426a9 100644 --- a/transport/http/pom.xml +++ b/common/transport/http/pom.xml @@ -19,15 +19,15 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 - org.thingsboard + org.thingsboard.common 2.2.0-SNAPSHOT transport - org.thingsboard.transport + org.thingsboard.common.transport http jar - Thingsboard HTTP Transport + Thingsboard HTTP Transport Common https://thingsboard.io @@ -37,8 +37,8 @@ - org.thingsboard.common - transport + org.thingsboard.common.transport + transport-api org.springframework.boot diff --git a/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 similarity index 60% rename from transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java rename to common/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java index cd6d89998c..bca05c0459 100644 --- a/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 @@ -1,12 +1,12 @@ /** * 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 - * + *

+ * 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. @@ -17,64 +17,68 @@ package org.thingsboard.server.transport.http; 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.http.HttpStatus; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; +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.SessionMsgProcessor; -import org.thingsboard.server.common.transport.auth.DeviceAuthService; -import org.thingsboard.server.common.transport.quota.host.HostRequestsQuotaService; +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; +import org.thingsboard.server.gen.transport.TransportProtos.*; import org.thingsboard.server.transport.http.session.HttpSessionCtx; 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 -@ConditionalOnProperty(prefix = "transport.http", value = "enabled", havingValue = "true") +@ConditionalOnProperty(prefix = "transport.http", value = "enabled", havingValue = "true", matchIfMissing = true) @RequestMapping("/api/v1") @Slf4j public class DeviceApiController { - @Value("${http.request_timeout}") - private long defaultTimeout; - - @Autowired(required = false) - private SessionMsgProcessor processor; - - @Autowired(required = false) - private DeviceAuthService authService; - - @Autowired(required = false) - private HostRequestsQuotaService quotaService; + @Autowired + private HttpTransportContext transportContext; @RequestMapping(value = "/{deviceToken}/attributes", method = RequestMethod.GET, produces = "application/json") public DeferredResult getDeviceAttributes(@PathVariable("deviceToken") String deviceToken, @RequestParam(value = "clientKeys", required = false, defaultValue = "") String clientKeys, @RequestParam(value = "sharedKeys", required = false, defaultValue = "") String sharedKeys, HttpServletRequest httpRequest) { - DeferredResult responseWriter = new DeferredResult(); + DeferredResult responseWriter = new DeferredResult<>(); if (quotaExceeded(httpRequest, responseWriter)) { return responseWriter; } -// HttpSessionCtx ctx = getHttpSessionCtx(responseWriter); -// if (ctx.login(new DeviceTokenCredentials(deviceToken))) { -// GetAttributesRequest request; -// if (StringUtils.isEmpty(clientKeys) && StringUtils.isEmpty(sharedKeys)) { -// request = new BasicGetAttributesRequest(0); -// } else { -// Set clientKeySet = !StringUtils.isEmpty(clientKeys) ? new HashSet<>(Arrays.asList(clientKeys.split(","))) : null; -// Set sharedKeySet = !StringUtils.isEmpty(sharedKeys) ? new HashSet<>(Arrays.asList(sharedKeys.split(","))) : null; -// request = new BasicGetAttributesRequest(0, clientKeySet, sharedKeySet); -// } -// process(ctx, request); -// } else { -// responseWriter.setResult(new ResponseEntity<>(HttpStatus.UNAUTHORIZED)); -// } - + transportContext.getTransportService().process(ValidateDeviceTokenRequestMsg.newBuilder().setToken(deviceToken).build(), + new DeviceAuthCallback(transportContext, responseWriter, sessionInfo -> { + GetAttributeRequestMsg.Builder request = GetAttributeRequestMsg.newBuilder().setRequestId(0); + List clientKeySet = !StringUtils.isEmpty(clientKeys) ? Arrays.asList(clientKeys.split(",")) : null; + List 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.registerSession(sessionInfo, TransportProtos.SessionType.SYNC, new HttpSessionListener(transportContext, responseWriter)); + transportService.process(sessionInfo, request.build(), new SessionCloseOnErrorCallback(transportService, sessionInfo)); + })); return responseWriter; } @@ -199,7 +203,7 @@ public class DeviceApiController { // } private HttpSessionCtx getHttpSessionCtx(DeferredResult responseWriter) { - return getHttpSessionCtx(responseWriter, defaultTimeout); + return getHttpSessionCtx(responseWriter, transportContext.getDefaultTimeout()); } private HttpSessionCtx getHttpSessionCtx(DeferredResult responseWriter, long timeout) { @@ -213,7 +217,7 @@ public class DeviceApiController { // } private boolean quotaExceeded(HttpServletRequest request, DeferredResult responseWriter) { - if (quotaService.isQuotaExceeded(request.getRemoteAddr())) { + 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; @@ -221,4 +225,96 @@ public class DeviceApiController { return false; } + private static class DeviceAuthCallback implements TransportServiceCallback { + private final TransportContext transportContext; + private final DeferredResult responseWriter; + private final Consumer onSuccess; + + DeviceAuthCallback(TransportContext transportContext, DeferredResult responseWriter, Consumer 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 { + 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 HttpSessionListener implements SessionMsgListener { + + private final TransportContext transportContext; + private final DeferredResult responseWriter; + + HttpSessionListener(TransportContext transportContext, DeferredResult responseWriter) { + this.transportContext = transportContext; + 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) { + } + + @Override + public void onToDeviceRpcRequest(ToDeviceRpcRequestMsg toDeviceRequest) { + + } + + @Override + public void onToServerRpcResponse(ToServerRpcResponseMsg toServerResponse) { + + } + } } diff --git a/common/transport/http/src/main/java/org/thingsboard/server/transport/http/HttpTransportContext.java b/common/transport/http/src/main/java/org/thingsboard/server/transport/http/HttpTransportContext.java new file mode 100644 index 0000000000..87db06e66b --- /dev/null +++ b/common/transport/http/src/main/java/org/thingsboard/server/transport/http/HttpTransportContext.java @@ -0,0 +1,37 @@ +/** + * 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 lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.transport.TransportContext; + +/** + * Created by ashvayka on 04.10.18. + */ +@Slf4j +@ConditionalOnProperty(prefix = "transport.mqtt", value = "enabled", havingValue = "true", matchIfMissing = true) +@Component +public class HttpTransportContext extends TransportContext { + + @Getter + @Value("${http.request_timeout}") + private long defaultTimeout; + +} diff --git a/transport/http/src/main/java/org/thingsboard/server/transport/http/session/HttpSessionCtx.java b/common/transport/http/src/main/java/org/thingsboard/server/transport/http/session/HttpSessionCtx.java similarity index 100% rename from transport/http/src/main/java/org/thingsboard/server/transport/http/session/HttpSessionCtx.java rename to common/transport/http/src/main/java/org/thingsboard/server/transport/http/session/HttpSessionCtx.java 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 index 9ae42e8ca3..16f8981d57 100644 --- 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 @@ -24,6 +24,7 @@ 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.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; @@ -40,48 +41,21 @@ import java.util.concurrent.Executors; @Slf4j @ConditionalOnProperty(prefix = "transport.mqtt", value = "enabled", havingValue = "true", matchIfMissing = true) @Component -@Data -public class MqttTransportContext { - - private final ObjectMapper mapper = new ObjectMapper(); - - @Autowired - private TransportService transportService; +public class MqttTransportContext extends TransportContext { + @Getter @Autowired(required = false) private MqttSslHandlerProvider sslHandlerProvider; - @Autowired(required = false) - private HostRequestsQuotaService quotaService; - + @Getter @Autowired private MqttTransportAdaptor adaptor; - @Autowired - private TbNodeIdProvider nodeIdProvider; - + @Getter @Value("${transport.mqtt.netty.max_payload_size}") private Integer maxPayloadSize; - - private SslHandler sslHandler; - @Getter - private ExecutorService executor; - - @PostConstruct - public void init() { - executor = Executors.newCachedThreadPool(); - } - - @PreDestroy - public void stop() { - if (executor != null) { - executor.shutdownNow(); - } - } + private SslHandler sslHandler; - public String getNodeId() { - return nodeIdProvider.getNodeId(); - } } diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java index 6a5597bb11..30cc9c75a2 100644 --- a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java +++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java @@ -506,7 +506,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement .setTenantIdLSB(msg.getDeviceInfo().getTenantIdLSB()) .build(); transportService.process(sessionInfo, getSessionEventMsg(SessionEvent.OPEN), null); - transportService.registerSession(sessionInfo, this); + transportService.registerSession(sessionInfo, TransportProtos.SessionType.ASYNC, this); checkGatewaySession(); ctx.writeAndFlush(createMqttConnAckMsg(CONNECTION_ACCEPTED)); } 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 index f233695fb1..43f997830f 100644 --- 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 @@ -121,7 +121,7 @@ public class GatewaySessionHandler { transportService.process(deviceSessionInfo, MqttTransportHandler.getSessionEventMsg(TransportProtos.SessionEvent.OPEN), null); transportService.process(deviceSessionInfo, TransportProtos.SubscribeToRPCMsg.getDefaultInstance(), null); transportService.process(deviceSessionInfo, TransportProtos.SubscribeToAttributeUpdatesMsg.getDefaultInstance(), null); - transportService.registerSession(deviceSessionInfo, deviceSessionCtx); + transportService.registerSession(deviceSessionInfo, TransportProtos.SessionType.ASYNC, deviceSessionCtx); } future.set(devices.get(deviceName)); } diff --git a/common/transport/pom.xml b/common/transport/pom.xml index 69f64e6307..c9b47e876f 100644 --- a/common/transport/pom.xml +++ b/common/transport/pom.xml @@ -37,7 +37,7 @@ transport-api mqtt - + http 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 0000000000..f846cf0c38 --- /dev/null +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportContext.java @@ -0,0 +1,53 @@ +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 index c9a04c4daf..9bbc40c1b6 100644 --- 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 @@ -59,7 +59,7 @@ public interface TransportService { void process(SessionInfoProto sessionInfo, ToServerRpcRequestMsg msg, TransportServiceCallback callback); - void registerSession(SessionInfoProto sessionInfo, SessionMsgListener listener); + void registerSession(SessionInfoProto sessionInfo, TransportProtos.SessionType sessionType, SessionMsgListener listener); void deregisterSession(SessionInfoProto sessionInfo); 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 index ab52d41366..eb6dbc5cb2 100644 --- 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 @@ -1,12 +1,12 @@ /** * 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 - * + *

+ * 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. @@ -85,7 +85,7 @@ public class RemoteTransportService implements TransportService { @Value("${kafka.transport_api.response_auto_commit_interval}") private int autoCommitInterval; - private ConcurrentMap sessions = new ConcurrentHashMap<>(); + private ConcurrentMap sessions = new ConcurrentHashMap<>(); @Autowired private TbKafkaSettings kafkaSettings; @@ -159,8 +159,9 @@ public class RemoteTransportService implements TransportService { if (toTransportMsg.hasToDeviceSessionMsg()) { DeviceActorToTransportMsg toSessionMsg = toTransportMsg.getToDeviceSessionMsg(); UUID sessionId = new UUID(toSessionMsg.getSessionIdMSB(), toSessionMsg.getSessionIdLSB()); - SessionMsgListener listener = sessions.get(sessionId); - if (listener != null) { + SessionMetaData md = sessions.get(sessionId); + if (md != null) { + SessionMsgListener listener = md.getListener(); transportCallbackExecutor.submit(() -> { if (toSessionMsg.hasGetAttributesResponse()) { listener.onGetAttributesResponse(toSessionMsg.getGetAttributesResponse()); @@ -178,6 +179,9 @@ public class RemoteTransportService implements TransportService { listener.onToServerRpcResponse(toSessionMsg.getToServerResponse()); } }); + if (md.getSessionType() == SessionType.SYNC) { + deregisterSession(md.getSessionInfo()); + } } else { //TODO: should we notify the device actor about missed session? log.debug("[{}] Missing session.", sessionId); @@ -311,8 +315,8 @@ public class RemoteTransportService implements TransportService { } @Override - public void registerSession(SessionInfoProto sessionInfo, SessionMsgListener listener) { - sessions.putIfAbsent(toId(sessionInfo), listener); + public void registerSession(SessionInfoProto sessionInfo, SessionType sessionType, SessionMsgListener listener) { + sessions.putIfAbsent(toId(sessionInfo), new SessionMetaData(sessionInfo, sessionType, listener)); //TODO: monitor sessions periodically: PING REQ/RESP, etc. } diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/SessionMetaData.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/SessionMetaData.java new file mode 100644 index 0000000000..dd52f77ca6 --- /dev/null +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/SessionMetaData.java @@ -0,0 +1,17 @@ +package org.thingsboard.server.common.transport.service; + +import lombok.Data; +import org.thingsboard.server.common.transport.SessionMsgListener; +import org.thingsboard.server.gen.transport.TransportProtos; + +/** + * Created by ashvayka on 15.10.18. + */ +@Data +public class SessionMetaData { + + private final TransportProtos.SessionInfoProto sessionInfo; + private final TransportProtos.SessionType sessionType; + private final SessionMsgListener listener; + +} diff --git a/pom.xml b/pom.xml index e999a4c3f8..f4c2957b08 100755 --- a/pom.xml +++ b/pom.xml @@ -365,7 +365,7 @@ ${project.version} - org.thingsboard.transport + org.thingsboard.common.transport http ${project.version} diff --git a/transport/pom.xml b/transport/pom.xml index 9a7c2e4690..96a5a08304 100644 --- a/transport/pom.xml +++ b/transport/pom.xml @@ -34,7 +34,6 @@ - http mqtt-transport From 823b6aa7a722b6a992dfcca9d63bd78f38c9b4d9 Mon Sep 17 00:00:00 2001 From: Andrew Shvayka Date: Wed, 17 Oct 2018 18:59:52 +0300 Subject: [PATCH 20/26] HTTP transport implementation --- .../transport/LocalTransportService.java | 63 +----- .../src/main/resources/thingsboard.yml | 2 +- .../transport/http/DeviceApiController.java | 193 +++++++++--------- .../transport/http/HttpTransportContext.java | 4 +- .../http/session/HttpSessionCtx.java | 161 --------------- .../transport/mqtt/MqttTransportContext.java | 2 + .../transport/mqtt/MqttTransportHandler.java | 10 +- .../mqtt/session/GatewaySessionHandler.java | 2 +- .../common/transport/TransportContext.java | 15 ++ .../common/transport/TransportService.java | 12 +- .../service/AbstractTransportService.java | 101 +++++++++ .../service/RemoteTransportService.java | 71 +------ .../transport/service/SessionMetaData.java | 15 ++ 13 files changed, 268 insertions(+), 383 deletions(-) delete mode 100644 common/transport/http/src/main/java/org/thingsboard/server/transport/http/session/HttpSessionCtx.java create mode 100644 common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/AbstractTransportService.java 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 index a7590c9610..b8001725d7 100644 --- a/application/src/main/java/org/thingsboard/server/service/transport/LocalTransportService.java +++ b/application/src/main/java/org/thingsboard/server/service/transport/LocalTransportService.java @@ -1,12 +1,12 @@ /** * 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 - * + *

+ * 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. @@ -26,6 +26,7 @@ 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; @@ -56,6 +57,8 @@ 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; @@ -67,11 +70,7 @@ import java.util.function.Consumer; @Slf4j @Service @ConditionalOnProperty(prefix = "transport", value = "type", havingValue = "local") -public class LocalTransportService implements TransportService, RuleEngineTransportService { - - private ConcurrentMap sessions = new ConcurrentHashMap<>(); - - private ExecutorService transportCallbackExecutor; +public class LocalTransportService extends AbstractTransportService implements RuleEngineTransportService { @Autowired private TransportApiService transportApiService; @@ -89,14 +88,12 @@ public class LocalTransportService implements TransportService, RuleEngineTransp @PostConstruct public void init() { - this.transportCallbackExecutor = new ThreadPoolExecutor(0, 100, 60L, TimeUnit.SECONDS, new SynchronousQueue<>()); + super.init(); } @PreDestroy public void destroy() { - if (transportCallbackExecutor != null) { - transportCallbackExecutor.shutdownNow(); - } + super.destroy(); } @Override @@ -175,21 +172,6 @@ public class LocalTransportService implements TransportService, RuleEngineTransp forwardToDeviceActor(TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setToServerRPCCallRequest(msg).build(), callback); } - @Override - public void registerSession(SessionInfoProto sessionInfo, TransportProtos.SessionType sessionType, SessionMsgListener listener) { - sessions.putIfAbsent(toId(sessionInfo), listener); - //TODO: monitor sessions periodically: PING REQ/RESP, etc. - } - - @Override - public void deregisterSession(SessionInfoProto sessionInfo) { - sessions.remove(toId(sessionInfo)); - } - - private UUID toId(SessionInfoProto sessionInfo) { - return new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB()); - } - @Override public void process(String nodeId, DeviceActorToTransportMsg msg) { process(nodeId, msg, null, null); @@ -197,30 +179,7 @@ public class LocalTransportService implements TransportService, RuleEngineTransp @Override public void process(String nodeId, DeviceActorToTransportMsg msg, Runnable onSuccess, Consumer onFailure) { - UUID sessionId = new UUID(msg.getSessionIdMSB(), msg.getSessionIdLSB()); - SessionMsgListener listener = sessions.get(sessionId); - if (listener != null) { - transportCallbackExecutor.submit(() -> { - if (msg.hasGetAttributesResponse()) { - listener.onGetAttributesResponse(msg.getGetAttributesResponse()); - } - if (msg.hasAttributeUpdateNotification()) { - listener.onAttributeUpdate(msg.getAttributeUpdateNotification()); - } - if (msg.hasSessionCloseNotification()) { - listener.onRemoteSessionCloseCommand(msg.getSessionCloseNotification()); - } - if (msg.hasToDeviceRequest()) { - listener.onToDeviceRpcRequest(msg.getToDeviceRequest()); - } - if (msg.hasToServerResponse()) { - listener.onToServerRpcResponse(msg.getToServerResponse()); - } - }); - } else { - //TODO: should we notify the device actor about missed session? - log.debug("[{}] Missing session.", sessionId); - } + processToTransportMsg(msg); if (onSuccess != null) { onSuccess.run(); } diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index ca509443bf..0eb624b181 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -439,7 +439,7 @@ transport: # Local MQTT transport parameters mqtt: # Enable/disable mqtt transport protocol. - enabled: "${MQTT_ENABLED:true}" + enabled: "${MQTT_ENABLED:false}" bind_address: "${MQTT_BIND_ADDRESS:0.0.0.0}" bind_port: "${MQTT_BIND_PORT:1883}" adaptor: "${MQTT_ADAPTOR_NAME:JsonMqttAdaptor}" 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 index bca05c0459..8d579d4eb2 100644 --- 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 @@ -15,6 +15,8 @@ */ 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.ConditionalOnProperty; @@ -33,9 +35,20 @@ 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; -import org.thingsboard.server.gen.transport.TransportProtos.*; -import org.thingsboard.server.transport.http.session.HttpSessionCtx; +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; @@ -76,7 +89,7 @@ public class DeviceApiController { request.addAllSharedAttributeNames(sharedKeySet); } TransportService transportService = transportContext.getTransportService(); - transportService.registerSession(sessionInfo, TransportProtos.SessionType.SYNC, new HttpSessionListener(transportContext, responseWriter)); + transportService.registerSyncSession(sessionInfo, new HttpSessionListener(responseWriter), transportContext.getDefaultTimeout()); transportService.process(sessionInfo, request.build(), new SessionCloseOnErrorCallback(transportService, sessionInfo)); })); return responseWriter; @@ -85,20 +98,16 @@ public class DeviceApiController { @RequestMapping(value = "/{deviceToken}/attributes", method = RequestMethod.POST) public DeferredResult postDeviceAttributes(@PathVariable("deviceToken") String deviceToken, @RequestBody String json, HttpServletRequest request) { - DeferredResult responseWriter = new DeferredResult(); + DeferredResult responseWriter = new DeferredResult<>(); if (quotaExceeded(request, responseWriter)) { return responseWriter; } -// HttpSessionCtx ctx = getHttpSessionCtx(responseWriter); -// if (ctx.login(new DeviceTokenCredentials(deviceToken))) { -// try { -// process(ctx, JsonConverter.convertToAttributes(new JsonParser().parse(json))); -// } catch (IllegalStateException | JsonSyntaxException ex) { -// responseWriter.setResult(new ResponseEntity<>(HttpStatus.BAD_REQUEST)); -// } -// } else { -// responseWriter.setResult(new ResponseEntity<>(HttpStatus.UNAUTHORIZED)); -// } + 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; } @@ -109,26 +118,33 @@ public class DeviceApiController { if (quotaExceeded(request, responseWriter)) { return responseWriter; } - HttpSessionCtx ctx = getHttpSessionCtx(responseWriter); -// if (ctx.login(new DeviceTokenCredentials(deviceToken))) { -// try { -// process(ctx, JsonConverter.convertToTelemetry(new JsonParser().parse(json))); -// } catch (IllegalStateException | JsonSyntaxException ex) { -// responseWriter.setResult(new ResponseEntity<>(HttpStatus.BAD_REQUEST)); -// } -// } else { -// responseWriter.setResult(new ResponseEntity<>(HttpStatus.UNAUTHORIZED)); -// } + 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 subscribeToCommands(@PathVariable("deviceToken") String deviceToken, @RequestParam(value = "timeout", required = false, defaultValue = "0") long timeout, - HttpServletRequest request) { + HttpServletRequest httpRequest) { + DeferredResult 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 subscribe(deviceToken, timeout, new RpcSubscribeMsg(), request); - return null; + })); + return responseWriter; } @RequestMapping(value = "/{deviceToken}/rpc/{requestId}", method = RequestMethod.POST) @@ -139,17 +155,11 @@ public class DeviceApiController { if (quotaExceeded(request, responseWriter)) { return responseWriter; } -// HttpSessionCtx ctx = getHttpSessionCtx(responseWriter); -// if (ctx.login(new DeviceTokenCredentials(deviceToken))) { -// try { -// JsonObject response = new JsonParser().parse(json).getAsJsonObject(); -// process(ctx, new ToDeviceRpcResponseMsg(requestId, response.toString())); -// } catch (IllegalStateException | JsonSyntaxException ex) { -// responseWriter.setResult(new ResponseEntity<>(HttpStatus.BAD_REQUEST)); -// } -// } else { -// responseWriter.setResult(new ResponseEntity<>(HttpStatus.UNAUTHORIZED)); -// } + 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; } @@ -160,19 +170,16 @@ public class DeviceApiController { if (quotaExceeded(httpRequest, responseWriter)) { return responseWriter; } - HttpSessionCtx ctx = getHttpSessionCtx(responseWriter); -// if (ctx.login(new DeviceTokenCredentials(deviceToken))) { -// try { -// JsonObject request = new JsonParser().parse(json).getAsJsonObject(); -// process(ctx, new ToServerRpcRequestMsg(0, -// request.get("method").getAsString(), -// request.get("params").toString())); -// } catch (IllegalStateException | JsonSyntaxException ex) { -// responseWriter.setResult(new ResponseEntity<>(HttpStatus.BAD_REQUEST)); -// } -// } else { -// responseWriter.setResult(new ResponseEntity<>(HttpStatus.UNAUTHORIZED)); -// } + 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; } @@ -180,49 +187,30 @@ public class DeviceApiController { public DeferredResult subscribeToAttributes(@PathVariable("deviceToken") String deviceToken, @RequestParam(value = "timeout", required = false, defaultValue = "0") long timeout, HttpServletRequest httpRequest) { - return null; -// return subscribe(deviceToken, timeout, new AttributesSubscribeMsg(), httpRequest); - } - -// private DeferredResult subscribe(String deviceToken, long timeout, FromDeviceMsg msg, HttpServletRequest httpRequest) { -// DeferredResult responseWriter = new DeferredResult(); -// if (quotaExceeded(httpRequest, responseWriter)) { -// return responseWriter; -// } -// HttpSessionCtx ctx = getHttpSessionCtx(responseWriter, timeout); -// if (ctx.login(new DeviceTokenCredentials(deviceToken))) { -// try { -// process(ctx, msg); -// } catch (IllegalStateException | JsonSyntaxException ex) { -// responseWriter.setResult(new ResponseEntity<>(HttpStatus.BAD_REQUEST)); -// } -// } else { -// responseWriter.setResult(new ResponseEntity<>(HttpStatus.UNAUTHORIZED)); -// } -// return responseWriter; -// } - - private HttpSessionCtx getHttpSessionCtx(DeferredResult responseWriter) { - return getHttpSessionCtx(responseWriter, transportContext.getDefaultTimeout()); - } + DeferredResult 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)); - private HttpSessionCtx getHttpSessionCtx(DeferredResult responseWriter, long timeout) { - return null; -// return new HttpSessionCtx(processor, authService, responseWriter, timeout != 0 ? timeout : defaultTimeout); + })); + return responseWriter; } -// private void process(HttpSessionCtx ctx, FromDeviceMsg request) { -// AdaptorToSessionActorMsg msg = new BasicAdaptorToSessionActorMsg(ctx, request); -//// processor.process(new BasicTransportToDeviceSessionActorMsg(ctx.getDevice(), msg)); -// } - private boolean quotaExceeded(HttpServletRequest request, DeferredResult 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; } - return false; } private static class DeviceAuthCallback implements TransportServiceCallback { @@ -274,7 +262,6 @@ public class DeviceApiController { @Override public void onSuccess(Void msg) { - } @Override @@ -283,13 +270,30 @@ public class DeviceApiController { } } + private static class HttpOkCallback implements TransportServiceCallback { + private final DeferredResult responseWriter; + + public HttpOkCallback(DeferredResult 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 TransportContext transportContext; private final DeferredResult responseWriter; - HttpSessionListener(TransportContext transportContext, DeferredResult responseWriter) { - this.transportContext = transportContext; + HttpSessionListener(DeferredResult responseWriter) { this.responseWriter = responseWriter; } @@ -305,16 +309,17 @@ public class DeviceApiController { @Override public void onRemoteSessionCloseCommand(SessionCloseNotificationProto sessionCloseNotification) { + responseWriter.setResult(new ResponseEntity<>(HttpStatus.REQUEST_TIMEOUT)); } @Override - public void onToDeviceRpcRequest(ToDeviceRpcRequestMsg toDeviceRequest) { - + public void onToDeviceRpcRequest(ToDeviceRpcRequestMsg msg) { + responseWriter.setResult(new ResponseEntity<>(JsonConverter.toJson(msg, true).toString(), HttpStatus.OK)); } @Override - public void onToServerRpcResponse(ToServerRpcResponseMsg toServerResponse) { - + public void onToServerRpcResponse(ToServerRpcResponseMsg msg) { + responseWriter.setResult(new ResponseEntity<>(JsonConverter.toJson(msg).toString(), HttpStatus.OK)); } } } diff --git a/common/transport/http/src/main/java/org/thingsboard/server/transport/http/HttpTransportContext.java b/common/transport/http/src/main/java/org/thingsboard/server/transport/http/HttpTransportContext.java index 87db06e66b..66654c1bed 100644 --- a/common/transport/http/src/main/java/org/thingsboard/server/transport/http/HttpTransportContext.java +++ b/common/transport/http/src/main/java/org/thingsboard/server/transport/http/HttpTransportContext.java @@ -26,12 +26,12 @@ import org.thingsboard.server.common.transport.TransportContext; * Created by ashvayka on 04.10.18. */ @Slf4j -@ConditionalOnProperty(prefix = "transport.mqtt", value = "enabled", havingValue = "true", matchIfMissing = true) +@ConditionalOnProperty(prefix = "transport.http", value = "enabled", havingValue = "true", matchIfMissing = true) @Component public class HttpTransportContext extends TransportContext { @Getter - @Value("${http.request_timeout}") + @Value("${transport.http.request_timeout}") private long defaultTimeout; } diff --git a/common/transport/http/src/main/java/org/thingsboard/server/transport/http/session/HttpSessionCtx.java b/common/transport/http/src/main/java/org/thingsboard/server/transport/http/session/HttpSessionCtx.java deleted file mode 100644 index 27cd4f1dd4..0000000000 --- a/common/transport/http/src/main/java/org/thingsboard/server/transport/http/session/HttpSessionCtx.java +++ /dev/null @@ -1,161 +0,0 @@ -/** - * 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.session; - -import lombok.extern.slf4j.Slf4j; -import org.thingsboard.server.common.transport.session.DeviceAwareSessionContext; - -import java.util.UUID; - -/** - * @author Andrew Shvayka - */ -@Slf4j -public class HttpSessionCtx extends DeviceAwareSessionContext { - - public HttpSessionCtx(UUID sessionId) { - super(sessionId); - } - - @Override - public int nextMsgId() { - return 0; - } - - // private final SessionId sessionId; -// private final long timeout; -// private final DeferredResult responseWriter; -// -// public HttpSessionCtx(SessionMsgProcessor processor, DeviceAuthService authService, DeferredResult responseWriter, long timeout) { -// super(); -// this.sessionId = new HttpSessionId(); -// this.responseWriter = responseWriter; -// this.timeout = timeout; -// } -// -// @Override -// public SessionType getSessionType() { -// return SessionType.SYNC; -// } -// -// @Override -// public void onMsg(SessionActorToAdaptorMsg source) throws SessionException { -// ToDeviceMsg msg = source.getMsg(); -// switch (msg.getSessionMsgType()) { -// case GET_ATTRIBUTES_RESPONSE: -// reply((GetAttributesResponse) msg); -// return; -// case STATUS_CODE_RESPONSE: -// reply((StatusCodeResponse) msg); -// return; -// case ATTRIBUTES_UPDATE_NOTIFICATION: -// reply((AttributesUpdateNotification) msg); -// return; -// case TO_DEVICE_RPC_REQUEST: -// reply((ToDeviceRpcRequestMsg) msg); -// return; -// case TO_SERVER_RPC_RESPONSE: -// reply((ToServerRpcResponseMsg) msg); -// return; -// case RULE_ENGINE_ERROR: -// reply((RuleEngineErrorMsg) msg); -// return; -// default: -// break; -// } -// } -// -// private void reply(RuleEngineErrorMsg msg) { -// HttpStatus status = HttpStatus.INTERNAL_SERVER_ERROR; -// switch (msg.getError()) { -// case QUEUE_PUT_TIMEOUT: -// status = HttpStatus.REQUEST_TIMEOUT; -// break; -// default: -// if (msg.getInSessionMsgType() == SessionMsgType.TO_SERVER_RPC_REQUEST) { -// status = HttpStatus.BAD_REQUEST; -// } -// break; -// } -// responseWriter.setResult(new ResponseEntity<>(JsonConverter.toErrorJson(msg.getErrorMsg()).toString(), status)); -// } -// -// private void reply(ResponseMsg msg, Consumer f) { -// Optional msgError = msg.getError(); -// if (!msgError.isPresent()) { -// Optional msgData = msg.getData(); -// if (msgData.isPresent()) { -// f.accept(msgData.get()); -// } -// } else { -// Exception e = msgError.get(); -// responseWriter.setResult(new ResponseEntity<>(e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR)); -// } -// } -// -// private void reply(ToDeviceRpcRequestMsg msg) { -// responseWriter.setResult(new ResponseEntity<>(JsonConverter.toJson(msg, true).toString(), HttpStatus.OK)); -// } -// -// private void reply(ToServerRpcResponseMsg msg) { -//// responseWriter.setResult(new ResponseEntity<>(JsonConverter.toJson(msg).toString(), HttpStatus.OK)); -// } -// -// private void reply(AttributesUpdateNotification msg) { -// responseWriter.setResult(new ResponseEntity<>(JsonConverter.toJson(msg.getData(), false).toString(), HttpStatus.OK)); -// } -// -// private void reply(GetAttributesResponse msg) { -// reply(msg, payload -> { -// if (payload.getClientAttributes().isEmpty() && payload.getSharedAttributes().isEmpty()) { -// responseWriter.setResult(new ResponseEntity<>(HttpStatus.NOT_FOUND)); -// } else { -// JsonObject result = JsonConverter.toJson(payload, false); -// responseWriter.setResult(new ResponseEntity<>(result.toString(), HttpStatus.OK)); -// } -// }); -// } -// -// private void reply(StatusCodeResponse msg) { -// reply(msg, payload -> { -// if (payload == 0) { -// responseWriter.setResult(new ResponseEntity<>(HttpStatus.OK)); -// } else { -// responseWriter.setResult(new ResponseEntity<>(HttpStatus.valueOf(payload))); -// } -// }); -// } -// -// @Override -// public void onMsg(SessionCtrlMsg msg) throws SessionException { -// //Do nothing -// } -// -// @Override -// public boolean isClosed() { -// return false; -// } -// -// @Override -// public long getTimeout() { -// return timeout; -// } -// -// @Override -// public SessionId getSessionId() { -// return sessionId; -// } -} 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 index 16f8981d57..91580bff7d 100644 --- 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 @@ -19,6 +19,7 @@ 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; @@ -56,6 +57,7 @@ public class MqttTransportContext extends TransportContext { private Integer maxPayloadSize; @Getter + @Setter private SslHandler sslHandler; } diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java index 30cc9c75a2..d739824aa5 100644 --- a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java +++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java @@ -1,12 +1,12 @@ /** * 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 - * + *

+ * 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. @@ -506,7 +506,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement .setTenantIdLSB(msg.getDeviceInfo().getTenantIdLSB()) .build(); transportService.process(sessionInfo, getSessionEventMsg(SessionEvent.OPEN), null); - transportService.registerSession(sessionInfo, TransportProtos.SessionType.ASYNC, this); + transportService.registerAsyncSession(sessionInfo, this); checkGatewaySession(); ctx.writeAndFlush(createMqttConnAckMsg(CONNECTION_ACCEPTED)); } 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 index 43f997830f..d6000591a8 100644 --- 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 @@ -121,7 +121,7 @@ public class GatewaySessionHandler { transportService.process(deviceSessionInfo, MqttTransportHandler.getSessionEventMsg(TransportProtos.SessionEvent.OPEN), null); transportService.process(deviceSessionInfo, TransportProtos.SubscribeToRPCMsg.getDefaultInstance(), null); transportService.process(deviceSessionInfo, TransportProtos.SubscribeToAttributeUpdatesMsg.getDefaultInstance(), null); - transportService.registerSession(deviceSessionInfo, TransportProtos.SessionType.ASYNC, deviceSessionCtx); + transportService.registerAsyncSession(deviceSessionInfo, deviceSessionCtx); } future.set(devices.get(deviceName)); } 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 index f846cf0c38..ab429829e4 100644 --- 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 @@ -1,3 +1,18 @@ +/** + * 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; 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 index 9bbc40c1b6..5d0c14d78c 100644 --- 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 @@ -1,12 +1,12 @@ /** * 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 - * + *

+ * 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. @@ -59,7 +59,9 @@ public interface TransportService { void process(SessionInfoProto sessionInfo, ToServerRpcRequestMsg msg, TransportServiceCallback callback); - void registerSession(SessionInfoProto sessionInfo, TransportProtos.SessionType sessionType, SessionMsgListener listener); + 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/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 0000000000..1bf4dd1cbf --- /dev/null +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/AbstractTransportService.java @@ -0,0 +1,101 @@ +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 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 index eb6dbc5cb2..1442cc5f21 100644 --- 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 @@ -1,12 +1,12 @@ /** * 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 - *

+ * + * 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. @@ -26,6 +26,7 @@ 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; @@ -62,7 +63,7 @@ import java.util.concurrent.Executors; @ConditionalOnProperty(prefix = "transport", value = "type", havingValue = "remote", matchIfMissing = true) @Service @Slf4j -public class RemoteTransportService implements TransportService { +public class RemoteTransportService extends AbstractTransportService { @Value("${kafka.rule_engine.topic}") private String ruleEngineTopic; @@ -85,16 +86,12 @@ public class RemoteTransportService implements TransportService { @Value("${kafka.transport_api.response_auto_commit_interval}") private int autoCommitInterval; - private ConcurrentMap sessions = new ConcurrentHashMap<>(); - @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 ExecutorService transportCallbackExecutor; - private TbKafkaRequestTemplate transportApiTemplate; private TBKafkaProducerTemplate ruleEngineProducer; private TBKafkaConsumerTemplate mainConsumer; @@ -105,7 +102,7 @@ public class RemoteTransportService implements TransportService { @PostConstruct public void init() { - this.transportCallbackExecutor = Executors.newCachedThreadPool(); + super.init(); TBKafkaProducerTemplate.TBKafkaProducerTemplateBuilder requestBuilder = TBKafkaProducerTemplate.builder(); requestBuilder.settings(kafkaSettings); @@ -157,36 +154,7 @@ public class RemoteTransportService implements TransportService { try { ToTransportMsg toTransportMsg = mainConsumer.decode(record); if (toTransportMsg.hasToDeviceSessionMsg()) { - DeviceActorToTransportMsg toSessionMsg = toTransportMsg.getToDeviceSessionMsg(); - 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() == SessionType.SYNC) { - deregisterSession(md.getSessionInfo()); - } - } else { - //TODO: should we notify the device actor about missed session? - log.debug("[{}] Missing session.", sessionId); - } - + processToTransportMsg(toTransportMsg.getToDeviceSessionMsg()); } } catch (Throwable e) { log.warn("Failed to process the notification.", e); @@ -206,13 +174,11 @@ public class RemoteTransportService implements TransportService { @PreDestroy public void destroy() { + super.destroy(); stopped = true; if (transportApiTemplate != null) { transportApiTemplate.stop(); } - if (transportCallbackExecutor != null) { - transportCallbackExecutor.shutdownNow(); - } if (mainConsumer != null) { mainConsumer.unsubscribe(); } @@ -314,25 +280,6 @@ public class RemoteTransportService implements TransportService { send(sessionInfo, toRuleEngineMsg, callback); } - @Override - public void registerSession(SessionInfoProto sessionInfo, SessionType sessionType, SessionMsgListener listener) { - sessions.putIfAbsent(toId(sessionInfo), new SessionMetaData(sessionInfo, sessionType, listener)); - //TODO: monitor sessions periodically: PING REQ/RESP, etc. - } - - @Override - public void deregisterSession(SessionInfoProto sessionInfo) { - sessions.remove(toId(sessionInfo)); - } - - private UUID toId(SessionInfoProto sessionInfo) { - return new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB()); - } - - private String getRoutingKey(SessionInfoProto sessionInfo) { - return new UUID(sessionInfo.getDeviceIdMSB(), sessionInfo.getDeviceIdLSB()).toString(); - } - private static class TransportCallbackAdaptor implements Callback { private final TransportServiceCallback callback; diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/SessionMetaData.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/SessionMetaData.java index dd52f77ca6..1de57114e4 100644 --- a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/SessionMetaData.java +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/SessionMetaData.java @@ -1,3 +1,18 @@ +/** + * 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.Data; From d7c4ff6bb023850b83ad428941d20dace4da9c93 Mon Sep 17 00:00:00 2001 From: Andrew Shvayka Date: Wed, 17 Oct 2018 20:49:35 +0300 Subject: [PATCH 21/26] HTTP transport implementation --- .../transport/LocalTransportService.java | 8 +- .../RemoteRuleEngineTransportService.java | 2 - .../src/main/resources/thingsboard.yml | 17 +- common/pom.xml | 2 +- common/transport/http/pom.xml | 2 +- .../transport/http/DeviceApiController.java | 11 +- .../transport/http/HttpTransportContext.java | 13 +- .../mqtt/MqttSslHandlerProvider.java | 2 + .../transport/mqtt/MqttTransportContext.java | 3 +- .../transport/mqtt/MqttTransportHandler.java | 8 +- .../transport/mqtt/MqttTransportService.java | 3 +- .../common/transport/TransportService.java | 8 +- .../service/AbstractTransportService.java | 15 + .../service/RemoteTransportService.java | 3 +- transport/http/build.gradle | 140 +++++++ transport/http/pom.xml | 341 ++++++++++++++++++ transport/http/src/main/assembly/windows.xml | 71 ++++ transport/http/src/main/conf/logback.xml | 43 +++ .../http/src/main/conf/tb-http-transport.conf | 23 ++ .../http/src/main/filters/unix.properties | 1 + .../http/src/main/filters/windows.properties | 2 + .../ThingsboardHttpTransportApplication.java | 48 +++ transport/http/src/main/resources/logback.xml | 34 ++ .../src/main/resources/tb-http-transport.yml | 69 ++++ .../src/main/scripts/control/deb/postinst | 6 + .../http/src/main/scripts/control/deb/postrm | 3 + .../http/src/main/scripts/control/deb/preinst | 18 + .../http/src/main/scripts/control/deb/prerm | 5 + .../src/main/scripts/control/rpm/postinst | 9 + .../http/src/main/scripts/control/rpm/postrm | 6 + .../http/src/main/scripts/control/rpm/preinst | 6 + .../http/src/main/scripts/control/rpm/prerm | 6 + .../scripts/control/tb-http-transport.service | 11 + .../http/src/main/scripts/windows/install.bat | 87 +++++ .../http/src/main/scripts/windows/service.xml | 36 ++ .../src/main/scripts/windows/uninstall.bat | 9 + transport/pom.xml | 17 +- 37 files changed, 1041 insertions(+), 47 deletions(-) create mode 100644 transport/http/build.gradle create mode 100644 transport/http/pom.xml create mode 100644 transport/http/src/main/assembly/windows.xml create mode 100644 transport/http/src/main/conf/logback.xml create mode 100644 transport/http/src/main/conf/tb-http-transport.conf create mode 100644 transport/http/src/main/filters/unix.properties create mode 100644 transport/http/src/main/filters/windows.properties create mode 100644 transport/http/src/main/java/org/thingsboard/server/mqtt/ThingsboardHttpTransportApplication.java create mode 100644 transport/http/src/main/resources/logback.xml create mode 100644 transport/http/src/main/resources/tb-http-transport.yml create mode 100644 transport/http/src/main/scripts/control/deb/postinst create mode 100644 transport/http/src/main/scripts/control/deb/postrm create mode 100644 transport/http/src/main/scripts/control/deb/preinst create mode 100644 transport/http/src/main/scripts/control/deb/prerm create mode 100644 transport/http/src/main/scripts/control/rpm/postinst create mode 100644 transport/http/src/main/scripts/control/rpm/postrm create mode 100644 transport/http/src/main/scripts/control/rpm/preinst create mode 100644 transport/http/src/main/scripts/control/rpm/prerm create mode 100644 transport/http/src/main/scripts/control/tb-http-transport.service create mode 100644 transport/http/src/main/scripts/windows/install.bat create mode 100644 transport/http/src/main/scripts/windows/service.xml create mode 100644 transport/http/src/main/scripts/windows/uninstall.bat 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 index b8001725d7..d670db3c19 100644 --- a/application/src/main/java/org/thingsboard/server/service/transport/LocalTransportService.java +++ b/application/src/main/java/org/thingsboard/server/service/transport/LocalTransportService.java @@ -1,12 +1,12 @@ /** * 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 - *

+ * + * 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. 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 index a0791adddf..4f8860daf7 100644 --- a/application/src/main/java/org/thingsboard/server/service/transport/RemoteRuleEngineTransportService.java +++ b/application/src/main/java/org/thingsboard/server/service/transport/RemoteRuleEngineTransportService.java @@ -58,8 +58,6 @@ import java.util.function.Consumer; @ConditionalOnProperty(prefix = "transport", value = "type", havingValue = "remote") public class RemoteRuleEngineTransportService implements RuleEngineTransportService { - private static final ObjectMapper mapper = new ObjectMapper(); - @Value("${transport.remote.rule_engine.topic}") private String ruleEngineTopic; @Value("${transport.remote.notifications.topic}") diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 0eb624b181..6238a3e232 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -386,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 @@ -434,12 +447,12 @@ transport: topic: "${TB_TRANSPORT_NOTIFICATIONS_TOPIC:tb.transport.notifications}" # Local HTTP transport parameters http: - enabled: "${MQTT_ENABLED:true}" + enabled: "${HTTP_ENABLED:true}" request_timeout: "${HTTP_REQUEST_TIMEOUT:60000}" # Local MQTT transport parameters mqtt: # Enable/disable mqtt transport protocol. - enabled: "${MQTT_ENABLED:false}" + enabled: "${MQTT_ENABLED:true}" bind_address: "${MQTT_BIND_ADDRESS:0.0.0.0}" bind_port: "${MQTT_BIND_PORT:1883}" adaptor: "${MQTT_ADAPTOR_NAME:JsonMqttAdaptor}" diff --git a/common/pom.xml b/common/pom.xml index 84481334e9..e55d71fcd3 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -36,8 +36,8 @@ data message - transport queue + transport diff --git a/common/transport/http/pom.xml b/common/transport/http/pom.xml index 2ee11426a9..5d735c9def 100644 --- a/common/transport/http/pom.xml +++ b/common/transport/http/pom.xml @@ -32,7 +32,7 @@ UTF-8 - ${basedir}/../.. + ${basedir}/../../.. 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 index 8d579d4eb2..b40fbc194f 100644 --- 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 @@ -1,12 +1,12 @@ /** * 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 - *

+ * + * 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. @@ -19,6 +19,7 @@ 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; @@ -60,7 +61,7 @@ import java.util.function.Consumer; * @author Andrew Shvayka */ @RestController -@ConditionalOnProperty(prefix = "transport.http", value = "enabled", havingValue = "true", matchIfMissing = true) +@ConditionalOnExpression("'${transport.type:null}'=='null' || ('${transport.type}'=='local' && '${transport.http.enabled}'=='true')") @RequestMapping("/api/v1") @Slf4j public class DeviceApiController { diff --git a/common/transport/http/src/main/java/org/thingsboard/server/transport/http/HttpTransportContext.java b/common/transport/http/src/main/java/org/thingsboard/server/transport/http/HttpTransportContext.java index 66654c1bed..601c9ee8e8 100644 --- a/common/transport/http/src/main/java/org/thingsboard/server/transport/http/HttpTransportContext.java +++ b/common/transport/http/src/main/java/org/thingsboard/server/transport/http/HttpTransportContext.java @@ -1,12 +1,12 @@ /** * 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 - *

+ * + * 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. @@ -18,15 +18,18 @@ package org.thingsboard.server.transport.http; import lombok.Getter; import lombok.extern.slf4j.Slf4j; 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 javax.annotation.PostConstruct; + /** * Created by ashvayka on 04.10.18. */ @Slf4j -@ConditionalOnProperty(prefix = "transport.http", value = "enabled", havingValue = "true", matchIfMissing = true) +@ConditionalOnExpression("'${transport.type:null}'=='null' || ('${transport.type}'=='local' && '${transport.http.enabled}'=='true')") @Component public class HttpTransportContext extends TransportContext { diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttSslHandlerProvider.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttSslHandlerProvider.java index ee28738a1e..aa643ebc9b 100644 --- a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttSslHandlerProvider.java +++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttSslHandlerProvider.java @@ -20,6 +20,7 @@ import io.netty.handler.ssl.SslHandler; 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.springframework.util.StringUtils; @@ -52,6 +53,7 @@ import java.util.concurrent.TimeUnit; */ @Slf4j @Component("MqttSslHandlerProvider") +@ConditionalOnExpression("'${transport.type:null}'=='null' || ('${transport.type}'=='local' && '${transport.http.enabled}'=='true')") @ConditionalOnProperty(prefix = "mqtt.ssl", value = "enabled", havingValue = "true", matchIfMissing = false) public class MqttSslHandlerProvider { 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 index 91580bff7d..538daa1f9c 100644 --- 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 @@ -23,6 +23,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.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.stereotype.Component; import org.thingsboard.server.common.transport.TransportContext; @@ -40,7 +41,7 @@ import java.util.concurrent.Executors; * Created by ashvayka on 04.10.18. */ @Slf4j -@ConditionalOnProperty(prefix = "transport.mqtt", value = "enabled", havingValue = "true", matchIfMissing = true) +@ConditionalOnExpression("'${transport.type:null}'=='null' || ('${transport.type}'=='local' && '${transport.mqtt.enabled}'=='true')") @Component public class MqttTransportContext extends TransportContext { diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java index d739824aa5..89ecdd583e 100644 --- a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java +++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java @@ -1,12 +1,12 @@ /** * 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 - *

+ * + * 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. diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportService.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportService.java index 199b567430..c1639ced7d 100644 --- a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportService.java +++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportService.java @@ -24,6 +24,7 @@ import io.netty.util.ResourceLeakDetector; 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.Service; @@ -34,7 +35,7 @@ import javax.annotation.PreDestroy; * @author Andrew Shvayka */ @Service("MqttTransportService") -@ConditionalOnProperty(prefix = "transport.mqtt", value = "enabled", havingValue = "true", matchIfMissing = true) +@ConditionalOnExpression("'${transport.type:null}'=='null' || ('${transport.type}'=='local' && '${transport.mqtt.enabled}'=='true')") @Slf4j public class MqttTransportService { 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 index 5d0c14d78c..caf178a641 100644 --- 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 @@ -1,12 +1,12 @@ /** * 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 - *

+ * + * 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. 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 index 1bf4dd1cbf..e299a027e8 100644 --- 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 @@ -1,3 +1,18 @@ +/** + * 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; 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 index 1442cc5f21..582fe49b06 100644 --- 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 @@ -21,6 +21,7 @@ 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; @@ -60,7 +61,7 @@ import java.util.concurrent.Executors; /** * Created by ashvayka on 05.10.18. */ -@ConditionalOnProperty(prefix = "transport", value = "type", havingValue = "remote", matchIfMissing = true) +@ConditionalOnExpression("'${transport.type:null}'=='null'") @Service @Slf4j public class RemoteTransportService extends AbstractTransportService { diff --git a/transport/http/build.gradle b/transport/http/build.gradle new file mode 100644 index 0000000000..6d54cb4789 --- /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") +} diff --git a/transport/http/pom.xml b/transport/http/pom.xml new file mode 100644 index 0000000000..225f064f17 --- /dev/null +++ b/transport/http/pom.xml @@ -0,0 +1,341 @@ + + + 4.0.0 + + org.thingsboard + 2.2.0-SNAPSHOT + transport + + org.thingsboard.transport + http + jar + + Thingsboard HTTP Transport Service + https://thingsboard.io + + + UTF-8 + ${basedir}/../.. + tb-http-transport + /var/log/${pkg.name} + /usr/share/${pkg.name} + ${project.build.directory}/windows + + + + + org.thingsboard.common.transport + http + + + org.thingsboard.common + queue + + + com.sun.winsw + winsw + bin + exe + provided + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-test + test + + + junit + junit + test + + + org.mockito + mockito-all + test + + + + + ${pkg.name}-${project.version} + + + ${project.basedir}/src/main/resources + + + + + org.apache.maven.plugins + maven-resources-plugin + + + copy-conf + process-resources + + copy-resources + + + ${project.build.directory}/conf + + + src/main/resources + + logback.xml + + false + + + + + + copy-service-conf + process-resources + + copy-resources + + + ${project.build.directory}/conf + + + src/main/conf + true + + + + src/main/filters/unix.properties + + + + + copy-win-conf + process-resources + + copy-resources + + + ${pkg.win.dist}/conf + + + src/main/resources + + logback.xml + + false + + + src/main/conf + + tb-mqtt-transport.conf + + true + + + + src/main/filters/windows.properties + + + + + copy-control + process-resources + + copy-resources + + + ${project.build.directory}/control + + + src/main/scripts/control + true + + + + src/main/filters/unix.properties + + + + + copy-windows-control + process-resources + + copy-resources + + + ${pkg.win.dist} + + + src/main/scripts/windows + true + + + + src/main/filters/windows.properties + + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + copy-winsw-service + package + + copy + + + + + com.sun.winsw + winsw + bin + exe + service.exe + + + ${pkg.win.dist} + + + + + + org.apache.maven.plugins + maven-jar-plugin + + + **/logback.xml + + + + ThingsBoard MQTT Transport Service + ${project.version} + + + + + + org.springframework.boot + spring-boot-maven-plugin + + org.thingsboard.server.mqtt.ThingsboardHttpTransportApplication + boot + ZIP + true + true + + ${pkg.installFolder}/conf + ${pkg.unixLogFolder} + ${pkg.name}.out + ${pkg.name} + + + + + + repackage + + + + + + org.fortasoft + gradle-maven-plugin + + + build + buildDeb + buildRpm + + + -PprojectBuildDir=${project.build.directory} + -PprojectVersion=${project.version} + -PmainJar=${project.build.directory}/${project.build.finalName}-boot.${project.packaging} + -PpkgName=${pkg.name} + -PpkgInstallFolder=${pkg.installFolder} + -PpkgLogFolder=${pkg.unixLogFolder} + + + + + package + + invoke + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + ${pkg.name} + + src/main/assembly/windows.xml + + + + + assembly + package + + single + + + + + + org.apache.maven.plugins + maven-install-plugin + + ${project.build.directory}/${pkg.name}.deb + ${project.artifactId} + ${project.groupId} + ${project.version} + deb + deb + + + + install-deb + package + + install-file + + + + + + + + + jenkins + Jenkins Repository + http://repo.jenkins-ci.org/releases + + false + + + + diff --git a/transport/http/src/main/assembly/windows.xml b/transport/http/src/main/assembly/windows.xml new file mode 100644 index 0000000000..82da34ed13 --- /dev/null +++ b/transport/http/src/main/assembly/windows.xml @@ -0,0 +1,71 @@ + + + windows + + + zip + + + + + + ${pkg.win.dist} + logs + + */** + + + + ${pkg.win.dist}/conf + conf + windows + + + + + + ${project.build.directory}/${project.build.finalName}-boot.${project.packaging} + lib + ${pkg.name}.jar + + + ${pkg.win.dist}/service.exe + + ${pkg.name}.exe + + + ${pkg.win.dist}/service.xml + + ${pkg.name}.xml + windows + + + ${pkg.win.dist}/install.bat + + windows + + + ${pkg.win.dist}/uninstall.bat + + windows + + + diff --git a/transport/http/src/main/conf/logback.xml b/transport/http/src/main/conf/logback.xml new file mode 100644 index 0000000000..f36469d5f2 --- /dev/null +++ b/transport/http/src/main/conf/logback.xml @@ -0,0 +1,43 @@ + + + + + + + ${pkg.logFolder}/${pkg.name}.log + + ${pkg.logFolder}/${pkg.name}.%d{yyyy-MM-dd}.%i.log + 100MB + 30 + 3GB + + + %d{ISO8601} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + + + 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 0000000000..0afa91c18e --- /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 0000000000..8967278673 --- /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 0000000000..a6e48d91ba --- /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/java/org/thingsboard/server/mqtt/ThingsboardHttpTransportApplication.java b/transport/http/src/main/java/org/thingsboard/server/mqtt/ThingsboardHttpTransportApplication.java new file mode 100644 index 0000000000..29fae1bc6d --- /dev/null +++ b/transport/http/src/main/java/org/thingsboard/server/mqtt/ThingsboardHttpTransportApplication.java @@ -0,0 +1,48 @@ +package org.thingsboard.server.mqtt; /** + * 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.springframework.boot.SpringApplication; +import org.springframework.boot.SpringBootConfiguration; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.scheduling.annotation.EnableAsync; +import org.springframework.scheduling.annotation.EnableScheduling; + +import java.util.Arrays; + +@SpringBootApplication +@EnableAsync +@ComponentScan({"org.thingsboard.server.http", "org.thingsboard.server.common", "org.thingsboard.server.transport.http", "org.thingsboard.server.kafka"}) +public class ThingsboardHttpTransportApplication { + + private static final String SPRING_CONFIG_NAME_KEY = "--spring.config.name"; + private static final String DEFAULT_SPRING_CONFIG_PARAM = SPRING_CONFIG_NAME_KEY + "=" + "tb-http-transport"; + + public static void main(String[] args) { + SpringApplication.run(ThingsboardHttpTransportApplication.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/http/src/main/resources/logback.xml b/transport/http/src/main/resources/logback.xml new file mode 100644 index 0000000000..18864a9fab --- /dev/null +++ b/transport/http/src/main/resources/logback.xml @@ -0,0 +1,34 @@ + + + + + + + + %d{ISO8601} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + + + \ 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 0000000000..65a1ad93a0 --- /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 0000000000..d4066c027b --- /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 0000000000..61865803c3 --- /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 0000000000..6be5959285 --- /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 0000000000..898d3efd5c --- /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 0000000000..8a7a88f7e0 --- /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 0000000000..8e1f8a2048 --- /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 0000000000..e19fc884c8 --- /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 0000000000..accb487b8e --- /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 0000000000..d456fc03c0 --- /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 0000000000..dba773663c --- /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 0000000000..f7b9d307c8 --- /dev/null +++ b/transport/http/src/main/scripts/windows/service.xml @@ -0,0 +1,36 @@ + + ${pkg.name} + ${project.name} + ${project.description} + %BASE%\conf + ${pkg.winWrapperLogFolder} + rotate + + java + -Xloggc:%BASE%\logs\gc.log + -XX:+HeapDumpOnOutOfMemoryError + -XX:+PrintGCDetails + -XX:+PrintGCDateStamps + -XX:+PrintHeapAtGC + -XX:+PrintTenuringDistribution + -XX:+PrintGCApplicationStoppedTime + -XX:+UseGCLogFileRotation + -XX:NumberOfGCLogFiles=10 + -XX:GCLogFileSize=10M + -XX:-UseBiasedLocking + -XX:+UseTLAB + -XX:+ResizeTLAB + -XX:+PerfDisableSharedMem + -XX:+UseCondCardMark + -XX:CMSWaitDuration=10000 + -XX:+UseParNewGC + -XX:+UseConcMarkSweepGC + -XX:+CMSParallelRemarkEnabled + -XX:+CMSParallelInitialMarkEnabled + -XX:+CMSEdenChunksRecordAlways + -XX:CMSInitiatingOccupancyFraction=75 + -XX:+UseCMSInitiatingOccupancyOnly + -jar + %BASE%\lib\${pkg.name}.jar + + 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 0000000000..921e4c8395 --- /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 diff --git a/transport/pom.xml b/transport/pom.xml index a10188bd56..281f511550 100644 --- a/transport/pom.xml +++ b/transport/pom.xml @@ -34,23 +34,8 @@ - + http mqtt - - - org.thingsboard - dao - - - org.springframework.boot - spring-boot-autoconfigure - - - org.bouncycastle - bcprov-jdk15on - - - From 5242864ccccdee77f95d08a80a6340b5f767eb9f Mon Sep 17 00:00:00 2001 From: Andrew Shvayka Date: Thu, 18 Oct 2018 13:47:42 +0300 Subject: [PATCH 22/26] CoAP transport implementation --- application/pom.xml | 8 +- .../src/main/resources/thingsboard.yml | 2 - common/transport/coap/pom.xml | 88 ++++ .../transport/coap/CoapTransportContext.java | 51 ++ .../transport/coap/CoapTransportResource.java | 445 ++++++++++++++++++ .../transport/coap/CoapTransportService.java | 38 +- .../coap/adaptors/CoapTransportAdaptor.java | 47 ++ .../coap/adaptors/JsonCoapAdaptor.java | 155 ++++++ .../transport/coap/client/DeviceEmulator.java | 0 .../transport/mqtt/MqttTransportService.java | 2 - common/transport/pom.xml | 2 +- pom.xml | 2 +- transport/coap/build.gradle | 140 ++++++ transport/coap/pom.xml | 299 +++++++++++- transport/coap/src/main/assembly/windows.xml | 71 +++ transport/coap/src/main/conf/logback.xml | 43 ++ .../coap/src/main/conf/tb-coap-transport.conf | 23 + .../coap/src/main/filters/unix.properties | 1 + .../coap/src/main/filters/windows.properties | 2 + .../ThingsboardCoapTransportApplication.java | 48 ++ .../transport/coap/CoapTransportResource.java | 242 ---------- .../coap/adaptors/CoapTransportAdaptor.java | 25 - .../coap/adaptors/JsonCoapAdaptor.java | 260 ---------- .../session/CoapExchangeObserverProxy.java | 38 -- .../coap/session/CoapSessionCtx.java | 127 ----- .../transport/coap/session/CoapSessionId.java | 75 --- transport/coap/src/main/resources/logback.xml | 34 ++ .../src/main/resources/tb-coap-transport.yml | 68 +++ .../src/main/scripts/control/deb/postinst | 6 + .../coap/src/main/scripts/control/deb/postrm | 3 + .../coap/src/main/scripts/control/deb/preinst | 18 + .../coap/src/main/scripts/control/deb/prerm | 5 + .../src/main/scripts/control/rpm/postinst | 9 + .../coap/src/main/scripts/control/rpm/postrm | 6 + .../coap/src/main/scripts/control/rpm/preinst | 6 + .../coap/src/main/scripts/control/rpm/prerm | 6 + .../scripts/control/tb-coap-transport.service | 11 + .../coap/src/main/scripts/windows/install.bat | 87 ++++ .../coap/src/main/scripts/windows/service.xml | 36 ++ .../src/main/scripts/windows/uninstall.bat | 9 + .../server/transport/coap/CoapServerTest.java | 197 -------- .../coap/CoapServerTestConfiguration.java | 35 -- .../resources/coap-transport-test.properties | 4 - transport/http/pom.xml | 6 +- transport/mqtt/pom.xml | 4 + .../src/main/resources/tb-mqtt-transport.yml | 1 + transport/pom.xml | 1 + 47 files changed, 1719 insertions(+), 1067 deletions(-) create mode 100644 common/transport/coap/pom.xml create mode 100644 common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportContext.java create mode 100644 common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java rename {transport => common/transport}/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportService.java (72%) create mode 100644 common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/CoapTransportAdaptor.java create mode 100644 common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/JsonCoapAdaptor.java rename {transport => common/transport}/coap/src/main/java/org/thingsboard/server/transport/coap/client/DeviceEmulator.java (100%) create mode 100644 transport/coap/build.gradle create mode 100644 transport/coap/src/main/assembly/windows.xml create mode 100644 transport/coap/src/main/conf/logback.xml create mode 100644 transport/coap/src/main/conf/tb-coap-transport.conf create mode 100644 transport/coap/src/main/filters/unix.properties create mode 100644 transport/coap/src/main/filters/windows.properties create mode 100644 transport/coap/src/main/java/org/thingsboard/server/coap/ThingsboardCoapTransportApplication.java delete mode 100644 transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java delete mode 100644 transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/CoapTransportAdaptor.java delete mode 100644 transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/JsonCoapAdaptor.java delete mode 100644 transport/coap/src/main/java/org/thingsboard/server/transport/coap/session/CoapExchangeObserverProxy.java delete mode 100644 transport/coap/src/main/java/org/thingsboard/server/transport/coap/session/CoapSessionCtx.java delete mode 100644 transport/coap/src/main/java/org/thingsboard/server/transport/coap/session/CoapSessionId.java create mode 100644 transport/coap/src/main/resources/logback.xml create mode 100644 transport/coap/src/main/resources/tb-coap-transport.yml create mode 100644 transport/coap/src/main/scripts/control/deb/postinst create mode 100644 transport/coap/src/main/scripts/control/deb/postrm create mode 100644 transport/coap/src/main/scripts/control/deb/preinst create mode 100644 transport/coap/src/main/scripts/control/deb/prerm create mode 100644 transport/coap/src/main/scripts/control/rpm/postinst create mode 100644 transport/coap/src/main/scripts/control/rpm/postrm create mode 100644 transport/coap/src/main/scripts/control/rpm/preinst create mode 100644 transport/coap/src/main/scripts/control/rpm/prerm create mode 100644 transport/coap/src/main/scripts/control/tb-coap-transport.service create mode 100644 transport/coap/src/main/scripts/windows/install.bat create mode 100644 transport/coap/src/main/scripts/windows/service.xml create mode 100644 transport/coap/src/main/scripts/windows/uninstall.bat delete mode 100644 transport/coap/src/test/java/org/thingsboard/server/transport/coap/CoapServerTest.java delete mode 100644 transport/coap/src/test/java/org/thingsboard/server/transport/coap/CoapServerTestConfiguration.java delete mode 100644 transport/coap/src/test/resources/coap-transport-test.properties diff --git a/application/pom.xml b/application/pom.xml index 309f7ded5b..d99f60773e 100644 --- a/application/pom.xml +++ b/application/pom.xml @@ -60,10 +60,6 @@ org.thingsboard.common.transport transport-api - - - - org.thingsboard.common.transport mqtt @@ -72,6 +68,10 @@ org.thingsboard.common.transport http + + org.thingsboard.common.transport + coap + org.thingsboard dao diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 6238a3e232..90d15003bc 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -455,7 +455,6 @@ transport: 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}" @@ -482,5 +481,4 @@ transport: enabled: "${COAP_ENABLED:true}" bind_address: "${COAP_BIND_ADDRESS:0.0.0.0}" bind_port: "${COAP_BIND_PORT:5683}" - adaptor: "${COAP_ADAPTOR_NAME:JsonCoapAdaptor}" timeout: "${COAP_TIMEOUT:10000}" diff --git a/common/transport/coap/pom.xml b/common/transport/coap/pom.xml new file mode 100644 index 0000000000..d68a6db39f --- /dev/null +++ b/common/transport/coap/pom.xml @@ -0,0 +1,88 @@ + + + 4.0.0 + + org.thingsboard.common + 2.2.0-SNAPSHOT + transport + + org.thingsboard.common.transport + coap + jar + + Thingsboard CoAP Transport Common + https://thingsboard.io + + + UTF-8 + ${basedir}/../../.. + + + + + org.thingsboard.common.transport + transport-api + + + org.eclipse.californium + californium-core + + + org.springframework + spring-context-support + + + org.springframework + spring-context + + + org.slf4j + slf4j-api + + + org.slf4j + log4j-over-slf4j + + + ch.qos.logback + logback-core + + + ch.qos.logback + logback-classic + + + org.springframework.boot + spring-boot-starter-test + test + + + junit + junit + test + + + org.mockito + mockito-all + test + + + + diff --git a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportContext.java b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportContext.java new file mode 100644 index 0000000000..13662cf1be --- /dev/null +++ b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportContext.java @@ -0,0 +1,51 @@ +/** + * 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; + +import lombok.Getter; +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.stereotype.Component; +import org.thingsboard.server.common.transport.TransportContext; +import org.thingsboard.server.transport.coap.adaptors.CoapTransportAdaptor; + +/** + * Created by ashvayka on 18.10.18. + */ +@Slf4j +@ConditionalOnExpression("'${transport.type:null}'=='null' || ('${transport.type}'=='local' && '${transport.coap.enabled}'=='true')") +@Component +public class CoapTransportContext extends TransportContext { + + @Getter + @Value("${transport.coap.bind_address}") + private String host; + + @Getter + @Value("${transport.coap.bind_port}") + private Integer port; + + @Getter + @Value("${transport.coap.timeout}") + private Long timeout; + + @Getter + @Autowired + private CoapTransportAdaptor adaptor; + +} diff --git a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java new file mode 100644 index 0000000000..0e2a794a45 --- /dev/null +++ b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java @@ -0,0 +1,445 @@ +/** + * 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; + +import lombok.extern.slf4j.Slf4j; +import org.eclipse.californium.core.CoapResource; +import org.eclipse.californium.core.coap.CoAP.ResponseCode; +import org.eclipse.californium.core.coap.Request; +import org.eclipse.californium.core.coap.Response; +import org.eclipse.californium.core.network.Exchange; +import org.eclipse.californium.core.network.ExchangeObserver; +import org.eclipse.californium.core.server.resources.CoapExchange; +import org.eclipse.californium.core.server.resources.Resource; +import org.springframework.util.ReflectionUtils; +import org.thingsboard.server.common.data.security.DeviceTokenCredentials; +import org.thingsboard.server.common.msg.session.FeatureType; +import org.thingsboard.server.common.msg.session.SessionMsgType; +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.AdaptorException; +import org.thingsboard.server.gen.transport.TransportProtos; + +import java.lang.reflect.Field; +import java.util.List; +import java.util.Optional; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Consumer; + +@Slf4j +public class CoapTransportResource extends CoapResource { + // coap://localhost:port/api/v1/DEVICE_TOKEN/[attributes|telemetry|rpc[/requestId]] + private static final int ACCESS_TOKEN_POSITION = 3; + private static final int FEATURE_TYPE_POSITION = 4; + private static final int REQUEST_ID_POSITION = 5; + + private final CoapTransportContext transportContext; + private final TransportService transportService; + private final Field observerField; + private final long timeout; + private final ConcurrentMap tokenToSessionIdMap = new ConcurrentHashMap<>(); + + public CoapTransportResource(CoapTransportContext context, String name) { + super(name); + this.transportContext = context; + this.transportService = context.getTransportService(); + this.timeout = context.getTimeout(); + // This is important to turn off existing observable logic in + // CoapResource. We will have our own observe monitoring due to 1:1 + // observe relationship. + this.setObservable(false); + observerField = ReflectionUtils.findField(Exchange.class, "observer"); + observerField.setAccessible(true); + } + + @Override + public void handleGET(CoapExchange exchange) { + if (transportContext.getQuotaService().isQuotaExceeded(exchange.getSourceAddress().getHostAddress())) { + log.warn("CoAP Quota exceeded for [{}:{}] . Disconnect", exchange.getSourceAddress().getHostAddress(), exchange.getSourcePort()); + exchange.respond(ResponseCode.BAD_REQUEST); + return; + } + + Optional featureType = getFeatureType(exchange.advanced().getRequest()); + if (!featureType.isPresent()) { + log.trace("Missing feature type parameter"); + exchange.respond(ResponseCode.BAD_REQUEST); + } else if (featureType.get() == FeatureType.TELEMETRY) { + log.trace("Can't fetch/subscribe to timeseries updates"); + exchange.respond(ResponseCode.BAD_REQUEST); + } else if (exchange.getRequestOptions().hasObserve()) { + processExchangeGetRequest(exchange, featureType.get()); + } else if (featureType.get() == FeatureType.ATTRIBUTES) { + processRequest(exchange, SessionMsgType.GET_ATTRIBUTES_REQUEST); + } else { + log.trace("Invalid feature type parameter"); + exchange.respond(ResponseCode.BAD_REQUEST); + } + } + + private void processExchangeGetRequest(CoapExchange exchange, FeatureType featureType) { + boolean unsubscribe = exchange.getRequestOptions().getObserve() == 1; + SessionMsgType sessionMsgType; + if (featureType == FeatureType.RPC) { + sessionMsgType = unsubscribe ? SessionMsgType.UNSUBSCRIBE_RPC_COMMANDS_REQUEST : SessionMsgType.SUBSCRIBE_RPC_COMMANDS_REQUEST; + } else { + sessionMsgType = unsubscribe ? SessionMsgType.UNSUBSCRIBE_ATTRIBUTES_REQUEST : SessionMsgType.SUBSCRIBE_ATTRIBUTES_REQUEST; + } + processRequest(exchange, sessionMsgType); + } + + @Override + public void handlePOST(CoapExchange exchange) { + if (transportContext.getQuotaService().isQuotaExceeded(exchange.getSourceAddress().getHostAddress())) { + log.warn("CoAP Quota exceeded for [{}:{}] . Disconnect", exchange.getSourceAddress().getHostAddress(), exchange.getSourcePort()); + exchange.respond(ResponseCode.BAD_REQUEST); + return; + } + + Optional featureType = getFeatureType(exchange.advanced().getRequest()); + if (!featureType.isPresent()) { + log.trace("Missing feature type parameter"); + exchange.respond(ResponseCode.BAD_REQUEST); + } else { + switch (featureType.get()) { + case ATTRIBUTES: + processRequest(exchange, SessionMsgType.POST_ATTRIBUTES_REQUEST); + break; + case TELEMETRY: + processRequest(exchange, SessionMsgType.POST_TELEMETRY_REQUEST); + break; + case RPC: + Optional requestId = getRequestId(exchange.advanced().getRequest()); + if (requestId.isPresent()) { + processRequest(exchange, SessionMsgType.TO_DEVICE_RPC_RESPONSE); + } else { + processRequest(exchange, SessionMsgType.TO_SERVER_RPC_REQUEST); + } + break; + } + } + } + + private void processRequest(CoapExchange exchange, SessionMsgType type) { + log.trace("Processing {}", exchange.advanced().getRequest()); + exchange.accept(); + Exchange advanced = exchange.advanced(); + Request request = advanced.getRequest(); + + Optional credentials = decodeCredentials(request); + if (!credentials.isPresent()) { + exchange.respond(ResponseCode.BAD_REQUEST); + return; + } + + transportService.process(TransportProtos.ValidateDeviceTokenRequestMsg.newBuilder().setToken(credentials.get().getCredentialsId()).build(), + new DeviceAuthCallback(transportContext, exchange, sessionInfo -> { + UUID sessionId = new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB()); + try { + switch (type) { + case POST_ATTRIBUTES_REQUEST: + transportService.process(sessionInfo, + transportContext.getAdaptor().convertToPostAttributes(sessionId, request), + new CoapOkCallback(exchange)); + break; + case POST_TELEMETRY_REQUEST: + transportService.process(sessionInfo, + transportContext.getAdaptor().convertToPostTelemetry(sessionId, request), + new CoapOkCallback(exchange)); + break; + case SUBSCRIBE_ATTRIBUTES_REQUEST: + advanced.setObserver(new CoapExchangeObserverProxy((ExchangeObserver) observerField.get(advanced), + registerAsyncCoapSession(exchange, request, sessionInfo, sessionId))); + transportService.process(sessionInfo, + TransportProtos.SubscribeToAttributeUpdatesMsg.getDefaultInstance(), + new CoapNoOpCallback(exchange)); + break; + case UNSUBSCRIBE_ATTRIBUTES_REQUEST: + TransportProtos.SessionInfoProto attrSession = lookupAsyncSessionInfo(request); + if (attrSession != null) { + transportService.process(attrSession, + TransportProtos.SubscribeToAttributeUpdatesMsg.newBuilder().setUnsubscribe(true).build(), + new CoapOkCallback(exchange)); + closeAndDeregister(sessionInfo); + } + break; + case SUBSCRIBE_RPC_COMMANDS_REQUEST: + advanced.setObserver(new CoapExchangeObserverProxy((ExchangeObserver) observerField.get(advanced), + registerAsyncCoapSession(exchange, request, sessionInfo, sessionId))); + transportService.process(sessionInfo, + TransportProtos.SubscribeToRPCMsg.getDefaultInstance(), + new CoapNoOpCallback(exchange)); + break; + case UNSUBSCRIBE_RPC_COMMANDS_REQUEST: + TransportProtos.SessionInfoProto rpcSession = lookupAsyncSessionInfo(request); + if (rpcSession != null) { + transportService.process(rpcSession, + TransportProtos.SubscribeToRPCMsg.newBuilder().setUnsubscribe(true).build(), + new CoapOkCallback(exchange)); + transportService.process(sessionInfo, getSessionEventMsg(TransportProtos.SessionEvent.CLOSED), null); + transportService.deregisterSession(rpcSession); + } + break; + case TO_DEVICE_RPC_RESPONSE: + transportService.process(sessionInfo, + transportContext.getAdaptor().convertToDeviceRpcResponse(sessionId, request), + new CoapOkCallback(exchange)); + break; + case TO_SERVER_RPC_REQUEST: + transportService.process(sessionInfo, + transportContext.getAdaptor().convertToServerRpcRequest(sessionId, request), + new CoapNoOpCallback(exchange)); + break; + case GET_ATTRIBUTES_REQUEST: + transportService.registerSyncSession(sessionInfo, new CoapSessionListener(sessionId, exchange), transportContext.getTimeout()); + transportService.process(sessionInfo, + transportContext.getAdaptor().convertToGetAttributes(sessionId, request), + new CoapNoOpCallback(exchange)); + break; + } + } catch (AdaptorException e) { + log.trace("[{}] Failed to decode message: ", sessionId, e); + exchange.respond(ResponseCode.BAD_REQUEST); + } catch (IllegalAccessException e) { + log.trace("[{}] Failed to process message: ", sessionId, e); + exchange.respond(ResponseCode.INTERNAL_SERVER_ERROR); + } + })); + } + + private TransportProtos.SessionInfoProto lookupAsyncSessionInfo(Request request) { + String token = request.getSource().getHostAddress() + ":" + request.getSourcePort() + ":" + request.getTokenString(); + return tokenToSessionIdMap.remove(token); + } + + private String registerAsyncCoapSession(CoapExchange exchange, Request request, TransportProtos.SessionInfoProto sessionInfo, UUID sessionId) { + String token = request.getSource().getHostAddress() + ":" + request.getSourcePort() + ":" + request.getTokenString(); + tokenToSessionIdMap.putIfAbsent(token, sessionInfo); + CoapSessionListener attrListener = new CoapSessionListener(sessionId, exchange); + transportService.registerAsyncSession(sessionInfo, attrListener); + transportService.process(sessionInfo, getSessionEventMsg(TransportProtos.SessionEvent.OPEN), null); + return token; + } + + private static TransportProtos.SessionEventMsg getSessionEventMsg(TransportProtos.SessionEvent event) { + return TransportProtos.SessionEventMsg.newBuilder() + .setSessionType(TransportProtos.SessionType.ASYNC) + .setEvent(event).build(); + } + + private Optional decodeCredentials(Request request) { + List uriPath = request.getOptions().getUriPath(); + if (uriPath.size() >= ACCESS_TOKEN_POSITION) { + return Optional.of(new DeviceTokenCredentials(uriPath.get(ACCESS_TOKEN_POSITION - 1))); + } else { + return Optional.empty(); + } + } + + private Optional getFeatureType(Request request) { + List uriPath = request.getOptions().getUriPath(); + try { + if (uriPath.size() >= FEATURE_TYPE_POSITION) { + return Optional.of(FeatureType.valueOf(uriPath.get(FEATURE_TYPE_POSITION - 1).toUpperCase())); + } + } catch (RuntimeException e) { + log.warn("Failed to decode feature type: {}", uriPath); + } + return Optional.empty(); + } + + public static Optional getRequestId(Request request) { + List uriPath = request.getOptions().getUriPath(); + try { + if (uriPath.size() >= REQUEST_ID_POSITION) { + return Optional.of(Integer.valueOf(uriPath.get(REQUEST_ID_POSITION - 1))); + } + } catch (RuntimeException e) { + log.warn("Failed to decode feature type: {}", uriPath); + } + return Optional.empty(); + } + + @Override + public Resource getChild(String name) { + return this; + } + + private static class DeviceAuthCallback implements TransportServiceCallback { + private final TransportContext transportContext; + private final CoapExchange exchange; + private final Consumer onSuccess; + + DeviceAuthCallback(TransportContext transportContext, CoapExchange exchange, Consumer onSuccess) { + this.transportContext = transportContext; + this.exchange = exchange; + this.onSuccess = onSuccess; + } + + @Override + public void onSuccess(TransportProtos.ValidateDeviceCredentialsResponseMsg msg) { + if (msg.hasDeviceInfo()) { + UUID sessionId = UUID.randomUUID(); + TransportProtos.DeviceInfoProto deviceInfoProto = msg.getDeviceInfo(); + TransportProtos.SessionInfoProto sessionInfo = TransportProtos.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 { + exchange.respond(ResponseCode.UNAUTHORIZED); + } + } + + @Override + public void onError(Throwable e) { + log.warn("Failed to process request", e); + exchange.respond(ResponseCode.INTERNAL_SERVER_ERROR); + } + } + + private static class CoapOkCallback implements TransportServiceCallback { + private final CoapExchange exchange; + + CoapOkCallback(CoapExchange exchange) { + this.exchange = exchange; + } + + @Override + public void onSuccess(Void msg) { + exchange.respond(ResponseCode.VALID); + } + + @Override + public void onError(Throwable e) { + exchange.respond(ResponseCode.INTERNAL_SERVER_ERROR); + } + } + + private static class CoapNoOpCallback implements TransportServiceCallback { + private final CoapExchange exchange; + + CoapNoOpCallback(CoapExchange exchange) { + this.exchange = exchange; + } + + @Override + public void onSuccess(Void msg) { + + } + + @Override + public void onError(Throwable e) { + exchange.respond(ResponseCode.INTERNAL_SERVER_ERROR); + } + } + + public class CoapSessionListener implements SessionMsgListener { + + private final CoapExchange exchange; + private final AtomicInteger seqNumber = new AtomicInteger(2); + + CoapSessionListener(UUID sessionId, CoapExchange exchange) { + this.exchange = exchange; + } + + @Override + public void onGetAttributesResponse(TransportProtos.GetAttributeResponseMsg msg) { + try { + exchange.respond(transportContext.getAdaptor().convertToPublish(this, msg)); + } catch (AdaptorException e) { + log.trace("Failed to reply due to error", e); + exchange.respond(ResponseCode.INTERNAL_SERVER_ERROR); + } + } + + @Override + public void onAttributeUpdate(TransportProtos.AttributeUpdateNotificationMsg msg) { + try { + exchange.respond(transportContext.getAdaptor().convertToPublish(this, msg)); + } catch (AdaptorException e) { + log.trace("Failed to reply due to error", e); + exchange.respond(ResponseCode.INTERNAL_SERVER_ERROR); + } + } + + @Override + public void onRemoteSessionCloseCommand(TransportProtos.SessionCloseNotificationProto sessionCloseNotification) { + exchange.respond(ResponseCode.SERVICE_UNAVAILABLE); + } + + @Override + public void onToDeviceRpcRequest(TransportProtos.ToDeviceRpcRequestMsg msg) { + try { + exchange.respond(transportContext.getAdaptor().convertToPublish(this, msg)); + } catch (AdaptorException e) { + log.trace("Failed to reply due to error", e); + exchange.respond(ResponseCode.INTERNAL_SERVER_ERROR); + } + } + + @Override + public void onToServerRpcResponse(TransportProtos.ToServerRpcResponseMsg msg) { + try { + exchange.respond(transportContext.getAdaptor().convertToPublish(this, msg)); + } catch (AdaptorException e) { + log.trace("Failed to reply due to error", e); + exchange.respond(ResponseCode.INTERNAL_SERVER_ERROR); + } + } + + public int getNextSeqNumber() { + return seqNumber.getAndIncrement(); + } + } + + public class CoapExchangeObserverProxy implements ExchangeObserver { + + private final ExchangeObserver proxy; + private final String token; + + CoapExchangeObserverProxy(ExchangeObserver proxy, String token) { + super(); + this.proxy = proxy; + this.token = token; + } + + @Override + public void completed(Exchange exchange) { + proxy.completed(exchange); + TransportProtos.SessionInfoProto session = tokenToSessionIdMap.remove(token); + if (session != null) { + closeAndDeregister(session); + } + } + } + + private void closeAndDeregister(TransportProtos.SessionInfoProto session) { + transportService.process(session, getSessionEventMsg(TransportProtos.SessionEvent.CLOSED), null); + transportService.deregisterSession(session); + } + +} diff --git a/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportService.java b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportService.java similarity index 72% rename from transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportService.java rename to common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportService.java index ece0bb7e10..278ca6d8c5 100644 --- a/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportService.java +++ b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportService.java @@ -21,6 +21,7 @@ import org.eclipse.californium.core.CoapServer; import org.eclipse.californium.core.network.CoapEndpoint; 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.context.ApplicationContext; import org.springframework.stereotype.Service; @@ -37,49 +38,26 @@ import java.net.InetSocketAddress; import java.net.UnknownHostException; @Service("CoapTransportService") -@ConditionalOnProperty(prefix = "transport.coap", value = "enabled", havingValue = "true") +@ConditionalOnExpression("'${transport.type:null}'=='null' || ('${transport.type}'=='local' && '${transport.coap.enabled}'=='true')") @Slf4j public class CoapTransportService { private static final String V1 = "v1"; private static final String API = "api"; - private CoapServer server; - - @Autowired(required = false) - private ApplicationContext appContext; - - @Autowired(required = false) - private SessionMsgProcessor processor; - - @Autowired(required = false) - private DeviceAuthService authService; + @Autowired + private CoapTransportContext coapTransportContext; - @Autowired(required = false) - private HostRequestsQuotaService quotaService; - - - @Value("${coap.bind_address}") - private String host; - @Value("${coap.bind_port}") - private Integer port; - @Value("${coap.adaptor}") - private String adaptorName; - @Value("${coap.timeout}") - private Long timeout; - - private CoapTransportAdaptor adaptor; + private CoapServer server; @PostConstruct public void init() throws UnknownHostException { log.info("Starting CoAP transport..."); - log.info("Lookup CoAP transport adaptor {}", adaptorName); - this.adaptor = (CoapTransportAdaptor) appContext.getBean(adaptorName); log.info("Starting CoAP transport server"); this.server = new CoapServer(); createResources(); - InetAddress addr = InetAddress.getByName(host); - InetSocketAddress sockAddr = new InetSocketAddress(addr, port); + InetAddress addr = InetAddress.getByName(coapTransportContext.getHost()); + InetSocketAddress sockAddr = new InetSocketAddress(addr, coapTransportContext.getPort()); server.addEndpoint(new CoapEndpoint(sockAddr)); server.start(); log.info("CoAP transport started!"); @@ -87,7 +65,7 @@ public class CoapTransportService { private void createResources() { CoapResource api = new CoapResource(API); - api.add(new CoapTransportResource(processor, authService, adaptor, V1, timeout, quotaService)); + api.add(new CoapTransportResource(coapTransportContext, V1)); server.add(api); } diff --git a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/CoapTransportAdaptor.java b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/CoapTransportAdaptor.java new file mode 100644 index 0000000000..9e48cd3030 --- /dev/null +++ b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/CoapTransportAdaptor.java @@ -0,0 +1,47 @@ +/** + * 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 org.eclipse.californium.core.coap.Request; +import org.eclipse.californium.core.coap.Response; +import org.thingsboard.server.common.transport.adaptor.AdaptorException; +import org.thingsboard.server.gen.transport.TransportProtos; +import org.thingsboard.server.transport.coap.CoapTransportResource; + +import java.util.UUID; +import java.util.Optional; + +public interface CoapTransportAdaptor { + + TransportProtos.PostTelemetryMsg convertToPostTelemetry(UUID sessionId, Request inbound) throws AdaptorException; + + TransportProtos.PostAttributeMsg convertToPostAttributes(UUID sessionId, Request inbound) throws AdaptorException; + + TransportProtos.GetAttributeRequestMsg convertToGetAttributes(UUID sessionId, Request inbound) throws AdaptorException; + + TransportProtos.ToDeviceRpcResponseMsg convertToDeviceRpcResponse(UUID sessionId, Request inbound) throws AdaptorException; + + TransportProtos.ToServerRpcRequestMsg convertToServerRpcRequest(UUID sessionId, Request inbound) throws AdaptorException; + + Response convertToPublish(CoapTransportResource.CoapSessionListener session, TransportProtos.GetAttributeResponseMsg responseMsg) throws AdaptorException; + + Response convertToPublish(CoapTransportResource.CoapSessionListener session, TransportProtos.AttributeUpdateNotificationMsg notificationMsg) throws AdaptorException; + + Response convertToPublish(CoapTransportResource.CoapSessionListener session, TransportProtos.ToDeviceRpcRequestMsg rpcRequest) throws AdaptorException; + + Response convertToPublish(CoapTransportResource.CoapSessionListener coapSessionListener, TransportProtos.ToServerRpcResponseMsg msg) throws AdaptorException; + +} 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 0000000000..f5bfb49c08 --- /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 queryElements = inbound.getOptions().getUriQuery(); + TransportProtos.GetAttributeRequestMsg.Builder result = TransportProtos.GetAttributeRequestMsg.newBuilder(); + if (queryElements != null && queryElements.size() > 0) { + Set clientKeys = toKeys(queryElements, "clientKeys"); + Set 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 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 toKeys(List 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; + } + } + +} diff --git a/transport/coap/src/main/java/org/thingsboard/server/transport/coap/client/DeviceEmulator.java b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/client/DeviceEmulator.java similarity index 100% rename from transport/coap/src/main/java/org/thingsboard/server/transport/coap/client/DeviceEmulator.java rename to common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/client/DeviceEmulator.java diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportService.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportService.java index c1639ced7d..55df31a735 100644 --- a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportService.java +++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportService.java @@ -43,8 +43,6 @@ public class MqttTransportService { private String host; @Value("${transport.mqtt.bind_port}") private Integer port; - @Value("${transport.mqtt.adaptor}") - private String adaptorName; @Value("${transport.mqtt.netty.leak_detector_level}") private String leakDetectorLevel; diff --git a/common/transport/pom.xml b/common/transport/pom.xml index c9b47e876f..83e7ba806a 100644 --- a/common/transport/pom.xml +++ b/common/transport/pom.xml @@ -38,7 +38,7 @@ transport-api mqtt http - + coap diff --git a/pom.xml b/pom.xml index f4c2957b08..684c3d02a4 100755 --- a/pom.xml +++ b/pom.xml @@ -370,7 +370,7 @@ ${project.version} - org.thingsboard.transport + org.thingsboard.common.transport coap ${project.version} diff --git a/transport/coap/build.gradle b/transport/coap/build.gradle new file mode 100644 index 0000000000..6d54cb4789 --- /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") +} diff --git a/transport/coap/pom.xml b/transport/coap/pom.xml index a7015f0a35..a5e8107ab6 100644 --- a/transport/coap/pom.xml +++ b/transport/coap/pom.xml @@ -27,42 +27,37 @@ coap jar - Thingsboard COAP Transport + Thingsboard CoAP Transport Service https://thingsboard.io UTF-8 ${basedir}/../.. + tb-coap-transport + /var/log/${pkg.name} + /usr/share/${pkg.name} + ${project.build.directory}/windows - org.thingsboard.common - transport - - - org.eclipse.californium - californium-core - - - org.springframework - spring-context + org.thingsboard.common.transport + coap - org.slf4j - slf4j-api - - - org.slf4j - log4j-over-slf4j + org.thingsboard.common + queue - ch.qos.logback - logback-core + org.springframework.boot + spring-boot-starter-web - ch.qos.logback - logback-classic + com.sun.winsw + winsw + bin + exe + provided org.springframework.boot @@ -81,4 +76,266 @@ + + ${pkg.name}-${project.version} + + + ${project.basedir}/src/main/resources + + + + + org.apache.maven.plugins + maven-resources-plugin + + + copy-conf + process-resources + + copy-resources + + + ${project.build.directory}/conf + + + src/main/resources + + logback.xml + + false + + + + + + copy-service-conf + process-resources + + copy-resources + + + ${project.build.directory}/conf + + + src/main/conf + true + + + + src/main/filters/unix.properties + + + + + copy-win-conf + process-resources + + copy-resources + + + ${pkg.win.dist}/conf + + + src/main/resources + + logback.xml + + false + + + src/main/conf + + tb-coap-transport.conf + + true + + + + src/main/filters/windows.properties + + + + + copy-control + process-resources + + copy-resources + + + ${project.build.directory}/control + + + src/main/scripts/control + true + + + + src/main/filters/unix.properties + + + + + copy-windows-control + process-resources + + copy-resources + + + ${pkg.win.dist} + + + src/main/scripts/windows + true + + + + src/main/filters/windows.properties + + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + copy-winsw-service + package + + copy + + + + + com.sun.winsw + winsw + bin + exe + service.exe + + + ${pkg.win.dist} + + + + + + org.apache.maven.plugins + maven-jar-plugin + + + **/logback.xml + + + + ThingsBoard CoAP Transport Service + ${project.version} + + + + + + org.springframework.boot + spring-boot-maven-plugin + + org.thingsboard.server.coap.ThingsboardCoapTransportApplication + boot + ZIP + true + true + + ${pkg.installFolder}/conf + ${pkg.unixLogFolder} + ${pkg.name}.out + ${pkg.name} + + + + + + repackage + + + + + + org.fortasoft + gradle-maven-plugin + + + build + buildDeb + buildRpm + + + -PprojectBuildDir=${project.build.directory} + -PprojectVersion=${project.version} + -PmainJar=${project.build.directory}/${project.build.finalName}-boot.${project.packaging} + -PpkgName=${pkg.name} + -PpkgInstallFolder=${pkg.installFolder} + -PpkgLogFolder=${pkg.unixLogFolder} + + + + + package + + invoke + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + ${pkg.name} + + src/main/assembly/windows.xml + + + + + assembly + package + + single + + + + + + org.apache.maven.plugins + maven-install-plugin + + ${project.build.directory}/${pkg.name}.deb + ${project.artifactId} + ${project.groupId} + ${project.version} + deb + deb + + + + install-deb + package + + install-file + + + + + + + + + jenkins + Jenkins Repository + http://repo.jenkins-ci.org/releases + + false + + + diff --git a/transport/coap/src/main/assembly/windows.xml b/transport/coap/src/main/assembly/windows.xml new file mode 100644 index 0000000000..82da34ed13 --- /dev/null +++ b/transport/coap/src/main/assembly/windows.xml @@ -0,0 +1,71 @@ + + + windows + + + zip + + + + + + ${pkg.win.dist} + logs + + */** + + + + ${pkg.win.dist}/conf + conf + windows + + + + + + ${project.build.directory}/${project.build.finalName}-boot.${project.packaging} + lib + ${pkg.name}.jar + + + ${pkg.win.dist}/service.exe + + ${pkg.name}.exe + + + ${pkg.win.dist}/service.xml + + ${pkg.name}.xml + windows + + + ${pkg.win.dist}/install.bat + + windows + + + ${pkg.win.dist}/uninstall.bat + + windows + + + diff --git a/transport/coap/src/main/conf/logback.xml b/transport/coap/src/main/conf/logback.xml new file mode 100644 index 0000000000..f36469d5f2 --- /dev/null +++ b/transport/coap/src/main/conf/logback.xml @@ -0,0 +1,43 @@ + + + + + + + ${pkg.logFolder}/${pkg.name}.log + + ${pkg.logFolder}/${pkg.name}.%d{yyyy-MM-dd}.%i.log + 100MB + 30 + 3GB + + + %d{ISO8601} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + + + 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 0000000000..0afa91c18e --- /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 0000000000..8967278673 --- /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 0000000000..a6e48d91ba --- /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 0000000000..1ad86dd166 --- /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/java/org/thingsboard/server/transport/coap/CoapTransportResource.java b/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java deleted file mode 100644 index 88fb69ecd0..0000000000 --- a/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java +++ /dev/null @@ -1,242 +0,0 @@ -/** - * 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; - -import java.lang.reflect.Field; -import java.util.List; -import java.util.Optional; - -import lombok.extern.slf4j.Slf4j; -import org.eclipse.californium.core.CoapResource; -import org.eclipse.californium.core.coap.CoAP.ResponseCode; -import org.eclipse.californium.core.coap.Request; -import org.eclipse.californium.core.network.Exchange; -import org.eclipse.californium.core.network.ExchangeObserver; -import org.eclipse.californium.core.server.resources.CoapExchange; -import org.eclipse.californium.core.server.resources.Resource; -import org.thingsboard.server.common.data.security.DeviceCredentialsFilter; -import org.thingsboard.server.common.data.security.DeviceTokenCredentials; -import org.thingsboard.server.common.msg.session.*; -import org.thingsboard.server.common.transport.SessionMsgProcessor; -import org.thingsboard.server.common.transport.adaptor.AdaptorException; -import org.thingsboard.server.common.transport.auth.DeviceAuthService; -import org.thingsboard.server.common.transport.quota.QuotaService; -import org.thingsboard.server.transport.coap.adaptors.CoapTransportAdaptor; -import org.thingsboard.server.transport.coap.session.CoapExchangeObserverProxy; -import org.thingsboard.server.transport.coap.session.CoapSessionCtx; -import org.springframework.util.ReflectionUtils; - -@Slf4j -public class CoapTransportResource extends CoapResource { - // coap://localhost:port/api/v1/DEVICE_TOKEN/[attributes|telemetry|rpc[/requestId]] - private static final int ACCESS_TOKEN_POSITION = 3; - private static final int FEATURE_TYPE_POSITION = 4; - private static final int REQUEST_ID_POSITION = 5; - - private final CoapTransportAdaptor adaptor; - private final SessionMsgProcessor processor; - private final DeviceAuthService authService; - private final QuotaService quotaService; - private final Field observerField; - private final long timeout; - - public CoapTransportResource(SessionMsgProcessor processor, DeviceAuthService authService, CoapTransportAdaptor adaptor, String name, - long timeout, QuotaService quotaService) { - super(name); - this.processor = processor; - this.authService = authService; - this.quotaService = quotaService; - this.adaptor = adaptor; - this.timeout = timeout; - // This is important to turn off existing observable logic in - // CoapResource. We will have our own observe monitoring due to 1:1 - // observe relationship. - this.setObservable(false); - observerField = ReflectionUtils.findField(Exchange.class, "observer"); - observerField.setAccessible(true); - } - - @Override - public void handleGET(CoapExchange exchange) { - if(quotaService.isQuotaExceeded(exchange.getSourceAddress().getHostAddress())) { - log.warn("COAP Quota exceeded for [{}:{}] . Disconnect", exchange.getSourceAddress().getHostAddress(), exchange.getSourcePort()); - exchange.respond(ResponseCode.BAD_REQUEST); - return; - } - - Optional featureType = getFeatureType(exchange.advanced().getRequest()); - if (!featureType.isPresent()) { - log.trace("Missing feature type parameter"); - exchange.respond(ResponseCode.BAD_REQUEST); - } else if (featureType.get() == FeatureType.TELEMETRY) { - log.trace("Can't fetch/subscribe to timeseries updates"); - exchange.respond(ResponseCode.BAD_REQUEST); - } else if (exchange.getRequestOptions().hasObserve()) { - processExchangeGetRequest(exchange, featureType.get()); - } else if (featureType.get() == FeatureType.ATTRIBUTES) { - processRequest(exchange, SessionMsgType.GET_ATTRIBUTES_REQUEST); - } else { - log.trace("Invalid feature type parameter"); - exchange.respond(ResponseCode.BAD_REQUEST); - } - } - - private void processExchangeGetRequest(CoapExchange exchange, FeatureType featureType) { - boolean unsubscribe = exchange.getRequestOptions().getObserve() == 1; - SessionMsgType sessionMsgType; - if (featureType == FeatureType.RPC) { - sessionMsgType = unsubscribe ? SessionMsgType.UNSUBSCRIBE_RPC_COMMANDS_REQUEST : SessionMsgType.SUBSCRIBE_RPC_COMMANDS_REQUEST; - } else { - sessionMsgType = unsubscribe ? SessionMsgType.UNSUBSCRIBE_ATTRIBUTES_REQUEST : SessionMsgType.SUBSCRIBE_ATTRIBUTES_REQUEST; - } - Optional sessionId = processRequest(exchange, sessionMsgType); - if (sessionId.isPresent()) { - if (exchange.getRequestOptions().getObserve() == 1) { - exchange.respond(ResponseCode.VALID); - } - } - } - - @Override - public void handlePOST(CoapExchange exchange) { - if(quotaService.isQuotaExceeded(exchange.getSourceAddress().getHostAddress())) { - log.warn("COAP Quota exceeded for [{}:{}] . Disconnect", exchange.getSourceAddress().getHostAddress(), exchange.getSourcePort()); - exchange.respond(ResponseCode.BAD_REQUEST); - return; - } - - Optional featureType = getFeatureType(exchange.advanced().getRequest()); - if (!featureType.isPresent()) { - log.trace("Missing feature type parameter"); - exchange.respond(ResponseCode.BAD_REQUEST); - } else { - switch (featureType.get()) { - case ATTRIBUTES: - processRequest(exchange, SessionMsgType.POST_ATTRIBUTES_REQUEST); - break; - case TELEMETRY: - processRequest(exchange, SessionMsgType.POST_TELEMETRY_REQUEST); - break; - case RPC: - Optional requestId = getRequestId(exchange.advanced().getRequest()); - if (requestId.isPresent()) { - processRequest(exchange, SessionMsgType.TO_DEVICE_RPC_RESPONSE); - } else { - processRequest(exchange, SessionMsgType.TO_SERVER_RPC_REQUEST); - } - break; - } - } - } - - private Optional processRequest(CoapExchange exchange, SessionMsgType type) { - log.trace("Processing {}", exchange.advanced().getRequest()); - exchange.accept(); - Exchange advanced = exchange.advanced(); - Request request = advanced.getRequest(); - - Optional credentials = decodeCredentials(request); - if (!credentials.isPresent()) { - exchange.respond(ResponseCode.BAD_REQUEST); - return Optional.empty(); - } - - CoapSessionCtx ctx = new CoapSessionCtx(exchange, adaptor, processor, authService, timeout); - -// if (!ctx.login(credentials.get())) { -// exchange.respond(ResponseCode.UNAUTHORIZED); -// return Optional.empty(); -// } - - AdaptorToSessionActorMsg msg; - try { - switch (type) { - case GET_ATTRIBUTES_REQUEST: - case POST_ATTRIBUTES_REQUEST: - case POST_TELEMETRY_REQUEST: - case TO_DEVICE_RPC_RESPONSE: - case TO_SERVER_RPC_REQUEST: - ctx.setSessionType(SessionType.SYNC); - msg = adaptor.convertToActorMsg(ctx, type, request); - break; - case SUBSCRIBE_ATTRIBUTES_REQUEST: - case SUBSCRIBE_RPC_COMMANDS_REQUEST: - ExchangeObserver systemObserver = (ExchangeObserver) observerField.get(advanced); - advanced.setObserver(new CoapExchangeObserverProxy(systemObserver, ctx)); - ctx.setSessionType(SessionType.ASYNC); - msg = adaptor.convertToActorMsg(ctx, type, request); - break; - case UNSUBSCRIBE_ATTRIBUTES_REQUEST: - case UNSUBSCRIBE_RPC_COMMANDS_REQUEST: - ctx.setSessionType(SessionType.ASYNC); - msg = adaptor.convertToActorMsg(ctx, type, request); - break; - default: - log.trace("[{}] Unsupported msg type: {}", ctx.getSessionId(), type); - throw new IllegalArgumentException("Unsupported msg type: " + type); - } - log.trace("Processing msg: {}", msg); -// processor.process(new BasicTransportToDeviceSessionActorMsg(ctx.getDevice(), msg)); - } catch (AdaptorException e) { - log.debug("Failed to decode payload {}", e); - exchange.respond(ResponseCode.BAD_REQUEST, e.getMessage()); - return Optional.empty(); - } catch (IllegalArgumentException | IllegalAccessException e) { - log.debug("Failed to process payload {}", e); - exchange.respond(ResponseCode.INTERNAL_SERVER_ERROR, e.getMessage()); - } - return Optional.of(ctx.getSessionId()); - } - - private Optional decodeCredentials(Request request) { - List uriPath = request.getOptions().getUriPath(); - DeviceCredentialsFilter credentials = null; - if (uriPath.size() >= ACCESS_TOKEN_POSITION) { - credentials = new DeviceTokenCredentials(uriPath.get(ACCESS_TOKEN_POSITION - 1)); - } - return Optional.ofNullable(credentials); - } - - private Optional getFeatureType(Request request) { - List uriPath = request.getOptions().getUriPath(); - try { - if (uriPath.size() >= FEATURE_TYPE_POSITION) { - return Optional.of(FeatureType.valueOf(uriPath.get(FEATURE_TYPE_POSITION - 1).toUpperCase())); - } - } catch (RuntimeException e) { - log.warn("Failed to decode feature type: {}", uriPath); - } - return Optional.empty(); - } - - public static Optional getRequestId(Request request) { - List uriPath = request.getOptions().getUriPath(); - try { - if (uriPath.size() >= REQUEST_ID_POSITION) { - return Optional.of(Integer.valueOf(uriPath.get(REQUEST_ID_POSITION - 1))); - } - } catch (RuntimeException e) { - log.warn("Failed to decode feature type: {}", uriPath); - } - return Optional.empty(); - } - - @Override - public Resource getChild(String name) { - return this; - } - -} diff --git a/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/CoapTransportAdaptor.java b/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/CoapTransportAdaptor.java deleted file mode 100644 index d9a04b163a..0000000000 --- a/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/CoapTransportAdaptor.java +++ /dev/null @@ -1,25 +0,0 @@ -/** - * 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 org.eclipse.californium.core.coap.Request; -import org.eclipse.californium.core.coap.Response; -import org.thingsboard.server.common.transport.TransportAdaptor; -import org.thingsboard.server.transport.coap.session.CoapSessionCtx; - -public interface CoapTransportAdaptor extends TransportAdaptor { - -} diff --git a/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/JsonCoapAdaptor.java b/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/JsonCoapAdaptor.java deleted file mode 100644 index 1c587f0fae..0000000000 --- a/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/JsonCoapAdaptor.java +++ /dev/null @@ -1,260 +0,0 @@ -/** - * 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 lombok.extern.slf4j.Slf4j; -import org.eclipse.californium.core.coap.CoAP.ResponseCode; -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.core.*; -import org.thingsboard.server.common.msg.kv.AttributesKVMsg; -import org.thingsboard.server.common.msg.session.SessionMsgType; -import org.thingsboard.server.common.msg.session.SessionContext; -import org.thingsboard.server.common.msg.session.ex.ProcessingTimeoutException; -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.JsonObject; -import com.google.gson.JsonParser; -import com.google.gson.JsonSyntaxException; -import org.thingsboard.server.transport.coap.CoapTransportResource; -import org.thingsboard.server.transport.coap.session.CoapSessionCtx; - -@Component("JsonCoapAdaptor") -@Slf4j -public class JsonCoapAdaptor implements CoapTransportAdaptor { - - @Override - public AdaptorToSessionActorMsg convertToActorMsg(CoapSessionCtx ctx, SessionMsgType type, Request inbound) throws AdaptorException { - FromDeviceMsg msg = null; - switch (type) { - case POST_TELEMETRY_REQUEST: - msg = convertToTelemetryUploadRequest(ctx, inbound); - break; - case POST_ATTRIBUTES_REQUEST: - msg = convertToUpdateAttributesRequest(ctx, inbound); - break; - case GET_ATTRIBUTES_REQUEST: - msg = convertToGetAttributesRequest(ctx, inbound); - break; - case SUBSCRIBE_RPC_COMMANDS_REQUEST: - msg = new RpcSubscribeMsg(); - break; - case UNSUBSCRIBE_RPC_COMMANDS_REQUEST: - msg = new RpcUnsubscribeMsg(); - break; - case TO_DEVICE_RPC_RESPONSE: - msg = convertToDeviceRpcResponse(ctx, inbound); - break; - case TO_SERVER_RPC_REQUEST: - msg = convertToServerRpcRequest(ctx, inbound); - break; - case SUBSCRIBE_ATTRIBUTES_REQUEST: - msg = new AttributesSubscribeMsg(); - break; - case UNSUBSCRIBE_ATTRIBUTES_REQUEST: - msg = new AttributesUnsubscribeMsg(); - break; - default: - log.warn("[{}] Unsupported msg type: {}!", ctx.getSessionId(), type); - throw new AdaptorException(new IllegalArgumentException("Unsupported msg type: " + type + "!")); - } - return new BasicAdaptorToSessionActorMsg(ctx, msg); - } - - private FromDeviceMsg convertToDeviceRpcResponse(CoapSessionCtx ctx, Request inbound) throws AdaptorException { - Optional requestId = CoapTransportResource.getRequestId(inbound); - String payload = validatePayload(ctx, inbound); - JsonObject response = new JsonParser().parse(payload).getAsJsonObject(); - return new ToDeviceRpcResponseMsg( - requestId.orElseThrow(() -> new AdaptorException("Request id is missing!")), - response.get("response").toString()); - } - - private FromDeviceMsg convertToServerRpcRequest(CoapSessionCtx ctx, Request inbound) throws AdaptorException { - - String payload = validatePayload(ctx, inbound); - -// return JsonConverter.convertToServerRpcRequest(new JsonParser().parse(payload), 0); - return null; - } - - @Override - public Optional convertToAdaptorMsg(CoapSessionCtx ctx, SessionActorToAdaptorMsg source) throws AdaptorException { - ToDeviceMsg msg = source.getMsg(); - switch (msg.getSessionMsgType()) { - case STATUS_CODE_RESPONSE: - case TO_DEVICE_RPC_RESPONSE_ACK: - return Optional.of(convertStatusCodeResponse((StatusCodeResponse) msg)); - case GET_ATTRIBUTES_RESPONSE: - return Optional.of(convertGetAttributesResponse((GetAttributesResponse) msg)); - case ATTRIBUTES_UPDATE_NOTIFICATION: - return Optional.of(convertNotificationResponse(ctx, (AttributesUpdateNotification) msg)); - case TO_DEVICE_RPC_REQUEST: - return Optional.of(convertToDeviceRpcRequest(ctx, (ToDeviceRpcRequestMsg) msg)); - case TO_SERVER_RPC_RESPONSE: - return Optional.of(convertToServerRpcResponse(ctx, (ToServerRpcResponseMsg) msg)); - case RULE_ENGINE_ERROR: - return Optional.of(convertToRuleEngineErrorResponse(ctx, (RuleEngineErrorMsg) msg)); - default: - log.warn("[{}] Unsupported msg type: {}!", source.getSessionId(), msg.getSessionMsgType()); - throw new AdaptorException(new IllegalArgumentException("Unsupported msg type: " + msg.getSessionMsgType() + "!")); - } - } - - private Response convertToRuleEngineErrorResponse(CoapSessionCtx ctx, RuleEngineErrorMsg msg) { - ResponseCode status = ResponseCode.INTERNAL_SERVER_ERROR; - switch (msg.getError()) { - case QUEUE_PUT_TIMEOUT: - status = ResponseCode.GATEWAY_TIMEOUT; - break; - default: - if (msg.getInSessionMsgType() == SessionMsgType.TO_SERVER_RPC_REQUEST) { - status = ResponseCode.BAD_REQUEST; - } - break; - } - Response response = new Response(status); - response.setPayload(JsonConverter.toErrorJson(msg.getErrorMsg()).toString()); - return response; - } - - private Response convertNotificationResponse(CoapSessionCtx ctx, AttributesUpdateNotification msg) { - return getObserveNotification(ctx, JsonConverter.toJson(msg.getData(), false)); - } - - private Response convertToDeviceRpcRequest(CoapSessionCtx ctx, ToDeviceRpcRequestMsg msg) { - return getObserveNotification(ctx, JsonConverter.toJson(msg, true)); - } - - private Response getObserveNotification(CoapSessionCtx ctx, JsonObject json) { - Response response = new Response(ResponseCode.CONTENT); - response.getOptions().setObserve(ctx.nextSeqNumber()); - response.setPayload(json.toString()); - return response; - } - - private AttributesUpdateRequest convertToUpdateAttributesRequest(SessionContext ctx, Request inbound) throws AdaptorException { - String payload = validatePayload(ctx, inbound); - try { - return JsonConverter.convertToAttributes(new JsonParser().parse(payload)); - } catch (IllegalStateException | JsonSyntaxException ex) { - throw new AdaptorException(ex); - } - } - - private FromDeviceMsg convertToGetAttributesRequest(SessionContext ctx, Request inbound) throws AdaptorException { - List queryElements = inbound.getOptions().getUriQuery(); - if (queryElements != null && queryElements.size() > 0) { - Set clientKeys = toKeys(ctx, queryElements, "clientKeys"); - Set sharedKeys = toKeys(ctx, queryElements, "sharedKeys"); - return new BasicGetAttributesRequest(0, clientKeys, sharedKeys); - } else { - return new BasicGetAttributesRequest(0); - } - } - - private Set toKeys(SessionContext ctx, List 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; - } - } - - private TelemetryUploadRequest convertToTelemetryUploadRequest(SessionContext ctx, Request inbound) throws AdaptorException { - String payload = validatePayload(ctx, inbound); - try { - return JsonConverter.convertToTelemetry(new JsonParser().parse(payload)); - } catch (IllegalStateException | JsonSyntaxException ex) { - throw new AdaptorException(ex); - } - } - - private Response convertStatusCodeResponse(StatusCodeResponse msg) { - if (msg.isSuccess()) { - Optional code = msg.getData(); - if (code.isPresent() && code.get() == 200) { - return new Response(ResponseCode.VALID); - } else { - return new Response(ResponseCode.CREATED); - } - } else { - return convertError(msg.getError()); - } - } - - private String validatePayload(SessionContext ctx, Request inbound) throws AdaptorException { - String payload = inbound.getPayloadString(); - if (payload == null) { - log.warn("[{}] Payload is empty!", ctx.getSessionId()); - throw new AdaptorException(new IllegalArgumentException("Payload is empty!")); - } - return payload; - } - - private Response convertToServerRpcResponse(SessionContext ctx, ToServerRpcResponseMsg msg) { - if (msg.isSuccess()) { - Response response = new Response(ResponseCode.CONTENT); -// JsonElement result = JsonConverter.toJson(msg); -// response.setPayload(result.toString()); - return response; - } else { - return convertError(Optional.of(new RuntimeException("Server RPC response is empty!"))); - } - } - - private Response convertGetAttributesResponse(GetAttributesResponse msg) { - if (msg.isSuccess()) { - Optional payload = msg.getData(); - if (!payload.isPresent() || (payload.get().getClientAttributes().isEmpty() && payload.get().getSharedAttributes().isEmpty())) { - return new Response(ResponseCode.NOT_FOUND); - } else { - Response response = new Response(ResponseCode.CONTENT); - JsonObject result = JsonConverter.toJson(payload.get(), false); - response.setPayload(result.toString()); - return response; - } - } else { - return convertError(msg.getError()); - } - } - - private Response convertError(Optional exception) { - if (exception.isPresent()) { - log.warn("Converting exception: {}", exception.get().getMessage(), exception.get()); - if (exception.get() instanceof ProcessingTimeoutException) { - return new Response(ResponseCode.SERVICE_UNAVAILABLE); - } else { - return new Response(ResponseCode.INTERNAL_SERVER_ERROR); - } - } else { - return new Response(ResponseCode.INTERNAL_SERVER_ERROR); - } - } - -} diff --git a/transport/coap/src/main/java/org/thingsboard/server/transport/coap/session/CoapExchangeObserverProxy.java b/transport/coap/src/main/java/org/thingsboard/server/transport/coap/session/CoapExchangeObserverProxy.java deleted file mode 100644 index 0c264817d5..0000000000 --- a/transport/coap/src/main/java/org/thingsboard/server/transport/coap/session/CoapExchangeObserverProxy.java +++ /dev/null @@ -1,38 +0,0 @@ -/** - * 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.session; - -import org.eclipse.californium.core.network.Exchange; -import org.eclipse.californium.core.network.ExchangeObserver; - -public class CoapExchangeObserverProxy implements ExchangeObserver { - - private final ExchangeObserver proxy; - private final CoapSessionCtx ctx; - - public CoapExchangeObserverProxy(ExchangeObserver proxy, CoapSessionCtx ctx) { - super(); - this.proxy = proxy; - this.ctx = ctx; - } - - @Override - public void completed(Exchange exchange) { - proxy.completed(exchange); - ctx.close(); - } - -} diff --git a/transport/coap/src/main/java/org/thingsboard/server/transport/coap/session/CoapSessionCtx.java b/transport/coap/src/main/java/org/thingsboard/server/transport/coap/session/CoapSessionCtx.java deleted file mode 100644 index 60d54087bf..0000000000 --- a/transport/coap/src/main/java/org/thingsboard/server/transport/coap/session/CoapSessionCtx.java +++ /dev/null @@ -1,127 +0,0 @@ -/** - * 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.session; - -import lombok.extern.slf4j.Slf4j; -import org.eclipse.californium.core.coap.CoAP.ResponseCode; -import org.eclipse.californium.core.coap.Request; -import org.eclipse.californium.core.coap.Response; -import org.eclipse.californium.core.server.resources.CoapExchange; -import org.thingsboard.server.common.msg.session.ex.SessionException; -import org.thingsboard.server.common.transport.SessionMsgProcessor; -import org.thingsboard.server.common.transport.adaptor.AdaptorException; -import org.thingsboard.server.common.transport.auth.DeviceAuthService; -import org.thingsboard.server.common.transport.session.DeviceAwareSessionContext; -import org.thingsboard.server.transport.coap.adaptors.CoapTransportAdaptor; - -import java.util.concurrent.atomic.AtomicInteger; - -@Slf4j -public class CoapSessionCtx extends DeviceAwareSessionContext { - - private final SessionId sessionId; - private final CoapExchange exchange; - private final CoapTransportAdaptor adaptor; - private final String token; - private final long timeout; - private SessionType sessionType; - private final AtomicInteger seqNumber = new AtomicInteger(2); - - public CoapSessionCtx(CoapExchange exchange, CoapTransportAdaptor adaptor, SessionMsgProcessor processor, DeviceAuthService authService, long timeout) { - super(); - Request request = exchange.advanced().getRequest(); - this.token = request.getTokenString(); - this.sessionId = new CoapSessionId(request.getSource().getHostAddress(), request.getSourcePort(), this.token); - this.exchange = exchange; - this.adaptor = adaptor; - this.timeout = timeout; - } - - - @Override - public void onMsg(SessionActorToAdaptorMsg msg) throws SessionException { - try { - adaptor.convertToAdaptorMsg(this, msg).ifPresent(this::pushToNetwork); - } catch (AdaptorException e) { - logAndWrap(e); - } - } - - private void pushToNetwork(Response response) { - exchange.respond(response); - } - - private void logAndWrap(AdaptorException e) throws SessionException { - log.warn("Failed to convert msg: {}", e.getMessage(), e); - throw new SessionException(e); - } - - @Override - public void onMsg(SessionCtrlMsg msg) throws SessionException { - log.debug("[{}] onCtrl: {}", sessionId, msg); - if (msg instanceof SessionCloseMsg) { - onSessionClose((SessionCloseMsg) msg); - } - } - - private void onSessionClose(SessionCloseMsg msg) { - if (msg.isTimeout()) { - exchange.respond(ResponseCode.SERVICE_UNAVAILABLE); - } else if (msg.isCredentialsRevoked()) { - exchange.respond(ResponseCode.UNAUTHORIZED); - } else { - exchange.respond(ResponseCode.INTERNAL_SERVER_ERROR); - } - } - - @Override - public SessionId getSessionId() { - return sessionId; - } - - @Override - public String toString() { - return "CoapSessionCtx [sessionId=" + sessionId + "]"; - } - - @Override - public boolean isClosed() { - return exchange.advanced().isComplete() || exchange.advanced().isTimedOut(); - } - - public void close() { - log.info("[{}] Closing processing context. Timeout: {}", sessionId, exchange.advanced().isTimedOut()); -// processor.process(exchange.advanced().isTimedOut() ? SessionCloseMsg.onTimeout(sessionId) : SessionCloseMsg.onError(sessionId)); - } - - @Override - public long getTimeout() { - return timeout; - } - - public void setSessionType(SessionType sessionType) { - this.sessionType = sessionType; - } - - @Override - public SessionType getSessionType() { - return sessionType; - } - - public int nextSeqNumber() { - return seqNumber.getAndIncrement(); - } -} diff --git a/transport/coap/src/main/java/org/thingsboard/server/transport/coap/session/CoapSessionId.java b/transport/coap/src/main/java/org/thingsboard/server/transport/coap/session/CoapSessionId.java deleted file mode 100644 index 2bfcf1d2c6..0000000000 --- a/transport/coap/src/main/java/org/thingsboard/server/transport/coap/session/CoapSessionId.java +++ /dev/null @@ -1,75 +0,0 @@ -/** - * 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.session; - -public final class CoapSessionId implements SessionId { - - private final String clientAddress; - private final int clientPort; - private final String token; - - public CoapSessionId(String host, int port, String token) { - super(); - this.clientAddress = host; - this.clientPort = port; - this.token = token; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((clientAddress == null) ? 0 : clientAddress.hashCode()); - result = prime * result + clientPort; - result = prime * result + ((token == null) ? 0 : token.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - CoapSessionId other = (CoapSessionId) obj; - if (clientAddress == null) { - if (other.clientAddress != null) - return false; - } else if (!clientAddress.equals(other.clientAddress)) - return false; - if (clientPort != other.clientPort) - return false; - if (token == null) { - if (other.token != null) - return false; - } else if (!token.equals(other.token)) - return false; - return true; - } - - @Override - public String toString() { - return "CoapSessionId [clientAddress=" + clientAddress + ", clientPort=" + clientPort + ", token=" + token + "]"; - } - - @Override - public String toUidStr() { - return clientAddress + ":" + clientPort + ":" + token; - } - -} diff --git a/transport/coap/src/main/resources/logback.xml b/transport/coap/src/main/resources/logback.xml new file mode 100644 index 0000000000..18864a9fab --- /dev/null +++ b/transport/coap/src/main/resources/logback.xml @@ -0,0 +1,34 @@ + + + + + + + + %d{ISO8601} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + + + \ 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 0000000000..1790d822d2 --- /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 0000000000..d4066c027b --- /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 0000000000..61865803c3 --- /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 0000000000..6be5959285 --- /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 0000000000..898d3efd5c --- /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 0000000000..8a7a88f7e0 --- /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 0000000000..8e1f8a2048 --- /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 0000000000..e19fc884c8 --- /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 0000000000..accb487b8e --- /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 0000000000..d456fc03c0 --- /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 0000000000..dba773663c --- /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 0000000000..f7b9d307c8 --- /dev/null +++ b/transport/coap/src/main/scripts/windows/service.xml @@ -0,0 +1,36 @@ + + ${pkg.name} + ${project.name} + ${project.description} + %BASE%\conf + ${pkg.winWrapperLogFolder} + rotate + + java + -Xloggc:%BASE%\logs\gc.log + -XX:+HeapDumpOnOutOfMemoryError + -XX:+PrintGCDetails + -XX:+PrintGCDateStamps + -XX:+PrintHeapAtGC + -XX:+PrintTenuringDistribution + -XX:+PrintGCApplicationStoppedTime + -XX:+UseGCLogFileRotation + -XX:NumberOfGCLogFiles=10 + -XX:GCLogFileSize=10M + -XX:-UseBiasedLocking + -XX:+UseTLAB + -XX:+ResizeTLAB + -XX:+PerfDisableSharedMem + -XX:+UseCondCardMark + -XX:CMSWaitDuration=10000 + -XX:+UseParNewGC + -XX:+UseConcMarkSweepGC + -XX:+CMSParallelRemarkEnabled + -XX:+CMSParallelInitialMarkEnabled + -XX:+CMSEdenChunksRecordAlways + -XX:CMSInitiatingOccupancyFraction=75 + -XX:+UseCMSInitiatingOccupancyOnly + -jar + %BASE%\lib\${pkg.name}.jar + + 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 0000000000..921e4c8395 --- /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 diff --git a/transport/coap/src/test/java/org/thingsboard/server/transport/coap/CoapServerTest.java b/transport/coap/src/test/java/org/thingsboard/server/transport/coap/CoapServerTest.java deleted file mode 100644 index 84aeb848c8..0000000000 --- a/transport/coap/src/test/java/org/thingsboard/server/transport/coap/CoapServerTest.java +++ /dev/null @@ -1,197 +0,0 @@ -/** - * 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; - -import lombok.extern.slf4j.Slf4j; -import org.eclipse.californium.core.CoapClient; -import org.eclipse.californium.core.CoapResponse; -import org.eclipse.californium.core.coap.CoAP.ResponseCode; -import org.eclipse.californium.core.coap.MediaTypeRegistry; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.test.annotation.DirtiesContext; -import org.springframework.test.annotation.DirtiesContext.ClassMode; -import org.springframework.test.context.junit4.SpringRunner; -import org.thingsboard.server.common.data.Device; -import org.thingsboard.server.common.data.id.CustomerId; -import org.thingsboard.server.common.data.id.DeviceId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.security.DeviceCredentialsFilter; -import org.thingsboard.server.common.data.security.DeviceCredentialsType; -import org.thingsboard.server.common.data.security.DeviceTokenCredentials; -import org.thingsboard.server.common.msg.session.*; -import org.thingsboard.server.common.transport.SessionMsgProcessor; -import org.thingsboard.server.common.transport.auth.DeviceAuthResult; -import org.thingsboard.server.common.transport.auth.DeviceAuthService; -import org.thingsboard.server.common.transport.quota.host.HostRequestsQuotaService; - -import java.util.Optional; -import java.util.UUID; - -@RunWith(SpringRunner.class) -@DirtiesContext(classMode = ClassMode.BEFORE_CLASS) -@Slf4j -public class CoapServerTest { - - private static final int TEST_PORT = 5555; - private static final String TELEMETRY_POST_MESSAGE = "[{\"key1\":\"value1\"}]"; - private static final String TEST_ATTRIBUTES_RESPONSE = "{\"key1\":\"value1\",\"key2\":42}"; - private static final String DEVICE1_TOKEN = "Device1Token"; - - @Configuration - public static class EchoCoapServerITConfiguration extends CoapServerTestConfiguration { - - @Bean - public static DeviceAuthService authService() { - return new DeviceAuthService() { - - private final DeviceId devId = new DeviceId(UUID.randomUUID()); - - @Override - public DeviceAuthResult process(DeviceCredentialsFilter credentials) { - if (credentials != null && credentials.getCredentialsType() == DeviceCredentialsType.ACCESS_TOKEN) { - DeviceTokenCredentials tokenCredentials = (DeviceTokenCredentials) credentials; - if (tokenCredentials.getCredentialsId().equals(DEVICE1_TOKEN)) { - return DeviceAuthResult.of(devId); - } - } - return DeviceAuthResult.of("Credentials are invalid!"); - } - - @Override - public Optional findDeviceById(DeviceId deviceId) { - if (deviceId.equals(devId)) { - Device dev = new Device(); - dev.setId(devId); - dev.setTenantId(new TenantId(UUID.randomUUID())); - dev.setCustomerId(new CustomerId(UUID.randomUUID())); - return Optional.of(dev); - } else { - return Optional.empty(); - } - } - }; - } - - @Bean - public static SessionMsgProcessor sessionMsgProcessor() { - return new SessionMsgProcessor() { - -// @Override -// public void process(SessionAwareMsg toActorMsg) { -// if (toActorMsg instanceof TransportToDeviceSessionActorMsg) { -// AdaptorToSessionActorMsg sessionMsg = ((TransportToDeviceSessionActorMsg) toActorMsg).getSessionMsg(); -// try { -// FromDeviceMsg deviceMsg = sessionMsg.getMsg(); -// ToDeviceMsg toDeviceMsg = null; -// if (deviceMsg.getMsgType() == SessionMsgType.POST_TELEMETRY_REQUEST) { -// toDeviceMsg = BasicStatusCodeResponse.onSuccess(deviceMsg.getMsgType(), BasicRequest.DEFAULT_REQUEST_ID); -// } else if (deviceMsg.getMsgType() == SessionMsgType.GET_ATTRIBUTES_REQUEST) { -// List data = new ArrayList<>(); -// data.add(new BaseAttributeKvEntry(new StringDataEntry("key1", "value1"), System.currentTimeMillis())); -// data.add(new BaseAttributeKvEntry(new LongDataEntry("key2", 42L), System.currentTimeMillis())); -// BasicAttributeKVMsg kv = BasicAttributeKVMsg.fromClient(data); -// toDeviceMsg = BasicGetAttributesResponse.onSuccess(deviceMsg.getMsgType(), BasicRequest.DEFAULT_REQUEST_ID, kv); -// } -// if (toDeviceMsg != null) { -// sessionMsg.getSessionContext().onMsg(new BasicSessionActorToAdaptorMsg(sessionMsg.getSessionContext(), toDeviceMsg)); -// } -// } catch (Exception e) { -// e.printStackTrace(); -// } -// } -// } - - @Override - public void onDeviceAdded(Device device) { - - } - - - }; - } - - @Bean - public static HostRequestsQuotaService quotaService() { - return new HostRequestsQuotaService(null, null, null, null, false); - } - } - - @Autowired - private CoapTransportService service; - - @Before - public void beforeTest() { - log.info("Service info: {}", service.toString()); - } - - @Test - public void testBadJsonTelemetryPostRequest() { - CoapClient client = new CoapClient(getBaseTestUrl() + DEVICE1_TOKEN + "/" + FeatureType.TELEMETRY.name().toLowerCase()); - CoapResponse response = client.setTimeout(6000).post("test", MediaTypeRegistry.APPLICATION_JSON); - Assert.assertEquals(ResponseCode.BAD_REQUEST, response.getCode()); - log.info("Response: {}, {}", response.getCode(), response.getResponseText()); - } - - @Test - public void testNoCredentialsPostRequest() { - CoapClient client = new CoapClient(getBaseTestUrl()); - CoapResponse response = client.setTimeout(6000).post(TELEMETRY_POST_MESSAGE, MediaTypeRegistry.APPLICATION_JSON); - Assert.assertEquals(ResponseCode.BAD_REQUEST, response.getCode()); - log.info("Response: {}, {}", response.getCode(), response.getResponseText()); - } - - @Test - public void testValidJsonTelemetryPostRequest() { - CoapClient client = new CoapClient(getBaseTestUrl() + DEVICE1_TOKEN + "/" + FeatureType.TELEMETRY.name().toLowerCase()); - CoapResponse response = client.setTimeout(6000).post(TELEMETRY_POST_MESSAGE, MediaTypeRegistry.APPLICATION_JSON); - Assert.assertEquals(ResponseCode.CREATED, response.getCode()); - log.info("Response: {}, {}", response.getCode(), response.getResponseText()); - } - - @Test - public void testNoCredentialsAttributesGetRequest() { - CoapClient client = new CoapClient("coap://localhost:5555/api/v1?keys=key1,key2"); - CoapResponse response = client.setTimeout(6000).get(); - Assert.assertEquals(ResponseCode.BAD_REQUEST, response.getCode()); - } - - @Test - public void testNoKeysAttributesGetRequest() { - CoapClient client = new CoapClient(getBaseTestUrl() + DEVICE1_TOKEN + "/" + FeatureType.ATTRIBUTES.name().toLowerCase() + "?data=key1,key2"); - CoapResponse response = client.setTimeout(6000).get(); - Assert.assertEquals(ResponseCode.CONTENT, response.getCode()); - } - - @Test - public void testValidAttributesGetRequest() { - CoapClient client = new CoapClient(getBaseTestUrl() + DEVICE1_TOKEN + "/" + FeatureType.ATTRIBUTES.name().toLowerCase() + "?clientKeys=key1,key2"); - CoapResponse response = client.setTimeout(6000).get(); - Assert.assertEquals(ResponseCode.CONTENT, response.getCode()); - Assert.assertEquals(TEST_ATTRIBUTES_RESPONSE, response.getResponseText()); - log.info("Response: {}, {}", response.getCode(), response.getResponseText()); - } - - private String getBaseTestUrl() { - return "coap://localhost:" + TEST_PORT + "/api/v1/"; - } - -} diff --git a/transport/coap/src/test/java/org/thingsboard/server/transport/coap/CoapServerTestConfiguration.java b/transport/coap/src/test/java/org/thingsboard/server/transport/coap/CoapServerTestConfiguration.java deleted file mode 100644 index f2cd080293..0000000000 --- a/transport/coap/src/test/java/org/thingsboard/server/transport/coap/CoapServerTestConfiguration.java +++ /dev/null @@ -1,35 +0,0 @@ -/** - * 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; - -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.ComponentScan; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.PropertySource; -import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; -import org.springframework.test.context.TestPropertySource; - -@Configuration -@ComponentScan({ "org.thingsboard.server.transport.coap" }) -@PropertySource("classpath:coap-transport-test.properties") -public class CoapServerTestConfiguration { - - @Bean - public static PropertySourcesPlaceholderConfigurer propertyConfig() { - return new PropertySourcesPlaceholderConfigurer(); - } - -} diff --git a/transport/coap/src/test/resources/coap-transport-test.properties b/transport/coap/src/test/resources/coap-transport-test.properties deleted file mode 100644 index 469b0348b1..0000000000 --- a/transport/coap/src/test/resources/coap-transport-test.properties +++ /dev/null @@ -1,4 +0,0 @@ -coap.bind_address=0.0.0.0 -coap.bind_port=5555 -coap.adaptor=JsonCoapAdaptor -coap.timeout=10000 \ No newline at end of file diff --git a/transport/http/pom.xml b/transport/http/pom.xml index 225f064f17..407d995381 100644 --- a/transport/http/pom.xml +++ b/transport/http/pom.xml @@ -145,7 +145,7 @@ src/main/conf - tb-mqtt-transport.conf + tb-http-transport.conf true @@ -229,7 +229,7 @@ - ThingsBoard MQTT Transport Service + ThingsBoard HTTP Transport Service ${project.version} @@ -239,7 +239,7 @@ org.springframework.boot spring-boot-maven-plugin - org.thingsboard.server.mqtt.ThingsboardHttpTransportApplication + org.thingsboard.server.http.ThingsboardHttpTransportApplication boot ZIP true diff --git a/transport/mqtt/pom.xml b/transport/mqtt/pom.xml index 2750e1e511..6395b40365 100644 --- a/transport/mqtt/pom.xml +++ b/transport/mqtt/pom.xml @@ -48,6 +48,10 @@ org.thingsboard.common queue + + org.springframework.boot + spring-boot-starter-web + com.sun.winsw winsw diff --git a/transport/mqtt/src/main/resources/tb-mqtt-transport.yml b/transport/mqtt/src/main/resources/tb-mqtt-transport.yml index e46de8e9ab..1d425e4761 100644 --- a/transport/mqtt/src/main/resources/tb-mqtt-transport.yml +++ b/transport/mqtt/src/main/resources/tb-mqtt-transport.yml @@ -14,6 +14,7 @@ # limitations under the License. # +spring.main.web-environment: false spring.main.web-application-type: none # MQTT server parameters diff --git a/transport/pom.xml b/transport/pom.xml index 281f511550..0bb9300b27 100644 --- a/transport/pom.xml +++ b/transport/pom.xml @@ -36,6 +36,7 @@ http mqtt + coap From 0ae28408c9feacb0e79862d10c73e07dd614ffc2 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Thu, 18 Oct 2018 16:21:50 +0300 Subject: [PATCH 23/26] Docker build for transports. --- .../service/RemoteTransportService.java | 21 ++- msa/docker/.env | 2 + msa/docker/docker-compose.yml | 57 +++++++- msa/docker/haproxy/config/haproxy.cfg | 8 + msa/docker/tb-coap-transport.env | 6 + msa/docker/tb-http-transport.env | 6 + msa/transport/coap/docker/Dockerfile | 31 ++++ msa/transport/coap/docker/logback.xml | 50 +++++++ .../coap/docker/start-tb-coap-transport.sh | 29 ++++ .../coap/docker/tb-coap-transport.conf | 23 +++ msa/transport/coap/pom.xml | 137 ++++++++++++++++++ msa/transport/http/docker/Dockerfile | 31 ++++ msa/transport/http/docker/logback.xml | 50 +++++++ .../http/docker/start-tb-http-transport.sh | 29 ++++ .../http/docker/tb-http-transport.conf | 23 +++ msa/transport/http/pom.xml | 137 ++++++++++++++++++ msa/transport/pom.xml | 2 + .../ThingsboardHttpTransportApplication.java | 7 +- .../ThingsboardMqttTransportApplication.java | 4 +- 19 files changed, 640 insertions(+), 13 deletions(-) create mode 100644 msa/docker/tb-coap-transport.env create mode 100644 msa/docker/tb-http-transport.env create mode 100644 msa/transport/coap/docker/Dockerfile create mode 100644 msa/transport/coap/docker/logback.xml create mode 100755 msa/transport/coap/docker/start-tb-coap-transport.sh create mode 100644 msa/transport/coap/docker/tb-coap-transport.conf create mode 100644 msa/transport/coap/pom.xml create mode 100644 msa/transport/http/docker/Dockerfile create mode 100644 msa/transport/http/docker/logback.xml create mode 100755 msa/transport/http/docker/start-tb-http-transport.sh create mode 100644 msa/transport/http/docker/tb-http-transport.conf create mode 100644 msa/transport/http/pom.xml rename transport/http/src/main/java/org/thingsboard/server/{mqtt => http}/ThingsboardHttpTransportApplication.java (88%) 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 index 582fe49b06..aa3b42b58c 100644 --- 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 @@ -16,6 +16,8 @@ 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; @@ -42,12 +44,7 @@ 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.AsyncCallbackTemplate; -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.kafka.*; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; @@ -136,9 +133,19 @@ public class RemoteTransportService extends AbstractTransportService { 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 mainConsumerBuilder = TBKafkaConsumerTemplate.builder(); mainConsumerBuilder.settings(kafkaSettings); - mainConsumerBuilder.topic(notificationsTopic + "." + nodeIdProvider.getNodeId()); + mainConsumerBuilder.topic(notificationsTopicName); mainConsumerBuilder.clientId("transport-" + nodeIdProvider.getNodeId()); mainConsumerBuilder.groupId("transport"); mainConsumerBuilder.autoCommit(true); diff --git a/msa/docker/.env b/msa/docker/.env index 72eaef8695..3d8faeb355 100644 --- a/msa/docker/.env +++ b/msa/docker/.env @@ -5,6 +5,8 @@ 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 diff --git a/msa/docker/docker-compose.yml b/msa/docker/docker-compose.yml index 5b88a79c72..0735443fc2 100644 --- a/msa/docker/docker-compose.yml +++ b/msa/docker/docker-compose.yml @@ -56,7 +56,6 @@ services: image: "${DOCKER_REPO}/${TB_NODE_DOCKER_NAME}:${TB_VERSION}" ports: - "8080" - - "5683:5683/udp" logging: driver: "json-file" options: @@ -96,6 +95,50 @@ services: - 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-transport1: + restart: always + image: "${DOCKER_REPO}/${COAP_TRANSPORT_DOCKER_NAME}:${TB_VERSION}" + ports: + - "5683" + environment: + TB_KAFKA_SERVERS: kafka:9092 + env_file: + - tb-coap-transport.env + depends_on: + - kafka + tb-coap-transport2: + restart: always + image: "${DOCKER_REPO}/${COAP_TRANSPORT_DOCKER_NAME}:${TB_VERSION}" + ports: + - "5683" + 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}" @@ -116,6 +159,16 @@ services: TB_PORT: 8080 env_file: - tb-web-ui.env + coap-lb: + restart: always + container_name: coap-loadbalancer + image: instantlinux/udp-nginx-proxy + ports: + - "5683:5683/udp" + environment: + BACKENDS: tb-coap-transport1 tb-coap-transport2 + PORT_BACKEND: 5683 + PORT_LISTEN: 5683 web: restart: always container_name: haproxy-certbot @@ -140,3 +193,5 @@ services: - tb-web-ui2 - tb-mqtt-transport1 - tb-mqtt-transport2 + - tb-http-transport1 + - tb-http-transport2 diff --git a/msa/docker/haproxy/config/haproxy.cfg b/msa/docker/haproxy/config/haproxy.cfg index d334c9272b..b8c3ced4cf 100644 --- a/msa/docker/haproxy/config/haproxy.cfg +++ b/msa/docker/haproxy/config/haproxy.cfg @@ -39,6 +39,7 @@ frontend http-in 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 @@ -72,3 +73,10 @@ backend tb-mqtt-backend option log-health-checks server tbMqtt1 tb-mqtt-transport1:1883 check server tbMqtt2 tb-mqtt-transport2:1883 check + +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 diff --git a/msa/docker/tb-coap-transport.env b/msa/docker/tb-coap-transport.env new file mode 100644 index 0000000000..b3331fe849 --- /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 diff --git a/msa/docker/tb-http-transport.env b/msa/docker/tb-http-transport.env new file mode 100644 index 0000000000..33c479104e --- /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 diff --git a/msa/transport/coap/docker/Dockerfile b/msa/transport/coap/docker/Dockerfile new file mode 100644 index 0000000000..dcaef3a358 --- /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"] diff --git a/msa/transport/coap/docker/logback.xml b/msa/transport/coap/docker/logback.xml new file mode 100644 index 0000000000..e9d86928bd --- /dev/null +++ b/msa/transport/coap/docker/logback.xml @@ -0,0 +1,50 @@ + + + + + + + /var/log/${pkg.name}/${pkg.name}.log + + /var/log/${pkg.name}/${pkg.name}.%d{yyyy-MM-dd}.%i.log + 100MB + 30 + 3GB + + + %d{ISO8601} [%thread] %-5level %logger{36} - %msg%n + + + + + + %d{ISO8601} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + + + + \ 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 0000000000..43d4602230 --- /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 0000000000..6d4c54aad1 --- /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 diff --git a/msa/transport/coap/pom.xml b/msa/transport/coap/pom.xml new file mode 100644 index 0000000000..07c4e63342 --- /dev/null +++ b/msa/transport/coap/pom.xml @@ -0,0 +1,137 @@ + + + 4.0.0 + + org.thingsboard.msa + 2.2.0-SNAPSHOT + transport + + org.thingsboard.msa.transport + coap + pom + + ThingsBoard COAP Transport Microservice + https://thingsboard.io + ThingsBoard COAP Transport Microservice + + + UTF-8 + ${basedir}/../../.. + tb-coap-transport + tb-coap-transport + thingsboard + /var/log/${pkg.name} + /usr/share/${pkg.name} + + + + + org.thingsboard.transport + coap + ${project.version} + deb + deb + provided + + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + copy-tb-coap-transport-deb + package + + copy + + + + + org.thingsboard.transport + coap + deb + deb + ${pkg.name}.deb + ${project.build.directory} + + + + + + + + org.apache.maven.plugins + maven-resources-plugin + + + copy-docker-config + process-resources + + copy-resources + + + ${project.build.directory} + + + docker + true + + + + + + + + com.spotify + dockerfile-maven-plugin + + + build-docker-image + pre-integration-test + + build + + + + + ${dockerfile.skip} + ${docker.repo}/${docker.name} + ${project.version} + true + false + ${project.build.directory} + + + + + + + jenkins + Jenkins Repository + http://repo.jenkins-ci.org/releases + + false + + + + diff --git a/msa/transport/http/docker/Dockerfile b/msa/transport/http/docker/Dockerfile new file mode 100644 index 0000000000..212047fa42 --- /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"] diff --git a/msa/transport/http/docker/logback.xml b/msa/transport/http/docker/logback.xml new file mode 100644 index 0000000000..e9d86928bd --- /dev/null +++ b/msa/transport/http/docker/logback.xml @@ -0,0 +1,50 @@ + + + + + + + /var/log/${pkg.name}/${pkg.name}.log + + /var/log/${pkg.name}/${pkg.name}.%d{yyyy-MM-dd}.%i.log + 100MB + 30 + 3GB + + + %d{ISO8601} [%thread] %-5level %logger{36} - %msg%n + + + + + + %d{ISO8601} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + + + + \ 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 0000000000..667988fa42 --- /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 0000000000..6d4c54aad1 --- /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 diff --git a/msa/transport/http/pom.xml b/msa/transport/http/pom.xml new file mode 100644 index 0000000000..22eb299dd5 --- /dev/null +++ b/msa/transport/http/pom.xml @@ -0,0 +1,137 @@ + + + 4.0.0 + + org.thingsboard.msa + 2.2.0-SNAPSHOT + transport + + org.thingsboard.msa.transport + http + pom + + ThingsBoard HTTP Transport Microservice + https://thingsboard.io + ThingsBoard HTTP Transport Microservice + + + UTF-8 + ${basedir}/../../.. + tb-http-transport + tb-http-transport + thingsboard + /var/log/${pkg.name} + /usr/share/${pkg.name} + + + + + org.thingsboard.transport + http + ${project.version} + deb + deb + provided + + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + copy-tb-http-transport-deb + package + + copy + + + + + org.thingsboard.transport + http + deb + deb + ${pkg.name}.deb + ${project.build.directory} + + + + + + + + org.apache.maven.plugins + maven-resources-plugin + + + copy-docker-config + process-resources + + copy-resources + + + ${project.build.directory} + + + docker + true + + + + + + + + com.spotify + dockerfile-maven-plugin + + + build-docker-image + pre-integration-test + + build + + + + + ${dockerfile.skip} + ${docker.repo}/${docker.name} + ${project.version} + true + false + ${project.build.directory} + + + + + + + jenkins + Jenkins Repository + http://repo.jenkins-ci.org/releases + + false + + + + diff --git a/msa/transport/pom.xml b/msa/transport/pom.xml index 7fc7b34c0d..9f091e3d20 100644 --- a/msa/transport/pom.xml +++ b/msa/transport/pom.xml @@ -36,6 +36,8 @@ mqtt + http + coap diff --git a/transport/http/src/main/java/org/thingsboard/server/mqtt/ThingsboardHttpTransportApplication.java b/transport/http/src/main/java/org/thingsboard/server/http/ThingsboardHttpTransportApplication.java similarity index 88% rename from transport/http/src/main/java/org/thingsboard/server/mqtt/ThingsboardHttpTransportApplication.java rename to transport/http/src/main/java/org/thingsboard/server/http/ThingsboardHttpTransportApplication.java index 29fae1bc6d..517794c346 100644 --- a/transport/http/src/main/java/org/thingsboard/server/mqtt/ThingsboardHttpTransportApplication.java +++ b/transport/http/src/main/java/org/thingsboard/server/http/ThingsboardHttpTransportApplication.java @@ -1,4 +1,4 @@ -package org.thingsboard.server.mqtt; /** +/** * Copyright © 2016-2018 The Thingsboard Authors * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -14,13 +14,12 @@ package org.thingsboard.server.mqtt; /** * limitations under the License. */ +package org.thingsboard.server.http; + import org.springframework.boot.SpringApplication; -import org.springframework.boot.SpringBootConfiguration; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.ComponentScan; import org.springframework.scheduling.annotation.EnableAsync; -import org.springframework.scheduling.annotation.EnableScheduling; import java.util.Arrays; 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 index 4740342692..2bae2ff855 100644 --- a/transport/mqtt/src/main/java/org/thingsboard/server/mqtt/ThingsboardMqttTransportApplication.java +++ b/transport/mqtt/src/main/java/org/thingsboard/server/mqtt/ThingsboardMqttTransportApplication.java @@ -1,4 +1,4 @@ -package org.thingsboard.server.mqtt; /** +/** * Copyright © 2016-2018 The Thingsboard Authors * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -14,6 +14,8 @@ package org.thingsboard.server.mqtt; /** * 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; From 7a764778e013519edf8a4e90d23a4b1a4def4e23 Mon Sep 17 00:00:00 2001 From: Andrew Shvayka Date: Thu, 18 Oct 2018 16:23:35 +0300 Subject: [PATCH 24/26] Fixed consumer id uniquness --- .../service/cluster/discovery/DiscoveryService.java | 2 -- .../service/cluster/discovery/DummyDiscoveryService.java | 8 -------- .../service/cluster/discovery/ZkDiscoveryService.java | 8 -------- .../server/service/script/RemoteJsInvokeService.java | 7 ++++--- .../transport/RemoteRuleEngineTransportService.java | 5 +++-- 5 files changed, 7 insertions(+), 23 deletions(-) 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 04c4cedabd..f9caafa412 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 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 859a7af4fc..358c8477d8 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 b4829f27aa..bb92642970 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")); @@ -183,11 +180,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/script/RemoteJsInvokeService.java b/application/src/main/java/org/thingsboard/server/service/script/RemoteJsInvokeService.java index b334ff15ae..db31bda942 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,8 +98,8 @@ public class RemoteJsInvokeService extends AbstractJsInvokeService { TBKafkaConsumerTemplate.TBKafkaConsumerTemplateBuilder 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); 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 index 4f8860daf7..38db1a415b 100644 --- a/application/src/main/java/org/thingsboard/server/service/transport/RemoteRuleEngineTransportService.java +++ b/application/src/main/java/org/thingsboard/server/service/transport/RemoteRuleEngineTransportService.java @@ -35,6 +35,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.TransportToDeviceAct 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; @@ -71,7 +72,7 @@ public class RemoteRuleEngineTransportService implements RuleEngineTransportServ private TbKafkaSettings kafkaSettings; @Autowired - private DiscoveryService discoveryService; + private TbNodeIdProvider nodeIdProvider; @Autowired private ActorSystemContext actorContext; @@ -104,7 +105,7 @@ public class RemoteRuleEngineTransportService implements RuleEngineTransportServ TBKafkaConsumerTemplate.TBKafkaConsumerTemplateBuilder ruleEngineConsumerBuilder = TBKafkaConsumerTemplate.builder(); ruleEngineConsumerBuilder.settings(kafkaSettings); ruleEngineConsumerBuilder.topic(ruleEngineTopic); - ruleEngineConsumerBuilder.clientId(discoveryService.getNodeId()); + ruleEngineConsumerBuilder.clientId("transport-" + nodeIdProvider.getNodeId()); ruleEngineConsumerBuilder.groupId("tb-node"); ruleEngineConsumerBuilder.autoCommit(true); ruleEngineConsumerBuilder.autoCommitIntervalMs(autoCommitInterval); From b2aed2f30736b36b6a5bfc61951c0f851871b8ab Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Thu, 18 Oct 2018 16:31:48 +0300 Subject: [PATCH 25/26] Fix compilation error --- .../transport/RemoteTransportApiService.java | 33 ++----------------- 1 file changed, 3 insertions(+), 30 deletions(-) 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 index ebd35a414e..6d422f4dd6 100644 --- a/application/src/main/java/org/thingsboard/server/service/transport/RemoteTransportApiService.java +++ b/application/src/main/java/org/thingsboard/server/service/transport/RemoteTransportApiService.java @@ -15,48 +15,21 @@ */ 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.Component; -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.GetOrCreateDeviceFromGatewayRequestMsg; -import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFromGatewayResponseMsg; -import org.thingsboard.server.gen.transport.TransportProtos.DeviceInfoProto; import org.thingsboard.server.gen.transport.TransportProtos.TransportApiRequestMsg; import org.thingsboard.server.gen.transport.TransportProtos.TransportApiResponseMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceTokenRequestMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceX509CertRequestMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceCredentialsResponseMsg; -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.state.DeviceStateService; +import org.thingsboard.server.kafka.*; 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.SynchronousQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; -import java.util.concurrent.locks.ReentrantLock; /** * Created by ashvayka on 05.10.18. @@ -83,7 +56,7 @@ public class RemoteTransportApiService { private TbKafkaSettings kafkaSettings; @Autowired - private DiscoveryService discoveryService; + private TbNodeIdProvider nodeIdProvider; @Autowired private TransportApiService transportApiService; @@ -104,7 +77,7 @@ public class RemoteTransportApiService { TBKafkaConsumerTemplate.TBKafkaConsumerTemplateBuilder requestBuilder = TBKafkaConsumerTemplate.builder(); requestBuilder.settings(kafkaSettings); requestBuilder.topic(transportApiRequestsTopic); - requestBuilder.clientId(discoveryService.getNodeId()); + requestBuilder.clientId(nodeIdProvider.getNodeId()); requestBuilder.groupId("tb-node"); requestBuilder.autoCommit(true); requestBuilder.autoCommitIntervalMs(autoCommitInterval); From a1d7715b211ce58520d6cbc4b7d902c4517cc282 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Thu, 18 Oct 2018 18:28:19 +0300 Subject: [PATCH 26/26] Update Docker loadbalancer config. Use signle instance COAP transport. --- msa/docker/docker-compose.yml | 25 ++---------------- msa/docker/haproxy/config/haproxy.cfg | 38 ++++++++++++++++----------- 2 files changed, 24 insertions(+), 39 deletions(-) diff --git a/msa/docker/docker-compose.yml b/msa/docker/docker-compose.yml index 0735443fc2..e3b56ae425 100644 --- a/msa/docker/docker-compose.yml +++ b/msa/docker/docker-compose.yml @@ -117,22 +117,11 @@ services: - tb-http-transport.env depends_on: - kafka - tb-coap-transport1: + tb-coap-transport: restart: always image: "${DOCKER_REPO}/${COAP_TRANSPORT_DOCKER_NAME}:${TB_VERSION}" ports: - - "5683" - environment: - TB_KAFKA_SERVERS: kafka:9092 - env_file: - - tb-coap-transport.env - depends_on: - - kafka - tb-coap-transport2: - restart: always - image: "${DOCKER_REPO}/${COAP_TRANSPORT_DOCKER_NAME}:${TB_VERSION}" - ports: - - "5683" + - "5683:5683/udp" environment: TB_KAFKA_SERVERS: kafka:9092 env_file: @@ -159,16 +148,6 @@ services: TB_PORT: 8080 env_file: - tb-web-ui.env - coap-lb: - restart: always - container_name: coap-loadbalancer - image: instantlinux/udp-nginx-proxy - ports: - - "5683:5683/udp" - environment: - BACKENDS: tb-coap-transport1 tb-coap-transport2 - PORT_BACKEND: 5683 - PORT_LISTEN: 5683 web: restart: always container_name: haproxy-certbot diff --git a/msa/docker/haproxy/config/haproxy.cfg b/msa/docker/haproxy/config/haproxy.cfg index b8c3ced4cf..cb70552a73 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,9 +31,22 @@ 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/ @@ -46,15 +60,14 @@ frontend http-in 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 - reqadd X-Forwarded-Proto:\ https + option forwardfor - default_backend tb-web-backend + reqadd X-Forwarded-Proto:\ https -frontend mqtt-in - mode tcp - bind *:${MQTT_PORT} + acl transport_http_acl path_beg /api/v1/ + use_backend tb-http-backend if transport_http_acl - default_backend tb-mqtt-backend + default_backend tb-web-backend backend letsencrypt_http server letsencrypt_http_srv 127.0.0.1:8080 @@ -67,13 +80,6 @@ backend tb-web-backend server tbWeb2 tb-web-ui2:8080 check http-request set-header X-Forwarded-Port %[dst_port] -backend tb-mqtt-backend - balance leastconn - option tcp-check - option log-health-checks - server tbMqtt1 tb-mqtt-transport1:1883 check - server tbMqtt2 tb-mqtt-transport2:1883 check - backend tb-http-backend balance leastconn option tcp-check