From bc591ce09fd718222a26ca89fc69f1304c0cada0 Mon Sep 17 00:00:00 2001 From: Andrew Shvayka Date: Thu, 4 Oct 2018 16:36:46 +0300 Subject: [PATCH 01/46] 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/46] 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/46] 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/46] 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/46] 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/46] 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/46] 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/46] 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/46] 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/46] 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/46] 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/46] 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/46] 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/46] 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/46] 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/46] 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/46] 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/46] 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 6bd8f1f9590e289a6d179a5a8820795ee02488ac Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Mon, 15 Oct 2018 21:56:30 +0300 Subject: [PATCH 19/46] Entity view updates --- application/pom.xml | 2 +- .../src/main/resources/thingsboard.yml | 4 +- common/data/pom.xml | 2 +- common/message/pom.xml | 2 +- common/pom.xml | 2 +- common/transport/pom.xml | 2 +- dao/pom.xml | 2 +- .../entityview/CassandraEntityViewDao.java | 3 +- .../dao/entityview/EntityViewServiceImpl.java | 2 +- .../dao/timeseries/BaseTimeseriesService.java | 20 ++++++- .../server/dao/SqlDaoServiceTestSuite.java | 2 +- .../dao/service/AbstractServiceTest.java | 4 ++ .../timeseries/BaseTimeseriesServiceTest.java | 51 +++++++++++++++- netty-mqtt/pom.xml | 4 +- pom.xml | 2 +- rule-engine/pom.xml | 2 +- rule-engine/rule-engine-api/pom.xml | 2 +- rule-engine/rule-engine-components/pom.xml | 2 +- tools/pom.xml | 2 +- transport/coap/pom.xml | 2 +- transport/http/pom.xml | 2 +- transport/mqtt/pom.xml | 2 +- transport/pom.xml | 2 +- ui/pom.xml | 2 +- .../entity-view/entity-view-fieldset.tpl.html | 58 +++++++++++++++++-- .../app/entity-view/entity-view.directive.js | 21 ++++++- ui/src/app/locale/locale.constant-en_US.json | 6 +- ui/src/app/locale/locale.constant-es_ES.json | 2 +- 28 files changed, 172 insertions(+), 37 deletions(-) diff --git a/application/pom.xml b/application/pom.xml index 72e7e7d7c2..c6503ef248 100644 --- a/application/pom.xml +++ b/application/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.1.2 + 2.1.3-SNAPSHOT thingsboard application diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 5eef1bdda0..b889143122 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -165,9 +165,9 @@ quota: database: entities: - type: "${DATABASE_ENTITIES_TYPE:sql}" # cassandra OR sql + type: "${DATABASE_ENTITIES_TYPE:cassandra}" # cassandra OR sql ts: - type: "${DATABASE_TS_TYPE:sql}" # cassandra OR sql (for hybrid mode, only this value should be cassandra) + type: "${DATABASE_TS_TYPE:cassandra}" # cassandra OR sql (for hybrid mode, only this value should be cassandra) # Cassandra driver configuration parameters diff --git a/common/data/pom.xml b/common/data/pom.xml index aa68b1adf6..f25e78de0e 100644 --- a/common/data/pom.xml +++ b/common/data/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.1.2 + 2.1.3-SNAPSHOT common org.thingsboard.common diff --git a/common/message/pom.xml b/common/message/pom.xml index 4da27e28f6..67648e9c4d 100644 --- a/common/message/pom.xml +++ b/common/message/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.1.2 + 2.1.3-SNAPSHOT common org.thingsboard.common diff --git a/common/pom.xml b/common/pom.xml index 4ee5937e7a..aba6d7f3a9 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.1.2 + 2.1.3-SNAPSHOT thingsboard org.thingsboard diff --git a/common/transport/pom.xml b/common/transport/pom.xml index efd63b5240..583fcd28f7 100644 --- a/common/transport/pom.xml +++ b/common/transport/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.1.2 + 2.1.3-SNAPSHOT common org.thingsboard.common diff --git a/dao/pom.xml b/dao/pom.xml index 8cc83e9393..e7d6783911 100644 --- a/dao/pom.xml +++ b/dao/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.1.2 + 2.1.3-SNAPSHOT thingsboard dao diff --git a/dao/src/main/java/org/thingsboard/server/dao/entityview/CassandraEntityViewDao.java b/dao/src/main/java/org/thingsboard/server/dao/entityview/CassandraEntityViewDao.java index fe1a8d1f8d..6ddc18d463 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/entityview/CassandraEntityViewDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/entityview/CassandraEntityViewDao.java @@ -84,8 +84,7 @@ public class CassandraEntityViewDao extends CassandraAbstractSearchTextDao>> futures = new ArrayList<>(); - if (savedEntityView.getKeys() != null) { + if (savedEntityView.getKeys() != null && savedEntityView.getKeys().getAttributes() != null) { futures.add(copyAttributesFromEntityToEntityView(savedEntityView, DataConstants.CLIENT_SCOPE, savedEntityView.getKeys().getAttributes().getCs())); futures.add(copyAttributesFromEntityToEntityView(savedEntityView, DataConstants.SERVER_SCOPE, savedEntityView.getKeys().getAttributes().getSs())); futures.add(copyAttributesFromEntityToEntityView(savedEntityView, DataConstants.SHARED_SCOPE, savedEntityView.getKeys().getAttributes().getSh())); diff --git a/dao/src/main/java/org/thingsboard/server/dao/timeseries/BaseTimeseriesService.java b/dao/src/main/java/org/thingsboard/server/dao/timeseries/BaseTimeseriesService.java index 4a09bb7a23..4658cbe490 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/timeseries/BaseTimeseriesService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/timeseries/BaseTimeseriesService.java @@ -79,12 +79,16 @@ public class BaseTimeseriesService implements TimeseriesService { if (entityId.getEntityType().equals(EntityType.ENTITY_VIEW)) { EntityView entityView = entityViewService.findEntityViewById((EntityViewId) entityId); List filteredKeys = new ArrayList<>(keys); - if (!entityView.getKeys().getTimeseries().isEmpty()) { + if (entityView.getKeys() != null && entityView.getKeys().getTimeseries() != null && + !entityView.getKeys().getTimeseries().isEmpty()) { filteredKeys.retainAll(entityView.getKeys().getTimeseries()); } List queries = filteredKeys.stream() - .map(key -> new BaseReadTsKvQuery(key, entityView.getStartTimeMs(), entityView.getEndTimeMs(), 1, "ASC")) + .map(key -> { + long endTs = entityView.getEndTimeMs() != 0 ? entityView.getEndTimeMs() : Long.MAX_VALUE; + return new BaseReadTsKvQuery(key, entityView.getStartTimeMs(), endTs, 1, "DESC"); + }) .collect(Collectors.toList()); if (queries.size() > 0) { @@ -100,7 +104,17 @@ public class BaseTimeseriesService implements TimeseriesService { @Override public ListenableFuture> findAllLatest(EntityId entityId) { validate(entityId); - return timeseriesDao.findAllLatest(entityId); + if (entityId.getEntityType().equals(EntityType.ENTITY_VIEW)) { + EntityView entityView = entityViewService.findEntityViewById((EntityViewId) entityId); + if (entityView.getKeys() != null && entityView.getKeys().getTimeseries() != null && + !entityView.getKeys().getTimeseries().isEmpty()) { + return findLatest(entityId, entityView.getKeys().getTimeseries()); + } else { + return Futures.immediateFuture(new ArrayList<>()); + } + } else { + return timeseriesDao.findAllLatest(entityId); + } } @Override diff --git a/dao/src/test/java/org/thingsboard/server/dao/SqlDaoServiceTestSuite.java b/dao/src/test/java/org/thingsboard/server/dao/SqlDaoServiceTestSuite.java index 9e56d64aba..0eaebfbbdc 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/SqlDaoServiceTestSuite.java +++ b/dao/src/test/java/org/thingsboard/server/dao/SqlDaoServiceTestSuite.java @@ -24,7 +24,7 @@ import java.util.Arrays; @RunWith(ClasspathSuite.class) @ClassnameFilters({ - "org.thingsboard.server.dao.service.*ServiceSqlTest" + "org.thingsboard.server.dao.service.*.TimeseriesServiceSqlTest" }) public class SqlDaoServiceTestSuite { diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/AbstractServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/AbstractServiceTest.java index 1e1f15daff..f49c357caa 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/AbstractServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/AbstractServiceTest.java @@ -45,6 +45,7 @@ import org.thingsboard.server.dao.customer.CustomerService; import org.thingsboard.server.dao.dashboard.DashboardService; import org.thingsboard.server.dao.device.DeviceCredentialsService; import org.thingsboard.server.dao.device.DeviceService; +import org.thingsboard.server.dao.entityview.EntityViewService; import org.thingsboard.server.dao.event.EventService; import org.thingsboard.server.dao.relation.RelationService; import org.thingsboard.server.dao.rule.RuleChainService; @@ -88,6 +89,9 @@ public abstract class AbstractServiceTest { @Autowired protected AssetService assetService; + @Autowired + protected EntityViewService entityViewService; + @Autowired protected DeviceCredentialsService deviceCredentialsService; diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/timeseries/BaseTimeseriesServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/timeseries/BaseTimeseriesServiceTest.java index 4528d9afb1..88f4d84eb6 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/timeseries/BaseTimeseriesServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/timeseries/BaseTimeseriesServiceTest.java @@ -17,9 +17,15 @@ package org.thingsboard.server.dao.service.timeseries; import com.datastax.driver.core.utils.UUIDs; import lombok.extern.slf4j.Slf4j; +import org.junit.After; import org.junit.Assert; +import org.junit.Before; import org.junit.Test; +import org.thingsboard.server.common.data.EntityView; +import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.kv.Aggregation; import org.thingsboard.server.common.data.kv.BaseDeleteTsKvQuery; import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery; @@ -30,6 +36,7 @@ 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.data.kv.TsKvEntry; +import org.thingsboard.server.common.data.objects.TelemetryEntityView; import org.thingsboard.server.dao.service.AbstractServiceTest; import java.util.ArrayList; @@ -61,6 +68,22 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest { KvEntry doubleKvEntry = new DoubleDataEntry(DOUBLE_KEY, Double.MAX_VALUE); KvEntry booleanKvEntry = new BooleanDataEntry(BOOLEAN_KEY, Boolean.TRUE); + private TenantId tenantId; + + @Before + public void before() { + Tenant tenant = new Tenant(); + tenant.setTitle("My tenant"); + Tenant savedTenant = tenantService.saveTenant(tenant); + Assert.assertNotNull(savedTenant); + tenantId = savedTenant.getId(); + } + + @After + public void after() { + tenantService.deleteTenant(tenantId); + } + @Test public void testFindAllLatest() throws Exception { DeviceId deviceId = new DeviceId(UUIDs.timeBased()); @@ -69,7 +92,15 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest { saveEntries(deviceId, TS - 1); saveEntries(deviceId, TS); - List tsList = tsService.findAllLatest(deviceId).get(); + testLatestTsAndVerify(deviceId); + + EntityView entityView = saveAndCreateEntityView(deviceId, Arrays.asList(STRING_KEY, DOUBLE_KEY, LONG_KEY, BOOLEAN_KEY)); + + testLatestTsAndVerify(entityView.getId()); + } + + private void testLatestTsAndVerify(EntityId entityId) throws ExecutionException, InterruptedException { + List tsList = tsService.findAllLatest(entityId).get(); assertNotNull(tsList); assertEquals(4, tsList.size()); @@ -89,6 +120,18 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest { assertEquals(expected, tsList); } + private EntityView saveAndCreateEntityView(DeviceId deviceId, List timeseries) { + EntityView entityView = new EntityView(); + entityView.setName("entity_view_name"); + entityView.setType("default"); + entityView.setTenantId(tenantId); + TelemetryEntityView keys = new TelemetryEntityView(); + keys.setTimeseries(timeseries); + entityView.setKeys(keys); + entityView.setEntityId(deviceId); + return entityViewService.saveEntityView(entityView); + } + @Test public void testFindLatest() throws Exception { DeviceId deviceId = new DeviceId(UUIDs.timeBased()); @@ -100,6 +143,12 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest { List entries = tsService.findLatest(deviceId, Collections.singleton(STRING_KEY)).get(); Assert.assertEquals(1, entries.size()); Assert.assertEquals(toTsEntry(TS, stringKvEntry), entries.get(0)); + + EntityView entityView = saveAndCreateEntityView(deviceId, Arrays.asList(STRING_KEY)); + + entries = tsService.findLatest(entityView.getId(), Collections.singleton(STRING_KEY)).get(); + Assert.assertEquals(1, entries.size()); + Assert.assertEquals(toTsEntry(TS, stringKvEntry), entries.get(0)); } @Test diff --git a/netty-mqtt/pom.xml b/netty-mqtt/pom.xml index f7e32b53e7..0b1ea1e35e 100644 --- a/netty-mqtt/pom.xml +++ b/netty-mqtt/pom.xml @@ -19,12 +19,12 @@ 4.0.0 org.thingsboard - 2.1.2 + 2.1.3-SNAPSHOT thingsboard org.thingsboard netty-mqtt - 2.1.2 + 2.1.3-SNAPSHOT jar Netty MQTT Client diff --git a/pom.xml b/pom.xml index 981482b35a..05ab0d6f39 100755 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard thingsboard - 2.1.2 + 2.1.3-SNAPSHOT pom Thingsboard diff --git a/rule-engine/pom.xml b/rule-engine/pom.xml index 5c5fe56075..3847120030 100644 --- a/rule-engine/pom.xml +++ b/rule-engine/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.1.2 + 2.1.3-SNAPSHOT thingsboard rule-engine diff --git a/rule-engine/rule-engine-api/pom.xml b/rule-engine/rule-engine-api/pom.xml index 4b4f33624f..4e79b11056 100644 --- a/rule-engine/rule-engine-api/pom.xml +++ b/rule-engine/rule-engine-api/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 2.1.2 + 2.1.3-SNAPSHOT rule-engine org.thingsboard.rule-engine diff --git a/rule-engine/rule-engine-components/pom.xml b/rule-engine/rule-engine-components/pom.xml index ebab903190..6e7cfdc6a7 100644 --- a/rule-engine/rule-engine-components/pom.xml +++ b/rule-engine/rule-engine-components/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 2.1.2 + 2.1.3-SNAPSHOT rule-engine org.thingsboard.rule-engine diff --git a/tools/pom.xml b/tools/pom.xml index 8a9481d0b9..af4c53b4a1 100644 --- a/tools/pom.xml +++ b/tools/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.1.2 + 2.1.3-SNAPSHOT thingsboard org.thingsboard diff --git a/transport/coap/pom.xml b/transport/coap/pom.xml index 537a9bee06..bcb45236c1 100644 --- a/transport/coap/pom.xml +++ b/transport/coap/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.1.2 + 2.1.3-SNAPSHOT transport org.thingsboard.transport diff --git a/transport/http/pom.xml b/transport/http/pom.xml index 24e4ff94af..d73692f260 100644 --- a/transport/http/pom.xml +++ b/transport/http/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.1.2 + 2.1.3-SNAPSHOT transport org.thingsboard.transport diff --git a/transport/mqtt/pom.xml b/transport/mqtt/pom.xml index f5c1104b6a..e89bba42b1 100644 --- a/transport/mqtt/pom.xml +++ b/transport/mqtt/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.1.2 + 2.1.3-SNAPSHOT transport org.thingsboard.transport diff --git a/transport/pom.xml b/transport/pom.xml index 7858383d82..22ec9be255 100644 --- a/transport/pom.xml +++ b/transport/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.1.2 + 2.1.3-SNAPSHOT thingsboard org.thingsboard diff --git a/ui/pom.xml b/ui/pom.xml index a2eadb5445..c219a22e6b 100644 --- a/ui/pom.xml +++ b/ui/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.1.2 + 2.1.3-SNAPSHOT thingsboard org.thingsboard diff --git a/ui/src/app/entity-view/entity-view-fieldset.tpl.html b/ui/src/app/entity-view/entity-view-fieldset.tpl.html index 66dc116cc8..74d1e3397c 100644 --- a/ui/src/app/entity-view/entity-view-fieldset.tpl.html +++ b/ui/src/app/entity-view/entity-view-fieldset.tpl.html @@ -78,31 +78,79 @@ ng-required="false" readonly="!isEdit" ng-model="entityView.keys.attributes.cs" - placeholder="{{'entity-view.client-attributes' | translate}}" + placeholder="{{'entity-view.client-attributes-placeholder' | translate}}" md-separator-keys="separatorKeys"> + + {{item}} + + + {{item}} + + + {{item}} + - + + + {{item}} +

