Commit 21ee2aec11ae5bf0d0660001987cfeb4b8ba4cea

Authored by Volodymyr Babak
2 parents 8462481e 8d9a5875

Merge remote-tracking branch 'upstream/develop/2.5.3' into feature/edge

Showing 88 changed files with 2264 additions and 742 deletions

Too many changes to show.

To preserve performance only 88 of 132 files are displayed.

... ... @@ -20,7 +20,7 @@
20 20 <modelVersion>4.0.0</modelVersion>
21 21 <parent>
22 22 <groupId>org.thingsboard</groupId>
23   - <version>2.5.1-SNAPSHOT</version>
  23 + <version>2.5.3-SNAPSHOT</version>
24 24 <artifactId>thingsboard</artifactId>
25 25 </parent>
26 26 <artifactId>application</artifactId>
... ... @@ -59,6 +59,10 @@
59 59 </dependency>
60 60 <dependency>
61 61 <groupId>org.thingsboard.common</groupId>
  62 + <artifactId>actor</artifactId>
  63 + </dependency>
  64 + <dependency>
  65 + <groupId>org.thingsboard.common</groupId>
62 66 <artifactId>util</artifactId>
63 67 </dependency>
64 68 <dependency>
... ... @@ -178,14 +182,6 @@
178 182 <artifactId>spring-context-support</artifactId>
179 183 </dependency>
180 184 <dependency>
181   - <groupId>com.typesafe.akka</groupId>
182   - <artifactId>akka-actor_${scala.version}</artifactId>
183   - </dependency>
184   - <dependency>
185   - <groupId>com.typesafe.akka</groupId>
186   - <artifactId>akka-slf4j_${scala.version}</artifactId>
187   - </dependency>
188   - <dependency>
189 185 <groupId>org.slf4j</groupId>
190 186 <artifactId>slf4j-api</artifactId>
191 187 </dependency>
... ... @@ -375,7 +371,7 @@
375 371 <repository>
376 372 <id>jenkins</id>
377 373 <name>Jenkins Repository</name>
378   - <url>http://repo.jenkins-ci.org/releases</url>
  374 + <url>https://repo.jenkins-ci.org/releases</url>
379 375 <snapshots>
380 376 <enabled>false</enabled>
381 377 </snapshots>
... ...
... ... @@ -35,7 +35,6 @@
35 35 </appender>
36 36
37 37 <logger name="org.thingsboard.server" level="INFO" />
38   - <logger name="akka" level="INFO" />
39 38 <logger name="com.microsoft.azure.servicebus.primitives.CoreMessageReceiver" level="OFF" />
40 39
41 40 <root level="INFO">
... ...
... ... @@ -15,9 +15,6 @@
15 15 */
16 16 package org.thingsboard.server.actors;
17 17
18   -import akka.actor.ActorRef;
19   -import akka.actor.ActorSystem;
20   -import akka.actor.Scheduler;
21 18 import com.fasterxml.jackson.databind.JsonNode;
22 19 import com.fasterxml.jackson.databind.ObjectMapper;
23 20 import com.fasterxml.jackson.databind.node.ObjectNode;
... ... @@ -25,8 +22,6 @@ import com.google.common.util.concurrent.FutureCallback;
25 22 import com.google.common.util.concurrent.Futures;
26 23 import com.google.common.util.concurrent.ListenableFuture;
27 24 import com.google.common.util.concurrent.MoreExecutors;
28   -import com.typesafe.config.Config;
29   -import com.typesafe.config.ConfigFactory;
30 25 import lombok.Getter;
31 26 import lombok.Setter;
32 27 import lombok.extern.slf4j.Slf4j;
... ... @@ -93,12 +88,13 @@ import java.io.StringWriter;
93 88 import java.util.Optional;
94 89 import java.util.concurrent.ConcurrentHashMap;
95 90 import java.util.concurrent.ConcurrentMap;
  91 +import java.util.concurrent.ScheduledExecutorService;
  92 +import java.util.concurrent.TimeUnit;
