diff --git a/application/pom.xml b/application/pom.xml index 8449246059..6da40a9143 100644 --- a/application/pom.xml +++ b/application/pom.xml @@ -53,6 +53,14 @@ org.thingsboard extensions-api + + org.thingsboard.rule-engine + rule-engine-api + + + org.thingsboard.rule-engine + rule-engine-components + org.thingsboard extensions-core diff --git a/application/src/main/data/upgrade/1.5.0/schema_update.cql b/application/src/main/data/upgrade/1.5.0/schema_update.cql index aa8b10bdfd..ab6884659d 100644 --- a/application/src/main/data/upgrade/1.5.0/schema_update.cql +++ b/application/src/main/data/upgrade/1.5.0/schema_update.cql @@ -69,6 +69,7 @@ CREATE TABLE IF NOT EXISTS thingsboard.rule_chain ( search_text text, first_rule_node_id uuid, root boolean, + debug_mode boolean, configuration text, additional_info text, PRIMARY KEY (id, tenant_id) @@ -85,6 +86,7 @@ CREATE TABLE IF NOT EXISTS thingsboard.rule_node ( id uuid, type text, name text, + debug_mode boolean, search_text text, configuration text, additional_info text, diff --git a/application/src/main/data/upgrade/1.5.0/schema_update.sql b/application/src/main/data/upgrade/1.5.0/schema_update.sql index 0043ed5bb0..2bed6ad9da 100644 --- a/application/src/main/data/upgrade/1.5.0/schema_update.sql +++ b/application/src/main/data/upgrade/1.5.0/schema_update.sql @@ -21,6 +21,7 @@ CREATE TABLE IF NOT EXISTS rule_chain ( name varchar(255), first_rule_node_id varchar(31), root boolean, + debug_mode boolean, search_text varchar(255), tenant_id varchar(31) ); @@ -31,5 +32,6 @@ CREATE TABLE IF NOT EXISTS rule_node ( configuration varchar(10000000), type varchar(255), name varchar(255), + debug_mode boolean, search_text varchar(255) ); \ No newline at end of file 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 f59ec63ed7..a5a20b83fa 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java +++ b/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java @@ -28,12 +28,16 @@ import lombok.Setter; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; +import org.springframework.util.Base64Utils; +import org.thingsboard.rule.engine.api.ListeningExecutor; +import org.thingsboard.rule.engine.js.JsExecutorService; import org.thingsboard.server.actors.service.ActorService; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.Event; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; +import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.cluster.ServerAddress; import org.thingsboard.server.common.transport.auth.DeviceAuthService; import org.thingsboard.server.controller.plugin.PluginWebSocketMsgEndpoint; @@ -50,6 +54,7 @@ import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.rule.RuleService; import org.thingsboard.server.dao.tenant.TenantService; import org.thingsboard.server.dao.timeseries.TimeseriesService; +import org.thingsboard.server.dao.user.UserService; import org.thingsboard.server.service.cluster.discovery.DiscoveryService; import org.thingsboard.server.service.cluster.routing.ClusterRoutingService; import org.thingsboard.server.service.cluster.rpc.ClusterRpcService; @@ -57,6 +62,7 @@ import org.thingsboard.server.service.component.ComponentDiscoveryService; import java.io.PrintWriter; import java.io.StringWriter; +import java.nio.charset.StandardCharsets; import java.util.Optional; @Component @@ -65,101 +71,154 @@ public class ActorSystemContext { protected final ObjectMapper mapper = new ObjectMapper(); - @Getter @Setter private ActorService actorService; + @Getter + @Setter + private ActorService actorService; @Autowired - @Getter private DiscoveryService discoveryService; + @Getter + private DiscoveryService discoveryService; @Autowired - @Getter @Setter private ComponentDiscoveryService componentService; + @Getter + @Setter + private ComponentDiscoveryService componentService; @Autowired - @Getter private ClusterRoutingService routingService; + @Getter + private ClusterRoutingService routingService; @Autowired - @Getter private ClusterRpcService rpcService; + @Getter + private ClusterRpcService rpcService; @Autowired - @Getter private DeviceAuthService deviceAuthService; + @Getter + private DeviceAuthService deviceAuthService; @Autowired - @Getter private DeviceService deviceService; + @Getter + private DeviceService deviceService; @Autowired - @Getter private AssetService assetService; + @Getter + private AssetService assetService; @Autowired - @Getter private TenantService tenantService; + @Getter + private TenantService tenantService; @Autowired - @Getter private CustomerService customerService; + @Getter + private CustomerService customerService; @Autowired - @Getter private RuleService ruleService; + @Getter + private UserService userService; @Autowired - @Getter private RuleChainService ruleChainService; + @Getter + private RuleService ruleService; @Autowired - @Getter private PluginService pluginService; + @Getter + private RuleChainService ruleChainService; @Autowired - @Getter private TimeseriesService tsService; + @Getter + private PluginService pluginService; @Autowired - @Getter private AttributesService attributesService; + @Getter + private TimeseriesService tsService; @Autowired - @Getter private EventService eventService; + @Getter + private AttributesService attributesService; @Autowired - @Getter private AlarmService alarmService; + @Getter + private EventService eventService; @Autowired - @Getter private RelationService relationService; + @Getter + private AlarmService alarmService; @Autowired - @Getter private AuditLogService auditLogService; + @Getter + private RelationService relationService; @Autowired - @Getter @Setter private PluginWebSocketMsgEndpoint wsMsgEndpoint; + @Getter + private AuditLogService auditLogService; + + @Autowired + @Getter + @Setter + private PluginWebSocketMsgEndpoint wsMsgEndpoint; @Value("${actors.session.sync.timeout}") - @Getter private long syncSessionTimeout; + @Getter + private long syncSessionTimeout; @Value("${actors.plugin.termination.delay}") - @Getter private long pluginActorTerminationDelay; + @Getter + private long pluginActorTerminationDelay; @Value("${actors.plugin.processing.timeout}") - @Getter private long pluginProcessingTimeout; + @Getter + private long pluginProcessingTimeout; @Value("${actors.plugin.error_persist_frequency}") - @Getter private long pluginErrorPersistFrequency; + @Getter + private long pluginErrorPersistFrequency; + + @Value("${actors.rule.chain.error_persist_frequency}") + @Getter + private long ruleChainErrorPersistFrequency; + + @Value("${actors.rule.node.error_persist_frequency}") + @Getter + private long ruleNodeErrorPersistFrequency; @Value("${actors.rule.termination.delay}") - @Getter private long ruleActorTerminationDelay; + @Getter + private long ruleActorTerminationDelay; @Value("${actors.rule.error_persist_frequency}") - @Getter private long ruleErrorPersistFrequency; + @Getter + private long ruleErrorPersistFrequency; @Value("${actors.statistics.enabled}") - @Getter private boolean statisticsEnabled; + @Getter + private boolean statisticsEnabled; @Value("${actors.statistics.persist_frequency}") - @Getter private long statisticsPersistFrequency; + @Getter + private long statisticsPersistFrequency; @Value("${actors.tenant.create_components_on_init}") - @Getter private boolean tenantComponentsInitEnabled; + @Getter + private boolean tenantComponentsInitEnabled; - @Getter @Setter private ActorSystem actorSystem; + @Getter + @Setter + private ActorSystem actorSystem; - @Getter @Setter private ActorRef appActor; + @Getter + @Setter + private ActorRef appActor; - @Getter @Setter private ActorRef sessionManagerActor; + @Getter + @Setter + private ActorRef sessionManagerActor; - @Getter @Setter private ActorRef statsActor; + @Getter + @Setter + private ActorRef statsActor; - @Getter private final Config config; + @Getter + private final Config config; public ActorSystemContext() { config = ConfigFactory.parseResources(AKKA_CONF_FILE_NAME).withFallback(ConfigFactory.load()); @@ -191,7 +250,7 @@ public class ActorSystemContext { eventService.save(event); } - private String toString(Exception e) { + private String toString(Throwable e) { StringWriter sw = new StringWriter(); e.printStackTrace(new PrintWriter(sw)); return sw.toString(); @@ -211,4 +270,69 @@ public class ActorSystemContext { private JsonNode toBodyJson(ServerAddress server, String method, String body) { return mapper.createObjectNode().put("server", server.toString()).put("method", method).put("error", body); } + + public String getServerAddress() { + return discoveryService.getCurrentServer().getServerAddress().toString(); + } + + public void persistDebugInput(TenantId tenantId, EntityId entityId, TbMsg tbMsg) { + persistDebug(tenantId, entityId, "IN", tbMsg, null); + } + + public void persistDebugInput(TenantId tenantId, EntityId entityId, TbMsg tbMsg, Throwable error) { + persistDebug(tenantId, entityId, "IN", tbMsg, error); + } + + public void persistDebugOutput(TenantId tenantId, EntityId entityId, TbMsg tbMsg, Throwable error) { + persistDebug(tenantId, entityId, "OUT", tbMsg, error); + } + + public void persistDebugOutput(TenantId tenantId, EntityId entityId, TbMsg tbMsg) { + persistDebug(tenantId, entityId, "OUT", tbMsg, null); + } + + private void persistDebug(TenantId tenantId, EntityId entityId, String type, TbMsg tbMsg, Throwable error) { + Event event = new Event(); + event.setTenantId(tenantId); + event.setEntityId(entityId); + event.setType(DataConstants.DEBUG); + + ObjectNode node = mapper.createObjectNode() + .put("type", type) + .put("server", getServerAddress()) + .put("entityId", tbMsg.getOriginator().getId().toString()) + .put("entityName", tbMsg.getOriginator().getEntityType().name()) + .put("msgId", tbMsg.getId().toString()) + .put("msgType", tbMsg.getType()) + .put("dataType", tbMsg.getDataType().name()); + + ObjectNode mdNode = node.putObject("metadata"); + tbMsg.getMetaData().getData().forEach(mdNode::put); + + switch (tbMsg.getDataType()) { + case BINARY: + node.put("data", Base64Utils.encodeUrlSafe(tbMsg.getData())); + break; + default: + node.put("data", new String(tbMsg.getData(), StandardCharsets.UTF_8)); + break; + } + + if (error != null) { + node = node.put("error", toString(error)); + } + + event.setBody(node); + eventService.save(event); + } + + public static Exception toException(Throwable error) { + return Exception.class.isInstance(error) ? (Exception) error : new Exception(error); + } + + public ListeningExecutor getExecutor() { + //TODO: take thread count from yml. + return new JsExecutorService(1); + } + } 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 b475277630..a75158f2dd 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 @@ -22,48 +22,41 @@ import akka.event.LoggingAdapter; import akka.japi.Function; import org.thingsboard.server.actors.ActorSystemContext; import org.thingsboard.server.actors.plugin.PluginTerminationMsg; -import org.thingsboard.server.actors.service.ContextAwareActor; +import org.thingsboard.server.actors.ruleChain.RuleChainManagerActor; import org.thingsboard.server.actors.service.ContextBasedCreator; import org.thingsboard.server.actors.service.DefaultActorService; -import org.thingsboard.server.actors.shared.plugin.PluginManager; import org.thingsboard.server.actors.shared.plugin.SystemPluginManager; -import org.thingsboard.server.actors.shared.rule.RuleManager; -import org.thingsboard.server.actors.shared.rule.SystemRuleManager; -import org.thingsboard.server.actors.tenant.RuleChainDeviceMsg; +import org.thingsboard.server.actors.shared.rulechain.SystemRuleChainManager; import org.thingsboard.server.actors.tenant.TenantActor; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.id.PluginId; -import org.thingsboard.server.common.data.id.RuleId; +import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageDataIterable; +import org.thingsboard.server.common.msg.TbActorMsg; import org.thingsboard.server.common.msg.cluster.ClusterEventMsg; import org.thingsboard.server.common.msg.device.ToDeviceActorMsg; import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; +import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg; import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.tenant.TenantService; import org.thingsboard.server.extensions.api.device.ToDeviceActorNotificationMsg; import org.thingsboard.server.extensions.api.plugins.msg.ToPluginActorMsg; -import org.thingsboard.server.extensions.api.rules.ToRuleActorMsg; import scala.concurrent.duration.Duration; import java.util.HashMap; import java.util.Map; -import java.util.Optional; -public class AppActor extends ContextAwareActor { +public class AppActor extends RuleChainManagerActor { private final LoggingAdapter logger = Logging.getLogger(getContext().system(), this); public static final TenantId SYSTEM_TENANT = new TenantId(ModelConstants.NULL_UUID); - private final RuleManager ruleManager; - private final PluginManager pluginManager; private final TenantService tenantService; private final Map tenantActors; private AppActor(ActorSystemContext systemContext) { - super(systemContext); - this.ruleManager = new SystemRuleManager(systemContext); - this.pluginManager = new SystemPluginManager(systemContext); + super(systemContext, new SystemRuleChainManager(systemContext), new SystemPluginManager(systemContext)); this.tenantService = systemContext.getTenantService(); this.tenantActors = new HashMap<>(); } @@ -77,8 +70,7 @@ public class AppActor extends ContextAwareActor { public void preStart() { logger.info("Starting main system actor."); try { - ruleManager.init(this.context()); - pluginManager.init(this.context()); + initRuleChains(); if (systemContext.isTenantComponentsInitEnabled()) { PageDataIterable tenantIterator = new PageDataIterable<>(tenantService::findTenants, ENTITY_PACK_LIMIT); @@ -96,29 +88,51 @@ public class AppActor extends ContextAwareActor { } @Override - public void onReceive(Object msg) throws Exception { - logger.debug("Received message: {}", msg); - if (msg instanceof ToDeviceActorMsg) { - processDeviceMsg((ToDeviceActorMsg) msg); - } else if (msg instanceof ToPluginActorMsg) { - onToPluginMsg((ToPluginActorMsg) msg); - } else if (msg instanceof ToRuleActorMsg) { - onToRuleMsg((ToRuleActorMsg) msg); - } else if (msg instanceof ToDeviceActorNotificationMsg) { - onToDeviceActorMsg((ToDeviceActorNotificationMsg) msg); - } else if (msg instanceof Terminated) { - processTermination((Terminated) msg); - } else if (msg instanceof ClusterEventMsg) { - broadcast(msg); - } else if (msg instanceof ComponentLifecycleMsg) { - onComponentLifecycleMsg((ComponentLifecycleMsg) msg); - } else if (msg instanceof PluginTerminationMsg) { - onPluginTerminated((PluginTerminationMsg) msg); + protected boolean process(TbActorMsg msg) { + switch (msg.getMsgType()) { + case COMPONENT_LIFE_CYCLE_MSG: + onComponentLifecycleMsg((ComponentLifecycleMsg) msg); + break; + case SERVICE_TO_RULE_ENGINE_MSG: + onServiceToRuleEngineMsg((ServiceToRuleEngineMsg) msg); + break; + default: + return false; + } + return true; + } + + private void onServiceToRuleEngineMsg(ServiceToRuleEngineMsg msg) { + if (SYSTEM_TENANT.equals(msg.getTenantId())) { + //TODO: ashvayka handle this. } else { - logger.warning("Unknown message: {}!", msg); + getOrCreateTenantActor(msg.getTenantId()).tell(msg, self()); } } + +// @Override +// public void onReceive(Object msg) throws Exception { +// logger.debug("Received message: {}", msg); +// if (msg instanceof ToDeviceActorMsg) { +// processDeviceMsg((ToDeviceActorMsg) msg); +// } else if (msg instanceof ToPluginActorMsg) { +// onToPluginMsg((ToPluginActorMsg) msg); +// } else if (msg instanceof ToDeviceActorNotificationMsg) { +// onToDeviceActorMsg((ToDeviceActorNotificationMsg) msg); +// } else if (msg instanceof Terminated) { +// processTermination((Terminated) msg); +// } else if (msg instanceof ClusterEventMsg) { +// broadcast(msg); +// } else if (msg instanceof ComponentLifecycleMsg) { +// onComponentLifecycleMsg((ComponentLifecycleMsg) msg); +// } else if (msg instanceof PluginTerminationMsg) { +// onPluginTerminated((PluginTerminationMsg) msg); +// } else { +// logger.warning("Unknown message: {}!", msg); +// } +// } + private void onPluginTerminated(PluginTerminationMsg msg) { pluginManager.remove(msg.getId()); } @@ -128,20 +142,10 @@ public class AppActor extends ContextAwareActor { tenantActors.values().forEach(actorRef -> actorRef.tell(msg, ActorRef.noSender())); } - private void onToRuleMsg(ToRuleActorMsg msg) { - ActorRef target; - if (SYSTEM_TENANT.equals(msg.getTenantId())) { - target = ruleManager.getOrCreateRuleActor(this.context(), msg.getRuleId()); - } else { - target = getOrCreateTenantActor(msg.getTenantId()); - } - target.tell(msg, ActorRef.noSender()); - } - private void onToPluginMsg(ToPluginActorMsg msg) { ActorRef target; if (SYSTEM_TENANT.equals(msg.getPluginTenantId())) { - target = pluginManager.getOrCreatePluginActor(this.context(), msg.getPluginId()); + target = pluginManager.getOrCreateActor(this.context(), msg.getPluginId()); } else { target = getOrCreateTenantActor(msg.getPluginTenantId()); } @@ -149,26 +153,16 @@ public class AppActor extends ContextAwareActor { } private void onComponentLifecycleMsg(ComponentLifecycleMsg msg) { - ActorRef target = null; + ActorRef target; if (SYSTEM_TENANT.equals(msg.getTenantId())) { - Optional pluginId = msg.getPluginId(); - Optional ruleId = msg.getRuleId(); - if (pluginId.isPresent()) { - target = pluginManager.getOrCreatePluginActor(this.context(), pluginId.get()); - } else if (ruleId.isPresent()) { - Optional ref = ruleManager.update(this.context(), ruleId.get(), msg.getEvent()); - if (ref.isPresent()) { - target = ref.get(); - } else { - logger.debug("Failed to find actor for rule: [{}]", ruleId); - return; - } - } + target = getEntityActorRef(msg.getEntityId()); } else { target = getOrCreateTenantActor(msg.getTenantId()); } if (target != null) { target.tell(msg, ActorRef.noSender()); + } else { + logger.debug("Invalid component lifecycle msg: {}", msg); } } @@ -180,7 +174,7 @@ public class AppActor extends ContextAwareActor { TenantId tenantId = toDeviceActorMsg.getTenantId(); ActorRef tenantActor = getOrCreateTenantActor(tenantId); if (toDeviceActorMsg.getPayload().getMsgType().requiresRulesProcessing()) { - tenantActor.tell(new RuleChainDeviceMsg(toDeviceActorMsg, ruleManager.getRuleChain(this.context())), context().self()); +// tenantActor.tell(new RuleChainDeviceMsg(toDeviceActorMsg, ruleManager.getRuleChain(this.context())), context().self()); } else { tenantActor.tell(toDeviceActorMsg, context().self()); } 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 861c405e08..87bc9926d4 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 @@ -18,19 +18,19 @@ package org.thingsboard.server.actors.device; import akka.event.Logging; import akka.event.LoggingAdapter; import org.thingsboard.server.actors.ActorSystemContext; -import org.thingsboard.server.actors.rule.RulesProcessedMsg; import org.thingsboard.server.actors.service.ContextAwareActor; import org.thingsboard.server.actors.service.ContextBasedCreator; -import org.thingsboard.server.actors.tenant.RuleChainDeviceMsg; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.msg.TbActorMsg; import org.thingsboard.server.common.msg.cluster.ClusterEventMsg; import org.thingsboard.server.common.msg.device.ToDeviceActorMsg; import org.thingsboard.server.extensions.api.device.DeviceAttributesEventNotificationMsg; import org.thingsboard.server.extensions.api.device.DeviceCredentialsUpdateNotificationMsg; import org.thingsboard.server.extensions.api.device.DeviceNameOrTypeUpdateMsg; import org.thingsboard.server.extensions.api.device.ToDeviceActorNotificationMsg; -import org.thingsboard.server.extensions.api.plugins.msg.*; +import org.thingsboard.server.extensions.api.plugins.msg.TimeoutMsg; +import org.thingsboard.server.extensions.api.plugins.msg.ToDeviceRpcRequestPluginMsg; public class DeviceActor extends ContextAwareActor { @@ -47,13 +47,18 @@ public class DeviceActor extends ContextAwareActor { this.processor = new DeviceActorMessageProcessor(systemContext, logger, deviceId); } + @Override + protected boolean process(TbActorMsg msg) { + return false; + } + @Override public void onReceive(Object msg) throws Exception { - if (msg instanceof RuleChainDeviceMsg) { - processor.process(context(), (RuleChainDeviceMsg) msg); - } else if (msg instanceof RulesProcessedMsg) { - processor.onRulesProcessedMsg(context(), (RulesProcessedMsg) msg); - } else if (msg instanceof ToDeviceActorMsg) { +// if (msg instanceof RuleChainDeviceMsg) { +// processor.process(context(), (RuleChainDeviceMsg) msg); +// } else if (msg instanceof RulesProcessedMsg) { +// processor.onRulesProcessedMsg(context(), (RulesProcessedMsg) msg); + if (msg instanceof ToDeviceActorMsg) { processor.process(context(), (ToDeviceActorMsg) msg); } else if (msg instanceof ToDeviceActorNotificationMsg) { if (msg instanceof DeviceAttributesEventNotificationMsg) { 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 21112bf4df..3644a491a4 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 @@ -19,9 +19,7 @@ import akka.actor.ActorContext; import akka.actor.ActorRef; import akka.event.LoggingAdapter; import org.thingsboard.server.actors.ActorSystemContext; -import org.thingsboard.server.actors.rule.*; import org.thingsboard.server.actors.shared.AbstractContextAwareMsgProcessor; -import org.thingsboard.server.actors.tenant.RuleChainDeviceMsg; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.id.DeviceId; @@ -37,15 +35,10 @@ import org.thingsboard.server.common.msg.session.FromDeviceMsg; import org.thingsboard.server.common.msg.session.MsgType; import org.thingsboard.server.common.msg.session.SessionType; import org.thingsboard.server.common.msg.session.ToDeviceMsg; -import org.thingsboard.server.extensions.api.device.*; -import org.thingsboard.server.extensions.api.plugins.msg.FromDeviceRpcResponse; -import org.thingsboard.server.extensions.api.plugins.msg.RpcError; -import org.thingsboard.server.extensions.api.plugins.msg.TimeoutIntMsg; -import org.thingsboard.server.extensions.api.plugins.msg.TimeoutMsg; -import org.thingsboard.server.extensions.api.plugins.msg.ToDeviceRpcRequest; -import org.thingsboard.server.extensions.api.plugins.msg.ToDeviceRpcRequestBody; -import org.thingsboard.server.extensions.api.plugins.msg.ToDeviceRpcRequestPluginMsg; -import org.thingsboard.server.extensions.api.plugins.msg.ToPluginRpcResponseDeviceMsg; +import org.thingsboard.server.extensions.api.device.DeviceAttributes; +import org.thingsboard.server.extensions.api.device.DeviceAttributesEventNotificationMsg; +import org.thingsboard.server.extensions.api.device.DeviceNameOrTypeUpdateMsg; +import org.thingsboard.server.extensions.api.plugins.msg.*; import java.util.*; import java.util.concurrent.ExecutionException; @@ -230,18 +223,18 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso } } - void process(ActorContext context, RuleChainDeviceMsg srcMsg) { - ChainProcessingMetaData md = new ChainProcessingMetaData(srcMsg.getRuleChain(), - srcMsg.getToDeviceActorMsg(), new DeviceMetaData(deviceId, deviceName, deviceType, deviceAttributes), context.self()); - ChainProcessingContext ctx = new ChainProcessingContext(md); - if (ctx.getChainLength() > 0) { - RuleProcessingMsg msg = new RuleProcessingMsg(ctx); - ActorRef ruleActorRef = ctx.getCurrentActor(); - ruleActorRef.tell(msg, ActorRef.noSender()); - } else { - context.self().tell(new RulesProcessedMsg(ctx), context.self()); - } - } +// void process(ActorContext context, RuleChainDeviceMsg srcMsg) { +// ChainProcessingMetaData md = new ChainProcessingMetaData(srcMsg.getRuleChain(), +// srcMsg.getToDeviceActorMsg(), new DeviceMetaData(deviceId, deviceName, deviceType, deviceAttributes), context.self()); +// ChainProcessingContext ctx = new ChainProcessingContext(md); +// if (ctx.getChainLength() > 0) { +// RuleProcessingMsg msg = new RuleProcessingMsg(ctx); +// ActorRef ruleActorRef = ctx.getCurrentActor(); +// ruleActorRef.tell(msg, ActorRef.noSender()); +// } else { +// context.self().tell(new RulesProcessedMsg(ctx), context.self()); +// } +// } void processRpcResponses(ActorContext context, ToDeviceActorMsg msg) { SessionId sessionId = msg.getSessionId(); @@ -302,18 +295,18 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso ); } - void onRulesProcessedMsg(ActorContext context, RulesProcessedMsg msg) { - ChainProcessingContext ctx = msg.getCtx(); - ToDeviceActorMsg inMsg = ctx.getInMsg(); - SessionId sid = inMsg.getSessionId(); - ToDeviceSessionActorMsg response; - if (ctx.getResponse() != null) { - response = new BasicToDeviceSessionActorMsg(ctx.getResponse(), sid); - } else { - response = new BasicToDeviceSessionActorMsg(ctx.getError(), sid); - } - sendMsgToSessionActor(response, inMsg.getServerAddress()); - } +// void onRulesProcessedMsg(ActorContext context, RulesProcessedMsg msg) { +// ChainProcessingContext ctx = msg.getCtx(); +// ToDeviceActorMsg inMsg = ctx.getInMsg(); +// SessionId sid = inMsg.getSessionId(); +// ToDeviceSessionActorMsg response; +// if (ctx.getResponse() != null) { +// response = new BasicToDeviceSessionActorMsg(ctx.getResponse(), sid); +// } else { +// response = new BasicToDeviceSessionActorMsg(ctx.getError(), sid); +// } +// sendMsgToSessionActor(response, inMsg.getServerAddress()); +// } private void processSubscriptionCommands(ActorContext context, ToDeviceActorMsg msg) { SessionId sessionId = msg.getSessionId(); diff --git a/application/src/main/java/org/thingsboard/server/actors/plugin/PluginActor.java b/application/src/main/java/org/thingsboard/server/actors/plugin/PluginActor.java index 265da386ff..88278f316d 100644 --- a/application/src/main/java/org/thingsboard/server/actors/plugin/PluginActor.java +++ b/application/src/main/java/org/thingsboard/server/actors/plugin/PluginActor.java @@ -23,6 +23,7 @@ import org.thingsboard.server.actors.service.ContextBasedCreator; import org.thingsboard.server.actors.stats.StatsPersistTick; import org.thingsboard.server.common.data.id.PluginId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.msg.TbActorMsg; import org.thingsboard.server.common.msg.cluster.ClusterEventMsg; import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; import org.thingsboard.server.extensions.api.plugins.msg.TimeoutMsg; @@ -40,6 +41,12 @@ public class PluginActor extends ComponentActor } @Override - public void start() throws Exception { + public void start(ActorContext context) throws Exception { logger.info("[{}] Going to start plugin actor.", entityId); pluginMd = systemContext.getPluginService().findPluginById(entityId); if (pluginMd == null) { @@ -76,7 +76,7 @@ public class PluginActorMessageProcessor extends ComponentMsgProcessor } @Override - public void stop() throws Exception { + public void stop(ActorContext context) throws Exception { onStop(); } @@ -191,7 +191,7 @@ public class PluginActorMessageProcessor extends ComponentMsgProcessor if (pluginImpl != null) { pluginImpl.stop(trustedCtx); } - start(); + start(context); } } @@ -217,7 +217,7 @@ public class PluginActorMessageProcessor extends ComponentMsgProcessor pluginImpl.resume(trustedCtx); logger.info("[{}] Plugin resumed.", entityId); } else { - start(); + start(context); } } diff --git a/application/src/main/java/org/thingsboard/server/actors/rpc/RpcManagerActor.java b/application/src/main/java/org/thingsboard/server/actors/rpc/RpcManagerActor.java index 9290a8fe58..ba20013456 100644 --- a/application/src/main/java/org/thingsboard/server/actors/rpc/RpcManagerActor.java +++ b/application/src/main/java/org/thingsboard/server/actors/rpc/RpcManagerActor.java @@ -23,6 +23,7 @@ import org.thingsboard.server.actors.ActorSystemContext; 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.common.msg.TbActorMsg; import org.thingsboard.server.common.msg.cluster.ClusterEventMsg; import org.thingsboard.server.common.msg.cluster.ServerAddress; import org.thingsboard.server.gen.cluster.ClusterAPIProtos; @@ -56,6 +57,12 @@ public class RpcManagerActor extends ContextAwareActor { } + @Override + protected boolean process(TbActorMsg msg) { + //TODO Move everything here, to work with TbActorMsg + return false; + } + @Override public void onReceive(Object msg) throws Exception { if (msg instanceof RpcSessionTellMsg) { diff --git a/application/src/main/java/org/thingsboard/server/actors/rpc/RpcSessionActor.java b/application/src/main/java/org/thingsboard/server/actors/rpc/RpcSessionActor.java index db029fa13b..a187444ea5 100644 --- a/application/src/main/java/org/thingsboard/server/actors/rpc/RpcSessionActor.java +++ b/application/src/main/java/org/thingsboard/server/actors/rpc/RpcSessionActor.java @@ -23,6 +23,7 @@ import io.grpc.stub.StreamObserver; import org.thingsboard.server.actors.ActorSystemContext; import org.thingsboard.server.actors.service.ContextAwareActor; import org.thingsboard.server.actors.service.ContextBasedCreator; +import org.thingsboard.server.common.msg.TbActorMsg; import org.thingsboard.server.common.msg.cluster.ServerAddress; import org.thingsboard.server.gen.cluster.ClusterAPIProtos; import org.thingsboard.server.gen.cluster.ClusterRpcServiceGrpc; @@ -47,6 +48,12 @@ public class RpcSessionActor extends ContextAwareActor { this.sessionId = sessionId; } + @Override + protected boolean process(TbActorMsg msg) { + //TODO Move everything here, to work with TbActorMsg + return false; + } + @Override public void onReceive(Object msg) throws Exception { if (msg instanceof RpcSessionTellMsg) { diff --git a/application/src/main/java/org/thingsboard/server/actors/rule/ChainProcessingContext.java b/application/src/main/java/org/thingsboard/server/actors/rule/ChainProcessingContext.java deleted file mode 100644 index 8723a68516..0000000000 --- a/application/src/main/java/org/thingsboard/server/actors/rule/ChainProcessingContext.java +++ /dev/null @@ -1,117 +0,0 @@ -/** - * Copyright © 2016-2018 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.rule; - -import akka.actor.ActorRef; -import org.thingsboard.server.common.msg.core.RuleEngineError; -import org.thingsboard.server.common.msg.core.RuleEngineErrorMsg; -import org.thingsboard.server.common.msg.device.ToDeviceActorMsg; -import org.thingsboard.server.common.msg.session.ToDeviceMsg; -import org.thingsboard.server.extensions.api.device.DeviceAttributes; -import org.thingsboard.server.extensions.api.device.DeviceMetaData; - -public class ChainProcessingContext { - - private final ChainProcessingMetaData md; - private final int index; - private final RuleEngineError error; - private ToDeviceMsg response; - - - public ChainProcessingContext(ChainProcessingMetaData md) { - super(); - this.md = md; - this.index = 0; - this.error = RuleEngineError.NO_RULES; - } - - private ChainProcessingContext(ChainProcessingContext other, int indexOffset, RuleEngineError error) { - super(); - this.md = other.md; - this.index = other.index + indexOffset; - this.error = error; - this.response = other.response; - - if (this.index < 0 || this.index >= this.md.chain.size()) { - throw new IllegalArgumentException("Can't apply offset " + indexOffset + " to the chain!"); - } - } - - public ActorRef getDeviceActor() { - return md.originator; - } - - public ActorRef getCurrentActor() { - return md.chain.getRuleActorMd(index).getActorRef(); - } - - public boolean hasNext() { - return (getChainLength() - 1) > index; - } - - public boolean isFailure() { - return (error != null && error.isCritical()) || (response != null && !response.isSuccess()); - } - - public ChainProcessingContext getNext() { - return new ChainProcessingContext(this, 1, this.error); - } - - public ChainProcessingContext withError(RuleEngineError error) { - if (error != null && (this.error == null || this.error.getPriority() < error.getPriority())) { - return new ChainProcessingContext(this, 0, error); - } else { - return this; - } - } - - public int getChainLength() { - return md.chain.size(); - } - - public ToDeviceActorMsg getInMsg() { - return md.inMsg; - } - - public DeviceMetaData getDeviceMetaData() { - return md.deviceMetaData; - } - - public String getDeviceName() { - return md.deviceMetaData.getDeviceName(); - } - - public String getDeviceType() { - return md.deviceMetaData.getDeviceType(); - } - - public DeviceAttributes getAttributes() { - return md.deviceMetaData.getDeviceAttributes(); - } - - public ToDeviceMsg getResponse() { - return response; - } - - public void mergeResponse(ToDeviceMsg response) { - // TODO add merge logic - this.response = response; - } - - public RuleEngineErrorMsg getError() { - return new RuleEngineErrorMsg(md.inMsg.getPayload().getMsgType(), error); - } -} diff --git a/application/src/main/java/org/thingsboard/server/actors/rule/ChainProcessingMetaData.java b/application/src/main/java/org/thingsboard/server/actors/rule/ChainProcessingMetaData.java deleted file mode 100644 index 8080d4ecc9..0000000000 --- a/application/src/main/java/org/thingsboard/server/actors/rule/ChainProcessingMetaData.java +++ /dev/null @@ -1,41 +0,0 @@ -/** - * Copyright © 2016-2018 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.rule; - -import akka.actor.ActorRef; -import org.thingsboard.server.common.msg.device.ToDeviceActorMsg; -import org.thingsboard.server.extensions.api.device.DeviceMetaData; - -/** - * Immutable part of chain processing data; - * - * @author ashvayka - */ -public final class ChainProcessingMetaData { - - final RuleActorChain chain; - final ToDeviceActorMsg inMsg; - final ActorRef originator; - final DeviceMetaData deviceMetaData; - - public ChainProcessingMetaData(RuleActorChain chain, ToDeviceActorMsg inMsg, DeviceMetaData deviceMetaData, ActorRef originator) { - super(); - this.chain = chain; - this.inMsg = inMsg; - this.originator = originator; - this.deviceMetaData = deviceMetaData; - } -} diff --git a/application/src/main/java/org/thingsboard/server/actors/rule/ComplexRuleActorChain.java b/application/src/main/java/org/thingsboard/server/actors/rule/ComplexRuleActorChain.java deleted file mode 100644 index 89d31bdc84..0000000000 --- a/application/src/main/java/org/thingsboard/server/actors/rule/ComplexRuleActorChain.java +++ /dev/null @@ -1,43 +0,0 @@ -/** - * Copyright © 2016-2018 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.rule; - -public class ComplexRuleActorChain implements RuleActorChain { - - private final RuleActorChain systemChain; - private final RuleActorChain tenantChain; - - public ComplexRuleActorChain(RuleActorChain systemChain, RuleActorChain tenantChain) { - super(); - this.systemChain = systemChain; - this.tenantChain = tenantChain; - } - - @Override - public int size() { - return systemChain.size() + tenantChain.size(); - } - - @Override - public RuleActorMetaData getRuleActorMd(int index) { - if (index < systemChain.size()) { - return systemChain.getRuleActorMd(index); - } else { - return tenantChain.getRuleActorMd(index - systemChain.size()); - } - } - -} diff --git a/application/src/main/java/org/thingsboard/server/actors/rule/RuleActor.java b/application/src/main/java/org/thingsboard/server/actors/rule/RuleActor.java deleted file mode 100644 index 062da2a3bb..0000000000 --- a/application/src/main/java/org/thingsboard/server/actors/rule/RuleActor.java +++ /dev/null @@ -1,90 +0,0 @@ -/** - * Copyright © 2016-2018 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.rule; - -import org.thingsboard.server.actors.ActorSystemContext; -import org.thingsboard.server.actors.service.ComponentActor; -import org.thingsboard.server.actors.service.ContextBasedCreator; -import org.thingsboard.server.actors.stats.StatsPersistTick; -import org.thingsboard.server.common.data.id.RuleId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.msg.cluster.ClusterEventMsg; -import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; -import org.thingsboard.server.extensions.api.plugins.msg.PluginToRuleMsg; - -public class RuleActor extends ComponentActor { - - private RuleActor(ActorSystemContext systemContext, TenantId tenantId, RuleId ruleId) { - super(systemContext, tenantId, ruleId); - setProcessor(new RuleActorMessageProcessor(tenantId, ruleId, systemContext, logger)); - } - - @Override - public void onReceive(Object msg) throws Exception { - logger.debug("[{}] Received message: {}", id, msg); - if (msg instanceof RuleProcessingMsg) { - try { - processor.onRuleProcessingMsg(context(), (RuleProcessingMsg) msg); - increaseMessagesProcessedCount(); - } catch (Exception e) { - logAndPersist("onDeviceMsg", e); - } - } else if (msg instanceof PluginToRuleMsg) { - try { - processor.onPluginMsg(context(), (PluginToRuleMsg) msg); - } catch (Exception e) { - logAndPersist("onPluginMsg", e); - } - } else if (msg instanceof ComponentLifecycleMsg) { - onComponentLifecycleMsg((ComponentLifecycleMsg) msg); - } else if (msg instanceof ClusterEventMsg) { - onClusterEventMsg((ClusterEventMsg) msg); - } else if (msg instanceof RuleToPluginTimeoutMsg) { - try { - processor.onTimeoutMsg(context(), (RuleToPluginTimeoutMsg) msg); - } catch (Exception e) { - logAndPersist("onTimeoutMsg", e); - } - } else if (msg instanceof StatsPersistTick) { - onStatsPersistTick(id); - } else { - logger.debug("[{}][{}] Unknown msg type.", tenantId, id, msg.getClass().getName()); - } - } - - public static class ActorCreator extends ContextBasedCreator { - private static final long serialVersionUID = 1L; - - private final TenantId tenantId; - private final RuleId ruleId; - - public ActorCreator(ActorSystemContext context, TenantId tenantId, RuleId ruleId) { - super(context); - this.tenantId = tenantId; - this.ruleId = ruleId; - } - - @Override - public RuleActor create() throws Exception { - return new RuleActor(context, tenantId, ruleId); - } - } - - @Override - protected long getErrorPersistFrequency() { - return systemContext.getRuleErrorPersistFrequency(); - } -} diff --git a/application/src/main/java/org/thingsboard/server/actors/rule/RuleActorMessageProcessor.java b/application/src/main/java/org/thingsboard/server/actors/rule/RuleActorMessageProcessor.java deleted file mode 100644 index 2ebebfca33..0000000000 --- a/application/src/main/java/org/thingsboard/server/actors/rule/RuleActorMessageProcessor.java +++ /dev/null @@ -1,345 +0,0 @@ -/** - * Copyright © 2016-2018 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.rule; - -import java.util.*; - -import com.fasterxml.jackson.core.JsonProcessingException; -import org.springframework.util.StringUtils; -import org.thingsboard.server.actors.ActorSystemContext; -import org.thingsboard.server.actors.plugin.RuleToPluginMsgWrapper; -import org.thingsboard.server.actors.shared.ComponentMsgProcessor; -import org.thingsboard.server.common.data.id.PluginId; -import org.thingsboard.server.common.data.id.RuleId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.plugin.ComponentLifecycleState; -import org.thingsboard.server.common.data.plugin.PluginMetaData; -import org.thingsboard.server.common.data.rule.RuleMetaData; -import org.thingsboard.server.common.msg.cluster.ClusterEventMsg; -import org.thingsboard.server.common.msg.core.BasicRequest; -import org.thingsboard.server.common.msg.core.BasicStatusCodeResponse; -import org.thingsboard.server.common.msg.core.RuleEngineError; -import org.thingsboard.server.common.msg.device.ToDeviceActorMsg; -import org.thingsboard.server.common.msg.session.MsgType; -import org.thingsboard.server.common.msg.session.ToDeviceMsg; -import org.thingsboard.server.common.msg.session.ex.ProcessingTimeoutException; -import org.thingsboard.server.extensions.api.rules.*; -import org.thingsboard.server.extensions.api.plugins.PluginAction; -import org.thingsboard.server.extensions.api.plugins.msg.PluginToRuleMsg; -import org.thingsboard.server.extensions.api.plugins.msg.RuleToPluginMsg; - -import com.fasterxml.jackson.databind.JsonNode; - -import akka.actor.ActorContext; -import akka.actor.ActorRef; -import akka.event.LoggingAdapter; - -class RuleActorMessageProcessor extends ComponentMsgProcessor { - - private final RuleProcessingContext ruleCtx; - private final Map pendingMsgMap; - - private RuleMetaData ruleMd; - private ComponentLifecycleState state; - private List filters; - private RuleProcessor processor; - private PluginAction action; - - private TenantId pluginTenantId; - private PluginId pluginId; - - protected RuleActorMessageProcessor(TenantId tenantId, RuleId ruleId, ActorSystemContext systemContext, LoggingAdapter logger) { - super(systemContext, logger, tenantId, ruleId); - this.pendingMsgMap = new HashMap<>(); - this.ruleCtx = new RuleProcessingContext(systemContext, ruleId); - } - - @Override - public void start() throws Exception { - logger.info("[{}][{}] Starting rule actor.", entityId, tenantId); - ruleMd = systemContext.getRuleService().findRuleById(entityId); - if (ruleMd == null) { - throw new RuleInitializationException("Rule not found!"); - } - state = ruleMd.getState(); - if (state == ComponentLifecycleState.ACTIVE) { - logger.info("[{}] Rule is active. Going to initialize rule components.", entityId); - initComponent(); - } else { - logger.info("[{}] Rule is suspended. Skipping rule components initialization.", entityId); - } - - logger.info("[{}][{}] Started rule actor.", entityId, tenantId); - } - - @Override - public void stop() throws Exception { - onStop(); - } - - - private void initComponent() throws RuleException { - try { - if (!ruleMd.getFilters().isArray()) { - throw new RuntimeException("Filters are not array!"); - } - fetchPluginInfo(); - initFilters(); - initProcessor(); - initAction(); - } catch (RuntimeException e) { - throw new RuleInitializationException("Unknown runtime exception!", e); - } catch (InstantiationException e) { - throw new RuleInitializationException("No default constructor for rule implementation!", e); - } catch (IllegalAccessException e) { - throw new RuleInitializationException("Illegal Access Exception during rule initialization!", e); - } catch (ClassNotFoundException e) { - throw new RuleInitializationException("Rule Class not found!", e); - } catch (Exception e) { - throw new RuleException(e.getMessage(), e); - } - } - - private void initAction() throws Exception { - if (ruleMd.getAction() != null && !ruleMd.getAction().isNull()) { - action = initComponent(ruleMd.getAction()); - } - } - - private void initProcessor() throws Exception { - if (ruleMd.getProcessor() != null && !ruleMd.getProcessor().isNull()) { - processor = initComponent(ruleMd.getProcessor()); - } - } - - private void initFilters() throws Exception { - filters = new ArrayList<>(ruleMd.getFilters().size()); - for (int i = 0; i < ruleMd.getFilters().size(); i++) { - filters.add(initComponent(ruleMd.getFilters().get(i))); - } - } - - private void fetchPluginInfo() { - if (!StringUtils.isEmpty(ruleMd.getPluginToken())) { - PluginMetaData pluginMd = systemContext.getPluginService().findPluginByApiToken(ruleMd.getPluginToken()); - pluginTenantId = pluginMd.getTenantId(); - pluginId = pluginMd.getId(); - } - } - - protected void onRuleProcessingMsg(ActorContext context, RuleProcessingMsg msg) throws RuleException { - if (state != ComponentLifecycleState.ACTIVE) { - pushToNextRule(context, msg.getCtx(), RuleEngineError.NO_ACTIVE_RULES); - return; - } - ChainProcessingContext chainCtx = msg.getCtx(); - ToDeviceActorMsg inMsg = chainCtx.getInMsg(); - - ruleCtx.update(inMsg, chainCtx.getDeviceMetaData()); - - logger.debug("[{}] Going to filter in msg: {}", entityId, inMsg); - for (RuleFilter filter : filters) { - if (!filter.filter(ruleCtx, inMsg)) { - logger.debug("[{}] In msg is NOT valid for processing by current rule: {}", entityId, inMsg); - pushToNextRule(context, msg.getCtx(), RuleEngineError.NO_FILTERS_MATCHED); - return; - } - } - RuleProcessingMetaData inMsgMd; - if (processor != null) { - logger.debug("[{}] Going to process in msg: {}", entityId, inMsg); - inMsgMd = processor.process(ruleCtx, inMsg); - } else { - inMsgMd = new RuleProcessingMetaData(); - } - logger.debug("[{}] Going to convert in msg: {}", entityId, inMsg); - if (action != null) { - Optional> ruleToPluginMsgOptional = action.convert(ruleCtx, inMsg, inMsgMd); - if (ruleToPluginMsgOptional.isPresent()) { - RuleToPluginMsg ruleToPluginMsg = ruleToPluginMsgOptional.get(); - logger.debug("[{}] Device msg is converted to: {}", entityId, ruleToPluginMsg); - context.parent().tell(new RuleToPluginMsgWrapper(pluginTenantId, pluginId, tenantId, entityId, ruleToPluginMsg), context.self()); - if (action.isOneWayAction()) { - pushToNextRule(context, msg.getCtx(), RuleEngineError.NO_TWO_WAY_ACTIONS); - return; - } else { - pendingMsgMap.put(ruleToPluginMsg.getUid(), msg); - scheduleMsgWithDelay(context, new RuleToPluginTimeoutMsg(ruleToPluginMsg.getUid()), systemContext.getPluginProcessingTimeout()); - return; - } - } - } - logger.debug("[{}] Nothing to send to plugin: {}", entityId, pluginId); - pushToNextRule(context, msg.getCtx(), RuleEngineError.NO_TWO_WAY_ACTIONS); - } - - void onPluginMsg(ActorContext context, PluginToRuleMsg msg) { - RuleProcessingMsg pendingMsg = pendingMsgMap.remove(msg.getUid()); - if (pendingMsg != null) { - ChainProcessingContext ctx = pendingMsg.getCtx(); - Optional ruleResponseOptional = action.convert(msg); - if (ruleResponseOptional.isPresent()) { - ctx.mergeResponse(ruleResponseOptional.get()); - pushToNextRule(context, ctx, null); - } else { - pushToNextRule(context, ctx, RuleEngineError.NO_RESPONSE_FROM_ACTIONS); - } - } else { - logger.warning("[{}] Processing timeout detected: [{}]", entityId, msg.getUid()); - } - } - - void onTimeoutMsg(ActorContext context, RuleToPluginTimeoutMsg msg) { - RuleProcessingMsg pendingMsg = pendingMsgMap.remove(msg.getMsgId()); - if (pendingMsg != null) { - logger.debug("[{}] Processing timeout detected [{}]: {}", entityId, msg.getMsgId(), pendingMsg); - ChainProcessingContext ctx = pendingMsg.getCtx(); - pushToNextRule(context, ctx, RuleEngineError.PLUGIN_TIMEOUT); - } - } - - private void pushToNextRule(ActorContext context, ChainProcessingContext ctx, RuleEngineError error) { - if (error != null) { - ctx = ctx.withError(error); - } - if (ctx.isFailure()) { - logger.debug("[{}][{}] Forwarding processing chain to device actor due to failure.", ruleMd.getId(), ctx.getInMsg().getDeviceId()); - ctx.getDeviceActor().tell(new RulesProcessedMsg(ctx), ActorRef.noSender()); - } else if (!ctx.hasNext()) { - logger.debug("[{}][{}] Forwarding processing chain to device actor due to end of chain.", ruleMd.getId(), ctx.getInMsg().getDeviceId()); - ctx.getDeviceActor().tell(new RulesProcessedMsg(ctx), ActorRef.noSender()); - } else { - logger.debug("[{}][{}] Forwarding processing chain to next rule actor.", ruleMd.getId(), ctx.getInMsg().getDeviceId()); - ChainProcessingContext nextTask = ctx.getNext(); - nextTask.getCurrentActor().tell(new RuleProcessingMsg(nextTask), context.self()); - } - } - - @Override - public void onCreated(ActorContext context) { - logger.info("[{}] Going to process onCreated rule.", entityId); - } - - @Override - public void onUpdate(ActorContext context) throws RuleException { - RuleMetaData oldRuleMd = ruleMd; - ruleMd = systemContext.getRuleService().findRuleById(entityId); - logger.info("[{}] Rule configuration was updated from {} to {}.", entityId, oldRuleMd, ruleMd); - try { - fetchPluginInfo(); - if (filters == null || !Objects.equals(oldRuleMd.getFilters(), ruleMd.getFilters())) { - logger.info("[{}] Rule filters require restart due to json change from {} to {}.", - entityId, mapper.writeValueAsString(oldRuleMd.getFilters()), mapper.writeValueAsString(ruleMd.getFilters())); - stopFilters(); - initFilters(); - } - if (processor == null || !Objects.equals(oldRuleMd.getProcessor(), ruleMd.getProcessor())) { - logger.info("[{}] Rule processor require restart due to configuration change.", entityId); - stopProcessor(); - initProcessor(); - } - if (action == null || !Objects.equals(oldRuleMd.getAction(), ruleMd.getAction())) { - logger.info("[{}] Rule action require restart due to configuration change.", entityId); - stopAction(); - initAction(); - } - } catch (RuntimeException e) { - throw new RuleInitializationException("Unknown runtime exception!", e); - } catch (InstantiationException e) { - throw new RuleInitializationException("No default constructor for rule implementation!", e); - } catch (IllegalAccessException e) { - throw new RuleInitializationException("Illegal Access Exception during rule initialization!", e); - } catch (ClassNotFoundException e) { - throw new RuleInitializationException("Rule Class not found!", e); - } catch (JsonProcessingException e) { - throw new RuleInitializationException("Rule configuration is invalid!", e); - } catch (Exception e) { - throw new RuleInitializationException(e.getMessage(), e); - } - } - - @Override - public void onActivate(ActorContext context) throws Exception { - logger.info("[{}] Going to process onActivate rule.", entityId); - this.state = ComponentLifecycleState.ACTIVE; - if (filters != null) { - filters.forEach(RuleLifecycleComponent::resume); - if (processor != null) { - processor.resume(); - } else { - initProcessor(); - } - if (action != null) { - action.resume(); - } - logger.info("[{}] Rule resumed.", entityId); - } else { - start(); - } - } - - @Override - public void onSuspend(ActorContext context) { - logger.info("[{}] Going to process onSuspend rule.", entityId); - this.state = ComponentLifecycleState.SUSPENDED; - if (filters != null) { - filters.forEach(f -> f.suspend()); - } - if (processor != null) { - processor.suspend(); - } - if (action != null) { - action.suspend(); - } - } - - @Override - public void onStop(ActorContext context) { - logger.info("[{}] Going to process onStop rule.", entityId); - onStop(); - scheduleMsgWithDelay(context, new RuleTerminationMsg(entityId), systemContext.getRuleActorTerminationDelay()); - } - - private void onStop() { - this.state = ComponentLifecycleState.SUSPENDED; - stopFilters(); - stopProcessor(); - stopAction(); - } - - @Override - public void onClusterEventMsg(ClusterEventMsg msg) throws Exception { - //Do nothing - } - - private void stopAction() { - if (action != null) { - action.stop(); - } - } - - private void stopProcessor() { - if (processor != null) { - processor.stop(); - } - } - - private void stopFilters() { - if (filters != null) { - filters.forEach(f -> f.stop()); - } - } -} diff --git a/application/src/main/java/org/thingsboard/server/actors/rule/RuleActorMetaData.java b/application/src/main/java/org/thingsboard/server/actors/rule/RuleActorMetaData.java deleted file mode 100644 index df7adac396..0000000000 --- a/application/src/main/java/org/thingsboard/server/actors/rule/RuleActorMetaData.java +++ /dev/null @@ -1,107 +0,0 @@ -/** - * Copyright © 2016-2018 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.rule; - -import java.util.Comparator; - -import org.thingsboard.server.common.data.id.RuleId; - -import akka.actor.ActorRef; - -public class RuleActorMetaData { - - private final RuleId ruleId; - private final boolean systemRule; - private final int weight; - private final ActorRef actorRef; - - public static final Comparator RULE_ACTOR_MD_COMPARATOR = new Comparator() { - - @Override - public int compare(RuleActorMetaData r1, RuleActorMetaData r2) { - if (r1.isSystemRule() && !r2.isSystemRule()) { - return 1; - } else if (!r1.isSystemRule() && r2.isSystemRule()) { - return -1; - } else { - return Integer.compare(r2.getWeight(), r1.getWeight()); - } - } - }; - - public static RuleActorMetaData systemRule(RuleId ruleId, int weight, ActorRef actorRef) { - return new RuleActorMetaData(ruleId, true, weight, actorRef); - } - - public static RuleActorMetaData tenantRule(RuleId ruleId, int weight, ActorRef actorRef) { - return new RuleActorMetaData(ruleId, false, weight, actorRef); - } - - private RuleActorMetaData(RuleId ruleId, boolean systemRule, int weight, ActorRef actorRef) { - super(); - this.ruleId = ruleId; - this.systemRule = systemRule; - this.weight = weight; - this.actorRef = actorRef; - } - - public RuleId getRuleId() { - return ruleId; - } - - public boolean isSystemRule() { - return systemRule; - } - - public int getWeight() { - return weight; - } - - public ActorRef getActorRef() { - return actorRef; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((ruleId == null) ? 0 : ruleId.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - RuleActorMetaData other = (RuleActorMetaData) obj; - if (ruleId == null) { - if (other.ruleId != null) - return false; - } else if (!ruleId.equals(other.ruleId)) - return false; - return true; - } - - @Override - public String toString() { - return "RuleActorMetaData [ruleId=" + ruleId + ", systemRule=" + systemRule + ", weight=" + weight + ", actorRef=" + actorRef + "]"; - } - -} diff --git a/application/src/main/java/org/thingsboard/server/actors/rule/RuleProcessingContext.java b/application/src/main/java/org/thingsboard/server/actors/rule/RuleProcessingContext.java deleted file mode 100644 index 179307bb05..0000000000 --- a/application/src/main/java/org/thingsboard/server/actors/rule/RuleProcessingContext.java +++ /dev/null @@ -1,115 +0,0 @@ -/** - * Copyright © 2016-2018 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.rule; - -import com.google.common.util.concurrent.ListenableFuture; -import org.thingsboard.server.actors.ActorSystemContext; -import org.thingsboard.server.common.data.Event; -import org.thingsboard.server.common.data.alarm.Alarm; -import org.thingsboard.server.common.data.alarm.AlarmId; -import org.thingsboard.server.common.data.id.*; -import org.thingsboard.server.dao.alarm.AlarmService; -import org.thingsboard.server.dao.event.EventService; -import org.thingsboard.server.dao.timeseries.TimeseriesService; -import org.thingsboard.server.common.msg.device.ToDeviceActorMsg; -import org.thingsboard.server.extensions.api.device.DeviceMetaData; -import org.thingsboard.server.extensions.api.rules.RuleContext; - -import java.util.Optional; -import java.util.concurrent.ExecutionException; - -public class RuleProcessingContext implements RuleContext { - - private final TimeseriesService tsService; - private final EventService eventService; - private final AlarmService alarmService; - private final RuleId ruleId; - private TenantId tenantId; - private CustomerId customerId; - private DeviceId deviceId; - private DeviceMetaData deviceMetaData; - - RuleProcessingContext(ActorSystemContext systemContext, RuleId ruleId) { - this.tsService = systemContext.getTsService(); - this.eventService = systemContext.getEventService(); - this.alarmService = systemContext.getAlarmService(); - this.ruleId = ruleId; - } - - void update(ToDeviceActorMsg toDeviceActorMsg, DeviceMetaData deviceMetaData) { - this.tenantId = toDeviceActorMsg.getTenantId(); - this.customerId = toDeviceActorMsg.getCustomerId(); - this.deviceId = toDeviceActorMsg.getDeviceId(); - this.deviceMetaData = deviceMetaData; - } - - @Override - public RuleId getRuleId() { - return ruleId; - } - - @Override - public DeviceMetaData getDeviceMetaData() { - return deviceMetaData; - } - - @Override - public Event save(Event event) { - checkEvent(event); - return eventService.save(event); - } - - @Override - public Optional saveIfNotExists(Event event) { - checkEvent(event); - return eventService.saveIfNotExists(event); - } - - @Override - public Optional findEvent(String eventType, String eventUid) { - return eventService.findEvent(tenantId, deviceId, eventType, eventUid); - } - - @Override - public Alarm createOrUpdateAlarm(Alarm alarm) { - alarm.setTenantId(tenantId); - return alarmService.createOrUpdateAlarm(alarm); - } - - public Optional findLatestAlarm(EntityId originator, String alarmType) { - try { - return Optional.ofNullable(alarmService.findLatestByOriginatorAndType(tenantId, originator, alarmType).get()); - } catch (InterruptedException | ExecutionException e) { - throw new RuntimeException("Failed to lookup alarm!", e); - } - } - - @Override - public ListenableFuture clearAlarm(AlarmId alarmId, long clearTs) { - return alarmService.clearAlarm(alarmId, clearTs); - } - - private void checkEvent(Event event) { - if (event.getTenantId() == null) { - event.setTenantId(tenantId); - } else if (!tenantId.equals(event.getTenantId())) { - throw new IllegalArgumentException("Invalid Tenant id!"); - } - if (event.getEntityId() == null) { - event.setEntityId(deviceId); - } - } -} diff --git a/application/src/main/java/org/thingsboard/server/actors/rule/RuleToPluginTimeoutMsg.java b/application/src/main/java/org/thingsboard/server/actors/rule/RuleToPluginTimeoutMsg.java deleted file mode 100644 index 0258bce130..0000000000 --- a/application/src/main/java/org/thingsboard/server/actors/rule/RuleToPluginTimeoutMsg.java +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Copyright © 2016-2018 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.rule; - -import java.io.Serializable; -import java.util.UUID; - -public class RuleToPluginTimeoutMsg implements Serializable { - - private static final long serialVersionUID = 1L; - - private final UUID msgId; - - public RuleToPluginTimeoutMsg(UUID msgId) { - super(); - this.msgId = msgId; - } - - public UUID getMsgId() { - return msgId; - } - -} 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 new file mode 100644 index 0000000000..012a09f84e --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java @@ -0,0 +1,154 @@ +/** + * Copyright © 2016-2018 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.ruleChain; + +import org.thingsboard.rule.engine.api.ListeningExecutor; +import org.thingsboard.rule.engine.api.TbContext; +import org.thingsboard.server.actors.ActorSystemContext; +import org.thingsboard.server.common.msg.TbMsg; +import org.thingsboard.server.common.msg.cluster.ServerAddress; +import org.thingsboard.server.dao.alarm.AlarmService; +import org.thingsboard.server.dao.asset.AssetService; +import org.thingsboard.server.dao.attributes.AttributesService; +import org.thingsboard.server.dao.customer.CustomerService; +import org.thingsboard.server.dao.device.DeviceService; +import org.thingsboard.server.dao.plugin.PluginService; +import org.thingsboard.server.dao.relation.RelationService; +import org.thingsboard.server.dao.rule.RuleChainService; +import org.thingsboard.server.dao.timeseries.TimeseriesService; +import org.thingsboard.server.dao.user.UserService; + +import java.util.Set; + +/** + * Created by ashvayka on 19.03.18. + */ +class DefaultTbContext implements TbContext { + + private final ActorSystemContext mainCtx; + private final RuleNodeCtx nodeCtx; + + public DefaultTbContext(ActorSystemContext mainCtx, RuleNodeCtx nodeCtx) { + this.mainCtx = mainCtx; + this.nodeCtx = nodeCtx; + } + + @Override + public void tellNext(TbMsg msg) { + tellNext(msg, (String) null); + } + + @Override + public void tellNext(TbMsg msg, String relationType) { + if (nodeCtx.getSelf().isDebugMode()) { + mainCtx.persistDebugOutput(nodeCtx.getTenantId(), nodeCtx.getSelf().getId(), msg); + } + nodeCtx.getChainActor().tell(new RuleNodeToRuleChainTellNextMsg(nodeCtx.getSelf().getId(), relationType, msg), nodeCtx.getSelfActor()); + } + + @Override + public void tellSelf(TbMsg msg, long delayMs) { + throw new RuntimeException("Not Implemented!"); + } + + @Override + public void tellOthers(TbMsg msg) { + throw new RuntimeException("Not Implemented!"); + } + + @Override + public void tellSibling(TbMsg msg, ServerAddress address) { + throw new RuntimeException("Not Implemented!"); + } + + @Override + public void spawn(TbMsg msg) { + throw new RuntimeException("Not Implemented!"); + } + + @Override + public void ack(TbMsg msg) { + + } + + @Override + public void tellError(TbMsg msg, Throwable th) { + if (nodeCtx.getSelf().isDebugMode()) { + mainCtx.persistDebugOutput(nodeCtx.getTenantId(), nodeCtx.getSelf().getId(), msg, th); + } + nodeCtx.getSelfActor().tell(new RuleNodeToSelfErrorMsg(msg, th), nodeCtx.getSelfActor()); + } + + @Override + public void tellNext(TbMsg msg, Set relationTypes) { + relationTypes.forEach(type -> tellNext(msg, type)); + } + + @Override + public ListeningExecutor getJsExecutor() { + return mainCtx.getExecutor(); + } + + @Override + public AttributesService getAttributesService() { + return mainCtx.getAttributesService(); + } + + @Override + public CustomerService getCustomerService() { + return mainCtx.getCustomerService(); + } + + @Override + public UserService getUserService() { + return mainCtx.getUserService(); + } + + @Override + public PluginService getPluginService() { + return mainCtx.getPluginService(); + } + + @Override + public AssetService getAssetService() { + return mainCtx.getAssetService(); + } + + @Override + public DeviceService getDeviceService() { + return mainCtx.getDeviceService(); + } + + @Override + public AlarmService getAlarmService() { + return mainCtx.getAlarmService(); + } + + @Override + public RuleChainService getRuleChainService() { + return mainCtx.getRuleChainService(); + } + + @Override + public TimeseriesService getTimeseriesService() { + return mainCtx.getTsService(); + } + + @Override + public RelationService getRelationService() { + return mainCtx.getRelationService(); + } +} 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 new file mode 100644 index 0000000000..f539e32a3c --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActor.java @@ -0,0 +1,88 @@ +/** + * Copyright © 2016-2018 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.ruleChain; + +import akka.actor.OneForOneStrategy; +import akka.actor.SupervisorStrategy; +import org.thingsboard.server.actors.ActorSystemContext; +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.TenantId; +import org.thingsboard.server.common.msg.TbActorMsg; +import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; +import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg; +import scala.concurrent.duration.Duration; + +public class RuleChainActor extends ComponentActor { + + private RuleChainActor(ActorSystemContext systemContext, TenantId tenantId, RuleChainId ruleChainId) { + super(systemContext, tenantId, ruleChainId); + setProcessor(new RuleChainActorMessageProcessor(tenantId, ruleChainId, systemContext, + logger, context().parent(), context().self())); + } + + @Override + protected boolean process(TbActorMsg msg) { + switch (msg.getMsgType()) { + case COMPONENT_LIFE_CYCLE_MSG: + onComponentLifecycleMsg((ComponentLifecycleMsg) msg); + break; + case SERVICE_TO_RULE_ENGINE_MSG: + processor.onServiceToRuleEngineMsg((ServiceToRuleEngineMsg) msg); + break; + case RULE_TO_RULE_CHAIN_TELL_NEXT_MSG: + processor.onTellNext((RuleNodeToRuleChainTellNextMsg) msg); + break; + default: + return false; + } + return true; + } + + public static class ActorCreator extends ContextBasedCreator { + private static final long serialVersionUID = 1L; + + private final TenantId tenantId; + private final RuleChainId ruleChainId; + + public ActorCreator(ActorSystemContext context, TenantId tenantId, RuleChainId pluginId) { + super(context); + this.tenantId = tenantId; + this.ruleChainId = pluginId; + } + + @Override + public RuleChainActor create() throws Exception { + return new RuleChainActor(context, tenantId, ruleChainId); + } + } + + @Override + protected long getErrorPersistFrequency() { + return systemContext.getRuleChainErrorPersistFrequency(); + } + + @Override + public SupervisorStrategy supervisorStrategy() { + return strategy; + } + + private final SupervisorStrategy strategy = new OneForOneStrategy(3, Duration.create("1 minute"), t -> { + 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 new file mode 100644 index 0000000000..a853b1576b --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java @@ -0,0 +1,194 @@ +/** + * Copyright © 2016-2018 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.ruleChain; + +import akka.actor.ActorContext; +import akka.actor.ActorRef; +import akka.actor.Props; +import akka.event.LoggingAdapter; +import org.thingsboard.server.actors.ActorSystemContext; +import org.thingsboard.server.actors.service.DefaultActorService; +import org.thingsboard.server.actors.shared.ComponentMsgProcessor; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.id.EntityId; +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.plugin.ComponentLifecycleEvent; +import org.thingsboard.server.common.data.plugin.ComponentLifecycleState; +import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.rule.RuleChain; +import org.thingsboard.server.common.data.rule.RuleNode; +import org.thingsboard.server.common.msg.TbMsg; +import org.thingsboard.server.common.msg.cluster.ClusterEventMsg; +import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; +import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg; +import org.thingsboard.server.dao.rule.RuleChainService; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * @author Andrew Shvayka + */ +public class RuleChainActorMessageProcessor extends ComponentMsgProcessor { + + private final ActorRef parent; + private final ActorRef self; + private final Map nodeActors; + private final Map> nodeRoutes; + private final RuleChainService service; + + private RuleNodeId firstId; + private RuleNodeCtx firstNode; + + RuleChainActorMessageProcessor(TenantId tenantId, RuleChainId ruleChainId, ActorSystemContext systemContext + , LoggingAdapter logger, ActorRef parent, ActorRef self) { + super(systemContext, logger, tenantId, ruleChainId); + this.parent = parent; + this.self = self; + this.nodeActors = new HashMap<>(); + this.nodeRoutes = new HashMap<>(); + this.service = systemContext.getRuleChainService(); + } + + @Override + public void start(ActorContext context) throws Exception { + RuleChain ruleChain = service.findRuleChainById(entityId); + List ruleNodeList = service.getRuleChainNodes(entityId); + // Creating and starting the actors; + for (RuleNode ruleNode : ruleNodeList) { + ActorRef ruleNodeActor = createRuleNodeActor(context, ruleNode); + nodeActors.put(ruleNode.getId(), new RuleNodeCtx(tenantId, self, ruleNodeActor, ruleNode)); + } + initRoutes(ruleChain, ruleNodeList); + } + + @Override + public void onUpdate(ActorContext context) throws Exception { + RuleChain ruleChain = service.findRuleChainById(entityId); + List ruleNodeList = service.getRuleChainNodes(entityId); + + for (RuleNode ruleNode : ruleNodeList) { + RuleNodeCtx existing = nodeActors.get(ruleNode.getId()); + if (existing == null) { + ActorRef ruleNodeActor = createRuleNodeActor(context, ruleNode); + nodeActors.put(ruleNode.getId(), new RuleNodeCtx(tenantId, self, ruleNodeActor, ruleNode)); + } else { + existing.getSelfActor().tell(new ComponentLifecycleMsg(tenantId, existing.getSelf().getId(), ComponentLifecycleEvent.UPDATED), 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 -> { + RuleNodeCtx removed = nodeActors.remove(ruleNodeId); + removed.getSelfActor().tell(new ComponentLifecycleMsg(tenantId, removed.getSelf().getId(), ComponentLifecycleEvent.DELETED), self); + }); + + initRoutes(ruleChain, ruleNodeList); + } + + @Override + public void stop(ActorContext context) throws Exception { + nodeActors.values().stream().map(RuleNodeCtx::getSelfActor).forEach(context::stop); + nodeActors.clear(); + nodeRoutes.clear(); + context.stop(self); + } + + @Override + public void onClusterEventMsg(ClusterEventMsg msg) throws Exception { + + } + + 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.getId())) + .withDispatcher(dispatcherName), ruleNode.getId().toString()); + } + + private void initRoutes(RuleChain ruleChain, List ruleNodeList) { + nodeRoutes.clear(); + // Populating the routes map; + for (RuleNode ruleNode : ruleNodeList) { + List relations = service.getRuleNodeRelations(ruleNode.getId()); + for (EntityRelation relation : relations) { + if (relation.getTo().getEntityType() == EntityType.RULE_NODE) { + RuleNodeCtx ruleNodeCtx = nodeActors.get(new RuleNodeId(relation.getTo().getId())); + if (ruleNodeCtx == null) { + throw new IllegalArgumentException("Rule Node [" + relation.getFrom() + "] has invalid relation to Rule node [" + relation.getTo() + "]"); + } + } + nodeRoutes.computeIfAbsent(ruleNode.getId(), k -> new ArrayList<>()) + .add(new RuleNodeRelation(ruleNode.getId(), relation.getTo(), relation.getType())); + } + } + + firstId = ruleChain.getFirstRuleNodeId(); + firstNode = nodeActors.get(ruleChain.getFirstRuleNodeId()); + state = ComponentLifecycleState.ACTIVE; + } + + void onServiceToRuleEngineMsg(ServiceToRuleEngineMsg envelope) { + checkActive(); + TbMsg tbMsg = envelope.getTbMsg(); + //TODO: push to queue and act on ack in async way + pushMstToNode(firstNode, tbMsg); + } + + void onTellNext(RuleNodeToRuleChainTellNextMsg envelope) { + checkActive(); + RuleNodeId originator = envelope.getOriginator(); + String targetRelationType = envelope.getRelationType(); + List relations = nodeRoutes.get(originator); + if (relations == null) { + return; + } + boolean copy = relations.size() > 1; + for (RuleNodeRelation relation : relations) { + TbMsg msg = envelope.getMsg(); + if (copy) { + msg = msg.copy(); + } + if (targetRelationType == null || targetRelationType.equalsIgnoreCase(relation.getType())) { + switch (relation.getOut().getEntityType()) { + case RULE_NODE: + RuleNodeId targetRuleNodeId = new RuleNodeId(relation.getOut().getId()); + RuleNodeCtx targetRuleNode = nodeActors.get(targetRuleNodeId); + pushMstToNode(targetRuleNode, msg); + break; + case RULE_CHAIN: +// TODO: implement + break; + } + } + } + } + + private void pushMstToNode(RuleNodeCtx nodeCtx, TbMsg msg) { + if (nodeCtx != null) { + nodeCtx.getSelfActor().tell(new RuleChainToRuleNodeMsg(new DefaultTbContext(systemContext, nodeCtx), msg), self); + } + } + +} diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainManagerActor.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainManagerActor.java new file mode 100644 index 0000000000..940bd5b0fb --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainManagerActor.java @@ -0,0 +1,61 @@ +/** + * Copyright © 2016-2018 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.ruleChain; + +import akka.actor.ActorRef; +import org.thingsboard.server.actors.ActorSystemContext; +import org.thingsboard.server.actors.service.ContextAwareActor; +import org.thingsboard.server.actors.shared.plugin.PluginManager; +import org.thingsboard.server.actors.shared.rulechain.RuleChainManager; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.PluginId; +import org.thingsboard.server.common.data.id.RuleChainId; +import org.thingsboard.server.dao.rule.RuleChainService; + +/** + * Created by ashvayka on 15.03.18. + */ +public abstract class RuleChainManagerActor extends ContextAwareActor { + + protected final RuleChainManager ruleChainManager; + protected final PluginManager pluginManager; + protected final RuleChainService ruleChainService; + + public RuleChainManagerActor(ActorSystemContext systemContext, RuleChainManager ruleChainManager, PluginManager pluginManager) { + super(systemContext); + this.ruleChainManager = ruleChainManager; + this.pluginManager = pluginManager; + this.ruleChainService = systemContext.getRuleChainService(); + } + + protected void initRuleChains() { + pluginManager.init(this.context()); + ruleChainManager.init(this.context()); + } + + protected ActorRef getEntityActorRef(EntityId entityId) { + ActorRef target = null; + switch (entityId.getEntityType()) { + case PLUGIN: + target = pluginManager.getOrCreateActor(this.context(), (PluginId) entityId); + break; + case RULE_CHAIN: + target = ruleChainManager.getOrCreateActor(this.context(), (RuleChainId) entityId); + break; + } + return target; + } +} diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/RuleMsg.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainToRuleNodeMsg.java similarity index 54% rename from common/message/src/main/java/org/thingsboard/server/common/msg/RuleMsg.java rename to application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainToRuleNodeMsg.java index ee352ad722..e7d866c1eb 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/RuleMsg.java +++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainToRuleNodeMsg.java @@ -13,26 +13,25 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.common.msg; +package org.thingsboard.server.actors.ruleChain; -import org.thingsboard.server.common.data.rule.Scope; -import org.thingsboard.server.common.data.rule.RuleType; -import org.thingsboard.server.common.msg.aware.RuleAwareMsg; +import lombok.Data; +import org.thingsboard.rule.engine.api.TbContext; +import org.thingsboard.server.common.msg.MsgType; +import org.thingsboard.server.common.msg.TbActorMsg; +import org.thingsboard.server.common.msg.TbMsg; /** - * Message that is used to deliver some data to the rule instance. - * For example: aggregated statistics or command decoded from http request. - * - * @author ashvayka - * - * @param - payload + * Created by ashvayka on 19.03.18. */ -public interface RuleMsg extends RuleAwareMsg { +@Data +final class RuleChainToRuleNodeMsg implements TbActorMsg { + + private final TbContext ctx; + private final TbMsg msg; - Scope getRuleLevel(); - - RuleType getRuleType(); - - V getPayload(); - + @Override + public MsgType getMsgType() { + return MsgType.RULE_CHAIN_TO_RULE_MSG; + } } 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 new file mode 100644 index 0000000000..268c597714 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeActor.java @@ -0,0 +1,96 @@ +/** + * Copyright © 2016-2018 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.ruleChain; + +import org.thingsboard.server.actors.ActorSystemContext; +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.msg.TbActorMsg; +import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; + +public class RuleNodeActor extends ComponentActor { + + private final RuleChainId ruleChainId; + + private RuleNodeActor(ActorSystemContext systemContext, TenantId tenantId, RuleChainId ruleChainId, RuleNodeId ruleNodeId) { + super(systemContext, tenantId, ruleNodeId); + this.ruleChainId = ruleChainId; + setProcessor(new RuleNodeActorMessageProcessor(tenantId, ruleChainId, ruleNodeId, systemContext, + logger, context().parent(), context().self())); + } + + @Override + protected boolean process(TbActorMsg msg) { + switch (msg.getMsgType()) { + case COMPONENT_LIFE_CYCLE_MSG: + onComponentLifecycleMsg((ComponentLifecycleMsg) msg); + break; + case RULE_CHAIN_TO_RULE_MSG: + onRuleChainToRuleNodeMsg((RuleChainToRuleNodeMsg) msg); + break; + case RULE_TO_SELF_ERROR_MSG: + onRuleNodeToSelfErrorMsg((RuleNodeToSelfErrorMsg) msg); + break; + default: + return false; + } + return true; + } + + private void onRuleChainToRuleNodeMsg(RuleChainToRuleNodeMsg msg) { + logger.debug("[{}] Going to process rule msg: {}", id, msg.getMsg()); + try { + processor.onRuleChainToRuleNodeMsg(msg); + increaseMessagesProcessedCount(); + } catch (Exception e) { + logAndPersist("onRuleMsg", e); + } + } + + private void onRuleNodeToSelfErrorMsg(RuleNodeToSelfErrorMsg msg) { + logAndPersist("onRuleMsg", ActorSystemContext.toException(msg.getError())); + } + + public static class ActorCreator extends ContextBasedCreator { + private static final long serialVersionUID = 1L; + + private final TenantId tenantId; + private final RuleChainId ruleChainId; + private final RuleNodeId ruleNodeId; + + public ActorCreator(ActorSystemContext context, TenantId tenantId, RuleChainId ruleChainId, RuleNodeId ruleNodeId) { + super(context); + this.tenantId = tenantId; + this.ruleChainId = ruleChainId; + this.ruleNodeId = ruleNodeId; + + } + + @Override + public RuleNodeActor create() throws Exception { + return new RuleNodeActor(context, tenantId, ruleChainId, ruleNodeId); + } + } + + @Override + protected long getErrorPersistFrequency() { + return systemContext.getRuleNodeErrorPersistFrequency(); + } + +} diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeActorMessageProcessor.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeActorMessageProcessor.java new file mode 100644 index 0000000000..a86e2b908c --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeActorMessageProcessor.java @@ -0,0 +1,99 @@ +/** + * Copyright © 2016-2018 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.ruleChain; + +import akka.actor.ActorContext; +import akka.actor.ActorRef; +import akka.event.LoggingAdapter; +import org.thingsboard.rule.engine.api.TbNode; +import org.thingsboard.rule.engine.api.TbNodeConfiguration; +import org.thingsboard.rule.engine.api.TbNodeState; +import org.thingsboard.server.actors.ActorSystemContext; +import org.thingsboard.server.actors.shared.ComponentMsgProcessor; +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.plugin.ComponentLifecycleState; +import org.thingsboard.server.common.data.rule.RuleNode; +import org.thingsboard.server.common.msg.cluster.ClusterEventMsg; +import org.thingsboard.server.dao.rule.RuleChainService; + +/** + * @author Andrew Shvayka + */ +public class RuleNodeActorMessageProcessor extends ComponentMsgProcessor { + + private final ActorRef parent; + private final ActorRef self; + private final RuleChainService service; + private RuleNode ruleNode; + private TbNode tbNode; + + RuleNodeActorMessageProcessor(TenantId tenantId, RuleChainId ruleChainId, RuleNodeId ruleNodeId, ActorSystemContext systemContext + , LoggingAdapter logger, ActorRef parent, ActorRef self) { + super(systemContext, logger, tenantId, ruleNodeId); + this.parent = parent; + this.self = self; + this.service = systemContext.getRuleChainService(); + this.ruleNode = systemContext.getRuleChainService().findRuleNodeById(entityId); + } + + @Override + public void start(ActorContext context) throws Exception { + tbNode = initComponent(ruleNode); + state = ComponentLifecycleState.ACTIVE; + } + + @Override + public void onUpdate(ActorContext context) throws Exception { + RuleNode newRuleNode = systemContext.getRuleChainService().findRuleNodeById(entityId); + boolean restartRequired = !(ruleNode.getType().equals(newRuleNode.getType()) + && ruleNode.getConfiguration().equals(newRuleNode.getConfiguration())); + this.ruleNode = newRuleNode; + if (restartRequired) { + tbNode.destroy(); + start(context); + } + } + + @Override + public void stop(ActorContext context) throws Exception { + tbNode.destroy(); + context.stop(self); + } + + @Override + public void onClusterEventMsg(ClusterEventMsg msg) throws Exception { + + } + + void onRuleChainToRuleNodeMsg(RuleChainToRuleNodeMsg msg) throws Exception { + checkActive(); + if (ruleNode.isDebugMode()) { + systemContext.persistDebugInput(tenantId, entityId, msg.getMsg()); + } + tbNode.onMsg(msg.getCtx(), msg.getMsg()); + } + + private TbNode initComponent(RuleNode ruleNode) throws Exception { + Class componentClazz = Class.forName(ruleNode.getType()); + TbNode tbNode = (TbNode) (componentClazz.newInstance()); + tbNode.init(new TbNodeConfiguration(ruleNode.getConfiguration()), new TbNodeState()); + return tbNode; + } + + +} diff --git a/application/src/main/java/org/thingsboard/server/actors/rule/RuleTerminationMsg.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeCtx.java similarity index 59% rename from application/src/main/java/org/thingsboard/server/actors/rule/RuleTerminationMsg.java rename to application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeCtx.java index 4458e5217c..f4f733b566 100644 --- a/application/src/main/java/org/thingsboard/server/actors/rule/RuleTerminationMsg.java +++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeCtx.java @@ -13,18 +13,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.actors.rule; +package org.thingsboard.server.actors.ruleChain; -import org.thingsboard.server.actors.shared.ActorTerminationMsg; -import org.thingsboard.server.common.data.id.PluginId; -import org.thingsboard.server.common.data.id.RuleId; +import akka.actor.ActorRef; +import lombok.Data; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.rule.RuleNode; /** - * @author Andrew Shvayka + * Created by ashvayka on 19.03.18. */ -public class RuleTerminationMsg extends ActorTerminationMsg { - - public RuleTerminationMsg(RuleId id) { - super(id); - } +@Data +final class RuleNodeCtx { + private final TenantId tenantId; + private final ActorRef chainActor; + private final ActorRef selfActor; + private final RuleNode self; } diff --git a/application/src/main/java/org/thingsboard/server/actors/rule/RuleProcessingMsg.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeRelation.java similarity index 67% rename from application/src/main/java/org/thingsboard/server/actors/rule/RuleProcessingMsg.java rename to application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeRelation.java index 291a9a7cef..7861e5473c 100644 --- a/application/src/main/java/org/thingsboard/server/actors/rule/RuleProcessingMsg.java +++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeRelation.java @@ -13,19 +13,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.actors.rule; +package org.thingsboard.server.actors.ruleChain; -public class RuleProcessingMsg { +import lombok.Data; +import org.thingsboard.server.common.data.id.EntityId; - private final ChainProcessingContext ctx; +/** + * Created by ashvayka on 19.03.18. + */ - public RuleProcessingMsg(ChainProcessingContext ctx) { - super(); - this.ctx = ctx; - } +@Data +final class RuleNodeRelation { - public ChainProcessingContext getCtx() { - return ctx; - } + private final EntityId in; + private final EntityId out; + private final String type; } diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeToRuleChainTellNextMsg.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeToRuleChainTellNextMsg.java new file mode 100644 index 0000000000..054284dc59 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeToRuleChainTellNextMsg.java @@ -0,0 +1,39 @@ +/** + * Copyright © 2016-2018 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.ruleChain; + +import lombok.Data; +import org.thingsboard.server.common.data.id.RuleNodeId; +import org.thingsboard.server.common.msg.MsgType; +import org.thingsboard.server.common.msg.TbActorMsg; +import org.thingsboard.server.common.msg.TbMsg; + +/** + * Created by ashvayka on 19.03.18. + */ +@Data +final class RuleNodeToRuleChainTellNextMsg implements TbActorMsg { + + private final RuleNodeId originator; + private final String relationType; + private final TbMsg msg; + + @Override + public MsgType getMsgType() { + return MsgType.RULE_TO_RULE_CHAIN_TELL_NEXT_MSG; + } + +} diff --git a/application/src/main/java/org/thingsboard/server/actors/rule/RulesProcessedMsg.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeToSelfErrorMsg.java similarity index 57% rename from application/src/main/java/org/thingsboard/server/actors/rule/RulesProcessedMsg.java rename to application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeToSelfErrorMsg.java index 82b6a6784e..e6248f199f 100644 --- a/application/src/main/java/org/thingsboard/server/actors/rule/RulesProcessedMsg.java +++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeToSelfErrorMsg.java @@ -13,22 +13,25 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.actors.rule; +package org.thingsboard.server.actors.ruleChain; -public class RulesProcessedMsg { - private final ChainProcessingContext ctx; +import lombok.Data; +import org.thingsboard.server.common.msg.MsgType; +import org.thingsboard.server.common.msg.TbActorMsg; +import org.thingsboard.server.common.msg.TbMsg; - public RulesProcessedMsg(ChainProcessingContext ctx) { - super(); - this.ctx = ctx; - } +/** + * Created by ashvayka on 19.03.18. + */ +@Data +final class RuleNodeToSelfErrorMsg implements TbActorMsg { - public ChainProcessingContext getCtx() { - return ctx; - } + private final TbMsg msg; + private final Throwable error; @Override - public String toString() { - return "RulesProcessedMsg [ctx=" + ctx + "]"; + public MsgType getMsgType() { + return MsgType.RULE_TO_SELF_ERROR_MSG; } + } diff --git a/application/src/main/java/org/thingsboard/server/actors/service/ActorService.java b/application/src/main/java/org/thingsboard/server/actors/service/ActorService.java index baae376c64..0be0385365 100644 --- a/application/src/main/java/org/thingsboard/server/actors/service/ActorService.java +++ b/application/src/main/java/org/thingsboard/server/actors/service/ActorService.java @@ -15,20 +15,19 @@ */ package org.thingsboard.server.actors.service; -import org.thingsboard.server.common.data.id.DeviceId; -import org.thingsboard.server.common.data.id.PluginId; -import org.thingsboard.server.common.data.id.RuleId; -import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.id.*; import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; +import org.thingsboard.server.common.msg.TbMsg; +import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg; import org.thingsboard.server.common.transport.SessionMsgProcessor; import org.thingsboard.server.service.cluster.discovery.DiscoveryServiceListener; import org.thingsboard.server.service.cluster.rpc.RpcMsgListener; public interface ActorService extends SessionMsgProcessor, WebSocketMsgProcessor, RestMsgProcessor, RpcMsgListener, DiscoveryServiceListener { - void onPluginStateChange(TenantId tenantId, PluginId pluginId, ComponentLifecycleEvent state); + void onEntityStateChange(TenantId tenantId, EntityId entityId, ComponentLifecycleEvent state); - void onRuleStateChange(TenantId tenantId, RuleId ruleId, ComponentLifecycleEvent state); + void onMsg(ServiceToRuleEngineMsg msg); void onCredentialsUpdate(TenantId tenantId, DeviceId deviceId); diff --git a/application/src/main/java/org/thingsboard/server/actors/service/ComponentActor.java b/application/src/main/java/org/thingsboard/server/actors/service/ComponentActor.java index 76b9be96b3..6aa68d3c5b 100644 --- a/application/src/main/java/org/thingsboard/server/actors/service/ComponentActor.java +++ b/application/src/main/java/org/thingsboard/server/actors/service/ComponentActor.java @@ -54,7 +54,7 @@ public abstract class ComponentActor(INITIAL_SESSION_MAP_SIZE); } + @Override + protected boolean process(TbActorMsg msg) { + //TODO Move everything here, to work with TbActorMsg + return false; + } + @Override public void onReceive(Object msg) throws Exception { if (msg instanceof SessionCtrlMsg) { 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 73b221ffc5..e1313d20ef 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 @@ -102,9 +102,6 @@ public abstract class AbstractContextAwareMsgProcessor { case FILTER: configurationClazz = ((Filter) componentClazz.getAnnotation(Filter.class)).configuration(); break; - case PROCESSOR: - configurationClazz = ((Processor) componentClazz.getAnnotation(Processor.class)).configuration(); - break; case ACTION: configurationClazz = ((Action) componentClazz.getAnnotation(Action.class)).configuration(); break; 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 18d32d9476..e25d3a7af9 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 @@ -20,12 +20,14 @@ import akka.event.LoggingAdapter; import org.thingsboard.server.actors.ActorSystemContext; import org.thingsboard.server.actors.stats.StatsPersistTick; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.plugin.ComponentLifecycleState; import org.thingsboard.server.common.msg.cluster.ClusterEventMsg; public abstract class ComponentMsgProcessor extends AbstractContextAwareMsgProcessor { protected final TenantId tenantId; protected final T entityId; + protected ComponentLifecycleState state; protected ComponentMsgProcessor(ActorSystemContext systemContext, LoggingAdapter logger, TenantId tenantId, T id) { super(systemContext, logger); @@ -33,23 +35,44 @@ public abstract class ComponentMsgProcessor extends AbstractContextAwareMsgPr this.entityId = id; } - public abstract void start() throws Exception; + public abstract void start(ActorContext context) throws Exception; - public abstract void stop() throws Exception; + public abstract void stop(ActorContext context) throws Exception; - public abstract void onCreated(ActorContext context) throws Exception; + public abstract void onClusterEventMsg(ClusterEventMsg msg) throws Exception; - public abstract void onUpdate(ActorContext context) throws Exception; + public void onCreated(ActorContext context) throws Exception { + start(context); + } - public abstract void onActivate(ActorContext context) throws Exception; + public void onUpdate(ActorContext context) throws Exception { + restart(context); + } - public abstract void onSuspend(ActorContext context) throws Exception; + public void onActivate(ActorContext context) throws Exception { + restart(context); + } - public abstract void onStop(ActorContext context) throws Exception; + public void onSuspend(ActorContext context) throws Exception { + stop(context); + } - public abstract void onClusterEventMsg(ClusterEventMsg msg) throws Exception; + public void onStop(ActorContext context) throws Exception { + stop(context); + } + + private void restart(ActorContext context) throws Exception { + stop(context); + start(context); + } public void scheduleStatsPersistTick(ActorContext context, long statsPersistFrequency) { schedulePeriodicMsgWithDelay(context, new StatsPersistTick(), statsPersistFrequency, statsPersistFrequency); } + + protected void checkActive() { + if (state != ComponentLifecycleState.ACTIVE) { + throw new IllegalStateException("Rule chain is not active!"); + } + } } diff --git a/application/src/main/java/org/thingsboard/server/actors/shared/EntityActorsManager.java b/application/src/main/java/org/thingsboard/server/actors/shared/EntityActorsManager.java new file mode 100644 index 0000000000..d4a1f34006 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/actors/shared/EntityActorsManager.java @@ -0,0 +1,86 @@ +/** + * Copyright © 2016-2018 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.shared; + +import akka.actor.ActorContext; +import akka.actor.ActorRef; +import akka.actor.Props; +import akka.actor.UntypedActor; +import akka.japi.Creator; +import lombok.extern.slf4j.Slf4j; +import org.thingsboard.server.actors.ActorSystemContext; +import org.thingsboard.server.actors.service.ContextAwareActor; +import org.thingsboard.server.common.data.SearchTextBased; +import org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.id.UUIDBased; +import org.thingsboard.server.common.data.page.PageDataIterable; +import org.thingsboard.server.common.data.plugin.PluginMetaData; + +import java.util.HashMap; +import java.util.Map; + +/** + * Created by ashvayka on 15.03.18. + */ +@Slf4j +public abstract class EntityActorsManager> { + + protected final ActorSystemContext systemContext; + protected final Map actors; + + public EntityActorsManager(ActorSystemContext systemContext) { + this.systemContext = systemContext; + this.actors = new HashMap<>(); + } + + protected abstract TenantId getTenantId(); + + protected abstract String getDispatcherName(); + + protected abstract Creator creator(T entityId); + + protected abstract PageDataIterable.FetchFunction getFetchEntitiesFunction(); + + public void init(ActorContext context) { + for (M entity : new PageDataIterable<>(getFetchEntitiesFunction(), ContextAwareActor.ENTITY_PACK_LIMIT)) { + T entityId = (T) entity.getId(); + log.debug("[{}|{}] Creating entity actor", entityId.getEntityType(), entityId.getId()); + //TODO: remove this cast making UUIDBased subclass of EntityId an interface and vice versa. + ActorRef actorRef = getOrCreateActor(context, entityId); + visit(entity, actorRef); + log.debug("[{}|{}] Entity actor created.", entityId.getEntityType(), entityId.getId()); + } + } + + protected void visit(M entity, ActorRef actorRef) {} + + public ActorRef getOrCreateActor(ActorContext context, T entityId) { + return actors.computeIfAbsent(entityId, eId -> + context.actorOf(Props.create(creator(eId)) + .withDispatcher(getDispatcherName()), eId.toString())); + } + + public void broadcast(Object msg) { + actors.values().forEach(actorRef -> actorRef.tell(msg, ActorRef.noSender())); + } + + public void remove(T id) { + actors.remove(id); + } + +} diff --git a/application/src/main/java/org/thingsboard/server/actors/shared/plugin/PluginManager.java b/application/src/main/java/org/thingsboard/server/actors/shared/plugin/PluginManager.java index 4f5871f585..3345e5fff5 100644 --- a/application/src/main/java/org/thingsboard/server/actors/shared/plugin/PluginManager.java +++ b/application/src/main/java/org/thingsboard/server/actors/shared/plugin/PluginManager.java @@ -15,63 +15,28 @@ */ package org.thingsboard.server.actors.shared.plugin; -import akka.actor.ActorContext; -import akka.actor.ActorRef; -import akka.actor.Props; +import akka.japi.Creator; import lombok.extern.slf4j.Slf4j; import org.thingsboard.server.actors.ActorSystemContext; import org.thingsboard.server.actors.plugin.PluginActor; -import org.thingsboard.server.actors.service.ContextAwareActor; +import org.thingsboard.server.actors.shared.EntityActorsManager; import org.thingsboard.server.common.data.id.PluginId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.page.PageDataIterable; -import org.thingsboard.server.common.data.page.PageDataIterable.FetchFunction; import org.thingsboard.server.common.data.plugin.PluginMetaData; import org.thingsboard.server.dao.plugin.PluginService; -import java.util.HashMap; -import java.util.Map; - @Slf4j -public abstract class PluginManager { +public abstract class PluginManager extends EntityActorsManager { - protected final ActorSystemContext systemContext; protected final PluginService pluginService; - protected final Map pluginActors; public PluginManager(ActorSystemContext systemContext) { - this.systemContext = systemContext; + super(systemContext); this.pluginService = systemContext.getPluginService(); - this.pluginActors = new HashMap<>(); } - public void init(ActorContext context) { - PageDataIterable pluginIterator = new PageDataIterable<>(getFetchPluginsFunction(), - ContextAwareActor.ENTITY_PACK_LIMIT); - for (PluginMetaData plugin : pluginIterator) { - log.debug("[{}] Creating plugin actor", plugin.getId()); - getOrCreatePluginActor(context, plugin.getId()); - log.debug("Plugin actor created."); - } + @Override + public Creator creator(PluginId entityId){ + return new PluginActor.ActorCreator(systemContext, getTenantId(), entityId); } - abstract FetchFunction getFetchPluginsFunction(); - - abstract TenantId getTenantId(); - - abstract String getDispatcherName(); - - public ActorRef getOrCreatePluginActor(ActorContext context, PluginId pluginId) { - return pluginActors.computeIfAbsent(pluginId, pId -> - context.actorOf(Props.create(new PluginActor.ActorCreator(systemContext, getTenantId(), pId)) - .withDispatcher(getDispatcherName()), pId.toString())); - } - - public void broadcast(Object msg) { - pluginActors.values().forEach(actorRef -> actorRef.tell(msg, ActorRef.noSender())); - } - - public void remove(PluginId id) { - pluginActors.remove(id); - } } diff --git a/application/src/main/java/org/thingsboard/server/actors/shared/plugin/SystemPluginManager.java b/application/src/main/java/org/thingsboard/server/actors/shared/plugin/SystemPluginManager.java index 0888e23f25..88c52a6043 100644 --- a/application/src/main/java/org/thingsboard/server/actors/shared/plugin/SystemPluginManager.java +++ b/application/src/main/java/org/thingsboard/server/actors/shared/plugin/SystemPluginManager.java @@ -29,12 +29,12 @@ public class SystemPluginManager extends PluginManager { } @Override - FetchFunction getFetchPluginsFunction() { + protected FetchFunction getFetchEntitiesFunction() { return pluginService::findSystemPlugins; } @Override - TenantId getTenantId() { + protected TenantId getTenantId() { return BasePluginService.SYSTEM_TENANT; } diff --git a/application/src/main/java/org/thingsboard/server/actors/shared/plugin/TenantPluginManager.java b/application/src/main/java/org/thingsboard/server/actors/shared/plugin/TenantPluginManager.java index 14ea2aa7f8..09115f0f7d 100644 --- a/application/src/main/java/org/thingsboard/server/actors/shared/plugin/TenantPluginManager.java +++ b/application/src/main/java/org/thingsboard/server/actors/shared/plugin/TenantPluginManager.java @@ -19,6 +19,7 @@ import akka.actor.ActorContext; import org.thingsboard.server.actors.ActorSystemContext; import org.thingsboard.server.actors.service.DefaultActorService; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.page.PageDataIterable; import org.thingsboard.server.common.data.page.PageDataIterable.FetchFunction; import org.thingsboard.server.common.data.plugin.PluginMetaData; @@ -39,12 +40,12 @@ public class TenantPluginManager extends PluginManager { } @Override - FetchFunction getFetchPluginsFunction() { + protected FetchFunction getFetchEntitiesFunction() { return link -> pluginService.findTenantPlugins(tenantId, link); } @Override - TenantId getTenantId() { + protected TenantId getTenantId() { return tenantId; } diff --git a/application/src/main/java/org/thingsboard/server/actors/shared/rule/RuleManager.java b/application/src/main/java/org/thingsboard/server/actors/shared/rule/RuleManager.java deleted file mode 100644 index 95d762afdf..0000000000 --- a/application/src/main/java/org/thingsboard/server/actors/shared/rule/RuleManager.java +++ /dev/null @@ -1,135 +0,0 @@ -/** - * Copyright © 2016-2018 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.shared.rule; - -import akka.actor.ActorContext; -import akka.actor.ActorRef; -import akka.actor.Props; -import lombok.extern.slf4j.Slf4j; -import org.thingsboard.server.actors.ActorSystemContext; -import org.thingsboard.server.actors.rule.RuleActor; -import org.thingsboard.server.actors.rule.RuleActorChain; -import org.thingsboard.server.actors.rule.RuleActorMetaData; -import org.thingsboard.server.actors.rule.SimpleRuleActorChain; -import org.thingsboard.server.actors.service.ContextAwareActor; -import org.thingsboard.server.common.data.id.RuleId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.page.PageDataIterable; -import org.thingsboard.server.common.data.page.PageDataIterable.FetchFunction; -import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; -import org.thingsboard.server.common.data.plugin.ComponentLifecycleState; -import org.thingsboard.server.common.data.rule.RuleMetaData; -import org.thingsboard.server.dao.rule.RuleService; - -import java.util.*; - -@Slf4j -public abstract class RuleManager { - - protected final ActorSystemContext systemContext; - protected final RuleService ruleService; - protected final Map ruleActors; - protected final TenantId tenantId; - - private Map ruleMap; - private RuleActorChain ruleChain; - - public RuleManager(ActorSystemContext systemContext, TenantId tenantId) { - this.systemContext = systemContext; - this.ruleService = systemContext.getRuleService(); - this.ruleActors = new HashMap<>(); - this.tenantId = tenantId; - } - - public void init(ActorContext context) { - doInit(context); - } - - private void doInit(ActorContext context) { - PageDataIterable ruleIterator = new PageDataIterable<>(getFetchRulesFunction(), - ContextAwareActor.ENTITY_PACK_LIMIT); - ruleMap = new HashMap<>(); - - for (RuleMetaData rule : ruleIterator) { - log.debug("[{}] Creating rule actor {}", rule.getId(), rule); - ActorRef ref = getOrCreateRuleActor(context, rule.getId()); - ruleMap.put(rule, RuleActorMetaData.systemRule(rule.getId(), rule.getWeight(), ref)); - log.debug("[{}] Rule actor created.", rule.getId()); - } - - refreshRuleChain(); - } - - public Optional update(ActorContext context, RuleId ruleId, ComponentLifecycleEvent event) { - if (ruleMap == null) { - doInit(context); - } - RuleMetaData rule; - if (event != ComponentLifecycleEvent.DELETED) { - rule = systemContext.getRuleService().findRuleById(ruleId); - } else { - rule = ruleMap.keySet().stream() - .filter(r -> r.getId().equals(ruleId)) - .peek(r -> r.setState(ComponentLifecycleState.SUSPENDED)) - .findFirst() - .orElse(null); - if (rule != null) { - ruleMap.remove(rule); - ruleActors.remove(ruleId); - } - } - if (rule != null) { - RuleActorMetaData actorMd = ruleMap.get(rule); - if (actorMd == null) { - ActorRef ref = getOrCreateRuleActor(context, rule.getId()); - actorMd = RuleActorMetaData.systemRule(rule.getId(), rule.getWeight(), ref); - ruleMap.put(rule, actorMd); - } - refreshRuleChain(); - return Optional.of(actorMd.getActorRef()); - } else { - log.warn("[{}] Can't process unknown rule!", ruleId); - return Optional.empty(); - } - } - - abstract FetchFunction getFetchRulesFunction(); - - abstract String getDispatcherName(); - - public ActorRef getOrCreateRuleActor(ActorContext context, RuleId ruleId) { - return ruleActors.computeIfAbsent(ruleId, rId -> - context.actorOf(Props.create(new RuleActor.ActorCreator(systemContext, tenantId, rId)) - .withDispatcher(getDispatcherName()), rId.toString())); - } - - public RuleActorChain getRuleChain(ActorContext context) { - if (ruleChain == null) { - doInit(context); - } - return ruleChain; - } - - private void refreshRuleChain() { - Set activeRuleSet = new HashSet<>(); - for (Map.Entry rule : ruleMap.entrySet()) { - if (rule.getKey().getState() == ComponentLifecycleState.ACTIVE) { - activeRuleSet.add(rule.getValue()); - } - } - ruleChain = new SimpleRuleActorChain(activeRuleSet); - } -} diff --git a/application/src/main/java/org/thingsboard/server/actors/shared/rulechain/RuleChainManager.java b/application/src/main/java/org/thingsboard/server/actors/shared/rulechain/RuleChainManager.java new file mode 100644 index 0000000000..ff0c52ef45 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/actors/shared/rulechain/RuleChainManager.java @@ -0,0 +1,59 @@ +/** + * Copyright © 2016-2018 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.shared.rulechain; + +import akka.actor.ActorRef; +import akka.japi.Creator; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import org.thingsboard.server.actors.ActorSystemContext; +import org.thingsboard.server.actors.ruleChain.RuleChainActor; +import org.thingsboard.server.actors.shared.EntityActorsManager; +import org.thingsboard.server.common.data.id.RuleChainId; +import org.thingsboard.server.common.data.rule.RuleChain; +import org.thingsboard.server.dao.rule.RuleChainService; + +/** + * Created by ashvayka on 15.03.18. + */ +@Slf4j +public abstract class RuleChainManager extends EntityActorsManager { + + protected final RuleChainService service; + @Getter + protected RuleChain rootChain; + @Getter + protected ActorRef rootChainActor; + + public RuleChainManager(ActorSystemContext systemContext) { + super(systemContext); + this.service = systemContext.getRuleChainService(); + } + + @Override + public Creator creator(RuleChainId entityId) { + return new RuleChainActor.ActorCreator(systemContext, getTenantId(), entityId); + } + + @Override + protected void visit(RuleChain entity, ActorRef actorRef) { + if (entity.isRoot()) { + rootChain = entity; + rootChainActor = actorRef; + } + } + +} diff --git a/application/src/main/java/org/thingsboard/server/actors/shared/rule/SystemRuleManager.java b/application/src/main/java/org/thingsboard/server/actors/shared/rulechain/SystemRuleChainManager.java similarity index 57% rename from application/src/main/java/org/thingsboard/server/actors/shared/rule/SystemRuleManager.java rename to application/src/main/java/org/thingsboard/server/actors/shared/rulechain/SystemRuleChainManager.java index d10731caed..a8bb069685 100644 --- a/application/src/main/java/org/thingsboard/server/actors/shared/rule/SystemRuleManager.java +++ b/application/src/main/java/org/thingsboard/server/actors/shared/rulechain/SystemRuleChainManager.java @@ -13,28 +13,35 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.actors.shared.rule; +package org.thingsboard.server.actors.shared.rulechain; import org.thingsboard.server.actors.ActorSystemContext; import org.thingsboard.server.actors.service.DefaultActorService; +import org.thingsboard.server.actors.shared.plugin.PluginManager; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageDataIterable.FetchFunction; -import org.thingsboard.server.common.data.rule.RuleMetaData; -import org.thingsboard.server.dao.model.ModelConstants; +import org.thingsboard.server.common.data.plugin.PluginMetaData; +import org.thingsboard.server.common.data.rule.RuleChain; +import org.thingsboard.server.dao.plugin.BasePluginService; -public class SystemRuleManager extends RuleManager { +public class SystemRuleChainManager extends RuleChainManager { - public SystemRuleManager(ActorSystemContext systemContext) { - super(systemContext, new TenantId(ModelConstants.NULL_UUID)); + public SystemRuleChainManager(ActorSystemContext systemContext) { + super(systemContext); } @Override - FetchFunction getFetchRulesFunction() { - return ruleService::findSystemRules; + protected FetchFunction getFetchEntitiesFunction() { + return service::findSystemRuleChains; } @Override - String getDispatcherName() { + protected TenantId getTenantId() { + return BasePluginService.SYSTEM_TENANT; + } + + @Override + protected String getDispatcherName() { return DefaultActorService.SYSTEM_RULE_DISPATCHER_NAME; } } diff --git a/application/src/main/java/org/thingsboard/server/actors/shared/rule/TenantRuleManager.java b/application/src/main/java/org/thingsboard/server/actors/shared/rulechain/TenantRuleChainManager.java similarity index 65% rename from application/src/main/java/org/thingsboard/server/actors/shared/rule/TenantRuleManager.java rename to application/src/main/java/org/thingsboard/server/actors/shared/rulechain/TenantRuleChainManager.java index e4d023ca90..731d8d8e6c 100644 --- a/application/src/main/java/org/thingsboard/server/actors/shared/rule/TenantRuleManager.java +++ b/application/src/main/java/org/thingsboard/server/actors/shared/rulechain/TenantRuleChainManager.java @@ -13,19 +13,22 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.actors.shared.rule; +package org.thingsboard.server.actors.shared.rulechain; import akka.actor.ActorContext; import org.thingsboard.server.actors.ActorSystemContext; import org.thingsboard.server.actors.service.DefaultActorService; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageDataIterable.FetchFunction; -import org.thingsboard.server.common.data.rule.RuleMetaData; +import org.thingsboard.server.common.data.rule.RuleChain; -public class TenantRuleManager extends RuleManager { - - public TenantRuleManager(ActorSystemContext systemContext, TenantId tenantId) { - super(systemContext, tenantId); +public class TenantRuleChainManager extends RuleChainManager { + + private final TenantId tenantId; + + public TenantRuleChainManager(ActorSystemContext systemContext, TenantId tenantId) { + super(systemContext); + this.tenantId = tenantId; } @Override @@ -36,13 +39,17 @@ public class TenantRuleManager extends RuleManager { } @Override - FetchFunction getFetchRulesFunction() { - return link -> ruleService.findTenantRules(tenantId, link); + protected TenantId getTenantId() { + return tenantId; } @Override - String getDispatcherName() { + protected String getDispatcherName() { return DefaultActorService.TENANT_RULE_DISPATCHER_NAME; } + @Override + protected FetchFunction getFetchEntitiesFunction() { + return link -> service.findTenantRuleChains(tenantId, link); + } } 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 ccc31cca29..8623370896 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 @@ -24,6 +24,7 @@ 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.TbActorMsg; import org.thingsboard.server.common.msg.cluster.ServerAddress; public class StatsActor extends ContextAwareActor { @@ -35,6 +36,12 @@ public class StatsActor extends ContextAwareActor { super(context); } + @Override + protected boolean process(TbActorMsg msg) { + //TODO Move everything here, to work with TbActorMsg\ + return false; + } + @Override public void onReceive(Object msg) throws Exception { logger.debug("Received message: {}", msg); diff --git a/application/src/main/java/org/thingsboard/server/actors/tenant/RuleChainDeviceMsg.java b/application/src/main/java/org/thingsboard/server/actors/tenant/RuleChainDeviceMsg.java deleted file mode 100644 index a84e0b5fd4..0000000000 --- a/application/src/main/java/org/thingsboard/server/actors/tenant/RuleChainDeviceMsg.java +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Copyright © 2016-2018 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.tenant; - -import org.thingsboard.server.actors.rule.RuleActorChain; -import org.thingsboard.server.common.msg.device.ToDeviceActorMsg; - -public class RuleChainDeviceMsg { - - private final ToDeviceActorMsg toDeviceActorMsg; - private final RuleActorChain ruleChain; - - public RuleChainDeviceMsg(ToDeviceActorMsg toDeviceActorMsg, RuleActorChain ruleChain) { - super(); - this.toDeviceActorMsg = toDeviceActorMsg; - this.ruleChain = ruleChain; - } - - public ToDeviceActorMsg getToDeviceActorMsg() { - return toDeviceActorMsg; - } - - public RuleActorChain getRuleChain() { - return ruleChain; - } - -} 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 b923fe15a3..d53c054414 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,52 +15,38 @@ */ package org.thingsboard.server.actors.tenant; -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; - +import akka.actor.ActorRef; +import akka.actor.Props; +import akka.event.Logging; +import akka.event.LoggingAdapter; import org.thingsboard.server.actors.ActorSystemContext; import org.thingsboard.server.actors.device.DeviceActor; import org.thingsboard.server.actors.plugin.PluginTerminationMsg; -import org.thingsboard.server.actors.rule.ComplexRuleActorChain; -import org.thingsboard.server.actors.rule.RuleActorChain; -import org.thingsboard.server.actors.service.ContextAwareActor; +import org.thingsboard.server.actors.ruleChain.RuleChainManagerActor; import org.thingsboard.server.actors.service.ContextBasedCreator; import org.thingsboard.server.actors.service.DefaultActorService; -import org.thingsboard.server.actors.shared.plugin.PluginManager; import org.thingsboard.server.actors.shared.plugin.TenantPluginManager; -import org.thingsboard.server.actors.shared.rule.RuleManager; -import org.thingsboard.server.actors.shared.rule.TenantRuleManager; +import org.thingsboard.server.actors.shared.rulechain.TenantRuleChainManager; import org.thingsboard.server.common.data.id.DeviceId; -import org.thingsboard.server.common.data.id.PluginId; -import org.thingsboard.server.common.data.id.RuleId; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.msg.cluster.ClusterEventMsg; +import org.thingsboard.server.common.msg.TbActorMsg; import org.thingsboard.server.common.msg.device.ToDeviceActorMsg; - -import akka.actor.ActorRef; -import akka.actor.Props; -import akka.event.Logging; -import akka.event.LoggingAdapter; import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; +import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg; import org.thingsboard.server.extensions.api.device.ToDeviceActorNotificationMsg; import org.thingsboard.server.extensions.api.plugins.msg.ToPluginActorMsg; -import org.thingsboard.server.extensions.api.rules.ToRuleActorMsg; -public class TenantActor extends ContextAwareActor { +import java.util.HashMap; +import java.util.Map; - private final LoggingAdapter logger = Logging.getLogger(getContext().system(), this); +public class TenantActor extends RuleChainManagerActor { private final TenantId tenantId; - private final RuleManager ruleManager; - private final PluginManager pluginManager; private final Map deviceActors; private TenantActor(ActorSystemContext systemContext, TenantId tenantId) { - super(systemContext); + super(systemContext, new TenantRuleChainManager(systemContext, tenantId), new TenantPluginManager(systemContext, tenantId)); this.tenantId = tenantId; - this.ruleManager = new TenantRuleManager(systemContext, tenantId); - this.pluginManager = new TenantPluginManager(systemContext, tenantId); this.deviceActors = new HashMap<>(); } @@ -68,8 +54,7 @@ public class TenantActor extends ContextAwareActor { public void preStart() { logger.info("[{}] Starting tenant actor.", tenantId); try { - ruleManager.init(this.context()); - pluginManager.init(this.context()); + initRuleChains(); logger.info("[{}] Tenant actor started.", tenantId); } catch (Exception e) { logger.error(e, "[{}] Unknown failure", tenantId); @@ -77,29 +62,45 @@ public class TenantActor extends ContextAwareActor { } @Override - public void onReceive(Object msg) throws Exception { - logger.debug("[{}] Received message: {}", tenantId, msg); - if (msg instanceof RuleChainDeviceMsg) { - process((RuleChainDeviceMsg) msg); - } else if (msg instanceof ToDeviceActorMsg) { - onToDeviceActorMsg((ToDeviceActorMsg) msg); - } else if (msg instanceof ToPluginActorMsg) { - onToPluginMsg((ToPluginActorMsg) msg); - } else if (msg instanceof ToRuleActorMsg) { - onToRuleMsg((ToRuleActorMsg) msg); - } else if (msg instanceof ToDeviceActorNotificationMsg) { - onToDeviceActorMsg((ToDeviceActorNotificationMsg) msg); - } else if (msg instanceof ClusterEventMsg) { - broadcast(msg); - } else if (msg instanceof ComponentLifecycleMsg) { - onComponentLifecycleMsg((ComponentLifecycleMsg) msg); - } else if (msg instanceof PluginTerminationMsg) { - onPluginTerminated((PluginTerminationMsg) msg); - } else { - logger.warning("[{}] Unknown message: {}!", tenantId, msg); + protected boolean process(TbActorMsg msg) { + switch (msg.getMsgType()) { + case COMPONENT_LIFE_CYCLE_MSG: + onComponentLifecycleMsg((ComponentLifecycleMsg) msg); + break; + case SERVICE_TO_RULE_ENGINE_MSG: + onServiceToRuleEngineMsg((ServiceToRuleEngineMsg) msg); + break; + default: + return false; } + return true; } + private void onServiceToRuleEngineMsg(ServiceToRuleEngineMsg msg) { + ruleChainManager.getRootChainActor().tell(msg, self()); + } + + +// @Override +// public void onReceive(Object msg) throws Exception { +// logger.debug("[{}] Received message: {}", tenantId, msg); +// if (msg instanceof ToDeviceActorMsg) { +// onToDeviceActorMsg((ToDeviceActorMsg) msg); +// } else if (msg instanceof ToPluginActorMsg) { +// onToPluginMsg((ToPluginActorMsg) msg); +// } else if (msg instanceof ToDeviceActorNotificationMsg) { +// onToDeviceActorMsg((ToDeviceActorNotificationMsg) msg); +// } else if (msg instanceof ClusterEventMsg) { +// broadcast(msg); +// } else if (msg instanceof ComponentLifecycleMsg) { +// onComponentLifecycleMsg((ComponentLifecycleMsg) msg); +// } else if (msg instanceof PluginTerminationMsg) { +// onPluginTerminated((PluginTerminationMsg) msg); +// } else { +// logger.warning("[{}] Unknown message: {}!", tenantId, msg); +// } +// } + private void broadcast(Object msg) { pluginManager.broadcast(msg); deviceActors.values().forEach(actorRef -> actorRef.tell(msg, ActorRef.noSender())); @@ -113,14 +114,9 @@ public class TenantActor extends ContextAwareActor { getOrCreateDeviceActor(msg.getDeviceId()).tell(msg, ActorRef.noSender()); } - private void onToRuleMsg(ToRuleActorMsg msg) { - ActorRef target = ruleManager.getOrCreateRuleActor(this.context(), msg.getRuleId()); - target.tell(msg, ActorRef.noSender()); - } - private void onToPluginMsg(ToPluginActorMsg msg) { if (msg.getPluginTenantId().equals(tenantId)) { - ActorRef pluginActor = pluginManager.getOrCreatePluginActor(this.context(), msg.getPluginId()); + ActorRef pluginActor = pluginManager.getOrCreateActor(this.context(), msg.getPluginId()); pluginActor.tell(msg, ActorRef.noSender()); } else { context().parent().tell(msg, ActorRef.noSender()); @@ -128,23 +124,11 @@ public class TenantActor extends ContextAwareActor { } private void onComponentLifecycleMsg(ComponentLifecycleMsg msg) { - Optional pluginId = msg.getPluginId(); - Optional ruleId = msg.getRuleId(); - if (pluginId.isPresent()) { - ActorRef pluginActor = pluginManager.getOrCreatePluginActor(this.context(), pluginId.get()); - pluginActor.tell(msg, ActorRef.noSender()); - } else if (ruleId.isPresent()) { - ActorRef target; - Optional ref = ruleManager.update(this.context(), ruleId.get(), msg.getEvent()); - if (ref.isPresent()) { - target = ref.get(); - } else { - logger.debug("Failed to find actor for rule: [{}]", ruleId); - return; - } + ActorRef target = getEntityActorRef(msg.getEntityId()); + if (target != null) { target.tell(msg, ActorRef.noSender()); } else { - logger.debug("[{}] Invalid component lifecycle msg.", tenantId); + logger.debug("Invalid component lifecycle msg: {}", msg); } } @@ -152,13 +136,6 @@ public class TenantActor extends ContextAwareActor { pluginManager.remove(msg.getId()); } - private void process(RuleChainDeviceMsg msg) { - ToDeviceActorMsg toDeviceActorMsg = msg.getToDeviceActorMsg(); - ActorRef deviceActor = getOrCreateDeviceActor(toDeviceActorMsg.getDeviceId()); - RuleActorChain tenantChain = ruleManager.getRuleChain(this.context()); - RuleActorChain chain = new ComplexRuleActorChain(msg.getRuleChain(), tenantChain); - deviceActor.tell(new RuleChainDeviceMsg(toDeviceActorMsg, chain), context().self()); - } private ActorRef getOrCreateDeviceActor(DeviceId deviceId) { return deviceActors.computeIfAbsent(deviceId, k -> context().actorOf(Props.create(new DeviceActor.ActorCreator(systemContext, tenantId, deviceId)) diff --git a/application/src/main/java/org/thingsboard/server/controller/PluginController.java b/application/src/main/java/org/thingsboard/server/controller/PluginController.java index 2c69248faa..ed1760057b 100644 --- a/application/src/main/java/org/thingsboard/server/controller/PluginController.java +++ b/application/src/main/java/org/thingsboard/server/controller/PluginController.java @@ -71,7 +71,7 @@ public class PluginController extends BaseController { boolean created = source.getId() == null; source.setTenantId(getCurrentUser().getTenantId()); PluginMetaData plugin = checkNotNull(pluginService.savePlugin(source)); - actorService.onPluginStateChange(plugin.getTenantId(), plugin.getId(), + actorService.onEntityStateChange(plugin.getTenantId(), plugin.getId(), created ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED); logEntityAction(plugin.getId(), plugin, @@ -97,7 +97,7 @@ public class PluginController extends BaseController { PluginId pluginId = new PluginId(toUUID(strPluginId)); PluginMetaData plugin = checkPlugin(pluginService.findPluginById(pluginId)); pluginService.activatePluginById(pluginId); - actorService.onPluginStateChange(plugin.getTenantId(), plugin.getId(), ComponentLifecycleEvent.ACTIVATED); + actorService.onEntityStateChange(plugin.getTenantId(), plugin.getId(), ComponentLifecycleEvent.ACTIVATED); logEntityAction(plugin.getId(), plugin, null, @@ -123,7 +123,7 @@ public class PluginController extends BaseController { PluginId pluginId = new PluginId(toUUID(strPluginId)); PluginMetaData plugin = checkPlugin(pluginService.findPluginById(pluginId)); pluginService.suspendPluginById(pluginId); - actorService.onPluginStateChange(plugin.getTenantId(), plugin.getId(), ComponentLifecycleEvent.SUSPENDED); + actorService.onEntityStateChange(plugin.getTenantId(), plugin.getId(), ComponentLifecycleEvent.SUSPENDED); logEntityAction(plugin.getId(), plugin, null, @@ -221,7 +221,7 @@ public class PluginController extends BaseController { PluginId pluginId = new PluginId(toUUID(strPluginId)); PluginMetaData plugin = checkPlugin(pluginService.findPluginById(pluginId)); pluginService.deletePluginById(pluginId); - actorService.onPluginStateChange(plugin.getTenantId(), plugin.getId(), ComponentLifecycleEvent.DELETED); + actorService.onEntityStateChange(plugin.getTenantId(), plugin.getId(), ComponentLifecycleEvent.DELETED); logEntityAction(pluginId, plugin, null, diff --git a/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java b/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java index f24646b56f..c6befdd8ca 100644 --- a/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java +++ b/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java @@ -78,6 +78,9 @@ public class RuleChainController extends BaseController { ruleChain.setTenantId(getCurrentUser().getTenantId()); RuleChain savedRuleChain = checkNotNull(ruleChainService.saveRuleChain(ruleChain)); + actorService.onEntityStateChange(ruleChain.getTenantId(), savedRuleChain.getId(), + created ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED); + logEntityAction(savedRuleChain.getId(), savedRuleChain, null, created ? ActionType.ADDED : ActionType.UPDATED, null); @@ -100,6 +103,8 @@ public class RuleChainController extends BaseController { RuleChain ruleChain = checkRuleChain(ruleChainMetaData.getRuleChainId()); RuleChainMetaData savedRuleChainMetaData = checkNotNull(ruleChainService.saveRuleChainMetaData(ruleChainMetaData)); + actorService.onEntityStateChange(ruleChain.getTenantId(), ruleChain.getId(), ComponentLifecycleEvent.UPDATED); + logEntityAction(ruleChain.getId(), ruleChain, null, ActionType.UPDATED, null, ruleChainMetaData); @@ -183,6 +188,8 @@ public class RuleChainController extends BaseController { RuleChain ruleChain = checkRuleChain(ruleChainId); ruleChainService.deleteRuleChainById(ruleChainId); + actorService.onEntityStateChange(ruleChain.getTenantId(), ruleChain.getId(), ComponentLifecycleEvent.DELETED); + logEntityAction(ruleChainId, ruleChain, null, ActionType.DELETED, null, strRuleChainId); diff --git a/application/src/main/java/org/thingsboard/server/controller/RuleController.java b/application/src/main/java/org/thingsboard/server/controller/RuleController.java index e498c8fffc..9a269029b5 100644 --- a/application/src/main/java/org/thingsboard/server/controller/RuleController.java +++ b/application/src/main/java/org/thingsboard/server/controller/RuleController.java @@ -73,7 +73,7 @@ public class RuleController extends BaseController { boolean created = source.getId() == null; source.setTenantId(getCurrentUser().getTenantId()); RuleMetaData rule = checkNotNull(ruleService.saveRule(source)); - actorService.onRuleStateChange(rule.getTenantId(), rule.getId(), + actorService.onEntityStateChange(rule.getTenantId(), rule.getId(), created ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED); logEntityAction(rule.getId(), rule, @@ -99,7 +99,7 @@ public class RuleController extends BaseController { RuleId ruleId = new RuleId(toUUID(strRuleId)); RuleMetaData rule = checkRule(ruleService.findRuleById(ruleId)); ruleService.activateRuleById(ruleId); - actorService.onRuleStateChange(rule.getTenantId(), rule.getId(), ComponentLifecycleEvent.ACTIVATED); + actorService.onEntityStateChange(rule.getTenantId(), rule.getId(), ComponentLifecycleEvent.ACTIVATED); logEntityAction(rule.getId(), rule, null, @@ -125,7 +125,7 @@ public class RuleController extends BaseController { RuleId ruleId = new RuleId(toUUID(strRuleId)); RuleMetaData rule = checkRule(ruleService.findRuleById(ruleId)); ruleService.suspendRuleById(ruleId); - actorService.onRuleStateChange(rule.getTenantId(), rule.getId(), ComponentLifecycleEvent.SUSPENDED); + actorService.onEntityStateChange(rule.getTenantId(), rule.getId(), ComponentLifecycleEvent.SUSPENDED); logEntityAction(rule.getId(), rule, null, @@ -219,7 +219,7 @@ public class RuleController extends BaseController { RuleId ruleId = new RuleId(toUUID(strRuleId)); RuleMetaData rule = checkRule(ruleService.findRuleById(ruleId)); ruleService.deleteRuleById(ruleId); - actorService.onRuleStateChange(rule.getTenantId(), rule.getId(), ComponentLifecycleEvent.DELETED); + actorService.onEntityStateChange(rule.getTenantId(), rule.getId(), ComponentLifecycleEvent.DELETED); logEntityAction(ruleId, rule, null, diff --git a/application/src/main/java/org/thingsboard/server/service/component/AnnotationComponentDiscoveryService.java b/application/src/main/java/org/thingsboard/server/service/component/AnnotationComponentDiscoveryService.java index 0a6081d8da..910b45954a 100644 --- a/application/src/main/java/org/thingsboard/server/service/component/AnnotationComponentDiscoveryService.java +++ b/application/src/main/java/org/thingsboard/server/service/component/AnnotationComponentDiscoveryService.java @@ -26,6 +26,10 @@ import org.springframework.context.annotation.ClassPathScanningCandidateComponen import org.springframework.core.env.Environment; import org.springframework.core.type.filter.AnnotationTypeFilter; import org.springframework.stereotype.Service; +import org.thingsboard.rule.engine.api.ActionNode; +import org.thingsboard.rule.engine.api.EnrichmentNode; +import org.thingsboard.rule.engine.api.FilterNode; +import org.thingsboard.rule.engine.api.TransformationNode; import org.thingsboard.server.common.data.plugin.ComponentDescriptor; import org.thingsboard.server.common.data.plugin.ComponentType; import org.thingsboard.server.dao.component.ComponentDescriptorService; @@ -79,8 +83,7 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe private List persist(Set filterDefs, ComponentType type) { List result = new ArrayList<>(); for (BeanDefinition def : filterDefs) { - ComponentDescriptor scannedComponent = scanAndPersistComponent(def, type); - result.add(scannedComponent); + result.add(scanAndPersistComponent(def, type)); } return result; } @@ -93,24 +96,36 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe Class clazz = Class.forName(clazzName); String descriptorResourceName; switch (type) { + case ENRICHMENT: + EnrichmentNode enrichmentAnnotation = clazz.getAnnotation(EnrichmentNode.class); + scannedComponent.setName(enrichmentAnnotation.name()); + scannedComponent.setScope(enrichmentAnnotation.scope()); + descriptorResourceName = enrichmentAnnotation.descriptor(); + break; case FILTER: - Filter filterAnnotation = clazz.getAnnotation(Filter.class); + FilterNode filterAnnotation = clazz.getAnnotation(FilterNode.class); scannedComponent.setName(filterAnnotation.name()); scannedComponent.setScope(filterAnnotation.scope()); descriptorResourceName = filterAnnotation.descriptor(); break; - case PROCESSOR: - Processor processorAnnotation = clazz.getAnnotation(Processor.class); - scannedComponent.setName(processorAnnotation.name()); - scannedComponent.setScope(processorAnnotation.scope()); - descriptorResourceName = processorAnnotation.descriptor(); + case TRANSFORMATION: + TransformationNode trAnnotation = clazz.getAnnotation(TransformationNode.class); + scannedComponent.setName(trAnnotation.name()); + scannedComponent.setScope(trAnnotation.scope()); + descriptorResourceName = trAnnotation.descriptor(); break; case ACTION: - Action actionAnnotation = clazz.getAnnotation(Action.class); + ActionNode actionAnnotation = clazz.getAnnotation(ActionNode.class); scannedComponent.setName(actionAnnotation.name()); scannedComponent.setScope(actionAnnotation.scope()); descriptorResourceName = actionAnnotation.descriptor(); break; + case OLD_ACTION: + Action oldActionAnnotation = clazz.getAnnotation(Action.class); + scannedComponent.setName(oldActionAnnotation.name()); + scannedComponent.setScope(oldActionAnnotation.scope()); + descriptorResourceName = oldActionAnnotation.descriptor(); + break; case PLUGIN: Plugin pluginAnnotation = clazz.getAnnotation(Plugin.class); scannedComponent.setName(pluginAnnotation.name()); @@ -122,12 +137,12 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe log.error("Can't initialize plugin {}, due to missing action {}!", def.getBeanClassName(), actionClazz.getName()); return new ClassNotFoundException("Action: " + actionClazz.getName() + "is missing!"); }); - if (actionComponent.getType() != ComponentType.ACTION) { + if (actionComponent.getType() != ComponentType.OLD_ACTION) { log.error("Plugin {} action {} has wrong component type!", def.getBeanClassName(), actionClazz.getName(), actionComponent.getType()); throw new RuntimeException("Plugin " + def.getBeanClassName() + "action " + actionClazz.getName() + " has wrong component type!"); } } - scannedComponent.setActions(Arrays.stream(pluginAnnotation.actions()).map(action -> action.getName()).collect(Collectors.joining(","))); + scannedComponent.setActions(Arrays.stream(pluginAnnotation.actions()).map(Class::getName).collect(Collectors.joining(","))); break; default: throw new RuntimeException(type + " is not supported yet!"); @@ -168,11 +183,15 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe @Override public void discoverComponents() { - registerComponents(ComponentType.FILTER, Filter.class); + registerComponents(ComponentType.ENRICHMENT, EnrichmentNode.class); + + registerComponents(ComponentType.FILTER, FilterNode.class); + + registerComponents(ComponentType.TRANSFORMATION, TransformationNode.class); - registerComponents(ComponentType.PROCESSOR, Processor.class); + registerComponents(ComponentType.ACTION, ActionNode.class); - registerComponents(ComponentType.ACTION, Action.class); + registerComponents(ComponentType.OLD_ACTION, Action.class); registerComponents(ComponentType.PLUGIN, Plugin.class); @@ -199,7 +218,7 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe } List result = new ArrayList<>(); for (String action : plugin.getActions().split(",")) { - getComponent(action).ifPresent(v -> result.add(v)); + getComponent(action).ifPresent(result::add); } return result; } else { diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 1c842c8a1b..27585212db 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -62,7 +62,7 @@ cluster: # Plugins configuration parameters plugins: # Comma seperated package list used during classpath scanning for plugins - scan_packages: "${PLUGINS_SCAN_PACKAGES:org.thingsboard.server.extensions}" + scan_packages: "${PLUGINS_SCAN_PACKAGES:org.thingsboard.server.extensions,org.thingsboard.rule.engine}" # JWT Token parameters security.jwt: @@ -215,6 +215,12 @@ actors: termination.delay: "${ACTORS_RULE_TERMINATION_DELAY:30000}" # Errors for particular actor are persisted once per specified amount of milliseconds error_persist_frequency: "${ACTORS_RULE_ERROR_FREQUENCY:3000}" + chain: + # Errors for particular actor are persisted once per specified amount of milliseconds + error_persist_frequency: "${ACTORS_RULE_CHAIN_ERROR_FREQUENCY:3000}" + node: + # Errors for particular actor are persisted once per specified amount of milliseconds + error_persist_frequency: "${ACTORS_RULE_NODE_ERROR_FREQUENCY:3000}" statistics: # Enable/disable actor statistics enabled: "${ACTORS_STATISTICS_ENABLED:true}" diff --git a/application/src/test/java/org/thingsboard/server/controller/AbstractControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/AbstractControllerTest.java index b92e464a6e..3ec4dc815b 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AbstractControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AbstractControllerTest.java @@ -96,6 +96,8 @@ import static org.springframework.test.web.servlet.setup.MockMvcBuilders.webAppC @Slf4j public abstract class AbstractControllerTest { + protected ObjectMapper mapper = new ObjectMapper(); + protected static final String TEST_TENANT_NAME = "TEST TENANT"; protected static final String SYS_ADMIN_EMAIL = "sysadmin@thingsboard.org"; diff --git a/application/src/test/java/org/thingsboard/server/controller/AbstractRuleEngineControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/AbstractRuleEngineControllerTest.java new file mode 100644 index 0000000000..bbcb98ff48 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/controller/AbstractRuleEngineControllerTest.java @@ -0,0 +1,56 @@ +/** + * Copyright © 2016-2018 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.controller; + +import com.fasterxml.jackson.core.type.TypeReference; +import org.thingsboard.server.common.data.DataConstants; +import org.thingsboard.server.common.data.Event; +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.page.TimePageData; +import org.thingsboard.server.common.data.page.TimePageLink; +import org.thingsboard.server.common.data.rule.RuleChain; +import org.thingsboard.server.common.data.rule.RuleChainMetaData; + +/** + * Created by ashvayka on 20.03.18. + */ +public class AbstractRuleEngineControllerTest extends AbstractControllerTest { + + protected RuleChain saveRuleChain(RuleChain ruleChain) throws Exception { + return doPost("/api/ruleChain", ruleChain, RuleChain.class); + } + + protected RuleChain getRuleChain(RuleChainId ruleChainId) throws Exception { + return doGet("/api/ruleChain/" + ruleChainId.getId().toString(), RuleChain.class); + } + + protected RuleChainMetaData saveRuleChainMetaData(RuleChainMetaData ruleChainMD) throws Exception { + return doPost("/api/ruleChain/metadata", ruleChainMD, RuleChainMetaData.class); + } + + protected RuleChainMetaData getRuleChainMetaData(RuleChainId ruleChainId) throws Exception { + return doGet("/api/ruleChain/metadata/" + ruleChainId.getId().toString(), RuleChainMetaData.class); + } + + protected TimePageData getDebugEvents(TenantId tenantId, EntityId entityId, int limit) throws Exception { + TimePageLink pageLink = new TimePageLink(limit); + return doGetTypedWithTimePageLink("/api/events/{entityType}/{entityId}/{eventType}?tenantId={tenantId}&", + new TypeReference>() { + }, pageLink, entityId.getEntityType(), entityId.getId(), DataConstants.DEBUG, tenantId.getId()); + } +} diff --git a/application/src/main/java/org/thingsboard/server/actors/rule/RuleContextAwareMsgProcessor.java b/application/src/test/java/org/thingsboard/server/rules/RuleEngineSqlTestSuite.java similarity index 50% rename from application/src/main/java/org/thingsboard/server/actors/rule/RuleContextAwareMsgProcessor.java rename to application/src/test/java/org/thingsboard/server/rules/RuleEngineSqlTestSuite.java index c0d0705c4d..65b4293490 100644 --- a/application/src/main/java/org/thingsboard/server/actors/rule/RuleContextAwareMsgProcessor.java +++ b/application/src/test/java/org/thingsboard/server/rules/RuleEngineSqlTestSuite.java @@ -13,21 +13,23 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.actors.rule; +package org.thingsboard.server.rules; -import org.thingsboard.server.actors.ActorSystemContext; -import org.thingsboard.server.actors.shared.AbstractContextAwareMsgProcessor; -import org.thingsboard.server.common.data.id.RuleId; +import org.junit.ClassRule; +import org.junit.extensions.cpsuite.ClasspathSuite; +import org.junit.runner.RunWith; +import org.thingsboard.server.dao.CustomSqlUnit; -import akka.event.LoggingAdapter; +import java.util.Arrays; -public class RuleContextAwareMsgProcessor extends AbstractContextAwareMsgProcessor { - - private final RuleId ruleId; - - protected RuleContextAwareMsgProcessor(ActorSystemContext systemContext, LoggingAdapter logger, RuleId ruleId) { - super(systemContext, logger); - this.ruleId = ruleId; - } +@RunWith(ClasspathSuite.class) +@ClasspathSuite.ClassnameFilters({ + "org.thingsboard.server.rules.flow.*Test"}) +public class RuleEngineSqlTestSuite { + @ClassRule + public static CustomSqlUnit sqlUnit = new CustomSqlUnit( + Arrays.asList("sql/schema.sql", "sql/system-data.sql"), + "sql/drop-all-tables.sql", + "sql-test.properties"); } 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 new file mode 100644 index 0000000000..acbace6ae7 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/rules/flow/AbstractRuleEngineFlowIntegrationTest.java @@ -0,0 +1,190 @@ +/** + * Copyright © 2016-2018 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.rules.flow; + +import com.datastax.driver.core.utils.UUIDs; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.thingsboard.rule.engine.metadata.TbGetAttributesNodeConfiguration; +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; +import org.thingsboard.server.common.data.page.TimePageData; +import org.thingsboard.server.common.data.rule.RuleChain; +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.TbMsgMetaData; +import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg; +import org.thingsboard.server.controller.AbstractRuleEngineControllerTest; +import org.thingsboard.server.dao.attributes.AttributesService; +import org.thingsboard.server.dao.rule.RuleChainService; + +import java.util.Arrays; +import java.util.Collections; + +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +/** + * @author Valerii Sosliuk + */ +@Slf4j +public abstract class AbstractRuleEngineFlowIntegrationTest extends AbstractRuleEngineControllerTest { + + protected Tenant savedTenant; + protected User tenantAdmin; + + @Autowired + protected ActorService actorService; + + @Autowired + protected AttributesService attributesService; + + @Autowired + protected RuleChainService ruleChainService; + + @Before + public void beforeTest() throws Exception { + loginSysAdmin(); + + Tenant tenant = new Tenant(); + tenant.setTitle("My tenant"); + savedTenant = doPost("/api/tenant", tenant, Tenant.class); + Assert.assertNotNull(savedTenant); + + tenantAdmin = new User(); + tenantAdmin.setAuthority(Authority.TENANT_ADMIN); + tenantAdmin.setTenantId(savedTenant.getId()); + tenantAdmin.setEmail("tenant2@thingsboard.org"); + tenantAdmin.setFirstName("Joe"); + tenantAdmin.setLastName("Downs"); + + createUserAndLogin(tenantAdmin, "testPassword1"); + } + + @After + public void afterTest() throws Exception { + loginSysAdmin(); + if (savedTenant != null) { + doDelete("/api/tenant/" + savedTenant.getId().getId().toString()).andExpect(status().isOk()); + } + } + + @Test + public void testRuleChainWithTwoRules() throws Exception { + // Creating Rule Chain + RuleChain ruleChain = new RuleChain(); + ruleChain.setName("Simple Rule Chain"); + ruleChain.setTenantId(savedTenant.getId()); + ruleChain.setRoot(true); + ruleChain.setDebugMode(true); + ruleChain = saveRuleChain(ruleChain); + Assert.assertNull(ruleChain.getFirstRuleNodeId()); + + RuleChainMetaData metaData = new RuleChainMetaData(); + metaData.setRuleChainId(ruleChain.getId()); + + RuleNode ruleNode1 = new RuleNode(); + ruleNode1.setName("Simple Rule Node 1"); + ruleNode1.setType(org.thingsboard.rule.engine.metadata.TbGetAttributesNode.class.getName()); + ruleNode1.setDebugMode(true); + TbGetAttributesNodeConfiguration configuration1 = new TbGetAttributesNodeConfiguration(); + configuration1.setServerAttributeNames(Collections.singletonList("serverAttributeKey1")); + ruleNode1.setConfiguration(mapper.valueToTree(configuration1)); + + RuleNode ruleNode2 = new RuleNode(); + ruleNode2.setName("Simple Rule Node 2"); + ruleNode2.setType(org.thingsboard.rule.engine.metadata.TbGetAttributesNode.class.getName()); + ruleNode2.setDebugMode(true); + TbGetAttributesNodeConfiguration configuration2 = new TbGetAttributesNodeConfiguration(); + configuration2.setServerAttributeNames(Collections.singletonList("serverAttributeKey2")); + ruleNode2.setConfiguration(mapper.valueToTree(configuration2)); + + + metaData.setNodes(Arrays.asList(ruleNode1, ruleNode2)); + metaData.setFirstNodeIndex(0); + metaData.addConnectionInfo(0, 1, "Success"); + metaData = saveRuleChainMetaData(metaData); + Assert.assertNotNull(metaData); + + ruleChain = getRuleChain(ruleChain.getId()); + Assert.assertNotNull(ruleChain.getFirstRuleNodeId()); + + // Saving the device + Device device = new Device(); + device.setName("My device"); + device.setType("default"); + device = doPost("/api/device", device, Device.class); + + attributesService.save(device.getId(), DataConstants.SERVER_SCOPE, + Collections.singletonList(new BaseAttributeKvEntry(new StringDataEntry("serverAttributeKey1", "serverAttributeValue1"), System.currentTimeMillis()))); + attributesService.save(device.getId(), DataConstants.SERVER_SCOPE, + Collections.singletonList(new BaseAttributeKvEntry(new StringDataEntry("serverAttributeKey2", "serverAttributeValue2"), System.currentTimeMillis()))); + + + Thread.sleep(1000); + + // Pushing Message to the system + TbMsg tbMsg = new TbMsg(UUIDs.timeBased(), + "CUSTOM", + device.getId(), + new TbMsgMetaData(), + new byte[]{}); + actorService.onMsg(new ServiceToRuleEngineMsg(savedTenant.getId(), tbMsg)); + + Thread.sleep(3000); + + TimePageData events = getDebugEvents(savedTenant.getId(), ruleChain.getFirstRuleNodeId(), 1000); + + Assert.assertEquals(2, events.getData().size()); + + Event inEvent = events.getData().stream().filter(e -> e.getBody().get("type").asText().equals(DataConstants.IN)).findFirst().get(); + Assert.assertEquals(ruleChain.getFirstRuleNodeId(), inEvent.getEntityId()); + Assert.assertEquals(device.getId().getId().toString(), inEvent.getBody().get("entityId").asText()); + + Event outEvent = events.getData().stream().filter(e -> e.getBody().get("type").asText().equals(DataConstants.OUT)).findFirst().get(); + Assert.assertEquals(ruleChain.getFirstRuleNodeId(), outEvent.getEntityId()); + Assert.assertEquals(device.getId().getId().toString(), outEvent.getBody().get("entityId").asText()); + + Assert.assertEquals("serverAttributeValue1", outEvent.getBody().get("metadata").get("ss.serverAttributeKey1").asText()); + + RuleChain finalRuleChain = ruleChain; + RuleNode lastRuleNode = metaData.getNodes().stream().filter(node -> !node.getId().equals(finalRuleChain.getFirstRuleNodeId())).findFirst().get(); + + events = getDebugEvents(savedTenant.getId(), lastRuleNode.getId(), 1000); + + Assert.assertEquals(2, events.getData().size()); + + inEvent = events.getData().stream().filter(e -> e.getBody().get("type").asText().equals(DataConstants.IN)).findFirst().get(); + Assert.assertEquals(lastRuleNode.getId(), inEvent.getEntityId()); + Assert.assertEquals(device.getId().getId().toString(), inEvent.getBody().get("entityId").asText()); + + outEvent = events.getData().stream().filter(e -> e.getBody().get("type").asText().equals(DataConstants.OUT)).findFirst().get(); + Assert.assertEquals(lastRuleNode.getId(), outEvent.getEntityId()); + Assert.assertEquals(device.getId().getId().toString(), outEvent.getBody().get("entityId").asText()); + + Assert.assertEquals("serverAttributeValue1", outEvent.getBody().get("metadata").get("ss.serverAttributeKey1").asText()); + Assert.assertEquals("serverAttributeValue2", outEvent.getBody().get("metadata").get("ss.serverAttributeKey2").asText()); + } + +} diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/rule/sql/RuleServiceSqlTest.java b/application/src/test/java/org/thingsboard/server/rules/flow/RuleEngineFlowSqlIntegrationTest.java similarity index 71% rename from dao/src/test/java/org/thingsboard/server/dao/service/rule/sql/RuleServiceSqlTest.java rename to application/src/test/java/org/thingsboard/server/rules/flow/RuleEngineFlowSqlIntegrationTest.java index da3d4a0b7b..18a164ef33 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/rule/sql/RuleServiceSqlTest.java +++ b/application/src/test/java/org/thingsboard/server/rules/flow/RuleEngineFlowSqlIntegrationTest.java @@ -13,11 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.dao.service.rule.sql; +package org.thingsboard.server.rules.flow; import org.thingsboard.server.dao.service.DaoSqlTest; -import org.thingsboard.server.dao.service.rule.BaseRuleServiceTest; +import org.thingsboard.server.mqtt.rpc.AbstractMqttServerSideRpcIntegrationTest; +/** + * Created by Valerii Sosliuk on 8/22/2017. + */ @DaoSqlTest -public class RuleServiceSqlTest extends BaseRuleServiceTest { +public class RuleEngineFlowSqlIntegrationTest extends AbstractRuleEngineFlowIntegrationTest { } 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 new file mode 100644 index 0000000000..de690a745f --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/rules/lifecycle/AbstractRuleEngineLifecycleIntegrationTest.java @@ -0,0 +1,158 @@ +/** + * Copyright © 2016-2018 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.rules.lifecycle; + +import com.datastax.driver.core.utils.UUIDs; +import lombok.extern.slf4j.Slf4j; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.thingsboard.rule.engine.metadata.TbGetAttributesNodeConfiguration; +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; +import org.thingsboard.server.common.data.Tenant; +import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; +import org.thingsboard.server.common.data.kv.StringDataEntry; +import org.thingsboard.server.common.data.page.TimePageData; +import org.thingsboard.server.common.data.rule.RuleChain; +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.TbMsgMetaData; +import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg; +import org.thingsboard.server.controller.AbstractRuleEngineControllerTest; +import org.thingsboard.server.dao.attributes.AttributesService; + +import java.util.Collections; + +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +/** + * @author Valerii Sosliuk + */ +@Slf4j +public abstract class AbstractRuleEngineLifecycleIntegrationTest extends AbstractRuleEngineControllerTest { + + protected Tenant savedTenant; + protected User tenantAdmin; + + @Autowired + protected ActorService actorService; + + @Autowired + protected AttributesService attributesService; + + @Before + public void beforeTest() throws Exception { + loginSysAdmin(); + + Tenant tenant = new Tenant(); + tenant.setTitle("My tenant"); + savedTenant = doPost("/api/tenant", tenant, Tenant.class); + Assert.assertNotNull(savedTenant); + + tenantAdmin = new User(); + tenantAdmin.setAuthority(Authority.TENANT_ADMIN); + tenantAdmin.setTenantId(savedTenant.getId()); + tenantAdmin.setEmail("tenant2@thingsboard.org"); + tenantAdmin.setFirstName("Joe"); + tenantAdmin.setLastName("Downs"); + + createUserAndLogin(tenantAdmin, "testPassword1"); + } + + @After + public void afterTest() throws Exception { + loginSysAdmin(); + if (savedTenant != null) { + doDelete("/api/tenant/" + savedTenant.getId().getId().toString()).andExpect(status().isOk()); + } + } + + @Test + public void testRuleChainWithOneRule() throws Exception { + // Creating Rule Chain + RuleChain ruleChain = new RuleChain(); + ruleChain.setName("Simple Rule Chain"); + ruleChain.setTenantId(savedTenant.getId()); + ruleChain.setRoot(true); + ruleChain.setDebugMode(true); + ruleChain = saveRuleChain(ruleChain); + Assert.assertNull(ruleChain.getFirstRuleNodeId()); + + RuleChainMetaData metaData = new RuleChainMetaData(); + metaData.setRuleChainId(ruleChain.getId()); + + RuleNode ruleNode = new RuleNode(); + ruleNode.setName("Simple Rule Node"); + ruleNode.setType(org.thingsboard.rule.engine.metadata.TbGetAttributesNode.class.getName()); + ruleNode.setDebugMode(true); + TbGetAttributesNodeConfiguration configuration = new TbGetAttributesNodeConfiguration(); + configuration.setServerAttributeNames(Collections.singletonList("serverAttributeKey")); + ruleNode.setConfiguration(mapper.valueToTree(configuration)); + + metaData.setNodes(Collections.singletonList(ruleNode)); + metaData.setFirstNodeIndex(0); + + metaData = saveRuleChainMetaData(metaData); + Assert.assertNotNull(metaData); + + ruleChain = getRuleChain(ruleChain.getId()); + Assert.assertNotNull(ruleChain.getFirstRuleNodeId()); + + // Saving the device + Device device = new Device(); + device.setName("My device"); + device.setType("default"); + device = doPost("/api/device", device, Device.class); + + attributesService.save(device.getId(), DataConstants.SERVER_SCOPE, + Collections.singletonList(new BaseAttributeKvEntry(new StringDataEntry("serverAttributeKey", "serverAttributeValue"), System.currentTimeMillis()))); + + Thread.sleep(1000); + + // Pushing Message to the system + TbMsg tbMsg = new TbMsg(UUIDs.timeBased(), + "CUSTOM", + device.getId(), + new TbMsgMetaData(), + new byte[]{}); + actorService.onMsg(new ServiceToRuleEngineMsg(savedTenant.getId(), tbMsg)); + + Thread.sleep(3000); + + TimePageData events = getDebugEvents(savedTenant.getId(), ruleChain.getFirstRuleNodeId(), 1000); + + Assert.assertEquals(2, events.getData().size()); + + Event inEvent = events.getData().stream().filter(e -> e.getBody().get("type").asText().equals(DataConstants.IN)).findFirst().get(); + Assert.assertEquals(ruleChain.getFirstRuleNodeId(), inEvent.getEntityId()); + Assert.assertEquals(device.getId().getId().toString(), inEvent.getBody().get("entityId").asText()); + + Event outEvent = events.getData().stream().filter(e -> e.getBody().get("type").asText().equals(DataConstants.OUT)).findFirst().get(); + Assert.assertEquals(ruleChain.getFirstRuleNodeId(), outEvent.getEntityId()); + Assert.assertEquals(device.getId().getId().toString(), outEvent.getBody().get("entityId").asText()); + + Assert.assertEquals("serverAttributeValue", outEvent.getBody().get("metadata").get("ss.serverAttributeKey").asText()); + } + +} diff --git a/application/src/test/java/org/thingsboard/server/rules/lifecycle/RuleEngineLifecycleSqlIntegrationTest.java b/application/src/test/java/org/thingsboard/server/rules/lifecycle/RuleEngineLifecycleSqlIntegrationTest.java new file mode 100644 index 0000000000..004958b2ea --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/rules/lifecycle/RuleEngineLifecycleSqlIntegrationTest.java @@ -0,0 +1,26 @@ +/** + * Copyright © 2016-2018 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.rules.lifecycle; + +import org.thingsboard.server.dao.service.DaoSqlTest; +import org.thingsboard.server.rules.flow.AbstractRuleEngineFlowIntegrationTest; + +/** + * Created by Valerii Sosliuk on 8/22/2017. + */ +@DaoSqlTest +public class RuleEngineLifecycleSqlIntegrationTest extends AbstractRuleEngineLifecycleIntegrationTest { +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/DataConstants.java b/common/data/src/main/java/org/thingsboard/server/common/data/DataConstants.java index a776d7b6ce..659a242882 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/DataConstants.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/DataConstants.java @@ -37,7 +37,12 @@ public class DataConstants { public static final String ERROR = "ERROR"; public static final String LC_EVENT = "LC_EVENT"; public static final String STATS = "STATS"; + public static final String DEBUG = "DEBUG"; public static final String ONEWAY = "ONEWAY"; public static final String TWOWAY = "TWOWAY"; + + public static final String IN = "IN"; + public static final String OUT = "OUT"; + } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/page/PageDataIterable.java b/common/data/src/main/java/org/thingsboard/server/common/data/page/PageDataIterable.java index 34f8c3aed6..ffd78225c4 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/page/PageDataIterable.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/page/PageDataIterable.java @@ -20,6 +20,7 @@ import java.util.List; import java.util.NoSuchElementException; import org.thingsboard.server.common.data.SearchTextBased; +import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.UUIDBased; public class PageDataIterable> implements Iterable, Iterator { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/plugin/ComponentType.java b/common/data/src/main/java/org/thingsboard/server/common/data/plugin/ComponentType.java index 45fb590ed6..a1030640cb 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/plugin/ComponentType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/plugin/ComponentType.java @@ -20,6 +20,6 @@ package org.thingsboard.server.common.data.plugin; */ public enum ComponentType { - FILTER, PROCESSOR, ACTION, PLUGIN + ENRICHMENT, FILTER, TRANSFORMATION, ACTION, OLD_ACTION, PLUGIN } diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/rule/nosql/RuleServiceNoSqlTest.java b/common/data/src/main/java/org/thingsboard/server/common/data/rule/NodeConnectionInfo.java similarity index 70% rename from dao/src/test/java/org/thingsboard/server/dao/service/rule/nosql/RuleServiceNoSqlTest.java rename to common/data/src/main/java/org/thingsboard/server/common/data/rule/NodeConnectionInfo.java index 7ff9066499..0c9fd5feb5 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/rule/nosql/RuleServiceNoSqlTest.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/rule/NodeConnectionInfo.java @@ -13,11 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.dao.service.rule.nosql; +package org.thingsboard.server.common.data.rule; -import org.thingsboard.server.dao.service.DaoNoSqlTest; -import org.thingsboard.server.dao.service.rule.BaseRuleServiceTest; +import lombok.Data; -@DaoNoSqlTest -public class RuleServiceNoSqlTest extends BaseRuleServiceTest { +/** + * Created by ashvayka on 21.03.18. + */ +@Data +public class NodeConnectionInfo { + private int fromIndex; + private int toIndex; + private String type; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChain.java b/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChain.java index f2ba0cc66e..218061adb1 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChain.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChain.java @@ -38,6 +38,7 @@ public class RuleChain extends SearchTextBasedWithAdditionalInfo im private String name; private RuleNodeId firstRuleNodeId; private boolean root; + private boolean debugMode; private transient JsonNode configuration; @JsonIgnore private byte[] configurationBytes; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChainConnectionInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChainConnectionInfo.java new file mode 100644 index 0000000000..35cf6aadad --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChainConnectionInfo.java @@ -0,0 +1,29 @@ +/** + * Copyright © 2016-2018 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.common.data.rule; + +import lombok.Data; +import org.thingsboard.server.common.data.id.RuleChainId; + +/** + * Created by ashvayka on 21.03.18. + */ +@Data +public class RuleChainConnectionInfo { + private int fromIndex; + private RuleChainId targetRuleChainId; + private String type; +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChainMetaData.java b/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChainMetaData.java index af141d6142..1be1518637 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChainMetaData.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChainMetaData.java @@ -58,18 +58,4 @@ public class RuleChainMetaData { ruleChainConnections.add(connectionInfo); } - @Data - public class NodeConnectionInfo { - private int fromIndex; - private int toIndex; - private String type; - } - - @Data - public class RuleChainConnectionInfo { - private int fromIndex; - private RuleChainId targetRuleChainId; - private String type; - } - } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleNode.java b/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleNode.java index d044000117..fbc1103dee 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleNode.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleNode.java @@ -34,6 +34,7 @@ public class RuleNode extends SearchTextBasedWithAdditionalInfo impl private String type; private String name; + private boolean debugMode; private transient JsonNode configuration; @JsonIgnore private byte[] configurationBytes; 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 new file mode 100644 index 0000000000..7c00ee64ab --- /dev/null +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/MsgType.java @@ -0,0 +1,57 @@ +/** + * Copyright © 2016-2018 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.common.msg; + +/** + * Created by ashvayka on 15.03.18. + */ +public enum MsgType { + + /** + * ADDED/UPDATED/DELETED events for main entities. + * + * @See {@link org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg} + */ + COMPONENT_LIFE_CYCLE_MSG, + + /** + * Misc messages from the REST API/SERVICE layer to the new rule engine. + * + * @See {@link org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg} + */ + SERVICE_TO_RULE_ENGINE_MSG, + + + SESSION_TO_DEVICE_ACTOR_MSG, + DEVICE_ACTOR_TO_SESSION_MSG, + + + /** + * Message that is sent by RuleChainActor to RuleActor with command to process TbMsg. + */ + RULE_CHAIN_TO_RULE_MSG, + + /** + * Message that is sent by RuleActor to RuleChainActor with command to process TbMsg by next nodes in chain. + */ + RULE_TO_RULE_CHAIN_TELL_NEXT_MSG, + + /** + * Message that is sent by RuleActor implementation to RuleActor itself to log the error. + */ + RULE_TO_SELF_ERROR_MSG, + +} diff --git a/application/src/main/java/org/thingsboard/server/actors/rule/RuleActorChain.java b/common/message/src/main/java/org/thingsboard/server/common/msg/TbActorMsg.java similarity index 81% rename from application/src/main/java/org/thingsboard/server/actors/rule/RuleActorChain.java rename to common/message/src/main/java/org/thingsboard/server/common/msg/TbActorMsg.java index 3f3bd36f65..c361c11c0e 100644 --- a/application/src/main/java/org/thingsboard/server/actors/rule/RuleActorChain.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/TbActorMsg.java @@ -13,12 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.actors.rule; +package org.thingsboard.server.common.msg; -public interface RuleActorChain { - - int size(); +/** + * Created by ashvayka on 15.03.18. + */ +public interface TbActorMsg { - RuleActorMetaData getRuleActorMd(int index); + MsgType getMsgType(); } 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 261dda7670..524cc5fe60 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 @@ -17,6 +17,7 @@ package org.thingsboard.server.common.msg; import com.google.protobuf.ByteString; import com.google.protobuf.InvalidProtocolBufferException; +import lombok.AllArgsConstructor; import lombok.Data; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityIdFactory; @@ -30,18 +31,23 @@ import java.util.UUID; * Created by ashvayka on 13.01.18. */ @Data -public final class TbMsg implements Serializable, Cloneable { +@AllArgsConstructor +public final class TbMsg implements Serializable { private final UUID id; private final String type; private final EntityId originator; private final TbMsgMetaData metaData; - + private final TbMsgDataType dataType; private final byte[] data; - @Override - public TbMsg clone() { - return fromBytes(toBytes(this)); + public TbMsg(UUID id, String type, EntityId originator, TbMsgMetaData metaData, byte[] data) { + this.id = id; + this.type = type; + this.originator = originator; + this.metaData = metaData; + this.dataType = TbMsgDataType.JSON; + this.data = data; } public static ByteBuffer toBytes(TbMsg msg) { @@ -54,11 +60,10 @@ public final class TbMsg implements Serializable, Cloneable { } if (msg.getMetaData() != null) { - MsgProtos.TbMsgProto.TbMsgMetaDataProto.Builder metadataBuilder = MsgProtos.TbMsgProto.TbMsgMetaDataProto.newBuilder(); - metadataBuilder.putAllData(msg.getMetaData().getData()); - builder.addMetaData(metadataBuilder.build()); + builder.setMetaData(MsgProtos.TbMsgMetaDataProto.newBuilder().putAllData(msg.getMetaData().getData()).build()); } + builder.setDataType(msg.getDataType().ordinal()); builder.setData(ByteString.copyFrom(msg.getData())); byte[] bytes = builder.build().toByteArray(); return ByteBuffer.wrap(bytes); @@ -67,20 +72,19 @@ public final class TbMsg implements Serializable, Cloneable { public static TbMsg fromBytes(ByteBuffer buffer) { try { MsgProtos.TbMsgProto proto = MsgProtos.TbMsgProto.parseFrom(buffer.array()); - TbMsgMetaData metaData = new TbMsgMetaData(); - if (proto.getMetaDataCount() > 0) { - metaData.setData(proto.getMetaData(0).getDataMap()); - } - - EntityId entityId = null; - if (proto.getEntityId() != null) { - entityId = EntityIdFactory.getByTypeAndId(proto.getEntityType(), proto.getEntityId()); - } - - return new TbMsg(UUID.fromString(proto.getId()), proto.getType(), entityId, metaData, proto.getData().toByteArray()); + TbMsgMetaData metaData = new TbMsgMetaData(proto.getMetaData().getDataMap()); + EntityId entityId = EntityIdFactory.getByTypeAndId(proto.getEntityType(), proto.getEntityId()); + TbMsgDataType dataType = TbMsgDataType.values()[proto.getDataType()]; + return new TbMsg(UUID.fromString(proto.getId()), proto.getType(), entityId, metaData, dataType, proto.getData().toByteArray()); } catch (InvalidProtocolBufferException e) { throw new IllegalStateException("Could not parse protobuf for TbMsg", e); } } + public TbMsg copy() { + int dataSize = data.length; + byte[] dataCopy = new byte[dataSize]; + System.arraycopy( data, 0, dataCopy, 0, data.length ); + return new TbMsg(id, type, originator, metaData.copy(), dataType, dataCopy); + } } diff --git a/application/src/main/java/org/thingsboard/server/actors/rule/CompoundRuleActorChain.java b/common/message/src/main/java/org/thingsboard/server/common/msg/TbMsgDataType.java similarity index 73% rename from application/src/main/java/org/thingsboard/server/actors/rule/CompoundRuleActorChain.java rename to common/message/src/main/java/org/thingsboard/server/common/msg/TbMsgDataType.java index b2eb53fe3f..2e367e9536 100644 --- a/application/src/main/java/org/thingsboard/server/actors/rule/CompoundRuleActorChain.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/TbMsgDataType.java @@ -13,8 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.actors.rule; +package org.thingsboard.server.common.msg; -public class CompoundRuleActorChain { +/** + * Created by ashvayka on 15.03.18. + */ +public enum TbMsgDataType { + + // Do not change ordering. We use ordinal to save some bytes on serialization + JSON, TEXT, BINARY; } diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/TbMsgMetaData.java b/common/message/src/main/java/org/thingsboard/server/common/msg/TbMsgMetaData.java index 1bbc7929e2..eca153bdfe 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/TbMsgMetaData.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/TbMsgMetaData.java @@ -15,9 +15,12 @@ */ package org.thingsboard.server.common.msg; +import lombok.AllArgsConstructor; import lombok.Data; +import lombok.NoArgsConstructor; import java.io.Serializable; +import java.util.HashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -25,10 +28,15 @@ import java.util.concurrent.ConcurrentHashMap; * Created by ashvayka on 13.01.18. */ @Data +@NoArgsConstructor public final class TbMsgMetaData implements Serializable { private Map data = new ConcurrentHashMap<>(); + TbMsgMetaData(Map data) { + this.data = data; + } + public String getValue(String key) { return data.get(key); } @@ -37,4 +45,7 @@ public final class TbMsgMetaData implements Serializable { data.put(key, value); } + public TbMsgMetaData copy() { + return new TbMsgMetaData(new ConcurrentHashMap<>(data)); + } } diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/plugin/ComponentLifecycleMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/plugin/ComponentLifecycleMsg.java index d48c3feb10..c104281ffe 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/plugin/ComponentLifecycleMsg.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/plugin/ComponentLifecycleMsg.java @@ -15,14 +15,14 @@ */ package org.thingsboard.server.common.msg.plugin; -import lombok.Data; import lombok.Getter; import lombok.ToString; -import org.thingsboard.server.common.data.id.PluginId; -import org.thingsboard.server.common.data.id.RuleId; -import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.id.*; import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; -import org.thingsboard.server.common.data.plugin.ComponentLifecycleState; +import org.thingsboard.server.common.data.rule.RuleChain; +import org.thingsboard.server.common.msg.MsgType; +import org.thingsboard.server.common.msg.TbActorMsg; import org.thingsboard.server.common.msg.aware.TenantAwareMsg; import org.thingsboard.server.common.msg.cluster.ToAllNodesMsg; @@ -32,34 +32,34 @@ import java.util.Optional; * @author Andrew Shvayka */ @ToString -public class ComponentLifecycleMsg implements TenantAwareMsg, ToAllNodesMsg { +public class ComponentLifecycleMsg implements TbActorMsg, TenantAwareMsg, ToAllNodesMsg { @Getter private final TenantId tenantId; - private final PluginId pluginId; - private final RuleId ruleId; + @Getter + private final EntityId entityId; @Getter private final ComponentLifecycleEvent event; - public static ComponentLifecycleMsg forPlugin(TenantId tenantId, PluginId pluginId, ComponentLifecycleEvent event) { - return new ComponentLifecycleMsg(tenantId, pluginId, null, event); - } - - public static ComponentLifecycleMsg forRule(TenantId tenantId, RuleId ruleId, ComponentLifecycleEvent event) { - return new ComponentLifecycleMsg(tenantId, null, ruleId, event); - } - - private ComponentLifecycleMsg(TenantId tenantId, PluginId pluginId, RuleId ruleId, ComponentLifecycleEvent event) { + public ComponentLifecycleMsg(TenantId tenantId, EntityId entityId, ComponentLifecycleEvent event) { this.tenantId = tenantId; - this.pluginId = pluginId; - this.ruleId = ruleId; + this.entityId = entityId; this.event = event; } public Optional getPluginId() { - return Optional.ofNullable(pluginId); + return entityId.getEntityType() == EntityType.PLUGIN ? Optional.of((PluginId) entityId) : Optional.empty(); } public Optional getRuleId() { - return Optional.ofNullable(ruleId); + return entityId.getEntityType() == EntityType.RULE ? Optional.of((RuleId) entityId) : Optional.empty(); + } + + public Optional getRuleChainId() { + return entityId.getEntityType() == EntityType.RULE_CHAIN ? Optional.of((RuleChainId) entityId) : Optional.empty(); + } + + @Override + public MsgType getMsgType() { + return MsgType.COMPONENT_LIFE_CYCLE_MSG; } } diff --git a/application/src/main/java/org/thingsboard/server/actors/rule/SimpleRuleActorChain.java b/common/message/src/main/java/org/thingsboard/server/common/msg/system/ServiceToRuleEngineMsg.java similarity index 53% rename from application/src/main/java/org/thingsboard/server/actors/rule/SimpleRuleActorChain.java rename to common/message/src/main/java/org/thingsboard/server/common/msg/system/ServiceToRuleEngineMsg.java index 70a26e4430..0792b63c28 100644 --- a/application/src/main/java/org/thingsboard/server/actors/rule/SimpleRuleActorChain.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/system/ServiceToRuleEngineMsg.java @@ -13,27 +13,25 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.actors.rule; +package org.thingsboard.server.common.msg.system; -import java.util.ArrayList; -import java.util.List; -import java.util.Set; +import lombok.Data; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.msg.MsgType; +import org.thingsboard.server.common.msg.TbActorMsg; +import org.thingsboard.server.common.msg.TbMsg; -public class SimpleRuleActorChain implements RuleActorChain { - - private final List rules; - - public SimpleRuleActorChain(Set ruleSet) { - rules = new ArrayList<>(ruleSet); - rules.sort(RuleActorMetaData.RULE_ACTOR_MD_COMPARATOR); - } +/** + * Created by ashvayka on 15.03.18. + */ +@Data +public final class ServiceToRuleEngineMsg implements TbActorMsg { - public int size() { - return rules.size(); - } + private final TenantId tenantId; + private final TbMsg tbMsg; - public RuleActorMetaData getRuleActorMd(int index) { - return rules.get(index); + @Override + public MsgType getMsgType() { + return MsgType.SERVICE_TO_RULE_ENGINE_MSG; } - } diff --git a/common/message/src/main/proto/tbmsg.proto b/common/message/src/main/proto/tbmsg.proto index 90fa2bdbae..62acff20ce 100644 --- a/common/message/src/main/proto/tbmsg.proto +++ b/common/message/src/main/proto/tbmsg.proto @@ -19,6 +19,9 @@ package msgqueue; option java_package = "org.thingsboard.server.common.msg.gen"; option java_outer_classname = "MsgProtos"; +message TbMsgMetaDataProto { + map data = 1; +} message TbMsgProto { string id = 1; @@ -26,11 +29,8 @@ message TbMsgProto { string entityType = 3; string entityId = 4; - message TbMsgMetaDataProto { - map data = 1; - } + TbMsgMetaDataProto metaData = 5; - repeated TbMsgMetaDataProto metaData = 5; - - bytes data = 6; + int32 dataType = 6; + bytes data = 7; } \ No newline at end of file diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java index a159b9e6ec..8c34cd3540 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java @@ -332,6 +332,8 @@ public class ModelConstants { public static final String EVENT_BY_TYPE_AND_ID_VIEW_NAME = "event_by_type_and_id"; public static final String EVENT_BY_ID_VIEW_NAME = "event_by_id"; + public static final String DEBUG_MODE = "debug_mode"; + /** * Cassandra rule chain constants. */ diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/nosql/RuleChainEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/nosql/RuleChainEntity.java index 34659a83df..251a68901a 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/nosql/RuleChainEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/nosql/RuleChainEntity.java @@ -22,6 +22,8 @@ import com.datastax.driver.mapping.annotations.PartitionKey; import com.datastax.driver.mapping.annotations.Table; import com.fasterxml.jackson.databind.JsonNode; import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; import lombok.ToString; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.RuleNodeId; @@ -54,6 +56,10 @@ public class RuleChainEntity implements SearchTextEntity { private UUID firstRuleNodeId; @Column(name = RULE_CHAIN_ROOT_PROPERTY) private boolean root; + @Getter + @Setter + @Column(name = DEBUG_MODE) + private boolean debugMode; @Column(name = RULE_CHAIN_CONFIGURATION_PROPERTY, codec = JsonCodec.class) private JsonNode configuration; @Column(name = ADDITIONAL_INFO_PROPERTY, codec = JsonCodec.class) @@ -71,6 +77,7 @@ public class RuleChainEntity implements SearchTextEntity { this.searchText = ruleChain.getName(); this.firstRuleNodeId = DaoUtil.getId(ruleChain.getFirstRuleNodeId()); this.root = ruleChain.isRoot(); + this.debugMode = ruleChain.isDebugMode(); this.configuration = ruleChain.getConfiguration(); this.additionalInfo = ruleChain.getAdditionalInfo(); } @@ -157,6 +164,7 @@ public class RuleChainEntity implements SearchTextEntity { ruleChain.setFirstRuleNodeId(new RuleNodeId(this.firstRuleNodeId)); } ruleChain.setRoot(this.root); + ruleChain.setDebugMode(this.debugMode); ruleChain.setConfiguration(this.configuration); ruleChain.setAdditionalInfo(this.additionalInfo); return ruleChain; diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/nosql/RuleNodeEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/nosql/RuleNodeEntity.java index ba96e4b98e..8d3f3c3eaf 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/nosql/RuleNodeEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/nosql/RuleNodeEntity.java @@ -21,6 +21,8 @@ import com.datastax.driver.mapping.annotations.PartitionKey; import com.datastax.driver.mapping.annotations.Table; import com.fasterxml.jackson.databind.JsonNode; import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; import lombok.ToString; import org.thingsboard.server.common.data.id.RuleNodeId; import org.thingsboard.server.common.data.rule.RuleNode; @@ -49,6 +51,11 @@ public class RuleNodeEntity implements SearchTextEntity { private JsonNode configuration; @Column(name = ADDITIONAL_INFO_PROPERTY, codec = JsonCodec.class) private JsonNode additionalInfo; + @Getter + @Setter + @Column(name = DEBUG_MODE) + private boolean debugMode; + public RuleNodeEntity() { } @@ -59,6 +66,7 @@ public class RuleNodeEntity implements SearchTextEntity { } this.type = ruleNode.getType(); this.name = ruleNode.getName(); + this.debugMode = ruleNode.isDebugMode(); this.searchText = ruleNode.getName(); this.configuration = ruleNode.getConfiguration(); this.additionalInfo = ruleNode.getAdditionalInfo(); @@ -126,6 +134,7 @@ public class RuleNodeEntity implements SearchTextEntity { ruleNode.setCreatedTime(UUIDs.unixTimestamp(id)); ruleNode.setType(this.type); ruleNode.setName(this.name); + ruleNode.setDebugMode(this.debugMode); ruleNode.setConfiguration(this.configuration); ruleNode.setAdditionalInfo(this.additionalInfo); return ruleNode; diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/RuleChainEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/RuleChainEntity.java index 471ec7b06b..a48421a96a 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/RuleChainEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/RuleChainEntity.java @@ -58,6 +58,9 @@ public class RuleChainEntity extends BaseSqlEntity implements SearchT @Column(name = ModelConstants.RULE_CHAIN_ROOT_PROPERTY) private boolean root; + @Column(name = ModelConstants.DEBUG_MODE) + private boolean debugMode; + @Type(type = "json") @Column(name = ModelConstants.RULE_CHAIN_CONFIGURATION_PROPERTY) private JsonNode configuration; @@ -80,6 +83,7 @@ public class RuleChainEntity extends BaseSqlEntity implements SearchT this.firstRuleNodeId = UUIDConverter.fromTimeUUID(ruleChain.getFirstRuleNodeId().getId()); } this.root = ruleChain.isRoot(); + this.debugMode = ruleChain.isDebugMode(); this.configuration = ruleChain.getConfiguration(); this.additionalInfo = ruleChain.getAdditionalInfo(); } @@ -104,6 +108,7 @@ public class RuleChainEntity extends BaseSqlEntity implements SearchT ruleChain.setFirstRuleNodeId(new RuleNodeId(UUIDConverter.fromString(firstRuleNodeId))); } ruleChain.setRoot(root); + ruleChain.setDebugMode(debugMode); ruleChain.setConfiguration(configuration); ruleChain.setAdditionalInfo(additionalInfo); return ruleChain; diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/RuleNodeEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/RuleNodeEntity.java index d96048756c..6a888c2d72 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/RuleNodeEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/RuleNodeEntity.java @@ -56,6 +56,9 @@ public class RuleNodeEntity extends BaseSqlEntity implements SearchTex @Column(name = ModelConstants.ADDITIONAL_INFO_PROPERTY) private JsonNode additionalInfo; + @Column(name = ModelConstants.DEBUG_MODE) + private boolean debugMode; + public RuleNodeEntity() { } @@ -65,6 +68,7 @@ public class RuleNodeEntity extends BaseSqlEntity implements SearchTex } this.type = ruleNode.getType(); this.name = ruleNode.getName(); + this.debugMode = ruleNode.isDebugMode(); this.searchText = ruleNode.getName(); this.configuration = ruleNode.getConfiguration(); this.additionalInfo = ruleNode.getAdditionalInfo(); @@ -86,6 +90,7 @@ public class RuleNodeEntity extends BaseSqlEntity implements SearchTex ruleNode.setCreatedTime(UUIDs.unixTimestamp(getId())); ruleNode.setType(type); ruleNode.setName(name); + ruleNode.setDebugMode(debugMode); ruleNode.setConfiguration(configuration); ruleNode.setAdditionalInfo(additionalInfo); return ruleNode; diff --git a/dao/src/main/java/org/thingsboard/server/dao/queue/QueueBenchmark.java b/dao/src/main/java/org/thingsboard/server/dao/queue/QueueBenchmark.java index da991fa97d..bb76b97b18 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/queue/QueueBenchmark.java +++ b/dao/src/main/java/org/thingsboard/server/dao/queue/QueueBenchmark.java @@ -32,6 +32,7 @@ import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.thingsboard.server.common.msg.TbMsg; +import org.thingsboard.server.common.msg.TbMsgDataType; import org.thingsboard.server.common.msg.TbMsgMetaData; import javax.annotation.Nullable; @@ -125,7 +126,7 @@ public class QueueBenchmark implements CommandLineRunner { TbMsgMetaData metaData = new TbMsgMetaData(); metaData.putValue("key", "value"); String dataStr = "someContent"; - return new TbMsg(UUIDs.timeBased(), "type", null, metaData, dataStr.getBytes()); + return new TbMsg(UUIDs.timeBased(), "type", null, metaData, TbMsgDataType.JSON, dataStr.getBytes()); } @Bean diff --git a/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java b/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java index 04207ec2f5..7d6cd0032e 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java @@ -31,7 +31,9 @@ import org.thingsboard.server.common.data.page.TextPageData; import org.thingsboard.server.common.data.page.TextPageLink; import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.RelationTypeGroup; +import org.thingsboard.server.common.data.rule.NodeConnectionInfo; import org.thingsboard.server.common.data.rule.RuleChain; +import org.thingsboard.server.common.data.rule.RuleChainConnectionInfo; import org.thingsboard.server.common.data.rule.RuleChainMetaData; import org.thingsboard.server.common.data.rule.RuleNode; import org.thingsboard.server.dao.entity.AbstractEntityService; @@ -148,7 +150,7 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC ruleChainDao.save(ruleChain); } if (ruleChainMetaData.getConnections() != null) { - for (RuleChainMetaData.NodeConnectionInfo nodeConnection : ruleChainMetaData.getConnections()) { + for (NodeConnectionInfo nodeConnection : ruleChainMetaData.getConnections()) { EntityId from = nodes.get(nodeConnection.getFromIndex()).getId(); EntityId to = nodes.get(nodeConnection.getToIndex()).getId(); String type = nodeConnection.getType(); @@ -161,7 +163,7 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC } } if (ruleChainMetaData.getRuleChainConnections() != null) { - for (RuleChainMetaData.RuleChainConnectionInfo nodeToRuleChainConnection : ruleChainMetaData.getRuleChainConnections()) { + for (RuleChainConnectionInfo nodeToRuleChainConnection : ruleChainMetaData.getRuleChainConnections()) { EntityId from = nodes.get(nodeToRuleChainConnection.getFromIndex()).getId(); EntityId to = nodeToRuleChainConnection.getTargetRuleChainId(); String type = nodeToRuleChainConnection.getType(); @@ -219,6 +221,12 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC return ruleChainDao.findById(ruleChainId.getId()); } + @Override + public RuleNode findRuleNodeById(RuleNodeId ruleNodeId) { + Validator.validateId(ruleNodeId, "Incorrect rule node id for search request."); + return ruleNodeDao.findById(ruleNodeId.getId()); + } + @Override public ListenableFuture findRuleChainByIdAsync(RuleChainId ruleChainId) { Validator.validateId(ruleChainId, "Incorrect rule chain id for search request."); @@ -308,7 +316,7 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC private void createRelation(EntityRelation relation) throws ExecutionException, InterruptedException { log.debug("Creating relation: {}", relation); - relationService.saveRelationAsync(relation).get(); + relationService.saveRelation(relation); } private DataValidator ruleChainValidator = @@ -325,7 +333,7 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC } if (ruleChain.isRoot()) { RuleChain rootRuleChain = getRootTenantRuleChain(ruleChain.getTenantId()); - if (ruleChain.getId() == null || !ruleChain.getId().equals(rootRuleChain.getId())) { + if (rootRuleChain != null && !rootRuleChain.getId().equals(ruleChain.getId())) { throw new DataValidationException("Another root rule chain is present in scope of current tenant!"); } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleService.java b/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleService.java index f1df09edb9..fff3f6d907 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleService.java @@ -16,7 +16,6 @@ package org.thingsboard.server.dao.rule; import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.ArrayNode; import com.google.common.util.concurrent.ListenableFuture; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; @@ -67,67 +66,7 @@ public class BaseRuleService extends AbstractEntityService implements RuleServic @Override public RuleMetaData saveRule(RuleMetaData rule) { - ruleValidator.validate(rule); - if (rule.getTenantId() == null) { - log.trace("Save system rule metadata with predefined id {}", systemTenantId); - rule.setTenantId(systemTenantId); - } - if (rule.getId() != null) { - RuleMetaData oldVersion = ruleDao.findById(rule.getId()); - if (rule.getState() == null) { - rule.setState(oldVersion.getState()); - } else if (rule.getState() != oldVersion.getState()) { - throw new IncorrectParameterException("Use Activate/Suspend method to control state of the rule!"); - } - } else { - if (rule.getState() == null) { - rule.setState(ComponentLifecycleState.SUSPENDED); - } else if (rule.getState() != ComponentLifecycleState.SUSPENDED) { - throw new IncorrectParameterException("Use Activate/Suspend method to control state of the rule!"); - } - } - - validateFilters(rule.getFilters()); - if (rule.getProcessor() != null && !rule.getProcessor().isNull()) { - validateComponentJson(rule.getProcessor(), ComponentType.PROCESSOR); - } - if (rule.getAction() != null && !rule.getAction().isNull()) { - validateComponentJson(rule.getAction(), ComponentType.ACTION); - } - validateRuleAndPluginState(rule); - return ruleDao.save(rule); - } - - private void validateFilters(JsonNode filtersJson) { - if (filtersJson == null || filtersJson.isNull()) { - throw new IncorrectParameterException("Rule filters are required!"); - } - if (!filtersJson.isArray()) { - throw new IncorrectParameterException("Filters json is not an array!"); - } - ArrayNode filtersArray = (ArrayNode) filtersJson; - for (int i = 0; i < filtersArray.size(); i++) { - validateComponentJson(filtersArray.get(i), ComponentType.FILTER); - } - } - - private void validateComponentJson(JsonNode json, ComponentType type) { - if (json == null || json.isNull()) { - throw new IncorrectParameterException(type.name() + " is required!"); - } - String clazz = getIfValid(type.name(), json, "clazz", JsonNode::isTextual, JsonNode::asText); - String name = getIfValid(type.name(), json, "name", JsonNode::isTextual, JsonNode::asText); - JsonNode configuration = getIfValid(type.name(), json, "configuration", JsonNode::isObject, node -> node); - ComponentDescriptor descriptor = componentDescriptorService.findByClazz(clazz); - if (descriptor == null) { - throw new IncorrectParameterException(type.name() + " clazz " + clazz + " is not a valid component!"); - } - if (descriptor.getType() != type) { - throw new IncorrectParameterException("Clazz " + clazz + " is not a valid " + type.name() + " component!"); - } - if (!componentDescriptorService.validate(descriptor, configuration)) { - throw new IncorrectParameterException(type.name() + " configuration is not valid!"); - } + throw new RuntimeException("Not supported since v1.5!"); } private void validateRuleAndPluginState(RuleMetaData rule) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/rule/RuleChainService.java b/dao/src/main/java/org/thingsboard/server/dao/rule/RuleChainService.java index e5f2840b8b..da7833d696 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/rule/RuleChainService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/rule/RuleChainService.java @@ -42,6 +42,8 @@ public interface RuleChainService { RuleChain findRuleChainById(RuleChainId ruleChainId); + RuleNode findRuleNodeById(RuleNodeId ruleNodeId); + ListenableFuture findRuleChainByIdAsync(RuleChainId ruleChainId); RuleChain getRootTenantRuleChain(TenantId tenantId); diff --git a/dao/src/main/resources/cassandra/schema.cql b/dao/src/main/resources/cassandra/schema.cql index 42c13f3ee6..d0e62b2827 100644 --- a/dao/src/main/resources/cassandra/schema.cql +++ b/dao/src/main/resources/cassandra/schema.cql @@ -669,6 +669,7 @@ CREATE TABLE IF NOT EXISTS thingsboard.rule_chain ( search_text text, first_rule_node_id uuid, root boolean, + debug_mode boolean, configuration text, additional_info text, PRIMARY KEY (id, tenant_id) @@ -685,6 +686,7 @@ CREATE TABLE IF NOT EXISTS thingsboard.rule_node ( id uuid, type text, name text, + debug_mode boolean, search_text text, configuration text, additional_info text, diff --git a/dao/src/main/resources/sql/schema.sql b/dao/src/main/resources/sql/schema.sql index 106204ab64..d7a0978140 100644 --- a/dao/src/main/resources/sql/schema.sql +++ b/dao/src/main/resources/sql/schema.sql @@ -263,6 +263,7 @@ CREATE TABLE IF NOT EXISTS rule_chain ( name varchar(255), first_rule_node_id varchar(31), root boolean, + debug_mode boolean, search_text varchar(255), tenant_id varchar(31) ); @@ -273,5 +274,6 @@ CREATE TABLE IF NOT EXISTS rule_node ( configuration varchar(10000000), type varchar(255), name varchar(255), + debug_mode boolean, search_text varchar(255) ); diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/AbstractServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/AbstractServiceTest.java index d083a90262..44a1a0927b 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/AbstractServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/AbstractServiceTest.java @@ -217,10 +217,10 @@ public abstract class AbstractServiceTest { ruleMetaData.setWeight(weight); ruleMetaData.setPluginToken(pluginToken); - ruleMetaData.setAction(createNode(ComponentScope.TENANT, ComponentType.ACTION, + ruleMetaData.setAction(createNode(ComponentScope.TENANT, ComponentType.OLD_ACTION, "org.thingsboard.component.ActionTest", "TestJsonDescriptor.json", "TestJsonData.json")); - ruleMetaData.setProcessor(createNode(ComponentScope.TENANT, ComponentType.PROCESSOR, - "org.thingsboard.component.ProcessorTest", "TestJsonDescriptor.json", "TestJsonData.json")); +// ruleMetaData.setProcessor(createNode(ComponentScope.TENANT, ComponentType.PROCESSOR, +// "org.thingsboard.component.ProcessorTest", "TestJsonDescriptor.json", "TestJsonData.json")); ruleMetaData.setFilters(mapper.createArrayNode().add( createNode(ComponentScope.TENANT, ComponentType.FILTER, "org.thingsboard.component.FilterTest", "TestJsonDescriptor.json", "TestJsonData.json") diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/queue/cassandra/UnprocessedMsgFilterTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/queue/cassandra/UnprocessedMsgFilterTest.java index 6302e63f89..3935c9b92e 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/queue/cassandra/UnprocessedMsgFilterTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/queue/cassandra/UnprocessedMsgFilterTest.java @@ -33,8 +33,8 @@ public class UnprocessedMsgFilterTest { public void acknowledgedMsgsAreFilteredOut() { UUID id1 = UUID.randomUUID(); UUID id2 = UUID.randomUUID(); - TbMsg msg1 = new TbMsg(id1, "T", null, null, null); - TbMsg msg2 = new TbMsg(id2, "T", null, null, null); + TbMsg msg1 = new TbMsg(id1, "T", null, null, null, null); + TbMsg msg2 = new TbMsg(id2, "T", null, null, null, null); List msgs = Lists.newArrayList(msg1, msg2); List acks = Lists.newArrayList(new MsgAck(id2, UUID.randomUUID(), 1L, 1L)); Collection actual = msgFilter.filter(msgs, acks); diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/queue/cassandra/repository/impl/CassandraMsgRepositoryTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/queue/cassandra/repository/impl/CassandraMsgRepositoryTest.java index d17e1f2819..99c6a91f84 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/queue/cassandra/repository/impl/CassandraMsgRepositoryTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/queue/cassandra/repository/impl/CassandraMsgRepositoryTest.java @@ -24,6 +24,7 @@ import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.thingsboard.server.common.data.id.DeviceId; 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.dao.service.AbstractServiceTest; import org.thingsboard.server.dao.service.DaoNoSqlTest; @@ -44,7 +45,7 @@ public class CassandraMsgRepositoryTest extends AbstractServiceTest { @Test public void msgCanBeSavedAndRead() throws ExecutionException, InterruptedException { - TbMsg msg = new TbMsg(UUIDs.timeBased(), "type", new DeviceId(UUIDs.timeBased()), null, new byte[4]); + TbMsg msg = new TbMsg(UUIDs.timeBased(), "type", new DeviceId(UUIDs.timeBased()), null, TbMsgDataType.JSON, new byte[4]); UUID nodeId = UUIDs.timeBased(); ListenableFuture future = msgRepository.save(msg, nodeId, 1L, 1L, 1L); future.get(); @@ -54,7 +55,7 @@ public class CassandraMsgRepositoryTest extends AbstractServiceTest { @Test public void expiredMsgsAreNotReturned() throws ExecutionException, InterruptedException { - TbMsg msg = new TbMsg(UUIDs.timeBased(), "type", new DeviceId(UUIDs.timeBased()), null, new byte[4]); + TbMsg msg = new TbMsg(UUIDs.timeBased(), "type", new DeviceId(UUIDs.timeBased()), null, TbMsgDataType.JSON, new byte[4]); UUID nodeId = UUIDs.timeBased(); ListenableFuture future = msgRepository.save(msg, nodeId, 2L, 2L, 2L); future.get(); @@ -67,7 +68,7 @@ public class CassandraMsgRepositoryTest extends AbstractServiceTest { TbMsgMetaData metaData = new TbMsgMetaData(); metaData.putValue("key", "value"); String dataStr = "someContent"; - TbMsg msg = new TbMsg(UUIDs.timeBased(), "type", new DeviceId(UUIDs.timeBased()), metaData, dataStr.getBytes()); + TbMsg msg = new TbMsg(UUIDs.timeBased(), "type", new DeviceId(UUIDs.timeBased()), metaData, TbMsgDataType.JSON, dataStr.getBytes()); UUID nodeId = UUIDs.timeBased(); ListenableFuture future = msgRepository.save(msg, nodeId, 1L, 1L, 1L); future.get(); diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/rule/BaseRuleServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/rule/BaseRuleServiceTest.java deleted file mode 100644 index b6139df898..0000000000 --- a/dao/src/test/java/org/thingsboard/server/dao/service/rule/BaseRuleServiceTest.java +++ /dev/null @@ -1,163 +0,0 @@ -/** - * Copyright © 2016-2018 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.dao.service.rule; - -import com.datastax.driver.core.utils.UUIDs; -import org.junit.Assert; -import org.junit.Test; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.page.TextPageData; -import org.thingsboard.server.common.data.page.TextPageLink; -import org.thingsboard.server.common.data.plugin.PluginMetaData; -import org.thingsboard.server.common.data.rule.RuleMetaData; -import org.thingsboard.server.dao.model.ModelConstants; -import org.thingsboard.server.dao.service.AbstractServiceTest; - -import java.util.List; -import java.util.concurrent.ThreadLocalRandom; - -public abstract class BaseRuleServiceTest extends AbstractServiceTest { - - @Test - public void saveRule() throws Exception { - PluginMetaData plugin = generatePlugin(null, "testPluginToken" + ThreadLocalRandom.current().nextInt()); - pluginService.savePlugin(plugin); - RuleMetaData ruleMetaData = ruleService.saveRule(generateRule(plugin.getTenantId(), null, plugin.getApiToken())); - Assert.assertNotNull(ruleMetaData.getId()); - Assert.assertNotNull(ruleMetaData.getAdditionalInfo()); - ruleMetaData.setAdditionalInfo(mapper.readTree("{\"description\":\"test\"}")); - RuleMetaData newRuleMetaData = ruleService.saveRule(ruleMetaData); - Assert.assertEquals(ruleMetaData.getAdditionalInfo(), newRuleMetaData.getAdditionalInfo()); - } - - @Test - public void findRuleById() throws Exception { - PluginMetaData plugin = generatePlugin(null, "testPluginToken" + ThreadLocalRandom.current().nextInt()); - pluginService.savePlugin(plugin); - - RuleMetaData expected = ruleService.saveRule(generateRule(plugin.getTenantId(), null, plugin.getApiToken())); - Assert.assertNotNull(expected.getId()); - RuleMetaData found = ruleService.findRuleById(expected.getId()); - Assert.assertEquals(expected, found); - } - - @Test - public void findPluginRules() throws Exception { - TenantId tenantIdA = new TenantId(UUIDs.timeBased()); - TenantId tenantIdB = new TenantId(UUIDs.timeBased()); - - PluginMetaData pluginA = generatePlugin(tenantIdA, "testPluginToken" + ThreadLocalRandom.current().nextInt()); - PluginMetaData pluginB = generatePlugin(tenantIdB, "testPluginToken" + ThreadLocalRandom.current().nextInt()); - pluginService.savePlugin(pluginA); - pluginService.savePlugin(pluginB); - - ruleService.saveRule(generateRule(tenantIdA, null, pluginA.getApiToken())); - ruleService.saveRule(generateRule(tenantIdA, null, pluginA.getApiToken())); - ruleService.saveRule(generateRule(tenantIdA, null, pluginA.getApiToken())); - - ruleService.saveRule(generateRule(tenantIdB, null, pluginB.getApiToken())); - ruleService.saveRule(generateRule(tenantIdB, null, pluginB.getApiToken())); - - List foundA = ruleService.findPluginRules(pluginA.getApiToken()); - Assert.assertEquals(3, foundA.size()); - - List foundB = ruleService.findPluginRules(pluginB.getApiToken()); - Assert.assertEquals(2, foundB.size()); - } - - @Test - public void findSystemRules() throws Exception { - TenantId systemTenant = new TenantId(ModelConstants.NULL_UUID); // system tenant id - - PluginMetaData plugin = generatePlugin(systemTenant, "testPluginToken" + ThreadLocalRandom.current().nextInt()); - pluginService.savePlugin(plugin); - ruleService.saveRule(generateRule(systemTenant, null, plugin.getApiToken())); - ruleService.saveRule(generateRule(systemTenant, null, plugin.getApiToken())); - ruleService.saveRule(generateRule(systemTenant, null, plugin.getApiToken())); - TextPageData found = ruleService.findSystemRules(new TextPageLink(100)); - Assert.assertEquals(3, found.getData().size()); - } - - @Test - public void findTenantRules() throws Exception { - TenantId tenantIdA = new TenantId(UUIDs.timeBased()); - TenantId tenantIdB = new TenantId(UUIDs.timeBased()); - - PluginMetaData pluginA = generatePlugin(tenantIdA, "testPluginToken" + ThreadLocalRandom.current().nextInt()); - PluginMetaData pluginB = generatePlugin(tenantIdB, "testPluginToken" + ThreadLocalRandom.current().nextInt()); - pluginService.savePlugin(pluginA); - pluginService.savePlugin(pluginB); - - ruleService.saveRule(generateRule(tenantIdA, null, pluginA.getApiToken())); - ruleService.saveRule(generateRule(tenantIdA, null, pluginA.getApiToken())); - ruleService.saveRule(generateRule(tenantIdA, null, pluginA.getApiToken())); - - ruleService.saveRule(generateRule(tenantIdB, null, pluginB.getApiToken())); - ruleService.saveRule(generateRule(tenantIdB, null, pluginB.getApiToken())); - - TextPageData foundA = ruleService.findTenantRules(tenantIdA, new TextPageLink(100)); - Assert.assertEquals(3, foundA.getData().size()); - - TextPageData foundB = ruleService.findTenantRules(tenantIdB, new TextPageLink(100)); - Assert.assertEquals(2, foundB.getData().size()); - } - - @Test - public void deleteRuleById() throws Exception { - PluginMetaData plugin = generatePlugin(null, "testPluginToken" + ThreadLocalRandom.current().nextInt()); - pluginService.savePlugin(plugin); - - RuleMetaData expected = ruleService.saveRule(generateRule(plugin.getTenantId(), null, plugin.getApiToken())); - Assert.assertNotNull(expected.getId()); - RuleMetaData found = ruleService.findRuleById(expected.getId()); - Assert.assertEquals(expected, found); - ruleService.deleteRuleById(expected.getId()); - found = ruleService.findRuleById(expected.getId()); - Assert.assertNull(found); - } - - @Test - public void deleteRulesByTenantId() throws Exception { - TenantId tenantIdA = new TenantId(UUIDs.timeBased()); - TenantId tenantIdB = new TenantId(UUIDs.timeBased()); - - PluginMetaData pluginA = generatePlugin(tenantIdA, "testPluginToken" + ThreadLocalRandom.current().nextInt()); - PluginMetaData pluginB = generatePlugin(tenantIdB, "testPluginToken" + ThreadLocalRandom.current().nextInt()); - pluginService.savePlugin(pluginA); - pluginService.savePlugin(pluginB); - - ruleService.saveRule(generateRule(tenantIdA, null, pluginA.getApiToken())); - ruleService.saveRule(generateRule(tenantIdA, null, pluginA.getApiToken())); - ruleService.saveRule(generateRule(tenantIdA, null, pluginA.getApiToken())); - - ruleService.saveRule(generateRule(tenantIdB, null, pluginB.getApiToken())); - ruleService.saveRule(generateRule(tenantIdB, null, pluginB.getApiToken())); - - TextPageData foundA = ruleService.findTenantRules(tenantIdA, new TextPageLink(100)); - Assert.assertEquals(3, foundA.getData().size()); - - TextPageData foundB = ruleService.findTenantRules(tenantIdB, new TextPageLink(100)); - Assert.assertEquals(2, foundB.getData().size()); - - ruleService.deleteRulesByTenantId(tenantIdA); - - foundA = ruleService.findTenantRules(tenantIdA, new TextPageLink(100)); - Assert.assertEquals(0, foundA.getData().size()); - - foundB = ruleService.findTenantRules(tenantIdB, new TextPageLink(100)); - Assert.assertEquals(2, foundB.getData().size()); - } -} \ No newline at end of file diff --git a/pom.xml b/pom.xml index f331e32fb2..f0c915a5b0 100755 --- a/pom.xml +++ b/pom.xml @@ -378,6 +378,11 @@ rule-engine-api ${project.version} + + org.thingsboard.rule-engine + rule-engine-components + ${project.version} + org.thingsboard.common message diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/ActionNode.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/ActionNode.java new file mode 100644 index 0000000000..64e28f1bf2 --- /dev/null +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/ActionNode.java @@ -0,0 +1,43 @@ +/** + * Copyright © 2016-2018 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.rule.engine.api; + +import org.thingsboard.server.common.data.plugin.ComponentScope; +import org.thingsboard.server.extensions.api.component.EmptyComponentConfiguration; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @author Andrew Shvayka + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface ActionNode { + + String name(); + + ComponentScope scope() default ComponentScope.TENANT; + + String descriptor() default "EmptyNodeDescriptor.json"; + + String[] relationTypes() default {"Success","Failure"}; + + boolean customRelations() default false; + +} diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/EnrichmentNode.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/EnrichmentNode.java new file mode 100644 index 0000000000..2267bdae67 --- /dev/null +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/EnrichmentNode.java @@ -0,0 +1,42 @@ +/** + * Copyright © 2016-2018 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.rule.engine.api; + +import org.thingsboard.server.common.data.plugin.ComponentScope; +import org.thingsboard.server.extensions.api.component.EmptyComponentConfiguration; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @author Andrew Shvayka + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface EnrichmentNode { + + String name(); + + ComponentScope scope() default ComponentScope.TENANT; + + String descriptor() default "EmptyNodeDescriptor.json"; + + String[] relationTypes() default {"Success","Failure"}; + + boolean customRelations() default false; +} diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/FilterNode.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/FilterNode.java new file mode 100644 index 0000000000..5247e397dd --- /dev/null +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/FilterNode.java @@ -0,0 +1,43 @@ +/** + * Copyright © 2016-2018 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.rule.engine.api; + +import org.thingsboard.server.common.data.plugin.ComponentScope; +import org.thingsboard.server.extensions.api.component.EmptyComponentConfiguration; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @author Andrew Shvayka + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface FilterNode { + + String name(); + + ComponentScope scope() default ComponentScope.TENANT; + + String descriptor() default "EmptyNodeDescriptor.json"; + + String[] relationTypes() default {"Success","Failure"}; + + boolean customRelations() default false; + +} 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 fdcf56aac9..260a51ff17 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 @@ -51,7 +51,7 @@ public interface TbContext { void spawn(TbMsg msg); - void ack(UUID msg); + void ack(TbMsg msg); void tellError(TbMsg msg, Throwable th); @@ -61,8 +61,6 @@ public interface TbContext { UserService getUserService(); - RuleService getRuleService(); - PluginService getPluginService(); AssetService getAssetService(); diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbNodeConfiguration.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbNodeConfiguration.java index d06c0d277b..64053cd61c 100644 --- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbNodeConfiguration.java +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbNodeConfiguration.java @@ -22,8 +22,8 @@ import lombok.Data; * Created by ashvayka on 19.01.18. */ @Data -public class TbNodeConfiguration { +public final class TbNodeConfiguration { - private JsonNode data; + private final JsonNode data; } diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbNodeState.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbNodeState.java index c48b11d387..2c77a69ba3 100644 --- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbNodeState.java +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbNodeState.java @@ -18,5 +18,5 @@ package org.thingsboard.rule.engine.api; /** * Created by ashvayka on 19.01.18. */ -public class TbNodeState { +public final class TbNodeState { } diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TransformationNode.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TransformationNode.java new file mode 100644 index 0000000000..bfe1dcaeda --- /dev/null +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TransformationNode.java @@ -0,0 +1,43 @@ +/** + * Copyright © 2016-2018 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.rule.engine.api; + +import org.thingsboard.server.common.data.plugin.ComponentScope; +import org.thingsboard.server.extensions.api.component.EmptyComponentConfiguration; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @author Andrew Shvayka + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface TransformationNode { + + String name(); + + ComponentScope scope() default ComponentScope.TENANT; + + String descriptor() default "EmptyNodeDescriptor.json"; + + String[] relationTypes() default {"Success","Failure"}; + + boolean customRelations() default false; + +} diff --git a/rule-engine/rule-engine-api/src/main/resources/EmptyNodeDescriptor.json b/rule-engine/rule-engine-api/src/main/resources/EmptyNodeDescriptor.json new file mode 100644 index 0000000000..7a73a41bfd --- /dev/null +++ b/rule-engine/rule-engine-api/src/main/resources/EmptyNodeDescriptor.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/rule-engine/rule-engine-components/pom.xml b/rule-engine/rule-engine-components/pom.xml index 9b903b1b00..a97493bdd7 100644 --- a/rule-engine/rule-engine-components/pom.xml +++ b/rule-engine/rule-engine-components/pom.xml @@ -88,11 +88,6 @@ mockito-all test - - org.junit.jupiter - junit-jupiter-api - RELEASE - diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetAttributesNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetAttributesNode.java index 887311cc9a..90eadcb2c7 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetAttributesNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetAttributesNode.java @@ -21,9 +21,13 @@ import com.google.common.util.concurrent.ListenableFuture; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections.CollectionUtils; import org.thingsboard.rule.engine.TbNodeUtils; -import org.thingsboard.rule.engine.api.*; +import org.thingsboard.rule.engine.api.TbContext; +import org.thingsboard.rule.engine.api.TbNodeConfiguration; +import org.thingsboard.rule.engine.api.TbNodeException; +import org.thingsboard.rule.engine.api.TbNodeState; +import org.thingsboard.rule.engine.api.TbNode; +import org.thingsboard.rule.engine.api.EnrichmentNode; import org.thingsboard.server.common.data.kv.AttributeKvEntry; -import org.thingsboard.server.common.data.kv.KvEntry; import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.common.msg.TbMsg; @@ -36,6 +40,7 @@ import static org.thingsboard.server.common.data.DataConstants.*; * Created by ashvayka on 19.01.18. */ @Slf4j +@EnrichmentNode(name = "Get Attributes Node") public class TbGetAttributesNode implements TbNode { private TbGetAttributesNodeConfiguration config; @@ -61,21 +66,22 @@ public class TbGetAttributesNode implements TbNode { } } - private ListenableFuture putAttr(TbMsg msg, List attributes, String prefix) { - attributes.forEach(r -> msg.getMetaData().putValue(prefix + r.getKey(), r.getValueAsString())); - return Futures.immediateFuture(null); - } - - private ListenableFuture putAttrAsync(TbContext ctx, TbMsg msg, String scope, List attributes, String prefix) { - ListenableFuture> latest = ctx.getAttributesService().find(msg.getOriginator(), scope, attributes); + private ListenableFuture putAttrAsync(TbContext ctx, TbMsg msg, String scope, List keys, String prefix) { + if (keys == null) { + return Futures.immediateFuture(null); + } + ListenableFuture> latest = ctx.getAttributesService().find(msg.getOriginator(), scope, keys); return Futures.transform(latest, (Function, Void>) l -> { l.forEach(r -> msg.getMetaData().putValue(prefix + r.getKey(), r.getValueAsString())); return null; }); } - private ListenableFuture getLatestTelemetry(TbContext ctx, TbMsg msg, List attributes) { - ListenableFuture> latest = ctx.getTimeseriesService().findLatest(msg.getOriginator(), attributes); + private ListenableFuture getLatestTelemetry(TbContext ctx, TbMsg msg, List keys) { + if (keys == null) { + return Futures.immediateFuture(null); + } + ListenableFuture> latest = ctx.getTimeseriesService().findLatest(msg.getOriginator(), keys); return Futures.transform(latest, (Function, Void>) l -> { l.forEach(r -> msg.getMetaData().putValue(r.getKey(), r.getValueAsString())); return null; diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetCustomerAttributeNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetCustomerAttributeNode.java index b7b1fd73a3..18ddfcf6b9 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetCustomerAttributeNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetCustomerAttributeNode.java @@ -16,11 +16,13 @@ package org.thingsboard.rule.engine.metadata; import com.google.common.util.concurrent.ListenableFuture; +import org.thingsboard.rule.engine.api.EnrichmentNode; import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.util.EntitiesCustomerIdAsyncLoader; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EntityId; +@EnrichmentNode(name="Get Customer Attributes Node") public class TbGetCustomerAttributeNode extends TbEntityGetAttrNode { @Override diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetRelatedAttributeNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetRelatedAttributeNode.java index 474fb5d6c0..3a0dce85a2 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetRelatedAttributeNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetRelatedAttributeNode.java @@ -21,9 +21,12 @@ import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeException; import org.thingsboard.rule.engine.api.TbNodeState; +import org.thingsboard.rule.engine.api.EnrichmentNode; import org.thingsboard.rule.engine.util.EntitiesRelatedEntityIdAsyncLoader; + import org.thingsboard.server.common.data.id.EntityId; +@EnrichmentNode(name="Get Related Entity Attributes Node") public class TbGetRelatedAttributeNode extends TbEntityGetAttrNode { private TbGetRelatedAttrNodeConfiguration config; diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTenantAttributeNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTenantAttributeNode.java index b97e220c04..e51c053ca2 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTenantAttributeNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTenantAttributeNode.java @@ -17,12 +17,14 @@ package org.thingsboard.rule.engine.metadata; import com.google.common.util.concurrent.ListenableFuture; import lombok.extern.slf4j.Slf4j; +import org.thingsboard.rule.engine.api.EnrichmentNode; import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.util.EntitiesTenantIdAsyncLoader; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; @Slf4j +@EnrichmentNode(name="Get Tenant Attributes Node") public class TbGetTenantAttributeNode extends TbEntityGetAttrNode { @Override diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/EntitiesTenantIdAsyncLoader.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/EntitiesTenantIdAsyncLoader.java index 388881b84e..5d2aaa81d1 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/EntitiesTenantIdAsyncLoader.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/EntitiesTenantIdAsyncLoader.java @@ -35,8 +35,6 @@ public class EntitiesTenantIdAsyncLoader { return getTenantAsync(ctx.getCustomerService().findCustomerByIdAsync((CustomerId) original)); case USER: return getTenantAsync(ctx.getUserService().findUserByIdAsync((UserId) original)); - case RULE: - return getTenantAsync(ctx.getRuleService().findRuleByIdAsync((RuleId) original)); case PLUGIN: return getTenantAsync(ctx.getPluginService().findPluginByIdAsync((PluginId) original)); case ASSET: diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbJsFilterNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbJsFilterNodeTest.java index 2fd9f4e8ee..96f7032e5e 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbJsFilterNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbJsFilterNodeTest.java @@ -141,8 +141,7 @@ public class TbJsFilterNodeTest { TbJsFilterNodeConfiguration config = new TbJsFilterNodeConfiguration(); config.setJsScript(script); ObjectMapper mapper = new ObjectMapper(); - TbNodeConfiguration nodeConfiguration = new TbNodeConfiguration(); - nodeConfiguration.setData(mapper.valueToTree(config)); + TbNodeConfiguration nodeConfiguration = new TbNodeConfiguration(mapper.valueToTree(config)); node = new TbJsFilterNode(); node.init(nodeConfiguration, null); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbJsSwitchNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbJsSwitchNodeTest.java index a2f5f7d6f2..e70d4e16f4 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbJsSwitchNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbJsSwitchNodeTest.java @@ -138,8 +138,7 @@ public class TbJsSwitchNodeTest { config.setAllowedRelations(relations); config.setRouteToAllWithNoCheck(routeToAll); ObjectMapper mapper = new ObjectMapper(); - TbNodeConfiguration nodeConfiguration = new TbNodeConfiguration(); - nodeConfiguration.setData(mapper.valueToTree(config)); + TbNodeConfiguration nodeConfiguration = new TbNodeConfiguration(mapper.valueToTree(config)); node = new TbJsSwitchNode(); node.init(nodeConfiguration, null); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetCustomerAttributeNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetCustomerAttributeNodeTest.java index 8e5ddb8069..ad40f03006 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetCustomerAttributeNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetCustomerAttributeNodeTest.java @@ -85,8 +85,7 @@ public class TbGetCustomerAttributeNodeTest { config.setAttrMapping(attrMapping); config.setTelemetry(false); ObjectMapper mapper = new ObjectMapper(); - TbNodeConfiguration nodeConfiguration = new TbNodeConfiguration(); - nodeConfiguration.setData(mapper.valueToTree(config)); + TbNodeConfiguration nodeConfiguration = new TbNodeConfiguration(mapper.valueToTree(config)); node = new TbGetCustomerAttributeNode(); node.init(nodeConfiguration, null); @@ -224,8 +223,7 @@ public class TbGetCustomerAttributeNodeTest { config.setAttrMapping(attrMapping); config.setTelemetry(true); ObjectMapper mapper = new ObjectMapper(); - TbNodeConfiguration nodeConfiguration = new TbNodeConfiguration(); - nodeConfiguration.setData(mapper.valueToTree(config)); + TbNodeConfiguration nodeConfiguration = new TbNodeConfiguration(mapper.valueToTree(config)); node = new TbGetCustomerAttributeNode(); node.init(nodeConfiguration, null); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNodeTest.java index 77b00fb778..190692c4e5 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNodeTest.java @@ -116,8 +116,7 @@ public class TbChangeOriginatorNodeTest { config.setOriginatorSource(TbChangeOriginatorNode.CUSTOMER_SOURCE); config.setStartNewChain(startNewChain); ObjectMapper mapper = new ObjectMapper(); - TbNodeConfiguration nodeConfiguration = new TbNodeConfiguration(); - nodeConfiguration.setData(mapper.valueToTree(config)); + TbNodeConfiguration nodeConfiguration = new TbNodeConfiguration(mapper.valueToTree(config)); node = new TbChangeOriginatorNode(); node.init(nodeConfiguration, null); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbTransformMsgNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbTransformMsgNodeTest.java index 876e70f9b8..d69bad8864 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbTransformMsgNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbTransformMsgNodeTest.java @@ -111,8 +111,7 @@ public class TbTransformMsgNodeTest { TbTransformMsgNodeConfiguration config = new TbTransformMsgNodeConfiguration(); config.setJsScript(script); ObjectMapper mapper = new ObjectMapper(); - TbNodeConfiguration nodeConfiguration = new TbNodeConfiguration(); - nodeConfiguration.setData(mapper.valueToTree(config)); + TbNodeConfiguration nodeConfiguration = new TbNodeConfiguration(mapper.valueToTree(config)); node = new TbTransformMsgNode(); node.init(nodeConfiguration, null);