diff --git a/application/pom.xml b/application/pom.xml index f3c042b46f..3fce91213d 100644 --- a/application/pom.xml +++ b/application/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.5.1-SNAPSHOT + 2.5.3-SNAPSHOT thingsboard application @@ -57,6 +57,10 @@ linux-x86_64 + + org.thingsboard.common + actor + org.thingsboard.common util @@ -177,14 +181,6 @@ org.springframework spring-context-support - - com.typesafe.akka - akka-actor_${scala.version} - - - com.typesafe.akka - akka-slf4j_${scala.version} - org.slf4j slf4j-api @@ -375,7 +371,7 @@ jenkins Jenkins Repository - http://repo.jenkins-ci.org/releases + https://repo.jenkins-ci.org/releases false diff --git a/application/src/main/conf/logback.xml b/application/src/main/conf/logback.xml index 46b42182fb..6f930bb19f 100644 --- a/application/src/main/conf/logback.xml +++ b/application/src/main/conf/logback.xml @@ -35,7 +35,6 @@ - diff --git a/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java b/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java index e0c4e502e1..fd0dce6562 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java +++ b/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java @@ -15,9 +15,6 @@ */ package org.thingsboard.server.actors; -import akka.actor.ActorRef; -import akka.actor.ActorSystem; -import akka.actor.Scheduler; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; @@ -25,8 +22,6 @@ import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; -import com.typesafe.config.Config; -import com.typesafe.config.ConfigFactory; import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; @@ -93,12 +88,13 @@ import java.io.StringWriter; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; @Slf4j @Component public class ActorSystemContext { - private static final String AKKA_CONF_FILE_NAME = "actor-system.conf"; protected final ObjectMapper mapper = new ObjectMapper(); @@ -272,14 +268,6 @@ public class ActorSystemContext { @Getter private long syncSessionTimeout; - @Value("${actors.queue.enabled}") - @Getter - private boolean queuePersistenceEnabled; - - @Value("${actors.queue.timeout}") - @Getter - private long queuePersistenceTimeout; - @Value("${actors.rule.chain.error_persist_frequency}") @Getter private long ruleChainErrorPersistFrequency; @@ -339,17 +327,14 @@ public class ActorSystemContext { @Getter @Setter - private ActorSystem actorSystem; + private TbActorSystem actorSystem; @Setter - private ActorRef appActor; + private TbActorRef appActor; @Getter @Setter - private ActorRef statsActor; - - @Getter - private final Config config; + private TbActorRef statsActor; @Autowired(required = false) @Getter @@ -363,14 +348,8 @@ public class ActorSystemContext { @Getter private RedisTemplate redisTemplate; - public ActorSystemContext() { - config = ConfigFactory.parseResources(AKKA_CONF_FILE_NAME).withFallback(ConfigFactory.load()); - } - - - - public Scheduler getScheduler() { - return actorSystem.scheduler(); + public ScheduledExecutorService getScheduler() { + return actorSystem.getScheduler(); } public void persistError(TenantId tenantId, EntityId entityId, String method, Exception e) { @@ -543,7 +522,21 @@ public class ActorSystemContext { return Exception.class.isInstance(error) ? (Exception) error : new Exception(error); } - public void tell(TbActorMsg tbActorMsg, ActorRef sender) { - appActor.tell(tbActorMsg, sender); + public void tell(TbActorMsg tbActorMsg) { + appActor.tell(tbActorMsg); + } + + public void tellWithHighPriority(TbActorMsg tbActorMsg) { + appActor.tellWithHighPriority(tbActorMsg); + } + + public void schedulePeriodicMsgWithDelay(TbActorRef ctx, TbActorMsg msg, long delayInMs, long periodInMs) { + log.debug("Scheduling periodic msg {} every {} ms with delay {} ms", msg, periodInMs, delayInMs); + getScheduler().scheduleWithFixedDelay(() -> ctx.tell(msg), delayInMs, periodInMs, TimeUnit.MILLISECONDS); + } + + public void scheduleMsgWithDelay(TbActorRef ctx, TbActorMsg msg, long delayInMs) { + log.debug("Scheduling msg {} with delay {} ms", msg, delayInMs); + getScheduler().schedule(() -> ctx.tell(msg), delayInMs, TimeUnit.MILLISECONDS); } } diff --git a/application/src/main/java/org/thingsboard/server/actors/TbEntityTypeActorIdPredicate.java b/application/src/main/java/org/thingsboard/server/actors/TbEntityTypeActorIdPredicate.java new file mode 100644 index 0000000000..480b3b9ae3 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/actors/TbEntityTypeActorIdPredicate.java @@ -0,0 +1,37 @@ +/** + * Copyright © 2016-2020 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.actors; + +import lombok.RequiredArgsConstructor; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.id.EntityId; + +import java.util.function.Predicate; + +@RequiredArgsConstructor +public class TbEntityTypeActorIdPredicate implements Predicate { + + private final EntityType entityType; + + @Override + public boolean test(TbActorId actorId) { + return actorId instanceof TbEntityActorId && testEntityId(((TbEntityActorId) actorId).getEntityId()); + } + + protected boolean testEntityId(EntityId entityId) { + return entityId.getEntityType().equals(entityType); + } +} diff --git a/application/src/main/java/org/thingsboard/server/actors/app/AppActor.java b/application/src/main/java/org/thingsboard/server/actors/app/AppActor.java index b7782a6a87..953188f3f7 100644 --- a/application/src/main/java/org/thingsboard/server/actors/app/AppActor.java +++ b/application/src/main/java/org/thingsboard/server/actors/app/AppActor.java @@ -15,21 +15,19 @@ */ package org.thingsboard.server.actors.app; -import akka.actor.ActorRef; -import akka.actor.LocalActorRef; -import akka.actor.OneForOneStrategy; -import akka.actor.Props; -import akka.actor.SupervisorStrategy; -import akka.actor.Terminated; -import com.google.common.collect.BiMap; -import com.google.common.collect.HashBiMap; +import lombok.extern.slf4j.Slf4j; import org.thingsboard.server.actors.ActorSystemContext; +import org.thingsboard.server.actors.TbActor; +import org.thingsboard.server.actors.TbActorId; +import org.thingsboard.server.actors.TbActorRef; +import org.thingsboard.server.actors.TbEntityActorId; import org.thingsboard.server.actors.service.ContextAwareActor; import org.thingsboard.server.actors.service.ContextBasedCreator; import org.thingsboard.server.actors.service.DefaultActorService; import org.thingsboard.server.actors.tenant.TenantActor; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.Tenant; +import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageDataIterable; import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; @@ -43,38 +41,27 @@ import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.tenant.TenantService; import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper; -import scala.concurrent.duration.Duration; import java.util.HashSet; import java.util.Optional; import java.util.Set; +@Slf4j public class AppActor extends ContextAwareActor { private static final TenantId SYSTEM_TENANT = new TenantId(ModelConstants.NULL_UUID); private final TenantService tenantService; - private final BiMap tenantActors; private final Set deletedTenants; private boolean ruleChainsInitialized; private AppActor(ActorSystemContext systemContext) { super(systemContext); this.tenantService = systemContext.getTenantService(); - this.tenantActors = HashBiMap.create(); this.deletedTenants = new HashSet<>(); } @Override - public SupervisorStrategy supervisorStrategy() { - return strategy; - } - - @Override - public void preStart() { - } - - @Override - protected boolean process(TbActorMsg msg) { + protected boolean doProcess(TbActorMsg msg) { if (!ruleChainsInitialized) { initTenantActors(); ruleChainsInitialized = true; @@ -86,7 +73,7 @@ public class AppActor extends ContextAwareActor { case APP_INIT_MSG: break; case PARTITION_CHANGE_MSG: - broadcast(msg); + ctx.broadcastToChildren(msg); break; case COMPONENT_LIFE_CYCLE_MSG: onComponentLifecycleMsg((ComponentLifecycleMsg) msg); @@ -95,12 +82,14 @@ public class AppActor extends ContextAwareActor { onQueueToRuleEngineMsg((QueueToRuleEngineMsg) msg); break; case TRANSPORT_TO_DEVICE_ACTOR_MSG: + onToDeviceActorMsg((TenantAwareMsg) msg, false); + break; case DEVICE_ATTRIBUTES_UPDATE_TO_DEVICE_ACTOR_MSG: case DEVICE_CREDENTIALS_UPDATE_TO_DEVICE_ACTOR_MSG: case DEVICE_NAME_OR_TYPE_UPDATE_TO_DEVICE_ACTOR_MSG: case DEVICE_RPC_REQUEST_TO_DEVICE_ACTOR_MSG: case SERVER_RPC_RESPONSE_TO_DEVICE_ACTOR_MSG: - onToDeviceActorMsg((TenantAwareMsg) msg); + onToDeviceActorMsg((TenantAwareMsg) msg, true); break; default: return false; @@ -145,19 +134,15 @@ public class AppActor extends ContextAwareActor { msg.getTbMsg().getCallback().onFailure(new RuleEngineException("Message has system tenant id!")); } else { if (!deletedTenants.contains(msg.getTenantId())) { - getOrCreateTenantActor(msg.getTenantId()).tell(msg, self()); + getOrCreateTenantActor(msg.getTenantId()).tell(msg); } else { msg.getTbMsg().getCallback().onSuccess(); } } } - protected void broadcast(Object msg) { - tenantActors.values().forEach(actorRef -> actorRef.tell(msg, ActorRef.noSender())); - } - private void onComponentLifecycleMsg(ComponentLifecycleMsg msg) { - ActorRef target = null; + TbActorRef target = null; if (SYSTEM_TENANT.equals(msg.getTenantId())) { log.warn("Message has system tenant id: {}", msg); } else { @@ -166,25 +151,26 @@ public class AppActor extends ContextAwareActor { log.info("[{}] Handling tenant deleted notification: {}", msg.getTenantId(), msg); TenantId tenantId = new TenantId(msg.getEntityId().getId()); deletedTenants.add(tenantId); - ActorRef tenantActor = tenantActors.get(tenantId); - if (tenantActor != null) { - log.debug("[{}] Deleting tenant actor: {}", msg.getTenantId(), tenantActor); - context().stop(tenantActor); - } + ctx.stop(new TbEntityActorId(tenantId)); } else { target = getOrCreateTenantActor(msg.getTenantId()); } } if (target != null) { - target.tell(msg, ActorRef.noSender()); + target.tellWithHighPriority(msg); } else { log.debug("[{}] Invalid component lifecycle msg: {}", msg.getTenantId(), msg); } } - private void onToDeviceActorMsg(TenantAwareMsg msg) { + private void onToDeviceActorMsg(TenantAwareMsg msg, boolean priority) { if (!deletedTenants.contains(msg.getTenantId())) { - getOrCreateTenantActor(msg.getTenantId()).tell(msg, ActorRef.noSender()); + TbActorRef tenantActor = getOrCreateTenantActor(msg.getTenantId()); + if (priority) { + tenantActor.tellWithHighPriority(msg); + } else { + tenantActor.tell(msg); + } } else { if (msg instanceof TransportToDeviceActorMsgWrapper) { ((TransportToDeviceActorMsgWrapper) msg).getCallback().onSuccess(); @@ -192,49 +178,27 @@ public class AppActor extends ContextAwareActor { } } - private ActorRef getOrCreateTenantActor(TenantId tenantId) { - return tenantActors.computeIfAbsent(tenantId, k -> { - log.info("[{}] Creating tenant actor.", tenantId); - ActorRef tenantActor = context().actorOf(Props.create(new TenantActor.ActorCreator(systemContext, tenantId)) - .withDispatcher(DefaultActorService.CORE_DISPATCHER_NAME), tenantId.toString()); - context().watch(tenantActor); - log.info("[{}] Created tenant actor: {}.", tenantId, tenantActor); - return tenantActor; - }); + private TbActorRef getOrCreateTenantActor(TenantId tenantId) { + return ctx.getOrCreateChildActor(new TbEntityActorId(tenantId), + () -> DefaultActorService.TENANT_DISPATCHER_NAME, + () -> new TenantActor.ActorCreator(systemContext, tenantId)); } - @Override - protected void processTermination(Terminated message) { - ActorRef terminated = message.actor(); - if (terminated instanceof LocalActorRef) { - boolean removed = tenantActors.inverse().remove(terminated) != null; - if (removed) { - log.debug("[{}] Removed actor:", terminated); - } - } else { - throw new IllegalStateException("Remote actors are not supported!"); - } - } - - public static class ActorCreator extends ContextBasedCreator { - private static final long serialVersionUID = 1L; + public static class ActorCreator extends ContextBasedCreator { public ActorCreator(ActorSystemContext context) { super(context); } @Override - public AppActor create() { + public TbActorId createActorId() { + return new TbEntityActorId(new TenantId(EntityId.NULL_UUID)); + } + + @Override + public TbActor createActor() { return new AppActor(context); } } - private final SupervisorStrategy strategy = new OneForOneStrategy(3, Duration.create("1 minute"), t -> { - log.warn("Unknown failure", t); - if (t instanceof RuntimeException) { - return SupervisorStrategy.restart(); - } else { - return SupervisorStrategy.stop(); - } - }); } diff --git a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActor.java b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActor.java index 256ffd0b30..feecef859f 100644 --- a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActor.java +++ b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActor.java @@ -15,9 +15,12 @@ */ package org.thingsboard.server.actors.device; +import lombok.extern.slf4j.Slf4j; import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg; import org.thingsboard.rule.engine.api.msg.DeviceNameOrTypeUpdateMsg; import org.thingsboard.server.actors.ActorSystemContext; +import org.thingsboard.server.actors.TbActorCtx; +import org.thingsboard.server.actors.TbActorException; import org.thingsboard.server.actors.service.ContextAwareActor; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.TenantId; @@ -26,6 +29,7 @@ import org.thingsboard.server.common.msg.timeout.DeviceActorServerSideRpcTimeout import org.thingsboard.server.service.rpc.ToDeviceRpcRequestActorMsg; import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper; +@Slf4j public class DeviceActor extends ContextAwareActor { private final DeviceActorMessageProcessor processor; @@ -36,29 +40,26 @@ public class DeviceActor extends ContextAwareActor { } @Override - public void preStart() { + public void init(TbActorCtx ctx) throws TbActorException { + super.init(ctx); log.debug("[{}][{}] Starting device actor.", processor.tenantId, processor.deviceId); try { - processor.initSessionTimeout(context()); + processor.initSessionTimeout(ctx); log.debug("[{}][{}] Device actor started.", processor.tenantId, processor.deviceId); } catch (Exception e) { log.warn("[{}][{}] Unknown failure", processor.tenantId, processor.deviceId, e); + throw new TbActorException("Failed to initialize device actor", e); } } @Override - public void postStop() { - - } - - @Override - protected boolean process(TbActorMsg msg) { + protected boolean doProcess(TbActorMsg msg) { switch (msg.getMsgType()) { case TRANSPORT_TO_DEVICE_ACTOR_MSG: - processor.process(context(), (TransportToDeviceActorMsgWrapper) msg); + processor.process(ctx, (TransportToDeviceActorMsgWrapper) msg); break; case DEVICE_ATTRIBUTES_UPDATE_TO_DEVICE_ACTOR_MSG: - processor.processAttributesUpdate(context(), (DeviceAttributesEventNotificationMsg) msg); + processor.processAttributesUpdate(ctx, (DeviceAttributesEventNotificationMsg) msg); break; case DEVICE_CREDENTIALS_UPDATE_TO_DEVICE_ACTOR_MSG: processor.processCredentialsUpdate(); @@ -67,10 +68,10 @@ public class DeviceActor extends ContextAwareActor { processor.processNameOrTypeUpdate((DeviceNameOrTypeUpdateMsg) msg); break; case DEVICE_RPC_REQUEST_TO_DEVICE_ACTOR_MSG: - processor.processRpcRequest(context(), (ToDeviceRpcRequestActorMsg) msg); + processor.processRpcRequest(ctx, (ToDeviceRpcRequestActorMsg) msg); break; case DEVICE_ACTOR_SERVER_SIDE_RPC_TIMEOUT_MSG: - processor.processServerSideRpcTimeout(context(), (DeviceActorServerSideRpcTimeoutMsg) msg); + processor.processServerSideRpcTimeout(ctx, (DeviceActorServerSideRpcTimeoutMsg) msg); break; case SESSION_TIMEOUT_MSG: processor.checkSessionsTimeout(); diff --git a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorCreator.java b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorCreator.java index 5a77003895..3624378989 100644 --- a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorCreator.java +++ b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorCreator.java @@ -16,12 +16,14 @@ package org.thingsboard.server.actors.device; import org.thingsboard.server.actors.ActorSystemContext; +import org.thingsboard.server.actors.TbActor; +import org.thingsboard.server.actors.TbActorId; +import org.thingsboard.server.actors.TbEntityActorId; import org.thingsboard.server.actors.service.ContextBasedCreator; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.TenantId; -public class DeviceActorCreator extends ContextBasedCreator { - private static final long serialVersionUID = 1L; +public class DeviceActorCreator extends ContextBasedCreator { private final TenantId tenantId; private final DeviceId deviceId; @@ -33,7 +35,13 @@ public class DeviceActorCreator extends ContextBasedCreator { } @Override - public DeviceActor create() { + public TbActorId createActorId() { + return new TbEntityActorId(deviceId); + } + + @Override + public TbActor createActor() { return new DeviceActor(context, tenantId, deviceId); } + } diff --git a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java index d908258cc9..e328931c0e 100644 --- a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java +++ b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java @@ -15,7 +15,6 @@ */ package org.thingsboard.server.actors.device; -import akka.actor.ActorContext; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; @@ -27,6 +26,7 @@ import org.thingsboard.rule.engine.api.RpcError; import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg; import org.thingsboard.rule.engine.api.msg.DeviceNameOrTypeUpdateMsg; import org.thingsboard.server.actors.ActorSystemContext; +import org.thingsboard.server.actors.TbActorCtx; import org.thingsboard.server.actors.shared.AbstractContextAwareMsgProcessor; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.id.DeviceId; @@ -127,7 +127,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { } } - void processRpcRequest(ActorContext context, ToDeviceRpcRequestActorMsg msg) { + void processRpcRequest(TbActorCtx context, ToDeviceRpcRequestActorMsg msg) { ToDeviceRpcRequest request = msg.getMsg(); ToDeviceRpcRequestBody body = request.getBody(); ToDeviceRpcRequestMsg rpcRequest = ToDeviceRpcRequestMsg.newBuilder().setRequestId( @@ -162,13 +162,13 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { } } - private void registerPendingRpcRequest(ActorContext context, ToDeviceRpcRequestActorMsg msg, boolean sent, ToDeviceRpcRequestMsg rpcRequest, long timeout) { + private void registerPendingRpcRequest(TbActorCtx context, ToDeviceRpcRequestActorMsg msg, boolean sent, ToDeviceRpcRequestMsg rpcRequest, long timeout) { toDeviceRpcPendingMap.put(rpcRequest.getRequestId(), new ToDeviceRpcRequestMetadata(msg, sent)); DeviceActorServerSideRpcTimeoutMsg timeoutMsg = new DeviceActorServerSideRpcTimeoutMsg(rpcRequest.getRequestId(), timeout); scheduleMsgWithDelay(context, timeoutMsg, timeoutMsg.getTimeout()); } - void processServerSideRpcTimeout(ActorContext context, DeviceActorServerSideRpcTimeoutMsg msg) { + void processServerSideRpcTimeout(TbActorCtx context, DeviceActorServerSideRpcTimeoutMsg msg) { ToDeviceRpcRequestMetadata requestMd = toDeviceRpcPendingMap.remove(msg.getId()); if (requestMd != null) { log.debug("[{}] RPC request [{}] timeout detected!", deviceId, msg.getId()); @@ -177,7 +177,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { } } - private void sendPendingRequests(ActorContext context, UUID sessionId, SessionInfoProto sessionInfo) { + private void sendPendingRequests(TbActorCtx context, UUID sessionId, SessionInfoProto sessionInfo) { SessionType sessionType = getSessionType(sessionId); if (!toDeviceRpcPendingMap.isEmpty()) { log.debug("[{}] Pushing {} pending RPC messages to new async session [{}]", deviceId, toDeviceRpcPendingMap.size(), sessionId); @@ -198,7 +198,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { sentOneWayIds.forEach(toDeviceRpcPendingMap::remove); } - private Consumer> processPendingRpc(ActorContext context, UUID sessionId, String nodeId, Set sentOneWayIds) { + private Consumer> processPendingRpc(TbActorCtx context, UUID sessionId, String nodeId, Set sentOneWayIds) { return entry -> { ToDeviceRpcRequest request = entry.getValue().getMsg().getMsg(); ToDeviceRpcRequestBody body = request.getBody(); @@ -212,7 +212,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { }; } - void process(ActorContext context, TransportToDeviceActorMsgWrapper wrapper) { + void process(TbActorCtx context, TransportToDeviceActorMsgWrapper wrapper) { TransportToDeviceActorMsg msg = wrapper.getMsg(); TbCallback callback = wrapper.getCallback(); if (msg.hasSessionEvent()) { @@ -239,7 +239,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { callback.onSuccess(); } - private void handleClaimDeviceMsg(ActorContext context, SessionInfoProto sessionInfo, TransportProtos.ClaimDeviceMsg msg) { + private void handleClaimDeviceMsg(TbActorCtx context, SessionInfoProto sessionInfo, TransportProtos.ClaimDeviceMsg msg) { DeviceId deviceId = new DeviceId(new UUID(msg.getDeviceIdMSB(), msg.getDeviceIdLSB())); systemContext.getClaimDevicesService().registerClaimingInfo(tenantId, deviceId, msg.getSecretKey(), msg.getDurationMs()); } @@ -252,7 +252,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { systemContext.getDeviceStateService().onDeviceDisconnect(deviceId); } - private void handleGetAttributesRequest(ActorContext context, SessionInfoProto sessionInfo, GetAttributeRequestMsg request) { + private void handleGetAttributesRequest(TbActorCtx context, SessionInfoProto sessionInfo, GetAttributeRequestMsg request) { int requestId = request.getRequestId(); Futures.addCallback(getAttributesKvEntries(request), new FutureCallback>>() { @Override @@ -310,7 +310,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { return sessions.containsKey(sessionId) ? SessionType.ASYNC : SessionType.SYNC; } - void processAttributesUpdate(ActorContext context, DeviceAttributesEventNotificationMsg msg) { + void processAttributesUpdate(TbActorCtx context, DeviceAttributesEventNotificationMsg msg) { if (attributeSubscriptions.size() > 0) { boolean hasNotificationData = false; AttributeUpdateNotificationMsg.Builder notification = AttributeUpdateNotificationMsg.newBuilder(); @@ -349,7 +349,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { } } - private void processRpcResponses(ActorContext context, SessionInfoProto sessionInfo, ToDeviceRpcResponseMsg responseMsg) { + private void processRpcResponses(TbActorCtx context, SessionInfoProto sessionInfo, ToDeviceRpcResponseMsg responseMsg) { UUID sessionId = getSessionId(sessionInfo); log.debug("[{}] Processing rpc command response [{}]", deviceId, sessionId); ToDeviceRpcRequestMetadata requestMd = toDeviceRpcPendingMap.remove(responseMsg.getRequestId()); @@ -362,7 +362,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { } } - private void processSubscriptionCommands(ActorContext context, SessionInfoProto sessionInfo, SubscribeToAttributeUpdatesMsg subscribeCmd) { + private void processSubscriptionCommands(TbActorCtx context, SessionInfoProto sessionInfo, SubscribeToAttributeUpdatesMsg subscribeCmd) { UUID sessionId = getSessionId(sessionInfo); if (subscribeCmd.getUnsubscribe()) { log.debug("[{}] Canceling attributes subscription for session [{}]", deviceId, sessionId); @@ -383,7 +383,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { return new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB()); } - private void processSubscriptionCommands(ActorContext context, SessionInfoProto sessionInfo, SubscribeToRPCMsg subscribeCmd) { + private void processSubscriptionCommands(TbActorCtx context, SessionInfoProto sessionInfo, SubscribeToRPCMsg subscribeCmd) { UUID sessionId = getSessionId(sessionInfo); if (subscribeCmd.getUnsubscribe()) { log.debug("[{}] Canceling rpc subscription for session [{}]", deviceId, sessionId); @@ -433,7 +433,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { } } - private void handleSessionActivity(ActorContext context, SessionInfoProto sessionInfoProto, SubscriptionInfoProto subscriptionInfo) { + private void handleSessionActivity(TbActorCtx context, SessionInfoProto sessionInfoProto, SubscriptionInfoProto subscriptionInfo) { UUID sessionId = getSessionId(sessionInfoProto); SessionInfoMetaData sessionMD = sessions.computeIfAbsent(sessionId, id -> new SessionInfoMetaData(new SessionInfo(SessionType.ASYNC, sessionInfoProto.getNodeId()), 0L)); @@ -612,8 +612,8 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { .addAllSessions(sessionsList).build().toByteArray()); } - void initSessionTimeout(ActorContext context) { - schedulePeriodicMsgWithDelay(context, SessionTimeoutCheckMsg.instance(), systemContext.getSessionInactivityTimeout(), systemContext.getSessionInactivityTimeout()); + void initSessionTimeout(TbActorCtx ctx) { + schedulePeriodicMsgWithDelay(ctx, SessionTimeoutCheckMsg.instance(), systemContext.getSessionInactivityTimeout(), systemContext.getSessionInactivityTimeout()); } void checkSessionsTimeout() { diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java index 55229694a7..c4ccd485ab 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java +++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java @@ -15,7 +15,6 @@ */ package org.thingsboard.server.actors.ruleChain; -import akka.actor.ActorRef; import com.datastax.driver.core.ResultSetFuture; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; @@ -30,6 +29,7 @@ import org.thingsboard.rule.engine.api.ScriptEngine; import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.TbRelationTypes; import org.thingsboard.server.actors.ActorSystemContext; +import org.thingsboard.server.actors.TbActorRef; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.Device; @@ -40,6 +40,7 @@ import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.RuleNodeId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.rule.RuleNode; +import org.thingsboard.server.common.msg.TbActorMsg; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgMetaData; import org.thingsboard.server.common.msg.queue.ServiceType; @@ -64,7 +65,6 @@ import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.queue.TbQueueCallback; import org.thingsboard.server.queue.TbQueueMsgMetadata; import org.thingsboard.server.service.script.RuleNodeJsScriptEngine; -import scala.concurrent.duration.Duration; import java.util.Collections; import java.util.Set; @@ -106,7 +106,7 @@ class DefaultTbContext implements TbContext { if (nodeCtx.getSelf().isDebugMode()) { relationTypes.forEach(relationType -> mainCtx.persistDebugOutput(nodeCtx.getTenantId(), nodeCtx.getSelf().getId(), msg, relationType, th)); } - nodeCtx.getChainActor().tell(new RuleNodeToRuleChainTellNextMsg(nodeCtx.getSelf().getId(), relationTypes, msg, th != null ? th.getMessage() : null), nodeCtx.getSelfActor()); + nodeCtx.getChainActor().tell(new RuleNodeToRuleChainTellNextMsg(nodeCtx.getSelf().getId(), relationTypes, msg, th != null ? th.getMessage() : null)); } @Override @@ -132,7 +132,7 @@ class DefaultTbContext implements TbContext { .setTenantIdMSB(getTenantId().getId().getMostSignificantBits()) .setTenantIdLSB(getTenantId().getId().getLeastSignificantBits()) .setTbMsg(TbMsg.toByteString(tbMsg)).build(); - mainCtx.getClusterService().pushMsgToRuleEngine(tpi, tbMsg.getId(), msg, new SimpleTbQueueCallback(onSuccess, onFailure)); + mainCtx.getClusterService().pushMsgToRuleEngine(tpi, tbMsg.getId(), msg, new SimpleTbQueueCallback(onSuccess, onFailure)); } @Override @@ -189,7 +189,7 @@ class DefaultTbContext implements TbContext { if (failureMessage != null) { msg.setFailureMessage(failureMessage); } - mainCtx.getClusterService().pushMsgToRuleEngine(tpi, tbMsg.getId(), msg.build(), new SimpleTbQueueCallback(onSuccess, onFailure)); + mainCtx.getClusterService().pushMsgToRuleEngine(tpi, tbMsg.getId(), msg.build(), new SimpleTbQueueCallback(onSuccess, onFailure)); } @Override @@ -205,8 +205,8 @@ class DefaultTbContext implements TbContext { return mainCtx.resolve(ServiceType.TB_RULE_ENGINE, getTenantId(), entityId).isMyPartition(); } - private void scheduleMsgWithDelay(Object msg, long delayInMs, ActorRef target) { - mainCtx.getScheduler().scheduleOnce(Duration.create(delayInMs, TimeUnit.MILLISECONDS), target, msg, mainCtx.getActorSystem().dispatcher(), nodeCtx.getSelfActor()); + private void scheduleMsgWithDelay(TbActorMsg msg, long delayInMs, TbActorRef target) { + mainCtx.scheduleMsgWithDelay(target, msg, delayInMs); } @Override @@ -215,7 +215,7 @@ class DefaultTbContext implements TbContext { mainCtx.persistDebugOutput(nodeCtx.getTenantId(), nodeCtx.getSelf().getId(), msg, TbRelationTypes.FAILURE, th); } nodeCtx.getChainActor().tell(new RuleNodeToRuleChainTellNextMsg(nodeCtx.getSelf().getId(), Collections.singleton(TbRelationTypes.FAILURE), - msg, th != null ? th.getMessage() : null), nodeCtx.getSelfActor()); + msg, th != null ? th.getMessage() : null)); } public void updateSelf(RuleNode self) { @@ -223,8 +223,8 @@ class DefaultTbContext implements TbContext { } @Override - public TbMsg newMsg(String type, EntityId originator, TbMsgMetaData metaData, String data) { - return TbMsg.newMsg(type, originator, metaData, data, nodeCtx.getSelf().getRuleChainId(), nodeCtx.getSelf().getId()); + public TbMsg newMsg(String queueName, String type, EntityId originator, TbMsgMetaData metaData, String data) { + return TbMsg.newMsg(queueName, type, originator, metaData, data, nodeCtx.getSelf().getRuleChainId(), nodeCtx.getSelf().getId()); } @Override diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActor.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActor.java index 027f4aac9d..aac7094fc2 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActor.java +++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActor.java @@ -15,9 +15,11 @@ */ package org.thingsboard.server.actors.ruleChain; -import akka.actor.OneForOneStrategy; -import akka.actor.SupervisorStrategy; import org.thingsboard.server.actors.ActorSystemContext; +import org.thingsboard.server.actors.TbActor; +import org.thingsboard.server.actors.TbActorCtx; +import org.thingsboard.server.actors.TbActorId; +import org.thingsboard.server.actors.TbEntityActorId; import org.thingsboard.server.actors.service.ComponentActor; import org.thingsboard.server.actors.service.ContextBasedCreator; import org.thingsboard.server.common.data.id.RuleChainId; @@ -27,18 +29,24 @@ import org.thingsboard.server.common.msg.TbActorMsg; import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; import org.thingsboard.server.common.msg.queue.PartitionChangeMsg; import org.thingsboard.server.common.msg.queue.QueueToRuleEngineMsg; -import scala.concurrent.duration.Duration; public class RuleChainActor extends ComponentActor { + private final RuleChain ruleChain; + private RuleChainActor(ActorSystemContext systemContext, TenantId tenantId, RuleChain ruleChain) { super(systemContext, tenantId, ruleChain.getId()); - setProcessor(new RuleChainActorMessageProcessor(tenantId, ruleChain, systemContext, - context().parent(), context().self())); + this.ruleChain = ruleChain; } @Override - protected boolean process(TbActorMsg msg) { + protected RuleChainActorMessageProcessor createProcessor(TbActorCtx ctx) { + return new RuleChainActorMessageProcessor(tenantId, ruleChain, systemContext, + ctx.getParentRef(), ctx); + } + + @Override + protected boolean doProcess(TbActorMsg msg) { switch (msg.getMsgType()) { case COMPONENT_LIFE_CYCLE_MSG: onComponentLifecycleMsg((ComponentLifecycleMsg) msg); @@ -64,7 +72,7 @@ public class RuleChainActor extends ComponentActor { + public static class ActorCreator extends ContextBasedCreator { private static final long serialVersionUID = 1L; private final TenantId tenantId; @@ -77,7 +85,12 @@ public class RuleChainActor extends ComponentActor { - logAndPersist("Unknown Failure", ActorSystemContext.toException(t)); - return SupervisorStrategy.resume(); - }); } diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java index d583eb6b0a..96814407ad 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java +++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java @@ -15,12 +15,12 @@ */ package org.thingsboard.server.actors.ruleChain; -import akka.actor.ActorContext; -import akka.actor.ActorRef; -import akka.actor.Props; import lombok.extern.slf4j.Slf4j; import org.thingsboard.rule.engine.api.TbRelationTypes; import org.thingsboard.server.actors.ActorSystemContext; +import org.thingsboard.server.actors.TbActorCtx; +import org.thingsboard.server.actors.TbActorRef; +import org.thingsboard.server.actors.TbEntityActorId; import org.thingsboard.server.actors.service.DefaultActorService; import org.thingsboard.server.actors.shared.ComponentMsgProcessor; import org.thingsboard.server.common.data.EntityType; @@ -64,20 +64,21 @@ import java.util.stream.Collectors; @Slf4j public class RuleChainActorMessageProcessor extends ComponentMsgProcessor { - private final ActorRef parent; - private final ActorRef self; + private final TbActorRef parent; + private final TbActorRef self; private final Map nodeActors; private final Map> nodeRoutes; private final RuleChainService service; private final TbClusterService clusterService; private final EdgeService edgeService; private String ruleChainName; + private RuleNodeId firstId; private RuleNodeCtx firstNode; private boolean started; RuleChainActorMessageProcessor(TenantId tenantId, RuleChain ruleChain, ActorSystemContext systemContext - , ActorRef parent, ActorRef self) { + , TbActorRef parent, TbActorRef self) { super(systemContext, tenantId, ruleChain.getId()); this.ruleChainName = ruleChain.getName(); this.parent = parent; @@ -95,7 +96,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor existingNodes = ruleNodeList.stream().map(RuleNode::getId).collect(Collectors.toSet()); - List removedRules = nodeActors.keySet().stream().filter(node -> !existingNodes.contains(node)).collect(Collectors.toList()); - removedRules.forEach(ruleNodeId -> { - log.trace("[{}][{}] Removing rule node [{}]", tenantId, entityId, ruleNodeId); - RuleNodeCtx removed = nodeActors.remove(ruleNodeId); - removed.getSelfActor().tell(new ComponentLifecycleMsg(tenantId, removed.getSelf().getId(), ComponentLifecycleEvent.DELETED), self); - }); + Set existingNodes = ruleNodeList.stream().map(RuleNode::getId).collect(Collectors.toSet()); + List removedRules = nodeActors.keySet().stream().filter(node -> !existingNodes.contains(node)).collect(Collectors.toList()); + removedRules.forEach(ruleNodeId -> { + log.trace("[{}][{}] Removing rule node [{}]", tenantId, entityId, ruleNodeId); + RuleNodeCtx removed = nodeActors.remove(ruleNodeId); + removed.getSelfActor().tellWithHighPriority(new ComponentLifecycleMsg(tenantId, removed.getSelf().getId(), ComponentLifecycleEvent.DELETED)); + }); initRoutes(ruleChain, ruleNodeList); } else if (ruleChain.getType().equals(RuleChainType.EDGE)) { @@ -154,26 +155,23 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor actorRef.tell(msg, self)); + nodeActors.values().stream().map(RuleNodeCtx::getSelfActor).forEach(actorRef -> actorRef.tellWithHighPriority(msg)); } - private ActorRef createRuleNodeActor(ActorContext context, RuleNode ruleNode) { - String dispatcherName = tenantId.getId().equals(EntityId.NULL_UUID) ? - DefaultActorService.SYSTEM_RULE_DISPATCHER_NAME : DefaultActorService.TENANT_RULE_DISPATCHER_NAME; - return context.actorOf( - Props.create(new RuleNodeActor.ActorCreator(systemContext, tenantId, entityId, ruleNode.getName(), ruleNode.getId())) - .withDispatcher(dispatcherName), ruleNode.getId().toString()); + private TbActorRef createRuleNodeActor(TbActorCtx ctx, RuleNode ruleNode) { + return ctx.getOrCreateChildActor(new TbEntityActorId(ruleNode.getId()), + () -> DefaultActorService.RULE_DISPATCHER_NAME, + () -> new RuleNodeActor.ActorCreator(systemContext, tenantId, entityId, ruleNode.getName(), ruleNode.getId())); } private void initRoutes(RuleChain ruleChain, List ruleNodeList) { @@ -256,7 +254,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor relations = nodeRoutes.get(originatorNodeId).stream() .filter(r -> contains(relationTypes, r.getType())) .collect(Collectors.toList()); @@ -312,7 +310,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor actors; @Getter protected RuleChain rootChain; @Getter - protected ActorRef rootChainActor; + protected TbActorRef rootChainActor; public RuleChainManagerActor(ActorSystemContext systemContext, TenantId tenantId) { super(systemContext); this.tenantId = tenantId; - this.actors = HashBiMap.create(); this.ruleChainService = systemContext.getRuleChainService(); } @@ -59,46 +59,41 @@ public abstract class RuleChainManagerActor extends ContextAwareActor { for (RuleChain ruleChain : new PageDataIterable<>(link -> ruleChainService.findTenantRuleChainsByType(tenantId, RuleChainType.CORE, link), ContextAwareActor.ENTITY_PACK_LIMIT)) { RuleChainId ruleChainId = ruleChain.getId(); log.debug("[{}|{}] Creating rule chain actor", ruleChainId.getEntityType(), ruleChain.getId()); - //TODO: remove this cast making UUIDBased subclass of EntityId an interface and vice versa. - ActorRef actorRef = getOrCreateActor(this.context(), ruleChainId, id -> ruleChain); + TbActorRef actorRef = getOrCreateActor(ruleChainId, id -> ruleChain); visit(ruleChain, actorRef); log.debug("[{}|{}] Rule Chain actor created.", ruleChainId.getEntityType(), ruleChainId.getId()); } } - protected void visit(RuleChain entity, ActorRef actorRef) { + protected void visit(RuleChain entity, TbActorRef actorRef) { if (entity != null && entity.isRoot() && entity.getType().equals(RuleChainType.CORE)) { rootChain = entity; rootChainActor = actorRef; } } - public ActorRef getOrCreateActor(akka.actor.ActorContext context, RuleChainId ruleChainId) { - return getOrCreateActor(context, ruleChainId, eId -> ruleChainService.findRuleChainById(TenantId.SYS_TENANT_ID, eId)); + protected TbActorRef getOrCreateActor(RuleChainId ruleChainId) { + return getOrCreateActor(ruleChainId, eId -> ruleChainService.findRuleChainById(TenantId.SYS_TENANT_ID, eId)); } - public ActorRef getOrCreateActor(akka.actor.ActorContext context, RuleChainId ruleChainId, Function provider) { - return actors.computeIfAbsent(ruleChainId, eId -> { - RuleChain ruleChain = provider.apply(eId); - return context.actorOf(Props.create(new RuleChainActor.ActorCreator(systemContext, tenantId, ruleChain)) - .withDispatcher(DefaultActorService.TENANT_RULE_DISPATCHER_NAME), eId.toString()); - }); + protected TbActorRef getOrCreateActor(RuleChainId ruleChainId, Function provider) { + return ctx.getOrCreateChildActor(new TbEntityActorId(ruleChainId), + () -> DefaultActorService.RULE_DISPATCHER_NAME, + () -> { + RuleChain ruleChain = provider.apply(ruleChainId); + return new RuleChainActor.ActorCreator(systemContext, tenantId, ruleChain); + }); } - protected ActorRef getEntityActorRef(EntityId entityId) { - ActorRef target = null; + protected TbActorRef getEntityActorRef(EntityId entityId) { + TbActorRef target = null; if (entityId.getEntityType() == EntityType.RULE_CHAIN) { - target = getOrCreateActor(this.context(), (RuleChainId) entityId); + target = getOrCreateActor((RuleChainId) entityId); } return target; } - protected void broadcast(Object msg) { - actors.values().forEach(actorRef -> actorRef.tell(msg, ActorRef.noSender())); + protected void broadcast(TbActorMsg msg) { + ctx.broadcastToChildren(msg, new TbEntityTypeActorIdPredicate(EntityType.RULE_CHAIN)); } - - public ActorRef get(RuleChainId id) { - return actors.get(id); - } - } diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeActor.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeActor.java index 74316a996b..f11c087958 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeActor.java +++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeActor.java @@ -15,31 +15,43 @@ */ package org.thingsboard.server.actors.ruleChain; +import lombok.extern.slf4j.Slf4j; import org.thingsboard.server.actors.ActorSystemContext; +import org.thingsboard.server.actors.TbActor; +import org.thingsboard.server.actors.TbActorCtx; +import org.thingsboard.server.actors.TbActorId; +import org.thingsboard.server.actors.TbEntityActorId; import org.thingsboard.server.actors.service.ComponentActor; import org.thingsboard.server.actors.service.ContextBasedCreator; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.RuleNodeId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.msg.TbActorMsg; import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; import org.thingsboard.server.common.msg.queue.PartitionChangeMsg; +@Slf4j public class RuleNodeActor extends ComponentActor { private final String ruleChainName; private final RuleChainId ruleChainId; + private final RuleNodeId ruleNodeId; private RuleNodeActor(ActorSystemContext systemContext, TenantId tenantId, RuleChainId ruleChainId, String ruleChainName, RuleNodeId ruleNodeId) { super(systemContext, tenantId, ruleNodeId); this.ruleChainName = ruleChainName; this.ruleChainId = ruleChainId; - setProcessor(new RuleNodeActorMessageProcessor(tenantId, this.ruleChainName, ruleNodeId, systemContext, - context().parent(), context().self())); + this.ruleNodeId = ruleNodeId; } @Override - protected boolean process(TbActorMsg msg) { + protected RuleNodeActorMessageProcessor createProcessor(TbActorCtx ctx) { + return new RuleNodeActorMessageProcessor(tenantId, this.ruleChainName, ruleNodeId, systemContext, ctx.getParentRef(), ctx); + } + + @Override + protected boolean doProcess(TbActorMsg msg) { switch (msg.getMsgType()) { case COMPONENT_LIFE_CYCLE_MSG: onComponentLifecycleMsg((ComponentLifecycleMsg) msg); @@ -93,8 +105,7 @@ public class RuleNodeActor extends ComponentActor { - private static final long serialVersionUID = 1L; + public static class ActorCreator extends ContextBasedCreator { private final TenantId tenantId; private final RuleChainId ruleChainId; @@ -111,7 +122,12 @@ public class RuleNodeActor extends ComponentActor { private final String ruleChainName; - private final ActorRef self; + private final TbActorRef self; private RuleNode ruleNode; private TbNode tbNode; private DefaultTbContext defaultCtx; RuleNodeActorMessageProcessor(TenantId tenantId, String ruleChainName, RuleNodeId ruleNodeId, ActorSystemContext systemContext - , ActorRef parent, ActorRef self) { + , TbActorRef parent, TbActorRef self) { super(systemContext, tenantId, ruleNodeId); this.ruleChainName = ruleChainName; this.self = self; @@ -49,7 +49,7 @@ public class RuleNodeActorMessageProcessor extends ComponentMsgProcessor> extends ContextAwareActor { private long lastPersistedErrorTs = 0L; @@ -43,30 +47,34 @@ public abstract class ComponentActor getErrorPersistFrequency()) { diff --git a/application/src/main/java/org/thingsboard/server/actors/service/ContextAwareActor.java b/application/src/main/java/org/thingsboard/server/actors/service/ContextAwareActor.java index a2c0d08ade..833dbee751 100644 --- a/application/src/main/java/org/thingsboard/server/actors/service/ContextAwareActor.java +++ b/application/src/main/java/org/thingsboard/server/actors/service/ContextAwareActor.java @@ -15,17 +15,18 @@ */ package org.thingsboard.server.actors.service; -import akka.actor.Terminated; -import akka.actor.UntypedAbstractActor; +import lombok.extern.slf4j.Slf4j; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.thingsboard.server.actors.AbstractTbActor; import org.thingsboard.server.actors.ActorSystemContext; +import org.thingsboard.server.actors.ProcessFailureStrategy; +import org.thingsboard.server.actors.TbActor; +import org.thingsboard.server.actors.TbActorCtx; import org.thingsboard.server.common.msg.TbActorMsg; - -public abstract class ContextAwareActor extends UntypedAbstractActor { - - protected final Logger log = LoggerFactory.getLogger(getClass()); +@Slf4j +public abstract class ContextAwareActor extends AbstractTbActor { public static final int ENTITY_PACK_LIMIT = 1024; @@ -37,27 +38,29 @@ public abstract class ContextAwareActor extends UntypedAbstractActor { } @Override - public void onReceive(Object msg) { + public boolean process(TbActorMsg msg) { if (log.isDebugEnabled()) { log.debug("Processing msg: {}", msg); } - if (msg instanceof TbActorMsg) { - try { - if (!process((TbActorMsg) msg)) { - log.warn("Unknown message: {}!", msg); - } - } catch (Exception e) { - throw e; - } - } else if (msg instanceof Terminated) { - processTermination((Terminated) msg); - } else { - log.warn("Unknown message: {}!", msg); + if (!doProcess(msg)) { + log.warn("Unprocessed message: {}!", msg); } + return false; } - protected void processTermination(Terminated msg) { + protected abstract boolean doProcess(TbActorMsg msg); + + @Override + public ProcessFailureStrategy onProcessFailure(Throwable t) { + log.debug("[{}] Processing failure: ", getActorRef().getActorId(), t); + return doProcessFailure(t); } - protected abstract boolean process(TbActorMsg msg); + protected ProcessFailureStrategy doProcessFailure(Throwable t) { + if (t instanceof Error) { + return ProcessFailureStrategy.stop(); + } else { + return ProcessFailureStrategy.resume(); + } + } } diff --git a/application/src/main/java/org/thingsboard/server/actors/service/ContextBasedCreator.java b/application/src/main/java/org/thingsboard/server/actors/service/ContextBasedCreator.java index f8ce3baf41..28b6221863 100644 --- a/application/src/main/java/org/thingsboard/server/actors/service/ContextBasedCreator.java +++ b/application/src/main/java/org/thingsboard/server/actors/service/ContextBasedCreator.java @@ -15,12 +15,10 @@ */ package org.thingsboard.server.actors.service; -import akka.japi.Creator; import org.thingsboard.server.actors.ActorSystemContext; +import org.thingsboard.server.actors.TbActorCreator; -public abstract class ContextBasedCreator implements Creator { - - private static final long serialVersionUID = 1L; +public abstract class ContextBasedCreator implements TbActorCreator { protected final transient ActorSystemContext context; diff --git a/application/src/main/java/org/thingsboard/server/actors/service/DefaultActorService.java b/application/src/main/java/org/thingsboard/server/actors/service/DefaultActorService.java index 7b04e82786..2d3775c9f5 100644 --- a/application/src/main/java/org/thingsboard/server/actors/service/DefaultActorService.java +++ b/application/src/main/java/org/thingsboard/server/actors/service/DefaultActorService.java @@ -15,85 +15,121 @@ */ package org.thingsboard.server.actors.service; -import akka.actor.ActorRef; -import akka.actor.ActorSystem; -import akka.actor.Props; -import akka.actor.Terminated; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.context.event.EventListener; -import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; +import org.thingsboard.common.util.ThingsBoardThreadFactory; import org.thingsboard.server.actors.ActorSystemContext; +import org.thingsboard.server.actors.DefaultTbActorSystem; +import org.thingsboard.server.actors.TbActorId; +import org.thingsboard.server.actors.TbActorRef; +import org.thingsboard.server.actors.TbActorSystem; +import org.thingsboard.server.actors.TbActorSystemSettings; import org.thingsboard.server.actors.app.AppActor; import org.thingsboard.server.actors.app.AppInitMsg; import org.thingsboard.server.actors.stats.StatsActor; import org.thingsboard.server.common.msg.queue.PartitionChangeMsg; import org.thingsboard.server.queue.discovery.PartitionChangeEvent; -import scala.concurrent.Await; -import scala.concurrent.Future; -import scala.concurrent.duration.Duration; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; -import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; @Service @Slf4j public class DefaultActorService implements ActorService { - private static final String ACTOR_SYSTEM_NAME = "Akka"; - public static final String APP_DISPATCHER_NAME = "app-dispatcher"; - public static final String CORE_DISPATCHER_NAME = "core-dispatcher"; - public static final String SYSTEM_RULE_DISPATCHER_NAME = "system-rule-dispatcher"; - public static final String TENANT_RULE_DISPATCHER_NAME = "rule-dispatcher"; + public static final String TENANT_DISPATCHER_NAME = "tenant-dispatcher"; + public static final String DEVICE_DISPATCHER_NAME = "device-dispatcher"; + public static final String RULE_DISPATCHER_NAME = "rule-dispatcher"; @Autowired private ActorSystemContext actorContext; - private ActorSystem system; + private TbActorSystem system; + + private TbActorRef appActor; + + @Value("${actors.system.throughput:5}") + private int actorThroughput; + + @Value("${actors.system.max_actor_init_attempts:10}") + private int maxActorInitAttempts; + + @Value("${actors.system.scheduler_pool_size:1}") + private int schedulerPoolSize; + + @Value("${actors.system.app_dispatcher_pool_size:1}") + private int appDispatcherSize; + + @Value("${actors.system.tenant_dispatcher_pool_size:2}") + private int tenantDispatcherSize; - private ActorRef appActor; + @Value("${actors.system.device_dispatcher_pool_size:4}") + private int deviceDispatcherSize; + + @Value("${actors.system.rule_dispatcher_pool_size:4}") + private int ruleDispatcherSize; @PostConstruct public void initActorSystem() { - log.info("Initializing Actor system."); + log.info("Initializing actor system."); actorContext.setActorService(this); - system = ActorSystem.create(ACTOR_SYSTEM_NAME, actorContext.getConfig()); + TbActorSystemSettings settings = new TbActorSystemSettings(actorThroughput, schedulerPoolSize, maxActorInitAttempts); + system = new DefaultTbActorSystem(settings); + + system.createDispatcher(APP_DISPATCHER_NAME, initDispatcherExecutor(APP_DISPATCHER_NAME, appDispatcherSize)); + system.createDispatcher(TENANT_DISPATCHER_NAME, initDispatcherExecutor(TENANT_DISPATCHER_NAME, tenantDispatcherSize)); + system.createDispatcher(DEVICE_DISPATCHER_NAME, initDispatcherExecutor(DEVICE_DISPATCHER_NAME, deviceDispatcherSize)); + system.createDispatcher(RULE_DISPATCHER_NAME, initDispatcherExecutor(RULE_DISPATCHER_NAME, ruleDispatcherSize)); + actorContext.setActorSystem(system); - appActor = system.actorOf(Props.create(new AppActor.ActorCreator(actorContext)).withDispatcher(APP_DISPATCHER_NAME), "appActor"); + appActor = system.createRootActor(APP_DISPATCHER_NAME, new AppActor.ActorCreator(actorContext)); actorContext.setAppActor(appActor); - ActorRef statsActor = system.actorOf(Props.create(new StatsActor.ActorCreator(actorContext)).withDispatcher(CORE_DISPATCHER_NAME), "statsActor"); + TbActorRef statsActor = system.createRootActor(TENANT_DISPATCHER_NAME, new StatsActor.ActorCreator(actorContext, "StatsActor")); actorContext.setStatsActor(statsActor); log.info("Actor system initialized."); } + private ExecutorService initDispatcherExecutor(String dispatcherName, int poolSize) { + if (poolSize == 0) { + int cores = Runtime.getRuntime().availableProcessors(); + poolSize = Math.max(1, cores / 2); + } + if (poolSize == 1) { + return Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName(dispatcherName)); + } else { + return Executors.newWorkStealingPool(poolSize); + } + } + @EventListener(ApplicationReadyEvent.class) public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) { log.info("Received application ready event. Sending application init message to actor system"); - appActor.tell(new AppInitMsg(), ActorRef.noSender()); + appActor.tellWithHighPriority(new AppInitMsg()); } @EventListener(PartitionChangeEvent.class) public void onApplicationEvent(PartitionChangeEvent partitionChangeEvent) { log.info("Received partition change event."); - this.appActor.tell(new PartitionChangeMsg(partitionChangeEvent.getServiceQueueKey(), partitionChangeEvent.getPartitions()), ActorRef.noSender()); + this.appActor.tellWithHighPriority(new PartitionChangeMsg(partitionChangeEvent.getServiceQueueKey(), partitionChangeEvent.getPartitions())); } @PreDestroy public void stopActorSystem() { - Future status = system.terminate(); - try { - Terminated terminated = Await.result(status, Duration.Inf()); - log.info("Actor system terminated: {}", terminated); - } catch (Exception e) { - log.error("Failed to terminate actor system.", e); + if (system != null) { + log.info("Stopping actor system."); + system.stop(); + log.info("Actor system stopped."); } } diff --git a/application/src/main/java/org/thingsboard/server/actors/shared/AbstractContextAwareMsgProcessor.java b/application/src/main/java/org/thingsboard/server/actors/shared/AbstractContextAwareMsgProcessor.java index 064d5f28c8..dc78416c06 100644 --- a/application/src/main/java/org/thingsboard/server/actors/shared/AbstractContextAwareMsgProcessor.java +++ b/application/src/main/java/org/thingsboard/server/actors/shared/AbstractContextAwareMsgProcessor.java @@ -15,18 +15,13 @@ */ package org.thingsboard.server.actors.shared; -import akka.actor.ActorContext; -import akka.actor.ActorRef; -import akka.actor.Scheduler; -import akka.event.LoggingAdapter; import com.fasterxml.jackson.databind.ObjectMapper; -import lombok.AllArgsConstructor; -import lombok.Data; import lombok.extern.slf4j.Slf4j; import org.thingsboard.server.actors.ActorSystemContext; -import scala.concurrent.ExecutionContextExecutor; -import scala.concurrent.duration.Duration; +import org.thingsboard.server.actors.TbActorCtx; +import org.thingsboard.server.common.msg.TbActorMsg; +import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; @Slf4j @@ -40,31 +35,16 @@ public abstract class AbstractContextAwareMsgProcessor { this.systemContext = systemContext; } - private Scheduler getScheduler() { + private ScheduledExecutorService getScheduler() { return systemContext.getScheduler(); } - private ExecutionContextExecutor getSystemDispatcher() { - return systemContext.getActorSystem().dispatcher(); + protected void schedulePeriodicMsgWithDelay(TbActorCtx ctx, TbActorMsg msg, long delayInMs, long periodInMs) { + systemContext.schedulePeriodicMsgWithDelay(ctx, msg, delayInMs, periodInMs); } - protected void schedulePeriodicMsgWithDelay(ActorContext ctx, Object msg, long delayInMs, long periodInMs) { - schedulePeriodicMsgWithDelay(msg, delayInMs, periodInMs, ctx.self()); + protected void scheduleMsgWithDelay(TbActorCtx ctx, TbActorMsg msg, long delayInMs) { + systemContext.scheduleMsgWithDelay(ctx, msg, delayInMs); } - private void schedulePeriodicMsgWithDelay(Object msg, long delayInMs, long periodInMs, ActorRef target) { - log.debug("Scheduling periodic msg {} every {} ms with delay {} ms", msg, periodInMs, delayInMs); - getScheduler().schedule(Duration.create(delayInMs, TimeUnit.MILLISECONDS), Duration.create(periodInMs, TimeUnit.MILLISECONDS), target, msg, getSystemDispatcher(), null); - } - - protected void scheduleMsgWithDelay(ActorContext ctx, Object msg, long delayInMs) { - scheduleMsgWithDelay(msg, delayInMs, ctx.self()); - } - - private void scheduleMsgWithDelay(Object msg, long delayInMs, ActorRef target) { - log.debug("Scheduling msg {} with delay {} ms", msg, delayInMs); - getScheduler().scheduleOnce(Duration.create(delayInMs, TimeUnit.MILLISECONDS), target, msg, getSystemDispatcher(), null); - } - - } diff --git a/application/src/main/java/org/thingsboard/server/actors/shared/ComponentMsgProcessor.java b/application/src/main/java/org/thingsboard/server/actors/shared/ComponentMsgProcessor.java index 45779a9f89..8ced2be204 100644 --- a/application/src/main/java/org/thingsboard/server/actors/shared/ComponentMsgProcessor.java +++ b/application/src/main/java/org/thingsboard/server/actors/shared/ComponentMsgProcessor.java @@ -15,16 +15,15 @@ */ package org.thingsboard.server.actors.shared; -import akka.actor.ActorContext; import lombok.extern.slf4j.Slf4j; import org.thingsboard.server.actors.ActorSystemContext; +import org.thingsboard.server.actors.TbActorCtx; import org.thingsboard.server.actors.stats.StatsPersistTick; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.plugin.ComponentLifecycleState; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.queue.PartitionChangeMsg; -import org.thingsboard.server.common.msg.queue.RuleEngineException; import org.thingsboard.server.common.msg.queue.RuleNodeException; @Slf4j @@ -42,38 +41,38 @@ public abstract class ComponentMsgProcessor extends Abstract public abstract String getComponentName(); - public abstract void start(ActorContext context) throws Exception; + public abstract void start(TbActorCtx context) throws Exception; - public abstract void stop(ActorContext context) throws Exception; + public abstract void stop(TbActorCtx context) throws Exception; public abstract void onPartitionChangeMsg(PartitionChangeMsg msg) throws Exception; - public void onCreated(ActorContext context) throws Exception { + public void onCreated(TbActorCtx context) throws Exception { start(context); } - public void onUpdate(ActorContext context) throws Exception { + public void onUpdate(TbActorCtx context) throws Exception { restart(context); } - public void onActivate(ActorContext context) throws Exception { + public void onActivate(TbActorCtx context) throws Exception { restart(context); } - public void onSuspend(ActorContext context) throws Exception { + public void onSuspend(TbActorCtx context) throws Exception { stop(context); } - public void onStop(ActorContext context) throws Exception { + public void onStop(TbActorCtx context) throws Exception { stop(context); } - private void restart(ActorContext context) throws Exception { + private void restart(TbActorCtx context) throws Exception { stop(context); start(context); } - public void scheduleStatsPersistTick(ActorContext context, long statsPersistFrequency) { + public void scheduleStatsPersistTick(TbActorCtx context, long statsPersistFrequency) { schedulePeriodicMsgWithDelay(context, new StatsPersistTick(), statsPersistFrequency, statsPersistFrequency); } diff --git a/application/src/main/java/org/thingsboard/server/actors/stats/StatsActor.java b/application/src/main/java/org/thingsboard/server/actors/stats/StatsActor.java index 717dcecc19..1718305cde 100644 --- a/application/src/main/java/org/thingsboard/server/actors/stats/StatsActor.java +++ b/application/src/main/java/org/thingsboard/server/actors/stats/StatsActor.java @@ -19,10 +19,15 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; import org.thingsboard.server.actors.ActorSystemContext; +import org.thingsboard.server.actors.TbActor; +import org.thingsboard.server.actors.TbActorCtx; +import org.thingsboard.server.actors.TbActorId; +import org.thingsboard.server.actors.TbStringActorId; import org.thingsboard.server.actors.service.ContextAwareActor; import org.thingsboard.server.actors.service.ContextBasedCreator; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.Event; +import org.thingsboard.server.common.msg.MsgType; import org.thingsboard.server.common.msg.TbActorMsg; @Slf4j @@ -35,24 +40,17 @@ public class StatsActor extends ContextAwareActor { } @Override - protected boolean process(TbActorMsg msg) { - //TODO Move everything here, to work with TbActorMsg\ - return false; - } - - @Override - public void onReceive(Object msg) { + protected boolean doProcess(TbActorMsg msg) { log.debug("Received message: {}", msg); - if (msg instanceof StatsPersistMsg) { - try { - onStatsPersistMsg((StatsPersistMsg) msg); - } catch (Exception e) { - log.warn("Failed to persist statistics: {}", msg, e); - } + if (msg.getMsgType().equals(MsgType.STATS_PERSIST_MSG)) { + onStatsPersistMsg((StatsPersistMsg) msg); + return true; + } else { + return false; } } - public void onStatsPersistMsg(StatsPersistMsg msg) throws Exception { + public void onStatsPersistMsg(StatsPersistMsg msg) { Event event = new Event(); event.setEntityId(msg.getEntityId()); event.setTenantId(msg.getTenantId()); @@ -65,15 +63,21 @@ public class StatsActor extends ContextAwareActor { return mapper.createObjectNode().put("server", serviceId).put("messagesProcessed", messagesProcessed).put("errorsOccurred", errorsOccurred); } - public static class ActorCreator extends ContextBasedCreator { - private static final long serialVersionUID = 1L; + public static class ActorCreator extends ContextBasedCreator { + private final String actorId; - public ActorCreator(ActorSystemContext context) { + public ActorCreator(ActorSystemContext context, String actorId) { super(context); + this.actorId = actorId; + } + + @Override + public TbActorId createActorId() { + return new TbStringActorId(actorId); } @Override - public StatsActor create() { + public TbActor createActor() { return new StatsActor(context); } } diff --git a/application/src/main/java/org/thingsboard/server/actors/stats/StatsPersistMsg.java b/application/src/main/java/org/thingsboard/server/actors/stats/StatsPersistMsg.java index a9e2583878..0d946f46ff 100644 --- a/application/src/main/java/org/thingsboard/server/actors/stats/StatsPersistMsg.java +++ b/application/src/main/java/org/thingsboard/server/actors/stats/StatsPersistMsg.java @@ -20,13 +20,21 @@ import lombok.Getter; import lombok.ToString; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.msg.MsgType; +import org.thingsboard.server.common.msg.TbActorMsg; @AllArgsConstructor @Getter @ToString -public final class StatsPersistMsg { +public final class StatsPersistMsg implements TbActorMsg { + private long messagesProcessed; private long errorsOccurred; private TenantId tenantId; private EntityId entityId; + + @Override + public MsgType getMsgType() { + return MsgType.STATS_PERSIST_MSG; + } } diff --git a/application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java b/application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java index 246b5ad1e5..7ce5fb3206 100644 --- a/application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java +++ b/application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java @@ -15,16 +15,16 @@ */ package org.thingsboard.server.actors.tenant; -import akka.actor.ActorInitializationException; -import akka.actor.ActorRef; -import akka.actor.LocalActorRef; -import akka.actor.OneForOneStrategy; -import akka.actor.Props; -import akka.actor.SupervisorStrategy; -import akka.actor.Terminated; -import com.google.common.collect.BiMap; -import com.google.common.collect.HashBiMap; +import lombok.extern.slf4j.Slf4j; import org.thingsboard.server.actors.ActorSystemContext; +import org.thingsboard.server.actors.TbActor; +import org.thingsboard.server.actors.TbActorCtx; +import org.thingsboard.server.actors.TbActorException; +import org.thingsboard.server.actors.TbActorId; +import org.thingsboard.server.actors.TbActorNotRegisteredException; +import org.thingsboard.server.actors.TbActorRef; +import org.thingsboard.server.actors.TbEntityActorId; +import org.thingsboard.server.actors.TbEntityTypeActorIdPredicate; import org.thingsboard.server.actors.device.DeviceActorCreator; import org.thingsboard.server.actors.ruleChain.RuleChainManagerActor; import org.thingsboard.server.actors.service.ContextBasedCreator; @@ -32,6 +32,7 @@ import org.thingsboard.server.actors.service.DefaultActorService; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.rule.RuleChain; @@ -46,32 +47,25 @@ import org.thingsboard.server.common.msg.queue.PartitionChangeMsg; import org.thingsboard.server.common.msg.queue.QueueToRuleEngineMsg; import org.thingsboard.server.common.msg.queue.RuleEngineException; import org.thingsboard.server.common.msg.queue.ServiceType; -import scala.concurrent.duration.Duration; import java.util.List; import java.util.Optional; -import java.util.stream.Collectors; +@Slf4j public class TenantActor extends RuleChainManagerActor { - private final BiMap deviceActors; private boolean isRuleEngineForCurrentTenant; private boolean isCore; private TenantActor(ActorSystemContext systemContext, TenantId tenantId) { super(systemContext, tenantId); - this.deviceActors = HashBiMap.create(); - } - - @Override - public SupervisorStrategy supervisorStrategy() { - return strategy; } boolean cantFindTenant = false; @Override - public void preStart() { + public void init(TbActorCtx ctx) throws TbActorException { + super.init(ctx); log.info("[{}] Starting tenant actor.", tenantId); try { Tenant tenant = systemContext.getTenantService().findTenantById(tenantId); @@ -101,16 +95,18 @@ public class TenantActor extends RuleChainManagerActor { } } catch (Exception e) { log.warn("[{}] Unknown failure", tenantId, e); +// TODO: throw this in 3.1? +// throw new TbActorException("Failed to init actor", e); } } @Override - public void postStop() { + public void destroy() { log.info("[{}] Stopping tenant actor.", tenantId); } @Override - protected boolean process(TbActorMsg msg) { + protected boolean doProcess(TbActorMsg msg) { if (cantFindTenant) { log.info("[{}] Processing missing Tenant msg: {}", tenantId, msg); if (msg.getMsgType().equals(MsgType.QUEUE_TO_RULE_ENGINE_MSG)) { @@ -127,13 +123,13 @@ public class TenantActor extends RuleChainManagerActor { //To Rule Chain Actors broadcast(msg); } else if (ServiceType.TB_CORE.equals(serviceType)) { - //To Device Actors - List repartitionedDevices = - deviceActors.keySet().stream().filter(deviceId -> !isMyPartition(deviceId)).collect(Collectors.toList()); - for (DeviceId deviceId : repartitionedDevices) { - ActorRef deviceActor = deviceActors.remove(deviceId); - context().stop(deviceActor); - } + List deviceActorIds = ctx.filterChildren(new TbEntityTypeActorIdPredicate(EntityType.DEVICE) { + @Override + protected boolean testEntityId(EntityId entityId) { + return super.testEntityId(entityId) && !isMyPartition(entityId); + } + }); + deviceActorIds.forEach(id -> ctx.stop(id)); } break; case COMPONENT_LIFE_CYCLE_MSG: @@ -143,12 +139,14 @@ public class TenantActor extends RuleChainManagerActor { onQueueToRuleEngineMsg((QueueToRuleEngineMsg) msg); break; case TRANSPORT_TO_DEVICE_ACTOR_MSG: + onToDeviceActorMsg((DeviceAwareMsg) msg, false); + break; case DEVICE_ATTRIBUTES_UPDATE_TO_DEVICE_ACTOR_MSG: case DEVICE_CREDENTIALS_UPDATE_TO_DEVICE_ACTOR_MSG: case DEVICE_NAME_OR_TYPE_UPDATE_TO_DEVICE_ACTOR_MSG: case DEVICE_RPC_REQUEST_TO_DEVICE_ACTOR_MSG: case SERVER_RPC_RESPONSE_TO_DEVICE_ACTOR_MSG: - onToDeviceActorMsg((DeviceAwareMsg) msg); + onToDeviceActorMsg((DeviceAwareMsg) msg, true); break; case RULE_CHAIN_TO_RULE_CHAIN_MSG: onRuleChainMsg((RuleChainAwareMsg) msg); @@ -159,8 +157,8 @@ public class TenantActor extends RuleChainManagerActor { return true; } - private boolean isMyPartition(DeviceId deviceId) { - return systemContext.resolve(ServiceType.TB_CORE, tenantId, deviceId).isMyPartition(); + private boolean isMyPartition(EntityId entityId) { + return systemContext.resolve(ServiceType.TB_CORE, tenantId, entityId).isMyPartition(); } private void onQueueToRuleEngineMsg(QueueToRuleEngineMsg msg) { @@ -171,16 +169,15 @@ public class TenantActor extends RuleChainManagerActor { TbMsg tbMsg = msg.getTbMsg(); if (tbMsg.getRuleChainId() == null) { if (getRootChainActor() != null) { - getRootChainActor().tell(msg, self()); + getRootChainActor().tell(msg); } else { tbMsg.getCallback().onFailure(new RuleEngineException("No Root Rule Chain available!")); log.info("[{}] No Root Chain: {}", tenantId, msg); } } else { - ActorRef ruleChainActor = get(tbMsg.getRuleChainId()); - if (ruleChainActor != null) { - ruleChainActor.tell(msg, self()); - } else { + try { + ctx.tell(new TbEntityActorId(tbMsg.getRuleChainId()), msg); + } catch (TbActorNotRegisteredException ex) { log.trace("Received message for non-existing rule chain: [{}]", tbMsg.getRuleChainId()); //TODO: 3.1 Log it to dead letters queue; tbMsg.getCallback().onSuccess(); @@ -189,19 +186,24 @@ public class TenantActor extends RuleChainManagerActor { } private void onRuleChainMsg(RuleChainAwareMsg msg) { - getOrCreateActor(context(), msg.getRuleChainId()).tell(msg, self()); + getOrCreateActor(msg.getRuleChainId()).tell(msg); } - private void onToDeviceActorMsg(DeviceAwareMsg msg) { + private void onToDeviceActorMsg(DeviceAwareMsg msg, boolean priority) { if (!isCore) { log.warn("RECEIVED INVALID MESSAGE: {}", msg); } - getOrCreateDeviceActor(msg.getDeviceId()).tell(msg, ActorRef.noSender()); + TbActorRef deviceActor = getOrCreateDeviceActor(msg.getDeviceId()); + if (priority) { + deviceActor.tellWithHighPriority(msg); + } else { + deviceActor.tell(msg); + } } private void onComponentLifecycleMsg(ComponentLifecycleMsg msg) { if (isRuleEngineForCurrentTenant) { - ActorRef target = getEntityActorRef(msg.getEntityId()); + TbActorRef target = getEntityActorRef(msg.getEntityId()); if (target != null) { if (msg.getEntityId().getEntityType() == EntityType.RULE_CHAIN) { RuleChain ruleChain = systemContext.getRuleChainService(). @@ -210,42 +212,20 @@ public class TenantActor extends RuleChainManagerActor { visit(ruleChain, target); } } - target.tell(msg, ActorRef.noSender()); + target.tellWithHighPriority(msg); } else { log.debug("[{}] Invalid component lifecycle msg: {}", tenantId, msg); } } } - private ActorRef getOrCreateDeviceActor(DeviceId deviceId) { - return deviceActors.computeIfAbsent(deviceId, k -> { - log.debug("[{}][{}] Creating device actor.", tenantId, deviceId); - ActorRef deviceActor = context().actorOf(Props.create(new DeviceActorCreator(systemContext, tenantId, deviceId)) - .withDispatcher(DefaultActorService.CORE_DISPATCHER_NAME) - , deviceId.toString()); - context().watch(deviceActor); - log.debug("[{}][{}] Created device actor: {}.", tenantId, deviceId, deviceActor); - return deviceActor; - }); - } - - @Override - protected void processTermination(Terminated message) { - ActorRef terminated = message.actor(); - if (terminated instanceof LocalActorRef) { - boolean removed = deviceActors.inverse().remove(terminated) != null; - if (removed) { - log.debug("[{}] Removed actor:", terminated); - } else { - log.debug("Removed actor was not found in the device map!"); - } - } else { - throw new IllegalStateException("Remote actors are not supported!"); - } + private TbActorRef getOrCreateDeviceActor(DeviceId deviceId) { + return ctx.getOrCreateChildActor(new TbEntityActorId(deviceId), + () -> DefaultActorService.DEVICE_DISPATCHER_NAME, + () -> new DeviceActorCreator(systemContext, tenantId, deviceId)); } - public static class ActorCreator extends ContextBasedCreator { - private static final long serialVersionUID = 1L; + public static class ActorCreator extends ContextBasedCreator { private final TenantId tenantId; @@ -255,18 +235,14 @@ public class TenantActor extends RuleChainManagerActor { } @Override - public TenantActor create() { - return new TenantActor(context, tenantId); + public TbActorId createActorId() { + return new TbEntityActorId(tenantId); } - } - private final SupervisorStrategy strategy = new OneForOneStrategy(3, Duration.create("1 minute"), t -> { - log.warn("[{}] Unknown failure", tenantId, t); - if (t instanceof ActorInitializationException) { - return SupervisorStrategy.stop(); - } else { - return SupervisorStrategy.resume(); + @Override + public TbActor createActor() { + return new TenantActor(context, tenantId); } - }); + } } diff --git a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java index 3c6e991d5e..16125133f8 100644 --- a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java +++ b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java @@ -15,7 +15,6 @@ */ package org.thingsboard.server.service.queue; -import akka.actor.ActorRef; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.scheduling.annotation.Scheduled; @@ -157,7 +156,7 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService log.warn("[{}] Timeout to process message: {}", id, msg.getValue())); + ctx.getAckMap().forEach((id, msg) -> log.debug("[{}] Timeout to process message: {}", id, msg.getValue())); ctx.getFailedMap().forEach((id, msg) -> log.warn("[{}] Failed to process message: {}", id, msg.getValue())); } mainConsumer.commit(); @@ -215,7 +214,7 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService actorMsg = encodingService.decode(toCoreNotification.getComponentLifecycleMsg().toByteArray()); if (actorMsg.isPresent()) { log.trace("[{}] Forwarding message to App Actor {}", id, actorMsg.get()); - actorContext.tell(actorMsg.get(), ActorRef.noSender()); + actorContext.tellWithHighPriority(actorMsg.get()); } callback.onSuccess(); } @@ -293,7 +292,7 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService actorMsg = encodingService.decode(nfMsg.getComponentLifecycleMsg().toByteArray()); if (actorMsg.isPresent()) { log.trace("[{}] Forwarding message to App Actor {}", id, actorMsg.get()); - actorContext.tell(actorMsg.get(), ActorRef.noSender()); + actorContext.tellWithHighPriority(actorMsg.get()); } callback.onSuccess(); } else if (nfMsg.hasFromDeviceRpcResponse()) { @@ -248,8 +246,8 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService< } } - private void forwardToRuleEngineActor(TenantId tenantId, ToRuleEngineMsg toRuleEngineMsg, TbMsgCallback callback) { - TbMsg tbMsg = TbMsg.fromBytes(toRuleEngineMsg.getTbMsg().toByteArray(), callback); + private void forwardToRuleEngineActor(String queueName, TenantId tenantId, ToRuleEngineMsg toRuleEngineMsg, TbMsgCallback callback) { + TbMsg tbMsg = TbMsg.fromBytes(queueName, toRuleEngineMsg.getTbMsg().toByteArray(), callback); QueueToRuleEngineMsg msg; ProtocolStringList relationTypesList = toRuleEngineMsg.getRelationTypesList(); Set relationTypes = null; @@ -261,7 +259,7 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService< } } msg = new QueueToRuleEngineMsg(tenantId, tbMsg, relationTypes, toRuleEngineMsg.getFailureMessage()); - actorContext.tell(msg, ActorRef.noSender()); + actorContext.tell(msg); } @Scheduled(fixedDelayString = "${queue.rule-engine.stats.print-interval-ms}") diff --git a/application/src/main/java/org/thingsboard/server/service/queue/processing/TbRuleEngineProcessingResult.java b/application/src/main/java/org/thingsboard/server/service/queue/processing/TbRuleEngineProcessingResult.java index e818ffb9d7..bbe717ce83 100644 --- a/application/src/main/java/org/thingsboard/server/service/queue/processing/TbRuleEngineProcessingResult.java +++ b/application/src/main/java/org/thingsboard/server/service/queue/processing/TbRuleEngineProcessingResult.java @@ -27,6 +27,8 @@ import java.util.concurrent.ConcurrentMap; public class TbRuleEngineProcessingResult { + @Getter + private final String queueName; @Getter private final boolean success; @Getter @@ -34,7 +36,8 @@ public class TbRuleEngineProcessingResult { @Getter private final TbMsgPackProcessingContext ctx; - public TbRuleEngineProcessingResult(boolean timeout, TbMsgPackProcessingContext ctx) { + public TbRuleEngineProcessingResult(String queueName, boolean timeout, TbMsgPackProcessingContext ctx) { + this.queueName = queueName; this.timeout = timeout; this.ctx = ctx; this.success = !timeout && ctx.getPendingMap().isEmpty() && ctx.getFailedMap().isEmpty(); diff --git a/application/src/main/java/org/thingsboard/server/service/queue/processing/TbRuleEngineProcessingStrategyFactory.java b/application/src/main/java/org/thingsboard/server/service/queue/processing/TbRuleEngineProcessingStrategyFactory.java index 80b0523a81..b6220f5f94 100644 --- a/application/src/main/java/org/thingsboard/server/service/queue/processing/TbRuleEngineProcessingStrategyFactory.java +++ b/application/src/main/java/org/thingsboard/server/service/queue/processing/TbRuleEngineProcessingStrategyFactory.java @@ -100,7 +100,7 @@ public class TbRuleEngineProcessingStrategyFactory { } log.debug("[{}] Going to reprocess {} messages", queueName, toReprocess.size()); if (log.isTraceEnabled()) { - toReprocess.forEach((id, msg) -> log.trace("Going to reprocess [{}]: {}", id, TbMsg.fromBytes(msg.getValue().getTbMsg().toByteArray(), TbMsgCallback.EMPTY))); + toReprocess.forEach((id, msg) -> log.trace("Going to reprocess [{}]: {}", id, TbMsg.fromBytes(result.getQueueName(), msg.getValue().getTbMsg().toByteArray(), TbMsgCallback.EMPTY))); } if (pauseBetweenRetries > 0) { try { @@ -129,10 +129,10 @@ public class TbRuleEngineProcessingStrategyFactory { log.debug("[{}] Reprocessing skipped for {} failed and {} timeout messages", queueName, result.getFailedMap().size(), result.getPendingMap().size()); } if (log.isTraceEnabled()) { - result.getFailedMap().forEach((id, msg) -> log.trace("Failed messages [{}]: {}", id, TbMsg.fromBytes(msg.getValue().getTbMsg().toByteArray(), TbMsgCallback.EMPTY))); + result.getFailedMap().forEach((id, msg) -> log.trace("Failed messages [{}]: {}", id, TbMsg.fromBytes(result.getQueueName(), msg.getValue().getTbMsg().toByteArray(), TbMsgCallback.EMPTY))); } if (log.isTraceEnabled()) { - result.getPendingMap().forEach((id, msg) -> log.trace("Timeout messages [{}]: {}", id, TbMsg.fromBytes(msg.getValue().getTbMsg().toByteArray(), TbMsgCallback.EMPTY))); + result.getPendingMap().forEach((id, msg) -> log.trace("Timeout messages [{}]: {}", id, TbMsg.fromBytes(result.getQueueName(), msg.getValue().getTbMsg().toByteArray(), TbMsgCallback.EMPTY))); } return new TbRuleEngineProcessingDecision(true, null); } diff --git a/application/src/main/java/org/thingsboard/server/service/rpc/DefaultTbCoreDeviceRpcService.java b/application/src/main/java/org/thingsboard/server/service/rpc/DefaultTbCoreDeviceRpcService.java index f6e5eb8b43..b5e273c223 100644 --- a/application/src/main/java/org/thingsboard/server/service/rpc/DefaultTbCoreDeviceRpcService.java +++ b/application/src/main/java/org/thingsboard/server/service/rpc/DefaultTbCoreDeviceRpcService.java @@ -15,7 +15,6 @@ */ package org.thingsboard.server.service.rpc; -import akka.actor.ActorRef; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; @@ -122,7 +121,7 @@ public class DefaultTbCoreDeviceRpcService implements TbCoreDeviceRpcService { log.trace("[{}][{}] Processing local rpc call to device actor [{}]", request.getTenantId(), request.getId(), request.getDeviceId()); UUID requestId = request.getId(); localToDeviceRpcRequests.put(requestId, rpcMsg); - actorContext.tell(rpcMsg, ActorRef.noSender()); + actorContext.tellWithHighPriority(rpcMsg); scheduleToDeviceTimeout(request, requestId); } @@ -176,7 +175,7 @@ public class DefaultTbCoreDeviceRpcService implements TbCoreDeviceRpcService { } private void scheduleToRuleEngineTimeout(ToDeviceRpcRequest request, UUID requestId) { - long timeout = Math.max(0, request.getExpirationTime() - System.currentTimeMillis()); + long timeout = Math.max(0, request.getExpirationTime() - System.currentTimeMillis()) + TimeUnit.SECONDS.toMillis(1); log.trace("[{}] processing to rule engine request.", requestId); scheduler.schedule(() -> { log.trace("[{}] timeout for processing to rule engine request.", requestId); @@ -188,7 +187,7 @@ public class DefaultTbCoreDeviceRpcService implements TbCoreDeviceRpcService { } private void scheduleToDeviceTimeout(ToDeviceRpcRequest request, UUID requestId) { - long timeout = Math.max(0, request.getExpirationTime() - System.currentTimeMillis()); + long timeout = Math.max(0, request.getExpirationTime() - System.currentTimeMillis()) + TimeUnit.SECONDS.toMillis(1); log.trace("[{}] processing to device request.", requestId); scheduler.schedule(() -> { log.trace("[{}] timeout for to device request.", requestId); diff --git a/application/src/main/java/org/thingsboard/server/service/rpc/DefaultTbRuleEngineRpcService.java b/application/src/main/java/org/thingsboard/server/service/rpc/DefaultTbRuleEngineRpcService.java index 0ec730b7dc..80d47a84e8 100644 --- a/application/src/main/java/org/thingsboard/server/service/rpc/DefaultTbRuleEngineRpcService.java +++ b/application/src/main/java/org/thingsboard/server/service/rpc/DefaultTbRuleEngineRpcService.java @@ -164,7 +164,7 @@ public class DefaultTbRuleEngineRpcService implements TbRuleEngineDeviceRpcServi } private void scheduleTimeout(ToDeviceRpcRequest request, UUID requestId) { - long timeout = Math.max(0, request.getExpirationTime() - System.currentTimeMillis()); + long timeout = Math.max(0, request.getExpirationTime() - System.currentTimeMillis()) + TimeUnit.SECONDS.toMillis(1); log.trace("[{}] processing the request: [{}]", this.hashCode(), requestId); scheduler.schedule(() -> { log.trace("[{}] timeout the request: [{}]", this.hashCode(), requestId); diff --git a/application/src/main/java/org/thingsboard/server/service/script/AbstractNashornJsInvokeService.java b/application/src/main/java/org/thingsboard/server/service/script/AbstractNashornJsInvokeService.java index 5ea8d13270..8e6666bbba 100644 --- a/application/src/main/java/org/thingsboard/server/service/script/AbstractNashornJsInvokeService.java +++ b/application/src/main/java/org/thingsboard/server/service/script/AbstractNashornJsInvokeService.java @@ -126,7 +126,7 @@ public abstract class AbstractNashornJsInvokeService extends AbstractJsInvokeSer scriptIdToNameMap.put(scriptId, functionName); return scriptId; } catch (Exception e) { - log.warn("Failed to compile JS script: {}", e.getMessage(), e); + log.debug("Failed to compile JS script: {}", e.getMessage(), e); throw new ExecutionException(e); } }); diff --git a/application/src/main/resources/actor-system.conf b/application/src/main/resources/actor-system.conf deleted file mode 100644 index 5a20c58fc7..0000000000 --- a/application/src/main/resources/actor-system.conf +++ /dev/null @@ -1,139 +0,0 @@ -# -# Copyright © 2016-2020 The Thingsboard Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - - -akka { - # JVM shutdown, System.exit(-1), in case of a fatal error, - # such as OutOfMemoryError - jvm-exit-on-fatal-error = off - loglevel = "INFO" - loggers = ["akka.event.slf4j.Slf4jLogger"] -} - -# This dispatcher is used for app -app-dispatcher { - type = Dispatcher - executor = "fork-join-executor" - fork-join-executor { - # Min number of threads to cap factor-based parallelism number to - parallelism-min = 1 - # Max number of threads to cap factor-based parallelism number to - parallelism-max = 1 - - # The parallelism factor is used to determine thread pool size using the - # following formula: ceil(available processors * factor). Resulting size - # is then bounded by the parallelism-min and parallelism-max values. - parallelism-factor = 1.0 - } - # How long time the dispatcher will wait for new actors until it shuts down - shutdown-timeout = 1s - - # Throughput defines the number of messages that are processed in a batch - # before the thread is returned to the pool. Set to 1 for as fair as possible. - throughput = 5 -} - -# This dispatcher is used for rpc actors -rpc-dispatcher { - type = Dispatcher - executor = "fork-join-executor" - fork-join-executor { - # Min number of threads to cap factor-based parallelism number to - parallelism-min = 2 - # Max number of threads to cap factor-based parallelism number to - parallelism-max = 8 - - # The parallelism factor is used to determine thread pool size using the - # following formula: ceil(available processors * factor). Resulting size - # is then bounded by the parallelism-min and parallelism-max values. - parallelism-factor = 0.5 - } - # How long time the dispatcher will wait for new actors until it shuts down - shutdown-timeout = 1s - - # Throughput defines the number of messages that are processed in a batch - # before the thread is returned to the pool. Set to 1 for as fair as possible. - throughput = 5 -} - -# This dispatcher is used for auth -core-dispatcher { - type = Dispatcher - executor = "fork-join-executor" - fork-join-executor { - # Min number of threads to cap factor-based parallelism number to - parallelism-min = 2 - # Max number of threads to cap factor-based parallelism number to - parallelism-max = 12 - - # The parallelism factor is used to determine thread pool size using the - # following formula: ceil(available processors * factor). Resulting size - # is then bounded by the parallelism-min and parallelism-max values. - parallelism-factor = 0.25 - } - # How long time the dispatcher will wait for new actors until it shuts down - shutdown-timeout = 1s - - # Throughput defines the number of messages that are processed in a batch - # before the thread is returned to the pool. Set to 1 for as fair as possible. - throughput = 5 -} - -# This dispatcher is used for system rule chains and rule node actors -system-rule-dispatcher { - type = Dispatcher - executor = "fork-join-executor" - fork-join-executor { - # Min number of threads to cap factor-based parallelism number to - parallelism-min = 2 - # Max number of threads to cap factor-based parallelism number to - parallelism-max = 12 - - # The parallelism factor is used to determine thread pool size using the - # following formula: ceil(available processors * factor). Resulting size - # is then bounded by the parallelism-min and parallelism-max values. - parallelism-factor = 0.25 - } - # How long time the dispatcher will wait for new actors until it shuts down - shutdown-timeout = 1s - - # Throughput defines the number of messages that are processed in a batch - # before the thread is returned to the pool. Set to 1 for as fair as possible. - throughput = 5 -} - -# This dispatcher is used for tenant rule chains and rule node actors -rule-dispatcher { - type = Dispatcher - executor = "fork-join-executor" - fork-join-executor { - # Min number of threads to cap factor-based parallelism number to - parallelism-min = 2 - # Max number of threads to cap factor-based parallelism number to - parallelism-max = 12 - - # The parallelism factor is used to determine thread pool size using the - # following formula: ceil(available processors * factor). Resulting size - # is then bounded by the parallelism-min and parallelism-max values. - parallelism-factor = 0.25 - } - # How long time the dispatcher will wait for new actors until it shuts down - shutdown-timeout = 1s - - # Throughput defines the number of messages that are processed in a batch - # before the thread is returned to the pool. Set to 1 for as fair as possible. - throughput = 5 -} diff --git a/application/src/main/resources/logback.xml b/application/src/main/resources/logback.xml index c868009a7e..417a76326b 100644 --- a/application/src/main/resources/logback.xml +++ b/application/src/main/resources/logback.xml @@ -27,7 +27,6 @@ - diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index a974de3cd6..089953e81b 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -281,6 +281,14 @@ sql: # Actor system parameters actors: + system: + throughput: "${ACTORS_SYSTEM_THROUGHPUT:5}" + scheduler_pool_size: "${ACTORS_SYSTEM_SCHEDULER_POOL_SIZE:1}" + max_actor_init_attempts: "${ACTORS_SYSTEM_MAX_ACTOR_INIT_ATTEMPTS:10}" + app_dispatcher_pool_size: "${ACTORS_SYSTEM_APP_DISPATCHER_POOL_SIZE:1}" + tenant_dispatcher_pool_size: "${ACTORS_SYSTEM_TENANT_DISPATCHER_POOL_SIZE:2}" + device_dispatcher_pool_size: "${ACTORS_SYSTEM_DEVICE_DISPATCHER_POOL_SIZE:4}" + rule_dispatcher_pool_size: "${ACTORS_SYSTEM_RULE_DISPATCHER_POOL_SIZE:4}" tenant: create_components_on_init: "${ACTORS_TENANT_CREATE_COMPONENTS_ON_INIT:true}" session: @@ -318,11 +326,6 @@ actors: enabled: "${ACTORS_STATISTICS_ENABLED:true}" js_print_interval_ms: "${ACTORS_JS_STATISTICS_PRINT_INTERVAL_MS:10000}" persist_frequency: "${ACTORS_STATISTICS_PERSIST_FREQUENCY:3600000}" - queue: - # Enable/disable persistence of un-processed messages to the queue - enabled: "${ACTORS_QUEUE_ENABLED:true}" - # Maximum allowed timeout for persistence into the queue - timeout: "${ACTORS_QUEUE_PERSISTENCE_TIMEOUT:30000}" cache: # caffeine or redis diff --git a/application/src/test/java/org/thingsboard/server/mqtt/MqttSqlTestSuite.java b/application/src/test/java/org/thingsboard/server/mqtt/MqttSqlTestSuite.java index 69a6e99353..0e7234c716 100644 --- a/application/src/test/java/org/thingsboard/server/mqtt/MqttSqlTestSuite.java +++ b/application/src/test/java/org/thingsboard/server/mqtt/MqttSqlTestSuite.java @@ -26,7 +26,9 @@ import java.util.Arrays; @RunWith(ClasspathSuite.class) @ClasspathSuite.ClassnameFilters({ - "org.thingsboard.server.mqtt.rpc.sql.*Test", "org.thingsboard.server.mqtt.telemetry.sql.*Test"}) + "org.thingsboard.server.mqtt.rpc.sql.*Test", + "org.thingsboard.server.mqtt.telemetry.sql.*Test" +}) public class MqttSqlTestSuite { @ClassRule diff --git a/application/src/test/java/org/thingsboard/server/mqtt/rpc/AbstractMqttServerSideRpcIntegrationTest.java b/application/src/test/java/org/thingsboard/server/mqtt/rpc/AbstractMqttServerSideRpcIntegrationTest.java index 9ddb2c81d9..54bd9a4836 100644 --- a/application/src/test/java/org/thingsboard/server/mqtt/rpc/AbstractMqttServerSideRpcIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/mqtt/rpc/AbstractMqttServerSideRpcIntegrationTest.java @@ -136,7 +136,7 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractC String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"24\",\"value\": 1},\"timeout\": 6000}"; String deviceId = savedDevice.getId().getId().toString(); - doPostAsync("/api/plugins/rpc/oneway/" + deviceId, setGpioRequest, String.class, status().isRequestTimeout(), + doPostAsync("/api/plugins/rpc/oneway/" + deviceId, setGpioRequest, String.class, status().is(409), asyncContextTimeoutToUseRpcPlugin); } @@ -193,7 +193,7 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractC String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"27\",\"value\": 1},\"timeout\": 6000}"; String deviceId = savedDevice.getId().getId().toString(); - doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().isRequestTimeout(), + doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().is(409), asyncContextTimeoutToUseRpcPlugin); } diff --git a/application/src/test/java/org/thingsboard/server/rules/flow/AbstractRuleEngineFlowIntegrationTest.java b/application/src/test/java/org/thingsboard/server/rules/flow/AbstractRuleEngineFlowIntegrationTest.java index 87a181deae..3b2c66a9e6 100644 --- a/application/src/test/java/org/thingsboard/server/rules/flow/AbstractRuleEngineFlowIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/rules/flow/AbstractRuleEngineFlowIntegrationTest.java @@ -15,8 +15,6 @@ */ package org.thingsboard.server.rules.flow; -import akka.actor.ActorRef; -import com.datastax.driver.core.utils.UUIDs; import lombok.extern.slf4j.Slf4j; import org.junit.After; import org.junit.Assert; @@ -26,7 +24,6 @@ import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; import org.thingsboard.rule.engine.metadata.TbGetAttributesNodeConfiguration; import org.thingsboard.server.actors.ActorSystemContext; -import org.thingsboard.server.actors.service.ActorService; import org.thingsboard.server.common.data.*; import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; import org.thingsboard.server.common.data.kv.StringDataEntry; @@ -36,7 +33,6 @@ import org.thingsboard.server.common.data.rule.RuleChainMetaData; import org.thingsboard.server.common.data.rule.RuleNode; import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.msg.TbMsg; -import org.thingsboard.server.common.msg.TbMsgDataType; import org.thingsboard.server.common.msg.TbMsgMetaData; import org.thingsboard.server.common.msg.queue.QueueToRuleEngineMsg; import org.thingsboard.server.common.msg.queue.TbMsgCallback; @@ -151,7 +147,7 @@ public abstract class AbstractRuleEngineFlowIntegrationTest extends AbstractRule TbMsg tbMsg = TbMsg.newMsg("CUSTOM", device.getId(), new TbMsgMetaData(), "{}", tbMsgCallback); QueueToRuleEngineMsg qMsg = new QueueToRuleEngineMsg(savedTenant.getId(), tbMsg, null, null); // Pushing Message to the system - actorSystem.tell(qMsg, ActorRef.noSender()); + actorSystem.tell(qMsg); Mockito.verify(tbMsgCallback, Mockito.timeout(10000)).onSuccess(); TimePageData eventsPage = getDebugEvents(savedTenant.getId(), ruleChain.getFirstRuleNodeId(), 1000); @@ -263,7 +259,7 @@ public abstract class AbstractRuleEngineFlowIntegrationTest extends AbstractRule TbMsg tbMsg = TbMsg.newMsg("CUSTOM", device.getId(), new TbMsgMetaData(), "{}", tbMsgCallback); QueueToRuleEngineMsg qMsg = new QueueToRuleEngineMsg(savedTenant.getId(), tbMsg, null, null); // Pushing Message to the system - actorSystem.tell(qMsg, ActorRef.noSender()); + actorSystem.tell(qMsg); Mockito.verify(tbMsgCallback, Mockito.timeout(10000)).onSuccess(); diff --git a/application/src/test/java/org/thingsboard/server/rules/lifecycle/AbstractRuleEngineLifecycleIntegrationTest.java b/application/src/test/java/org/thingsboard/server/rules/lifecycle/AbstractRuleEngineLifecycleIntegrationTest.java index 17f17698c9..50d7caa7bd 100644 --- a/application/src/test/java/org/thingsboard/server/rules/lifecycle/AbstractRuleEngineLifecycleIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/rules/lifecycle/AbstractRuleEngineLifecycleIntegrationTest.java @@ -15,8 +15,6 @@ */ package org.thingsboard.server.rules.lifecycle; -import akka.actor.ActorRef; -import com.datastax.driver.core.utils.UUIDs; import lombok.extern.slf4j.Slf4j; import org.junit.After; import org.junit.Assert; @@ -26,7 +24,6 @@ import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; import org.thingsboard.rule.engine.metadata.TbGetAttributesNodeConfiguration; import org.thingsboard.server.actors.ActorSystemContext; -import org.thingsboard.server.actors.service.ActorService; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.Event; @@ -40,7 +37,6 @@ import org.thingsboard.server.common.data.rule.RuleChainMetaData; import org.thingsboard.server.common.data.rule.RuleNode; import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.msg.TbMsg; -import org.thingsboard.server.common.msg.TbMsgDataType; import org.thingsboard.server.common.msg.TbMsgMetaData; import org.thingsboard.server.common.msg.queue.QueueToRuleEngineMsg; import org.thingsboard.server.common.msg.queue.TbMsgCallback; @@ -142,7 +138,7 @@ public abstract class AbstractRuleEngineLifecycleIntegrationTest extends Abstrac TbMsg tbMsg = TbMsg.newMsg("CUSTOM", device.getId(), new TbMsgMetaData(), "{}", tbMsgCallback); QueueToRuleEngineMsg qMsg = new QueueToRuleEngineMsg(savedTenant.getId(), tbMsg, null, null); // Pushing Message to the system - actorSystem.tell(qMsg, ActorRef.noSender()); + actorSystem.tell(qMsg); Mockito.verify(tbMsgCallback, Mockito.timeout(3000)).onSuccess(); diff --git a/application/src/test/resources/logback.xml b/application/src/test/resources/logback.xml index 47dacce343..4f083906e5 100644 --- a/application/src/test/resources/logback.xml +++ b/application/src/test/resources/logback.xml @@ -13,8 +13,6 @@ - - diff --git a/common/actor/pom.xml b/common/actor/pom.xml new file mode 100644 index 0000000000..9d101a2edb --- /dev/null +++ b/common/actor/pom.xml @@ -0,0 +1,75 @@ + + + 4.0.0 + + org.thingsboard + 2.5.3-SNAPSHOT + common + + org.thingsboard.common + actor + jar + + Thingsboard Actor system + https://thingsboard.io + + + UTF-8 + ${basedir}/../.. + + + + + org.thingsboard.common + util + + + org.thingsboard.common + message + + + org.slf4j + slf4j-api + + + org.slf4j + log4j-over-slf4j + + + ch.qos.logback + logback-core + + + ch.qos.logback + logback-classic + + + junit + junit + test + + + org.mockito + mockito-all + test + + + + diff --git a/common/actor/src/main/java/org/thingsboard/server/actors/AbstractTbActor.java b/common/actor/src/main/java/org/thingsboard/server/actors/AbstractTbActor.java new file mode 100644 index 0000000000..9ea2b6c67d --- /dev/null +++ b/common/actor/src/main/java/org/thingsboard/server/actors/AbstractTbActor.java @@ -0,0 +1,34 @@ +/** + * Copyright © 2016-2020 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.actors; + +import lombok.Getter; + +public abstract class AbstractTbActor implements TbActor { + + @Getter + protected TbActorCtx ctx; + + @Override + public void init(TbActorCtx ctx) throws TbActorException { + this.ctx = ctx; + } + + @Override + public TbActorRef getActorRef() { + return ctx; + } +} diff --git a/common/actor/src/main/java/org/thingsboard/server/actors/DefaultTbActorSystem.java b/common/actor/src/main/java/org/thingsboard/server/actors/DefaultTbActorSystem.java new file mode 100644 index 0000000000..a642ccec69 --- /dev/null +++ b/common/actor/src/main/java/org/thingsboard/server/actors/DefaultTbActorSystem.java @@ -0,0 +1,215 @@ +/** + * Copyright © 2016-2020 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.actors; + +import lombok.Data; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import org.thingsboard.common.util.ThingsBoardThreadFactory; +import org.thingsboard.server.common.msg.TbActorMsg; + +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +@Slf4j +@Data +public class DefaultTbActorSystem implements TbActorSystem { + + private final ConcurrentMap dispatchers = new ConcurrentHashMap<>(); + private final ConcurrentMap actors = new ConcurrentHashMap<>(); + private final ConcurrentMap actorCreationLocks = new ConcurrentHashMap<>(); + private final ConcurrentMap> parentChildMap = new ConcurrentHashMap<>(); + + @Getter + private final TbActorSystemSettings settings; + @Getter + private final ScheduledExecutorService scheduler; + + public DefaultTbActorSystem(TbActorSystemSettings settings) { + this.settings = settings; + this.scheduler = Executors.newScheduledThreadPool(settings.getSchedulerPoolSize(), ThingsBoardThreadFactory.forName("actor-system-scheduler")); + } + + @Override + public void createDispatcher(String dispatcherId, ExecutorService executor) { + Dispatcher current = dispatchers.putIfAbsent(dispatcherId, new Dispatcher(dispatcherId, executor)); + if (current != null) { + throw new RuntimeException("Dispatcher with id [" + dispatcherId + "] is already registered!"); + } + } + + @Override + public void destroyDispatcher(String dispatcherId) { + Dispatcher dispatcher = dispatchers.remove(dispatcherId); + if (dispatcher != null) { + dispatcher.getExecutor().shutdownNow(); + } else { + throw new RuntimeException("Dispatcher with id [" + dispatcherId + "] is not registered!"); + } + } + + @Override + public TbActorRef getActor(TbActorId actorId) { + return actors.get(actorId); + } + + @Override + public TbActorRef createRootActor(String dispatcherId, TbActorCreator creator) { + return createActor(dispatcherId, creator, null); + } + + @Override + public TbActorRef createChildActor(String dispatcherId, TbActorCreator creator, TbActorId parent) { + return createActor(dispatcherId, creator, parent); + } + + private TbActorRef createActor(String dispatcherId, TbActorCreator creator, TbActorId parent) { + Dispatcher dispatcher = dispatchers.get(dispatcherId); + if (dispatcher == null) { + log.warn("Dispatcher with id [{}] is not registered!", dispatcherId); + throw new RuntimeException("Dispatcher with id [" + dispatcherId + "] is not registered!"); + } + + TbActorId actorId = creator.createActorId(); + TbActorMailbox actorMailbox = actors.get(actorId); + if (actorMailbox != null) { + log.debug("Actor with id [{}] is already registered!", actorId); + } else { + Lock actorCreationLock = actorCreationLocks.computeIfAbsent(actorId, id -> new ReentrantLock()); + actorCreationLock.lock(); + try { + actorMailbox = actors.get(actorId); + if (actorMailbox == null) { + log.debug("Creating actor with id [{}]!", actorId); + TbActor actor = creator.createActor(); + TbActorRef parentRef = null; + if (parent != null) { + parentRef = getActor(parent); + if (parentRef == null) { + throw new TbActorNotRegisteredException(parent, "Parent Actor with id [" + parent + "] is not registered!"); + } + } + TbActorMailbox mailbox = new TbActorMailbox(this, settings, actorId, parentRef, actor, dispatcher); + actors.put(actorId, mailbox); + mailbox.initActor(); + actorMailbox = mailbox; + if (parent != null) { + parentChildMap.computeIfAbsent(parent, id -> ConcurrentHashMap.newKeySet()).add(actorId); + } + } else { + log.debug("Actor with id [{}] is already registered!", actorId); + } + } finally { + actorCreationLock.unlock(); + actorCreationLocks.remove(actorId); + } + } + return actorMailbox; + } + + @Override + public void tellWithHighPriority(TbActorId target, TbActorMsg actorMsg) { + tell(target, actorMsg, true); + } + + @Override + public void tell(TbActorId target, TbActorMsg actorMsg) { + tell(target, actorMsg, false); + } + + private void tell(TbActorId target, TbActorMsg actorMsg, boolean highPriority) { + TbActorMailbox mailbox = actors.get(target); + if (mailbox == null) { + throw new TbActorNotRegisteredException(target, "Actor with id [" + target + "] is not registered!"); + } + if (highPriority) { + mailbox.tellWithHighPriority(actorMsg); + } else { + mailbox.tell(actorMsg); + } + } + + + @Override + public void broadcastToChildren(TbActorId parent, TbActorMsg msg) { + broadcastToChildren(parent, id -> true, msg); + } + + @Override + public void broadcastToChildren(TbActorId parent, Predicate childFilter, TbActorMsg msg) { + Set children = parentChildMap.get(parent); + if (children != null) { + children.stream().filter(childFilter).forEach(id -> tell(id, msg)); + } + } + + @Override + public List filterChildren(TbActorId parent, Predicate childFilter) { + Set children = parentChildMap.get(parent); + if (children != null) { + return children.stream().filter(childFilter).collect(Collectors.toList()); + } else { + return Collections.emptyList(); + } + } + + @Override + public void stop(TbActorRef actorRef) { + stop(actorRef.getActorId()); + } + + @Override + public void stop(TbActorId actorId) { + Set children = parentChildMap.remove(actorId); + if (children != null) { + for (TbActorId child : children) { + stop(child); + } + } + TbActorMailbox mailbox = actors.remove(actorId); + if (mailbox != null) { + mailbox.destroy(); + } + } + + @Override + public void stop() { + dispatchers.values().forEach(dispatcher -> { + dispatcher.getExecutor().shutdown(); + try { + dispatcher.getExecutor().awaitTermination(3, TimeUnit.SECONDS); + } catch (InterruptedException e) { + log.warn("[{}] Failed to stop dispatcher", dispatcher.getDispatcherId(), e); + } + }); + if (scheduler != null) { + scheduler.shutdownNow(); + } + actors.clear(); + } + +} diff --git a/common/actor/src/main/java/org/thingsboard/server/actors/Dispatcher.java b/common/actor/src/main/java/org/thingsboard/server/actors/Dispatcher.java new file mode 100644 index 0000000000..bd02e74603 --- /dev/null +++ b/common/actor/src/main/java/org/thingsboard/server/actors/Dispatcher.java @@ -0,0 +1,28 @@ +/** + * Copyright © 2016-2020 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.actors; + +import lombok.Data; + +import java.util.concurrent.ExecutorService; + +@Data +class Dispatcher { + + private final String dispatcherId; + private final ExecutorService executor; + +} diff --git a/common/actor/src/main/java/org/thingsboard/server/actors/InitFailureStrategy.java b/common/actor/src/main/java/org/thingsboard/server/actors/InitFailureStrategy.java new file mode 100644 index 0000000000..37d0379bb0 --- /dev/null +++ b/common/actor/src/main/java/org/thingsboard/server/actors/InitFailureStrategy.java @@ -0,0 +1,45 @@ +/** + * Copyright © 2016-2020 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.actors; + +import lombok.Getter; +import lombok.ToString; + +@ToString +public class InitFailureStrategy { + + @Getter + private boolean stop; + @Getter + private long retryDelay; + + private InitFailureStrategy(boolean stop, long retryDelay) { + this.stop = stop; + this.retryDelay = retryDelay; + } + + public static InitFailureStrategy retryImmediately() { + return new InitFailureStrategy(false, 0); + } + + public static InitFailureStrategy retryWithDelay(long ms) { + return new InitFailureStrategy(false, ms); + } + + public static InitFailureStrategy stop() { + return new InitFailureStrategy(true, 0); + } +} diff --git a/common/actor/src/main/java/org/thingsboard/server/actors/ProcessFailureStrategy.java b/common/actor/src/main/java/org/thingsboard/server/actors/ProcessFailureStrategy.java new file mode 100644 index 0000000000..7448ba964b --- /dev/null +++ b/common/actor/src/main/java/org/thingsboard/server/actors/ProcessFailureStrategy.java @@ -0,0 +1,38 @@ +/** + * Copyright © 2016-2020 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.actors; + +import lombok.Getter; +import lombok.ToString; + +@ToString +public class ProcessFailureStrategy { + + @Getter + private boolean stop; + + private ProcessFailureStrategy(boolean stop) { + this.stop = stop; + } + + public static ProcessFailureStrategy stop() { + return new ProcessFailureStrategy(true); + } + + public static ProcessFailureStrategy resume() { + return new ProcessFailureStrategy(false); + } +} diff --git a/common/actor/src/main/java/org/thingsboard/server/actors/TbActor.java b/common/actor/src/main/java/org/thingsboard/server/actors/TbActor.java new file mode 100644 index 0000000000..97adc5da93 --- /dev/null +++ b/common/actor/src/main/java/org/thingsboard/server/actors/TbActor.java @@ -0,0 +1,43 @@ +/** + * Copyright © 2016-2020 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.actors; + +import org.thingsboard.server.common.msg.TbActorMsg; + +public interface TbActor { + + boolean process(TbActorMsg msg); + + TbActorRef getActorRef(); + + default void init(TbActorCtx ctx) throws TbActorException { + } + + default void destroy() throws TbActorException { + } + + default InitFailureStrategy onInitFailure(int attempt, Throwable t) { + return InitFailureStrategy.retryWithDelay(5000 * attempt); + } + + default ProcessFailureStrategy onProcessFailure(Throwable t) { + if (t instanceof Error) { + return ProcessFailureStrategy.stop(); + } else { + return ProcessFailureStrategy.resume(); + } + } +} diff --git a/common/actor/src/main/java/org/thingsboard/server/actors/TbActorCreator.java b/common/actor/src/main/java/org/thingsboard/server/actors/TbActorCreator.java new file mode 100644 index 0000000000..f62bf17d53 --- /dev/null +++ b/common/actor/src/main/java/org/thingsboard/server/actors/TbActorCreator.java @@ -0,0 +1,24 @@ +/** + * Copyright © 2016-2020 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.actors; + +public interface TbActorCreator { + + TbActorId createActorId(); + + TbActor createActor(); + +} diff --git a/common/actor/src/main/java/org/thingsboard/server/actors/TbActorCtx.java b/common/actor/src/main/java/org/thingsboard/server/actors/TbActorCtx.java new file mode 100644 index 0000000000..d21eea1a57 --- /dev/null +++ b/common/actor/src/main/java/org/thingsboard/server/actors/TbActorCtx.java @@ -0,0 +1,41 @@ +/** + * Copyright © 2016-2020 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.actors; + +import org.thingsboard.server.common.msg.TbActorMsg; + +import java.util.List; +import java.util.function.Predicate; +import java.util.function.Supplier; + +public interface TbActorCtx extends TbActorRef { + + TbActorId getSelf(); + + TbActorRef getParentRef(); + + void tell(TbActorId target, TbActorMsg msg); + + void stop(TbActorId target); + + TbActorRef getOrCreateChildActor(TbActorId actorId, Supplier dispatcher, Supplier creator); + + void broadcastToChildren(TbActorMsg msg); + + void broadcastToChildren(TbActorMsg msg, Predicate childFilter); + + List filterChildren(Predicate childFilter); +} diff --git a/common/actor/src/main/java/org/thingsboard/server/actors/TbActorException.java b/common/actor/src/main/java/org/thingsboard/server/actors/TbActorException.java new file mode 100644 index 0000000000..c62fb5ac22 --- /dev/null +++ b/common/actor/src/main/java/org/thingsboard/server/actors/TbActorException.java @@ -0,0 +1,23 @@ +/** + * Copyright © 2016-2020 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.actors; + +public class TbActorException extends Exception { + + public TbActorException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/common/actor/src/main/java/org/thingsboard/server/actors/TbActorId.java b/common/actor/src/main/java/org/thingsboard/server/actors/TbActorId.java new file mode 100644 index 0000000000..773ee2ddfb --- /dev/null +++ b/common/actor/src/main/java/org/thingsboard/server/actors/TbActorId.java @@ -0,0 +1,20 @@ +/** + * Copyright © 2016-2020 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.actors; + +public interface TbActorId { + +} diff --git a/common/actor/src/main/java/org/thingsboard/server/actors/TbActorMailbox.java b/common/actor/src/main/java/org/thingsboard/server/actors/TbActorMailbox.java new file mode 100644 index 0000000000..a0dd7482de --- /dev/null +++ b/common/actor/src/main/java/org/thingsboard/server/actors/TbActorMailbox.java @@ -0,0 +1,209 @@ +/** + * Copyright © 2016-2020 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.actors; + +import lombok.Data; +import lombok.extern.slf4j.Slf4j; +import org.thingsboard.server.common.msg.TbActorMsg; + +import java.util.List; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Predicate; +import java.util.function.Supplier; + +@Slf4j +@Data +public final class TbActorMailbox implements TbActorCtx { + private static final boolean HIGH_PRIORITY = true; + private static final boolean NORMAL_PRIORITY = false; + + private static final boolean FREE = false; + private static final boolean BUSY = true; + + private static final boolean NOT_READY = false; + private static final boolean READY = true; + + private final TbActorSystem system; + private final TbActorSystemSettings settings; + private final TbActorId selfId; + private final TbActorRef parentRef; + private final TbActor actor; + private final Dispatcher dispatcher; + private final ConcurrentLinkedQueue highPriorityMsgs = new ConcurrentLinkedQueue<>(); + private final ConcurrentLinkedQueue normalPriorityMsgs = new ConcurrentLinkedQueue<>(); + private final AtomicBoolean busy = new AtomicBoolean(FREE); + private final AtomicBoolean ready = new AtomicBoolean(NOT_READY); + private final AtomicBoolean destroyInProgress = new AtomicBoolean(); + + public void initActor() { + dispatcher.getExecutor().execute(() -> tryInit(1)); + } + + private void tryInit(int attempt) { + try { + log.debug("[{}] Trying to init actor, attempt: {}", selfId, attempt); + if (!destroyInProgress.get()) { + actor.init(this); + if (!destroyInProgress.get()) { + ready.set(READY); + tryProcessQueue(false); + } + } + } catch (Throwable t) { + log.debug("[{}] Failed to init actor, attempt: {}", selfId, attempt, t); + int attemptIdx = attempt + 1; + InitFailureStrategy strategy = actor.onInitFailure(attempt, t); + if (strategy.isStop() || (settings.getMaxActorInitAttempts() > 0 && attemptIdx > settings.getMaxActorInitAttempts())) { + log.info("[{}] Failed to init actor, attempt {}, going to stop attempts.", selfId, attempt, t); + system.stop(selfId); + } else if (strategy.getRetryDelay() > 0) { + log.info("[{}] Failed to init actor, attempt {}, going to retry in attempts in {}ms", selfId, attempt, strategy.getRetryDelay()); + log.debug("[{}] Error", selfId, t); + system.getScheduler().schedule(() -> dispatcher.getExecutor().execute(() -> tryInit(attemptIdx)), strategy.getRetryDelay(), TimeUnit.MILLISECONDS); + } else { + log.info("[{}] Failed to init actor, attempt {}, going to retry immediately", selfId, attempt); + log.debug("[{}] Error", selfId, t); + dispatcher.getExecutor().execute(() -> tryInit(attemptIdx)); + } + } + } + + private void enqueue(TbActorMsg msg, boolean highPriority) { + if (highPriority) { + highPriorityMsgs.add(msg); + } else { + normalPriorityMsgs.add(msg); + } + tryProcessQueue(true); + } + + private void tryProcessQueue(boolean newMsg) { + if (ready.get() == READY) { + if (newMsg || !highPriorityMsgs.isEmpty() || !normalPriorityMsgs.isEmpty()) { + if (busy.compareAndSet(FREE, BUSY)) { + dispatcher.getExecutor().execute(this::processMailbox); + } else { + log.trace("[{}] MessageBox is busy, new msg: {}", selfId, newMsg); + } + } else { + log.trace("[{}] MessageBox is empty, new msg: {}", selfId, newMsg); + } + } else { + log.trace("[{}] MessageBox is not ready, new msg: {}", selfId, newMsg); + } + } + + private void processMailbox() { + boolean noMoreElements = false; + for (int i = 0; i < settings.getActorThroughput(); i++) { + TbActorMsg msg = highPriorityMsgs.poll(); + if (msg == null) { + msg = normalPriorityMsgs.poll(); + } + if (msg != null) { + try { + log.debug("[{}] Going to process message: {}", selfId, msg); + actor.process(msg); + } catch (Throwable t) { + log.debug("[{}] Failed to process message: {}", selfId, msg, t); + ProcessFailureStrategy strategy = actor.onProcessFailure(t); + if (strategy.isStop()) { + system.stop(selfId); + } + } + } else { + noMoreElements = true; + break; + } + } + if (noMoreElements) { + busy.set(FREE); + dispatcher.getExecutor().execute(() -> tryProcessQueue(false)); + } else { + dispatcher.getExecutor().execute(this::processMailbox); + } + } + + @Override + public TbActorId getSelf() { + return selfId; + } + + @Override + public void tell(TbActorId target, TbActorMsg actorMsg) { + system.tell(target, actorMsg); + } + + @Override + public void broadcastToChildren(TbActorMsg msg) { + system.broadcastToChildren(selfId, msg); + } + + @Override + public void broadcastToChildren(TbActorMsg msg, Predicate childFilter) { + system.broadcastToChildren(selfId, childFilter, msg); + } + + @Override + public List filterChildren(Predicate childFilter) { + return system.filterChildren(selfId, childFilter); + } + + @Override + public void stop(TbActorId target) { + system.stop(target); + } + + @Override + public TbActorRef getOrCreateChildActor(TbActorId actorId, Supplier dispatcher, Supplier creator) { + TbActorRef actorRef = system.getActor(actorId); + if (actorRef == null) { + return system.createChildActor(dispatcher.get(), creator.get(), selfId); + } else { + return actorRef; + } + } + + public void destroy() { + destroyInProgress.set(true); + dispatcher.getExecutor().execute(() -> { + try { + ready.set(NOT_READY); + actor.destroy(); + } catch (Throwable t) { + log.warn("[{}] Failed to destroy actor: {}", selfId, t); + } + }); + } + + @Override + public TbActorId getActorId() { + return selfId; + } + + @Override + public void tell(TbActorMsg actorMsg) { + enqueue(actorMsg, NORMAL_PRIORITY); + } + + @Override + public void tellWithHighPriority(TbActorMsg actorMsg) { + enqueue(actorMsg, HIGH_PRIORITY); + } + +} diff --git a/common/actor/src/main/java/org/thingsboard/server/actors/TbActorNotRegisteredException.java b/common/actor/src/main/java/org/thingsboard/server/actors/TbActorNotRegisteredException.java new file mode 100644 index 0000000000..1f30b29c1b --- /dev/null +++ b/common/actor/src/main/java/org/thingsboard/server/actors/TbActorNotRegisteredException.java @@ -0,0 +1,29 @@ +/** + * Copyright © 2016-2020 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.actors; + +import lombok.Getter; + +public class TbActorNotRegisteredException extends RuntimeException { + + @Getter + private TbActorId target; + + public TbActorNotRegisteredException(TbActorId target, String message) { + super(message); + this.target = target; + } +} diff --git a/common/actor/src/main/java/org/thingsboard/server/actors/TbActorRef.java b/common/actor/src/main/java/org/thingsboard/server/actors/TbActorRef.java new file mode 100644 index 0000000000..5866021bd0 --- /dev/null +++ b/common/actor/src/main/java/org/thingsboard/server/actors/TbActorRef.java @@ -0,0 +1,28 @@ +/** + * Copyright © 2016-2020 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.actors; + +import org.thingsboard.server.common.msg.TbActorMsg; + +public interface TbActorRef { + + TbActorId getActorId(); + + void tell(TbActorMsg actorMsg); + + void tellWithHighPriority(TbActorMsg actorMsg); + +} diff --git a/common/actor/src/main/java/org/thingsboard/server/actors/TbActorSystem.java b/common/actor/src/main/java/org/thingsboard/server/actors/TbActorSystem.java new file mode 100644 index 0000000000..89a5e3744e --- /dev/null +++ b/common/actor/src/main/java/org/thingsboard/server/actors/TbActorSystem.java @@ -0,0 +1,54 @@ +/** + * Copyright © 2016-2020 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.actors; + +import org.thingsboard.server.common.msg.TbActorMsg; + +import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.ScheduledExecutorService; +import java.util.function.Predicate; + +public interface TbActorSystem { + + ScheduledExecutorService getScheduler(); + + void createDispatcher(String dispatcherId, ExecutorService executor); + + void destroyDispatcher(String dispatcherId); + + TbActorRef getActor(TbActorId actorId); + + TbActorRef createRootActor(String dispatcherId, TbActorCreator creator); + + TbActorRef createChildActor(String dispatcherId, TbActorCreator creator, TbActorId parent); + + void tell(TbActorId target, TbActorMsg actorMsg); + + void tellWithHighPriority(TbActorId target, TbActorMsg actorMsg); + + void stop(TbActorRef actorRef); + + void stop(TbActorId actorId); + + void stop(); + + void broadcastToChildren(TbActorId parent, TbActorMsg msg); + + void broadcastToChildren(TbActorId parent, Predicate childFilter, TbActorMsg msg); + + List filterChildren(TbActorId parent, Predicate childFilter); +} diff --git a/common/actor/src/main/java/org/thingsboard/server/actors/TbActorSystemSettings.java b/common/actor/src/main/java/org/thingsboard/server/actors/TbActorSystemSettings.java new file mode 100644 index 0000000000..51798611f2 --- /dev/null +++ b/common/actor/src/main/java/org/thingsboard/server/actors/TbActorSystemSettings.java @@ -0,0 +1,27 @@ +/** + * Copyright © 2016-2020 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.actors; + +import lombok.Data; + +@Data +public class TbActorSystemSettings { + + private final int actorThroughput; + private final int schedulerPoolSize; + private final int maxActorInitAttempts; + +} diff --git a/common/actor/src/main/java/org/thingsboard/server/actors/TbEntityActorId.java b/common/actor/src/main/java/org/thingsboard/server/actors/TbEntityActorId.java new file mode 100644 index 0000000000..3a3e501c56 --- /dev/null +++ b/common/actor/src/main/java/org/thingsboard/server/actors/TbEntityActorId.java @@ -0,0 +1,49 @@ +/** + * Copyright © 2016-2020 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.actors; + +import lombok.Getter; +import org.thingsboard.server.common.data.id.EntityId; + +import java.util.Objects; + +public class TbEntityActorId implements TbActorId { + + @Getter + private final EntityId entityId; + + public TbEntityActorId(EntityId entityId) { + this.entityId = entityId; + } + + @Override + public String toString() { + return entityId.getEntityType() + "|" + entityId.getId(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + TbEntityActorId that = (TbEntityActorId) o; + return entityId.equals(that.entityId); + } + + @Override + public int hashCode() { + return Objects.hash(entityId); + } +} diff --git a/common/actor/src/main/java/org/thingsboard/server/actors/TbStringActorId.java b/common/actor/src/main/java/org/thingsboard/server/actors/TbStringActorId.java new file mode 100644 index 0000000000..2c2f3e2ba1 --- /dev/null +++ b/common/actor/src/main/java/org/thingsboard/server/actors/TbStringActorId.java @@ -0,0 +1,45 @@ +/** + * Copyright © 2016-2020 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.actors; + +import java.util.Objects; + +public class TbStringActorId implements TbActorId { + + private final String id; + + public TbStringActorId(String id) { + this.id = id; + } + + @Override + public String toString() { + return id; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + TbStringActorId that = (TbStringActorId) o; + return id.equals(that.id); + } + + @Override + public int hashCode() { + return Objects.hash(id); + } +} diff --git a/common/actor/src/test/java/org/thingsboard/server/actors/ActorSystemTest.java b/common/actor/src/test/java/org/thingsboard/server/actors/ActorSystemTest.java new file mode 100644 index 0000000000..5e0a9af548 --- /dev/null +++ b/common/actor/src/test/java/org/thingsboard/server/actors/ActorSystemTest.java @@ -0,0 +1,224 @@ +/** + * Copyright © 2016-2020 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.actors; + +import lombok.extern.slf4j.Slf4j; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.runners.MockitoJUnitRunner; +import org.thingsboard.server.common.data.id.DeviceId; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; +import java.util.UUID; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; + +@Slf4j +@RunWith(MockitoJUnitRunner.class) +public class ActorSystemTest { + + public static final String ROOT_DISPATCHER = "root-dispatcher"; + private static final int _100K = 100 * 1024; + + private volatile TbActorSystem actorSystem; + private volatile ExecutorService submitPool; + private int parallelism; + + @Before + public void initActorSystem() { + int cores = Runtime.getRuntime().availableProcessors(); + parallelism = Math.max(2, cores / 2); + TbActorSystemSettings settings = new TbActorSystemSettings(5, parallelism, 42); + actorSystem = new DefaultTbActorSystem(settings); + submitPool = Executors.newWorkStealingPool(parallelism); + } + + @After + public void shutdownActorSystem() { + actorSystem.stop(); + submitPool.shutdownNow(); + } + + @Test + public void test1actorsAnd100KMessages() throws InterruptedException { + actorSystem.createDispatcher(ROOT_DISPATCHER, Executors.newWorkStealingPool(parallelism)); + testActorsAndMessages(1, _100K, 1); + } + + @Test + public void test10actorsAnd100KMessages() throws InterruptedException { + actorSystem.createDispatcher(ROOT_DISPATCHER, Executors.newWorkStealingPool(parallelism)); + testActorsAndMessages(10, _100K, 1); + } + + @Test + public void test100KActorsAnd1Messages5timesSingleThread() throws InterruptedException { + actorSystem.createDispatcher(ROOT_DISPATCHER, Executors.newSingleThreadExecutor()); + testActorsAndMessages(_100K, 1, 5); + } + + @Test + public void test100KActorsAnd1Messages5times() throws InterruptedException { + actorSystem.createDispatcher(ROOT_DISPATCHER, Executors.newWorkStealingPool(parallelism)); + testActorsAndMessages(_100K, 1, 5); + } + + @Test + public void test100KActorsAnd10Messages() throws InterruptedException { + actorSystem.createDispatcher(ROOT_DISPATCHER, Executors.newWorkStealingPool(parallelism)); + testActorsAndMessages(_100K, 10, 1); + } + + @Test + public void test1KActorsAnd1KMessages() throws InterruptedException { + actorSystem.createDispatcher(ROOT_DISPATCHER, Executors.newWorkStealingPool(parallelism)); + testActorsAndMessages(1000, 1000, 10); + } + + @Test + public void testNoMessagesAfterDestroy() throws InterruptedException { + actorSystem.createDispatcher(ROOT_DISPATCHER, Executors.newWorkStealingPool(parallelism)); + ActorTestCtx testCtx1 = getActorTestCtx(1); + ActorTestCtx testCtx2 = getActorTestCtx(1); + + TbActorRef actorId1 = actorSystem.createRootActor(ROOT_DISPATCHER, new SlowInitActor.SlowInitActorCreator( + new TbEntityActorId(new DeviceId(UUID.randomUUID())), testCtx1)); + TbActorRef actorId2 = actorSystem.createRootActor(ROOT_DISPATCHER, new SlowInitActor.SlowInitActorCreator( + new TbEntityActorId(new DeviceId(UUID.randomUUID())), testCtx2)); + + actorId1.tell(new IntTbActorMsg(42)); + actorId2.tell(new IntTbActorMsg(42)); + actorSystem.stop(actorId1); + + Assert.assertTrue(testCtx2.getLatch().await(1, TimeUnit.SECONDS)); + Assert.assertFalse(testCtx1.getLatch().await(1, TimeUnit.SECONDS)); + } + + @Test + public void testOneActorCreated() throws InterruptedException { + actorSystem.createDispatcher(ROOT_DISPATCHER, Executors.newWorkStealingPool(parallelism)); + ActorTestCtx testCtx1 = getActorTestCtx(1); + ActorTestCtx testCtx2 = getActorTestCtx(1); + TbActorId actorId = new TbEntityActorId(new DeviceId(UUID.randomUUID())); + submitPool.submit(() -> actorSystem.createRootActor(ROOT_DISPATCHER, new SlowCreateActor.SlowCreateActorCreator(actorId, testCtx1))); + submitPool.submit(() -> actorSystem.createRootActor(ROOT_DISPATCHER, new SlowCreateActor.SlowCreateActorCreator(actorId, testCtx2))); + + Thread.sleep(1000); + actorSystem.tell(actorId, new IntTbActorMsg(42)); + + Assert.assertTrue(testCtx1.getLatch().await(1, TimeUnit.SECONDS)); + Assert.assertFalse(testCtx2.getLatch().await(1, TimeUnit.SECONDS)); + } + + @Test + public void testActorCreatorCalledOnce() throws InterruptedException { + actorSystem.createDispatcher(ROOT_DISPATCHER, Executors.newWorkStealingPool(parallelism)); + ActorTestCtx testCtx = getActorTestCtx(1); + TbActorId actorId = new TbEntityActorId(new DeviceId(UUID.randomUUID())); + for (int i = 0; i < 1000; i++) { + submitPool.submit(() -> actorSystem.createRootActor(ROOT_DISPATCHER, new SlowCreateActor.SlowCreateActorCreator(actorId, testCtx))); + } + Thread.sleep(1000); + actorSystem.tell(actorId, new IntTbActorMsg(42)); + + Assert.assertTrue(testCtx.getLatch().await(1, TimeUnit.SECONDS)); + //One for creation and one for message + Assert.assertEquals(2, testCtx.getInvocationCount().get()); + } + + @Test + public void testFailedInit() throws InterruptedException { + actorSystem.createDispatcher(ROOT_DISPATCHER, Executors.newWorkStealingPool(parallelism)); + ActorTestCtx testCtx1 = getActorTestCtx(1); + ActorTestCtx testCtx2 = getActorTestCtx(1); + + TbActorRef actorId1 = actorSystem.createRootActor(ROOT_DISPATCHER, new FailedToInitActor.FailedToInitActorCreator( + new TbEntityActorId(new DeviceId(UUID.randomUUID())), testCtx1, 1, 3000)); + TbActorRef actorId2 = actorSystem.createRootActor(ROOT_DISPATCHER, new FailedToInitActor.FailedToInitActorCreator( + new TbEntityActorId(new DeviceId(UUID.randomUUID())), testCtx2, 2, 1)); + + actorId1.tell(new IntTbActorMsg(42)); + actorId2.tell(new IntTbActorMsg(42)); + + Assert.assertFalse(testCtx1.getLatch().await(2, TimeUnit.SECONDS)); + Assert.assertTrue(testCtx2.getLatch().await(1, TimeUnit.SECONDS)); + Assert.assertTrue(testCtx1.getLatch().await(3, TimeUnit.SECONDS)); + } + + + public void testActorsAndMessages(int actorsCount, int msgNumber, int times) throws InterruptedException { + Random random = new Random(); + int[] randomIntegers = new int[msgNumber]; + long sumTmp = 0; + for (int i = 0; i < msgNumber; i++) { + int tmp = random.nextInt(); + randomIntegers[i] = tmp; + sumTmp += tmp; + } + long expected = sumTmp; + + List testCtxes = new ArrayList<>(); + + List actorRefs = new ArrayList<>(); + for (int actorIdx = 0; actorIdx < actorsCount; actorIdx++) { + ActorTestCtx testCtx = getActorTestCtx(msgNumber); + actorRefs.add(actorSystem.createRootActor(ROOT_DISPATCHER, new TestRootActor.TestRootActorCreator( + new TbEntityActorId(new DeviceId(UUID.randomUUID())), testCtx))); + testCtxes.add(testCtx); + } + + for (int t = 0; t < times; t++) { + long start = System.nanoTime(); + for (int i = 0; i < msgNumber; i++) { + int tmp = randomIntegers[i]; + submitPool.execute(() -> actorRefs.forEach(actorId -> actorId.tell(new IntTbActorMsg(tmp)))); + } + log.info("Submitted all messages"); + testCtxes.forEach(ctx -> { + try { + boolean success = ctx.getLatch().await(1, TimeUnit.MINUTES); + if (!success) { + log.warn("Failed: {}, {}", ctx.getActual().get(), ctx.getInvocationCount().get()); + } + Assert.assertTrue(success); + Assert.assertEquals(expected, ctx.getActual().get()); + Assert.assertEquals(msgNumber, ctx.getInvocationCount().get()); + ctx.clear(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + }); + long duration = System.nanoTime() - start; + log.info("Time spend: {}ns ({} ms)", duration, TimeUnit.NANOSECONDS.toMillis(duration)); + } + } + + private ActorTestCtx getActorTestCtx(int i) { + CountDownLatch countDownLatch = new CountDownLatch(1); + AtomicLong actual = new AtomicLong(); + AtomicInteger invocations = new AtomicInteger(); + return new ActorTestCtx(countDownLatch, invocations, i, actual); + } +} diff --git a/common/actor/src/test/java/org/thingsboard/server/actors/ActorTestCtx.java b/common/actor/src/test/java/org/thingsboard/server/actors/ActorTestCtx.java new file mode 100644 index 0000000000..69d783b6cb --- /dev/null +++ b/common/actor/src/test/java/org/thingsboard/server/actors/ActorTestCtx.java @@ -0,0 +1,39 @@ +/** + * Copyright © 2016-2020 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.actors; + +import lombok.AllArgsConstructor; +import lombok.Data; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; + +@Data +@AllArgsConstructor +public class ActorTestCtx { + + private volatile CountDownLatch latch; + private final AtomicInteger invocationCount; + private final int expectedInvocationCount; + private final AtomicLong actual; + + public void clear() { + latch = new CountDownLatch(1); + invocationCount.set(0); + actual.set(0L); + } +} diff --git a/common/actor/src/test/java/org/thingsboard/server/actors/FailedToInitActor.java b/common/actor/src/test/java/org/thingsboard/server/actors/FailedToInitActor.java new file mode 100644 index 0000000000..509c3e1af8 --- /dev/null +++ b/common/actor/src/test/java/org/thingsboard/server/actors/FailedToInitActor.java @@ -0,0 +1,72 @@ +/** + * Copyright © 2016-2020 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.actors; + +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class FailedToInitActor extends TestRootActor { + + int retryAttempts; + int retryDelay; + int attempts = 0; + + public FailedToInitActor(TbActorId actorId, ActorTestCtx testCtx, int retryAttempts, int retryDelay) { + super(actorId, testCtx); + this.retryAttempts = retryAttempts; + this.retryDelay = retryDelay; + } + + @Override + public void init(TbActorCtx ctx) throws TbActorException { + if (attempts < retryAttempts) { + attempts++; + throw new TbActorException("Test attempt", new RuntimeException()); + } else { + super.init(ctx); + } + } + + @Override + public InitFailureStrategy onInitFailure(int attempt, Throwable t) { + return InitFailureStrategy.retryWithDelay(retryDelay); + } + + public static class FailedToInitActorCreator implements TbActorCreator { + + private final TbActorId actorId; + private final ActorTestCtx testCtx; + private final int retryAttempts; + private final int retryDelay; + + public FailedToInitActorCreator(TbActorId actorId, ActorTestCtx testCtx, int retryAttempts, int retryDelay) { + this.actorId = actorId; + this.testCtx = testCtx; + this.retryAttempts = retryAttempts; + this.retryDelay = retryDelay; + } + + @Override + public TbActorId createActorId() { + return actorId; + } + + @Override + public TbActor createActor() { + return new FailedToInitActor(actorId, testCtx, retryAttempts, retryDelay); + } + } +} diff --git a/common/actor/src/test/java/org/thingsboard/server/actors/IntTbActorMsg.java b/common/actor/src/test/java/org/thingsboard/server/actors/IntTbActorMsg.java new file mode 100644 index 0000000000..b3591d9642 --- /dev/null +++ b/common/actor/src/test/java/org/thingsboard/server/actors/IntTbActorMsg.java @@ -0,0 +1,35 @@ +/** + * Copyright © 2016-2020 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.actors; + +import lombok.Getter; +import org.thingsboard.server.common.msg.MsgType; +import org.thingsboard.server.common.msg.TbActorMsg; + +public class IntTbActorMsg implements TbActorMsg { + + @Getter + private final int value; + + public IntTbActorMsg(int value) { + this.value = value; + } + + @Override + public MsgType getMsgType() { + return MsgType.QUEUE_TO_RULE_ENGINE_MSG; + } +} diff --git a/common/actor/src/test/java/org/thingsboard/server/actors/SlowCreateActor.java b/common/actor/src/test/java/org/thingsboard/server/actors/SlowCreateActor.java new file mode 100644 index 0000000000..af1dfa6b45 --- /dev/null +++ b/common/actor/src/test/java/org/thingsboard/server/actors/SlowCreateActor.java @@ -0,0 +1,53 @@ +/** + * Copyright © 2016-2020 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.actors; + +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class SlowCreateActor extends TestRootActor { + + public SlowCreateActor(TbActorId actorId, ActorTestCtx testCtx) { + super(actorId, testCtx); + try { + Thread.sleep(500); + } catch (InterruptedException e) { + e.printStackTrace(); + } + testCtx.getInvocationCount().incrementAndGet(); + } + + public static class SlowCreateActorCreator implements TbActorCreator { + + private final TbActorId actorId; + private final ActorTestCtx testCtx; + + public SlowCreateActorCreator(TbActorId actorId, ActorTestCtx testCtx) { + this.actorId = actorId; + this.testCtx = testCtx; + } + + @Override + public TbActorId createActorId() { + return actorId; + } + + @Override + public TbActor createActor() { + return new SlowCreateActor(actorId, testCtx); + } + } +} diff --git a/common/actor/src/test/java/org/thingsboard/server/actors/SlowInitActor.java b/common/actor/src/test/java/org/thingsboard/server/actors/SlowInitActor.java new file mode 100644 index 0000000000..1b59b164ff --- /dev/null +++ b/common/actor/src/test/java/org/thingsboard/server/actors/SlowInitActor.java @@ -0,0 +1,57 @@ +/** + * Copyright © 2016-2020 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.actors; + +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class SlowInitActor extends TestRootActor { + + public SlowInitActor(TbActorId actorId, ActorTestCtx testCtx) { + super(actorId, testCtx); + } + + @Override + public void init(TbActorCtx ctx) throws TbActorException { + try { + Thread.sleep(500); + } catch (InterruptedException e) { + e.printStackTrace(); + } + super.init(ctx); + } + + public static class SlowInitActorCreator implements TbActorCreator { + + private final TbActorId actorId; + private final ActorTestCtx testCtx; + + public SlowInitActorCreator(TbActorId actorId, ActorTestCtx testCtx) { + this.actorId = actorId; + this.testCtx = testCtx; + } + + @Override + public TbActorId createActorId() { + return actorId; + } + + @Override + public TbActor createActor() { + return new SlowInitActor(actorId, testCtx); + } + } +} diff --git a/common/actor/src/test/java/org/thingsboard/server/actors/TestRootActor.java b/common/actor/src/test/java/org/thingsboard/server/actors/TestRootActor.java new file mode 100644 index 0000000000..d4d56a0de5 --- /dev/null +++ b/common/actor/src/test/java/org/thingsboard/server/actors/TestRootActor.java @@ -0,0 +1,87 @@ +/** + * Copyright © 2016-2020 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.actors; + +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import org.thingsboard.server.common.msg.TbActorMsg; + +@Slf4j +public class TestRootActor extends AbstractTbActor { + + @Getter + private final TbActorId actorId; + @Getter + private final ActorTestCtx testCtx; + + private boolean initialized; + private long sum; + private int count; + + public TestRootActor(TbActorId actorId, ActorTestCtx testCtx) { + this.actorId = actorId; + this.testCtx = testCtx; + } + + @Override + public void init(TbActorCtx ctx) throws TbActorException { + super.init(ctx); + initialized = true; + } + + @Override + public boolean process(TbActorMsg msg) { + if (initialized) { + int value = ((IntTbActorMsg) msg).getValue(); + sum += value; + count += 1; + if (count == testCtx.getExpectedInvocationCount()) { + testCtx.getActual().set(sum); + testCtx.getInvocationCount().addAndGet(count); + sum = 0; + count = 0; + testCtx.getLatch().countDown(); + } + } + return true; + } + + @Override + public void destroy() { + + } + + public static class TestRootActorCreator implements TbActorCreator { + + private final TbActorId actorId; + private final ActorTestCtx testCtx; + + public TestRootActorCreator(TbActorId actorId, ActorTestCtx testCtx) { + this.actorId = actorId; + this.testCtx = testCtx; + } + + @Override + public TbActorId createActorId() { + return actorId; + } + + @Override + public TbActor createActor() { + return new TestRootActor(actorId, testCtx); + } + } +} diff --git a/common/actor/src/test/resources/logback.xml b/common/actor/src/test/resources/logback.xml new file mode 100644 index 0000000000..52f2a77588 --- /dev/null +++ b/common/actor/src/test/resources/logback.xml @@ -0,0 +1,14 @@ + + + + + + %d{ISO8601} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + diff --git a/common/dao-api/pom.xml b/common/dao-api/pom.xml index 1c9417b763..b880c42bfd 100644 --- a/common/dao-api/pom.xml +++ b/common/dao-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.5.1-SNAPSHOT + 2.5.3-SNAPSHOT common org.thingsboard.common diff --git a/common/data/pom.xml b/common/data/pom.xml index 2f89107d95..96892bdc59 100644 --- a/common/data/pom.xml +++ b/common/data/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.5.1-SNAPSHOT + 2.5.3-SNAPSHOT common org.thingsboard.common diff --git a/common/edge-api/pom.xml b/common/edge-api/pom.xml index 53b7d62472..60ea1a695b 100644 --- a/common/edge-api/pom.xml +++ b/common/edge-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.5.1-SNAPSHOT + 2.5.3-SNAPSHOT common org.thingsboard.common diff --git a/common/message/pom.xml b/common/message/pom.xml index 3241e98efc..066beec7a1 100644 --- a/common/message/pom.xml +++ b/common/message/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.5.1-SNAPSHOT + 2.5.3-SNAPSHOT common org.thingsboard.common diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/MsgType.java b/common/message/src/main/java/org/thingsboard/server/common/msg/MsgType.java index 0345355aa3..d7f0df9838 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/MsgType.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/MsgType.java @@ -21,7 +21,6 @@ import org.thingsboard.server.common.msg.queue.QueueToRuleEngineMsg; /** * Created by ashvayka on 15.03.18. */ -//TODO: add all "See" references public enum MsgType { /** @@ -97,6 +96,7 @@ public enum MsgType { STATS_PERSIST_TICK_MSG, + STATS_PERSIST_MSG, /** * Message that is sent by TransportRuleEngineService to Device Actor. Represents messages from the device itself. diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/TbMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/TbMsg.java index 845138d92c..1604875c7b 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/TbMsg.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/TbMsg.java @@ -25,6 +25,7 @@ import org.thingsboard.server.common.data.id.EntityIdFactory; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.RuleNodeId; import org.thingsboard.server.common.msg.gen.MsgProtos; +import org.thingsboard.server.common.msg.queue.ServiceQueue; import org.thingsboard.server.common.msg.queue.TbMsgCallback; import java.io.IOException; @@ -39,6 +40,7 @@ import java.util.UUID; @Slf4j public final class TbMsg implements Serializable { + private final String queueName; private final UUID id; private final long ts; private final String type; @@ -51,39 +53,44 @@ public final class TbMsg implements Serializable { //This field is not serialized because we use queues and there is no need to do it transient private final TbMsgCallback callback; + public static TbMsg newMsg(String queueName, String type, EntityId originator, TbMsgMetaData metaData, String data, RuleChainId ruleChainId, RuleNodeId ruleNodeId) { + return new TbMsg(queueName, UUID.randomUUID(), System.currentTimeMillis(), type, originator, metaData.copy(), TbMsgDataType.JSON, data, ruleChainId, ruleNodeId, TbMsgCallback.EMPTY); + } + public static TbMsg newMsg(String type, EntityId originator, TbMsgMetaData metaData, String data) { - return new TbMsg(UUID.randomUUID(), System.currentTimeMillis(), type, originator, metaData.copy(), TbMsgDataType.JSON, data, null, null, TbMsgCallback.EMPTY); + return new TbMsg(ServiceQueue.MAIN, UUID.randomUUID(), System.currentTimeMillis(), type, originator, metaData.copy(), TbMsgDataType.JSON, data, null, null, TbMsgCallback.EMPTY); } - public static TbMsg newMsg(String type, EntityId originator, TbMsgMetaData metaData, String data, RuleChainId ruleChainId, RuleNodeId ruleNodeId) { - return new TbMsg(UUID.randomUUID(), System.currentTimeMillis(), type, originator, metaData.copy(), TbMsgDataType.JSON, data, ruleChainId, ruleNodeId, TbMsgCallback.EMPTY); + public static TbMsg newMsg(String queueName, String type, EntityId originator, TbMsgMetaData metaData, String data) { + return new TbMsg(queueName, UUID.randomUUID(), System.currentTimeMillis(), type, originator, metaData.copy(), TbMsgDataType.JSON, data, null, null, TbMsgCallback.EMPTY); } public static TbMsg newMsg(String type, EntityId originator, TbMsgMetaData metaData, TbMsgDataType dataType, String data) { - return new TbMsg(UUID.randomUUID(), System.currentTimeMillis(), type, originator, metaData.copy(), dataType, data, null, null, TbMsgCallback.EMPTY); + return new TbMsg(ServiceQueue.MAIN, UUID.randomUUID(), System.currentTimeMillis(), type, originator, metaData.copy(), dataType, data, null, null, TbMsgCallback.EMPTY); } public static TbMsg newMsg(String type, EntityId originator, TbMsgMetaData metaData, TbMsgDataType dataType, String data, RuleChainId ruleChainId, RuleNodeId ruleNodeId) { - return new TbMsg(UUID.randomUUID(), System.currentTimeMillis(), type, originator, metaData.copy(), dataType, data, ruleChainId, ruleNodeId, TbMsgCallback.EMPTY); + return new TbMsg(ServiceQueue.MAIN, UUID.randomUUID(), System.currentTimeMillis(), type, originator, metaData.copy(), dataType, data, ruleChainId, ruleNodeId, TbMsgCallback.EMPTY); } public static TbMsg newMsg(String type, EntityId originator, TbMsgMetaData metaData, String data, TbMsgCallback callback) { - return new TbMsg(UUID.randomUUID(), System.currentTimeMillis(), type, originator, metaData.copy(), TbMsgDataType.JSON, data, null, null, callback); + return new TbMsg(ServiceQueue.MAIN, UUID.randomUUID(), System.currentTimeMillis(), type, originator, metaData.copy(), TbMsgDataType.JSON, data, null, null, callback); } public static TbMsg transformMsg(TbMsg origMsg, String type, EntityId originator, TbMsgMetaData metaData, String data) { - return new TbMsg(origMsg.getId(), origMsg.getTs(), type, originator, metaData.copy(), origMsg.getDataType(), + return new TbMsg(origMsg.getQueueName(), origMsg.getId(), origMsg.getTs(), type, originator, metaData.copy(), origMsg.getDataType(), data, origMsg.getRuleChainId(), origMsg.getRuleNodeId(), origMsg.getCallback()); } public static TbMsg newMsg(TbMsg tbMsg, RuleChainId ruleChainId, RuleNodeId ruleNodeId) { - return new TbMsg(UUID.randomUUID(), tbMsg.getTs(), tbMsg.getType(), tbMsg.getOriginator(), tbMsg.getMetaData().copy(), + return new TbMsg(tbMsg.getQueueName(), UUID.randomUUID(), tbMsg.getTs(), tbMsg.getType(), tbMsg.getOriginator(), tbMsg.getMetaData().copy(), tbMsg.getDataType(), tbMsg.getData(), ruleChainId, ruleNodeId, TbMsgCallback.EMPTY); } - private TbMsg(UUID id, long ts, String type, EntityId originator, TbMsgMetaData metaData, TbMsgDataType dataType, String data, + private TbMsg(String queueName, UUID id, long ts, String type, EntityId originator, TbMsgMetaData metaData, TbMsgDataType dataType, String data, RuleChainId ruleChainId, RuleNodeId ruleNodeId, TbMsgCallback callback) { this.id = id; + this.queueName = queueName; if (ts > 0) { this.ts = ts; } else { @@ -136,7 +143,7 @@ public final class TbMsg implements Serializable { return builder.build().toByteArray(); } - public static TbMsg fromBytes(byte[] data, TbMsgCallback callback) { + public static TbMsg fromBytes(String queueName, byte[] data, TbMsgCallback callback) { try { MsgProtos.TbMsgProto proto = MsgProtos.TbMsgProto.parseFrom(data); TbMsgMetaData metaData = new TbMsgMetaData(proto.getMetaData().getDataMap()); @@ -150,18 +157,18 @@ public final class TbMsg implements Serializable { ruleNodeId = new RuleNodeId(new UUID(proto.getRuleNodeIdMSB(), proto.getRuleNodeIdLSB())); } TbMsgDataType dataType = TbMsgDataType.values()[proto.getDataType()]; - return new TbMsg(UUID.fromString(proto.getId()), proto.getTs(), proto.getType(), entityId, metaData, dataType, proto.getData(), ruleChainId, ruleNodeId, callback); + return new TbMsg(queueName, UUID.fromString(proto.getId()), proto.getTs(), proto.getType(), entityId, metaData, dataType, proto.getData(), ruleChainId, ruleNodeId, callback); } catch (InvalidProtocolBufferException e) { throw new IllegalStateException("Could not parse protobuf for TbMsg", e); } } public TbMsg copyWithRuleChainId(RuleChainId ruleChainId) { - return new TbMsg(this.id, this.ts, this.type, this.originator, this.metaData, this.dataType, this.data, ruleChainId, null, callback); + return new TbMsg(this.queueName, this.id, this.ts, this.type, this.originator, this.metaData, this.dataType, this.data, ruleChainId, null, callback); } public TbMsg copyWithRuleNodeId(RuleChainId ruleChainId, RuleNodeId ruleNodeId) { - return new TbMsg(this.id, this.ts, this.type, this.originator, this.metaData, this.dataType, this.data, ruleChainId, ruleNodeId, callback); + return new TbMsg(this.queueName, this.id, this.ts, this.type, this.originator, this.metaData, this.dataType, this.data, ruleChainId, ruleNodeId, callback); } public TbMsgCallback getCallback() { @@ -172,4 +179,8 @@ public final class TbMsg implements Serializable { return TbMsgCallback.EMPTY; } } + + public String getQueueName() { + return queueName != null ? queueName : ServiceQueue.MAIN; + } } diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/aware/DeviceAwareMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/aware/DeviceAwareMsg.java index ec1028d239..c812711e18 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/aware/DeviceAwareMsg.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/aware/DeviceAwareMsg.java @@ -16,8 +16,9 @@ package org.thingsboard.server.common.msg.aware; import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.msg.TbActorMsg; -public interface DeviceAwareMsg { +public interface DeviceAwareMsg extends TbActorMsg { DeviceId getDeviceId(); } diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/aware/RuleChainAwareMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/aware/RuleChainAwareMsg.java index c9a94b5a3f..a6bacb3594 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/aware/RuleChainAwareMsg.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/aware/RuleChainAwareMsg.java @@ -16,8 +16,9 @@ package org.thingsboard.server.common.msg.aware; import org.thingsboard.server.common.data.id.RuleChainId; +import org.thingsboard.server.common.msg.TbActorMsg; -public interface RuleChainAwareMsg { +public interface RuleChainAwareMsg extends TbActorMsg { RuleChainId getRuleChainId(); diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/aware/TenantAwareMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/aware/TenantAwareMsg.java index fb1fbb821f..f0ccaa65d8 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/aware/TenantAwareMsg.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/aware/TenantAwareMsg.java @@ -16,8 +16,9 @@ package org.thingsboard.server.common.msg.aware; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.msg.TbActorMsg; -public interface TenantAwareMsg { +public interface TenantAwareMsg extends TbActorMsg { TenantId getTenantId(); diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/queue/ServiceQueue.java b/common/message/src/main/java/org/thingsboard/server/common/msg/queue/ServiceQueue.java index be08f054b6..12abdde8bf 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/queue/ServiceQueue.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/queue/ServiceQueue.java @@ -34,7 +34,7 @@ public class ServiceQueue { public ServiceQueue(ServiceType type, String queue) { this.type = type; - this.queue = queue; + this.queue = queue != null ? queue : MAIN; } public ServiceType getType() { diff --git a/common/pom.xml b/common/pom.xml index 47498b2a48..13af8b1716 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.5.1-SNAPSHOT + 2.5.3-SNAPSHOT thingsboard common @@ -37,6 +37,7 @@ data util message + actor queue transport dao-api diff --git a/common/queue/pom.xml b/common/queue/pom.xml index 838d032482..2072d34b40 100644 --- a/common/queue/pom.xml +++ b/common/queue/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.5.1-SNAPSHOT + 2.5.3-SNAPSHOT common org.thingsboard.common diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplate.java b/common/queue/src/main/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplate.java index f02ae63441..ae7760638f 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplate.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplate.java @@ -91,8 +91,6 @@ public class DefaultTbQueueRequestTemplate responses = responseTemplate.poll(pollInterval); if (responses.size() > 0) { log.trace("Polling responses completed, consumer records count [{}]", responses.size()); - } else { - continue; } responses.forEach(response -> { byte[] requestIdHeader = response.getHeaders().get(REQUEST_ID_HEADER); diff --git a/common/transport/coap/pom.xml b/common/transport/coap/pom.xml index fcbacf2bb5..5ec305329c 100644 --- a/common/transport/coap/pom.xml +++ b/common/transport/coap/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 2.5.1-SNAPSHOT + 2.5.3-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/transport/http/pom.xml b/common/transport/http/pom.xml index 0a8479688d..4089f3c0d8 100644 --- a/common/transport/http/pom.xml +++ b/common/transport/http/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 2.5.1-SNAPSHOT + 2.5.3-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/transport/mqtt/pom.xml b/common/transport/mqtt/pom.xml index 68a1e55c0d..7a3e81348c 100644 --- a/common/transport/mqtt/pom.xml +++ b/common/transport/mqtt/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 2.5.1-SNAPSHOT + 2.5.3-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java index ac19e72037..d62ab6a7ee 100644 --- a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java +++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java @@ -521,11 +521,22 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement .setDeviceName(msg.getDeviceInfo().getDeviceName()) .setDeviceType(msg.getDeviceInfo().getDeviceType()) .build(); - transportService.process(sessionInfo, DefaultTransportService.getSessionEventMsg(SessionEvent.OPEN), null); - transportService.registerAsyncSession(sessionInfo, this); - checkGatewaySession(); - ctx.writeAndFlush(createMqttConnAckMsg(CONNECTION_ACCEPTED)); - log.info("[{}] Client connected!", sessionId); + transportService.process(sessionInfo, DefaultTransportService.getSessionEventMsg(SessionEvent.OPEN), new TransportServiceCallback() { + @Override + public void onSuccess(Void msg) { + transportService.registerAsyncSession(sessionInfo, MqttTransportHandler.this); + checkGatewaySession(); + ctx.writeAndFlush(createMqttConnAckMsg(CONNECTION_ACCEPTED)); + log.info("[{}] Client connected!", sessionId); + } + + @Override + public void onError(Throwable e) { + log.warn("[{}] Failed to submit session event", sessionId, e); + ctx.writeAndFlush(createMqttConnAckMsg(MqttConnectReturnCode.CONNECTION_REFUSED_SERVER_UNAVAILABLE)); + ctx.close(); + } + }); } } diff --git a/common/transport/pom.xml b/common/transport/pom.xml index 121301f5bf..3dfb3ee695 100644 --- a/common/transport/pom.xml +++ b/common/transport/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.5.1-SNAPSHOT + 2.5.3-SNAPSHOT common org.thingsboard.common diff --git a/common/transport/transport-api/pom.xml b/common/transport/transport-api/pom.xml index a0b73855d3..2724c0eb83 100644 --- a/common/transport/transport-api/pom.xml +++ b/common/transport/transport-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 2.5.1-SNAPSHOT + 2.5.3-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/util/pom.xml b/common/util/pom.xml index 4f0fa51066..25b8925b9a 100644 --- a/common/util/pom.xml +++ b/common/util/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.5.1-SNAPSHOT + 2.5.3-SNAPSHOT common org.thingsboard.common diff --git a/dao/pom.xml b/dao/pom.xml index 0b7643ddb2..86139125cb 100644 --- a/dao/pom.xml +++ b/dao/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.5.1-SNAPSHOT + 2.5.3-SNAPSHOT thingsboard dao @@ -91,26 +91,26 @@ mockito-all test - - org.apache.commons - commons-lang3 - - - commons-validator - commons-validator - - - com.fasterxml.jackson.core - jackson-databind - + + org.apache.commons + commons-lang3 + + + commons-collections + commons-collections + + + com.fasterxml.jackson.core + jackson-databind + org.springframework spring-context - - org.springframework - spring-tx - + + org.springframework + spring-tx + org.springframework spring-web @@ -120,24 +120,24 @@ org.springframework.security spring-security-oauth2-client - - com.datastax.cassandra - cassandra-driver-core - - - com.datastax.cassandra - cassandra-driver-mapping - - - com.datastax.cassandra - cassandra-driver-extras - + + com.datastax.cassandra + cassandra-driver-core + + + com.datastax.cassandra + cassandra-driver-mapping + + + com.datastax.cassandra + cassandra-driver-extras + io.takari.junit takari-cpsuite test - - + + com.google.guava guava @@ -215,18 +215,18 @@ - - org.apache.maven.plugins - maven-jar-plugin + + org.apache.maven.plugins + maven-jar-plugin ${jar-plugin.version} - - - - test-jar - - - - + + + + test-jar + + + + diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java index 05969874ed..7f04ddf2aa 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java @@ -17,7 +17,6 @@ package org.thingsboard.server.dao.service; import com.fasterxml.jackson.databind.JsonNode; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.validator.routines.EmailValidator; import org.thingsboard.server.common.data.BaseData; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.exception.DataValidationException; @@ -26,11 +25,13 @@ import java.util.HashSet; import java.util.Iterator; import java.util.Set; import java.util.function.Function; +import java.util.regex.Matcher; +import java.util.regex.Pattern; @Slf4j public abstract class DataValidator> { - - private static EmailValidator emailValidator = EmailValidator.getInstance(); + private static final Pattern EMAIL_PATTERN = + Pattern.compile("^[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,6}$", Pattern.CASE_INSENSITIVE); public void validate(D data, Function tenantIdFunction) { try { @@ -64,11 +65,20 @@ public abstract class DataValidator> { } protected static void validateEmail(String email) { - if (!emailValidator.isValid(email)) { + if (!doValidateEmail(email)) { throw new DataValidationException("Invalid email address format '" + email + "'!"); } } + private static boolean doValidateEmail(String email) { + if (email == null) { + return false; + } + + Matcher emailMatcher = EMAIL_PATTERN.matcher(email); + return emailMatcher.matches(); + } + protected static void validateJsonStructure(JsonNode expectedNode, JsonNode actualNode) { Set expectedFields = new HashSet<>(); Iterator fieldsIterator = expectedNode.fieldNames(); diff --git a/k8s/README.md b/k8s/README.md index 24520dd68c..5e563039aa 100644 --- a/k8s/README.md +++ b/k8s/README.md @@ -4,15 +4,15 @@ This folder containing scripts and Kubernetes resources configurations to run Th ## Prerequisites -ThingsBoard Microservices are running on Kubernetes cluster. +ThingsBoard Microservices run on the Kubernetes cluster. You need to have a Kubernetes cluster, and the kubectl command-line tool must be configured to communicate with your cluster. -If you do not already have a cluster, you can create one by using [Minikube](https://kubernetes.io/docs/setup/minikube), +If you do not have a cluster already, you can create one by using [Minikube](https://kubernetes.io/docs/setup/minikube), or you can choose any other available [Kubernetes cluster deployment solutions](https://kubernetes.io/docs/setup/pick-right-solution/). ### Enable ingress addon -By default ingress addon is disable in the Minikube, and available only in cluster providers. -To enable ingress, please execute next command: +By default ingress addon is disabled in the Minikube, and available only in cluster providers. +To enable ingress, please execute the following command: ` $ minikube addons enable ingress @@ -21,21 +21,21 @@ $ minikube addons enable ingress ## Installation Before performing initial installation you can configure the type of database to be used with ThingsBoard and the type of deployment. -In order to set database type change the value of `DATABASE` variable in `.env` file to one of the following: +To set database type change the value of `DATABASE` variable in `.env` file to one of the following: - `postgres` - use PostgreSQL database; - `cassandra` - use Cassandra database; **NOTE**: According to the database type corresponding kubernetes resources will be deployed (see `postgres.yml`, `cassandra.yml` for details). -In order to set deployment type change the value of `DEPLOYMENT_TYPE` variable in `.env` file to one of the following: +To set deployment type change the value of `DEPLOYMENT_TYPE` variable in `.env` file to one of the following: -- `basic` - start up with single instance of Zookeeper, Kafka and Redis; -- `high-availability` - start up with Zookeeper, Kafka and Redis in cluster modes; +- `basic` - startup with a single instance of Zookeeper, Kafka and Redis; +- `high-availability` - startup with Zookeeper, Kafka, and Redis in cluster modes; -**NOTE**: According to the deployment type corresponding kubernetes resources will be deployed (see content of the directories `./basic` and `./high-availability` for details). +**NOTE**: According to the deployment type corresponding kubernetes resources will be deployed (see the content of the directories `./basic` and `./high-availability` for details). -Execute the following command to run installation: +Execute the following command to run the installation: ` $ ./k8s-install-tb.sh --loadDemo @@ -47,7 +47,7 @@ Where: ## Running -Execute the following command to deploy thirdparty resources: +Execute the following command to deploy third-party resources: ` $ ./k8s-deploy-thirdparty.sh @@ -61,8 +61,8 @@ Execute the following command to deploy resources: $ ./k8s-deploy-resources.sh ` -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`). -You should see ThingsBoard login page. +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`). +You should see the ThingsBoard login page. Use the following default credentials: @@ -73,16 +73,16 @@ If you installed DataBase with demo data (using `--loadDemo` flag) you can also - **Tenant Administrator**: tenant@thingsboard.org / tenant - **Customer User**: customer@thingsboard.org / customer -In case of any issues you can examine service logs for errors. +In case of any issues, you can examine service logs for errors. For example to see ThingsBoard node logs execute the following commands: -1) Get list of the running tb-node pods: +1) Get the list of the running tb-node pods: ` $ kubectl get pods -l app=tb-node ` -2) Fetch logs of tb-node pod: +2) Fetch logs of the tb-node pod: ` $ kubectl logs -f [tb-node-pod-name] @@ -103,7 +103,7 @@ Execute the following command to delete all ThingsBoard microservices: $ ./k8s-delete-resources.sh ` -Execute the following command to delete all thirdparty microservices: +Execute the following command to delete all third-party microservices: ` $ ./k8s-delete-thirdparty.sh diff --git a/msa/black-box-tests/pom.xml b/msa/black-box-tests/pom.xml index 2747970be8..4880410053 100644 --- a/msa/black-box-tests/pom.xml +++ b/msa/black-box-tests/pom.xml @@ -21,7 +21,7 @@ org.thingsboard - 2.5.1-SNAPSHOT + 2.5.3-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/js-executor/package-lock.json b/msa/js-executor/package-lock.json index 818b77550e..5636c4f586 100644 --- a/msa/js-executor/package-lock.json +++ b/msa/js-executor/package-lock.json @@ -1,6 +1,6 @@ { "name": "thingsboard-js-executor", - "version": "2.5.1", + "version": "2.5.3", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -1461,7 +1461,7 @@ }, "enabled": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/enabled/-/enabled-1.0.2.tgz", + "resolved": "http://registry.npmjs.org/enabled/-/enabled-1.0.2.tgz", "integrity": "sha1-ll9lE9LC0cX0ZStkouM5ZGf8L5M=", "requires": { "env-variable": "0.0.x" @@ -1740,7 +1740,7 @@ }, "fecha": { "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fecha/-/fecha-2.3.3.tgz", + "resolved": "http://registry.npmjs.org/fecha/-/fecha-2.3.3.tgz", "integrity": "sha512-lUGBnIamTAwk4znq5BcqsDaxSmZ9nDVJaij6NvRt/Tg4R69gERA+otPKbS86ROw9nxVMw2/mp1fnaiWqbs6Sdg==" }, "file-stream-rotator": { @@ -1872,14 +1872,12 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, - "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -1894,20 +1892,17 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "core-util-is": { "version": "1.0.2", @@ -2024,8 +2019,7 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "ini": { "version": "1.3.5", @@ -2037,7 +2031,6 @@ "version": "1.0.0", "bundled": true, "dev": true, - "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -2052,7 +2045,6 @@ "version": "3.0.4", "bundled": true, "dev": true, - "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -2164,8 +2156,7 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "object-assign": { "version": "4.1.1", @@ -2177,7 +2168,6 @@ "version": "1.4.0", "bundled": true, "dev": true, - "optional": true, "requires": { "wrappy": "1" } @@ -2299,7 +2289,6 @@ "version": "1.0.2", "bundled": true, "dev": true, - "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -2401,7 +2390,7 @@ }, "get-stream": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "resolved": "http://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", "dev": true }, @@ -2558,7 +2547,7 @@ }, "got": { "version": "6.7.1", - "resolved": "https://registry.npmjs.org/got/-/got-6.7.1.tgz", + "resolved": "http://registry.npmjs.org/got/-/got-6.7.1.tgz", "integrity": "sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA=", "dev": true, "requires": { @@ -2890,7 +2879,7 @@ }, "is-obj": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "resolved": "http://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", "dev": true }, @@ -3252,7 +3241,7 @@ }, "minimist": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" }, "mixin-deep": { @@ -3551,7 +3540,7 @@ }, "path-is-absolute": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "resolved": "http://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true }, @@ -3990,7 +3979,7 @@ }, "safe-regex": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "resolved": "http://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", "dev": true, "requires": { @@ -4300,7 +4289,7 @@ }, "strip-eof": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "resolved": "http://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", "dev": true }, diff --git a/msa/js-executor/package.json b/msa/js-executor/package.json index 8e9c01d800..cedef9f059 100644 --- a/msa/js-executor/package.json +++ b/msa/js-executor/package.json @@ -1,7 +1,7 @@ { "name": "thingsboard-js-executor", "private": true, - "version": "2.5.1", + "version": "2.5.3", "description": "ThingsBoard JavaScript Executor Microservice", "main": "server.js", "bin": "server.js", diff --git a/msa/js-executor/pom.xml b/msa/js-executor/pom.xml index 3aedead07e..ab810ab4da 100644 --- a/msa/js-executor/pom.xml +++ b/msa/js-executor/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.5.1-SNAPSHOT + 2.5.3-SNAPSHOT msa org.thingsboard.msa @@ -215,7 +215,7 @@ jenkins Jenkins Repository - http://repo.jenkins-ci.org/releases + https://repo.jenkins-ci.org/releases false diff --git a/msa/pom.xml b/msa/pom.xml index dc3c84b58a..deee02a8a4 100644 --- a/msa/pom.xml +++ b/msa/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.5.1-SNAPSHOT + 2.5.3-SNAPSHOT thingsboard msa diff --git a/msa/tb-node/pom.xml b/msa/tb-node/pom.xml index 9f71336e7c..4a20d19a90 100644 --- a/msa/tb-node/pom.xml +++ b/msa/tb-node/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.5.1-SNAPSHOT + 2.5.3-SNAPSHOT msa org.thingsboard.msa @@ -180,7 +180,7 @@ jenkins Jenkins Repository - http://repo.jenkins-ci.org/releases + https://repo.jenkins-ci.org/releases false diff --git a/msa/tb/docker/logback.xml b/msa/tb/docker/logback.xml index 256e78436f..51a9c21f3b 100644 --- a/msa/tb/docker/logback.xml +++ b/msa/tb/docker/logback.xml @@ -41,7 +41,6 @@ - diff --git a/msa/tb/pom.xml b/msa/tb/pom.xml index 4f0f1f4433..b27fe66615 100644 --- a/msa/tb/pom.xml +++ b/msa/tb/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.5.1-SNAPSHOT + 2.5.3-SNAPSHOT msa org.thingsboard.msa @@ -360,7 +360,7 @@ jenkins Jenkins Repository - http://repo.jenkins-ci.org/releases + https://repo.jenkins-ci.org/releases false diff --git a/msa/transport/coap/pom.xml b/msa/transport/coap/pom.xml index 50089b9259..373e6416ff 100644 --- a/msa/transport/coap/pom.xml +++ b/msa/transport/coap/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 2.5.1-SNAPSHOT + 2.5.3-SNAPSHOT transport org.thingsboard.msa.transport @@ -180,7 +180,7 @@ jenkins Jenkins Repository - http://repo.jenkins-ci.org/releases + https://repo.jenkins-ci.org/releases false diff --git a/msa/transport/http/pom.xml b/msa/transport/http/pom.xml index a40fd0baca..e3e1d47a9b 100644 --- a/msa/transport/http/pom.xml +++ b/msa/transport/http/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 2.5.1-SNAPSHOT + 2.5.3-SNAPSHOT transport org.thingsboard.msa.transport @@ -180,7 +180,7 @@ jenkins Jenkins Repository - http://repo.jenkins-ci.org/releases + https://repo.jenkins-ci.org/releases false diff --git a/msa/transport/mqtt/pom.xml b/msa/transport/mqtt/pom.xml index 483cc6990a..b84d064f31 100644 --- a/msa/transport/mqtt/pom.xml +++ b/msa/transport/mqtt/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 2.5.1-SNAPSHOT + 2.5.3-SNAPSHOT transport org.thingsboard.msa.transport @@ -180,7 +180,7 @@ jenkins Jenkins Repository - http://repo.jenkins-ci.org/releases + https://repo.jenkins-ci.org/releases false diff --git a/msa/transport/pom.xml b/msa/transport/pom.xml index b39e660146..c4c17d5b3e 100644 --- a/msa/transport/pom.xml +++ b/msa/transport/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.5.1-SNAPSHOT + 2.5.3-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/web-ui/package-lock.json b/msa/web-ui/package-lock.json index 695ef56342..b7785ef48b 100644 --- a/msa/web-ui/package-lock.json +++ b/msa/web-ui/package-lock.json @@ -1,6 +1,6 @@ { "name": "thingsboard-web-ui", - "version": "2.5.1", + "version": "2.5.3", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/msa/web-ui/package.json b/msa/web-ui/package.json index 28f66fea46..749fb2df72 100644 --- a/msa/web-ui/package.json +++ b/msa/web-ui/package.json @@ -1,7 +1,7 @@ { "name": "thingsboard-web-ui", "private": true, - "version": "2.5.1", + "version": "2.5.3", "description": "ThingsBoard Web UI Microservice", "main": "server.js", "bin": "server.js", diff --git a/msa/web-ui/pom.xml b/msa/web-ui/pom.xml index 2b38860500..54e3094db8 100644 --- a/msa/web-ui/pom.xml +++ b/msa/web-ui/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.5.1-SNAPSHOT + 2.5.3-SNAPSHOT msa org.thingsboard.msa @@ -432,7 +432,7 @@ jenkins Jenkins Repository - http://repo.jenkins-ci.org/releases + https://repo.jenkins-ci.org/releases false diff --git a/netty-mqtt/pom.xml b/netty-mqtt/pom.xml index 5b77fce18e..eb32d84f20 100644 --- a/netty-mqtt/pom.xml +++ b/netty-mqtt/pom.xml @@ -19,11 +19,11 @@ 4.0.0 org.thingsboard - 2.5.1-SNAPSHOT + 2.5.3-SNAPSHOT thingsboard netty-mqtt - 2.5.1-SNAPSHOT + 2.5.3-SNAPSHOT jar Netty MQTT Client diff --git a/packaging/java/scripts/install/logback.xml b/packaging/java/scripts/install/logback.xml index 054ad42b94..d0908584c5 100644 --- a/packaging/java/scripts/install/logback.xml +++ b/packaging/java/scripts/install/logback.xml @@ -57,7 +57,6 @@ - diff --git a/pom.xml b/pom.xml index e4c9a1e8b8..784ecb05cf 100755 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard thingsboard - 2.5.1-SNAPSHOT + 2.5.3-SNAPSHOT pom Thingsboard @@ -55,15 +55,12 @@ 28.2-jre 2.6.1 3.4 - 1.6 2.5 1.4 2.10.2 2.10.2 2.10.2 2.2.6 - 2.13 - 2.6.3 1.0.2 2.6.2 1.7 @@ -780,6 +777,11 @@ util ${project.version} + + org.thingsboard.common + actor + ${project.version} + org.thingsboard.common dao-api @@ -1077,11 +1079,6 @@ commons-lang3 ${commons-lang3.version} - - commons-validator - commons-validator - ${commons-validator.version} - commons-io commons-io @@ -1118,16 +1115,6 @@ - - com.typesafe.akka - akka-actor_${scala.version} - ${akka.version} - - - com.typesafe.akka - akka-slf4j_${scala.version} - ${akka.version} - org.eclipse.californium californium-core diff --git a/rest-client/pom.xml b/rest-client/pom.xml index 1caa3bceac..edb6e9be2b 100644 --- a/rest-client/pom.xml +++ b/rest-client/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.5.1-SNAPSHOT + 2.5.3-SNAPSHOT thingsboard rest-client diff --git a/rule-engine/pom.xml b/rule-engine/pom.xml index fe128cb440..66f8cec913 100644 --- a/rule-engine/pom.xml +++ b/rule-engine/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.5.1-SNAPSHOT + 2.5.3-SNAPSHOT thingsboard rule-engine diff --git a/rule-engine/rule-engine-api/pom.xml b/rule-engine/rule-engine-api/pom.xml index 94200db301..49386f1f86 100644 --- a/rule-engine/rule-engine-api/pom.xml +++ b/rule-engine/rule-engine-api/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 2.5.1-SNAPSHOT + 2.5.3-SNAPSHOT rule-engine org.thingsboard.rule-engine diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java index 3761933c96..766d1b95ac 100644 --- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java @@ -132,7 +132,7 @@ public interface TbContext { void ack(TbMsg tbMsg); - TbMsg newMsg(String type, EntityId originator, TbMsgMetaData metaData, String data); + TbMsg newMsg(String queueName, String type, EntityId originator, TbMsgMetaData metaData, String data); TbMsg transformMsg(TbMsg origMsg, String type, EntityId originator, TbMsgMetaData metaData, String data); diff --git a/rule-engine/rule-engine-components/pom.xml b/rule-engine/rule-engine-components/pom.xml index 1b338c34f2..1b6fc2fc74 100644 --- a/rule-engine/rule-engine-components/pom.xml +++ b/rule-engine/rule-engine-components/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 2.5.1-SNAPSHOT + 2.5.3-SNAPSHOT rule-engine org.thingsboard.rule-engine diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCopyAttributesToEntityViewNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCopyAttributesToEntityViewNode.java index 0d256b302c..96595eb845 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCopyAttributesToEntityViewNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCopyAttributesToEntityViewNode.java @@ -138,7 +138,7 @@ public class TbCopyAttributesToEntityViewNode implements TbNode { } private void transformAndTellNext(TbContext ctx, TbMsg msg, EntityView entityView) { - ctx.enqueueForTellNext(ctx.newMsg(msg.getType(), entityView.getId(), msg.getMetaData(), msg.getData()), SUCCESS); + ctx.enqueueForTellNext(ctx.newMsg(msg.getQueueName(), msg.getType(), entityView.getId(), msg.getMetaData(), msg.getData()), SUCCESS); } private boolean attributeContainsInEntityView(String scope, String attrKey, EntityView entityView) { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbMsgCountNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbMsgCountNode.java index 4aeee24e1b..1569eff98e 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbMsgCountNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbMsgCountNode.java @@ -26,6 +26,7 @@ import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgDataType; import org.thingsboard.server.common.msg.TbMsgMetaData; +import org.thingsboard.server.common.msg.queue.ServiceQueue; import org.thingsboard.server.common.msg.session.SessionMsgType; import java.util.UUID; @@ -77,7 +78,7 @@ public class TbMsgCountNode implements TbNode { TbMsgMetaData metaData = new TbMsgMetaData(); metaData.putValue("delta", Long.toString(System.currentTimeMillis() - lastScheduledTs + delay)); - TbMsg tbMsg = TbMsg.newMsg(SessionMsgType.POST_TELEMETRY_REQUEST.name(), ctx.getTenantId(), metaData, gson.toJson(telemetryJson)); + TbMsg tbMsg = TbMsg.newMsg(msg.getQueueName(), SessionMsgType.POST_TELEMETRY_REQUEST.name(), ctx.getTenantId(), metaData, gson.toJson(telemetryJson)); ctx.enqueueForTellNext(tbMsg, SUCCESS); scheduleTickMsg(ctx); } else { @@ -93,7 +94,7 @@ public class TbMsgCountNode implements TbNode { } lastScheduledTs = lastScheduledTs + delay; long curDelay = Math.max(0L, (lastScheduledTs - curTs)); - TbMsg tickMsg = ctx.newMsg(TB_MSG_COUNT_NODE_MSG, ctx.getSelfId(), new TbMsgMetaData(), ""); + TbMsg tickMsg = ctx.newMsg(ServiceQueue.MAIN, TB_MSG_COUNT_NODE_MSG, ctx.getSelfId(), new TbMsgMetaData(), ""); nextTickId = tickMsg.getId(); ctx.tellSelf(tickMsg, curDelay); } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/debug/TbMsgGeneratorNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/debug/TbMsgGeneratorNode.java index c646b1ca36..696b606f2c 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/debug/TbMsgGeneratorNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/debug/TbMsgGeneratorNode.java @@ -27,6 +27,7 @@ import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgMetaData; import org.thingsboard.server.common.msg.queue.PartitionChangeMsg; +import org.thingsboard.server.common.msg.queue.ServiceQueue; import java.util.UUID; import java.util.concurrent.TimeUnit; @@ -120,7 +121,7 @@ public class TbMsgGeneratorNode implements TbNode { } lastScheduledTs = lastScheduledTs + delay; long curDelay = Math.max(0L, (lastScheduledTs - curTs)); - TbMsg tickMsg = ctx.newMsg(TB_MSG_GENERATOR_NODE_MSG, ctx.getSelfId(), new TbMsgMetaData(), ""); + TbMsg tickMsg = ctx.newMsg(ServiceQueue.MAIN, TB_MSG_GENERATOR_NODE_MSG, ctx.getSelfId(), new TbMsgMetaData(), ""); nextTickId = tickMsg.getId(); ctx.tellSelf(tickMsg, curDelay); } @@ -128,13 +129,13 @@ public class TbMsgGeneratorNode implements TbNode { private ListenableFuture generate(TbContext ctx) { return ctx.getJsExecutor().executeAsync(() -> { if (prevMsg == null) { - prevMsg = ctx.newMsg("", originatorId, new TbMsgMetaData(), "{}"); + prevMsg = ctx.newMsg(ServiceQueue.MAIN, "", originatorId, new TbMsgMetaData(), "{}"); } if (initialized) { ctx.logJsEvalRequest(); TbMsg generated = jsEngine.executeGenerate(prevMsg); ctx.logJsEvalResponse(); - prevMsg = ctx.newMsg(generated.getType(), originatorId, generated.getMetaData(), generated.getData()); + prevMsg = ctx.newMsg(ServiceQueue.MAIN, generated.getType(), originatorId, generated.getMetaData(), generated.getData()); } return prevMsg; }); diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/delay/TbMsgDelayNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/delay/TbMsgDelayNode.java index d7ceefda36..0b806465b0 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/delay/TbMsgDelayNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/delay/TbMsgDelayNode.java @@ -27,6 +27,7 @@ import org.thingsboard.server.common.data.plugin.ComponentType; import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgMetaData; +import org.thingsboard.server.common.msg.queue.ServiceQueue; import java.util.HashMap; import java.util.Map; @@ -72,7 +73,7 @@ public class TbMsgDelayNode implements TbNode { } else { if (pendingMsgs.size() < config.getMaxPendingMsgs()) { pendingMsgs.put(msg.getId(), msg); - TbMsg tickMsg = ctx.newMsg(TB_MSG_DELAY_NODE_MSG, ctx.getSelfId(), new TbMsgMetaData(), msg.getId().toString()); + TbMsg tickMsg = ctx.newMsg(ServiceQueue.MAIN, TB_MSG_DELAY_NODE_MSG, ctx.getSelfId(), new TbMsgMetaData(), msg.getId().toString()); ctx.tellSelf(tickMsg, getDelay(msg)); ctx.ack(msg); } else { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/kafka/TbKafkaNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/kafka/TbKafkaNode.java index 3504f7bd85..2945b09ab8 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/kafka/TbKafkaNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/kafka/TbKafkaNode.java @@ -97,8 +97,20 @@ public class TbKafkaNode implements TbNode { @Override public void onMsg(TbContext ctx, TbMsg msg) { String topic = TbNodeUtils.processPattern(config.getTopicPattern(), msg.getMetaData()); + try { + ctx.getExternalCallExecutor().executeAsync(() -> { + publish(ctx, msg, topic); + return null; + }); + } catch (Exception e) { + ctx.tellFailure(msg, e); + } + } + + protected void publish(TbContext ctx, TbMsg msg, String topic) { try { if (!addMetadataKeyValuesAsKafkaHeaders) { + //TODO: external system executor producer.send(new ProducerRecord<>(topic, msg.getData()), (metadata, e) -> processRecord(ctx, msg, metadata, e)); } else { @@ -107,9 +119,8 @@ public class TbKafkaNode implements TbNode { producer.send(new ProducerRecord<>(topic, null, null, null, msg.getData(), headers), (metadata, e) -> processRecord(ctx, msg, metadata, e)); } - } catch (Exception e) { - ctx.tellFailure(msg, e); + log.debug("[{}] Failed to process message: {}", ctx.getSelfId(), msg, e); } } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbHttpClient.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbHttpClient.java index ba860c103b..57bde77f9c 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbHttpClient.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbHttpClient.java @@ -20,11 +20,22 @@ import io.netty.channel.nio.NioEventLoopGroup; import io.netty.handler.ssl.SslContextBuilder; import lombok.Data; import lombok.extern.slf4j.Slf4j; +import org.apache.http.HttpHost; +import org.apache.http.auth.AuthScope; +import org.apache.http.auth.UsernamePasswordCredentials; +import org.apache.http.client.CredentialsProvider; +import org.apache.http.conn.ssl.DefaultHostnameVerifier; +import org.apache.http.impl.client.BasicCredentialsProvider; +import org.apache.http.impl.nio.client.CloseableHttpAsyncClient; +import org.apache.http.impl.nio.client.HttpAsyncClientBuilder; +import org.apache.http.impl.nio.client.HttpAsyncClients; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.ResponseEntity; +import org.springframework.http.client.HttpComponentsAsyncClientHttpRequestFactory; import org.springframework.http.client.Netty4ClientHttpRequestFactory; +import org.springframework.util.StringUtils; import org.springframework.util.concurrent.ListenableFuture; import org.springframework.util.concurrent.ListenableFutureCallback; import org.springframework.web.client.AsyncRestTemplate; @@ -36,7 +47,11 @@ import org.thingsboard.rule.engine.api.util.TbNodeUtils; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgMetaData; +import javax.net.ssl.SSLContext; import javax.net.ssl.SSLException; +import java.net.Authenticator; +import java.net.PasswordAuthentication; +import java.security.NoSuchAlgorithmException; import java.util.Deque; import java.util.concurrent.ConcurrentLinkedDeque; import java.util.concurrent.TimeUnit; @@ -50,6 +65,7 @@ class TbHttpClient { private static final String STATUS_REASON = "statusReason"; private static final String ERROR = "error"; private static final String ERROR_BODY = "error_body"; + private static final String ERROR_SYSTEM_PROPERTIES = "Didn't set any system proxy properties. Should be added next system proxy properties: \"http.proxyHost\" and \"http.proxyPort\" or \"https.proxyHost\" and \"https.proxyPort\" or \"socksProxyHost\" and \"socksProxyPort\""; private final TbRestApiCallNodeConfiguration config; @@ -63,7 +79,56 @@ class TbHttpClient { if (config.getMaxParallelRequestsCount() > 0) { pendingFutures = new ConcurrentLinkedDeque<>(); } - if (config.isUseSimpleClientHttpFactory()) { + + if (config.isEnableProxy()) { + checkProxyHost(config.getProxyHost()); + checkProxyPort(config.getProxyPort()); + + String proxyUser; + String proxyPassword; + + CloseableHttpAsyncClient asyncClient; + HttpComponentsAsyncClientHttpRequestFactory requestFactory = new HttpComponentsAsyncClientHttpRequestFactory(); + + if (config.isUseSystemProxyProperties()) { + checkSystemProxyProperties(); + + asyncClient = HttpAsyncClients.createSystem(); + + proxyUser = System.getProperty("tb.proxy.user"); + proxyPassword = System.getProperty("tb.proxy.password"); + + if (useAuth(proxyUser, proxyPassword)) { + Authenticator.setDefault(new Authenticator() { + protected PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication(proxyUser, proxyPassword.toCharArray()); + } + }); + } + } else { + HttpAsyncClientBuilder httpAsyncClientBuilder = HttpAsyncClientBuilder.create() + .setSSLHostnameVerifier(new DefaultHostnameVerifier()) + .setSSLContext(SSLContext.getDefault()) + .setProxy(new HttpHost(config.getProxyHost(), config.getProxyPort(), config.getProxyScheme())); + + proxyUser = config.getProxyUser(); + proxyPassword = config.getProxyPassword(); + + if (useAuth(proxyUser, proxyPassword)) { + CredentialsProvider credsProvider = new BasicCredentialsProvider(); + credsProvider.setCredentials( + new AuthScope(config.getProxyHost(), config.getProxyPort()), + new UsernamePasswordCredentials(proxyUser, proxyPassword) + ); + httpAsyncClientBuilder.setDefaultCredentialsProvider(credsProvider); + } + asyncClient = httpAsyncClientBuilder.build(); + } + + requestFactory.setAsyncClient(asyncClient); + requestFactory.setReadTimeout(config.getReadTimeoutMs()); + httpClient = new AsyncRestTemplate(requestFactory); + } else if (config.isUseSimpleClientHttpFactory()) { httpClient = new AsyncRestTemplate(); } else { this.eventLoopGroup = new NioEventLoopGroup(); @@ -72,11 +137,25 @@ class TbHttpClient { nettyFactory.setReadTimeout(config.getReadTimeoutMs()); httpClient = new AsyncRestTemplate(nettyFactory); } - } catch (SSLException e) { + } catch (SSLException | NoSuchAlgorithmException e) { throw new TbNodeException(e); } } + private void checkSystemProxyProperties() throws TbNodeException { + boolean useHttpProxy = !StringUtils.isEmpty(System.getProperty("http.proxyHost")) && !StringUtils.isEmpty(System.getProperty("http.proxyPort")); + boolean useHttpsProxy = !StringUtils.isEmpty(System.getProperty("https.proxyHost")) && !StringUtils.isEmpty(System.getProperty("https.proxyPort")); + boolean useSocksProxy = !StringUtils.isEmpty(System.getProperty("socksProxyHost")) && !StringUtils.isEmpty(System.getProperty("socksProxyPort")); + if (!(useHttpProxy || useHttpsProxy || useSocksProxy)) { + log.warn(ERROR_SYSTEM_PROPERTIES); + throw new TbNodeException(ERROR_SYSTEM_PROPERTIES); + } + } + + private boolean useAuth(String proxyUser, String proxyPassword) { + return !StringUtils.isEmpty(proxyUser) && !StringUtils.isEmpty(proxyPassword); + } + void destroy() { if (this.eventLoopGroup != null) { this.eventLoopGroup.shutdownGracefully(0, 5, TimeUnit.SECONDS); @@ -169,4 +248,15 @@ class TbHttpClient { } } + private static void checkProxyHost(String proxyHost) throws TbNodeException { + if (StringUtils.isEmpty(proxyHost)) { + throw new TbNodeException("Proxy host can't be empty"); + } + } + + private static void checkProxyPort(int proxyPort) throws TbNodeException { + if (proxyPort < 0 || proxyPort > 65535) { + throw new TbNodeException("Proxy port out of range:" + proxyPort); + } + } } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbRestApiCallNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbRestApiCallNode.java index 0ee938f7b7..eee1b8627f 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbRestApiCallNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbRestApiCallNode.java @@ -37,7 +37,9 @@ import org.thingsboard.server.common.msg.TbMsg; " Outbound message will contain response fields " + "(status, statusCode, statusReason and response headers) in the Message Metadata." + " Response body saved in outbound Message payload. " + - "For example statusCode field can be accessed with metadata.statusCode.", + "For example statusCode field can be accessed with metadata.statusCode." + + "
Note- if you use system proxy properties, the next system proxy properties should be added: \"http.proxyHost\" and \"http.proxyPort\" or \"https.proxyHost\" and \"https.proxyPort\" or \"socksProxyHost\" and \"socksProxyPort\"," + + "and if your proxy with auth, the next ones should be added: \"tb.proxy.user\" and \"tb.proxy.password\" to the thingsboard.conf file.", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbActionNodeRestApiCallConfig", iconUrl = "data:image/svg+xml;base64,PHN2ZyBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA1MTIgNTEyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbDpzcGFjZT0icHJlc2VydmUiIHZpZXdCb3g9IjAgMCA1MTIgNTEyIiB2ZXJzaW9uPSIxLjEiIHk9IjBweCIgeD0iMHB4Ij48ZyB0cmFuc2Zvcm09Im1hdHJpeCguOTQ5NzUgMCAwIC45NDk3NSAxNy4xMiAyNi40OTIpIj48cGF0aCBkPSJtMTY5LjExIDEwOC41NGMtOS45MDY2IDAuMDczNC0xOS4wMTQgNi41NzI0LTIyLjAxNCAxNi40NjlsLTY5Ljk5MyAyMzEuMDhjLTMuNjkwNCAxMi4xODEgMy4yODkyIDI1LjIyIDE1LjQ2OSAyOC45MSAyLjIyNTkgMC42NzQ4MSA0LjQ5NjkgMSA2LjcyODUgMSA5Ljk3MjEgMCAxOS4xNjUtNi41MTUzIDIyLjE4Mi0xNi40NjdhNi41MjI0IDYuNTIyNCAwIDAgMCAwLjAwMiAtMC4wMDJsNjkuOTktMjMxLjA3YTYuNTIyNCA2LjUyMjQgMCAwIDAgMCAtMC4wMDJjMy42ODU1LTEyLjE4MS0zLjI4Ny0yNS4yMjUtMTUuNDcxLTI4LjkxMi0yLjI4MjUtMC42OTE0NS00LjYxMTYtMS4wMTY5LTYuODk4NC0xem04NC45ODggMGMtOS45MDQ4IDAuMDczNC0xOS4wMTggNi41Njc1LTIyLjAxOCAxNi40NjlsLTY5Ljk4NiAyMzEuMDhjLTMuNjg5OCAxMi4xNzkgMy4yODUzIDI1LjIxNyAxNS40NjUgMjguOTA4IDIuMjI5NyAwLjY3NjQ3IDQuNTAwOCAxLjAwMiA2LjczMjQgMS4wMDIgOS45NzIxIDAgMTkuMTY1LTYuNTE1MyAyMi4xODItMTYuNDY3YTYuNTIyNCA2LjUyMjQgMCAwIDAgMC4wMDIgLTAuMDAybDY5Ljk4OC0yMzEuMDdjMy42OTA4LTEyLjE4MS0zLjI4NTItMjUuMjIzLTE1LjQ2Ny0yOC45MTItMi4yODE0LTAuNjkyMzEtNC42MTA4LTEuMDE4OS02Ljg5ODQtMS4wMDJ6bS0yMTcuMjkgNDIuMjNjLTEyLjcyOS0wLjAwMDg3LTIzLjE4OCAxMC40NTYtMjMuMTg4IDIzLjE4NiAwLjAwMSAxMi43MjggMTAuNDU5IDIzLjE4NiAyMy4xODggMjMuMTg2IDEyLjcyNy0wLjAwMSAyMy4xODMtMTAuNDU5IDIzLjE4NC0yMy4xODYgMC4wMDA4NzYtMTIuNzI4LTEwLjQ1Ni0yMy4xODUtMjMuMTg0LTIzLjE4NnptMCAxNDYuNjRjLTEyLjcyNy0wLjAwMDg3LTIzLjE4NiAxMC40NTUtMjMuMTg4IDIzLjE4NC0wLjAwMDg3MyAxMi43MjkgMTAuNDU4IDIzLjE4OCAyMy4xODggMjMuMTg4IDEyLjcyOC0wLjAwMSAyMy4xODQtMTAuNDYgMjMuMTg0LTIzLjE4OC0wLjAwMS0xMi43MjYtMTAuNDU3LTIzLjE4My0yMy4xODQtMjMuMTg0em0yNzAuNzkgNDIuMjExYy0xMi43MjcgMC0yMy4xODQgMTAuNDU3LTIzLjE4NCAyMy4xODRzMTAuNDU1IDIzLjE4OCAyMy4xODQgMjMuMTg4aDE1NC45OGMxMi43MjkgMCAyMy4xODYtMTAuNDYgMjMuMTg2LTIzLjE4OCAwLjAwMS0xMi43MjgtMTAuNDU4LTIzLjE4NC0yMy4xODYtMjMuMTg0eiIgdHJhbnNmb3JtPSJtYXRyaXgoMS4wMzc2IDAgMCAxLjAzNzYgLTcuNTY3NiAtMTQuOTI1KSIgc3Ryb2tlLXdpZHRoPSIxLjI2OTMiLz48L2c+PC9zdmc+", diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbRestApiCallNodeConfiguration.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbRestApiCallNodeConfiguration.java index b361c5ed8e..3cccb2c2c5 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbRestApiCallNodeConfiguration.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbRestApiCallNodeConfiguration.java @@ -33,6 +33,13 @@ public class TbRestApiCallNodeConfiguration implements NodeConfiguration { if (!ruleEngineDeviceRpcResponse.getError().isPresent()) { - TbMsg next = ctx.newMsg(msg.getType(), msg.getOriginator(), msg.getMetaData(), ruleEngineDeviceRpcResponse.getResponse().orElse("{}")); + TbMsg next = ctx.newMsg(msg.getQueueName(), msg.getType(), msg.getOriginator(), msg.getMetaData(), ruleEngineDeviceRpcResponse.getResponse().orElse("{}")); ctx.enqueueForTellNext(next, TbRelationTypes.SUCCESS); } else { - TbMsg next = ctx.newMsg(msg.getType(), msg.getOriginator(), msg.getMetaData(), wrap("error", ruleEngineDeviceRpcResponse.getError().get().name())); - ctx.enqueueForTellFailure(next, ruleEngineDeviceRpcResponse.getError().get().name()); + TbMsg next = ctx.newMsg(msg.getQueueName(), msg.getType(), msg.getOriginator(), msg.getMetaData(), wrap("error", ruleEngineDeviceRpcResponse.getError().get().name())); + ctx.tellFailure(next, new RuntimeException(ruleEngineDeviceRpcResponse.getError().get().name())); } }); ctx.ack(msg); diff --git a/rule-engine/rule-engine-components/src/main/resources/public/static/rulenode/rulenode-core-config.js b/rule-engine/rule-engine-components/src/main/resources/public/static/rulenode/rulenode-core-config.js index fc861fca1f..02fca515df 100644 --- a/rule-engine/rule-engine-components/src/main/resources/public/static/rulenode/rulenode-core-config.js +++ b/rule-engine/rule-engine-components/src/main/resources/public/static/rulenode/rulenode-core-config.js @@ -1,6 +1,6 @@ !function(e){function t(a){if(n[a])return n[a].exports;var i=n[a]={exports:{},id:a,loaded:!1};return e[a].call(i.exports,i,i.exports,t),i.loaded=!0,i.exports}var n={};return t.m=e,t.c=n,t.p="/static/",t(0)}(function(e){for(var t in e)if(Object.prototype.hasOwnProperty.call(e,t))switch(typeof e[t]){case"function":break;case"object":e[t]=function(t){var n=t.slice(1),a=e[t[0]];return function(e,t,i){a.apply(this,[e,t,i].concat(n))}}(e[t]);break;default:e[t]=e[e[t]]}return e}([function(e,t,n){e.exports=n(105)},function(e,t){},1,1,1,1,function(e,t){e.exports="
tb.rulenode.customer-name-pattern-required
tb.rulenode.customer-name-pattern-hint
{{ 'tb.rulenode.create-customer-if-not-exists' | translate }}
tb.rulenode.customer-cache-expiration-required
tb.rulenode.customer-cache-expiration-range
tb.rulenode.customer-cache-expiration-hint
"},function(e,t){e.exports='
{{scope.name | translate}}
'},function(e,t){e.exports="
tb.rulenode.select-queue-hint
"},function(e,t){e.exports="
{{ 'tb.rulenode.test-details-function' | translate }}
tb.rulenode.alarm-type-required
tb.rulenode.entity-type-pattern-hint
"},function(e,t){e.exports="
{{ 'tb.rulenode.test-details-function' | translate }}
{{ 'tb.rulenode.use-message-alarm-data' | translate }}
tb.rulenode.alarm-type-required
tb.rulenode.entity-type-pattern-hint
{{ severity.name | translate}}
tb.rulenode.alarm-severity-required
{{ 'tb.rulenode.propagate' | translate }}
tb.rulenode.relation-types-list-hint
"},function(e,t){e.exports="
{{ ('relation.search-direction.' + direction) | translate}}
tb.rulenode.entity-name-pattern-required
tb.rulenode.entity-name-pattern-hint
tb.rulenode.entity-type-pattern-required
tb.rulenode.entity-type-pattern-hint
tb.rulenode.relation-type-pattern-required
tb.rulenode.relation-type-pattern-hint
{{ 'tb.rulenode.create-entity-if-not-exists' | translate }}
tb.rulenode.create-entity-if-not-exists-hint
{{ 'tb.rulenode.remove-current-relations' | translate }}
tb.rulenode.remove-current-relations-hint
{{ 'tb.rulenode.change-originator-to-related-entity' | translate }}
tb.rulenode.change-originator-to-related-entity-hint
tb.rulenode.entity-cache-expiration-required
tb.rulenode.entity-cache-expiration-range
tb.rulenode.entity-cache-expiration-hint
"},function(e,t){e.exports="
{{ 'tb.rulenode.delete-relation-to-specific-entity' | translate }}
tb.rulenode.delete-relation-hint
{{ ('relation.search-direction.' + direction) | translate}}
tb.rulenode.entity-name-pattern-required
tb.rulenode.entity-name-pattern-hint
tb.rulenode.relation-type-pattern-required
tb.rulenode.relation-type-pattern-hint
tb.rulenode.entity-cache-expiration-required
tb.rulenode.entity-cache-expiration-range
tb.rulenode.entity-cache-expiration-hint
"},function(e,t){e.exports="
tb.rulenode.message-count-required
tb.rulenode.min-message-count-message
tb.rulenode.period-seconds-required
tb.rulenode.min-period-seconds-message
{{ 'tb.rulenode.test-generator-function' | translate }}
"},function(e,t){e.exports='
tb.rulenode.latitude-key-name-required
tb.rulenode.longitude-key-name-required
{{ \'tb.rulenode.fetch-perimeter-info-from-message-metadata\' | translate }}
{{ type.name | translate}}
tb.rulenode.circle-center-latitude-required
tb.rulenode.circle-center-longitude-required
tb.rulenode.range-required
{{ type.name | translate}}
tb.rulenode.polygon-definition-required
tb.rulenode.polygon-definition-hint
tb.rulenode.min-inside-duration-value-required
tb.rulenode.time-value-range
tb.rulenode.time-value-range
{{timeUnit.name | translate}}
tb.rulenode.min-outside-duration-value-required
tb.rulenode.time-value-range
tb.rulenode.time-value-range
{{timeUnit.name | translate}}
'},function(e,t){e.exports='
tb.rulenode.topic-pattern-required
tb.rulenode.bootstrap-servers-required
tb.rulenode.min-retries-message
tb.rulenode.min-batch-size-bytes-message
tb.rulenode.min-linger-ms-message
tb.rulenode.min-buffer-memory-bytes-message
{{ ackValue }}
tb.rulenode.key-serializer-required
tb.rulenode.value-serializer-required
{{ \'tb.rulenode.add-metadata-key-values-as-kafka-headers\' | translate }}
tb.rulenode.add-metadata-key-values-as-kafka-headers-hint
{{charset.name | translate}}
'},function(e,t){e.exports="
{{ 'tb.rulenode.test-to-string-function' | translate }}
"},function(e,t){e.exports='
tb.rulenode.topic-pattern-required
tb.rulenode.mqtt-topic-pattern-hint
tb.rulenode.host-required
tb.rulenode.port-required
tb.rulenode.port-range
tb.rulenode.port-range
tb.rulenode.connect-timeout-required
tb.rulenode.connect-timeout-range
tb.rulenode.connect-timeout-range
{{ \'tb.rulenode.clean-session\' | translate }} {{ \'tb.rulenode.enable-ssl\' | translate }}
{{ \'tb.rulenode.credentials\' | translate }}
{{ ruleNodeTypes.mqttCredentialTypes[configuration.credentials.type].name | translate }}
{{ \'tb.rulenode.credentials\' | translate }}
{{ ruleNodeTypes.mqttCredentialTypes[configuration.credentials.type].name | translate }}
{{credentialsValue.name | translate}}
tb.rulenode.credentials-type-required
tb.rulenode.username-required
tb.rulenode.password-required
'; -},function(e,t){e.exports="
tb.rulenode.interval-seconds-required
tb.rulenode.min-interval-seconds-message
tb.rulenode.output-timeseries-key-prefix-required
"},function(e,t){e.exports='
{{ \'tb.rulenode.use-metadata-period-in-seconds-patterns\' | translate }}
tb.rulenode.use-metadata-period-in-seconds-patterns-hint
tb.rulenode.period-seconds-required
tb.rulenode.min-period-0-seconds-message
tb.rulenode.period-in-seconds-pattern-required
tb.rulenode.period-in-seconds-pattern-hint
tb.rulenode.max-pending-messages-required
tb.rulenode.max-pending-messages-range
tb.rulenode.max-pending-messages-range
'},function(e,t){e.exports="
tb.rulenode.gcp-project-id-required
tb.rulenode.pubsub-topic-name-required
{{ 'action.remove' | translate }} close
tb.rulenode.message-attributes-hint
"},function(e,t){e.exports='
{{ property }}
tb.rulenode.host-required
tb.rulenode.port-required
tb.rulenode.port-range
tb.rulenode.port-range
{{ \'tb.rulenode.automatic-recovery\' | translate }}
tb.rulenode.min-connection-timeout-ms-message
tb.rulenode.min-handshake-timeout-ms-message
'},function(e,t){e.exports='
tb.rulenode.endpoint-url-pattern-required
tb.rulenode.endpoint-url-pattern-hint
{{ type }} {{ \'tb.rulenode.use-simple-client-http-factory\' | translate }}
tb.rulenode.read-timeout-hint
tb.rulenode.max-parallel-requests-count-hint
tb.rulenode.headers-hint
{{ \'tb.rulenode.use-redis-queue\' | translate }}
{{ \'tb.rulenode.trim-redis-queue\' | translate }}
'},function(e,t){e.exports="
"},function(e,t){e.exports="
tb.rulenode.timeout-required
tb.rulenode.min-timeout-message
"},function(e,t){e.exports='
tb.rulenode.custom-table-name-required
tb.rulenode.custom-table-hint
'},function(e,t){e.exports='
{{ \'tb.rulenode.use-system-smtp-settings\' | translate }}
{{smtpProtocol.toUpperCase()}}
tb.rulenode.smtp-host-required
tb.rulenode.smtp-port-required
tb.rulenode.smtp-port-range
tb.rulenode.smtp-port-range
tb.rulenode.timeout-required
tb.rulenode.min-timeout-msec-message
{{ \'tb.rulenode.enable-tls\' | translate }} {{tlsVersion}} {{ \'tb.rulenode.enable-proxy\' | translate }}
tb.rulenode.proxy-host-required
tb.rulenode.proxy-port-required
tb.rulenode.proxy-port-range
tb.rulenode.proxy-port-range
'},function(e,t){e.exports="
tb.rulenode.topic-arn-pattern-required
tb.rulenode.topic-arn-pattern-hint
tb.rulenode.aws-access-key-id-required
tb.rulenode.aws-secret-access-key-required
tb.rulenode.aws-region-required
"},function(e,t){e.exports='
{{ type.name | translate }}
tb.rulenode.queue-url-pattern-required
tb.rulenode.queue-url-pattern-hint
tb.rulenode.min-delay-seconds-message
tb.rulenode.max-delay-seconds-message
tb.rulenode.message-attributes-hint
tb.rulenode.aws-access-key-id-required
tb.rulenode.aws-secret-access-key-required
tb.rulenode.aws-region-required
'},function(e,t){e.exports="
tb.rulenode.default-ttl-required
tb.rulenode.min-default-ttl-message
"},function(e,t){e.exports="
tb.rulenode.customer-name-pattern-required
tb.rulenode.customer-name-pattern-hint
tb.rulenode.customer-cache-expiration-required
tb.rulenode.customer-cache-expiration-range
tb.rulenode.customer-cache-expiration-hint
"},function(e,t){e.exports="
{{ 'alias.last-level-relation' | translate}}
{{ ('relation.search-direction.' + direction) | translate}}
relation.relation-type
device.device-types
"},function(e,t){e.exports="
{{ 'tb.rulenode.latest-telemetry' | translate }}
"},function(e,t){e.exports='
{{ \'tb.rulenode.tell-failure-if-absent\' | translate }}
tb.rulenode.tell-failure-if-absent-hint
{{ \'tb.rulenode.get-latest-value-with-ts\' | translate }}
tb.rulenode.get-latest-value-with-ts-hint
'},function(e,t){e.exports='
{{\'tb.rulenode.entity-details-\'+item.toLowerCase() | translate}} tb.rulenode.no-entity-details-matching {{\'tb.rulenode.entity-details-\'+$chip.toLowerCase() | translate}} {{ \'tb.rulenode.add-to-metadata\' | translate }}
tb.rulenode.add-to-metadata-hint
'},function(e,t){e.exports='
{{ type }}
tb.rulenode.fetch-mode-hint
{{ type }}
tb.rulenode.order-by-hint
tb.rulenode.limit-hint
{{ \'tb.rulenode.use-metadata-interval-patterns\' | translate }}
tb.rulenode.use-metadata-interval-patterns-hint
tb.rulenode.start-interval-value-required
tb.rulenode.time-value-range
tb.rulenode.time-value-range
{{timeUnit.name | translate}}
tb.rulenode.end-interval-value-required
tb.rulenode.time-value-range
tb.rulenode.time-value-range
{{timeUnit.name | translate}}
tb.rulenode.start-interval-pattern-required
tb.rulenode.start-interval-pattern-hint
tb.rulenode.end-interval-pattern-required
tb.rulenode.end-interval-pattern-hint
'; -},function(e,t){e.exports='
{{ \'tb.rulenode.tell-failure-if-absent\' | translate }}
tb.rulenode.tell-failure-if-absent-hint
{{ \'tb.rulenode.get-latest-value-with-ts\' | translate }}
tb.rulenode.get-latest-value-with-ts-hint
'},function(e,t){e.exports='
'},function(e,t){e.exports="
{{ 'tb.rulenode.latest-telemetry' | translate }}
"},32,function(e,t){e.exports="
{{'alarm.display-status.' + item | translate}} {{'alarm.display-status.' + $chip | translate}}
"},function(e,t){e.exports='
tb.rulenode.separator-hint
tb.rulenode.separator-hint
{{ \'tb.rulenode.check-all-keys\' | translate }}
tb.rulenode.check-all-keys-hint
'},function(e,t){e.exports="
{{ 'tb.rulenode.check-relation-to-specific-entity' | translate }}
tb.rulenode.check-relation-hint
{{ ('relation.search-direction.' + direction) | translate}}
"},function(e,t){e.exports='
tb.rulenode.latitude-key-name-required
tb.rulenode.longitude-key-name-required
{{ \'tb.rulenode.fetch-perimeter-info-from-message-metadata\' | translate }}
{{ type.name | translate}}
tb.rulenode.circle-center-latitude-required
tb.rulenode.circle-center-longitude-required
tb.rulenode.range-required
{{ type.name | translate}}
tb.rulenode.polygon-definition-required
tb.rulenode.polygon-definition-hint
'},function(e,t){e.exports='
{{item}}
tb.rulenode.no-message-types-found
tb.rulenode.no-message-type-matching tb.rulenode.create-new-message-type
{{$chip.name}}
'},function(e,t){e.exports='
'},function(e,t){e.exports="
{{ 'tb.rulenode.test-filter-function' | translate }}
"},function(e,t){e.exports="
{{ 'tb.rulenode.test-switch-function' | translate }}
"},function(e,t){e.exports='
{{ keyText }} {{ valText }}  
{{keyRequiredText}}
{{valRequiredText}}
{{ \'tb.key-val.remove-entry\' | translate }} close
{{ \'tb.key-val.add-entry\' | translate }} add {{ \'action.add\' | translate }}
'},function(e,t){e.exports="
{{ 'alias.last-level-relation' | translate}}
{{ ('relation.search-direction.' + direction) | translate}}
relation.relation-filters
"},function(e,t){e.exports='
{{ source.name | translate}}
'},function(e,t){e.exports="
{{ 'tb.rulenode.test-transformer-function' | translate }}
"},function(e,t){e.exports="
tb.rulenode.from-template-required
tb.rulenode.from-template-hint
tb.rulenode.to-template-required
tb.rulenode.mail-address-list-template-hint
tb.rulenode.mail-address-list-template-hint
tb.rulenode.mail-address-list-template-hint
tb.rulenode.subject-template-required
tb.rulenode.subject-template-hint
tb.rulenode.body-template-required
tb.rulenode.body-template-hint
"},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}i.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(6),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}i.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(7),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.types=t,n.serviceType="TB_RULE_ENGINE",n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}i.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(8),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t,n,a){var i=function(i,r,l,s){var d=o.default;r.html(d),i.types=n,i.$watch("configuration",function(e,t){angular.equals(e,t)||s.$setViewValue(i.configuration)}),s.$render=function(){i.configuration=s.$viewValue},i.testDetailsBuildJs=function(e){var n=angular.copy(i.configuration.alarmDetailsBuildJs);a.testNodeScript(e,n,"json",t.instant("tb.rulenode.details")+"","Details",["msg","metadata","msgType"],i.ruleNodeId).then(function(e){i.configuration.alarmDetailsBuildJs=e,s.$setDirty()})},e(r.contents())(i)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:i}}i.$inject=["$compile","$translate","types","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(9),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t,n,a){var i=function(i,r,l,s){var d=o.default;r.html(d),i.types=n,i.$watch("configuration",function(e,t){angular.equals(e,t)||s.$setViewValue(i.configuration)}),s.$render=function(){i.configuration=s.$viewValue,i.configuration.hasOwnProperty("relationTypes")||(i.configuration.relationTypes=[])},i.testDetailsBuildJs=function(e){var n=angular.copy(i.configuration.alarmDetailsBuildJs);a.testNodeScript(e,n,"json",t.instant("tb.rulenode.details")+"","Details",["msg","metadata","msgType"],i.ruleNodeId).then(function(e){i.configuration.alarmDetailsBuildJs=e,s.$setDirty()})},e(r.contents())(i)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:i}}i.$inject=["$compile","$translate","types","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(10),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}i.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(11),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}i.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(12),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t,n,a){var i=function(i,r,l,s){var d=o.default;r.html(d),i.types=n,i.originator=null,i.$watch("configuration",function(e,t){angular.equals(e,t)||s.$setViewValue(i.configuration)}),s.$render=function(){i.configuration=s.$viewValue,i.configuration.originatorId&&i.configuration.originatorType?i.originator={id:i.configuration.originatorId,entityType:i.configuration.originatorType}:i.originator=null,i.$watch("originator",function(e,t){angular.equals(e,t)||(i.originator?(s.$viewValue.originatorId=i.originator.id,s.$viewValue.originatorType=i.originator.entityType):(s.$viewValue.originatorId=null,s.$viewValue.originatorType=null))},!0)},i.testScript=function(e){var n=angular.copy(i.configuration.jsScript);a.testNodeScript(e,n,"generate",t.instant("tb.rulenode.generator")+"","Generate",["prevMsg","prevMetadata","prevMsgType"],i.ruleNodeId).then(function(e){i.configuration.jsScript=e,s.$setDirty()})},e(r.contents())(i)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:i}}i.$inject=["$compile","$translate","types","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i,n(1);var r=n(13),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:n}}i.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(14),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var i=n(77),r=a(i),o=n(54),l=a(o),s=n(60),d=a(s),u=n(57),c=a(u),m=n(56),g=a(m),p=n(64),f=a(p),b=n(71),v=a(b),y=n(72),h=a(y),q=n(70),x=a(q),k=n(63),$=a(k),T=n(75),C=a(T),w=n(76),M=a(w),N=n(69),S=a(N),_=n(65),P=a(_),F=n(74),E=a(F),A=n(67),V=a(A),I=n(66),j=a(I),O=n(53),D=a(O),L=n(78),R=a(L),K=n(59),U=a(K),z=n(58),H=a(z),B=n(73),G=a(B),Y=n(61),Q=a(Y),W=n(68),J=a(W),Z=n(55),X=a(Z);t.default=angular.module("thingsboard.ruleChain.config.action",[]).directive("tbActionNodeTimeseriesConfig",r.default).directive("tbActionNodeAttributesConfig",l.default).directive("tbActionNodeGeneratorConfig",d.default).directive("tbActionNodeCreateAlarmConfig",c.default).directive("tbActionNodeClearAlarmConfig",g.default).directive("tbActionNodeLogConfig",f.default).directive("tbActionNodeRpcReplyConfig",v.default).directive("tbActionNodeRpcRequestConfig",h.default).directive("tbActionNodeRestApiCallConfig",x.default).directive("tbActionNodeKafkaConfig",$.default).directive("tbActionNodeSnsConfig",C.default).directive("tbActionNodeSqsConfig",M.default).directive("tbActionNodeRabbitMqConfig",S.default).directive("tbActionNodeMqttConfig",P.default).directive("tbActionNodeSendEmailConfig",E.default).directive("tbActionNodeMsgDelayConfig",V.default).directive("tbActionNodeMsgCountConfig",j.default).directive("tbActionNodeAssignToCustomerConfig",D.default).directive("tbActionNodeUnAssignToCustomerConfig",R.default).directive("tbActionNodeDeleteRelationConfig",U.default).directive("tbActionNodeCreateRelationConfig",H.default).directive("tbActionNodeCustomTableConfig",G.default).directive("tbActionNodeGpsGeofencingConfig",Q.default).directive("tbActionNodePubSubConfig",J.default).directive("tbActionNodeCheckPointConfig",X.default).name},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.ackValues=["all","-1","0","1"],n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue,n.configuration.hasOwnProperty("kafkaHeadersCharset")||(n.configuration.kafkaHeadersCharset="UTF-8")},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}i.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(15),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t,n){var a=function(a,i,r,l){var s=o.default;i.html(s),a.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(a.configuration)}),l.$render=function(){a.configuration=l.$viewValue},a.testScript=function(e){var i=angular.copy(a.configuration.jsScript);n.testNodeScript(e,i,"string",t.instant("tb.rulenode.to-string")+"","ToString",["msg","metadata","msgType"],a.ruleNodeId).then(function(e){a.configuration.jsScript=e,l.$setDirty()})},e(i.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:a}}i.$inject=["$compile","$translate","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(16),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t,n){var a=function(a,i,r,l){var s=o.default;i.html(s),a.$mdExpansionPanel=t,a.ruleNodeTypes=n,a.credentialsTypeChanged=function(){var e=a.configuration.credentials.type;a.configuration.credentials={},a.configuration.credentials.type=e,a.updateValidity()},a.certFileAdded=function(e,t){var n=new FileReader;n.onload=function(n){a.$apply(function(){if(n.target.result){l.$setDirty();var i=n.target.result;i&&i.length>0&&("caCert"==t&&(a.configuration.credentials.caCertFileName=e.name,a.configuration.credentials.caCert=i),"privateKey"==t&&(a.configuration.credentials.privateKeyFileName=e.name,a.configuration.credentials.privateKey=i),"Cert"==t&&(a.configuration.credentials.certFileName=e.name,a.configuration.credentials.cert=i)),a.updateValidity()}})},n.readAsText(e.file)},a.clearCertFile=function(e){l.$setDirty(),"caCert"==e&&(a.configuration.credentials.caCertFileName=null,a.configuration.credentials.caCert=null),"privateKey"==e&&(a.configuration.credentials.privateKeyFileName=null,a.configuration.credentials.privateKey=null),"Cert"==e&&(a.configuration.credentials.certFileName=null,a.configuration.credentials.cert=null),a.updateValidity()},a.updateValidity=function(){var e=!0,t=a.configuration.credentials;t.type==n.mqttCredentialTypes["cert.PEM"].value&&(t.caCert&&t.cert&&t.privateKey||(e=!1)),l.$setValidity("Certs",e)},a.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(a.configuration)}),l.$render=function(){a.configuration=l.$viewValue},e(i.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:a}}i.$inject=["$compile","$mdExpansionPanel","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i,n(2);var r=n(17),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e){var t=function(t,n,a,i){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||i.$setViewValue(t.configuration)}),i.$render=function(){t.configuration=i.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}i.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(18),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e){ -var t=function(t,n,a,i){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||i.$setViewValue(t.configuration)}),i.$render=function(){t.configuration=i.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}i.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(19),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.ruleNodeTypes=t,n.serviceAccountFileAdded=function(e){var t=new FileReader;t.onload=function(t){n.$apply(function(){if(t.target.result){r.$setDirty();var a=t.target.result;a&&a.length>0&&(n.configuration.serviceAccountKeyFileName=e.name,n.configuration.serviceAccountKey=a),n.updateValidity()}})},t.readAsText(e.file)},n.clearServiceAccountFile=function(){r.$setDirty(),n.configuration.serviceAccountKeyFileName=null,n.configuration.serviceAccountKey=null,n.updateValidity()},n.updateValidity=function(){var e=!0,t=n.configuration;t.serviceAccountKeyFileName&&t.serviceAccountKey||(e=!1),r.$setValidity("SAKey",e)},n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:n}}i.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(20),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e){var t=function(t,n,a,i){var r=o.default;n.html(r),t.messageProperties=[null,"BASIC","TEXT_PLAIN","MINIMAL_BASIC","MINIMAL_PERSISTENT_BASIC","PERSISTENT_BASIC","PERSISTENT_TEXT_PLAIN"],t.$watch("configuration",function(e,n){angular.equals(e,n)||i.$setViewValue(t.configuration)}),i.$render=function(){t.configuration=i.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:t}}i.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(21),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:n}}i.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(22),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e){var t=function(t,n,a,i){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||i.$setViewValue(t.configuration)}),i.$render=function(){t.configuration=i.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}i.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(23),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e){var t=function(t,n,a,i){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||i.$setViewValue(t.configuration)}),i.$render=function(){t.configuration=i.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}i.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(24),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e){var t=function(t,n,a,i){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||i.$setViewValue(t.configuration)}),i.$render=function(){t.configuration=i.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}i.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(25),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e){var t=function(t,n,a,i){var r=o.default;n.html(r),t.smtpProtocols=["smtp","smtps"],t.tlsVersions=["TLSv1","TLSv1.1","TLSv1.2","TLSv1.3"],t.$watch("configuration",function(e,n){angular.equals(e,n)||i.$setViewValue(t.configuration)}),i.$render=function(){t.configuration=i.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:t}}i.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(26),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e){var t=function(t,n,a,i){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||i.$setViewValue(t.configuration)}),i.$render=function(){t.configuration=i.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}i.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(27),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:n}}i.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(28),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e){var t=function(t,n,a,i){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||i.$setViewValue(t.configuration)}),i.$render=function(){t.configuration=i.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}i.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(29),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}i.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(30),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.types=t,n.$watch("query",function(e,t){angular.equals(e,t)||r.$setViewValue(n.query)}),r.$render=function(){n.query=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}i.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(31),o=a(r)},function(e,t){"use strict";function n(e){var t=function(t,n,a,i){n.html("
"),t.$watch("configuration",function(e,n){angular.equals(e,n)||i.$setViewValue(t.configuration)}),i.$render=function(){t.configuration=i.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}n.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=n},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e){var t=function(t,n,a,i){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||i.$setViewValue(t.configuration)}),i.$render=function(){t.configuration=i.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}i.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(32),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l);var s=186;n.separatorKeys=[t.KEY_CODE.ENTER,t.KEY_CODE.COMMA,s],n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}i.$inject=["$compile","$mdConstant"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(33),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),n.entityDetailsList=[];for(var s in t.entityDetails){var d=s;n.entityDetailsList.push(d)}r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}i.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(34),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t,n){var a=function(a,i,r,l){var s=o.default;i.html(s);var d=186;a.separatorKeys=[t.KEY_CODE.ENTER,t.KEY_CODE.COMMA,d],a.ruleNodeTypes=n,a.aggPeriodTimeUnits={},a.aggPeriodTimeUnits.MINUTES=n.timeUnit.MINUTES,a.aggPeriodTimeUnits.HOURS=n.timeUnit.HOURS,a.aggPeriodTimeUnits.DAYS=n.timeUnit.DAYS,a.aggPeriodTimeUnits.MILLISECONDS=n.timeUnit.MILLISECONDS,a.aggPeriodTimeUnits.SECONDS=n.timeUnit.SECONDS,a.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(a.configuration)}),l.$render=function(){a.configuration=l.$viewValue},e(i.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{},link:a}}i.$inject=["$compile","$mdConstant","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(35),o=a(r);n(3)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var i=n(86),r=a(i),o=n(87),l=a(o),s=n(82),d=a(s),u=n(88),c=a(u),m=n(81),g=a(m),p=n(89),f=a(p),b=n(84),v=a(b),y=n(83),h=a(y);t.default=angular.module("thingsboard.ruleChain.config.enrichment",[]).directive("tbEnrichmentNodeOriginatorAttributesConfig",r.default).directive("tbEnrichmentNodeOriginatorFieldsConfig",l.default).directive("tbEnrichmentNodeDeviceAttributesConfig",d.default).directive("tbEnrichmentNodeRelatedAttributesConfig",c.default).directive("tbEnrichmentNodeCustomerAttributesConfig",g.default).directive("tbEnrichmentNodeTenantAttributesConfig",f.default).directive("tbEnrichmentNodeGetTelemetryFromDatabase",v.default).directive("tbEnrichmentNodeEntityDetailsConfig",h.default).name},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l);var s=186;n.separatorKeys=[t.KEY_CODE.ENTER,t.KEY_CODE.COMMA,s],n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}i.$inject=["$compile","$mdConstant"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(36),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e){var t=function(t,n,a,i){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||i.$setViewValue(t.configuration)}),i.$render=function(){t.configuration=i.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}i.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(37),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e){var t=function(t,n,a,i){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||i.$setViewValue(t.configuration)}),i.$render=function(){t.configuration=i.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}i.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(38),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e){var t=function(t,n,a,i){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||i.$setViewValue(t.configuration)}),i.$render=function(){t.configuration=i.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}i.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(39),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),n.alarmStatusList=[];for(var s in t.alarmStatus)n.alarmStatusList.push(t.alarmStatus[s]);r.$render=function(){n.configuration=r.$viewValue},n.getAlarmStatusList=function(){return n.alarmStatusList.filter(function(e){return n.configuration.alarmStatusList.indexOf(e)===-1})},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{required:"=ngRequired",readonly:"=ngReadonly"},link:n}}i.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(40),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l);var s=186;n.separatorKeys=[t.KEY_CODE.ENTER,t.KEY_CODE.COMMA,s],n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}i.$inject=["$compile","$mdConstant"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(41),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}i.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(42),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:n}}i.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(43),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var i=n(97),r=a(i),o=n(95),l=a(o),s=n(98),d=a(s),u=n(92),c=a(u),m=n(96),g=a(m),p=n(91),f=a(p),b=n(93),v=a(b),y=n(90),h=a(y);t.default=angular.module("thingsboard.ruleChain.config.filter",[]).directive("tbFilterNodeScriptConfig",r.default).directive("tbFilterNodeMessageTypeConfig",l.default).directive("tbFilterNodeSwitchConfig",d.default).directive("tbFilterNodeCheckRelationConfig",c.default).directive("tbFilterNodeOriginatorTypeConfig",g.default).directive("tbFilterNodeCheckMessageConfig",f.default).directive("tbFilterNodeGpsGeofencingConfig",v.default).directive("tbFilterNodeCheckAlarmStatusConfig",h.default).name},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t,n){var a=function(a,i,r,l){function s(){if(l.$viewValue){for(var e=[],t=0;t-1&&t.kvList.splice(e,1)}function l(){t.kvList||(t.kvList=[]),t.kvList.push({key:"",value:""})}function s(){var e={};t.kvList.forEach(function(t){t.key&&(e[t.key]=t.value)}),i.$setViewValue(e),d()}function d(){var e=!0;t.required&&!t.kvList.length&&(e=!1),i.$setValidity("kvMap",e)}var u=o.default;n.html(u),t.ngModelCtrl=i,t.removeKeyVal=r,t.addKeyVal=l,t.kvList=[],t.$watch("query",function(e,n){angular.equals(e,n)||i.$setViewValue(t.query)}),i.$render=function(){if(i.$viewValue){var e=i.$viewValue;t.kvList.length=0;for(var n in e)t.kvList.push({key:n,value:e[n]})}t.$watch("kvList",function(e,t){angular.equals(e,t)||s()},!0),d()},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{required:"=ngRequired",disabled:"=ngDisabled",requiredText:"=",keyText:"=",keyRequiredText:"=",valText:"=",valRequiredText:"="},link:t}}i.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(48),o=a(r);n(5)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.types=t,n.$watch("query",function(e,t){angular.equals(e,t)||r.$setViewValue(n.query)}),r.$render=function(){n.query=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}i.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(49),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}i.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(50),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var i=n(101),r=a(i),o=n(103),l=a(o),s=n(104),d=a(s);t.default=angular.module("thingsboard.ruleChain.config.transform",[]).directive("tbTransformationNodeChangeOriginatorConfig",r.default).directive("tbTransformationNodeScriptConfig",l.default).directive("tbTransformationNodeToEmailConfig",d.default).name},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t,n){var a=function(a,i,r,l){var s=o.default;i.html(s),a.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(a.configuration)}),l.$render=function(){a.configuration=l.$viewValue},a.testScript=function(e){var i=angular.copy(a.configuration.jsScript);n.testNodeScript(e,i,"update",t.instant("tb.rulenode.transformer")+"","Transform",["msg","metadata","msgType"],a.ruleNodeId).then(function(e){a.configuration.jsScript=e,l.$setDirty()})},e(i.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:a}}i.$inject=["$compile","$translate","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(51),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e){var t=function(t,n,a,i){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||i.$setViewValue(t.configuration)}),i.$render=function(){t.configuration=i.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}i.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(52),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var i=n(108),r=a(i),o=n(94),l=a(o),s=n(85),d=a(s),u=n(102),c=a(u),m=n(62),g=a(m),p=n(80),f=a(p),b=n(100),v=a(b),y=n(79),h=a(y),q=n(99),x=a(q),k=n(107),$=a(k);t.default=angular.module("thingsboard.ruleChain.config",[r.default,l.default,d.default,c.default,g.default]).directive("tbNodeEmptyConfig",f.default).directive("tbRelationsQueryConfig",v.default).directive("tbDeviceRelationsQueryConfig",h.default).directive("tbKvMapConfig",x.default).config($.default).name},function(e,t){"use strict";function n(e){var t={tb:{rulenode:{"create-entity-if-not-exists":"Create new entity if not exists","create-entity-if-not-exists-hint":"Create a new entity set above if it does not exist.","entity-name-pattern":"Name pattern","entity-name-pattern-required":"Name pattern is required","entity-name-pattern-hint":"Name pattern, use ${metaKeyName} to substitute variables from metadata","entity-type-pattern":"Type pattern","entity-type-pattern-required":"Type pattern is required","entity-type-pattern-hint":"Type pattern, use ${metaKeyName} to substitute variables from metadata","entity-cache-expiration":"Entities cache expiration time (sec)","entity-cache-expiration-hint":"Specifies maximum time interval allowed to store found entity records. 0 value means that records will never expire.","entity-cache-expiration-required":"Entities cache expiration time is required.","entity-cache-expiration-range":"Entities cache expiration time should be greater than or equal to 0.","customer-name-pattern":"Customer name pattern","customer-name-pattern-required":"Customer name pattern is required","create-customer-if-not-exists":"Create new customer if not exists","customer-cache-expiration":"Customers cache expiration time (sec)","customer-name-pattern-hint":"Customer name pattern, use ${metaKeyName} to substitute variables from metadata","customer-cache-expiration-hint":"Specifies maximum time interval allowed to store found customer records. 0 value means that records will never expire.","customer-cache-expiration-required":"Customers cache expiration time is required.","customer-cache-expiration-range":"Customers cache expiration time should be greater than or equal to 0.","start-interval":"Start Interval","end-interval":"End Interval","start-interval-time-unit":"Start Interval Time Unit","end-interval-time-unit":"End Interval Time Unit","fetch-mode":"Fetch mode","fetch-mode-hint":"If selected fetch mode 'ALL' you able to choose telemetry sampling order.","order-by":"Order by","order-by-hint":"Select to choose telemetry sampling order.",limit:"Limit","limit-hint":"Min limit value is 2, max - 1000. In case you want to fetch a single entry, select fetch mode 'FIRST' or 'LAST'.","time-unit-milliseconds":"Milliseconds","time-unit-seconds":"Seconds","time-unit-minutes":"Minutes","time-unit-hours":"Hours","time-unit-days":"Days","time-value-range":"Time value should be in a range from 1 to 2147483647'.","start-interval-value-required":"Start interval value is required.","end-interval-value-required":"End interval value is required.",filter:"Filter",switch:"Switch","message-type":"Message type","message-type-required":"Message type is required.","message-types-filter":"Message types filter","no-message-types-found":"No message types found","no-message-type-matching":"'{{messageType}}' not found.","create-new-message-type":"Create a new one!","message-types-required":"Message types are required.","client-attributes":"Client attributes","shared-attributes":"Shared attributes","server-attributes":"Server attributes","latest-timeseries":"Latest timeseries","data-keys":"Message data","metadata-keys":"Message metadata","relations-query":"Relations query","device-relations-query":"Device relations query","max-relation-level":"Max relation level","relation-type-pattern":"Relation type pattern","relation-type-pattern-hint":"Relation type pattern, use ${metaKeyName} to substitute variables from metadata","relation-type-pattern-required":"Relation type pattern is required","relation-types-list":"Relation types to propagate","relation-types-list-hint":"If Propagate relation types are not selected, alarms will be propagated without filtering by relation type.","unlimited-level":"Unlimited level","latest-telemetry":"Latest telemetry","attr-mapping":"Attributes mapping","source-attribute":"Source attribute","source-attribute-required":"Source attribute is required.","source-telemetry":"Source telemetry","source-telemetry-required":"Source telemetry is required.","target-attribute":"Target attribute","target-attribute-required":"Target attribute is required.","attr-mapping-required":"At least one attribute mapping should be specified.","fields-mapping":"Fields mapping","fields-mapping-required":"At least one field mapping should be specified.","source-field":"Source field","source-field-required":"Source field is required.","originator-source":"Originator source","originator-customer":"Customer","originator-tenant":"Tenant","originator-related":"Related","originator-alarm-originator":"Alarm Originator","clone-message":"Clone message",transform:"Transform","default-ttl":"Default TTL in seconds","default-ttl-required":"Default TTL is required.","min-default-ttl-message":"Only 0 minimum TTL is allowed.","message-count":"Message count (0 - unlimited)","message-count-required":"Message count is required.","min-message-count-message":"Only 0 minimum message count is allowed.","period-seconds":"Period in seconds","period-seconds-required":"Period is required.","use-metadata-period-in-seconds-patterns":"Use metadata period in seconds pattern","use-metadata-period-in-seconds-patterns-hint":"If selected, rule node use period in seconds interval pattern from message metadata assuming that intervals are in the seconds.","period-in-seconds-pattern":"Period in seconds metadata pattern","period-in-seconds-pattern-required":"Period in seconds pattern is required","period-in-seconds-pattern-hint":"Period in seconds pattern, use ${metaKeyName} to substitute variables from metadata","min-period-seconds-message":"Only 1 second minimum period is allowed.",originator:"Originator","message-body":"Message body","message-metadata":"Message metadata",generate:"Generate","test-generator-function":"Test generator function",generator:"Generator","test-filter-function":"Test filter function","test-switch-function":"Test switch function","test-transformer-function":"Test transformer function",transformer:"Transformer","alarm-create-condition":"Alarm create condition","test-condition-function":"Test condition function","alarm-clear-condition":"Alarm clear condition","alarm-details-builder":"Alarm details builder","test-details-function":"Test details function","alarm-type":"Alarm type","alarm-type-required":"Alarm type is required.","alarm-severity":"Alarm severity","alarm-severity-required":"Alarm severity is required","alarm-statuses-filter":"Alarm statuses filter","alarm-statuses-required":"Alarm statuses is required",propagate:"Propagate",condition:"Condition",details:"Details","to-string":"To string","test-to-string-function":"Test to string function","from-template":"From Template","from-template-required":"From Template is required","from-template-hint":"From address template, use ${metaKeyName} to substitute variables from metadata","to-template":"To Template","to-template-required":"To Template is required","mail-address-list-template-hint":"Comma separated address list, use ${metaKeyName} to substitute variables from metadata","cc-template":"Cc Template","bcc-template":"Bcc Template","subject-template":"Subject Template","subject-template-required":"Subject Template is required","subject-template-hint":"Mail subject template, use ${metaKeyName} to substitute variables from metadata","body-template":"Body Template","body-template-required":"Body Template is required","body-template-hint":"Mail body template, use ${metaKeyName} to substitute variables from metadata","request-id-metadata-attribute":"Request Id Metadata attribute name","timeout-sec":"Timeout in seconds","timeout-required":"Timeout is required","min-timeout-message":"Only 0 minimum timeout value is allowed.","endpoint-url-pattern":"Endpoint URL pattern","endpoint-url-pattern-required":"Endpoint URL pattern is required", -"endpoint-url-pattern-hint":"HTTP URL address pattern, use ${metaKeyName} to substitute variables from metadata","request-method":"Request method","use-simple-client-http-factory":"Use simple client HTTP factory","read-timeout":"Read timeout in millis","read-timeout-hint":"The value of 0 means an infinite timeout","max-parallel-requests-count":"Max number of parallel requests","max-parallel-requests-count-hint":"The value of 0 specifies no limit in parallel processing",headers:"Headers","headers-hint":"Use ${metaKeyName} in header/value fields to substitute variables from metadata",header:"Header","header-required":"Header is required",value:"Value","value-required":"Value is required","topic-pattern":"Topic pattern","topic-pattern-required":"Topic pattern is required","mqtt-topic-pattern-hint":"MQTT topic pattern, use ${metaKeyName} to substitute variables from metadata","bootstrap-servers":"Bootstrap servers","bootstrap-servers-required":"Bootstrap servers value is required","other-properties":"Other properties",key:"Key","key-required":"Key is required",retries:"Automatically retry times if fails","min-retries-message":"Only 0 minimum retries is allowed.","batch-size-bytes":"Produces batch size in bytes","min-batch-size-bytes-message":"Only 0 minimum batch size is allowed.","linger-ms":"Time to buffer locally (ms)","min-linger-ms-message":"Only 0 ms minimum value is allowed.","buffer-memory-bytes":"Client buffer max size in bytes","min-buffer-memory-message":"Only 0 minimum buffer size is allowed.",acks:"Number of acknowledgments","key-serializer":"Key serializer","key-serializer-required":"Key serializer is required","value-serializer":"Value serializer","value-serializer-required":"Value serializer is required","topic-arn-pattern":"Topic ARN pattern","topic-arn-pattern-required":"Topic ARN pattern is required","topic-arn-pattern-hint":"Topic ARN pattern, use ${metaKeyName} to substitute variables from metadata","aws-access-key-id":"AWS Access Key ID","aws-access-key-id-required":"AWS Access Key ID is required","aws-secret-access-key":"AWS Secret Access Key","aws-secret-access-key-required":"AWS Secret Access Key is required","aws-region":"AWS Region","aws-region-required":"AWS Region is required","exchange-name-pattern":"Exchange name pattern","routing-key-pattern":"Routing key pattern","message-properties":"Message properties",host:"Host","host-required":"Host is required",port:"Port","port-required":"Port is required","port-range":"Port should be in a range from 1 to 65535.","virtual-host":"Virtual host",username:"Username",password:"Password","automatic-recovery":"Automatic recovery","connection-timeout-ms":"Connection timeout (ms)","min-connection-timeout-ms-message":"Only 0 ms minimum value is allowed.","handshake-timeout-ms":"Handshake timeout (ms)","min-handshake-timeout-ms-message":"Only 0 ms minimum value is allowed.","client-properties":"Client properties","queue-url-pattern":"Queue URL pattern","queue-url-pattern-required":"Queue URL pattern is required","queue-url-pattern-hint":"Queue URL pattern, use ${metaKeyName} to substitute variables from metadata","delay-seconds":"Delay (seconds)","min-delay-seconds-message":"Only 0 seconds minimum value is allowed.","max-delay-seconds-message":"Only 900 seconds maximum value is allowed.",name:"Name","name-required":"Name is required","queue-type":"Queue type","sqs-queue-standard":"Standard","sqs-queue-fifo":"FIFO","gcp-project-id":"GCP project ID","gcp-project-id-required":"GCP project ID is required","gcp-service-account-key":"GCP service account key file","gcp-service-account-key-required":"GCP service account key file is required","pubsub-topic-name":"Topic name","pubsub-topic-name-required":"Topic name is required","message-attributes":"Message attributes","message-attributes-hint":"Use ${metaKeyName} in name/value fields to substitute variables from metadata","connect-timeout":"Connection timeout (sec)","connect-timeout-required":"Connection timeout is required.","connect-timeout-range":"Connection timeout should be in a range from 1 to 200.","client-id":"Client ID","clean-session":"Clean session","enable-ssl":"Enable SSL",credentials:"Credentials","credentials-type":"Credentials type","credentials-type-required":"Credentials type is required.","credentials-anonymous":"Anonymous","credentials-basic":"Basic","credentials-pem":"PEM","username-required":"Username is required.","password-required":"Password is required.","ca-cert":"CA certificate file *","private-key":"Private key file *",cert:"Certificate file *","no-file":"No file selected.","drop-file":"Drop a file or click to select a file to upload.","private-key-password":"Private key password","use-system-smtp-settings":"Use system SMTP settings","use-metadata-interval-patterns":"Use metadata interval patterns","use-metadata-interval-patterns-hint":"If selected, rule node use start and end interval patterns from message metadata assuming that intervals are in the milliseconds.","use-message-alarm-data":"Use message alarm data","check-all-keys":"Check that all selected keys are present","check-all-keys-hint":"If selected, checks that all specified keys are present in the message data and metadata.","check-relation-to-specific-entity":"Check relation to specific entity","check-relation-hint":"Checks existence of relation to specific entity or to any entity based on direction and relation type.","delete-relation-to-specific-entity":"Delete relation to specific entity","delete-relation-hint":"Deletes relation from the originator of the incoming message to the specified entity or list of entities based on direction and type.","remove-current-relations":"Remove current relations","remove-current-relations-hint":"Removes current relations from the originator of the incoming message based on direction and type.","change-originator-to-related-entity":"Change originator to related entity","change-originator-to-related-entity-hint":"Used to process submitted message as a message from another entity.","start-interval-pattern":"Start interval pattern","end-interval-pattern":"End interval pattern","start-interval-pattern-required":"Start interval pattern is required","end-interval-pattern-required":"End interval pattern is required","start-interval-pattern-hint":"Start interval pattern, use ${metaKeyName} to substitute variables from metadata","end-interval-pattern-hint":"End interval pattern, use ${metaKeyName} to substitute variables from metadata","smtp-protocol":"Protocol","smtp-host":"SMTP host","smtp-host-required":"SMTP host is required.","smtp-port":"SMTP port","smtp-port-required":"You must supply a smtp port.","smtp-port-range":"SMTP port should be in a range from 1 to 65535.","timeout-msec":"Timeout ms","min-timeout-msec-message":"Only 0 ms minimum value is allowed.","enter-username":"Enter username","enter-password":"Enter password","enable-tls":"Enable TLS","enable-proxy":"Enable proxy","proxy-host":"Proxy host","proxy-host-required":"Proxy host is required.","proxy-port":"Proxy port","proxy-port-required":"You must supply a proxy port.","proxy-port-range":"Proxy port should be in a range from 1 to 65535.","proxy-user":"Proxy user","proxy-password":"Proxy password","tls-version":"TLS version","min-period-0-seconds-message":"Only 0 second minimum period is allowed.","max-pending-messages":"Maximum pending messages","max-pending-messages-required":"Maximum pending messages is required.","max-pending-messages-range":"Maximum pending messages should be in a range from 1 to 100000.","originator-types-filter":"Originator types filter","interval-seconds":"Interval in seconds","interval-seconds-required":"Interval is required.","min-interval-seconds-message":"Only 1 second minimum interval is allowed.","output-timeseries-key-prefix":"Output timeseries key prefix","output-timeseries-key-prefix-required":"Output timeseries key prefix required.","separator-hint":'You should press "enter" to complete field input.',"entity-details":"Select entity details:","entity-details-title":"Title","entity-details-country":"Country","entity-details-state":"State","entity-details-zip":"Zip","entity-details-address":"Address","entity-details-address2":"Address2","entity-details-additional_info":"Additional Info","entity-details-phone":"Phone","entity-details-email":"Email","add-to-metadata":"Add selected details to message metadata","add-to-metadata-hint":"If selected, adds the selected details keys to the message metadata instead of message data.","entity-details-list-empty":"No entity details selected.","no-entity-details-matching":"No entity details matching were found.","custom-table-name":"Custom table name","custom-table-name-required":"Table Name is required","custom-table-hint":"You should enter the table name without prefix 'cs_tb_'.","message-field":"Message field","message-field-required":"Message field is required.","table-col":"Table column","table-col-required":"Table column is required.","latitude-key-name":"Latitude key name","longitude-key-name":"Longitude key name","latitude-key-name-required":"Latitude key name is required.","longitude-key-name-required":"Longitude key name is required.","fetch-perimeter-info-from-message-metadata":"Fetch perimeter information from message metadata","perimeter-circle":"Circle","perimeter-polygon":"Polygon","perimeter-type":"Perimeter type","circle-center-latitude":"Center latitude","circle-center-latitude-required":"Center latitude is required.","circle-center-longitude":"Center longitude","circle-center-longitude-required":"Center longitude is required.","range-unit-meter":"Meter","range-unit-kilometer":"Kilometer","range-unit-foot":"Foot","range-unit-mile":"Mile","range-unit-nautical-mile":"Nautical mile","range-units":"Range units",range:"Range","range-required":"Range is required.","polygon-definition":"Polygon definition","polygon-definition-required":"Polygon definition is required.","polygon-definition-hint":"Please, use the following format for manual definition of polygon: [[lat1,lon1],[lat2,lon2], ... ,[latN,lonN]].","min-inside-duration":"Minimal inside duration","min-inside-duration-value-required":"Minimal inside duration is required","min-inside-duration-time-unit":"Minimal inside duration time unit","min-outside-duration":"Minimal outside duration","min-outside-duration-value-required":"Minimal outside duration is required","min-outside-duration-time-unit":"Minimal outside duration time unit","tell-failure-if-absent":"Tell Failure","tell-failure-if-absent-hint":'If at least one selected key doesn\'t exist the outbound message will report "Failure".',"get-latest-value-with-ts":"Fetch Latest telemetry with Timestamp","get-latest-value-with-ts-hint":'If selected, latest telemetry values will be added to the outbound message metadata with timestamp, e.g: "temp": "{\\"ts\\":1574329385897,\\"value\\":42}"',"use-redis-queue":"Use redis queue for message persistence","trim-redis-queue":"Trim redis queue","redis-queue-max-size":"Redis queue max size","add-metadata-key-values-as-kafka-headers":"Add Message metadata key-value pairs to Kafka record headers","add-metadata-key-values-as-kafka-headers-hint":"If selected, key-value pairs from message metadata will be added to the outgoing records headers as byte arrays with predefined charset encoding.","charset-encoding":"Charset encoding","charset-encoding-required":"Charset encoding is required.","charset-us-ascii":"US-ASCII","charset-iso-8859-1":"ISO-8859-1","charset-utf-8":"UTF-8","charset-utf-16be":"UTF-16BE","charset-utf-16le":"UTF-16LE","charset-utf-16":"UTF-16","select-queue-hint":"The queue name can be selected from a drop-down list or add a custom name."},"key-val":{key:"Key",value:"Value","remove-entry":"Remove entry","add-entry":"Add entry"}}};e.translations("en_US",t)}Object.defineProperty(t,"__esModule",{value:!0}),t.default=n},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e){(0,o.default)(e)}i.$inject=["$translateProvider"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(106),o=a(r)},function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=angular.module("thingsboard.ruleChain.config.types",[]).constant("ruleNodeTypes",{originatorSource:{CUSTOMER:{name:"tb.rulenode.originator-customer",value:"CUSTOMER"},TENANT:{name:"tb.rulenode.originator-tenant",value:"TENANT"},RELATED:{name:"tb.rulenode.originator-related",value:"RELATED"},ALARM_ORIGINATOR:{name:"tb.rulenode.originator-alarm-originator",value:"ALARM_ORIGINATOR"}},fetchModeType:["FIRST","LAST","ALL"],samplingOrder:["ASC","DESC"],httpRequestType:["GET","POST","PUT","DELETE"],entityDetails:{TITLE:{name:"tb.rulenode.entity-details-title",value:"TITLE"},COUNTRY:{name:"tb.rulenode.entity-details-country",value:"COUNTRY"},STATE:{name:"tb.rulenode.entity-details-state",value:"STATE"},ZIP:{name:"tb.rulenode.entity-details-zip",value:"ZIP"},ADDRESS:{name:"tb.rulenode.entity-details-address",value:"ADDRESS"},ADDRESS2:{name:"tb.rulenode.entity-details-address2",value:"ADDRESS2"},PHONE:{name:"tb.rulenode.entity-details-phone",value:"PHONE"},EMAIL:{name:"tb.rulenode.entity-details-email",value:"EMAIL"},ADDITIONAL_INFO:{name:"tb.rulenode.entity-details-additional_info",value:"ADDITIONAL_INFO"}},sqsQueueType:{STANDARD:{name:"tb.rulenode.sqs-queue-standard",value:"STANDARD"},FIFO:{name:"tb.rulenode.sqs-queue-fifo",value:"FIFO"}},perimeterType:{CIRCLE:{name:"tb.rulenode.perimeter-circle",value:"CIRCLE"},POLYGON:{name:"tb.rulenode.perimeter-polygon",value:"POLYGON"}},timeUnit:{MILLISECONDS:{value:"MILLISECONDS",name:"tb.rulenode.time-unit-milliseconds"},SECONDS:{value:"SECONDS",name:"tb.rulenode.time-unit-seconds"},MINUTES:{value:"MINUTES",name:"tb.rulenode.time-unit-minutes"},HOURS:{value:"HOURS",name:"tb.rulenode.time-unit-hours"},DAYS:{value:"DAYS",name:"tb.rulenode.time-unit-days"}},rangeUnit:{METER:{value:"METER",name:"tb.rulenode.range-unit-meter"},KILOMETER:{value:"KILOMETER",name:"tb.rulenode.range-unit-kilometer"},FOOT:{value:"FOOT",name:"tb.rulenode.range-unit-foot"},MILE:{value:"MILE",name:"tb.rulenode.range-unit-mile"},NAUTICAL_MILE:{value:"NAUTICAL_MILE",name:"tb.rulenode.range-unit-nautical-mile"}},mqttCredentialTypes:{anonymous:{value:"anonymous",name:"tb.rulenode.credentials-anonymous"},basic:{value:"basic",name:"tb.rulenode.credentials-basic"},"cert.PEM":{value:"cert.PEM",name:"tb.rulenode.credentials-pem"}},toBytesStandartCharsetTypes:{"US-ASCII":{value:"US-ASCII",name:"tb.rulenode.charset-us-ascii"},"ISO-8859-1":{value:"ISO-8859-1",name:"tb.rulenode.charset-iso-8859-1"},"UTF-8":{value:"UTF-8",name:"tb.rulenode.charset-utf-8"},"UTF-16BE":{value:"UTF-16BE",name:"tb.rulenode.charset-utf-16be"},"UTF-16LE":{value:"UTF-16LE",name:"tb.rulenode.charset-utf-16le"},"UTF-16":{value:"UTF-16",name:"tb.rulenode.charset-utf-16"}}}).name}])); +},function(e,t){e.exports="
tb.rulenode.interval-seconds-required
tb.rulenode.min-interval-seconds-message
tb.rulenode.output-timeseries-key-prefix-required
"},function(e,t){e.exports='
{{ \'tb.rulenode.use-metadata-period-in-seconds-patterns\' | translate }}
tb.rulenode.use-metadata-period-in-seconds-patterns-hint
tb.rulenode.period-seconds-required
tb.rulenode.min-period-0-seconds-message
tb.rulenode.period-in-seconds-pattern-required
tb.rulenode.period-in-seconds-pattern-hint
tb.rulenode.max-pending-messages-required
tb.rulenode.max-pending-messages-range
tb.rulenode.max-pending-messages-range
'},function(e,t){e.exports="
tb.rulenode.gcp-project-id-required
tb.rulenode.pubsub-topic-name-required
{{ 'action.remove' | translate }} close
tb.rulenode.message-attributes-hint
"},function(e,t){e.exports='
{{ property }}
tb.rulenode.host-required
tb.rulenode.port-required
tb.rulenode.port-range
tb.rulenode.port-range
{{ \'tb.rulenode.automatic-recovery\' | translate }}
tb.rulenode.min-connection-timeout-ms-message
tb.rulenode.min-handshake-timeout-ms-message
'},function(e,t){e.exports='
tb.rulenode.endpoint-url-pattern-required
tb.rulenode.endpoint-url-pattern-hint
{{ type }} {{ \'tb.rulenode.enable-proxy\' | translate }} {{ \'tb.rulenode.use-simple-client-http-factory\' | translate }}
{{ \'tb.rulenode.use-system-proxy-properties\' | translate }}
{{proxyScheme}}
tb.rulenode.proxy-host-required
tb.rulenode.proxy-port-required
tb.rulenode.proxy-port-range
tb.rulenode.proxy-port-range
tb.rulenode.read-timeout-hint
tb.rulenode.max-parallel-requests-count-hint
tb.rulenode.headers-hint
{{ \'tb.rulenode.use-redis-queue\' | translate }}
{{ \'tb.rulenode.trim-redis-queue\' | translate }}
'},function(e,t){e.exports="
"},function(e,t){e.exports="
tb.rulenode.timeout-required
tb.rulenode.min-timeout-message
"},function(e,t){e.exports='
tb.rulenode.custom-table-name-required
tb.rulenode.custom-table-hint
'},function(e,t){e.exports='
{{ \'tb.rulenode.use-system-smtp-settings\' | translate }}
{{smtpProtocol.toUpperCase()}}
tb.rulenode.smtp-host-required
tb.rulenode.smtp-port-required
tb.rulenode.smtp-port-range
tb.rulenode.smtp-port-range
tb.rulenode.timeout-required
tb.rulenode.min-timeout-msec-message
{{ \'tb.rulenode.enable-tls\' | translate }} {{tlsVersion}} {{ \'tb.rulenode.enable-proxy\' | translate }}
tb.rulenode.proxy-host-required
tb.rulenode.proxy-port-required
tb.rulenode.proxy-port-range
tb.rulenode.proxy-port-range
'},function(e,t){e.exports="
tb.rulenode.topic-arn-pattern-required
tb.rulenode.topic-arn-pattern-hint
tb.rulenode.aws-access-key-id-required
tb.rulenode.aws-secret-access-key-required
tb.rulenode.aws-region-required
"},function(e,t){e.exports='
{{ type.name | translate }}
tb.rulenode.queue-url-pattern-required
tb.rulenode.queue-url-pattern-hint
tb.rulenode.min-delay-seconds-message
tb.rulenode.max-delay-seconds-message
tb.rulenode.message-attributes-hint
tb.rulenode.aws-access-key-id-required
tb.rulenode.aws-secret-access-key-required
tb.rulenode.aws-region-required
'},function(e,t){e.exports="
tb.rulenode.default-ttl-required
tb.rulenode.min-default-ttl-message
"},function(e,t){e.exports="
tb.rulenode.customer-name-pattern-required
tb.rulenode.customer-name-pattern-hint
tb.rulenode.customer-cache-expiration-required
tb.rulenode.customer-cache-expiration-range
tb.rulenode.customer-cache-expiration-hint
"},function(e,t){e.exports="
{{ 'alias.last-level-relation' | translate}}
{{ ('relation.search-direction.' + direction) | translate}}
relation.relation-type
device.device-types
"},function(e,t){e.exports="
{{ 'tb.rulenode.latest-telemetry' | translate }}
"},function(e,t){e.exports='
{{ \'tb.rulenode.tell-failure-if-absent\' | translate }}
tb.rulenode.tell-failure-if-absent-hint
{{ \'tb.rulenode.get-latest-value-with-ts\' | translate }}
tb.rulenode.get-latest-value-with-ts-hint
'},function(e,t){e.exports='
{{\'tb.rulenode.entity-details-\'+item.toLowerCase() | translate}} tb.rulenode.no-entity-details-matching {{\'tb.rulenode.entity-details-\'+$chip.toLowerCase() | translate}} {{ \'tb.rulenode.add-to-metadata\' | translate }}
tb.rulenode.add-to-metadata-hint
'; +},function(e,t){e.exports='
{{ type }}
tb.rulenode.fetch-mode-hint
{{ type }}
tb.rulenode.order-by-hint
tb.rulenode.limit-hint
{{ \'tb.rulenode.use-metadata-interval-patterns\' | translate }}
tb.rulenode.use-metadata-interval-patterns-hint
tb.rulenode.start-interval-value-required
tb.rulenode.time-value-range
tb.rulenode.time-value-range
{{timeUnit.name | translate}}
tb.rulenode.end-interval-value-required
tb.rulenode.time-value-range
tb.rulenode.time-value-range
{{timeUnit.name | translate}}
tb.rulenode.start-interval-pattern-required
tb.rulenode.start-interval-pattern-hint
tb.rulenode.end-interval-pattern-required
tb.rulenode.end-interval-pattern-hint
'},function(e,t){e.exports='
{{ \'tb.rulenode.tell-failure-if-absent\' | translate }}
tb.rulenode.tell-failure-if-absent-hint
{{ \'tb.rulenode.get-latest-value-with-ts\' | translate }}
tb.rulenode.get-latest-value-with-ts-hint
'},function(e,t){e.exports='
'},function(e,t){e.exports="
{{ 'tb.rulenode.latest-telemetry' | translate }}
"},32,function(e,t){e.exports="
{{'alarm.display-status.' + item | translate}} {{'alarm.display-status.' + $chip | translate}}
"},function(e,t){e.exports='
tb.rulenode.separator-hint
tb.rulenode.separator-hint
{{ \'tb.rulenode.check-all-keys\' | translate }}
tb.rulenode.check-all-keys-hint
'},function(e,t){e.exports="
{{ 'tb.rulenode.check-relation-to-specific-entity' | translate }}
tb.rulenode.check-relation-hint
{{ ('relation.search-direction.' + direction) | translate}}
"},function(e,t){e.exports='
tb.rulenode.latitude-key-name-required
tb.rulenode.longitude-key-name-required
{{ \'tb.rulenode.fetch-perimeter-info-from-message-metadata\' | translate }}
{{ type.name | translate}}
tb.rulenode.circle-center-latitude-required
tb.rulenode.circle-center-longitude-required
tb.rulenode.range-required
{{ type.name | translate}}
tb.rulenode.polygon-definition-required
tb.rulenode.polygon-definition-hint
'},function(e,t){e.exports='
{{item}}
tb.rulenode.no-message-types-found
tb.rulenode.no-message-type-matching tb.rulenode.create-new-message-type
{{$chip.name}}
'},function(e,t){e.exports='
'},function(e,t){e.exports="
{{ 'tb.rulenode.test-filter-function' | translate }}
"},function(e,t){e.exports="
{{ 'tb.rulenode.test-switch-function' | translate }}
"},function(e,t){e.exports='
{{ keyText }} {{ valText }}  
{{keyRequiredText}}
{{valRequiredText}}
{{ \'tb.key-val.remove-entry\' | translate }} close
{{ \'tb.key-val.add-entry\' | translate }} add {{ \'action.add\' | translate }}
'},function(e,t){e.exports="
{{ 'alias.last-level-relation' | translate}}
{{ ('relation.search-direction.' + direction) | translate}}
relation.relation-filters
"},function(e,t){e.exports='
{{ source.name | translate}}
'},function(e,t){e.exports="
{{ 'tb.rulenode.test-transformer-function' | translate }}
"},function(e,t){e.exports="
tb.rulenode.from-template-required
tb.rulenode.from-template-hint
tb.rulenode.to-template-required
tb.rulenode.mail-address-list-template-hint
tb.rulenode.mail-address-list-template-hint
tb.rulenode.mail-address-list-template-hint
tb.rulenode.subject-template-required
tb.rulenode.subject-template-hint
tb.rulenode.body-template-required
tb.rulenode.body-template-hint
"},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}i.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(6),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}i.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(7),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.types=t,n.serviceType="TB_RULE_ENGINE",n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}i.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(8),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t,n,a){var i=function(i,r,l,s){var d=o.default;r.html(d),i.types=n,i.$watch("configuration",function(e,t){angular.equals(e,t)||s.$setViewValue(i.configuration)}),s.$render=function(){i.configuration=s.$viewValue},i.testDetailsBuildJs=function(e){var n=angular.copy(i.configuration.alarmDetailsBuildJs);a.testNodeScript(e,n,"json",t.instant("tb.rulenode.details")+"","Details",["msg","metadata","msgType"],i.ruleNodeId).then(function(e){i.configuration.alarmDetailsBuildJs=e,s.$setDirty()})},e(r.contents())(i)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:i}}i.$inject=["$compile","$translate","types","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(9),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t,n,a){var i=function(i,r,l,s){var d=o.default;r.html(d),i.types=n,i.$watch("configuration",function(e,t){angular.equals(e,t)||s.$setViewValue(i.configuration)}),s.$render=function(){i.configuration=s.$viewValue,i.configuration.hasOwnProperty("relationTypes")||(i.configuration.relationTypes=[])},i.testDetailsBuildJs=function(e){var n=angular.copy(i.configuration.alarmDetailsBuildJs);a.testNodeScript(e,n,"json",t.instant("tb.rulenode.details")+"","Details",["msg","metadata","msgType"],i.ruleNodeId).then(function(e){i.configuration.alarmDetailsBuildJs=e,s.$setDirty()})},e(r.contents())(i)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:i}}i.$inject=["$compile","$translate","types","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(10),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}i.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(11),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}i.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(12),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t,n,a){var i=function(i,r,l,s){var d=o.default;r.html(d),i.types=n,i.originator=null,i.$watch("configuration",function(e,t){angular.equals(e,t)||s.$setViewValue(i.configuration)}),s.$render=function(){i.configuration=s.$viewValue,i.configuration.originatorId&&i.configuration.originatorType?i.originator={id:i.configuration.originatorId,entityType:i.configuration.originatorType}:i.originator=null,i.$watch("originator",function(e,t){angular.equals(e,t)||(i.originator?(s.$viewValue.originatorId=i.originator.id,s.$viewValue.originatorType=i.originator.entityType):(s.$viewValue.originatorId=null,s.$viewValue.originatorType=null))},!0)},i.testScript=function(e){var n=angular.copy(i.configuration.jsScript);a.testNodeScript(e,n,"generate",t.instant("tb.rulenode.generator")+"","Generate",["prevMsg","prevMetadata","prevMsgType"],i.ruleNodeId).then(function(e){i.configuration.jsScript=e,s.$setDirty()})},e(r.contents())(i)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:i}}i.$inject=["$compile","$translate","types","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i,n(1);var r=n(13),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:n}}i.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(14),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var i=n(77),r=a(i),o=n(54),l=a(o),s=n(60),d=a(s),u=n(57),c=a(u),m=n(56),g=a(m),p=n(64),f=a(p),b=n(71),v=a(b),y=n(72),h=a(y),q=n(70),x=a(q),k=n(63),$=a(k),T=n(75),C=a(T),w=n(76),M=a(w),S=n(69),N=a(S),P=n(65),_=a(P),F=n(74),E=a(F),A=n(67),V=a(A),I=n(66),j=a(I),O=n(53),D=a(O),L=n(78),R=a(L),K=n(59),U=a(K),z=n(58),H=a(z),B=n(73),G=a(B),Y=n(61),Q=a(Y),W=n(68),J=a(W),Z=n(55),X=a(Z); +t.default=angular.module("thingsboard.ruleChain.config.action",[]).directive("tbActionNodeTimeseriesConfig",r.default).directive("tbActionNodeAttributesConfig",l.default).directive("tbActionNodeGeneratorConfig",d.default).directive("tbActionNodeCreateAlarmConfig",c.default).directive("tbActionNodeClearAlarmConfig",g.default).directive("tbActionNodeLogConfig",f.default).directive("tbActionNodeRpcReplyConfig",v.default).directive("tbActionNodeRpcRequestConfig",h.default).directive("tbActionNodeRestApiCallConfig",x.default).directive("tbActionNodeKafkaConfig",$.default).directive("tbActionNodeSnsConfig",C.default).directive("tbActionNodeSqsConfig",M.default).directive("tbActionNodeRabbitMqConfig",N.default).directive("tbActionNodeMqttConfig",_.default).directive("tbActionNodeSendEmailConfig",E.default).directive("tbActionNodeMsgDelayConfig",V.default).directive("tbActionNodeMsgCountConfig",j.default).directive("tbActionNodeAssignToCustomerConfig",D.default).directive("tbActionNodeUnAssignToCustomerConfig",R.default).directive("tbActionNodeDeleteRelationConfig",U.default).directive("tbActionNodeCreateRelationConfig",H.default).directive("tbActionNodeCustomTableConfig",G.default).directive("tbActionNodeGpsGeofencingConfig",Q.default).directive("tbActionNodePubSubConfig",J.default).directive("tbActionNodeCheckPointConfig",X.default).name},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.ackValues=["all","-1","0","1"],n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue,n.configuration.hasOwnProperty("kafkaHeadersCharset")||(n.configuration.kafkaHeadersCharset="UTF-8")},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}i.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(15),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t,n){var a=function(a,i,r,l){var s=o.default;i.html(s),a.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(a.configuration)}),l.$render=function(){a.configuration=l.$viewValue},a.testScript=function(e){var i=angular.copy(a.configuration.jsScript);n.testNodeScript(e,i,"string",t.instant("tb.rulenode.to-string")+"","ToString",["msg","metadata","msgType"],a.ruleNodeId).then(function(e){a.configuration.jsScript=e,l.$setDirty()})},e(i.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:a}}i.$inject=["$compile","$translate","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(16),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t,n){var a=function(a,i,r,l){var s=o.default;i.html(s),a.$mdExpansionPanel=t,a.ruleNodeTypes=n,a.credentialsTypeChanged=function(){var e=a.configuration.credentials.type;a.configuration.credentials={},a.configuration.credentials.type=e,a.updateValidity()},a.certFileAdded=function(e,t){var n=new FileReader;n.onload=function(n){a.$apply(function(){if(n.target.result){l.$setDirty();var i=n.target.result;i&&i.length>0&&("caCert"==t&&(a.configuration.credentials.caCertFileName=e.name,a.configuration.credentials.caCert=i),"privateKey"==t&&(a.configuration.credentials.privateKeyFileName=e.name,a.configuration.credentials.privateKey=i),"Cert"==t&&(a.configuration.credentials.certFileName=e.name,a.configuration.credentials.cert=i)),a.updateValidity()}})},n.readAsText(e.file)},a.clearCertFile=function(e){l.$setDirty(),"caCert"==e&&(a.configuration.credentials.caCertFileName=null,a.configuration.credentials.caCert=null),"privateKey"==e&&(a.configuration.credentials.privateKeyFileName=null,a.configuration.credentials.privateKey=null),"Cert"==e&&(a.configuration.credentials.certFileName=null,a.configuration.credentials.cert=null),a.updateValidity()},a.updateValidity=function(){var e=!0,t=a.configuration.credentials;t.type==n.mqttCredentialTypes["cert.PEM"].value&&(t.caCert&&t.cert&&t.privateKey||(e=!1)),l.$setValidity("Certs",e)},a.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(a.configuration)}),l.$render=function(){a.configuration=l.$viewValue},e(i.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:a}}i.$inject=["$compile","$mdExpansionPanel","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i,n(2);var r=n(17),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e){var t=function(t,n,a,i){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||i.$setViewValue(t.configuration)}),i.$render=function(){t.configuration=i.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}i.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(18),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e){var t=function(t,n,a,i){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||i.$setViewValue(t.configuration)}),i.$render=function(){t.configuration=i.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}i.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(19),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.ruleNodeTypes=t,n.serviceAccountFileAdded=function(e){var t=new FileReader;t.onload=function(t){n.$apply(function(){if(t.target.result){r.$setDirty();var a=t.target.result;a&&a.length>0&&(n.configuration.serviceAccountKeyFileName=e.name,n.configuration.serviceAccountKey=a),n.updateValidity()}})},t.readAsText(e.file)},n.clearServiceAccountFile=function(){r.$setDirty(),n.configuration.serviceAccountKeyFileName=null,n.configuration.serviceAccountKey=null,n.updateValidity()},n.updateValidity=function(){var e=!0,t=n.configuration;t.serviceAccountKeyFileName&&t.serviceAccountKey||(e=!1),r.$setValidity("SAKey",e)},n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:n}}i.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(20),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e){var t=function(t,n,a,i){var r=o.default;n.html(r),t.messageProperties=[null,"BASIC","TEXT_PLAIN","MINIMAL_BASIC","MINIMAL_PERSISTENT_BASIC","PERSISTENT_BASIC","PERSISTENT_TEXT_PLAIN"],t.$watch("configuration",function(e,n){angular.equals(e,n)||i.$setViewValue(t.configuration)}),i.$render=function(){t.configuration=i.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:t}}i.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(21),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.ruleNodeTypes=t,n.proxySchemes=["http","https"],n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:n}}i.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(22),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e){var t=function(t,n,a,i){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||i.$setViewValue(t.configuration)}),i.$render=function(){t.configuration=i.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}i.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(23),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e){var t=function(t,n,a,i){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||i.$setViewValue(t.configuration)}),i.$render=function(){t.configuration=i.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}i.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(24),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e){var t=function(t,n,a,i){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||i.$setViewValue(t.configuration)}),i.$render=function(){t.configuration=i.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}i.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(25),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e){var t=function(t,n,a,i){var r=o.default;n.html(r),t.smtpProtocols=["smtp","smtps"],t.tlsVersions=["TLSv1","TLSv1.1","TLSv1.2","TLSv1.3"],t.$watch("configuration",function(e,n){angular.equals(e,n)||i.$setViewValue(t.configuration)}),i.$render=function(){t.configuration=i.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:t}}i.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(26),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e){var t=function(t,n,a,i){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||i.$setViewValue(t.configuration)}),i.$render=function(){t.configuration=i.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}i.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(27),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:n}}i.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(28),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e){var t=function(t,n,a,i){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||i.$setViewValue(t.configuration)}),i.$render=function(){t.configuration=i.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}i.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(29),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}i.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(30),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.types=t,n.$watch("query",function(e,t){angular.equals(e,t)||r.$setViewValue(n.query)}),r.$render=function(){n.query=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}i.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(31),o=a(r)},function(e,t){"use strict";function n(e){var t=function(t,n,a,i){n.html("
"),t.$watch("configuration",function(e,n){angular.equals(e,n)||i.$setViewValue(t.configuration)}),i.$render=function(){t.configuration=i.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}n.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=n},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e){var t=function(t,n,a,i){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||i.$setViewValue(t.configuration)}),i.$render=function(){t.configuration=i.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}i.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(32),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l);var s=186;n.separatorKeys=[t.KEY_CODE.ENTER,t.KEY_CODE.COMMA,s],n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}i.$inject=["$compile","$mdConstant"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(33),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),n.entityDetailsList=[];for(var s in t.entityDetails){var d=s;n.entityDetailsList.push(d)}r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}i.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(34),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t,n){var a=function(a,i,r,l){var s=o.default;i.html(s);var d=186;a.separatorKeys=[t.KEY_CODE.ENTER,t.KEY_CODE.COMMA,d],a.ruleNodeTypes=n,a.aggPeriodTimeUnits={},a.aggPeriodTimeUnits.MINUTES=n.timeUnit.MINUTES,a.aggPeriodTimeUnits.HOURS=n.timeUnit.HOURS,a.aggPeriodTimeUnits.DAYS=n.timeUnit.DAYS,a.aggPeriodTimeUnits.MILLISECONDS=n.timeUnit.MILLISECONDS,a.aggPeriodTimeUnits.SECONDS=n.timeUnit.SECONDS,a.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(a.configuration)}),l.$render=function(){a.configuration=l.$viewValue},e(i.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{},link:a}}i.$inject=["$compile","$mdConstant","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(35),o=a(r);n(3)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var i=n(86),r=a(i),o=n(87),l=a(o),s=n(82),d=a(s),u=n(88),c=a(u),m=n(81),g=a(m),p=n(89),f=a(p),b=n(84),v=a(b),y=n(83),h=a(y);t.default=angular.module("thingsboard.ruleChain.config.enrichment",[]).directive("tbEnrichmentNodeOriginatorAttributesConfig",r.default).directive("tbEnrichmentNodeOriginatorFieldsConfig",l.default).directive("tbEnrichmentNodeDeviceAttributesConfig",d.default).directive("tbEnrichmentNodeRelatedAttributesConfig",c.default).directive("tbEnrichmentNodeCustomerAttributesConfig",g.default).directive("tbEnrichmentNodeTenantAttributesConfig",f.default).directive("tbEnrichmentNodeGetTelemetryFromDatabase",v.default).directive("tbEnrichmentNodeEntityDetailsConfig",h.default).name},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l);var s=186;n.separatorKeys=[t.KEY_CODE.ENTER,t.KEY_CODE.COMMA,s],n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}i.$inject=["$compile","$mdConstant"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(36),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e){var t=function(t,n,a,i){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||i.$setViewValue(t.configuration)}),i.$render=function(){t.configuration=i.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}i.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(37),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e){var t=function(t,n,a,i){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||i.$setViewValue(t.configuration)}),i.$render=function(){t.configuration=i.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}i.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(38),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e){var t=function(t,n,a,i){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||i.$setViewValue(t.configuration)}),i.$render=function(){t.configuration=i.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}i.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(39),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),n.alarmStatusList=[];for(var s in t.alarmStatus)n.alarmStatusList.push(t.alarmStatus[s]);r.$render=function(){n.configuration=r.$viewValue},n.getAlarmStatusList=function(){return n.alarmStatusList.filter(function(e){return n.configuration.alarmStatusList.indexOf(e)===-1})},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{required:"=ngRequired",readonly:"=ngReadonly"},link:n}}i.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(40),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l);var s=186;n.separatorKeys=[t.KEY_CODE.ENTER,t.KEY_CODE.COMMA,s],n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}i.$inject=["$compile","$mdConstant"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(41),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}i.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(42),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:n}}i.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(43),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var i=n(97),r=a(i),o=n(95),l=a(o),s=n(98),d=a(s),u=n(92),c=a(u),m=n(96),g=a(m),p=n(91),f=a(p),b=n(93),v=a(b),y=n(90),h=a(y);t.default=angular.module("thingsboard.ruleChain.config.filter",[]).directive("tbFilterNodeScriptConfig",r.default).directive("tbFilterNodeMessageTypeConfig",l.default).directive("tbFilterNodeSwitchConfig",d.default).directive("tbFilterNodeCheckRelationConfig",c.default).directive("tbFilterNodeOriginatorTypeConfig",g.default).directive("tbFilterNodeCheckMessageConfig",f.default).directive("tbFilterNodeGpsGeofencingConfig",v.default).directive("tbFilterNodeCheckAlarmStatusConfig",h.default).name},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t,n){var a=function(a,i,r,l){function s(){if(l.$viewValue){for(var e=[],t=0;t-1&&t.kvList.splice(e,1)}function l(){t.kvList||(t.kvList=[]),t.kvList.push({key:"",value:""})}function s(){var e={};t.kvList.forEach(function(t){t.key&&(e[t.key]=t.value)}),i.$setViewValue(e),d()}function d(){var e=!0;t.required&&!t.kvList.length&&(e=!1),i.$setValidity("kvMap",e)}var u=o.default;n.html(u),t.ngModelCtrl=i,t.removeKeyVal=r,t.addKeyVal=l,t.kvList=[],t.$watch("query",function(e,n){angular.equals(e,n)||i.$setViewValue(t.query)}),i.$render=function(){if(i.$viewValue){var e=i.$viewValue;t.kvList.length=0;for(var n in e)t.kvList.push({key:n,value:e[n]})}t.$watch("kvList",function(e,t){angular.equals(e,t)||s()},!0),d()},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{required:"=ngRequired",disabled:"=ngDisabled",requiredText:"=",keyText:"=",keyRequiredText:"=",valText:"=",valRequiredText:"="},link:t}}i.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(48),o=a(r);n(5)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.types=t,n.$watch("query",function(e,t){angular.equals(e,t)||r.$setViewValue(n.query)}),r.$render=function(){n.query=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}i.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(49),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}i.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(50),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var i=n(101),r=a(i),o=n(103),l=a(o),s=n(104),d=a(s);t.default=angular.module("thingsboard.ruleChain.config.transform",[]).directive("tbTransformationNodeChangeOriginatorConfig",r.default).directive("tbTransformationNodeScriptConfig",l.default).directive("tbTransformationNodeToEmailConfig",d.default).name},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t,n){var a=function(a,i,r,l){var s=o.default;i.html(s),a.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(a.configuration)}),l.$render=function(){a.configuration=l.$viewValue},a.testScript=function(e){var i=angular.copy(a.configuration.jsScript);n.testNodeScript(e,i,"update",t.instant("tb.rulenode.transformer")+"","Transform",["msg","metadata","msgType"],a.ruleNodeId).then(function(e){a.configuration.jsScript=e,l.$setDirty()})},e(i.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:a}}i.$inject=["$compile","$translate","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(51),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e){var t=function(t,n,a,i){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||i.$setViewValue(t.configuration)}),i.$render=function(){t.configuration=i.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}i.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(52),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var i=n(108),r=a(i),o=n(94),l=a(o),s=n(85),d=a(s),u=n(102),c=a(u),m=n(62),g=a(m),p=n(80),f=a(p),b=n(100),v=a(b),y=n(79),h=a(y),q=n(99),x=a(q),k=n(107),$=a(k);t.default=angular.module("thingsboard.ruleChain.config",[r.default,l.default,d.default,c.default,g.default]).directive("tbNodeEmptyConfig",f.default).directive("tbRelationsQueryConfig",v.default).directive("tbDeviceRelationsQueryConfig",h.default).directive("tbKvMapConfig",x.default).config($.default).name},function(e,t){"use strict";function n(e){var t={tb:{rulenode:{"create-entity-if-not-exists":"Create new entity if not exists","create-entity-if-not-exists-hint":"Create a new entity set above if it does not exist.","entity-name-pattern":"Name pattern","entity-name-pattern-required":"Name pattern is required","entity-name-pattern-hint":"Name pattern, use ${metaKeyName} to substitute variables from metadata","entity-type-pattern":"Type pattern","entity-type-pattern-required":"Type pattern is required","entity-type-pattern-hint":"Type pattern, use ${metaKeyName} to substitute variables from metadata","entity-cache-expiration":"Entities cache expiration time (sec)","entity-cache-expiration-hint":"Specifies maximum time interval allowed to store found entity records. 0 value means that records will never expire.","entity-cache-expiration-required":"Entities cache expiration time is required.","entity-cache-expiration-range":"Entities cache expiration time should be greater than or equal to 0.","customer-name-pattern":"Customer name pattern","customer-name-pattern-required":"Customer name pattern is required","create-customer-if-not-exists":"Create new customer if not exists","customer-cache-expiration":"Customers cache expiration time (sec)","customer-name-pattern-hint":"Customer name pattern, use ${metaKeyName} to substitute variables from metadata","customer-cache-expiration-hint":"Specifies maximum time interval allowed to store found customer records. 0 value means that records will never expire.","customer-cache-expiration-required":"Customers cache expiration time is required.","customer-cache-expiration-range":"Customers cache expiration time should be greater than or equal to 0.","start-interval":"Start Interval","end-interval":"End Interval","start-interval-time-unit":"Start Interval Time Unit","end-interval-time-unit":"End Interval Time Unit","fetch-mode":"Fetch mode","fetch-mode-hint":"If selected fetch mode 'ALL' you able to choose telemetry sampling order.","order-by":"Order by","order-by-hint":"Select to choose telemetry sampling order.",limit:"Limit","limit-hint":"Min limit value is 2, max - 1000. In case you want to fetch a single entry, select fetch mode 'FIRST' or 'LAST'.","time-unit-milliseconds":"Milliseconds","time-unit-seconds":"Seconds","time-unit-minutes":"Minutes","time-unit-hours":"Hours","time-unit-days":"Days","time-value-range":"Time value should be in a range from 1 to 2147483647'.","start-interval-value-required":"Start interval value is required.","end-interval-value-required":"End interval value is required.",filter:"Filter",switch:"Switch","message-type":"Message type", +"message-type-required":"Message type is required.","message-types-filter":"Message types filter","no-message-types-found":"No message types found","no-message-type-matching":"'{{messageType}}' not found.","create-new-message-type":"Create a new one!","message-types-required":"Message types are required.","client-attributes":"Client attributes","shared-attributes":"Shared attributes","server-attributes":"Server attributes","latest-timeseries":"Latest timeseries","data-keys":"Message data","metadata-keys":"Message metadata","relations-query":"Relations query","device-relations-query":"Device relations query","max-relation-level":"Max relation level","relation-type-pattern":"Relation type pattern","relation-type-pattern-hint":"Relation type pattern, use ${metaKeyName} to substitute variables from metadata","relation-type-pattern-required":"Relation type pattern is required","relation-types-list":"Relation types to propagate","relation-types-list-hint":"If Propagate relation types are not selected, alarms will be propagated without filtering by relation type.","unlimited-level":"Unlimited level","latest-telemetry":"Latest telemetry","attr-mapping":"Attributes mapping","source-attribute":"Source attribute","source-attribute-required":"Source attribute is required.","source-telemetry":"Source telemetry","source-telemetry-required":"Source telemetry is required.","target-attribute":"Target attribute","target-attribute-required":"Target attribute is required.","attr-mapping-required":"At least one attribute mapping should be specified.","fields-mapping":"Fields mapping","fields-mapping-required":"At least one field mapping should be specified.","source-field":"Source field","source-field-required":"Source field is required.","originator-source":"Originator source","originator-customer":"Customer","originator-tenant":"Tenant","originator-related":"Related","originator-alarm-originator":"Alarm Originator","clone-message":"Clone message",transform:"Transform","default-ttl":"Default TTL in seconds","default-ttl-required":"Default TTL is required.","min-default-ttl-message":"Only 0 minimum TTL is allowed.","message-count":"Message count (0 - unlimited)","message-count-required":"Message count is required.","min-message-count-message":"Only 0 minimum message count is allowed.","period-seconds":"Period in seconds","period-seconds-required":"Period is required.","use-metadata-period-in-seconds-patterns":"Use metadata period in seconds pattern","use-metadata-period-in-seconds-patterns-hint":"If selected, rule node use period in seconds interval pattern from message metadata assuming that intervals are in the seconds.","period-in-seconds-pattern":"Period in seconds metadata pattern","period-in-seconds-pattern-required":"Period in seconds pattern is required","period-in-seconds-pattern-hint":"Period in seconds pattern, use ${metaKeyName} to substitute variables from metadata","min-period-seconds-message":"Only 1 second minimum period is allowed.",originator:"Originator","message-body":"Message body","message-metadata":"Message metadata",generate:"Generate","test-generator-function":"Test generator function",generator:"Generator","test-filter-function":"Test filter function","test-switch-function":"Test switch function","test-transformer-function":"Test transformer function",transformer:"Transformer","alarm-create-condition":"Alarm create condition","test-condition-function":"Test condition function","alarm-clear-condition":"Alarm clear condition","alarm-details-builder":"Alarm details builder","test-details-function":"Test details function","alarm-type":"Alarm type","alarm-type-required":"Alarm type is required.","alarm-severity":"Alarm severity","alarm-severity-required":"Alarm severity is required","alarm-statuses-filter":"Alarm statuses filter","alarm-statuses-required":"Alarm statuses is required",propagate:"Propagate",condition:"Condition",details:"Details","to-string":"To string","test-to-string-function":"Test to string function","from-template":"From Template","from-template-required":"From Template is required","from-template-hint":"From address template, use ${metaKeyName} to substitute variables from metadata","to-template":"To Template","to-template-required":"To Template is required","mail-address-list-template-hint":"Comma separated address list, use ${metaKeyName} to substitute variables from metadata","cc-template":"Cc Template","bcc-template":"Bcc Template","subject-template":"Subject Template","subject-template-required":"Subject Template is required","subject-template-hint":"Mail subject template, use ${metaKeyName} to substitute variables from metadata","body-template":"Body Template","body-template-required":"Body Template is required","body-template-hint":"Mail body template, use ${metaKeyName} to substitute variables from metadata","request-id-metadata-attribute":"Request Id Metadata attribute name","timeout-sec":"Timeout in seconds","timeout-required":"Timeout is required","min-timeout-message":"Only 0 minimum timeout value is allowed.","endpoint-url-pattern":"Endpoint URL pattern","endpoint-url-pattern-required":"Endpoint URL pattern is required","endpoint-url-pattern-hint":"HTTP URL address pattern, use ${metaKeyName} to substitute variables from metadata","request-method":"Request method","use-simple-client-http-factory":"Use simple client HTTP factory","read-timeout":"Read timeout in millis","read-timeout-hint":"The value of 0 means an infinite timeout","max-parallel-requests-count":"Max number of parallel requests","max-parallel-requests-count-hint":"The value of 0 specifies no limit in parallel processing",headers:"Headers","headers-hint":"Use ${metaKeyName} in header/value fields to substitute variables from metadata",header:"Header","header-required":"Header is required",value:"Value","value-required":"Value is required","topic-pattern":"Topic pattern","topic-pattern-required":"Topic pattern is required","mqtt-topic-pattern-hint":"MQTT topic pattern, use ${metaKeyName} to substitute variables from metadata","bootstrap-servers":"Bootstrap servers","bootstrap-servers-required":"Bootstrap servers value is required","other-properties":"Other properties",key:"Key","key-required":"Key is required",retries:"Automatically retry times if fails","min-retries-message":"Only 0 minimum retries is allowed.","batch-size-bytes":"Produces batch size in bytes","min-batch-size-bytes-message":"Only 0 minimum batch size is allowed.","linger-ms":"Time to buffer locally (ms)","min-linger-ms-message":"Only 0 ms minimum value is allowed.","buffer-memory-bytes":"Client buffer max size in bytes","min-buffer-memory-message":"Only 0 minimum buffer size is allowed.",acks:"Number of acknowledgments","key-serializer":"Key serializer","key-serializer-required":"Key serializer is required","value-serializer":"Value serializer","value-serializer-required":"Value serializer is required","topic-arn-pattern":"Topic ARN pattern","topic-arn-pattern-required":"Topic ARN pattern is required","topic-arn-pattern-hint":"Topic ARN pattern, use ${metaKeyName} to substitute variables from metadata","aws-access-key-id":"AWS Access Key ID","aws-access-key-id-required":"AWS Access Key ID is required","aws-secret-access-key":"AWS Secret Access Key","aws-secret-access-key-required":"AWS Secret Access Key is required","aws-region":"AWS Region","aws-region-required":"AWS Region is required","exchange-name-pattern":"Exchange name pattern","routing-key-pattern":"Routing key pattern","message-properties":"Message properties",host:"Host","host-required":"Host is required",port:"Port","port-required":"Port is required","port-range":"Port should be in a range from 1 to 65535.","virtual-host":"Virtual host",username:"Username",password:"Password","automatic-recovery":"Automatic recovery","connection-timeout-ms":"Connection timeout (ms)","min-connection-timeout-ms-message":"Only 0 ms minimum value is allowed.","handshake-timeout-ms":"Handshake timeout (ms)","min-handshake-timeout-ms-message":"Only 0 ms minimum value is allowed.","client-properties":"Client properties","queue-url-pattern":"Queue URL pattern","queue-url-pattern-required":"Queue URL pattern is required","queue-url-pattern-hint":"Queue URL pattern, use ${metaKeyName} to substitute variables from metadata","delay-seconds":"Delay (seconds)","min-delay-seconds-message":"Only 0 seconds minimum value is allowed.","max-delay-seconds-message":"Only 900 seconds maximum value is allowed.",name:"Name","name-required":"Name is required","queue-type":"Queue type","sqs-queue-standard":"Standard","sqs-queue-fifo":"FIFO","gcp-project-id":"GCP project ID","gcp-project-id-required":"GCP project ID is required","gcp-service-account-key":"GCP service account key file","gcp-service-account-key-required":"GCP service account key file is required","pubsub-topic-name":"Topic name","pubsub-topic-name-required":"Topic name is required","message-attributes":"Message attributes","message-attributes-hint":"Use ${metaKeyName} in name/value fields to substitute variables from metadata","connect-timeout":"Connection timeout (sec)","connect-timeout-required":"Connection timeout is required.","connect-timeout-range":"Connection timeout should be in a range from 1 to 200.","client-id":"Client ID","clean-session":"Clean session","enable-ssl":"Enable SSL",credentials:"Credentials","credentials-type":"Credentials type","credentials-type-required":"Credentials type is required.","credentials-anonymous":"Anonymous","credentials-basic":"Basic","credentials-pem":"PEM","username-required":"Username is required.","password-required":"Password is required.","ca-cert":"CA certificate file *","private-key":"Private key file *",cert:"Certificate file *","no-file":"No file selected.","drop-file":"Drop a file or click to select a file to upload.","private-key-password":"Private key password","use-system-smtp-settings":"Use system SMTP settings","use-metadata-interval-patterns":"Use metadata interval patterns","use-metadata-interval-patterns-hint":"If selected, rule node use start and end interval patterns from message metadata assuming that intervals are in the milliseconds.","use-message-alarm-data":"Use message alarm data","check-all-keys":"Check that all selected keys are present","check-all-keys-hint":"If selected, checks that all specified keys are present in the message data and metadata.","check-relation-to-specific-entity":"Check relation to specific entity","check-relation-hint":"Checks existence of relation to specific entity or to any entity based on direction and relation type.","delete-relation-to-specific-entity":"Delete relation to specific entity","delete-relation-hint":"Deletes relation from the originator of the incoming message to the specified entity or list of entities based on direction and type.","remove-current-relations":"Remove current relations","remove-current-relations-hint":"Removes current relations from the originator of the incoming message based on direction and type.","change-originator-to-related-entity":"Change originator to related entity","change-originator-to-related-entity-hint":"Used to process submitted message as a message from another entity.","start-interval-pattern":"Start interval pattern","end-interval-pattern":"End interval pattern","start-interval-pattern-required":"Start interval pattern is required","end-interval-pattern-required":"End interval pattern is required","start-interval-pattern-hint":"Start interval pattern, use ${metaKeyName} to substitute variables from metadata","end-interval-pattern-hint":"End interval pattern, use ${metaKeyName} to substitute variables from metadata","smtp-protocol":"Protocol","smtp-host":"SMTP host","smtp-host-required":"SMTP host is required.","smtp-port":"SMTP port","smtp-port-required":"You must supply a smtp port.","smtp-port-range":"SMTP port should be in a range from 1 to 65535.","timeout-msec":"Timeout ms","min-timeout-msec-message":"Only 0 ms minimum value is allowed.","enter-username":"Enter username","enter-password":"Enter password","enable-tls":"Enable TLS","enable-proxy":"Enable proxy","use-system-proxy-properties":"Use system proxy properties","proxy-host":"Proxy host","proxy-host-required":"Proxy host is required.","proxy-port":"Proxy port","proxy-port-required":"You must supply a proxy port.","proxy-port-range":"Proxy port should be in a range from 1 to 65535.","proxy-user":"Proxy user","proxy-password":"Proxy password","proxy-scheme":"Proxy scheme","tls-version":"TLS version","min-period-0-seconds-message":"Only 0 second minimum period is allowed.","max-pending-messages":"Maximum pending messages","max-pending-messages-required":"Maximum pending messages is required.","max-pending-messages-range":"Maximum pending messages should be in a range from 1 to 100000.","originator-types-filter":"Originator types filter","interval-seconds":"Interval in seconds","interval-seconds-required":"Interval is required.","min-interval-seconds-message":"Only 1 second minimum interval is allowed.","output-timeseries-key-prefix":"Output timeseries key prefix","output-timeseries-key-prefix-required":"Output timeseries key prefix required.","separator-hint":'You should press "enter" to complete field input.',"entity-details":"Select entity details:","entity-details-title":"Title","entity-details-country":"Country","entity-details-state":"State","entity-details-zip":"Zip","entity-details-address":"Address","entity-details-address2":"Address2","entity-details-additional_info":"Additional Info","entity-details-phone":"Phone","entity-details-email":"Email","add-to-metadata":"Add selected details to message metadata","add-to-metadata-hint":"If selected, adds the selected details keys to the message metadata instead of message data.","entity-details-list-empty":"No entity details selected.","no-entity-details-matching":"No entity details matching were found.","custom-table-name":"Custom table name","custom-table-name-required":"Table Name is required","custom-table-hint":"You should enter the table name without prefix 'cs_tb_'.","message-field":"Message field","message-field-required":"Message field is required.","table-col":"Table column","table-col-required":"Table column is required.","latitude-key-name":"Latitude key name","longitude-key-name":"Longitude key name","latitude-key-name-required":"Latitude key name is required.","longitude-key-name-required":"Longitude key name is required.","fetch-perimeter-info-from-message-metadata":"Fetch perimeter information from message metadata","perimeter-circle":"Circle","perimeter-polygon":"Polygon","perimeter-type":"Perimeter type","circle-center-latitude":"Center latitude","circle-center-latitude-required":"Center latitude is required.","circle-center-longitude":"Center longitude","circle-center-longitude-required":"Center longitude is required.","range-unit-meter":"Meter","range-unit-kilometer":"Kilometer","range-unit-foot":"Foot","range-unit-mile":"Mile","range-unit-nautical-mile":"Nautical mile","range-units":"Range units",range:"Range","range-required":"Range is required.","polygon-definition":"Polygon definition","polygon-definition-required":"Polygon definition is required.","polygon-definition-hint":"Please, use the following format for manual definition of polygon: [[lat1,lon1],[lat2,lon2], ... ,[latN,lonN]].","min-inside-duration":"Minimal inside duration","min-inside-duration-value-required":"Minimal inside duration is required","min-inside-duration-time-unit":"Minimal inside duration time unit","min-outside-duration":"Minimal outside duration","min-outside-duration-value-required":"Minimal outside duration is required","min-outside-duration-time-unit":"Minimal outside duration time unit","tell-failure-if-absent":"Tell Failure","tell-failure-if-absent-hint":'If at least one selected key doesn\'t exist the outbound message will report "Failure".',"get-latest-value-with-ts":"Fetch Latest telemetry with Timestamp","get-latest-value-with-ts-hint":'If selected, latest telemetry values will be added to the outbound message metadata with timestamp, e.g: "temp": "{\\"ts\\":1574329385897,\\"value\\":42}"',"use-redis-queue":"Use redis queue for message persistence","trim-redis-queue":"Trim redis queue","redis-queue-max-size":"Redis queue max size","add-metadata-key-values-as-kafka-headers":"Add Message metadata key-value pairs to Kafka record headers","add-metadata-key-values-as-kafka-headers-hint":"If selected, key-value pairs from message metadata will be added to the outgoing records headers as byte arrays with predefined charset encoding.","charset-encoding":"Charset encoding","charset-encoding-required":"Charset encoding is required.","charset-us-ascii":"US-ASCII","charset-iso-8859-1":"ISO-8859-1","charset-utf-8":"UTF-8","charset-utf-16be":"UTF-16BE","charset-utf-16le":"UTF-16LE","charset-utf-16":"UTF-16","select-queue-hint":"The queue name can be selected from a drop-down list or add a custom name."},"key-val":{key:"Key",value:"Value","remove-entry":"Remove entry","add-entry":"Add entry"}}};e.translations("en_US",t)}Object.defineProperty(t,"__esModule",{value:!0}),t.default=n},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e){(0,o.default)(e)}i.$inject=["$translateProvider"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(106),o=a(r)},function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=angular.module("thingsboard.ruleChain.config.types",[]).constant("ruleNodeTypes",{originatorSource:{CUSTOMER:{name:"tb.rulenode.originator-customer",value:"CUSTOMER"},TENANT:{name:"tb.rulenode.originator-tenant",value:"TENANT"},RELATED:{name:"tb.rulenode.originator-related",value:"RELATED"},ALARM_ORIGINATOR:{name:"tb.rulenode.originator-alarm-originator",value:"ALARM_ORIGINATOR"}},fetchModeType:["FIRST","LAST","ALL"],samplingOrder:["ASC","DESC"],httpRequestType:["GET","POST","PUT","DELETE"],entityDetails:{TITLE:{name:"tb.rulenode.entity-details-title",value:"TITLE"},COUNTRY:{name:"tb.rulenode.entity-details-country",value:"COUNTRY"},STATE:{name:"tb.rulenode.entity-details-state",value:"STATE"},ZIP:{name:"tb.rulenode.entity-details-zip",value:"ZIP"},ADDRESS:{name:"tb.rulenode.entity-details-address",value:"ADDRESS"},ADDRESS2:{name:"tb.rulenode.entity-details-address2",value:"ADDRESS2"},PHONE:{name:"tb.rulenode.entity-details-phone",value:"PHONE"},EMAIL:{name:"tb.rulenode.entity-details-email",value:"EMAIL"},ADDITIONAL_INFO:{name:"tb.rulenode.entity-details-additional_info",value:"ADDITIONAL_INFO"}},sqsQueueType:{STANDARD:{name:"tb.rulenode.sqs-queue-standard",value:"STANDARD"},FIFO:{name:"tb.rulenode.sqs-queue-fifo",value:"FIFO"}},perimeterType:{CIRCLE:{name:"tb.rulenode.perimeter-circle",value:"CIRCLE"},POLYGON:{name:"tb.rulenode.perimeter-polygon",value:"POLYGON"}},timeUnit:{MILLISECONDS:{value:"MILLISECONDS",name:"tb.rulenode.time-unit-milliseconds"},SECONDS:{value:"SECONDS",name:"tb.rulenode.time-unit-seconds"},MINUTES:{value:"MINUTES",name:"tb.rulenode.time-unit-minutes"},HOURS:{value:"HOURS",name:"tb.rulenode.time-unit-hours"},DAYS:{value:"DAYS",name:"tb.rulenode.time-unit-days"}},rangeUnit:{METER:{value:"METER",name:"tb.rulenode.range-unit-meter"},KILOMETER:{value:"KILOMETER",name:"tb.rulenode.range-unit-kilometer"},FOOT:{value:"FOOT",name:"tb.rulenode.range-unit-foot"},MILE:{value:"MILE",name:"tb.rulenode.range-unit-mile"},NAUTICAL_MILE:{value:"NAUTICAL_MILE",name:"tb.rulenode.range-unit-nautical-mile"}},mqttCredentialTypes:{anonymous:{value:"anonymous",name:"tb.rulenode.credentials-anonymous"},basic:{value:"basic",name:"tb.rulenode.credentials-basic"},"cert.PEM":{value:"cert.PEM",name:"tb.rulenode.credentials-pem"}},toBytesStandartCharsetTypes:{"US-ASCII":{value:"US-ASCII",name:"tb.rulenode.charset-us-ascii"},"ISO-8859-1":{value:"ISO-8859-1",name:"tb.rulenode.charset-iso-8859-1"},"UTF-8":{value:"UTF-8",name:"tb.rulenode.charset-utf-8"},"UTF-16BE":{value:"UTF-16BE",name:"tb.rulenode.charset-utf-16be"},"UTF-16LE":{value:"UTF-16LE",name:"tb.rulenode.charset-utf-16le"},"UTF-16":{value:"UTF-16",name:"tb.rulenode.charset-utf-16"}}}).name}])); //# sourceMappingURL=rulenode-core-config.js.map \ No newline at end of file diff --git a/tools/pom.xml b/tools/pom.xml index 952ef2b06e..6cf3467015 100644 --- a/tools/pom.xml +++ b/tools/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.5.1-SNAPSHOT + 2.5.3-SNAPSHOT thingsboard tools diff --git a/transport/coap/pom.xml b/transport/coap/pom.xml index 84b92ba548..f265d6e9cf 100644 --- a/transport/coap/pom.xml +++ b/transport/coap/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.5.1-SNAPSHOT + 2.5.3-SNAPSHOT transport org.thingsboard.transport @@ -123,7 +123,7 @@ jenkins Jenkins Repository - http://repo.jenkins-ci.org/releases + https://repo.jenkins-ci.org/releases false diff --git a/transport/http/pom.xml b/transport/http/pom.xml index a5a377555e..fddc86a8da 100644 --- a/transport/http/pom.xml +++ b/transport/http/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.5.1-SNAPSHOT + 2.5.3-SNAPSHOT transport org.thingsboard.transport @@ -123,7 +123,7 @@ jenkins Jenkins Repository - http://repo.jenkins-ci.org/releases + https://repo.jenkins-ci.org/releases false diff --git a/transport/mqtt/pom.xml b/transport/mqtt/pom.xml index c447f8e8d2..cb37b62a34 100644 --- a/transport/mqtt/pom.xml +++ b/transport/mqtt/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.5.1-SNAPSHOT + 2.5.3-SNAPSHOT transport org.thingsboard.transport @@ -123,7 +123,7 @@ jenkins Jenkins Repository - http://repo.jenkins-ci.org/releases + https://repo.jenkins-ci.org/releases false diff --git a/transport/pom.xml b/transport/pom.xml index 01bdfcd714..9f2d726073 100644 --- a/transport/pom.xml +++ b/transport/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.5.1-SNAPSHOT + 2.5.3-SNAPSHOT thingsboard transport diff --git a/ui/package-lock.json b/ui/package-lock.json index 4de10ff7d8..709137d524 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -1,6 +1,6 @@ { "name": "thingsboard", - "version": "2.5.1", + "version": "2.5.3", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -5238,7 +5238,8 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -5662,7 +5663,8 @@ "safe-buffer": { "version": "5.1.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -5718,6 +5720,7 @@ "version": "3.0.1", "bundled": true, "dev": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -5761,12 +5764,14 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "yallist": { "version": "3.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true } } }, diff --git a/ui/package.json b/ui/package.json index 02a2f21219..c507bbe7e9 100644 --- a/ui/package.json +++ b/ui/package.json @@ -1,7 +1,7 @@ { "name": "thingsboard", "private": true, - "version": "2.5.1", + "version": "2.5.3", "description": "ThingsBoard UI", "licenses": [ { diff --git a/ui/pom.xml b/ui/pom.xml index d6918f36f3..00b8d3e87b 100644 --- a/ui/pom.xml +++ b/ui/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.5.1-SNAPSHOT + 2.5.3-SNAPSHOT thingsboard org.thingsboard diff --git a/ui/src/app/widget/lib/alarms-table-widget.js b/ui/src/app/widget/lib/alarms-table-widget.js index aa2d1cc6b8..53dd43f223 100644 --- a/ui/src/app/widget/lib/alarms-table-widget.js +++ b/ui/src/app/widget/lib/alarms-table-widget.js @@ -404,6 +404,9 @@ function AlarmsTableWidgetController($element, $scope, $filter, $mdMedia, $mdDia } function openAlarmDetails($event, alarm) { + if ($event) { + $event.stopPropagation(); + } if (alarm && alarm.id) { var onShowingCallback = { onShowing: function(){} diff --git a/ui/src/app/widget/widget-editor.scss b/ui/src/app/widget/widget-editor.scss index 55c69ca13c..9cd804803d 100644 --- a/ui/src/app/widget/widget-editor.scss +++ b/ui/src/app/widget/widget-editor.scss @@ -62,6 +62,11 @@ $edit-toolbar-height: 40px !default; height: 100%; } } + + .container{ + width: 100%; + height: 100%; + } } .tb-split-vertical { diff --git a/ui/src/app/widget/widget-editor.tpl.html b/ui/src/app/widget/widget-editor.tpl.html index 8c6662e81b..7e532504f2 100644 --- a/ui/src/app/widget/widget-editor.tpl.html +++ b/ui/src/app/widget/widget-editor.tpl.html @@ -114,18 +114,18 @@
-
+
- - + +
+ style="margin: 10px 0 0; max-height: 40px;"> @@ -154,7 +154,7 @@
- +
- +
- - + +
- +
-
+