diff --git a/ui/src/app/entity-view/entity-view.directive.js b/ui/src/app/entity-view/entity-view.directive.js index e1ae82fa6a..d183053f47 100644 --- a/ui/src/app/entity-view/entity-view.directive.js +++ b/ui/src/app/entity-view/entity-view.directive.js @@ -20,8 +20,8 @@ import entityViewFieldsetTemplate from './entity-view-fieldset.tpl.html'; /* eslint-enable import/no-unresolved, import/default */ /*@ngInject*/ -export default function EntityViewDirective($compile, $templateCache, $filter, toast, $translate, $mdConstant, - types, clipboardService, entityViewService, customerService) { +export default function EntityViewDirective($q, $compile, $templateCache, $filter, toast, $translate, $mdConstant, + types, clipboardService, entityViewService, customerService, entityService) { var linker = function (scope, element) { var template = $templateCache.get(entityViewFieldsetTemplate); element.html(template); @@ -53,9 +53,13 @@ export default function EntityViewDirective($compile, $templateCache, $filter, t } if (scope.entityView.startTimeMs > 0) { scope.startTimeMs = new Date(scope.entityView.startTimeMs); + } else { + scope.startTimeMs = null; } if (scope.entityView.endTimeMs > 0) { scope.endTimeMs = new Date(scope.entityView.endTimeMs); + } else { + scope.endTimeMs = null; } if (!scope.entityView.keys) { scope.entityView.keys = {}; @@ -68,6 +72,19 @@ export default function EntityViewDirective($compile, $templateCache, $filter, t } }); + scope.dataKeysSearch = function (searchText, type) { + var deferred = $q.defer(); + entityService.getEntityKeys(scope.entityView.entityId.entityType, scope.entityView.entityId.id, searchText, type, {ignoreLoading: true}).then( + function success(keys) { + deferred.resolve(keys); + }, + function fail() { + deferred.resolve([]); + } + ); + return deferred.promise; + + }; scope.$watch('startTimeMs', function (newDate) { if (newDate) { diff --git a/ui/src/app/locale/locale.constant-en_US.json b/ui/src/app/locale/locale.constant-en_US.json index 6c6ea438af..ccfcf659ac 100644 --- a/ui/src/app/locale/locale.constant-en_US.json +++ b/ui/src/app/locale/locale.constant-en_US.json @@ -844,7 +844,11 @@ "client-attributes": "Client attributes", "shared-attributes": "Shared attributes", "server-attributes": "Server attributes", - "latest-timeseries": "Latest timeseries", + "timeseries": "Timeseries", + "client-attributes-placeholder": "Client attributes", + "shared-attributes-placeholder": "Shared attributes", + "server-attributes-placeholder": "Server attributes", + "timeseries-placeholder": "Timeseries", "related-entity": "Related entity" }, "event": { diff --git a/ui/src/app/locale/locale.constant-es_ES.json b/ui/src/app/locale/locale.constant-es_ES.json index 285c589bd6..1b0a16939b 100644 --- a/ui/src/app/locale/locale.constant-es_ES.json +++ b/ui/src/app/locale/locale.constant-es_ES.json @@ -839,7 +839,7 @@ "client-attributes": "Client attributes", "shared-attributes": "Shared attributes", "server-attributes": "Server attributes", - "latest-timeseries": "Latest timeseries" + "timeseries": "Timeseries" }, "event": { "event-type": "Tipo de evento", From 59d249fc3cd29af0ba1b7cea48aec19e859928dd Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Mon, 15 Oct 2018 21:58:45 +0300 Subject: [PATCH 20/46] Entity view updates --- application/src/main/resources/thingsboard.yml | 4 ++-- .../org/thingsboard/server/dao/SqlDaoServiceTestSuite.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index b889143122..5eef1bdda0 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -165,9 +165,9 @@ quota: database: entities: - type: "${DATABASE_ENTITIES_TYPE:cassandra}" # cassandra OR sql + type: "${DATABASE_ENTITIES_TYPE:sql}" # cassandra OR sql ts: - type: "${DATABASE_TS_TYPE:cassandra}" # cassandra OR sql (for hybrid mode, only this value should be cassandra) + type: "${DATABASE_TS_TYPE:sql}" # cassandra OR sql (for hybrid mode, only this value should be cassandra) # Cassandra driver configuration parameters diff --git a/dao/src/test/java/org/thingsboard/server/dao/SqlDaoServiceTestSuite.java b/dao/src/test/java/org/thingsboard/server/dao/SqlDaoServiceTestSuite.java index 0eaebfbbdc..9e56d64aba 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/SqlDaoServiceTestSuite.java +++ b/dao/src/test/java/org/thingsboard/server/dao/SqlDaoServiceTestSuite.java @@ -24,7 +24,7 @@ import java.util.Arrays; @RunWith(ClasspathSuite.class) @ClassnameFilters({ - "org.thingsboard.server.dao.service.*.TimeseriesServiceSqlTest" + "org.thingsboard.server.dao.service.*ServiceSqlTest" }) public class SqlDaoServiceTestSuite { From 14be925c73420dd1a249e2a6a2bdfc434642d242 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Tue, 16 Oct 2018 13:20:18 +0300 Subject: [PATCH 21/46] Minor fixes --- .../DefaultTelemetrySubscriptionService.java | 12 ++++++++---- ui/package.json | 2 +- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java index fb9a160a2c..9906c57e6d 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java @@ -169,9 +169,13 @@ public class DefaultTelemetrySubscriptionService implements TelemetrySubscriptio Map keyStates; if (sub.getType().equals(TelemetryFeature.TIMESERIES) && !entityView.getKeys().getTimeseries().isEmpty()) { allKeys = false; - keyStates = sub.getKeyStates().entrySet() - .stream().filter(entry -> entityView.getKeys().getTimeseries().contains(entry.getKey())) - .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + if(sub.isAllKeys()) { + keyStates = entityView.getKeys().getTimeseries().stream().collect(Collectors.toMap(k -> k, k -> 0L)); + } else { + keyStates = sub.getKeyStates().entrySet() + .stream().filter(entry -> entityView.getKeys().getTimeseries().contains(entry.getKey())) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + } } else if (sub.getType().equals(TelemetryFeature.ATTRIBUTES)) { if (sub.getScope().equals(DataConstants.CLIENT_SCOPE) && !entityView.getKeys().getAttributes().getCs().isEmpty()) { allKeys = false; @@ -179,7 +183,7 @@ public class DefaultTelemetrySubscriptionService implements TelemetrySubscriptio } else if (sub.getScope().equals(DataConstants.SERVER_SCOPE) && !entityView.getKeys().getAttributes().getSs().isEmpty()) { allKeys = false; keyStates = filterMap(sub, entityView.getKeys().getAttributes().getSs()); - } else if (sub.getScope().equals(DataConstants.SERVER_SCOPE) && !entityView.getKeys().getAttributes().getSh().isEmpty()) { + } else if (sub.getScope().equals(DataConstants.SHARED_SCOPE) && !entityView.getKeys().getAttributes().getSh().isEmpty()) { allKeys = false; keyStates = filterMap(sub, entityView.getKeys().getAttributes().getSh()); } else { diff --git a/ui/package.json b/ui/package.json index 8c347c5ccb..ec473a84ab 100644 --- a/ui/package.json +++ b/ui/package.json @@ -1,7 +1,7 @@ { "name": "thingsboard", "private": true, - "version": "2.1.2", + "version": "2.1.3", "description": "Thingsboard UI", "licenses": [ { From 68eabb9ec20c9e0abae1398e5998b6ccd7e88a40 Mon Sep 17 00:00:00 2001 From: Andrew Shvayka Date: Tue, 16 Oct 2018 13:20:40 +0300 Subject: [PATCH 22/46] 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 acb94868355ac11b1dc6fd06e21b21561052e9b2 Mon Sep 17 00:00:00 2001 From: Andrew Shvayka Date: Tue, 16 Oct 2018 13:30:38 +0300 Subject: [PATCH 23/46] Fix for WS subscriptions --- .../DefaultTelemetrySubscriptionService.java | 43 ++++--------------- 1 file changed, 8 insertions(+), 35 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java index 9906c57e6d..7bd64544d8 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java @@ -143,7 +143,7 @@ public class DefaultTelemetrySubscriptionService implements TelemetrySubscriptio public void addLocalWsSubscription(String sessionId, EntityId entityId, SubscriptionState sub) { long startTime = 0L; long endTime = 0L; - if (entityId.getEntityType().equals(EntityType.ENTITY_VIEW)) { + if (entityId.getEntityType().equals(EntityType.ENTITY_VIEW) && TelemetryFeature.TIMESERIES.equals(sub.getType())) { EntityView entityView = entityViewService.findEntityViewById(new EntityViewId(entityId.getId())); entityId = entityView.getEntityId(); startTime = entityView.getStartTimeMs(); @@ -165,42 +165,15 @@ public class DefaultTelemetrySubscriptionService implements TelemetrySubscriptio } private SubscriptionState getUpdatedSubscriptionState(EntityId entityId, SubscriptionState sub, EntityView entityView) { - boolean allKeys; Map keyStates; - if (sub.getType().equals(TelemetryFeature.TIMESERIES) && !entityView.getKeys().getTimeseries().isEmpty()) { - allKeys = false; - if(sub.isAllKeys()) { - keyStates = entityView.getKeys().getTimeseries().stream().collect(Collectors.toMap(k -> k, k -> 0L)); - } else { - keyStates = sub.getKeyStates().entrySet() - .stream().filter(entry -> entityView.getKeys().getTimeseries().contains(entry.getKey())) - .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); - } - } else if (sub.getType().equals(TelemetryFeature.ATTRIBUTES)) { - if (sub.getScope().equals(DataConstants.CLIENT_SCOPE) && !entityView.getKeys().getAttributes().getCs().isEmpty()) { - allKeys = false; - keyStates = filterMap(sub, entityView.getKeys().getAttributes().getCs()); - } else if (sub.getScope().equals(DataConstants.SERVER_SCOPE) && !entityView.getKeys().getAttributes().getSs().isEmpty()) { - allKeys = false; - keyStates = filterMap(sub, entityView.getKeys().getAttributes().getSs()); - } else if (sub.getScope().equals(DataConstants.SHARED_SCOPE) && !entityView.getKeys().getAttributes().getSh().isEmpty()) { - allKeys = false; - keyStates = filterMap(sub, entityView.getKeys().getAttributes().getSh()); - } else { - allKeys = sub.isAllKeys(); - keyStates = sub.getKeyStates(); - } + if(sub.isAllKeys()) { + keyStates = entityView.getKeys().getTimeseries().stream().collect(Collectors.toMap(k -> k, k -> 0L)); } else { - allKeys = sub.isAllKeys(); - keyStates = sub.getKeyStates(); + keyStates = sub.getKeyStates().entrySet() + .stream().filter(entry -> entityView.getKeys().getTimeseries().contains(entry.getKey())) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); } - return new SubscriptionState(sub.getWsSessionId(), sub.getSubscriptionId(), entityId, sub.getType(), allKeys, keyStates, sub.getScope()); - } - - private Map filterMap(SubscriptionState sub, List allowedKeys) { - return sub.getKeyStates().entrySet() - .stream().filter(entry -> allowedKeys.contains(entry.getKey())) - .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + return new SubscriptionState(sub.getWsSessionId(), sub.getSubscriptionId(), entityId, sub.getType(), false, keyStates, sub.getScope()); } @Override @@ -471,7 +444,7 @@ public class DefaultTelemetrySubscriptionService implements TelemetrySubscriptio onLocalSubUpdate(entityId, s -> TelemetryFeature.ATTRIBUTES == s.getType() && (StringUtils.isEmpty(s.getScope()) || scope.equals(s.getScope())), s -> { List subscriptionUpdate = null; for (AttributeKvEntry kv : attributes) { - if (isInTimeRange(s, kv.getLastUpdateTs()) && (s.isAllKeys() || s.getKeyStates().containsKey(kv.getKey()))) { + if (s.isAllKeys() || s.getKeyStates().containsKey(kv.getKey())) { if (subscriptionUpdate == null) { subscriptionUpdate = new ArrayList<>(); } From cc1ad23c4e9244fa43d2217c245f6a4d4e39e8f8 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Tue, 16 Oct 2018 14:46:34 +0300 Subject: [PATCH 24/46] Improve Entity View UI --- .../app/entity-view/add-entity-view.tpl.html | 2 +- .../entity-view/entity-view-fieldset.tpl.html | 239 ++++++++++-------- .../app/entity-view/entity-view.directive.js | 9 +- ui/src/app/entity-view/entity-view.scss | 47 ++++ ui/src/app/locale/locale.constant-en_US.json | 8 +- 5 files changed, 200 insertions(+), 105 deletions(-) create mode 100644 ui/src/app/entity-view/entity-view.scss diff --git a/ui/src/app/entity-view/add-entity-view.tpl.html b/ui/src/app/entity-view/add-entity-view.tpl.html index 48a1788ed0..ebf1f1d341 100644 --- a/ui/src/app/entity-view/add-entity-view.tpl.html +++ b/ui/src/app/entity-view/add-entity-view.tpl.html @@ -15,7 +15,7 @@ limitations under the License. --> - +

diff --git a/ui/src/app/entity-view/entity-view-fieldset.tpl.html b/ui/src/app/entity-view/entity-view-fieldset.tpl.html index 74d1e3397c..140c199688 100644 --- a/ui/src/app/entity-view/entity-view-fieldset.tpl.html +++ b/ui/src/app/entity-view/entity-view-fieldset.tpl.html @@ -60,7 +60,7 @@ entity-type="types.entityType.entityView">
- +
+ + + +
{{ 'entity-view.attributes-propagation' | translate }}
+ + +
+ + +
{{ 'entity-view.attributes-propagation' | translate }}
+ + +
+ +
entity-view.attributes-propagation-hint
+ + + + {{item}} + + + + + + {{item}} + + + + + + {{item}} + + +
+
+
+ + +
{{ 'entity-view.timeseries-data' | translate }}
+ + +
+ + +
{{ 'entity-view.timeseries-data' | translate }}
+ + +
+ +
entity-view.timeseries-data-hint
+ + + + {{item}} + + +
+
+
+
+
+ + +
+
+ + +
-
- - - - {{item}} - - - - - - {{item}} - - - - - - {{item}} - - - - - - {{item}} - - -
-
-
- - -
-
- - -
-
diff --git a/ui/src/app/entity-view/entity-view.directive.js b/ui/src/app/entity-view/entity-view.directive.js index d183053f47..25377f4cb4 100644 --- a/ui/src/app/entity-view/entity-view.directive.js +++ b/ui/src/app/entity-view/entity-view.directive.js @@ -13,6 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +import './entity-view.scss'; + /* eslint-disable import/no-unresolved, import/default */ import entityViewFieldsetTemplate from './entity-view-fieldset.tpl.html'; @@ -20,12 +23,16 @@ import entityViewFieldsetTemplate from './entity-view-fieldset.tpl.html'; /* eslint-enable import/no-unresolved, import/default */ /*@ngInject*/ -export default function EntityViewDirective($q, $compile, $templateCache, $filter, toast, $translate, $mdConstant, +export default function EntityViewDirective($q, $compile, $templateCache, $filter, toast, $translate, $mdConstant, $mdExpansionPanel, types, clipboardService, entityViewService, customerService, entityService) { var linker = function (scope, element) { var template = $templateCache.get(entityViewFieldsetTemplate); element.html(template); + scope.attributesPanelId = (Math.random()*1000).toFixed(0); + scope.timeseriesPanelId = (Math.random()*1000).toFixed(0); + scope.$mdExpansionPanel = $mdExpansionPanel; + scope.types = types; scope.isAssignedToCustomer = false; scope.isPublic = false; diff --git a/ui/src/app/entity-view/entity-view.scss b/ui/src/app/entity-view/entity-view.scss new file mode 100644 index 0000000000..7b16b4a85d --- /dev/null +++ b/ui/src/app/entity-view/entity-view.scss @@ -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. + */ + +@import "../../scss/constants"; + +.tb-entity-view-panel-group { + .tb-panel-title { + min-width: 90px; + user-select: none; + + @media (min-width: $layout-breakpoint-sm) { + min-width: 180px; + } + } + + .tb-panel-prompt { + overflow: hidden; + font-size: 14px; + color: rgba(0, 0, 0, .87); + text-overflow: ellipsis; + white-space: nowrap; + } + + &.disabled { + .tb-panel-title, + .tb-panel-prompt { + color: rgba(0, 0, 0, .38); + } + } + + md-icon.md-expansion-panel-icon { + margin-right: 0; + } +} diff --git a/ui/src/app/locale/locale.constant-en_US.json b/ui/src/app/locale/locale.constant-en_US.json index ccfcf659ac..48e578ea13 100644 --- a/ui/src/app/locale/locale.constant-en_US.json +++ b/ui/src/app/locale/locale.constant-en_US.json @@ -838,7 +838,9 @@ "unable-entity-view-device-alias-text": "Device alias '{{entityViewAlias}}' can't be deleted as it used by the following widget(s):
{{widgetsList}}", "select-entity-view": "Select entity view", "make-public": "Make entity view public", + "start-date": "Start date", "start-ts": "Start time", + "end-date": "End date", "end-ts": "End time", "date-limits": "Date limits", "client-attributes": "Client attributes", @@ -849,7 +851,11 @@ "shared-attributes-placeholder": "Shared attributes", "server-attributes-placeholder": "Server attributes", "timeseries-placeholder": "Timeseries", - "related-entity": "Related entity" + "target-entity": "Target entity", + "attributes-propagation": "Attributes propagation", + "attributes-propagation-hint": "Entity View will automatically copy specified attributes from Target Entity each time you save or update this entity view. For performance reasons target entity attributes are not propagated to entity view on each attribute change. You can enable automatic propagation by configuring \"copy to view\" rule node in your rule chain and linking \"Post attributes\" and \"Attributes Updated\" messages to the new rule node.", + "timeseries-data": "Timeseries data", + "timeseries-data-hint": "Configure timeseries data keys of the target entity that will be accessible to the entity view. This timeseries data is read-only." }, "event": { "event-type": "Event type", From 438ee3bb640d66d0f33253407b76385024076456 Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Tue, 16 Oct 2018 15:26:00 +0300 Subject: [PATCH 25/46] Added DELETE attributes case handle --- .../TbCopyAttributesToEntityViewNode.java | 137 +++++++++++------- 1 file changed, 82 insertions(+), 55 deletions(-) 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 51386dc942..5d29a438f2 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 @@ -17,7 +17,9 @@ package org.thingsboard.rule.engine.action; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.ListenableFuture; +import com.google.gson.JsonElement; import com.google.gson.JsonParser; +import com.google.gson.JsonPrimitive; import lombok.extern.slf4j.Slf4j; import org.thingsboard.rule.engine.api.EmptyNodeConfiguration; import org.thingsboard.rule.engine.api.RuleNode; @@ -25,7 +27,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; @@ -37,12 +38,11 @@ import org.thingsboard.server.common.msg.session.SessionMsgType; import org.thingsboard.server.common.transport.adaptor.JsonConverter; import javax.annotation.Nullable; +import java.util.ArrayList; import java.util.List; import java.util.Set; -import java.util.concurrent.ExecutionException; import java.util.stream.Collectors; -import static org.thingsboard.rule.engine.api.TbRelationTypes.FAILURE; import static org.thingsboard.rule.engine.api.TbRelationTypes.SUCCESS; @Slf4j @@ -68,66 +68,93 @@ public class TbCopyAttributesToEntityViewNode implements TbNode { } @Override - public void onMsg(TbContext ctx, TbMsg msg) throws ExecutionException, InterruptedException, TbNodeException { - if (!msg.getMetaData().getData().isEmpty()) { - long now = System.currentTimeMillis(); - String scope = msg.getType().equals(SessionMsgType.POST_ATTRIBUTES_REQUEST.name()) ? - DataConstants.CLIENT_SCOPE : msg.getMetaData().getValue("scope"); + public void onMsg(TbContext ctx, TbMsg msg) { + if (DataConstants.ATTRIBUTES_UPDATED.equals(msg.getType()) || + DataConstants.ATTRIBUTES_DELETED.equals(msg.getType()) || + SessionMsgType.POST_ATTRIBUTES_REQUEST.name().equals(msg.getType())) { + if (!msg.getMetaData().getData().isEmpty()) { + long now = System.currentTimeMillis(); + String scope = msg.getType().equals(SessionMsgType.POST_ATTRIBUTES_REQUEST.name()) ? + DataConstants.CLIENT_SCOPE : msg.getMetaData().getValue("scope"); - ListenableFuture> entityViewsFuture = - ctx.getEntityViewService().findEntityViewsByTenantIdAndEntityIdAsync(ctx.getTenantId(), msg.getOriginator()); + ListenableFuture> entityViewsFuture = + ctx.getEntityViewService().findEntityViewsByTenantIdAndEntityIdAsync(ctx.getTenantId(), msg.getOriginator()); - DonAsynchron.withCallback(entityViewsFuture, - entityViews -> { - for (EntityView entityView : entityViews) { - long startTime = entityView.getStartTimeMs(); - 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(); - List filteredAttributes = - attributes.stream() - .filter(attr -> { - switch (scope) { - case DataConstants.CLIENT_SCOPE: - if (entityView.getKeys().getAttributes().getCs().isEmpty()) { - return true; - } - return entityView.getKeys().getAttributes().getCs().contains(attr.getKey()); - case DataConstants.SERVER_SCOPE: - if (entityView.getKeys().getAttributes().getSs().isEmpty()) { - return true; - } - return entityView.getKeys().getAttributes().getSs().contains(attr.getKey()); - case DataConstants.SHARED_SCOPE: - if (entityView.getKeys().getAttributes().getSh().isEmpty()) { - return true; - } - return entityView.getKeys().getAttributes().getSh().contains(attr.getKey()); + DonAsynchron.withCallback(entityViewsFuture, + entityViews -> { + for (EntityView entityView : entityViews) { + long startTime = entityView.getStartTimeMs(); + long endTime = entityView.getEndTimeMs(); + if ((endTime != 0 && endTime > now && startTime < now) || (endTime == 0 && startTime < now)) { + if (DataConstants.ATTRIBUTES_UPDATED.equals(msg.getType()) || + SessionMsgType.POST_ATTRIBUTES_REQUEST.name().equals(msg.getType())) { + Set attributes = + JsonConverter.convertToAttributes(new JsonParser().parse(msg.getData())).getAttributes(); + List filteredAttributes = + attributes.stream().filter(attr -> attributeContainsInEntityView(scope, attr.getKey(), entityView)).collect(Collectors.toList()); + ctx.getTelemetryService().saveAndNotify(entityView.getId(), scope, filteredAttributes, + new FutureCallback() { + @Override + public void onSuccess(@Nullable Void result) { + transformAndTellNext(ctx, msg, entityView); } - return false; - }).collect(Collectors.toList()); - ctx.getTelemetryService().saveAndNotify(entityView.getId(), scope, filteredAttributes, - new FutureCallback() { - @Override - public void onSuccess(@Nullable Void result) { - TbMsg updMsg = ctx.transformMsg(msg, msg.getType(), entityView.getId(), msg.getMetaData(), msg.getData()); - ctx.tellNext(updMsg, SUCCESS); - } - - @Override - public void onFailure(Throwable t) { - ctx.tellFailure(msg, t); + @Override + public void onFailure(Throwable t) { + ctx.tellFailure(msg, t); + } + }); + } else if (DataConstants.ATTRIBUTES_DELETED.equals(msg.getType())) { + List attributes = new ArrayList<>(); + for (JsonElement element : new JsonParser().parse(msg.getData()).getAsJsonObject().get("attributes").getAsJsonArray()) { + if (element.isJsonPrimitive()) { + JsonPrimitive value = element.getAsJsonPrimitive(); + if (value.isString()) { + attributes.add(value.getAsString()); + } } - }); + List filteredAttributes = + attributes.stream().filter(attr -> attributeContainsInEntityView(scope, attr, entityView)).collect(Collectors.toList()); + ctx.getAttributesService().removeAll(entityView.getId(), scope, filteredAttributes); + } + transformAndTellNext(ctx, msg, entityView); + } + } } - } - }, - t -> ctx.tellFailure(msg, t)); + }, + t -> ctx.tellFailure(msg, t)); + } else { + ctx.tellFailure(msg, new IllegalArgumentException("Message metadata is empty")); + } } else { - ctx.tellNext(msg, FAILURE); + ctx.tellFailure(msg, new IllegalArgumentException("Unsupported msg type [" + msg.getType() + "]")); + } + } + + private void transformAndTellNext(TbContext ctx, TbMsg msg, EntityView entityView) { + TbMsg updMsg = ctx.transformMsg(msg, msg.getType(), entityView.getId(), msg.getMetaData(), msg.getData()); + ctx.tellNext(updMsg, SUCCESS); + } + + private boolean attributeContainsInEntityView(String scope, String attrKey, EntityView entityView) { + switch (scope) { + case DataConstants.CLIENT_SCOPE: + if (entityView.getKeys().getAttributes().getCs().isEmpty()) { + return true; + } + return entityView.getKeys().getAttributes().getCs().contains(attrKey); + case DataConstants.SERVER_SCOPE: + if (entityView.getKeys().getAttributes().getSs().isEmpty()) { + return true; + } + return entityView.getKeys().getAttributes().getSs().contains(attrKey); + case DataConstants.SHARED_SCOPE: + if (entityView.getKeys().getAttributes().getSh().isEmpty()) { + return true; + } + return entityView.getKeys().getAttributes().getSh().contains(attrKey); } + return false; } @Override From 88a171d7df00e13ff75c2e55236a878622be28af Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Tue, 16 Oct 2018 15:27:10 +0300 Subject: [PATCH 26/46] Added DELETE attributes case handle --- .../engine/action/TbCopyAttributesToEntityViewNode.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 5d29a438f2..1ab76059f7 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 @@ -113,10 +113,10 @@ public class TbCopyAttributesToEntityViewNode implements TbNode { attributes.add(value.getAsString()); } } - List filteredAttributes = - attributes.stream().filter(attr -> attributeContainsInEntityView(scope, attr, entityView)).collect(Collectors.toList()); - ctx.getAttributesService().removeAll(entityView.getId(), scope, filteredAttributes); } + List filteredAttributes = + attributes.stream().filter(attr -> attributeContainsInEntityView(scope, attr, entityView)).collect(Collectors.toList()); + ctx.getAttributesService().removeAll(entityView.getId(), scope, filteredAttributes); transformAndTellNext(ctx, msg, entityView); } } From 8b1a9351f34717de9fc8f799fdee691a39ce9081 Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Tue, 16 Oct 2018 16:26:01 +0300 Subject: [PATCH 27/46] Fixed for entity view afte testing --- .../server/controller/BaseController.java | 8 ++ .../controller/EntityViewController.java | 85 +++++++++++++++++++ .../controller/TelemetryController.java | 6 -- .../server/dao/entity/BaseEntityService.java | 7 ++ .../dao/entityview/EntityViewServiceImpl.java | 45 ---------- .../TbCopyAttributesToEntityViewNode.java | 16 +--- ui/src/app/api/entity-view.service.js | 27 ------ ui/src/app/api/entity.service.js | 4 + 8 files changed, 108 insertions(+), 90 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/BaseController.java b/application/src/main/java/org/thingsboard/server/controller/BaseController.java index 889630b81f..154ed3adb2 100644 --- a/application/src/main/java/org/thingsboard/server/controller/BaseController.java +++ b/application/src/main/java/org/thingsboard/server/controller/BaseController.java @@ -51,6 +51,7 @@ import org.thingsboard.server.common.msg.TbMsgMetaData; import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg; import org.thingsboard.server.dao.alarm.AlarmService; import org.thingsboard.server.dao.asset.AssetService; +import org.thingsboard.server.dao.attributes.AttributesService; import org.thingsboard.server.dao.audit.AuditLogService; import org.thingsboard.server.dao.customer.CustomerService; import org.thingsboard.server.dao.dashboard.DashboardService; @@ -70,6 +71,7 @@ import org.thingsboard.server.exception.ThingsboardErrorResponseHandler; import org.thingsboard.server.service.component.ComponentDiscoveryService; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.state.DeviceStateService; +import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService; import javax.mail.MessagingException; import javax.servlet.http.HttpServletRequest; @@ -143,6 +145,12 @@ public abstract class BaseController { @Autowired protected EntityViewService entityViewService; + @Autowired + protected TelemetrySubscriptionService tsSubService; + + @Autowired + protected AttributesService attributesService; + @ExceptionHandler(ThingsboardException.class) public void handleThingsboardException(ThingsboardException ex, HttpServletResponse response) { errorResponseHandler.handle(ex, response); diff --git a/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java b/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java index a1ba1c63f8..69e28db65a 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java @@ -15,7 +15,10 @@ */ package org.thingsboard.server.controller; +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; +import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.PathVariable; @@ -26,7 +29,9 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; +import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg; import org.thingsboard.server.common.data.Customer; +import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityView; @@ -34,15 +39,24 @@ import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.entityview.EntityViewSearchQuery; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityViewId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.id.UUIDBased; +import org.thingsboard.server.common.data.kv.AttributeKvEntry; import org.thingsboard.server.common.data.page.TextPageData; import org.thingsboard.server.common.data.page.TextPageLink; +import org.thingsboard.server.common.msg.cluster.SendToClusterMsg; import org.thingsboard.server.dao.exception.IncorrectParameterException; import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.service.security.model.SecurityUser; +import javax.annotation.Nullable; +import java.util.ArrayList; +import java.util.Collection; import java.util.List; +import java.util.concurrent.ExecutionException; import java.util.stream.Collectors; import static org.thingsboard.server.controller.CustomerController.CUSTOMER_ID; @@ -52,6 +66,7 @@ import static org.thingsboard.server.controller.CustomerController.CUSTOMER_ID; */ @RestController @RequestMapping("/api") +@Slf4j public class EntityViewController extends BaseController { public static final String ENTITY_VIEW_ID = "entityViewId"; @@ -75,6 +90,20 @@ public class EntityViewController extends BaseController { try { entityView.setTenantId(getCurrentUser().getTenantId()); EntityView savedEntityView = checkNotNull(entityViewService.saveEntityView(entityView)); + List>> futures = new ArrayList<>(); + if (savedEntityView.getKeys() != null && savedEntityView.getKeys().getAttributes() != null) { + futures.add(copyAttributesFromEntityToEntityView(savedEntityView, DataConstants.CLIENT_SCOPE, savedEntityView.getKeys().getAttributes().getCs(), getCurrentUser())); + futures.add(copyAttributesFromEntityToEntityView(savedEntityView, DataConstants.SERVER_SCOPE, savedEntityView.getKeys().getAttributes().getSs(), getCurrentUser())); + futures.add(copyAttributesFromEntityToEntityView(savedEntityView, DataConstants.SHARED_SCOPE, savedEntityView.getKeys().getAttributes().getSh(), getCurrentUser())); + } + for (ListenableFuture> future : futures) { + try { + future.get(); + } catch (InterruptedException | ExecutionException e) { + throw new RuntimeException("Failed to copy attributes to entity view", e); + } + } + logEntityAction(savedEntityView.getId(), savedEntityView, null, entityView.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null); return savedEntityView; @@ -85,6 +114,62 @@ public class EntityViewController extends BaseController { } } + private ListenableFuture> copyAttributesFromEntityToEntityView(EntityView entityView, String scope, Collection keys, SecurityUser user) throws ThingsboardException { + EntityViewId entityId = entityView.getId(); + if (keys != null && !keys.isEmpty()) { + ListenableFuture> getAttrFuture = attributesService.find(entityView.getEntityId(), scope, keys); + return Futures.transform(getAttrFuture, attributeKvEntries -> { + List attributes; + if (attributeKvEntries != null && !attributeKvEntries.isEmpty()) { + attributes = + attributeKvEntries.stream() + .filter(attributeKvEntry -> { + long startTime = entityView.getStartTimeMs(); + long endTime = entityView.getEndTimeMs(); + long lastUpdateTs = attributeKvEntry.getLastUpdateTs(); + return startTime == 0 && endTime == 0 || + (endTime == 0 && startTime < lastUpdateTs) || + (startTime == 0 && endTime > lastUpdateTs) + ? true : startTime < lastUpdateTs && endTime > lastUpdateTs; + }).collect(Collectors.toList()); + tsSubService.saveAndNotify(entityId, scope, attributes, new FutureCallback() { + @Override + public void onSuccess(@Nullable Void tmp) { + try { + logAttributesUpdated(user, entityId, scope, attributes, null); + } catch (ThingsboardException e) { + log.error("Failed to log attribute updates", e); + } + if (entityId.getEntityType() == EntityType.ENTITY_VIEW) { + DeviceId deviceId = new DeviceId(entityId.getId()); + DeviceAttributesEventNotificationMsg notificationMsg = DeviceAttributesEventNotificationMsg.onUpdate( + user.getTenantId(), deviceId, scope, attributes); + actorService.onMsg(new SendToClusterMsg(deviceId, notificationMsg)); + } + } + + @Override + public void onFailure(Throwable t) { + try { + logAttributesUpdated(user, entityId, scope, attributes, t); + } catch (ThingsboardException e) { + log.error("Failed to log attribute updates", e); + } + } + }); + } + return null; + }); + } else { + return Futures.immediateFuture(null); + } + } + + private void logAttributesUpdated(SecurityUser user, EntityId entityId, String scope, List attributes, Throwable e) throws ThingsboardException { + logEntityAction(user, (UUIDBased & EntityId) entityId, null, null, ActionType.ATTRIBUTES_UPDATED, toException(e), + scope, attributes); + } + @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/entityView/{entityViewId}", method = RequestMethod.DELETE) @ResponseStatus(value = HttpStatus.OK) 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..9f92019550 100644 --- a/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java +++ b/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java @@ -93,15 +93,9 @@ import java.util.stream.Collectors; @Slf4j public class TelemetryController extends BaseController { - @Autowired - private AttributesService attributesService; - @Autowired private TimeseriesService tsService; - @Autowired - private TelemetrySubscriptionService tsSubService; - @Autowired private AccessValidator accessValidator; diff --git a/dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java b/dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java index 1b67ca0486..33a26997ca 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java @@ -29,6 +29,7 @@ import org.thingsboard.server.dao.asset.AssetService; import org.thingsboard.server.dao.customer.CustomerService; import org.thingsboard.server.dao.dashboard.DashboardService; import org.thingsboard.server.dao.device.DeviceService; +import org.thingsboard.server.dao.entityview.EntityViewService; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.tenant.TenantService; import org.thingsboard.server.dao.user.UserService; @@ -46,6 +47,9 @@ public class BaseEntityService extends AbstractEntityService implements EntitySe @Autowired private DeviceService deviceService; + @Autowired + private EntityViewService entityViewService; + @Autowired private TenantService tenantService; @@ -81,6 +85,9 @@ public class BaseEntityService extends AbstractEntityService implements EntitySe case DEVICE: hasName = deviceService.findDeviceByIdAsync(new DeviceId(entityId.getId())); break; + case ENTITY_VIEW: + hasName = entityViewService.findEntityViewByIdAsync(new EntityViewId(entityId.getId())); + break; case TENANT: hasName = tenantService.findTenantByIdAsync(new TenantId(entityId.getId())); break; diff --git a/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewServiceImpl.java index a44a4349a0..0cc6008ffb 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewServiceImpl.java @@ -105,21 +105,6 @@ public class EntityViewServiceImpl extends AbstractEntityService implements Enti log.trace("Executing save entity view [{}]", entityView); entityViewValidator.validate(entityView); EntityView savedEntityView = entityViewDao.save(entityView); - - List>> futures = new ArrayList<>(); - if (savedEntityView.getKeys() != null && savedEntityView.getKeys().getAttributes() != null) { - futures.add(copyAttributesFromEntityToEntityView(savedEntityView, DataConstants.CLIENT_SCOPE, savedEntityView.getKeys().getAttributes().getCs())); - futures.add(copyAttributesFromEntityToEntityView(savedEntityView, DataConstants.SERVER_SCOPE, savedEntityView.getKeys().getAttributes().getSs())); - futures.add(copyAttributesFromEntityToEntityView(savedEntityView, DataConstants.SHARED_SCOPE, savedEntityView.getKeys().getAttributes().getSh())); - } - for (ListenableFuture> future : futures) { - try { - future.get(); - } catch (InterruptedException | ExecutionException e) { - log.error("Failed to copy attributes to entity view", e); - throw new RuntimeException("Failed to copy attributes to entity view", e); - } - } return savedEntityView; } @@ -294,36 +279,6 @@ public class EntityViewServiceImpl extends AbstractEntityService implements Enti }); } - private ListenableFuture> copyAttributesFromEntityToEntityView(EntityView entityView, String scope, Collection keys) { - if (keys != null && !keys.isEmpty()) { - ListenableFuture> getAttrFuture = attributesService.find(entityView.getEntityId(), scope, keys); - return Futures.transform(getAttrFuture, attributeKvEntries -> { - List filteredAttributes = new ArrayList<>(); - if (attributeKvEntries != null && !attributeKvEntries.isEmpty()) { - filteredAttributes = - attributeKvEntries.stream() - .filter(attributeKvEntry -> { - long startTime = entityView.getStartTimeMs(); - long endTime = entityView.getEndTimeMs(); - long lastUpdateTs = attributeKvEntry.getLastUpdateTs(); - return startTime == 0 && endTime == 0 || - (endTime == 0 && startTime < lastUpdateTs) || - (startTime == 0 && endTime > lastUpdateTs) - ? true : startTime < lastUpdateTs && endTime > lastUpdateTs; - }).collect(Collectors.toList()); - } - try { - return attributesService.save(entityView.getId(), scope, filteredAttributes).get(); - } catch (InterruptedException | ExecutionException e) { - log.error("Failed to copy attributes to entity view", e); - throw new RuntimeException("Failed to copy attributes to entity view", e); - } - }); - } else { - return Futures.immediateFuture(null); - } - } - private DataValidator entityViewValidator = new DataValidator() { 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 1ab76059f7..fc0ff28749 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 @@ -116,8 +116,10 @@ public class TbCopyAttributesToEntityViewNode implements TbNode { } List filteredAttributes = attributes.stream().filter(attr -> attributeContainsInEntityView(scope, attr, entityView)).collect(Collectors.toList()); - ctx.getAttributesService().removeAll(entityView.getId(), scope, filteredAttributes); - transformAndTellNext(ctx, msg, entityView); + if (filteredAttributes != null && !filteredAttributes.isEmpty()) { + ctx.getAttributesService().removeAll(entityView.getId(), scope, filteredAttributes); + transformAndTellNext(ctx, msg, entityView); + } } } } @@ -139,19 +141,10 @@ public class TbCopyAttributesToEntityViewNode implements TbNode { private boolean attributeContainsInEntityView(String scope, String attrKey, EntityView entityView) { switch (scope) { case DataConstants.CLIENT_SCOPE: - if (entityView.getKeys().getAttributes().getCs().isEmpty()) { - return true; - } return entityView.getKeys().getAttributes().getCs().contains(attrKey); case DataConstants.SERVER_SCOPE: - if (entityView.getKeys().getAttributes().getSs().isEmpty()) { - return true; - } return entityView.getKeys().getAttributes().getSs().contains(attrKey); case DataConstants.SHARED_SCOPE: - if (entityView.getKeys().getAttributes().getSh().isEmpty()) { - return true; - } return entityView.getKeys().getAttributes().getSh().contains(attrKey); } return false; @@ -159,6 +152,5 @@ public class TbCopyAttributesToEntityViewNode implements TbNode { @Override public void destroy() { - } } diff --git a/ui/src/app/api/entity-view.service.js b/ui/src/app/api/entity-view.service.js index e34d3a4aee..23fefbc05b 100644 --- a/ui/src/app/api/entity-view.service.js +++ b/ui/src/app/api/entity-view.service.js @@ -27,7 +27,6 @@ function EntityViewService($http, $q, $window, userService, attributeService, cu deleteEntityView: deleteEntityView, getCustomerEntityViews: getCustomerEntityViews, getEntityView: getEntityView, - getEntityViews: getEntityViews, getTenantEntityViews: getTenantEntityViews, saveEntityView: saveEntityView, unassignEntityViewFromCustomer: unassignEntityViewFromCustomer, @@ -126,32 +125,6 @@ function EntityViewService($http, $q, $window, userService, attributeService, cu return deferred.promise; } - function getEntityViews(entityViewIds, config) { - var deferred = $q.defer(); - var ids = ''; - for (var i=0;i0) { - ids += ','; - } - ids += entityViewIds[i]; - } - var url = '/api/entityViews?entityViewIds=' + ids; - $http.get(url, config).then(function success(response) { - var entityViews = response.data; - entityViews.sort(function (entityView1, entityView2) { - var id1 = entityView1.id.id; - var id2 = entityView2.id.id; - var index1 = entityViewIds.indexOf(id1); - var index2 = entityViewIds.indexOf(id2); - return index1 - index2; - }); - deferred.resolve(entityViews); - }, function fail(response) { - deferred.reject(response.data); - }); - return deferred.promise; - } - function saveEntityView(entityView) { var deferred = $q.defer(); var url = '/api/entityView'; diff --git a/ui/src/app/api/entity.service.js b/ui/src/app/api/entity.service.js index 205abc9e4e..00c7ecf990 100644 --- a/ui/src/app/api/entity.service.js +++ b/ui/src/app/api/entity.service.js @@ -135,6 +135,10 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device case types.entityType.asset: promise = assetService.getAssets(entityIds, config); break; + case types.entityType.entityView: + promise = getEntitiesByIdsPromise( + (id) => entityViewService.getEntityView(id, config), entityIds); + break; case types.entityType.tenant: promise = getEntitiesByIdsPromise( (id) => tenantService.getTenant(id, config), entityIds); From 626d2ce194f5ca0beea5d78d32e89bcfc906210a Mon Sep 17 00:00:00 2001 From: vparomskiy Date: Tue, 16 Oct 2018 17:11:09 +0300 Subject: [PATCH 28/46] caffeine cache max size should include value collection size --- .../dao/cache/CaffeineCacheConfiguration.java | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/dao/src/main/java/org/thingsboard/server/dao/cache/CaffeineCacheConfiguration.java b/dao/src/main/java/org/thingsboard/server/dao/cache/CaffeineCacheConfiguration.java index 688dfb3f9f..fc2868e805 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/cache/CaffeineCacheConfiguration.java +++ b/dao/src/main/java/org/thingsboard/server/dao/cache/CaffeineCacheConfiguration.java @@ -18,6 +18,7 @@ package org.thingsboard.server.dao.cache; import com.github.benmanes.caffeine.cache.Caffeine; import com.github.benmanes.caffeine.cache.RemovalCause; import com.github.benmanes.caffeine.cache.Ticker; +import com.github.benmanes.caffeine.cache.Weigher; import lombok.Data; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; @@ -31,6 +32,7 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.Arrays; +import java.util.Collection; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; @@ -64,8 +66,9 @@ public class CaffeineCacheConfiguration { private CaffeineCache buildCache(String name, CacheSpecs cacheSpec) { final Caffeine caffeineBuilder = Caffeine.newBuilder() + .weigher(collectionSafeWeigher()) + .maximumWeight(cacheSpec.getMaxSize()) .expireAfterWrite(cacheSpec.getTimeToLiveInMinutes(), TimeUnit.MINUTES) - .maximumSize(cacheSpec.getMaxSize()) .ticker(ticker()); return new CaffeineCache(name, caffeineBuilder.build()); } @@ -80,4 +83,12 @@ public class CaffeineCacheConfiguration { return new PreviousDeviceCredentialsIdKeyGenerator(); } + private Weigher collectionSafeWeigher() { + return (Weigher) (key, value) -> { + if(value instanceof Collection) { + return ((Collection) value).size(); + } + return 1; + }; + } } From 840c04282779141ceba25dc8d755f45084be3aff Mon Sep 17 00:00:00 2001 From: vparomskiy Date: Tue, 16 Oct 2018 17:25:23 +0300 Subject: [PATCH 29/46] log tenantID/ruleEntityId when RuleChain is not active --- .../server/actors/shared/ComponentMsgProcessor.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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..c35b255af6 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 @@ -82,7 +82,8 @@ public abstract class ComponentMsgProcessor extends Abstract protected void checkActive() { if (state != ComponentLifecycleState.ACTIVE) { - throw new IllegalStateException("Rule chain is not active!"); + logger.warning("Rule chain is not active. Current state [{}] for processor [{}] tenant [{}]", state, tenantId, entityId); + throw new IllegalStateException("Rule chain is not active! " + entityId + " - " + tenantId); } } From ef9c432aec49205c9a403801f688859864c38f9a Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Tue, 16 Oct 2018 17:26:03 +0300 Subject: [PATCH 30/46] Removed unnecessary code --- .../thingsboard/server/controller/EntityViewController.java | 6 ------ 1 file changed, 6 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java b/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java index 69e28db65a..9b7ffd2d11 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java @@ -140,12 +140,6 @@ public class EntityViewController extends BaseController { } catch (ThingsboardException e) { log.error("Failed to log attribute updates", e); } - if (entityId.getEntityType() == EntityType.ENTITY_VIEW) { - DeviceId deviceId = new DeviceId(entityId.getId()); - DeviceAttributesEventNotificationMsg notificationMsg = DeviceAttributesEventNotificationMsg.onUpdate( - user.getTenantId(), deviceId, scope, attributes); - actorService.onMsg(new SendToClusterMsg(deviceId, notificationMsg)); - } } @Override From 591bc88c2ebf96671f963999de38c91d41700fce Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Tue, 16 Oct 2018 17:34:06 +0300 Subject: [PATCH 31/46] Docker: Improve kafka configuration. --- msa/docker/.env | 2 +- msa/docker/docker-compose.yml | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/msa/docker/.env b/msa/docker/.env index 111ff1e2b4..018d4ac7b7 100644 --- a/msa/docker/.env +++ b/msa/docker/.env @@ -7,4 +7,4 @@ WEB_UI_DOCKER_NAME=tb-web-ui TB_VERSION=2.2.0-SNAPSHOT -KAFKA_TOPICS=js.eval.requests:100:1 +KAFKA_TOPICS=js.eval.requests:100:1:delete --config=retention.ms=60000 --config=retention.bytes=1073741824 diff --git a/msa/docker/docker-compose.yml b/msa/docker/docker-compose.yml index 93c2ab08cd..91e6286d69 100644 --- a/msa/docker/docker-compose.yml +++ b/msa/docker/docker-compose.yml @@ -36,6 +36,9 @@ services: KAFKA_INTER_BROKER_LISTENER_NAME: INSIDE KAFKA_CREATE_TOPICS: "${KAFKA_TOPICS}" KAFKA_AUTO_CREATE_TOPICS_ENABLE: 'false' + KAFKA_LOG_RETENTION_BYTES: 1073741824 + KAFKA_LOG_RETENTION_MS: 300000 + KAFKA_LOG_CLEANUP_POLICY: delete depends_on: - zookeeper tb-js-executor: From 3aa3a56bab880111cde412e094f8692b2cbe0bbf Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Tue, 16 Oct 2018 18:01:46 +0300 Subject: [PATCH 32/46] Version set to 2.1.3 --- application/pom.xml | 2 +- common/data/pom.xml | 2 +- common/message/pom.xml | 2 +- common/pom.xml | 2 +- common/transport/pom.xml | 2 +- dao/pom.xml | 2 +- netty-mqtt/pom.xml | 4 ++-- pom.xml | 2 +- rule-engine/pom.xml | 2 +- rule-engine/rule-engine-api/pom.xml | 2 +- rule-engine/rule-engine-components/pom.xml | 2 +- tools/pom.xml | 2 +- transport/coap/pom.xml | 2 +- transport/http/pom.xml | 2 +- transport/mqtt/pom.xml | 2 +- transport/pom.xml | 2 +- ui/pom.xml | 2 +- 17 files changed, 18 insertions(+), 18 deletions(-) diff --git a/application/pom.xml b/application/pom.xml index c6503ef248..2945ddfcd7 100644 --- a/application/pom.xml +++ b/application/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.1.3-SNAPSHOT + 2.1.3 thingsboard application diff --git a/common/data/pom.xml b/common/data/pom.xml index f25e78de0e..b27b93c0f5 100644 --- a/common/data/pom.xml +++ b/common/data/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.1.3-SNAPSHOT + 2.1.3 common org.thingsboard.common diff --git a/common/message/pom.xml b/common/message/pom.xml index 67648e9c4d..18aa049307 100644 --- a/common/message/pom.xml +++ b/common/message/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.1.3-SNAPSHOT + 2.1.3 common org.thingsboard.common diff --git a/common/pom.xml b/common/pom.xml index aba6d7f3a9..f75fd89834 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.1.3-SNAPSHOT + 2.1.3 thingsboard org.thingsboard diff --git a/common/transport/pom.xml b/common/transport/pom.xml index 583fcd28f7..cc83bc0c03 100644 --- a/common/transport/pom.xml +++ b/common/transport/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.1.3-SNAPSHOT + 2.1.3 common org.thingsboard.common diff --git a/dao/pom.xml b/dao/pom.xml index e7d6783911..b6b6aa183b 100644 --- a/dao/pom.xml +++ b/dao/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.1.3-SNAPSHOT + 2.1.3 thingsboard dao diff --git a/netty-mqtt/pom.xml b/netty-mqtt/pom.xml index 0b1ea1e35e..d97013290a 100644 --- a/netty-mqtt/pom.xml +++ b/netty-mqtt/pom.xml @@ -19,12 +19,12 @@ 4.0.0 org.thingsboard - 2.1.3-SNAPSHOT + 2.1.3 thingsboard org.thingsboard netty-mqtt - 2.1.3-SNAPSHOT + 2.1.3 jar Netty MQTT Client diff --git a/pom.xml b/pom.xml index 05ab0d6f39..301b0e77ab 100755 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard thingsboard - 2.1.3-SNAPSHOT + 2.1.3 pom Thingsboard diff --git a/rule-engine/pom.xml b/rule-engine/pom.xml index 3847120030..60f7901b48 100644 --- a/rule-engine/pom.xml +++ b/rule-engine/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.1.3-SNAPSHOT + 2.1.3 thingsboard rule-engine diff --git a/rule-engine/rule-engine-api/pom.xml b/rule-engine/rule-engine-api/pom.xml index 4e79b11056..1dbf906d7d 100644 --- a/rule-engine/rule-engine-api/pom.xml +++ b/rule-engine/rule-engine-api/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 2.1.3-SNAPSHOT + 2.1.3 rule-engine org.thingsboard.rule-engine diff --git a/rule-engine/rule-engine-components/pom.xml b/rule-engine/rule-engine-components/pom.xml index 6e7cfdc6a7..d5bb024277 100644 --- a/rule-engine/rule-engine-components/pom.xml +++ b/rule-engine/rule-engine-components/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 2.1.3-SNAPSHOT + 2.1.3 rule-engine org.thingsboard.rule-engine diff --git a/tools/pom.xml b/tools/pom.xml index af4c53b4a1..c6115a7680 100644 --- a/tools/pom.xml +++ b/tools/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.1.3-SNAPSHOT + 2.1.3 thingsboard org.thingsboard diff --git a/transport/coap/pom.xml b/transport/coap/pom.xml index bcb45236c1..adceea8a43 100644 --- a/transport/coap/pom.xml +++ b/transport/coap/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.1.3-SNAPSHOT + 2.1.3 transport org.thingsboard.transport diff --git a/transport/http/pom.xml b/transport/http/pom.xml index d73692f260..0bcad5ed31 100644 --- a/transport/http/pom.xml +++ b/transport/http/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.1.3-SNAPSHOT + 2.1.3 transport org.thingsboard.transport diff --git a/transport/mqtt/pom.xml b/transport/mqtt/pom.xml index e89bba42b1..6df5738ff2 100644 --- a/transport/mqtt/pom.xml +++ b/transport/mqtt/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.1.3-SNAPSHOT + 2.1.3 transport org.thingsboard.transport diff --git a/transport/pom.xml b/transport/pom.xml index 22ec9be255..3a98387e85 100644 --- a/transport/pom.xml +++ b/transport/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.1.3-SNAPSHOT + 2.1.3 thingsboard org.thingsboard diff --git a/ui/pom.xml b/ui/pom.xml index c219a22e6b..7fe0bbad5e 100644 --- a/ui/pom.xml +++ b/ui/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.1.3-SNAPSHOT + 2.1.3 thingsboard org.thingsboard From e729494e82c35e2fa452300d5fecfe7d98bde043 Mon Sep 17 00:00:00 2001 From: vparomskiy Date: Wed, 17 Oct 2018 10:54:00 +0300 Subject: [PATCH 33/46] add alarm ack/clear event to the Rule Engine --- .../org/thingsboard/server/controller/BaseController.java | 6 ++++++ .../org/thingsboard/server/common/data/DataConstants.java | 2 ++ .../thingsboard/rule/engine/filter/TbMsgTypeSwitchNode.java | 6 +++++- 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/BaseController.java b/application/src/main/java/org/thingsboard/server/controller/BaseController.java index 889630b81f..951ee760ca 100644 --- a/application/src/main/java/org/thingsboard/server/controller/BaseController.java +++ b/application/src/main/java/org/thingsboard/server/controller/BaseController.java @@ -605,6 +605,12 @@ public abstract class BaseController { case ATTRIBUTES_DELETED: msgType = DataConstants.ATTRIBUTES_DELETED; break; + case ALARM_ACK: + msgType = DataConstants.ALARM_ACK; + break; + case ALARM_CLEAR: + msgType = DataConstants.ALARM_CLEAR; + break; } if (!StringUtils.isEmpty(msgType)) { try { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/DataConstants.java b/common/data/src/main/java/org/thingsboard/server/common/data/DataConstants.java index 34c14deb8c..307c59bdb9 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/DataConstants.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/DataConstants.java @@ -57,6 +57,8 @@ public class DataConstants { public static final String ENTITY_UNASSIGNED = "ENTITY_UNASSIGNED"; public static final String ATTRIBUTES_UPDATED = "ATTRIBUTES_UPDATED"; public static final String ATTRIBUTES_DELETED = "ATTRIBUTES_DELETED"; + public static final String ALARM_ACK = "ALARM_ACK"; + public static final String ALARM_CLEAR = "ALARM_CLEAR"; public static final String RPC_CALL_FROM_SERVER_TO_DEVICE = "RPC_CALL_FROM_SERVER_TO_DEVICE"; diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbMsgTypeSwitchNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbMsgTypeSwitchNode.java index f78b186b96..035a17ddc8 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbMsgTypeSwitchNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbMsgTypeSwitchNode.java @@ -30,7 +30,7 @@ import org.thingsboard.server.common.msg.session.SessionMsgType; configClazz = EmptyNodeConfiguration.class, relationTypes = {"Post attributes", "Post telemetry", "RPC Request from Device", "RPC Request to Device", "Activity Event", "Inactivity Event", "Connect Event", "Disconnect Event", "Entity Created", "Entity Updated", "Entity Deleted", "Entity Assigned", - "Entity Unassigned", "Attributes Updated", "Attributes Deleted", "Other"}, + "Entity Unassigned", "Attributes Updated", "Attributes Deleted", "Alarm Acknowledged", "Alarm Cleared", "Other"}, nodeDescription = "Route incoming messages by Message Type", nodeDetails = "Sends messages with message types \"Post attributes\", \"Post telemetry\", \"RPC Request\" etc. via corresponding chain, otherwise Other chain is used.", uiResources = {"static/rulenode/rulenode-core-config.js"}, @@ -75,6 +75,10 @@ public class TbMsgTypeSwitchNode implements TbNode { relationType = "Attributes Updated"; } else if (msg.getType().equals(DataConstants.ATTRIBUTES_DELETED)) { relationType = "Attributes Deleted"; + } else if (msg.getType().equals(DataConstants.ALARM_ACK)) { + relationType = "Alarm Acknowledged"; + } else if (msg.getType().equals(DataConstants.ALARM_CLEAR)) { + relationType = "Alarm Cleared"; } else if (msg.getType().equals(DataConstants.RPC_CALL_FROM_SERVER_TO_DEVICE)) { relationType = "RPC Request to Device"; } else { From 823b6aa7a722b6a992dfcca9d63bd78f38c9b4d9 Mon Sep 17 00:00:00 2001 From: Andrew Shvayka Date: Wed, 17 Oct 2018 18:59:52 +0300 Subject: [PATCH 34/46] 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 35/46] 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 36/46] 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 37/46] 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 38/46] 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 39/46] 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 40/46] 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 From f135b268affbd47054655937b700417ca5cbf48b Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Thu, 18 Oct 2018 18:54:22 +0300 Subject: [PATCH 41/46] Update jackson-databind version. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 684c3d02a4..0ac38cdf8b 100755 --- a/pom.xml +++ b/pom.xml @@ -50,7 +50,7 @@ 1.5.0 2.5 1.4 - 2.8.8.1 + 2.8.11.1 2.2.6 2.11 2.4.2 From 2638d1eaf7122927e04ec0bfd23e75a7c2563aa6 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Fri, 19 Oct 2018 13:39:46 +0300 Subject: [PATCH 42/46] Remove Msg queue. Fix tests. --- .../actors/shared/ComponentMsgProcessor.java | 1 - .../service/queue/DefaultMsgQueueService.java | 111 ------------- .../server/service/queue/MsgQueueService.java | 32 ---- .../src/main/resources/thingsboard.yml | 13 -- .../AbstractRuleEngineControllerTest.java | 5 - ...AbstractRuleEngineFlowIntegrationTest.java | 9 -- ...actRuleEngineLifecycleIntegrationTest.java | 69 -------- .../server/system/BaseHttpDeviceApiTest.java | 2 +- .../server/dao/queue/MsgQueue.java | 34 ---- .../server/dao/queue/QueueBenchmark.java | 152 ------------------ .../dao/queue/memory/InMemoryMsgKey.java | 29 ---- .../dao/queue/memory/InMemoryMsgQueue.java | 123 -------------- dao/src/test/resources/nosql-test.properties | 5 - 13 files changed, 1 insertion(+), 584 deletions(-) delete mode 100644 application/src/main/java/org/thingsboard/server/service/queue/DefaultMsgQueueService.java delete mode 100644 application/src/main/java/org/thingsboard/server/service/queue/MsgQueueService.java delete mode 100644 dao/src/main/java/org/thingsboard/server/dao/queue/MsgQueue.java delete mode 100644 dao/src/main/java/org/thingsboard/server/dao/queue/QueueBenchmark.java delete mode 100644 dao/src/main/java/org/thingsboard/server/dao/queue/memory/InMemoryMsgKey.java delete mode 100644 dao/src/main/java/org/thingsboard/server/dao/queue/memory/InMemoryMsgQueue.java 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 2c40be6fba..c9dc307451 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 @@ -26,7 +26,6 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.plugin.ComponentLifecycleState; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.cluster.ClusterEventMsg; -import org.thingsboard.server.service.queue.MsgQueueService; import javax.annotation.Nullable; import java.util.function.Consumer; 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 deleted file mode 100644 index 416a4d6b0f..0000000000 --- a/application/src/main/java/org/thingsboard/server/service/queue/DefaultMsgQueueService.java +++ /dev/null @@ -1,111 +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.service.queue; - -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.stereotype.Service; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.msg.TbMsg; -import org.thingsboard.server.common.transport.quota.tenant.TenantQuotaService; -import org.thingsboard.server.dao.queue.MsgQueue; - -import javax.annotation.PostConstruct; -import javax.annotation.PreDestroy; -import java.util.Map; -import java.util.UUID; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicLong; - -@Service -@Slf4j -public class DefaultMsgQueueService implements MsgQueueService { - - @Value("${actors.rule.queue.max_size}") - private long queueMaxSize; - - @Value("${actors.rule.queue.cleanup_period}") - private long queueCleanUpPeriod; - - @Autowired - private MsgQueue msgQueue; - - @Autowired(required = false) - private TenantQuotaService quotaService; - - private ScheduledExecutorService cleanupExecutor; - - private Map pendingCountPerTenant = new ConcurrentHashMap<>(); - - @PostConstruct - public void init() { - if (queueCleanUpPeriod > 0) { - cleanupExecutor = Executors.newSingleThreadScheduledExecutor(); - cleanupExecutor.scheduleAtFixedRate(() -> cleanup(), - queueCleanUpPeriod, queueCleanUpPeriod, TimeUnit.SECONDS); - } - } - - @PreDestroy - public void stop() { - if (cleanupExecutor != null) { - cleanupExecutor.shutdownNow(); - } - } - - @Override - public ListenableFuture put(TenantId tenantId, TbMsg msg, UUID nodeId, long clusterPartition) { - 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")); - } - - AtomicLong pendingMsgCount = pendingCountPerTenant.computeIfAbsent(tenantId, key -> new AtomicLong()); - if (pendingMsgCount.incrementAndGet() < queueMaxSize) { - return msgQueue.put(tenantId, msg, nodeId, clusterPartition); - } else { - pendingMsgCount.decrementAndGet(); - return Futures.immediateFailedFuture(new RuntimeException("Message queue is full!")); - } - } - - @Override - public ListenableFuture ack(TenantId tenantId, TbMsg msg, UUID nodeId, long clusterPartition) { - ListenableFuture result = msgQueue.ack(tenantId, msg, nodeId, clusterPartition); - AtomicLong pendingMsgCount = pendingCountPerTenant.computeIfAbsent(tenantId, key -> new AtomicLong()); - pendingMsgCount.decrementAndGet(); - return result; - } - - @Override - public Iterable findUnprocessed(TenantId tenantId, UUID nodeId, long clusterPartition) { - return msgQueue.findUnprocessed(tenantId, nodeId, clusterPartition); - } - - private void cleanup() { - pendingCountPerTenant.forEach((tenantId, pendingMsgCount) -> { - pendingMsgCount.set(0); - msgQueue.cleanUp(tenantId); - }); - } - -} diff --git a/application/src/main/java/org/thingsboard/server/service/queue/MsgQueueService.java b/application/src/main/java/org/thingsboard/server/service/queue/MsgQueueService.java deleted file mode 100644 index 2cf001ba73..0000000000 --- a/application/src/main/java/org/thingsboard/server/service/queue/MsgQueueService.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.service.queue; - -import com.google.common.util.concurrent.ListenableFuture; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.msg.TbMsg; - -import java.util.UUID; - -public interface MsgQueueService { - - ListenableFuture put(TenantId tenantId, TbMsg msg, UUID nodeId, long clusterPartition); - - ListenableFuture ack(TenantId tenantId, TbMsg msg, UUID nodeId, long clusterPartition); - - Iterable findUnprocessed(TenantId tenantId, UUID nodeId, long clusterPartition); - -} diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 90d15003bc..984fa280d6 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -182,12 +182,6 @@ cassandra: permit_max_wait_time: "${PERMIT_MAX_WAIT_TIME:120000}" rate_limit_print_interval_ms: "${CASSANDRA_QUERY_RATE_LIMIT_PRINT_MS:30000}" - queue: - msg.ttl: 604800 # 7 days - ack.ttl: 604800 # 7 days - partitions.ttl: 604800 # 7 days - partitioning: "HOURS" - # SQL configuration parameters sql: # Specify executor service type used to perform timeseries insert tasks: SINGLE FIXED CACHED @@ -221,13 +215,6 @@ actors: node: # Errors for particular actor are persisted once per specified amount of milliseconds error_persist_frequency: "${ACTORS_RULE_NODE_ERROR_FREQUENCY:3000}" - queue: - # Message queue type - type: "${ACTORS_RULE_QUEUE_TYPE:memory}" - # Message queue maximum size (per tenant) - max_size: "${ACTORS_RULE_QUEUE_MAX_SIZE:100}" - # Message queue cleanup period in seconds - cleanup_period: "${ACTORS_RULE_QUEUE_CLEANUP_PERIOD:3600}" statistics: # Enable/disable actor statistics enabled: "${ACTORS_STATISTICS_ENABLED:true}" diff --git a/application/src/test/java/org/thingsboard/server/controller/AbstractRuleEngineControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/AbstractRuleEngineControllerTest.java index 1b042a821c..cb31a4b50c 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AbstractRuleEngineControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AbstractRuleEngineControllerTest.java @@ -27,9 +27,7 @@ import org.thingsboard.server.common.data.page.TimePageData; import org.thingsboard.server.common.data.page.TimePageLink; import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChainMetaData; -import org.thingsboard.server.dao.queue.MsgQueue; import org.thingsboard.server.dao.rule.RuleChainService; -import org.thingsboard.server.service.queue.MsgQueueService; import java.io.IOException; import java.util.function.Predicate; @@ -42,9 +40,6 @@ public class AbstractRuleEngineControllerTest extends AbstractControllerTest { @Autowired protected RuleChainService ruleChainService; - @Autowired - protected MsgQueueService msgQueueService; - protected RuleChain saveRuleChain(RuleChain ruleChain) throws Exception { return doPost("/api/ruleChain", ruleChain, RuleChain.class); } diff --git a/application/src/test/java/org/thingsboard/server/rules/flow/AbstractRuleEngineFlowIntegrationTest.java b/application/src/test/java/org/thingsboard/server/rules/flow/AbstractRuleEngineFlowIntegrationTest.java index c86d49609e..f050f3b505 100644 --- a/application/src/test/java/org/thingsboard/server/rules/flow/AbstractRuleEngineFlowIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/rules/flow/AbstractRuleEngineFlowIntegrationTest.java @@ -191,9 +191,6 @@ public abstract class AbstractRuleEngineFlowIntegrationTest extends AbstractRule Assert.assertEquals("serverAttributeValue1", getMetadata(outEvent).get("ss_serverAttributeKey1").asText()); Assert.assertEquals("serverAttributeValue2", getMetadata(outEvent).get("ss_serverAttributeKey2").asText()); - - List unAckMsgList = Lists.newArrayList(msgQueueService.findUnprocessed(savedTenant.getId(), ruleChain.getId().getId(), 0L)); - Assert.assertEquals(0, unAckMsgList.size()); } @Test @@ -311,12 +308,6 @@ public abstract class AbstractRuleEngineFlowIntegrationTest extends AbstractRule Assert.assertEquals("serverAttributeValue1", getMetadata(outEvent).get("ss_serverAttributeKey1").asText()); Assert.assertEquals("serverAttributeValue2", getMetadata(outEvent).get("ss_serverAttributeKey2").asText()); - - List unAckMsgList = Lists.newArrayList(msgQueueService.findUnprocessed(savedTenant.getId(), rootRuleChain.getId().getId(), 0L)); - Assert.assertEquals(0, unAckMsgList.size()); - - unAckMsgList = Lists.newArrayList(msgQueueService.findUnprocessed(savedTenant.getId(), secondaryRuleChain.getId().getId(), 0L)); - Assert.assertEquals(0, unAckMsgList.size()); } } diff --git a/application/src/test/java/org/thingsboard/server/rules/lifecycle/AbstractRuleEngineLifecycleIntegrationTest.java b/application/src/test/java/org/thingsboard/server/rules/lifecycle/AbstractRuleEngineLifecycleIntegrationTest.java index 7ac0789383..24db4570bd 100644 --- a/application/src/test/java/org/thingsboard/server/rules/lifecycle/AbstractRuleEngineLifecycleIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/rules/lifecycle/AbstractRuleEngineLifecycleIntegrationTest.java @@ -162,73 +162,4 @@ public abstract class AbstractRuleEngineLifecycleIntegrationTest extends Abstrac Assert.assertEquals("serverAttributeValue", getMetadata(outEvent).get("ss_serverAttributeKey").asText()); } - @Test - public void testRuleChainWithOneRuleAndMsgFromQueue() throws Exception { - // Creating Rule Chain - RuleChain ruleChain = new RuleChain(); - ruleChain.setName("Simple Rule Chain"); - ruleChain.setTenantId(savedTenant.getId()); - ruleChain.setRoot(true); - ruleChain.setDebugMode(true); - ruleChain = saveRuleChain(ruleChain); - Assert.assertNull(ruleChain.getFirstRuleNodeId()); - - // Saving the device - Device device = new Device(); - device.setName("My device"); - device.setType("default"); - device = doPost("/api/device", device, Device.class); - - attributesService.save(device.getId(), DataConstants.SERVER_SCOPE, - Collections.singletonList(new BaseAttributeKvEntry(new StringDataEntry("serverAttributeKey", "serverAttributeValue"), System.currentTimeMillis()))); - - // Pushing Message to the system - TbMsg tbMsg = new TbMsg(UUIDs.timeBased(), - "CUSTOM", - device.getId(), - new TbMsgMetaData(), - "{}", - ruleChain.getId(), null, 0L); - msgQueueService.put(device.getTenantId(), tbMsg, ruleChain.getId().getId(), 0L); - - Thread.sleep(1000); - - RuleChainMetaData metaData = new RuleChainMetaData(); - metaData.setRuleChainId(ruleChain.getId()); - - RuleNode ruleNode = new RuleNode(); - ruleNode.setName("Simple Rule Node"); - ruleNode.setType(org.thingsboard.rule.engine.metadata.TbGetAttributesNode.class.getName()); - ruleNode.setDebugMode(true); - TbGetAttributesNodeConfiguration configuration = new TbGetAttributesNodeConfiguration(); - configuration.setServerAttributeNames(Collections.singletonList("serverAttributeKey")); - ruleNode.setConfiguration(mapper.valueToTree(configuration)); - - metaData.setNodes(Collections.singletonList(ruleNode)); - metaData.setFirstNodeIndex(0); - - metaData = saveRuleChainMetaData(metaData); - Assert.assertNotNull(metaData); - - ruleChain = getRuleChain(ruleChain.getId()); - Assert.assertNotNull(ruleChain.getFirstRuleNodeId()); - - Thread.sleep(3000); - - TimePageData eventsPage = getDebugEvents(savedTenant.getId(), ruleChain.getFirstRuleNodeId(), 1000); - List events = eventsPage.getData().stream().filter(filterByCustomEvent()).collect(Collectors.toList()); - - Assert.assertEquals(2, events.size()); - - Event inEvent = events.stream().filter(e -> e.getBody().get("type").asText().equals(DataConstants.IN)).findFirst().get(); - Assert.assertEquals(ruleChain.getFirstRuleNodeId(), inEvent.getEntityId()); - Assert.assertEquals(device.getId().getId().toString(), inEvent.getBody().get("entityId").asText()); - - Event outEvent = events.stream().filter(e -> e.getBody().get("type").asText().equals(DataConstants.OUT)).findFirst().get(); - Assert.assertEquals(ruleChain.getFirstRuleNodeId(), outEvent.getEntityId()); - Assert.assertEquals(device.getId().getId().toString(), outEvent.getBody().get("entityId").asText()); - - Assert.assertEquals("serverAttributeValue", getMetadata(outEvent).get("ss_serverAttributeKey").asText()); - } - } diff --git a/application/src/test/java/org/thingsboard/server/system/BaseHttpDeviceApiTest.java b/application/src/test/java/org/thingsboard/server/system/BaseHttpDeviceApiTest.java index 5e6896b2b7..1ebccadfea 100644 --- a/application/src/test/java/org/thingsboard/server/system/BaseHttpDeviceApiTest.java +++ b/application/src/test/java/org/thingsboard/server/system/BaseHttpDeviceApiTest.java @@ -57,7 +57,7 @@ public abstract class BaseHttpDeviceApiTest extends AbstractControllerTest { @Test public void testGetAttributes() throws Exception { doGetAsync("/api/v1/" + "WRONG_TOKEN" + "/attributes?clientKeys=keyA,keyB,keyC").andExpect(status().isUnauthorized()); - doGetAsync("/api/v1/" + deviceCredentials.getCredentialsId() + "/attributes?clientKeys=keyA,keyB,keyC").andExpect(status().isNotFound()); + doGetAsync("/api/v1/" + deviceCredentials.getCredentialsId() + "/attributes?clientKeys=keyA,keyB,keyC").andExpect(status().isOk()); Map attrMap = new HashMap<>(); attrMap.put("keyA", "valueA"); diff --git a/dao/src/main/java/org/thingsboard/server/dao/queue/MsgQueue.java b/dao/src/main/java/org/thingsboard/server/dao/queue/MsgQueue.java deleted file mode 100644 index 19021eb95c..0000000000 --- a/dao/src/main/java/org/thingsboard/server/dao/queue/MsgQueue.java +++ /dev/null @@ -1,34 +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.dao.queue; - -import com.google.common.util.concurrent.ListenableFuture; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.msg.TbMsg; - -import java.util.UUID; - -public interface MsgQueue { - - ListenableFuture put(TenantId tenantId, TbMsg msg, UUID nodeId, long clusterPartition); - - ListenableFuture ack(TenantId tenantId, TbMsg msg, UUID nodeId, long clusterPartition); - - Iterable findUnprocessed(TenantId tenantId, UUID nodeId, long clusterPartition); - - ListenableFuture cleanUp(TenantId tenantId); - -} diff --git a/dao/src/main/java/org/thingsboard/server/dao/queue/QueueBenchmark.java b/dao/src/main/java/org/thingsboard/server/dao/queue/QueueBenchmark.java deleted file mode 100644 index ca61a63a4c..0000000000 --- a/dao/src/main/java/org/thingsboard/server/dao/queue/QueueBenchmark.java +++ /dev/null @@ -1,152 +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.dao.queue; - -import com.datastax.driver.core.Cluster; -import com.datastax.driver.core.HostDistance; -import com.datastax.driver.core.PoolingOptions; -import com.datastax.driver.core.Session; -import com.datastax.driver.core.utils.UUIDs; -import com.google.common.util.concurrent.FutureCallback; -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.boot.CommandLineRunner; -import org.springframework.boot.SpringApplication; -import org.springframework.context.annotation.Bean; -import org.thingsboard.server.common.data.id.EntityId; -import org.thingsboard.server.common.data.id.RuleChainId; -import org.thingsboard.server.common.data.id.RuleNodeId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.msg.TbMsg; -import org.thingsboard.server.common.msg.TbMsgDataType; -import org.thingsboard.server.common.msg.TbMsgMetaData; - -import javax.annotation.Nullable; -import java.net.InetSocketAddress; -import java.util.UUID; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicLong; - -//@SpringBootApplication -//@EnableAutoConfiguration -//@ComponentScan({"org.thingsboard.rule.engine"}) -//@PropertySource("classpath:processing-pipeline.properties") -@Slf4j -public class QueueBenchmark implements CommandLineRunner { - - public static void main(String[] args) { - try { - SpringApplication.run(QueueBenchmark.class, args); - } catch (Throwable th) { - th.printStackTrace(); - System.exit(0); - } - } - - @Autowired - private MsgQueue msgQueue; - - @Override - public void run(String... strings) throws Exception { - System.out.println("It works + " + msgQueue); - - - long start = System.currentTimeMillis(); - int msgCount = 10000000; - AtomicLong count = new AtomicLong(0); - ExecutorService service = Executors.newFixedThreadPool(100); - - CountDownLatch latch = new CountDownLatch(msgCount); - for (int i = 0; i < msgCount; i++) { - service.submit(() -> { - boolean isFinished = false; - while (!isFinished) { - try { - TbMsg msg = randomMsg(); - UUID nodeId = UUIDs.timeBased(); - ListenableFuture put = msgQueue.put(new TenantId(EntityId.NULL_UUID), msg, nodeId, 100L); -// ListenableFuture put = msgQueue.ack(msg, nodeId, 100L); - Futures.addCallback(put, new FutureCallback() { - @Override - public void onSuccess(@Nullable Void result) { - latch.countDown(); - } - - @Override - public void onFailure(Throwable t) { -// t.printStackTrace(); - System.out.println("onFailure, because:" + t.getMessage()); - latch.countDown(); - } - }); - isFinished = true; - } catch (Throwable th) { -// th.printStackTrace(); - System.out.println("Repeat query, because:" + th.getMessage()); -// latch.countDown(); - } - } - }); - } - - long prev = 0L; - while (latch.getCount() != 0) { - TimeUnit.SECONDS.sleep(1); - long curr = latch.getCount(); - long rps = prev - curr; - prev = curr; - System.out.println("rps = " + rps); - } - - long end = System.currentTimeMillis(); - System.out.println("final rps = " + (msgCount / (end - start) * 1000)); - - System.out.println("Finished"); - - } - - private TbMsg randomMsg() { - TbMsgMetaData metaData = new TbMsgMetaData(); - metaData.putValue("key", "value"); - String dataStr = "someContent"; - return new TbMsg(UUIDs.timeBased(), "type", null, metaData, TbMsgDataType.JSON, dataStr, new RuleChainId(UUIDs.timeBased()), new RuleNodeId(UUIDs.timeBased()), 0L); - } - - @Bean - public Session session() { - Cluster thingsboard = Cluster.builder() - .addContactPointsWithPorts(new InetSocketAddress("127.0.0.1", 9042)) - .withClusterName("thingsboard") -// .withSocketOptions(socketOpts.getOpts()) - .withPoolingOptions(new PoolingOptions() - .setMaxRequestsPerConnection(HostDistance.LOCAL, 32768) - .setMaxRequestsPerConnection(HostDistance.REMOTE, 32768)).build(); - - Session session = thingsboard.connect("thingsboard"); - return session; - } - - @Bean - public int defaultTtl() { - return 6000; - } - -} diff --git a/dao/src/main/java/org/thingsboard/server/dao/queue/memory/InMemoryMsgKey.java b/dao/src/main/java/org/thingsboard/server/dao/queue/memory/InMemoryMsgKey.java deleted file mode 100644 index bb44a2b192..0000000000 --- a/dao/src/main/java/org/thingsboard/server/dao/queue/memory/InMemoryMsgKey.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.dao.queue.memory; - -import lombok.Data; - -import java.util.UUID; - -/** - * Created by ashvayka on 30.04.18. - */ -@Data -public final class InMemoryMsgKey { - final UUID nodeId; - final long clusterPartition; -} diff --git a/dao/src/main/java/org/thingsboard/server/dao/queue/memory/InMemoryMsgQueue.java b/dao/src/main/java/org/thingsboard/server/dao/queue/memory/InMemoryMsgQueue.java deleted file mode 100644 index 93057784f6..0000000000 --- a/dao/src/main/java/org/thingsboard/server/dao/queue/memory/InMemoryMsgQueue.java +++ /dev/null @@ -1,123 +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.dao.queue.memory; - -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListeningExecutorService; -import com.google.common.util.concurrent.MoreExecutors; -import lombok.extern.slf4j.Slf4j; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.stereotype.Component; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.msg.TbMsg; -import org.thingsboard.server.dao.queue.MsgQueue; - -import javax.annotation.PostConstruct; -import javax.annotation.PreDestroy; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.UUID; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Executors; - -/** - * Created by ashvayka on 27.04.18. - */ -@Component -@ConditionalOnProperty(prefix = "actors.rule.queue", value = "type", havingValue = "memory", matchIfMissing = true) -@Slf4j -public class InMemoryMsgQueue implements MsgQueue { - - private ListeningExecutorService queueExecutor; - private Map>> data = new HashMap<>(); - - @PostConstruct - public void init() { - // Should be always single threaded due to absence of locks. - queueExecutor = MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor()); - } - - @PreDestroy - public void stop() { - if (queueExecutor != null) { - queueExecutor.shutdownNow(); - } - } - - @Override - public ListenableFuture put(TenantId tenantId, TbMsg msg, UUID nodeId, long clusterPartition) { - return queueExecutor.submit(() -> { - data.computeIfAbsent(tenantId, key -> new HashMap<>()). - computeIfAbsent(new InMemoryMsgKey(nodeId, clusterPartition), key -> new HashMap<>()).put(msg.getId(), msg); - return null; - }); - } - - @Override - public ListenableFuture ack(TenantId tenantId, TbMsg msg, UUID nodeId, long clusterPartition) { - return queueExecutor.submit(() -> { - Map> tenantMap = data.get(tenantId); - if (tenantMap != null) { - InMemoryMsgKey key = new InMemoryMsgKey(nodeId, clusterPartition); - Map map = tenantMap.get(key); - if (map != null) { - map.remove(msg.getId()); - if (map.isEmpty()) { - tenantMap.remove(key); - } - } - if (tenantMap.isEmpty()) { - data.remove(tenantId); - } - } - return null; - }); - } - - @Override - public Iterable findUnprocessed(TenantId tenantId, UUID nodeId, long clusterPartition) { - ListenableFuture> list = queueExecutor.submit(() -> { - Map> tenantMap = data.get(tenantId); - if (tenantMap != null) { - InMemoryMsgKey key = new InMemoryMsgKey(nodeId, clusterPartition); - Map map = tenantMap.get(key); - if (map != null) { - return new ArrayList<>(map.values()); - } else { - return Collections.emptyList(); - } - } else { - return Collections.emptyList(); - } - }); - try { - return list.get(); - } catch (InterruptedException | ExecutionException e) { - throw new RuntimeException(e); - } - } - - @Override - public ListenableFuture cleanUp(TenantId tenantId) { - return queueExecutor.submit(() -> { - data.remove(tenantId); - return null; - }); - } -} diff --git a/dao/src/test/resources/nosql-test.properties b/dao/src/test/resources/nosql-test.properties index 06a92faa01..7c3ec51e8a 100644 --- a/dao/src/test/resources/nosql-test.properties +++ b/dao/src/test/resources/nosql-test.properties @@ -1,7 +1,2 @@ database.entities.type=cassandra database.ts.type=cassandra - -cassandra.queue.partitioning=HOURS -cassandra.queue.ack.ttl=3600 -cassandra.queue.msg.ttl=3600 -cassandra.queue.partitions.ttl=3600 \ No newline at end of file From a64d212335959d45fb4242cc4dac184dd941ea9a Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Fri, 19 Oct 2018 15:56:29 +0300 Subject: [PATCH 43/46] Fixed label --- ui/src/app/entity-view/entity-view-fieldset.tpl.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/app/entity-view/entity-view-fieldset.tpl.html b/ui/src/app/entity-view/entity-view-fieldset.tpl.html index 140c199688..766f8c82a1 100644 --- a/ui/src/app/entity-view/entity-view-fieldset.tpl.html +++ b/ui/src/app/entity-view/entity-view-fieldset.tpl.html @@ -121,7 +121,7 @@ md-items="item in dataKeysSearch(attributeDataKeySearchText, types.dataKeyType.attribute)" md-item-text="item.name" md-min-length="0" - placeholder="{{'entity-view.server-attributes-placeholder' | translate }}" + placeholder="{{'entity-view.shared-attributes-placeholder' | translate }}" md-menu-class="tb-attribute-datakey-autocomplete"> {{item}} From 78436f9baa4af41b114b8ff7e8a06bb1595cd141 Mon Sep 17 00:00:00 2001 From: Andrew Shvayka Date: Fri, 19 Oct 2018 17:59:48 +0300 Subject: [PATCH 44/46] Critical security fixes --- .../server/controller/BaseController.java | 36 +++++++------- .../service/security/ValidationCallback.java | 47 ++++++++++--------- .../DefaultTelemetryWebSocketService.java | 14 +++++- 3 files changed, 54 insertions(+), 43 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/BaseController.java b/application/src/main/java/org/thingsboard/server/controller/BaseController.java index 51a5e4c96c..582bbedebd 100644 --- a/application/src/main/java/org/thingsboard/server/controller/BaseController.java +++ b/application/src/main/java/org/thingsboard/server/controller/BaseController.java @@ -259,7 +259,6 @@ public abstract class BaseController { Customer checkCustomerId(CustomerId customerId) throws ThingsboardException { try { - validateId(customerId, "Incorrect customerId " + customerId); SecurityUser authUser = getCurrentUser(); if (authUser.getAuthority() == Authority.SYS_ADMIN || (authUser.getAuthority() != Authority.TENANT_ADMIN && @@ -267,9 +266,13 @@ public abstract class BaseController { throw new ThingsboardException(YOU_DON_T_HAVE_PERMISSION_TO_PERFORM_THIS_OPERATION, ThingsboardErrorCode.PERMISSION_DENIED); } - Customer customer = customerService.findCustomerById(customerId); - checkCustomer(customer); - return customer; + if (customerId != null && !customerId.isNullUid()) { + Customer customer = customerService.findCustomerById(customerId); + checkCustomer(customer); + return customer; + } else { + return null; + } } catch (Exception e) { throw handleException(e, false); } @@ -350,9 +353,7 @@ public abstract class BaseController { protected void checkDevice(Device device) throws ThingsboardException { checkNotNull(device); checkTenantId(device.getTenantId()); - if (device.getCustomerId() != null && !device.getCustomerId().getId().equals(ModelConstants.NULL_UUID)) { - checkCustomerId(device.getCustomerId()); - } + checkCustomerId(device.getCustomerId()); } protected EntityView checkEntityViewId(EntityViewId entityViewId) throws ThingsboardException { @@ -369,9 +370,7 @@ public abstract class BaseController { protected void checkEntityView(EntityView entityView) throws ThingsboardException { checkNotNull(entityView); checkTenantId(entityView.getTenantId()); - if (entityView.getCustomerId() != null && !entityView.getCustomerId().getId().equals(ModelConstants.NULL_UUID)) { - checkCustomerId(entityView.getCustomerId()); - } + checkCustomerId(entityView.getCustomerId()); } Asset checkAssetId(AssetId assetId) throws ThingsboardException { @@ -388,9 +387,7 @@ public abstract class BaseController { protected void checkAsset(Asset asset) throws ThingsboardException { checkNotNull(asset); checkTenantId(asset.getTenantId()); - if (asset.getCustomerId() != null && !asset.getCustomerId().getId().equals(ModelConstants.NULL_UUID)) { - checkCustomerId(asset.getCustomerId()); - } + checkCustomerId(asset.getCustomerId()); } Alarm checkAlarmId(AlarmId alarmId) throws ThingsboardException { @@ -499,8 +496,7 @@ public abstract class BaseController { ComponentDescriptor checkComponentDescriptorByClazz(String clazz) throws ThingsboardException { try { log.debug("[{}] Lookup component descriptor", clazz); - ComponentDescriptor componentDescriptor = checkNotNull(componentDescriptorService.getComponent(clazz)); - return componentDescriptor; + return checkNotNull(componentDescriptorService.getComponent(clazz)); } catch (Exception e) { throw handleException(e, false); } @@ -564,16 +560,16 @@ public abstract class BaseController { } protected I emptyId(EntityType entityType) { - return (I)EntityIdFactory.getByTypeAndUuid(entityType, ModelConstants.NULL_UUID); + return (I) EntityIdFactory.getByTypeAndUuid(entityType, ModelConstants.NULL_UUID); } protected void logEntityAction(I entityId, E entity, CustomerId customerId, - ActionType actionType, Exception e, Object... additionalInfo) throws ThingsboardException { + ActionType actionType, Exception e, Object... additionalInfo) throws ThingsboardException { logEntityAction(getCurrentUser(), entityId, entity, customerId, actionType, e, additionalInfo); } protected void logEntityAction(User user, I entityId, E entity, CustomerId customerId, - ActionType actionType, Exception e, Object... additionalInfo) throws ThingsboardException { + ActionType actionType, Exception e, Object... additionalInfo) throws ThingsboardException { if (customerId == null || customerId.isNullUid()) { customerId = user.getCustomerId(); } @@ -589,7 +585,7 @@ public abstract class BaseController { } private void pushEntityActionToRuleEngine(I entityId, E entity, User user, CustomerId customerId, - ActionType actionType, Object... additionalInfo) { + ActionType actionType, Object... additionalInfo) { String msgType = null; switch (actionType) { case ADDED: @@ -668,7 +664,7 @@ public abstract class BaseController { String scope = extractParameter(String.class, 0, additionalInfo); List keys = extractParameter(List.class, 1, additionalInfo); metaData.putValue("scope", scope); - ArrayNode attrsArrayNode = entityNode.putArray("attributes"); + ArrayNode attrsArrayNode = entityNode.putArray("attributes"); if (keys != null) { keys.forEach(attrsArrayNode::add); } diff --git a/application/src/main/java/org/thingsboard/server/service/security/ValidationCallback.java b/application/src/main/java/org/thingsboard/server/service/security/ValidationCallback.java index 81ab9b4eb6..d4ac75374f 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/ValidationCallback.java +++ b/application/src/main/java/org/thingsboard/server/service/security/ValidationCallback.java @@ -36,29 +36,10 @@ public class ValidationCallback implements FutureCallback { @Override public void onSuccess(ValidationResult result) { - ValidationResultCode resultCode = result.getResultCode(); - if (resultCode == ValidationResultCode.OK) { + if (result.getResultCode() == ValidationResultCode.OK) { action.onSuccess(response); } else { - Exception e; - switch (resultCode) { - case ENTITY_NOT_FOUND: - e = new EntityNotFoundException(result.getMessage()); - break; - case UNAUTHORIZED: - e = new UnauthorizedException(result.getMessage()); - break; - case ACCESS_DENIED: - e = new AccessDeniedException(result.getMessage()); - break; - case INTERNAL_ERROR: - e = new InternalErrorException(result.getMessage()); - break; - default: - e = new UnauthorizedException("Permission denied."); - break; - } - onFailure(e); + onFailure(getException(result)); } } @@ -66,4 +47,28 @@ public class ValidationCallback implements FutureCallback { public void onFailure(Throwable e) { action.onFailure(e); } + + public static Exception getException(ValidationResult result) { + ValidationResultCode resultCode = result.getResultCode(); + Exception e; + switch (resultCode) { + case ENTITY_NOT_FOUND: + e = new EntityNotFoundException(result.getMessage()); + break; + case UNAUTHORIZED: + e = new UnauthorizedException(result.getMessage()); + break; + case ACCESS_DENIED: + e = new AccessDeniedException(result.getMessage()); + break; + case INTERNAL_ERROR: + e = new InternalErrorException(result.getMessage()); + break; + default: + e = new UnauthorizedException("Permission denied."); + break; + } + return e; + } + } diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetryWebSocketService.java b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetryWebSocketService.java index 2ff8e89070..1251ca3b7e 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetryWebSocketService.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetryWebSocketService.java @@ -37,13 +37,18 @@ import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.dao.attributes.AttributesService; import org.thingsboard.server.dao.timeseries.TimeseriesService; import org.thingsboard.server.service.security.AccessValidator; +import org.thingsboard.server.service.security.ValidationCallback; import org.thingsboard.server.service.security.ValidationResult; +import org.thingsboard.server.service.security.ValidationResultCode; import org.thingsboard.server.service.telemetry.cmd.AttributesSubscriptionCmd; import org.thingsboard.server.service.telemetry.cmd.GetHistoryCmd; import org.thingsboard.server.service.telemetry.cmd.SubscriptionCmd; import org.thingsboard.server.service.telemetry.cmd.TelemetryPluginCmd; import org.thingsboard.server.service.telemetry.cmd.TelemetryPluginCmdsWrapper; import org.thingsboard.server.service.telemetry.cmd.TimeseriesSubscriptionCmd; +import org.thingsboard.server.service.telemetry.exception.AccessDeniedException; +import org.thingsboard.server.service.telemetry.exception.EntityNotFoundException; +import org.thingsboard.server.service.telemetry.exception.InternalErrorException; import org.thingsboard.server.service.telemetry.exception.UnauthorizedException; import org.thingsboard.server.service.telemetry.sub.SubscriptionErrorCode; import org.thingsboard.server.service.telemetry.sub.SubscriptionState; @@ -535,11 +540,16 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi }; } - private FutureCallback on(Consumer success, Consumer failure) { + private FutureCallback on(Consumer success, Consumer failure) { return new FutureCallback() { @Override public void onSuccess(@Nullable ValidationResult result) { - success.accept(result); + ValidationResultCode resultCode = result.getResultCode(); + if (resultCode == ValidationResultCode.OK) { + success.accept(null); + } else { + onFailure(ValidationCallback.getException(result)); + } } @Override From 39e6b904c17577e8e143ce5d8e2cf9e74b849c0c Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Sat, 20 Oct 2018 13:14:42 +0300 Subject: [PATCH 45/46] Fixed entity view cache evict --- .../server/dao/entityview/EntityViewServiceImpl.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewServiceImpl.java index 0cc6008ffb..2d94cc2eab 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewServiceImpl.java @@ -91,9 +91,6 @@ public class EntityViewServiceImpl extends AbstractEntityService implements Enti @Autowired private CustomerDao customerDao; - @Autowired - private AttributesService attributesService; - @Autowired private CacheManager cacheManager; @@ -108,6 +105,7 @@ public class EntityViewServiceImpl extends AbstractEntityService implements Enti return savedEntityView; } + @CacheEvict(cacheNames = ENTITY_VIEW_CACHE, key = "{#entityViewId}") @Override public EntityView assignEntityViewToCustomer(EntityViewId entityViewId, CustomerId customerId) { EntityView entityView = findEntityViewById(entityViewId); From b773ee0c66742cdef3223009a04845ef70858928 Mon Sep 17 00:00:00 2001 From: Andrew Shvayka Date: Mon, 22 Oct 2018 08:34:44 +0300 Subject: [PATCH 46/46] Rate limits draft --- .../transport/LocalTransportService.java | 32 +++++-- .../src/main/resources/thingsboard.yml | 2 +- .../transport/mqtt/MqttTransportHandler.java | 2 +- common/transport/transport-api/pom.xml | 4 + .../common/transport/TransportService.java | 2 + .../service/AbstractTransportService.java | 52 +++++++++- .../service/RemoteTransportService.java | 96 +++++++++++-------- .../service/TbRateLimitsException.java | 29 ++++++ .../service/TbTransportRateLimits.java | 53 ++++++++++ pom.xml | 6 ++ .../src/main/resources/tb-mqtt-transport.yml | 9 +- 11 files changed, 234 insertions(+), 53 deletions(-) create mode 100644 common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/TbRateLimitsException.java create mode 100644 common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/TbTransportRateLimits.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 d670db3c19..0f722306ff 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 @@ -134,42 +134,58 @@ public class LocalTransportService extends AbstractTransportService implements R @Override public void process(SessionInfoProto sessionInfo, SessionEventMsg msg, TransportServiceCallback callback) { - forwardToDeviceActor(TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setSessionEvent(msg).build(), callback); + if (checkLimits(sessionInfo, 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); + if (checkLimits(sessionInfo, 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); + if (checkLimits(sessionInfo, 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); + if (checkLimits(sessionInfo, 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); + if (checkLimits(sessionInfo, 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); + if (checkLimits(sessionInfo, 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); + if (checkLimits(sessionInfo, 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); + if (checkLimits(sessionInfo, callback)) { + forwardToDeviceActor(TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setToServerRPCCallRequest(msg).build(), callback); + } } @Override diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 984fa280d6..8d65e3dd33 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -444,7 +444,7 @@ transport: bind_port: "${MQTT_BIND_PORT:1883}" timeout: "${MQTT_TIMEOUT:10000}" netty: - leak_detector_level: "${NETTY_LEASK_DETECTOR_LVL:DISABLED}" + leak_detector_level: "${NETTY_LEAK_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}" 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 89ecdd583e..46a890f19e 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 @@ -249,7 +249,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement @Override public void onError(Throwable e) { log.trace("[{}] Failed to publish msg: {}", sessionId, msg, e); - ctx.close(); + processDisconnect(ctx); } }; } diff --git a/common/transport/transport-api/pom.xml b/common/transport/transport-api/pom.xml index 3538e468d4..4ed6ed784d 100644 --- a/common/transport/transport-api/pom.xml +++ b/common/transport/transport-api/pom.xml @@ -99,6 +99,10 @@ com.google.protobuf protobuf-java + + com.github.vladimir-bukhtoyarov + bucket4j-core + 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 caf178a641..a47438f30d 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 @@ -43,6 +43,8 @@ public interface TransportService { void process(TransportProtos.GetOrCreateDeviceFromGatewayRequestMsg msg, TransportServiceCallback callback); + boolean checkLimits(SessionInfoProto sessionInfo, TransportServiceCallback callback); + void process(SessionInfoProto sessionInfo, SessionEventMsg msg, TransportServiceCallback callback); void process(SessionInfoProto sessionInfo, PostTelemetryMsg msg, TransportServiceCallback callback); 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 e299a027e8..265dacb5de 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 @@ -16,8 +16,13 @@ package org.thingsboard.server.common.transport.service; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.TenantId; 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 java.util.UUID; @@ -36,9 +41,20 @@ import java.util.concurrent.TimeUnit; @Slf4j public abstract class AbstractTransportService implements TransportService { + @Value("${transport.rate_limits.enabled}") + private boolean rateLimitEnabled; + @Value("${transport.rate_limits.tenant}") + private String perTenantLimitsConf; + @Value("${transport.rate_limits.tenant}") + private String perDevicesLimitsConf; + protected ScheduledExecutorService schedulerExecutor; protected ExecutorService transportCallbackExecutor; - protected ConcurrentMap sessions = new ConcurrentHashMap<>(); + private ConcurrentMap sessions = new ConcurrentHashMap<>(); + + //TODO: Implement cleanup of this maps. + private ConcurrentMap perTenantLimits = new ConcurrentHashMap<>(); + private ConcurrentMap perDeviceLimits = new ConcurrentHashMap<>(); @Override public void registerAsyncSession(TransportProtos.SessionInfoProto sessionInfo, SessionMsgListener listener) { @@ -53,7 +69,6 @@ public abstract class AbstractTransportService implements TransportService { listener.onRemoteSessionCloseCommand(TransportProtos.SessionCloseNotificationProto.getDefaultInstance()); deregisterSession(sessionInfo); }, timeout, TimeUnit.MILLISECONDS); - } @Override @@ -61,6 +76,30 @@ public abstract class AbstractTransportService implements TransportService { sessions.remove(toId(sessionInfo)); } + @Override + public boolean checkLimits(TransportProtos.SessionInfoProto sessionInfo, TransportServiceCallback callback) { + if (!rateLimitEnabled) { + return true; + } + TenantId tenantId = new TenantId(new UUID(sessionInfo.getTenantIdMSB(), sessionInfo.getTenantIdLSB())); + TbTransportRateLimits rateLimits = perTenantLimits.computeIfAbsent(tenantId, id -> new TbTransportRateLimits(perTenantLimitsConf)); + if (!rateLimits.tryConsume()) { + if (callback != null) { + callback.onError(new TbRateLimitsException(EntityType.TENANT)); + } + return false; + } + DeviceId deviceId = new DeviceId(new UUID(sessionInfo.getDeviceIdMSB(), sessionInfo.getDeviceIdLSB())); + rateLimits = perDeviceLimits.computeIfAbsent(deviceId, id -> new TbTransportRateLimits(perDevicesLimitsConf)); + if (!rateLimits.tryConsume()) { + if (callback != null) { + callback.onError(new TbRateLimitsException(EntityType.DEVICE)); + } + return false; + } + return true; + } + protected void processToTransportMsg(TransportProtos.DeviceActorToTransportMsg toSessionMsg) { UUID sessionId = new UUID(toSessionMsg.getSessionIdMSB(), toSessionMsg.getSessionIdLSB()); SessionMetaData md = sessions.get(sessionId); @@ -101,11 +140,20 @@ public abstract class AbstractTransportService implements TransportService { } public void init() { + if (rateLimitEnabled) { + //Just checking the configuration parameters + new TbTransportRateLimits(perTenantLimitsConf); + new TbTransportRateLimits(perDevicesLimitsConf); + } this.schedulerExecutor = Executors.newSingleThreadScheduledExecutor(); this.transportCallbackExecutor = new ThreadPoolExecutor(0, 20, 60L, TimeUnit.SECONDS, new SynchronousQueue<>()); } public void destroy() { + if (rateLimitEnabled) { + perTenantLimits.clear(); + perDeviceLimits.clear(); + } if (schedulerExecutor != null) { schedulerExecutor.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 aa3b42b58c..6774942de3 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 @@ -218,74 +218,90 @@ public class RemoteTransportService extends AbstractTransportService { @Override public void process(SessionInfoProto sessionInfo, SessionEventMsg msg, TransportServiceCallback callback) { - ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg( - TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) - .setSessionEvent(msg).build() - ).build(); - send(sessionInfo, toRuleEngineMsg, callback); + if (checkLimits(sessionInfo, callback)) { + ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg( + TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) + .setSessionEvent(msg).build() + ).build(); + send(sessionInfo, toRuleEngineMsg, callback); + } } @Override public void process(SessionInfoProto sessionInfo, PostTelemetryMsg msg, TransportServiceCallback callback) { - ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg( - TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) - .setPostTelemetry(msg).build() - ).build(); - send(sessionInfo, toRuleEngineMsg, callback); + if (checkLimits(sessionInfo, callback)) { + ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg( + TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) + .setPostTelemetry(msg).build() + ).build(); + send(sessionInfo, toRuleEngineMsg, callback); + } } @Override public void process(SessionInfoProto sessionInfo, PostAttributeMsg msg, TransportServiceCallback callback) { - ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg( - TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) - .setPostAttributes(msg).build() - ).build(); - send(sessionInfo, toRuleEngineMsg, callback); + if (checkLimits(sessionInfo, callback)) { + ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg( + TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) + .setPostAttributes(msg).build() + ).build(); + send(sessionInfo, toRuleEngineMsg, callback); + } } @Override public void process(SessionInfoProto sessionInfo, GetAttributeRequestMsg msg, TransportServiceCallback callback) { - ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg( - TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) - .setGetAttributes(msg).build() - ).build(); - send(sessionInfo, toRuleEngineMsg, callback); + if (checkLimits(sessionInfo, callback)) { + ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg( + TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) + .setGetAttributes(msg).build() + ).build(); + send(sessionInfo, toRuleEngineMsg, callback); + } } @Override public void process(SessionInfoProto sessionInfo, SubscribeToAttributeUpdatesMsg msg, TransportServiceCallback callback) { - ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg( - TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) - .setSubscribeToAttributes(msg).build() - ).build(); - send(sessionInfo, toRuleEngineMsg, callback); + if (checkLimits(sessionInfo, callback)) { + ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg( + TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) + .setSubscribeToAttributes(msg).build() + ).build(); + send(sessionInfo, toRuleEngineMsg, callback); + } } @Override public void process(SessionInfoProto sessionInfo, SubscribeToRPCMsg msg, TransportServiceCallback callback) { - ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg( - TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) - .setSubscribeToRPC(msg).build() - ).build(); - send(sessionInfo, toRuleEngineMsg, callback); + if (checkLimits(sessionInfo, callback)) { + ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg( + TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) + .setSubscribeToRPC(msg).build() + ).build(); + send(sessionInfo, toRuleEngineMsg, callback); + } } @Override public void process(SessionInfoProto sessionInfo, ToDeviceRpcResponseMsg msg, TransportServiceCallback callback) { - ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg( - TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) - .setToDeviceRPCCallResponse(msg).build() - ).build(); - send(sessionInfo, toRuleEngineMsg, callback); + if (checkLimits(sessionInfo, callback)) { + ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg( + TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) + .setToDeviceRPCCallResponse(msg).build() + ).build(); + send(sessionInfo, toRuleEngineMsg, callback); + } } @Override public void process(SessionInfoProto sessionInfo, ToServerRpcRequestMsg msg, TransportServiceCallback callback) { - ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg( - TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) - .setToServerRPCCallRequest(msg).build() - ).build(); - send(sessionInfo, toRuleEngineMsg, callback); + if (checkLimits(sessionInfo, callback)) { + ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg( + TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) + .setToServerRPCCallRequest(msg).build() + ).build(); + send(sessionInfo, toRuleEngineMsg, callback); + } } private static class TransportCallbackAdaptor implements Callback { diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/TbRateLimitsException.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/TbRateLimitsException.java new file mode 100644 index 0000000000..9d2669da99 --- /dev/null +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/TbRateLimitsException.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.common.transport.service; + +import org.thingsboard.server.common.data.EntityType; + +/** + * Created by ashvayka on 22.10.18. + */ +public class TbRateLimitsException extends Exception { + private final EntityType entityType; + + TbRateLimitsException(EntityType entityType) { + this.entityType = entityType; + } +} diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/TbTransportRateLimits.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/TbTransportRateLimits.java new file mode 100644 index 0000000000..d598734a5a --- /dev/null +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/TbTransportRateLimits.java @@ -0,0 +1,53 @@ +/** + * Copyright © 2016-2018 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this 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 io.github.bucket4j.Bandwidth; +import io.github.bucket4j.Bucket4j; +import io.github.bucket4j.local.LocalBucket; +import io.github.bucket4j.local.LocalBucketBuilder; + +import java.time.Duration; + +/** + * Created by ashvayka on 22.10.18. + */ +class TbTransportRateLimits { + private final LocalBucket bucket; + + public TbTransportRateLimits(String limitsConfiguration) { + LocalBucketBuilder builder = Bucket4j.builder(); + boolean initialized = false; + for (String limitSrc : limitsConfiguration.split(",")) { + long capacity = Long.parseLong(limitSrc.split(":")[0]); + long duration = Long.parseLong(limitSrc.split(":")[1]); + builder.addLimit(Bandwidth.simple(capacity, Duration.ofSeconds(duration))); + initialized = true; + } + if (initialized) { + bucket = builder.build(); + } else { + throw new IllegalArgumentException("Failed to parse rate limits configuration: " + limitsConfiguration); + } + + + } + + boolean tryConsume() { + return bucket.tryConsume(1); + } + +} diff --git a/pom.xml b/pom.xml index 0ac38cdf8b..387e0eda42 100755 --- a/pom.xml +++ b/pom.xml @@ -82,6 +82,7 @@ 5.0.2 0.1.14 2.0.0 + 4.1.1 @@ -778,6 +779,11 @@ delight-nashorn-sandbox ${delight-nashorn-sandbox.version} + + com.github.vladimir-bukhtoyarov + bucket4j-core + ${bucket4j.version} + diff --git a/transport/mqtt/src/main/resources/tb-mqtt-transport.yml b/transport/mqtt/src/main/resources/tb-mqtt-transport.yml index 1d425e4761..a0e86bb1c2 100644 --- a/transport/mqtt/src/main/resources/tb-mqtt-transport.yml +++ b/transport/mqtt/src/main/resources/tb-mqtt-transport.yml @@ -25,7 +25,7 @@ transport: adaptor: "${MQTT_ADAPTOR_NAME:JsonMqttAdaptor}" timeout: "${MQTT_TIMEOUT:10000}" netty: - leak_detector_level: "${NETTY_LEASK_DETECTOR_LVL:DISABLED}" + leak_detector_level: "${NETTY_LEAK_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}" @@ -43,6 +43,13 @@ transport: key_password: "${MQTT_SSL_KEY_PASSWORD:server_key_password}" # Type of the key store key_store_type: "${MQTT_SSL_KEY_STORE_TYPE:JKS}" + sessions: + max_per_tenant: "${TB_TRANSPORT_SESSIONS_MAX_PER_TENANT:1000}" + max_per_device: "${TB_TRANSPORT_SESSIONS_MAX_PER_DEVICE:2}" + rate_limits: + enabled: "${TB_TRANSPORT_RATE_LIMITS_ENABLED:false}" + tenant: "${TB_TRANSPORT_RATE_LIMITS_TENANT:1000:1,20000:60}" + device: "${TB_TRANSPORT_RATE_LIMITS_DEVICE:10:1,300:60}" #Quota parameters quota: