Commit 0c54f836b33a5720635b7b1216424d7a14fd7c42

Authored by Andrii Shvaika
1 parent e991c0ef

Fixed tests

Showing 24 changed files with 159 additions and 107 deletions
@@ -42,21 +42,26 @@ import org.thingsboard.server.common.msg.queue.RuleEngineException; @@ -42,21 +42,26 @@ import org.thingsboard.server.common.msg.queue.RuleEngineException;
42 import org.thingsboard.server.common.msg.queue.ServiceType; 42 import org.thingsboard.server.common.msg.queue.ServiceType;
43 import org.thingsboard.server.dao.model.ModelConstants; 43 import org.thingsboard.server.dao.model.ModelConstants;
44 import org.thingsboard.server.dao.tenant.TenantService; 44 import org.thingsboard.server.dao.tenant.TenantService;
  45 +import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper;
45 import scala.concurrent.duration.Duration; 46 import scala.concurrent.duration.Duration;
46 47
  48 +import java.util.HashSet;
47 import java.util.Optional; 49 import java.util.Optional;
  50 +import java.util.Set;
48 51
49 public class AppActor extends ContextAwareActor { 52 public class AppActor extends ContextAwareActor {
50 53
51 private static final TenantId SYSTEM_TENANT = new TenantId(ModelConstants.NULL_UUID); 54 private static final TenantId SYSTEM_TENANT = new TenantId(ModelConstants.NULL_UUID);
52 private final TenantService tenantService; 55 private final TenantService tenantService;
53 private final BiMap<TenantId, ActorRef> tenantActors; 56 private final BiMap<TenantId, ActorRef> tenantActors;
  57 + private final Set<TenantId> deletedTenants;
54 private boolean ruleChainsInitialized; 58 private boolean ruleChainsInitialized;
55 59
56 private AppActor(ActorSystemContext systemContext) { 60 private AppActor(ActorSystemContext systemContext) {
57 super(systemContext); 61 super(systemContext);
58 this.tenantService = systemContext.getTenantService(); 62 this.tenantService = systemContext.getTenantService();
59 this.tenantActors = HashBiMap.create(); 63 this.tenantActors = HashBiMap.create();
  64 + this.deletedTenants = new HashSet<>();
60 } 65 }
61 66
62 @Override 67 @Override
@@ -139,7 +144,11 @@ public class AppActor extends ContextAwareActor { @@ -139,7 +144,11 @@ public class AppActor extends ContextAwareActor {
139 if (SYSTEM_TENANT.equals(msg.getTenantId())) { 144 if (SYSTEM_TENANT.equals(msg.getTenantId())) {
140 msg.getTbMsg().getCallback().onFailure(new RuleEngineException("Message has system tenant id!")); 145 msg.getTbMsg().getCallback().onFailure(new RuleEngineException("Message has system tenant id!"));
141 } else { 146 } else {
142 - getOrCreateTenantActor(msg.getTenantId()).tell(msg, self()); 147 + if (!deletedTenants.contains(msg.getTenantId())) {
  148 + getOrCreateTenantActor(msg.getTenantId()).tell(msg, self());
  149 + } else {
  150 + msg.getTbMsg().getCallback().onSuccess();
  151 + }
143 } 152 }
144 } 153 }
145 154
@@ -154,8 +163,10 @@ public class AppActor extends ContextAwareActor { @@ -154,8 +163,10 @@ public class AppActor extends ContextAwareActor {
154 } else { 163 } else {
155 if (msg.getEntityId().getEntityType() == EntityType.TENANT 164 if (msg.getEntityId().getEntityType() == EntityType.TENANT
156 && msg.getEvent() == ComponentLifecycleEvent.DELETED) { 165 && msg.getEvent() == ComponentLifecycleEvent.DELETED) {
157 - log.debug("[{}] Handling tenant deleted notification: {}", msg.getTenantId(), msg);  
158 - ActorRef tenantActor = tenantActors.remove(new TenantId(msg.getEntityId().getId())); 166 + log.info("[{}] Handling tenant deleted notification: {}", msg.getTenantId(), msg);
  167 + TenantId tenantId = new TenantId(msg.getEntityId().getId());
  168 + deletedTenants.add(tenantId);
  169 + ActorRef tenantActor = tenantActors.get(tenantId);
159 if (tenantActor != null) { 170 if (tenantActor != null) {
160 log.debug("[{}] Deleting tenant actor: {}", msg.getTenantId(), tenantActor); 171 log.debug("[{}] Deleting tenant actor: {}", msg.getTenantId(), tenantActor);
161 context().stop(tenantActor); 172 context().stop(tenantActor);
@@ -172,16 +183,22 @@ public class AppActor extends ContextAwareActor { @@ -172,16 +183,22 @@ public class AppActor extends ContextAwareActor {
172 } 183 }
173 184
174 private void onToDeviceActorMsg(TenantAwareMsg msg) { 185 private void onToDeviceActorMsg(TenantAwareMsg msg) {
175 - getOrCreateTenantActor(msg.getTenantId()).tell(msg, ActorRef.noSender()); 186 + if (!deletedTenants.contains(msg.getTenantId())) {
  187 + getOrCreateTenantActor(msg.getTenantId()).tell(msg, ActorRef.noSender());
  188 + } else {
  189 + if (msg instanceof TransportToDeviceActorMsgWrapper) {
  190 + ((TransportToDeviceActorMsgWrapper) msg).getCallback().onSuccess();
  191 + }
  192 + }
176 } 193 }
177 194
178 private ActorRef getOrCreateTenantActor(TenantId tenantId) { 195 private ActorRef getOrCreateTenantActor(TenantId tenantId) {
179 return tenantActors.computeIfAbsent(tenantId, k -> { 196 return tenantActors.computeIfAbsent(tenantId, k -> {
180 - log.debug("[{}] Creating tenant actor.", tenantId); 197 + log.info("[{}] Creating tenant actor.", tenantId);
181 ActorRef tenantActor = context().actorOf(Props.create(new TenantActor.ActorCreator(systemContext, tenantId)) 198 ActorRef tenantActor = context().actorOf(Props.create(new TenantActor.ActorCreator(systemContext, tenantId))
182 .withDispatcher(DefaultActorService.CORE_DISPATCHER_NAME), tenantId.toString()); 199 .withDispatcher(DefaultActorService.CORE_DISPATCHER_NAME), tenantId.toString());
183 context().watch(tenantActor); 200 context().watch(tenantActor);
184 - log.debug("[{}] Created tenant actor: {}.", tenantId, tenantActor); 201 + log.info("[{}] Created tenant actor: {}.", tenantId, tenantActor);
185 return tenantActor; 202 return tenantActor;
186 }); 203 });
187 } 204 }
@@ -66,6 +66,8 @@ public class TenantActor extends RuleChainManagerActor { @@ -66,6 +66,8 @@ public class TenantActor extends RuleChainManagerActor {
66 return strategy; 66 return strategy;
67 } 67 }
68 68
  69 + boolean cantFindTenant = false;
  70 +
69 @Override 71 @Override
70 public void preStart() { 72 public void preStart() {
71 log.info("[{}] Starting tenant actor.", tenantId); 73 log.info("[{}] Starting tenant actor.", tenantId);
@@ -78,10 +80,14 @@ public class TenantActor extends RuleChainManagerActor { @@ -78,10 +80,14 @@ public class TenantActor extends RuleChainManagerActor {
78 isCore = systemContext.getServiceInfoProvider().isService(ServiceType.TB_CORE); 80 isCore = systemContext.getServiceInfoProvider().isService(ServiceType.TB_CORE);
79 81
80 if (isRuleEngineForCurrentTenant) { 82 if (isRuleEngineForCurrentTenant) {
81 - if (isolatedTenantId.map(id -> id.equals(tenantId)).orElseGet(() -> !tenant.isIsolatedTbRuleEngine())) {  
82 - initRuleChains();  
83 - } else {  
84 - isRuleEngineForCurrentTenant = false; 83 + try {
  84 + if (isolatedTenantId.map(id -> id.equals(tenantId)).orElseGet(() -> !tenant.isIsolatedTbRuleEngine())) {
  85 + initRuleChains();
  86 + } else {
  87 + isRuleEngineForCurrentTenant = false;
  88 + }
  89 + } catch (Exception e) {
  90 + cantFindTenant = true;
85 } 91 }
86 } 92 }
87 log.info("[{}] Tenant actor started.", tenantId); 93 log.info("[{}] Tenant actor started.", tenantId);
@@ -97,6 +103,9 @@ public class TenantActor extends RuleChainManagerActor { @@ -97,6 +103,9 @@ public class TenantActor extends RuleChainManagerActor {
97 103
98 @Override 104 @Override
99 protected boolean process(TbActorMsg msg) { 105 protected boolean process(TbActorMsg msg) {
  106 + if (cantFindTenant) {
  107 + log.info("Missing Tenant msg: {}", msg);
  108 + }
100 switch (msg.getMsgType()) { 109 switch (msg.getMsgType()) {
101 case PARTITION_CHANGE_MSG: 110 case PARTITION_CHANGE_MSG:
102 PartitionChangeMsg partitionChangeMsg = (PartitionChangeMsg) msg; 111 PartitionChangeMsg partitionChangeMsg = (PartitionChangeMsg) msg;
@@ -34,6 +34,7 @@ import org.thingsboard.server.common.data.Device; @@ -34,6 +34,7 @@ import org.thingsboard.server.common.data.Device;
34 import org.thingsboard.server.common.data.EntityType; 34 import org.thingsboard.server.common.data.EntityType;
35 import org.thingsboard.server.common.data.EntityView; 35 import org.thingsboard.server.common.data.EntityView;
36 import org.thingsboard.server.common.data.HasName; 36 import org.thingsboard.server.common.data.HasName;
  37 +import org.thingsboard.server.common.data.HasTenantId;
37 import org.thingsboard.server.common.data.Tenant; 38 import org.thingsboard.server.common.data.Tenant;
38 import org.thingsboard.server.common.data.User; 39 import org.thingsboard.server.common.data.User;
39 import org.thingsboard.server.common.data.alarm.Alarm; 40 import org.thingsboard.server.common.data.alarm.Alarm;
@@ -667,7 +668,13 @@ public abstract class BaseController { @@ -667,7 +668,13 @@ public abstract class BaseController {
667 } 668 }
668 } 669 }
669 TbMsg tbMsg = TbMsg.newMsg(msgType, entityId, metaData, TbMsgDataType.JSON, json.writeValueAsString(entityNode)); 670 TbMsg tbMsg = TbMsg.newMsg(msgType, entityId, metaData, TbMsgDataType.JSON, json.writeValueAsString(entityNode));
670 - tbClusterService.pushMsgToRuleEngine(user.getTenantId(), entityId, tbMsg, null); 671 + TenantId tenantId = user.getTenantId();
  672 + if (tenantId.isNullUid()) {
  673 + if (entity instanceof HasTenantId) {
  674 + tenantId = ((HasTenantId) entity).getTenantId();
  675 + }
  676 + }
  677 + tbClusterService.pushMsgToRuleEngine(tenantId, entityId, tbMsg, null);
671 } catch (Exception e) { 678 } catch (Exception e) {
672 log.warn("[{}] Failed to push entity action to rule engine: {}", entityId, actionType, e); 679 log.warn("[{}] Failed to push entity action to rule engine: {}", entityId, actionType, e);
673 } 680 }
@@ -325,6 +325,8 @@ public class DefaultDeviceStateService implements DeviceStateService { @@ -325,6 +325,8 @@ public class DefaultDeviceStateService implements DeviceStateService {
325 }); 325 });
326 }); 326 });
327 327
  328 + addedPartitions.forEach(tpi -> partitionedDevices.computeIfAbsent(tpi, key -> ConcurrentHashMap.newKeySet()));
  329 +
328 //TODO 3.0: replace this dummy search with new functionality to search by partitions using SQL capabilities. 330 //TODO 3.0: replace this dummy search with new functionality to search by partitions using SQL capabilities.
329 // Adding only devices that are in new partitions 331 // Adding only devices that are in new partitions
330 List<Tenant> tenants = tenantService.findTenants(new TextPageLink(Integer.MAX_VALUE)).getData(); 332 List<Tenant> tenants = tenantService.findTenants(new TextPageLink(Integer.MAX_VALUE)).getData();
@@ -27,6 +27,7 @@ import org.thingsboard.server.common.data.kv.JsonDataEntry; @@ -27,6 +27,7 @@ import org.thingsboard.server.common.data.kv.JsonDataEntry;
27 import org.thingsboard.server.common.data.kv.LongDataEntry; 27 import org.thingsboard.server.common.data.kv.LongDataEntry;
28 import org.thingsboard.server.common.data.kv.TsKvEntry; 28 import org.thingsboard.server.common.data.kv.TsKvEntry;
29 import org.thingsboard.server.dao.asset.AssetService; 29 import org.thingsboard.server.dao.asset.AssetService;
  30 +import org.thingsboard.server.dao.exception.DataValidationException;
30 import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; 31 import org.thingsboard.server.queue.discovery.TbServiceInfoProvider;
31 import org.thingsboard.server.queue.util.TbRuleEngineComponent; 32 import org.thingsboard.server.queue.util.TbRuleEngineComponent;
32 import org.thingsboard.server.service.queue.TbRuleEngineConsumerStats; 33 import org.thingsboard.server.service.queue.TbRuleEngineConsumerStats;
@@ -76,13 +77,19 @@ public class DefaultRuleEngineStatisticsService implements RuleEngineStatisticsS @@ -76,13 +77,19 @@ public class DefaultRuleEngineStatisticsService implements RuleEngineStatisticsS
76 String queueName = ruleEngineStats.getQueueName(); 77 String queueName = ruleEngineStats.getQueueName();
77 ruleEngineStats.getTenantStats().forEach((id, stats) -> { 78 ruleEngineStats.getTenantStats().forEach((id, stats) -> {
78 TenantId tenantId = new TenantId(id); 79 TenantId tenantId = new TenantId(id);
79 - AssetId serviceAssetId = getServiceAssetId(tenantId, queueName);  
80 - if (stats.getTotalMsgCounter().get() > 0) {  
81 - List<TsKvEntry> tsList = stats.getCounters().entrySet().stream()  
82 - .map(kv -> new BasicTsKvEntry(ts, new LongDataEntry(kv.getKey(), (long) kv.getValue().get())))  
83 - .collect(Collectors.toList());  
84 - if (!tsList.isEmpty()) {  
85 - tsService.saveAndNotify(tenantId, serviceAssetId, tsList, CALLBACK); 80 + try {
  81 + AssetId serviceAssetId = getServiceAssetId(tenantId, queueName);
  82 + if (stats.getTotalMsgCounter().get() > 0) {
  83 + List<TsKvEntry> tsList = stats.getCounters().entrySet().stream()
  84 + .map(kv -> new BasicTsKvEntry(ts, new LongDataEntry(kv.getKey(), (long) kv.getValue().get())))
  85 + .collect(Collectors.toList());
  86 + if (!tsList.isEmpty()) {
  87 + tsService.saveAndNotify(tenantId, serviceAssetId, tsList, CALLBACK);
  88 + }
  89 + }
  90 + } catch (DataValidationException e) {
  91 + if (!e.getMessage().equalsIgnoreCase("Asset is referencing to non-existent tenant!")) {
  92 + throw e;
86 } 93 }
87 } 94 }
88 }); 95 });
@@ -27,7 +27,6 @@ @@ -27,7 +27,6 @@
27 27
28 <logger name="org.thingsboard.server" level="INFO" /> 28 <logger name="org.thingsboard.server" level="INFO" />
29 <logger name="akka" level="INFO" /> 29 <logger name="akka" level="INFO" />
30 - <logger name="org.springframework.boot.autoconfigure.logging" level="DEBUG" />  
31 30
32 <!-- <logger name="org.thingsboard.server.service.queue" level="TRACE" />--> 31 <!-- <logger name="org.thingsboard.server.service.queue" level="TRACE" />-->
33 <!-- <logger name="org.thingsboard.server.service.transport" level="TRACE" />--> 32 <!-- <logger name="org.thingsboard.server.service.transport" level="TRACE" />-->
@@ -31,7 +31,7 @@ server: @@ -31,7 +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 + log_controller_error_stack_trace: "${HTTP_LOG_CONTROLLER_ERROR_STACK_TRACE:false}"
35 ws: 35 ws:
36 send_timeout: "${TB_SERVER_WS_SEND_TIMEOUT:5000}" 36 send_timeout: "${TB_SERVER_WS_SEND_TIMEOUT:5000}"
37 limits: 37 limits:
@@ -412,7 +412,7 @@ audit-log: @@ -412,7 +412,7 @@ audit-log:
412 state: 412 state:
413 defaultInactivityTimeoutInSec: "${DEFAULT_INACTIVITY_TIMEOUT:10}" 413 defaultInactivityTimeoutInSec: "${DEFAULT_INACTIVITY_TIMEOUT:10}"
414 defaultStateCheckIntervalInSec: "${DEFAULT_STATE_CHECK_INTERVAL:10}" 414 defaultStateCheckIntervalInSec: "${DEFAULT_STATE_CHECK_INTERVAL:10}"
415 - persistToTelemetry: "${PERSIST_STATE_TO_TELEMETRY:true}" 415 + persistToTelemetry: "${PERSIST_STATE_TO_TELEMETRY:false}"
416 416
417 js: 417 js:
418 evaluator: "${JS_EVALUATOR:local}" # local/remote 418 evaluator: "${JS_EVALUATOR:local}" # local/remote
@@ -513,7 +513,7 @@ swagger: @@ -513,7 +513,7 @@ swagger:
513 version: "${SWAGGER_VERSION:2.0}" 513 version: "${SWAGGER_VERSION:2.0}"
514 514
515 queue: 515 queue:
516 - type: "${TB_QUEUE_TYPE:in-memory}" # kafka or in-memory or aws-sqs or pubsub or service-bus 516 + type: "${TB_QUEUE_TYPE:in-memory}" # in-memory or kafka (Apache Kafka) or aws-sqs (AWS SQS) or pubsub (PubSub) or service-bus (Azure Service Bus) or rabbitmq (RabbitMQ)
517 kafka: 517 kafka:
518 bootstrap.servers: "${TB_KAFKA_SERVERS:localhost:9092}" 518 bootstrap.servers: "${TB_KAFKA_SERVERS:localhost:9092}"
519 acks: "${TB_KAFKA_ACKS:all}" 519 acks: "${TB_KAFKA_ACKS:all}"
@@ -588,7 +588,7 @@ queue: @@ -588,7 +588,7 @@ queue:
588 enabled: "${TB_QUEUE_RULE_ENGINE_STATS_ENABLED:true}" 588 enabled: "${TB_QUEUE_RULE_ENGINE_STATS_ENABLED:true}"
589 print-interval-ms: "${TB_QUEUE_RULE_ENGINE_STATS_PRINT_INTERVAL_MS:10000}" 589 print-interval-ms: "${TB_QUEUE_RULE_ENGINE_STATS_PRINT_INTERVAL_MS:10000}"
590 queues: 590 queues:
591 - - name: "Main" 591 + - name: "${TB_QUEUE_RE_MAIN_QUEUE_NAME:Main}"
592 topic: "${TB_QUEUE_RE_MAIN_TOPIC:tb_rule_engine.main}" 592 topic: "${TB_QUEUE_RE_MAIN_TOPIC:tb_rule_engine.main}"
593 poll-interval: "${TB_QUEUE_RE_MAIN_POLL_INTERVAL_MS:25}" 593 poll-interval: "${TB_QUEUE_RE_MAIN_POLL_INTERVAL_MS:25}"
594 partitions: "${TB_QUEUE_RE_MAIN_PARTITIONS:10}" 594 partitions: "${TB_QUEUE_RE_MAIN_PARTITIONS:10}"
@@ -33,6 +33,7 @@ import org.junit.rules.TestRule; @@ -33,6 +33,7 @@ import org.junit.rules.TestRule;
33 import org.junit.rules.TestWatcher; 33 import org.junit.rules.TestWatcher;
34 import org.junit.runner.Description; 34 import org.junit.runner.Description;
35 import org.junit.runner.RunWith; 35 import org.junit.runner.RunWith;
  36 +import org.mockito.Mockito;
36 import org.springframework.beans.factory.annotation.Autowired; 37 import org.springframework.beans.factory.annotation.Autowired;
37 import org.springframework.boot.test.context.SpringBootContextLoader; 38 import org.springframework.boot.test.context.SpringBootContextLoader;
38 import org.springframework.boot.test.context.SpringBootTest; 39 import org.springframework.boot.test.context.SpringBootTest;
@@ -197,6 +198,7 @@ public abstract class AbstractControllerTest { @@ -197,6 +198,7 @@ public abstract class AbstractControllerTest {
197 createUserAndLogin(customerUser, CUSTOMER_USER_PASSWORD); 198 createUserAndLogin(customerUser, CUSTOMER_USER_PASSWORD);
198 199
199 logout(); 200 logout();
  201 +
200 log.info("Executed setup"); 202 log.info("Executed setup");
201 } 203 }
202 204
@@ -17,6 +17,7 @@ package org.thingsboard.server.controller; @@ -17,6 +17,7 @@ package org.thingsboard.server.controller;
17 17
18 import com.datastax.driver.core.utils.UUIDs; 18 import com.datastax.driver.core.utils.UUIDs;
19 import com.fasterxml.jackson.core.type.TypeReference; 19 import com.fasterxml.jackson.core.type.TypeReference;
  20 +import lombok.extern.slf4j.Slf4j;
20 import org.apache.commons.lang3.RandomStringUtils; 21 import org.apache.commons.lang3.RandomStringUtils;
21 import org.eclipse.paho.client.mqttv3.MqttAsyncClient; 22 import org.eclipse.paho.client.mqttv3.MqttAsyncClient;
22 import org.eclipse.paho.client.mqttv3.MqttConnectOptions; 23 import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
@@ -24,6 +25,7 @@ import org.eclipse.paho.client.mqttv3.MqttMessage; @@ -24,6 +25,7 @@ import org.eclipse.paho.client.mqttv3.MqttMessage;
24 import org.junit.After; 25 import org.junit.After;
25 import org.junit.Assert; 26 import org.junit.Assert;
26 import org.junit.Before; 27 import org.junit.Before;
  28 +import org.junit.Ignore;
27 import org.junit.Test; 29 import org.junit.Test;
28 import org.thingsboard.server.common.data.Customer; 30 import org.thingsboard.server.common.data.Customer;
29 import org.thingsboard.server.common.data.Device; 31 import org.thingsboard.server.common.data.Device;
@@ -46,6 +48,7 @@ import java.util.HashSet; @@ -46,6 +48,7 @@ import java.util.HashSet;
46 import java.util.List; 48 import java.util.List;
47 import java.util.Map; 49 import java.util.Map;
48 import java.util.Set; 50 import java.util.Set;
  51 +import java.util.concurrent.TimeUnit;
49 52
50 import static org.hamcrest.Matchers.containsString; 53 import static org.hamcrest.Matchers.containsString;
51 import static org.junit.Assert.assertEquals; 54 import static org.junit.Assert.assertEquals;
@@ -55,6 +58,7 @@ import static org.junit.Assert.assertTrue; @@ -55,6 +58,7 @@ import static org.junit.Assert.assertTrue;
55 import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; 58 import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
56 import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; 59 import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID;
57 60
  61 +@Slf4j
58 public abstract class BaseEntityViewControllerTest extends AbstractControllerTest { 62 public abstract class BaseEntityViewControllerTest extends AbstractControllerTest {
59 63
60 private IdComparator<EntityView> idComparator; 64 private IdComparator<EntityView> idComparator;
@@ -417,12 +421,22 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes @@ -417,12 +421,22 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
417 MqttConnectOptions options = new MqttConnectOptions(); 421 MqttConnectOptions options = new MqttConnectOptions();
418 options.setUserName(accessToken); 422 options.setUserName(accessToken);
419 client.connect(options); 423 client.connect(options);
420 - Thread.sleep(3000);  
421 - 424 + awaitConnected(client, TimeUnit.SECONDS.toMillis(30));
422 MqttMessage message = new MqttMessage(); 425 MqttMessage message = new MqttMessage();
423 message.setPayload(strKvs.getBytes()); 426 message.setPayload(strKvs.getBytes());
424 client.publish("v1/devices/me/telemetry", message); 427 client.publish("v1/devices/me/telemetry", message);
425 Thread.sleep(1000); 428 Thread.sleep(1000);
  429 +// client.disconnect();
  430 + }
  431 +
  432 + private void awaitConnected(MqttAsyncClient client, long ms) throws InterruptedException {
  433 + long start = System.currentTimeMillis();
  434 + while (!client.isConnected()) {
  435 + Thread.sleep(100);
  436 + if (start + ms < System.currentTimeMillis()) {
  437 + throw new RuntimeException("Client is not connected!");
  438 + }
  439 + }
426 } 440 }
427 441
428 private Set<String> getTelemetryKeys(String type, String id) throws Exception { 442 private Set<String> getTelemetryKeys(String type, String id) throws Exception {
@@ -80,7 +80,7 @@ public abstract class AbstractMqttTelemetryIntegrationTest extends AbstractContr @@ -80,7 +80,7 @@ public abstract class AbstractMqttTelemetryIntegrationTest extends AbstractContr
80 String deviceId = savedDevice.getId().getId().toString(); 80 String deviceId = savedDevice.getId().getId().toString();
81 81
82 Thread.sleep(1000); 82 Thread.sleep(1000);
83 - List<String> actualKeys = doGetAsync("/api/plugins/telemetry/DEVICE/" + deviceId + "/keys/timeseries", List.class); 83 + List<String> actualKeys = doGetAsync("/api/plugins/telemetry/DEVICE/" + deviceId + "/keys/timeseries", List.class);
84 Set<String> actualKeySet = new HashSet<>(actualKeys); 84 Set<String> actualKeySet = new HashSet<>(actualKeys);
85 85
86 List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4"); 86 List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4");
@@ -88,7 +88,7 @@ public abstract class AbstractMqttTelemetryIntegrationTest extends AbstractContr @@ -88,7 +88,7 @@ public abstract class AbstractMqttTelemetryIntegrationTest extends AbstractContr
88 88
89 assertEquals(expectedKeySet, actualKeySet); 89 assertEquals(expectedKeySet, actualKeySet);
90 90
91 - String getTelemetryValuesUrl = "/api/plugins/telemetry/DEVICE/" + deviceId + "/values/timeseries?keys=" + String.join(",", actualKeySet); 91 + String getTelemetryValuesUrl = "/api/plugins/telemetry/DEVICE/" + deviceId + "/values/timeseries?keys=" + String.join(",", actualKeySet);
92 Map<String, List<Map<String, String>>> values = doGetAsync(getTelemetryValuesUrl, Map.class); 92 Map<String, List<Map<String, String>>> values = doGetAsync(getTelemetryValuesUrl, Map.class);
93 93
94 assertEquals("value1", values.get("key1").get(0).get("value")); 94 assertEquals("value1", values.get("key1").get(0).get("value"));
@@ -104,13 +104,17 @@ public abstract class AbstractMqttTelemetryIntegrationTest extends AbstractContr @@ -104,13 +104,17 @@ public abstract class AbstractMqttTelemetryIntegrationTest extends AbstractContr
104 104
105 MqttConnectOptions options = new MqttConnectOptions(); 105 MqttConnectOptions options = new MqttConnectOptions();
106 options.setUserName(accessToken); 106 options.setUserName(accessToken);
107 - client.connect(options).waitForCompletion(3000);  
108 CountDownLatch latch = new CountDownLatch(1); 107 CountDownLatch latch = new CountDownLatch(1);
109 TestMqttCallback callback = new TestMqttCallback(client, latch); 108 TestMqttCallback callback = new TestMqttCallback(client, latch);
110 client.setCallback(callback); 109 client.setCallback(callback);
  110 + client.connect(options).waitForCompletion(3000);
111 client.subscribe("v1/devices/me/attributes", MqttQoS.AT_MOST_ONCE.value()); 111 client.subscribe("v1/devices/me/attributes", MqttQoS.AT_MOST_ONCE.value());
112 String payload = "{\"key\":\"value\"}"; 112 String payload = "{\"key\":\"value\"}";
113 - String result = doPostAsync("/api/plugins/telemetry/" + savedDevice.getId() + "/SHARED_SCOPE", payload, String.class, status().isOk()); 113 +// TODO 3.1: we need to acknowledge subscription only after it is processed by device actor and not when the message is pushed to queue.
  114 +// MqttClient -> SUB REQUEST -> Transport -> Kafka -> Device Actor (subscribed)
  115 +// MqttClient <- SUB_ACK <- Transport
  116 + Thread.sleep(1000);
  117 + doPostAsync("/api/plugins/telemetry/" + savedDevice.getId() + "/SHARED_SCOPE", payload, String.class, status().isOk());
114 latch.await(10, TimeUnit.SECONDS); 118 latch.await(10, TimeUnit.SECONDS);
115 assertEquals(payload, callback.getPayload()); 119 assertEquals(payload, callback.getPayload());
116 assertEquals(MqttQoS.AT_MOST_ONCE.value(), callback.getQoS()); 120 assertEquals(MqttQoS.AT_MOST_ONCE.value(), callback.getQoS());
@@ -120,8 +124,8 @@ public abstract class AbstractMqttTelemetryIntegrationTest extends AbstractContr @@ -120,8 +124,8 @@ public abstract class AbstractMqttTelemetryIntegrationTest extends AbstractContr
120 124
121 private final MqttAsyncClient client; 125 private final MqttAsyncClient client;
122 private final CountDownLatch latch; 126 private final CountDownLatch latch;
123 - private Integer qoS;  
124 - private String payload; 127 + private volatile Integer qoS;
  128 + private volatile String payload;
125 129
126 String getPayload() { 130 String getPayload() {
127 return payload; 131 return payload;
@@ -138,6 +142,7 @@ public abstract class AbstractMqttTelemetryIntegrationTest extends AbstractContr @@ -138,6 +142,7 @@ public abstract class AbstractMqttTelemetryIntegrationTest extends AbstractContr
138 142
139 @Override 143 @Override
140 public void connectionLost(Throwable throwable) { 144 public void connectionLost(Throwable throwable) {
  145 + log.error("Client connection lost", throwable);
141 } 146 }
142 147
143 @Override 148 @Override
@@ -15,14 +15,17 @@ @@ -15,14 +15,17 @@
15 */ 15 */
16 package org.thingsboard.server.rules.flow; 16 package org.thingsboard.server.rules.flow;
17 17
  18 +import akka.actor.ActorRef;
18 import com.datastax.driver.core.utils.UUIDs; 19 import com.datastax.driver.core.utils.UUIDs;
19 import lombok.extern.slf4j.Slf4j; 20 import lombok.extern.slf4j.Slf4j;
20 import org.junit.After; 21 import org.junit.After;
21 import org.junit.Assert; 22 import org.junit.Assert;
22 import org.junit.Before; 23 import org.junit.Before;
23 import org.junit.Test; 24 import org.junit.Test;
  25 +import org.mockito.Mockito;
24 import org.springframework.beans.factory.annotation.Autowired; 26 import org.springframework.beans.factory.annotation.Autowired;
25 import org.thingsboard.rule.engine.metadata.TbGetAttributesNodeConfiguration; 27 import org.thingsboard.rule.engine.metadata.TbGetAttributesNodeConfiguration;
  28 +import org.thingsboard.server.actors.ActorSystemContext;
26 import org.thingsboard.server.actors.service.ActorService; 29 import org.thingsboard.server.actors.service.ActorService;
27 import org.thingsboard.server.common.data.*; 30 import org.thingsboard.server.common.data.*;
28 import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; 31 import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry;
@@ -35,6 +38,8 @@ import org.thingsboard.server.common.data.security.Authority; @@ -35,6 +38,8 @@ import org.thingsboard.server.common.data.security.Authority;
35 import org.thingsboard.server.common.msg.TbMsg; 38 import org.thingsboard.server.common.msg.TbMsg;
36 import org.thingsboard.server.common.msg.TbMsgDataType; 39 import org.thingsboard.server.common.msg.TbMsgDataType;
37 import org.thingsboard.server.common.msg.TbMsgMetaData; 40 import org.thingsboard.server.common.msg.TbMsgMetaData;
  41 +import org.thingsboard.server.common.msg.queue.QueueToRuleEngineMsg;
  42 +import org.thingsboard.server.common.msg.queue.TbMsgCallback;
38 import org.thingsboard.server.controller.AbstractRuleEngineControllerTest; 43 import org.thingsboard.server.controller.AbstractRuleEngineControllerTest;
39 import org.thingsboard.server.dao.attributes.AttributesService; 44 import org.thingsboard.server.dao.attributes.AttributesService;
40 45
@@ -55,7 +60,7 @@ public abstract class AbstractRuleEngineFlowIntegrationTest extends AbstractRule @@ -55,7 +60,7 @@ public abstract class AbstractRuleEngineFlowIntegrationTest extends AbstractRule
55 protected User tenantAdmin; 60 protected User tenantAdmin;
56 61
57 @Autowired 62 @Autowired
58 - protected ActorService actorService; 63 + protected ActorSystemContext actorSystem;
59 64
60 @Autowired 65 @Autowired
61 protected AttributesService attributesService; 66 protected AttributesService attributesService;
@@ -142,15 +147,12 @@ public abstract class AbstractRuleEngineFlowIntegrationTest extends AbstractRule @@ -142,15 +147,12 @@ public abstract class AbstractRuleEngineFlowIntegrationTest extends AbstractRule
142 147
143 Thread.sleep(1000); 148 Thread.sleep(1000);
144 149
  150 + TbMsgCallback tbMsgCallback = Mockito.mock(TbMsgCallback.class);
  151 + TbMsg tbMsg = TbMsg.newMsg("CUSTOM", device.getId(), new TbMsgMetaData(), "{}", tbMsgCallback);
  152 + QueueToRuleEngineMsg qMsg = new QueueToRuleEngineMsg(savedTenant.getId(), tbMsg, null, null);
145 // Pushing Message to the system 153 // Pushing Message to the system
146 - TbMsg tbMsg = TbMsg.newMsg(  
147 - "CUSTOM",  
148 - device.getId(),  
149 - new TbMsgMetaData(), TbMsgDataType.JSON, "{}");  
150 - //TODO 2.5  
151 -// actorService.onMsg(new SendToClusterMsg(device.getId(), new QueueToRuleEngineMsg(savedTenant.getId(), tbMsg)));  
152 -  
153 - Thread.sleep(3000); 154 + actorSystem.tell(qMsg, ActorRef.noSender());
  155 + Mockito.verify(tbMsgCallback, Mockito.timeout(3000)).onSuccess();
154 156
155 TimePageData<Event> eventsPage = getDebugEvents(savedTenant.getId(), ruleChain.getFirstRuleNodeId(), 1000); 157 TimePageData<Event> eventsPage = getDebugEvents(savedTenant.getId(), ruleChain.getFirstRuleNodeId(), 1000);
156 List<Event> events = eventsPage.getData().stream().filter(filterByCustomEvent()).collect(Collectors.toList()); 158 List<Event> events = eventsPage.getData().stream().filter(filterByCustomEvent()).collect(Collectors.toList());
@@ -257,17 +259,13 @@ public abstract class AbstractRuleEngineFlowIntegrationTest extends AbstractRule @@ -257,17 +259,13 @@ public abstract class AbstractRuleEngineFlowIntegrationTest extends AbstractRule
257 259
258 Thread.sleep(1000); 260 Thread.sleep(1000);
259 261
  262 + TbMsgCallback tbMsgCallback = Mockito.mock(TbMsgCallback.class);
  263 + TbMsg tbMsg = TbMsg.newMsg("CUSTOM", device.getId(), new TbMsgMetaData(), "{}", tbMsgCallback);
  264 + QueueToRuleEngineMsg qMsg = new QueueToRuleEngineMsg(savedTenant.getId(), tbMsg, null, null);
260 // Pushing Message to the system 265 // Pushing Message to the system
261 - TbMsg tbMsg = TbMsg.newMsg(  
262 - "CUSTOM",  
263 - device.getId(),  
264 - new TbMsgMetaData(),  
265 - TbMsgDataType.JSON,  
266 - "{}");  
267 - //TODO 2.5  
268 -// actorService.onMsg(new SendToClusterMsg(device.getId(), new QueueToRuleEngineMsg(savedTenant.getId(), tbMsg)));  
269 -  
270 - Thread.sleep(3000); 266 + actorSystem.tell(qMsg, ActorRef.noSender());
  267 +
  268 + Mockito.verify(tbMsgCallback, Mockito.timeout(3000)).onSuccess();
271 269
272 TimePageData<Event> eventsPage = getDebugEvents(savedTenant.getId(), rootRuleChain.getFirstRuleNodeId(), 1000); 270 TimePageData<Event> eventsPage = getDebugEvents(savedTenant.getId(), rootRuleChain.getFirstRuleNodeId(), 1000);
273 List<Event> events = eventsPage.getData().stream().filter(filterByCustomEvent()).collect(Collectors.toList()); 271 List<Event> events = eventsPage.getData().stream().filter(filterByCustomEvent()).collect(Collectors.toList());
@@ -15,14 +15,17 @@ @@ -15,14 +15,17 @@
15 */ 15 */
16 package org.thingsboard.server.rules.lifecycle; 16 package org.thingsboard.server.rules.lifecycle;
17 17
  18 +import akka.actor.ActorRef;
18 import com.datastax.driver.core.utils.UUIDs; 19 import com.datastax.driver.core.utils.UUIDs;
19 import lombok.extern.slf4j.Slf4j; 20 import lombok.extern.slf4j.Slf4j;
20 import org.junit.After; 21 import org.junit.After;
21 import org.junit.Assert; 22 import org.junit.Assert;
22 import org.junit.Before; 23 import org.junit.Before;
23 import org.junit.Test; 24 import org.junit.Test;
  25 +import org.mockito.Mockito;
24 import org.springframework.beans.factory.annotation.Autowired; 26 import org.springframework.beans.factory.annotation.Autowired;
25 import org.thingsboard.rule.engine.metadata.TbGetAttributesNodeConfiguration; 27 import org.thingsboard.rule.engine.metadata.TbGetAttributesNodeConfiguration;
  28 +import org.thingsboard.server.actors.ActorSystemContext;
26 import org.thingsboard.server.actors.service.ActorService; 29 import org.thingsboard.server.actors.service.ActorService;
27 import org.thingsboard.server.common.data.DataConstants; 30 import org.thingsboard.server.common.data.DataConstants;
28 import org.thingsboard.server.common.data.Device; 31 import org.thingsboard.server.common.data.Device;
@@ -39,6 +42,8 @@ import org.thingsboard.server.common.data.security.Authority; @@ -39,6 +42,8 @@ import org.thingsboard.server.common.data.security.Authority;
39 import org.thingsboard.server.common.msg.TbMsg; 42 import org.thingsboard.server.common.msg.TbMsg;
40 import org.thingsboard.server.common.msg.TbMsgDataType; 43 import org.thingsboard.server.common.msg.TbMsgDataType;
41 import org.thingsboard.server.common.msg.TbMsgMetaData; 44 import org.thingsboard.server.common.msg.TbMsgMetaData;
  45 +import org.thingsboard.server.common.msg.queue.QueueToRuleEngineMsg;
  46 +import org.thingsboard.server.common.msg.queue.TbMsgCallback;
42 import org.thingsboard.server.controller.AbstractRuleEngineControllerTest; 47 import org.thingsboard.server.controller.AbstractRuleEngineControllerTest;
43 import org.thingsboard.server.dao.attributes.AttributesService; 48 import org.thingsboard.server.dao.attributes.AttributesService;
44 49
@@ -58,7 +63,7 @@ public abstract class AbstractRuleEngineLifecycleIntegrationTest extends Abstrac @@ -58,7 +63,7 @@ public abstract class AbstractRuleEngineLifecycleIntegrationTest extends Abstrac
58 protected User tenantAdmin; 63 protected User tenantAdmin;
59 64
60 @Autowired 65 @Autowired
61 - protected ActorService actorService; 66 + protected ActorSystemContext actorSystem;
62 67
63 @Autowired 68 @Autowired
64 protected AttributesService attributesService; 69 protected AttributesService attributesService;
@@ -133,17 +138,13 @@ public abstract class AbstractRuleEngineLifecycleIntegrationTest extends Abstrac @@ -133,17 +138,13 @@ public abstract class AbstractRuleEngineLifecycleIntegrationTest extends Abstrac
133 138
134 Thread.sleep(1000); 139 Thread.sleep(1000);
135 140
  141 + TbMsgCallback tbMsgCallback = Mockito.mock(TbMsgCallback.class);
  142 + TbMsg tbMsg = TbMsg.newMsg("CUSTOM", device.getId(), new TbMsgMetaData(), "{}", tbMsgCallback);
  143 + QueueToRuleEngineMsg qMsg = new QueueToRuleEngineMsg(savedTenant.getId(), tbMsg, null, null);
136 // Pushing Message to the system 144 // Pushing Message to the system
137 - TbMsg tbMsg = TbMsg.newMsg(  
138 - "CUSTOM",  
139 - device.getId(),  
140 - new TbMsgMetaData(),  
141 - TbMsgDataType.JSON,  
142 - "{}");  
143 - //TODO 2.5  
144 -// actorService.onMsg(new SendToClusterMsg(device.getId(), new QueueToRuleEngineMsg(savedTenant.getId(), tbMsg)));  
145 -  
146 - Thread.sleep(3000); 145 + actorSystem.tell(qMsg, ActorRef.noSender());
  146 + Mockito.verify(tbMsgCallback, Mockito.timeout(3000)).onSuccess();
  147 +
147 148
148 TimePageData<Event> eventsPage = getDebugEvents(savedTenant.getId(), ruleChain.getFirstRuleNodeId(), 1000); 149 TimePageData<Event> eventsPage = getDebugEvents(savedTenant.getId(), ruleChain.getFirstRuleNodeId(), 1000);
149 List<Event> events = eventsPage.getData().stream().filter(filterByCustomEvent()).collect(Collectors.toList()); 150 List<Event> events = eventsPage.getData().stream().filter(filterByCustomEvent()).collect(Collectors.toList());
@@ -7,7 +7,7 @@ @@ -7,7 +7,7 @@
7 </encoder> 7 </encoder>
8 </appender> 8 </appender>
9 9
10 - <logger name="org.thingsboard.server" level="WARN"/> 10 + <logger name="org.thingsboard.server" level="INFO"/>
11 <logger name="org.springframework" level="WARN"/> 11 <logger name="org.springframework" level="WARN"/>
12 <logger name="org.springframework.boot.test" level="WARN"/> 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"/>
@@ -65,6 +65,10 @@ public final class TbMsg implements Serializable { @@ -65,6 +65,10 @@ public final class TbMsg implements Serializable {
65 return new TbMsg(UUID.randomUUID(), type, originator, metaData.copy(), dataType, data, ruleChainId, ruleNodeId, TbMsgCallback.EMPTY); 65 return new TbMsg(UUID.randomUUID(), type, originator, metaData.copy(), dataType, data, ruleChainId, ruleNodeId, TbMsgCallback.EMPTY);
66 } 66 }
67 67
  68 + public static TbMsg newMsg(String type, EntityId originator, TbMsgMetaData metaData, String data, TbMsgCallback callback) {
  69 + return new TbMsg(UUID.randomUUID(), type, originator, metaData.copy(), TbMsgDataType.JSON, data, null, null, callback);
  70 + }
  71 +
68 public static TbMsg transformMsg(TbMsg origMsg, String type, EntityId originator, TbMsgMetaData metaData, String data) { 72 public static TbMsg transformMsg(TbMsg origMsg, String type, EntityId originator, TbMsgMetaData metaData, String data) {
69 return new TbMsg(origMsg.getId(), type, originator, metaData.copy(), origMsg.getDataType(), 73 return new TbMsg(origMsg.getId(), type, originator, metaData.copy(), origMsg.getDataType(),
70 data, origMsg.getRuleChainId(), origMsg.getRuleNodeId(), origMsg.getCallback()); 74 data, origMsg.getRuleChainId(), origMsg.getRuleNodeId(), origMsg.getCallback());
@@ -95,14 +95,13 @@ public class DefaultTbQueueRequestTemplate<Request extends TbQueueMsg, Response @@ -95,14 +95,13 @@ public class DefaultTbQueueRequestTemplate<Request extends TbQueueMsg, Response
95 continue; 95 continue;
96 } 96 }
97 responses.forEach(response -> { 97 responses.forEach(response -> {
98 - log.trace("Received response to Queue Template request: {}", response);  
99 byte[] requestIdHeader = response.getHeaders().get(REQUEST_ID_HEADER); 98 byte[] requestIdHeader = response.getHeaders().get(REQUEST_ID_HEADER);
100 UUID requestId; 99 UUID requestId;
101 if (requestIdHeader == null) { 100 if (requestIdHeader == null) {
102 log.error("[{}] Missing requestId in header and body", response); 101 log.error("[{}] Missing requestId in header and body", response);
103 } else { 102 } else {
104 requestId = bytesToUuid(requestIdHeader); 103 requestId = bytesToUuid(requestIdHeader);
105 - log.trace("[{}] Response received", requestId); 104 + log.trace("[{}] Response received: {}", requestId, response);
106 ResponseMetaData<Response> expectedResponse = pendingRequests.remove(requestId); 105 ResponseMetaData<Response> expectedResponse = pendingRequests.remove(requestId);
107 if (expectedResponse == null) { 106 if (expectedResponse == null) {
108 log.trace("[{}] Invalid or stale request", requestId); 107 log.trace("[{}] Invalid or stale request", requestId);
@@ -41,7 +41,7 @@ public class TBKafkaAdmin implements TbQueueAdmin { @@ -41,7 +41,7 @@ public class TBKafkaAdmin implements TbQueueAdmin {
41 client = AdminClient.create(settings.toProps()); 41 client = AdminClient.create(settings.toProps());
42 } 42 }
43 43
44 - //TODO 2.5 44 + //TODO 2.5 - ybondarenko Need to pass not only settings but also properties for topic creation. Somewhere in thingsboard.yml, in KV format.
45 @Override 45 @Override
46 public void createTopicIfNotExists(String topic) { 46 public void createTopicIfNotExists(String topic) {
47 try { 47 try {
@@ -57,29 +57,6 @@ public class TBKafkaAdmin implements TbQueueAdmin { @@ -57,29 +57,6 @@ public class TBKafkaAdmin implements TbQueueAdmin {
57 log.warn("[{}] Failed to create topic", topic, e); 57 log.warn("[{}] Failed to create topic", topic, e);
58 throw new RuntimeException(e); 58 throw new RuntimeException(e);
59 } 59 }
60 -//  
61 -// KafkaFuture<TopicDescription> topicDescriptionFuture = client.describeTopics(Collections.singleton(topic)).values().get(topic);  
62 -//  
63 -// ListenableFuture<TopicDescription> topicFuture = JdkFutureAdapters.listenInPoolThread(topicDescriptionFuture);  
64 -//  
65 -// return Futures.transformAsync(topicFuture, topicDescription -> {  
66 -// KafkaFuture<Void> resultFuture = createTopic(new NewTopic(topic, 1, (short) 1)).values().get(topic);  
67 -// return JdkFutureAdapters.listenInPoolThread(resultFuture);  
68 -// });  
69 - }  
70 -  
71 - public void waitForTopic(String topic, long timeout, TimeUnit timeoutUnit) throws InterruptedException, TimeoutException {  
72 - synchronized (this) {  
73 - long timeoutExpiredMs = System.currentTimeMillis() + timeoutUnit.toMillis(timeout);  
74 - while (!topicExists(topic)) {  
75 - long waitMs = timeoutExpiredMs - System.currentTimeMillis();  
76 - if (waitMs <= 0) {  
77 - throw new TimeoutException("Timeout occurred while waiting for topic [" + topic + "] to be available!");  
78 - } else {  
79 - wait(1000);  
80 - }  
81 - }  
82 - }  
83 } 60 }
84 61
85 public CreateTopicsResult createTopic(NewTopic topic) { 62 public CreateTopicsResult createTopic(NewTopic topic) {
@@ -60,16 +60,13 @@ public final class InMemoryStorage { @@ -60,16 +60,13 @@ public final class InMemoryStorage {
60 if (first != null) { 60 if (first != null) {
61 entities = new ArrayList<>(); 61 entities = new ArrayList<>();
62 entities.add(first); 62 entities.add(first);
63 - } else {  
64 - entities = Collections.emptyList();  
65 List<TbQueueMsg> otherList = new ArrayList<>(); 63 List<TbQueueMsg> otherList = new ArrayList<>();
66 - storage.get(topic).drainTo(otherList, 100); 64 + storage.get(topic).drainTo(otherList, 999);
67 for (TbQueueMsg other : otherList) { 65 for (TbQueueMsg other : otherList) {
68 entities.add((T) other); 66 entities.add((T) other);
69 } 67 }
70 - }  
71 - if (entities.size() > 0) {  
72 - storage.computeIfAbsent(topic, (t) -> new LinkedBlockingQueue<>()).addAll(entities); 68 + } else {
  69 + entities = Collections.emptyList();
73 } 70 }
74 return entities; 71 return entities;
75 } catch (InterruptedException e) { 72 } catch (InterruptedException e) {
@@ -36,11 +36,12 @@ public class TbQueueRuleEngineSettings { @@ -36,11 +36,12 @@ public class TbQueueRuleEngineSettings {
36 private String topic; 36 private String topic;
37 private List<TbRuleEngineQueueConfiguration> queues; 37 private List<TbRuleEngineQueueConfiguration> queues;
38 38
39 - //TODO 2.5 ybondarenko: make sure the queue names are valid to all queue providers. See how ther are used in TbRuleEngineQueueFactory.createToRuleEngineMsgConsumer and all producers 39 + //TODO 2.5 ybondarenko: make sure the queue names are valid to all queue providers.
  40 + // See how they are used in TbRuleEngineQueueFactory.createToRuleEngineMsgConsumer and all producers
40 @PostConstruct 41 @PostConstruct
41 public void validate() { 42 public void validate() {
42 queues.stream().filter(queue -> queue.getName().equals("Main")).findFirst().orElseThrow(() -> { 43 queues.stream().filter(queue -> queue.getName().equals("Main")).findFirst().orElseThrow(() -> {
43 - log.warn("Main queue is not configured in thingsboard.yml"); 44 + log.error("Main queue is not configured in thingsboard.yml");
44 return new RuntimeException("No \"Main\" queue configured!"); 45 return new RuntimeException("No \"Main\" queue configured!");
45 }); 46 });
46 } 47 }
@@ -400,11 +400,11 @@ message ToRuleEngineNotificationMsg { @@ -400,11 +400,11 @@ message ToRuleEngineNotificationMsg {
400 400
401 /* Messages that are handled by ThingsBoard Transport Service */ 401 /* Messages that are handled by ThingsBoard Transport Service */
402 message ToTransportMsg { 402 message ToTransportMsg {
403 - int64 sessionIdMSB = 1;  
404 - int64 sessionIdLSB = 2;  
405 - SessionCloseNotificationProto sessionCloseNotification = 3;  
406 - GetAttributeResponseMsg getAttributesResponse = 4;  
407 - AttributeUpdateNotificationMsg attributeUpdateNotification = 5;  
408 - ToDeviceRpcRequestMsg toDeviceRequest = 6;  
409 - ToServerRpcResponseMsg toServerResponse = 7; 403 + int64 sessionIdMSB = 1;
  404 + int64 sessionIdLSB = 2;
  405 + SessionCloseNotificationProto sessionCloseNotification = 3;
  406 + GetAttributeResponseMsg getAttributesResponse = 4;
  407 + AttributeUpdateNotificationMsg attributeUpdateNotification = 5;
  408 + ToDeviceRpcRequestMsg toDeviceRequest = 6;
  409 + ToServerRpcResponseMsg toServerResponse = 7;
410 } 410 }
@@ -60,3 +60,4 @@ cassandra.query.tenant_rate_limits.enabled=false @@ -60,3 +60,4 @@ cassandra.query.tenant_rate_limits.enabled=false
60 cassandra.query.tenant_rate_limits.configuration=5000:1,100000:60 60 cassandra.query.tenant_rate_limits.configuration=5000:1,100000:60
61 cassandra.query.tenant_rate_limits.print_tenant_names=false 61 cassandra.query.tenant_rate_limits.print_tenant_names=false
62 62
  63 +service.type=monolith
@@ -16,6 +16,8 @@ spring.datasource.url=jdbc:hsqldb:file:/tmp/testDb;sql.enforce_size=false @@ -16,6 +16,8 @@ spring.datasource.url=jdbc:hsqldb:file:/tmp/testDb;sql.enforce_size=false
16 spring.datasource.driverClassName=org.hsqldb.jdbc.JDBCDriver 16 spring.datasource.driverClassName=org.hsqldb.jdbc.JDBCDriver
17 spring.datasource.hikari.maximumPoolSize = 50 17 spring.datasource.hikari.maximumPoolSize = 50
18 18
  19 +service.type=monolith
  20 +
19 #database.ts.type=timescale 21 #database.ts.type=timescale
20 #database.ts.type=sql 22 #database.ts.type=sql
21 #database.entities.type=sql 23 #database.entities.type=sql
@@ -25,6 +25,7 @@ import org.junit.Before; @@ -25,6 +25,7 @@ import org.junit.Before;
25 import org.junit.Test; 25 import org.junit.Test;
26 import org.junit.runner.RunWith; 26 import org.junit.runner.RunWith;
27 import org.mockito.ArgumentCaptor; 27 import org.mockito.ArgumentCaptor;
  28 +import org.mockito.Captor;
28 import org.mockito.Mock; 29 import org.mockito.Mock;
29 import org.mockito.runners.MockitoJUnitRunner; 30 import org.mockito.runners.MockitoJUnitRunner;
30 import org.mockito.stubbing.Answer; 31 import org.mockito.stubbing.Answer;
@@ -47,6 +48,7 @@ import org.thingsboard.server.dao.alarm.AlarmService; @@ -47,6 +48,7 @@ import org.thingsboard.server.dao.alarm.AlarmService;
47 import javax.script.ScriptException; 48 import javax.script.ScriptException;
48 import java.io.IOException; 49 import java.io.IOException;
49 import java.util.concurrent.Callable; 50 import java.util.concurrent.Callable;
  51 +import java.util.function.Consumer;
50 52
51 import static org.junit.Assert.assertEquals; 53 import static org.junit.Assert.assertEquals;
52 import static org.junit.Assert.assertNotSame; 54 import static org.junit.Assert.assertNotSame;
@@ -82,6 +84,11 @@ public class TbAlarmNodeTest { @@ -82,6 +84,11 @@ public class TbAlarmNodeTest {
82 @Mock 84 @Mock
83 private ScriptEngine detailsJs; 85 private ScriptEngine detailsJs;
84 86
  87 + @Captor
  88 + private ArgumentCaptor<Runnable> successCaptor;
  89 + @Captor
  90 + private ArgumentCaptor<Consumer<Throwable>> failureCaptor;
  91 +
85 private RuleChainId ruleChainId = new RuleChainId(UUIDs.timeBased()); 92 private RuleChainId ruleChainId = new RuleChainId(UUIDs.timeBased());
86 private RuleNodeId ruleNodeId = new RuleNodeId(UUIDs.timeBased()); 93 private RuleNodeId ruleNodeId = new RuleNodeId(UUIDs.timeBased());
87 94
@@ -119,11 +126,12 @@ public class TbAlarmNodeTest { @@ -119,11 +126,12 @@ public class TbAlarmNodeTest {
119 126
120 when(detailsJs.executeJsonAsync(msg)).thenReturn(Futures.immediateFuture(null)); 127 when(detailsJs.executeJsonAsync(msg)).thenReturn(Futures.immediateFuture(null));
121 when(alarmService.findLatestByOriginatorAndType(tenantId, originator, "SomeType")).thenReturn(Futures.immediateFuture(null)); 128 when(alarmService.findLatestByOriginatorAndType(tenantId, originator, "SomeType")).thenReturn(Futures.immediateFuture(null));
122 -  
123 doAnswer((Answer<Alarm>) invocationOnMock -> (Alarm) (invocationOnMock.getArguments())[0]).when(alarmService).createOrUpdateAlarm(any(Alarm.class)); 129 doAnswer((Answer<Alarm>) invocationOnMock -> (Alarm) (invocationOnMock.getArguments())[0]).when(alarmService).createOrUpdateAlarm(any(Alarm.class));
124 130
125 node.onMsg(ctx, msg); 131 node.onMsg(ctx, msg);
126 132
  133 + verify(ctx).enqueue(any(), successCaptor.capture(), failureCaptor.capture());
  134 + successCaptor.getValue().run();
127 verify(ctx).tellNext(any(), eq("Created")); 135 verify(ctx).tellNext(any(), eq("Created"));
128 136
129 ArgumentCaptor<TbMsg> msgCaptor = ArgumentCaptor.forClass(TbMsg.class); 137 ArgumentCaptor<TbMsg> msgCaptor = ArgumentCaptor.forClass(TbMsg.class);
@@ -191,6 +199,8 @@ public class TbAlarmNodeTest { @@ -191,6 +199,8 @@ public class TbAlarmNodeTest {
191 199
192 node.onMsg(ctx, msg); 200 node.onMsg(ctx, msg);
193 201
  202 + verify(ctx).enqueue(any(), successCaptor.capture(), failureCaptor.capture());
  203 + successCaptor.getValue().run();
194 verify(ctx).tellNext(any(), eq("Created")); 204 verify(ctx).tellNext(any(), eq("Created"));
195 205
196 ArgumentCaptor<TbMsg> msgCaptor = ArgumentCaptor.forClass(TbMsg.class); 206 ArgumentCaptor<TbMsg> msgCaptor = ArgumentCaptor.forClass(TbMsg.class);
@@ -256,7 +256,7 @@ public class TbGetCustomerAttributeNodeTest { @@ -256,7 +256,7 @@ public class TbGetCustomerAttributeNodeTest {
256 .thenReturn(Futures.immediateFuture(timeseries)); 256 .thenReturn(Futures.immediateFuture(timeseries));
257 257
258 node.onMsg(ctx, msg); 258 node.onMsg(ctx, msg);
259 - verify(ctx).tellNext(msg, SUCCESS); 259 + verify(ctx).tellSuccess(msg);
260 assertEquals(msg.getMetaData().getValue("tempo"), "highest"); 260 assertEquals(msg.getMetaData().getValue("tempo"), "highest");
261 } 261 }
262 262
@@ -268,7 +268,7 @@ public class TbGetCustomerAttributeNodeTest { @@ -268,7 +268,7 @@ public class TbGetCustomerAttributeNodeTest {
268 .thenReturn(Futures.immediateFuture(attributes)); 268 .thenReturn(Futures.immediateFuture(attributes));
269 269
270 node.onMsg(ctx, msg); 270 node.onMsg(ctx, msg);
271 - verify(ctx).tellNext(msg, SUCCESS); 271 + verify(ctx).tellSuccess(msg);
272 assertEquals(msg.getMetaData().getValue("tempo"), "high"); 272 assertEquals(msg.getMetaData().getValue("tempo"), "high");
273 } 273 }
274 } 274 }
@@ -77,7 +77,7 @@ public class TbTransformMsgNodeTest { @@ -77,7 +77,7 @@ public class TbTransformMsgNodeTest {
77 node.onMsg(ctx, msg); 77 node.onMsg(ctx, msg);
78 verify(ctx).getDbCallbackExecutor(); 78 verify(ctx).getDbCallbackExecutor();
79 ArgumentCaptor<TbMsg> captor = ArgumentCaptor.forClass(TbMsg.class); 79 ArgumentCaptor<TbMsg> captor = ArgumentCaptor.forClass(TbMsg.class);
80 - verify(ctx).tellNext(captor.capture(), eq(SUCCESS)); 80 + verify(ctx).tellSuccess(captor.capture());
81 TbMsg actualMsg = captor.getValue(); 81 TbMsg actualMsg = captor.getValue();
82 assertEquals(transformedMsg, actualMsg); 82 assertEquals(transformedMsg, actualMsg);
83 } 83 }