96 93 import java.util.concurrent.atomic.AtomicInteger;
97 94
98 95 @Slf4j
99 96 @Component
100 97 public class ActorSystemContext {
101   - private static final String AKKA_CONF_FILE_NAME = "actor-system.conf";
102 98
103 99 protected final ObjectMapper mapper = new ObjectMapper();
104 100
... ... @@ -272,14 +268,6 @@ public class ActorSystemContext {
272 268 @Getter
273 269 private long syncSessionTimeout;
274 270
275   - @Value("${actors.queue.enabled}")
276   - @Getter
277   - private boolean queuePersistenceEnabled;
278   -
279   - @Value("${actors.queue.timeout}")
280   - @Getter
281   - private long queuePersistenceTimeout;
282   -
283 271 @Value("${actors.rule.chain.error_persist_frequency}")
284 272 @Getter
285 273 private long ruleChainErrorPersistFrequency;
... ... @@ -339,17 +327,14 @@ public class ActorSystemContext {
339 327
340 328 @Getter
341 329 @Setter
342   - private ActorSystem actorSystem;
  330 + private TbActorSystem actorSystem;
343 331
344 332 @Setter
345   - private ActorRef appActor;
  333 + private TbActorRef appActor;
346 334
347 335 @Getter
348 336 @Setter
349   - private ActorRef statsActor;
350   -
351   - @Getter
352   - private final Config config;
  337 + private TbActorRef statsActor;
353 338
354 339 @Autowired(required = false)
355 340 @Getter
... ... @@ -363,14 +348,8 @@ public class ActorSystemContext {
363 348 @Getter
364 349 private RedisTemplate<String, Object> redisTemplate;
365 350
366   - public ActorSystemContext() {
367   - config = ConfigFactory.parseResources(AKKA_CONF_FILE_NAME).withFallback(ConfigFactory.load());
368   - }
369   -
370   -
371   -
372   - public Scheduler getScheduler() {
373   - return actorSystem.scheduler();
  351 + public ScheduledExecutorService getScheduler() {
  352 + return actorSystem.getScheduler();
374 353 }
375 354
376 355 public void persistError(TenantId tenantId, EntityId entityId, String method, Exception e) {
... ... @@ -543,7 +522,21 @@ public class ActorSystemContext {
543 522 return Exception.class.isInstance(error) ? (Exception) error : new Exception(error);
544 523 }
545 524
546   - public void tell(TbActorMsg tbActorMsg, ActorRef sender) {
547   - appActor.tell(tbActorMsg, sender);
  525 + public void tell(TbActorMsg tbActorMsg) {
  526 + appActor.tell(tbActorMsg);
  527 + }
  528 +
  529 + public void tellWithHighPriority(TbActorMsg tbActorMsg) {
  530 + appActor.tellWithHighPriority(tbActorMsg);
  531 + }
  532 +
  533 + public void schedulePeriodicMsgWithDelay(TbActorRef ctx, TbActorMsg msg, long delayInMs, long periodInMs) {
  534 + log.debug("Scheduling periodic msg {} every {} ms with delay {} ms", msg, periodInMs, delayInMs);
  535 + getScheduler().scheduleWithFixedDelay(() -> ctx.tell(msg), delayInMs, periodInMs, TimeUnit.MILLISECONDS);
  536 + }
  537 +
  538 + public void scheduleMsgWithDelay(TbActorRef ctx, TbActorMsg msg, long delayInMs) {
  539 + log.debug("Scheduling msg {} with delay {} ms", msg, delayInMs);
  540 + getScheduler().schedule(() -> ctx.tell(msg), delayInMs, TimeUnit.MILLISECONDS);
548 541 }
549 542 }
... ...
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.actors;
  17 +
  18 +import lombok.RequiredArgsConstructor;
  19 +import org.thingsboard.server.common.data.EntityType;
  20 +import org.thingsboard.server.common.data.id.EntityId;
  21 +
  22 +import java.util.function.Predicate;
  23 +
  24 +@RequiredArgsConstructor
  25 +public class TbEntityTypeActorIdPredicate implements Predicate<TbActorId> {
  26 +
  27 + private final EntityType entityType;
  28 +
  29 + @Override
  30 + public boolean test(TbActorId actorId) {
  31 + return actorId instanceof TbEntityActorId && testEntityId(((TbEntityActorId) actorId).getEntityId());
  32 + }
  33 +
  34 + protected boolean testEntityId(EntityId entityId) {
  35 + return entityId.getEntityType().equals(entityType);
  36 + }
  37 +}
... ...
... ... @@ -15,21 +15,19 @@
15 15 */
16 16 package org.thingsboard.server.actors.app;
17 17
18   -import akka.actor.ActorRef;
19   -import akka.actor.LocalActorRef;
20   -import akka.actor.OneForOneStrategy;
21   -import akka.actor.Props;
22   -import akka.actor.SupervisorStrategy;
23   -import akka.actor.Terminated;
24   -import com.google.common.collect.BiMap;
25   -import com.google.common.collect.HashBiMap;
  18 +import lombok.extern.slf4j.Slf4j;
26 19 import org.thingsboard.server.actors.ActorSystemContext;
  20 +import org.thingsboard.server.actors.TbActor;
  21 +import org.thingsboard.server.actors.TbActorId;
  22 +import org.thingsboard.server.actors.TbActorRef;
  23 +import org.thingsboard.server.actors.TbEntityActorId;
27 24 import org.thingsboard.server.actors.service.ContextAwareActor;
28 25 import org.thingsboard.server.actors.service.ContextBasedCreator;
29 26 import org.thingsboard.server.actors.service.DefaultActorService;
30 27 import org.thingsboard.server.actors.tenant.TenantActor;
31 28 import org.thingsboard.server.common.data.EntityType;
32 29 import org.thingsboard.server.common.data.Tenant;
  30 +import org.thingsboard.server.common.data.id.EntityId;
33 31 import org.thingsboard.server.common.data.id.TenantId;
34 32 import org.thingsboard.server.common.data.page.PageDataIterable;
35 33 import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
... ... @@ -43,38 +41,27 @@ import org.thingsboard.server.common.msg.queue.ServiceType;
43 41 import org.thingsboard.server.dao.model.ModelConstants;
44 42 import org.thingsboard.server.dao.tenant.TenantService;
45 43 import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper;
46   -import scala.concurrent.duration.Duration;
47 44
48 45 import java.util.HashSet;
49 46 import java.util.Optional;
50 47 import java.util.Set;
51 48
  49 +@Slf4j
52 50 public class AppActor extends ContextAwareActor {
53 51
54 52 private static final TenantId SYSTEM_TENANT = new TenantId(ModelConstants.NULL_UUID);
55 53 private final TenantService tenantService;
56   - private final BiMap<TenantId, ActorRef> tenantActors;
57 54 private final Set<TenantId> deletedTenants;
58 55 private boolean ruleChainsInitialized;
59 56
60 57 private AppActor(ActorSystemContext systemContext) {
61 58 super(systemContext);
62 59 this.tenantService = systemContext.getTenantService();
63   - this.tenantActors = HashBiMap.create();
64 60 this.deletedTenants = new HashSet<>();
65 61 }
66 62
67 63 @Override
68   - public SupervisorStrategy supervisorStrategy() {
69   - return strategy;
70   - }
71   -
72   - @Override
73   - public void preStart() {
74   - }
75   -
76   - @Override
77   - protected boolean process(TbActorMsg msg) {
  64 + protected boolean doProcess(TbActorMsg msg) {
78 65 if (!ruleChainsInitialized) {
79 66 initTenantActors();
80 67 ruleChainsInitialized = true;
... ... @@ -86,7 +73,7 @@ public class AppActor extends ContextAwareActor {
86 73 case APP_INIT_MSG:
87 74 break;
88 75 case PARTITION_CHANGE_MSG:
89   - broadcast(msg);
  76 + ctx.broadcastToChildren(msg);
90 77 break;
91 78 case COMPONENT_LIFE_CYCLE_MSG:
92 79 onComponentLifecycleMsg((ComponentLifecycleMsg) msg);
... ... @@ -95,12 +82,14 @@ public class AppActor extends ContextAwareActor {
95 82 onQueueToRuleEngineMsg((QueueToRuleEngineMsg) msg);
96 83 break;
97 84 case TRANSPORT_TO_DEVICE_ACTOR_MSG:
  85 + onToDeviceActorMsg((TenantAwareMsg) msg, false);
  86 + break;
98 87 case DEVICE_ATTRIBUTES_UPDATE_TO_DEVICE_ACTOR_MSG:
99 88 case DEVICE_CREDENTIALS_UPDATE_TO_DEVICE_ACTOR_MSG:
100 89 case DEVICE_NAME_OR_TYPE_UPDATE_TO_DEVICE_ACTOR_MSG:
101 90 case DEVICE_RPC_REQUEST_TO_DEVICE_ACTOR_MSG:
102 91 case SERVER_RPC_RESPONSE_TO_DEVICE_ACTOR_MSG:
103   - onToDeviceActorMsg((TenantAwareMsg) msg);
  92 + onToDeviceActorMsg((TenantAwareMsg) msg, true);
104 93 break;
105 94 default:
106 95 return false;
... ... @@ -145,19 +134,15 @@ public class AppActor extends ContextAwareActor {
145 134 msg.getTbMsg().getCallback().onFailure(new RuleEngineException("Message has system tenant id!"));
146 135 } else {
147 136 if (!deletedTenants.contains(msg.getTenantId())) {
148   - getOrCreateTenantActor(msg.getTenantId()).tell(msg, self());
  137 + getOrCreateTenantActor(msg.getTenantId()).tell(msg);
149 138 } else {
150 139 msg.getTbMsg().getCallback().onSuccess();
151 140 }
152 141 }
153 142 }
154 143
155   - protected void broadcast(Object msg) {
156   - tenantActors.values().forEach(actorRef -> actorRef.tell(msg, ActorRef.noSender()));
157   - }
158   -
159 144 private void onComponentLifecycleMsg(ComponentLifecycleMsg msg) {
160   - ActorRef target = null;
  145 + TbActorRef target = null;
161 146 if (SYSTEM_TENANT.equals(msg.getTenantId())) {
162 147 log.warn("Message has system tenant id: {}", msg);
163 148 } else {
... ... @@ -166,25 +151,26 @@ public class AppActor extends ContextAwareActor {
166 151 log.info("[{}] Handling tenant deleted notification: {}", msg.getTenantId(), msg);
167 152 TenantId tenantId = new TenantId(msg.getEntityId().getId());
168 153 deletedTenants.add(tenantId);
169   - ActorRef tenantActor = tenantActors.get(tenantId);
170   - if (tenantActor != null) {
171   - log.debug("[{}] Deleting tenant actor: {}", msg.getTenantId(), tenantActor);
172   - context().stop(tenantActor);
173   - }
  154 + ctx.stop(new TbEntityActorId(tenantId));
174 155 } else {
175 156 target = getOrCreateTenantActor(msg.getTenantId());
176 157 }
177 158 }
178 159 if (target != null) {
179   - target.tell(msg, ActorRef.noSender());
  160 + target.tellWithHighPriority(msg);
180 161 } else {
181 162 log.debug("[{}] Invalid component lifecycle msg: {}", msg.getTenantId(), msg);
182 163 }
183 164 }
184 165
185   - private void onToDeviceActorMsg(TenantAwareMsg msg) {
  166 + private void onToDeviceActorMsg(TenantAwareMsg msg, boolean priority) {
186 167 if (!deletedTenants.contains(msg.getTenantId())) {
187   - getOrCreateTenantActor(msg.getTenantId()).tell(msg, ActorRef.noSender());
  168 + TbActorRef tenantActor = getOrCreateTenantActor(msg.getTenantId());
  169 + if (priority) {
  170 + tenantActor.tellWithHighPriority(msg);
  171 + } else {
  172 + tenantActor.tell(msg);
  173 + }
188 174 } else {
189 175 if (msg instanceof TransportToDeviceActorMsgWrapper) {
190 176 ((TransportToDeviceActorMsgWrapper) msg).getCallback().onSuccess();
... ... @@ -192,49 +178,27 @@ public class AppActor extends ContextAwareActor {
192 178 }
193 179 }
194 180
195   - private ActorRef getOrCreateTenantActor(TenantId tenantId) {
196   - return tenantActors.computeIfAbsent(tenantId, k -> {
197   - log.info("[{}] Creating tenant actor.", tenantId);
198   - ActorRef tenantActor = context().actorOf(Props.create(new TenantActor.ActorCreator(systemContext, tenantId))
199   - .withDispatcher(DefaultActorService.CORE_DISPATCHER_NAME), tenantId.toString());
200   - context().watch(tenantActor);
201   - log.info("[{}] Created tenant actor: {}.", tenantId, tenantActor);
202   - return tenantActor;
203   - });
  181 + private TbActorRef getOrCreateTenantActor(TenantId tenantId) {
  182 + return ctx.getOrCreateChildActor(new TbEntityActorId(tenantId),
  183 + () -> DefaultActorService.TENANT_DISPATCHER_NAME,
  184 + () -> new TenantActor.ActorCreator(systemContext, tenantId));
204 185 }
205 186
206   - @Override
207   - protected void processTermination(Terminated message) {
208   - ActorRef terminated = message.actor();
209   - if (terminated instanceof LocalActorRef) {
210   - boolean removed = tenantActors.inverse().remove(terminated) != null;
211   - if (removed) {
212   - log.debug("[{}] Removed actor:", terminated);
213   - }
214   - } else {
215   - throw new IllegalStateException("Remote actors are not supported!");
216   - }
217   - }
218   -
219   - public static class ActorCreator extends ContextBasedCreator<AppActor> {
220   - private static final long serialVersionUID = 1L;
  187 + public static class ActorCreator extends ContextBasedCreator {
221 188
222 189 public ActorCreator(ActorSystemContext context) {
223 190 super(context);
224 191 }
225 192
226 193 @Override
227   - public AppActor create() {
  194 + public TbActorId createActorId() {
  195 + return new TbEntityActorId(new TenantId(EntityId.NULL_UUID));
  196 + }
  197 +
  198 + @Override
  199 + public TbActor createActor() {
228 200 return new AppActor(context);
229 201 }
230 202 }
231 203
232   - private final SupervisorStrategy strategy = new OneForOneStrategy(3, Duration.create("1 minute"), t -> {
233   - log.warn("Unknown failure", t);
234   - if (t instanceof RuntimeException) {
235   - return SupervisorStrategy.restart();
236   - } else {
237   - return SupervisorStrategy.stop();
238   - }
239   - });
240 204 }
... ...
... ... @@ -15,9 +15,12 @@
15 15 */
16 16 package org.thingsboard.server.actors.device;
17 17
  18 +import lombok.extern.slf4j.Slf4j;
18 19 import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg;
19 20 import org.thingsboard.rule.engine.api.msg.DeviceNameOrTypeUpdateMsg;
20 21 import org.thingsboard.server.actors.ActorSystemContext;
  22 +import org.thingsboard.server.actors.TbActorCtx;
  23 +import org.thingsboard.server.actors.TbActorException;
21 24 import org.thingsboard.server.actors.service.ContextAwareActor;
22 25 import org.thingsboard.server.common.data.id.DeviceId;
23 26 import org.thingsboard.server.common.data.id.TenantId;
... ... @@ -26,6 +29,7 @@ import org.thingsboard.server.common.msg.timeout.DeviceActorServerSideRpcTimeout
26 29 import org.thingsboard.server.service.rpc.ToDeviceRpcRequestActorMsg;
27 30 import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper;
28 31
  32 +@Slf4j
29 33 public class DeviceActor extends ContextAwareActor {
30 34
31 35 private final DeviceActorMessageProcessor processor;
... ... @@ -36,29 +40,26 @@ public class DeviceActor extends ContextAwareActor {
36 40 }
37 41
38 42 @Override
39   - public void preStart() {
  43 + public void init(TbActorCtx ctx) throws TbActorException {
  44 + super.init(ctx);
40 45 log.debug("[{}][{}] Starting device actor.", processor.tenantId, processor.deviceId);
41 46 try {
42   - processor.initSessionTimeout(context());
  47 + processor.initSessionTimeout(ctx);
43 48 log.debug("[{}][{}] Device actor started.", processor.tenantId, processor.deviceId);
44 49 } catch (Exception e) {
45 50 log.warn("[{}][{}] Unknown failure", processor.tenantId, processor.deviceId, e);
  51 + throw new TbActorException("Failed to initialize device actor", e);
46 52 }
47 53 }
48 54
49 55 @Override
50   - public void postStop() {
51   -
52   - }
53   -
54   - @Override
55   - protected boolean process(TbActorMsg msg) {
  56 + protected boolean doProcess(TbActorMsg msg) {
56 57 switch (msg.getMsgType()) {
57 58 case TRANSPORT_TO_DEVICE_ACTOR_MSG:
58   - processor.process(context(), (TransportToDeviceActorMsgWrapper) msg);
  59 + processor.process(ctx, (TransportToDeviceActorMsgWrapper) msg);
59 60 break;
60 61 case DEVICE_ATTRIBUTES_UPDATE_TO_DEVICE_ACTOR_MSG:
61   - processor.processAttributesUpdate(context(), (DeviceAttributesEventNotificationMsg) msg);
  62 + processor.processAttributesUpdate(ctx, (DeviceAttributesEventNotificationMsg) msg);
62 63 break;
63 64 case DEVICE_CREDENTIALS_UPDATE_TO_DEVICE_ACTOR_MSG:
64 65 processor.processCredentialsUpdate();
... ... @@ -67,10 +68,10 @@ public class DeviceActor extends ContextAwareActor {
67 68 processor.processNameOrTypeUpdate((DeviceNameOrTypeUpdateMsg) msg);
68 69 break;
69 70 case DEVICE_RPC_REQUEST_TO_DEVICE_ACTOR_MSG:
70   - processor.processRpcRequest(context(), (ToDeviceRpcRequestActorMsg) msg);
  71 + processor.processRpcRequest(ctx, (ToDeviceRpcRequestActorMsg) msg);
71 72 break;
72 73 case DEVICE_ACTOR_SERVER_SIDE_RPC_TIMEOUT_MSG:
73   - processor.processServerSideRpcTimeout(context(), (DeviceActorServerSideRpcTimeoutMsg) msg);
  74 + processor.processServerSideRpcTimeout(ctx, (DeviceActorServerSideRpcTimeoutMsg) msg);
74 75 break;
75 76 case SESSION_TIMEOUT_MSG:
76 77 processor.checkSessionsTimeout();
... ...
... ... @@ -16,12 +16,14 @@
16 16 package org.thingsboard.server.actors.device;
17 17
18 18 import org.thingsboard.server.actors.ActorSystemContext;
  19 +import org.thingsboard.server.actors.TbActor;
  20 +import org.thingsboard.server.actors.TbActorId;
  21 +import org.thingsboard.server.actors.TbEntityActorId;
19 22 import org.thingsboard.server.actors.service.ContextBasedCreator;
20 23 import org.thingsboard.server.common.data.id.DeviceId;
21 24 import org.thingsboard.server.common.data.id.TenantId;
22 25
23   -public class DeviceActorCreator extends ContextBasedCreator<DeviceActor> {
24   - private static final long serialVersionUID = 1L;
  26 +public class DeviceActorCreator extends ContextBasedCreator {
25 27
26 28 private final TenantId tenantId;
27 29 private final DeviceId deviceId;
... ... @@ -33,7 +35,13 @@ public class DeviceActorCreator extends ContextBasedCreator<DeviceActor> {
33 35 }
34 36
35 37 @Override
36   - public DeviceActor create() {
  38 + public TbActorId createActorId() {
  39 + return new TbEntityActorId(deviceId);
  40 + }
  41 +
  42 + @Override
  43 + public TbActor createActor() {
37 44 return new DeviceActor(context, tenantId, deviceId);
38 45 }
  46 +
39 47 }
... ...
... ... @@ -15,7 +15,6 @@
15 15 */
16 16 package org.thingsboard.server.actors.device;
17 17
18   -import akka.actor.ActorContext;
19 18 import com.google.common.util.concurrent.FutureCallback;
20 19 import com.google.common.util.concurrent.Futures;
21 20 import com.google.common.util.concurrent.ListenableFuture;
... ... @@ -27,6 +26,7 @@ import org.thingsboard.rule.engine.api.RpcError;
27 26 import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg;
28 27 import org.thingsboard.rule.engine.api.msg.DeviceNameOrTypeUpdateMsg;
29 28 import org.thingsboard.server.actors.ActorSystemContext;
  29 +import org.thingsboard.server.actors.TbActorCtx;
30 30 import org.thingsboard.server.actors.shared.AbstractContextAwareMsgProcessor;
31 31 import org.thingsboard.server.common.data.Device;
32 32 import org.thingsboard.server.common.data.id.DeviceId;
... ... @@ -127,7 +127,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
127 127 }
128 128 }
129 129
130   - void processRpcRequest(ActorContext context, ToDeviceRpcRequestActorMsg msg) {
  130 + void processRpcRequest(TbActorCtx context, ToDeviceRpcRequestActorMsg msg) {
131 131 ToDeviceRpcRequest request = msg.getMsg();
132 132 ToDeviceRpcRequestBody body = request.getBody();
133 133 ToDeviceRpcRequestMsg rpcRequest = ToDeviceRpcRequestMsg.newBuilder().setRequestId(
... ... @@ -162,13 +162,13 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
162 162 }
163 163 }
164 164
165   - private void registerPendingRpcRequest(ActorContext context, ToDeviceRpcRequestActorMsg msg, boolean sent, ToDeviceRpcRequestMsg rpcRequest, long timeout) {
  165 + private void registerPendingRpcRequest(TbActorCtx context, ToDeviceRpcRequestActorMsg msg, boolean sent, ToDeviceRpcRequestMsg rpcRequest, long timeout) {
166 166 toDeviceRpcPendingMap.put(rpcRequest.getRequestId(), new ToDeviceRpcRequestMetadata(msg, sent));
167 167 DeviceActorServerSideRpcTimeoutMsg timeoutMsg = new DeviceActorServerSideRpcTimeoutMsg(rpcRequest.getRequestId(), timeout);
168 168 scheduleMsgWithDelay(context, timeoutMsg, timeoutMsg.getTimeout());
169 169 }
170 170
171   - void processServerSideRpcTimeout(ActorContext context, DeviceActorServerSideRpcTimeoutMsg msg) {
  171 + void processServerSideRpcTimeout(TbActorCtx context, DeviceActorServerSideRpcTimeoutMsg msg) {
172 172 ToDeviceRpcRequestMetadata requestMd = toDeviceRpcPendingMap.remove(msg.getId());
173 173 if (requestMd != null) {
174 174 log.debug("[{}] RPC request [{}] timeout detected!", deviceId, msg.getId());
... ... @@ -177,7 +177,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
177 177 }
178 178 }
179 179
180   - private void sendPendingRequests(ActorContext context, UUID sessionId, SessionInfoProto sessionInfo) {
  180 + private void sendPendingRequests(TbActorCtx context, UUID sessionId, SessionInfoProto sessionInfo) {
181 181 SessionType sessionType = getSessionType(sessionId);
182 182 if (!toDeviceRpcPendingMap.isEmpty()) {
183 183 log.debug("[{}] Pushing {} pending RPC messages to new async session [{}]", deviceId, toDeviceRpcPendingMap.size(), sessionId);
... ... @@ -198,7 +198,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
198 198 sentOneWayIds.forEach(toDeviceRpcPendingMap::remove);
199 199 }
200 200
201   - private Consumer<Map.Entry<Integer, ToDeviceRpcRequestMetadata>> processPendingRpc(ActorContext context, UUID sessionId, String nodeId, Set<Integer> sentOneWayIds) {
  201 + private Consumer<Map.Entry<Integer, ToDeviceRpcRequestMetadata>> processPendingRpc(TbActorCtx context, UUID sessionId, String nodeId, Set<Integer> sentOneWayIds) {
202 202 return entry -> {
203 203 ToDeviceRpcRequest request = entry.getValue().getMsg().getMsg();
204 204 ToDeviceRpcRequestBody body = request.getBody();
... ... @@ -212,7 +212,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
212 212 };
213 213 }
214 214
215   - void process(ActorContext context, TransportToDeviceActorMsgWrapper wrapper) {
  215 + void process(TbActorCtx context, TransportToDeviceActorMsgWrapper wrapper) {
216 216 TransportToDeviceActorMsg msg = wrapper.getMsg();
217 217 TbCallback callback = wrapper.getCallback();
218 218 if (msg.hasSessionEvent()) {
... ... @@ -239,7 +239,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
239 239 callback.onSuccess();
240 240 }
241 241
242   - private void handleClaimDeviceMsg(ActorContext context, SessionInfoProto sessionInfo, TransportProtos.ClaimDeviceMsg msg) {
  242 + private void handleClaimDeviceMsg(TbActorCtx context, SessionInfoProto sessionInfo, TransportProtos.ClaimDeviceMsg msg) {
243 243 DeviceId deviceId = new DeviceId(new UUID(msg.getDeviceIdMSB(), msg.getDeviceIdLSB()));
244 244 systemContext.getClaimDevicesService().registerClaimingInfo(tenantId, deviceId, msg.getSecretKey(), msg.getDurationMs());
245 245 }
... ... @@ -252,7 +252,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
252 252 systemContext.getDeviceStateService().onDeviceDisconnect(deviceId);
253 253 }
254 254
255   - private void handleGetAttributesRequest(ActorContext context, SessionInfoProto sessionInfo, GetAttributeRequestMsg request) {
  255 + private void handleGetAttributesRequest(TbActorCtx context, SessionInfoProto sessionInfo, GetAttributeRequestMsg request) {
256 256 int requestId = request.getRequestId();
257 257 Futures.addCallback(getAttributesKvEntries(request), new FutureCallback<List<List<AttributeKvEntry>>>() {
258 258 @Override
... ... @@ -310,7 +310,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
310 310 return sessions.containsKey(sessionId) ? SessionType.ASYNC : SessionType.SYNC;
311 311 }
312 312
313   - void processAttributesUpdate(ActorContext context, DeviceAttributesEventNotificationMsg msg) {
  313 + void processAttributesUpdate(TbActorCtx context, DeviceAttributesEventNotificationMsg msg) {
314 314 if (attributeSubscriptions.size() > 0) {
315 315 boolean hasNotificationData = false;
316 316 AttributeUpdateNotificationMsg.Builder notification = AttributeUpdateNotificationMsg.newBuilder();
... ... @@ -349,7 +349,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
349 349 }
350 350 }
351 351
352   - private void processRpcResponses(ActorContext context, SessionInfoProto sessionInfo, ToDeviceRpcResponseMsg responseMsg) {
  352 + private void processRpcResponses(TbActorCtx context, SessionInfoProto sessionInfo, ToDeviceRpcResponseMsg responseMsg) {
353 353 UUID sessionId = getSessionId(sessionInfo);
354 354 log.debug("[{}] Processing rpc command response [{}]", deviceId, sessionId);
355 355 ToDeviceRpcRequestMetadata requestMd = toDeviceRpcPendingMap.remove(responseMsg.getRequestId());
... ... @@ -362,7 +362,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
362 362 }
363 363 }
364 364
365   - private void processSubscriptionCommands(ActorContext context, SessionInfoProto sessionInfo, SubscribeToAttributeUpdatesMsg subscribeCmd) {
  365 + private void processSubscriptionCommands(TbActorCtx context, SessionInfoProto sessionInfo, SubscribeToAttributeUpdatesMsg subscribeCmd) {
366 366 UUID sessionId = getSessionId(sessionInfo);
367 367 if (subscribeCmd.getUnsubscribe()) {
368 368 log.debug("[{}] Canceling attributes subscription for session [{}]", deviceId, sessionId);
... ... @@ -383,7 +383,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
383 383 return new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB());
384 384 }
385 385
386   - private void processSubscriptionCommands(ActorContext context, SessionInfoProto sessionInfo, SubscribeToRPCMsg subscribeCmd) {
  386 + private void processSubscriptionCommands(TbActorCtx context, SessionInfoProto sessionInfo, SubscribeToRPCMsg subscribeCmd) {
387 387 UUID sessionId = getSessionId(sessionInfo);
388 388 if (subscribeCmd.getUnsubscribe()) {
389 389 log.debug("[{}] Canceling rpc subscription for session [{}]", deviceId, sessionId);
... ... @@ -433,7 +433,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
433 433 }
434 434 }
435 435
436   - private void handleSessionActivity(ActorContext context, SessionInfoProto sessionInfoProto, SubscriptionInfoProto subscriptionInfo) {
  436 + private void handleSessionActivity(TbActorCtx context, SessionInfoProto sessionInfoProto, SubscriptionInfoProto subscriptionInfo) {
437 437 UUID sessionId = getSessionId(sessionInfoProto);
438 438 SessionInfoMetaData sessionMD = sessions.computeIfAbsent(sessionId,
439 439 id -> new SessionInfoMetaData(new SessionInfo(SessionType.ASYNC, sessionInfoProto.getNodeId()), 0L));
... ... @@ -612,8 +612,8 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
612 612 .addAllSessions(sessionsList).build().toByteArray());
613 613 }
614 614
615   - void initSessionTimeout(ActorContext context) {
616   - schedulePeriodicMsgWithDelay(context, SessionTimeoutCheckMsg.instance(), systemContext.getSessionInactivityTimeout(), systemContext.getSessionInactivityTimeout());
  615 + void initSessionTimeout(TbActorCtx ctx) {
  616 + schedulePeriodicMsgWithDelay(ctx, SessionTimeoutCheckMsg.instance(), systemContext.getSessionInactivityTimeout(), systemContext.getSessionInactivityTimeout());
617 617 }
618 618
619 619 void checkSessionsTimeout() {
... ...
... ... @@ -15,7 +15,6 @@
15 15 */
16 16 package org.thingsboard.server.actors.ruleChain;
17 17
18   -import akka.actor.ActorRef;
19 18 import com.datastax.driver.core.ResultSetFuture;
20 19 import com.fasterxml.jackson.core.JsonProcessingException;
21 20 import com.fasterxml.jackson.databind.ObjectMapper;
... ... @@ -30,6 +29,7 @@ import org.thingsboard.rule.engine.api.ScriptEngine;
30 29 import org.thingsboard.rule.engine.api.TbContext;
31 30 import org.thingsboard.rule.engine.api.TbRelationTypes;
32 31 import org.thingsboard.server.actors.ActorSystemContext;
  32 +import org.thingsboard.server.actors.TbActorRef;
33 33 import org.thingsboard.server.common.data.Customer;
34 34 import org.thingsboard.server.common.data.DataConstants;
35 35 import org.thingsboard.server.common.data.Device;
... ... @@ -40,6 +40,7 @@ import org.thingsboard.server.common.data.id.RuleChainId;
40 40 import org.thingsboard.server.common.data.id.RuleNodeId;
41 41 import org.thingsboard.server.common.data.id.TenantId;
42 42 import org.thingsboard.server.common.data.rule.RuleNode;
  43 +import org.thingsboard.server.common.msg.TbActorMsg;
43 44 import org.thingsboard.server.common.msg.TbMsg;
44 45 import org.thingsboard.server.common.msg.TbMsgMetaData;
45 46 import org.thingsboard.server.common.msg.queue.ServiceType;
... ... @@ -64,7 +65,6 @@ import org.thingsboard.server.gen.transport.TransportProtos;
64 65 import org.thingsboard.server.queue.TbQueueCallback;
65 66 import org.thingsboard.server.queue.TbQueueMsgMetadata;
66 67 import org.thingsboard.server.service.script.RuleNodeJsScriptEngine;
67   -import scala.concurrent.duration.Duration;
68 68
69 69 import java.util.Collections;
70 70 import java.util.Set;
... ... @@ -106,7 +106,7 @@ class DefaultTbContext implements TbContext {
106 106 if (nodeCtx.getSelf().isDebugMode()) {
107 107 relationTypes.forEach(relationType -> mainCtx.persistDebugOutput(nodeCtx.getTenantId(), nodeCtx.getSelf().getId(), msg, relationType, th));
108 108 }
109   - nodeCtx.getChainActor().tell(new RuleNodeToRuleChainTellNextMsg(nodeCtx.getSelf().getId(), relationTypes, msg, th != null ? th.getMessage() : null), nodeCtx.getSelfActor());
  109 + nodeCtx.getChainActor().tell(new RuleNodeToRuleChainTellNextMsg(nodeCtx.getSelf().getId(), relationTypes, msg, th != null ? th.getMessage() : null));
110 110 }
111 111
112 112 @Override
... ... @@ -132,7 +132,7 @@ class DefaultTbContext implements TbContext {
132 132 .setTenantIdMSB(getTenantId().getId().getMostSignificantBits())
133 133 .setTenantIdLSB(getTenantId().getId().getLeastSignificantBits())
134 134 .setTbMsg(TbMsg.toByteString(tbMsg)).build();
135   - mainCtx.getClusterService().pushMsgToRuleEngine(tpi, tbMsg.getId(), msg, new SimpleTbQueueCallback(onSuccess, onFailure));
  135 + mainCtx.getClusterService().pushMsgToRuleEngine(tpi, tbMsg.getId(), msg, new SimpleTbQueueCallback(onSuccess, onFailure));
136 136 }
137 137
138 138 @Override
... ... @@ -189,7 +189,7 @@ class DefaultTbContext implements TbContext {
189 189 if (failureMessage != null) {
190 190 msg.setFailureMessage(failureMessage);
191 191 }
192   - mainCtx.getClusterService().pushMsgToRuleEngine(tpi, tbMsg.getId(), msg.build(), new SimpleTbQueueCallback(onSuccess, onFailure));
  192 + mainCtx.getClusterService().pushMsgToRuleEngine(tpi, tbMsg.getId(), msg.build(), new SimpleTbQueueCallback(onSuccess, onFailure));
193 193 }
194 194
195 195 @Override
... ... @@ -205,8 +205,8 @@ class DefaultTbContext implements TbContext {
205 205 return mainCtx.resolve(ServiceType.TB_RULE_ENGINE, getTenantId(), entityId).isMyPartition();
206 206 }
207 207
208   - private void scheduleMsgWithDelay(Object msg, long delayInMs, ActorRef target) {
209   - mainCtx.getScheduler().scheduleOnce(Duration.create(delayInMs, TimeUnit.MILLISECONDS), target, msg, mainCtx.getActorSystem().dispatcher(), nodeCtx.getSelfActor());
  208 + private void scheduleMsgWithDelay(TbActorMsg msg, long delayInMs, TbActorRef target) {
  209 + mainCtx.scheduleMsgWithDelay(target, msg, delayInMs);
210 210 }
211 211
212 212 @Override
... ... @@ -215,7 +215,7 @@ class DefaultTbContext implements TbContext {
215 215 mainCtx.persistDebugOutput(nodeCtx.getTenantId(), nodeCtx.getSelf().getId(), msg, TbRelationTypes.FAILURE, th);
216 216 }
217 217 nodeCtx.getChainActor().tell(new RuleNodeToRuleChainTellNextMsg(nodeCtx.getSelf().getId(), Collections.singleton(TbRelationTypes.FAILURE),
218   - msg, th != null ? th.getMessage() : null), nodeCtx.getSelfActor());
  218 + msg, th != null ? th.getMessage() : null));
219 219 }
220 220
221 221 public void updateSelf(RuleNode self) {
... ... @@ -223,8 +223,8 @@ class DefaultTbContext implements TbContext {
223 223 }
224 224
225 225 @Override
226   - public TbMsg newMsg(String type, EntityId originator, TbMsgMetaData metaData, String data) {
227   - return TbMsg.newMsg(type, originator, metaData, data, nodeCtx.getSelf().getRuleChainId(), nodeCtx.getSelf().getId());
  226 + public TbMsg newMsg(String queueName, String type, EntityId originator, TbMsgMetaData metaData, String data) {
  227 + return TbMsg.newMsg(queueName, type, originator, metaData, data, nodeCtx.getSelf().getRuleChainId(), nodeCtx.getSelf().getId());
228 228 }
229 229
230 230 @Override
... ...
... ... @@ -15,9 +15,11 @@
15 15 */
16 16 package org.thingsboard.server.actors.ruleChain;
17 17
18   -import akka.actor.OneForOneStrategy;
19   -import akka.actor.SupervisorStrategy;
20 18 import org.thingsboard.server.actors.ActorSystemContext;
  19 +import org.thingsboard.server.actors.TbActor;
  20 +import org.thingsboard.server.actors.TbActorCtx;
  21 +import org.thingsboard.server.actors.TbActorId;
  22 +import org.thingsboard.server.actors.TbEntityActorId;
21 23 import org.thingsboard.server.actors.service.ComponentActor;
22 24 import org.thingsboard.server.actors.service.ContextBasedCreator;
23 25 import org.thingsboard.server.common.data.id.RuleChainId;
... ... @@ -27,18 +29,24 @@ import org.thingsboard.server.common.msg.TbActorMsg;
27 29 import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
28 30 import org.thingsboard.server.common.msg.queue.PartitionChangeMsg;
29 31 import org.thingsboard.server.common.msg.queue.QueueToRuleEngineMsg;
30   -import scala.concurrent.duration.Duration;
31 32
32 33 public class RuleChainActor extends ComponentActor<RuleChainId, RuleChainActorMessageProcessor> {
33 34
  35 + private final RuleChain ruleChain;
  36 +
34 37 private RuleChainActor(ActorSystemContext systemContext, TenantId tenantId, RuleChain ruleChain) {
35 38 super(systemContext, tenantId, ruleChain.getId());
36   - setProcessor(new RuleChainActorMessageProcessor(tenantId, ruleChain, systemContext,
37   - context().parent(), context().self()));
  39 + this.ruleChain = ruleChain;
38 40 }
39 41
40 42 @Override
41   - protected boolean process(TbActorMsg msg) {
  43 + protected RuleChainActorMessageProcessor createProcessor(TbActorCtx ctx) {
  44 + return new RuleChainActorMessageProcessor(tenantId, ruleChain, systemContext,
  45 + ctx.getParentRef(), ctx);
  46 + }
  47 +
  48 + @Override
  49 + protected boolean doProcess(TbActorMsg msg) {
42 50 switch (msg.getMsgType()) {
43 51 case COMPONENT_LIFE_CYCLE_MSG:
44 52 onComponentLifecycleMsg((ComponentLifecycleMsg) msg);
... ... @@ -64,7 +72,7 @@ public class RuleChainActor extends ComponentActor<RuleChainId, RuleChainActorMe
64 72 return true;
65 73 }
66 74
67   - public static class ActorCreator extends ContextBasedCreator<RuleChainActor> {
  75 + public static class ActorCreator extends ContextBasedCreator {
68 76 private static final long serialVersionUID = 1L;
69 77
70 78 private final TenantId tenantId;
... ... @@ -77,7 +85,12 @@ public class RuleChainActor extends ComponentActor<RuleChainId, RuleChainActorMe
77 85 }
78 86
79 87 @Override
80   - public RuleChainActor create() {
  88 + public TbActorId createActorId() {
  89 + return new TbEntityActorId(ruleChain.getId());
  90 + }
  91 +
  92 + @Override
  93 + public TbActor createActor() {
81 94 return new RuleChainActor(context, tenantId, ruleChain);
82 95 }
83 96 }
... ... @@ -87,13 +100,4 @@ public class RuleChainActor extends ComponentActor<RuleChainId, RuleChainActorMe
87 100 return systemContext.getRuleChainErrorPersistFrequency();
88 101 }
89 102
90   - @Override
91   - public SupervisorStrategy supervisorStrategy() {
92   - return strategy;
93   - }
94   -
95   - private final SupervisorStrategy strategy = new OneForOneStrategy(3, Duration.create("1 minute"), t -> {
96   - logAndPersist("Unknown Failure", ActorSystemContext.toException(t));
97   - return SupervisorStrategy.resume();
98   - });
99 103 }
... ...
... ... @@ -15,12 +15,12 @@
15 15 */
16 16 package org.thingsboard.server.actors.ruleChain;
17 17
18   -import akka.actor.ActorContext;
19   -import akka.actor.ActorRef;
20   -import akka.actor.Props;
21 18 import lombok.extern.slf4j.Slf4j;
22 19 import org.thingsboard.rule.engine.api.TbRelationTypes;
23 20 import org.thingsboard.server.actors.ActorSystemContext;
  21 +import org.thingsboard.server.actors.TbActorCtx;
  22 +import org.thingsboard.server.actors.TbActorRef;
  23 +import org.thingsboard.server.actors.TbEntityActorId;
24 24 import org.thingsboard.server.actors.service.DefaultActorService;
25 25 import org.thingsboard.server.actors.shared.ComponentMsgProcessor;
26 26 import org.thingsboard.server.common.data.EntityType;
... ... @@ -64,20 +64,21 @@ import java.util.stream.Collectors;
64 64 @Slf4j
65 65 public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleChainId> {
66 66
67   - private final ActorRef parent;
68   - private final ActorRef self;
  67 + private final TbActorRef parent;
  68 + private final TbActorRef self;
69 69 private final Map<RuleNodeId, RuleNodeCtx> nodeActors;
70 70 private final Map<RuleNodeId, List<RuleNodeRelation>> nodeRoutes;
71 71 private final RuleChainService service;
72 72 private final TbClusterService clusterService;
73 73 private final EdgeService edgeService;
74 74 private String ruleChainName;
  75 +
75 76 private RuleNodeId firstId;
76 77 private RuleNodeCtx firstNode;
77 78 private boolean started;
78 79
79 80 RuleChainActorMessageProcessor(TenantId tenantId, RuleChain ruleChain, ActorSystemContext systemContext
80   - , ActorRef parent, ActorRef self) {
  81 + , TbActorRef parent, TbActorRef self) {
81 82 super(systemContext, tenantId, ruleChain.getId());
82 83 this.ruleChainName = ruleChain.getName();
83 84 this.parent = parent;
... ... @@ -95,7 +96,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
95 96 }
96 97
97 98 @Override
98   - public void start(ActorContext context) {
  99 + public void start(TbActorCtx context) {
99 100 if (!started) {
100 101 RuleChain ruleChain = service.findRuleChainById(tenantId, entityId);
101 102 if (ruleChain != null) {
... ... @@ -105,7 +106,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
105 106 // Creating and starting the actors;
106 107 for (RuleNode ruleNode : ruleNodeList) {
107 108 log.trace("[{}][{}] Creating rule node [{}]: {}", entityId, ruleNode.getId(), ruleNode.getName(), ruleNode);
108   - ActorRef ruleNodeActor = createRuleNodeActor(context, ruleNode);
  109 + TbActorRef ruleNodeActor = createRuleNodeActor(context, ruleNode);
109 110 nodeActors.put(ruleNode.getId(), new RuleNodeCtx(tenantId, self, ruleNodeActor, ruleNode));
110 111 }
111 112 initRoutes(ruleChain, ruleNodeList);
... ... @@ -118,7 +119,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
118 119 }
119 120
120 121 @Override
121   - public void onUpdate(ActorContext context) {
  122 + public void onUpdate(TbActorCtx context) {
122 123 RuleChain ruleChain = service.findRuleChainById(tenantId, entityId);
123 124 if (ruleChain != null) {
124 125 if (ruleChain.getType().equals(RuleChainType.CORE)) {
... ... @@ -129,22 +130,22 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
129 130 RuleNodeCtx existing = nodeActors.get(ruleNode.getId());
130 131 if (existing == null) {
131 132 log.trace("[{}][{}] Creating rule node [{}]: {}", entityId, ruleNode.getId(), ruleNode.getName(), ruleNode);
132   - ActorRef ruleNodeActor = createRuleNodeActor(context, ruleNode);
  133 + TbActorRef ruleNodeActor = createRuleNodeActor(context, ruleNode);
133 134 nodeActors.put(ruleNode.getId(), new RuleNodeCtx(tenantId, self, ruleNodeActor, ruleNode));
134 135 } else {
135 136 log.trace("[{}][{}] Updating rule node [{}]: {}", entityId, ruleNode.getId(), ruleNode.getName(), ruleNode);
136 137 existing.setSelf(ruleNode);
137   - existing.getSelfActor().tell(new ComponentLifecycleMsg(tenantId, existing.getSelf().getId(), ComponentLifecycleEvent.UPDATED), self);
  138 + existing.getSelfActor().tellWithHighPriority(new ComponentLifecycleMsg(tenantId, existing.getSelf().getId(), ComponentLifecycleEvent.UPDATED));
138 139 }
139 140 }
140 141
141   - Set<RuleNodeId> existingNodes = ruleNodeList.stream().map(RuleNode::getId).collect(Collectors.toSet());
142   - List<RuleNodeId> removedRules = nodeActors.keySet().stream().filter(node -> !existingNodes.contains(node)).collect(Collectors.toList());
143   - removedRules.forEach(ruleNodeId -> {
144   - log.trace("[{}][{}] Removing rule node [{}]", tenantId, entityId, ruleNodeId);
145   - RuleNodeCtx removed = nodeActors.remove(ruleNodeId);
146   - removed.getSelfActor().tell(new ComponentLifecycleMsg(tenantId, removed.getSelf().getId(), ComponentLifecycleEvent.DELETED), self);
147   - });
  142 + Set<RuleNodeId> existingNodes = ruleNodeList.stream().map(RuleNode::getId).collect(Collectors.toSet());
  143 + List<RuleNodeId> removedRules = nodeActors.keySet().stream().filter(node -> !existingNodes.contains(node)).collect(Collectors.toList());
  144 + removedRules.forEach(ruleNodeId -> {
  145 + log.trace("[{}][{}] Removing rule node [{}]", tenantId, entityId, ruleNodeId);
  146 + RuleNodeCtx removed = nodeActors.remove(ruleNodeId);
  147 + removed.getSelfActor().tellWithHighPriority(new ComponentLifecycleMsg(tenantId, removed.getSelf().getId(), ComponentLifecycleEvent.DELETED));
  148 + });
148 149
149 150 initRoutes(ruleChain, ruleNodeList);
150 151 } else if (ruleChain.getType().equals(RuleChainType.EDGE)) {
... ... @@ -154,26 +155,23 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
154 155 }
155 156
156 157 @Override
157   - public void stop(ActorContext context) {
  158 + public void stop(TbActorCtx ctx) {
158 159 log.trace("[{}][{}] Stopping rule chain with {} nodes", tenantId, entityId, nodeActors.size());
159   - nodeActors.values().stream().map(RuleNodeCtx::getSelfActor).forEach(context::stop);
  160 + nodeActors.values().stream().map(RuleNodeCtx::getSelfActor).map(TbActorRef::getActorId).forEach(ctx::stop);
160 161 nodeActors.clear();
161 162 nodeRoutes.clear();
162   - context.stop(self);
163 163 started = false;
164 164 }
165 165
166 166 @Override
167 167 public void onPartitionChangeMsg(PartitionChangeMsg msg) {
168   - nodeActors.values().stream().map(RuleNodeCtx::getSelfActor).forEach(actorRef -> actorRef.tell(msg, self));
  168 + nodeActors.values().stream().map(RuleNodeCtx::getSelfActor).forEach(actorRef -> actorRef.tellWithHighPriority(msg));
169 169 }
170 170
171   - private ActorRef createRuleNodeActor(ActorContext context, RuleNode ruleNode) {
172   - String dispatcherName = tenantId.getId().equals(EntityId.NULL_UUID) ?
173   - DefaultActorService.SYSTEM_RULE_DISPATCHER_NAME : DefaultActorService.TENANT_RULE_DISPATCHER_NAME;
174   - return context.actorOf(
175   - Props.create(new RuleNodeActor.ActorCreator(systemContext, tenantId, entityId, ruleNode.getName(), ruleNode.getId()))
176   - .withDispatcher(dispatcherName), ruleNode.getId().toString());
  171 + private TbActorRef createRuleNodeActor(TbActorCtx ctx, RuleNode ruleNode) {
  172 + return ctx.getOrCreateChildActor(new TbEntityActorId(ruleNode.getId()),
  173 + () -> DefaultActorService.RULE_DISPATCHER_NAME,
  174 + () -> new RuleNodeActor.ActorCreator(systemContext, tenantId, entityId, ruleNode.getName(), ruleNode.getId()));
177 175 }
178 176
179 177 private void initRoutes(RuleChain ruleChain, List<RuleNode> ruleNodeList) {
... ... @@ -256,7 +254,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
256 254 try {
257 255 checkActive(msg);
258 256 EntityId entityId = msg.getOriginator();
259   - TopicPartitionInfo tpi = systemContext.resolve(ServiceType.TB_RULE_ENGINE, tenantId, entityId);
  257 + TopicPartitionInfo tpi = systemContext.resolve(ServiceType.TB_RULE_ENGINE, msg.getQueueName(), tenantId, entityId);
260 258 List<RuleNodeRelation> relations = nodeRoutes.get(originatorNodeId).stream()
261 259 .filter(r -> contains(relationTypes, r.getType()))
262 260 .collect(Collectors.toList());
... ... @@ -312,7 +310,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
312 310 pushMsgToNode(nodeActors.get(new RuleNodeId(target.getId())), msg, fromRelationType);
313 311 break;
314 312 case RULE_CHAIN:
315   - parent.tell(new RuleChainToRuleChainMsg(new RuleChainId(target.getId()), entityId, msg, fromRelationType), self);
  313 + parent.tell(new RuleChainToRuleChainMsg(new RuleChainId(target.getId()), entityId, msg, fromRelationType));
316 314 break;
317 315 }
318 316 } else {
... ... @@ -343,7 +341,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
343 341
344 342 private void pushMsgToNode(RuleNodeCtx nodeCtx, TbMsg msg, String fromRelationType) {
345 343 if (nodeCtx != null) {
346   - nodeCtx.getSelfActor().tell(new RuleChainToRuleNodeMsg(new DefaultTbContext(systemContext, nodeCtx), msg, fromRelationType), self);
  344 + nodeCtx.getSelfActor().tell(new RuleChainToRuleNodeMsg(new DefaultTbContext(systemContext, nodeCtx), msg, fromRelationType));
347 345 } else {
348 346 log.error("[{}][{}] RuleNodeCtx is empty", entityId, ruleChainName);
349 347 msg.getCallback().onFailure(new RuleEngineException("Rule Node CTX is empty"));
... ...
... ... @@ -15,21 +15,22 @@
15 15 */
16 16 package org.thingsboard.server.actors.ruleChain;
17 17
18   -import akka.actor.ActorContext;
19   -import akka.actor.ActorRef;
20   -import akka.actor.Props;
21   -import com.google.common.collect.BiMap;
22   -import com.google.common.collect.HashBiMap;
23 18 import lombok.Getter;
  19 +import lombok.extern.slf4j.Slf4j;
24 20 import org.thingsboard.server.actors.ActorSystemContext;
  21 +import org.thingsboard.server.actors.TbActorRef;
  22 +import org.thingsboard.server.actors.TbEntityActorId;
  23 +import org.thingsboard.server.actors.TbEntityTypeActorIdPredicate;
25 24 import org.thingsboard.server.actors.service.ContextAwareActor;
26 25 import org.thingsboard.server.actors.service.DefaultActorService;
  26 +import org.thingsboard.server.actors.tenant.TenantActor;
27 27 import org.thingsboard.server.common.data.EntityType;
28 28 import org.thingsboard.server.common.data.id.EntityId;
29 29 import org.thingsboard.server.common.data.id.RuleChainId;
30 30 import org.thingsboard.server.common.data.id.TenantId;
31 31 import org.thingsboard.server.common.data.page.PageDataIterable;
32 32 import org.thingsboard.server.common.data.rule.RuleChain;
  33 +import org.thingsboard.server.common.msg.TbActorMsg;
33 34 import org.thingsboard.server.common.data.rule.RuleChainType;
34 35 import org.thingsboard.server.dao.rule.RuleChainService;
35 36
... ... @@ -38,20 +39,19 @@ import java.util.function.Function;
38 39 /**
39 40 * Created by ashvayka on 15.03.18.
40 41 */
  42 +@Slf4j
41 43 public abstract class RuleChainManagerActor extends ContextAwareActor {
42 44
43 45 protected final TenantId tenantId;
44 46 private final RuleChainService ruleChainService;
45   - private final BiMap<RuleChainId, ActorRef> actors;
46 47 @Getter
47 48 protected RuleChain rootChain;
48 49 @Getter
49   - protected ActorRef rootChainActor;
  50 + protected TbActorRef rootChainActor;
50 51
51 52 public RuleChainManagerActor(ActorSystemContext systemContext, TenantId tenantId) {
52 53 super(systemContext);
53 54 this.tenantId = tenantId;
54   - this.actors = HashBiMap.create();
55 55 this.ruleChainService = systemContext.getRuleChainService();
56 56 }
57 57
... ... @@ -59,46 +59,41 @@ public abstract class RuleChainManagerActor extends ContextAwareActor {
59 59 for (RuleChain ruleChain : new PageDataIterable<>(link -> ruleChainService.findTenantRuleChainsByType(tenantId, RuleChainType.CORE, link), ContextAwareActor.ENTITY_PACK_LIMIT)) {
60 60 RuleChainId ruleChainId = ruleChain.getId();
61 61 log.debug("[{}|{}] Creating rule chain actor", ruleChainId.getEntityType(), ruleChain.getId());
62   - //TODO: remove this cast making UUIDBased subclass of EntityId an interface and vice versa.
63   - ActorRef actorRef = getOrCreateActor(this.context(), ruleChainId, id -> ruleChain);
  62 + TbActorRef actorRef = getOrCreateActor(ruleChainId, id -> ruleChain);
64 63 visit(ruleChain, actorRef);
65 64 log.debug("[{}|{}] Rule Chain actor created.", ruleChainId.getEntityType(), ruleChainId.getId());
66 65 }
67 66 }
68 67
69   - protected void visit(RuleChain entity, ActorRef actorRef) {
  68 + protected void visit(RuleChain entity, TbActorRef actorRef) {
70 69 if (entity != null && entity.isRoot() && entity.getType().equals(RuleChainType.CORE)) {
71 70 rootChain = entity;
72 71 rootChainActor = actorRef;
73 72 }
74 73 }
75 74
76   - public ActorRef getOrCreateActor(akka.actor.ActorContext context, RuleChainId ruleChainId) {
77   - return getOrCreateActor(context, ruleChainId, eId -> ruleChainService.findRuleChainById(TenantId.SYS_TENANT_ID, eId));
  75 + protected TbActorRef getOrCreateActor(RuleChainId ruleChainId) {
  76 + return getOrCreateActor(ruleChainId, eId -> ruleChainService.findRuleChainById(TenantId.SYS_TENANT_ID, eId));
78 77 }
79 78
80   - public ActorRef getOrCreateActor(akka.actor.ActorContext context, RuleChainId ruleChainId, Function<RuleChainId, RuleChain> provider) {
81   - return actors.computeIfAbsent(ruleChainId, eId -> {
82   - RuleChain ruleChain = provider.apply(eId);
83   - return context.actorOf(Props.create(new RuleChainActor.ActorCreator(systemContext, tenantId, ruleChain))
84   - .withDispatcher(DefaultActorService.TENANT_RULE_DISPATCHER_NAME), eId.toString());
85   - });
  79 + protected TbActorRef getOrCreateActor(RuleChainId ruleChainId, Function<RuleChainId, RuleChain> provider) {
  80 + return ctx.getOrCreateChildActor(new TbEntityActorId(ruleChainId),
  81 + () -> DefaultActorService.RULE_DISPATCHER_NAME,
  82 + () -> {
  83 + RuleChain ruleChain = provider.apply(ruleChainId);
  84 + return new RuleChainActor.ActorCreator(systemContext, tenantId, ruleChain);
  85 + });
86 86 }
87 87
88   - protected ActorRef getEntityActorRef(EntityId entityId) {
89   - ActorRef target = null;
  88 + protected TbActorRef getEntityActorRef(EntityId entityId) {
  89 + TbActorRef target = null;
90 90 if (entityId.getEntityType() == EntityType.RULE_CHAIN) {
91   - target = getOrCreateActor(this.context(), (RuleChainId) entityId);
  91 + target = getOrCreateActor((RuleChainId) entityId);
92 92 }
93 93 return target;
94 94 }
95 95
96   - protected void broadcast(Object msg) {
97   - actors.values().forEach(actorRef -> actorRef.tell(msg, ActorRef.noSender()));
  96 + protected void broadcast(TbActorMsg msg) {
  97 + ctx.broadcastToChildren(msg, new TbEntityTypeActorIdPredicate(EntityType.RULE_CHAIN));
98 98 }
99   -
100   - public ActorRef get(RuleChainId id) {
101   - return actors.get(id);
102   - }
103   -
104 99 }
... ...
... ... @@ -15,31 +15,43 @@
15 15 */
16 16 package org.thingsboard.server.actors.ruleChain;
17 17
  18 +import lombok.extern.slf4j.Slf4j;
18 19 import org.thingsboard.server.actors.ActorSystemContext;
  20 +import org.thingsboard.server.actors.TbActor;
  21 +import org.thingsboard.server.actors.TbActorCtx;
  22 +import org.thingsboard.server.actors.TbActorId;
  23 +import org.thingsboard.server.actors.TbEntityActorId;
19 24 import org.thingsboard.server.actors.service.ComponentActor;
20 25 import org.thingsboard.server.actors.service.ContextBasedCreator;
21 26 import org.thingsboard.server.common.data.id.RuleChainId;
22 27 import org.thingsboard.server.common.data.id.RuleNodeId;
23 28 import org.thingsboard.server.common.data.id.TenantId;
  29 +import org.thingsboard.server.common.data.rule.RuleChain;
24 30 import org.thingsboard.server.common.msg.TbActorMsg;
25 31 import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
26 32 import org.thingsboard.server.common.msg.queue.PartitionChangeMsg;
27 33
  34 +@Slf4j
28 35 public class RuleNodeActor extends ComponentActor<RuleNodeId, RuleNodeActorMessageProcessor> {
29 36
30 37 private final String ruleChainName;
31 38 private final RuleChainId ruleChainId;
  39 + private final RuleNodeId ruleNodeId;
32 40
33 41 private RuleNodeActor(ActorSystemContext systemContext, TenantId tenantId, RuleChainId ruleChainId, String ruleChainName, RuleNodeId ruleNodeId) {
34 42 super(systemContext, tenantId, ruleNodeId);
35 43 this.ruleChainName = ruleChainName;
36 44 this.ruleChainId = ruleChainId;
37   - setProcessor(new RuleNodeActorMessageProcessor(tenantId, this.ruleChainName, ruleNodeId, systemContext,
38   - context().parent(), context().self()));
  45 + this.ruleNodeId = ruleNodeId;
39 46 }
40 47
41 48 @Override
42   - protected boolean process(TbActorMsg msg) {
  49 + protected RuleNodeActorMessageProcessor createProcessor(TbActorCtx ctx) {
  50 + return new RuleNodeActorMessageProcessor(tenantId, this.ruleChainName, ruleNodeId, systemContext, ctx.getParentRef(), ctx);
  51 + }
  52 +
  53 + @Override
  54 + protected boolean doProcess(TbActorMsg msg) {
43 55 switch (msg.getMsgType()) {
44 56 case COMPONENT_LIFE_CYCLE_MSG:
45 57 onComponentLifecycleMsg((ComponentLifecycleMsg) msg);
... ... @@ -93,8 +105,7 @@ public class RuleNodeActor extends ComponentActor<RuleNodeId, RuleNodeActorMessa
93 105 logAndPersist("onRuleMsg", ActorSystemContext.toException(msg.getError()));
94 106 }
95 107
96   - public static class ActorCreator extends ContextBasedCreator<RuleNodeActor> {
97   - private static final long serialVersionUID = 1L;
  108 + public static class ActorCreator extends ContextBasedCreator {
98 109
99 110 private final TenantId tenantId;
100 111 private final RuleChainId ruleChainId;
... ... @@ -111,7 +122,12 @@ public class RuleNodeActor extends ComponentActor<RuleNodeId, RuleNodeActorMessa
111 122 }
112 123
113 124 @Override
114   - public RuleNodeActor create() throws Exception {
  125 + public TbActorId createActorId() {
  126 + return new TbEntityActorId(ruleNodeId);
  127 + }
  128 +
  129 + @Override
  130 + public TbActor createActor() {
115 131 return new RuleNodeActor(context, tenantId, ruleChainId, ruleChainName, ruleNodeId);
116 132 }
117 133 }
... ...
... ... @@ -15,11 +15,11 @@
15 15 */
16 16 package org.thingsboard.server.actors.ruleChain;
17 17
18   -import akka.actor.ActorContext;
19   -import akka.actor.ActorRef;
20 18 import org.thingsboard.rule.engine.api.TbNode;
21 19 import org.thingsboard.rule.engine.api.TbNodeConfiguration;
22 20 import org.thingsboard.server.actors.ActorSystemContext;
  21 +import org.thingsboard.server.actors.TbActorCtx;
  22 +import org.thingsboard.server.actors.TbActorRef;
23 23 import org.thingsboard.server.actors.shared.ComponentMsgProcessor;
24 24 import org.thingsboard.server.common.data.id.RuleNodeId;
25 25 import org.thingsboard.server.common.data.id.TenantId;
... ... @@ -34,13 +34,13 @@ import org.thingsboard.server.common.msg.queue.RuleNodeException;
34 34 public class RuleNodeActorMessageProcessor extends ComponentMsgProcessor<RuleNodeId> {
35 35
36 36 private final String ruleChainName;
37   - private final ActorRef self;
  37 + private final TbActorRef self;
38 38 private RuleNode ruleNode;
39 39 private TbNode tbNode;
40 40 private DefaultTbContext defaultCtx;
41 41
42 42 RuleNodeActorMessageProcessor(TenantId tenantId, String ruleChainName, RuleNodeId ruleNodeId, ActorSystemContext systemContext
43   - , ActorRef parent, ActorRef self) {
  43 + , TbActorRef parent, TbActorRef self) {
44 44 super(systemContext, tenantId, ruleNodeId);
45 45 this.ruleChainName = ruleChainName;
46 46 this.self = self;
... ... @@ -49,7 +49,7 @@ public class RuleNodeActorMessageProcessor extends ComponentMsgProcessor<RuleNod
49 49 }
50 50
51 51 @Override
52   - public void start(ActorContext context) throws Exception {
  52 + public void start(TbActorCtx context) throws Exception {
53 53 tbNode = initComponent(ruleNode);
54 54 if (tbNode != null) {
55 55 state = ComponentLifecycleState.ACTIVE;
... ... @@ -57,7 +57,7 @@ public class RuleNodeActorMessageProcessor extends ComponentMsgProcessor<RuleNod
57 57 }
58 58
59 59 @Override
60   - public void onUpdate(ActorContext context) throws Exception {
  60 + public void onUpdate(TbActorCtx context) throws Exception {
61 61 RuleNode newRuleNode = systemContext.getRuleChainService().findRuleNodeById(tenantId, entityId);
62 62 boolean restartRequired = state != ComponentLifecycleState.ACTIVE ||
63 63 !(ruleNode.getType().equals(newRuleNode.getType()) && ruleNode.getConfiguration().equals(newRuleNode.getConfiguration()));
... ... @@ -72,11 +72,11 @@ public class RuleNodeActorMessageProcessor extends ComponentMsgProcessor<RuleNod
72 72 }
73 73
74 74 @Override
75   - public void stop(ActorContext context) {
  75 + public void stop(TbActorCtx context) {
76 76 if (tbNode != null) {
77 77 tbNode.destroy();
  78 + state = ComponentLifecycleState.SUSPENDED;
78 79 }
79   - context.stop(self);
80 80 }
81 81
82 82 @Override
... ...
... ... @@ -15,9 +15,9 @@
15 15 */
16 16 package org.thingsboard.server.actors.ruleChain;
17 17
18   -import akka.actor.ActorRef;
19 18 import lombok.AllArgsConstructor;
20 19 import lombok.Data;
  20 +import org.thingsboard.server.actors.TbActorRef;
21 21 import org.thingsboard.server.common.data.id.TenantId;
22 22 import org.thingsboard.server.common.data.rule.RuleNode;
23 23
... ... @@ -28,7 +28,7 @@ import org.thingsboard.server.common.data.rule.RuleNode;
28 28 @AllArgsConstructor
29 29 final class RuleNodeCtx {
30 30 private final TenantId tenantId;
31   - private final ActorRef chainActor;
32   - private final ActorRef selfActor;
  31 + private final TbActorRef chainActor;
  32 + private final TbActorRef selfActor;
33 33 private RuleNode self;
34 34 }
... ...
... ... @@ -15,19 +15,23 @@
15 15 */
16 16 package org.thingsboard.server.actors.service;
17 17
18   -import akka.actor.ActorRef;
  18 +import lombok.extern.slf4j.Slf4j;
19 19 import org.thingsboard.server.actors.ActorSystemContext;
  20 +import org.thingsboard.server.actors.TbActor;
  21 +import org.thingsboard.server.actors.TbActorCtx;
  22 +import org.thingsboard.server.actors.TbActorException;
20 23 import org.thingsboard.server.actors.shared.ComponentMsgProcessor;
21 24 import org.thingsboard.server.actors.stats.StatsPersistMsg;
22 25 import org.thingsboard.server.common.data.id.EntityId;
23 26 import org.thingsboard.server.common.data.id.TenantId;
24 27 import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
25   -import org.thingsboard.server.common.msg.queue.PartitionChangeMsg;
26 28 import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
  29 +import org.thingsboard.server.common.msg.queue.PartitionChangeMsg;
27 30
28 31 /**
29 32 * @author Andrew Shvayka
30 33 */
  34 +@Slf4j
31 35 public abstract class ComponentActor<T extends EntityId, P extends ComponentMsgProcessor<T>> extends ContextAwareActor {
32 36
33 37 private long lastPersistedErrorTs = 0L;
... ... @@ -43,30 +47,34 @@ public abstract class ComponentActor<T extends EntityId, P extends ComponentMsgP
43 47 this.id = id;
44 48 }
45 49
46   - protected void setProcessor(P processor) {
47   - this.processor = processor;
48   - }
  50 + abstract protected P createProcessor(TbActorCtx ctx);
49 51
50 52 @Override
51   - public void preStart() {
  53 + public void init(TbActorCtx ctx) throws TbActorException {
  54 + super.init(ctx);
  55 + this.processor = createProcessor(ctx);
  56 + initProcessor(ctx);
  57 + }
  58 +
  59 + protected void initProcessor(TbActorCtx ctx) throws TbActorException {
52 60 try {
53 61 log.debug("[{}][{}][{}] Starting processor.", tenantId, id, id.getEntityType());
54   - processor.start(context());
  62 + processor.start(ctx);
55 63 logLifecycleEvent(ComponentLifecycleEvent.STARTED);
56 64 if (systemContext.isStatisticsEnabled()) {
57 65 scheduleStatsPersistTick();
58 66 }
59 67 } catch (Exception e) {
60   - log.warn("[{}][{}] Failed to start {} processor.", tenantId, id, id.getEntityType());
61   - log.warn("Error:", e);
  68 + log.debug("[{}][{}] Failed to start {} processor.", tenantId, id, id.getEntityType(), e);
62 69 logAndPersist("OnStart", e, true);
63 70 logLifecycleEvent(ComponentLifecycleEvent.STARTED, e);
  71 + throw new TbActorException("Failed to init actor", e);
64 72 }
65 73 }
66 74
67 75 private void scheduleStatsPersistTick() {
68 76 try {
69   - processor.scheduleStatsPersistTick(context(), systemContext.getStatisticsPersistFrequency());
  77 + processor.scheduleStatsPersistTick(ctx, systemContext.getStatisticsPersistFrequency());
70 78 } catch (Exception e) {
71 79 log.error("[{}][{}] Failed to schedule statistics store message. No statistics is going to be stored: {}", tenantId, id, e.getMessage());
72 80 logAndPersist("onScheduleStatsPersistMsg", e);
... ... @@ -74,10 +82,12 @@ public abstract class ComponentActor<T extends EntityId, P extends ComponentMsgP
74 82 }
75 83
76 84 @Override
77   - public void postStop() {
  85 + public void destroy() {
78 86 try {
79   - log.debug("[{}][{}] Stopping processor.", tenantId, id, id.getEntityType());
80   - processor.stop(context());
  87 + log.debug("[{}][{}][{}] Stopping processor.", tenantId, id, id.getEntityType());
  88 + if (processor != null) {
  89 + processor.stop(ctx);
  90 + }
81 91 logLifecycleEvent(ComponentLifecycleEvent.STOPPED);
82 92 } catch (Exception e) {
83 93 log.warn("[{}][{}] Failed to stop {} processor: {}", tenantId, id, id.getEntityType(), e.getMessage());
... ... @@ -91,19 +101,20 @@ public abstract class ComponentActor<T extends EntityId, P extends ComponentMsgP
91 101 try {
92 102 switch (msg.getEvent()) {
93 103 case CREATED:
94   - processor.onCreated(context());
  104 + processor.onCreated(ctx);
95 105 break;
96 106 case UPDATED:
97   - processor.onUpdate(context());
  107 + processor.onUpdate(ctx);
98 108 break;
99 109 case ACTIVATED:
100   - processor.onActivate(context());
  110 + processor.onActivate(ctx);
101 111 break;
102 112 case SUSPENDED:
103   - processor.onSuspend(context());
  113 + processor.onSuspend(ctx);
104 114 break;
105 115 case DELETED:
106   - processor.onStop(context());
  116 + processor.onStop(ctx);
  117 + ctx.stop(ctx.getSelf());
107 118 break;
108 119 default:
109 120 break;
... ... @@ -125,7 +136,7 @@ public abstract class ComponentActor<T extends EntityId, P extends ComponentMsgP
125 136
126 137 protected void onStatsPersistTick(EntityId entityId) {
127 138 try {
128   - systemContext.getStatsActor().tell(new StatsPersistMsg(messagesProcessed, errorsOccurred, tenantId, entityId), ActorRef.noSender());
  139 + systemContext.getStatsActor().tell(new StatsPersistMsg(messagesProcessed, errorsOccurred, tenantId, entityId));
129 140 resetStatsCounters();
130 141 } catch (Exception e) {
131 142 logAndPersist("onStatsPersistTick", e);
... ... @@ -149,11 +160,11 @@ public abstract class ComponentActor<T extends EntityId, P extends ComponentMsgP
149 160 errorsOccurred++;
150 161 String componentName = processor != null ? processor.getComponentName() : "Unknown";
151 162 if (critical) {
152   - log.warn("[{}][{}][{}] Failed to process {} msg: {}", id, tenantId, componentName, method);
153   - log.warn("Critical Error: ", e);
  163 + log.debug("[{}][{}][{}] Failed to process method: {}", id, tenantId, componentName, method);
  164 + log.debug("Critical Error: ", e);
154 165 } else {
155   - log.debug("[{}][{}][{}] Failed to process {} msg: {}", id, tenantId, componentName, method);
156   - log.debug("Debug Error: ", e);
  166 + log.trace("[{}][{}][{}] Failed to process method: {}", id, tenantId, componentName, method);
  167 + log.trace("Debug Error: ", e);
157 168 }
158 169 long ts = System.currentTimeMillis();
159 170 if (ts - lastPersistedErrorTs > getErrorPersistFrequency()) {
... ...
... ... @@ -15,17 +15,18 @@
15 15 */
16 16 package org.thingsboard.server.actors.service;
17 17
18   -import akka.actor.Terminated;
19   -import akka.actor.UntypedAbstractActor;
  18 +import lombok.extern.slf4j.Slf4j;
20 19 import org.slf4j.Logger;
21 20 import org.slf4j.LoggerFactory;
  21 +import org.thingsboard.server.actors.AbstractTbActor;
22 22 import org.thingsboard.server.actors.ActorSystemContext;
  23 +import org.thingsboard.server.actors.ProcessFailureStrategy;
  24 +import org.thingsboard.server.actors.TbActor;
  25 +import org.thingsboard.server.actors.TbActorCtx;
23 26 import org.thingsboard.server.common.msg.TbActorMsg;
24 27
25   -
26   -public abstract class ContextAwareActor extends UntypedAbstractActor {
27   -
28   - protected final Logger log = LoggerFactory.getLogger(getClass());
  28 +@Slf4j
  29 +public abstract class ContextAwareActor extends AbstractTbActor {
29 30
30 31 public static final int ENTITY_PACK_LIMIT = 1024;
31 32
... ... @@ -37,27 +38,29 @@ public abstract class ContextAwareActor extends UntypedAbstractActor {
37 38 }
38 39
39 40 @Override
40   - public void onReceive(Object msg) {
  41 + public boolean process(TbActorMsg msg) {
41 42 if (log.isDebugEnabled()) {
42 43 log.debug("Processing msg: {}", msg);
43 44 }
44   - if (msg instanceof TbActorMsg) {
45   - try {
46   - if (!process((TbActorMsg) msg)) {
47   - log.warn("Unknown message: {}!", msg);
48   - }
49   - } catch (Exception e) {
50   - throw e;
51   - }
52   - } else if (msg instanceof Terminated) {
53   - processTermination((Terminated) msg);
54   - } else {
55   - log.warn("Unknown message: {}!", msg);
  45 + if (!doProcess(msg)) {
  46 + log.warn("Unprocessed message: {}!", msg);
56 47 }
  48 + return false;
57 49 }
58 50
59   - protected void processTermination(Terminated msg) {
  51 + protected abstract boolean doProcess(TbActorMsg msg);
  52 +
  53 + @Override
  54 + public ProcessFailureStrategy onProcessFailure(Throwable t) {
  55 + log.debug("[{}] Processing failure: ", getActorRef().getActorId(), t);
  56 + return doProcessFailure(t);
60 57 }
61 58
62   - protected abstract boolean process(TbActorMsg msg);
  59 + protected ProcessFailureStrategy doProcessFailure(Throwable t) {
  60 + if (t instanceof Error) {
  61 + return ProcessFailureStrategy.stop();
  62 + } else {
  63 + return ProcessFailureStrategy.resume();
  64 + }
  65 + }
63 66 }
... ...
... ... @@ -15,12 +15,10 @@
15 15 */
16 16 package org.thingsboard.server.actors.service;
17 17
18   -import akka.japi.Creator;
19 18 import org.thingsboard.server.actors.ActorSystemContext;
  19 +import org.thingsboard.server.actors.TbActorCreator;
20 20
21   -public abstract class ContextBasedCreator<T> implements Creator<T> {
22   -
23   - private static final long serialVersionUID = 1L;
  21 +public abstract class ContextBasedCreator implements TbActorCreator {
24 22
25 23 protected final transient ActorSystemContext context;
26 24
... ...
... ... @@ -15,85 +15,121 @@
15 15 */
16 16 package org.thingsboard.server.actors.service;
17 17
18   -import akka.actor.ActorRef;
19   -import akka.actor.ActorSystem;
20   -import akka.actor.Props;
21   -import akka.actor.Terminated;
22 18 import lombok.extern.slf4j.Slf4j;
23 19 import org.springframework.beans.factory.annotation.Autowired;
24 20 import org.springframework.beans.factory.annotation.Value;
25 21 import org.springframework.boot.context.event.ApplicationReadyEvent;
26 22 import org.springframework.context.event.EventListener;
27   -import org.springframework.scheduling.annotation.Scheduled;
28 23 import org.springframework.stereotype.Service;
  24 +import org.thingsboard.common.util.ThingsBoardThreadFactory;
29 25 import org.thingsboard.server.actors.ActorSystemContext;
  26 +import org.thingsboard.server.actors.DefaultTbActorSystem;
  27 +import org.thingsboard.server.actors.TbActorId;
  28 +import org.thingsboard.server.actors.TbActorRef;
  29 +import org.thingsboard.server.actors.TbActorSystem;
  30 +import org.thingsboard.server.actors.TbActorSystemSettings;
30 31 import org.thingsboard.server.actors.app.AppActor;
31 32 import org.thingsboard.server.actors.app.AppInitMsg;
32 33 import org.thingsboard.server.actors.stats.StatsActor;
33 34 import org.thingsboard.server.common.msg.queue.PartitionChangeMsg;
34 35 import org.thingsboard.server.queue.discovery.PartitionChangeEvent;
35   -import scala.concurrent.Await;
36   -import scala.concurrent.Future;
37   -import scala.concurrent.duration.Duration;
38 36
39 37 import javax.annotation.PostConstruct;
40 38 import javax.annotation.PreDestroy;
41   -import java.util.concurrent.atomic.AtomicInteger;
  39 +import java.util.concurrent.ExecutorService;
  40 +import java.util.concurrent.Executors;
  41 +import java.util.concurrent.ScheduledExecutorService;
42 42
43 43 @Service
44 44 @Slf4j
45 45 public class DefaultActorService implements ActorService {
46 46
47   - private static final String ACTOR_SYSTEM_NAME = "Akka";
48   -
49 47 public static final String APP_DISPATCHER_NAME = "app-dispatcher";
50   - public static final String CORE_DISPATCHER_NAME = "core-dispatcher";
51   - public static final String SYSTEM_RULE_DISPATCHER_NAME = "system-rule-dispatcher";
52   - public static final String TENANT_RULE_DISPATCHER_NAME = "rule-dispatcher";
  48 + public static final String TENANT_DISPATCHER_NAME = "tenant-dispatcher";
  49 + public static final String DEVICE_DISPATCHER_NAME = "device-dispatcher";
  50 + public static final String RULE_DISPATCHER_NAME = "rule-dispatcher";
53 51
54 52 @Autowired
55 53 private ActorSystemContext actorContext;
56 54
57   - private ActorSystem system;
  55 + private TbActorSystem system;
  56 +
  57 + private TbActorRef appActor;
  58 +
  59 + @Value("${actors.system.throughput:5}")
  60 + private int actorThroughput;
  61 +
  62 + @Value("${actors.system.max_actor_init_attempts:10}")
  63 + private int maxActorInitAttempts;
  64 +
  65 + @Value("${actors.system.scheduler_pool_size:1}")
  66 + private int schedulerPoolSize;
  67 +
  68 + @Value("${actors.system.app_dispatcher_pool_size:1}")
  69 + private int appDispatcherSize;
  70 +
  71 + @Value("${actors.system.tenant_dispatcher_pool_size:2}")
  72 + private int tenantDispatcherSize;
58 73
59   - private ActorRef appActor;
  74 + @Value("${actors.system.device_dispatcher_pool_size:4}")
  75 + private int deviceDispatcherSize;
  76 +
  77 + @Value("${actors.system.rule_dispatcher_pool_size:4}")
  78 + private int ruleDispatcherSize;
60 79
61 80 @PostConstruct
62 81 public void initActorSystem() {
63   - log.info("Initializing Actor system.");
  82 + log.info("Initializing actor system.");
64 83 actorContext.setActorService(this);
65   - system = ActorSystem.create(ACTOR_SYSTEM_NAME, actorContext.getConfig());
  84 + TbActorSystemSettings settings = new TbActorSystemSettings(actorThroughput, schedulerPoolSize, maxActorInitAttempts);
  85 + system = new DefaultTbActorSystem(settings);
  86 +
  87 + system.createDispatcher(APP_DISPATCHER_NAME, initDispatcherExecutor(APP_DISPATCHER_NAME, appDispatcherSize));
  88 + system.createDispatcher(TENANT_DISPATCHER_NAME, initDispatcherExecutor(TENANT_DISPATCHER_NAME, tenantDispatcherSize));
  89 + system.createDispatcher(DEVICE_DISPATCHER_NAME, initDispatcherExecutor(DEVICE_DISPATCHER_NAME, deviceDispatcherSize));
  90 + system.createDispatcher(RULE_DISPATCHER_NAME, initDispatcherExecutor(RULE_DISPATCHER_NAME, ruleDispatcherSize));
  91 +
66 92 actorContext.setActorSystem(system);
67 93
68   - appActor = system.actorOf(Props.create(new AppActor.ActorCreator(actorContext)).withDispatcher(APP_DISPATCHER_NAME), "appActor");
  94 + appActor = system.createRootActor(APP_DISPATCHER_NAME, new AppActor.ActorCreator(actorContext));
69 95 actorContext.setAppActor(appActor);
70 96
71   - ActorRef statsActor = system.actorOf(Props.create(new StatsActor.ActorCreator(actorContext)).withDispatcher(CORE_DISPATCHER_NAME), "statsActor");
  97 + TbActorRef statsActor = system.createRootActor(TENANT_DISPATCHER_NAME, new StatsActor.ActorCreator(actorContext, "StatsActor"));
72 98 actorContext.setStatsActor(statsActor);
73 99
74 100 log.info("Actor system initialized.");
75 101 }
76 102
  103 + private ExecutorService initDispatcherExecutor(String dispatcherName, int poolSize) {
  104 + if (poolSize == 0) {
  105 + int cores = Runtime.getRuntime().availableProcessors();
  106 + poolSize = Math.max(1, cores / 2);
  107 + }
  108 + if (poolSize == 1) {
  109 + return Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName(dispatcherName));
  110 + } else {
  111 + return Executors.newWorkStealingPool(poolSize);
  112 + }
  113 + }
  114 +
77 115 @EventListener(ApplicationReadyEvent.class)
78 116 public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) {
79 117 log.info("Received application ready event. Sending application init message to actor system");
80   - appActor.tell(new AppInitMsg(), ActorRef.noSender());
  118 + appActor.tellWithHighPriority(new AppInitMsg());
81 119 }
82 120
83 121 @EventListener(PartitionChangeEvent.class)
84 122 public void onApplicationEvent(PartitionChangeEvent partitionChangeEvent) {
85 123 log.info("Received partition change event.");
86   - this.appActor.tell(new PartitionChangeMsg(partitionChangeEvent.getServiceQueueKey(), partitionChangeEvent.getPartitions()), ActorRef.noSender());
  124 + this.appActor.tellWithHighPriority(new PartitionChangeMsg(partitionChangeEvent.getServiceQueueKey(), partitionChangeEvent.getPartitions()));
87 125 }
88 126
89 127 @PreDestroy
90 128 public void stopActorSystem() {
91   - Future<Terminated> status = system.terminate();
92   - try {
93   - Terminated terminated = Await.result(status, Duration.Inf());
94   - log.info("Actor system terminated: {}", terminated);
95   - } catch (Exception e) {
96   - log.error("Failed to terminate actor system.", e);
  129 + if (system != null) {
  130 + log.info("Stopping actor system.");
  131 + system.stop();
  132 + log.info("Actor system stopped.");
97 133 }
98 134 }
99 135
... ...
... ... @@ -15,18 +15,13 @@
15 15 */
16 16 package org.thingsboard.server.actors.shared;
17 17
18   -import akka.actor.ActorContext;
19   -import akka.actor.ActorRef;
20   -import akka.actor.Scheduler;
21   -import akka.event.LoggingAdapter;
22 18 import com.fasterxml.jackson.databind.ObjectMapper;
23   -import lombok.AllArgsConstructor;
24   -import lombok.Data;
25 19 import lombok.extern.slf4j.Slf4j;
26 20 import org.thingsboard.server.actors.ActorSystemContext;
27   -import scala.concurrent.ExecutionContextExecutor;
28   -import scala.concurrent.duration.Duration;
  21 +import org.thingsboard.server.actors.TbActorCtx;
  22 +import org.thingsboard.server.common.msg.TbActorMsg;
29 23
  24 +import java.util.concurrent.ScheduledExecutorService;
30 25 import java.util.concurrent.TimeUnit;
31 26
32 27 @Slf4j
... ... @@ -40,31 +35,16 @@ public abstract class AbstractContextAwareMsgProcessor {
40 35 this.systemContext = systemContext;
41 36 }
42 37
43   - private Scheduler getScheduler() {
  38 + private ScheduledExecutorService getScheduler() {
44 39 return systemContext.getScheduler();
45 40 }
46 41
47   - private ExecutionContextExecutor getSystemDispatcher() {
48   - return systemContext.getActorSystem().dispatcher();
  42 + protected void schedulePeriodicMsgWithDelay(TbActorCtx ctx, TbActorMsg msg, long delayInMs, long periodInMs) {
  43 + systemContext.schedulePeriodicMsgWithDelay(ctx, msg, delayInMs, periodInMs);
49 44 }
50 45
51   - protected void schedulePeriodicMsgWithDelay(ActorContext ctx, Object msg, long delayInMs, long periodInMs) {
52   - schedulePeriodicMsgWithDelay(msg, delayInMs, periodInMs, ctx.self());
  46 + protected void scheduleMsgWithDelay(TbActorCtx ctx, TbActorMsg msg, long delayInMs) {
  47 + systemContext.scheduleMsgWithDelay(ctx, msg, delayInMs);
53 48 }
54 49
55   - private void schedulePeriodicMsgWithDelay(Object msg, long delayInMs, long periodInMs, ActorRef target) {
56   - log.debug("Scheduling periodic msg {} every {} ms with delay {} ms", msg, periodInMs, delayInMs);
57   - getScheduler().schedule(Duration.create(delayInMs, TimeUnit.MILLISECONDS), Duration.create(periodInMs, TimeUnit.MILLISECONDS), target, msg, getSystemDispatcher(), null);
58   - }
59   -
60   - protected void scheduleMsgWithDelay(ActorContext ctx, Object msg, long delayInMs) {
61   - scheduleMsgWithDelay(msg, delayInMs, ctx.self());
62   - }
63   -
64   - private void scheduleMsgWithDelay(Object msg, long delayInMs, ActorRef target) {
65   - log.debug("Scheduling msg {} with delay {} ms", msg, delayInMs);
66   - getScheduler().scheduleOnce(Duration.create(delayInMs, TimeUnit.MILLISECONDS), target, msg, getSystemDispatcher(), null);
67   - }
68   -
69   -
70 50 }
... ...
... ... @@ -15,16 +15,15 @@
15 15 */
16 16 package org.thingsboard.server.actors.shared;
17 17
18   -import akka.actor.ActorContext;
19 18 import lombok.extern.slf4j.Slf4j;
20 19 import org.thingsboard.server.actors.ActorSystemContext;
  20 +import org.thingsboard.server.actors.TbActorCtx;
21 21 import org.thingsboard.server.actors.stats.StatsPersistTick;
22 22 import org.thingsboard.server.common.data.id.EntityId;
23 23 import org.thingsboard.server.common.data.id.TenantId;
24 24 import org.thingsboard.server.common.data.plugin.ComponentLifecycleState;
25 25 import org.thingsboard.server.common.msg.TbMsg;
26 26 import org.thingsboard.server.common.msg.queue.PartitionChangeMsg;
27   -import org.thingsboard.server.common.msg.queue.RuleEngineException;
28 27 import org.thingsboard.server.common.msg.queue.RuleNodeException;
29 28
30 29 @Slf4j
... ... @@ -42,38 +41,38 @@ public abstract class ComponentMsgProcessor<T extends EntityId> extends Abstract
42 41
43 42 public abstract String getComponentName();
44 43
45   - public abstract void start(ActorContext context) throws Exception;
  44 + public abstract void start(TbActorCtx context) throws Exception;
46 45
47   - public abstract void stop(ActorContext context) throws Exception;
  46 + public abstract void stop(TbActorCtx context) throws Exception;
48 47
49 48 public abstract void onPartitionChangeMsg(PartitionChangeMsg msg) throws Exception;
50 49
51   - public void onCreated(ActorContext context) throws Exception {
  50 + public void onCreated(TbActorCtx context) throws Exception {
52 51 start(context);
53 52 }
54 53
55   - public void onUpdate(ActorContext context) throws Exception {
  54 + public void onUpdate(TbActorCtx context) throws Exception {
56 55 restart(context);
57 56 }
58 57
59   - public void onActivate(ActorContext context) throws Exception {
  58 + public void onActivate(TbActorCtx context) throws Exception {
60 59 restart(context);
61 60 }
62 61
63   - public void onSuspend(ActorContext context) throws Exception {
  62 + public void onSuspend(TbActorCtx context) throws Exception {
64 63 stop(context);
65 64 }
66 65
67   - public void onStop(ActorContext context) throws Exception {
  66 + public void onStop(TbActorCtx context) throws Exception {
68 67 stop(context);
69 68 }
70 69
71   - private void restart(ActorContext context) throws Exception {
  70 + private void restart(TbActorCtx context) throws Exception {
72 71 stop(context);
73 72 start(context);
74 73 }
75 74
76   - public void scheduleStatsPersistTick(ActorContext context, long statsPersistFrequency) {
  75 + public void scheduleStatsPersistTick(TbActorCtx context, long statsPersistFrequency) {
77 76 schedulePeriodicMsgWithDelay(context, new StatsPersistTick(), statsPersistFrequency, statsPersistFrequency);
78 77 }
79 78
... ...
... ... @@ -19,10 +19,15 @@ import com.fasterxml.jackson.databind.JsonNode;
19 19 import com.fasterxml.jackson.databind.ObjectMapper;
20 20 import lombok.extern.slf4j.Slf4j;
21 21 import org.thingsboard.server.actors.ActorSystemContext;
  22 +import org.thingsboard.server.actors.TbActor;
  23 +import org.thingsboard.server.actors.TbActorCtx;
  24 +import org.thingsboard.server.actors.TbActorId;
  25 +import org.thingsboard.server.actors.TbStringActorId;
22 26 import org.thingsboard.server.actors.service.ContextAwareActor;
23 27 import org.thingsboard.server.actors.service.ContextBasedCreator;
24 28 import org.thingsboard.server.common.data.DataConstants;
25 29 import org.thingsboard.server.common.data.Event;
  30 +import org.thingsboard.server.common.msg.MsgType;
26 31 import org.thingsboard.server.common.msg.TbActorMsg;
27 32
28 33 @Slf4j
... ... @@ -35,24 +40,17 @@ public class StatsActor extends ContextAwareActor {
35 40 }
36 41
37 42 @Override
38   - protected boolean process(TbActorMsg msg) {
39   - //TODO Move everything here, to work with TbActorMsg\
40   - return false;
41   - }
42   -
43   - @Override
44   - public void onReceive(Object msg) {
  43 + protected boolean doProcess(TbActorMsg msg) {
45 44 log.debug("Received message: {}", msg);
46   - if (msg instanceof StatsPersistMsg) {
47   - try {
48   - onStatsPersistMsg((StatsPersistMsg) msg);
49   - } catch (Exception e) {
50   - log.warn("Failed to persist statistics: {}", msg, e);
51   - }
  45 + if (msg.getMsgType().equals(MsgType.STATS_PERSIST_MSG)) {
  46 + onStatsPersistMsg((StatsPersistMsg) msg);
  47 + return true;
  48 + } else {
  49 + return false;
52 50 }
53 51 }
54 52
55   - public void onStatsPersistMsg(StatsPersistMsg msg) throws Exception {
  53 + public void onStatsPersistMsg(StatsPersistMsg msg) {
56 54 Event event = new Event();
57 55 event.setEntityId(msg.getEntityId());
58 56 event.setTenantId(msg.getTenantId());
... ... @@ -65,15 +63,21 @@ public class StatsActor extends ContextAwareActor {
65 63 return mapper.createObjectNode().put("server", serviceId).put("messagesProcessed", messagesProcessed).put("errorsOccurred", errorsOccurred);
66 64 }
67 65
68   - public static class ActorCreator extends ContextBasedCreator<StatsActor> {
69   - private static final long serialVersionUID = 1L;
  66 + public static class ActorCreator extends ContextBasedCreator {
  67 + private final String actorId;
70 68
71   - public ActorCreator(ActorSystemContext context) {
  69 + public ActorCreator(ActorSystemContext context, String actorId) {
72 70 super(context);
  71 + this.actorId = actorId;
  72 + }
  73 +
  74 + @Override
  75 + public TbActorId createActorId() {
  76 + return new TbStringActorId(actorId);
73 77 }
74 78
75 79 @Override
76   - public StatsActor create() {
  80 + public TbActor createActor() {
77 81 return new StatsActor(context);
78 82 }
79 83 }
... ...
... ... @@ -20,13 +20,21 @@ import lombok.Getter;
20 20 import lombok.ToString;
21 21 import org.thingsboard.server.common.data.id.EntityId;
22 22 import org.thingsboard.server.common.data.id.TenantId;
  23 +import org.thingsboard.server.common.msg.MsgType;
  24 +import org.thingsboard.server.common.msg.TbActorMsg;
23 25
24 26 @AllArgsConstructor
25 27 @Getter
26 28 @ToString
27   -public final class StatsPersistMsg {
  29 +public final class StatsPersistMsg implements TbActorMsg {
  30 +
28 31 private long messagesProcessed;
29 32 private long errorsOccurred;
30 33 private TenantId tenantId;
31 34 private EntityId entityId;
  35 +
  36 + @Override
  37 + public MsgType getMsgType() {
  38 + return MsgType.STATS_PERSIST_MSG;
  39 + }
32 40 }
... ...
... ... @@ -15,16 +15,16 @@
15 15 */
16 16 package org.thingsboard.server.actors.tenant;
17 17
18   -import akka.actor.ActorInitializationException;
19   -import akka.actor.ActorRef;
20   -import akka.actor.LocalActorRef;
21   -import akka.actor.OneForOneStrategy;
22   -import akka.actor.Props;
23   -import akka.actor.SupervisorStrategy;
24   -import akka.actor.Terminated;
25   -import com.google.common.collect.BiMap;
26   -import com.google.common.collect.HashBiMap;
  18 +import lombok.extern.slf4j.Slf4j;
27 19 import org.thingsboard.server.actors.ActorSystemContext;
  20 +import org.thingsboard.server.actors.TbActor;
  21 +import org.thingsboard.server.actors.TbActorCtx;
  22 +import org.thingsboard.server.actors.TbActorException;
  23 +import org.thingsboard.server.actors.TbActorId;
  24 +import org.thingsboard.server.actors.TbActorNotRegisteredException;
  25 +import org.thingsboard.server.actors.TbActorRef;
  26 +import org.thingsboard.server.actors.TbEntityActorId;
  27 +import org.thingsboard.server.actors.TbEntityTypeActorIdPredicate;
28 28 import org.thingsboard.server.actors.device.DeviceActorCreator;
29 29 import org.thingsboard.server.actors.ruleChain.RuleChainManagerActor;
30 30 import org.thingsboard.server.actors.service.ContextBasedCreator;
... ... @@ -32,6 +32,7 @@ import org.thingsboard.server.actors.service.DefaultActorService;
32 32 import org.thingsboard.server.common.data.EntityType;
33 33 import org.thingsboard.server.common.data.Tenant;
34 34 import org.thingsboard.server.common.data.id.DeviceId;
  35 +import org.thingsboard.server.common.data.id.EntityId;
35 36 import org.thingsboard.server.common.data.id.RuleChainId;
36 37 import org.thingsboard.server.common.data.id.TenantId;
37 38 import org.thingsboard.server.common.data.rule.RuleChain;
... ... @@ -46,32 +47,25 @@ import org.thingsboard.server.common.msg.queue.PartitionChangeMsg;
46 47 import org.thingsboard.server.common.msg.queue.QueueToRuleEngineMsg;
47 48 import org.thingsboard.server.common.msg.queue.RuleEngineException;
48 49 import org.thingsboard.server.common.msg.queue.ServiceType;
49   -import scala.concurrent.duration.Duration;
50 50
51 51 import java.util.List;
52 52 import java.util.Optional;
53   -import java.util.stream.Collectors;
54 53
  54 +@Slf4j
55 55 public class TenantActor extends RuleChainManagerActor {
56 56
57   - private final BiMap<DeviceId, ActorRef> deviceActors;
58 57 private boolean isRuleEngineForCurrentTenant;
59 58 private boolean isCore;
60 59
61 60 private TenantActor(ActorSystemContext systemContext, TenantId tenantId) {
62 61 super(systemContext, tenantId);
63   - this.deviceActors = HashBiMap.create();
64   - }
65   -
66   - @Override
67   - public SupervisorStrategy supervisorStrategy() {
68   - return strategy;
69 62 }
70 63
71 64 boolean cantFindTenant = false;
72 65
73 66 @Override
74   - public void preStart() {
  67 + public void init(TbActorCtx ctx) throws TbActorException {
  68 + super.init(ctx);
75 69 log.info("[{}] Starting tenant actor.", tenantId);
76 70 try {
77 71 Tenant tenant = systemContext.getTenantService().findTenantById(tenantId);
... ... @@ -101,16 +95,18 @@ public class TenantActor extends RuleChainManagerActor {
101 95 }
102 96 } catch (Exception e) {
103 97 log.warn("[{}] Unknown failure", tenantId, e);
  98 +// TODO: throw this in 3.1?
  99 +// throw new TbActorException("Failed to init actor", e);
104 100 }
105 101 }
106 102
107 103 @Override
108   - public void postStop() {
  104 + public void destroy() {
109 105 log.info("[{}] Stopping tenant actor.", tenantId);
110 106 }
111 107
112 108 @Override
113   - protected boolean process(TbActorMsg msg) {
  109 + protected boolean doProcess(TbActorMsg msg) {
114 110 if (cantFindTenant) {
115 111 log.info("[{}] Processing missing Tenant msg: {}", tenantId, msg);
116 112 if (msg.getMsgType().equals(MsgType.QUEUE_TO_RULE_ENGINE_MSG)) {
... ... @@ -127,13 +123,13 @@ public class TenantActor extends RuleChainManagerActor {
127 123 //To Rule Chain Actors
128 124 broadcast(msg);
129 125 } else if (ServiceType.TB_CORE.equals(serviceType)) {
130   - //To Device Actors
131   - List<DeviceId> repartitionedDevices =
132   - deviceActors.keySet().stream().filter(deviceId -> !isMyPartition(deviceId)).collect(Collectors.toList());
133   - for (DeviceId deviceId : repartitionedDevices) {
134   - ActorRef deviceActor = deviceActors.remove(deviceId);
135   - context().stop(deviceActor);
136   - }
  126 + List<TbActorId> deviceActorIds = ctx.filterChildren(new TbEntityTypeActorIdPredicate(EntityType.DEVICE) {
  127 + @Override
  128 + protected boolean testEntityId(EntityId entityId) {
  129 + return super.testEntityId(entityId) && !isMyPartition(entityId);
  130 + }
  131 + });
  132 + deviceActorIds.forEach(id -> ctx.stop(id));
137 133 }
138 134 break;
139 135 case COMPONENT_LIFE_CYCLE_MSG:
... ... @@ -143,12 +139,14 @@ public class TenantActor extends RuleChainManagerActor {
143 139 onQueueToRuleEngineMsg((QueueToRuleEngineMsg) msg);
144 140 break;
145 141 case TRANSPORT_TO_DEVICE_ACTOR_MSG:
  142 + onToDeviceActorMsg((DeviceAwareMsg) msg, false);
  143 + break;
146 144 case DEVICE_ATTRIBUTES_UPDATE_TO_DEVICE_ACTOR_MSG:
147 145 case DEVICE_CREDENTIALS_UPDATE_TO_DEVICE_ACTOR_MSG:
148 146 case DEVICE_NAME_OR_TYPE_UPDATE_TO_DEVICE_ACTOR_MSG:
149 147 case DEVICE_RPC_REQUEST_TO_DEVICE_ACTOR_MSG:
150 148 case SERVER_RPC_RESPONSE_TO_DEVICE_ACTOR_MSG:
151   - onToDeviceActorMsg((DeviceAwareMsg) msg);
  149 + onToDeviceActorMsg((DeviceAwareMsg) msg, true);
152 150 break;
153 151 case RULE_CHAIN_TO_RULE_CHAIN_MSG:
154 152 onRuleChainMsg((RuleChainAwareMsg) msg);
... ... @@ -159,8 +157,8 @@ public class TenantActor extends RuleChainManagerActor {
159 157 return true;
160 158 }
161 159
162   - private boolean isMyPartition(DeviceId deviceId) {
163   - return systemContext.resolve(ServiceType.TB_CORE, tenantId, deviceId).isMyPartition();
  160 + private boolean isMyPartition(EntityId entityId) {
  161 + return systemContext.resolve(ServiceType.TB_CORE, tenantId, entityId).isMyPartition();
164 162 }
165 163
166 164 private void onQueueToRuleEngineMsg(QueueToRuleEngineMsg msg) {
... ... @@ -171,16 +169,15 @@ public class TenantActor extends RuleChainManagerActor {
171 169 TbMsg tbMsg = msg.getTbMsg();
172 170 if (tbMsg.getRuleChainId() == null) {
173 171 if (getRootChainActor() != null) {
174   - getRootChainActor().tell(msg, self());
  172 + getRootChainActor().tell(msg);
175 173 } else {
176 174 tbMsg.getCallback().onFailure(new RuleEngineException("No Root Rule Chain available!"));
177 175 log.info("[{}] No Root Chain: {}", tenantId, msg);
178 176 }
179 177 } else {
180   - ActorRef ruleChainActor = get(tbMsg.getRuleChainId());
181   - if (ruleChainActor != null) {
182   - ruleChainActor.tell(msg, self());
183   - } else {
  178 + try {
  179 + ctx.tell(new TbEntityActorId(tbMsg.getRuleChainId()), msg);
  180 + } catch (TbActorNotRegisteredException ex) {
184 181 log.trace("Received message for non-existing rule chain: [{}]", tbMsg.getRuleChainId());
185 182 //TODO: 3.1 Log it to dead letters queue;
186 183 tbMsg.getCallback().onSuccess();
... ... @@ -189,19 +186,24 @@ public class TenantActor extends RuleChainManagerActor {
189 186 }
190 187
191 188 private void onRuleChainMsg(RuleChainAwareMsg msg) {
192   - getOrCreateActor(context(), msg.getRuleChainId()).tell(msg, self());
  189 + getOrCreateActor(msg.getRuleChainId()).tell(msg);
193 190 }
194 191
195   - private void onToDeviceActorMsg(DeviceAwareMsg msg) {
  192 + private void onToDeviceActorMsg(DeviceAwareMsg msg, boolean priority) {
196 193 if (!isCore) {
197 194 log.warn("RECEIVED INVALID MESSAGE: {}", msg);
198 195 }
199   - getOrCreateDeviceActor(msg.getDeviceId()).tell(msg, ActorRef.noSender());
  196 + TbActorRef deviceActor = getOrCreateDeviceActor(msg.getDeviceId());
  197 + if (priority) {
  198 + deviceActor.tellWithHighPriority(msg);
  199 + } else {
  200 + deviceActor.tell(msg);
  201 + }
200 202 }
201 203
202 204 private void onComponentLifecycleMsg(ComponentLifecycleMsg msg) {
203 205 if (isRuleEngineForCurrentTenant) {
204   - ActorRef target = getEntityActorRef(msg.getEntityId());
  206 + TbActorRef target = getEntityActorRef(msg.getEntityId());
205 207 if (target != null) {
206 208 if (msg.getEntityId().getEntityType() == EntityType.RULE_CHAIN) {
207 209 RuleChain ruleChain = systemContext.getRuleChainService().
... ... @@ -210,42 +212,20 @@ public class TenantActor extends RuleChainManagerActor {
210 212 visit(ruleChain, target);
211 213 }
212 214 }
213   - target.tell(msg, ActorRef.noSender());
  215 + target.tellWithHighPriority(msg);
214 216 } else {
215 217 log.debug("[{}] Invalid component lifecycle msg: {}", tenantId, msg);
216 218 }
217 219 }
218 220 }
219 221
220   - private ActorRef getOrCreateDeviceActor(DeviceId deviceId) {
221   - return deviceActors.computeIfAbsent(deviceId, k -> {
222   - log.debug("[{}][{}] Creating device actor.", tenantId, deviceId);
223   - ActorRef deviceActor = context().actorOf(Props.create(new DeviceActorCreator(systemContext, tenantId, deviceId))
224   - .withDispatcher(DefaultActorService.CORE_DISPATCHER_NAME)
225   - , deviceId.toString());
226   - context().watch(deviceActor);
227   - log.debug("[{}][{}] Created device actor: {}.", tenantId, deviceId, deviceActor);
228   - return deviceActor;
229   - });
230   - }
231   -
232   - @Override
233   - protected void processTermination(Terminated message) {
234   - ActorRef terminated = message.actor();
235   - if (terminated instanceof LocalActorRef) {
236   - boolean removed = deviceActors.inverse().remove(terminated) != null;
237   - if (removed) {
238   - log.debug("[{}] Removed actor:", terminated);
239   - } else {
240   - log.debug("Removed actor was not found in the device map!");
241   - }
242   - } else {
243   - throw new IllegalStateException("Remote actors are not supported!");
244   - }
  222 + private TbActorRef getOrCreateDeviceActor(DeviceId deviceId) {
  223 + return ctx.getOrCreateChildActor(new TbEntityActorId(deviceId),
  224 + () -> DefaultActorService.DEVICE_DISPATCHER_NAME,
  225 + () -> new DeviceActorCreator(systemContext, tenantId, deviceId));
245 226 }
246 227
247   - public static class ActorCreator extends ContextBasedCreator<TenantActor> {
248   - private static final long serialVersionUID = 1L;
  228 + public static class ActorCreator extends ContextBasedCreator {
249 229
250 230 private final TenantId tenantId;
251 231
... ... @@ -255,18 +235,14 @@ public class TenantActor extends RuleChainManagerActor {
255 235 }
256 236
257 237 @Override
258   - public TenantActor create() {
259   - return new TenantActor(context, tenantId);
  238 + public TbActorId createActorId() {
  239 + return new TbEntityActorId(tenantId);
260 240 }
261   - }
262 241
263   - private final SupervisorStrategy strategy = new OneForOneStrategy(3, Duration.create("1 minute"), t -> {
264   - log.warn("[{}] Unknown failure", tenantId, t);
265   - if (t instanceof ActorInitializationException) {
266   - return SupervisorStrategy.stop();
267   - } else {
268   - return SupervisorStrategy.resume();
  242 + @Override
  243 + public TbActor createActor() {
  244 + return new TenantActor(context, tenantId);
269 245 }
270   - });
  246 + }
271 247
272 248 }
... ...
... ... @@ -15,7 +15,6 @@
15 15 */
16 16 package org.thingsboard.server.service.queue;
17 17
18   -import akka.actor.ActorRef;
19 18 import lombok.extern.slf4j.Slf4j;
20 19 import org.springframework.beans.factory.annotation.Value;
21 20 import org.springframework.scheduling.annotation.Scheduled;
... ... @@ -157,7 +156,7 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService<ToCore
157 156 tbCoreDeviceRpcService.forwardRpcRequestToDeviceActor((ToDeviceRpcRequestActorMsg) tbActorMsg);
158 157 } else {
159 158 log.trace("[{}] Forwarding message to App Actor {}", id, actorMsg.get());
160   - actorContext.tell(actorMsg.get(), ActorRef.noSender());
  159 + actorContext.tell(actorMsg.get());
161 160 }
162 161 }
163 162 callback.onSuccess();
... ... @@ -168,7 +167,7 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService<ToCore
168 167 }
169 168 });
170 169 if (!processingTimeoutLatch.await(packProcessingTimeout, TimeUnit.MILLISECONDS)) {
171   - ctx.getAckMap().forEach((id, msg) -> log.warn("[{}] Timeout to process message: {}", id, msg.getValue()));
  170 + ctx.getAckMap().forEach((id, msg) -> log.debug("[{}] Timeout to process message: {}", id, msg.getValue()));
172 171 ctx.getFailedMap().forEach((id, msg) -> log.warn("[{}] Failed to process message: {}", id, msg.getValue()));
173 172 }
174 173 mainConsumer.commit();
... ... @@ -215,7 +214,7 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService<ToCore
215 214 Optional<TbActorMsg> actorMsg = encodingService.decode(toCoreNotification.getComponentLifecycleMsg().toByteArray());
216 215 if (actorMsg.isPresent()) {
217 216 log.trace("[{}] Forwarding message to App Actor {}", id, actorMsg.get());
218   - actorContext.tell(actorMsg.get(), ActorRef.noSender());
  217 + actorContext.tellWithHighPriority(actorMsg.get());
219 218 }
220 219 callback.onSuccess();
221 220 }
... ... @@ -293,7 +292,7 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService<ToCore
293 292 if (statsEnabled) {
294 293 stats.log(toDeviceActorMsg);
295 294 }
296   - actorContext.tell(new TransportToDeviceActorMsgWrapper(toDeviceActorMsg, callback), ActorRef.noSender());
  295 + actorContext.tell(new TransportToDeviceActorMsgWrapper(toDeviceActorMsg, callback));
297 296 }
298 297
299 298 private void throwNotHandled(Object msg, TbCallback callback) {
... ...
... ... @@ -15,7 +15,6 @@
15 15 */
16 16 package org.thingsboard.server.service.queue;
17 17
18   -import akka.actor.ActorRef;
19 18 import com.google.protobuf.ProtocolStringList;
20 19 import lombok.extern.slf4j.Slf4j;
21 20 import org.springframework.beans.factory.annotation.Value;
... ... @@ -51,7 +50,6 @@ import org.thingsboard.server.service.queue.processing.TbRuleEngineProcessingStr
51 50 import org.thingsboard.server.service.queue.processing.TbRuleEngineSubmitStrategy;
52 51 import org.thingsboard.server.service.queue.processing.TbRuleEngineSubmitStrategyFactory;
53 52 import org.thingsboard.server.service.rpc.FromDeviceRpcResponse;
54   -import org.thingsboard.server.service.rpc.TbCoreDeviceRpcService;
55 53 import org.thingsboard.server.service.rpc.TbRuleEngineDeviceRpcService;
56 54 import org.thingsboard.server.service.stats.RuleEngineStatisticsService;
57 55
... ... @@ -168,7 +166,7 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService<
168 166 TbMsgCallback callback = new TbMsgPackCallback(id, tenantId, ctx);
169 167 try {
170 168 if (toRuleEngineMsg.getTbMsg() != null && !toRuleEngineMsg.getTbMsg().isEmpty()) {
171   - forwardToRuleEngineActor(tenantId, toRuleEngineMsg, callback);
  169 + forwardToRuleEngineActor(configuration.getName(), tenantId, toRuleEngineMsg, callback);
172 170 } else {
173 171 callback.onSuccess();
174 172 }
... ... @@ -182,7 +180,7 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService<
182 180 timeout = true;
183 181 }
184 182
185   - TbRuleEngineProcessingResult result = new TbRuleEngineProcessingResult(timeout, ctx);
  183 + TbRuleEngineProcessingResult result = new TbRuleEngineProcessingResult(configuration.getName(), timeout, ctx);
186 184 TbRuleEngineProcessingDecision decision = ackStrategy.analyze(result);
187 185 if (statsEnabled) {
188 186 stats.log(result, decision.isCommit());
... ... @@ -232,7 +230,7 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService<
232 230 Optional<TbActorMsg> actorMsg = encodingService.decode(nfMsg.getComponentLifecycleMsg().toByteArray());
233 231 if (actorMsg.isPresent()) {
234 232 log.trace("[{}] Forwarding message to App Actor {}", id, actorMsg.get());
235   - actorContext.tell(actorMsg.get(), ActorRef.noSender());
  233 + actorContext.tellWithHighPriority(actorMsg.get());
236 234 }
237 235 callback.onSuccess();
238 236 } else if (nfMsg.hasFromDeviceRpcResponse()) {
... ... @@ -248,8 +246,8 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService<
248 246 }
249 247 }
250 248
251   - private void forwardToRuleEngineActor(TenantId tenantId, ToRuleEngineMsg toRuleEngineMsg, TbMsgCallback callback) {
252   - TbMsg tbMsg = TbMsg.fromBytes(toRuleEngineMsg.getTbMsg().toByteArray(), callback);
  249 + private void forwardToRuleEngineActor(String queueName, TenantId tenantId, ToRuleEngineMsg toRuleEngineMsg, TbMsgCallback callback) {
  250 + TbMsg tbMsg = TbMsg.fromBytes(queueName, toRuleEngineMsg.getTbMsg().toByteArray(), callback);
253 251 QueueToRuleEngineMsg msg;
254 252 ProtocolStringList relationTypesList = toRuleEngineMsg.getRelationTypesList();
255 253 Set<String> relationTypes = null;
... ... @@ -261,7 +259,7 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService<
261 259 }
262 260 }
263 261 msg = new QueueToRuleEngineMsg(tenantId, tbMsg, relationTypes, toRuleEngineMsg.getFailureMessage());
264   - actorContext.tell(msg, ActorRef.noSender());
  262 + actorContext.tell(msg);
265 263 }
266 264
267 265 @Scheduled(fixedDelayString = "${queue.rule-engine.stats.print-interval-ms}")
... ...
... ... @@ -28,13 +28,16 @@ import java.util.concurrent.ConcurrentMap;
28 28 public class TbRuleEngineProcessingResult {
29 29
30 30 @Getter
  31 + private final String queueName;
  32 + @Getter
31 33 private final boolean success;
32 34 @Getter
33 35 private final boolean timeout;
34 36 @Getter
35 37 private final TbMsgPackProcessingContext ctx;
36 38
37   - public TbRuleEngineProcessingResult(boolean timeout, TbMsgPackProcessingContext ctx) {
  39 + public TbRuleEngineProcessingResult(String queueName, boolean timeout, TbMsgPackProcessingContext ctx) {
  40 + this.queueName = queueName;
38 41 this.timeout = timeout;
39 42 this.ctx = ctx;
40 43 this.success = !timeout && ctx.getPendingMap().isEmpty() && ctx.getFailedMap().isEmpty();
... ...
... ... @@ -100,7 +100,7 @@ public class TbRuleEngineProcessingStrategyFactory {
100 100 }
101 101 log.debug("[{}] Going to reprocess {} messages", queueName, toReprocess.size());
102 102 if (log.isTraceEnabled()) {
103   - toReprocess.forEach((id, msg) -> log.trace("Going to reprocess [{}]: {}", id, TbMsg.fromBytes(msg.getValue().getTbMsg().toByteArray(), TbMsgCallback.EMPTY)));
  103 + toReprocess.forEach((id, msg) -> log.trace("Going to reprocess [{}]: {}", id, TbMsg.fromBytes(result.getQueueName(), msg.getValue().getTbMsg().toByteArray(), TbMsgCallback.EMPTY)));
104 104 }
105 105 if (pauseBetweenRetries > 0) {
106 106 try {
... ... @@ -129,10 +129,10 @@ public class TbRuleEngineProcessingStrategyFactory {
129 129 log.debug("[{}] Reprocessing skipped for {} failed and {} timeout messages", queueName, result.getFailedMap().size(), result.getPendingMap().size());
130 130 }
131 131 if (log.isTraceEnabled()) {
132   - result.getFailedMap().forEach((id, msg) -> log.trace("Failed messages [{}]: {}", id, TbMsg.fromBytes(msg.getValue().getTbMsg().toByteArray(), TbMsgCallback.EMPTY)));
  132 + result.getFailedMap().forEach((id, msg) -> log.trace("Failed messages [{}]: {}", id, TbMsg.fromBytes(result.getQueueName(), msg.getValue().getTbMsg().toByteArray(), TbMsgCallback.EMPTY)));
133 133 }
134 134 if (log.isTraceEnabled()) {
135   - result.getPendingMap().forEach((id, msg) -> log.trace("Timeout messages [{}]: {}", id, TbMsg.fromBytes(msg.getValue().getTbMsg().toByteArray(), TbMsgCallback.EMPTY)));
  135 + result.getPendingMap().forEach((id, msg) -> log.trace("Timeout messages [{}]: {}", id, TbMsg.fromBytes(result.getQueueName(), msg.getValue().getTbMsg().toByteArray(), TbMsgCallback.EMPTY)));
136 136 }
137 137 return new TbRuleEngineProcessingDecision(true, null);
138 138 }
... ...
... ... @@ -15,7 +15,6 @@
15 15 */
16 16 package org.thingsboard.server.service.rpc;
17 17
18   -import akka.actor.ActorRef;
19 18 import com.fasterxml.jackson.core.JsonProcessingException;
20 19 import com.fasterxml.jackson.databind.ObjectMapper;
21 20 import com.fasterxml.jackson.databind.node.ObjectNode;
... ... @@ -122,7 +121,7 @@ public class DefaultTbCoreDeviceRpcService implements TbCoreDeviceRpcService {
122 121 log.trace("[{}][{}] Processing local rpc call to device actor [{}]", request.getTenantId(), request.getId(), request.getDeviceId());
123 122 UUID requestId = request.getId();
124 123 localToDeviceRpcRequests.put(requestId, rpcMsg);
125   - actorContext.tell(rpcMsg, ActorRef.noSender());
  124 + actorContext.tellWithHighPriority(rpcMsg);
126 125 scheduleToDeviceTimeout(request, requestId);
127 126 }
128 127
... ... @@ -176,7 +175,7 @@ public class DefaultTbCoreDeviceRpcService implements TbCoreDeviceRpcService {
176 175 }
177 176
178 177 private void scheduleToRuleEngineTimeout(ToDeviceRpcRequest request, UUID requestId) {
179   - long timeout = Math.max(0, request.getExpirationTime() - System.currentTimeMillis());
  178 + long timeout = Math.max(0, request.getExpirationTime() - System.currentTimeMillis()) + TimeUnit.SECONDS.toMillis(1);
180 179 log.trace("[{}] processing to rule engine request.", requestId);
181 180 scheduler.schedule(() -> {
182 181 log.trace("[{}] timeout for processing to rule engine request.", requestId);
... ... @@ -188,7 +187,7 @@ public class DefaultTbCoreDeviceRpcService implements TbCoreDeviceRpcService {
188 187 }
189 188
190 189 private void scheduleToDeviceTimeout(ToDeviceRpcRequest request, UUID requestId) {
191   - long timeout = Math.max(0, request.getExpirationTime() - System.currentTimeMillis());
  190 + long timeout = Math.max(0, request.getExpirationTime() - System.currentTimeMillis()) + TimeUnit.SECONDS.toMillis(1);
192 191 log.trace("[{}] processing to device request.", requestId);
193 192 scheduler.schedule(() -> {
194 193 log.trace("[{}] timeout for to device request.", requestId);
... ...
... ... @@ -164,7 +164,7 @@ public class DefaultTbRuleEngineRpcService implements TbRuleEngineDeviceRpcServi
164 164 }
165 165
166 166 private void scheduleTimeout(ToDeviceRpcRequest request, UUID requestId) {
167   - long timeout = Math.max(0, request.getExpirationTime() - System.currentTimeMillis());
  167 + long timeout = Math.max(0, request.getExpirationTime() - System.currentTimeMillis()) + TimeUnit.SECONDS.toMillis(1);
168 168 log.trace("[{}] processing the request: [{}]", this.hashCode(), requestId);
169 169 scheduler.schedule(() -> {
170 170 log.trace("[{}] timeout the request: [{}]", this.hashCode(), requestId);
... ...
... ... @@ -126,7 +126,7 @@ public abstract class AbstractNashornJsInvokeService extends AbstractJsInvokeSer
126 126 scriptIdToNameMap.put(scriptId, functionName);
127 127 return scriptId;
128 128 } catch (Exception e) {
129   - log.warn("Failed to compile JS script: {}", e.getMessage(), e);
  129 + log.debug("Failed to compile JS script: {}", e.getMessage(), e);
130 130 throw new ExecutionException(e);
131 131 }
132 132 });
... ...
1   -#
2   -# Copyright © 2016-2020 The Thingsboard Authors
3   -#
4   -# Licensed under the Apache License, Version 2.0 (the "License");
5   -# you may not use this file except in compliance with the License.
6   -# You may obtain a copy of the License at
7   -#
8   -# http://www.apache.org/licenses/LICENSE-2.0
9   -#
10   -# Unless required by applicable law or agreed to in writing, software
11   -# distributed under the License is distributed on an "AS IS" BASIS,
12   -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   -# See the License for the specific language governing permissions and
14   -# limitations under the License.
15   -#
16   -
17   -
18   -akka {
19   - # JVM shutdown, System.exit(-1), in case of a fatal error,
20   - # such as OutOfMemoryError
21   - jvm-exit-on-fatal-error = off
22   - loglevel = "INFO"
23   - loggers = ["akka.event.slf4j.Slf4jLogger"]
24   -}
25   -
26   -# This dispatcher is used for app
27   -app-dispatcher {
28   - type = Dispatcher
29   - executor = "fork-join-executor"
30   - fork-join-executor {
31   - # Min number of threads to cap factor-based parallelism number to
32   - parallelism-min = 1
33   - # Max number of threads to cap factor-based parallelism number to
34   - parallelism-max = 1
35   -
36   - # The parallelism factor is used to determine thread pool size using the
37   - # following formula: ceil(available processors * factor). Resulting size
38   - # is then bounded by the parallelism-min and parallelism-max values.
39   - parallelism-factor = 1.0
40   - }
41   - # How long time the dispatcher will wait for new actors until it shuts down
42   - shutdown-timeout = 1s
43   -
44   - # Throughput defines the number of messages that are processed in a batch
45   - # before the thread is returned to the pool. Set to 1 for as fair as possible.
46   - throughput = 5
47   -}
48   -
49   -# This dispatcher is used for rpc actors
50   -rpc-dispatcher {
51   - type = Dispatcher
52   - executor = "fork-join-executor"
53   - fork-join-executor {
54   - # Min number of threads to cap factor-based parallelism number to
55   - parallelism-min = 2
56   - # Max number of threads to cap factor-based parallelism number to
57   - parallelism-max = 8
58   -
59   - # The parallelism factor is used to determine thread pool size using the
60   - # following formula: ceil(available processors * factor). Resulting size
61   - # is then bounded by the parallelism-min and parallelism-max values.
62   - parallelism-factor = 0.5
63   - }
64   - # How long time the dispatcher will wait for new actors until it shuts down
65   - shutdown-timeout = 1s
66   -
67   - # Throughput defines the number of messages that are processed in a batch
68   - # before the thread is returned to the pool. Set to 1 for as fair as possible.
69   - throughput = 5
70   -}
71   -
72   -# This dispatcher is used for auth
73   -core-dispatcher {
74   - type = Dispatcher
75   - executor = "fork-join-executor"
76   - fork-join-executor {
77   - # Min number of threads to cap factor-based parallelism number to
78   - parallelism-min = 2
79   - # Max number of threads to cap factor-based parallelism number to
80   - parallelism-max = 12
81   -
82   - # The parallelism factor is used to determine thread pool size using the
83   - # following formula: ceil(available processors * factor). Resulting size
84   - # is then bounded by the parallelism-min and parallelism-max values.
85   - parallelism-factor = 0.25
86   - }
87   - # How long time the dispatcher will wait for new actors until it shuts down
88   - shutdown-timeout = 1s
89   -
90   - # Throughput defines the number of messages that are processed in a batch
91   - # before the thread is returned to the pool. Set to 1 for as fair as possible.
92   - throughput = 5
93   -}
94   -
95   -# This dispatcher is used for system rule chains and rule node actors
96   -system-rule-dispatcher {
97   - type = Dispatcher
98   - executor = "fork-join-executor"
99   - fork-join-executor {
100   - # Min number of threads to cap factor-based parallelism number to
101   - parallelism-min = 2
102   - # Max number of threads to cap factor-based parallelism number to
103   - parallelism-max = 12
104   -
105   - # The parallelism factor is used to determine thread pool size using the
106   - # following formula: ceil(available processors * factor). Resulting size
107   - # is then bounded by the parallelism-min and parallelism-max values.
108   - parallelism-factor = 0.25
109   - }
110   - # How long time the dispatcher will wait for new actors until it shuts down
111   - shutdown-timeout = 1s
112   -
113   - # Throughput defines the number of messages that are processed in a batch
114   - # before the thread is returned to the pool. Set to 1 for as fair as possible.
115   - throughput = 5
116   -}
117   -
118   -# This dispatcher is used for tenant rule chains and rule node actors
119   -rule-dispatcher {
120   - type = Dispatcher
121   - executor = "fork-join-executor"
122   - fork-join-executor {
123   - # Min number of threads to cap factor-based parallelism number to
124   - parallelism-min = 2
125   - # Max number of threads to cap factor-based parallelism number to
126   - parallelism-max = 12
127   -
128   - # The parallelism factor is used to determine thread pool size using the
129   - # following formula: ceil(available processors * factor). Resulting size
130   - # is then bounded by the parallelism-min and parallelism-max values.
131   - parallelism-factor = 0.25
132   - }
133   - # How long time the dispatcher will wait for new actors until it shuts down
134   - shutdown-timeout = 1s
135   -
136   - # Throughput defines the number of messages that are processed in a batch
137   - # before the thread is returned to the pool. Set to 1 for as fair as possible.
138   - throughput = 5
139   -}
... ... @@ -27,7 +27,6 @@
27 27
28 28 <logger name="org.thingsboard.server" level="INFO" />
29 29 <logger name="org.thingsboard.server.service.edge" level="TRACE" />
30   - <logger name="akka" level="INFO" />
31 30
32 31 <!-- <logger name="org.thingsboard.server.service.queue" level="TRACE" />-->
33 32 <!-- <logger name="org.thingsboard.server.service.transport" level="TRACE" />-->
... ...
... ... @@ -281,6 +281,14 @@ sql:
281 281
282 282 # Actor system parameters
283 283 actors:
  284 + system:
  285 + throughput: "${ACTORS_SYSTEM_THROUGHPUT:5}"
  286 + scheduler_pool_size: "${ACTORS_SYSTEM_SCHEDULER_POOL_SIZE:1}"
  287 + max_actor_init_attempts: "${ACTORS_SYSTEM_MAX_ACTOR_INIT_ATTEMPTS:10}"
  288 + app_dispatcher_pool_size: "${ACTORS_SYSTEM_APP_DISPATCHER_POOL_SIZE:1}"
  289 + tenant_dispatcher_pool_size: "${ACTORS_SYSTEM_TENANT_DISPATCHER_POOL_SIZE:2}"
  290 + device_dispatcher_pool_size: "${ACTORS_SYSTEM_DEVICE_DISPATCHER_POOL_SIZE:4}"
  291 + rule_dispatcher_pool_size: "${ACTORS_SYSTEM_RULE_DISPATCHER_POOL_SIZE:4}"
284 292 tenant:
285 293 create_components_on_init: "${ACTORS_TENANT_CREATE_COMPONENTS_ON_INIT:true}"
286 294 session:
... ... @@ -318,11 +326,6 @@ actors:
318 326 enabled: "${ACTORS_STATISTICS_ENABLED:true}"
319 327 js_print_interval_ms: "${ACTORS_JS_STATISTICS_PRINT_INTERVAL_MS:10000}"
320 328 persist_frequency: "${ACTORS_STATISTICS_PERSIST_FREQUENCY:3600000}"
321   - queue:
322   - # Enable/disable persistence of un-processed messages to the queue
323   - enabled: "${ACTORS_QUEUE_ENABLED:true}"
324   - # Maximum allowed timeout for persistence into the queue
325   - timeout: "${ACTORS_QUEUE_PERSISTENCE_TIMEOUT:30000}"
326 329
327 330 cache:
328 331 # caffeine or redis
... ...
... ... @@ -26,7 +26,9 @@ import java.util.Arrays;
26 26
27 27 @RunWith(ClasspathSuite.class)
28 28 @ClasspathSuite.ClassnameFilters({
29   - "org.thingsboard.server.mqtt.rpc.sql.*Test", "org.thingsboard.server.mqtt.telemetry.sql.*Test"})
  29 + "org.thingsboard.server.mqtt.rpc.sql.*Test",
  30 + "org.thingsboard.server.mqtt.telemetry.sql.*Test"
  31 +})
30 32 public class MqttSqlTestSuite {
31 33
32 34 @ClassRule
... ...
... ... @@ -136,7 +136,7 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractC
136 136 String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"24\",\"value\": 1},\"timeout\": 6000}";
137 137 String deviceId = savedDevice.getId().getId().toString();
138 138
139   - doPostAsync("/api/plugins/rpc/oneway/" + deviceId, setGpioRequest, String.class, status().isRequestTimeout(),
  139 + doPostAsync("/api/plugins/rpc/oneway/" + deviceId, setGpioRequest, String.class, status().is(409),
140 140 asyncContextTimeoutToUseRpcPlugin);
141 141 }
142 142
... ... @@ -193,7 +193,7 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractC
193 193 String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"27\",\"value\": 1},\"timeout\": 6000}";
194 194 String deviceId = savedDevice.getId().getId().toString();
195 195
196   - doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().isRequestTimeout(),
  196 + doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().is(409),
197 197 asyncContextTimeoutToUseRpcPlugin);
198 198 }
199 199
... ...
... ... @@ -15,8 +15,6 @@
15 15 */
16 16 package org.thingsboard.server.rules.flow;
17 17
18   -import akka.actor.ActorRef;
19   -import com.datastax.driver.core.utils.UUIDs;
20 18 import lombok.extern.slf4j.Slf4j;
21 19 import org.junit.After;
22 20 import org.junit.Assert;
... ... @@ -26,7 +24,6 @@ import org.mockito.Mockito;
26 24 import org.springframework.beans.factory.annotation.Autowired;
27 25 import org.thingsboard.rule.engine.metadata.TbGetAttributesNodeConfiguration;
28 26 import org.thingsboard.server.actors.ActorSystemContext;
29   -import org.thingsboard.server.actors.service.ActorService;
30 27 import org.thingsboard.server.common.data.*;
31 28 import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry;
32 29 import org.thingsboard.server.common.data.kv.StringDataEntry;
... ... @@ -36,7 +33,6 @@ import org.thingsboard.server.common.data.rule.RuleChainMetaData;
36 33 import org.thingsboard.server.common.data.rule.RuleNode;
37 34 import org.thingsboard.server.common.data.security.Authority;
38 35 import org.thingsboard.server.common.msg.TbMsg;
39   -import org.thingsboard.server.common.msg.TbMsgDataType;
40 36 import org.thingsboard.server.common.msg.TbMsgMetaData;
41 37 import org.thingsboard.server.common.msg.queue.QueueToRuleEngineMsg;
42 38 import org.thingsboard.server.common.msg.queue.TbMsgCallback;
... ... @@ -151,7 +147,7 @@ public abstract class AbstractRuleEngineFlowIntegrationTest extends AbstractRule
151 147 TbMsg tbMsg = TbMsg.newMsg("CUSTOM", device.getId(), new TbMsgMetaData(), "{}", tbMsgCallback);
152 148 QueueToRuleEngineMsg qMsg = new QueueToRuleEngineMsg(savedTenant.getId(), tbMsg, null, null);
153 149 // Pushing Message to the system
154   - actorSystem.tell(qMsg, ActorRef.noSender());
  150 + actorSystem.tell(qMsg);
155 151 Mockito.verify(tbMsgCallback, Mockito.timeout(10000)).onSuccess();
156 152
157 153 TimePageData<Event> eventsPage = getDebugEvents(savedTenant.getId(), ruleChain.getFirstRuleNodeId(), 1000);
... ... @@ -263,7 +259,7 @@ public abstract class AbstractRuleEngineFlowIntegrationTest extends AbstractRule
263 259 TbMsg tbMsg = TbMsg.newMsg("CUSTOM", device.getId(), new TbMsgMetaData(), "{}", tbMsgCallback);
264 260 QueueToRuleEngineMsg qMsg = new QueueToRuleEngineMsg(savedTenant.getId(), tbMsg, null, null);
265 261 // Pushing Message to the system
266   - actorSystem.tell(qMsg, ActorRef.noSender());
  262 + actorSystem.tell(qMsg);
267 263
268 264 Mockito.verify(tbMsgCallback, Mockito.timeout(10000)).onSuccess();
269 265
... ...
... ... @@ -15,8 +15,6 @@
15 15 */
16 16 package org.thingsboard.server.rules.lifecycle;
17 17
18   -import akka.actor.ActorRef;
19   -import com.datastax.driver.core.utils.UUIDs;
20 18 import lombok.extern.slf4j.Slf4j;
21 19 import org.junit.After;
22 20 import org.junit.Assert;
... ... @@ -26,7 +24,6 @@ import org.mockito.Mockito;
26 24 import org.springframework.beans.factory.annotation.Autowired;
27 25 import org.thingsboard.rule.engine.metadata.TbGetAttributesNodeConfiguration;
28 26 import org.thingsboard.server.actors.ActorSystemContext;
29   -import org.thingsboard.server.actors.service.ActorService;
30 27 import org.thingsboard.server.common.data.DataConstants;
31 28 import org.thingsboard.server.common.data.Device;
32 29 import org.thingsboard.server.common.data.Event;
... ... @@ -40,7 +37,6 @@ import org.thingsboard.server.common.data.rule.RuleChainMetaData;
40 37 import org.thingsboard.server.common.data.rule.RuleNode;
41 38 import org.thingsboard.server.common.data.security.Authority;
42 39 import org.thingsboard.server.common.msg.TbMsg;
43   -import org.thingsboard.server.common.msg.TbMsgDataType;
44 40 import org.thingsboard.server.common.msg.TbMsgMetaData;
45 41 import org.thingsboard.server.common.msg.queue.QueueToRuleEngineMsg;
46 42 import org.thingsboard.server.common.msg.queue.TbMsgCallback;
... ... @@ -142,7 +138,7 @@ public abstract class AbstractRuleEngineLifecycleIntegrationTest extends Abstrac
142 138 TbMsg tbMsg = TbMsg.newMsg("CUSTOM", device.getId(), new TbMsgMetaData(), "{}", tbMsgCallback);
143 139 QueueToRuleEngineMsg qMsg = new QueueToRuleEngineMsg(savedTenant.getId(), tbMsg, null, null);
144 140 // Pushing Message to the system
145   - actorSystem.tell(qMsg, ActorRef.noSender());
  141 + actorSystem.tell(qMsg);
146 142 Mockito.verify(tbMsgCallback, Mockito.timeout(3000)).onSuccess();
147 143
148 144
... ...
... ... @@ -13,8 +13,6 @@
13 13 <logger name="org.apache.cassandra" level="WARN"/>
14 14 <logger name="org.cassandraunit" level="INFO"/>
15 15
16   - <logger name="akka" level="INFO" />
17   -
18 16 <root level="WARN">
19 17 <appender-ref ref="console"/>
20 18 </root>
... ...
  1 +<!--
  2 +
  3 + Copyright © 2016-2020 The Thingsboard Authors
  4 +
  5 + Licensed under the Apache License, Version 2.0 (the "License");
  6 + you may not use this file except in compliance with the License.
  7 + You may obtain a copy of the License at
  8 +
  9 + http://www.apache.org/licenses/LICENSE-2.0
  10 +
  11 + Unless required by applicable law or agreed to in writing, software
  12 + distributed under the License is distributed on an "AS IS" BASIS,
  13 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14 + See the License for the specific language governing permissions and
  15 + limitations under the License.
  16 +
  17 +-->
  18 +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  19 + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  20 + <modelVersion>4.0.0</modelVersion>
  21 + <parent>
  22 + <groupId>org.thingsboard</groupId>
  23 + <version>2.5.3-SNAPSHOT</version>
  24 + <artifactId>common</artifactId>
  25 + </parent>
  26 + <groupId>org.thingsboard.common</groupId>
  27 + <artifactId>actor</artifactId>
  28 + <packaging>jar</packaging>
  29 +
  30 + <name>Thingsboard Actor system</name>
  31 + <url>https://thingsboard.io</url>
  32 +
  33 + <properties>
  34 + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  35 + <main.dir>${basedir}/../..</main.dir>
  36 + </properties>
  37 +
  38 + <dependencies>
  39 + <dependency>
  40 + <groupId>org.thingsboard.common</groupId>
  41 + <artifactId>util</artifactId>
  42 + </dependency>
  43 + <dependency>
  44 + <groupId>org.thingsboard.common</groupId>
  45 + <artifactId>message</artifactId>
  46 + </dependency>
  47 + <dependency>
  48 + <groupId>org.slf4j</groupId>
  49 + <artifactId>slf4j-api</artifactId>
  50 + </dependency>
  51 + <dependency>
  52 + <groupId>org.slf4j</groupId>
  53 + <artifactId>log4j-over-slf4j</artifactId>
  54 + </dependency>
  55 + <dependency>
  56 + <groupId>ch.qos.logback</groupId>
  57 + <artifactId>logback-core</artifactId>
  58 + </dependency>
  59 + <dependency>
  60 + <groupId>ch.qos.logback</groupId>
  61 + <artifactId>logback-classic</artifactId>
  62 + </dependency>
  63 + <dependency>
  64 + <groupId>junit</groupId>
  65 + <artifactId>junit</artifactId>
  66 + <scope>test</scope>
  67 + </dependency>
  68 + <dependency>
  69 + <groupId>org.mockito</groupId>
  70 + <artifactId>mockito-all</artifactId>
  71 + <scope>test</scope>
  72 + </dependency>
  73 + </dependencies>
  74 +
  75 +</project>
... ...
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.actors;
  17 +
  18 +import lombok.Getter;
  19 +
  20 +public abstract class AbstractTbActor implements TbActor {
  21 +
  22 + @Getter
  23 + protected TbActorCtx ctx;
  24 +
  25 + @Override
  26 + public void init(TbActorCtx ctx) throws TbActorException {
  27 + this.ctx = ctx;
  28 + }
  29 +
  30 + @Override
  31 + public TbActorRef getActorRef() {
  32 + return ctx;
  33 + }
  34 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.actors;
  17 +
  18 +import lombok.Data;
  19 +import lombok.Getter;
  20 +import lombok.extern.slf4j.Slf4j;
  21 +import org.thingsboard.common.util.ThingsBoardThreadFactory;
  22 +import org.thingsboard.server.common.msg.TbActorMsg;
  23 +
  24 +import java.util.Collections;
  25 +import java.util.List;
  26 +import java.util.Set;
  27 +import java.util.concurrent.ConcurrentHashMap;
  28 +import java.util.concurrent.ConcurrentMap;
  29 +import java.util.concurrent.ExecutorService;
  30 +import java.util.concurrent.Executors;
  31 +import java.util.concurrent.ScheduledExecutorService;
  32 +import java.util.concurrent.TimeUnit;
  33 +import java.util.concurrent.locks.Lock;
  34 +import java.util.concurrent.locks.ReentrantLock;
  35 +import java.util.function.Predicate;
  36 +import java.util.stream.Collectors;
  37 +
  38 +@Slf4j
  39 +@Data
  40 +public class DefaultTbActorSystem implements TbActorSystem {
  41 +
  42 + private final ConcurrentMap<String, Dispatcher> dispatchers = new ConcurrentHashMap<>();
  43 + private final ConcurrentMap<TbActorId, TbActorMailbox> actors = new ConcurrentHashMap<>();
  44 + private final ConcurrentMap<TbActorId, ReentrantLock> actorCreationLocks = new ConcurrentHashMap<>();
  45 + private final ConcurrentMap<TbActorId, Set<TbActorId>> parentChildMap = new ConcurrentHashMap<>();
  46 +
  47 + @Getter
  48 + private final TbActorSystemSettings settings;
  49 + @Getter
  50 + private final ScheduledExecutorService scheduler;
  51 +
  52 + public DefaultTbActorSystem(TbActorSystemSettings settings) {
  53 + this.settings = settings;
  54 + this.scheduler = Executors.newScheduledThreadPool(settings.getSchedulerPoolSize(), ThingsBoardThreadFactory.forName("actor-system-scheduler"));
  55 + }
  56 +
  57 + @Override
  58 + public void createDispatcher(String dispatcherId, ExecutorService executor) {
  59 + Dispatcher current = dispatchers.putIfAbsent(dispatcherId, new Dispatcher(dispatcherId, executor));
  60 + if (current != null) {
  61 + throw new RuntimeException("Dispatcher with id [" + dispatcherId + "] is already registered!");
  62 + }
  63 + }
  64 +
  65 + @Override
  66 + public void destroyDispatcher(String dispatcherId) {
  67 + Dispatcher dispatcher = dispatchers.remove(dispatcherId);
  68 + if (dispatcher != null) {
  69 + dispatcher.getExecutor().shutdownNow();
  70 + } else {
  71 + throw new RuntimeException("Dispatcher with id [" + dispatcherId + "] is not registered!");
  72 + }
  73 + }
  74 +
  75 + @Override
  76 + public TbActorRef getActor(TbActorId actorId) {
  77 + return actors.get(actorId);
  78 + }
  79 +
  80 + @Override
  81 + public TbActorRef createRootActor(String dispatcherId, TbActorCreator creator) {
  82 + return createActor(dispatcherId, creator, null);
  83 + }
  84 +
  85 + @Override
  86 + public TbActorRef createChildActor(String dispatcherId, TbActorCreator creator, TbActorId parent) {
  87 + return createActor(dispatcherId, creator, parent);
  88 + }
  89 +
  90 + private TbActorRef createActor(String dispatcherId, TbActorCreator creator, TbActorId parent) {
  91 + Dispatcher dispatcher = dispatchers.get(dispatcherId);
  92 + if (dispatcher == null) {
  93 + log.warn("Dispatcher with id [{}] is not registered!", dispatcherId);
  94 + throw new RuntimeException("Dispatcher with id [" + dispatcherId + "] is not registered!");
  95 + }
  96 +
  97 + TbActorId actorId = creator.createActorId();
  98 + TbActorMailbox actorMailbox = actors.get(actorId);
  99 + if (actorMailbox != null) {
  100 + log.debug("Actor with id [{}] is already registered!", actorId);
  101 + } else {
  102 + Lock actorCreationLock = actorCreationLocks.computeIfAbsent(actorId, id -> new ReentrantLock());
  103 + actorCreationLock.lock();
  104 + try {
  105 + actorMailbox = actors.get(actorId);
  106 + if (actorMailbox == null) {
  107 + log.debug("Creating actor with id [{}]!", actorId);
  108 + TbActor actor = creator.createActor();
  109 + TbActorRef parentRef = null;
  110 + if (parent != null) {
  111 + parentRef = getActor(parent);
  112 + if (parentRef == null) {
  113 + throw new TbActorNotRegisteredException(parent, "Parent Actor with id [" + parent + "] is not registered!");
  114 + }
  115 + }
  116 + TbActorMailbox mailbox = new TbActorMailbox(this, settings, actorId, parentRef, actor, dispatcher);
  117 + actors.put(actorId, mailbox);
  118 + mailbox.initActor();
  119 + actorMailbox = mailbox;
  120 + if (parent != null) {
  121 + parentChildMap.computeIfAbsent(parent, id -> ConcurrentHashMap.newKeySet()).add(actorId);
  122 + }
  123 + } else {
  124 + log.debug("Actor with id [{}] is already registered!", actorId);
  125 + }
  126 + } finally {
  127 + actorCreationLock.unlock();
  128 + actorCreationLocks.remove(actorId);
  129 + }
  130 + }
  131 + return actorMailbox;
  132 + }
  133 +
  134 + @Override
  135 + public void tellWithHighPriority(TbActorId target, TbActorMsg actorMsg) {
  136 + tell(target, actorMsg, true);
  137 + }
  138 +
  139 + @Override
  140 + public void tell(TbActorId target, TbActorMsg actorMsg) {
  141 + tell(target, actorMsg, false);
  142 + }
  143 +
  144 + private void tell(TbActorId target, TbActorMsg actorMsg, boolean highPriority) {
  145 + TbActorMailbox mailbox = actors.get(target);
  146 + if (mailbox == null) {
  147 + throw new TbActorNotRegisteredException(target, "Actor with id [" + target + "] is not registered!");
  148 + }
  149 + if (highPriority) {
  150 + mailbox.tellWithHighPriority(actorMsg);
  151 + } else {
  152 + mailbox.tell(actorMsg);
  153 + }
  154 + }
  155 +
  156 +
  157 + @Override
  158 + public void broadcastToChildren(TbActorId parent, TbActorMsg msg) {
  159 + broadcastToChildren(parent, id -> true, msg);
  160 + }
  161 +
  162 + @Override
  163 + public void broadcastToChildren(TbActorId parent, Predicate<TbActorId> childFilter, TbActorMsg msg) {
  164 + Set<TbActorId> children = parentChildMap.get(parent);
  165 + if (children != null) {
  166 + children.stream().filter(childFilter).forEach(id -> tell(id, msg));
  167 + }
  168 + }
  169 +
  170 + @Override
  171 + public List<TbActorId> filterChildren(TbActorId parent, Predicate<TbActorId> childFilter) {
  172 + Set<TbActorId> children = parentChildMap.get(parent);
  173 + if (children != null) {
  174 + return children.stream().filter(childFilter).collect(Collectors.toList());
  175 + } else {
  176 + return Collections.emptyList();
  177 + }
  178 + }
  179 +
  180 + @Override
  181 + public void stop(TbActorRef actorRef) {
  182 + stop(actorRef.getActorId());
  183 + }
  184 +
  185 + @Override
  186 + public void stop(TbActorId actorId) {
  187 + Set<TbActorId> children = parentChildMap.remove(actorId);
  188 + if (children != null) {
  189 + for (TbActorId child : children) {
  190 + stop(child);
  191 + }
  192 + }
  193 + TbActorMailbox mailbox = actors.remove(actorId);
  194 + if (mailbox != null) {
  195 + mailbox.destroy();
  196 + }
  197 + }
  198 +
  199 + @Override
  200 + public void stop() {
  201 + dispatchers.values().forEach(dispatcher -> {
  202 + dispatcher.getExecutor().shutdown();
  203 + try {
  204 + dispatcher.getExecutor().awaitTermination(3, TimeUnit.SECONDS);
  205 + } catch (InterruptedException e) {
  206 + log.warn("[{}] Failed to stop dispatcher", dispatcher.getDispatcherId(), e);
  207 + }
  208 + });
  209 + if (scheduler != null) {
  210 + scheduler.shutdownNow();
  211 + }
  212 + actors.clear();
  213 + }
  214 +
  215 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.actors;
  17 +
  18 +import lombok.Data;
  19 +
  20 +import java.util.concurrent.ExecutorService;
  21 +
  22 +@Data
  23 +class Dispatcher {
  24 +
  25 + private final String dispatcherId;
  26 + private final ExecutorService executor;
  27 +
  28 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.actors;
  17 +
  18 +import lombok.Getter;
  19 +import lombok.ToString;
  20 +
  21 +@ToString
  22 +public class InitFailureStrategy {
  23 +
  24 + @Getter
  25 + private boolean stop;
  26 + @Getter
  27 + private long retryDelay;
  28 +
  29 + private InitFailureStrategy(boolean stop, long retryDelay) {
  30 + this.stop = stop;
  31 + this.retryDelay = retryDelay;
  32 + }
  33 +
  34 + public static InitFailureStrategy retryImmediately() {
  35 + return new InitFailureStrategy(false, 0);
  36 + }
  37 +
  38 + public static InitFailureStrategy retryWithDelay(long ms) {
  39 + return new InitFailureStrategy(false, ms);
  40 + }
  41 +
  42 + public static InitFailureStrategy stop() {
  43 + return new InitFailureStrategy(true, 0);
  44 + }
  45 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.actors;
  17 +
  18 +import lombok.Getter;
  19 +import lombok.ToString;
  20 +
  21 +@ToString
  22 +public class ProcessFailureStrategy {
  23 +
  24 + @Getter
  25 + private boolean stop;
  26 +
  27 + private ProcessFailureStrategy(boolean stop) {
  28 + this.stop = stop;
  29 + }
  30 +
  31 + public static ProcessFailureStrategy stop() {
  32 + return new ProcessFailureStrategy(true);
  33 + }
  34 +
  35 + public static ProcessFailureStrategy resume() {
  36 + return new ProcessFailureStrategy(false);
  37 + }
  38 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.actors;
  17 +
  18 +import org.thingsboard.server.common.msg.TbActorMsg;
  19 +
  20 +public interface TbActor {
  21 +
  22 + boolean process(TbActorMsg msg);
  23 +
  24 + TbActorRef getActorRef();
  25 +
  26 + default void init(TbActorCtx ctx) throws TbActorException {
  27 + }
  28 +
  29 + default void destroy() throws TbActorException {
  30 + }
  31 +
  32 + default InitFailureStrategy onInitFailure(int attempt, Throwable t) {
  33 + return InitFailureStrategy.retryWithDelay(5000 * attempt);
  34 + }
  35 +
  36 + default ProcessFailureStrategy onProcessFailure(Throwable t) {
  37 + if (t instanceof Error) {
  38 + return ProcessFailureStrategy.stop();
  39 + } else {
  40 + return ProcessFailureStrategy.resume();
  41 + }
  42 + }
  43 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.actors;
  17 +
  18 +public interface TbActorCreator {
  19 +
  20 + TbActorId createActorId();
  21 +
  22 + TbActor createActor();
  23 +
  24 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.actors;
  17 +
  18 +import org.thingsboard.server.common.msg.TbActorMsg;
  19 +
  20 +import java.util.List;
  21 +import java.util.function.Predicate;
  22 +import java.util.function.Supplier;
  23 +
  24 +public interface TbActorCtx extends TbActorRef {
  25 +
  26 + TbActorId getSelf();
  27 +
  28 + TbActorRef getParentRef();
  29 +
  30 + void tell(TbActorId target, TbActorMsg msg);
  31 +
  32 + void stop(TbActorId target);
  33 +
  34 + TbActorRef getOrCreateChildActor(TbActorId actorId, Supplier<String> dispatcher, Supplier<TbActorCreator> creator);
  35 +
  36 + void broadcastToChildren(TbActorMsg msg);
  37 +
  38 + void broadcastToChildren(TbActorMsg msg, Predicate<TbActorId> childFilter);
  39 +
  40 + List<TbActorId> filterChildren(Predicate<TbActorId> childFilter);
  41 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.actors;
  17 +
  18 +public class TbActorException extends Exception {
  19 +
  20 + public TbActorException(String message, Throwable cause) {
  21 + super(message, cause);
  22 + }
  23 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.actors;
  17 +
  18 +public interface TbActorId {
  19 +
  20 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.actors;
  17 +
  18 +import lombok.Data;
  19 +import lombok.extern.slf4j.Slf4j;
  20 +import org.thingsboard.server.common.msg.TbActorMsg;
  21 +
  22 +import java.util.List;
  23 +import java.util.concurrent.ConcurrentLinkedQueue;
  24 +import java.util.concurrent.TimeUnit;
  25 +import java.util.concurrent.atomic.AtomicBoolean;
  26 +import java.util.function.Predicate;
  27 +import java.util.function.Supplier;
  28 +
  29 +@Slf4j
  30 +@Data
  31 +public final class TbActorMailbox implements TbActorCtx {
  32 + private static final boolean HIGH_PRIORITY = true;
  33 + private static final boolean NORMAL_PRIORITY = false;
  34 +
  35 + private static final boolean FREE = false;
  36 + private static final boolean BUSY = true;
  37 +
  38 + private static final boolean NOT_READY = false;
  39 + private static final boolean READY = true;
  40 +
  41 + private final TbActorSystem system;
  42 + private final TbActorSystemSettings settings;
  43 + private final TbActorId selfId;
  44 + private final TbActorRef parentRef;
  45 + private final TbActor actor;
  46 + private final Dispatcher dispatcher;
  47 + private final ConcurrentLinkedQueue<TbActorMsg> highPriorityMsgs = new ConcurrentLinkedQueue<>();
  48 + private final ConcurrentLinkedQueue<TbActorMsg> normalPriorityMsgs = new ConcurrentLinkedQueue<>();
  49 + private final AtomicBoolean busy = new AtomicBoolean(FREE);
  50 + private final AtomicBoolean ready = new AtomicBoolean(NOT_READY);
  51 + private final AtomicBoolean destroyInProgress = new AtomicBoolean();
  52 +
  53 + public void initActor() {
  54 + dispatcher.getExecutor().execute(() -> tryInit(1));
  55 + }
  56 +
  57 + private void tryInit(int attempt) {
  58 + try {
  59 + log.debug("[{}] Trying to init actor, attempt: {}", selfId, attempt);
  60 + if (!destroyInProgress.get()) {
  61 + actor.init(this);
  62 + if (!destroyInProgress.get()) {
  63 + ready.set(READY);
  64 + tryProcessQueue(false);
  65 + }
  66 + }
  67 + } catch (Throwable t) {
  68 + log.debug("[{}] Failed to init actor, attempt: {}", selfId, attempt, t);
  69 + int attemptIdx = attempt + 1;
  70 + InitFailureStrategy strategy = actor.onInitFailure(attempt, t);
  71 + if (strategy.isStop() || (settings.getMaxActorInitAttempts() > 0 && attemptIdx > settings.getMaxActorInitAttempts())) {
  72 + log.info("[{}] Failed to init actor, attempt {}, going to stop attempts.", selfId, attempt, t);
  73 + system.stop(selfId);
  74 + } else if (strategy.getRetryDelay() > 0) {
  75 + log.info("[{}] Failed to init actor, attempt {}, going to retry in attempts in {}ms", selfId, attempt, strategy.getRetryDelay());
  76 + log.debug("[{}] Error", selfId, t);
  77 + system.getScheduler().schedule(() -> dispatcher.getExecutor().execute(() -> tryInit(attemptIdx)), strategy.getRetryDelay(), TimeUnit.MILLISECONDS);
  78 + } else {
  79 + log.info("[{}] Failed to init actor, attempt {}, going to retry immediately", selfId, attempt);
  80 + log.debug("[{}] Error", selfId, t);
  81 + dispatcher.getExecutor().execute(() -> tryInit(attemptIdx));
  82 + }
  83 + }
  84 + }
  85 +
  86 + private void enqueue(TbActorMsg msg, boolean highPriority) {
  87 + if (highPriority) {
  88 + highPriorityMsgs.add(msg);
  89 + } else {
  90 + normalPriorityMsgs.add(msg);
  91 + }
  92 + tryProcessQueue(true);
  93 + }
  94 +
  95 + private void tryProcessQueue(boolean newMsg) {
  96 + if (ready.get() == READY) {
  97 + if (newMsg || !highPriorityMsgs.isEmpty() || !normalPriorityMsgs.isEmpty()) {
  98 + if (busy.compareAndSet(FREE, BUSY)) {
  99 + dispatcher.getExecutor().execute(this::processMailbox);
  100 + } else {
  101 + log.trace("[{}] MessageBox is busy, new msg: {}", selfId, newMsg);
  102 + }
  103 + } else {
  104 + log.trace("[{}] MessageBox is empty, new msg: {}", selfId, newMsg);
  105 + }
  106 + } else {
  107 + log.trace("[{}] MessageBox is not ready, new msg: {}", selfId, newMsg);
  108 + }
  109 + }
  110 +
  111 + private void processMailbox() {
  112 + boolean noMoreElements = false;
  113 + for (int i = 0; i < settings.getActorThroughput(); i++) {
  114 + TbActorMsg msg = highPriorityMsgs.poll();
  115 + if (msg == null) {
  116 + msg = normalPriorityMsgs.poll();
  117 + }
  118 + if (msg != null) {
  119 + try {
  120 + log.debug("[{}] Going to process message: {}", selfId, msg);
  121 + actor.process(msg);
  122 + } catch (Throwable t) {
  123 + log.debug("[{}] Failed to process message: {}", selfId, msg, t);
  124 + ProcessFailureStrategy strategy = actor.onProcessFailure(t);
  125 + if (strategy.isStop()) {
  126 + system.stop(selfId);
  127 + }
  128 + }
  129 + } else {
  130 + noMoreElements = true;
  131 + break;
  132 + }
  133 + }
  134 + if (noMoreElements) {
  135 + busy.set(FREE);
  136 + dispatcher.getExecutor().execute(() -> tryProcessQueue(false));
  137 + } else {
  138 + dispatcher.getExecutor().execute(this::processMailbox);
  139 + }
  140 + }
  141 +
  142 + @Override
  143 + public TbActorId getSelf() {
  144 + return selfId;
  145 + }
  146 +
  147 + @Override
  148 + public void tell(TbActorId target, TbActorMsg actorMsg) {
  149 + system.tell(target, actorMsg);
  150 + }
  151 +
  152 + @Override
  153 + public void broadcastToChildren(TbActorMsg msg) {
  154 + system.broadcastToChildren(selfId, msg);
  155 + }
  156 +
  157 + @Override
  158 + public void broadcastToChildren(TbActorMsg msg, Predicate<TbActorId> childFilter) {
  159 + system.broadcastToChildren(selfId, childFilter, msg);
  160 + }
  161 +
  162 + @Override
  163 + public List<TbActorId> filterChildren(Predicate<TbActorId> childFilter) {
  164 + return system.filterChildren(selfId, childFilter);
  165 + }
  166 +
  167 + @Override
  168 + public void stop(TbActorId target) {
  169 + system.stop(target);
  170 + }
  171 +
  172 + @Override
  173 + public TbActorRef getOrCreateChildActor(TbActorId actorId, Supplier<String> dispatcher, Supplier<TbActorCreator> creator) {
  174 + TbActorRef actorRef = system.getActor(actorId);
  175 + if (actorRef == null) {
  176 + return system.createChildActor(dispatcher.get(), creator.get(), selfId);
  177 + } else {
  178 + return actorRef;
  179 + }
  180 + }
  181 +
  182 + public void destroy() {
  183 + destroyInProgress.set(true);
  184 + dispatcher.getExecutor().execute(() -> {
  185 + try {
  186 + ready.set(NOT_READY);
  187 + actor.destroy();
  188 + } catch (Throwable t) {
  189 + log.warn("[{}] Failed to destroy actor: {}", selfId, t);
  190 + }
  191 + });
  192 + }
  193 +
  194 + @Override
  195 + public TbActorId getActorId() {
  196 + return selfId;
  197 + }
  198 +
  199 + @Override
  200 + public void tell(TbActorMsg actorMsg) {
  201 + enqueue(actorMsg, NORMAL_PRIORITY);
  202 + }
  203 +
  204 + @Override
  205 + public void tellWithHighPriority(TbActorMsg actorMsg) {
  206 + enqueue(actorMsg, HIGH_PRIORITY);
  207 + }
  208 +
  209 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.actors;
  17 +
  18 +import lombok.Getter;
  19 +
  20 +public class TbActorNotRegisteredException extends RuntimeException {
  21 +
  22 + @Getter
  23 + private TbActorId target;
  24 +
  25 + public TbActorNotRegisteredException(TbActorId target, String message) {
  26 + super(message);
  27 + this.target = target;
  28 + }
  29 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.actors;
  17 +
  18 +import org.thingsboard.server.common.msg.TbActorMsg;
  19 +
  20 +public interface TbActorRef {
  21 +
  22 + TbActorId getActorId();
  23 +
  24 + void tell(TbActorMsg actorMsg);
  25 +
  26 + void tellWithHighPriority(TbActorMsg actorMsg);
  27 +
  28 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.actors;
  17 +
  18 +import org.thingsboard.server.common.msg.TbActorMsg;
  19 +
  20 +import java.util.List;
  21 +import java.util.concurrent.ExecutorService;
  22 +import java.util.concurrent.ScheduledExecutorService;
  23 +import java.util.function.Predicate;
  24 +
  25 +public interface TbActorSystem {
  26 +
  27 + ScheduledExecutorService getScheduler();
  28 +
  29 + void createDispatcher(String dispatcherId, ExecutorService executor);
  30 +
  31 + void destroyDispatcher(String dispatcherId);
  32 +
  33 + TbActorRef getActor(TbActorId actorId);
  34 +
  35 + TbActorRef createRootActor(String dispatcherId, TbActorCreator creator);
  36 +
  37 + TbActorRef createChildActor(String dispatcherId, TbActorCreator creator, TbActorId parent);
  38 +
  39 + void tell(TbActorId target, TbActorMsg actorMsg);
  40 +
  41 + void tellWithHighPriority(TbActorId target, TbActorMsg actorMsg);
  42 +
  43 + void stop(TbActorRef actorRef);
  44 +
  45 + void stop(TbActorId actorId);
  46 +
  47 + void stop();
  48 +
  49 + void broadcastToChildren(TbActorId parent, TbActorMsg msg);
  50 +
  51 + void broadcastToChildren(TbActorId parent, Predicate<TbActorId> childFilter, TbActorMsg msg);
  52 +
  53 + List<TbActorId> filterChildren(TbActorId parent, Predicate<TbActorId> childFilter);
  54 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.actors;
  17 +
  18 +import lombok.Data;
  19 +
  20 +@Data
  21 +public class TbActorSystemSettings {
  22 +
  23 + private final int actorThroughput;
  24 + private final int schedulerPoolSize;
  25 + private final int maxActorInitAttempts;
  26 +
  27 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.actors;
  17 +
  18 +import lombok.Getter;
  19 +import org.thingsboard.server.common.data.id.EntityId;
  20 +
  21 +import java.util.Objects;
  22 +
  23 +public class TbEntityActorId implements TbActorId {
  24 +
  25 + @Getter
  26 + private final EntityId entityId;
  27 +
  28 + public TbEntityActorId(EntityId entityId) {
  29 + this.entityId = entityId;
  30 + }
  31 +
  32 + @Override
  33 + public String toString() {
  34 + return entityId.getEntityType() + "|" + entityId.getId();
  35 + }
  36 +
  37 + @Override
  38 + public boolean equals(Object o) {
  39 + if (this == o) return true;
  40 + if (o == null || getClass() != o.getClass()) return false;
  41 + TbEntityActorId that = (TbEntityActorId) o;
  42 + return entityId.equals(that.entityId);
  43 + }
  44 +
  45 + @Override
  46 + public int hashCode() {
  47 + return Objects.hash(entityId);
  48 + }
  49 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.actors;
  17 +
  18 +import java.util.Objects;
  19 +
  20 +public class TbStringActorId implements TbActorId {
  21 +
  22 + private final String id;
  23 +
  24 + public TbStringActorId(String id) {
  25 + this.id = id;
  26 + }
  27 +
  28 + @Override
  29 + public String toString() {
  30 + return id;
  31 + }
  32 +
  33 + @Override
  34 + public boolean equals(Object o) {
  35 + if (this == o) return true;
  36 + if (o == null || getClass() != o.getClass()) return false;
  37 + TbStringActorId that = (TbStringActorId) o;
  38 + return id.equals(that.id);
  39 + }
  40 +
  41 + @Override
  42 + public int hashCode() {
  43 + return Objects.hash(id);
  44 + }
  45 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.actors;
  17 +
  18 +import lombok.extern.slf4j.Slf4j;
  19 +import org.junit.After;
  20 +import org.junit.Assert;
  21 +import org.junit.Before;
  22 +import org.junit.Test;
  23 +import org.junit.runner.RunWith;
  24 +import org.mockito.runners.MockitoJUnitRunner;
  25 +import org.thingsboard.server.common.data.id.DeviceId;
  26 +
  27 +import java.util.ArrayList;
  28 +import java.util.List;
  29 +import java.util.Random;
  30 +import java.util.UUID;
  31 +import java.util.concurrent.CountDownLatch;
  32 +import java.util.concurrent.ExecutorService;
  33 +import java.util.concurrent.Executors;
  34 +import java.util.concurrent.TimeUnit;
  35 +import java.util.concurrent.atomic.AtomicInteger;
  36 +import java.util.concurrent.atomic.AtomicLong;
  37 +
  38 +@Slf4j
  39 +@RunWith(MockitoJUnitRunner.class)
  40 +public class ActorSystemTest {
  41 +
  42 + public static final String ROOT_DISPATCHER = "root-dispatcher";
  43 + private static final int _100K = 100 * 1024;
  44 +
  45 + private volatile TbActorSystem actorSystem;
  46 + private volatile ExecutorService submitPool;
  47 + private int parallelism;
  48 +
  49 + @Before
  50 + public void initActorSystem() {
  51 + int cores = Runtime.getRuntime().availableProcessors();
  52 + parallelism = Math.max(2, cores / 2);
  53 + TbActorSystemSettings settings = new TbActorSystemSettings(5, parallelism, 42);
  54 + actorSystem = new DefaultTbActorSystem(settings);
  55 + submitPool = Executors.newWorkStealingPool(parallelism);
  56 + }
  57 +
  58 + @After
  59 + public void shutdownActorSystem() {
  60 + actorSystem.stop();
  61 + submitPool.shutdownNow();
  62 + }
  63 +
  64 + @Test
  65 + public void test1actorsAnd100KMessages() throws InterruptedException {
  66 + actorSystem.createDispatcher(ROOT_DISPATCHER, Executors.newWorkStealingPool(parallelism));
  67 + testActorsAndMessages(1, _100K, 1);
  68 + }
  69 +
  70 + @Test
  71 + public void test10actorsAnd100KMessages() throws InterruptedException {
  72 + actorSystem.createDispatcher(ROOT_DISPATCHER, Executors.newWorkStealingPool(parallelism));
  73 + testActorsAndMessages(10, _100K, 1);
  74 + }
  75 +
  76 + @Test
  77 + public void test100KActorsAnd1Messages5timesSingleThread() throws InterruptedException {
  78 + actorSystem.createDispatcher(ROOT_DISPATCHER, Executors.newSingleThreadExecutor());
  79 + testActorsAndMessages(_100K, 1, 5);
  80 + }
  81 +
  82 + @Test
  83 + public void test100KActorsAnd1Messages5times() throws InterruptedException {
  84 + actorSystem.createDispatcher(ROOT_DISPATCHER, Executors.newWorkStealingPool(parallelism));
  85 + testActorsAndMessages(_100K, 1, 5);
  86 + }
  87 +
  88 + @Test
  89 + public void test100KActorsAnd10Messages() throws InterruptedException {
  90 + actorSystem.createDispatcher(ROOT_DISPATCHER, Executors.newWorkStealingPool(parallelism));
  91 + testActorsAndMessages(_100K, 10, 1);
  92 + }
  93 +
  94 + @Test
  95 + public void test1KActorsAnd1KMessages() throws InterruptedException {
  96 + actorSystem.createDispatcher(ROOT_DISPATCHER, Executors.newWorkStealingPool(parallelism));
  97 + testActorsAndMessages(1000, 1000, 10);
  98 + }
  99 +
  100 + @Test
  101 + public void testNoMessagesAfterDestroy() throws InterruptedException {
  102 + actorSystem.createDispatcher(ROOT_DISPATCHER, Executors.newWorkStealingPool(parallelism));
  103 + ActorTestCtx testCtx1 = getActorTestCtx(1);
  104 + ActorTestCtx testCtx2 = getActorTestCtx(1);
  105 +
  106 + TbActorRef actorId1 = actorSystem.createRootActor(ROOT_DISPATCHER, new SlowInitActor.SlowInitActorCreator(
  107 + new TbEntityActorId(new DeviceId(UUID.randomUUID())), testCtx1));
  108 + TbActorRef actorId2 = actorSystem.createRootActor(ROOT_DISPATCHER, new SlowInitActor.SlowInitActorCreator(
  109 + new TbEntityActorId(new DeviceId(UUID.randomUUID())), testCtx2));
  110 +
  111 + actorId1.tell(new IntTbActorMsg(42));
  112 + actorId2.tell(new IntTbActorMsg(42));
  113 + actorSystem.stop(actorId1);
  114 +
  115 + Assert.assertTrue(testCtx2.getLatch().await(1, TimeUnit.SECONDS));
  116 + Assert.assertFalse(testCtx1.getLatch().await(1, TimeUnit.SECONDS));
  117 + }
  118 +
  119 + @Test
  120 + public void testOneActorCreated() throws InterruptedException {
  121 + actorSystem.createDispatcher(ROOT_DISPATCHER, Executors.newWorkStealingPool(parallelism));
  122 + ActorTestCtx testCtx1 = getActorTestCtx(1);
  123 + ActorTestCtx testCtx2 = getActorTestCtx(1);
  124 + TbActorId actorId = new TbEntityActorId(new DeviceId(UUID.randomUUID()));
  125 + submitPool.submit(() -> actorSystem.createRootActor(ROOT_DISPATCHER, new SlowCreateActor.SlowCreateActorCreator(actorId, testCtx1)));
  126 + submitPool.submit(() -> actorSystem.createRootActor(ROOT_DISPATCHER, new SlowCreateActor.SlowCreateActorCreator(actorId, testCtx2)));
  127 +
  128 + Thread.sleep(1000);
  129 + actorSystem.tell(actorId, new IntTbActorMsg(42));
  130 +
  131 + Assert.assertTrue(testCtx1.getLatch().await(1, TimeUnit.SECONDS));
  132 + Assert.assertFalse(testCtx2.getLatch().await(1, TimeUnit.SECONDS));
  133 + }
  134 +
  135 + @Test
  136 + public void testActorCreatorCalledOnce() throws InterruptedException {
  137 + actorSystem.createDispatcher(ROOT_DISPATCHER, Executors.newWorkStealingPool(parallelism));
  138 + ActorTestCtx testCtx = getActorTestCtx(1);
  139 + TbActorId actorId = new TbEntityActorId(new DeviceId(UUID.randomUUID()));
  140 + for (int i = 0; i < 1000; i++) {
  141 + submitPool.submit(() -> actorSystem.createRootActor(ROOT_DISPATCHER, new SlowCreateActor.SlowCreateActorCreator(actorId, testCtx)));
  142 + }
  143 + Thread.sleep(1000);
  144 + actorSystem.tell(actorId, new IntTbActorMsg(42));
  145 +
  146 + Assert.assertTrue(testCtx.getLatch().await(1, TimeUnit.SECONDS));
  147 + //One for creation and one for message
  148 + Assert.assertEquals(2, testCtx.getInvocationCount().get());
  149 + }
  150 +
  151 + @Test
  152 + public void testFailedInit() throws InterruptedException {
  153 + actorSystem.createDispatcher(ROOT_DISPATCHER, Executors.newWorkStealingPool(parallelism));
  154 + ActorTestCtx testCtx1 = getActorTestCtx(1);
  155 + ActorTestCtx testCtx2 = getActorTestCtx(1);
  156 +
  157 + TbActorRef actorId1 = actorSystem.createRootActor(ROOT_DISPATCHER, new FailedToInitActor.FailedToInitActorCreator(
  158 + new TbEntityActorId(new DeviceId(UUID.randomUUID())), testCtx1, 1, 3000));
  159 + TbActorRef actorId2 = actorSystem.createRootActor(ROOT_DISPATCHER, new FailedToInitActor.FailedToInitActorCreator(
  160 + new TbEntityActorId(new DeviceId(UUID.randomUUID())), testCtx2, 2, 1));
  161 +
  162 + actorId1.tell(new IntTbActorMsg(42));
  163 + actorId2.tell(new IntTbActorMsg(42));
  164 +
  165 + Assert.assertFalse(testCtx1.getLatch().await(2, TimeUnit.SECONDS));
  166 + Assert.assertTrue(testCtx2.getLatch().await(1, TimeUnit.SECONDS));
  167 + Assert.assertTrue(testCtx1.getLatch().await(3, TimeUnit.SECONDS));
  168 + }
  169 +
  170 +
  171 + public void testActorsAndMessages(int actorsCount, int msgNumber, int times) throws InterruptedException {
  172 + Random random = new Random();
  173 + int[] randomIntegers = new int[msgNumber];
  174 + long sumTmp = 0;
  175 + for (int i = 0; i < msgNumber; i++) {
  176 + int tmp = random.nextInt();
  177 + randomIntegers[i] = tmp;
  178 + sumTmp += tmp;
  179 + }
  180 + long expected = sumTmp;
  181 +
  182 + List<ActorTestCtx> testCtxes = new ArrayList<>();
  183 +
  184 + List<TbActorRef> actorRefs = new ArrayList<>();
  185 + for (int actorIdx = 0; actorIdx < actorsCount; actorIdx++) {
  186 + ActorTestCtx testCtx = getActorTestCtx(msgNumber);
  187 + actorRefs.add(actorSystem.createRootActor(ROOT_DISPATCHER, new TestRootActor.TestRootActorCreator(
  188 + new TbEntityActorId(new DeviceId(UUID.randomUUID())), testCtx)));
  189 + testCtxes.add(testCtx);
  190 + }
  191 +
  192 + for (int t = 0; t < times; t++) {
  193 + long start = System.nanoTime();
  194 + for (int i = 0; i < msgNumber; i++) {
  195 + int tmp = randomIntegers[i];
  196 + submitPool.execute(() -> actorRefs.forEach(actorId -> actorId.tell(new IntTbActorMsg(tmp))));
  197 + }
  198 + log.info("Submitted all messages");
  199 + testCtxes.forEach(ctx -> {
  200 + try {
  201 + boolean success = ctx.getLatch().await(1, TimeUnit.MINUTES);
  202 + if (!success) {
  203 + log.warn("Failed: {}, {}", ctx.getActual().get(), ctx.getInvocationCount().get());
  204 + }
  205 + Assert.assertTrue(success);
  206 + Assert.assertEquals(expected, ctx.getActual().get());
  207 + Assert.assertEquals(msgNumber, ctx.getInvocationCount().get());
  208 + ctx.clear();
  209 + } catch (InterruptedException e) {
  210 + e.printStackTrace();
  211 + }
  212 + });
  213 + long duration = System.nanoTime() - start;
  214 + log.info("Time spend: {}ns ({} ms)", duration, TimeUnit.NANOSECONDS.toMillis(duration));
  215 + }
  216 + }
  217 +
  218 + private ActorTestCtx getActorTestCtx(int i) {
  219 + CountDownLatch countDownLatch = new CountDownLatch(1);
  220 + AtomicLong actual = new AtomicLong();
  221 + AtomicInteger invocations = new AtomicInteger();
  222 + return new ActorTestCtx(countDownLatch, invocations, i, actual);
  223 + }
  224 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.actors;
  17 +
  18 +import lombok.AllArgsConstructor;
  19 +import lombok.Data;
  20 +
  21 +import java.util.concurrent.CountDownLatch;
  22 +import java.util.concurrent.atomic.AtomicInteger;
  23 +import java.util.concurrent.atomic.AtomicLong;
  24 +
  25 +@Data
  26 +@AllArgsConstructor
  27 +public class ActorTestCtx {
  28 +
  29 + private volatile CountDownLatch latch;
  30 + private final AtomicInteger invocationCount;
  31 + private final int expectedInvocationCount;
  32 + private final AtomicLong actual;
  33 +
  34 + public void clear() {
  35 + latch = new CountDownLatch(1);
  36 + invocationCount.set(0);
  37 + actual.set(0L);
  38 + }
  39 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.actors;
  17 +
  18 +import lombok.extern.slf4j.Slf4j;
  19 +
  20 +@Slf4j
  21 +public class FailedToInitActor extends TestRootActor {
  22 +
  23 + int retryAttempts;
  24 + int retryDelay;
  25 + int attempts = 0;
  26 +
  27 + public FailedToInitActor(TbActorId actorId, ActorTestCtx testCtx, int retryAttempts, int retryDelay) {
  28 + super(actorId, testCtx);
  29 + this.retryAttempts = retryAttempts;
  30 + this.retryDelay = retryDelay;
  31 + }
  32 +
  33 + @Override
  34 + public void init(TbActorCtx ctx) throws TbActorException {
  35 + if (attempts < retryAttempts) {
  36 + attempts++;
  37 + throw new TbActorException("Test attempt", new RuntimeException());
  38 + } else {
  39 + super.init(ctx);
  40 + }
  41 + }
  42 +
  43 + @Override
  44 + public InitFailureStrategy onInitFailure(int attempt, Throwable t) {
  45 + return InitFailureStrategy.retryWithDelay(retryDelay);
  46 + }
  47 +
  48 + public static class FailedToInitActorCreator implements TbActorCreator {
  49 +
  50 + private final TbActorId actorId;
  51 + private final ActorTestCtx testCtx;
  52 + private final int retryAttempts;
  53 + private final int retryDelay;
  54 +
  55 + public FailedToInitActorCreator(TbActorId actorId, ActorTestCtx testCtx, int retryAttempts, int retryDelay) {
  56 + this.actorId = actorId;
  57 + this.testCtx = testCtx;
  58 + this.retryAttempts = retryAttempts;
  59 + this.retryDelay = retryDelay;
  60 + }
  61 +
  62 + @Override
  63 + public TbActorId createActorId() {
  64 + return actorId;
  65 + }
  66 +
  67 + @Override
  68 + public TbActor createActor() {
  69 + return new FailedToInitActor(actorId, testCtx, retryAttempts, retryDelay);
  70 + }
  71 + }
  72 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.actors;
  17 +
  18 +import lombok.Getter;
  19 +import org.thingsboard.server.common.msg.MsgType;
  20 +import org.thingsboard.server.common.msg.TbActorMsg;
  21 +
  22 +public class IntTbActorMsg implements TbActorMsg {
  23 +
  24 + @Getter
  25 + private final int value;
  26 +
  27 + public IntTbActorMsg(int value) {
  28 + this.value = value;
  29 + }
  30 +
  31 + @Override
  32 + public MsgType getMsgType() {
  33 + return MsgType.QUEUE_TO_RULE_ENGINE_MSG;
  34 + }
  35 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.actors;
  17 +
  18 +import lombok.extern.slf4j.Slf4j;
  19 +
  20 +@Slf4j
  21 +public class SlowCreateActor extends TestRootActor {
  22 +
  23 + public SlowCreateActor(TbActorId actorId, ActorTestCtx testCtx) {
  24 + super(actorId, testCtx);
  25 + try {
  26 + Thread.sleep(500);
  27 + } catch (InterruptedException e) {
  28 + e.printStackTrace();
  29 + }
  30 + testCtx.getInvocationCount().incrementAndGet();
  31 + }
  32 +
  33 + public static class SlowCreateActorCreator implements TbActorCreator {
  34 +
  35 + private final TbActorId actorId;
  36 + private final ActorTestCtx testCtx;
  37 +
  38 + public SlowCreateActorCreator(TbActorId actorId, ActorTestCtx testCtx) {
  39 + this.actorId = actorId;
  40 + this.testCtx = testCtx;
  41 + }
  42 +
  43 + @Override
  44 + public TbActorId createActorId() {
  45 + return actorId;
  46 + }
  47 +
  48 + @Override
  49 + public TbActor createActor() {
  50 + return new SlowCreateActor(actorId, testCtx);
  51 + }
  52 + }
  53 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.actors;
  17 +
  18 +import lombok.extern.slf4j.Slf4j;
  19 +
  20 +@Slf4j
  21 +public class SlowInitActor extends TestRootActor {
  22 +
  23 + public SlowInitActor(TbActorId actorId, ActorTestCtx testCtx) {
  24 + super(actorId, testCtx);
  25 + }
  26 +
  27 + @Override
  28 + public void init(TbActorCtx ctx) throws TbActorException {
  29 + try {
  30 + Thread.sleep(500);
  31 + } catch (InterruptedException e) {
  32 + e.printStackTrace();
  33 + }
  34 + super.init(ctx);
  35 + }
  36 +
  37 + public static class SlowInitActorCreator implements TbActorCreator {
  38 +
  39 + private final TbActorId actorId;
  40 + private final ActorTestCtx testCtx;
  41 +
  42 + public SlowInitActorCreator(TbActorId actorId, ActorTestCtx testCtx) {
  43 + this.actorId = actorId;
  44 + this.testCtx = testCtx;
  45 + }
  46 +
  47 + @Override
  48 + public TbActorId createActorId() {
  49 + return actorId;
  50 + }
  51 +
  52 + @Override
  53 + public TbActor createActor() {
  54 + return new SlowInitActor(actorId, testCtx);
  55 + }
  56 + }
  57 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.actors;
  17 +
  18 +import lombok.Getter;
  19 +import lombok.extern.slf4j.Slf4j;
  20 +import org.thingsboard.server.common.msg.TbActorMsg;
  21 +
  22 +@Slf4j
  23 +public class TestRootActor extends AbstractTbActor {
  24 +
  25 + @Getter
  26 + private final TbActorId actorId;
  27 + @Getter
  28 + private final ActorTestCtx testCtx;
  29 +
  30 + private boolean initialized;
  31 + private long sum;
  32 + private int count;
  33 +
  34 + public TestRootActor(TbActorId actorId, ActorTestCtx testCtx) {
  35 + this.actorId = actorId;
  36 + this.testCtx = testCtx;
  37 + }
  38 +
  39 + @Override
  40 + public void init(TbActorCtx ctx) throws TbActorException {
  41 + super.init(ctx);
  42 + initialized = true;
  43 + }
  44 +
  45 + @Override
  46 + public boolean process(TbActorMsg msg) {
  47 + if (initialized) {
  48 + int value = ((IntTbActorMsg) msg).getValue();
  49 + sum += value;
  50 + count += 1;
  51 + if (count == testCtx.getExpectedInvocationCount()) {
  52 + testCtx.getActual().set(sum);
  53 + testCtx.getInvocationCount().addAndGet(count);
  54 + sum = 0;
  55 + count = 0;
  56 + testCtx.getLatch().countDown();
  57 + }
  58 + }
  59 + return true;
  60 + }
  61 +
  62 + @Override
  63 + public void destroy() {
  64 +
  65 + }
  66 +
  67 + public static class TestRootActorCreator implements TbActorCreator {
  68 +
  69 + private final TbActorId actorId;
  70 + private final ActorTestCtx testCtx;
  71 +
  72 + public TestRootActorCreator(TbActorId actorId, ActorTestCtx testCtx) {
  73 + this.actorId = actorId;
  74 + this.testCtx = testCtx;
  75 + }
  76 +
  77 + @Override
  78 + public TbActorId createActorId() {
  79 + return actorId;
  80 + }
  81 +
  82 + @Override
  83 + public TbActor createActor() {
  84 + return new TestRootActor(actorId, testCtx);
  85 + }
  86 + }
  87 +}
... ...
  1 +<?xml version="1.0" encoding="UTF-8" ?>
  2 +
  3 +<configuration>
  4 + <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
  5 + <encoder>
  6 + <pattern>%d{ISO8601} [%thread] %-5level %logger{36} - %msg%n</pattern>
  7 + </encoder>
  8 + </appender>
  9 +
  10 + <root level="INFO">
  11 + <appender-ref ref="console"/>
  12 + </root>
  13 +
  14 +</configuration>
... ...
... ... @@ -20,7 +20,7 @@
20 20 <modelVersion>4.0.0</modelVersion>
21 21 <parent>
22 22 <groupId>org.thingsboard</groupId>
23   - <version>2.5.1-SNAPSHOT</version>
  23 + <version>2.5.3-SNAPSHOT</version>
24 24 <artifactId>common</artifactId>
25 25 </parent>
26 26 <groupId>org.thingsboard.common</groupId>
... ...
... ... @@ -20,7 +20,7 @@
20 20 <modelVersion>4.0.0</modelVersion>
21 21 <parent>
22 22 <groupId>org.thingsboard</groupId>
23   - <version>2.5.1-SNAPSHOT</version>
  23 + <version>2.5.3-SNAPSHOT</version>
24 24 <artifactId>common</artifactId>
25 25 </parent>
26 26 <groupId>org.thingsboard.common</groupId>
... ...
... ... @@ -20,7 +20,7 @@
20 20 <modelVersion>4.0.0</modelVersion>
21 21 <parent>
22 22 <groupId>org.thingsboard</groupId>
23   - <version>2.5.1-SNAPSHOT</version>
  23 + <version>2.5.3-SNAPSHOT</version>
24 24 <artifactId>common</artifactId>
25 25 </parent>
26 26 <groupId>org.thingsboard.common</groupId>
... ...
... ... @@ -20,7 +20,7 @@
20 20 <modelVersion>4.0.0</modelVersion>
21 21 <parent>
22 22 <groupId>org.thingsboard</groupId>
23   - <version>2.5.1-SNAPSHOT</version>
  23 + <version>2.5.3-SNAPSHOT</version>
24 24 <artifactId>common</artifactId>
25 25 </parent>
26 26 <groupId>org.thingsboard.common</groupId>
... ...
... ... @@ -21,7 +21,6 @@ import org.thingsboard.server.common.msg.queue.QueueToRuleEngineMsg;
21 21 /**
22 22 * Created by ashvayka on 15.03.18.
23 23 */
24   -//TODO: add all "See" references
25 24 public enum MsgType {
26 25
27 26 /**
... ... @@ -97,6 +96,7 @@ public enum MsgType {
97 96
98 97 STATS_PERSIST_TICK_MSG,
99 98
  99 + STATS_PERSIST_MSG,
100 100
101 101 /**
102 102 * Message that is sent by TransportRuleEngineService to Device Actor. Represents messages from the device itself.
... ...
... ... @@ -25,6 +25,7 @@ import org.thingsboard.server.common.data.id.EntityIdFactory;
25 25 import org.thingsboard.server.common.data.id.RuleChainId;
26 26 import org.thingsboard.server.common.data.id.RuleNodeId;
27 27 import org.thingsboard.server.common.msg.gen.MsgProtos;
  28 +import org.thingsboard.server.common.msg.queue.ServiceQueue;
28 29 import org.thingsboard.server.common.msg.queue.TbMsgCallback;
29 30
30 31 import java.io.IOException;
... ... @@ -39,6 +40,7 @@ import java.util.UUID;
39 40 @Slf4j
40 41 public final class TbMsg implements Serializable {
41 42
  43 + private final String queueName;
42 44 private final UUID id;
43 45 private final long ts;
44 46 private final String type;
... ... @@ -51,39 +53,44 @@ public final class TbMsg implements Serializable {
51 53 //This field is not serialized because we use queues and there is no need to do it
52 54 transient private final TbMsgCallback callback;
53 55
  56 + public static TbMsg newMsg(String queueName, String type, EntityId originator, TbMsgMetaData metaData, String data, RuleChainId ruleChainId, RuleNodeId ruleNodeId) {
  57 + return new TbMsg(queueName, UUID.randomUUID(), System.currentTimeMillis(), type, originator, metaData.copy(), TbMsgDataType.JSON, data, ruleChainId, ruleNodeId, TbMsgCallback.EMPTY);
  58 + }
  59 +
54 60 public static TbMsg newMsg(String type, EntityId originator, TbMsgMetaData metaData, String data) {
55   - return new TbMsg(UUID.randomUUID(), System.currentTimeMillis(), type, originator, metaData.copy(), TbMsgDataType.JSON, data, null, null, TbMsgCallback.EMPTY);
  61 + return new TbMsg(ServiceQueue.MAIN, UUID.randomUUID(), System.currentTimeMillis(), type, originator, metaData.copy(), TbMsgDataType.JSON, data, null, null, TbMsgCallback.EMPTY);
56 62 }
57 63
58   - public static TbMsg newMsg(String type, EntityId originator, TbMsgMetaData metaData, String data, RuleChainId ruleChainId, RuleNodeId ruleNodeId) {
59   - return new TbMsg(UUID.randomUUID(), System.currentTimeMillis(), type, originator, metaData.copy(), TbMsgDataType.JSON, data, ruleChainId, ruleNodeId, TbMsgCallback.EMPTY);
  64 + public static TbMsg newMsg(String queueName, String type, EntityId originator, TbMsgMetaData metaData, String data) {
  65 + return new TbMsg(queueName, UUID.randomUUID(), System.currentTimeMillis(), type, originator, metaData.copy(), TbMsgDataType.JSON, data, null, null, TbMsgCallback.EMPTY);
60 66 }
61 67
62 68 public static TbMsg newMsg(String type, EntityId originator, TbMsgMetaData metaData, TbMsgDataType dataType, String data) {
63   - return new TbMsg(UUID.randomUUID(), System.currentTimeMillis(), type, originator, metaData.copy(), dataType, data, null, null, TbMsgCallback.EMPTY);
  69 + return new TbMsg(ServiceQueue.MAIN, UUID.randomUUID(), System.currentTimeMillis(), type, originator, metaData.copy(), dataType, data, null, null, TbMsgCallback.EMPTY);
64 70 }
65 71
66 72 public static TbMsg newMsg(String type, EntityId originator, TbMsgMetaData metaData, TbMsgDataType dataType, String data, RuleChainId ruleChainId, RuleNodeId ruleNodeId) {
67   - return new TbMsg(UUID.randomUUID(), System.currentTimeMillis(), type, originator, metaData.copy(), dataType, data, ruleChainId, ruleNodeId, TbMsgCallback.EMPTY);
  73 + return new TbMsg(ServiceQueue.MAIN, UUID.randomUUID(), System.currentTimeMillis(), type, originator, metaData.copy(), dataType, data, ruleChainId, ruleNodeId, TbMsgCallback.EMPTY);
68 74 }
69 75
70 76 public static TbMsg newMsg(String type, EntityId originator, TbMsgMetaData metaData, String data, TbMsgCallback callback) {
71   - return new TbMsg(UUID.randomUUID(), System.currentTimeMillis(), type, originator, metaData.copy(), TbMsgDataType.JSON, data, null, null, callback);
  77 + return new TbMsg(ServiceQueue.MAIN, UUID.randomUUID(), System.currentTimeMillis(), type, originator, metaData.copy(), TbMsgDataType.JSON, data, null, null, callback);
72 78 }
73 79
74 80 public static TbMsg transformMsg(TbMsg origMsg, String type, EntityId originator, TbMsgMetaData metaData, String data) {
75   - return new TbMsg(origMsg.getId(), origMsg.getTs(), type, originator, metaData.copy(), origMsg.getDataType(),
  81 + return new TbMsg(origMsg.getQueueName(), origMsg.getId(), origMsg.getTs(), type, originator, metaData.copy(), origMsg.getDataType(),
76 82 data, origMsg.getRuleChainId(), origMsg.getRuleNodeId(), origMsg.getCallback());
77 83 }
78 84
79 85 public static TbMsg newMsg(TbMsg tbMsg, RuleChainId ruleChainId, RuleNodeId ruleNodeId) {
80   - return new TbMsg(UUID.randomUUID(), tbMsg.getTs(), tbMsg.getType(), tbMsg.getOriginator(), tbMsg.getMetaData().copy(),
  86 + return new TbMsg(tbMsg.getQueueName(), UUID.randomUUID(), tbMsg.getTs(), tbMsg.getType(), tbMsg.getOriginator(), tbMsg.getMetaData().copy(),
81 87 tbMsg.getDataType(), tbMsg.getData(), ruleChainId, ruleNodeId, TbMsgCallback.EMPTY);
82 88 }
83 89
84   - private TbMsg(UUID id, long ts, String type, EntityId originator, TbMsgMetaData metaData, TbMsgDataType dataType, String data,
  90 + private TbMsg(String queueName, UUID id, long ts, String type, EntityId originator, TbMsgMetaData metaData, TbMsgDataType dataType, String data,
85 91 RuleChainId ruleChainId, RuleNodeId ruleNodeId, TbMsgCallback callback) {
86 92 this.id = id;
  93 + this.queueName = queueName;
87 94 if (ts > 0) {
88 95 this.ts = ts;
89 96 } else {
... ... @@ -136,7 +143,7 @@ public final class TbMsg implements Serializable {
136 143 return builder.build().toByteArray();
137 144 }
138 145
139   - public static TbMsg fromBytes(byte[] data, TbMsgCallback callback) {
  146 + public static TbMsg fromBytes(String queueName, byte[] data, TbMsgCallback callback) {
140 147 try {
141 148 MsgProtos.TbMsgProto proto = MsgProtos.TbMsgProto.parseFrom(data);
142 149 TbMsgMetaData metaData = new TbMsgMetaData(proto.getMetaData().getDataMap());
... ... @@ -150,18 +157,18 @@ public final class TbMsg implements Serializable {
150 157 ruleNodeId = new RuleNodeId(new UUID(proto.getRuleNodeIdMSB(), proto.getRuleNodeIdLSB()));
151 158 }
152 159 TbMsgDataType dataType = TbMsgDataType.values()[proto.getDataType()];
153   - return new TbMsg(UUID.fromString(proto.getId()), proto.getTs(), proto.getType(), entityId, metaData, dataType, proto.getData(), ruleChainId, ruleNodeId, callback);
  160 + return new TbMsg(queueName, UUID.fromString(proto.getId()), proto.getTs(), proto.getType(), entityId, metaData, dataType, proto.getData(), ruleChainId, ruleNodeId, callback);
154 161 } catch (InvalidProtocolBufferException e) {
155 162 throw new IllegalStateException("Could not parse protobuf for TbMsg", e);
156 163 }
157 164 }
158 165
159 166 public TbMsg copyWithRuleChainId(RuleChainId ruleChainId) {
160   - return new TbMsg(this.id, this.ts, this.type, this.originator, this.metaData, this.dataType, this.data, ruleChainId, null, callback);
  167 + return new TbMsg(this.queueName, this.id, this.ts, this.type, this.originator, this.metaData, this.dataType, this.data, ruleChainId, null, callback);
161 168 }
162 169
163 170 public TbMsg copyWithRuleNodeId(RuleChainId ruleChainId, RuleNodeId ruleNodeId) {
164   - return new TbMsg(this.id, this.ts, this.type, this.originator, this.metaData, this.dataType, this.data, ruleChainId, ruleNodeId, callback);
  171 + return new TbMsg(this.queueName, this.id, this.ts, this.type, this.originator, this.metaData, this.dataType, this.data, ruleChainId, ruleNodeId, callback);
165 172 }
166 173
167 174 public TbMsgCallback getCallback() {
... ... @@ -172,4 +179,8 @@ public final class TbMsg implements Serializable {
172 179 return TbMsgCallback.EMPTY;
173 180 }
174 181 }
  182 +
  183 + public String getQueueName() {
  184 + return queueName != null ? queueName : ServiceQueue.MAIN;
  185 + }
175 186 }
... ...
... ... @@ -16,8 +16,9 @@
16 16 package org.thingsboard.server.common.msg.aware;
17 17
18 18 import org.thingsboard.server.common.data.id.DeviceId;
  19 +import org.thingsboard.server.common.msg.TbActorMsg;
19 20
20   -public interface DeviceAwareMsg {
  21 +public interface DeviceAwareMsg extends TbActorMsg {
21 22
22 23 DeviceId getDeviceId();
23 24 }
... ...
... ... @@ -16,8 +16,9 @@
16 16 package org.thingsboard.server.common.msg.aware;
17 17
18 18 import org.thingsboard.server.common.data.id.RuleChainId;
  19 +import org.thingsboard.server.common.msg.TbActorMsg;
19 20
20   -public interface RuleChainAwareMsg {
  21 +public interface RuleChainAwareMsg extends TbActorMsg {
21 22
22 23 RuleChainId getRuleChainId();
23 24
... ...
... ... @@ -16,8 +16,9 @@
16 16 package org.thingsboard.server.common.msg.aware;
17 17
18 18 import org.thingsboard.server.common.data.id.TenantId;
  19 +import org.thingsboard.server.common.msg.TbActorMsg;
19 20
20   -public interface TenantAwareMsg {
  21 +public interface TenantAwareMsg extends TbActorMsg {
21 22
22 23 TenantId getTenantId();
23 24
... ...
... ... @@ -34,7 +34,7 @@ public class ServiceQueue {
34 34
35 35 public ServiceQueue(ServiceType type, String queue) {
36 36 this.type = type;
37   - this.queue = queue;
  37 + this.queue = queue != null ? queue : MAIN;
38 38 }
39 39
40 40 public ServiceType getType() {
... ...
... ... @@ -20,7 +20,7 @@
20 20 <modelVersion>4.0.0</modelVersion>
21 21 <parent>
22 22 <groupId>org.thingsboard</groupId>
23   - <version>2.5.1-SNAPSHOT</version>
  23 + <version>2.5.3-SNAPSHOT</version>
24 24 <artifactId>thingsboard</artifactId>
25 25 </parent>
26 26 <artifactId>common</artifactId>
... ... @@ -37,6 +37,7 @@
37 37 <module>data</module>
38 38 <module>util</module>
39 39 <module>message</module>
  40 + <module>actor</module>
40 41 <module>queue</module>
41 42 <module>transport</module>
42 43 <module>dao-api</module>
... ...
... ... @@ -20,7 +20,7 @@
20 20 <modelVersion>4.0.0</modelVersion>
21 21 <parent>
22 22 <groupId>org.thingsboard</groupId>
23   - <version>2.5.1-SNAPSHOT</version>
  23 + <version>2.5.3-SNAPSHOT</version>
24 24 <artifactId>common</artifactId>
25 25 </parent>
26 26 <groupId>org.thingsboard.common</groupId>
... ...
... ... @@ -91,8 +91,6 @@ public class DefaultTbQueueRequestTemplate<Request extends TbQueueMsg, Response
91 91 List<Response> responses = responseTemplate.poll(pollInterval);
92 92 if (responses.size() > 0) {
93 93 log.trace("Polling responses completed, consumer records count [{}]", responses.size());
94   - } else {
95   - continue;
96 94 }
97 95 responses.forEach(response -> {
98 96 byte[] requestIdHeader = response.getHeaders().get(REQUEST_ID_HEADER);
... ...
... ... @@ -20,7 +20,7 @@
20 20 <modelVersion>4.0.0</modelVersion>
21 21 <parent>
22 22 <groupId>org.thingsboard.common</groupId>
23   - <version>2.5.1-SNAPSHOT</version>
  23 + <version>2.5.3-SNAPSHOT</version>
24 24 <artifactId>transport</artifactId>
25 25 </parent>
26 26 <groupId>org.thingsboard.common.transport</groupId>
... ...
... ... @@ -20,7 +20,7 @@
20 20 <modelVersion>4.0.0</modelVersion>
21 21 <parent>
22 22 <groupId>org.thingsboard.common</groupId>
23   - <version>2.5.1-SNAPSHOT</version>
  23 + <version>2.5.3-SNAPSHOT</version>
24 24 <artifactId>transport</artifactId>
25 25 </parent>
26 26 <groupId>org.thingsboard.common.transport</groupId>
... ...
... ... @@ -20,7 +20,7 @@
20 20 <modelVersion>4.0.0</modelVersion>
21 21 <parent>
22 22 <groupId>org.thingsboard.common</groupId>
23   - <version>2.5.1-SNAPSHOT</version>
  23 + <version>2.5.3-SNAPSHOT</version>
24 24 <artifactId>transport</artifactId>
25 25 </parent>
26 26 <groupId>org.thingsboard.common.transport</groupId>
... ...
... ... @@ -521,11 +521,22 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
521 521 .setDeviceName(msg.getDeviceInfo().getDeviceName())
522 522 .setDeviceType(msg.getDeviceInfo().getDeviceType())
523 523 .build();
524   - transportService.process(sessionInfo, DefaultTransportService.getSessionEventMsg(SessionEvent.OPEN), null);
525   - transportService.registerAsyncSession(sessionInfo, this);
526   - checkGatewaySession();
527   - ctx.writeAndFlush(createMqttConnAckMsg(CONNECTION_ACCEPTED));
528   - log.info("[{}] Client connected!", sessionId);
  524 + transportService.process(sessionInfo, DefaultTransportService.getSessionEventMsg(SessionEvent.OPEN), new TransportServiceCallback<Void>() {
  525 + @Override
  526 + public void onSuccess(Void msg) {
  527 + transportService.registerAsyncSession(sessionInfo, MqttTransportHandler.this);
  528 + checkGatewaySession();
  529 + ctx.writeAndFlush(createMqttConnAckMsg(CONNECTION_ACCEPTED));
  530 + log.info("[{}] Client connected!", sessionId);
  531 + }
  532 +
  533 + @Override
  534 + public void onError(Throwable e) {
  535 + log.warn("[{}] Failed to submit session event", sessionId, e);
  536 + ctx.writeAndFlush(createMqttConnAckMsg(MqttConnectReturnCode.CONNECTION_REFUSED_SERVER_UNAVAILABLE));
  537 + ctx.close();
  538 + }
  539 + });
529 540 }
530 541 }
531 542
... ...
... ... @@ -20,7 +20,7 @@
20 20 <modelVersion>4.0.0</modelVersion>
21 21 <parent>
22 22 <groupId>org.thingsboard</groupId>
23   - <version>2.5.1-SNAPSHOT</version>
  23 + <version>2.5.3-SNAPSHOT</version>
24 24 <artifactId>common</artifactId>
25 25 </parent>
26 26 <groupId>org.thingsboard.common</groupId>
... ...
... ... @@ -20,7 +20,7 @@
20 20 <modelVersion>4.0.0</modelVersion>
21 21 <parent>
22 22 <groupId>org.thingsboard.common</groupId>
23   - <version>2.5.1-SNAPSHOT</version>
  23 + <version>2.5.3-SNAPSHOT</version>
24 24 <artifactId>transport</artifactId>
25 25 </parent>
26 26 <groupId>org.thingsboard.common.transport</groupId>
... ...
... ... @@ -20,7 +20,7 @@
20 20 <modelVersion>4.0.0</modelVersion>
21 21 <parent>
22 22 <groupId>org.thingsboard</groupId>
23   - <version>2.5.1-SNAPSHOT</version>
  23 + <version>2.5.3-SNAPSHOT</version>
24 24 <artifactId>common</artifactId>
25 25 </parent>
26 26 <groupId>org.thingsboard.common</groupId>
... ...
... ... @@ -20,7 +20,7 @@
20 20 <modelVersion>4.0.0</modelVersion>
21 21 <parent>
22 22 <groupId>org.thingsboard</groupId>
23   - <version>2.5.1-SNAPSHOT</version>
  23 + <version>2.5.3-SNAPSHOT</version>
24 24 <artifactId>thingsboard</artifactId>
25 25 </parent>
26 26 <artifactId>dao</artifactId>
... ... @@ -91,26 +91,26 @@
91 91 <artifactId>mockito-all</artifactId>
92 92 <scope>test</scope>
93 93 </dependency>
94   - <dependency>
95   - <groupId>org.apache.commons</groupId>
96   - <artifactId>commons-lang3</artifactId>
97   - </dependency>
98   - <dependency>
99   - <groupId>commons-validator</groupId>
100   - <artifactId>commons-validator</artifactId>
101   - </dependency>
102   - <dependency>
103   - <groupId>com.fasterxml.jackson.core</groupId>
104   - <artifactId>jackson-databind</artifactId>
105   - </dependency>
  94 + <dependency>
  95 + <groupId>org.apache.commons</groupId>
  96 + <artifactId>commons-lang3</artifactId>
  97 + </dependency>
  98 + <dependency>
  99 + <groupId>commons-collections</groupId>
  100 + <artifactId>commons-collections</artifactId>
  101 + </dependency>
  102 + <dependency>
  103 + <groupId>com.fasterxml.jackson.core</groupId>
  104 + <artifactId>jackson-databind</artifactId>
  105 + </dependency>
106 106 <dependency>
107 107 <groupId>org.springframework</groupId>
108 108 <artifactId>spring-context</artifactId>
109 109 </dependency>
110   - <dependency>
111   - <groupId>org.springframework</groupId>
112   - <artifactId>spring-tx</artifactId>
113   - </dependency>
  110 + <dependency>
  111 + <groupId>org.springframework</groupId>
  112 + <artifactId>spring-tx</artifactId>
  113 + </dependency>
114 114 <dependency>
115 115 <groupId>org.springframework</groupId>
116 116 <artifactId>spring-web</artifactId>
... ... @@ -120,24 +120,24 @@
120 120 <groupId>org.springframework.security</groupId>
121 121 <artifactId>spring-security-oauth2-client</artifactId>
122 122 </dependency>
123   - <dependency>
124   - <groupId>com.datastax.cassandra</groupId>
125   - <artifactId>cassandra-driver-core</artifactId>
126   - </dependency>
127   - <dependency>
128   - <groupId>com.datastax.cassandra</groupId>
129   - <artifactId>cassandra-driver-mapping</artifactId>
130   - </dependency>
131   - <dependency>
132   - <groupId>com.datastax.cassandra</groupId>
133   - <artifactId>cassandra-driver-extras</artifactId>
134   - </dependency>
  123 + <dependency>
  124 + <groupId>com.datastax.cassandra</groupId>
  125 + <artifactId>cassandra-driver-core</artifactId>
  126 + </dependency>
  127 + <dependency>
  128 + <groupId>com.datastax.cassandra</groupId>
  129 + <artifactId>cassandra-driver-mapping</artifactId>
  130 + </dependency>
  131 + <dependency>
  132 + <groupId>com.datastax.cassandra</groupId>
  133 + <artifactId>cassandra-driver-extras</artifactId>
  134 + </dependency>
135 135 <dependency>
136 136 <groupId>io.takari.junit</groupId>
137 137 <artifactId>takari-cpsuite</artifactId>
138 138 <scope>test</scope>
139   - </dependency>
140   - <dependency>
  139 + </dependency>
  140 + <dependency>
141 141 <groupId>com.google.guava</groupId>
142 142 <artifactId>guava</artifactId>
143 143 </dependency>
... ... @@ -215,18 +215,18 @@
215 215 </includes>
216 216 </configuration>
217 217 </plugin>
218   - <plugin>
219   - <groupId>org.apache.maven.plugins</groupId>
220   - <artifactId>maven-jar-plugin</artifactId>
  218 + <plugin>
  219 + <groupId>org.apache.maven.plugins</groupId>
  220 + <artifactId>maven-jar-plugin</artifactId>
221 221 <version>${jar-plugin.version}</version>
222   - <executions>
223   - <execution>
224   - <goals>
225   - <goal>test-jar</goal>
226   - </goals>
227   - </execution>
228   - </executions>
229   - </plugin>
  222 + <executions>
  223 + <execution>
  224 + <goals>
  225 + <goal>test-jar</goal>
  226 + </goals>
  227 + </execution>
  228 + </executions>
  229 + </plugin>
230 230 </plugins>
231 231 </build>
232 232 </project>
... ...
... ... @@ -17,7 +17,6 @@ package org.thingsboard.server.dao.service;
17 17
18 18 import com.fasterxml.jackson.databind.JsonNode;
19 19 import lombok.extern.slf4j.Slf4j;
20   -import org.apache.commons.validator.routines.EmailValidator;
21 20 import org.thingsboard.server.common.data.BaseData;
22 21 import org.thingsboard.server.common.data.id.TenantId;
23 22 import org.thingsboard.server.dao.exception.DataValidationException;
... ... @@ -26,11 +25,13 @@ import java.util.HashSet;
26 25 import java.util.Iterator;
27 26 import java.util.Set;
28 27 import java.util.function.Function;
  28 +import java.util.regex.Matcher;
  29 +import java.util.regex.Pattern;
29 30
30 31 @Slf4j
31 32 public abstract class DataValidator<D extends BaseData<?>> {
32   -
33   - private static EmailValidator emailValidator = EmailValidator.getInstance();
  33 + private static final Pattern EMAIL_PATTERN =
  34 + Pattern.compile("^[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,6}$", Pattern.CASE_INSENSITIVE);
34 35
35 36 public void validate(D data, Function<D, TenantId> tenantIdFunction) {
36 37 try {
... ... @@ -64,11 +65,20 @@ public abstract class DataValidator<D extends BaseData<?>> {
64 65 }
65 66
66 67 protected static void validateEmail(String email) {
67   - if (!emailValidator.isValid(email)) {
  68 + if (!doValidateEmail(email)) {
68 69 throw new DataValidationException("Invalid email address format '" + email + "'!");
69 70 }
70 71 }
71 72
  73 + private static boolean doValidateEmail(String email) {
  74 + if (email == null) {
  75 + return false;
  76 + }
  77 +
  78 + Matcher emailMatcher = EMAIL_PATTERN.matcher(email);
  79 + return emailMatcher.matches();
  80 + }
  81 +
72 82 protected static void validateJsonStructure(JsonNode expectedNode, JsonNode actualNode) {
73 83 Set<String> expectedFields = new HashSet<>();
74 84 Iterator<String> fieldsIterator = expectedNode.fieldNames();
... ...
... ... @@ -4,15 +4,15 @@ This folder containing scripts and Kubernetes resources configurations to run Th
4 4
5 5 ## Prerequisites
6 6
7   -ThingsBoard Microservices are running on Kubernetes cluster.
  7 +ThingsBoard Microservices run on the Kubernetes cluster.
8 8 You need to have a Kubernetes cluster, and the kubectl command-line tool must be configured to communicate with your cluster.
9   -If you do not already have a cluster, you can create one by using [Minikube](https://kubernetes.io/docs/setup/minikube),
  9 +If you do not have a cluster already, you can create one by using [Minikube](https://kubernetes.io/docs/setup/minikube),
10 10 or you can choose any other available [Kubernetes cluster deployment solutions](https://kubernetes.io/docs/setup/pick-right-solution/).
11 11
12 12 ### Enable ingress addon
13 13
14   -By default ingress addon is disable in the Minikube, and available only in cluster providers.
15   -To enable ingress, please execute next command:
  14 +By default ingress addon is disabled in the Minikube, and available only in cluster providers.
  15 +To enable ingress, please execute the following command:
16 16
17 17 `
18 18 $ minikube addons enable ingress
... ... @@ -21,21 +21,21 @@ $ minikube addons enable ingress
21 21 ## Installation
22 22
23 23 Before performing initial installation you can configure the type of database to be used with ThingsBoard and the type of deployment.
24   -In order to set database type change the value of `DATABASE` variable in `.env` file to one of the following:
  24 +To set database type change the value of `DATABASE` variable in `.env` file to one of the following:
25 25
26 26 - `postgres` - use PostgreSQL database;
27 27 - `cassandra` - use Cassandra database;
28 28
29 29 **NOTE**: According to the database type corresponding kubernetes resources will be deployed (see `postgres.yml`, `cassandra.yml` for details).
30 30
31   -In order to set deployment type change the value of `DEPLOYMENT_TYPE` variable in `.env` file to one of the following:
  31 +To set deployment type change the value of `DEPLOYMENT_TYPE` variable in `.env` file to one of the following:
32 32
33   -- `basic` - start up with single instance of Zookeeper, Kafka and Redis;
34   -- `high-availability` - start up with Zookeeper, Kafka and Redis in cluster modes;
  33 +- `basic` - startup with a single instance of Zookeeper, Kafka and Redis;
  34 +- `high-availability` - startup with Zookeeper, Kafka, and Redis in cluster modes;
35 35
36   -**NOTE**: According to the deployment type corresponding kubernetes resources will be deployed (see content of the directories `./basic` and `./high-availability` for details).
  36 +**NOTE**: According to the deployment type corresponding kubernetes resources will be deployed (see the content of the directories `./basic` and `./high-availability` for details).
37 37
38   -Execute the following command to run installation:
  38 +Execute the following command to run the installation:
39 39
40 40 `
41 41 $ ./k8s-install-tb.sh --loadDemo
... ... @@ -47,7 +47,7 @@ Where:
47 47
48 48 ## Running
49 49
50   -Execute the following command to deploy thirdparty resources:
  50 +Execute the following command to deploy third-party resources:
51 51
52 52 `
53 53 $ ./k8s-deploy-thirdparty.sh
... ... @@ -61,8 +61,8 @@ Execute the following command to deploy resources:
61 61 $ ./k8s-deploy-resources.sh
62 62 `
63 63
64   -After a while when all resources will be successfully started you can open `http://{your-cluster-ip}` in you browser (for ex. `http://192.168.99.101`).
65   -You should see ThingsBoard login page.
  64 +After a while when all resources will be successfully started you can open `http://{your-cluster-ip}` in your browser (for ex. `http://192.168.99.101`).
  65 +You should see the ThingsBoard login page.
66 66
67 67 Use the following default credentials:
68 68
... ... @@ -73,16 +73,16 @@ If you installed DataBase with demo data (using `--loadDemo` flag) you can also
73 73 - **Tenant Administrator**: tenant@thingsboard.org / tenant
74 74 - **Customer User**: customer@thingsboard.org / customer
75 75
76   -In case of any issues you can examine service logs for errors.
  76 +In case of any issues, you can examine service logs for errors.
77 77 For example to see ThingsBoard node logs execute the following commands:
78 78
79   -1) Get list of the running tb-node pods:
  79 +1) Get the list of the running tb-node pods:
80 80
81 81 `
82 82 $ kubectl get pods -l app=tb-node
83 83 `
84 84
85   -2) Fetch logs of tb-node pod:
  85 +2) Fetch logs of the tb-node pod:
86 86
87 87 `
88 88 $ kubectl logs -f [tb-node-pod-name]
... ... @@ -103,7 +103,7 @@ Execute the following command to delete all ThingsBoard microservices:
103 103 $ ./k8s-delete-resources.sh
104 104 `
105 105
106   -Execute the following command to delete all thirdparty microservices:
  106 +Execute the following command to delete all third-party microservices:
107 107
108 108 `
109 109 $ ./k8s-delete-thirdparty.sh
... ...