Commit 2fb756ba5b897eb78a568b1b684f9945a05b2f38

Authored by Oleg Kolesnik
2 parents 88b93419 69263e3a
Showing 93 changed files with 1984 additions and 482 deletions

Too many changes to show.

To preserve performance only 93 of 113 files are displayed.

@@ -2,6 +2,7 @@ before_install: @@ -2,6 +2,7 @@ before_install:
2 - sudo rm -f /etc/mavenrc 2 - sudo rm -f /etc/mavenrc
3 - export M2_HOME=/usr/local/maven 3 - export M2_HOME=/usr/local/maven
4 - export MAVEN_OPTS="-Dmaven.repo.local=$HOME/.m2/repository -Xms1024m -Xmx3072m" 4 - export MAVEN_OPTS="-Dmaven.repo.local=$HOME/.m2/repository -Xms1024m -Xmx3072m"
  5 + - export HTTP_LOG_CONTROLLER_ERROR_STACK_TRACE=false
5 jdk: 6 jdk:
6 - oraclejdk8 7 - oraclejdk8
7 language: java 8 language: java
@@ -61,6 +61,7 @@ import org.thingsboard.server.service.cluster.routing.ClusterRoutingService; @@ -61,6 +61,7 @@ import org.thingsboard.server.service.cluster.routing.ClusterRoutingService;
61 import org.thingsboard.server.service.cluster.rpc.ClusterRpcService; 61 import org.thingsboard.server.service.cluster.rpc.ClusterRpcService;
62 import org.thingsboard.server.service.component.ComponentDiscoveryService; 62 import org.thingsboard.server.service.component.ComponentDiscoveryService;
63 import org.thingsboard.server.service.encoding.DataDecodingEncodingService; 63 import org.thingsboard.server.service.encoding.DataDecodingEncodingService;
  64 +import org.thingsboard.server.service.executors.ClusterRpcCallbackExecutorService;
64 import org.thingsboard.server.service.executors.DbCallbackExecutorService; 65 import org.thingsboard.server.service.executors.DbCallbackExecutorService;
65 import org.thingsboard.server.service.executors.ExternalCallExecutorService; 66 import org.thingsboard.server.service.executors.ExternalCallExecutorService;
66 import org.thingsboard.server.service.mail.MailExecutorService; 67 import org.thingsboard.server.service.mail.MailExecutorService;
@@ -188,6 +189,10 @@ public class ActorSystemContext { @@ -188,6 +189,10 @@ public class ActorSystemContext {
188 189
189 @Autowired 190 @Autowired
190 @Getter 191 @Getter
  192 + private ClusterRpcCallbackExecutorService clusterRpcCallbackExecutor;
  193 +
  194 + @Autowired
  195 + @Getter
191 private DbCallbackExecutorService dbCallbackExecutor; 196 private DbCallbackExecutorService dbCallbackExecutor;
192 197
193 @Autowired 198 @Autowired
@@ -25,15 +25,21 @@ import akka.actor.Terminated; @@ -25,15 +25,21 @@ import akka.actor.Terminated;
25 import akka.event.Logging; 25 import akka.event.Logging;
26 import akka.event.LoggingAdapter; 26 import akka.event.LoggingAdapter;
27 import akka.japi.Function; 27 import akka.japi.Function;
  28 +import com.google.common.collect.BiMap;
  29 +import com.google.common.collect.HashBiMap;
  30 +import lombok.extern.slf4j.Slf4j;
28 import org.thingsboard.server.actors.ActorSystemContext; 31 import org.thingsboard.server.actors.ActorSystemContext;
29 import org.thingsboard.server.actors.ruleChain.RuleChainManagerActor; 32 import org.thingsboard.server.actors.ruleChain.RuleChainManagerActor;
30 import org.thingsboard.server.actors.service.ContextBasedCreator; 33 import org.thingsboard.server.actors.service.ContextBasedCreator;
31 import org.thingsboard.server.actors.service.DefaultActorService; 34 import org.thingsboard.server.actors.service.DefaultActorService;
32 import org.thingsboard.server.actors.shared.rulechain.SystemRuleChainManager; 35 import org.thingsboard.server.actors.shared.rulechain.SystemRuleChainManager;
33 import org.thingsboard.server.actors.tenant.TenantActor; 36 import org.thingsboard.server.actors.tenant.TenantActor;
  37 +import org.thingsboard.server.common.data.EntityType;
34 import org.thingsboard.server.common.data.Tenant; 38 import org.thingsboard.server.common.data.Tenant;
35 import org.thingsboard.server.common.data.id.TenantId; 39 import org.thingsboard.server.common.data.id.TenantId;
36 import org.thingsboard.server.common.data.page.PageDataIterable; 40 import org.thingsboard.server.common.data.page.PageDataIterable;
  41 +import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
  42 +import org.thingsboard.server.common.msg.MsgType;
37 import org.thingsboard.server.common.msg.TbActorMsg; 43 import org.thingsboard.server.common.msg.TbActorMsg;
38 import org.thingsboard.server.common.msg.aware.TenantAwareMsg; 44 import org.thingsboard.server.common.msg.aware.TenantAwareMsg;
39 import org.thingsboard.server.common.msg.cluster.SendToClusterMsg; 45 import org.thingsboard.server.common.msg.cluster.SendToClusterMsg;
@@ -50,16 +56,15 @@ import java.util.Optional; @@ -50,16 +56,15 @@ import java.util.Optional;
50 56
51 public class AppActor extends RuleChainManagerActor { 57 public class AppActor extends RuleChainManagerActor {
52 58
53 - private final LoggingAdapter logger = Logging.getLogger(getContext().system(), this);  
54 -  
55 - public static final TenantId SYSTEM_TENANT = new TenantId(ModelConstants.NULL_UUID); 59 + private static final TenantId SYSTEM_TENANT = new TenantId(ModelConstants.NULL_UUID);
56 private final TenantService tenantService; 60 private final TenantService tenantService;
57 - private final Map<TenantId, ActorRef> tenantActors; 61 + private final BiMap<TenantId, ActorRef> tenantActors;
  62 + private boolean ruleChainsInitialized;
58 63
59 private AppActor(ActorSystemContext systemContext) { 64 private AppActor(ActorSystemContext systemContext) {
60 super(systemContext, new SystemRuleChainManager(systemContext)); 65 super(systemContext, new SystemRuleChainManager(systemContext));
61 this.tenantService = systemContext.getTenantService(); 66 this.tenantService = systemContext.getTenantService();
62 - this.tenantActors = new HashMap<>(); 67 + this.tenantActors = HashBiMap.create();
63 } 68 }
64 69
65 @Override 70 @Override
@@ -69,28 +74,20 @@ public class AppActor extends RuleChainManagerActor { @@ -69,28 +74,20 @@ public class AppActor extends RuleChainManagerActor {
69 74
70 @Override 75 @Override
71 public void preStart() { 76 public void preStart() {
72 - logger.info("Starting main system actor.");  
73 - try {  
74 - initRuleChains();  
75 -  
76 - if (systemContext.isTenantComponentsInitEnabled()) {  
77 - PageDataIterable<Tenant> tenantIterator = new PageDataIterable<>(tenantService::findTenants, ENTITY_PACK_LIMIT);  
78 - for (Tenant tenant : tenantIterator) {  
79 - logger.debug("[{}] Creating tenant actor", tenant.getId());  
80 - getOrCreateTenantActor(tenant.getId());  
81 - logger.debug("Tenant actor created.");  
82 - }  
83 - }  
84 -  
85 - logger.info("Main system actor started.");  
86 - } catch (Exception e) {  
87 - logger.error(e, "Unknown failure");  
88 - }  
89 } 77 }
90 78
91 @Override 79 @Override
92 protected boolean process(TbActorMsg msg) { 80 protected boolean process(TbActorMsg msg) {
  81 + if (!ruleChainsInitialized) {
  82 + initRuleChainsAndTenantActors();
  83 + ruleChainsInitialized = true;
  84 + if (msg.getMsgType() != MsgType.APP_INIT_MSG) {
  85 + log.warn("Rule Chains initialized by unexpected message: {}", msg);
  86 + }
  87 + }
93 switch (msg.getMsgType()) { 88 switch (msg.getMsgType()) {
  89 + case APP_INIT_MSG:
  90 + break;
94 case SEND_TO_CLUSTER_MSG: 91 case SEND_TO_CLUSTER_MSG:
95 onPossibleClusterMsg((SendToClusterMsg) msg); 92 onPossibleClusterMsg((SendToClusterMsg) msg);
96 break; 93 break;
@@ -118,6 +115,24 @@ public class AppActor extends RuleChainManagerActor { @@ -118,6 +115,24 @@ public class AppActor extends RuleChainManagerActor {
118 return true; 115 return true;
119 } 116 }
120 117
  118 + private void initRuleChainsAndTenantActors() {
  119 + log.info("Starting main system actor.");
  120 + try {
  121 + initRuleChains();
  122 + if (systemContext.isTenantComponentsInitEnabled()) {
  123 + PageDataIterable<Tenant> tenantIterator = new PageDataIterable<>(tenantService::findTenants, ENTITY_PACK_LIMIT);
  124 + for (Tenant tenant : tenantIterator) {
  125 + log.debug("[{}] Creating tenant actor", tenant.getId());
  126 + getOrCreateTenantActor(tenant.getId());
  127 + log.debug("Tenant actor created.");
  128 + }
  129 + }
  130 + log.info("Main system actor started.");
  131 + } catch (Exception e) {
  132 + log.warn("Unknown failure", e);
  133 + }
  134 + }
  135 +
121 private void onPossibleClusterMsg(SendToClusterMsg msg) { 136 private void onPossibleClusterMsg(SendToClusterMsg msg) {
122 Optional<ServerAddress> address = systemContext.getRoutingService().resolveById(msg.getEntityId()); 137 Optional<ServerAddress> address = systemContext.getRoutingService().resolveById(msg.getEntityId());
123 if (address.isPresent()) { 138 if (address.isPresent()) {
@@ -130,7 +145,8 @@ public class AppActor extends RuleChainManagerActor { @@ -130,7 +145,8 @@ public class AppActor extends RuleChainManagerActor {
130 145
131 private void onServiceToRuleEngineMsg(ServiceToRuleEngineMsg msg) { 146 private void onServiceToRuleEngineMsg(ServiceToRuleEngineMsg msg) {
132 if (SYSTEM_TENANT.equals(msg.getTenantId())) { 147 if (SYSTEM_TENANT.equals(msg.getTenantId())) {
133 - //TODO: ashvayka handle this. 148 +// this may be a notification about system entities created.
  149 +// log.warn("[{}] Invalid service to rule engine msg called. System messages are not supported yet: {}", SYSTEM_TENANT, msg);
134 } else { 150 } else {
135 getOrCreateTenantActor(msg.getTenantId()).tell(msg, self()); 151 getOrCreateTenantActor(msg.getTenantId()).tell(msg, self());
136 } 152 }
@@ -143,16 +159,26 @@ public class AppActor extends RuleChainManagerActor { @@ -143,16 +159,26 @@ public class AppActor extends RuleChainManagerActor {
143 } 159 }
144 160
145 private void onComponentLifecycleMsg(ComponentLifecycleMsg msg) { 161 private void onComponentLifecycleMsg(ComponentLifecycleMsg msg) {
146 - ActorRef target; 162 + ActorRef target = null;
147 if (SYSTEM_TENANT.equals(msg.getTenantId())) { 163 if (SYSTEM_TENANT.equals(msg.getTenantId())) {
148 target = getEntityActorRef(msg.getEntityId()); 164 target = getEntityActorRef(msg.getEntityId());
149 } else { 165 } else {
150 - target = getOrCreateTenantActor(msg.getTenantId()); 166 + if (msg.getEntityId().getEntityType() == EntityType.TENANT
  167 + && msg.getEvent() == ComponentLifecycleEvent.DELETED) {
  168 + log.debug("[{}] Handling tenant deleted notification: {}", msg.getTenantId(), msg);
  169 + ActorRef tenantActor = tenantActors.remove(new TenantId(msg.getEntityId().getId()));
  170 + if (tenantActor != null) {
  171 + log.debug("[{}] Deleting tenant actor: {}", msg.getTenantId(), tenantActor);
  172 + context().stop(tenantActor);
  173 + }
  174 + } else {
  175 + target = getOrCreateTenantActor(msg.getTenantId());
  176 + }
151 } 177 }
152 if (target != null) { 178 if (target != null) {
153 target.tell(msg, ActorRef.noSender()); 179 target.tell(msg, ActorRef.noSender());
154 } else { 180 } else {
155 - logger.debug("Invalid component lifecycle msg: {}", msg); 181 + log.debug("[{}] Invalid component lifecycle msg: {}", msg.getTenantId(), msg);
156 } 182 }
157 } 183 }
158 184
@@ -161,14 +187,24 @@ public class AppActor extends RuleChainManagerActor { @@ -161,14 +187,24 @@ public class AppActor extends RuleChainManagerActor {
161 } 187 }
162 188
163 private ActorRef getOrCreateTenantActor(TenantId tenantId) { 189 private ActorRef getOrCreateTenantActor(TenantId tenantId) {
164 - return tenantActors.computeIfAbsent(tenantId, k -> context().actorOf(Props.create(new TenantActor.ActorCreator(systemContext, tenantId))  
165 - .withDispatcher(DefaultActorService.CORE_DISPATCHER_NAME), tenantId.toString())); 190 + return tenantActors.computeIfAbsent(tenantId, k -> {
  191 + log.debug("[{}] Creating tenant actor.", tenantId);
  192 + ActorRef tenantActor = context().actorOf(Props.create(new TenantActor.ActorCreator(systemContext, tenantId))
  193 + .withDispatcher(DefaultActorService.CORE_DISPATCHER_NAME), tenantId.toString());
  194 + context().watch(tenantActor);
  195 + log.debug("[{}] Created tenant actor: {}.", tenantId, tenantActor);
  196 + return tenantActor;
  197 + });
166 } 198 }
167 199
168 - private void processTermination(Terminated message) { 200 + @Override
  201 + protected void processTermination(Terminated message) {
169 ActorRef terminated = message.actor(); 202 ActorRef terminated = message.actor();
170 if (terminated instanceof LocalActorRef) { 203 if (terminated instanceof LocalActorRef) {
171 - logger.debug("Removed actor: {}", terminated); 204 + boolean removed = tenantActors.inverse().remove(terminated) != null;
  205 + if (removed) {
  206 + log.debug("[{}] Removed actor:", terminated);
  207 + }
172 } else { 208 } else {
173 throw new IllegalStateException("Remote actors are not supported!"); 209 throw new IllegalStateException("Remote actors are not supported!");
174 } 210 }
@@ -182,20 +218,17 @@ public class AppActor extends RuleChainManagerActor { @@ -182,20 +218,17 @@ public class AppActor extends RuleChainManagerActor {
182 } 218 }
183 219
184 @Override 220 @Override
185 - public AppActor create() throws Exception { 221 + public AppActor create() {
186 return new AppActor(context); 222 return new AppActor(context);
187 } 223 }
188 } 224 }
189 225
190 - private final SupervisorStrategy strategy = new OneForOneStrategy(3, Duration.create("1 minute"), new Function<Throwable, Directive>() {  
191 - @Override  
192 - public Directive apply(Throwable t) {  
193 - logger.error(t, "Unknown failure");  
194 - if (t instanceof RuntimeException) {  
195 - return SupervisorStrategy.restart();  
196 - } else {  
197 - return SupervisorStrategy.stop();  
198 - } 226 + private final SupervisorStrategy strategy = new OneForOneStrategy(3, Duration.create("1 minute"), t -> {
  227 + log.warn("Unknown failure", t);
  228 + if (t instanceof RuntimeException) {
  229 + return SupervisorStrategy.restart();
  230 + } else {
  231 + return SupervisorStrategy.stop();
199 } 232 }
200 }); 233 });
201 } 234 }
  1 +/**
  2 + * Copyright © 2016-2018 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.actors.app;
  17 +
  18 +import org.thingsboard.server.common.msg.MsgType;
  19 +import org.thingsboard.server.common.msg.TbActorMsg;
  20 +
  21 +public class AppInitMsg implements TbActorMsg {
  22 +
  23 + @Override
  24 + public MsgType getMsgType() {
  25 + return MsgType.APP_INIT_MSG;
  26 + }
  27 +}
@@ -15,17 +15,13 @@ @@ -15,17 +15,13 @@
15 */ 15 */
16 package org.thingsboard.server.actors.device; 16 package org.thingsboard.server.actors.device;
17 17
18 -import akka.event.Logging;  
19 -import akka.event.LoggingAdapter;  
20 import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg; 18 import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg;
21 import org.thingsboard.rule.engine.api.msg.DeviceNameOrTypeUpdateMsg; 19 import org.thingsboard.rule.engine.api.msg.DeviceNameOrTypeUpdateMsg;
22 import org.thingsboard.server.actors.ActorSystemContext; 20 import org.thingsboard.server.actors.ActorSystemContext;
23 import org.thingsboard.server.actors.service.ContextAwareActor; 21 import org.thingsboard.server.actors.service.ContextAwareActor;
24 -import org.thingsboard.server.actors.service.ContextBasedCreator;  
25 import org.thingsboard.server.common.data.id.DeviceId; 22 import org.thingsboard.server.common.data.id.DeviceId;
26 import org.thingsboard.server.common.data.id.TenantId; 23 import org.thingsboard.server.common.data.id.TenantId;
27 import org.thingsboard.server.common.msg.TbActorMsg; 24 import org.thingsboard.server.common.msg.TbActorMsg;
28 -import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;  
29 import org.thingsboard.server.common.msg.timeout.DeviceActorClientSideRpcTimeoutMsg; 25 import org.thingsboard.server.common.msg.timeout.DeviceActorClientSideRpcTimeoutMsg;
30 import org.thingsboard.server.common.msg.timeout.DeviceActorServerSideRpcTimeoutMsg; 26 import org.thingsboard.server.common.msg.timeout.DeviceActorServerSideRpcTimeoutMsg;
31 import org.thingsboard.server.service.rpc.ToDeviceRpcRequestActorMsg; 27 import org.thingsboard.server.service.rpc.ToDeviceRpcRequestActorMsg;
@@ -34,23 +30,21 @@ import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWra @@ -34,23 +30,21 @@ import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWra
34 30
35 public class DeviceActor extends ContextAwareActor { 31 public class DeviceActor extends ContextAwareActor {
36 32
37 - private final LoggingAdapter logger = Logging.getLogger(getContext().system(), this);  
38 -  
39 private final DeviceActorMessageProcessor processor; 33 private final DeviceActorMessageProcessor processor;
40 34
41 - private DeviceActor(ActorSystemContext systemContext, TenantId tenantId, DeviceId deviceId) { 35 + DeviceActor(ActorSystemContext systemContext, TenantId tenantId, DeviceId deviceId) {
42 super(systemContext); 36 super(systemContext);
43 - this.processor = new DeviceActorMessageProcessor(systemContext, logger, tenantId, deviceId); 37 + this.processor = new DeviceActorMessageProcessor(systemContext, tenantId, deviceId);
44 } 38 }
45 39
46 @Override 40 @Override
47 public void preStart() { 41 public void preStart() {
48 - logger.debug("[{}][{}] Starting device actor.", processor.tenantId, processor.deviceId); 42 + log.debug("[{}][{}] Starting device actor.", processor.tenantId, processor.deviceId);
49 try { 43 try {
50 processor.initSessionTimeout(context()); 44 processor.initSessionTimeout(context());
51 - logger.debug("[{}][{}] Device actor started.", processor.tenantId, processor.deviceId); 45 + log.debug("[{}][{}] Device actor started.", processor.tenantId, processor.deviceId);
52 } catch (Exception e) { 46 } catch (Exception e) {
53 - logger.error(e, "[{}][{}] Unknown failure", processor.tenantId, processor.deviceId); 47 + log.warn("[{}][{}] Unknown failure", processor.tenantId, processor.deviceId, e);
54 } 48 }
55 } 49 }
56 50
@@ -90,22 +84,4 @@ public class DeviceActor extends ContextAwareActor { @@ -90,22 +84,4 @@ public class DeviceActor extends ContextAwareActor {
90 return true; 84 return true;
91 } 85 }
92 86
93 - public static class ActorCreator extends ContextBasedCreator<DeviceActor> {  
94 - private static final long serialVersionUID = 1L;  
95 -  
96 - private final TenantId tenantId;  
97 - private final DeviceId deviceId;  
98 -  
99 - public ActorCreator(ActorSystemContext context, TenantId tenantId, DeviceId deviceId) {  
100 - super(context);  
101 - this.tenantId = tenantId;  
102 - this.deviceId = deviceId;  
103 - }  
104 -  
105 - @Override  
106 - public DeviceActor create() throws Exception {  
107 - return new DeviceActor(context, tenantId, deviceId);  
108 - }  
109 - }  
110 -  
111 } 87 }
  1 +/**
  2 + * Copyright © 2016-2018 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.actors.device;
  17 +
  18 +import org.thingsboard.server.actors.ActorSystemContext;
  19 +import org.thingsboard.server.actors.service.ContextBasedCreator;
  20 +import org.thingsboard.server.common.data.id.DeviceId;
  21 +import org.thingsboard.server.common.data.id.TenantId;
  22 +
  23 +public class DeviceActorCreator extends ContextBasedCreator<DeviceActor> {
  24 + private static final long serialVersionUID = 1L;
  25 +
  26 + private final TenantId tenantId;
  27 + private final DeviceId deviceId;
  28 +
  29 + public DeviceActorCreator(ActorSystemContext context, TenantId tenantId, DeviceId deviceId) {
  30 + super(context);
  31 + this.tenantId = tenantId;
  32 + this.deviceId = deviceId;
  33 + }
  34 +
  35 + @Override
  36 + public DeviceActor create() {
  37 + return new DeviceActor(context, tenantId, deviceId);
  38 + }
  39 +}
@@ -24,6 +24,8 @@ import com.google.common.util.concurrent.ListenableFuture; @@ -24,6 +24,8 @@ import com.google.common.util.concurrent.ListenableFuture;
24 import com.google.gson.Gson; 24 import com.google.gson.Gson;
25 import com.google.gson.JsonObject; 25 import com.google.gson.JsonObject;
26 import com.google.gson.JsonParser; 26 import com.google.gson.JsonParser;
  27 +import com.google.protobuf.InvalidProtocolBufferException;
  28 +import lombok.extern.slf4j.Slf4j;
27 import org.thingsboard.rule.engine.api.RpcError; 29 import org.thingsboard.rule.engine.api.RpcError;
28 import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg; 30 import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg;
29 import org.thingsboard.rule.engine.api.msg.DeviceNameOrTypeUpdateMsg; 31 import org.thingsboard.rule.engine.api.msg.DeviceNameOrTypeUpdateMsg;
@@ -88,6 +90,7 @@ import java.util.stream.Collectors; @@ -88,6 +90,7 @@ import java.util.stream.Collectors;
88 /** 90 /**
89 * @author Andrew Shvayka 91 * @author Andrew Shvayka
90 */ 92 */
  93 +@Slf4j
91 class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { 94 class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
92 95
93 final TenantId tenantId; 96 final TenantId tenantId;
@@ -106,8 +109,8 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -106,8 +109,8 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
106 private String deviceType; 109 private String deviceType;
107 private TbMsgMetaData defaultMetaData; 110 private TbMsgMetaData defaultMetaData;
108 111
109 - DeviceActorMessageProcessor(ActorSystemContext systemContext, LoggingAdapter logger, TenantId tenantId, DeviceId deviceId) {  
110 - super(systemContext, logger); 112 + DeviceActorMessageProcessor(ActorSystemContext systemContext, TenantId tenantId, DeviceId deviceId) {
  113 + super(systemContext);
111 this.tenantId = tenantId; 114 this.tenantId = tenantId;
112 this.deviceId = deviceId; 115 this.deviceId = deviceId;
113 this.sessions = new LinkedHashMap<>(); 116 this.sessions = new LinkedHashMap<>();
@@ -136,30 +139,30 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -136,30 +139,30 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
136 139
137 long timeout = request.getExpirationTime() - System.currentTimeMillis(); 140 long timeout = request.getExpirationTime() - System.currentTimeMillis();
138 if (timeout <= 0) { 141 if (timeout <= 0) {
139 - logger.debug("[{}][{}] Ignoring message due to exp time reached", deviceId, request.getId(), request.getExpirationTime()); 142 + log.debug("[{}][{}] Ignoring message due to exp time reached, {}", deviceId, request.getId(), request.getExpirationTime());
140 return; 143 return;
141 } 144 }
142 145
143 boolean sent = rpcSubscriptions.size() > 0; 146 boolean sent = rpcSubscriptions.size() > 0;
144 Set<UUID> syncSessionSet = new HashSet<>(); 147 Set<UUID> syncSessionSet = new HashSet<>();
145 - rpcSubscriptions.entrySet().forEach(sub -> {  
146 - sendToTransport(rpcRequest, sub.getKey(), sub.getValue().getNodeId());  
147 - if (TransportProtos.SessionType.SYNC == sub.getValue().getType()) {  
148 - syncSessionSet.add(sub.getKey()); 148 + rpcSubscriptions.forEach((key, value) -> {
  149 + sendToTransport(rpcRequest, key, value.getNodeId());
  150 + if (TransportProtos.SessionType.SYNC == value.getType()) {
  151 + syncSessionSet.add(key);
149 } 152 }
150 }); 153 });
151 syncSessionSet.forEach(rpcSubscriptions::remove); 154 syncSessionSet.forEach(rpcSubscriptions::remove);
152 155
153 if (request.isOneway() && sent) { 156 if (request.isOneway() && sent) {
154 - logger.debug("[{}] Rpc command response sent [{}]!", deviceId, request.getId()); 157 + log.debug("[{}] Rpc command response sent [{}]!", deviceId, request.getId());
155 systemContext.getDeviceRpcService().processResponseToServerSideRPCRequestFromDeviceActor(new FromDeviceRpcResponse(msg.getMsg().getId(), null, null)); 158 systemContext.getDeviceRpcService().processResponseToServerSideRPCRequestFromDeviceActor(new FromDeviceRpcResponse(msg.getMsg().getId(), null, null));
156 } else { 159 } else {
157 registerPendingRpcRequest(context, msg, sent, rpcRequest, timeout); 160 registerPendingRpcRequest(context, msg, sent, rpcRequest, timeout);
158 } 161 }
159 if (sent) { 162 if (sent) {
160 - logger.debug("[{}] RPC request {} is sent!", deviceId, request.getId()); 163 + log.debug("[{}] RPC request {} is sent!", deviceId, request.getId());
161 } else { 164 } else {
162 - logger.debug("[{}] RPC request {} is NOT sent!", deviceId, request.getId()); 165 + log.debug("[{}] RPC request {} is NOT sent!", deviceId, request.getId());
163 } 166 }
164 } 167 }
165 168
@@ -172,7 +175,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -172,7 +175,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
172 void processServerSideRpcTimeout(ActorContext context, DeviceActorServerSideRpcTimeoutMsg msg) { 175 void processServerSideRpcTimeout(ActorContext context, DeviceActorServerSideRpcTimeoutMsg msg) {
173 ToDeviceRpcRequestMetadata requestMd = toDeviceRpcPendingMap.remove(msg.getId()); 176 ToDeviceRpcRequestMetadata requestMd = toDeviceRpcPendingMap.remove(msg.getId());
174 if (requestMd != null) { 177 if (requestMd != null) {
175 - logger.debug("[{}] RPC request [{}] timeout detected!", deviceId, msg.getId()); 178 + log.debug("[{}] RPC request [{}] timeout detected!", deviceId, msg.getId());
176 systemContext.getDeviceRpcService().processResponseToServerSideRPCRequestFromDeviceActor(new FromDeviceRpcResponse(requestMd.getMsg().getMsg().getId(), 179 systemContext.getDeviceRpcService().processResponseToServerSideRPCRequestFromDeviceActor(new FromDeviceRpcResponse(requestMd.getMsg().getMsg().getId(),
177 null, requestMd.isSent() ? RpcError.TIMEOUT : RpcError.NO_ACTIVE_CONNECTION)); 180 null, requestMd.isSent() ? RpcError.TIMEOUT : RpcError.NO_ACTIVE_CONNECTION));
178 } 181 }
@@ -181,13 +184,13 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -181,13 +184,13 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
181 private void sendPendingRequests(ActorContext context, UUID sessionId, SessionInfoProto sessionInfo) { 184 private void sendPendingRequests(ActorContext context, UUID sessionId, SessionInfoProto sessionInfo) {
182 TransportProtos.SessionType sessionType = getSessionType(sessionId); 185 TransportProtos.SessionType sessionType = getSessionType(sessionId);
183 if (!toDeviceRpcPendingMap.isEmpty()) { 186 if (!toDeviceRpcPendingMap.isEmpty()) {
184 - logger.debug("[{}] Pushing {} pending RPC messages to new async session [{}]", deviceId, toDeviceRpcPendingMap.size(), sessionId); 187 + log.debug("[{}] Pushing {} pending RPC messages to new async session [{}]", deviceId, toDeviceRpcPendingMap.size(), sessionId);
185 if (sessionType == TransportProtos.SessionType.SYNC) { 188 if (sessionType == TransportProtos.SessionType.SYNC) {
186 - logger.debug("[{}] Cleanup sync rpc session [{}]", deviceId, sessionId); 189 + log.debug("[{}] Cleanup sync rpc session [{}]", deviceId, sessionId);
187 rpcSubscriptions.remove(sessionId); 190 rpcSubscriptions.remove(sessionId);
188 } 191 }
189 } else { 192 } else {
190 - logger.debug("[{}] No pending RPC messages for new async session [{}]", deviceId, sessionId); 193 + log.debug("[{}] No pending RPC messages for new async session [{}]", deviceId, sessionId);
191 } 194 }
192 Set<Integer> sentOneWayIds = new HashSet<>(); 195 Set<Integer> sentOneWayIds = new HashSet<>();
193 if (sessionType == TransportProtos.SessionType.ASYNC) { 196 if (sessionType == TransportProtos.SessionType.ASYNC) {
@@ -335,7 +338,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -335,7 +338,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
335 void processClientSideRpcTimeout(ActorContext context, DeviceActorClientSideRpcTimeoutMsg msg) { 338 void processClientSideRpcTimeout(ActorContext context, DeviceActorClientSideRpcTimeoutMsg msg) {
336 ToServerRpcRequestMetadata data = toServerRpcPendingMap.remove(msg.getId()); 339 ToServerRpcRequestMetadata data = toServerRpcPendingMap.remove(msg.getId());
337 if (data != null) { 340 if (data != null) {
338 - logger.debug("[{}] Client side RPC request [{}] timeout detected!", deviceId, msg.getId()); 341 + log.debug("[{}] Client side RPC request [{}] timeout detected!", deviceId, msg.getId());
339 sendToTransport(TransportProtos.ToServerRpcResponseMsg.newBuilder() 342 sendToTransport(TransportProtos.ToServerRpcResponseMsg.newBuilder()
340 .setRequestId(msg.getId()).setError("timeout").build() 343 .setRequestId(msg.getId()).setError("timeout").build()
341 , data.getSessionId(), data.getNodeId()); 344 , data.getSessionId(), data.getNodeId());
@@ -346,9 +349,12 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -346,9 +349,12 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
346 int requestId = msg.getMsg().getRequestId(); 349 int requestId = msg.getMsg().getRequestId();
347 ToServerRpcRequestMetadata data = toServerRpcPendingMap.remove(requestId); 350 ToServerRpcRequestMetadata data = toServerRpcPendingMap.remove(requestId);
348 if (data != null) { 351 if (data != null) {
  352 + log.debug("[{}] Pushing reply to [{}][{}]!", deviceId, data.getNodeId(), data.getSessionId());
349 sendToTransport(TransportProtos.ToServerRpcResponseMsg.newBuilder() 353 sendToTransport(TransportProtos.ToServerRpcResponseMsg.newBuilder()
350 .setRequestId(requestId).setPayload(msg.getMsg().getData()).build() 354 .setRequestId(requestId).setPayload(msg.getMsg().getData()).build()
351 , data.getSessionId(), data.getNodeId()); 355 , data.getSessionId(), data.getNodeId());
  356 + } else {
  357 + log.debug("[{}][{}] Pending RPC request to server not found!", deviceId, requestId);
352 } 358 }
353 } 359 }
354 360
@@ -380,7 +386,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -380,7 +386,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
380 hasNotificationData = true; 386 hasNotificationData = true;
381 } 387 }
382 } else { 388 } else {
383 - logger.debug("[{}] No public server side attributes changed!", deviceId); 389 + log.debug("[{}] No public server side attributes changed!", deviceId);
384 } 390 }
385 } 391 }
386 } 392 }
@@ -391,27 +397,27 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -391,27 +397,27 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
391 }); 397 });
392 } 398 }
393 } else { 399 } else {
394 - logger.debug("[{}] No registered attributes subscriptions to process!", deviceId); 400 + log.debug("[{}] No registered attributes subscriptions to process!", deviceId);
395 } 401 }
396 } 402 }
397 403
398 private void processRpcResponses(ActorContext context, SessionInfoProto sessionInfo, ToDeviceRpcResponseMsg responseMsg) { 404 private void processRpcResponses(ActorContext context, SessionInfoProto sessionInfo, ToDeviceRpcResponseMsg responseMsg) {
399 UUID sessionId = getSessionId(sessionInfo); 405 UUID sessionId = getSessionId(sessionInfo);
400 - logger.debug("[{}] Processing rpc command response [{}]", deviceId, sessionId); 406 + log.debug("[{}] Processing rpc command response [{}]", deviceId, sessionId);
401 ToDeviceRpcRequestMetadata requestMd = toDeviceRpcPendingMap.remove(responseMsg.getRequestId()); 407 ToDeviceRpcRequestMetadata requestMd = toDeviceRpcPendingMap.remove(responseMsg.getRequestId());
402 boolean success = requestMd != null; 408 boolean success = requestMd != null;
403 if (success) { 409 if (success) {
404 systemContext.getDeviceRpcService().processResponseToServerSideRPCRequestFromDeviceActor(new FromDeviceRpcResponse(requestMd.getMsg().getMsg().getId(), 410 systemContext.getDeviceRpcService().processResponseToServerSideRPCRequestFromDeviceActor(new FromDeviceRpcResponse(requestMd.getMsg().getMsg().getId(),
405 responseMsg.getPayload(), null)); 411 responseMsg.getPayload(), null));
406 } else { 412 } else {
407 - logger.debug("[{}] Rpc command response [{}] is stale!", deviceId, responseMsg.getRequestId()); 413 + log.debug("[{}] Rpc command response [{}] is stale!", deviceId, responseMsg.getRequestId());
408 } 414 }
409 } 415 }
410 416
411 private void processSubscriptionCommands(ActorContext context, SessionInfoProto sessionInfo, SubscribeToAttributeUpdatesMsg subscribeCmd) { 417 private void processSubscriptionCommands(ActorContext context, SessionInfoProto sessionInfo, SubscribeToAttributeUpdatesMsg subscribeCmd) {
412 UUID sessionId = getSessionId(sessionInfo); 418 UUID sessionId = getSessionId(sessionInfo);
413 if (subscribeCmd.getUnsubscribe()) { 419 if (subscribeCmd.getUnsubscribe()) {
414 - logger.debug("[{}] Canceling attributes subscription for session [{}]", deviceId, sessionId); 420 + log.debug("[{}] Canceling attributes subscription for session [{}]", deviceId, sessionId);
415 attributeSubscriptions.remove(sessionId); 421 attributeSubscriptions.remove(sessionId);
416 } else { 422 } else {
417 SessionInfoMetaData sessionMD = sessions.get(sessionId); 423 SessionInfoMetaData sessionMD = sessions.get(sessionId);
@@ -419,7 +425,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -419,7 +425,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
419 sessionMD = new SessionInfoMetaData(new SessionInfo(TransportProtos.SessionType.SYNC, sessionInfo.getNodeId())); 425 sessionMD = new SessionInfoMetaData(new SessionInfo(TransportProtos.SessionType.SYNC, sessionInfo.getNodeId()));
420 } 426 }
421 sessionMD.setSubscribedToAttributes(true); 427 sessionMD.setSubscribedToAttributes(true);
422 - logger.debug("[{}] Registering attributes subscription for session [{}]", deviceId, sessionId); 428 + log.debug("[{}] Registering attributes subscription for session [{}]", deviceId, sessionId);
423 attributeSubscriptions.put(sessionId, sessionMD.getSessionInfo()); 429 attributeSubscriptions.put(sessionId, sessionMD.getSessionInfo());
424 dumpSessions(); 430 dumpSessions();
425 } 431 }
@@ -432,7 +438,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -432,7 +438,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
432 private void processSubscriptionCommands(ActorContext context, SessionInfoProto sessionInfo, SubscribeToRPCMsg subscribeCmd) { 438 private void processSubscriptionCommands(ActorContext context, SessionInfoProto sessionInfo, SubscribeToRPCMsg subscribeCmd) {
433 UUID sessionId = getSessionId(sessionInfo); 439 UUID sessionId = getSessionId(sessionInfo);
434 if (subscribeCmd.getUnsubscribe()) { 440 if (subscribeCmd.getUnsubscribe()) {
435 - logger.debug("[{}] Canceling rpc subscription for session [{}]", deviceId, sessionId); 441 + log.debug("[{}] Canceling rpc subscription for session [{}]", deviceId, sessionId);
436 rpcSubscriptions.remove(sessionId); 442 rpcSubscriptions.remove(sessionId);
437 } else { 443 } else {
438 SessionInfoMetaData sessionMD = sessions.get(sessionId); 444 SessionInfoMetaData sessionMD = sessions.get(sessionId);
@@ -440,7 +446,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -440,7 +446,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
440 sessionMD = new SessionInfoMetaData(new SessionInfo(TransportProtos.SessionType.SYNC, sessionInfo.getNodeId())); 446 sessionMD = new SessionInfoMetaData(new SessionInfo(TransportProtos.SessionType.SYNC, sessionInfo.getNodeId()));
441 } 447 }
442 sessionMD.setSubscribedToRPC(true); 448 sessionMD.setSubscribedToRPC(true);
443 - logger.debug("[{}] Registering rpc subscription for session [{}]", deviceId, sessionId); 449 + log.debug("[{}] Registering rpc subscription for session [{}]", deviceId, sessionId);
444 rpcSubscriptions.put(sessionId, sessionMD.getSessionInfo()); 450 rpcSubscriptions.put(sessionId, sessionMD.getSessionInfo());
445 sendPendingRequests(context, sessionId, sessionInfo); 451 sendPendingRequests(context, sessionId, sessionInfo);
446 dumpSessions(); 452 dumpSessions();
@@ -451,10 +457,10 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -451,10 +457,10 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
451 UUID sessionId = getSessionId(sessionInfo); 457 UUID sessionId = getSessionId(sessionInfo);
452 if (msg.getEvent() == SessionEvent.OPEN) { 458 if (msg.getEvent() == SessionEvent.OPEN) {
453 if (sessions.containsKey(sessionId)) { 459 if (sessions.containsKey(sessionId)) {
454 - logger.debug("[{}] Received duplicate session open event [{}]", deviceId, sessionId); 460 + log.debug("[{}] Received duplicate session open event [{}]", deviceId, sessionId);
455 return; 461 return;
456 } 462 }
457 - logger.debug("[{}] Processing new session [{}]", deviceId, sessionId); 463 + log.debug("[{}] Processing new session [{}]", deviceId, sessionId);
458 if (sessions.size() >= systemContext.getMaxConcurrentSessionsPerDevice()) { 464 if (sessions.size() >= systemContext.getMaxConcurrentSessionsPerDevice()) {
459 UUID sessionIdToRemove = sessions.keySet().stream().findFirst().orElse(null); 465 UUID sessionIdToRemove = sessions.keySet().stream().findFirst().orElse(null);
460 if (sessionIdToRemove != null) { 466 if (sessionIdToRemove != null) {
@@ -467,7 +473,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -467,7 +473,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
467 } 473 }
468 dumpSessions(); 474 dumpSessions();
469 } else if (msg.getEvent() == SessionEvent.CLOSED) { 475 } else if (msg.getEvent() == SessionEvent.CLOSED) {
470 - logger.debug("[{}] Canceling subscriptions for closed session [{}]", deviceId, sessionId); 476 + log.debug("[{}] Canceling subscriptions for closed session [{}]", deviceId, sessionId);
471 sessions.remove(sessionId); 477 sessions.remove(sessionId);
472 attributeSubscriptions.remove(sessionId); 478 attributeSubscriptions.remove(sessionId);
473 rpcSubscriptions.remove(sessionId); 479 rpcSubscriptions.remove(sessionId);
@@ -478,19 +484,19 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -478,19 +484,19 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
478 } 484 }
479 } 485 }
480 486
481 - private void handleSessionActivity(ActorContext context, SessionInfoProto sessionInfo, TransportProtos.SubscriptionInfoProto subscriptionInfo) {  
482 - UUID sessionId = getSessionId(sessionInfo);  
483 - SessionInfoMetaData sessionMD = sessions.get(sessionId);  
484 - if (sessionMD != null) {  
485 - sessionMD.setLastActivityTime(subscriptionInfo.getLastActivityTime());  
486 - sessionMD.setSubscribedToAttributes(subscriptionInfo.getAttributeSubscription());  
487 - sessionMD.setSubscribedToRPC(subscriptionInfo.getRpcSubscription());  
488 - if (subscriptionInfo.getAttributeSubscription()) {  
489 - attributeSubscriptions.putIfAbsent(sessionId, sessionMD.getSessionInfo());  
490 - }  
491 - if (subscriptionInfo.getRpcSubscription()) {  
492 - rpcSubscriptions.putIfAbsent(sessionId, sessionMD.getSessionInfo());  
493 - } 487 + private void handleSessionActivity(ActorContext context, SessionInfoProto sessionInfoProto, TransportProtos.SubscriptionInfoProto subscriptionInfo) {
  488 + UUID sessionId = getSessionId(sessionInfoProto);
  489 + SessionInfoMetaData sessionMD = sessions.computeIfAbsent(sessionId,
  490 + id -> new SessionInfoMetaData(new SessionInfo(TransportProtos.SessionType.ASYNC, sessionInfoProto.getNodeId()), 0L));
  491 +
  492 + sessionMD.setLastActivityTime(subscriptionInfo.getLastActivityTime());
  493 + sessionMD.setSubscribedToAttributes(subscriptionInfo.getAttributeSubscription());
  494 + sessionMD.setSubscribedToRPC(subscriptionInfo.getRpcSubscription());
  495 + if (subscriptionInfo.getAttributeSubscription()) {
  496 + attributeSubscriptions.putIfAbsent(sessionId, sessionMD.getSessionInfo());
  497 + }
  498 + if (subscriptionInfo.getRpcSubscription()) {
  499 + rpcSubscriptions.putIfAbsent(sessionId, sessionMD.getSessionInfo());
494 } 500 }
495 dumpSessions(); 501 dumpSessions();
496 } 502 }
@@ -623,10 +629,16 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -623,10 +629,16 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
623 } 629 }
624 630
625 private void restoreSessions() { 631 private void restoreSessions() {
626 - logger.debug("[{}] Restoring sessions from cache", deviceId);  
627 - TransportProtos.DeviceSessionsCacheEntry sessionsDump = systemContext.getDeviceSessionCacheService().get(deviceId);  
628 - if (sessionsDump.getSerializedSize() == 0) {  
629 - logger.debug("[{}] No session information found", deviceId); 632 + log.debug("[{}] Restoring sessions from cache", deviceId);
  633 + TransportProtos.DeviceSessionsCacheEntry sessionsDump = null;
  634 + try {
  635 + sessionsDump = TransportProtos.DeviceSessionsCacheEntry.parseFrom(systemContext.getDeviceSessionCacheService().get(deviceId));
  636 + } catch (InvalidProtocolBufferException e) {
  637 + log.warn("[{}] Failed to decode device sessions from cache", deviceId);
  638 + return;
  639 + }
  640 + if (sessionsDump.getSessionsCount() == 0) {
  641 + log.debug("[{}] No session information found", deviceId);
630 return; 642 return;
631 } 643 }
632 for (TransportProtos.SessionSubscriptionInfoProto sessionSubscriptionInfoProto : sessionsDump.getSessionsList()) { 644 for (TransportProtos.SessionSubscriptionInfoProto sessionSubscriptionInfoProto : sessionsDump.getSessionsList()) {
@@ -644,13 +656,13 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -644,13 +656,13 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
644 rpcSubscriptions.put(sessionId, sessionInfo); 656 rpcSubscriptions.put(sessionId, sessionInfo);
645 sessionMD.setSubscribedToRPC(true); 657 sessionMD.setSubscribedToRPC(true);
646 } 658 }
647 - logger.debug("[{}] Restored session: {}", deviceId, sessionMD); 659 + log.debug("[{}] Restored session: {}", deviceId, sessionMD);
648 } 660 }
649 - logger.debug("[{}] Restored sessions: {}, rpc subscriptions: {}, attribute subscriptions: {}", deviceId, sessions.size(), rpcSubscriptions.size(), attributeSubscriptions.size()); 661 + log.debug("[{}] Restored sessions: {}, rpc subscriptions: {}, attribute subscriptions: {}", deviceId, sessions.size(), rpcSubscriptions.size(), attributeSubscriptions.size());
650 } 662 }
651 663
652 private void dumpSessions() { 664 private void dumpSessions() {
653 - logger.debug("[{}] Dumping sessions: {}, rpc subscriptions: {}, attribute subscriptions: {} to cache", deviceId, sessions.size(), rpcSubscriptions.size(), attributeSubscriptions.size()); 665 + log.debug("[{}] Dumping sessions: {}, rpc subscriptions: {}, attribute subscriptions: {} to cache", deviceId, sessions.size(), rpcSubscriptions.size(), attributeSubscriptions.size());
654 List<TransportProtos.SessionSubscriptionInfoProto> sessionsList = new ArrayList<>(sessions.size()); 666 List<TransportProtos.SessionSubscriptionInfoProto> sessionsList = new ArrayList<>(sessions.size());
655 sessions.forEach((uuid, sessionMD) -> { 667 sessions.forEach((uuid, sessionMD) -> {
656 if (sessionMD.getSessionInfo().getType() == TransportProtos.SessionType.SYNC) { 668 if (sessionMD.getSessionInfo().getType() == TransportProtos.SessionType.SYNC) {
@@ -668,11 +680,11 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -668,11 +680,11 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
668 sessionsList.add(TransportProtos.SessionSubscriptionInfoProto.newBuilder() 680 sessionsList.add(TransportProtos.SessionSubscriptionInfoProto.newBuilder()
669 .setSessionInfo(sessionInfoProto) 681 .setSessionInfo(sessionInfoProto)
670 .setSubscriptionInfo(subscriptionInfoProto).build()); 682 .setSubscriptionInfo(subscriptionInfoProto).build());
671 - logger.debug("[{}] Dumping session: {}", deviceId, sessionMD); 683 + log.debug("[{}] Dumping session: {}", deviceId, sessionMD);
672 }); 684 });
673 systemContext.getDeviceSessionCacheService() 685 systemContext.getDeviceSessionCacheService()
674 .put(deviceId, TransportProtos.DeviceSessionsCacheEntry.newBuilder() 686 .put(deviceId, TransportProtos.DeviceSessionsCacheEntry.newBuilder()
675 - .addAllSessions(sessionsList).build()); 687 + .addAllSessions(sessionsList).build().toByteArray());
676 } 688 }
677 689
678 void initSessionTimeout(ActorContext context) { 690 void initSessionTimeout(ActorContext context) {
@@ -17,10 +17,12 @@ package org.thingsboard.server.actors.rpc; @@ -17,10 +17,12 @@ package org.thingsboard.server.actors.rpc;
17 17
18 import akka.actor.ActorRef; 18 import akka.actor.ActorRef;
19 import lombok.extern.slf4j.Slf4j; 19 import lombok.extern.slf4j.Slf4j;
  20 +import org.thingsboard.server.actors.ActorSystemContext;
20 import org.thingsboard.server.actors.service.ActorService; 21 import org.thingsboard.server.actors.service.ActorService;
21 import org.thingsboard.server.gen.cluster.ClusterAPIProtos; 22 import org.thingsboard.server.gen.cluster.ClusterAPIProtos;
22 import org.thingsboard.server.service.cluster.rpc.GrpcSession; 23 import org.thingsboard.server.service.cluster.rpc.GrpcSession;
23 import org.thingsboard.server.service.cluster.rpc.GrpcSessionListener; 24 import org.thingsboard.server.service.cluster.rpc.GrpcSessionListener;
  25 +import org.thingsboard.server.service.executors.ClusterRpcCallbackExecutorService;
24 26
25 /** 27 /**
26 * @author Andrew Shvayka 28 * @author Andrew Shvayka
@@ -28,19 +30,21 @@ import org.thingsboard.server.service.cluster.rpc.GrpcSessionListener; @@ -28,19 +30,21 @@ import org.thingsboard.server.service.cluster.rpc.GrpcSessionListener;
28 @Slf4j 30 @Slf4j
29 public class BasicRpcSessionListener implements GrpcSessionListener { 31 public class BasicRpcSessionListener implements GrpcSessionListener {
30 32
  33 + private final ClusterRpcCallbackExecutorService callbackExecutorService;
31 private final ActorService service; 34 private final ActorService service;
32 private final ActorRef manager; 35 private final ActorRef manager;
33 private final ActorRef self; 36 private final ActorRef self;
34 37
35 - BasicRpcSessionListener(ActorService service, ActorRef manager, ActorRef self) {  
36 - this.service = service; 38 + BasicRpcSessionListener(ActorSystemContext context, ActorRef manager, ActorRef self) {
  39 + this.service = context.getActorService();
  40 + this.callbackExecutorService = context.getClusterRpcCallbackExecutor();
37 this.manager = manager; 41 this.manager = manager;
38 this.self = self; 42 this.self = self;
39 } 43 }
40 44
41 @Override 45 @Override
42 public void onConnected(GrpcSession session) { 46 public void onConnected(GrpcSession session) {
43 - log.info("{} session started -> {}", getType(session), session.getRemoteServer()); 47 + log.info("[{}][{}] session started", session.getRemoteServer(), getType(session));
44 if (!session.isClient()) { 48 if (!session.isClient()) {
45 manager.tell(new RpcSessionConnectedMsg(session.getRemoteServer(), session.getSessionId()), self); 49 manager.tell(new RpcSessionConnectedMsg(session.getRemoteServer(), session.getSessionId()), self);
46 } 50 }
@@ -48,21 +52,25 @@ public class BasicRpcSessionListener implements GrpcSessionListener { @@ -48,21 +52,25 @@ public class BasicRpcSessionListener implements GrpcSessionListener {
48 52
49 @Override 53 @Override
50 public void onDisconnected(GrpcSession session) { 54 public void onDisconnected(GrpcSession session) {
51 - log.info("{} session closed -> {}", getType(session), session.getRemoteServer()); 55 + log.info("[{}][{}] session closed", session.getRemoteServer(), getType(session));
52 manager.tell(new RpcSessionDisconnectedMsg(session.isClient(), session.getRemoteServer()), self); 56 manager.tell(new RpcSessionDisconnectedMsg(session.isClient(), session.getRemoteServer()), self);
53 } 57 }
54 58
55 @Override 59 @Override
56 public void onReceiveClusterGrpcMsg(GrpcSession session, ClusterAPIProtos.ClusterMessage clusterMessage) { 60 public void onReceiveClusterGrpcMsg(GrpcSession session, ClusterAPIProtos.ClusterMessage clusterMessage) {
57 - log.trace("{} Service [{}] received session actor msg {}", getType(session),  
58 - session.getRemoteServer(),  
59 - clusterMessage);  
60 - service.onReceivedMsg(session.getRemoteServer(), clusterMessage); 61 + log.trace("Received session actor msg from [{}][{}]: {}", session.getRemoteServer(), getType(session), clusterMessage);
  62 + callbackExecutorService.execute(() -> {
  63 + try {
  64 + service.onReceivedMsg(session.getRemoteServer(), clusterMessage);
  65 + } catch (Exception e) {
  66 + log.debug("[{}][{}] Failed to process cluster message: {}", session.getRemoteServer(), getType(session), clusterMessage, e);
  67 + }
  68 + });
61 } 69 }
62 70
63 @Override 71 @Override
64 public void onError(GrpcSession session, Throwable t) { 72 public void onError(GrpcSession session, Throwable t) {
65 - log.warn("{} session got error -> {}", getType(session), session.getRemoteServer(), t); 73 + log.warn("[{}][{}] session got error -> {}", session.getRemoteServer(), getType(session), t);
66 manager.tell(new RpcSessionClosedMsg(session.isClient(), session.getRemoteServer()), self); 74 manager.tell(new RpcSessionClosedMsg(session.isClient(), session.getRemoteServer()), self);
67 session.close(); 75 session.close();
68 } 76 }
@@ -16,9 +16,12 @@ @@ -16,9 +16,12 @@
16 package org.thingsboard.server.actors.rpc; 16 package org.thingsboard.server.actors.rpc;
17 17
18 import akka.actor.ActorRef; 18 import akka.actor.ActorRef;
  19 +import akka.actor.OneForOneStrategy;
19 import akka.actor.Props; 20 import akka.actor.Props;
  21 +import akka.actor.SupervisorStrategy;
20 import akka.event.Logging; 22 import akka.event.Logging;
21 import akka.event.LoggingAdapter; 23 import akka.event.LoggingAdapter;
  24 +import lombok.extern.slf4j.Slf4j;
22 import org.thingsboard.server.actors.ActorSystemContext; 25 import org.thingsboard.server.actors.ActorSystemContext;
23 import org.thingsboard.server.actors.service.ContextAwareActor; 26 import org.thingsboard.server.actors.service.ContextAwareActor;
24 import org.thingsboard.server.actors.service.ContextBasedCreator; 27 import org.thingsboard.server.actors.service.ContextBasedCreator;
@@ -29,6 +32,7 @@ import org.thingsboard.server.common.msg.cluster.ServerAddress; @@ -29,6 +32,7 @@ import org.thingsboard.server.common.msg.cluster.ServerAddress;
29 import org.thingsboard.server.common.msg.cluster.ServerType; 32 import org.thingsboard.server.common.msg.cluster.ServerType;
30 import org.thingsboard.server.gen.cluster.ClusterAPIProtos; 33 import org.thingsboard.server.gen.cluster.ClusterAPIProtos;
31 import org.thingsboard.server.service.cluster.discovery.ServerInstance; 34 import org.thingsboard.server.service.cluster.discovery.ServerInstance;
  35 +import scala.concurrent.duration.Duration;
32 36
33 import java.util.*; 37 import java.util.*;
34 38
@@ -37,15 +41,11 @@ import java.util.*; @@ -37,15 +41,11 @@ import java.util.*;
37 */ 41 */
38 public class RpcManagerActor extends ContextAwareActor { 42 public class RpcManagerActor extends ContextAwareActor {
39 43
40 - private final LoggingAdapter log = Logging.getLogger(getContext().system(), this);  
41 -  
42 private final Map<ServerAddress, SessionActorInfo> sessionActors; 44 private final Map<ServerAddress, SessionActorInfo> sessionActors;
43 -  
44 private final Map<ServerAddress, Queue<ClusterAPIProtos.ClusterMessage>> pendingMsgs; 45 private final Map<ServerAddress, Queue<ClusterAPIProtos.ClusterMessage>> pendingMsgs;
45 -  
46 private final ServerAddress instance; 46 private final ServerAddress instance;
47 47
48 - RpcManagerActor(ActorSystemContext systemContext) { 48 + private RpcManagerActor(ActorSystemContext systemContext) {
49 super(systemContext); 49 super(systemContext);
50 this.sessionActors = new HashMap<>(); 50 this.sessionActors = new HashMap<>();
51 this.pendingMsgs = new HashMap<>(); 51 this.pendingMsgs = new HashMap<>();
@@ -64,7 +64,7 @@ public class RpcManagerActor extends ContextAwareActor { @@ -64,7 +64,7 @@ public class RpcManagerActor extends ContextAwareActor {
64 } 64 }
65 65
66 @Override 66 @Override
67 - public void onReceive(Object msg) throws Exception { 67 + public void onReceive(Object msg) {
68 if (msg instanceof ClusterAPIProtos.ClusterMessage) { 68 if (msg instanceof ClusterAPIProtos.ClusterMessage) {
69 onMsg((ClusterAPIProtos.ClusterMessage) msg); 69 onMsg((ClusterAPIProtos.ClusterMessage) msg);
70 } else if (msg instanceof RpcBroadcastMsg) { 70 } else if (msg instanceof RpcBroadcastMsg) {
@@ -116,7 +116,7 @@ public class RpcManagerActor extends ContextAwareActor { @@ -116,7 +116,7 @@ public class RpcManagerActor extends ContextAwareActor {
116 queue.add(msg); 116 queue.add(msg);
117 } 117 }
118 } else { 118 } else {
119 - logger.warning("Cluster msg doesn't have server address [{}]", msg); 119 + log.warn("Cluster msg doesn't have server address [{}]", msg);
120 } 120 }
121 } 121 }
122 122
@@ -164,6 +164,7 @@ public class RpcManagerActor extends ContextAwareActor { @@ -164,6 +164,7 @@ public class RpcManagerActor extends ContextAwareActor {
164 log.info("[{}] session closed. Should reconnect: {}", remoteAddress, reconnect); 164 log.info("[{}] session closed. Should reconnect: {}", remoteAddress, reconnect);
165 SessionActorInfo sessionRef = sessionActors.get(remoteAddress); 165 SessionActorInfo sessionRef = sessionActors.get(remoteAddress);
166 if (sessionRef != null && context().sender() != null && context().sender().equals(sessionRef.actor)) { 166 if (sessionRef != null && context().sender() != null && context().sender().equals(sessionRef.actor)) {
  167 + context().stop(sessionRef.actor);
167 sessionActors.remove(remoteAddress); 168 sessionActors.remove(remoteAddress);
168 pendingMsgs.remove(remoteAddress); 169 pendingMsgs.remove(remoteAddress);
169 if (reconnect) { 170 if (reconnect) {
@@ -173,9 +174,13 @@ public class RpcManagerActor extends ContextAwareActor { @@ -173,9 +174,13 @@ public class RpcManagerActor extends ContextAwareActor {
173 } 174 }
174 175
175 private void onCreateSessionRequest(RpcSessionCreateRequestMsg msg) { 176 private void onCreateSessionRequest(RpcSessionCreateRequestMsg msg) {
176 - ActorRef actorRef = createSessionActor(msg);  
177 if (msg.getRemoteAddress() != null) { 177 if (msg.getRemoteAddress() != null) {
178 - register(msg.getRemoteAddress(), msg.getMsgUid(), actorRef); 178 + if (!sessionActors.containsKey(msg.getRemoteAddress())) {
  179 + ActorRef actorRef = createSessionActor(msg);
  180 + register(msg.getRemoteAddress(), msg.getMsgUid(), actorRef);
  181 + }
  182 + } else {
  183 + createSessionActor(msg);
179 } 184 }
180 } 185 }
181 186
@@ -194,7 +199,8 @@ public class RpcManagerActor extends ContextAwareActor { @@ -194,7 +199,8 @@ public class RpcManagerActor extends ContextAwareActor {
194 private ActorRef createSessionActor(RpcSessionCreateRequestMsg msg) { 199 private ActorRef createSessionActor(RpcSessionCreateRequestMsg msg) {
195 log.info("[{}] Creating session actor.", msg.getMsgUid()); 200 log.info("[{}] Creating session actor.", msg.getMsgUid());
196 ActorRef actor = context().actorOf( 201 ActorRef actor = context().actorOf(
197 - Props.create(new RpcSessionActor.ActorCreator(systemContext, msg.getMsgUid())).withDispatcher(DefaultActorService.RPC_DISPATCHER_NAME)); 202 + Props.create(new RpcSessionActor.ActorCreator(systemContext, msg.getMsgUid()))
  203 + .withDispatcher(DefaultActorService.RPC_DISPATCHER_NAME));
198 actor.tell(msg, context().self()); 204 actor.tell(msg, context().self());
199 return actor; 205 return actor;
200 } 206 }
@@ -207,8 +213,18 @@ public class RpcManagerActor extends ContextAwareActor { @@ -207,8 +213,18 @@ public class RpcManagerActor extends ContextAwareActor {
207 } 213 }
208 214
209 @Override 215 @Override
210 - public RpcManagerActor create() throws Exception { 216 + public RpcManagerActor create() {
211 return new RpcManagerActor(context); 217 return new RpcManagerActor(context);
212 } 218 }
213 } 219 }
  220 +
  221 + @Override
  222 + public SupervisorStrategy supervisorStrategy() {
  223 + return strategy;
  224 + }
  225 +
  226 + private final SupervisorStrategy strategy = new OneForOneStrategy(3, Duration.create("1 minute"), t -> {
  227 + log.warn("Unknown failure", t);
  228 + return SupervisorStrategy.resume();
  229 + });
214 } 230 }
@@ -15,12 +15,10 @@ @@ -15,12 +15,10 @@
15 */ 15 */
16 package org.thingsboard.server.actors.rpc; 16 package org.thingsboard.server.actors.rpc;
17 17
18 -import akka.event.Logging;  
19 -import akka.event.LoggingAdapter;  
20 -import io.grpc.Channel;  
21 import io.grpc.ManagedChannel; 18 import io.grpc.ManagedChannel;
22 import io.grpc.ManagedChannelBuilder; 19 import io.grpc.ManagedChannelBuilder;
23 import io.grpc.stub.StreamObserver; 20 import io.grpc.stub.StreamObserver;
  21 +import lombok.extern.slf4j.Slf4j;
24 import org.thingsboard.server.actors.ActorSystemContext; 22 import org.thingsboard.server.actors.ActorSystemContext;
25 import org.thingsboard.server.actors.service.ContextAwareActor; 23 import org.thingsboard.server.actors.service.ContextAwareActor;
26 import org.thingsboard.server.actors.service.ContextBasedCreator; 24 import org.thingsboard.server.actors.service.ContextBasedCreator;
@@ -38,15 +36,15 @@ import static org.thingsboard.server.gen.cluster.ClusterAPIProtos.MessageType.CO @@ -38,15 +36,15 @@ import static org.thingsboard.server.gen.cluster.ClusterAPIProtos.MessageType.CO
38 /** 36 /**
39 * @author Andrew Shvayka 37 * @author Andrew Shvayka
40 */ 38 */
  39 +@Slf4j
41 public class RpcSessionActor extends ContextAwareActor { 40 public class RpcSessionActor extends ContextAwareActor {
42 41
43 - private final LoggingAdapter log = Logging.getLogger(getContext().system(), this);  
44 42
45 private final UUID sessionId; 43 private final UUID sessionId;
46 private GrpcSession session; 44 private GrpcSession session;
47 private GrpcSessionListener listener; 45 private GrpcSessionListener listener;
48 46
49 - public RpcSessionActor(ActorSystemContext systemContext, UUID sessionId) { 47 + private RpcSessionActor(ActorSystemContext systemContext, UUID sessionId) {
50 super(systemContext); 48 super(systemContext);
51 this.sessionId = sessionId; 49 this.sessionId = sessionId;
52 } 50 }
@@ -58,7 +56,7 @@ public class RpcSessionActor extends ContextAwareActor { @@ -58,7 +56,7 @@ public class RpcSessionActor extends ContextAwareActor {
58 } 56 }
59 57
60 @Override 58 @Override
61 - public void onReceive(Object msg) throws Exception { 59 + public void onReceive(Object msg) {
62 if (msg instanceof ClusterAPIProtos.ClusterMessage) { 60 if (msg instanceof ClusterAPIProtos.ClusterMessage) {
63 tell((ClusterAPIProtos.ClusterMessage) msg); 61 tell((ClusterAPIProtos.ClusterMessage) msg);
64 } else if (msg instanceof RpcSessionCreateRequestMsg) { 62 } else if (msg instanceof RpcSessionCreateRequestMsg) {
@@ -67,19 +65,29 @@ public class RpcSessionActor extends ContextAwareActor { @@ -67,19 +65,29 @@ public class RpcSessionActor extends ContextAwareActor {
67 } 65 }
68 66
69 private void tell(ClusterAPIProtos.ClusterMessage msg) { 67 private void tell(ClusterAPIProtos.ClusterMessage msg) {
70 - session.sendMsg(msg); 68 + if (session != null) {
  69 + session.sendMsg(msg);
  70 + } else {
  71 + log.trace("Failed to send message due to missing session!");
  72 + }
71 } 73 }
72 74
73 @Override 75 @Override
74 public void postStop() { 76 public void postStop() {
75 - log.info("Closing session -> {}", session.getRemoteServer());  
76 - session.close(); 77 + if (session != null) {
  78 + log.info("Closing session -> {}", session.getRemoteServer());
  79 + try {
  80 + session.close();
  81 + } catch (RuntimeException e) {
  82 + log.trace("Failed to close session!", e);
  83 + }
  84 + }
77 } 85 }
78 86
79 private void initSession(RpcSessionCreateRequestMsg msg) { 87 private void initSession(RpcSessionCreateRequestMsg msg) {
80 log.info("[{}] Initializing session", context().self()); 88 log.info("[{}] Initializing session", context().self());
81 ServerAddress remoteServer = msg.getRemoteAddress(); 89 ServerAddress remoteServer = msg.getRemoteAddress();
82 - listener = new BasicRpcSessionListener(systemContext.getActorService(), context().parent(), context().self()); 90 + listener = new BasicRpcSessionListener(systemContext, context().parent(), context().self());
83 if (msg.getRemoteAddress() == null) { 91 if (msg.getRemoteAddress() == null) {
84 // Server session 92 // Server session
85 session = new GrpcSession(listener); 93 session = new GrpcSession(listener);
@@ -113,7 +121,7 @@ public class RpcSessionActor extends ContextAwareActor { @@ -113,7 +121,7 @@ public class RpcSessionActor extends ContextAwareActor {
113 } 121 }
114 122
115 @Override 123 @Override
116 - public RpcSessionActor create() throws Exception { 124 + public RpcSessionActor create() {
117 return new RpcSessionActor(context, sessionId); 125 return new RpcSessionActor(context, sessionId);
118 } 126 }
119 } 127 }
@@ -15,6 +15,7 @@ @@ -15,6 +15,7 @@
15 */ 15 */
16 package org.thingsboard.server.actors.ruleChain; 16 package org.thingsboard.server.actors.ruleChain;
17 17
  18 +import akka.actor.ActorInitializationException;
18 import akka.actor.OneForOneStrategy; 19 import akka.actor.OneForOneStrategy;
19 import akka.actor.SupervisorStrategy; 20 import akka.actor.SupervisorStrategy;
20 import org.thingsboard.server.actors.ActorSystemContext; 21 import org.thingsboard.server.actors.ActorSystemContext;
@@ -33,7 +34,7 @@ public class RuleChainActor extends ComponentActor<RuleChainId, RuleChainActorMe @@ -33,7 +34,7 @@ public class RuleChainActor extends ComponentActor<RuleChainId, RuleChainActorMe
33 private RuleChainActor(ActorSystemContext systemContext, TenantId tenantId, RuleChainId ruleChainId) { 34 private RuleChainActor(ActorSystemContext systemContext, TenantId tenantId, RuleChainId ruleChainId) {
34 super(systemContext, tenantId, ruleChainId); 35 super(systemContext, tenantId, ruleChainId);
35 setProcessor(new RuleChainActorMessageProcessor(tenantId, ruleChainId, systemContext, 36 setProcessor(new RuleChainActorMessageProcessor(tenantId, ruleChainId, systemContext,
36 - logger, context().parent(), context().self())); 37 + context().parent(), context().self()));
37 } 38 }
38 39
39 @Override 40 @Override
@@ -79,7 +80,7 @@ public class RuleChainActor extends ComponentActor<RuleChainId, RuleChainActorMe @@ -79,7 +80,7 @@ public class RuleChainActor extends ComponentActor<RuleChainId, RuleChainActorMe
79 } 80 }
80 81
81 @Override 82 @Override
82 - public RuleChainActor create() throws Exception { 83 + public RuleChainActor create() {
83 return new RuleChainActor(context, tenantId, ruleChainId); 84 return new RuleChainActor(context, tenantId, ruleChainId);
84 } 85 }
85 } 86 }
@@ -23,6 +23,7 @@ import com.datastax.driver.core.utils.UUIDs; @@ -23,6 +23,7 @@ import com.datastax.driver.core.utils.UUIDs;
23 23
24 import java.util.Optional; 24 import java.util.Optional;
25 25
  26 +import lombok.extern.slf4j.Slf4j;
26 import org.thingsboard.server.actors.ActorSystemContext; 27 import org.thingsboard.server.actors.ActorSystemContext;
27 import org.thingsboard.server.actors.device.DeviceActorToRuleEngineMsg; 28 import org.thingsboard.server.actors.device.DeviceActorToRuleEngineMsg;
28 import org.thingsboard.server.actors.service.DefaultActorService; 29 import org.thingsboard.server.actors.service.DefaultActorService;
@@ -55,6 +56,7 @@ import java.util.stream.Collectors; @@ -55,6 +56,7 @@ import java.util.stream.Collectors;
55 /** 56 /**
56 * @author Andrew Shvayka 57 * @author Andrew Shvayka
57 */ 58 */
  59 +@Slf4j
58 public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleChainId> { 60 public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleChainId> {
59 61
60 private static final long DEFAULT_CLUSTER_PARTITION = 0L; 62 private static final long DEFAULT_CLUSTER_PARTITION = 0L;
@@ -67,24 +69,34 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh @@ -67,24 +69,34 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
67 private RuleNodeId firstId; 69 private RuleNodeId firstId;
68 private RuleNodeCtx firstNode; 70 private RuleNodeCtx firstNode;
69 private boolean started; 71 private boolean started;
  72 + private String ruleChainName;
70 73
71 RuleChainActorMessageProcessor(TenantId tenantId, RuleChainId ruleChainId, ActorSystemContext systemContext 74 RuleChainActorMessageProcessor(TenantId tenantId, RuleChainId ruleChainId, ActorSystemContext systemContext
72 - , LoggingAdapter logger, ActorRef parent, ActorRef self) {  
73 - super(systemContext, logger, tenantId, ruleChainId); 75 + , ActorRef parent, ActorRef self) {
  76 + super(systemContext, tenantId, ruleChainId);
74 this.parent = parent; 77 this.parent = parent;
75 this.self = self; 78 this.self = self;
76 this.nodeActors = new HashMap<>(); 79 this.nodeActors = new HashMap<>();
77 this.nodeRoutes = new HashMap<>(); 80 this.nodeRoutes = new HashMap<>();
78 this.service = systemContext.getRuleChainService(); 81 this.service = systemContext.getRuleChainService();
  82 + this.ruleChainName = ruleChainId.toString();
79 } 83 }
80 84
81 @Override 85 @Override
82 - public void start(ActorContext context) throws Exception { 86 + public String getComponentName() {
  87 + return null;
  88 + }
  89 +
  90 + @Override
  91 + public void start(ActorContext context) {
83 if (!started) { 92 if (!started) {
84 RuleChain ruleChain = service.findRuleChainById(entityId); 93 RuleChain ruleChain = service.findRuleChainById(entityId);
  94 + ruleChainName = ruleChain.getName();
85 List<RuleNode> ruleNodeList = service.getRuleChainNodes(entityId); 95 List<RuleNode> ruleNodeList = service.getRuleChainNodes(entityId);
  96 + log.trace("[{}][{}] Starting rule chain with {} nodes", tenantId, entityId, ruleNodeList.size());
86 // Creating and starting the actors; 97 // Creating and starting the actors;
87 for (RuleNode ruleNode : ruleNodeList) { 98 for (RuleNode ruleNode : ruleNodeList) {
  99 + log.trace("[{}][{}] Creating rule node [{}]: {}", entityId, ruleNode.getId(), ruleNode.getName(), ruleNode);
88 ActorRef ruleNodeActor = createRuleNodeActor(context, ruleNode); 100 ActorRef ruleNodeActor = createRuleNodeActor(context, ruleNode);
89 nodeActors.put(ruleNode.getId(), new RuleNodeCtx(tenantId, self, ruleNodeActor, ruleNode)); 101 nodeActors.put(ruleNode.getId(), new RuleNodeCtx(tenantId, self, ruleNodeActor, ruleNode));
90 } 102 }
@@ -96,16 +108,19 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh @@ -96,16 +108,19 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
96 } 108 }
97 109
98 @Override 110 @Override
99 - public void onUpdate(ActorContext context) throws Exception { 111 + public void onUpdate(ActorContext context) {
100 RuleChain ruleChain = service.findRuleChainById(entityId); 112 RuleChain ruleChain = service.findRuleChainById(entityId);
  113 + ruleChainName = ruleChain.getName();
101 List<RuleNode> ruleNodeList = service.getRuleChainNodes(entityId); 114 List<RuleNode> ruleNodeList = service.getRuleChainNodes(entityId);
102 - 115 + log.trace("[{}][{}] Updating rule chain with {} nodes", tenantId, entityId, ruleNodeList.size());
103 for (RuleNode ruleNode : ruleNodeList) { 116 for (RuleNode ruleNode : ruleNodeList) {
104 RuleNodeCtx existing = nodeActors.get(ruleNode.getId()); 117 RuleNodeCtx existing = nodeActors.get(ruleNode.getId());
105 if (existing == null) { 118 if (existing == null) {
  119 + log.trace("[{}][{}] Creating rule node [{}]: {}", entityId, ruleNode.getId(), ruleNode.getName(), ruleNode);
106 ActorRef ruleNodeActor = createRuleNodeActor(context, ruleNode); 120 ActorRef ruleNodeActor = createRuleNodeActor(context, ruleNode);
107 nodeActors.put(ruleNode.getId(), new RuleNodeCtx(tenantId, self, ruleNodeActor, ruleNode)); 121 nodeActors.put(ruleNode.getId(), new RuleNodeCtx(tenantId, self, ruleNodeActor, ruleNode));
108 } else { 122 } else {
  123 + log.trace("[{}][{}] Updating rule node [{}]: {}", entityId, ruleNode.getId(), ruleNode.getName(), ruleNode);
109 existing.setSelf(ruleNode); 124 existing.setSelf(ruleNode);
110 existing.getSelfActor().tell(new ComponentLifecycleMsg(tenantId, existing.getSelf().getId(), ComponentLifecycleEvent.UPDATED), self); 125 existing.getSelfActor().tell(new ComponentLifecycleMsg(tenantId, existing.getSelf().getId(), ComponentLifecycleEvent.UPDATED), self);
111 } 126 }
@@ -114,6 +129,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh @@ -114,6 +129,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
114 Set<RuleNodeId> existingNodes = ruleNodeList.stream().map(RuleNode::getId).collect(Collectors.toSet()); 129 Set<RuleNodeId> existingNodes = ruleNodeList.stream().map(RuleNode::getId).collect(Collectors.toSet());
115 List<RuleNodeId> removedRules = nodeActors.keySet().stream().filter(node -> !existingNodes.contains(node)).collect(Collectors.toList()); 130 List<RuleNodeId> removedRules = nodeActors.keySet().stream().filter(node -> !existingNodes.contains(node)).collect(Collectors.toList());
116 removedRules.forEach(ruleNodeId -> { 131 removedRules.forEach(ruleNodeId -> {
  132 + log.trace("[{}][{}] Removing rule node [{}]", tenantId, entityId, ruleNodeId);
117 RuleNodeCtx removed = nodeActors.remove(ruleNodeId); 133 RuleNodeCtx removed = nodeActors.remove(ruleNodeId);
118 removed.getSelfActor().tell(new ComponentLifecycleMsg(tenantId, removed.getSelf().getId(), ComponentLifecycleEvent.DELETED), self); 134 removed.getSelfActor().tell(new ComponentLifecycleMsg(tenantId, removed.getSelf().getId(), ComponentLifecycleEvent.DELETED), self);
119 }); 135 });
@@ -122,7 +138,8 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh @@ -122,7 +138,8 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
122 } 138 }
123 139
124 @Override 140 @Override
125 - public void stop(ActorContext context) throws Exception { 141 + public void stop(ActorContext context) {
  142 + log.trace("[{}][{}] Stopping rule chain with {} nodes", tenantId, entityId, nodeActors.size());
126 nodeActors.values().stream().map(RuleNodeCtx::getSelfActor).forEach(context::stop); 143 nodeActors.values().stream().map(RuleNodeCtx::getSelfActor).forEach(context::stop);
127 nodeActors.clear(); 144 nodeActors.clear();
128 nodeRoutes.clear(); 145 nodeRoutes.clear();
@@ -131,7 +148,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh @@ -131,7 +148,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
131 } 148 }
132 149
133 @Override 150 @Override
134 - public void onClusterEventMsg(ClusterEventMsg msg) throws Exception { 151 + public void onClusterEventMsg(ClusterEventMsg msg) {
135 152
136 } 153 }
137 154
@@ -148,10 +165,12 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh @@ -148,10 +165,12 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
148 // Populating the routes map; 165 // Populating the routes map;
149 for (RuleNode ruleNode : ruleNodeList) { 166 for (RuleNode ruleNode : ruleNodeList) {
150 List<EntityRelation> relations = service.getRuleNodeRelations(ruleNode.getId()); 167 List<EntityRelation> relations = service.getRuleNodeRelations(ruleNode.getId());
  168 + log.trace("[{}][{}][{}] Processing rule node relations [{}]", tenantId, entityId, ruleNode.getId(), relations.size());
151 if (relations.size() == 0) { 169 if (relations.size() == 0) {
152 nodeRoutes.put(ruleNode.getId(), Collections.emptyList()); 170 nodeRoutes.put(ruleNode.getId(), Collections.emptyList());
153 } else { 171 } else {
154 for (EntityRelation relation : relations) { 172 for (EntityRelation relation : relations) {
  173 + log.trace("[{}][{}][{}] Processing rule node relation [{}]", tenantId, entityId, ruleNode.getId(), relation.getTo());
155 if (relation.getTo().getEntityType() == EntityType.RULE_NODE) { 174 if (relation.getTo().getEntityType() == EntityType.RULE_NODE) {
156 RuleNodeCtx ruleNodeCtx = nodeActors.get(new RuleNodeId(relation.getTo().getId())); 175 RuleNodeCtx ruleNodeCtx = nodeActors.get(new RuleNodeId(relation.getTo().getId()));
157 if (ruleNodeCtx == null) { 176 if (ruleNodeCtx == null) {
@@ -165,13 +184,15 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh @@ -165,13 +184,15 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
165 } 184 }
166 185
167 firstId = ruleChain.getFirstRuleNodeId(); 186 firstId = ruleChain.getFirstRuleNodeId();
168 - firstNode = nodeActors.get(ruleChain.getFirstRuleNodeId()); 187 + firstNode = nodeActors.get(firstId);
169 state = ComponentLifecycleState.ACTIVE; 188 state = ComponentLifecycleState.ACTIVE;
170 } 189 }
171 190
172 void onServiceToRuleEngineMsg(ServiceToRuleEngineMsg envelope) { 191 void onServiceToRuleEngineMsg(ServiceToRuleEngineMsg envelope) {
  192 + log.trace("[{}][{}] Processing message [{}]: {}", entityId, firstId, envelope.getTbMsg().getId(), envelope.getTbMsg());
173 checkActive(); 193 checkActive();
174 if (firstNode != null) { 194 if (firstNode != null) {
  195 + log.trace("[{}][{}] Pushing message to first rule node", entityId, firstId);
175 pushMsgToNode(firstNode, enrichWithRuleChainId(envelope.getTbMsg()), ""); 196 pushMsgToNode(firstNode, enrichWithRuleChainId(envelope.getTbMsg()), "");
176 } 197 }
177 } 198 }
@@ -216,7 +237,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh @@ -216,7 +237,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
216 237
217 private void onRemoteTellNext(ServerAddress serverAddress, RuleNodeToRuleChainTellNextMsg envelope) { 238 private void onRemoteTellNext(ServerAddress serverAddress, RuleNodeToRuleChainTellNextMsg envelope) {
218 TbMsg msg = envelope.getMsg(); 239 TbMsg msg = envelope.getMsg();
219 - logger.debug("Forwarding [{}] msg to remote server [{}] due to changed originator id: [{}]", msg.getId(), serverAddress, msg.getOriginator()); 240 + log.debug("Forwarding [{}] msg to remote server [{}] due to changed originator id: [{}]", msg.getId(), serverAddress, msg.getOriginator());
220 envelope = new RemoteToRuleChainTellNextMsg(envelope, tenantId, entityId); 241 envelope = new RemoteToRuleChainTellNextMsg(envelope, tenantId, entityId);
221 systemContext.getRpcService().tell(systemContext.getEncodingService().convertToProtoDataMessage(serverAddress, envelope)); 242 systemContext.getRpcService().tell(systemContext.getEncodingService().convertToProtoDataMessage(serverAddress, envelope));
222 } 243 }
@@ -230,17 +251,20 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh @@ -230,17 +251,20 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
230 int relationsCount = relations.size(); 251 int relationsCount = relations.size();
231 EntityId ackId = msg.getRuleNodeId() != null ? msg.getRuleNodeId() : msg.getRuleChainId(); 252 EntityId ackId = msg.getRuleNodeId() != null ? msg.getRuleNodeId() : msg.getRuleChainId();
232 if (relationsCount == 0) { 253 if (relationsCount == 0) {
  254 + log.trace("[{}][{}][{}] No outbound relations to process", tenantId, entityId, msg.getId());
233 if (ackId != null) { 255 if (ackId != null) {
234 // TODO: Ack this message in Kafka 256 // TODO: Ack this message in Kafka
235 // queue.ack(tenantId, msg, ackId.getId(), msg.getClusterPartition()); 257 // queue.ack(tenantId, msg, ackId.getId(), msg.getClusterPartition());
236 } 258 }
237 } else if (relationsCount == 1) { 259 } else if (relationsCount == 1) {
238 for (RuleNodeRelation relation : relations) { 260 for (RuleNodeRelation relation : relations) {
  261 + log.trace("[{}][{}][{}] Pushing message to single target: [{}]", tenantId, entityId, msg.getId(), relation.getOut());
239 pushToTarget(msg, relation.getOut(), relation.getType()); 262 pushToTarget(msg, relation.getOut(), relation.getType());
240 } 263 }
241 } else { 264 } else {
242 for (RuleNodeRelation relation : relations) { 265 for (RuleNodeRelation relation : relations) {
243 EntityId target = relation.getOut(); 266 EntityId target = relation.getOut();
  267 + log.trace("[{}][{}][{}] Pushing message to multiple targets: [{}]", tenantId, entityId, msg.getId(), relation.getOut());
244 switch (target.getEntityType()) { 268 switch (target.getEntityType()) {
245 case RULE_NODE: 269 case RULE_NODE:
246 enqueueAndForwardMsgCopyToNode(msg, target, relation.getType()); 270 enqueueAndForwardMsgCopyToNode(msg, target, relation.getType());
@@ -32,7 +32,7 @@ public class RuleNodeActor extends ComponentActor<RuleNodeId, RuleNodeActorMessa @@ -32,7 +32,7 @@ public class RuleNodeActor extends ComponentActor<RuleNodeId, RuleNodeActorMessa
32 super(systemContext, tenantId, ruleNodeId); 32 super(systemContext, tenantId, ruleNodeId);
33 this.ruleChainId = ruleChainId; 33 this.ruleChainId = ruleChainId;
34 setProcessor(new RuleNodeActorMessageProcessor(tenantId, ruleChainId, ruleNodeId, systemContext, 34 setProcessor(new RuleNodeActorMessageProcessor(tenantId, ruleChainId, ruleNodeId, systemContext,
35 - logger, context().parent(), context().self())); 35 + context().parent(), context().self()));
36 } 36 }
37 37
38 @Override 38 @Override
@@ -60,7 +60,9 @@ public class RuleNodeActor extends ComponentActor<RuleNodeId, RuleNodeActorMessa @@ -60,7 +60,9 @@ public class RuleNodeActor extends ComponentActor<RuleNodeId, RuleNodeActorMessa
60 } 60 }
61 61
62 private void onRuleNodeToSelfMsg(RuleNodeToSelfMsg msg) { 62 private void onRuleNodeToSelfMsg(RuleNodeToSelfMsg msg) {
63 - logger.debug("[{}] Going to process rule msg: {}", id, msg.getMsg()); 63 + if (log.isDebugEnabled()) {
  64 + log.debug("[{}][{}][{}] Going to process rule msg: {}", ruleChainId, id, processor.getComponentName(), msg.getMsg());
  65 + }
64 try { 66 try {
65 processor.onRuleToSelfMsg(msg); 67 processor.onRuleToSelfMsg(msg);
66 increaseMessagesProcessedCount(); 68 increaseMessagesProcessedCount();
@@ -70,7 +72,9 @@ public class RuleNodeActor extends ComponentActor<RuleNodeId, RuleNodeActorMessa @@ -70,7 +72,9 @@ public class RuleNodeActor extends ComponentActor<RuleNodeId, RuleNodeActorMessa
70 } 72 }
71 73
72 private void onRuleChainToRuleNodeMsg(RuleChainToRuleNodeMsg msg) { 74 private void onRuleChainToRuleNodeMsg(RuleChainToRuleNodeMsg msg) {
73 - logger.debug("[{}] Going to process rule msg: {}", id, msg.getMsg()); 75 + if (log.isDebugEnabled()) {
  76 + log.debug("[{}][{}][{}] Going to process rule msg: {}", ruleChainId, id, processor.getComponentName(), msg.getMsg());
  77 + }
74 try { 78 try {
75 processor.onRuleChainToRuleNodeMsg(msg); 79 processor.onRuleChainToRuleNodeMsg(msg);
76 increaseMessagesProcessedCount(); 80 increaseMessagesProcessedCount();
@@ -44,8 +44,8 @@ public class RuleNodeActorMessageProcessor extends ComponentMsgProcessor<RuleNod @@ -44,8 +44,8 @@ public class RuleNodeActorMessageProcessor extends ComponentMsgProcessor<RuleNod
44 private TbContext defaultCtx; 44 private TbContext defaultCtx;
45 45
46 RuleNodeActorMessageProcessor(TenantId tenantId, RuleChainId ruleChainId, RuleNodeId ruleNodeId, ActorSystemContext systemContext 46 RuleNodeActorMessageProcessor(TenantId tenantId, RuleChainId ruleChainId, RuleNodeId ruleNodeId, ActorSystemContext systemContext
47 - , LoggingAdapter logger, ActorRef parent, ActorRef self) {  
48 - super(systemContext, logger, tenantId, ruleNodeId); 47 + , ActorRef parent, ActorRef self) {
  48 + super(systemContext, tenantId, ruleNodeId);
49 this.parent = parent; 49 this.parent = parent;
50 this.self = self; 50 this.self = self;
51 this.service = systemContext.getRuleChainService(); 51 this.service = systemContext.getRuleChainService();
@@ -75,7 +75,7 @@ public class RuleNodeActorMessageProcessor extends ComponentMsgProcessor<RuleNod @@ -75,7 +75,7 @@ public class RuleNodeActorMessageProcessor extends ComponentMsgProcessor<RuleNod
75 } 75 }
76 76
77 @Override 77 @Override
78 - public void stop(ActorContext context) throws Exception { 78 + public void stop(ActorContext context) {
79 if (tbNode != null) { 79 if (tbNode != null) {
80 tbNode.destroy(); 80 tbNode.destroy();
81 } 81 }
@@ -83,7 +83,7 @@ public class RuleNodeActorMessageProcessor extends ComponentMsgProcessor<RuleNod @@ -83,7 +83,7 @@ public class RuleNodeActorMessageProcessor extends ComponentMsgProcessor<RuleNod
83 } 83 }
84 84
85 @Override 85 @Override
86 - public void onClusterEventMsg(ClusterEventMsg msg) throws Exception { 86 + public void onClusterEventMsg(ClusterEventMsg msg) {
87 87
88 } 88 }
89 89
@@ -111,6 +111,11 @@ public class RuleNodeActorMessageProcessor extends ComponentMsgProcessor<RuleNod @@ -111,6 +111,11 @@ public class RuleNodeActorMessageProcessor extends ComponentMsgProcessor<RuleNod
111 } 111 }
112 } 112 }
113 113
  114 + @Override
  115 + public String getComponentName() {
  116 + return ruleNode.getName();
  117 + }
  118 +
114 private TbNode initComponent(RuleNode ruleNode) throws Exception { 119 private TbNode initComponent(RuleNode ruleNode) throws Exception {
115 Class<?> componentClazz = Class.forName(ruleNode.getType()); 120 Class<?> componentClazz = Class.forName(ruleNode.getType());
116 TbNode tbNode = (TbNode) (componentClazz.newInstance()); 121 TbNode tbNode = (TbNode) (componentClazz.newInstance());
@@ -18,6 +18,7 @@ package org.thingsboard.server.actors.service; @@ -18,6 +18,7 @@ package org.thingsboard.server.actors.service;
18 import akka.actor.ActorRef; 18 import akka.actor.ActorRef;
19 import akka.event.Logging; 19 import akka.event.Logging;
20 import akka.event.LoggingAdapter; 20 import akka.event.LoggingAdapter;
  21 +import lombok.extern.slf4j.Slf4j;
21 import org.thingsboard.server.actors.ActorSystemContext; 22 import org.thingsboard.server.actors.ActorSystemContext;
22 import org.thingsboard.server.actors.shared.ComponentMsgProcessor; 23 import org.thingsboard.server.actors.shared.ComponentMsgProcessor;
23 import org.thingsboard.server.actors.stats.StatsPersistMsg; 24 import org.thingsboard.server.actors.stats.StatsPersistMsg;
@@ -32,8 +33,6 @@ import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; @@ -32,8 +33,6 @@ import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
32 */ 33 */
33 public abstract class ComponentActor<T extends EntityId, P extends ComponentMsgProcessor<T>> extends ContextAwareActor { 34 public abstract class ComponentActor<T extends EntityId, P extends ComponentMsgProcessor<T>> extends ContextAwareActor {
34 35
35 - protected final LoggingAdapter logger = Logging.getLogger(getContext().system(), this);  
36 -  
37 private long lastPersistedErrorTs = 0L; 36 private long lastPersistedErrorTs = 0L;
38 protected final TenantId tenantId; 37 protected final TenantId tenantId;
39 protected final T id; 38 protected final T id;
@@ -54,13 +53,14 @@ public abstract class ComponentActor<T extends EntityId, P extends ComponentMsgP @@ -54,13 +53,14 @@ public abstract class ComponentActor<T extends EntityId, P extends ComponentMsgP
54 @Override 53 @Override
55 public void preStart() { 54 public void preStart() {
56 try { 55 try {
  56 + log.debug("[{}][{}][{}] Starting processor.", tenantId, id, id.getEntityType());
57 processor.start(context()); 57 processor.start(context());
58 logLifecycleEvent(ComponentLifecycleEvent.STARTED); 58 logLifecycleEvent(ComponentLifecycleEvent.STARTED);
59 if (systemContext.isStatisticsEnabled()) { 59 if (systemContext.isStatisticsEnabled()) {
60 scheduleStatsPersistTick(); 60 scheduleStatsPersistTick();
61 } 61 }
62 } catch (Exception e) { 62 } catch (Exception e) {
63 - logger.warning("[{}][{}] Failed to start {} processor: {}", tenantId, id, id.getEntityType(), e); 63 + log.warn("[{}][{}] Failed to start {} processor: {}", tenantId, id, id.getEntityType(), e);
64 logAndPersist("OnStart", e, true); 64 logAndPersist("OnStart", e, true);
65 logLifecycleEvent(ComponentLifecycleEvent.STARTED, e); 65 logLifecycleEvent(ComponentLifecycleEvent.STARTED, e);
66 } 66 }
@@ -70,7 +70,7 @@ public abstract class ComponentActor<T extends EntityId, P extends ComponentMsgP @@ -70,7 +70,7 @@ public abstract class ComponentActor<T extends EntityId, P extends ComponentMsgP
70 try { 70 try {
71 processor.scheduleStatsPersistTick(context(), systemContext.getStatisticsPersistFrequency()); 71 processor.scheduleStatsPersistTick(context(), systemContext.getStatisticsPersistFrequency());
72 } catch (Exception e) { 72 } catch (Exception e) {
73 - logger.error("[{}][{}] Failed to schedule statistics store message. No statistics is going to be stored: {}", tenantId, id, e.getMessage()); 73 + log.error("[{}][{}] Failed to schedule statistics store message. No statistics is going to be stored: {}", tenantId, id, e.getMessage());
74 logAndPersist("onScheduleStatsPersistMsg", e); 74 logAndPersist("onScheduleStatsPersistMsg", e);
75 } 75 }
76 } 76 }
@@ -78,16 +78,18 @@ public abstract class ComponentActor<T extends EntityId, P extends ComponentMsgP @@ -78,16 +78,18 @@ public abstract class ComponentActor<T extends EntityId, P extends ComponentMsgP
78 @Override 78 @Override
79 public void postStop() { 79 public void postStop() {
80 try { 80 try {
  81 + log.debug("[{}][{}] Stopping processor.", tenantId, id, id.getEntityType());
81 processor.stop(context()); 82 processor.stop(context());
82 logLifecycleEvent(ComponentLifecycleEvent.STOPPED); 83 logLifecycleEvent(ComponentLifecycleEvent.STOPPED);
83 } catch (Exception e) { 84 } catch (Exception e) {
84 - logger.warning("[{}][{}] Failed to stop {} processor: {}", tenantId, id, id.getEntityType(), e.getMessage()); 85 + log.warn("[{}][{}] Failed to stop {} processor: {}", tenantId, id, id.getEntityType(), e.getMessage());
85 logAndPersist("OnStop", e, true); 86 logAndPersist("OnStop", e, true);
86 logLifecycleEvent(ComponentLifecycleEvent.STOPPED, e); 87 logLifecycleEvent(ComponentLifecycleEvent.STOPPED, e);
87 } 88 }
88 } 89 }
89 90
90 protected void onComponentLifecycleMsg(ComponentLifecycleMsg msg) { 91 protected void onComponentLifecycleMsg(ComponentLifecycleMsg msg) {
  92 + log.debug("[{}][{}][{}] onComponentLifecycleMsg: [{}]", tenantId, id, id.getEntityType(), msg.getEvent());
91 try { 93 try {
92 switch (msg.getEvent()) { 94 switch (msg.getEvent()) {
93 case CREATED: 95 case CREATED:
@@ -148,9 +150,9 @@ public abstract class ComponentActor<T extends EntityId, P extends ComponentMsgP @@ -148,9 +150,9 @@ public abstract class ComponentActor<T extends EntityId, P extends ComponentMsgP
148 private void logAndPersist(String method, Exception e, boolean critical) { 150 private void logAndPersist(String method, Exception e, boolean critical) {
149 errorsOccurred++; 151 errorsOccurred++;
150 if (critical) { 152 if (critical) {
151 - logger.warning("[{}][{}] Failed to process {} msg: {}", id, tenantId, method, e); 153 + log.warn("[{}][{}][{}] Failed to process {} msg: {}", id, tenantId, processor.getComponentName(), method, e);
152 } else { 154 } else {
153 - logger.debug("[{}][{}] Failed to process {} msg: {}", id, tenantId, method, e); 155 + log.debug("[{}][{}][{}] Failed to process {} msg: {}", id, tenantId, processor.getComponentName(), method, e);
154 } 156 }
155 long ts = System.currentTimeMillis(); 157 long ts = System.currentTimeMillis();
156 if (ts - lastPersistedErrorTs > getErrorPersistFrequency()) { 158 if (ts - lastPersistedErrorTs > getErrorPersistFrequency()) {
@@ -15,14 +15,17 @@ @@ -15,14 +15,17 @@
15 */ 15 */
16 package org.thingsboard.server.actors.service; 16 package org.thingsboard.server.actors.service;
17 17
  18 +import akka.actor.Terminated;
18 import akka.actor.UntypedActor; 19 import akka.actor.UntypedActor;
19 -import akka.event.Logging;  
20 -import akka.event.LoggingAdapter; 20 +import org.slf4j.Logger;
  21 +import org.slf4j.LoggerFactory;
21 import org.thingsboard.server.actors.ActorSystemContext; 22 import org.thingsboard.server.actors.ActorSystemContext;
22 import org.thingsboard.server.common.msg.TbActorMsg; 23 import org.thingsboard.server.common.msg.TbActorMsg;
23 24
  25 +
24 public abstract class ContextAwareActor extends UntypedActor { 26 public abstract class ContextAwareActor extends UntypedActor {
25 - protected final LoggingAdapter logger = Logging.getLogger(getContext().system(), this); 27 +
  28 + protected final Logger log = LoggerFactory.getLogger(getClass());
26 29
27 public static final int ENTITY_PACK_LIMIT = 1024; 30 public static final int ENTITY_PACK_LIMIT = 1024;
28 31
@@ -34,22 +37,27 @@ public abstract class ContextAwareActor extends UntypedActor { @@ -34,22 +37,27 @@ public abstract class ContextAwareActor extends UntypedActor {
34 } 37 }
35 38
36 @Override 39 @Override
37 - public void onReceive(Object msg) throws Exception {  
38 - if (logger.isDebugEnabled()) {  
39 - logger.debug("Processing msg: {}", msg); 40 + public void onReceive(Object msg) {
  41 + if (log.isDebugEnabled()) {
  42 + log.debug("Processing msg: {}", msg);
40 } 43 }
41 if (msg instanceof TbActorMsg) { 44 if (msg instanceof TbActorMsg) {
42 try { 45 try {
43 if (!process((TbActorMsg) msg)) { 46 if (!process((TbActorMsg) msg)) {
44 - logger.warning("Unknown message: {}!", msg); 47 + log.warn("Unknown message: {}!", msg);
45 } 48 }
46 } catch (Exception e) { 49 } catch (Exception e) {
47 throw e; 50 throw e;
48 } 51 }
  52 + } else if (msg instanceof Terminated) {
  53 + processTermination((Terminated) msg);
49 } else { 54 } else {
50 - logger.warning("Unknown message: {}!", msg); 55 + log.warn("Unknown message: {}!", msg);
51 } 56 }
52 } 57 }
53 58
  59 + protected void processTermination(Terminated msg) {
  60 + }
  61 +
54 protected abstract boolean process(TbActorMsg msg); 62 protected abstract boolean process(TbActorMsg msg);
55 } 63 }
@@ -22,11 +22,14 @@ import akka.actor.Terminated; @@ -22,11 +22,14 @@ import akka.actor.Terminated;
22 import com.google.protobuf.ByteString; 22 import com.google.protobuf.ByteString;
23 import lombok.extern.slf4j.Slf4j; 23 import lombok.extern.slf4j.Slf4j;
24 import org.springframework.beans.factory.annotation.Autowired; 24 import org.springframework.beans.factory.annotation.Autowired;
  25 +import org.springframework.boot.context.event.ApplicationReadyEvent;
  26 +import org.springframework.context.event.EventListener;
25 import org.springframework.stereotype.Service; 27 import org.springframework.stereotype.Service;
26 import org.thingsboard.rule.engine.api.msg.DeviceCredentialsUpdateNotificationMsg; 28 import org.thingsboard.rule.engine.api.msg.DeviceCredentialsUpdateNotificationMsg;
27 import org.thingsboard.rule.engine.api.msg.DeviceNameOrTypeUpdateMsg; 29 import org.thingsboard.rule.engine.api.msg.DeviceNameOrTypeUpdateMsg;
28 import org.thingsboard.server.actors.ActorSystemContext; 30 import org.thingsboard.server.actors.ActorSystemContext;
29 import org.thingsboard.server.actors.app.AppActor; 31 import org.thingsboard.server.actors.app.AppActor;
  32 +import org.thingsboard.server.actors.app.AppInitMsg;
30 import org.thingsboard.server.actors.rpc.RpcBroadcastMsg; 33 import org.thingsboard.server.actors.rpc.RpcBroadcastMsg;
31 import org.thingsboard.server.actors.rpc.RpcManagerActor; 34 import org.thingsboard.server.actors.rpc.RpcManagerActor;
32 import org.thingsboard.server.actors.rpc.RpcSessionCreateRequestMsg; 35 import org.thingsboard.server.actors.rpc.RpcSessionCreateRequestMsg;
@@ -54,6 +57,12 @@ import scala.concurrent.duration.Duration; @@ -54,6 +57,12 @@ import scala.concurrent.duration.Duration;
54 import javax.annotation.PostConstruct; 57 import javax.annotation.PostConstruct;
55 import javax.annotation.PreDestroy; 58 import javax.annotation.PreDestroy;
56 59
  60 +import java.util.Arrays;
  61 +import java.util.UUID;
  62 +import java.util.concurrent.Executors;
  63 +import java.util.concurrent.ScheduledExecutorService;
  64 +import java.util.concurrent.TimeUnit;
  65 +
57 import static org.thingsboard.server.gen.cluster.ClusterAPIProtos.MessageType.CLUSTER_ACTOR_MESSAGE; 66 import static org.thingsboard.server.gen.cluster.ClusterAPIProtos.MessageType.CLUSTER_ACTOR_MESSAGE;
58 67
59 @Service 68 @Service
@@ -86,6 +95,8 @@ public class DefaultActorService implements ActorService { @@ -86,6 +95,8 @@ public class DefaultActorService implements ActorService {
86 95
87 private ActorRef rpcManagerActor; 96 private ActorRef rpcManagerActor;
88 97
  98 + private ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
  99 +
89 @PostConstruct 100 @PostConstruct
90 public void initActorSystem() { 101 public void initActorSystem() {
91 log.info("Initializing Actor system. {}", actorContext.getRuleChainService()); 102 log.info("Initializing Actor system. {}", actorContext.getRuleChainService());
@@ -106,6 +117,12 @@ public class DefaultActorService implements ActorService { @@ -106,6 +117,12 @@ public class DefaultActorService implements ActorService {
106 log.info("Actor system initialized."); 117 log.info("Actor system initialized.");
107 } 118 }
108 119
  120 + @EventListener(ApplicationReadyEvent.class)
  121 + public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) {
  122 + log.info("Received application ready event. Sending application init message to actor system");
  123 + appActor.tell(new AppInitMsg(), ActorRef.noSender());
  124 + }
  125 +
109 @PreDestroy 126 @PreDestroy
110 public void stopActorSystem() { 127 public void stopActorSystem() {
111 Future<Terminated> status = system.terminate(); 128 Future<Terminated> status = system.terminate();
@@ -22,22 +22,22 @@ import akka.event.LoggingAdapter; @@ -22,22 +22,22 @@ import akka.event.LoggingAdapter;
22 import com.fasterxml.jackson.databind.ObjectMapper; 22 import com.fasterxml.jackson.databind.ObjectMapper;
23 import lombok.AllArgsConstructor; 23 import lombok.AllArgsConstructor;
24 import lombok.Data; 24 import lombok.Data;
  25 +import lombok.extern.slf4j.Slf4j;
25 import org.thingsboard.server.actors.ActorSystemContext; 26 import org.thingsboard.server.actors.ActorSystemContext;
26 import scala.concurrent.ExecutionContextExecutor; 27 import scala.concurrent.ExecutionContextExecutor;
27 import scala.concurrent.duration.Duration; 28 import scala.concurrent.duration.Duration;
28 29
29 import java.util.concurrent.TimeUnit; 30 import java.util.concurrent.TimeUnit;
30 31
  32 +@Slf4j
31 public abstract class AbstractContextAwareMsgProcessor { 33 public abstract class AbstractContextAwareMsgProcessor {
32 34
33 protected final ActorSystemContext systemContext; 35 protected final ActorSystemContext systemContext;
34 - protected final LoggingAdapter logger;  
35 protected final ObjectMapper mapper = new ObjectMapper(); 36 protected final ObjectMapper mapper = new ObjectMapper();
36 37
37 - protected AbstractContextAwareMsgProcessor(ActorSystemContext systemContext, LoggingAdapter logger) { 38 + protected AbstractContextAwareMsgProcessor(ActorSystemContext systemContext) {
38 super(); 39 super();
39 this.systemContext = systemContext; 40 this.systemContext = systemContext;
40 - this.logger = logger;  
41 } 41 }
42 42
43 private Scheduler getScheduler() { 43 private Scheduler getScheduler() {
@@ -53,7 +53,7 @@ public abstract class AbstractContextAwareMsgProcessor { @@ -53,7 +53,7 @@ public abstract class AbstractContextAwareMsgProcessor {
53 } 53 }
54 54
55 private void schedulePeriodicMsgWithDelay(Object msg, long delayInMs, long periodInMs, ActorRef target) { 55 private void schedulePeriodicMsgWithDelay(Object msg, long delayInMs, long periodInMs, ActorRef target) {
56 - logger.debug("Scheduling periodic msg {} every {} ms with delay {} ms", msg, periodInMs, delayInMs); 56 + log.debug("Scheduling periodic msg {} every {} ms with delay {} ms", msg, periodInMs, delayInMs);
57 getScheduler().schedule(Duration.create(delayInMs, TimeUnit.MILLISECONDS), Duration.create(periodInMs, TimeUnit.MILLISECONDS), target, msg, getSystemDispatcher(), null); 57 getScheduler().schedule(Duration.create(delayInMs, TimeUnit.MILLISECONDS), Duration.create(periodInMs, TimeUnit.MILLISECONDS), target, msg, getSystemDispatcher(), null);
58 } 58 }
59 59
@@ -62,7 +62,7 @@ public abstract class AbstractContextAwareMsgProcessor { @@ -62,7 +62,7 @@ public abstract class AbstractContextAwareMsgProcessor {
62 } 62 }
63 63
64 private void scheduleMsgWithDelay(Object msg, long delayInMs, ActorRef target) { 64 private void scheduleMsgWithDelay(Object msg, long delayInMs, ActorRef target) {
65 - logger.debug("Scheduling msg {} with delay {} ms", msg, delayInMs); 65 + log.debug("Scheduling msg {} with delay {} ms", msg, delayInMs);
66 getScheduler().scheduleOnce(Duration.create(delayInMs, TimeUnit.MILLISECONDS), target, msg, getSystemDispatcher(), null); 66 getScheduler().scheduleOnce(Duration.create(delayInMs, TimeUnit.MILLISECONDS), target, msg, getSystemDispatcher(), null);
67 } 67 }
68 68
@@ -19,6 +19,7 @@ import akka.actor.ActorContext; @@ -19,6 +19,7 @@ import akka.actor.ActorContext;
19 import akka.event.LoggingAdapter; 19 import akka.event.LoggingAdapter;
20 import com.google.common.util.concurrent.FutureCallback; 20 import com.google.common.util.concurrent.FutureCallback;
21 import com.google.common.util.concurrent.Futures; 21 import com.google.common.util.concurrent.Futures;
  22 +import lombok.extern.slf4j.Slf4j;
22 import org.thingsboard.server.actors.ActorSystemContext; 23 import org.thingsboard.server.actors.ActorSystemContext;
23 import org.thingsboard.server.actors.stats.StatsPersistTick; 24 import org.thingsboard.server.actors.stats.StatsPersistTick;
24 import org.thingsboard.server.common.data.id.EntityId; 25 import org.thingsboard.server.common.data.id.EntityId;
@@ -30,18 +31,21 @@ import org.thingsboard.server.common.msg.cluster.ClusterEventMsg; @@ -30,18 +31,21 @@ import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;
30 import javax.annotation.Nullable; 31 import javax.annotation.Nullable;
31 import java.util.function.Consumer; 32 import java.util.function.Consumer;
32 33
  34 +@Slf4j
33 public abstract class ComponentMsgProcessor<T extends EntityId> extends AbstractContextAwareMsgProcessor { 35 public abstract class ComponentMsgProcessor<T extends EntityId> extends AbstractContextAwareMsgProcessor {
34 36
35 protected final TenantId tenantId; 37 protected final TenantId tenantId;
36 protected final T entityId; 38 protected final T entityId;
37 protected ComponentLifecycleState state; 39 protected ComponentLifecycleState state;
38 40
39 - protected ComponentMsgProcessor(ActorSystemContext systemContext, LoggingAdapter logger, TenantId tenantId, T id) {  
40 - super(systemContext, logger); 41 + protected ComponentMsgProcessor(ActorSystemContext systemContext, TenantId tenantId, T id) {
  42 + super(systemContext);
41 this.tenantId = tenantId; 43 this.tenantId = tenantId;
42 this.entityId = id; 44 this.entityId = id;
43 } 45 }
44 46
  47 + public abstract String getComponentName();
  48 +
45 public abstract void start(ActorContext context) throws Exception; 49 public abstract void start(ActorContext context) throws Exception;
46 50
47 public abstract void stop(ActorContext context) throws Exception; 51 public abstract void stop(ActorContext context) throws Exception;
@@ -79,7 +83,7 @@ public abstract class ComponentMsgProcessor<T extends EntityId> extends Abstract @@ -79,7 +83,7 @@ public abstract class ComponentMsgProcessor<T extends EntityId> extends Abstract
79 83
80 protected void checkActive() { 84 protected void checkActive() {
81 if (state != ComponentLifecycleState.ACTIVE) { 85 if (state != ComponentLifecycleState.ACTIVE) {
82 - logger.warning("Rule chain is not active. Current state [{}] for processor [{}] tenant [{}]", state, tenantId, entityId); 86 + log.warn("Rule chain is not active. Current state [{}] for processor [{}] tenant [{}]", state, tenantId, entityId);
83 throw new IllegalStateException("Rule chain is not active! " + entityId + " - " + tenantId); 87 throw new IllegalStateException("Rule chain is not active! " + entityId + " - " + tenantId);
84 } 88 }
85 } 89 }
@@ -20,6 +20,8 @@ import akka.actor.ActorRef; @@ -20,6 +20,8 @@ import akka.actor.ActorRef;
20 import akka.actor.Props; 20 import akka.actor.Props;
21 import akka.actor.UntypedActor; 21 import akka.actor.UntypedActor;
22 import akka.japi.Creator; 22 import akka.japi.Creator;
  23 +import com.google.common.collect.BiMap;
  24 +import com.google.common.collect.HashBiMap;
23 import lombok.extern.slf4j.Slf4j; 25 import lombok.extern.slf4j.Slf4j;
24 import org.thingsboard.server.actors.ActorSystemContext; 26 import org.thingsboard.server.actors.ActorSystemContext;
25 import org.thingsboard.server.actors.service.ContextAwareActor; 27 import org.thingsboard.server.actors.service.ContextAwareActor;
@@ -39,11 +41,11 @@ import java.util.Map; @@ -39,11 +41,11 @@ import java.util.Map;
39 public abstract class EntityActorsManager<T extends EntityId, A extends UntypedActor, M extends SearchTextBased<? extends UUIDBased>> { 41 public abstract class EntityActorsManager<T extends EntityId, A extends UntypedActor, M extends SearchTextBased<? extends UUIDBased>> {
40 42
41 protected final ActorSystemContext systemContext; 43 protected final ActorSystemContext systemContext;
42 - protected final Map<T, ActorRef> actors; 44 + protected final BiMap<T, ActorRef> actors;
43 45
44 public EntityActorsManager(ActorSystemContext systemContext) { 46 public EntityActorsManager(ActorSystemContext systemContext) {
45 this.systemContext = systemContext; 47 this.systemContext = systemContext;
46 - this.actors = new HashMap<>(); 48 + this.actors = HashBiMap.create();
47 } 49 }
48 50
49 protected abstract TenantId getTenantId(); 51 protected abstract TenantId getTenantId();
@@ -65,7 +67,8 @@ public abstract class EntityActorsManager<T extends EntityId, A extends UntypedA @@ -65,7 +67,8 @@ public abstract class EntityActorsManager<T extends EntityId, A extends UntypedA
65 } 67 }
66 } 68 }
67 69
68 - public void visit(M entity, ActorRef actorRef) {} 70 + public void visit(M entity, ActorRef actorRef) {
  71 + }
69 72
70 public ActorRef getOrCreateActor(ActorContext context, T entityId) { 73 public ActorRef getOrCreateActor(ActorContext context, T entityId) {
71 return actors.computeIfAbsent(entityId, eId -> 74 return actors.computeIfAbsent(entityId, eId ->
@@ -15,10 +15,9 @@ @@ -15,10 +15,9 @@
15 */ 15 */
16 package org.thingsboard.server.actors.stats; 16 package org.thingsboard.server.actors.stats;
17 17
18 -import akka.event.Logging;  
19 -import akka.event.LoggingAdapter;  
20 import com.fasterxml.jackson.databind.JsonNode; 18 import com.fasterxml.jackson.databind.JsonNode;
21 import com.fasterxml.jackson.databind.ObjectMapper; 19 import com.fasterxml.jackson.databind.ObjectMapper;
  20 +import lombok.extern.slf4j.Slf4j;
22 import org.thingsboard.server.actors.ActorSystemContext; 21 import org.thingsboard.server.actors.ActorSystemContext;
23 import org.thingsboard.server.actors.service.ContextAwareActor; 22 import org.thingsboard.server.actors.service.ContextAwareActor;
24 import org.thingsboard.server.actors.service.ContextBasedCreator; 23 import org.thingsboard.server.actors.service.ContextBasedCreator;
@@ -27,9 +26,9 @@ import org.thingsboard.server.common.data.Event; @@ -27,9 +26,9 @@ import org.thingsboard.server.common.data.Event;
27 import org.thingsboard.server.common.msg.TbActorMsg; 26 import org.thingsboard.server.common.msg.TbActorMsg;
28 import org.thingsboard.server.common.msg.cluster.ServerAddress; 27 import org.thingsboard.server.common.msg.cluster.ServerAddress;
29 28
  29 +@Slf4j
30 public class StatsActor extends ContextAwareActor { 30 public class StatsActor extends ContextAwareActor {
31 31
32 - private final LoggingAdapter logger = Logging.getLogger(getContext().system(), this);  
33 private final ObjectMapper mapper = new ObjectMapper(); 32 private final ObjectMapper mapper = new ObjectMapper();
34 33
35 public StatsActor(ActorSystemContext context) { 34 public StatsActor(ActorSystemContext context) {
@@ -43,13 +42,13 @@ public class StatsActor extends ContextAwareActor { @@ -43,13 +42,13 @@ public class StatsActor extends ContextAwareActor {
43 } 42 }
44 43
45 @Override 44 @Override
46 - public void onReceive(Object msg) throws Exception {  
47 - logger.debug("Received message: {}", msg); 45 + public void onReceive(Object msg) {
  46 + log.debug("Received message: {}", msg);
48 if (msg instanceof StatsPersistMsg) { 47 if (msg instanceof StatsPersistMsg) {
49 try { 48 try {
50 onStatsPersistMsg((StatsPersistMsg) msg); 49 onStatsPersistMsg((StatsPersistMsg) msg);
51 } catch (Exception e) { 50 } catch (Exception e) {
52 - logger.warning("Failed to persist statistics: {}", msg, e); 51 + log.warn("Failed to persist statistics: {}", msg, e);
53 } 52 }
54 } 53 }
55 } 54 }
@@ -75,7 +74,7 @@ public class StatsActor extends ContextAwareActor { @@ -75,7 +74,7 @@ public class StatsActor extends ContextAwareActor {
75 } 74 }
76 75
77 @Override 76 @Override
78 - public StatsActor create() throws Exception { 77 + public StatsActor create() {
79 return new StatsActor(context); 78 return new StatsActor(context);
80 } 79 }
81 } 80 }
@@ -17,15 +17,19 @@ package org.thingsboard.server.actors.tenant; @@ -17,15 +17,19 @@ package org.thingsboard.server.actors.tenant;
17 17
18 import akka.actor.ActorInitializationException; 18 import akka.actor.ActorInitializationException;
19 import akka.actor.ActorRef; 19 import akka.actor.ActorRef;
  20 +import akka.actor.LocalActorRef;
20 import akka.actor.OneForOneStrategy; 21 import akka.actor.OneForOneStrategy;
21 import akka.actor.Props; 22 import akka.actor.Props;
22 import akka.actor.SupervisorStrategy; 23 import akka.actor.SupervisorStrategy;
  24 +import akka.actor.Terminated;
23 import akka.japi.Function; 25 import akka.japi.Function;
  26 +import com.google.common.collect.BiMap;
  27 +import com.google.common.collect.HashBiMap;
  28 +import lombok.extern.slf4j.Slf4j;
24 import org.thingsboard.server.actors.ActorSystemContext; 29 import org.thingsboard.server.actors.ActorSystemContext;
25 -import org.thingsboard.server.actors.device.DeviceActor; 30 +import org.thingsboard.server.actors.device.DeviceActorCreator;
26 import org.thingsboard.server.actors.device.DeviceActorToRuleEngineMsg; 31 import org.thingsboard.server.actors.device.DeviceActorToRuleEngineMsg;
27 import org.thingsboard.server.actors.ruleChain.RuleChainManagerActor; 32 import org.thingsboard.server.actors.ruleChain.RuleChainManagerActor;
28 -import org.thingsboard.server.actors.ruleChain.RuleChainToRuleChainMsg;  
29 import org.thingsboard.server.actors.service.ContextBasedCreator; 33 import org.thingsboard.server.actors.service.ContextBasedCreator;
30 import org.thingsboard.server.actors.service.DefaultActorService; 34 import org.thingsboard.server.actors.service.DefaultActorService;
31 import org.thingsboard.server.actors.shared.rulechain.TenantRuleChainManager; 35 import org.thingsboard.server.actors.shared.rulechain.TenantRuleChainManager;
@@ -33,6 +37,7 @@ import org.thingsboard.server.common.data.EntityType; @@ -33,6 +37,7 @@ import org.thingsboard.server.common.data.EntityType;
33 import org.thingsboard.server.common.data.id.DeviceId; 37 import org.thingsboard.server.common.data.id.DeviceId;
34 import org.thingsboard.server.common.data.id.RuleChainId; 38 import org.thingsboard.server.common.data.id.RuleChainId;
35 import org.thingsboard.server.common.data.id.TenantId; 39 import org.thingsboard.server.common.data.id.TenantId;
  40 +import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
36 import org.thingsboard.server.common.data.rule.RuleChain; 41 import org.thingsboard.server.common.data.rule.RuleChain;
37 import org.thingsboard.server.common.msg.TbActorMsg; 42 import org.thingsboard.server.common.msg.TbActorMsg;
38 import org.thingsboard.server.common.msg.aware.DeviceAwareMsg; 43 import org.thingsboard.server.common.msg.aware.DeviceAwareMsg;
@@ -47,15 +52,14 @@ import java.util.Map; @@ -47,15 +52,14 @@ import java.util.Map;
47 public class TenantActor extends RuleChainManagerActor { 52 public class TenantActor extends RuleChainManagerActor {
48 53
49 private final TenantId tenantId; 54 private final TenantId tenantId;
50 - private final Map<DeviceId, ActorRef> deviceActors; 55 + private final BiMap<DeviceId, ActorRef> deviceActors;
51 56
52 private TenantActor(ActorSystemContext systemContext, TenantId tenantId) { 57 private TenantActor(ActorSystemContext systemContext, TenantId tenantId) {
53 super(systemContext, new TenantRuleChainManager(systemContext, tenantId)); 58 super(systemContext, new TenantRuleChainManager(systemContext, tenantId));
54 this.tenantId = tenantId; 59 this.tenantId = tenantId;
55 - this.deviceActors = new HashMap<>(); 60 + this.deviceActors = HashBiMap.create();
56 } 61 }
57 62
58 -  
59 @Override 63 @Override
60 public SupervisorStrategy supervisorStrategy() { 64 public SupervisorStrategy supervisorStrategy() {
61 return strategy; 65 return strategy;
@@ -63,16 +67,21 @@ public class TenantActor extends RuleChainManagerActor { @@ -63,16 +67,21 @@ public class TenantActor extends RuleChainManagerActor {
63 67
64 @Override 68 @Override
65 public void preStart() { 69 public void preStart() {
66 - logger.info("[{}] Starting tenant actor.", tenantId); 70 + log.info("[{}] Starting tenant actor.", tenantId);
67 try { 71 try {
68 initRuleChains(); 72 initRuleChains();
69 - logger.info("[{}] Tenant actor started.", tenantId); 73 + log.info("[{}] Tenant actor started.", tenantId);
70 } catch (Exception e) { 74 } catch (Exception e) {
71 - logger.error(e, "[{}] Unknown failure", tenantId); 75 + log.warn("[{}] Unknown failure", tenantId, e);
72 } 76 }
73 } 77 }
74 78
75 @Override 79 @Override
  80 + public void postStop() {
  81 + log.info("[{}] Stopping tenant actor.", tenantId);
  82 + }
  83 +
  84 + @Override
76 protected boolean process(TbActorMsg msg) { 85 protected boolean process(TbActorMsg msg) {
77 switch (msg.getMsgType()) { 86 switch (msg.getMsgType()) {
78 case CLUSTER_EVENT_MSG: 87 case CLUSTER_EVENT_MSG:
@@ -105,22 +114,20 @@ public class TenantActor extends RuleChainManagerActor { @@ -105,22 +114,20 @@ public class TenantActor extends RuleChainManagerActor {
105 return true; 114 return true;
106 } 115 }
107 116
108 - @Override  
109 - protected void broadcast(Object msg) {  
110 - super.broadcast(msg);  
111 -// deviceActors.values().forEach(actorRef -> actorRef.tell(msg, ActorRef.noSender()));  
112 - }  
113 -  
114 private void onServiceToRuleEngineMsg(ServiceToRuleEngineMsg msg) { 117 private void onServiceToRuleEngineMsg(ServiceToRuleEngineMsg msg) {
115 - if (ruleChainManager.getRootChainActor()!=null)  
116 - ruleChainManager.getRootChainActor().tell(msg, self());  
117 - else logger.info("[{}] No Root Chain", msg); 118 + if (ruleChainManager.getRootChainActor() != null) {
  119 + ruleChainManager.getRootChainActor().tell(msg, self());
  120 + } else {
  121 + log.info("[{}] No Root Chain: {}", tenantId, msg);
  122 + }
118 } 123 }
119 124
120 private void onDeviceActorToRuleEngineMsg(DeviceActorToRuleEngineMsg msg) { 125 private void onDeviceActorToRuleEngineMsg(DeviceActorToRuleEngineMsg msg) {
121 - if (ruleChainManager.getRootChainActor()!=null)  
122 - ruleChainManager.getRootChainActor().tell(msg, self());  
123 - else logger.info("[{}] No Root Chain", msg); 126 + if (ruleChainManager.getRootChainActor() != null) {
  127 + ruleChainManager.getRootChainActor().tell(msg, self());
  128 + } else {
  129 + log.info("[{}] No Root Chain: {}", tenantId, msg);
  130 + }
124 } 131 }
125 132
126 private void onRuleChainMsg(RuleChainAwareMsg msg) { 133 private void onRuleChainMsg(RuleChainAwareMsg msg) {
@@ -141,13 +148,35 @@ public class TenantActor extends RuleChainManagerActor { @@ -141,13 +148,35 @@ public class TenantActor extends RuleChainManagerActor {
141 } 148 }
142 target.tell(msg, ActorRef.noSender()); 149 target.tell(msg, ActorRef.noSender());
143 } else { 150 } else {
144 - logger.debug("Invalid component lifecycle msg: {}", msg); 151 + log.debug("[{}] Invalid component lifecycle msg: {}", tenantId, msg);
145 } 152 }
146 } 153 }
147 154
148 private ActorRef getOrCreateDeviceActor(DeviceId deviceId) { 155 private ActorRef getOrCreateDeviceActor(DeviceId deviceId) {
149 - return deviceActors.computeIfAbsent(deviceId, k -> context().actorOf(Props.create(new DeviceActor.ActorCreator(systemContext, tenantId, deviceId))  
150 - .withDispatcher(DefaultActorService.CORE_DISPATCHER_NAME), deviceId.toString())); 156 + return deviceActors.computeIfAbsent(deviceId, k -> {
  157 + log.debug("[{}][{}] Creating device actor.", tenantId, deviceId);
  158 + ActorRef deviceActor = context().actorOf(Props.create(new DeviceActorCreator(systemContext, tenantId, deviceId))
  159 + .withDispatcher(DefaultActorService.CORE_DISPATCHER_NAME)
  160 + , deviceId.toString());
  161 + context().watch(deviceActor);
  162 + log.debug("[{}][{}] Created device actor: {}.", tenantId, deviceId, deviceActor);
  163 + return deviceActor;
  164 + });
  165 + }
  166 +
  167 + @Override
  168 + protected void processTermination(Terminated message) {
  169 + ActorRef terminated = message.actor();
  170 + if (terminated instanceof LocalActorRef) {
  171 + boolean removed = deviceActors.inverse().remove(terminated) != null;
  172 + if (removed) {
  173 + log.debug("[{}] Removed actor:", terminated);
  174 + } else {
  175 + log.warn("[{}] Removed actor was not found in the device map!");
  176 + }
  177 + } else {
  178 + throw new IllegalStateException("Remote actors are not supported!");
  179 + }
151 } 180 }
152 181
153 public static class ActorCreator extends ContextBasedCreator<TenantActor> { 182 public static class ActorCreator extends ContextBasedCreator<TenantActor> {
@@ -161,7 +190,7 @@ public class TenantActor extends RuleChainManagerActor { @@ -161,7 +190,7 @@ public class TenantActor extends RuleChainManagerActor {
161 } 190 }
162 191
163 @Override 192 @Override
164 - public TenantActor create() throws Exception { 193 + public TenantActor create() {
165 return new TenantActor(context, tenantId); 194 return new TenantActor(context, tenantId);
166 } 195 }
167 } 196 }
@@ -169,8 +198,8 @@ public class TenantActor extends RuleChainManagerActor { @@ -169,8 +198,8 @@ public class TenantActor extends RuleChainManagerActor {
169 private final SupervisorStrategy strategy = new OneForOneStrategy(3, Duration.create("1 minute"), new Function<Throwable, SupervisorStrategy.Directive>() { 198 private final SupervisorStrategy strategy = new OneForOneStrategy(3, Duration.create("1 minute"), new Function<Throwable, SupervisorStrategy.Directive>() {
170 @Override 199 @Override
171 public SupervisorStrategy.Directive apply(Throwable t) { 200 public SupervisorStrategy.Directive apply(Throwable t) {
172 - logger.error(t, "Unknown failure");  
173 - if(t instanceof ActorInitializationException){ 201 + log.warn("[{}] Unknown failure", tenantId, t);
  202 + if (t instanceof ActorInitializationException) {
174 return SupervisorStrategy.stop(); 203 return SupervisorStrategy.stop();
175 } else { 204 } else {
176 return SupervisorStrategy.resume(); 205 return SupervisorStrategy.resume();
@@ -19,9 +19,11 @@ import com.datastax.driver.core.utils.UUIDs; @@ -19,9 +19,11 @@ import com.datastax.driver.core.utils.UUIDs;
19 import com.fasterxml.jackson.databind.ObjectMapper; 19 import com.fasterxml.jackson.databind.ObjectMapper;
20 import com.fasterxml.jackson.databind.node.ArrayNode; 20 import com.fasterxml.jackson.databind.node.ArrayNode;
21 import com.fasterxml.jackson.databind.node.ObjectNode; 21 import com.fasterxml.jackson.databind.node.ObjectNode;
  22 +import lombok.Getter;
22 import lombok.extern.slf4j.Slf4j; 23 import lombok.extern.slf4j.Slf4j;
23 import org.apache.commons.lang3.StringUtils; 24 import org.apache.commons.lang3.StringUtils;
24 import org.springframework.beans.factory.annotation.Autowired; 25 import org.springframework.beans.factory.annotation.Autowired;
  26 +import org.springframework.beans.factory.annotation.Value;
25 import org.springframework.security.core.Authentication; 27 import org.springframework.security.core.Authentication;
26 import org.springframework.security.core.context.SecurityContextHolder; 28 import org.springframework.security.core.context.SecurityContextHolder;
27 import org.springframework.web.bind.annotation.ExceptionHandler; 29 import org.springframework.web.bind.annotation.ExceptionHandler;
@@ -152,6 +154,11 @@ public abstract class BaseController { @@ -152,6 +154,11 @@ public abstract class BaseController {
152 @Autowired 154 @Autowired
153 protected AttributesService attributesService; 155 protected AttributesService attributesService;
154 156
  157 + @Value("${server.log_controller_error_stack_trace}")
  158 + @Getter
  159 + private boolean logControllerErrorStackTrace;
  160 +
  161 +
155 @ExceptionHandler(ThingsboardException.class) 162 @ExceptionHandler(ThingsboardException.class)
156 public void handleThingsboardException(ThingsboardException ex, HttpServletResponse response) { 163 public void handleThingsboardException(ThingsboardException ex, HttpServletResponse response) {
157 errorResponseHandler.handle(ex, response); 164 errorResponseHandler.handle(ex, response);
@@ -162,7 +169,7 @@ public abstract class BaseController { @@ -162,7 +169,7 @@ public abstract class BaseController {
162 } 169 }
163 170
164 private ThingsboardException handleException(Exception exception, boolean logException) { 171 private ThingsboardException handleException(Exception exception, boolean logException) {
165 - if (logException) { 172 + if (logException && logControllerErrorStackTrace) {
166 log.error("Error [{}]", exception.getMessage(), exception); 173 log.error("Error [{}]", exception.getMessage(), exception);
167 } 174 }
168 175
@@ -52,7 +52,6 @@ public class DashboardController extends BaseController { @@ -52,7 +52,6 @@ public class DashboardController extends BaseController {
52 public static final String DASHBOARD_ID = "dashboardId"; 52 public static final String DASHBOARD_ID = "dashboardId";
53 53
54 @Value("${dashboard.max_datapoints_limit}") 54 @Value("${dashboard.max_datapoints_limit}")
55 - @Getter  
56 private long maxDatapointsLimit; 55 private long maxDatapointsLimit;
57 56
58 57
@@ -29,7 +29,6 @@ import org.springframework.web.bind.annotation.RequestParam; @@ -29,7 +29,6 @@ import org.springframework.web.bind.annotation.RequestParam;
29 import org.springframework.web.bind.annotation.ResponseBody; 29 import org.springframework.web.bind.annotation.ResponseBody;
30 import org.springframework.web.bind.annotation.ResponseStatus; 30 import org.springframework.web.bind.annotation.ResponseStatus;
31 import org.springframework.web.bind.annotation.RestController; 31 import org.springframework.web.bind.annotation.RestController;
32 -import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg;  
33 import org.thingsboard.server.common.data.Customer; 32 import org.thingsboard.server.common.data.Customer;
34 import org.thingsboard.server.common.data.DataConstants; 33 import org.thingsboard.server.common.data.DataConstants;
35 import org.thingsboard.server.common.data.EntitySubtype; 34 import org.thingsboard.server.common.data.EntitySubtype;
@@ -39,7 +38,6 @@ import org.thingsboard.server.common.data.audit.ActionType; @@ -39,7 +38,6 @@ import org.thingsboard.server.common.data.audit.ActionType;
39 import org.thingsboard.server.common.data.entityview.EntityViewSearchQuery; 38 import org.thingsboard.server.common.data.entityview.EntityViewSearchQuery;
40 import org.thingsboard.server.common.data.exception.ThingsboardException; 39 import org.thingsboard.server.common.data.exception.ThingsboardException;
41 import org.thingsboard.server.common.data.id.CustomerId; 40 import org.thingsboard.server.common.data.id.CustomerId;
42 -import org.thingsboard.server.common.data.id.DeviceId;  
43 import org.thingsboard.server.common.data.id.EntityId; 41 import org.thingsboard.server.common.data.id.EntityId;
44 import org.thingsboard.server.common.data.id.EntityViewId; 42 import org.thingsboard.server.common.data.id.EntityViewId;
45 import org.thingsboard.server.common.data.id.TenantId; 43 import org.thingsboard.server.common.data.id.TenantId;
@@ -47,7 +45,6 @@ import org.thingsboard.server.common.data.id.UUIDBased; @@ -47,7 +45,6 @@ import org.thingsboard.server.common.data.id.UUIDBased;
47 import org.thingsboard.server.common.data.kv.AttributeKvEntry; 45 import org.thingsboard.server.common.data.kv.AttributeKvEntry;
48 import org.thingsboard.server.common.data.page.TextPageData; 46 import org.thingsboard.server.common.data.page.TextPageData;
49 import org.thingsboard.server.common.data.page.TextPageLink; 47 import org.thingsboard.server.common.data.page.TextPageLink;
50 -import org.thingsboard.server.common.msg.cluster.SendToClusterMsg;  
51 import org.thingsboard.server.dao.exception.IncorrectParameterException; 48 import org.thingsboard.server.dao.exception.IncorrectParameterException;
52 import org.thingsboard.server.dao.model.ModelConstants; 49 import org.thingsboard.server.dao.model.ModelConstants;
53 import org.thingsboard.server.service.security.model.SecurityUser; 50 import org.thingsboard.server.service.security.model.SecurityUser;
@@ -174,7 +171,7 @@ public class EntityViewController extends BaseController { @@ -174,7 +171,7 @@ public class EntityViewController extends BaseController {
174 EntityView entityView = checkEntityViewId(entityViewId); 171 EntityView entityView = checkEntityViewId(entityViewId);
175 entityViewService.deleteEntityView(entityViewId); 172 entityViewService.deleteEntityView(entityViewId);
176 logEntityAction(entityViewId, entityView, entityView.getCustomerId(), 173 logEntityAction(entityViewId, entityView, entityView.getCustomerId(),
177 - ActionType.DELETED,null, strEntityViewId); 174 + ActionType.DELETED, null, strEntityViewId);
178 } catch (Exception e) { 175 } catch (Exception e) {
179 logEntityAction(emptyId(EntityType.ENTITY_VIEW), 176 logEntityAction(emptyId(EntityType.ENTITY_VIEW),
180 null, 177 null,
@@ -185,10 +182,23 @@ public class EntityViewController extends BaseController { @@ -185,10 +182,23 @@ public class EntityViewController extends BaseController {
185 } 182 }
186 183
187 @PreAuthorize("hasAuthority('TENANT_ADMIN')") 184 @PreAuthorize("hasAuthority('TENANT_ADMIN')")
  185 + @RequestMapping(value = "/tenant/entityViews", params = {"entityViewName"}, method = RequestMethod.GET)
  186 + @ResponseBody
  187 + public EntityView getTenantEntityView(
  188 + @RequestParam String entityViewName) throws ThingsboardException {
  189 + try {
  190 + TenantId tenantId = getCurrentUser().getTenantId();
  191 + return checkNotNull(entityViewService.findEntityViewByTenantIdAndName(tenantId, entityViewName));
  192 + } catch (Exception e) {
  193 + throw handleException(e);
  194 + }
  195 + }
  196 +
  197 + @PreAuthorize("hasAuthority('TENANT_ADMIN')")
188 @RequestMapping(value = "/customer/{customerId}/entityView/{entityViewId}", method = RequestMethod.POST) 198 @RequestMapping(value = "/customer/{customerId}/entityView/{entityViewId}", method = RequestMethod.POST)
189 @ResponseBody 199 @ResponseBody
190 public EntityView assignEntityViewToCustomer(@PathVariable(CUSTOMER_ID) String strCustomerId, 200 public EntityView assignEntityViewToCustomer(@PathVariable(CUSTOMER_ID) String strCustomerId,
191 - @PathVariable(ENTITY_VIEW_ID) String strEntityViewId) throws ThingsboardException { 201 + @PathVariable(ENTITY_VIEW_ID) String strEntityViewId) throws ThingsboardException {
192 checkParameter(CUSTOMER_ID, strCustomerId); 202 checkParameter(CUSTOMER_ID, strCustomerId);
193 checkParameter(ENTITY_VIEW_ID, strEntityViewId); 203 checkParameter(ENTITY_VIEW_ID, strEntityViewId);
194 try { 204 try {
@@ -49,9 +49,11 @@ import org.thingsboard.server.common.data.kv.Aggregation; @@ -49,9 +49,11 @@ import org.thingsboard.server.common.data.kv.Aggregation;
49 import org.thingsboard.server.common.data.kv.AttributeKey; 49 import org.thingsboard.server.common.data.kv.AttributeKey;
50 import org.thingsboard.server.common.data.kv.AttributeKvEntry; 50 import org.thingsboard.server.common.data.kv.AttributeKvEntry;
51 import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; 51 import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry;
  52 +import org.thingsboard.server.common.data.kv.BaseDeleteTsKvQuery;
52 import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery; 53 import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery;
53 import org.thingsboard.server.common.data.kv.BasicTsKvEntry; 54 import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
54 import org.thingsboard.server.common.data.kv.BooleanDataEntry; 55 import org.thingsboard.server.common.data.kv.BooleanDataEntry;
  56 +import org.thingsboard.server.common.data.kv.DeleteTsKvQuery;
55 import org.thingsboard.server.common.data.kv.DoubleDataEntry; 57 import org.thingsboard.server.common.data.kv.DoubleDataEntry;
56 import org.thingsboard.server.common.data.kv.KvEntry; 58 import org.thingsboard.server.common.data.kv.KvEntry;
57 import org.thingsboard.server.common.data.kv.LongDataEntry; 59 import org.thingsboard.server.common.data.kv.LongDataEntry;
@@ -60,12 +62,10 @@ import org.thingsboard.server.common.data.kv.StringDataEntry; @@ -60,12 +62,10 @@ import org.thingsboard.server.common.data.kv.StringDataEntry;
60 import org.thingsboard.server.common.data.kv.TsKvEntry; 62 import org.thingsboard.server.common.data.kv.TsKvEntry;
61 import org.thingsboard.server.common.msg.cluster.SendToClusterMsg; 63 import org.thingsboard.server.common.msg.cluster.SendToClusterMsg;
62 import org.thingsboard.server.common.transport.adaptor.JsonConverter; 64 import org.thingsboard.server.common.transport.adaptor.JsonConverter;
63 -import org.thingsboard.server.dao.attributes.AttributesService;  
64 import org.thingsboard.server.dao.timeseries.TimeseriesService; 65 import org.thingsboard.server.dao.timeseries.TimeseriesService;
65 import org.thingsboard.server.service.security.AccessValidator; 66 import org.thingsboard.server.service.security.AccessValidator;
66 import org.thingsboard.server.service.security.model.SecurityUser; 67 import org.thingsboard.server.service.security.model.SecurityUser;
67 import org.thingsboard.server.service.telemetry.AttributeData; 68 import org.thingsboard.server.service.telemetry.AttributeData;
68 -import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService;  
69 import org.thingsboard.server.service.telemetry.TsData; 69 import org.thingsboard.server.service.telemetry.TsData;
70 import org.thingsboard.server.service.telemetry.exception.InvalidParametersException; 70 import org.thingsboard.server.service.telemetry.exception.InvalidParametersException;
71 import org.thingsboard.server.service.telemetry.exception.UncheckedApiException; 71 import org.thingsboard.server.service.telemetry.exception.UncheckedApiException;
@@ -250,6 +250,60 @@ public class TelemetryController extends BaseController { @@ -250,6 +250,60 @@ public class TelemetryController extends BaseController {
250 } 250 }
251 251
252 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") 252 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
  253 + @RequestMapping(value = "/{entityType}/{entityId}/timeseries/delete", method = RequestMethod.DELETE)
  254 + @ResponseBody
  255 + public DeferredResult<ResponseEntity> deleteEntityTimeseries(@PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr,
  256 + @RequestParam(name = "keys") String keysStr,
  257 + @RequestParam(name = "deleteAllDataForKeys", defaultValue = "false") boolean deleteAllDataForKeys,
  258 + @RequestParam(name = "startTs", required = false) Long startTs,
  259 + @RequestParam(name = "endTs", required = false) Long endTs,
  260 + @RequestParam(name = "rewriteLatestIfDeleted", defaultValue = "false") boolean rewriteLatestIfDeleted) throws ThingsboardException {
  261 + EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr);
  262 + return deleteTimeseries(entityId, keysStr, deleteAllDataForKeys, startTs, endTs, rewriteLatestIfDeleted);
  263 + }
  264 +
  265 + private DeferredResult<ResponseEntity> deleteTimeseries(EntityId entityIdStr, String keysStr, boolean deleteAllDataForKeys,
  266 + Long startTs, Long endTs, boolean rewriteLatestIfDeleted) throws ThingsboardException {
  267 + List<String> keys = toKeysList(keysStr);
  268 + if (keys.isEmpty()) {
  269 + return getImmediateDeferredResult("Empty keys: " + keysStr, HttpStatus.BAD_REQUEST);
  270 + }
  271 + SecurityUser user = getCurrentUser();
  272 +
  273 + long deleteFromTs;
  274 + long deleteToTs;
  275 + if (deleteAllDataForKeys) {
  276 + deleteFromTs = 0L;
  277 + deleteToTs = System.currentTimeMillis();
  278 + } else {
  279 + deleteFromTs = startTs;
  280 + deleteToTs = endTs;
  281 + }
  282 +
  283 + return accessValidator.validateEntityAndCallback(user, entityIdStr, (result, entityId) -> {
  284 + List<DeleteTsKvQuery> deleteTsKvQueries = new ArrayList<>();
  285 + for (String key : keys) {
  286 + deleteTsKvQueries.add(new BaseDeleteTsKvQuery(key, deleteFromTs, deleteToTs, rewriteLatestIfDeleted));
  287 + }
  288 +
  289 + ListenableFuture<List<Void>> future = tsService.remove(entityId, deleteTsKvQueries);
  290 + Futures.addCallback(future, new FutureCallback<List<Void>>() {
  291 + @Override
  292 + public void onSuccess(@Nullable List<Void> tmp) {
  293 + logTimeseriesDeleted(user, entityId, keys, null);
  294 + result.setResult(new ResponseEntity<>(HttpStatus.OK));
  295 + }
  296 +
  297 + @Override
  298 + public void onFailure(Throwable t) {
  299 + logTimeseriesDeleted(user, entityId, keys, t);
  300 + result.setResult(new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR));
  301 + }
  302 + }, executor);
  303 + });
  304 + }
  305 +
  306 + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
253 @RequestMapping(value = "/{deviceId}/{scope}", method = RequestMethod.DELETE) 307 @RequestMapping(value = "/{deviceId}/{scope}", method = RequestMethod.DELETE)
254 @ResponseBody 308 @ResponseBody
255 public DeferredResult<ResponseEntity> deleteEntityAttributes(@PathVariable("deviceId") String deviceIdStr, 309 public DeferredResult<ResponseEntity> deleteEntityAttributes(@PathVariable("deviceId") String deviceIdStr,
@@ -506,6 +560,15 @@ public class TelemetryController extends BaseController { @@ -506,6 +560,15 @@ public class TelemetryController extends BaseController {
506 }; 560 };
507 } 561 }
508 562
  563 + private void logTimeseriesDeleted(SecurityUser user, EntityId entityId, List<String> keys, Throwable e) {
  564 + try {
  565 + logEntityAction(user, (UUIDBased & EntityId) entityId, null, null, ActionType.TIMESERIES_DELETED, toException(e),
  566 + keys);
  567 + } catch (ThingsboardException te) {
  568 + log.warn("Failed to log timeseries delete", te);
  569 + }
  570 + }
  571 +
509 private void logAttributesDeleted(SecurityUser user, EntityId entityId, String scope, List<String> keys, Throwable e) { 572 private void logAttributesDeleted(SecurityUser user, EntityId entityId, String scope, List<String> keys, Throwable e) {
510 try { 573 try {
511 logEntityAction(user, (UUIDBased & EntityId) entityId, null, null, ActionType.ATTRIBUTES_DELETED, toException(e), 574 logEntityAction(user, (UUIDBased & EntityId) entityId, null, null, ActionType.ATTRIBUTES_DELETED, toException(e),
@@ -32,6 +32,7 @@ import org.thingsboard.server.common.data.exception.ThingsboardException; @@ -32,6 +32,7 @@ import org.thingsboard.server.common.data.exception.ThingsboardException;
32 import org.thingsboard.server.common.data.id.TenantId; 32 import org.thingsboard.server.common.data.id.TenantId;
33 import org.thingsboard.server.common.data.page.TextPageData; 33 import org.thingsboard.server.common.data.page.TextPageData;
34 import org.thingsboard.server.common.data.page.TextPageLink; 34 import org.thingsboard.server.common.data.page.TextPageLink;
  35 +import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
35 import org.thingsboard.server.dao.tenant.TenantService; 36 import org.thingsboard.server.dao.tenant.TenantService;
36 import org.thingsboard.server.service.install.InstallScripts; 37 import org.thingsboard.server.service.install.InstallScripts;
37 38
@@ -84,6 +85,8 @@ public class TenantController extends BaseController { @@ -84,6 +85,8 @@ public class TenantController extends BaseController {
84 try { 85 try {
85 TenantId tenantId = new TenantId(toUUID(strTenantId)); 86 TenantId tenantId = new TenantId(toUUID(strTenantId));
86 tenantService.deleteTenant(tenantId); 87 tenantService.deleteTenant(tenantId);
  88 +
  89 + actorService.onEntityStateChange(tenantId, tenantId, ComponentLifecycleEvent.DELETED);
87 } catch (Exception e) { 90 } catch (Exception e) {
88 throw handleException(e); 91 throw handleException(e);
89 } 92 }
@@ -21,6 +21,7 @@ import org.apache.commons.lang3.SerializationException; @@ -21,6 +21,7 @@ import org.apache.commons.lang3.SerializationException;
21 import org.apache.commons.lang3.SerializationUtils; 21 import org.apache.commons.lang3.SerializationUtils;
22 import org.apache.curator.framework.CuratorFramework; 22 import org.apache.curator.framework.CuratorFramework;
23 import org.apache.curator.framework.CuratorFrameworkFactory; 23 import org.apache.curator.framework.CuratorFrameworkFactory;
  24 +import org.apache.curator.framework.imps.CuratorFrameworkState;
24 import org.apache.curator.framework.recipes.cache.ChildData; 25 import org.apache.curator.framework.recipes.cache.ChildData;
25 import org.apache.curator.framework.recipes.cache.PathChildrenCache; 26 import org.apache.curator.framework.recipes.cache.PathChildrenCache;
26 import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent; 27 import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent;
@@ -30,12 +31,14 @@ import org.apache.curator.framework.state.ConnectionStateListener; @@ -30,12 +31,14 @@ import org.apache.curator.framework.state.ConnectionStateListener;
30 import org.apache.curator.retry.RetryForever; 31 import org.apache.curator.retry.RetryForever;
31 import org.apache.curator.utils.CloseableUtils; 32 import org.apache.curator.utils.CloseableUtils;
32 import org.apache.zookeeper.CreateMode; 33 import org.apache.zookeeper.CreateMode;
  34 +import org.apache.zookeeper.KeeperException;
33 import org.springframework.beans.factory.annotation.Autowired; 35 import org.springframework.beans.factory.annotation.Autowired;
34 import org.springframework.beans.factory.annotation.Value; 36 import org.springframework.beans.factory.annotation.Value;
35 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 37 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
36 import org.springframework.boot.context.event.ApplicationReadyEvent; 38 import org.springframework.boot.context.event.ApplicationReadyEvent;
37 import org.springframework.context.ApplicationListener; 39 import org.springframework.context.ApplicationListener;
38 import org.springframework.context.annotation.Lazy; 40 import org.springframework.context.annotation.Lazy;
  41 +import org.springframework.context.event.EventListener;
39 import org.springframework.stereotype.Service; 42 import org.springframework.stereotype.Service;
40 import org.springframework.util.Assert; 43 import org.springframework.util.Assert;
41 import org.thingsboard.server.actors.service.ActorService; 44 import org.thingsboard.server.actors.service.ActorService;
@@ -51,13 +54,15 @@ import java.util.List; @@ -51,13 +54,15 @@ import java.util.List;
51 import java.util.NoSuchElementException; 54 import java.util.NoSuchElementException;
52 import java.util.stream.Collectors; 55 import java.util.stream.Collectors;
53 56
  57 +import static org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent.Type.CHILD_REMOVED;
  58 +
54 /** 59 /**
55 * @author Andrew Shvayka 60 * @author Andrew Shvayka
56 */ 61 */
57 @Service 62 @Service
58 @ConditionalOnProperty(prefix = "zk", value = "enabled", havingValue = "true", matchIfMissing = false) 63 @ConditionalOnProperty(prefix = "zk", value = "enabled", havingValue = "true", matchIfMissing = false)
59 @Slf4j 64 @Slf4j
60 -public class ZkDiscoveryService implements DiscoveryService, PathChildrenCacheListener, ApplicationListener<ApplicationReadyEvent> { 65 +public class ZkDiscoveryService implements DiscoveryService, PathChildrenCacheListener {
61 66
62 @Value("${zk.url}") 67 @Value("${zk.url}")
63 private String zkUrl; 68 private String zkUrl;
@@ -95,6 +100,8 @@ public class ZkDiscoveryService implements DiscoveryService, PathChildrenCacheLi @@ -95,6 +100,8 @@ public class ZkDiscoveryService implements DiscoveryService, PathChildrenCacheLi
95 private PathChildrenCache cache; 100 private PathChildrenCache cache;
96 private String nodePath; 101 private String nodePath;
97 102
  103 + private volatile boolean stopped = false;
  104 +
98 @PostConstruct 105 @PostConstruct
99 public void init() { 106 public void init() {
100 log.info("Initializing..."); 107 log.info("Initializing...");
@@ -115,6 +122,7 @@ public class ZkDiscoveryService implements DiscoveryService, PathChildrenCacheLi @@ -115,6 +122,7 @@ public class ZkDiscoveryService implements DiscoveryService, PathChildrenCacheLi
115 cache.start(); 122 cache.start();
116 } catch (Exception e) { 123 } catch (Exception e) {
117 log.error("Failed to connect to ZK: {}", e.getMessage(), e); 124 log.error("Failed to connect to ZK: {}", e.getMessage(), e);
  125 + CloseableUtils.closeQuietly(cache);
118 CloseableUtils.closeQuietly(client); 126 CloseableUtils.closeQuietly(client);
119 throw new RuntimeException(e); 127 throw new RuntimeException(e);
120 } 128 }
@@ -122,25 +130,50 @@ public class ZkDiscoveryService implements DiscoveryService, PathChildrenCacheLi @@ -122,25 +130,50 @@ public class ZkDiscoveryService implements DiscoveryService, PathChildrenCacheLi
122 130
123 @PreDestroy 131 @PreDestroy
124 public void destroy() { 132 public void destroy() {
  133 + stopped = true;
125 unpublishCurrentServer(); 134 unpublishCurrentServer();
  135 + CloseableUtils.closeQuietly(cache);
126 CloseableUtils.closeQuietly(client); 136 CloseableUtils.closeQuietly(client);
127 log.info("Stopped discovery service"); 137 log.info("Stopped discovery service");
128 } 138 }
129 139
130 @Override 140 @Override
131 - public void publishCurrentServer() { 141 + public synchronized void publishCurrentServer() {
  142 + ServerInstance self = this.serverInstance.getSelf();
  143 + if (currentServerExists()) {
  144 + log.info("[{}:{}] ZK node for current instance already exists, NOT created new one: {}", self.getHost(), self.getPort(), nodePath);
  145 + } else {
  146 + try {
  147 + log.info("[{}:{}] Creating ZK node for current instance", self.getHost(), self.getPort());
  148 + nodePath = client.create()
  149 + .creatingParentsIfNeeded()
  150 + .withMode(CreateMode.EPHEMERAL_SEQUENTIAL).forPath(zkNodesDir + "/", SerializationUtils.serialize(self.getServerAddress()));
  151 + log.info("[{}:{}] Created ZK node for current instance: {}", self.getHost(), self.getPort(), nodePath);
  152 + client.getConnectionStateListenable().addListener(checkReconnect(self));
  153 + } catch (Exception e) {
  154 + log.error("Failed to create ZK node", e);
  155 + throw new RuntimeException(e);
  156 + }
  157 + }
  158 + }
  159 +
  160 + private boolean currentServerExists() {
  161 + if (nodePath == null) {
  162 + return false;
  163 + }
132 try { 164 try {
133 ServerInstance self = this.serverInstance.getSelf(); 165 ServerInstance self = this.serverInstance.getSelf();
134 - log.info("[{}:{}] Creating ZK node for current instance", self.getHost(), self.getPort());  
135 - nodePath = client.create()  
136 - .creatingParentsIfNeeded()  
137 - .withMode(CreateMode.EPHEMERAL_SEQUENTIAL).forPath(zkNodesDir + "/", SerializationUtils.serialize(self.getServerAddress()));  
138 - log.info("[{}:{}] Created ZK node for current instance: {}", self.getHost(), self.getPort(), nodePath);  
139 - client.getConnectionStateListenable().addListener(checkReconnect(self)); 166 + ServerAddress registeredServerAdress = null;
  167 + registeredServerAdress = SerializationUtils.deserialize(client.getData().forPath(nodePath));
  168 + if (self.getServerAddress() != null && self.getServerAddress().equals(registeredServerAdress)) {
  169 + return true;
  170 + }
  171 + } catch (KeeperException.NoNodeException e) {
  172 + log.info("ZK node does not exist: {}", nodePath);
140 } catch (Exception e) { 173 } catch (Exception e) {
141 - log.error("Failed to create ZK node", e);  
142 - throw new RuntimeException(e); 174 + log.error("Couldn't check if ZK node exists", e);
143 } 175 }
  176 + return false;
144 } 177 }
145 178
146 private ConnectionStateListener checkReconnect(ServerInstance self) { 179 private ConnectionStateListener checkReconnect(ServerInstance self) {
@@ -200,8 +233,17 @@ public class ZkDiscoveryService implements DiscoveryService, PathChildrenCacheLi @@ -200,8 +233,17 @@ public class ZkDiscoveryService implements DiscoveryService, PathChildrenCacheLi
200 .collect(Collectors.toList()); 233 .collect(Collectors.toList());
201 } 234 }
202 235
203 - @Override 236 + @EventListener(ApplicationReadyEvent.class)
204 public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) { 237 public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) {
  238 + log.info("Received application ready event. Starting current ZK node.");
  239 + if (stopped) {
  240 + log.debug("Ignoring application ready event. Service is stopped.");
  241 + return;
  242 + }
  243 + if (client.getState() != CuratorFrameworkState.STARTED) {
  244 + log.debug("Ignoring application ready event, ZK client is not started, ZK client state [{}]", client.getState());
  245 + return;
  246 + }
205 publishCurrentServer(); 247 publishCurrentServer();
206 getOtherServers().forEach( 248 getOtherServers().forEach(
207 server -> log.info("Found active server: [{}:{}]", server.getHost(), server.getPort()) 249 server -> log.info("Found active server: [{}:{}]", server.getHost(), server.getPort())
@@ -210,6 +252,14 @@ public class ZkDiscoveryService implements DiscoveryService, PathChildrenCacheLi @@ -210,6 +252,14 @@ public class ZkDiscoveryService implements DiscoveryService, PathChildrenCacheLi
210 252
211 @Override 253 @Override
212 public void childEvent(CuratorFramework curatorFramework, PathChildrenCacheEvent pathChildrenCacheEvent) throws Exception { 254 public void childEvent(CuratorFramework curatorFramework, PathChildrenCacheEvent pathChildrenCacheEvent) throws Exception {
  255 + if (stopped) {
  256 + log.debug("Ignoring {}. Service is stopped.", pathChildrenCacheEvent);
  257 + return;
  258 + }
  259 + if (client.getState() != CuratorFrameworkState.STARTED) {
  260 + log.debug("Ignoring {}, ZK client is not started, ZK client state [{}]", pathChildrenCacheEvent, client.getState());
  261 + return;
  262 + }
213 ChildData data = pathChildrenCacheEvent.getData(); 263 ChildData data = pathChildrenCacheEvent.getData();
214 if (data == null) { 264 if (data == null) {
215 log.debug("Ignoring {} due to empty child data", pathChildrenCacheEvent); 265 log.debug("Ignoring {} due to empty child data", pathChildrenCacheEvent);
@@ -218,6 +268,10 @@ public class ZkDiscoveryService implements DiscoveryService, PathChildrenCacheLi @@ -218,6 +268,10 @@ public class ZkDiscoveryService implements DiscoveryService, PathChildrenCacheLi
218 log.debug("Ignoring {} due to empty child's data", pathChildrenCacheEvent); 268 log.debug("Ignoring {} due to empty child's data", pathChildrenCacheEvent);
219 return; 269 return;
220 } else if (nodePath != null && nodePath.equals(data.getPath())) { 270 } else if (nodePath != null && nodePath.equals(data.getPath())) {
  271 + if (pathChildrenCacheEvent.getType() == CHILD_REMOVED) {
  272 + log.info("ZK node for current instance is somehow deleted.");
  273 + publishCurrentServer();
  274 + }
221 log.debug("Ignoring event about current server {}", pathChildrenCacheEvent); 275 log.debug("Ignoring event about current server {}", pathChildrenCacheEvent);
222 return; 276 return;
223 } 277 }
  1 +/**
  2 + * Copyright © 2016-2018 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.service.executors;
  17 +
  18 +import org.springframework.beans.factory.annotation.Value;
  19 +import org.springframework.stereotype.Component;
  20 +
  21 +@Component
  22 +public class ClusterRpcCallbackExecutorService extends AbstractListeningExecutor {
  23 +
  24 + @Value("${actors.cluster.grpc_callback_thread_pool_size}")
  25 + private int grpcCallbackExecutorThreadPoolSize;
  26 +
  27 + @Override
  28 + protected int getThreadPollSize() {
  29 + return grpcCallbackExecutorThreadPoolSize;
  30 + }
  31 +
  32 +}
@@ -71,7 +71,7 @@ public class RemoteJsInvokeService extends AbstractJsInvokeService { @@ -71,7 +71,7 @@ public class RemoteJsInvokeService extends AbstractJsInvokeService {
71 private int maxErrors; 71 private int maxErrors;
72 72
73 private TbKafkaRequestTemplate<JsInvokeProtos.RemoteJsRequest, JsInvokeProtos.RemoteJsResponse> kafkaTemplate; 73 private TbKafkaRequestTemplate<JsInvokeProtos.RemoteJsRequest, JsInvokeProtos.RemoteJsResponse> kafkaTemplate;
74 - protected Map<UUID, String> scriptIdToBodysMap = new ConcurrentHashMap<>(); 74 + private Map<UUID, String> scriptIdToBodysMap = new ConcurrentHashMap<>();
75 75
76 @PostConstruct 76 @PostConstruct
77 public void init() { 77 public void init() {
@@ -100,7 +100,7 @@ public class RemoteJsInvokeService extends AbstractJsInvokeService { @@ -100,7 +100,7 @@ public class RemoteJsInvokeService extends AbstractJsInvokeService {
100 responseBuilder.settings(kafkaSettings); 100 responseBuilder.settings(kafkaSettings);
101 responseBuilder.topic(responseTopicPrefix + "." + nodeIdProvider.getNodeId()); 101 responseBuilder.topic(responseTopicPrefix + "." + nodeIdProvider.getNodeId());
102 responseBuilder.clientId("js-" + nodeIdProvider.getNodeId()); 102 responseBuilder.clientId("js-" + nodeIdProvider.getNodeId());
103 - responseBuilder.groupId("rule-engine-node"); 103 + responseBuilder.groupId("rule-engine-node-" + nodeIdProvider.getNodeId());
104 responseBuilder.autoCommit(true); 104 responseBuilder.autoCommit(true);
105 responseBuilder.autoCommitIntervalMs(autoCommitInterval); 105 responseBuilder.autoCommitIntervalMs(autoCommitInterval);
106 responseBuilder.decoder(new RemoteJsResponseDecoder()); 106 responseBuilder.decoder(new RemoteJsResponseDecoder());
@@ -136,6 +136,7 @@ public class RemoteJsInvokeService extends AbstractJsInvokeService { @@ -136,6 +136,7 @@ public class RemoteJsInvokeService extends AbstractJsInvokeService {
136 .setCompileRequest(jsRequest) 136 .setCompileRequest(jsRequest)
137 .build(); 137 .build();
138 138
  139 + log.trace("Post compile request for scriptId [{}]", scriptId);
139 ListenableFuture<JsInvokeProtos.RemoteJsResponse> future = kafkaTemplate.post(scriptId.toString(), jsRequestWrapper); 140 ListenableFuture<JsInvokeProtos.RemoteJsResponse> future = kafkaTemplate.post(scriptId.toString(), jsRequestWrapper);
140 return Futures.transform(future, response -> { 141 return Futures.transform(future, response -> {
141 JsInvokeProtos.JsCompileResponse compilationResult = response.getCompileResponse(); 142 JsInvokeProtos.JsCompileResponse compilationResult = response.getCompileResponse();
@@ -22,6 +22,7 @@ import io.jsonwebtoken.Jwts; @@ -22,6 +22,7 @@ import io.jsonwebtoken.Jwts;
22 import io.jsonwebtoken.MalformedJwtException; 22 import io.jsonwebtoken.MalformedJwtException;
23 import io.jsonwebtoken.SignatureException; 23 import io.jsonwebtoken.SignatureException;
24 import io.jsonwebtoken.UnsupportedJwtException; 24 import io.jsonwebtoken.UnsupportedJwtException;
  25 +import lombok.extern.slf4j.Slf4j;
25 import org.slf4j.Logger; 26 import org.slf4j.Logger;
26 import org.slf4j.LoggerFactory; 27 import org.slf4j.LoggerFactory;
27 import org.springframework.security.authentication.BadCredentialsException; 28 import org.springframework.security.authentication.BadCredentialsException;
@@ -29,12 +30,11 @@ import org.thingsboard.server.service.security.exception.JwtExpiredTokenExceptio @@ -29,12 +30,11 @@ import org.thingsboard.server.service.security.exception.JwtExpiredTokenExceptio
29 30
30 import java.io.Serializable; 31 import java.io.Serializable;
31 32
  33 +@Slf4j
32 public class RawAccessJwtToken implements JwtToken, Serializable { 34 public class RawAccessJwtToken implements JwtToken, Serializable {
33 35
34 private static final long serialVersionUID = -797397445703066079L; 36 private static final long serialVersionUID = -797397445703066079L;
35 37
36 - private static Logger logger = LoggerFactory.getLogger(RawAccessJwtToken.class);  
37 -  
38 private String token; 38 private String token;
39 39
40 public RawAccessJwtToken(String token) { 40 public RawAccessJwtToken(String token) {
@@ -52,10 +52,10 @@ public class RawAccessJwtToken implements JwtToken, Serializable { @@ -52,10 +52,10 @@ public class RawAccessJwtToken implements JwtToken, Serializable {
52 try { 52 try {
53 return Jwts.parser().setSigningKey(signingKey).parseClaimsJws(this.token); 53 return Jwts.parser().setSigningKey(signingKey).parseClaimsJws(this.token);
54 } catch (UnsupportedJwtException | MalformedJwtException | IllegalArgumentException | SignatureException ex) { 54 } catch (UnsupportedJwtException | MalformedJwtException | IllegalArgumentException | SignatureException ex) {
55 - logger.error("Invalid JWT Token", ex); 55 + log.error("Invalid JWT Token", ex);
56 throw new BadCredentialsException("Invalid JWT token: ", ex); 56 throw new BadCredentialsException("Invalid JWT token: ", ex);
57 } catch (ExpiredJwtException expiredEx) { 57 } catch (ExpiredJwtException expiredEx) {
58 - logger.info("JWT Token is expired", expiredEx); 58 + log.info("JWT Token is expired", expiredEx);
59 throw new JwtExpiredTokenException(this, "JWT Token expired", expiredEx); 59 throw new JwtExpiredTokenException(this, "JWT Token expired", expiredEx);
60 } 60 }
61 } 61 }
@@ -22,8 +22,8 @@ import org.springframework.stereotype.Service; @@ -22,8 +22,8 @@ import org.springframework.stereotype.Service;
22 import org.thingsboard.server.common.data.id.DeviceId; 22 import org.thingsboard.server.common.data.id.DeviceId;
23 import org.thingsboard.server.gen.transport.TransportProtos.DeviceSessionsCacheEntry; 23 import org.thingsboard.server.gen.transport.TransportProtos.DeviceSessionsCacheEntry;
24 24
25 -import java.util.ArrayList;  
26 import java.util.Collections; 25 import java.util.Collections;
  26 +import java.util.UUID;
27 27
28 import static org.thingsboard.server.common.data.CacheConstants.SESSIONS_CACHE; 28 import static org.thingsboard.server.common.data.CacheConstants.SESSIONS_CACHE;
29 29
@@ -35,16 +35,23 @@ import static org.thingsboard.server.common.data.CacheConstants.SESSIONS_CACHE; @@ -35,16 +35,23 @@ import static org.thingsboard.server.common.data.CacheConstants.SESSIONS_CACHE;
35 public class DefaultDeviceSessionCacheService implements DeviceSessionCacheService { 35 public class DefaultDeviceSessionCacheService implements DeviceSessionCacheService {
36 36
37 @Override 37 @Override
38 - @Cacheable(cacheNames = SESSIONS_CACHE, key = "#deviceId")  
39 - public DeviceSessionsCacheEntry get(DeviceId deviceId) { 38 + @Cacheable(cacheNames = SESSIONS_CACHE, key = "#deviceId.toString()")
  39 + public byte[] get(DeviceId deviceId) {
40 log.debug("[{}] Fetching session data from cache", deviceId); 40 log.debug("[{}] Fetching session data from cache", deviceId);
41 - return DeviceSessionsCacheEntry.newBuilder().addAllSessions(Collections.emptyList()).build(); 41 + return DeviceSessionsCacheEntry.newBuilder().addAllSessions(Collections.emptyList()).build().toByteArray();
42 } 42 }
43 43
44 @Override 44 @Override
45 - @CachePut(cacheNames = SESSIONS_CACHE, key = "#deviceId")  
46 - public DeviceSessionsCacheEntry put(DeviceId deviceId, DeviceSessionsCacheEntry sessions) {  
47 - log.debug("[{}] Pushing session data from cache: {}", deviceId, sessions); 45 + @CachePut(cacheNames = SESSIONS_CACHE, key = "#deviceId.toString()")
  46 + public byte[] put(DeviceId deviceId, byte[] sessions) {
  47 + log.debug("[{}] Pushing session data to cache: {}", deviceId, sessions);
48 return sessions; 48 return sessions;
49 } 49 }
  50 +
  51 + public static void main (String[] args){
  52 + UUID uuid = UUID.fromString("d5db434e-9cd2-4903-8b3b-421b2d93664d");
  53 + System.out.println(uuid.getMostSignificantBits());
  54 + System.out.println(uuid.getLeastSignificantBits());
  55 + }
  56 +
50 } 57 }
@@ -23,8 +23,8 @@ import org.thingsboard.server.gen.transport.TransportProtos.DeviceSessionsCacheE @@ -23,8 +23,8 @@ import org.thingsboard.server.gen.transport.TransportProtos.DeviceSessionsCacheE
23 */ 23 */
24 public interface DeviceSessionCacheService { 24 public interface DeviceSessionCacheService {
25 25
26 - DeviceSessionsCacheEntry get(DeviceId deviceId); 26 + byte[] get(DeviceId deviceId);
27 27
28 - DeviceSessionsCacheEntry put(DeviceId deviceId, DeviceSessionsCacheEntry sessions); 28 + byte[] put(DeviceId deviceId, byte[] sessions);
29 29
30 } 30 }
@@ -35,23 +35,11 @@ import org.thingsboard.server.common.data.id.EntityId; @@ -35,23 +35,11 @@ import org.thingsboard.server.common.data.id.EntityId;
35 import org.thingsboard.server.common.data.id.EntityIdFactory; 35 import org.thingsboard.server.common.data.id.EntityIdFactory;
36 import org.thingsboard.server.common.data.id.EntityViewId; 36 import org.thingsboard.server.common.data.id.EntityViewId;
37 import org.thingsboard.server.common.data.id.TenantId; 37 import org.thingsboard.server.common.data.id.TenantId;
38 -import org.thingsboard.server.common.data.kv.AttributeKvEntry;  
39 -import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry;  
40 -import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery;  
41 -import org.thingsboard.server.common.data.kv.BasicTsKvEntry;  
42 -import org.thingsboard.server.common.data.kv.BooleanDataEntry;  
43 -import org.thingsboard.server.common.data.kv.DataType;  
44 -import org.thingsboard.server.common.data.kv.DoubleDataEntry;  
45 -import org.thingsboard.server.common.data.kv.KvEntry;  
46 -import org.thingsboard.server.common.data.kv.LongDataEntry;  
47 -import org.thingsboard.server.common.data.kv.ReadTsKvQuery;  
48 -import org.thingsboard.server.common.data.kv.StringDataEntry;  
49 -import org.thingsboard.server.common.data.kv.TsKvEntry; 38 +import org.thingsboard.server.common.data.kv.*;
50 import org.thingsboard.server.common.msg.cluster.SendToClusterMsg; 39 import org.thingsboard.server.common.msg.cluster.SendToClusterMsg;
51 import org.thingsboard.server.common.msg.cluster.ServerAddress; 40 import org.thingsboard.server.common.msg.cluster.ServerAddress;
52 import org.thingsboard.server.dao.attributes.AttributesService; 41 import org.thingsboard.server.dao.attributes.AttributesService;
53 import org.thingsboard.server.dao.entityview.EntityViewService; 42 import org.thingsboard.server.dao.entityview.EntityViewService;
54 -import org.thingsboard.server.dao.model.ModelConstants;  
55 import org.thingsboard.server.dao.timeseries.TimeseriesService; 43 import org.thingsboard.server.dao.timeseries.TimeseriesService;
56 import org.thingsboard.server.gen.cluster.ClusterAPIProtos; 44 import org.thingsboard.server.gen.cluster.ClusterAPIProtos;
57 import org.thingsboard.server.service.cluster.routing.ClusterRoutingService; 45 import org.thingsboard.server.service.cluster.routing.ClusterRoutingService;
@@ -68,7 +56,6 @@ import javax.annotation.PostConstruct; @@ -68,7 +56,6 @@ import javax.annotation.PostConstruct;
68 import javax.annotation.PreDestroy; 56 import javax.annotation.PreDestroy;
69 import java.util.ArrayList; 57 import java.util.ArrayList;
70 import java.util.Collections; 58 import java.util.Collections;
71 -import java.util.HashMap;  
72 import java.util.HashSet; 59 import java.util.HashSet;
73 import java.util.Iterator; 60 import java.util.Iterator;
74 import java.util.List; 61 import java.util.List;
@@ -339,9 +326,9 @@ public class DefaultTelemetrySubscriptionService implements TelemetrySubscriptio @@ -339,9 +326,9 @@ public class DefaultTelemetrySubscriptionService implements TelemetrySubscriptio
339 Set<Subscription> subscriptions = e.getValue(); 326 Set<Subscription> subscriptions = e.getValue();
340 Optional<ServerAddress> newAddressOptional = routingService.resolveById(e.getKey()); 327 Optional<ServerAddress> newAddressOptional = routingService.resolveById(e.getKey());
341 if (newAddressOptional.isPresent()) { 328 if (newAddressOptional.isPresent()) {
342 - newAddressOptional.ifPresent(serverAddress -> checkSubsciptionsNewAddress(serverAddress, subscriptions)); 329 + newAddressOptional.ifPresent(serverAddress -> checkSubscriptionsNewAddress(serverAddress, subscriptions));
343 } else { 330 } else {
344 - checkSubsciptionsPrevAddress(subscriptions); 331 + checkSubscriptionsPrevAddress(subscriptions);
345 } 332 }
346 if (subscriptions.size() == 0) { 333 if (subscriptions.size() == 0) {
347 log.trace("[{}] No more subscriptions for this device on current server.", e.getKey()); 334 log.trace("[{}] No more subscriptions for this device on current server.", e.getKey());
@@ -350,7 +337,7 @@ public class DefaultTelemetrySubscriptionService implements TelemetrySubscriptio @@ -350,7 +337,7 @@ public class DefaultTelemetrySubscriptionService implements TelemetrySubscriptio
350 } 337 }
351 } 338 }
352 339
353 - private void checkSubsciptionsNewAddress(ServerAddress newAddress, Set<Subscription> subscriptions) { 340 + private void checkSubscriptionsNewAddress(ServerAddress newAddress, Set<Subscription> subscriptions) {
354 Iterator<Subscription> subscriptionIterator = subscriptions.iterator(); 341 Iterator<Subscription> subscriptionIterator = subscriptions.iterator();
355 while (subscriptionIterator.hasNext()) { 342 while (subscriptionIterator.hasNext()) {
356 Subscription s = subscriptionIterator.next(); 343 Subscription s = subscriptionIterator.next();
@@ -368,7 +355,7 @@ public class DefaultTelemetrySubscriptionService implements TelemetrySubscriptio @@ -368,7 +355,7 @@ public class DefaultTelemetrySubscriptionService implements TelemetrySubscriptio
368 } 355 }
369 } 356 }
370 357
371 - private void checkSubsciptionsPrevAddress(Set<Subscription> subscriptions) { 358 + private void checkSubscriptionsPrevAddress(Set<Subscription> subscriptions) {
372 for (Subscription s : subscriptions) { 359 for (Subscription s : subscriptions) {
373 if (s.isLocal() && s.getServer() != null) { 360 if (s.isLocal() && s.getServer() != null) {
374 log.trace("[{}] Local subscription is no longer handled on remote server address [{}]", s.getWsSessionId(), s.getServer()); 361 log.trace("[{}] Local subscription is no longer handled on remote server address [{}]", s.getWsSessionId(), s.getServer());
@@ -381,7 +368,7 @@ public class DefaultTelemetrySubscriptionService implements TelemetrySubscriptio @@ -381,7 +368,7 @@ public class DefaultTelemetrySubscriptionService implements TelemetrySubscriptio
381 368
382 private void addRemoteWsSubscription(ServerAddress address, String sessionId, Subscription subscription) { 369 private void addRemoteWsSubscription(ServerAddress address, String sessionId, Subscription subscription) {
383 EntityId entityId = subscription.getEntityId(); 370 EntityId entityId = subscription.getEntityId();
384 - log.trace("[{}] Registering remote subscription [{}] for device [{}] to [{}]", sessionId, subscription.getSubscriptionId(), entityId, address); 371 + log.trace("[{}] Registering remote subscription [{}] for entity [{}] to [{}]", sessionId, subscription.getSubscriptionId(), entityId, address);
385 registerSubscription(sessionId, entityId, subscription); 372 registerSubscription(sessionId, entityId, subscription);
386 if (subscription.getType() == TelemetryFeature.ATTRIBUTES) { 373 if (subscription.getType() == TelemetryFeature.ATTRIBUTES) {
387 final Map<String, Long> keyStates = subscription.getKeyStates(); 374 final Map<String, Long> keyStates = subscription.getKeyStates();
@@ -401,17 +388,22 @@ public class DefaultTelemetrySubscriptionService implements TelemetrySubscriptio @@ -401,17 +388,22 @@ public class DefaultTelemetrySubscriptionService implements TelemetrySubscriptio
401 long curTs = System.currentTimeMillis(); 388 long curTs = System.currentTimeMillis();
402 List<ReadTsKvQuery> queries = new ArrayList<>(); 389 List<ReadTsKvQuery> queries = new ArrayList<>();
403 subscription.getKeyStates().entrySet().forEach(e -> { 390 subscription.getKeyStates().entrySet().forEach(e -> {
404 - queries.add(new BaseReadTsKvQuery(e.getKey(), e.getValue() + 1L, curTs)); 391 + if (curTs > e.getValue()) {
  392 + queries.add(new BaseReadTsKvQuery(e.getKey(), e.getValue() + 1L, curTs, 0, 1000, Aggregation.NONE));
  393 + } else {
  394 + log.debug("[{}] Invalid subscription [{}], entityId [{}] curTs [{}]", sessionId, subscription, entityId, curTs);
  395 + }
405 }); 396 });
406 -  
407 - DonAsynchron.withCallback(tsService.findAll(entityId, queries),  
408 - missedUpdates -> {  
409 - if (missedUpdates != null && !missedUpdates.isEmpty()) {  
410 - tellRemoteSubUpdate(address, sessionId, new SubscriptionUpdate(subscription.getSubscriptionId(), missedUpdates));  
411 - }  
412 - },  
413 - e -> log.error("Failed to fetch missed updates.", e),  
414 - tsCallBackExecutor); 397 + if (!queries.isEmpty()) {
  398 + DonAsynchron.withCallback(tsService.findAll(entityId, queries),
  399 + missedUpdates -> {
  400 + if (missedUpdates != null && !missedUpdates.isEmpty()) {
  401 + tellRemoteSubUpdate(address, sessionId, new SubscriptionUpdate(subscription.getSubscriptionId(), missedUpdates));
  402 + }
  403 + },
  404 + e -> log.error("Failed to fetch missed updates.", e),
  405 + tsCallBackExecutor);
  406 + }
415 } 407 }
416 } 408 }
417 409
@@ -29,6 +29,9 @@ import org.apache.kafka.clients.producer.RecordMetadata; @@ -29,6 +29,9 @@ import org.apache.kafka.clients.producer.RecordMetadata;
29 import org.springframework.beans.factory.annotation.Autowired; 29 import org.springframework.beans.factory.annotation.Autowired;
30 import org.springframework.beans.factory.annotation.Value; 30 import org.springframework.beans.factory.annotation.Value;
31 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 31 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
  32 +import org.springframework.boot.context.event.ApplicationReadyEvent;
  33 +import org.springframework.context.event.ContextRefreshedEvent;
  34 +import org.springframework.context.event.EventListener;
32 import org.springframework.stereotype.Service; 35 import org.springframework.stereotype.Service;
33 import org.thingsboard.server.actors.ActorSystemContext; 36 import org.thingsboard.server.actors.ActorSystemContext;
34 import org.thingsboard.server.actors.service.ActorService; 37 import org.thingsboard.server.actors.service.ActorService;
@@ -127,7 +130,11 @@ public class RemoteRuleEngineTransportService implements RuleEngineTransportServ @@ -127,7 +130,11 @@ public class RemoteRuleEngineTransportService implements RuleEngineTransportServ
127 130
128 ruleEngineConsumer = ruleEngineConsumerBuilder.build(); 131 ruleEngineConsumer = ruleEngineConsumerBuilder.build();
129 ruleEngineConsumer.subscribe(); 132 ruleEngineConsumer.subscribe();
  133 + }
130 134
  135 + @EventListener(ApplicationReadyEvent.class)
  136 + public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) {
  137 + log.info("Received application ready event. Starting polling for events.");
131 LocalBucketBuilder builder = Bucket4j.builder(); 138 LocalBucketBuilder builder = Bucket4j.builder();
132 builder.addLimit(Bandwidth.simple(pollRecordsPerSecond, Duration.ofSeconds(1))); 139 builder.addLimit(Bandwidth.simple(pollRecordsPerSecond, Duration.ofSeconds(1)));
133 builder.addLimit(Bandwidth.simple(pollRecordsPerMinute, Duration.ofMinutes(1))); 140 builder.addLimit(Bandwidth.simple(pollRecordsPerMinute, Duration.ofMinutes(1)));
@@ -149,6 +156,7 @@ public class RemoteRuleEngineTransportService implements RuleEngineTransportServ @@ -149,6 +156,7 @@ public class RemoteRuleEngineTransportService implements RuleEngineTransportServ
149 records.forEach(record -> { 156 records.forEach(record -> {
150 try { 157 try {
151 ToRuleEngineMsg toRuleEngineMsg = ruleEngineConsumer.decode(record); 158 ToRuleEngineMsg toRuleEngineMsg = ruleEngineConsumer.decode(record);
  159 + log.trace("Forwarding message to rule engine {}", toRuleEngineMsg);
152 if (toRuleEngineMsg.hasToDeviceActorMsg()) { 160 if (toRuleEngineMsg.hasToDeviceActorMsg()) {
153 forwardToDeviceActor(toRuleEngineMsg.getToDeviceActorMsg()); 161 forwardToDeviceActor(toRuleEngineMsg.getToDeviceActorMsg());
154 } 162 }
@@ -175,18 +183,21 @@ public class RemoteRuleEngineTransportService implements RuleEngineTransportServ @@ -175,18 +183,21 @@ public class RemoteRuleEngineTransportService implements RuleEngineTransportServ
175 183
176 @Override 184 @Override
177 public void process(String nodeId, DeviceActorToTransportMsg msg, Runnable onSuccess, Consumer<Throwable> onFailure) { 185 public void process(String nodeId, DeviceActorToTransportMsg msg, Runnable onSuccess, Consumer<Throwable> onFailure) {
178 - notificationsProducer.send(notificationsTopic + "." + nodeId,  
179 - new UUID(msg.getSessionIdMSB(), msg.getSessionIdLSB()).toString(),  
180 - ToTransportMsg.newBuilder().setToDeviceSessionMsg(msg).build()  
181 - , new QueueCallbackAdaptor(onSuccess, onFailure)); 186 + String topic = notificationsTopic + "." + nodeId;
  187 + UUID sessionId = new UUID(msg.getSessionIdMSB(), msg.getSessionIdLSB());
  188 + ToTransportMsg transportMsg = ToTransportMsg.newBuilder().setToDeviceSessionMsg(msg).build();
  189 + log.trace("[{}][{}] Pushing session data to topic: {}", topic, sessionId, transportMsg);
  190 + notificationsProducer.send(topic, sessionId.toString(), transportMsg, new QueueCallbackAdaptor(onSuccess, onFailure));
182 } 191 }
183 192
184 private void forwardToDeviceActor(TransportToDeviceActorMsg toDeviceActorMsg) { 193 private void forwardToDeviceActor(TransportToDeviceActorMsg toDeviceActorMsg) {
185 TransportToDeviceActorMsgWrapper wrapper = new TransportToDeviceActorMsgWrapper(toDeviceActorMsg); 194 TransportToDeviceActorMsgWrapper wrapper = new TransportToDeviceActorMsgWrapper(toDeviceActorMsg);
186 Optional<ServerAddress> address = routingService.resolveById(wrapper.getDeviceId()); 195 Optional<ServerAddress> address = routingService.resolveById(wrapper.getDeviceId());
187 if (address.isPresent()) { 196 if (address.isPresent()) {
  197 + log.trace("[{}] Pushing message to remote server: {}", address.get(), toDeviceActorMsg);
188 rpcService.tell(encodingService.convertToProtoDataMessage(address.get(), wrapper)); 198 rpcService.tell(encodingService.convertToProtoDataMessage(address.get(), wrapper));
189 } else { 199 } else {
  200 + log.trace("Pushing message to local server: {}", toDeviceActorMsg);
190 actorContext.getAppActor().tell(wrapper, ActorRef.noSender()); 201 actorContext.getAppActor().tell(wrapper, ActorRef.noSender());
191 } 202 }
192 } 203 }
@@ -19,6 +19,8 @@ import lombok.extern.slf4j.Slf4j; @@ -19,6 +19,8 @@ import lombok.extern.slf4j.Slf4j;
19 import org.springframework.beans.factory.annotation.Autowired; 19 import org.springframework.beans.factory.annotation.Autowired;
20 import org.springframework.beans.factory.annotation.Value; 20 import org.springframework.beans.factory.annotation.Value;
21 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 21 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
  22 +import org.springframework.boot.context.event.ApplicationReadyEvent;
  23 +import org.springframework.context.event.EventListener;
22 import org.springframework.stereotype.Component; 24 import org.springframework.stereotype.Component;
23 import org.thingsboard.server.gen.transport.TransportProtos.TransportApiRequestMsg; 25 import org.thingsboard.server.gen.transport.TransportProtos.TransportApiRequestMsg;
24 import org.thingsboard.server.gen.transport.TransportProtos.TransportApiResponseMsg; 26 import org.thingsboard.server.gen.transport.TransportProtos.TransportApiResponseMsg;
@@ -93,6 +95,11 @@ public class RemoteTransportApiService { @@ -93,6 +95,11 @@ public class RemoteTransportApiService {
93 builder.executor(transportCallbackExecutor); 95 builder.executor(transportCallbackExecutor);
94 builder.handler(transportApiService); 96 builder.handler(transportApiService);
95 transportApiTemplate = builder.build(); 97 transportApiTemplate = builder.build();
  98 + }
  99 +
  100 + @EventListener(ApplicationReadyEvent.class)
  101 + public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) {
  102 + log.info("Received application ready event. Starting polling for events.");
96 transportApiTemplate.init(); 103 transportApiTemplate.init();
97 } 104 }
98 105
@@ -19,7 +19,7 @@ akka { @@ -19,7 +19,7 @@ akka {
19 # JVM shutdown, System.exit(-1), in case of a fatal error, 19 # JVM shutdown, System.exit(-1), in case of a fatal error,
20 # such as OutOfMemoryError 20 # such as OutOfMemoryError
21 jvm-exit-on-fatal-error = off 21 jvm-exit-on-fatal-error = off
22 - loglevel = "DEBUG" 22 + loglevel = "INFO"
23 loggers = ["akka.event.slf4j.Slf4jLogger"] 23 loggers = ["akka.event.slf4j.Slf4jLogger"]
24 } 24 }
25 25
@@ -31,6 +31,7 @@ server: @@ -31,6 +31,7 @@ server:
31 key-store-type: "${SSL_KEY_STORE_TYPE:PKCS12}" 31 key-store-type: "${SSL_KEY_STORE_TYPE:PKCS12}"
32 # Alias that identifies the key in the key store 32 # Alias that identifies the key in the key store
33 key-alias: "${SSL_KEY_ALIAS:tomcat}" 33 key-alias: "${SSL_KEY_ALIAS:tomcat}"
  34 + log_controller_error_stack_trace: "${HTTP_LOG_CONTROLLER_ERROR_STACK_TRACE:true}"
34 35
35 # Zookeeper connection parameters. Used for service discovery. 36 # Zookeeper connection parameters. Used for service discovery.
36 zk: 37 zk:
@@ -63,7 +64,7 @@ cluster: @@ -63,7 +64,7 @@ cluster:
63 64
64 # Plugins configuration parameters 65 # Plugins configuration parameters
65 plugins: 66 plugins:
66 - # Comma seperated package list used during classpath scanning for plugins 67 + # Comma separated package list used during classpath scanning for plugins
67 scan_packages: "${PLUGINS_SCAN_PACKAGES:org.thingsboard.server.extensions,org.thingsboard.rule.engine}" 68 scan_packages: "${PLUGINS_SCAN_PACKAGES:org.thingsboard.server.extensions,org.thingsboard.rule.engine}"
68 69
69 # Security parameters 70 # Security parameters
@@ -83,6 +84,7 @@ dashboard: @@ -83,6 +84,7 @@ dashboard:
83 max_datapoints_limit: "${DASHBOARD_MAX_DATAPOINTS_LIMIT:50000}" 84 max_datapoints_limit: "${DASHBOARD_MAX_DATAPOINTS_LIMIT:50000}"
84 85
85 database: 86 database:
  87 + ts_max_intervals: "${DATABASE_TS_MAX_INTERVALS:700}" # Max number of DB queries generated by single API call to fetch telemetry records
86 entities: 88 entities:
87 type: "${DATABASE_ENTITIES_TYPE:sql}" # cassandra OR sql 89 type: "${DATABASE_ENTITIES_TYPE:sql}" # cassandra OR sql
88 ts: 90 ts:
@@ -105,7 +107,7 @@ cassandra: @@ -105,7 +107,7 @@ cassandra:
105 metrics: "${CASSANDRA_DISABLE_METRICS:true}" 107 metrics: "${CASSANDRA_DISABLE_METRICS:true}"
106 # NONE SNAPPY LZ4 108 # NONE SNAPPY LZ4
107 compression: "${CASSANDRA_COMPRESSION:none}" 109 compression: "${CASSANDRA_COMPRESSION:none}"
108 - # Specify cassandra claster initialization timeout (if no hosts available during startup) 110 + # Specify cassandra cluster initialization timeout in milliseconds (if no hosts available during startup)
109 init_timeout_ms: "${CASSANDRA_CLUSTER_INIT_TIMEOUT_MS:300000}" 111 init_timeout_ms: "${CASSANDRA_CLUSTER_INIT_TIMEOUT_MS:300000}"
110 # Specify cassandra claster initialization retry interval (if no hosts available during startup) 112 # Specify cassandra claster initialization retry interval (if no hosts available during startup)
111 init_retry_interval_ms: "${CASSANDRA_CLUSTER_INIT_RETRY_INTERVAL_MS:3000}" 113 init_retry_interval_ms: "${CASSANDRA_CLUSTER_INIT_RETRY_INTERVAL_MS:3000}"
@@ -151,6 +153,8 @@ sql: @@ -151,6 +153,8 @@ sql:
151 153
152 # Actor system parameters 154 # Actor system parameters
153 actors: 155 actors:
  156 + cluster:
  157 + grpc_callback_thread_pool_size: "${ACTORS_CLUSTER_GRPC_CALLBACK_THREAD_POOL_SIZE:10}"
154 tenant: 158 tenant:
155 create_components_on_init: "${ACTORS_TENANT_CREATE_COMPONENTS_ON_INIT:true}" 159 create_components_on_init: "${ACTORS_TENANT_CREATE_COMPONENTS_ON_INIT:true}"
156 session: 160 session:
@@ -306,7 +310,7 @@ audit_log: @@ -306,7 +310,7 @@ audit_log:
306 "user": "${AUDIT_LOG_MASK_USER:W}" 310 "user": "${AUDIT_LOG_MASK_USER:W}"
307 "rule_chain": "${AUDIT_LOG_MASK_RULE_CHAIN:W}" 311 "rule_chain": "${AUDIT_LOG_MASK_RULE_CHAIN:W}"
308 "alarm": "${AUDIT_LOG_MASK_ALARM:W}" 312 "alarm": "${AUDIT_LOG_MASK_ALARM:W}"
309 - "entity_view": "${AUDIT_LOG_MASK_RULE_CHAIN:W}" 313 + "entity_view": "${AUDIT_LOG_MASK_ENTITY_VIEW:W}"
310 sink: 314 sink:
311 # Type of external sink. possible options: none, elasticsearch 315 # Type of external sink. possible options: none, elasticsearch
312 type: "${AUDIT_LOG_SINK_TYPE:none}" 316 type: "${AUDIT_LOG_SINK_TYPE:none}"
@@ -320,7 +324,7 @@ audit_log: @@ -320,7 +324,7 @@ audit_log:
320 date_format: "${AUDIT_LOG_SINK_DATE_FORMAT:YYYY.MM.DD}" 324 date_format: "${AUDIT_LOG_SINK_DATE_FORMAT:YYYY.MM.DD}"
321 scheme_name: "${AUDIT_LOG_SINK_SCHEME_NAME:http}" # http or https 325 scheme_name: "${AUDIT_LOG_SINK_SCHEME_NAME:http}" # http or https
322 host: "${AUDIT_LOG_SINK_HOST:localhost}" 326 host: "${AUDIT_LOG_SINK_HOST:localhost}"
323 - port: "${AUDIT_LOG_SINK_POST:9200}" 327 + port: "${AUDIT_LOG_SINK_PORT:9200}"
324 user_name: "${AUDIT_LOG_SINK_USER_NAME:}" 328 user_name: "${AUDIT_LOG_SINK_USER_NAME:}"
325 password: "${AUDIT_LOG_SINK_PASSWORD:}" 329 password: "${AUDIT_LOG_SINK_PASSWORD:}"
326 330
@@ -340,7 +344,7 @@ kafka: @@ -340,7 +344,7 @@ kafka:
340 requests_topic: "${TB_TRANSPORT_API_REQUEST_TOPIC:tb.transport.api.requests}" 344 requests_topic: "${TB_TRANSPORT_API_REQUEST_TOPIC:tb.transport.api.requests}"
341 responses_topic: "${TB_TRANSPORT_API_RESPONSE_TOPIC:tb.transport.api.responses}" 345 responses_topic: "${TB_TRANSPORT_API_RESPONSE_TOPIC:tb.transport.api.responses}"
342 max_pending_requests: "${TB_TRANSPORT_MAX_PENDING_REQUESTS:10000}" 346 max_pending_requests: "${TB_TRANSPORT_MAX_PENDING_REQUESTS:10000}"
343 - request_timeout: "${TB_TRANSPORT_MAX_REQUEST_TIMEOUT:10000}" 347 + max_requests_timeout: "${TB_TRANSPORT_MAX_REQUEST_TIMEOUT:10000}"
344 request_poll_interval: "${TB_TRANSPORT_REQUEST_POLL_INTERVAL_MS:25}" 348 request_poll_interval: "${TB_TRANSPORT_REQUEST_POLL_INTERVAL_MS:25}"
345 request_auto_commit_interval: "${TB_TRANSPORT_REQUEST_AUTO_COMMIT_INTERVAL_MS:100}" 349 request_auto_commit_interval: "${TB_TRANSPORT_REQUEST_AUTO_COMMIT_INTERVAL_MS:100}"
346 rule_engine: 350 rule_engine:
@@ -367,7 +371,7 @@ js: @@ -367,7 +371,7 @@ js:
367 # JS Eval request topic 371 # JS Eval request topic
368 request_topic: "${REMOTE_JS_EVAL_REQUEST_TOPIC:js.eval.requests}" 372 request_topic: "${REMOTE_JS_EVAL_REQUEST_TOPIC:js.eval.requests}"
369 # JS Eval responses topic prefix that is combined with node id 373 # JS Eval responses topic prefix that is combined with node id
370 - response_topic_prefix: "${REMOTE_JS_EVAL_REQUEST_TOPIC:js.eval.responses}" 374 + response_topic_prefix: "${REMOTE_JS_EVAL_RESPONSE_TOPIC:js.eval.responses}"
371 # JS Eval max pending requests 375 # JS Eval max pending requests
372 max_pending_requests: "${REMOTE_JS_MAX_PENDING_REQUESTS:10000}" 376 max_pending_requests: "${REMOTE_JS_MAX_PENDING_REQUESTS:10000}"
373 # JS Eval max request timeout 377 # JS Eval max request timeout
@@ -405,6 +409,9 @@ transport: @@ -405,6 +409,9 @@ transport:
405 enabled: "${TB_TRANSPORT_RATE_LIMITS_ENABLED:false}" 409 enabled: "${TB_TRANSPORT_RATE_LIMITS_ENABLED:false}"
406 tenant: "${TB_TRANSPORT_RATE_LIMITS_TENANT:1000:1,20000:60}" 410 tenant: "${TB_TRANSPORT_RATE_LIMITS_TENANT:1000:1,20000:60}"
407 device: "${TB_TRANSPORT_RATE_LIMITS_DEVICE:10:1,300:60}" 411 device: "${TB_TRANSPORT_RATE_LIMITS_DEVICE:10:1,300:60}"
  412 + json:
  413 + # Cast String data types to Numeric if possible when processing Telemetry/Attributes JSON
  414 + type_cast_enabled: "${JSON_TYPE_CAST_ENABLED:true}"
408 # Local HTTP transport parameters 415 # Local HTTP transport parameters
409 http: 416 http:
410 enabled: "${HTTP_ENABLED:true}" 417 enabled: "${HTTP_ENABLED:true}"
@@ -9,7 +9,7 @@ @@ -9,7 +9,7 @@
9 9
10 <logger name="org.thingsboard.server" level="WARN"/> 10 <logger name="org.thingsboard.server" level="WARN"/>
11 <logger name="org.springframework" level="WARN"/> 11 <logger name="org.springframework" level="WARN"/>
12 - <logger name="org.springframework.boot.test" level="DEBUG"/> 12 + <logger name="org.springframework.boot.test" level="WARN"/>
13 <logger name="org.apache.cassandra" level="WARN"/> 13 <logger name="org.apache.cassandra" level="WARN"/>
14 <logger name="org.cassandraunit" level="INFO"/> 14 <logger name="org.cassandraunit" level="INFO"/>
15 15
@@ -24,6 +24,7 @@ public enum ActionType { @@ -24,6 +24,7 @@ public enum ActionType {
24 UPDATED(false), // log entity 24 UPDATED(false), // log entity
25 ATTRIBUTES_UPDATED(false), // log attributes/values 25 ATTRIBUTES_UPDATED(false), // log attributes/values
26 ATTRIBUTES_DELETED(false), // log attributes 26 ATTRIBUTES_DELETED(false), // log attributes
  27 + TIMESERIES_DELETED(false), // log timeseries
27 RPC_CALL(false), // log method and params 28 RPC_CALL(false), // log method and params
28 CREDENTIALS_UPDATED(false), // log new credentials 29 CREDENTIALS_UPDATED(false), // log new credentials
29 ASSIGNED_TO_CUSTOMER(false), // log customer name 30 ASSIGNED_TO_CUSTOMER(false), // log customer name
@@ -32,11 +33,11 @@ public enum ActionType { @@ -32,11 +33,11 @@ public enum ActionType {
32 SUSPENDED(false), // log string id 33 SUSPENDED(false), // log string id
33 CREDENTIALS_READ(true), // log device id 34 CREDENTIALS_READ(true), // log device id
34 ATTRIBUTES_READ(true), // log attributes 35 ATTRIBUTES_READ(true), // log attributes
35 - RELATION_ADD_OR_UPDATE (false),  
36 - RELATION_DELETED (false),  
37 - RELATIONS_DELETED (false),  
38 - ALARM_ACK (false),  
39 - ALARM_CLEAR (false); 36 + RELATION_ADD_OR_UPDATE(false),
  37 + RELATION_DELETED(false),
  38 + RELATIONS_DELETED(false),
  39 + ALARM_ACK(false),
  40 + ALARM_CLEAR(false);
40 41
41 private final boolean isRead; 42 private final boolean isRead;
42 43
@@ -18,6 +18,7 @@ package org.thingsboard.server.common.data.objects; @@ -18,6 +18,7 @@ package org.thingsboard.server.common.data.objects;
18 import lombok.Data; 18 import lombok.Data;
19 import lombok.NoArgsConstructor; 19 import lombok.NoArgsConstructor;
20 20
  21 +import java.io.Serializable;
21 import java.util.ArrayList; 22 import java.util.ArrayList;
22 import java.util.List; 23 import java.util.List;
23 24
@@ -26,7 +27,7 @@ import java.util.List; @@ -26,7 +27,7 @@ import java.util.List;
26 */ 27 */
27 @Data 28 @Data
28 @NoArgsConstructor 29 @NoArgsConstructor
29 -public class AttributesEntityView { 30 +public class AttributesEntityView implements Serializable {
30 31
31 private List<String> cs = new ArrayList<>(); 32 private List<String> cs = new ArrayList<>();
32 private List<String> ss = new ArrayList<>(); 33 private List<String> ss = new ArrayList<>();
@@ -18,6 +18,7 @@ package org.thingsboard.server.common.data.objects; @@ -18,6 +18,7 @@ package org.thingsboard.server.common.data.objects;
18 import lombok.Data; 18 import lombok.Data;
19 import lombok.NoArgsConstructor; 19 import lombok.NoArgsConstructor;
20 20
  21 +import java.io.Serializable;
21 import java.util.ArrayList; 22 import java.util.ArrayList;
22 import java.util.List; 23 import java.util.List;
23 24
@@ -26,7 +27,7 @@ import java.util.List; @@ -26,7 +27,7 @@ import java.util.List;
26 */ 27 */
27 @Data 28 @Data
28 @NoArgsConstructor 29 @NoArgsConstructor
29 -public class TelemetryEntityView { 30 +public class TelemetryEntityView implements Serializable {
30 31
31 private List<String> timeseries; 32 private List<String> timeseries;
32 private AttributesEntityView attributes; 33 private AttributesEntityView attributes;
@@ -28,6 +28,8 @@ public enum MsgType { @@ -28,6 +28,8 @@ public enum MsgType {
28 */ 28 */
29 CLUSTER_EVENT_MSG, 29 CLUSTER_EVENT_MSG,
30 30
  31 + APP_INIT_MSG,
  32 +
31 /** 33 /**
32 * All messages, could be send to cluster 34 * All messages, could be send to cluster
33 */ 35 */
@@ -77,9 +77,10 @@ public class TBKafkaProducerTemplate<T> { @@ -77,9 +77,10 @@ public class TBKafkaProducerTemplate<T> {
77 result.all().get(); 77 result.all().get();
78 } catch (Exception e) { 78 } catch (Exception e) {
79 if ((e instanceof TopicExistsException) || (e.getCause() != null && e.getCause() instanceof TopicExistsException)) { 79 if ((e instanceof TopicExistsException) || (e.getCause() != null && e.getCause() instanceof TopicExistsException)) {
80 - log.trace("[{}] Topic already exists: ", defaultTopic); 80 + log.trace("[{}] Topic already exists.", defaultTopic);
81 } else { 81 } else {
82 - log.trace("[{}] Failed to create topic: {}", defaultTopic, e.getMessage(), e); 82 + log.info("[{}] Failed to create topic: {}", defaultTopic, e.getMessage(), e);
  83 + throw new RuntimeException(e);
83 } 84 }
84 } 85 }
85 //Maybe this should not be cached, but we don't plan to change size of partitions 86 //Maybe this should not be cached, but we don't plan to change size of partitions
@@ -23,6 +23,9 @@ import lombok.extern.slf4j.Slf4j; @@ -23,6 +23,9 @@ import lombok.extern.slf4j.Slf4j;
23 import org.apache.kafka.clients.admin.CreateTopicsResult; 23 import org.apache.kafka.clients.admin.CreateTopicsResult;
24 import org.apache.kafka.clients.admin.NewTopic; 24 import org.apache.kafka.clients.admin.NewTopic;
25 import org.apache.kafka.clients.consumer.ConsumerRecords; 25 import org.apache.kafka.clients.consumer.ConsumerRecords;
  26 +import org.apache.kafka.clients.producer.Callback;
  27 +import org.apache.kafka.clients.producer.RecordMetadata;
  28 +import org.apache.kafka.common.errors.TopicExistsException;
26 import org.apache.kafka.common.header.Header; 29 import org.apache.kafka.common.header.Header;
27 import org.apache.kafka.common.header.internals.RecordHeader; 30 import org.apache.kafka.common.header.internals.RecordHeader;
28 31
@@ -83,7 +86,13 @@ public class TbKafkaRequestTemplate<Request, Response> extends AbstractTbKafkaTe @@ -83,7 +86,13 @@ public class TbKafkaRequestTemplate<Request, Response> extends AbstractTbKafkaTe
83 CreateTopicsResult result = admin.createTopic(new NewTopic(responseTemplate.getTopic(), 1, (short) 1)); 86 CreateTopicsResult result = admin.createTopic(new NewTopic(responseTemplate.getTopic(), 1, (short) 1));
84 result.all().get(); 87 result.all().get();
85 } catch (Exception e) { 88 } catch (Exception e) {
86 - log.trace("Failed to create topic: {}", e.getMessage(), e); 89 + if ((e instanceof TopicExistsException) || (e.getCause() != null && e.getCause() instanceof TopicExistsException)) {
  90 + log.trace("[{}] Topic already exists. ", responseTemplate.getTopic());
  91 + } else {
  92 + log.info("[{}] Failed to create topic: {}", responseTemplate.getTopic(), e.getMessage(), e);
  93 + throw new RuntimeException(e);
  94 + }
  95 +
87 } 96 }
88 this.requestTemplate.init(); 97 this.requestTemplate.init();
89 tickTs = System.currentTimeMillis(); 98 tickTs = System.currentTimeMillis();
@@ -92,7 +101,11 @@ public class TbKafkaRequestTemplate<Request, Response> extends AbstractTbKafkaTe @@ -92,7 +101,11 @@ public class TbKafkaRequestTemplate<Request, Response> extends AbstractTbKafkaTe
92 long nextCleanupMs = 0L; 101 long nextCleanupMs = 0L;
93 while (!stopped) { 102 while (!stopped) {
94 ConsumerRecords<String, byte[]> responses = responseTemplate.poll(Duration.ofMillis(pollInterval)); 103 ConsumerRecords<String, byte[]> responses = responseTemplate.poll(Duration.ofMillis(pollInterval));
  104 + if (responses.count() > 0) {
  105 + log.trace("Polling responses completed, consumer records count [{}]", responses.count());
  106 + }
95 responses.forEach(response -> { 107 responses.forEach(response -> {
  108 + log.trace("Received response to Kafka Template request: {}", response);
96 Header requestIdHeader = response.headers().lastHeader(TbKafkaSettings.REQUEST_ID_HEADER); 109 Header requestIdHeader = response.headers().lastHeader(TbKafkaSettings.REQUEST_ID_HEADER);
97 Response decodedResponse = null; 110 Response decodedResponse = null;
98 UUID requestId = null; 111 UUID requestId = null;
@@ -109,6 +122,7 @@ public class TbKafkaRequestTemplate<Request, Response> extends AbstractTbKafkaTe @@ -109,6 +122,7 @@ public class TbKafkaRequestTemplate<Request, Response> extends AbstractTbKafkaTe
109 if (requestId == null) { 122 if (requestId == null) {
110 log.error("[{}] Missing requestId in header and body", response); 123 log.error("[{}] Missing requestId in header and body", response);
111 } else { 124 } else {
  125 + log.trace("[{}] Response received", requestId);
112 ResponseMetaData<Response> expectedResponse = pendingRequests.remove(requestId); 126 ResponseMetaData<Response> expectedResponse = pendingRequests.remove(requestId);
113 if (expectedResponse == null) { 127 if (expectedResponse == null) {
114 log.trace("[{}] Invalid or stale request", requestId); 128 log.trace("[{}] Invalid or stale request", requestId);
@@ -132,6 +146,7 @@ public class TbKafkaRequestTemplate<Request, Response> extends AbstractTbKafkaTe @@ -132,6 +146,7 @@ public class TbKafkaRequestTemplate<Request, Response> extends AbstractTbKafkaTe
132 if (kv.getValue().expTime < tickTs) { 146 if (kv.getValue().expTime < tickTs) {
133 ResponseMetaData<Response> staleRequest = pendingRequests.remove(kv.getKey()); 147 ResponseMetaData<Response> staleRequest = pendingRequests.remove(kv.getKey());
134 if (staleRequest != null) { 148 if (staleRequest != null) {
  149 + log.trace("[{}] Request timeout detected, expTime [{}], tickTs [{}]", kv.getKey(), staleRequest.expTime, tickTs);
135 staleRequest.future.setException(new TimeoutException()); 150 staleRequest.future.setException(new TimeoutException());
136 } 151 }
137 } 152 }
@@ -158,9 +173,17 @@ public class TbKafkaRequestTemplate<Request, Response> extends AbstractTbKafkaTe @@ -158,9 +173,17 @@ public class TbKafkaRequestTemplate<Request, Response> extends AbstractTbKafkaTe
158 headers.add(new RecordHeader(TbKafkaSettings.REQUEST_ID_HEADER, uuidToBytes(requestId))); 173 headers.add(new RecordHeader(TbKafkaSettings.REQUEST_ID_HEADER, uuidToBytes(requestId)));
159 headers.add(new RecordHeader(TbKafkaSettings.RESPONSE_TOPIC_HEADER, stringToBytes(responseTemplate.getTopic()))); 174 headers.add(new RecordHeader(TbKafkaSettings.RESPONSE_TOPIC_HEADER, stringToBytes(responseTemplate.getTopic())));
160 SettableFuture<Response> future = SettableFuture.create(); 175 SettableFuture<Response> future = SettableFuture.create();
161 - pendingRequests.putIfAbsent(requestId, new ResponseMetaData<>(tickTs + maxRequestTimeout, future)); 176 + ResponseMetaData<Response> responseMetaData = new ResponseMetaData<>(tickTs + maxRequestTimeout, future);
  177 + pendingRequests.putIfAbsent(requestId, responseMetaData);
162 request = requestTemplate.enrich(request, responseTemplate.getTopic(), requestId); 178 request = requestTemplate.enrich(request, responseTemplate.getTopic(), requestId);
163 - requestTemplate.send(key, request, headers, null); 179 + log.trace("[{}] Sending request, key [{}], expTime [{}]", requestId, key, responseMetaData.expTime);
  180 + requestTemplate.send(key, request, headers, (metadata, exception) -> {
  181 + if (exception != null) {
  182 + log.trace("[{}] Failed to post the request", requestId, exception);
  183 + } else {
  184 + log.trace("[{}] Posted the request", requestId, metadata);
  185 + }
  186 + });
164 return future; 187 return future;
165 } 188 }
166 189
@@ -18,6 +18,7 @@ package org.thingsboard.server.kafka; @@ -18,6 +18,7 @@ package org.thingsboard.server.kafka;
18 import lombok.Builder; 18 import lombok.Builder;
19 import lombok.extern.slf4j.Slf4j; 19 import lombok.extern.slf4j.Slf4j;
20 import org.apache.kafka.clients.consumer.ConsumerRecords; 20 import org.apache.kafka.clients.consumer.ConsumerRecords;
  21 +import org.apache.kafka.common.errors.InterruptException;
21 import org.apache.kafka.common.header.Header; 22 import org.apache.kafka.common.header.Header;
22 import org.apache.kafka.common.header.internals.RecordHeader; 23 import org.apache.kafka.common.header.internals.RecordHeader;
23 24
@@ -127,6 +128,10 @@ public class TbKafkaResponseTemplate<Request, Response> extends AbstractTbKafkaT @@ -127,6 +128,10 @@ public class TbKafkaResponseTemplate<Request, Response> extends AbstractTbKafkaT
127 log.warn("[{}] Failed to process the request: {}", requestId, request, e); 128 log.warn("[{}] Failed to process the request: {}", requestId, request, e);
128 } 129 }
129 }); 130 });
  131 + } catch (InterruptException ie) {
  132 + if (!stopped) {
  133 + log.warn("Fetching data from kafka was interrupted.", ie);
  134 + }
130 } catch (Throwable e) { 135 } catch (Throwable e) {
131 log.warn("Failed to obtain messages from queue.", e); 136 log.warn("Failed to obtain messages from queue.", e);
132 try { 137 try {
@@ -141,7 +141,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement @@ -141,7 +141,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
141 processUnsubscribe(ctx, (MqttUnsubscribeMessage) msg); 141 processUnsubscribe(ctx, (MqttUnsubscribeMessage) msg);
142 break; 142 break;
143 case PINGREQ: 143 case PINGREQ:
144 - if (checkConnected(ctx)) { 144 + if (checkConnected(ctx, msg)) {
145 ctx.writeAndFlush(new MqttMessage(new MqttFixedHeader(PINGRESP, false, AT_MOST_ONCE, false, 0))); 145 ctx.writeAndFlush(new MqttMessage(new MqttFixedHeader(PINGRESP, false, AT_MOST_ONCE, false, 0)));
146 transportService.reportActivity(sessionInfo); 146 transportService.reportActivity(sessionInfo);
147 if (gatewaySessionHandler != null) { 147 if (gatewaySessionHandler != null) {
@@ -150,7 +150,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement @@ -150,7 +150,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
150 } 150 }
151 break; 151 break;
152 case DISCONNECT: 152 case DISCONNECT:
153 - if (checkConnected(ctx)) { 153 + if (checkConnected(ctx, msg)) {
154 processDisconnect(ctx); 154 processDisconnect(ctx);
155 } 155 }
156 break; 156 break;
@@ -161,12 +161,12 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement @@ -161,12 +161,12 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
161 } 161 }
162 162
163 private void processPublish(ChannelHandlerContext ctx, MqttPublishMessage mqttMsg) { 163 private void processPublish(ChannelHandlerContext ctx, MqttPublishMessage mqttMsg) {
164 - if (!checkConnected(ctx)) { 164 + if (!checkConnected(ctx, mqttMsg)) {
165 return; 165 return;
166 } 166 }
167 String topicName = mqttMsg.variableHeader().topicName(); 167 String topicName = mqttMsg.variableHeader().topicName();
168 int msgId = mqttMsg.variableHeader().packetId(); 168 int msgId = mqttMsg.variableHeader().packetId();
169 - log.trace("[{}] Processing publish msg [{}][{}]!", sessionId, topicName, msgId); 169 + log.trace("[{}][{}] Processing publish msg [{}][{}]!", sessionId, deviceSessionCtx.getDeviceId(), topicName, msgId);
170 170
171 if (topicName.startsWith(MqttTopics.BASE_GATEWAY_API_TOPIC)) { 171 if (topicName.startsWith(MqttTopics.BASE_GATEWAY_API_TOPIC)) {
172 if (gatewaySessionHandler != null) { 172 if (gatewaySessionHandler != null) {
@@ -248,7 +248,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement @@ -248,7 +248,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
248 } 248 }
249 249
250 private void processSubscribe(ChannelHandlerContext ctx, MqttSubscribeMessage mqttMsg) { 250 private void processSubscribe(ChannelHandlerContext ctx, MqttSubscribeMessage mqttMsg) {
251 - if (!checkConnected(ctx)) { 251 + if (!checkConnected(ctx, mqttMsg)) {
252 return; 252 return;
253 } 253 }
254 log.trace("[{}] Processing subscription [{}]!", sessionId, mqttMsg.variableHeader().messageId()); 254 log.trace("[{}] Processing subscription [{}]!", sessionId, mqttMsg.variableHeader().messageId());
@@ -293,7 +293,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement @@ -293,7 +293,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
293 } 293 }
294 294
295 private void processUnsubscribe(ChannelHandlerContext ctx, MqttUnsubscribeMessage mqttMsg) { 295 private void processUnsubscribe(ChannelHandlerContext ctx, MqttUnsubscribeMessage mqttMsg) {
296 - if (!checkConnected(ctx)) { 296 + if (!checkConnected(ctx, mqttMsg)) {
297 return; 297 return;
298 } 298 }
299 log.trace("[{}] Processing subscription [{}]!", sessionId, mqttMsg.variableHeader().messageId()); 299 log.trace("[{}] Processing subscription [{}]!", sessionId, mqttMsg.variableHeader().messageId());
@@ -336,6 +336,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement @@ -336,6 +336,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
336 336
337 private void processAuthTokenConnect(ChannelHandlerContext ctx, MqttConnectMessage msg) { 337 private void processAuthTokenConnect(ChannelHandlerContext ctx, MqttConnectMessage msg) {
338 String userName = msg.payload().userName(); 338 String userName = msg.payload().userName();
  339 + log.info("[{}] Processing connect msg for client with user name: {}!", sessionId, userName);
339 if (StringUtils.isEmpty(userName)) { 340 if (StringUtils.isEmpty(userName)) {
340 ctx.writeAndFlush(createMqttConnAckMsg(CONNECTION_REFUSED_BAD_USER_NAME_OR_PASSWORD)); 341 ctx.writeAndFlush(createMqttConnAckMsg(CONNECTION_REFUSED_BAD_USER_NAME_OR_PASSWORD));
341 ctx.close(); 342 ctx.close();
@@ -444,11 +445,11 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement @@ -444,11 +445,11 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
444 return new MqttPubAckMessage(mqttFixedHeader, mqttMsgIdVariableHeader); 445 return new MqttPubAckMessage(mqttFixedHeader, mqttMsgIdVariableHeader);
445 } 446 }
446 447
447 - private boolean checkConnected(ChannelHandlerContext ctx) { 448 + private boolean checkConnected(ChannelHandlerContext ctx, MqttMessage msg) {
448 if (deviceSessionCtx.isConnected()) { 449 if (deviceSessionCtx.isConnected()) {
449 return true; 450 return true;
450 } else { 451 } else {
451 - log.info("[{}] Closing current session due to invalid msg order [{}][{}]", sessionId); 452 + log.info("[{}] Closing current session due to invalid msg order: {}", sessionId, msg);
452 ctx.close(); 453 ctx.close();
453 return false; 454 return false;
454 } 455 }
@@ -496,6 +497,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement @@ -496,6 +497,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
496 transportService.registerAsyncSession(sessionInfo, this); 497 transportService.registerAsyncSession(sessionInfo, this);
497 checkGatewaySession(); 498 checkGatewaySession();
498 ctx.writeAndFlush(createMqttConnAckMsg(CONNECTION_ACCEPTED)); 499 ctx.writeAndFlush(createMqttConnAckMsg(CONNECTION_ACCEPTED));
  500 + log.info("[{}] Client connected!", sessionId);
499 } 501 }
500 } 502 }
501 503
@@ -119,10 +119,10 @@ public class GatewaySessionHandler { @@ -119,10 +119,10 @@ public class GatewaySessionHandler {
119 GatewayDeviceSessionCtx deviceSessionCtx = new GatewayDeviceSessionCtx(GatewaySessionHandler.this, msg.getDeviceInfo(), mqttQoSMap); 119 GatewayDeviceSessionCtx deviceSessionCtx = new GatewayDeviceSessionCtx(GatewaySessionHandler.this, msg.getDeviceInfo(), mqttQoSMap);
120 if (devices.putIfAbsent(deviceName, deviceSessionCtx) == null) { 120 if (devices.putIfAbsent(deviceName, deviceSessionCtx) == null) {
121 SessionInfoProto deviceSessionInfo = deviceSessionCtx.getSessionInfo(); 121 SessionInfoProto deviceSessionInfo = deviceSessionCtx.getSessionInfo();
  122 + transportService.registerAsyncSession(deviceSessionInfo, deviceSessionCtx);
122 transportService.process(deviceSessionInfo, AbstractTransportService.getSessionEventMsg(TransportProtos.SessionEvent.OPEN), null); 123 transportService.process(deviceSessionInfo, AbstractTransportService.getSessionEventMsg(TransportProtos.SessionEvent.OPEN), null);
123 transportService.process(deviceSessionInfo, TransportProtos.SubscribeToRPCMsg.getDefaultInstance(), null); 124 transportService.process(deviceSessionInfo, TransportProtos.SubscribeToRPCMsg.getDefaultInstance(), null);
124 transportService.process(deviceSessionInfo, TransportProtos.SubscribeToAttributeUpdatesMsg.getDefaultInstance(), null); 125 transportService.process(deviceSessionInfo, TransportProtos.SubscribeToAttributeUpdatesMsg.getDefaultInstance(), null);
125 - transportService.registerAsyncSession(deviceSessionInfo, deviceSessionCtx);  
126 } 126 }
127 future.set(devices.get(deviceName)); 127 future.set(devices.get(deviceName));
128 } 128 }
@@ -43,7 +43,7 @@ public interface TransportService { @@ -43,7 +43,7 @@ public interface TransportService {
43 void process(TransportProtos.GetOrCreateDeviceFromGatewayRequestMsg msg, 43 void process(TransportProtos.GetOrCreateDeviceFromGatewayRequestMsg msg,
44 TransportServiceCallback<TransportProtos.GetOrCreateDeviceFromGatewayResponseMsg> callback); 44 TransportServiceCallback<TransportProtos.GetOrCreateDeviceFromGatewayResponseMsg> callback);
45 45
46 - boolean checkLimits(SessionInfoProto sessionInfo, TransportServiceCallback<Void> callback); 46 + boolean checkLimits(SessionInfoProto sessionInfo, Object msg, TransportServiceCallback<Void> callback);
47 47
48 void process(SessionInfoProto sessionInfo, SessionEventMsg msg, TransportServiceCallback<Void> callback); 48 void process(SessionInfoProto sessionInfo, SessionEventMsg msg, TransportServiceCallback<Void> callback);
49 49
@@ -22,6 +22,7 @@ import com.google.gson.JsonObject; @@ -22,6 +22,7 @@ import com.google.gson.JsonObject;
22 import com.google.gson.JsonParser; 22 import com.google.gson.JsonParser;
23 import com.google.gson.JsonPrimitive; 23 import com.google.gson.JsonPrimitive;
24 import com.google.gson.JsonSyntaxException; 24 import com.google.gson.JsonSyntaxException;
  25 +import org.apache.commons.lang3.math.NumberUtils;
25 import org.springframework.util.StringUtils; 26 import org.springframework.util.StringUtils;
26 import org.thingsboard.server.common.data.kv.AttributeKey; 27 import org.thingsboard.server.common.data.kv.AttributeKey;
27 import org.thingsboard.server.common.data.kv.AttributeKvEntry; 28 import org.thingsboard.server.common.data.kv.AttributeKvEntry;
@@ -58,6 +59,8 @@ public class JsonConverter { @@ -58,6 +59,8 @@ public class JsonConverter {
58 private static final String CAN_T_PARSE_VALUE = "Can't parse value: "; 59 private static final String CAN_T_PARSE_VALUE = "Can't parse value: ";
59 private static final String DEVICE_PROPERTY = "device"; 60 private static final String DEVICE_PROPERTY = "device";
60 61
  62 + private static boolean isTypeCastEnabled = true;
  63 +
61 public static PostTelemetryMsg convertToTelemetryProto(JsonElement jsonObject) throws JsonSyntaxException { 64 public static PostTelemetryMsg convertToTelemetryProto(JsonElement jsonObject) throws JsonSyntaxException {
62 long systemTs = System.currentTimeMillis(); 65 long systemTs = System.currentTimeMillis();
63 PostTelemetryMsg.Builder builder = PostTelemetryMsg.newBuilder(); 66 PostTelemetryMsg.Builder builder = PostTelemetryMsg.newBuilder();
@@ -128,24 +131,22 @@ public class JsonConverter { @@ -128,24 +131,22 @@ public class JsonConverter {
128 if (element.isJsonPrimitive()) { 131 if (element.isJsonPrimitive()) {
129 JsonPrimitive value = element.getAsJsonPrimitive(); 132 JsonPrimitive value = element.getAsJsonPrimitive();
130 if (value.isString()) { 133 if (value.isString()) {
131 - result.add(KeyValueProto.newBuilder().setKey(valueEntry.getKey()).setType(KeyValueType.STRING_V)  
132 - .setStringV(value.getAsString()).build()); 134 + if(isTypeCastEnabled && NumberUtils.isParsable(value.getAsString())) {
  135 + try {
  136 + result.add(buildNumericKeyValueProto(value, valueEntry.getKey()));
  137 + } catch (RuntimeException th) {
  138 + result.add(KeyValueProto.newBuilder().setKey(valueEntry.getKey()).setType(KeyValueType.STRING_V)
  139 + .setStringV(value.getAsString()).build());
  140 + }
  141 + } else {
  142 + result.add(KeyValueProto.newBuilder().setKey(valueEntry.getKey()).setType(KeyValueType.STRING_V)
  143 + .setStringV(value.getAsString()).build());
  144 + }
133 } else if (value.isBoolean()) { 145 } else if (value.isBoolean()) {
134 result.add(KeyValueProto.newBuilder().setKey(valueEntry.getKey()).setType(KeyValueType.BOOLEAN_V) 146 result.add(KeyValueProto.newBuilder().setKey(valueEntry.getKey()).setType(KeyValueType.BOOLEAN_V)
135 .setBoolV(value.getAsBoolean()).build()); 147 .setBoolV(value.getAsBoolean()).build());
136 } else if (value.isNumber()) { 148 } else if (value.isNumber()) {
137 - if (value.getAsString().contains(".")) {  
138 - result.add(KeyValueProto.newBuilder().setKey(valueEntry.getKey()).setType(KeyValueType.DOUBLE_V)  
139 - .setDoubleV(value.getAsDouble()).build());  
140 - } else {  
141 - try {  
142 - long longValue = Long.parseLong(value.getAsString());  
143 - result.add(KeyValueProto.newBuilder().setKey(valueEntry.getKey()).setType(KeyValueType.LONG_V)  
144 - .setLongV(longValue).build());  
145 - } catch (NumberFormatException e) {  
146 - throw new JsonSyntaxException("Big integer values are not supported!");  
147 - }  
148 - } 149 + result.add(buildNumericKeyValueProto(value, valueEntry.getKey()));
149 } else { 150 } else {
150 throw new JsonSyntaxException(CAN_T_PARSE_VALUE + value); 151 throw new JsonSyntaxException(CAN_T_PARSE_VALUE + value);
151 } 152 }
@@ -156,6 +157,24 @@ public class JsonConverter { @@ -156,6 +157,24 @@ public class JsonConverter {
156 return result; 157 return result;
157 } 158 }
158 159
  160 + private static KeyValueProto buildNumericKeyValueProto(JsonPrimitive value, String key) {
  161 + if (value.getAsString().contains(".")) {
  162 + return KeyValueProto.newBuilder()
  163 + .setKey(key)
  164 + .setType(KeyValueType.DOUBLE_V)
  165 + .setDoubleV(value.getAsDouble())
  166 + .build();
  167 + } else {
  168 + try {
  169 + long longValue = Long.parseLong(value.getAsString());
  170 + return KeyValueProto.newBuilder().setKey(key).setType(KeyValueType.LONG_V)
  171 + .setLongV(longValue).build();
  172 + } catch (NumberFormatException e) {
  173 + throw new JsonSyntaxException("Big integer values are not supported!");
  174 + }
  175 + }
  176 + }
  177 +
159 public static TransportProtos.ToServerRpcRequestMsg convertToServerRpcRequest(JsonElement json, int requestId) throws JsonSyntaxException { 178 public static TransportProtos.ToServerRpcRequestMsg convertToServerRpcRequest(JsonElement json, int requestId) throws JsonSyntaxException {
160 JsonObject object = json.getAsJsonObject(); 179 JsonObject object = json.getAsJsonObject();
161 return TransportProtos.ToServerRpcRequestMsg.newBuilder().setRequestId(requestId).setMethodName(object.get("method").getAsString()).setParams(GSON.toJson(object.get("params"))).build(); 180 return TransportProtos.ToServerRpcRequestMsg.newBuilder().setRequestId(requestId).setMethodName(object.get("method").getAsString()).setParams(GSON.toJson(object.get("params"))).build();
@@ -370,7 +389,15 @@ public class JsonConverter { @@ -370,7 +389,15 @@ public class JsonConverter {
370 if (element.isJsonPrimitive()) { 389 if (element.isJsonPrimitive()) {
371 JsonPrimitive value = element.getAsJsonPrimitive(); 390 JsonPrimitive value = element.getAsJsonPrimitive();
372 if (value.isString()) { 391 if (value.isString()) {
373 - result.add(new StringDataEntry(valueEntry.getKey(), value.getAsString())); 392 + if(isTypeCastEnabled && NumberUtils.isParsable(value.getAsString())) {
  393 + try {
  394 + parseNumericValue(result, valueEntry, value);
  395 + } catch (RuntimeException th) {
  396 + result.add(new StringDataEntry(valueEntry.getKey(), value.getAsString()));
  397 + }
  398 + } else {
  399 + result.add(new StringDataEntry(valueEntry.getKey(), value.getAsString()));
  400 + }
374 } else if (value.isBoolean()) { 401 } else if (value.isBoolean()) {
375 result.add(new BooleanDataEntry(valueEntry.getKey(), value.getAsBoolean())); 402 result.add(new BooleanDataEntry(valueEntry.getKey(), value.getAsBoolean()));
376 } else if (value.isNumber()) { 403 } else if (value.isNumber()) {
@@ -426,5 +453,7 @@ public class JsonConverter { @@ -426,5 +453,7 @@ public class JsonConverter {
426 } 453 }
427 } 454 }
428 455
429 - 456 + public static void setTypeCastEnabled(boolean enabled) {
  457 + isTypeCastEnabled = enabled;
  458 + }
430 } 459 }
  1 +/**
  2 + * Copyright © 2016-2018 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.transport.adaptor;
  17 +
  18 +import lombok.extern.slf4j.Slf4j;
  19 +import org.springframework.beans.factory.annotation.Value;
  20 +import org.springframework.context.annotation.Configuration;
  21 +
  22 +@Configuration
  23 +@Slf4j
  24 +public class JsonConverterConfig {
  25 +
  26 + @Value("${transport.json.type_cast_enabled:true}")
  27 + public void setIsJsonTypeCastEnabled(boolean jsonTypeCastEnabled) {
  28 + JsonConverter.setTypeCastEnabled(jsonTypeCastEnabled);
  29 + log.info("JSON type cast enabled = {}", jsonTypeCastEnabled);
  30 + }
  31 +}
@@ -68,7 +68,7 @@ public abstract class AbstractTransportService implements TransportService { @@ -68,7 +68,7 @@ public abstract class AbstractTransportService implements TransportService {
68 68
69 @Override 69 @Override
70 public void process(TransportProtos.SessionInfoProto sessionInfo, TransportProtos.SessionEventMsg msg, TransportServiceCallback<Void> callback) { 70 public void process(TransportProtos.SessionInfoProto sessionInfo, TransportProtos.SessionEventMsg msg, TransportServiceCallback<Void> callback) {
71 - if (checkLimits(sessionInfo, callback)) { 71 + if (checkLimits(sessionInfo, msg, callback)) {
72 reportActivityInternal(sessionInfo); 72 reportActivityInternal(sessionInfo);
73 doProcess(sessionInfo, msg, callback); 73 doProcess(sessionInfo, msg, callback);
74 } 74 }
@@ -76,7 +76,7 @@ public abstract class AbstractTransportService implements TransportService { @@ -76,7 +76,7 @@ public abstract class AbstractTransportService implements TransportService {
76 76
77 @Override 77 @Override
78 public void process(TransportProtos.SessionInfoProto sessionInfo, TransportProtos.PostTelemetryMsg msg, TransportServiceCallback<Void> callback) { 78 public void process(TransportProtos.SessionInfoProto sessionInfo, TransportProtos.PostTelemetryMsg msg, TransportServiceCallback<Void> callback) {
79 - if (checkLimits(sessionInfo, callback)) { 79 + if (checkLimits(sessionInfo, msg, callback)) {
80 reportActivityInternal(sessionInfo); 80 reportActivityInternal(sessionInfo);
81 doProcess(sessionInfo, msg, callback); 81 doProcess(sessionInfo, msg, callback);
82 } 82 }
@@ -84,7 +84,7 @@ public abstract class AbstractTransportService implements TransportService { @@ -84,7 +84,7 @@ public abstract class AbstractTransportService implements TransportService {
84 84
85 @Override 85 @Override
86 public void process(TransportProtos.SessionInfoProto sessionInfo, TransportProtos.PostAttributeMsg msg, TransportServiceCallback<Void> callback) { 86 public void process(TransportProtos.SessionInfoProto sessionInfo, TransportProtos.PostAttributeMsg msg, TransportServiceCallback<Void> callback) {
87 - if (checkLimits(sessionInfo, callback)) { 87 + if (checkLimits(sessionInfo, msg, callback)) {
88 reportActivityInternal(sessionInfo); 88 reportActivityInternal(sessionInfo);
89 doProcess(sessionInfo, msg, callback); 89 doProcess(sessionInfo, msg, callback);
90 } 90 }
@@ -92,7 +92,7 @@ public abstract class AbstractTransportService implements TransportService { @@ -92,7 +92,7 @@ public abstract class AbstractTransportService implements TransportService {
92 92
93 @Override 93 @Override
94 public void process(TransportProtos.SessionInfoProto sessionInfo, TransportProtos.GetAttributeRequestMsg msg, TransportServiceCallback<Void> callback) { 94 public void process(TransportProtos.SessionInfoProto sessionInfo, TransportProtos.GetAttributeRequestMsg msg, TransportServiceCallback<Void> callback) {
95 - if (checkLimits(sessionInfo, callback)) { 95 + if (checkLimits(sessionInfo, msg, callback)) {
96 reportActivityInternal(sessionInfo); 96 reportActivityInternal(sessionInfo);
97 doProcess(sessionInfo, msg, callback); 97 doProcess(sessionInfo, msg, callback);
98 } 98 }
@@ -100,7 +100,7 @@ public abstract class AbstractTransportService implements TransportService { @@ -100,7 +100,7 @@ public abstract class AbstractTransportService implements TransportService {
100 100
101 @Override 101 @Override
102 public void process(TransportProtos.SessionInfoProto sessionInfo, TransportProtos.SubscribeToAttributeUpdatesMsg msg, TransportServiceCallback<Void> callback) { 102 public void process(TransportProtos.SessionInfoProto sessionInfo, TransportProtos.SubscribeToAttributeUpdatesMsg msg, TransportServiceCallback<Void> callback) {
103 - if (checkLimits(sessionInfo, callback)) { 103 + if (checkLimits(sessionInfo, msg, callback)) {
104 SessionMetaData sessionMetaData = reportActivityInternal(sessionInfo); 104 SessionMetaData sessionMetaData = reportActivityInternal(sessionInfo);
105 sessionMetaData.setSubscribedToAttributes(!msg.getUnsubscribe()); 105 sessionMetaData.setSubscribedToAttributes(!msg.getUnsubscribe());
106 doProcess(sessionInfo, msg, callback); 106 doProcess(sessionInfo, msg, callback);
@@ -109,7 +109,7 @@ public abstract class AbstractTransportService implements TransportService { @@ -109,7 +109,7 @@ public abstract class AbstractTransportService implements TransportService {
109 109
110 @Override 110 @Override
111 public void process(TransportProtos.SessionInfoProto sessionInfo, TransportProtos.SubscribeToRPCMsg msg, TransportServiceCallback<Void> callback) { 111 public void process(TransportProtos.SessionInfoProto sessionInfo, TransportProtos.SubscribeToRPCMsg msg, TransportServiceCallback<Void> callback) {
112 - if (checkLimits(sessionInfo, callback)) { 112 + if (checkLimits(sessionInfo, msg, callback)) {
113 SessionMetaData sessionMetaData = reportActivityInternal(sessionInfo); 113 SessionMetaData sessionMetaData = reportActivityInternal(sessionInfo);
114 sessionMetaData.setSubscribedToRPC(!msg.getUnsubscribe()); 114 sessionMetaData.setSubscribedToRPC(!msg.getUnsubscribe());
115 doProcess(sessionInfo, msg, callback); 115 doProcess(sessionInfo, msg, callback);
@@ -118,7 +118,7 @@ public abstract class AbstractTransportService implements TransportService { @@ -118,7 +118,7 @@ public abstract class AbstractTransportService implements TransportService {
118 118
119 @Override 119 @Override
120 public void process(TransportProtos.SessionInfoProto sessionInfo, TransportProtos.ToDeviceRpcResponseMsg msg, TransportServiceCallback<Void> callback) { 120 public void process(TransportProtos.SessionInfoProto sessionInfo, TransportProtos.ToDeviceRpcResponseMsg msg, TransportServiceCallback<Void> callback) {
121 - if (checkLimits(sessionInfo, callback)) { 121 + if (checkLimits(sessionInfo, msg, callback)) {
122 reportActivityInternal(sessionInfo); 122 reportActivityInternal(sessionInfo);
123 doProcess(sessionInfo, msg, callback); 123 doProcess(sessionInfo, msg, callback);
124 } 124 }
@@ -126,7 +126,7 @@ public abstract class AbstractTransportService implements TransportService { @@ -126,7 +126,7 @@ public abstract class AbstractTransportService implements TransportService {
126 126
127 @Override 127 @Override
128 public void process(TransportProtos.SessionInfoProto sessionInfo, TransportProtos.ToServerRpcRequestMsg msg, TransportServiceCallback<Void> callback) { 128 public void process(TransportProtos.SessionInfoProto sessionInfo, TransportProtos.ToServerRpcRequestMsg msg, TransportServiceCallback<Void> callback) {
129 - if (checkLimits(sessionInfo, callback)) { 129 + if (checkLimits(sessionInfo, msg, callback)) {
130 reportActivityInternal(sessionInfo); 130 reportActivityInternal(sessionInfo);
131 doProcess(sessionInfo, msg, callback); 131 doProcess(sessionInfo, msg, callback);
132 } 132 }
@@ -196,7 +196,10 @@ public abstract class AbstractTransportService implements TransportService { @@ -196,7 +196,10 @@ public abstract class AbstractTransportService implements TransportService {
196 } 196 }
197 197
198 @Override 198 @Override
199 - public boolean checkLimits(TransportProtos.SessionInfoProto sessionInfo, TransportServiceCallback<Void> callback) { 199 + public boolean checkLimits(TransportProtos.SessionInfoProto sessionInfo, Object msg, TransportServiceCallback<Void> callback) {
  200 + if (log.isTraceEnabled()) {
  201 + log.trace("[{}] Processing msg: {}", toId(sessionInfo), msg);
  202 + }
200 if (!rateLimitEnabled) { 203 if (!rateLimitEnabled) {
201 return true; 204 return true;
202 } 205 }
@@ -206,6 +209,9 @@ public abstract class AbstractTransportService implements TransportService { @@ -206,6 +209,9 @@ public abstract class AbstractTransportService implements TransportService {
206 if (callback != null) { 209 if (callback != null) {
207 callback.onError(new TbRateLimitsException(EntityType.TENANT)); 210 callback.onError(new TbRateLimitsException(EntityType.TENANT));
208 } 211 }
  212 + if (log.isTraceEnabled()) {
  213 + log.trace("[{}][{}] Tenant level rate limit detected: {}", toId(sessionInfo), tenantId, msg);
  214 + }
209 return false; 215 return false;
210 } 216 }
211 DeviceId deviceId = new DeviceId(new UUID(sessionInfo.getDeviceIdMSB(), sessionInfo.getDeviceIdLSB())); 217 DeviceId deviceId = new DeviceId(new UUID(sessionInfo.getDeviceIdMSB(), sessionInfo.getDeviceIdLSB()));
@@ -214,8 +220,12 @@ public abstract class AbstractTransportService implements TransportService { @@ -214,8 +220,12 @@ public abstract class AbstractTransportService implements TransportService {
214 if (callback != null) { 220 if (callback != null) {
215 callback.onError(new TbRateLimitsException(EntityType.DEVICE)); 221 callback.onError(new TbRateLimitsException(EntityType.DEVICE));
216 } 222 }
  223 + if (log.isTraceEnabled()) {
  224 + log.trace("[{}][{}] Device level rate limit detected: {}", toId(sessionInfo), deviceId, msg);
  225 + }
217 return false; 226 return false;
218 } 227 }
  228 +
219 return true; 229 return true;
220 } 230 }
221 231
@@ -250,11 +260,11 @@ public abstract class AbstractTransportService implements TransportService { @@ -250,11 +260,11 @@ public abstract class AbstractTransportService implements TransportService {
250 } 260 }
251 } 261 }
252 262
253 - private UUID toId(TransportProtos.SessionInfoProto sessionInfo) { 263 + protected UUID toId(TransportProtos.SessionInfoProto sessionInfo) {
254 return new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB()); 264 return new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB());
255 } 265 }
256 266
257 - String getRoutingKey(TransportProtos.SessionInfoProto sessionInfo) { 267 + protected String getRoutingKey(TransportProtos.SessionInfoProto sessionInfo) {
258 return new UUID(sessionInfo.getDeviceIdMSB(), sessionInfo.getDeviceIdLSB()).toString(); 268 return new UUID(sessionInfo.getDeviceIdMSB(), sessionInfo.getDeviceIdLSB()).toString();
259 } 269 }
260 270
@@ -197,6 +197,7 @@ public class RemoteTransportService extends AbstractTransportService { @@ -197,6 +197,7 @@ public class RemoteTransportService extends AbstractTransportService {
197 197
198 @Override 198 @Override
199 public void process(ValidateDeviceTokenRequestMsg msg, TransportServiceCallback<ValidateDeviceCredentialsResponseMsg> callback) { 199 public void process(ValidateDeviceTokenRequestMsg msg, TransportServiceCallback<ValidateDeviceCredentialsResponseMsg> callback) {
  200 + log.trace("Processing msg: {}", msg);
200 AsyncCallbackTemplate.withCallback(transportApiTemplate.post(msg.getToken(), 201 AsyncCallbackTemplate.withCallback(transportApiTemplate.post(msg.getToken(),
201 TransportApiRequestMsg.newBuilder().setValidateTokenRequestMsg(msg).build()), 202 TransportApiRequestMsg.newBuilder().setValidateTokenRequestMsg(msg).build()),
202 response -> callback.onSuccess(response.getValidateTokenResponseMsg()), callback::onError, transportCallbackExecutor); 203 response -> callback.onSuccess(response.getValidateTokenResponseMsg()), callback::onError, transportCallbackExecutor);
@@ -204,6 +205,7 @@ public class RemoteTransportService extends AbstractTransportService { @@ -204,6 +205,7 @@ public class RemoteTransportService extends AbstractTransportService {
204 205
205 @Override 206 @Override
206 public void process(ValidateDeviceX509CertRequestMsg msg, TransportServiceCallback<ValidateDeviceCredentialsResponseMsg> callback) { 207 public void process(ValidateDeviceX509CertRequestMsg msg, TransportServiceCallback<ValidateDeviceCredentialsResponseMsg> callback) {
  208 + log.trace("Processing msg: {}", msg);
207 AsyncCallbackTemplate.withCallback(transportApiTemplate.post(msg.getHash(), 209 AsyncCallbackTemplate.withCallback(transportApiTemplate.post(msg.getHash(),
208 TransportApiRequestMsg.newBuilder().setValidateX509CertRequestMsg(msg).build()), 210 TransportApiRequestMsg.newBuilder().setValidateX509CertRequestMsg(msg).build()),
209 response -> callback.onSuccess(response.getValidateTokenResponseMsg()), callback::onError, transportCallbackExecutor); 211 response -> callback.onSuccess(response.getValidateTokenResponseMsg()), callback::onError, transportCallbackExecutor);
@@ -211,6 +213,7 @@ public class RemoteTransportService extends AbstractTransportService { @@ -211,6 +213,7 @@ public class RemoteTransportService extends AbstractTransportService {
211 213
212 @Override 214 @Override
213 public void process(GetOrCreateDeviceFromGatewayRequestMsg msg, TransportServiceCallback<GetOrCreateDeviceFromGatewayResponseMsg> callback) { 215 public void process(GetOrCreateDeviceFromGatewayRequestMsg msg, TransportServiceCallback<GetOrCreateDeviceFromGatewayResponseMsg> callback) {
  216 + log.trace("Processing msg: {}", msg);
214 AsyncCallbackTemplate.withCallback(transportApiTemplate.post(msg.getDeviceName(), 217 AsyncCallbackTemplate.withCallback(transportApiTemplate.post(msg.getDeviceName(),
215 TransportApiRequestMsg.newBuilder().setGetOrCreateDeviceRequestMsg(msg).build()), 218 TransportApiRequestMsg.newBuilder().setGetOrCreateDeviceRequestMsg(msg).build()),
216 response -> callback.onSuccess(response.getGetOrCreateDeviceResponseMsg()), callback::onError, transportCallbackExecutor); 219 response -> callback.onSuccess(response.getGetOrCreateDeviceResponseMsg()), callback::onError, transportCallbackExecutor);
@@ -218,6 +221,9 @@ public class RemoteTransportService extends AbstractTransportService { @@ -218,6 +221,9 @@ public class RemoteTransportService extends AbstractTransportService {
218 221
219 @Override 222 @Override
220 public void process(SessionInfoProto sessionInfo, SubscriptionInfoProto msg, TransportServiceCallback<Void> callback) { 223 public void process(SessionInfoProto sessionInfo, SubscriptionInfoProto msg, TransportServiceCallback<Void> callback) {
  224 + if (log.isTraceEnabled()) {
  225 + log.trace("[{}] Processing msg: {}", toId(sessionInfo), msg);
  226 + }
221 ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg( 227 ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg(
222 TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) 228 TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo)
223 .setSubscriptionInfo(msg).build() 229 .setSubscriptionInfo(msg).build()
@@ -45,7 +45,6 @@ public abstract class DeviceAwareSessionContext implements SessionContext { @@ -45,7 +45,6 @@ public abstract class DeviceAwareSessionContext implements SessionContext {
45 this.deviceId = new DeviceId(new UUID(deviceInfo.getDeviceIdMSB(), deviceInfo.getDeviceIdLSB())); 45 this.deviceId = new DeviceId(new UUID(deviceInfo.getDeviceIdMSB(), deviceInfo.getDeviceIdLSB()));
46 } 46 }
47 47
48 -  
49 public boolean isConnected() { 48 public boolean isConnected() {
50 return deviceInfo != null; 49 return deviceInfo != null;
51 } 50 }
@@ -43,6 +43,8 @@ public interface EntityViewService { @@ -43,6 +43,8 @@ public interface EntityViewService {
43 43
44 EntityView findEntityViewById(EntityViewId entityViewId); 44 EntityView findEntityViewById(EntityViewId entityViewId);
45 45
  46 + EntityView findEntityViewByTenantIdAndName(TenantId tenantId, String name);
  47 +
46 TextPageData<EntityView> findEntityViewByTenantId(TenantId tenantId, TextPageLink pageLink); 48 TextPageData<EntityView> findEntityViewByTenantId(TenantId tenantId, TextPageLink pageLink);
47 49
48 TextPageData<EntityView> findEntityViewByTenantIdAndType(TenantId tenantId, TextPageLink pageLink, String type); 50 TextPageData<EntityView> findEntityViewByTenantIdAndType(TenantId tenantId, TextPageLink pageLink, String type);
@@ -29,8 +29,6 @@ import org.springframework.cache.annotation.Cacheable; @@ -29,8 +29,6 @@ import org.springframework.cache.annotation.Cacheable;
29 import org.springframework.cache.annotation.Caching; 29 import org.springframework.cache.annotation.Caching;
30 import org.springframework.stereotype.Service; 30 import org.springframework.stereotype.Service;
31 import org.thingsboard.server.common.data.Customer; 31 import org.thingsboard.server.common.data.Customer;
32 -import org.thingsboard.server.common.data.DataConstants;  
33 -import org.thingsboard.server.common.data.Device;  
34 import org.thingsboard.server.common.data.EntitySubtype; 32 import org.thingsboard.server.common.data.EntitySubtype;
35 import org.thingsboard.server.common.data.EntityType; 33 import org.thingsboard.server.common.data.EntityType;
36 import org.thingsboard.server.common.data.EntityView; 34 import org.thingsboard.server.common.data.EntityView;
@@ -40,12 +38,10 @@ import org.thingsboard.server.common.data.id.CustomerId; @@ -40,12 +38,10 @@ import org.thingsboard.server.common.data.id.CustomerId;
40 import org.thingsboard.server.common.data.id.EntityId; 38 import org.thingsboard.server.common.data.id.EntityId;
41 import org.thingsboard.server.common.data.id.EntityViewId; 39 import org.thingsboard.server.common.data.id.EntityViewId;
42 import org.thingsboard.server.common.data.id.TenantId; 40 import org.thingsboard.server.common.data.id.TenantId;
43 -import org.thingsboard.server.common.data.kv.AttributeKvEntry;  
44 import org.thingsboard.server.common.data.page.TextPageData; 41 import org.thingsboard.server.common.data.page.TextPageData;
45 import org.thingsboard.server.common.data.page.TextPageLink; 42 import org.thingsboard.server.common.data.page.TextPageLink;
46 import org.thingsboard.server.common.data.relation.EntityRelation; 43 import org.thingsboard.server.common.data.relation.EntityRelation;
47 import org.thingsboard.server.common.data.relation.EntitySearchDirection; 44 import org.thingsboard.server.common.data.relation.EntitySearchDirection;
48 -import org.thingsboard.server.dao.attributes.AttributesService;  
49 import org.thingsboard.server.dao.customer.CustomerDao; 45 import org.thingsboard.server.dao.customer.CustomerDao;
50 import org.thingsboard.server.dao.entity.AbstractEntityService; 46 import org.thingsboard.server.dao.entity.AbstractEntityService;
51 import org.thingsboard.server.dao.exception.DataValidationException; 47 import org.thingsboard.server.dao.exception.DataValidationException;
@@ -56,15 +52,13 @@ import org.thingsboard.server.dao.tenant.TenantDao; @@ -56,15 +52,13 @@ import org.thingsboard.server.dao.tenant.TenantDao;
56 import javax.annotation.Nullable; 52 import javax.annotation.Nullable;
57 import java.util.ArrayList; 53 import java.util.ArrayList;
58 import java.util.Arrays; 54 import java.util.Arrays;
59 -import java.util.Collection;  
60 import java.util.Collections; 55 import java.util.Collections;
61 import java.util.Comparator; 56 import java.util.Comparator;
62 import java.util.List; 57 import java.util.List;
63 -import java.util.concurrent.ExecutionException; 58 +import java.util.Optional;
64 import java.util.stream.Collectors; 59 import java.util.stream.Collectors;
65 60
66 import static org.thingsboard.server.common.data.CacheConstants.ENTITY_VIEW_CACHE; 61 import static org.thingsboard.server.common.data.CacheConstants.ENTITY_VIEW_CACHE;
67 -import static org.thingsboard.server.common.data.CacheConstants.RELATIONS_CACHE;  
68 import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; 62 import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID;
69 import static org.thingsboard.server.dao.service.Validator.validateId; 63 import static org.thingsboard.server.dao.service.Validator.validateId;
70 import static org.thingsboard.server.dao.service.Validator.validatePageLink; 64 import static org.thingsboard.server.dao.service.Validator.validatePageLink;
@@ -96,6 +90,7 @@ public class EntityViewServiceImpl extends AbstractEntityService implements Enti @@ -96,6 +90,7 @@ public class EntityViewServiceImpl extends AbstractEntityService implements Enti
96 90
97 @Caching(evict = { 91 @Caching(evict = {
98 @CacheEvict(cacheNames = ENTITY_VIEW_CACHE, key = "{#entityView.tenantId, #entityView.entityId}"), 92 @CacheEvict(cacheNames = ENTITY_VIEW_CACHE, key = "{#entityView.tenantId, #entityView.entityId}"),
  93 + @CacheEvict(cacheNames = ENTITY_VIEW_CACHE, key = "{#entityView.tenantId, #entityView.name}"),
99 @CacheEvict(cacheNames = ENTITY_VIEW_CACHE, key = "{#entityView.id}")}) 94 @CacheEvict(cacheNames = ENTITY_VIEW_CACHE, key = "{#entityView.id}")})
100 @Override 95 @Override
101 public EntityView saveEntityView(EntityView entityView) { 96 public EntityView saveEntityView(EntityView entityView) {
@@ -137,6 +132,15 @@ public class EntityViewServiceImpl extends AbstractEntityService implements Enti @@ -137,6 +132,15 @@ public class EntityViewServiceImpl extends AbstractEntityService implements Enti
137 return entityViewDao.findById(entityViewId.getId()); 132 return entityViewDao.findById(entityViewId.getId());
138 } 133 }
139 134
  135 + @Cacheable(cacheNames = ENTITY_VIEW_CACHE, key = "{#tenantId, #name}")
  136 + @Override
  137 + public EntityView findEntityViewByTenantIdAndName(TenantId tenantId, String name) {
  138 + log.trace("Executing findEntityViewByTenantIdAndName [{}][{}]", tenantId, name);
  139 + validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
  140 + Optional<EntityView> entityViewOpt = entityViewDao.findEntityViewByTenantIdAndName(tenantId.getId(), name);
  141 + return entityViewOpt.orElse(null);
  142 + }
  143 +
140 @Override 144 @Override
141 public TextPageData<EntityView> findEntityViewByTenantId(TenantId tenantId, TextPageLink pageLink) { 145 public TextPageData<EntityView> findEntityViewByTenantId(TenantId tenantId, TextPageLink pageLink) {
142 log.trace("Executing findEntityViewsByTenantId, tenantId [{}], pageLink [{}]", tenantId, pageLink); 146 log.trace("Executing findEntityViewsByTenantId, tenantId [{}], pageLink [{}]", tenantId, pageLink);
@@ -255,6 +259,7 @@ public class EntityViewServiceImpl extends AbstractEntityService implements Enti @@ -255,6 +259,7 @@ public class EntityViewServiceImpl extends AbstractEntityService implements Enti
255 deleteEntityRelations(entityViewId); 259 deleteEntityRelations(entityViewId);
256 EntityView entityView = entityViewDao.findById(entityViewId.getId()); 260 EntityView entityView = entityViewDao.findById(entityViewId.getId());
257 cacheManager.getCache(ENTITY_VIEW_CACHE).evict(Arrays.asList(entityView.getTenantId(), entityView.getEntityId())); 261 cacheManager.getCache(ENTITY_VIEW_CACHE).evict(Arrays.asList(entityView.getTenantId(), entityView.getEntityId()));
  262 + cacheManager.getCache(ENTITY_VIEW_CACHE).evict(Arrays.asList(entityView.getTenantId(), entityView.getName()));
258 entityViewDao.removeById(entityViewId.getId()); 263 entityViewDao.removeById(entityViewId.getId());
259 } 264 }
260 265
@@ -20,14 +20,29 @@ import lombok.Data; @@ -20,14 +20,29 @@ import lombok.Data;
20 import lombok.NoArgsConstructor; 20 import lombok.NoArgsConstructor;
21 import org.thingsboard.server.common.data.EntityType; 21 import org.thingsboard.server.common.data.EntityType;
22 22
  23 +import javax.persistence.Column;
  24 +import javax.persistence.Embeddable;
  25 +import javax.persistence.EnumType;
  26 +import javax.persistence.Enumerated;
23 import java.io.Serializable; 27 import java.io.Serializable;
24 28
  29 +import static org.thingsboard.server.dao.model.ModelConstants.ATTRIBUTE_KEY_COLUMN;
  30 +import static org.thingsboard.server.dao.model.ModelConstants.ATTRIBUTE_TYPE_COLUMN;
  31 +import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_ID_COLUMN;
  32 +import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_TYPE_COLUMN;
  33 +
25 @Data 34 @Data
26 @AllArgsConstructor 35 @AllArgsConstructor
27 @NoArgsConstructor 36 @NoArgsConstructor
  37 +@Embeddable
28 public class AttributeKvCompositeKey implements Serializable { 38 public class AttributeKvCompositeKey implements Serializable {
  39 + @Enumerated(EnumType.STRING)
  40 + @Column(name = ENTITY_TYPE_COLUMN)
29 private EntityType entityType; 41 private EntityType entityType;
  42 + @Column(name = ENTITY_ID_COLUMN)
30 private String entityId; 43 private String entityId;
  44 + @Column(name = ATTRIBUTE_TYPE_COLUMN)
31 private String attributeType; 45 private String attributeType;
  46 + @Column(name = ATTRIBUTE_KEY_COLUMN)
32 private String attributeKey; 47 private String attributeKey;
33 } 48 }
@@ -27,6 +27,7 @@ import org.thingsboard.server.common.data.kv.StringDataEntry; @@ -27,6 +27,7 @@ import org.thingsboard.server.common.data.kv.StringDataEntry;
27 import org.thingsboard.server.dao.model.ToData; 27 import org.thingsboard.server.dao.model.ToData;
28 28
29 import javax.persistence.Column; 29 import javax.persistence.Column;
  30 +import javax.persistence.EmbeddedId;
30 import javax.persistence.Entity; 31 import javax.persistence.Entity;
31 import javax.persistence.EnumType; 32 import javax.persistence.EnumType;
32 import javax.persistence.Enumerated; 33 import javax.persistence.Enumerated;
@@ -48,25 +49,10 @@ import static org.thingsboard.server.dao.model.ModelConstants.STRING_VALUE_COLUM @@ -48,25 +49,10 @@ import static org.thingsboard.server.dao.model.ModelConstants.STRING_VALUE_COLUM
48 @Data 49 @Data
49 @Entity 50 @Entity
50 @Table(name = "attribute_kv") 51 @Table(name = "attribute_kv")
51 -@IdClass(AttributeKvCompositeKey.class)  
52 public class AttributeKvEntity implements ToData<AttributeKvEntry>, Serializable { 52 public class AttributeKvEntity implements ToData<AttributeKvEntry>, Serializable {
53 53
54 - @Id  
55 - @Enumerated(EnumType.STRING)  
56 - @Column(name = ENTITY_TYPE_COLUMN)  
57 - private EntityType entityType;  
58 -  
59 - @Id  
60 - @Column(name = ENTITY_ID_COLUMN)  
61 - private String entityId;  
62 -  
63 - @Id  
64 - @Column(name = ATTRIBUTE_TYPE_COLUMN)  
65 - private String attributeType;  
66 -  
67 - @Id  
68 - @Column(name = ATTRIBUTE_KEY_COLUMN)  
69 - private String attributeKey; 54 + @EmbeddedId
  55 + private AttributeKvCompositeKey id;
70 56
71 @Column(name = BOOLEAN_VALUE_COLUMN) 57 @Column(name = BOOLEAN_VALUE_COLUMN)
72 private Boolean booleanValue; 58 private Boolean booleanValue;
@@ -87,13 +73,13 @@ public class AttributeKvEntity implements ToData<AttributeKvEntry>, Serializable @@ -87,13 +73,13 @@ public class AttributeKvEntity implements ToData<AttributeKvEntry>, Serializable
87 public AttributeKvEntry toData() { 73 public AttributeKvEntry toData() {
88 KvEntry kvEntry = null; 74 KvEntry kvEntry = null;
89 if (strValue != null) { 75 if (strValue != null) {
90 - kvEntry = new StringDataEntry(attributeKey, strValue); 76 + kvEntry = new StringDataEntry(id.getAttributeKey(), strValue);
91 } else if (booleanValue != null) { 77 } else if (booleanValue != null) {
92 - kvEntry = new BooleanDataEntry(attributeKey, booleanValue); 78 + kvEntry = new BooleanDataEntry(id.getAttributeKey(), booleanValue);
93 } else if (doubleValue != null) { 79 } else if (doubleValue != null) {
94 - kvEntry = new DoubleDataEntry(attributeKey, doubleValue); 80 + kvEntry = new DoubleDataEntry(id.getAttributeKey(), doubleValue);
95 } else if (longValue != null) { 81 } else if (longValue != null) {
96 - kvEntry = new LongDataEntry(attributeKey, longValue); 82 + kvEntry = new LongDataEntry(id.getAttributeKey(), longValue);
97 } 83 }
98 return new BaseAttributeKvEntry(kvEntry, lastUpdateTs); 84 return new BaseAttributeKvEntry(kvEntry, lastUpdateTs);
99 } 85 }
@@ -15,7 +15,9 @@ @@ -15,7 +15,9 @@
15 */ 15 */
16 package org.thingsboard.server.dao.sql.attributes; 16 package org.thingsboard.server.dao.sql.attributes;
17 17
  18 +import org.springframework.data.jpa.repository.Query;
18 import org.springframework.data.repository.CrudRepository; 19 import org.springframework.data.repository.CrudRepository;
  20 +import org.springframework.data.repository.query.Param;
19 import org.thingsboard.server.common.data.EntityType; 21 import org.thingsboard.server.common.data.EntityType;
20 import org.thingsboard.server.dao.model.sql.AttributeKvCompositeKey; 22 import org.thingsboard.server.dao.model.sql.AttributeKvCompositeKey;
21 import org.thingsboard.server.dao.model.sql.AttributeKvEntity; 23 import org.thingsboard.server.dao.model.sql.AttributeKvEntity;
@@ -26,8 +28,11 @@ import java.util.List; @@ -26,8 +28,11 @@ import java.util.List;
26 @SqlDao 28 @SqlDao
27 public interface AttributeKvRepository extends CrudRepository<AttributeKvEntity, AttributeKvCompositeKey> { 29 public interface AttributeKvRepository extends CrudRepository<AttributeKvEntity, AttributeKvCompositeKey> {
28 30
29 - List<AttributeKvEntity> findAllByEntityTypeAndEntityIdAndAttributeType(EntityType entityType,  
30 - String entityId,  
31 - String attributeType); 31 + @Query("SELECT a FROM AttributeKvEntity a WHERE a.id.entityType = :entityType " +
  32 + "AND a.id.entityId = :entityId " +
  33 + "AND a.id.attributeType = :attributeType")
  34 + List<AttributeKvEntity> findAllByEntityTypeAndEntityIdAndAttributeType(@Param("entityType") EntityType entityType,
  35 + @Param("entityId") String entityId,
  36 + @Param("attributeType") String attributeType);
32 } 37 }
33 38
@@ -79,10 +79,7 @@ public class JpaAttributeDao extends JpaAbstractDaoListeningExecutorService impl @@ -79,10 +79,7 @@ public class JpaAttributeDao extends JpaAbstractDaoListeningExecutorService impl
79 @Override 79 @Override
80 public ListenableFuture<Void> save(EntityId entityId, String attributeType, AttributeKvEntry attribute) { 80 public ListenableFuture<Void> save(EntityId entityId, String attributeType, AttributeKvEntry attribute) {
81 AttributeKvEntity entity = new AttributeKvEntity(); 81 AttributeKvEntity entity = new AttributeKvEntity();
82 - entity.setEntityType(entityId.getEntityType());  
83 - entity.setEntityId(fromTimeUUID(entityId.getId()));  
84 - entity.setAttributeType(attributeType);  
85 - entity.setAttributeKey(attribute.getKey()); 82 + entity.setId(new AttributeKvCompositeKey(entityId.getEntityType(), fromTimeUUID(entityId.getId()), attributeType, attribute.getKey()));
86 entity.setLastUpdateTs(attribute.getLastUpdateTs()); 83 entity.setLastUpdateTs(attribute.getLastUpdateTs());
87 entity.setStrValue(attribute.getStrValue().orElse(null)); 84 entity.setStrValue(attribute.getStrValue().orElse(null));
88 entity.setDoubleValue(attribute.getDoubleValue().orElse(null)); 85 entity.setDoubleValue(attribute.getDoubleValue().orElse(null));
@@ -100,10 +97,7 @@ public class JpaAttributeDao extends JpaAbstractDaoListeningExecutorService impl @@ -100,10 +97,7 @@ public class JpaAttributeDao extends JpaAbstractDaoListeningExecutorService impl
100 .stream() 97 .stream()
101 .map(key -> { 98 .map(key -> {
102 AttributeKvEntity entityToDelete = new AttributeKvEntity(); 99 AttributeKvEntity entityToDelete = new AttributeKvEntity();
103 - entityToDelete.setEntityType(entityId.getEntityType());  
104 - entityToDelete.setEntityId(fromTimeUUID(entityId.getId()));  
105 - entityToDelete.setAttributeType(attributeType);  
106 - entityToDelete.setAttributeKey(key); 100 + entityToDelete.setId(new AttributeKvCompositeKey(entityId.getEntityType(), fromTimeUUID(entityId.getId()), attributeType, key));
107 return entityToDelete; 101 return entityToDelete;
108 }).collect(Collectors.toList()); 102 }).collect(Collectors.toList());
109 103
@@ -17,6 +17,7 @@ package org.thingsboard.server.dao.sql.timeseries; @@ -17,6 +17,7 @@ package org.thingsboard.server.dao.sql.timeseries;
17 17
18 import com.google.common.base.Function; 18 import com.google.common.base.Function;
19 import com.google.common.collect.Lists; 19 import com.google.common.collect.Lists;
  20 +import com.google.common.util.concurrent.FutureCallback;
20 import com.google.common.util.concurrent.Futures; 21 import com.google.common.util.concurrent.Futures;
21 import com.google.common.util.concurrent.ListenableFuture; 22 import com.google.common.util.concurrent.ListenableFuture;
22 import com.google.common.util.concurrent.ListeningExecutorService; 23 import com.google.common.util.concurrent.ListeningExecutorService;
@@ -31,6 +32,7 @@ import org.springframework.stereotype.Component; @@ -31,6 +32,7 @@ import org.springframework.stereotype.Component;
31 import org.thingsboard.server.common.data.UUIDConverter; 32 import org.thingsboard.server.common.data.UUIDConverter;
32 import org.thingsboard.server.common.data.id.EntityId; 33 import org.thingsboard.server.common.data.id.EntityId;
33 import org.thingsboard.server.common.data.kv.Aggregation; 34 import org.thingsboard.server.common.data.kv.Aggregation;
  35 +import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery;
34 import org.thingsboard.server.common.data.kv.BasicTsKvEntry; 36 import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
35 import org.thingsboard.server.common.data.kv.DeleteTsKvQuery; 37 import org.thingsboard.server.common.data.kv.DeleteTsKvQuery;
36 import org.thingsboard.server.common.data.kv.ReadTsKvQuery; 38 import org.thingsboard.server.common.data.kv.ReadTsKvQuery;
@@ -41,9 +43,9 @@ import org.thingsboard.server.dao.model.sql.TsKvEntity; @@ -41,9 +43,9 @@ import org.thingsboard.server.dao.model.sql.TsKvEntity;
41 import org.thingsboard.server.dao.model.sql.TsKvLatestCompositeKey; 43 import org.thingsboard.server.dao.model.sql.TsKvLatestCompositeKey;
42 import org.thingsboard.server.dao.model.sql.TsKvLatestEntity; 44 import org.thingsboard.server.dao.model.sql.TsKvLatestEntity;
43 import org.thingsboard.server.dao.sql.JpaAbstractDaoListeningExecutorService; 45 import org.thingsboard.server.dao.sql.JpaAbstractDaoListeningExecutorService;
  46 +import org.thingsboard.server.dao.timeseries.SimpleListenableFuture;
44 import org.thingsboard.server.dao.timeseries.TimeseriesDao; 47 import org.thingsboard.server.dao.timeseries.TimeseriesDao;
45 import org.thingsboard.server.dao.timeseries.TsInsertExecutorType; 48 import org.thingsboard.server.dao.timeseries.TsInsertExecutorType;
46 -import org.thingsboard.server.dao.util.SqlDao;  
47 import org.thingsboard.server.dao.util.SqlTsDao; 49 import org.thingsboard.server.dao.util.SqlTsDao;
48 50
49 import javax.annotation.Nullable; 51 import javax.annotation.Nullable;
@@ -53,6 +55,7 @@ import java.util.ArrayList; @@ -53,6 +55,7 @@ import java.util.ArrayList;
53 import java.util.List; 55 import java.util.List;
54 import java.util.Optional; 56 import java.util.Optional;
55 import java.util.concurrent.CompletableFuture; 57 import java.util.concurrent.CompletableFuture;
  58 +import java.util.concurrent.ExecutionException;
56 import java.util.concurrent.Executors; 59 import java.util.concurrent.Executors;
57 import java.util.stream.Collectors; 60 import java.util.stream.Collectors;
58 61
@@ -64,6 +67,8 @@ import static org.thingsboard.server.common.data.UUIDConverter.fromTimeUUID; @@ -64,6 +67,8 @@ import static org.thingsboard.server.common.data.UUIDConverter.fromTimeUUID;
64 @SqlTsDao 67 @SqlTsDao
65 public class JpaTimeseriesDao extends JpaAbstractDaoListeningExecutorService implements TimeseriesDao { 68 public class JpaTimeseriesDao extends JpaAbstractDaoListeningExecutorService implements TimeseriesDao {
66 69
  70 + private static final String DESC_ORDER = "DESC";
  71 +
67 @Value("${sql.ts_inserts_executor_type}") 72 @Value("${sql.ts_inserts_executor_type}")
68 private String insertExecutorType; 73 private String insertExecutorType;
69 74
@@ -326,14 +331,72 @@ public class JpaTimeseriesDao extends JpaAbstractDaoListeningExecutorService imp @@ -326,14 +331,72 @@ public class JpaTimeseriesDao extends JpaAbstractDaoListeningExecutorService imp
326 331
327 @Override 332 @Override
328 public ListenableFuture<Void> removeLatest(EntityId entityId, DeleteTsKvQuery query) { 333 public ListenableFuture<Void> removeLatest(EntityId entityId, DeleteTsKvQuery query) {
329 - TsKvLatestEntity latestEntity = new TsKvLatestEntity();  
330 - latestEntity.setEntityType(entityId.getEntityType());  
331 - latestEntity.setEntityId(fromTimeUUID(entityId.getId()));  
332 - latestEntity.setKey(query.getKey());  
333 - return service.submit(() -> {  
334 - tsKvLatestRepository.delete(latestEntity);  
335 - return null; 334 + ListenableFuture<TsKvEntry> latestFuture = findLatest(entityId, query.getKey());
  335 +
  336 + ListenableFuture<Boolean> booleanFuture = Futures.transform(latestFuture, tsKvEntry -> {
  337 + long ts = tsKvEntry.getTs();
  338 + return ts > query.getStartTs() && ts <= query.getEndTs();
  339 + }, service);
  340 +
  341 + ListenableFuture<Void> removedLatestFuture = Futures.transformAsync(booleanFuture, isRemove -> {
  342 + if (isRemove) {
  343 + TsKvLatestEntity latestEntity = new TsKvLatestEntity();
  344 + latestEntity.setEntityType(entityId.getEntityType());
  345 + latestEntity.setEntityId(fromTimeUUID(entityId.getId()));
  346 + latestEntity.setKey(query.getKey());
  347 + return service.submit(() -> {
  348 + tsKvLatestRepository.delete(latestEntity);
  349 + return null;
  350 + });
  351 + }
  352 + return Futures.immediateFuture(null);
  353 + }, service);
  354 +
  355 + final SimpleListenableFuture<Void> resultFuture = new SimpleListenableFuture<>();
  356 + Futures.addCallback(removedLatestFuture, new FutureCallback<Void>() {
  357 + @Override
  358 + public void onSuccess(@Nullable Void result) {
  359 + if (query.getRewriteLatestIfDeleted()) {
  360 + ListenableFuture<Void> savedLatestFuture = Futures.transformAsync(booleanFuture, isRemove -> {
  361 + if (isRemove) {
  362 + return getNewLatestEntryFuture(entityId, query);
  363 + }
  364 + return Futures.immediateFuture(null);
  365 + }, service);
  366 +
  367 + try {
  368 + resultFuture.set(savedLatestFuture.get());
  369 + } catch (InterruptedException | ExecutionException e) {
  370 + log.warn("Could not get latest saved value for [{}], {}", entityId, query.getKey(), e);
  371 + }
  372 + } else {
  373 + resultFuture.set(null);
  374 + }
  375 + }
  376 +
  377 + @Override
  378 + public void onFailure(Throwable t) {
  379 + log.warn("[{}] Failed to process remove of the latest value", entityId, t);
  380 + }
336 }); 381 });
  382 + return resultFuture;
  383 + }
  384 +
  385 + private ListenableFuture<Void> getNewLatestEntryFuture(EntityId entityId, DeleteTsKvQuery query) {
  386 + long startTs = 0;
  387 + long endTs = query.getStartTs() - 1;
  388 + ReadTsKvQuery findNewLatestQuery = new BaseReadTsKvQuery(query.getKey(), startTs, endTs, endTs - startTs, 1,
  389 + Aggregation.NONE, DESC_ORDER);
  390 + ListenableFuture<List<TsKvEntry>> future = findAllAsync(entityId, findNewLatestQuery);
  391 +
  392 + return Futures.transformAsync(future, entryList -> {
  393 + if (entryList.size() == 1) {
  394 + return saveLatest(entityId, entryList.get(0));
  395 + } else {
  396 + log.trace("Could not find new latest value for [{}], key - {}", entityId, query.getKey());
  397 + }
  398 + return Futures.immediateFuture(null);
  399 + }, service);
337 } 400 }
338 401
339 @Override 402 @Override
@@ -47,7 +47,7 @@ public interface TsKvRepository extends CrudRepository<TsKvEntity, TsKvComposite @@ -47,7 +47,7 @@ public interface TsKvRepository extends CrudRepository<TsKvEntity, TsKvComposite
47 @Modifying 47 @Modifying
48 @Query("DELETE FROM TsKvEntity tskv WHERE tskv.entityId = :entityId " + 48 @Query("DELETE FROM TsKvEntity tskv WHERE tskv.entityId = :entityId " +
49 "AND tskv.entityType = :entityType AND tskv.key = :entityKey " + 49 "AND tskv.entityType = :entityType AND tskv.key = :entityKey " +
50 - "AND tskv.ts > :startTs AND tskv.ts < :endTs") 50 + "AND tskv.ts > :startTs AND tskv.ts <= :endTs")
51 void delete(@Param("entityId") String entityId, 51 void delete(@Param("entityId") String entityId,
52 @Param("entityType") EntityType entityType, 52 @Param("entityType") EntityType entityType,
53 @Param("entityKey") String key, 53 @Param("entityKey") String key,
@@ -20,11 +20,13 @@ import com.google.common.util.concurrent.Futures; @@ -20,11 +20,13 @@ import com.google.common.util.concurrent.Futures;
20 import com.google.common.util.concurrent.ListenableFuture; 20 import com.google.common.util.concurrent.ListenableFuture;
21 import lombok.extern.slf4j.Slf4j; 21 import lombok.extern.slf4j.Slf4j;
22 import org.springframework.beans.factory.annotation.Autowired; 22 import org.springframework.beans.factory.annotation.Autowired;
  23 +import org.springframework.beans.factory.annotation.Value;
23 import org.springframework.stereotype.Service; 24 import org.springframework.stereotype.Service;
24 import org.thingsboard.server.common.data.EntityType; 25 import org.thingsboard.server.common.data.EntityType;
25 import org.thingsboard.server.common.data.EntityView; 26 import org.thingsboard.server.common.data.EntityView;
26 import org.thingsboard.server.common.data.id.EntityId; 27 import org.thingsboard.server.common.data.id.EntityId;
27 import org.thingsboard.server.common.data.id.EntityViewId; 28 import org.thingsboard.server.common.data.id.EntityViewId;
  29 +import org.thingsboard.server.common.data.kv.Aggregation;
28 import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery; 30 import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery;
29 import org.thingsboard.server.common.data.kv.DeleteTsKvQuery; 31 import org.thingsboard.server.common.data.kv.DeleteTsKvQuery;
30 import org.thingsboard.server.common.data.kv.ReadTsKvQuery; 32 import org.thingsboard.server.common.data.kv.ReadTsKvQuery;
@@ -47,8 +49,11 @@ import static org.apache.commons.lang3.StringUtils.isBlank; @@ -47,8 +49,11 @@ import static org.apache.commons.lang3.StringUtils.isBlank;
47 @Slf4j 49 @Slf4j
48 public class BaseTimeseriesService implements TimeseriesService { 50 public class BaseTimeseriesService implements TimeseriesService {
49 51
50 - public static final int INSERTS_PER_ENTRY = 3;  
51 - public static final int DELETES_PER_ENTRY = INSERTS_PER_ENTRY; 52 + private static final int INSERTS_PER_ENTRY = 3;
  53 + private static final int DELETES_PER_ENTRY = INSERTS_PER_ENTRY;
  54 +
  55 + @Value("${database.ts_max_intervals}")
  56 + private long maxTsIntervals;
52 57
53 @Autowired 58 @Autowired
54 private TimeseriesDao timeseriesDao; 59 private TimeseriesDao timeseriesDao;
@@ -59,7 +64,7 @@ public class BaseTimeseriesService implements TimeseriesService { @@ -59,7 +64,7 @@ public class BaseTimeseriesService implements TimeseriesService {
59 @Override 64 @Override
60 public ListenableFuture<List<TsKvEntry>> findAll(EntityId entityId, List<ReadTsKvQuery> queries) { 65 public ListenableFuture<List<TsKvEntry>> findAll(EntityId entityId, List<ReadTsKvQuery> queries) {
61 validate(entityId); 66 validate(entityId);
62 - queries.forEach(BaseTimeseriesService::validate); 67 + queries.forEach(this::validate);
63 if (entityId.getEntityType().equals(EntityType.ENTITY_VIEW)) { 68 if (entityId.getEntityType().equals(EntityType.ENTITY_VIEW)) {
64 EntityView entityView = entityViewService.findEntityViewById((EntityViewId) entityId); 69 EntityView entityView = entityViewService.findEntityViewById((EntityViewId) entityId);
65 List<ReadTsKvQuery> filteredQueries = 70 List<ReadTsKvQuery> filteredQueries =
@@ -189,7 +194,7 @@ public class BaseTimeseriesService implements TimeseriesService { @@ -189,7 +194,7 @@ public class BaseTimeseriesService implements TimeseriesService {
189 Validator.validateEntityId(entityId, "Incorrect entityId " + entityId); 194 Validator.validateEntityId(entityId, "Incorrect entityId " + entityId);
190 } 195 }
191 196
192 - private static void validate(ReadTsKvQuery query) { 197 + private void validate(ReadTsKvQuery query) {
193 if (query == null) { 198 if (query == null) {
194 throw new IncorrectParameterException("ReadTsKvQuery can't be null"); 199 throw new IncorrectParameterException("ReadTsKvQuery can't be null");
195 } else if (isBlank(query.getKey())) { 200 } else if (isBlank(query.getKey())) {
@@ -197,6 +202,14 @@ public class BaseTimeseriesService implements TimeseriesService { @@ -197,6 +202,14 @@ public class BaseTimeseriesService implements TimeseriesService {
197 } else if (query.getAggregation() == null) { 202 } else if (query.getAggregation() == null) {
198 throw new IncorrectParameterException("Incorrect ReadTsKvQuery. Aggregation can't be empty"); 203 throw new IncorrectParameterException("Incorrect ReadTsKvQuery. Aggregation can't be empty");
199 } 204 }
  205 + if(!Aggregation.NONE.equals(query.getAggregation())) {
  206 + long step = Math.max(query.getInterval(), 1000);
  207 + long intervalCounts = (query.getEndTs() - query.getStartTs()) / step;
  208 + if (intervalCounts > maxTsIntervals || intervalCounts < 0) {
  209 + throw new IncorrectParameterException("Incorrect TsKvQuery. Number of intervals is to high - " + intervalCounts + ". " +
  210 + "Please increase 'interval' parameter for your query or reduce the time range of the query.");
  211 + }
  212 + }
200 } 213 }
201 214
202 private static void validate(DeleteTsKvQuery query) { 215 private static void validate(DeleteTsKvQuery query) {
@@ -48,7 +48,6 @@ import org.thingsboard.server.common.data.kv.StringDataEntry; @@ -48,7 +48,6 @@ import org.thingsboard.server.common.data.kv.StringDataEntry;
48 import org.thingsboard.server.common.data.kv.TsKvEntry; 48 import org.thingsboard.server.common.data.kv.TsKvEntry;
49 import org.thingsboard.server.dao.model.ModelConstants; 49 import org.thingsboard.server.dao.model.ModelConstants;
50 import org.thingsboard.server.dao.nosql.CassandraAbstractAsyncDao; 50 import org.thingsboard.server.dao.nosql.CassandraAbstractAsyncDao;
51 -import org.thingsboard.server.dao.util.NoSqlDao;  
52 import org.thingsboard.server.dao.util.NoSqlTsDao; 51 import org.thingsboard.server.dao.util.NoSqlTsDao;
53 52
54 import javax.annotation.Nullable; 53 import javax.annotation.Nullable;
@@ -62,6 +61,7 @@ import java.util.Arrays; @@ -62,6 +61,7 @@ import java.util.Arrays;
62 import java.util.Collections; 61 import java.util.Collections;
63 import java.util.List; 62 import java.util.List;
64 import java.util.Optional; 63 import java.util.Optional;
  64 +import java.util.concurrent.ExecutionException;
65 import java.util.stream.Collectors; 65 import java.util.stream.Collectors;
66 66
67 import static com.datastax.driver.core.querybuilder.QueryBuilder.eq; 67 import static com.datastax.driver.core.querybuilder.QueryBuilder.eq;
@@ -434,14 +434,14 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem @@ -434,14 +434,14 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem
434 public ListenableFuture<Void> removeLatest(EntityId entityId, DeleteTsKvQuery query) { 434 public ListenableFuture<Void> removeLatest(EntityId entityId, DeleteTsKvQuery query) {
435 ListenableFuture<TsKvEntry> latestEntryFuture = findLatest(entityId, query.getKey()); 435 ListenableFuture<TsKvEntry> latestEntryFuture = findLatest(entityId, query.getKey());
436 436
437 - ListenableFuture<Boolean> booleanFuture = Futures.transformAsync(latestEntryFuture, latestEntry -> { 437 + ListenableFuture<Boolean> booleanFuture = Futures.transform(latestEntryFuture, latestEntry -> {
438 long ts = latestEntry.getTs(); 438 long ts = latestEntry.getTs();
439 - if (ts >= query.getStartTs() && ts <= query.getEndTs()) {  
440 - return Futures.immediateFuture(true); 439 + if (ts > query.getStartTs() && ts <= query.getEndTs()) {
  440 + return true;
441 } else { 441 } else {
442 log.trace("Won't be deleted latest value for [{}], key - {}", entityId, query.getKey()); 442 log.trace("Won't be deleted latest value for [{}], key - {}", entityId, query.getKey());
443 } 443 }
444 - return Futures.immediateFuture(false); 444 + return false;
445 }, readResultsProcessingExecutor); 445 }, readResultsProcessingExecutor);
446 446
447 ListenableFuture<Void> removedLatestFuture = Futures.transformAsync(booleanFuture, isRemove -> { 447 ListenableFuture<Void> removedLatestFuture = Futures.transformAsync(booleanFuture, isRemove -> {
@@ -451,18 +451,34 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem @@ -451,18 +451,34 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem
451 return Futures.immediateFuture(null); 451 return Futures.immediateFuture(null);
452 }, readResultsProcessingExecutor); 452 }, readResultsProcessingExecutor);
453 453
454 - if (query.getRewriteLatestIfDeleted()) {  
455 - ListenableFuture<Void> savedLatestFuture = Futures.transformAsync(booleanFuture, isRemove -> {  
456 - if (isRemove) {  
457 - return getNewLatestEntryFuture(entityId, query); 454 + final SimpleListenableFuture<Void> resultFuture = new SimpleListenableFuture<>();
  455 + Futures.addCallback(removedLatestFuture, new FutureCallback<Void>() {
  456 + @Override
  457 + public void onSuccess(@Nullable Void result) {
  458 + if (query.getRewriteLatestIfDeleted()) {
  459 + ListenableFuture<Void> savedLatestFuture = Futures.transformAsync(booleanFuture, isRemove -> {
  460 + if (isRemove) {
  461 + return getNewLatestEntryFuture(entityId, query);
  462 + }
  463 + return Futures.immediateFuture(null);
  464 + }, readResultsProcessingExecutor);
  465 +
  466 + try {
  467 + resultFuture.set(savedLatestFuture.get());
  468 + } catch (InterruptedException | ExecutionException e) {
  469 + log.warn("Could not get latest saved value for [{}], {}", entityId, query.getKey(), e);
  470 + }
  471 + } else {
  472 + resultFuture.set(null);
458 } 473 }
459 - return Futures.immediateFuture(null);  
460 - }, readResultsProcessingExecutor); 474 + }
461 475
462 - return Futures.transformAsync(Futures.allAsList(Arrays.asList(savedLatestFuture, removedLatestFuture)),  
463 - list -> Futures.immediateFuture(null), readResultsProcessingExecutor);  
464 - }  
465 - return removedLatestFuture; 476 + @Override
  477 + public void onFailure(Throwable t) {
  478 + log.warn("[{}] Failed to process remove of the latest value", entityId, t);
  479 + }
  480 + });
  481 + return resultFuture;
466 } 482 }
467 483
468 private ListenableFuture<Void> getNewLatestEntryFuture(EntityId entityId, DeleteTsKvQuery query) { 484 private ListenableFuture<Void> getNewLatestEntryFuture(EntityId entityId, DeleteTsKvQuery query) {
@@ -152,7 +152,7 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest { @@ -152,7 +152,7 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest {
152 } 152 }
153 153
154 @Test 154 @Test
155 - public void testDeleteDeviceTsData() throws Exception { 155 + public void testDeleteDeviceTsDataWithoutOverwritingLatest() throws Exception {
156 DeviceId deviceId = new DeviceId(UUIDs.timeBased()); 156 DeviceId deviceId = new DeviceId(UUIDs.timeBased());
157 157
158 saveEntries(deviceId, 10000); 158 saveEntries(deviceId, 10000);
@@ -172,6 +172,26 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest { @@ -172,6 +172,26 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest {
172 } 172 }
173 173
174 @Test 174 @Test
  175 + public void testDeleteDeviceTsDataWithOverwritingLatest() throws Exception {
  176 + DeviceId deviceId = new DeviceId(UUIDs.timeBased());
  177 +
  178 + saveEntries(deviceId, 10000);
  179 + saveEntries(deviceId, 20000);
  180 + saveEntries(deviceId, 30000);
  181 + saveEntries(deviceId, 40000);
  182 +
  183 + tsService.remove(deviceId, Collections.singletonList(
  184 + new BaseDeleteTsKvQuery(STRING_KEY, 25000, 45000, true))).get();
  185 +
  186 + List<TsKvEntry> list = tsService.findAll(deviceId, Collections.singletonList(
  187 + new BaseReadTsKvQuery(STRING_KEY, 5000, 45000, 10000, 10, Aggregation.NONE))).get();
  188 + Assert.assertEquals(2, list.size());
  189 +
  190 + List<TsKvEntry> latest = tsService.findLatest(deviceId, Collections.singletonList(STRING_KEY)).get();
  191 + Assert.assertEquals(20000, latest.get(0).getTs());
  192 + }
  193 +
  194 + @Test
175 public void testFindDeviceTsData() throws Exception { 195 public void testFindDeviceTsData() throws Exception {
176 DeviceId deviceId = new DeviceId(UUIDs.timeBased()); 196 DeviceId deviceId = new DeviceId(UUIDs.timeBased());
177 List<TsKvEntry> entries = new ArrayList<>(); 197 List<TsKvEntry> entries = new ArrayList<>();
@@ -35,4 +35,4 @@ redis.connection.port=6379 @@ -35,4 +35,4 @@ redis.connection.port=6379
35 redis.connection.db=0 35 redis.connection.db=0
36 redis.connection.password= 36 redis.connection.password=
37 37
38 -rule.queue.type=memory 38 +database.ts_max_intervals=700
@@ -15,4 +15,4 @@ TB_VERSION=latest @@ -15,4 +15,4 @@ TB_VERSION=latest
15 15
16 DATABASE=postgres 16 DATABASE=postgres
17 17
18 -KAFKA_TOPICS="js.eval.requests:100:1:delete --config=retention.ms=60000 --config=segment.bytes=26214400 --config=retention.bytes=104857600,tb.transport.api.requests:30:1:delete --config=retention.ms=60000 --config=segment.bytes=26214400 --config=retention.bytes=104857600,tb.rule-engine:30:1" 18 +LOAD_BALANCER_NAME=haproxy-certbot
@@ -4,4 +4,5 @@ tb-node/log/** @@ -4,4 +4,5 @@ tb-node/log/**
4 tb-node/db/** 4 tb-node/db/**
5 tb-node/postgres/** 5 tb-node/postgres/**
6 tb-node/cassandra/** 6 tb-node/cassandra/**
  7 +tb-transports/*/log
7 !.env 8 !.env
1 -#!/bin/bash  
2 -#  
3 -# Copyright © 2016-2018 The Thingsboard Authors  
4 -#  
5 -# Licensed under the Apache License, Version 2.0 (the "License");  
6 -# you may not use this file except in compliance with the License.  
7 -# You may obtain a copy of the License at  
8 -#  
9 -# http://www.apache.org/licenses/LICENSE-2.0  
10 -#  
11 -# Unless required by applicable law or agreed to in writing, software  
12 -# distributed under the License is distributed on an "AS IS" BASIS,  
13 -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  
14 -# See the License for the specific language governing permissions and  
15 -# limitations under the License.  
16 -#  
17 -  
18 -dirsArray=("./haproxy/certs.d" "./haproxy/letsencrypt" "./tb-node/postgres" "./tb-node/cassandra" "./tb-node/log/tb1" "./tb-node/log/tb2")  
19 -  
20 -for dir in ${dirsArray[@]}  
21 -do  
22 - if [ ! -d "$dir" ]; then  
23 - echo creating dir $dir  
24 - mkdir -p $dir  
25 - fi  
26 -done  
  1 +#
  2 +# Copyright © 2016-2018 The Thingsboard Authors
  3 +#
  4 +# Licensed under the Apache License, Version 2.0 (the "License");
  5 +# you may not use this file except in compliance with the License.
  6 +# You may obtain a copy of the License at
  7 +#
  8 +# http://www.apache.org/licenses/LICENSE-2.0
  9 +#
  10 +# Unless required by applicable law or agreed to in writing, software
  11 +# distributed under the License is distributed on an "AS IS" BASIS,
  12 +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 +# See the License for the specific language governing permissions and
  14 +# limitations under the License.
  15 +#
  16 +
  17 +version: '2.2'
  18 +
  19 +services:
  20 + postgres:
  21 + volumes:
  22 + - postgres-db-volume:/var/lib/postgresql/data
  23 + tb1:
  24 + volumes:
  25 + - tb-log-volume:/var/log/thingsboard
  26 + tb2:
  27 + volumes:
  28 + - tb-log-volume:/var/log/thingsboard
  29 + tb-coap-transport:
  30 + volumes:
  31 + - tb-coap-transport-log-volume:/var/log/tb-coap-transport
  32 + tb-http-transport1:
  33 + volumes:
  34 + - tb-http-transport-log-volume:/var/log/tb-http-transport
  35 + tb-http-transport2:
  36 + volumes:
  37 + - tb-http-transport-log-volume:/var/log/tb-http-transport
  38 + tb-mqtt-transport1:
  39 + volumes:
  40 + - tb-mqtt-transport-log-volume:/var/log/tb-mqtt-transport
  41 + tb-mqtt-transport2:
  42 + volumes:
  43 + - tb-mqtt-transport-log-volume:/var/log/tb-mqtt-transport
  44 +
  45 +volumes:
  46 + postgres-db-volume:
  47 + external: true
  48 + name: ${POSTGRES_DATA_VOLUME}
  49 + tb-log-volume:
  50 + external: true
  51 + name: ${TB_LOG_VOLUME}
  52 + tb-coap-transport-log-volume:
  53 + external: true
  54 + name: ${TB_COAP_TRANSPORT_LOG_VOLUME}
  55 + tb-http-transport-log-volume:
  56 + external: true
  57 + name: ${TB_HTTP_TRANSPORT_LOG_VOLUME}
  58 + tb-mqtt-transport-log-volume:
  59 + external: true
  60 + name: ${TB_MQTT_TRANSPORT_LOG_VOLUME}
@@ -64,6 +64,7 @@ services: @@ -64,6 +64,7 @@ services:
64 depends_on: 64 depends_on:
65 - kafka 65 - kafka
66 - redis 66 - redis
  67 + - tb-js-executor
67 tb2: 68 tb2:
68 restart: always 69 restart: always
69 image: "${DOCKER_REPO}/${TB_NODE_DOCKER_NAME}:${TB_VERSION}" 70 image: "${DOCKER_REPO}/${TB_NODE_DOCKER_NAME}:${TB_VERSION}"
@@ -84,13 +85,19 @@ services: @@ -84,13 +85,19 @@ services:
84 depends_on: 85 depends_on:
85 - kafka 86 - kafka
86 - redis 87 - redis
  88 + - tb-js-executor
87 tb-mqtt-transport1: 89 tb-mqtt-transport1:
88 restart: always 90 restart: always
89 image: "${DOCKER_REPO}/${MQTT_TRANSPORT_DOCKER_NAME}:${TB_VERSION}" 91 image: "${DOCKER_REPO}/${MQTT_TRANSPORT_DOCKER_NAME}:${TB_VERSION}"
90 ports: 92 ports:
91 - "1883" 93 - "1883"
  94 + environment:
  95 + TB_HOST: tb-mqtt-transport1
92 env_file: 96 env_file:
93 - tb-mqtt-transport.env 97 - tb-mqtt-transport.env
  98 + volumes:
  99 + - ./tb-transports/mqtt/conf:/config
  100 + - ./tb-transports/mqtt/log:/var/log/tb-mqtt-transport
94 depends_on: 101 depends_on:
95 - kafka 102 - kafka
96 tb-mqtt-transport2: 103 tb-mqtt-transport2:
@@ -98,8 +105,13 @@ services: @@ -98,8 +105,13 @@ services:
98 image: "${DOCKER_REPO}/${MQTT_TRANSPORT_DOCKER_NAME}:${TB_VERSION}" 105 image: "${DOCKER_REPO}/${MQTT_TRANSPORT_DOCKER_NAME}:${TB_VERSION}"
99 ports: 106 ports:
100 - "1883" 107 - "1883"
  108 + environment:
  109 + TB_HOST: tb-mqtt-transport2
101 env_file: 110 env_file:
102 - tb-mqtt-transport.env 111 - tb-mqtt-transport.env
  112 + volumes:
  113 + - ./tb-transports/mqtt/conf:/config
  114 + - ./tb-transports/mqtt/log:/var/log/tb-mqtt-transport
103 depends_on: 115 depends_on:
104 - kafka 116 - kafka
105 tb-http-transport1: 117 tb-http-transport1:
@@ -107,8 +119,13 @@ services: @@ -107,8 +119,13 @@ services:
107 image: "${DOCKER_REPO}/${HTTP_TRANSPORT_DOCKER_NAME}:${TB_VERSION}" 119 image: "${DOCKER_REPO}/${HTTP_TRANSPORT_DOCKER_NAME}:${TB_VERSION}"
108 ports: 120 ports:
109 - "8081" 121 - "8081"
  122 + environment:
  123 + TB_HOST: tb-http-transport1
110 env_file: 124 env_file:
111 - tb-http-transport.env 125 - tb-http-transport.env
  126 + volumes:
  127 + - ./tb-transports/http/conf:/config
  128 + - ./tb-transports/http/log:/var/log/tb-http-transport
112 depends_on: 129 depends_on:
113 - kafka 130 - kafka
114 tb-http-transport2: 131 tb-http-transport2:
@@ -116,8 +133,13 @@ services: @@ -116,8 +133,13 @@ services:
116 image: "${DOCKER_REPO}/${HTTP_TRANSPORT_DOCKER_NAME}:${TB_VERSION}" 133 image: "${DOCKER_REPO}/${HTTP_TRANSPORT_DOCKER_NAME}:${TB_VERSION}"
117 ports: 134 ports:
118 - "8081" 135 - "8081"
  136 + environment:
  137 + TB_HOST: tb-http-transport2
119 env_file: 138 env_file:
120 - tb-http-transport.env 139 - tb-http-transport.env
  140 + volumes:
  141 + - ./tb-transports/http/conf:/config
  142 + - ./tb-transports/http/log:/var/log/tb-http-transport
121 depends_on: 143 depends_on:
122 - kafka 144 - kafka
123 tb-coap-transport: 145 tb-coap-transport:
@@ -125,8 +147,13 @@ services: @@ -125,8 +147,13 @@ services:
125 image: "${DOCKER_REPO}/${COAP_TRANSPORT_DOCKER_NAME}:${TB_VERSION}" 147 image: "${DOCKER_REPO}/${COAP_TRANSPORT_DOCKER_NAME}:${TB_VERSION}"
126 ports: 148 ports:
127 - "5683:5683/udp" 149 - "5683:5683/udp"
  150 + environment:
  151 + TB_HOST: tb-coap-transport
128 env_file: 152 env_file:
129 - tb-coap-transport.env 153 - tb-coap-transport.env
  154 + volumes:
  155 + - ./tb-transports/coap/conf:/config
  156 + - ./tb-transports/coap/log:/var/log/tb-coap-transport
130 depends_on: 157 depends_on:
131 - kafka 158 - kafka
132 tb-web-ui1: 159 tb-web-ui1:
@@ -145,7 +172,7 @@ services: @@ -145,7 +172,7 @@ services:
145 - tb-web-ui.env 172 - tb-web-ui.env
146 haproxy: 173 haproxy:
147 restart: always 174 restart: always
148 - container_name: haproxy-certbot 175 + container_name: "${LOAD_BALANCER_NAME}"
149 image: xalauc/haproxy-certbot:1.7.9 176 image: xalauc/haproxy-certbot:1.7.9
150 volumes: 177 volumes:
151 - ./haproxy/config:/config 178 - ./haproxy/config:/config
@@ -153,7 +180,6 @@ services: @@ -153,7 +180,6 @@ services:
153 - ./haproxy/certs.d:/usr/local/etc/haproxy/certs.d 180 - ./haproxy/certs.d:/usr/local/etc/haproxy/certs.d
154 ports: 181 ports:
155 - "80:80" 182 - "80:80"
156 - - "8080"  
157 - "443:443" 183 - "443:443"
158 - "1883:1883" 184 - "1883:1883"
159 - "9999:9999" 185 - "9999:9999"
@@ -163,7 +189,6 @@ services: @@ -163,7 +189,6 @@ services:
163 HTTP_PORT: 80 189 HTTP_PORT: 80
164 HTTPS_PORT: 443 190 HTTPS_PORT: 443
165 MQTT_PORT: 1883 191 MQTT_PORT: 1883
166 - TB_API_PORT: 8080  
167 FORCE_HTTPS_REDIRECT: "false" 192 FORCE_HTTPS_REDIRECT: "false"
168 links: 193 links:
169 - tb1 194 - tb1
@@ -15,8 +15,6 @@ @@ -15,8 +15,6 @@
15 # limitations under the License. 15 # limitations under the License.
16 # 16 #
17 17
18 -./check-dirs.sh  
19 -  
20 while [[ $# -gt 0 ]] 18 while [[ $# -gt 0 ]]
21 do 19 do
22 key="$1" 20 key="$1"
@@ -15,8 +15,6 @@ @@ -15,8 +15,6 @@
15 # limitations under the License. 15 # limitations under the License.
16 # 16 #
17 17
18 -./check-dirs.sh  
19 -  
20 set -e 18 set -e
21 19
22 source compose-utils.sh 20 source compose-utils.sh
@@ -15,8 +15,6 @@ @@ -15,8 +15,6 @@
15 # limitations under the License. 15 # limitations under the License.
16 # 16 #
17 17
18 -./check-dirs.sh  
19 -  
20 for i in "$@" 18 for i in "$@"
21 do 19 do
22 case $i in 20 case $i in
@@ -56,13 +56,20 @@ frontend http-in @@ -56,13 +56,20 @@ frontend http-in
56 56
57 reqadd X-Forwarded-Proto:\ http 57 reqadd X-Forwarded-Proto:\ http
58 58
  59 + acl acl_static path_beg /static/ /index.html
  60 + acl acl_static path /
  61 + acl acl_static_rulenode path_beg /static/rulenode/
  62 +
59 acl transport_http_acl path_beg /api/v1/ 63 acl transport_http_acl path_beg /api/v1/
60 acl letsencrypt_http_acl path_beg /.well-known/acme-challenge/ 64 acl letsencrypt_http_acl path_beg /.well-known/acme-challenge/
  65 +
61 redirect scheme https if !letsencrypt_http_acl !transport_http_acl { env(FORCE_HTTPS_REDIRECT) -m str true } 66 redirect scheme https if !letsencrypt_http_acl !transport_http_acl { env(FORCE_HTTPS_REDIRECT) -m str true }
  67 +
62 use_backend letsencrypt_http if letsencrypt_http_acl 68 use_backend letsencrypt_http if letsencrypt_http_acl
63 use_backend tb-http-backend if transport_http_acl 69 use_backend tb-http-backend if transport_http_acl
  70 + use_backend tb-web-backend if acl_static !acl_static_rulenode
64 71
65 - default_backend tb-web-backend 72 + default_backend tb-api-backend
66 73
67 frontend https_in 74 frontend https_in
68 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 75 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
@@ -72,14 +79,15 @@ frontend https_in @@ -72,14 +79,15 @@ frontend https_in
72 reqadd X-Forwarded-Proto:\ https 79 reqadd X-Forwarded-Proto:\ https
73 80
74 acl transport_http_acl path_beg /api/v1/ 81 acl transport_http_acl path_beg /api/v1/
75 - use_backend tb-http-backend if transport_http_acl  
76 82
77 - default_backend tb-web-backend 83 + acl acl_static path_beg /static/ /index.html
  84 + acl acl_static path /
  85 + acl acl_static_rulenode path_beg /static/rulenode/
78 86
79 -frontend http-api-in  
80 - bind *:${TB_API_PORT} 87 + use_backend tb-http-backend if transport_http_acl
  88 + use_backend tb-web-backend if acl_static !acl_static_rulenode
81 89
82 - default_backend tb-api-backend 90 + default_backend tb-api-backend
83 91
84 backend letsencrypt_http 92 backend letsencrypt_http
85 server letsencrypt_http_srv 127.0.0.1:8080 93 server letsencrypt_http_srv 127.0.0.1:8080
@@ -4,7 +4,7 @@ KAFKA_LISTENERS=INSIDE://:9093,OUTSIDE://:9092 @@ -4,7 +4,7 @@ KAFKA_LISTENERS=INSIDE://:9093,OUTSIDE://:9092
4 KAFKA_ADVERTISED_LISTENERS=INSIDE://:9093,OUTSIDE://kafka:9092 4 KAFKA_ADVERTISED_LISTENERS=INSIDE://:9093,OUTSIDE://kafka:9092
5 KAFKA_LISTENER_SECURITY_PROTOCOL_MAP=INSIDE:PLAINTEXT,OUTSIDE:PLAINTEXT 5 KAFKA_LISTENER_SECURITY_PROTOCOL_MAP=INSIDE:PLAINTEXT,OUTSIDE:PLAINTEXT
6 KAFKA_INTER_BROKER_LISTENER_NAME=INSIDE 6 KAFKA_INTER_BROKER_LISTENER_NAME=INSIDE
7 -KAFKA_CREATE_TOPICS=${KAFKA_TOPICS} 7 +KAFKA_CREATE_TOPICS=js.eval.requests:100:1:delete --config=retention.ms=60000 --config=segment.bytes=26214400 --config=retention.bytes=104857600,tb.transport.api.requests:30:1:delete --config=retention.ms=60000 --config=segment.bytes=26214400 --config=retention.bytes=104857600,tb.rule-engine:30:1
8 KAFKA_AUTO_CREATE_TOPICS_ENABLE=false 8 KAFKA_AUTO_CREATE_TOPICS_ENABLE=false
9 KAFKA_LOG_RETENTION_BYTES=1073741824 9 KAFKA_LOG_RETENTION_BYTES=1073741824
10 KAFKA_LOG_SEGMENT_BYTES=268435456 10 KAFKA_LOG_SEGMENT_BYTES=268435456
@@ -24,7 +24,7 @@ @@ -24,7 +24,7 @@
24 <file>/var/log/thingsboard/${TB_HOST}/thingsboard.log</file> 24 <file>/var/log/thingsboard/${TB_HOST}/thingsboard.log</file>
25 <rollingPolicy 25 <rollingPolicy
26 class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"> 26 class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
27 - <fileNamePattern>/var/log/thingsboard/thingsboard.%d{yyyy-MM-dd}.%i.log</fileNamePattern> 27 + <fileNamePattern>/var/log/thingsboard/${TB_HOST}/thingsboard.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
28 <maxFileSize>100MB</maxFileSize> 28 <maxFileSize>100MB</maxFileSize>
29 <maxHistory>30</maxHistory> 29 <maxHistory>30</maxHistory>
30 <totalSizeCap>3GB</totalSizeCap> 30 <totalSizeCap>3GB</totalSizeCap>
@@ -15,7 +15,7 @@ @@ -15,7 +15,7 @@
15 # 15 #
16 16
17 export JAVA_OPTS="$JAVA_OPTS -Dplatform=deb -Dinstall.data_dir=/usr/share/thingsboard/data" 17 export JAVA_OPTS="$JAVA_OPTS -Dplatform=deb -Dinstall.data_dir=/usr/share/thingsboard/data"
18 -export JAVA_OPTS="$JAVA_OPTS -Xloggc:/var/log/thingsboard/${TB_HOST}/gc.log -XX:+IgnoreUnrecognizedVMOptions -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDetails -XX:+PrintGCDateStamps" 18 +export JAVA_OPTS="$JAVA_OPTS -Xloggc:/var/log/thingsboard/${TB_HOST}/gc.log -XX:+IgnoreUnrecognizedVMOptions -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/thingsboard/${TB_HOST}/heapdump.bin -XX:+PrintGCDetails -XX:+PrintGCDateStamps"
19 export JAVA_OPTS="$JAVA_OPTS -XX:+PrintHeapAtGC -XX:+PrintTenuringDistribution -XX:+PrintGCApplicationStoppedTime -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10" 19 export JAVA_OPTS="$JAVA_OPTS -XX:+PrintHeapAtGC -XX:+PrintTenuringDistribution -XX:+PrintGCApplicationStoppedTime -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10"
20 export JAVA_OPTS="$JAVA_OPTS -XX:GCLogFileSize=10M -XX:-UseBiasedLocking -XX:+UseTLAB -XX:+ResizeTLAB -XX:+PerfDisableSharedMem -XX:+UseCondCardMark" 20 export JAVA_OPTS="$JAVA_OPTS -XX:GCLogFileSize=10M -XX:-UseBiasedLocking -XX:+UseTLAB -XX:+ResizeTLAB -XX:+PerfDisableSharedMem -XX:+UseCondCardMark"
21 export JAVA_OPTS="$JAVA_OPTS -XX:CMSWaitDuration=10000 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+CMSParallelInitialMarkEnabled" 21 export JAVA_OPTS="$JAVA_OPTS -XX:CMSWaitDuration=10000 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+CMSParallelInitialMarkEnabled"
docker/tb-transports/coap/conf/logback.xml renamed from msa/transport/coap/docker/logback.xml
@@ -21,10 +21,10 @@ @@ -21,10 +21,10 @@
21 21
22 <appender name="fileLogAppender" 22 <appender name="fileLogAppender"
23 class="ch.qos.logback.core.rolling.RollingFileAppender"> 23 class="ch.qos.logback.core.rolling.RollingFileAppender">
24 - <file>/var/log/${pkg.name}/${pkg.name}.log</file> 24 + <file>/var/log/tb-coap-transport/${TB_HOST}/tb-coap-transport.log</file>
25 <rollingPolicy 25 <rollingPolicy
26 class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"> 26 class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
27 - <fileNamePattern>/var/log/${pkg.name}/${pkg.name}.%d{yyyy-MM-dd}.%i.log</fileNamePattern> 27 + <fileNamePattern>/var/log/tb-coap-transport/${TB_HOST}/tb-coap-transport.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
28 <maxFileSize>100MB</maxFileSize> 28 <maxFileSize>100MB</maxFileSize>
29 <maxHistory>30</maxHistory> 29 <maxHistory>30</maxHistory>
30 <totalSizeCap>3GB</totalSizeCap> 30 <totalSizeCap>3GB</totalSizeCap>
docker/tb-transports/coap/conf/tb-coap-transport.conf renamed from msa/transport/coap/docker/tb-coap-transport.conf
@@ -14,10 +14,10 @@ @@ -14,10 +14,10 @@
14 # limitations under the License. 14 # limitations under the License.
15 # 15 #
16 16
17 -export JAVA_OPTS="$JAVA_OPTS -Xloggc:@pkg.logFolder@/gc.log -XX:+IgnoreUnrecognizedVMOptions -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDetails -XX:+PrintGCDateStamps" 17 +export JAVA_OPTS="$JAVA_OPTS -Xloggc:/var/log/tb-coap-transport/${TB_HOST}/gc.log -XX:+IgnoreUnrecognizedVMOptions -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/tb-coap-transport/${TB_HOST}/heapdump.bin -XX:+PrintGCDetails -XX:+PrintGCDateStamps"
18 export JAVA_OPTS="$JAVA_OPTS -XX:+PrintHeapAtGC -XX:+PrintTenuringDistribution -XX:+PrintGCApplicationStoppedTime -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10" 18 export JAVA_OPTS="$JAVA_OPTS -XX:+PrintHeapAtGC -XX:+PrintTenuringDistribution -XX:+PrintGCApplicationStoppedTime -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10"
19 export JAVA_OPTS="$JAVA_OPTS -XX:GCLogFileSize=10M -XX:-UseBiasedLocking -XX:+UseTLAB -XX:+ResizeTLAB -XX:+PerfDisableSharedMem -XX:+UseCondCardMark" 19 export JAVA_OPTS="$JAVA_OPTS -XX:GCLogFileSize=10M -XX:-UseBiasedLocking -XX:+UseTLAB -XX:+ResizeTLAB -XX:+PerfDisableSharedMem -XX:+UseCondCardMark"
20 export JAVA_OPTS="$JAVA_OPTS -XX:CMSWaitDuration=10000 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+CMSParallelInitialMarkEnabled" 20 export JAVA_OPTS="$JAVA_OPTS -XX:CMSWaitDuration=10000 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+CMSParallelInitialMarkEnabled"
21 export JAVA_OPTS="$JAVA_OPTS -XX:+CMSEdenChunksRecordAlways -XX:CMSInitiatingOccupancyFraction=75 -XX:+UseCMSInitiatingOccupancyOnly -XX:+ExitOnOutOfMemoryError" 21 export JAVA_OPTS="$JAVA_OPTS -XX:+CMSEdenChunksRecordAlways -XX:CMSInitiatingOccupancyFraction=75 -XX:+UseCMSInitiatingOccupancyOnly -XX:+ExitOnOutOfMemoryError"
22 -export LOG_FILENAME=${pkg.name}.out  
23 -export LOADER_PATH=${pkg.installFolder}/conf 22 +export LOG_FILENAME=tb-coap-transport.out
  23 +export LOADER_PATH=/usr/share/tb-coap-transport/conf
docker/tb-transports/http/conf/logback.xml renamed from msa/transport/http/docker/logback.xml
@@ -21,10 +21,10 @@ @@ -21,10 +21,10 @@
21 21
22 <appender name="fileLogAppender" 22 <appender name="fileLogAppender"
23 class="ch.qos.logback.core.rolling.RollingFileAppender"> 23 class="ch.qos.logback.core.rolling.RollingFileAppender">
24 - <file>/var/log/${pkg.name}/${pkg.name}.log</file> 24 + <file>/var/log/tb-http-transport/${TB_HOST}/tb-http-transport.log</file>
25 <rollingPolicy 25 <rollingPolicy
26 class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"> 26 class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
27 - <fileNamePattern>/var/log/${pkg.name}/${pkg.name}.%d{yyyy-MM-dd}.%i.log</fileNamePattern> 27 + <fileNamePattern>/var/log/tb-http-transport/${TB_HOST}/tb-http-transport.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
28 <maxFileSize>100MB</maxFileSize> 28 <maxFileSize>100MB</maxFileSize>
29 <maxHistory>30</maxHistory> 29 <maxHistory>30</maxHistory>
30 <totalSizeCap>3GB</totalSizeCap> 30 <totalSizeCap>3GB</totalSizeCap>
docker/tb-transports/http/conf/tb-http-transport.conf renamed from msa/transport/http/docker/tb-http-transport.conf
@@ -14,10 +14,10 @@ @@ -14,10 +14,10 @@
14 # limitations under the License. 14 # limitations under the License.
15 # 15 #
16 16
17 -export JAVA_OPTS="$JAVA_OPTS -Xloggc:@pkg.logFolder@/gc.log -XX:+IgnoreUnrecognizedVMOptions -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDetails -XX:+PrintGCDateStamps" 17 +export JAVA_OPTS="$JAVA_OPTS -Xloggc:/var/log/tb-http-transport/${TB_HOST}/gc.log -XX:+IgnoreUnrecognizedVMOptions -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/tb-http-transport/${TB_HOST}/heapdump.bin -XX:+PrintGCDetails -XX:+PrintGCDateStamps"
18 export JAVA_OPTS="$JAVA_OPTS -XX:+PrintHeapAtGC -XX:+PrintTenuringDistribution -XX:+PrintGCApplicationStoppedTime -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10" 18 export JAVA_OPTS="$JAVA_OPTS -XX:+PrintHeapAtGC -XX:+PrintTenuringDistribution -XX:+PrintGCApplicationStoppedTime -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10"
19 export JAVA_OPTS="$JAVA_OPTS -XX:GCLogFileSize=10M -XX:-UseBiasedLocking -XX:+UseTLAB -XX:+ResizeTLAB -XX:+PerfDisableSharedMem -XX:+UseCondCardMark" 19 export JAVA_OPTS="$JAVA_OPTS -XX:GCLogFileSize=10M -XX:-UseBiasedLocking -XX:+UseTLAB -XX:+ResizeTLAB -XX:+PerfDisableSharedMem -XX:+UseCondCardMark"
20 export JAVA_OPTS="$JAVA_OPTS -XX:CMSWaitDuration=10000 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+CMSParallelInitialMarkEnabled" 20 export JAVA_OPTS="$JAVA_OPTS -XX:CMSWaitDuration=10000 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+CMSParallelInitialMarkEnabled"
21 export JAVA_OPTS="$JAVA_OPTS -XX:+CMSEdenChunksRecordAlways -XX:CMSInitiatingOccupancyFraction=75 -XX:+UseCMSInitiatingOccupancyOnly -XX:+ExitOnOutOfMemoryError" 21 export JAVA_OPTS="$JAVA_OPTS -XX:+CMSEdenChunksRecordAlways -XX:CMSInitiatingOccupancyFraction=75 -XX:+UseCMSInitiatingOccupancyOnly -XX:+ExitOnOutOfMemoryError"
22 -export LOG_FILENAME=${pkg.name}.out  
23 -export LOADER_PATH=${pkg.installFolder}/conf 22 +export LOG_FILENAME=tb-http-transport.out
  23 +export LOADER_PATH=/usr/share/tb-http-transport/conf
docker/tb-transports/mqtt/conf/logback.xml renamed from msa/transport/mqtt/docker/logback.xml
@@ -21,10 +21,10 @@ @@ -21,10 +21,10 @@
21 21
22 <appender name="fileLogAppender" 22 <appender name="fileLogAppender"
23 class="ch.qos.logback.core.rolling.RollingFileAppender"> 23 class="ch.qos.logback.core.rolling.RollingFileAppender">
24 - <file>/var/log/${pkg.name}/${pkg.name}.log</file> 24 + <file>/var/log/tb-mqtt-transport/${TB_HOST}/tb-mqtt-transport.log</file>
25 <rollingPolicy 25 <rollingPolicy
26 class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"> 26 class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
27 - <fileNamePattern>/var/log/${pkg.name}/${pkg.name}.%d{yyyy-MM-dd}.%i.log</fileNamePattern> 27 + <fileNamePattern>/var/log/tb-mqtt-transport/${TB_HOST}/tb-mqtt-transport.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
28 <maxFileSize>100MB</maxFileSize> 28 <maxFileSize>100MB</maxFileSize>
29 <maxHistory>30</maxHistory> 29 <maxHistory>30</maxHistory>
30 <totalSizeCap>3GB</totalSizeCap> 30 <totalSizeCap>3GB</totalSizeCap>
docker/tb-transports/mqtt/conf/tb-mqtt-transport.conf renamed from msa/transport/mqtt/docker/tb-mqtt-transport.conf
@@ -14,10 +14,10 @@ @@ -14,10 +14,10 @@
14 # limitations under the License. 14 # limitations under the License.
15 # 15 #
16 16
17 -export JAVA_OPTS="$JAVA_OPTS -Xloggc:@pkg.logFolder@/gc.log -XX:+IgnoreUnrecognizedVMOptions -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDetails -XX:+PrintGCDateStamps" 17 +export JAVA_OPTS="$JAVA_OPTS -Xloggc:/var/log/tb-mqtt-transport/${TB_HOST}/gc.log -XX:+IgnoreUnrecognizedVMOptions -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/tb-mqtt-transport/${TB_HOST}/heapdump.bin -XX:+PrintGCDetails -XX:+PrintGCDateStamps"
18 export JAVA_OPTS="$JAVA_OPTS -XX:+PrintHeapAtGC -XX:+PrintTenuringDistribution -XX:+PrintGCApplicationStoppedTime -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10" 18 export JAVA_OPTS="$JAVA_OPTS -XX:+PrintHeapAtGC -XX:+PrintTenuringDistribution -XX:+PrintGCApplicationStoppedTime -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10"
19 export JAVA_OPTS="$JAVA_OPTS -XX:GCLogFileSize=10M -XX:-UseBiasedLocking -XX:+UseTLAB -XX:+ResizeTLAB -XX:+PerfDisableSharedMem -XX:+UseCondCardMark" 19 export JAVA_OPTS="$JAVA_OPTS -XX:GCLogFileSize=10M -XX:-UseBiasedLocking -XX:+UseTLAB -XX:+ResizeTLAB -XX:+PerfDisableSharedMem -XX:+UseCondCardMark"
20 export JAVA_OPTS="$JAVA_OPTS -XX:CMSWaitDuration=10000 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+CMSParallelInitialMarkEnabled" 20 export JAVA_OPTS="$JAVA_OPTS -XX:CMSWaitDuration=10000 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+CMSParallelInitialMarkEnabled"
21 export JAVA_OPTS="$JAVA_OPTS -XX:+CMSEdenChunksRecordAlways -XX:CMSInitiatingOccupancyFraction=75 -XX:+UseCMSInitiatingOccupancyOnly -XX:+ExitOnOutOfMemoryError" 21 export JAVA_OPTS="$JAVA_OPTS -XX:+CMSEdenChunksRecordAlways -XX:CMSInitiatingOccupancyFraction=75 -XX:+UseCMSInitiatingOccupancyOnly -XX:+ExitOnOutOfMemoryError"
22 -export LOG_FILENAME=${pkg.name}.out  
23 -export LOADER_PATH=${pkg.installFolder}/conf 22 +export LOG_FILENAME=tb-mqtt-transport.out
  23 +export LOADER_PATH=/usr/share/tb-mqtt-transport/conf
1 1
2 HTTP_BIND_ADDRESS=0.0.0.0 2 HTTP_BIND_ADDRESS=0.0.0.0
3 HTTP_BIND_PORT=8080 3 HTTP_BIND_PORT=8080
4 -TB_HOST=haproxy  
5 -TB_PORT=8080 4 +TB_ENABLE_PROXY=false
6 LOGGER_LEVEL=info 5 LOGGER_LEVEL=info
7 LOG_FOLDER=logs 6 LOG_FOLDER=logs
8 LOGGER_FILENAME=tb-web-ui-%DATE%.log 7 LOGGER_FILENAME=tb-web-ui-%DATE%.log
  1 +
  2 +## Black box tests execution
  3 +To run the black box tests with using Docker, the local Docker images of Thingsboard's microservices should be built. <br />
  4 +- Build the local Docker images in the directory with the Thingsboard's main [pom.xml](./../../pom.xml):
  5 +
  6 + mvn clean install -Ddockerfile.skip=false
  7 +- Verify that the new local images were built:
  8 +
  9 + docker image ls
  10 +As result, in REPOSITORY column, next images should be present:
  11 +
  12 + thingsboard/tb-coap-transport
  13 + thingsboard/tb-http-transport
  14 + thingsboard/tb-mqtt-transport
  15 + thingsboard/tb-node
  16 + thingsboard/tb-web-ui
  17 + thingsboard/tb-js-executor
  18 +
  19 +- Run the black box tests in the [msa/black-box-tests](../black-box-tests) directory:
  20 +
  21 + mvn clean install -DblackBoxTests.skip=false
  22 +
  23 +
  1 +<!--
  2 +
  3 + Copyright © 2016-2018 The Thingsboard Authors
  4 +
  5 + Licensed under the Apache License, Version 2.0 (the "License");
  6 + you may not use this file except in compliance with the License.
  7 + You may obtain a copy of the License at
  8 +
  9 + http://www.apache.org/licenses/LICENSE-2.0
  10 +
  11 + Unless required by applicable law or agreed to in writing, software
  12 + distributed under the License is distributed on an "AS IS" BASIS,
  13 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14 + See the License for the specific language governing permissions and
  15 + limitations under the License.
  16 +
  17 +-->
  18 +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  19 + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  20 + <modelVersion>4.0.0</modelVersion>
  21 +
  22 + <parent>
  23 + <groupId>org.thingsboard</groupId>
  24 + <version>2.2.0-SNAPSHOT</version>
  25 + <artifactId>msa</artifactId>
  26 + </parent>
  27 + <groupId>org.thingsboard.msa</groupId>
  28 + <artifactId>black-box-tests</artifactId>
  29 +
  30 + <name>ThingsBoard Black Box Tests</name>
  31 + <url>https://thingsboard.io</url>
  32 + <description>Project for ThingsBoard black box testing with using Docker</description>
  33 +
  34 + <properties>
  35 + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  36 + <main.dir>${basedir}/../..</main.dir>
  37 + <blackBoxTests.skip>true</blackBoxTests.skip>
  38 + <testcontainers.version>1.9.1</testcontainers.version>
  39 + <zeroturnaround.version>1.10</zeroturnaround.version>
  40 + <java-websocket.version>1.3.9</java-websocket.version>
  41 + <httpclient.version>4.5.6</httpclient.version>
  42 + </properties>
  43 +
  44 + <dependencies>
  45 + <dependency>
  46 + <groupId>org.testcontainers</groupId>
  47 + <artifactId>testcontainers</artifactId>
  48 + <version>${testcontainers.version}</version>
  49 + </dependency>
  50 + <dependency>
  51 + <groupId>org.zeroturnaround</groupId>
  52 + <artifactId>zt-exec</artifactId>
  53 + <version>${zeroturnaround.version}</version>
  54 + </dependency>
  55 + <dependency>
  56 + <groupId>org.java-websocket</groupId>
  57 + <artifactId>Java-WebSocket</artifactId>
  58 + <version>${java-websocket.version}</version>
  59 + </dependency>
  60 + <dependency>
  61 + <groupId>org.apache.httpcomponents</groupId>
  62 + <artifactId>httpclient</artifactId>
  63 + <version>${httpclient.version}</version>
  64 + </dependency>
  65 + <dependency>
  66 + <groupId>io.takari.junit</groupId>
  67 + <artifactId>takari-cpsuite</artifactId>
  68 + </dependency>
  69 + <dependency>
  70 + <groupId>ch.qos.logback</groupId>
  71 + <artifactId>logback-classic</artifactId>
  72 + </dependency>
  73 + <dependency>
  74 + <groupId>com.google.code.gson</groupId>
  75 + <artifactId>gson</artifactId>
  76 + </dependency>
  77 + <dependency>
  78 + <groupId>org.apache.commons</groupId>
  79 + <artifactId>commons-lang3</artifactId>
  80 + </dependency>
  81 + <dependency>
  82 + <groupId>com.google.guava</groupId>
  83 + <artifactId>guava</artifactId>
  84 + </dependency>
  85 + <dependency>
  86 + <groupId>org.thingsboard</groupId>
  87 + <artifactId>netty-mqtt</artifactId>
  88 + </dependency>
  89 + <dependency>
  90 + <groupId>org.thingsboard</groupId>
  91 + <artifactId>tools</artifactId>
  92 + </dependency>
  93 + </dependencies>
  94 +
  95 + <build>
  96 + <plugins>
  97 + <plugin>
  98 + <groupId>org.apache.maven.plugins</groupId>
  99 + <artifactId>maven-surefire-plugin</artifactId>
  100 + <configuration>
  101 + <includes>
  102 + <include>**/*TestSuite.java</include>
  103 + </includes>
  104 + <skipTests>${blackBoxTests.skip}</skipTests>
  105 + </configuration>
  106 + </plugin>
  107 + </plugins>
  108 + </build>
  109 +
  110 +</project>
  1 +/**
  2 + * Copyright © 2016-2018 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.msa;
  17 +
  18 +import com.fasterxml.jackson.databind.ObjectMapper;
  19 +import com.google.common.collect.ImmutableMap;
  20 +import com.google.gson.JsonArray;
  21 +import com.google.gson.JsonObject;
  22 +import lombok.extern.slf4j.Slf4j;
  23 +import org.apache.commons.lang3.RandomStringUtils;
  24 +import org.apache.http.config.Registry;
  25 +import org.apache.http.config.RegistryBuilder;
  26 +import org.apache.http.conn.socket.ConnectionSocketFactory;
  27 +import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
  28 +import org.apache.http.conn.ssl.TrustStrategy;
  29 +import org.apache.http.conn.ssl.X509HostnameVerifier;
  30 +import org.apache.http.impl.client.CloseableHttpClient;
  31 +import org.apache.http.impl.client.HttpClients;
  32 +import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
  33 +import org.apache.http.ssl.SSLContextBuilder;
  34 +import org.apache.http.ssl.SSLContexts;
  35 +import org.junit.*;
  36 +import org.junit.rules.TestRule;
  37 +import org.junit.rules.TestWatcher;
  38 +import org.junit.runner.Description;
  39 +import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
  40 +import org.thingsboard.client.tools.RestClient;
  41 +import org.thingsboard.server.common.data.Device;
  42 +import org.thingsboard.server.common.data.EntityType;
  43 +import org.thingsboard.server.common.data.id.DeviceId;
  44 +import org.thingsboard.server.msa.mapper.WsTelemetryResponse;
  45 +
  46 +import javax.net.ssl.*;
  47 +import java.net.URI;
  48 +import java.security.cert.X509Certificate;
  49 +import java.util.List;
  50 +import java.util.Map;
  51 +import java.util.Random;
  52 +
  53 +@Slf4j
  54 +public abstract class AbstractContainerTest {
  55 + protected static final String HTTPS_URL = "https://localhost";
  56 + protected static final String WSS_URL = "wss://localhost";
  57 + protected static RestClient restClient;
  58 + protected ObjectMapper mapper = new ObjectMapper();
  59 +
  60 + @BeforeClass
  61 + public static void before() throws Exception {
  62 + restClient = new RestClient(HTTPS_URL);
  63 + restClient.getRestTemplate().setRequestFactory(getRequestFactoryForSelfSignedCert());
  64 + }
  65 +
  66 + @Rule
  67 + public TestRule watcher = new TestWatcher() {
  68 + protected void starting(Description description) {
  69 + log.info("=================================================");
  70 + log.info("STARTING TEST: {}" , description.getMethodName());
  71 + log.info("=================================================");
  72 + }
  73 +
  74 + /**
  75 + * Invoked when a test succeeds
  76 + */
  77 + protected void succeeded(Description description) {
  78 + log.info("=================================================");
  79 + log.info("SUCCEEDED TEST: {}" , description.getMethodName());
  80 + log.info("=================================================");
  81 + }
  82 +
  83 + /**
  84 + * Invoked when a test fails
  85 + */
  86 + protected void failed(Throwable e, Description description) {
  87 + log.info("=================================================");
  88 + log.info("FAILED TEST: {}" , description.getMethodName(), e);
  89 + log.info("=================================================");
  90 + }
  91 + };
  92 +
  93 + protected Device createDevice(String name) {
  94 + return restClient.createDevice(name + RandomStringUtils.randomAlphanumeric(7), "DEFAULT");
  95 + }
  96 +
  97 + protected WsClient subscribeToWebSocket(DeviceId deviceId, String scope, CmdsType property) throws Exception {
  98 + WsClient wsClient = new WsClient(new URI(WSS_URL + "/api/ws/plugins/telemetry?token=" + restClient.getToken()));
  99 + SSLContextBuilder builder = SSLContexts.custom();
  100 + builder.loadTrustMaterial(null, (TrustStrategy) (chain, authType) -> true);
  101 + wsClient.setSocket(builder.build().getSocketFactory().createSocket());
  102 + wsClient.connectBlocking();
  103 +
  104 + JsonObject cmdsObject = new JsonObject();
  105 + cmdsObject.addProperty("entityType", EntityType.DEVICE.name());
  106 + cmdsObject.addProperty("entityId", deviceId.toString());
  107 + cmdsObject.addProperty("scope", scope);
  108 + cmdsObject.addProperty("cmdId", new Random().nextInt(100));
  109 +
  110 + JsonArray cmd = new JsonArray();
  111 + cmd.add(cmdsObject);
  112 + JsonObject wsRequest = new JsonObject();
  113 + wsRequest.add(property.toString(), cmd);
  114 + wsClient.send(wsRequest.toString());
  115 + wsClient.waitForFirstReply();
  116 + return wsClient;
  117 + }
  118 +
  119 + protected Map<String, Long> getExpectedLatestValues(long ts) {
  120 + return ImmutableMap.<String, Long>builder()
  121 + .put("booleanKey", ts)
  122 + .put("stringKey", ts)
  123 + .put("doubleKey", ts)
  124 + .put("longKey", ts)
  125 + .build();
  126 + }
  127 +
  128 + protected boolean verify(WsTelemetryResponse wsTelemetryResponse, String key, Long expectedTs, String expectedValue) {
  129 + List<Object> list = wsTelemetryResponse.getDataValuesByKey(key);
  130 + return expectedTs.equals(list.get(0)) && expectedValue.equals(list.get(1));
  131 + }
  132 +
  133 + protected boolean verify(WsTelemetryResponse wsTelemetryResponse, String key, String expectedValue) {
  134 + List<Object> list = wsTelemetryResponse.getDataValuesByKey(key);
  135 + return expectedValue.equals(list.get(1));
  136 + }
  137 +
  138 + protected JsonObject createPayload(long ts) {
  139 + JsonObject values = createPayload();
  140 + JsonObject payload = new JsonObject();
  141 + payload.addProperty("ts", ts);
  142 + payload.add("values", values);
  143 + return payload;
  144 + }
  145 +
  146 + protected JsonObject createPayload() {
  147 + JsonObject values = new JsonObject();
  148 + values.addProperty("stringKey", "value1");
  149 + values.addProperty("booleanKey", true);
  150 + values.addProperty("doubleKey", 42.0);
  151 + values.addProperty("longKey", 73L);
  152 +
  153 + return values;
  154 + }
  155 +
  156 + protected enum CmdsType {
  157 + TS_SUB_CMDS("tsSubCmds"),
  158 + HISTORY_CMDS("historyCmds"),
  159 + ATTR_SUB_CMDS("attrSubCmds");
  160 +
  161 + private final String text;
  162 +
  163 + CmdsType(final String text) {
  164 + this.text = text;
  165 + }
  166 +
  167 + @Override
  168 + public String toString() {
  169 + return text;
  170 + }
  171 + }
  172 +
  173 + private static HttpComponentsClientHttpRequestFactory getRequestFactoryForSelfSignedCert() throws Exception {
  174 + SSLContextBuilder builder = SSLContexts.custom();
  175 + builder.loadTrustMaterial(null, (TrustStrategy) (chain, authType) -> true);
  176 + SSLContext sslContext = builder.build();
  177 + SSLConnectionSocketFactory sslSelfSigned = new SSLConnectionSocketFactory(sslContext, new X509HostnameVerifier() {
  178 + @Override
  179 + public void verify(String host, SSLSocket ssl) {
  180 + }
  181 +
  182 + @Override
  183 + public void verify(String host, X509Certificate cert) {
  184 + }
  185 +
  186 + @Override
  187 + public void verify(String host, String[] cns, String[] subjectAlts) {
  188 + }
  189 +
  190 + @Override
  191 + public boolean verify(String s, SSLSession sslSession) {
  192 + return true;
  193 + }
  194 + });
  195 +
  196 + Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder
  197 + .<ConnectionSocketFactory>create()
  198 + .register("https", sslSelfSigned)
  199 + .build();
  200 +
  201 + PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
  202 + CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(cm).build();
  203 + return new HttpComponentsClientHttpRequestFactory(httpClient);
  204 + }
  205 +
  206 +}
  1 +/**
  2 + * Copyright © 2016-2018 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.msa;
  17 +
  18 +import org.junit.ClassRule;
  19 +import org.junit.extensions.cpsuite.ClasspathSuite;
  20 +import org.junit.rules.ExternalResource;
  21 +import org.junit.runner.RunWith;
  22 +import org.testcontainers.containers.DockerComposeContainer;
  23 +import org.testcontainers.containers.wait.strategy.Wait;
  24 +import org.testcontainers.utility.Base58;
  25 +
  26 +import java.io.File;
  27 +import java.time.Duration;
  28 +import java.util.Arrays;
  29 +import java.util.HashMap;
  30 +import java.util.List;
  31 +import java.util.Map;
  32 +
  33 +@RunWith(ClasspathSuite.class)
  34 +@ClasspathSuite.ClassnameFilters({"org.thingsboard.server.msa.*Test"})
  35 +public class ContainerTestSuite {
  36 +
  37 + private static DockerComposeContainer testContainer;
  38 +
  39 + @ClassRule
  40 + public static ThingsBoardDbInstaller installTb = new ThingsBoardDbInstaller();
  41 +
  42 + @ClassRule
  43 + public static DockerComposeContainer getTestContainer() {
  44 + if (testContainer == null) {
  45 + testContainer = new DockerComposeContainer(
  46 + new File("./../../docker/docker-compose.yml"),
  47 + new File("./../../docker/docker-compose.postgres.yml"),
  48 + new File("./../../docker/docker-compose.postgres.volumes.yml"))
  49 + .withPull(false)
  50 + .withLocalCompose(true)
  51 + .withTailChildContainers(true)
  52 + .withEnv(installTb.getEnv())
  53 + .withEnv("LOAD_BALANCER_NAME", "")
  54 + .withExposedService("haproxy", 80, Wait.forHttp("/swagger-ui.html").withStartupTimeout(Duration.ofSeconds(120)));
  55 + }
  56 + return testContainer;
  57 + }
  58 +}
  1 +/**
  2 + * Copyright © 2016-2018 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +
  17 +package org.thingsboard.server.msa;
  18 +
  19 +
  20 +import com.google.common.base.Splitter;
  21 +import com.google.common.collect.Maps;
  22 +import lombok.extern.slf4j.Slf4j;
  23 +import org.apache.commons.lang3.SystemUtils;
  24 +import org.testcontainers.containers.ContainerLaunchException;
  25 +import org.testcontainers.utility.CommandLine;
  26 +import org.zeroturnaround.exec.InvalidExitValueException;
  27 +import org.zeroturnaround.exec.ProcessExecutor;
  28 +import org.zeroturnaround.exec.stream.slf4j.Slf4jStream;
  29 +
  30 +import java.io.File;
  31 +import java.util.HashMap;
  32 +import java.util.List;
  33 +import java.util.Map;
  34 +import java.util.Objects;
  35 +import java.util.stream.Stream;
  36 +
  37 +import static com.google.common.base.Preconditions.checkArgument;
  38 +import static com.google.common.base.Preconditions.checkNotNull;
  39 +import static java.util.stream.Collectors.joining;
  40 +
  41 +@Slf4j
  42 +public class DockerComposeExecutor {
  43 +
  44 + String ENV_PROJECT_NAME = "COMPOSE_PROJECT_NAME";
  45 + String ENV_COMPOSE_FILE = "COMPOSE_FILE";
  46 +
  47 + private static final String COMPOSE_EXECUTABLE = SystemUtils.IS_OS_WINDOWS ? "docker-compose.exe" : "docker-compose";
  48 + private static final String DOCKER_EXECUTABLE = SystemUtils.IS_OS_WINDOWS ? "docker.exe" : "docker";
  49 +
  50 + private final List<File> composeFiles;
  51 + private final String identifier;
  52 + private String cmd = "";
  53 + private Map<String, String> env = new HashMap<>();
  54 +
  55 + public DockerComposeExecutor(List<File> composeFiles, String identifier) {
  56 + validateFileList(composeFiles);
  57 + this.composeFiles = composeFiles;
  58 + this.identifier = identifier;
  59 + }
  60 +
  61 + public DockerComposeExecutor withCommand(String cmd) {
  62 + this.cmd = cmd;
  63 + return this;
  64 + }
  65 +
  66 + public DockerComposeExecutor withEnv(Map<String, String> env) {
  67 + this.env = env;
  68 + return this;
  69 + }
  70 +
  71 + public void invokeCompose() {
  72 + // bail out early
  73 + if (!CommandLine.executableExists(COMPOSE_EXECUTABLE)) {
  74 + throw new ContainerLaunchException("Local Docker Compose not found. Is " + COMPOSE_EXECUTABLE + " on the PATH?");
  75 + }
  76 + final Map<String, String> environment = Maps.newHashMap(env);
  77 + environment.put(ENV_PROJECT_NAME, identifier);
  78 + final Stream<String> absoluteDockerComposeFilePaths = composeFiles.stream().map(File::getAbsolutePath).map(Objects::toString);
  79 + final String composeFileEnvVariableValue = absoluteDockerComposeFilePaths.collect(joining(File.pathSeparator + ""));
  80 + log.debug("Set env COMPOSE_FILE={}", composeFileEnvVariableValue);
  81 + final File pwd = composeFiles.get(0).getAbsoluteFile().getParentFile().getAbsoluteFile();
  82 + environment.put(ENV_COMPOSE_FILE, composeFileEnvVariableValue);
  83 + log.info("Local Docker Compose is running command: {}", cmd);
  84 + final List<String> command = Splitter.onPattern(" ").omitEmptyStrings().splitToList(COMPOSE_EXECUTABLE + " " + cmd);
  85 + try {
  86 + new ProcessExecutor().command(command).redirectOutput(Slf4jStream.of(log).asInfo()).redirectError(Slf4jStream.of(log).asError()).environment(environment).directory(pwd).exitValueNormal().executeNoTimeout();
  87 + log.info("Docker Compose has finished running");
  88 + } catch (InvalidExitValueException e) {
  89 + throw new ContainerLaunchException("Local Docker Compose exited abnormally with code " + e.getExitValue() + " whilst running command: " + cmd);
  90 + } catch (Exception e) {
  91 + throw new ContainerLaunchException("Error running local Docker Compose command: " + cmd, e);
  92 + }
  93 + }
  94 +
  95 + public void invokeDocker() {
  96 + // bail out early
  97 + if (!CommandLine.executableExists(DOCKER_EXECUTABLE)) {
  98 + throw new ContainerLaunchException("Local Docker not found. Is " + DOCKER_EXECUTABLE + " on the PATH?");
  99 + }
  100 + final File pwd = composeFiles.get(0).getAbsoluteFile().getParentFile().getAbsoluteFile();
  101 + log.info("Local Docker is running command: {}", cmd);
  102 + final List<String> command = Splitter.onPattern(" ").omitEmptyStrings().splitToList(DOCKER_EXECUTABLE + " " + cmd);
  103 + try {
  104 + new ProcessExecutor().command(command).redirectOutput(Slf4jStream.of(log).asInfo()).redirectError(Slf4jStream.of(log).asError()).directory(pwd).exitValueNormal().executeNoTimeout();
  105 + log.info("Docker has finished running");
  106 + } catch (InvalidExitValueException e) {
  107 + throw new ContainerLaunchException("Local Docker exited abnormally with code " + e.getExitValue() + " whilst running command: " + cmd);
  108 + } catch (Exception e) {
  109 + throw new ContainerLaunchException("Error running local Docker command: " + cmd, e);
  110 + }
  111 + }
  112 +
  113 + void validateFileList(List<File> composeFiles) {
  114 + checkNotNull(composeFiles);
  115 + checkArgument(!composeFiles.isEmpty(), "No docker compose file have been provided");
  116 + }
  117 +
  118 +
  119 +}
  1 +/**
  2 + * Copyright © 2016-2018 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.msa;
  17 +
  18 +import org.junit.rules.ExternalResource;
  19 +import org.testcontainers.utility.Base58;
  20 +
  21 +import java.io.File;
  22 +import java.util.Arrays;
  23 +import java.util.HashMap;
  24 +import java.util.List;
  25 +import java.util.Map;
  26 +
  27 +public class ThingsBoardDbInstaller extends ExternalResource {
  28 +
  29 + private final static String POSTGRES_DATA_VOLUME = "tb-postgres-test-data-volume";
  30 + private final static String TB_LOG_VOLUME = "tb-log-test-volume";
  31 + private final static String TB_COAP_TRANSPORT_LOG_VOLUME = "tb-coap-transport-log-test-volume";
  32 + private final static String TB_HTTP_TRANSPORT_LOG_VOLUME = "tb-http-transport-log-test-volume";
  33 + private final static String TB_MQTT_TRANSPORT_LOG_VOLUME = "tb-mqtt-transport-log-test-volume";
  34 +
  35 + private final DockerComposeExecutor dockerCompose;
  36 +
  37 + private final String postgresDataVolume;
  38 + private final String tbLogVolume;
  39 + private final String tbCoapTransportLogVolume;
  40 + private final String tbHttpTransportLogVolume;
  41 + private final String tbMqttTransportLogVolume;
  42 + private final Map<String, String> env;
  43 +
  44 + public ThingsBoardDbInstaller() {
  45 + List<File> composeFiles = Arrays.asList(new File("./../../docker/docker-compose.yml"),
  46 + new File("./../../docker/docker-compose.postgres.yml"),
  47 + new File("./../../docker/docker-compose.postgres.volumes.yml"));
  48 +
  49 + String identifier = Base58.randomString(6).toLowerCase();
  50 + String project = identifier + Base58.randomString(6).toLowerCase();
  51 +
  52 + postgresDataVolume = project + "_" + POSTGRES_DATA_VOLUME;
  53 + tbLogVolume = project + "_" + TB_LOG_VOLUME;
  54 + tbCoapTransportLogVolume = project + "_" + TB_COAP_TRANSPORT_LOG_VOLUME;
  55 + tbHttpTransportLogVolume = project + "_" + TB_HTTP_TRANSPORT_LOG_VOLUME;
  56 + tbMqttTransportLogVolume = project + "_" + TB_MQTT_TRANSPORT_LOG_VOLUME;
  57 +
  58 + dockerCompose = new DockerComposeExecutor(composeFiles, project);
  59 +
  60 + env = new HashMap<>();
  61 + env.put("POSTGRES_DATA_VOLUME", postgresDataVolume);
  62 + env.put("TB_LOG_VOLUME", tbLogVolume);
  63 + env.put("TB_COAP_TRANSPORT_LOG_VOLUME", tbCoapTransportLogVolume);
  64 + env.put("TB_HTTP_TRANSPORT_LOG_VOLUME", tbHttpTransportLogVolume);
  65 + env.put("TB_MQTT_TRANSPORT_LOG_VOLUME", tbMqttTransportLogVolume);
  66 + dockerCompose.withEnv(env);
  67 + }
  68 +
  69 + public Map<String, String> getEnv() {
  70 + return env;
  71 + }
  72 +
  73 + @Override
  74 + protected void before() throws Throwable {
  75 + try {
  76 +
  77 + dockerCompose.withCommand("volume create " + postgresDataVolume);
  78 + dockerCompose.invokeDocker();
  79 +
  80 + dockerCompose.withCommand("volume create " + tbLogVolume);
  81 + dockerCompose.invokeDocker();
  82 +
  83 + dockerCompose.withCommand("volume create " + tbCoapTransportLogVolume);
  84 + dockerCompose.invokeDocker();
  85 +
  86 + dockerCompose.withCommand("volume create " + tbHttpTransportLogVolume);
  87 + dockerCompose.invokeDocker();
  88 +
  89 + dockerCompose.withCommand("volume create " + tbMqttTransportLogVolume);
  90 + dockerCompose.invokeDocker();
  91 +
  92 + dockerCompose.withCommand("up -d redis postgres");
  93 + dockerCompose.invokeCompose();
  94 +
  95 + dockerCompose.withCommand("run --no-deps --rm -e INSTALL_TB=true -e LOAD_DEMO=true tb1");
  96 + dockerCompose.invokeCompose();
  97 +
  98 + } finally {
  99 + try {
  100 + dockerCompose.withCommand("down -v");
  101 + dockerCompose.invokeCompose();
  102 + } catch (Exception e) {}
  103 + }
  104 + }
  105 +
  106 + @Override
  107 + protected void after() {
  108 + copyLogs(tbLogVolume, "./target/tb-logs/");
  109 + copyLogs(tbCoapTransportLogVolume, "./target/tb-coap-transport-logs/");
  110 + copyLogs(tbHttpTransportLogVolume, "./target/tb-http-transport-logs/");
  111 + copyLogs(tbMqttTransportLogVolume, "./target/tb-mqtt-transport-logs/");
  112 +
  113 + dockerCompose.withCommand("volume rm -f " + postgresDataVolume + " " + tbLogVolume +
  114 + " " + tbCoapTransportLogVolume + " " + tbHttpTransportLogVolume + " " + tbMqttTransportLogVolume);
  115 + dockerCompose.invokeDocker();
  116 + }
  117 +
  118 + private void copyLogs(String volumeName, String targetDir) {
  119 + File tbLogsDir = new File(targetDir);
  120 + tbLogsDir.mkdirs();
  121 +
  122 + dockerCompose.withCommand("run -d --rm --name tb-logs-container -v " + volumeName + ":/root alpine tail -f /dev/null");
  123 + dockerCompose.invokeDocker();
  124 +
  125 + dockerCompose.withCommand("cp tb-logs-container:/root/. "+tbLogsDir.getAbsolutePath());
  126 + dockerCompose.invokeDocker();
  127 +
  128 + dockerCompose.withCommand("rm -f tb-logs-container");
  129 + dockerCompose.invokeDocker();
  130 + }
  131 +
  132 +}
  1 +/**
  2 + * Copyright © 2016-2018 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.msa;
  17 +
  18 +import com.fasterxml.jackson.databind.ObjectMapper;
  19 +import lombok.extern.slf4j.Slf4j;
  20 +import org.java_websocket.client.WebSocketClient;
  21 +import org.java_websocket.handshake.ServerHandshake;
  22 +import org.thingsboard.server.msa.mapper.WsTelemetryResponse;
  23 +
  24 +import java.io.IOException;
  25 +import java.net.URI;
  26 +import java.util.concurrent.CountDownLatch;
  27 +import java.util.concurrent.TimeUnit;
  28 +
  29 +@Slf4j
  30 +public class WsClient extends WebSocketClient {
  31 + private static final ObjectMapper mapper = new ObjectMapper();
  32 + private WsTelemetryResponse message;
  33 +
  34 + private volatile boolean firstReplyReceived;
  35 + private CountDownLatch firstReply = new CountDownLatch(1);
  36 + private CountDownLatch latch = new CountDownLatch(1);
  37 +
  38 + WsClient(URI serverUri) {
  39 + super(serverUri);
  40 + }
  41 +
  42 + @Override
  43 + public void onOpen(ServerHandshake serverHandshake) {
  44 + }
  45 +
  46 + @Override
  47 + public void onMessage(String message) {
  48 + if (!firstReplyReceived) {
  49 + firstReplyReceived = true;
  50 + firstReply.countDown();
  51 + } else {
  52 + try {
  53 + WsTelemetryResponse response = mapper.readValue(message, WsTelemetryResponse.class);
  54 + if (!response.getData().isEmpty()) {
  55 + this.message = response;
  56 + latch.countDown();
  57 + }
  58 + } catch (IOException e) {
  59 + log.error("ws message can't be read");
  60 + }
  61 + }
  62 + }
  63 +
  64 + @Override
  65 + public void onClose(int code, String reason, boolean remote) {
  66 + log.info("ws is closed, due to [{}]", reason);
  67 + }
  68 +
  69 + @Override
  70 + public void onError(Exception ex) {
  71 + ex.printStackTrace();
  72 + }
  73 +
  74 + public WsTelemetryResponse getLastMessage() {
  75 + try {
  76 + latch.await(10, TimeUnit.SECONDS);
  77 + return this.message;
  78 + } catch (InterruptedException e) {
  79 + log.error("Timeout, ws message wasn't received");
  80 + }
  81 + return null;
  82 + }
  83 +
  84 + void waitForFirstReply() {
  85 + try {
  86 + firstReply.await(10, TimeUnit.SECONDS);
  87 + } catch (InterruptedException e) {
  88 + log.error("Timeout, ws message wasn't received");
  89 + throw new RuntimeException(e);
  90 + }
  91 + }
  92 +}
  1 +/**
  2 + * Copyright © 2016-2018 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.msa.connectivity;
  17 +
  18 +import com.google.common.collect.Sets;
  19 +import org.junit.Assert;
  20 +import org.junit.Test;
  21 +import org.springframework.http.ResponseEntity;
  22 +import org.thingsboard.server.common.data.Device;
  23 +import org.thingsboard.server.common.data.security.DeviceCredentials;
  24 +import org.thingsboard.server.msa.AbstractContainerTest;
  25 +import org.thingsboard.server.msa.WsClient;
  26 +import org.thingsboard.server.msa.mapper.WsTelemetryResponse;
  27 +
  28 +public class HttpClientTest extends AbstractContainerTest {
  29 +
  30 + @Test
  31 + public void telemetryUpload() throws Exception {
  32 + restClient.login("tenant@thingsboard.org", "tenant");
  33 +
  34 + Device device = createDevice("http_");
  35 + DeviceCredentials deviceCredentials = restClient.getCredentials(device.getId());
  36 +
  37 + WsClient wsClient = subscribeToWebSocket(device.getId(), "LATEST_TELEMETRY", CmdsType.TS_SUB_CMDS);
  38 + ResponseEntity deviceTelemetryResponse = restClient.getRestTemplate()
  39 + .postForEntity(HTTPS_URL + "/api/v1/{credentialsId}/telemetry",
  40 + mapper.readTree(createPayload().toString()),
  41 + ResponseEntity.class,
  42 + deviceCredentials.getCredentialsId());
  43 + Assert.assertTrue(deviceTelemetryResponse.getStatusCode().is2xxSuccessful());
  44 + WsTelemetryResponse actualLatestTelemetry = wsClient.getLastMessage();
  45 + wsClient.closeBlocking();
  46 +
  47 + Assert.assertEquals(Sets.newHashSet("booleanKey", "stringKey", "doubleKey", "longKey"),
  48 + actualLatestTelemetry.getLatestValues().keySet());
  49 +
  50 + Assert.assertTrue(verify(actualLatestTelemetry, "booleanKey", Boolean.TRUE.toString()));
  51 + Assert.assertTrue(verify(actualLatestTelemetry, "stringKey", "value1"));
  52 + Assert.assertTrue(verify(actualLatestTelemetry, "doubleKey", Double.toString(42.0)));
  53 + Assert.assertTrue(verify(actualLatestTelemetry, "longKey", Long.toString(73)));
  54 +
  55 + restClient.getRestTemplate().delete(HTTPS_URL + "/api/device/" + device.getId());
  56 + }
  57 +}