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 | } |