Browse Source

First integration test for the new rule engine. Fixed a lot of bugs

pull/670/head
Andrew Shvayka 9 years ago
parent
commit
64a8e5c152
  1. 4
      application/pom.xml
  2. 2
      application/src/main/data/upgrade/1.5.0/schema_update.cql
  3. 2
      application/src/main/data/upgrade/1.5.0/schema_update.sql
  4. 193
      application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java
  5. 13
      application/src/main/java/org/thingsboard/server/actors/app/AppActor.java
  6. 21
      application/src/main/java/org/thingsboard/server/actors/device/DeviceActor.java
  7. 63
      application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java
  8. 7
      application/src/main/java/org/thingsboard/server/actors/plugin/PluginActor.java
  9. 7
      application/src/main/java/org/thingsboard/server/actors/rpc/RpcManagerActor.java
  10. 7
      application/src/main/java/org/thingsboard/server/actors/rpc/RpcSessionActor.java
  11. 117
      application/src/main/java/org/thingsboard/server/actors/rule/ChainProcessingContext.java
  12. 41
      application/src/main/java/org/thingsboard/server/actors/rule/ChainProcessingMetaData.java
  13. 43
      application/src/main/java/org/thingsboard/server/actors/rule/ComplexRuleActorChain.java
  14. 20
      application/src/main/java/org/thingsboard/server/actors/rule/CompoundRuleActorChain.java
  15. 90
      application/src/main/java/org/thingsboard/server/actors/rule/RuleActor.java
  16. 345
      application/src/main/java/org/thingsboard/server/actors/rule/RuleActorMessageProcessor.java
  17. 107
      application/src/main/java/org/thingsboard/server/actors/rule/RuleActorMetaData.java
  18. 33
      application/src/main/java/org/thingsboard/server/actors/rule/RuleContextAwareMsgProcessor.java
  19. 115
      application/src/main/java/org/thingsboard/server/actors/rule/RuleProcessingContext.java
  20. 31
      application/src/main/java/org/thingsboard/server/actors/rule/RuleProcessingMsg.java
  21. 30
      application/src/main/java/org/thingsboard/server/actors/rule/RuleTerminationMsg.java
  22. 36
      application/src/main/java/org/thingsboard/server/actors/rule/RuleToPluginTimeoutMsg.java
  23. 39
      application/src/main/java/org/thingsboard/server/actors/rule/SimpleRuleActorChain.java
  24. 90
      application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java
  25. 15
      application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActor.java
  26. 33
      application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java
  27. 15
      application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainManagerActor.java
  28. 15
      application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainToRuleNodeMsg.java
  29. 26
      application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeActor.java
  30. 40
      application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeActorMessageProcessor.java
  31. 22
      application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeCtx.java
  32. 15
      application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeRelation.java
  33. 15
      application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeToRuleChainTellNextMsg.java
  34. 28
      application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeToSelfErrorMsg.java
  35. 1
      application/src/main/java/org/thingsboard/server/actors/service/ComponentActor.java
  36. 9
      application/src/main/java/org/thingsboard/server/actors/service/ContextAwareActor.java
  37. 8
      application/src/main/java/org/thingsboard/server/actors/service/DefaultActorService.java
  38. 7
      application/src/main/java/org/thingsboard/server/actors/session/SessionActor.java
  39. 7
      application/src/main/java/org/thingsboard/server/actors/session/SessionManagerActor.java
  40. 15
      application/src/main/java/org/thingsboard/server/actors/shared/EntityActorsManager.java
  41. 8
      application/src/main/java/org/thingsboard/server/actors/shared/plugin/PluginManager.java
  42. 135
      application/src/main/java/org/thingsboard/server/actors/shared/rule/RuleManager.java
  43. 40
      application/src/main/java/org/thingsboard/server/actors/shared/rule/SystemRuleManager.java
  44. 48
      application/src/main/java/org/thingsboard/server/actors/shared/rule/TenantRuleManager.java
  45. 15
      application/src/main/java/org/thingsboard/server/actors/shared/rulechain/RuleChainManager.java
  46. 11
      application/src/main/java/org/thingsboard/server/actors/shared/rulechain/TenantRuleChainManager.java
  47. 7
      application/src/main/java/org/thingsboard/server/actors/stats/StatsActor.java
  48. 40
      application/src/main/java/org/thingsboard/server/actors/tenant/RuleChainDeviceMsg.java
  49. 13
      application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java
  50. 2
      application/src/main/java/org/thingsboard/server/controller/RuleChainController.java
  51. 49
      application/src/main/java/org/thingsboard/server/service/component/AnnotationComponentDiscoveryService.java
  52. 14
      application/src/main/resources/thingsboard.yml
  53. 2
      application/src/test/java/org/thingsboard/server/controller/AbstractControllerTest.java
  54. 41
      application/src/test/java/org/thingsboard/server/controller/AbstractRuleEngineControllerTest.java
  55. 35
      application/src/test/java/org/thingsboard/server/rules/RuleEngineSqlTestSuite.java
  56. 156
      application/src/test/java/org/thingsboard/server/rules/flow/AbstractRuleEngineFlowIntegrationTest.java
  57. 14
      application/src/test/java/org/thingsboard/server/rules/flow/RuleEngineFlowSqlIntegrationTest.java
  58. 7
      common/data/src/main/java/org/thingsboard/server/common/data/DataConstants.java
  59. 2
      common/data/src/main/java/org/thingsboard/server/common/data/plugin/ComponentType.java
  60. 1
      common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChain.java
  61. 1
      common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleNode.java
  62. 20
      common/message/src/main/java/org/thingsboard/server/common/msg/MsgType.java
  63. 15
      common/message/src/main/java/org/thingsboard/server/common/msg/TbActorMsg.java
  64. 30
      common/message/src/main/java/org/thingsboard/server/common/msg/TbMsg.java
  65. 15
      common/message/src/main/java/org/thingsboard/server/common/msg/TbMsgDataType.java
  66. 19
      common/message/src/main/java/org/thingsboard/server/common/msg/TbMsgMetaData.java
  67. 8
      common/message/src/main/java/org/thingsboard/server/common/msg/plugin/ComponentLifecycleMsg.java
  68. 15
      common/message/src/main/java/org/thingsboard/server/common/msg/system/ServiceToRuleEngineMsg.java
  69. 2
      dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java
  70. 16
      dao/src/main/java/org/thingsboard/server/dao/model/nosql/RuleChainEntity.java
  71. 9
      dao/src/main/java/org/thingsboard/server/dao/model/nosql/RuleNodeEntity.java
  72. 5
      dao/src/main/java/org/thingsboard/server/dao/model/sql/RuleChainEntity.java
  73. 5
      dao/src/main/java/org/thingsboard/server/dao/model/sql/RuleNodeEntity.java
  74. 3
      dao/src/main/java/org/thingsboard/server/dao/queue/QueueBenchmark.java
  75. 8
      dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java
  76. 2
      dao/src/main/java/org/thingsboard/server/dao/rule/RuleChainService.java
  77. 2
      dao/src/main/resources/cassandra/schema.cql
  78. 2
      dao/src/main/resources/sql/schema.sql
  79. 6
      dao/src/test/java/org/thingsboard/server/dao/service/AbstractServiceTest.java
  80. 4
      dao/src/test/java/org/thingsboard/server/dao/service/queue/cassandra/UnprocessedMsgFilterTest.java
  81. 7
      dao/src/test/java/org/thingsboard/server/dao/service/queue/cassandra/repository/impl/CassandraMsgRepositoryTest.java
  82. 5
      pom.xml
  83. 43
      rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/ActionNode.java
  84. 42
      rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/EnrichmentNode.java
  85. 43
      rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/FilterNode.java
  86. 2
      rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java
  87. 4
      rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbNodeConfiguration.java
  88. 2
      rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbNodeState.java
  89. 43
      rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TransformationNode.java
  90. 2
      rule-engine/rule-engine-api/src/main/resources/EmptyNodeDescriptor.json
  91. 5
      rule-engine/rule-engine-components/pom.xml
  92. 26
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetAttributesNode.java
  93. 2
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetCustomerAttributeNode.java
  94. 6
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetRelatedAttributeNode.java
  95. 4
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTenantAttributeNode.java
  96. 6
      rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetCustomerAttributeNodeTest.java

4
application/pom.xml

@ -57,6 +57,10 @@
<groupId>org.thingsboard.rule-engine</groupId> <groupId>org.thingsboard.rule-engine</groupId>
<artifactId>rule-engine-api</artifactId> <artifactId>rule-engine-api</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.thingsboard.rule-engine</groupId>
<artifactId>rule-engine-components</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<artifactId>extensions-core</artifactId> <artifactId>extensions-core</artifactId>

2
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, search_text text,
first_rule_node_id uuid, first_rule_node_id uuid,
root boolean, root boolean,
debug_mode boolean,
configuration text, configuration text,
additional_info text, additional_info text,
PRIMARY KEY (id, tenant_id) PRIMARY KEY (id, tenant_id)
@ -85,6 +86,7 @@ CREATE TABLE IF NOT EXISTS thingsboard.rule_node (
id uuid, id uuid,
type text, type text,
name text, name text,
debug_mode boolean,
search_text text, search_text text,
configuration text, configuration text,
additional_info text, additional_info text,

2
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), name varchar(255),
first_rule_node_id varchar(31), first_rule_node_id varchar(31),
root boolean, root boolean,
debug_mode boolean,
search_text varchar(255), search_text varchar(255),
tenant_id varchar(31) tenant_id varchar(31)
); );
@ -31,5 +32,6 @@ CREATE TABLE IF NOT EXISTS rule_node (
configuration varchar(10000000), configuration varchar(10000000),
type varchar(255), type varchar(255),
name varchar(255), name varchar(255),
debug_mode boolean,
search_text varchar(255) search_text varchar(255)
); );

193
application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java

@ -1,12 +1,12 @@
/** /**
* Copyright © 2016-2018 The Thingsboard Authors * Copyright © 2016-2018 The Thingsboard Authors
* * <p>
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* * <p>
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* * <p>
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -28,12 +28,14 @@ import lombok.Setter;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.util.Base64Utils;
import org.thingsboard.server.actors.service.ActorService; import org.thingsboard.server.actors.service.ActorService;
import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.Event; import org.thingsboard.server.common.data.Event;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; 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.msg.cluster.ServerAddress;
import org.thingsboard.server.common.transport.auth.DeviceAuthService; import org.thingsboard.server.common.transport.auth.DeviceAuthService;
import org.thingsboard.server.controller.plugin.PluginWebSocketMsgEndpoint; import org.thingsboard.server.controller.plugin.PluginWebSocketMsgEndpoint;
@ -50,6 +52,7 @@ import org.thingsboard.server.dao.rule.RuleChainService;
import org.thingsboard.server.dao.rule.RuleService; import org.thingsboard.server.dao.rule.RuleService;
import org.thingsboard.server.dao.tenant.TenantService; import org.thingsboard.server.dao.tenant.TenantService;
import org.thingsboard.server.dao.timeseries.TimeseriesService; 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.discovery.DiscoveryService;
import org.thingsboard.server.service.cluster.routing.ClusterRoutingService; import org.thingsboard.server.service.cluster.routing.ClusterRoutingService;
import org.thingsboard.server.service.cluster.rpc.ClusterRpcService; import org.thingsboard.server.service.cluster.rpc.ClusterRpcService;
@ -57,6 +60,7 @@ import org.thingsboard.server.service.component.ComponentDiscoveryService;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.io.StringWriter; import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
import java.util.Optional; import java.util.Optional;
@Component @Component
@ -65,107 +69,154 @@ public class ActorSystemContext {
protected final ObjectMapper mapper = new ObjectMapper(); protected final ObjectMapper mapper = new ObjectMapper();
@Getter @Setter private ActorService actorService; @Getter
@Setter
private ActorService actorService;
@Autowired @Autowired
@Getter private DiscoveryService discoveryService; @Getter
private DiscoveryService discoveryService;
@Autowired @Autowired
@Getter @Setter private ComponentDiscoveryService componentService; @Getter
@Setter
private ComponentDiscoveryService componentService;
@Autowired @Autowired
@Getter private ClusterRoutingService routingService; @Getter
private ClusterRoutingService routingService;
@Autowired @Autowired
@Getter private ClusterRpcService rpcService; @Getter
private ClusterRpcService rpcService;
@Autowired @Autowired
@Getter private DeviceAuthService deviceAuthService; @Getter
private DeviceAuthService deviceAuthService;
@Autowired @Autowired
@Getter private DeviceService deviceService; @Getter
private DeviceService deviceService;
@Autowired @Autowired
@Getter private AssetService assetService; @Getter
private AssetService assetService;
@Autowired @Autowired
@Getter private TenantService tenantService; @Getter
private TenantService tenantService;
@Autowired @Autowired
@Getter private CustomerService customerService; @Getter
private CustomerService customerService;
@Autowired @Autowired
@Getter private RuleService ruleService; @Getter
private UserService userService;
@Autowired @Autowired
@Getter private RuleChainService ruleChainService; @Getter
private RuleService ruleService;
@Autowired @Autowired
@Getter private PluginService pluginService; @Getter
private RuleChainService ruleChainService;
@Autowired @Autowired
@Getter private TimeseriesService tsService; @Getter
private PluginService pluginService;
@Autowired @Autowired
@Getter private AttributesService attributesService; @Getter
private TimeseriesService tsService;
@Autowired @Autowired
@Getter private EventService eventService; @Getter
private AttributesService attributesService;
@Autowired @Autowired
@Getter private AlarmService alarmService; @Getter
private EventService eventService;
@Autowired @Autowired
@Getter private RelationService relationService; @Getter
private AlarmService alarmService;
@Autowired @Autowired
@Getter private AuditLogService auditLogService; @Getter
private RelationService relationService;
@Autowired @Autowired
@Getter @Setter private PluginWebSocketMsgEndpoint wsMsgEndpoint; @Getter
private AuditLogService auditLogService;
@Autowired
@Getter
@Setter
private PluginWebSocketMsgEndpoint wsMsgEndpoint;
@Value("${actors.session.sync.timeout}") @Value("${actors.session.sync.timeout}")
@Getter private long syncSessionTimeout; @Getter
private long syncSessionTimeout;
@Value("${actors.plugin.termination.delay}") @Value("${actors.plugin.termination.delay}")
@Getter private long pluginActorTerminationDelay; @Getter
private long pluginActorTerminationDelay;
@Value("${actors.plugin.processing.timeout}") @Value("${actors.plugin.processing.timeout}")
@Getter private long pluginProcessingTimeout; @Getter
private long pluginProcessingTimeout;
@Value("${actors.plugin.error_persist_frequency}") @Value("${actors.plugin.error_persist_frequency}")
@Getter private long pluginErrorPersistFrequency; @Getter
private long pluginErrorPersistFrequency;
@Value("${actors.rule.chain.error_persist_frequency}") @Value("${actors.rule.chain.error_persist_frequency}")
@Getter private long ruleChainErrorPersistFrequency; @Getter
private long ruleChainErrorPersistFrequency;
@Value("${actors.rule.node.error_persist_frequency}") @Value("${actors.rule.node.error_persist_frequency}")
@Getter private long ruleNodeErrorPersistFrequency; @Getter
private long ruleNodeErrorPersistFrequency;
@Value("${actors.rule.termination.delay}") @Value("${actors.rule.termination.delay}")
@Getter private long ruleActorTerminationDelay; @Getter
private long ruleActorTerminationDelay;
@Value("${actors.rule.error_persist_frequency}") @Value("${actors.rule.error_persist_frequency}")
@Getter private long ruleErrorPersistFrequency; @Getter
private long ruleErrorPersistFrequency;
@Value("${actors.statistics.enabled}") @Value("${actors.statistics.enabled}")
@Getter private boolean statisticsEnabled; @Getter
private boolean statisticsEnabled;
@Value("${actors.statistics.persist_frequency}") @Value("${actors.statistics.persist_frequency}")
@Getter private long statisticsPersistFrequency; @Getter
private long statisticsPersistFrequency;
@Value("${actors.tenant.create_components_on_init}") @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() { public ActorSystemContext() {
config = ConfigFactory.parseResources(AKKA_CONF_FILE_NAME).withFallback(ConfigFactory.load()); config = ConfigFactory.parseResources(AKKA_CONF_FILE_NAME).withFallback(ConfigFactory.load());
@ -197,7 +248,7 @@ public class ActorSystemContext {
eventService.save(event); eventService.save(event);
} }
private String toString(Exception e) { private String toString(Throwable e) {
StringWriter sw = new StringWriter(); StringWriter sw = new StringWriter();
e.printStackTrace(new PrintWriter(sw)); e.printStackTrace(new PrintWriter(sw));
return sw.toString(); return sw.toString();
@ -217,4 +268,64 @@ public class ActorSystemContext {
private JsonNode toBodyJson(ServerAddress server, String method, String body) { private JsonNode toBodyJson(ServerAddress server, String method, String body) {
return mapper.createObjectNode().put("server", server.toString()).put("method", method).put("error", 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);
}
} }

13
application/src/main/java/org/thingsboard/server/actors/app/AppActor.java

@ -1,12 +1,12 @@
/** /**
* Copyright © 2016-2018 The Thingsboard Authors * Copyright © 2016-2018 The Thingsboard Authors
* <p> *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* <p> *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* <p> *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -88,7 +88,7 @@ public class AppActor extends RuleChainManagerActor {
} }
@Override @Override
protected void process(TbActorMsg msg) { protected boolean process(TbActorMsg msg) {
switch (msg.getMsgType()) { switch (msg.getMsgType()) {
case COMPONENT_LIFE_CYCLE_MSG: case COMPONENT_LIFE_CYCLE_MSG:
onComponentLifecycleMsg((ComponentLifecycleMsg) msg); onComponentLifecycleMsg((ComponentLifecycleMsg) msg);
@ -96,7 +96,10 @@ public class AppActor extends RuleChainManagerActor {
case SERVICE_TO_RULE_ENGINE_MSG: case SERVICE_TO_RULE_ENGINE_MSG:
onServiceToRuleEngineMsg((ServiceToRuleEngineMsg) msg); onServiceToRuleEngineMsg((ServiceToRuleEngineMsg) msg);
break; break;
default:
return false;
} }
return true;
} }
private void onServiceToRuleEngineMsg(ServiceToRuleEngineMsg msg) { private void onServiceToRuleEngineMsg(ServiceToRuleEngineMsg msg) {

21
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.Logging;
import akka.event.LoggingAdapter; import akka.event.LoggingAdapter;
import org.thingsboard.server.actors.ActorSystemContext; 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.ContextAwareActor;
import org.thingsboard.server.actors.service.ContextBasedCreator; 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.DeviceId;
import org.thingsboard.server.common.data.id.TenantId; 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.cluster.ClusterEventMsg;
import org.thingsboard.server.common.msg.device.ToDeviceActorMsg; import org.thingsboard.server.common.msg.device.ToDeviceActorMsg;
import org.thingsboard.server.extensions.api.device.DeviceAttributesEventNotificationMsg; import org.thingsboard.server.extensions.api.device.DeviceAttributesEventNotificationMsg;
import org.thingsboard.server.extensions.api.device.DeviceCredentialsUpdateNotificationMsg; import org.thingsboard.server.extensions.api.device.DeviceCredentialsUpdateNotificationMsg;
import org.thingsboard.server.extensions.api.device.DeviceNameOrTypeUpdateMsg; import org.thingsboard.server.extensions.api.device.DeviceNameOrTypeUpdateMsg;
import org.thingsboard.server.extensions.api.device.ToDeviceActorNotificationMsg; 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 { public class DeviceActor extends ContextAwareActor {
@ -47,13 +47,18 @@ public class DeviceActor extends ContextAwareActor {
this.processor = new DeviceActorMessageProcessor(systemContext, logger, deviceId); this.processor = new DeviceActorMessageProcessor(systemContext, logger, deviceId);
} }
@Override
protected boolean process(TbActorMsg msg) {
return false;
}
@Override @Override
public void onReceive(Object msg) throws Exception { public void onReceive(Object msg) throws Exception {
if (msg instanceof RuleChainDeviceMsg) { // if (msg instanceof RuleChainDeviceMsg) {
processor.process(context(), (RuleChainDeviceMsg) msg); // processor.process(context(), (RuleChainDeviceMsg) msg);
} else if (msg instanceof RulesProcessedMsg) { // } else if (msg instanceof RulesProcessedMsg) {
processor.onRulesProcessedMsg(context(), (RulesProcessedMsg) msg); // processor.onRulesProcessedMsg(context(), (RulesProcessedMsg) msg);
} else if (msg instanceof ToDeviceActorMsg) { if (msg instanceof ToDeviceActorMsg) {
processor.process(context(), (ToDeviceActorMsg) msg); processor.process(context(), (ToDeviceActorMsg) msg);
} else if (msg instanceof ToDeviceActorNotificationMsg) { } else if (msg instanceof ToDeviceActorNotificationMsg) {
if (msg instanceof DeviceAttributesEventNotificationMsg) { if (msg instanceof DeviceAttributesEventNotificationMsg) {

63
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.actor.ActorRef;
import akka.event.LoggingAdapter; import akka.event.LoggingAdapter;
import org.thingsboard.server.actors.ActorSystemContext; import org.thingsboard.server.actors.ActorSystemContext;
import org.thingsboard.server.actors.rule.*;
import org.thingsboard.server.actors.shared.AbstractContextAwareMsgProcessor; 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.DataConstants;
import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.id.DeviceId; 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.MsgType;
import org.thingsboard.server.common.msg.session.SessionType; import org.thingsboard.server.common.msg.session.SessionType;
import org.thingsboard.server.common.msg.session.ToDeviceMsg; import org.thingsboard.server.common.msg.session.ToDeviceMsg;
import org.thingsboard.server.extensions.api.device.*; import org.thingsboard.server.extensions.api.device.DeviceAttributes;
import org.thingsboard.server.extensions.api.plugins.msg.FromDeviceRpcResponse; import org.thingsboard.server.extensions.api.device.DeviceAttributesEventNotificationMsg;
import org.thingsboard.server.extensions.api.plugins.msg.RpcError; import org.thingsboard.server.extensions.api.device.DeviceNameOrTypeUpdateMsg;
import org.thingsboard.server.extensions.api.plugins.msg.TimeoutIntMsg; 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.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 java.util.*; import java.util.*;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
@ -230,18 +223,18 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
} }
} }
void process(ActorContext context, RuleChainDeviceMsg srcMsg) { // void process(ActorContext context, RuleChainDeviceMsg srcMsg) {
ChainProcessingMetaData md = new ChainProcessingMetaData(srcMsg.getRuleChain(), // ChainProcessingMetaData md = new ChainProcessingMetaData(srcMsg.getRuleChain(),
srcMsg.getToDeviceActorMsg(), new DeviceMetaData(deviceId, deviceName, deviceType, deviceAttributes), context.self()); // srcMsg.getToDeviceActorMsg(), new DeviceMetaData(deviceId, deviceName, deviceType, deviceAttributes), context.self());
ChainProcessingContext ctx = new ChainProcessingContext(md); // ChainProcessingContext ctx = new ChainProcessingContext(md);
if (ctx.getChainLength() > 0) { // if (ctx.getChainLength() > 0) {
RuleProcessingMsg msg = new RuleProcessingMsg(ctx); // RuleProcessingMsg msg = new RuleProcessingMsg(ctx);
ActorRef ruleActorRef = ctx.getCurrentActor(); // ActorRef ruleActorRef = ctx.getCurrentActor();
ruleActorRef.tell(msg, ActorRef.noSender()); // ruleActorRef.tell(msg, ActorRef.noSender());
} else { // } else {
context.self().tell(new RulesProcessedMsg(ctx), context.self()); // context.self().tell(new RulesProcessedMsg(ctx), context.self());
} // }
} // }
void processRpcResponses(ActorContext context, ToDeviceActorMsg msg) { void processRpcResponses(ActorContext context, ToDeviceActorMsg msg) {
SessionId sessionId = msg.getSessionId(); SessionId sessionId = msg.getSessionId();
@ -302,18 +295,18 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
); );
} }
void onRulesProcessedMsg(ActorContext context, RulesProcessedMsg msg) { // void onRulesProcessedMsg(ActorContext context, RulesProcessedMsg msg) {
ChainProcessingContext ctx = msg.getCtx(); // ChainProcessingContext ctx = msg.getCtx();
ToDeviceActorMsg inMsg = ctx.getInMsg(); // ToDeviceActorMsg inMsg = ctx.getInMsg();
SessionId sid = inMsg.getSessionId(); // SessionId sid = inMsg.getSessionId();
ToDeviceSessionActorMsg response; // ToDeviceSessionActorMsg response;
if (ctx.getResponse() != null) { // if (ctx.getResponse() != null) {
response = new BasicToDeviceSessionActorMsg(ctx.getResponse(), sid); // response = new BasicToDeviceSessionActorMsg(ctx.getResponse(), sid);
} else { // } else {
response = new BasicToDeviceSessionActorMsg(ctx.getError(), sid); // response = new BasicToDeviceSessionActorMsg(ctx.getError(), sid);
} // }
sendMsgToSessionActor(response, inMsg.getServerAddress()); // sendMsgToSessionActor(response, inMsg.getServerAddress());
} // }
private void processSubscriptionCommands(ActorContext context, ToDeviceActorMsg msg) { private void processSubscriptionCommands(ActorContext context, ToDeviceActorMsg msg) {
SessionId sessionId = msg.getSessionId(); SessionId sessionId = msg.getSessionId();

7
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.actors.stats.StatsPersistTick;
import org.thingsboard.server.common.data.id.PluginId; import org.thingsboard.server.common.data.id.PluginId;
import org.thingsboard.server.common.data.id.TenantId; 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.cluster.ClusterEventMsg;
import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
import org.thingsboard.server.extensions.api.plugins.msg.TimeoutMsg; import org.thingsboard.server.extensions.api.plugins.msg.TimeoutMsg;
@ -40,6 +41,12 @@ public class PluginActor extends ComponentActor<PluginId, PluginActorMessageProc
logger, context().parent(), context().self())); logger, context().parent(), context().self()));
} }
@Override
protected boolean process(TbActorMsg msg) {
//TODO Move everything here, to work with TbActorMsg
return false;
}
@Override @Override
public void onReceive(Object msg) throws Exception { public void onReceive(Object msg) throws Exception {
if (msg instanceof PluginWebsocketMsg) { if (msg instanceof PluginWebsocketMsg) {

7
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.ContextAwareActor;
import org.thingsboard.server.actors.service.ContextBasedCreator; import org.thingsboard.server.actors.service.ContextBasedCreator;
import org.thingsboard.server.actors.service.DefaultActorService; 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.ClusterEventMsg;
import org.thingsboard.server.common.msg.cluster.ServerAddress; import org.thingsboard.server.common.msg.cluster.ServerAddress;
import org.thingsboard.server.gen.cluster.ClusterAPIProtos; 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 @Override
public void onReceive(Object msg) throws Exception { public void onReceive(Object msg) throws Exception {
if (msg instanceof RpcSessionTellMsg) { if (msg instanceof RpcSessionTellMsg) {

7
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.ActorSystemContext;
import org.thingsboard.server.actors.service.ContextAwareActor; import org.thingsboard.server.actors.service.ContextAwareActor;
import org.thingsboard.server.actors.service.ContextBasedCreator; 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.common.msg.cluster.ServerAddress;
import org.thingsboard.server.gen.cluster.ClusterAPIProtos; import org.thingsboard.server.gen.cluster.ClusterAPIProtos;
import org.thingsboard.server.gen.cluster.ClusterRpcServiceGrpc; import org.thingsboard.server.gen.cluster.ClusterRpcServiceGrpc;
@ -47,6 +48,12 @@ public class RpcSessionActor extends ContextAwareActor {
this.sessionId = sessionId; this.sessionId = sessionId;
} }
@Override
protected boolean process(TbActorMsg msg) {
//TODO Move everything here, to work with TbActorMsg
return false;
}
@Override @Override
public void onReceive(Object msg) throws Exception { public void onReceive(Object msg) throws Exception {
if (msg instanceof RpcSessionTellMsg) { if (msg instanceof RpcSessionTellMsg) {

117
application/src/main/java/org/thingsboard/server/actors/rule/ChainProcessingContext.java

@ -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);
}
}

41
application/src/main/java/org/thingsboard/server/actors/rule/ChainProcessingMetaData.java

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

43
application/src/main/java/org/thingsboard/server/actors/rule/ComplexRuleActorChain.java

@ -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());
}
}
}

20
application/src/main/java/org/thingsboard/server/actors/rule/CompoundRuleActorChain.java

@ -1,20 +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 CompoundRuleActorChain {
}

90
application/src/main/java/org/thingsboard/server/actors/rule/RuleActor.java

@ -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<RuleId, RuleActorMessageProcessor> {
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<RuleActor> {
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();
}
}

345
application/src/main/java/org/thingsboard/server/actors/rule/RuleActorMessageProcessor.java

@ -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<RuleId> {
private final RuleProcessingContext ruleCtx;
private final Map<UUID, RuleProcessingMsg> pendingMsgMap;
private RuleMetaData ruleMd;
private ComponentLifecycleState state;
private List<RuleFilter> 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(ActorContext context) 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(ActorContext context) 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<RuleToPluginMsg<?>> 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<ToDeviceMsg> 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());
}
}
}

107
application/src/main/java/org/thingsboard/server/actors/rule/RuleActorMetaData.java

@ -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<RuleActorMetaData> RULE_ACTOR_MD_COMPARATOR = new Comparator<RuleActorMetaData>() {
@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 + "]";
}
}

33
application/src/main/java/org/thingsboard/server/actors/rule/RuleContextAwareMsgProcessor.java

@ -1,33 +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.shared.AbstractContextAwareMsgProcessor;
import org.thingsboard.server.common.data.id.RuleId;
import akka.event.LoggingAdapter;
public class RuleContextAwareMsgProcessor extends AbstractContextAwareMsgProcessor {
private final RuleId ruleId;
protected RuleContextAwareMsgProcessor(ActorSystemContext systemContext, LoggingAdapter logger, RuleId ruleId) {
super(systemContext, logger);
this.ruleId = ruleId;
}
}

115
application/src/main/java/org/thingsboard/server/actors/rule/RuleProcessingContext.java

@ -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<Event> saveIfNotExists(Event event) {
checkEvent(event);
return eventService.saveIfNotExists(event);
}
@Override
public Optional<Event> 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<Alarm> 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<Boolean> 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);
}
}
}

31
application/src/main/java/org/thingsboard/server/actors/rule/RuleProcessingMsg.java

@ -1,31 +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 RuleProcessingMsg {
private final ChainProcessingContext ctx;
public RuleProcessingMsg(ChainProcessingContext ctx) {
super();
this.ctx = ctx;
}
public ChainProcessingContext getCtx() {
return ctx;
}
}

30
application/src/main/java/org/thingsboard/server/actors/rule/RuleTerminationMsg.java

@ -1,30 +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.shared.ActorTerminationMsg;
import org.thingsboard.server.common.data.id.PluginId;
import org.thingsboard.server.common.data.id.RuleId;
/**
* @author Andrew Shvayka
*/
public class RuleTerminationMsg extends ActorTerminationMsg<RuleId> {
public RuleTerminationMsg(RuleId id) {
super(id);
}
}

36
application/src/main/java/org/thingsboard/server/actors/rule/RuleToPluginTimeoutMsg.java

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

39
application/src/main/java/org/thingsboard/server/actors/rule/SimpleRuleActorChain.java

@ -1,39 +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.ArrayList;
import java.util.List;
import java.util.Set;
public class SimpleRuleActorChain implements RuleActorChain {
private final List<RuleActorMetaData> rules;
public SimpleRuleActorChain(Set<RuleActorMetaData> ruleSet) {
rules = new ArrayList<>(ruleSet);
rules.sort(RuleActorMetaData.RULE_ACTOR_MD_COMPARATOR);
}
public int size() {
return rules.size();
}
public RuleActorMetaData getRuleActorMd(int index) {
return rules.get(index);
}
}

90
application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java

@ -1,10 +1,34 @@
/**
* Copyright © 2016-2018 The Thingsboard Authors
* <p>
* 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
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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; package org.thingsboard.server.actors.ruleChain;
import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.TbContext;
import org.thingsboard.server.actors.ActorSystemContext; import org.thingsboard.server.actors.ActorSystemContext;
import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.cluster.ServerAddress; 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.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;
/** /**
* Created by ashvayka on 19.03.18. * Created by ashvayka on 19.03.18.
@ -26,27 +50,30 @@ class DefaultTbContext implements TbContext {
@Override @Override
public void tellNext(TbMsg msg, String relationType) { public void tellNext(TbMsg msg, String relationType) {
nodeCtx.getChainActor().tell(new RuleNodeToRuleChainTellNextMsg(nodeCtx.getSelfId(), relationType, msg), nodeCtx.getSelf()); 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 @Override
public void tellSelf(TbMsg msg, long delayMs) { public void tellSelf(TbMsg msg, long delayMs) {
throw new RuntimeException("Not Implemented!");
} }
@Override @Override
public void tellOthers(TbMsg msg) { public void tellOthers(TbMsg msg) {
throw new RuntimeException("Not Implemented!");
} }
@Override @Override
public void tellSibling(TbMsg msg, ServerAddress address) { public void tellSibling(TbMsg msg, ServerAddress address) {
throw new RuntimeException("Not Implemented!");
} }
@Override @Override
public void spawn(TbMsg msg) { public void spawn(TbMsg msg) {
throw new RuntimeException("Not Implemented!");
} }
@Override @Override
@ -54,8 +81,61 @@ class DefaultTbContext implements TbContext {
} }
@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 @Override
public AttributesService getAttributesService() { public AttributesService getAttributesService() {
return mainCtx.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();
}
} }

15
application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActor.java

@ -1,12 +1,12 @@
/** /**
* Copyright © 2016-2018 The Thingsboard Authors * Copyright © 2016-2018 The Thingsboard Authors
* <p> *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* <p> *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* <p> *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -33,7 +33,7 @@ public class RuleChainActor extends ComponentActor<RuleChainId, RuleChainActorMe
} }
@Override @Override
protected void process(TbActorMsg msg) { protected boolean process(TbActorMsg msg) {
switch (msg.getMsgType()) { switch (msg.getMsgType()) {
case COMPONENT_LIFE_CYCLE_MSG: case COMPONENT_LIFE_CYCLE_MSG:
onComponentLifecycleMsg((ComponentLifecycleMsg) msg); onComponentLifecycleMsg((ComponentLifecycleMsg) msg);
@ -44,7 +44,10 @@ public class RuleChainActor extends ComponentActor<RuleChainId, RuleChainActorMe
case RULE_TO_RULE_CHAIN_TELL_NEXT_MSG: case RULE_TO_RULE_CHAIN_TELL_NEXT_MSG:
processor.onTellNext((RuleNodeToRuleChainTellNextMsg) msg); processor.onTellNext((RuleNodeToRuleChainTellNextMsg) msg);
break; break;
default:
return false;
} }
return true;
} }
public static class ActorCreator extends ContextBasedCreator<RuleChainActor> { public static class ActorCreator extends ContextBasedCreator<RuleChainActor> {
@ -69,4 +72,6 @@ public class RuleChainActor extends ComponentActor<RuleChainId, RuleChainActorMe
protected long getErrorPersistFrequency() { protected long getErrorPersistFrequency() {
return systemContext.getRuleChainErrorPersistFrequency(); return systemContext.getRuleChainErrorPersistFrequency();
} }
//TODO: failover strategy
} }

33
application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java

@ -27,6 +27,7 @@ import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.RuleNodeId; import org.thingsboard.server.common.data.id.RuleNodeId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.plugin.ComponentLifecycleState;
import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.data.rule.RuleNode; import org.thingsboard.server.common.data.rule.RuleNode;
@ -53,6 +54,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
private RuleNodeId firstId; private RuleNodeId firstId;
private RuleNodeCtx firstNode; private RuleNodeCtx firstNode;
private ComponentLifecycleState state;
RuleChainActorMessageProcessor(TenantId tenantId, RuleChainId ruleChainId, ActorSystemContext systemContext RuleChainActorMessageProcessor(TenantId tenantId, RuleChainId ruleChainId, ActorSystemContext systemContext
, LoggingAdapter logger, ActorRef parent, ActorRef self) { , LoggingAdapter logger, ActorRef parent, ActorRef self) {
@ -66,6 +68,9 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
@Override @Override
public void start(ActorContext context) throws Exception { public void start(ActorContext context) throws Exception {
if (state == ComponentLifecycleState.ACTIVE) {
return;
}
RuleChain ruleChain = service.findRuleChainById(entityId); RuleChain ruleChain = service.findRuleChainById(entityId);
List<RuleNode> ruleNodeList = service.getRuleChainNodes(entityId); List<RuleNode> ruleNodeList = service.getRuleChainNodes(entityId);
// Creating and starting the actors; // Creating and starting the actors;
@ -74,8 +79,8 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
DefaultActorService.SYSTEM_RULE_DISPATCHER_NAME : DefaultActorService.TENANT_RULE_DISPATCHER_NAME; DefaultActorService.SYSTEM_RULE_DISPATCHER_NAME : DefaultActorService.TENANT_RULE_DISPATCHER_NAME;
ActorRef ruleNodeActor = context.actorOf( ActorRef ruleNodeActor = context.actorOf(
Props.create(new RuleNodeActor.ActorCreator(systemContext, tenantId, entityId, ruleNode.getId())) Props.create(new RuleNodeActor.ActorCreator(systemContext, tenantId, entityId, ruleNode.getId()))
.withDispatcher(dispatcherName), ruleNode.toString()); .withDispatcher(dispatcherName), ruleNode.getId().toString());
nodeActors.put(ruleNode.getId(), new RuleNodeCtx(self, ruleNodeActor, ruleNode.getId())); nodeActors.put(ruleNode.getId(), new RuleNodeCtx(tenantId, self, ruleNodeActor, ruleNode));
} }
// Populating the routes map; // Populating the routes map;
for (RuleNode ruleNode : ruleNodeList) { for (RuleNode ruleNode : ruleNodeList) {
@ -94,13 +99,15 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
firstId = ruleChain.getFirstRuleNodeId(); firstId = ruleChain.getFirstRuleNodeId();
firstNode = nodeActors.get(ruleChain.getFirstRuleNodeId()); firstNode = nodeActors.get(ruleChain.getFirstRuleNodeId());
state = ComponentLifecycleState.ACTIVE;
} }
@Override @Override
public void stop(ActorContext context) throws Exception { public void stop(ActorContext context) throws Exception {
nodeActors.values().stream().map(RuleNodeCtx::getSelf).forEach(context::stop); nodeActors.values().stream().map(RuleNodeCtx::getSelfActor).forEach(context::stop);
nodeActors.clear(); nodeActors.clear();
nodeRoutes.clear(); nodeRoutes.clear();
state = ComponentLifecycleState.SUSPENDED;
} }
@Override @Override
@ -109,23 +116,33 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
} }
void onServiceToRuleEngineMsg(ServiceToRuleEngineMsg envelope) { void onServiceToRuleEngineMsg(ServiceToRuleEngineMsg envelope) {
checkActive();
TbMsg tbMsg = envelope.getTbMsg(); TbMsg tbMsg = envelope.getTbMsg();
//TODO: push to queue and act on ack in async way //TODO: push to queue and act on ack in async way
pushMstToNode(firstNode, tbMsg); pushMstToNode(firstNode, tbMsg);
} }
void onTellNext(RuleNodeToRuleChainTellNextMsg envelope) { void onTellNext(RuleNodeToRuleChainTellNextMsg envelope) {
checkActive();
RuleNodeId originator = envelope.getOriginator(); RuleNodeId originator = envelope.getOriginator();
String targetRelationType = envelope.getRelationType(); String targetRelationType = envelope.getRelationType();
//TODO: log debug output //TODO: log debug output
List<RuleNodeRelation> relations = nodeRoutes.get(originator); List<RuleNodeRelation> relations = nodeRoutes.get(originator);
if (relations == null) {
return;
}
boolean copy = relations.size() > 1;
for (RuleNodeRelation relation : relations) { for (RuleNodeRelation relation : relations) {
TbMsg msg = envelope.getMsg();
if (copy) {
msg = msg.copy();
}
if (targetRelationType == null || targetRelationType.equalsIgnoreCase(relation.getType())) { if (targetRelationType == null || targetRelationType.equalsIgnoreCase(relation.getType())) {
switch (relation.getOut().getEntityType()) { switch (relation.getOut().getEntityType()) {
case RULE_NODE: case RULE_NODE:
RuleNodeId targetRuleNodeId = new RuleNodeId(relation.getOut().getId()); RuleNodeId targetRuleNodeId = new RuleNodeId(relation.getOut().getId());
RuleNodeCtx targetRuleNode = nodeActors.get(targetRuleNodeId); RuleNodeCtx targetRuleNode = nodeActors.get(targetRuleNodeId);
pushMstToNode(targetRuleNode, envelope.getMsg()); pushMstToNode(targetRuleNode, msg);
break; break;
case RULE_CHAIN: case RULE_CHAIN:
// TODO: implement // TODO: implement
@ -137,7 +154,13 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
private void pushMstToNode(RuleNodeCtx nodeCtx, TbMsg msg) { private void pushMstToNode(RuleNodeCtx nodeCtx, TbMsg msg) {
//TODO: log debug input //TODO: log debug input
firstNode.getSelf().tell(new RuleChainToRuleNodeMsg(new DefaultTbContext(systemContext, nodeCtx), msg), self); firstNode.getSelfActor().tell(new RuleChainToRuleNodeMsg(new DefaultTbContext(systemContext, nodeCtx), msg), self);
}
private void checkActive() {
if (state != ComponentLifecycleState.ACTIVE) {
throw new IllegalStateException("Rule chain is not active!");
}
} }
} }

15
application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainManagerActor.java

@ -1,3 +1,18 @@
/**
* 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; package org.thingsboard.server.actors.ruleChain;
import akka.actor.ActorRef; import akka.actor.ActorRef;

15
application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainToRuleNodeMsg.java

@ -1,3 +1,18 @@
/**
* 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; package org.thingsboard.server.actors.ruleChain;
import lombok.Data; import lombok.Data;

26
application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeActor.java

@ -35,14 +35,34 @@ public class RuleNodeActor extends ComponentActor<RuleNodeId, RuleNodeActorMessa
} }
@Override @Override
protected void process(TbActorMsg msg) { protected boolean process(TbActorMsg msg) {
switch (msg.getMsgType()) { switch (msg.getMsgType()) {
case RULE_CHAIN_TO_RULE_MSG: case RULE_CHAIN_TO_RULE_MSG:
processor.onRuleChainToRuleNodeMsg((RuleChainToRuleNodeMsg) msg); onRuleChainToRuleNodeMsg((RuleChainToRuleNodeMsg) msg);
break; 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<RuleNodeActor> { public static class ActorCreator extends ContextBasedCreator<RuleNodeActor> {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@ -69,4 +89,6 @@ public class RuleNodeActor extends ComponentActor<RuleNodeId, RuleNodeActorMessa
return systemContext.getRuleNodeErrorPersistFrequency(); return systemContext.getRuleNodeErrorPersistFrequency();
} }
//TODO: failover strategy
} }

40
application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeActorMessageProcessor.java

@ -17,28 +17,28 @@ package org.thingsboard.server.actors.ruleChain;
import akka.actor.ActorContext; import akka.actor.ActorContext;
import akka.actor.ActorRef; import akka.actor.ActorRef;
import akka.actor.Props;
import akka.event.LoggingAdapter; import akka.event.LoggingAdapter;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.springframework.util.Base64Utils;
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.ActorSystemContext;
import org.thingsboard.server.actors.service.DefaultActorService;
import org.thingsboard.server.actors.shared.ComponentMsgProcessor; import org.thingsboard.server.actors.shared.ComponentMsgProcessor;
import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.Event;
import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.RuleNodeId; import org.thingsboard.server.common.data.id.RuleNodeId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
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.data.rule.RuleNode;
import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.cluster.ClusterEventMsg; import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;
import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg;
import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.rule.RuleChainService;
import java.util.ArrayList; import java.io.PrintWriter;
import java.util.HashMap; import java.io.StringWriter;
import java.util.List; import java.nio.charset.StandardCharsets;
import java.util.Map;
/** /**
* @author Andrew Shvayka * @author Andrew Shvayka
@ -48,6 +48,8 @@ public class RuleNodeActorMessageProcessor extends ComponentMsgProcessor<RuleNod
private final ActorRef parent; private final ActorRef parent;
private final ActorRef self; private final ActorRef self;
private final RuleChainService service; private final RuleChainService service;
private RuleNode ruleNode;
private TbNode tbNode;
RuleNodeActorMessageProcessor(TenantId tenantId, RuleChainId ruleChainId, RuleNodeId ruleNodeId, ActorSystemContext systemContext RuleNodeActorMessageProcessor(TenantId tenantId, RuleChainId ruleChainId, RuleNodeId ruleNodeId, ActorSystemContext systemContext
, LoggingAdapter logger, ActorRef parent, ActorRef self) { , LoggingAdapter logger, ActorRef parent, ActorRef self) {
@ -55,15 +57,17 @@ public class RuleNodeActorMessageProcessor extends ComponentMsgProcessor<RuleNod
this.parent = parent; this.parent = parent;
this.self = self; this.self = self;
this.service = systemContext.getRuleChainService(); this.service = systemContext.getRuleChainService();
this.ruleNode = systemContext.getRuleChainService().findRuleNodeById(entityId);
} }
@Override @Override
public void start(ActorContext context) throws Exception { public void start(ActorContext context) throws Exception {
tbNode = initComponent(ruleNode);
} }
@Override @Override
public void stop(ActorContext context) throws Exception { public void stop(ActorContext context) throws Exception {
tbNode.destroy();
} }
@Override @Override
@ -71,8 +75,18 @@ public class RuleNodeActorMessageProcessor extends ComponentMsgProcessor<RuleNod
} }
void onRuleChainToRuleNodeMsg(RuleChainToRuleNodeMsg msg) { void onRuleChainToRuleNodeMsg(RuleChainToRuleNodeMsg msg) throws Exception {
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;
} }
} }

22
application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeCtx.java

@ -1,15 +1,33 @@
/**
* 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; package org.thingsboard.server.actors.ruleChain;
import akka.actor.ActorRef; import akka.actor.ActorRef;
import lombok.Data; import lombok.Data;
import org.thingsboard.server.common.data.id.RuleNodeId; import org.thingsboard.server.common.data.id.RuleNodeId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.rule.RuleNode;
/** /**
* Created by ashvayka on 19.03.18. * Created by ashvayka on 19.03.18.
*/ */
@Data @Data
final class RuleNodeCtx { final class RuleNodeCtx {
private final TenantId tenantId;
private final ActorRef chainActor; private final ActorRef chainActor;
private final ActorRef self; private final ActorRef selfActor;
private final RuleNodeId selfId; private final RuleNode self;
} }

15
application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeRelation.java

@ -1,3 +1,18 @@
/**
* 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; package org.thingsboard.server.actors.ruleChain;
import lombok.Data; import lombok.Data;

15
application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeToRuleChainTellNextMsg.java

@ -1,3 +1,18 @@
/**
* 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; package org.thingsboard.server.actors.ruleChain;
import lombok.Data; import lombok.Data;

28
application/src/main/java/org/thingsboard/server/actors/rule/RulesProcessedMsg.java → application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeToSelfErrorMsg.java

@ -13,22 +13,26 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.thingsboard.server.actors.rule; package org.thingsboard.server.actors.ruleChain;
public class RulesProcessedMsg { import lombok.Data;
private final ChainProcessingContext ctx; 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;
public RulesProcessedMsg(ChainProcessingContext ctx) { /**
super(); * Created by ashvayka on 19.03.18.
this.ctx = ctx; */
} @Data
final class RuleNodeToSelfErrorMsg implements TbActorMsg {
public ChainProcessingContext getCtx() { private final TbMsg msg;
return ctx; private final Throwable error;
}
@Override @Override
public String toString() { public MsgType getMsgType() {
return "RulesProcessedMsg [ctx=" + ctx + "]"; return MsgType.RULE_TO_SELF_ERROR_MSG;
} }
} }

1
application/src/main/java/org/thingsboard/server/actors/service/ComponentActor.java

@ -141,7 +141,6 @@ public abstract class ComponentActor<T extends EntityId, P extends ComponentMsgP
messagesProcessed++; messagesProcessed++;
} }
protected void logAndPersist(String method, Exception e) { protected void logAndPersist(String method, Exception e) {
logAndPersist(method, e, false); logAndPersist(method, e, false);
} }

9
application/src/main/java/org/thingsboard/server/actors/service/ContextAwareActor.java

@ -39,12 +39,13 @@ public abstract class ContextAwareActor extends UntypedActor {
logger.debug("Processing msg: {}", msg); logger.debug("Processing msg: {}", msg);
} }
if (msg instanceof TbActorMsg) { if (msg instanceof TbActorMsg) {
process((TbActorMsg) msg); if(!process((TbActorMsg) msg)){
} logger.warning("Unknown message: {}!", msg);
else { }
} else {
logger.warning("Unknown message: {}!", msg); logger.warning("Unknown message: {}!", msg);
} }
} }
protected abstract void process(TbActorMsg msg); protected abstract boolean process(TbActorMsg msg);
} }

8
application/src/main/java/org/thingsboard/server/actors/service/DefaultActorService.java

@ -1,12 +1,12 @@
/** /**
* Copyright © 2016-2018 The Thingsboard Authors * Copyright © 2016-2018 The Thingsboard Authors
* <p> *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* <p> *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* <p> *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

7
application/src/main/java/org/thingsboard/server/actors/session/SessionActor.java

@ -23,6 +23,7 @@ import org.thingsboard.server.actors.service.ContextAwareActor;
import org.thingsboard.server.actors.service.ContextBasedCreator; import org.thingsboard.server.actors.service.ContextBasedCreator;
import org.thingsboard.server.actors.shared.SessionTimeoutMsg; import org.thingsboard.server.actors.shared.SessionTimeoutMsg;
import org.thingsboard.server.common.data.id.SessionId; import org.thingsboard.server.common.data.id.SessionId;
import org.thingsboard.server.common.msg.TbActorMsg;
import org.thingsboard.server.common.msg.cluster.ClusterEventMsg; import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;
import org.thingsboard.server.common.msg.core.ToDeviceSessionActorMsg; import org.thingsboard.server.common.msg.core.ToDeviceSessionActorMsg;
import org.thingsboard.server.common.msg.session.ToDeviceActorSessionMsg; import org.thingsboard.server.common.msg.session.ToDeviceActorSessionMsg;
@ -60,6 +61,12 @@ public class SessionActor extends ContextAwareActor {
}); });
} }
@Override
protected boolean process(TbActorMsg msg) {
//TODO Move everything here, to work with TbActorMsg
return false;
}
@Override @Override
public void onReceive(Object msg) throws Exception { public void onReceive(Object msg) throws Exception {
logger.debug("[{}] Processing: {}.", sessionId, msg); logger.debug("[{}] Processing: {}.", sessionId, msg);

7
application/src/main/java/org/thingsboard/server/actors/session/SessionManagerActor.java

@ -26,6 +26,7 @@ import org.thingsboard.server.actors.service.ContextBasedCreator;
import org.thingsboard.server.actors.service.DefaultActorService; import org.thingsboard.server.actors.service.DefaultActorService;
import org.thingsboard.server.actors.shared.SessionTimeoutMsg; import org.thingsboard.server.actors.shared.SessionTimeoutMsg;
import org.thingsboard.server.common.data.id.SessionId; import org.thingsboard.server.common.data.id.SessionId;
import org.thingsboard.server.common.msg.TbActorMsg;
import org.thingsboard.server.common.msg.aware.SessionAwareMsg; import org.thingsboard.server.common.msg.aware.SessionAwareMsg;
import akka.event.Logging; import akka.event.Logging;
@ -48,6 +49,12 @@ public class SessionManagerActor extends ContextAwareActor {
this.sessionActors = new HashMap<>(INITIAL_SESSION_MAP_SIZE); this.sessionActors = new HashMap<>(INITIAL_SESSION_MAP_SIZE);
} }
@Override
protected boolean process(TbActorMsg msg) {
//TODO Move everything here, to work with TbActorMsg
return false;
}
@Override @Override
public void onReceive(Object msg) throws Exception { public void onReceive(Object msg) throws Exception {
if (msg instanceof SessionCtrlMsg) { if (msg instanceof SessionCtrlMsg) {

15
application/src/main/java/org/thingsboard/server/actors/shared/EntityActorsManager.java

@ -1,3 +1,18 @@
/**
* 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; package org.thingsboard.server.actors.shared;
import akka.actor.ActorContext; import akka.actor.ActorContext;

8
application/src/main/java/org/thingsboard/server/actors/shared/plugin/PluginManager.java

@ -1,12 +1,12 @@
/** /**
* Copyright © 2016-2018 The Thingsboard Authors * Copyright © 2016-2018 The Thingsboard Authors
* <p> *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* <p> *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* <p> *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

135
application/src/main/java/org/thingsboard/server/actors/shared/rule/RuleManager.java

@ -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<RuleId, ActorRef> ruleActors;
protected final TenantId tenantId;
private Map<RuleMetaData, RuleActorMetaData> 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<RuleMetaData> 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<ActorRef> 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<RuleMetaData> 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<RuleActorMetaData> activeRuleSet = new HashSet<>();
for (Map.Entry<RuleMetaData, RuleActorMetaData> rule : ruleMap.entrySet()) {
if (rule.getKey().getState() == ComponentLifecycleState.ACTIVE) {
activeRuleSet.add(rule.getValue());
}
}
ruleChain = new SimpleRuleActorChain(activeRuleSet);
}
}

40
application/src/main/java/org/thingsboard/server/actors/shared/rule/SystemRuleManager.java

@ -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.shared.rule;
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.dao.model.ModelConstants;
public class SystemRuleManager extends RuleManager {
public SystemRuleManager(ActorSystemContext systemContext) {
super(systemContext, new TenantId(ModelConstants.NULL_UUID));
}
@Override
FetchFunction<RuleMetaData> getFetchRulesFunction() {
return ruleService::findSystemRules;
}
@Override
String getDispatcherName() {
return DefaultActorService.SYSTEM_RULE_DISPATCHER_NAME;
}
}

48
application/src/main/java/org/thingsboard/server/actors/shared/rule/TenantRuleManager.java

@ -1,48 +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 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;
public class TenantRuleManager extends RuleManager {
public TenantRuleManager(ActorSystemContext systemContext, TenantId tenantId) {
super(systemContext, tenantId);
}
@Override
public void init(ActorContext context) {
if (systemContext.isTenantComponentsInitEnabled()) {
super.init(context);
}
}
@Override
FetchFunction<RuleMetaData> getFetchRulesFunction() {
return link -> ruleService.findTenantRules(tenantId, link);
}
@Override
String getDispatcherName() {
return DefaultActorService.TENANT_RULE_DISPATCHER_NAME;
}
}

15
application/src/main/java/org/thingsboard/server/actors/shared/rulechain/RuleChainManager.java

@ -1,3 +1,18 @@
/**
* 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; package org.thingsboard.server.actors.shared.rulechain;
import akka.actor.ActorRef; import akka.actor.ActorRef;

11
application/src/main/java/org/thingsboard/server/actors/shared/rulechain/TenantRuleChainManager.java

@ -1,12 +1,12 @@
/** /**
* Copyright © 2016-2018 The Thingsboard Authors * Copyright © 2016-2018 The Thingsboard Authors
* <p> *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* <p> *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* <p> *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -18,12 +18,9 @@ package org.thingsboard.server.actors.shared.rulechain;
import akka.actor.ActorContext; import akka.actor.ActorContext;
import org.thingsboard.server.actors.ActorSystemContext; import org.thingsboard.server.actors.ActorSystemContext;
import org.thingsboard.server.actors.service.DefaultActorService; import org.thingsboard.server.actors.service.DefaultActorService;
import org.thingsboard.server.actors.shared.rule.RuleManager;
import org.thingsboard.server.common.data.id.TenantId; 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.page.PageDataIterable.FetchFunction;
import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.data.rule.RuleMetaData;
public class TenantRuleChainManager extends RuleChainManager { public class TenantRuleChainManager extends RuleChainManager {

7
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.actors.service.ContextBasedCreator;
import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.Event; import org.thingsboard.server.common.data.Event;
import org.thingsboard.server.common.msg.TbActorMsg;
import org.thingsboard.server.common.msg.cluster.ServerAddress; import org.thingsboard.server.common.msg.cluster.ServerAddress;
public class StatsActor extends ContextAwareActor { public class StatsActor extends ContextAwareActor {
@ -35,6 +36,12 @@ public class StatsActor extends ContextAwareActor {
super(context); super(context);
} }
@Override
protected boolean process(TbActorMsg msg) {
//TODO Move everything here, to work with TbActorMsg\
return false;
}
@Override @Override
public void onReceive(Object msg) throws Exception { public void onReceive(Object msg) throws Exception {
logger.debug("Received message: {}", msg); logger.debug("Received message: {}", msg);

40
application/src/main/java/org/thingsboard/server/actors/tenant/RuleChainDeviceMsg.java

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

13
application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java

@ -1,12 +1,12 @@
/** /**
* Copyright © 2016-2018 The Thingsboard Authors * Copyright © 2016-2018 The Thingsboard Authors
* <p> *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* <p> *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* <p> *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -62,7 +62,7 @@ public class TenantActor extends RuleChainManagerActor {
} }
@Override @Override
protected void process(TbActorMsg msg) { protected boolean process(TbActorMsg msg) {
switch (msg.getMsgType()) { switch (msg.getMsgType()) {
case COMPONENT_LIFE_CYCLE_MSG: case COMPONENT_LIFE_CYCLE_MSG:
onComponentLifecycleMsg((ComponentLifecycleMsg) msg); onComponentLifecycleMsg((ComponentLifecycleMsg) msg);
@ -70,7 +70,10 @@ public class TenantActor extends RuleChainManagerActor {
case SERVICE_TO_RULE_ENGINE_MSG: case SERVICE_TO_RULE_ENGINE_MSG:
onServiceToRuleEngineMsg((ServiceToRuleEngineMsg) msg); onServiceToRuleEngineMsg((ServiceToRuleEngineMsg) msg);
break; break;
default:
return false;
} }
return true;
} }
private void onServiceToRuleEngineMsg(ServiceToRuleEngineMsg msg) { private void onServiceToRuleEngineMsg(ServiceToRuleEngineMsg msg) {

2
application/src/main/java/org/thingsboard/server/controller/RuleChainController.java

@ -78,7 +78,7 @@ public class RuleChainController extends BaseController {
ruleChain.setTenantId(getCurrentUser().getTenantId()); ruleChain.setTenantId(getCurrentUser().getTenantId());
RuleChain savedRuleChain = checkNotNull(ruleChainService.saveRuleChain(ruleChain)); RuleChain savedRuleChain = checkNotNull(ruleChainService.saveRuleChain(ruleChain));
actorService.onEntityStateChange(ruleChain.getTenantId(), ruleChain.getId(), actorService.onEntityStateChange(ruleChain.getTenantId(), savedRuleChain.getId(),
created ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED); created ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED);
logEntityAction(savedRuleChain.getId(), savedRuleChain, logEntityAction(savedRuleChain.getId(), savedRuleChain,

49
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.env.Environment;
import org.springframework.core.type.filter.AnnotationTypeFilter; import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.stereotype.Service; 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.ComponentDescriptor;
import org.thingsboard.server.common.data.plugin.ComponentType; import org.thingsboard.server.common.data.plugin.ComponentType;
import org.thingsboard.server.dao.component.ComponentDescriptorService; import org.thingsboard.server.dao.component.ComponentDescriptorService;
@ -79,8 +83,7 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe
private List<ComponentDescriptor> persist(Set<BeanDefinition> filterDefs, ComponentType type) { private List<ComponentDescriptor> persist(Set<BeanDefinition> filterDefs, ComponentType type) {
List<ComponentDescriptor> result = new ArrayList<>(); List<ComponentDescriptor> result = new ArrayList<>();
for (BeanDefinition def : filterDefs) { for (BeanDefinition def : filterDefs) {
ComponentDescriptor scannedComponent = scanAndPersistComponent(def, type); result.add(scanAndPersistComponent(def, type));
result.add(scannedComponent);
} }
return result; return result;
} }
@ -93,24 +96,36 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe
Class<?> clazz = Class.forName(clazzName); Class<?> clazz = Class.forName(clazzName);
String descriptorResourceName; String descriptorResourceName;
switch (type) { switch (type) {
case ENRICHMENT:
EnrichmentNode enrichmentAnnotation = clazz.getAnnotation(EnrichmentNode.class);
scannedComponent.setName(enrichmentAnnotation.name());
scannedComponent.setScope(enrichmentAnnotation.scope());
descriptorResourceName = enrichmentAnnotation.descriptor();
break;
case FILTER: case FILTER:
Filter filterAnnotation = clazz.getAnnotation(Filter.class); FilterNode filterAnnotation = clazz.getAnnotation(FilterNode.class);
scannedComponent.setName(filterAnnotation.name()); scannedComponent.setName(filterAnnotation.name());
scannedComponent.setScope(filterAnnotation.scope()); scannedComponent.setScope(filterAnnotation.scope());
descriptorResourceName = filterAnnotation.descriptor(); descriptorResourceName = filterAnnotation.descriptor();
break; break;
case PROCESSOR: case TRANSFORMATION:
Processor processorAnnotation = clazz.getAnnotation(Processor.class); TransformationNode trAnnotation = clazz.getAnnotation(TransformationNode.class);
scannedComponent.setName(processorAnnotation.name()); scannedComponent.setName(trAnnotation.name());
scannedComponent.setScope(processorAnnotation.scope()); scannedComponent.setScope(trAnnotation.scope());
descriptorResourceName = processorAnnotation.descriptor(); descriptorResourceName = trAnnotation.descriptor();
break; break;
case ACTION: case ACTION:
Action actionAnnotation = clazz.getAnnotation(Action.class); ActionNode actionAnnotation = clazz.getAnnotation(ActionNode.class);
scannedComponent.setName(actionAnnotation.name()); scannedComponent.setName(actionAnnotation.name());
scannedComponent.setScope(actionAnnotation.scope()); scannedComponent.setScope(actionAnnotation.scope());
descriptorResourceName = actionAnnotation.descriptor(); descriptorResourceName = actionAnnotation.descriptor();
break; break;
case OLD_ACTION:
Action oldActionAnnotation = clazz.getAnnotation(Action.class);
scannedComponent.setName(oldActionAnnotation.name());
scannedComponent.setScope(oldActionAnnotation.scope());
descriptorResourceName = oldActionAnnotation.descriptor();
break;
case PLUGIN: case PLUGIN:
Plugin pluginAnnotation = clazz.getAnnotation(Plugin.class); Plugin pluginAnnotation = clazz.getAnnotation(Plugin.class);
scannedComponent.setName(pluginAnnotation.name()); 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()); log.error("Can't initialize plugin {}, due to missing action {}!", def.getBeanClassName(), actionClazz.getName());
return new ClassNotFoundException("Action: " + actionClazz.getName() + "is missing!"); 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()); 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!"); 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; break;
default: default:
throw new RuntimeException(type + " is not supported yet!"); throw new RuntimeException(type + " is not supported yet!");
@ -168,11 +183,15 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe
@Override @Override
public void discoverComponents() { 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); registerComponents(ComponentType.PLUGIN, Plugin.class);
@ -199,7 +218,7 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe
} }
List<ComponentDescriptor> result = new ArrayList<>(); List<ComponentDescriptor> result = new ArrayList<>();
for (String action : plugin.getActions().split(",")) { for (String action : plugin.getActions().split(",")) {
getComponent(action).ifPresent(v -> result.add(v)); getComponent(action).ifPresent(result::add);
} }
return result; return result;
} else { } else {

14
application/src/main/resources/thingsboard.yml

@ -62,7 +62,7 @@ cluster:
# Plugins configuration parameters # Plugins configuration parameters
plugins: plugins:
# Comma seperated package list used during classpath scanning for 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 # JWT Token parameters
security.jwt: security.jwt:
@ -215,12 +215,12 @@ actors:
termination.delay: "${ACTORS_RULE_TERMINATION_DELAY:30000}" termination.delay: "${ACTORS_RULE_TERMINATION_DELAY:30000}"
# Errors for particular actor are persisted once per specified amount of milliseconds # Errors for particular actor are persisted once per specified amount of milliseconds
error_persist_frequency: "${ACTORS_RULE_ERROR_FREQUENCY:3000}" error_persist_frequency: "${ACTORS_RULE_ERROR_FREQUENCY:3000}"
chain: chain:
# Errors for particular actor are persisted once per specified amount of milliseconds # Errors for particular actor are persisted once per specified amount of milliseconds
error_persist_frequency: "${ACTORS_RULE_CHAIN_ERROR_FREQUENCY:3000}" error_persist_frequency: "${ACTORS_RULE_CHAIN_ERROR_FREQUENCY:3000}"
node: node:
# Errors for particular actor are persisted once per specified amount of milliseconds # Errors for particular actor are persisted once per specified amount of milliseconds
error_persist_frequency: "${ACTORS_RULE_NODE_ERROR_FREQUENCY:3000}" error_persist_frequency: "${ACTORS_RULE_NODE_ERROR_FREQUENCY:3000}"
statistics: statistics:
# Enable/disable actor statistics # Enable/disable actor statistics
enabled: "${ACTORS_STATISTICS_ENABLED:true}" enabled: "${ACTORS_STATISTICS_ENABLED:true}"

2
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 @Slf4j
public abstract class AbstractControllerTest { public abstract class AbstractControllerTest {
protected ObjectMapper mapper = new ObjectMapper();
protected static final String TEST_TENANT_NAME = "TEST TENANT"; protected static final String TEST_TENANT_NAME = "TEST TENANT";
protected static final String SYS_ADMIN_EMAIL = "sysadmin@thingsboard.org"; protected static final String SYS_ADMIN_EMAIL = "sysadmin@thingsboard.org";

41
application/src/test/java/org/thingsboard/server/controller/AbstractRuleEngineControllerTest.java

@ -0,0 +1,41 @@
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<Event> 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<TimePageData<Event>>() {
}, pageLink, entityId.getEntityType(), entityId.getId(), DataConstants.DEBUG, tenantId.getId());
}
}

35
application/src/test/java/org/thingsboard/server/rules/RuleEngineSqlTestSuite.java

@ -0,0 +1,35 @@
/**
* 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;
import org.junit.ClassRule;
import org.junit.extensions.cpsuite.ClasspathSuite;
import org.junit.runner.RunWith;
import org.thingsboard.server.dao.CustomSqlUnit;
import java.util.Arrays;
@RunWith(ClasspathSuite.class)
@ClasspathSuite.ClassnameFilters({
"org.thingsboard.server.rules.flow.*Test", "org.thingsboard.server.rules.lifecycle.*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");
}

156
application/src/test/java/org/thingsboard/server/rules/flow/AbstractRuleEngineFlowIntegrationTest.java

@ -0,0 +1,156 @@
/**
* Copyright © 2016-2018 The Thingsboard Authors
* <p>
* 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
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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 java.util.Collections;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
/**
* @author Valerii Sosliuk
*/
@Slf4j
public abstract class AbstractRuleEngineFlowIntegrationTest extends AbstractRuleEngineControllerTest {
private static final String MQTT_URL = "tcp://localhost:1883";
private static final Long TIME_TO_HANDLE_REQUEST = 500L;
private Tenant savedTenant;
private User tenantAdmin;
@Autowired
private ActorService actorService;
@Autowired
private 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 testSimpleRuleChainCreation() 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())));
// 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<Event> 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());
}
}

14
application/src/main/java/org/thingsboard/server/actors/rule/RuleActorChain.java → application/src/test/java/org/thingsboard/server/rules/flow/RuleEngineFlowSqlIntegrationTest.java

@ -13,12 +13,14 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.thingsboard.server.actors.rule; package org.thingsboard.server.rules.flow;
public interface RuleActorChain { import org.thingsboard.server.dao.service.DaoSqlTest;
import org.thingsboard.server.mqtt.rpc.AbstractMqttServerSideRpcIntegrationTest;
int size();
RuleActorMetaData getRuleActorMd(int index);
/**
* Created by Valerii Sosliuk on 8/22/2017.
*/
@DaoSqlTest
public class RuleEngineFlowSqlIntegrationTest extends AbstractRuleEngineFlowIntegrationTest {
} }

7
common/data/src/main/java/org/thingsboard/server/common/data/DataConstants.java

@ -37,9 +37,12 @@ public class DataConstants {
public static final String ERROR = "ERROR"; public static final String ERROR = "ERROR";
public static final String LC_EVENT = "LC_EVENT"; public static final String LC_EVENT = "LC_EVENT";
public static final String STATS = "STATS"; public static final String STATS = "STATS";
public static final String RULE_CHAIN_DEBUG = "DEBUG_RULE_CHAIN"; public static final String DEBUG = "DEBUG";
public static final String RULE_NODE_DEBUG = "DEBUG_RULE_NODE";
public static final String ONEWAY = "ONEWAY"; public static final String ONEWAY = "ONEWAY";
public static final String TWOWAY = "TWOWAY"; public static final String TWOWAY = "TWOWAY";
public static final String IN = "IN";
public static final String OUT = "OUT";
} }

2
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 { public enum ComponentType {
ENRICHMENT, FILTER, PROCESSOR, TRANSFORMATION, ACTION, PLUGIN ENRICHMENT, FILTER, TRANSFORMATION, ACTION, OLD_ACTION, PLUGIN
} }

1
common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChain.java

@ -38,6 +38,7 @@ public class RuleChain extends SearchTextBasedWithAdditionalInfo<RuleChainId> im
private String name; private String name;
private RuleNodeId firstRuleNodeId; private RuleNodeId firstRuleNodeId;
private boolean root; private boolean root;
private boolean debugMode;
private transient JsonNode configuration; private transient JsonNode configuration;
@JsonIgnore @JsonIgnore
private byte[] configurationBytes; private byte[] configurationBytes;

1
common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleNode.java

@ -34,6 +34,7 @@ public class RuleNode extends SearchTextBasedWithAdditionalInfo<RuleNodeId> impl
private String type; private String type;
private String name; private String name;
private boolean debugMode;
private transient JsonNode configuration; private transient JsonNode configuration;
@JsonIgnore @JsonIgnore
private byte[] configurationBytes; private byte[] configurationBytes;

20
common/message/src/main/java/org/thingsboard/server/common/msg/MsgType.java

@ -1,3 +1,18 @@
/**
* 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; package org.thingsboard.server.common.msg;
/** /**
@ -34,4 +49,9 @@ public enum MsgType {
*/ */
RULE_TO_RULE_CHAIN_TELL_NEXT_MSG, 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,
} }

15
common/message/src/main/java/org/thingsboard/server/common/msg/TbActorMsg.java

@ -1,3 +1,18 @@
/**
* 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; package org.thingsboard.server.common.msg;
/** /**

30
common/message/src/main/java/org/thingsboard/server/common/msg/TbMsg.java

@ -1,12 +1,12 @@
/** /**
* Copyright © 2016-2018 The Thingsboard Authors * Copyright © 2016-2018 The Thingsboard Authors
* <p> *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* <p> *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* <p> *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -17,6 +17,7 @@ package org.thingsboard.server.common.msg;
import com.google.protobuf.ByteString; import com.google.protobuf.ByteString;
import com.google.protobuf.InvalidProtocolBufferException; import com.google.protobuf.InvalidProtocolBufferException;
import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.EntityIdFactory; import org.thingsboard.server.common.data.id.EntityIdFactory;
@ -30,6 +31,7 @@ import java.util.UUID;
* Created by ashvayka on 13.01.18. * Created by ashvayka on 13.01.18.
*/ */
@Data @Data
@AllArgsConstructor
public final class TbMsg implements Serializable { public final class TbMsg implements Serializable {
private final UUID id; private final UUID id;
@ -39,6 +41,15 @@ public final class TbMsg implements Serializable {
private final TbMsgDataType dataType; private final TbMsgDataType dataType;
private final byte[] data; private final byte[] data;
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) { public static ByteBuffer toBytes(TbMsg msg) {
MsgProtos.TbMsgProto.Builder builder = MsgProtos.TbMsgProto.newBuilder(); MsgProtos.TbMsgProto.Builder builder = MsgProtos.TbMsgProto.newBuilder();
builder.setId(msg.getId().toString()); builder.setId(msg.getId().toString());
@ -61,9 +72,7 @@ public final class TbMsg implements Serializable {
public static TbMsg fromBytes(ByteBuffer buffer) { public static TbMsg fromBytes(ByteBuffer buffer) {
try { try {
MsgProtos.TbMsgProto proto = MsgProtos.TbMsgProto.parseFrom(buffer.array()); MsgProtos.TbMsgProto proto = MsgProtos.TbMsgProto.parseFrom(buffer.array());
TbMsgMetaData metaData = new TbMsgMetaData(); TbMsgMetaData metaData = new TbMsgMetaData(proto.getMetaData().getDataMap());
metaData.setData(proto.getMetaData().getDataMap());
EntityId entityId = EntityIdFactory.getByTypeAndId(proto.getEntityType(), proto.getEntityId()); EntityId entityId = EntityIdFactory.getByTypeAndId(proto.getEntityType(), proto.getEntityId());
TbMsgDataType dataType = TbMsgDataType.values()[proto.getDataType()]; TbMsgDataType dataType = TbMsgDataType.values()[proto.getDataType()];
return new TbMsg(UUID.fromString(proto.getId()), proto.getType(), entityId, metaData, dataType, proto.getData().toByteArray()); return new TbMsg(UUID.fromString(proto.getId()), proto.getType(), entityId, metaData, dataType, proto.getData().toByteArray());
@ -71,4 +80,11 @@ public final class TbMsg implements Serializable {
throw new IllegalStateException("Could not parse protobuf for TbMsg", 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);
}
} }

15
common/message/src/main/java/org/thingsboard/server/common/msg/TbMsgDataType.java

@ -1,3 +1,18 @@
/**
* 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; package org.thingsboard.server.common.msg;
/** /**

19
common/message/src/main/java/org/thingsboard/server/common/msg/TbMsgMetaData.java

@ -1,12 +1,12 @@
/** /**
* Copyright © 2016-2018 The Thingsboard Authors * Copyright © 2016-2018 The Thingsboard Authors
* * <p>
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* * <p>
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* * <p>
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -15,9 +15,12 @@
*/ */
package org.thingsboard.server.common.msg; package org.thingsboard.server.common.msg;
import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable; import java.io.Serializable;
import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
@ -25,10 +28,15 @@ import java.util.concurrent.ConcurrentHashMap;
* Created by ashvayka on 13.01.18. * Created by ashvayka on 13.01.18.
*/ */
@Data @Data
@NoArgsConstructor
public final class TbMsgMetaData implements Serializable { public final class TbMsgMetaData implements Serializable {
private Map<String, String> data = new ConcurrentHashMap<>(); private Map<String, String> data = new ConcurrentHashMap<>();
TbMsgMetaData(Map<String, String> data) {
this.data = data;
}
public String getValue(String key) { public String getValue(String key) {
return data.get(key); return data.get(key);
} }
@ -37,4 +45,7 @@ public final class TbMsgMetaData implements Serializable {
data.put(key, value); data.put(key, value);
} }
public TbMsgMetaData copy() {
return new TbMsgMetaData(new ConcurrentHashMap<>(data));
}
} }

8
common/message/src/main/java/org/thingsboard/server/common/msg/plugin/ComponentLifecycleMsg.java

@ -1,12 +1,12 @@
/** /**
* Copyright © 2016-2018 The Thingsboard Authors * Copyright © 2016-2018 The Thingsboard Authors
* <p> *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* <p> *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* <p> *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

15
common/message/src/main/java/org/thingsboard/server/common/msg/system/ServiceToRuleEngineMsg.java

@ -1,3 +1,18 @@
/**
* 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.system; package org.thingsboard.server.common.msg.system;
import lombok.Data; import lombok.Data;

2
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_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 EVENT_BY_ID_VIEW_NAME = "event_by_id";
public static final String DEBUG_MODE = "debug_mode";
/** /**
* Cassandra rule chain constants. * Cassandra rule chain constants.
*/ */

16
dao/src/main/java/org/thingsboard/server/dao/model/nosql/RuleChainEntity.java

@ -1,12 +1,12 @@
/** /**
* Copyright © 2016-2018 The Thingsboard Authors * Copyright © 2016-2018 The Thingsboard Authors
* * <p>
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* * <p>
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* * <p>
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -22,6 +22,8 @@ import com.datastax.driver.mapping.annotations.PartitionKey;
import com.datastax.driver.mapping.annotations.Table; import com.datastax.driver.mapping.annotations.Table;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString; import lombok.ToString;
import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.RuleNodeId; import org.thingsboard.server.common.data.id.RuleNodeId;
@ -54,6 +56,10 @@ public class RuleChainEntity implements SearchTextEntity<RuleChain> {
private UUID firstRuleNodeId; private UUID firstRuleNodeId;
@Column(name = RULE_CHAIN_ROOT_PROPERTY) @Column(name = RULE_CHAIN_ROOT_PROPERTY)
private boolean root; private boolean root;
@Getter
@Setter
@Column(name = DEBUG_MODE)
private boolean debugMode;
@Column(name = RULE_CHAIN_CONFIGURATION_PROPERTY, codec = JsonCodec.class) @Column(name = RULE_CHAIN_CONFIGURATION_PROPERTY, codec = JsonCodec.class)
private JsonNode configuration; private JsonNode configuration;
@Column(name = ADDITIONAL_INFO_PROPERTY, codec = JsonCodec.class) @Column(name = ADDITIONAL_INFO_PROPERTY, codec = JsonCodec.class)
@ -71,6 +77,7 @@ public class RuleChainEntity implements SearchTextEntity<RuleChain> {
this.searchText = ruleChain.getName(); this.searchText = ruleChain.getName();
this.firstRuleNodeId = DaoUtil.getId(ruleChain.getFirstRuleNodeId()); this.firstRuleNodeId = DaoUtil.getId(ruleChain.getFirstRuleNodeId());
this.root = ruleChain.isRoot(); this.root = ruleChain.isRoot();
this.debugMode = ruleChain.isDebugMode();
this.configuration = ruleChain.getConfiguration(); this.configuration = ruleChain.getConfiguration();
this.additionalInfo = ruleChain.getAdditionalInfo(); this.additionalInfo = ruleChain.getAdditionalInfo();
} }
@ -157,6 +164,7 @@ public class RuleChainEntity implements SearchTextEntity<RuleChain> {
ruleChain.setFirstRuleNodeId(new RuleNodeId(this.firstRuleNodeId)); ruleChain.setFirstRuleNodeId(new RuleNodeId(this.firstRuleNodeId));
} }
ruleChain.setRoot(this.root); ruleChain.setRoot(this.root);
ruleChain.setDebugMode(this.debugMode);
ruleChain.setConfiguration(this.configuration); ruleChain.setConfiguration(this.configuration);
ruleChain.setAdditionalInfo(this.additionalInfo); ruleChain.setAdditionalInfo(this.additionalInfo);
return ruleChain; return ruleChain;

9
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.datastax.driver.mapping.annotations.Table;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString; import lombok.ToString;
import org.thingsboard.server.common.data.id.RuleNodeId; import org.thingsboard.server.common.data.id.RuleNodeId;
import org.thingsboard.server.common.data.rule.RuleNode; import org.thingsboard.server.common.data.rule.RuleNode;
@ -49,6 +51,11 @@ public class RuleNodeEntity implements SearchTextEntity<RuleNode> {
private JsonNode configuration; private JsonNode configuration;
@Column(name = ADDITIONAL_INFO_PROPERTY, codec = JsonCodec.class) @Column(name = ADDITIONAL_INFO_PROPERTY, codec = JsonCodec.class)
private JsonNode additionalInfo; private JsonNode additionalInfo;
@Getter
@Setter
@Column(name = DEBUG_MODE)
private boolean debugMode;
public RuleNodeEntity() { public RuleNodeEntity() {
} }
@ -59,6 +66,7 @@ public class RuleNodeEntity implements SearchTextEntity<RuleNode> {
} }
this.type = ruleNode.getType(); this.type = ruleNode.getType();
this.name = ruleNode.getName(); this.name = ruleNode.getName();
this.debugMode = ruleNode.isDebugMode();
this.searchText = ruleNode.getName(); this.searchText = ruleNode.getName();
this.configuration = ruleNode.getConfiguration(); this.configuration = ruleNode.getConfiguration();
this.additionalInfo = ruleNode.getAdditionalInfo(); this.additionalInfo = ruleNode.getAdditionalInfo();
@ -126,6 +134,7 @@ public class RuleNodeEntity implements SearchTextEntity<RuleNode> {
ruleNode.setCreatedTime(UUIDs.unixTimestamp(id)); ruleNode.setCreatedTime(UUIDs.unixTimestamp(id));
ruleNode.setType(this.type); ruleNode.setType(this.type);
ruleNode.setName(this.name); ruleNode.setName(this.name);
ruleNode.setDebugMode(this.debugMode);
ruleNode.setConfiguration(this.configuration); ruleNode.setConfiguration(this.configuration);
ruleNode.setAdditionalInfo(this.additionalInfo); ruleNode.setAdditionalInfo(this.additionalInfo);
return ruleNode; return ruleNode;

5
dao/src/main/java/org/thingsboard/server/dao/model/sql/RuleChainEntity.java

@ -58,6 +58,9 @@ public class RuleChainEntity extends BaseSqlEntity<RuleChain> implements SearchT
@Column(name = ModelConstants.RULE_CHAIN_ROOT_PROPERTY) @Column(name = ModelConstants.RULE_CHAIN_ROOT_PROPERTY)
private boolean root; private boolean root;
@Column(name = ModelConstants.DEBUG_MODE)
private boolean debugMode;
@Type(type = "json") @Type(type = "json")
@Column(name = ModelConstants.RULE_CHAIN_CONFIGURATION_PROPERTY) @Column(name = ModelConstants.RULE_CHAIN_CONFIGURATION_PROPERTY)
private JsonNode configuration; private JsonNode configuration;
@ -80,6 +83,7 @@ public class RuleChainEntity extends BaseSqlEntity<RuleChain> implements SearchT
this.firstRuleNodeId = UUIDConverter.fromTimeUUID(ruleChain.getFirstRuleNodeId().getId()); this.firstRuleNodeId = UUIDConverter.fromTimeUUID(ruleChain.getFirstRuleNodeId().getId());
} }
this.root = ruleChain.isRoot(); this.root = ruleChain.isRoot();
this.debugMode = ruleChain.isDebugMode();
this.configuration = ruleChain.getConfiguration(); this.configuration = ruleChain.getConfiguration();
this.additionalInfo = ruleChain.getAdditionalInfo(); this.additionalInfo = ruleChain.getAdditionalInfo();
} }
@ -104,6 +108,7 @@ public class RuleChainEntity extends BaseSqlEntity<RuleChain> implements SearchT
ruleChain.setFirstRuleNodeId(new RuleNodeId(UUIDConverter.fromString(firstRuleNodeId))); ruleChain.setFirstRuleNodeId(new RuleNodeId(UUIDConverter.fromString(firstRuleNodeId)));
} }
ruleChain.setRoot(root); ruleChain.setRoot(root);
ruleChain.setDebugMode(debugMode);
ruleChain.setConfiguration(configuration); ruleChain.setConfiguration(configuration);
ruleChain.setAdditionalInfo(additionalInfo); ruleChain.setAdditionalInfo(additionalInfo);
return ruleChain; return ruleChain;

5
dao/src/main/java/org/thingsboard/server/dao/model/sql/RuleNodeEntity.java

@ -56,6 +56,9 @@ public class RuleNodeEntity extends BaseSqlEntity<RuleNode> implements SearchTex
@Column(name = ModelConstants.ADDITIONAL_INFO_PROPERTY) @Column(name = ModelConstants.ADDITIONAL_INFO_PROPERTY)
private JsonNode additionalInfo; private JsonNode additionalInfo;
@Column(name = ModelConstants.DEBUG_MODE)
private boolean debugMode;
public RuleNodeEntity() { public RuleNodeEntity() {
} }
@ -65,6 +68,7 @@ public class RuleNodeEntity extends BaseSqlEntity<RuleNode> implements SearchTex
} }
this.type = ruleNode.getType(); this.type = ruleNode.getType();
this.name = ruleNode.getName(); this.name = ruleNode.getName();
this.debugMode = ruleNode.isDebugMode();
this.searchText = ruleNode.getName(); this.searchText = ruleNode.getName();
this.configuration = ruleNode.getConfiguration(); this.configuration = ruleNode.getConfiguration();
this.additionalInfo = ruleNode.getAdditionalInfo(); this.additionalInfo = ruleNode.getAdditionalInfo();
@ -86,6 +90,7 @@ public class RuleNodeEntity extends BaseSqlEntity<RuleNode> implements SearchTex
ruleNode.setCreatedTime(UUIDs.unixTimestamp(getId())); ruleNode.setCreatedTime(UUIDs.unixTimestamp(getId()));
ruleNode.setType(type); ruleNode.setType(type);
ruleNode.setName(name); ruleNode.setName(name);
ruleNode.setDebugMode(debugMode);
ruleNode.setConfiguration(configuration); ruleNode.setConfiguration(configuration);
ruleNode.setAdditionalInfo(additionalInfo); ruleNode.setAdditionalInfo(additionalInfo);
return ruleNode; return ruleNode;

3
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.Bean;
import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.ComponentScan;
import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbMsgDataType;
import org.thingsboard.server.common.msg.TbMsgMetaData; import org.thingsboard.server.common.msg.TbMsgMetaData;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@ -125,7 +126,7 @@ public class QueueBenchmark implements CommandLineRunner {
TbMsgMetaData metaData = new TbMsgMetaData(); TbMsgMetaData metaData = new TbMsgMetaData();
metaData.putValue("key", "value"); metaData.putValue("key", "value");
String dataStr = "someContent"; 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 @Bean

8
dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java

@ -219,6 +219,12 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
return ruleChainDao.findById(ruleChainId.getId()); 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 @Override
public ListenableFuture<RuleChain> findRuleChainByIdAsync(RuleChainId ruleChainId) { public ListenableFuture<RuleChain> findRuleChainByIdAsync(RuleChainId ruleChainId) {
Validator.validateId(ruleChainId, "Incorrect rule chain id for search request."); Validator.validateId(ruleChainId, "Incorrect rule chain id for search request.");
@ -325,7 +331,7 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
} }
if (ruleChain.isRoot()) { if (ruleChain.isRoot()) {
RuleChain rootRuleChain = getRootTenantRuleChain(ruleChain.getTenantId()); 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!"); throw new DataValidationException("Another root rule chain is present in scope of current tenant!");
} }
} }

2
dao/src/main/java/org/thingsboard/server/dao/rule/RuleChainService.java

@ -42,6 +42,8 @@ public interface RuleChainService {
RuleChain findRuleChainById(RuleChainId ruleChainId); RuleChain findRuleChainById(RuleChainId ruleChainId);
RuleNode findRuleNodeById(RuleNodeId ruleNodeId);
ListenableFuture<RuleChain> findRuleChainByIdAsync(RuleChainId ruleChainId); ListenableFuture<RuleChain> findRuleChainByIdAsync(RuleChainId ruleChainId);
RuleChain getRootTenantRuleChain(TenantId tenantId); RuleChain getRootTenantRuleChain(TenantId tenantId);

2
dao/src/main/resources/cassandra/schema.cql

@ -669,6 +669,7 @@ CREATE TABLE IF NOT EXISTS thingsboard.rule_chain (
search_text text, search_text text,
first_rule_node_id uuid, first_rule_node_id uuid,
root boolean, root boolean,
debug_mode boolean,
configuration text, configuration text,
additional_info text, additional_info text,
PRIMARY KEY (id, tenant_id) PRIMARY KEY (id, tenant_id)
@ -685,6 +686,7 @@ CREATE TABLE IF NOT EXISTS thingsboard.rule_node (
id uuid, id uuid,
type text, type text,
name text, name text,
debug_mode boolean,
search_text text, search_text text,
configuration text, configuration text,
additional_info text, additional_info text,

2
dao/src/main/resources/sql/schema.sql

@ -263,6 +263,7 @@ CREATE TABLE IF NOT EXISTS rule_chain (
name varchar(255), name varchar(255),
first_rule_node_id varchar(31), first_rule_node_id varchar(31),
root boolean, root boolean,
debug_mode boolean,
search_text varchar(255), search_text varchar(255),
tenant_id varchar(31) tenant_id varchar(31)
); );
@ -273,5 +274,6 @@ CREATE TABLE IF NOT EXISTS rule_node (
configuration varchar(10000000), configuration varchar(10000000),
type varchar(255), type varchar(255),
name varchar(255), name varchar(255),
debug_mode boolean,
search_text varchar(255) search_text varchar(255)
); );

6
dao/src/test/java/org/thingsboard/server/dao/service/AbstractServiceTest.java

@ -217,10 +217,10 @@ public abstract class AbstractServiceTest {
ruleMetaData.setWeight(weight); ruleMetaData.setWeight(weight);
ruleMetaData.setPluginToken(pluginToken); 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")); "org.thingsboard.component.ActionTest", "TestJsonDescriptor.json", "TestJsonData.json"));
ruleMetaData.setProcessor(createNode(ComponentScope.TENANT, ComponentType.PROCESSOR, // ruleMetaData.setProcessor(createNode(ComponentScope.TENANT, ComponentType.PROCESSOR,
"org.thingsboard.component.ProcessorTest", "TestJsonDescriptor.json", "TestJsonData.json")); // "org.thingsboard.component.ProcessorTest", "TestJsonDescriptor.json", "TestJsonData.json"));
ruleMetaData.setFilters(mapper.createArrayNode().add( ruleMetaData.setFilters(mapper.createArrayNode().add(
createNode(ComponentScope.TENANT, ComponentType.FILTER, createNode(ComponentScope.TENANT, ComponentType.FILTER,
"org.thingsboard.component.FilterTest", "TestJsonDescriptor.json", "TestJsonData.json") "org.thingsboard.component.FilterTest", "TestJsonDescriptor.json", "TestJsonData.json")

4
dao/src/test/java/org/thingsboard/server/dao/service/queue/cassandra/UnprocessedMsgFilterTest.java

@ -33,8 +33,8 @@ public class UnprocessedMsgFilterTest {
public void acknowledgedMsgsAreFilteredOut() { public void acknowledgedMsgsAreFilteredOut() {
UUID id1 = UUID.randomUUID(); UUID id1 = UUID.randomUUID();
UUID id2 = UUID.randomUUID(); UUID id2 = UUID.randomUUID();
TbMsg msg1 = new TbMsg(id1, "T", null, null, null); TbMsg msg1 = new TbMsg(id1, "T", null, null, null, null);
TbMsg msg2 = new TbMsg(id2, "T", null, null, null); TbMsg msg2 = new TbMsg(id2, "T", null, null, null, null);
List<TbMsg> msgs = Lists.newArrayList(msg1, msg2); List<TbMsg> msgs = Lists.newArrayList(msg1, msg2);
List<MsgAck> acks = Lists.newArrayList(new MsgAck(id2, UUID.randomUUID(), 1L, 1L)); List<MsgAck> acks = Lists.newArrayList(new MsgAck(id2, UUID.randomUUID(), 1L, 1L));
Collection<TbMsg> actual = msgFilter.filter(msgs, acks); Collection<TbMsg> actual = msgFilter.filter(msgs, acks);

7
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.springframework.beans.factory.annotation.Autowired;
import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbMsgDataType;
import org.thingsboard.server.common.msg.TbMsgMetaData; import org.thingsboard.server.common.msg.TbMsgMetaData;
import org.thingsboard.server.dao.service.AbstractServiceTest; import org.thingsboard.server.dao.service.AbstractServiceTest;
import org.thingsboard.server.dao.service.DaoNoSqlTest; import org.thingsboard.server.dao.service.DaoNoSqlTest;
@ -44,7 +45,7 @@ public class CassandraMsgRepositoryTest extends AbstractServiceTest {
@Test @Test
public void msgCanBeSavedAndRead() throws ExecutionException, InterruptedException { 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(); UUID nodeId = UUIDs.timeBased();
ListenableFuture<Void> future = msgRepository.save(msg, nodeId, 1L, 1L, 1L); ListenableFuture<Void> future = msgRepository.save(msg, nodeId, 1L, 1L, 1L);
future.get(); future.get();
@ -54,7 +55,7 @@ public class CassandraMsgRepositoryTest extends AbstractServiceTest {
@Test @Test
public void expiredMsgsAreNotReturned() throws ExecutionException, InterruptedException { 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(); UUID nodeId = UUIDs.timeBased();
ListenableFuture<Void> future = msgRepository.save(msg, nodeId, 2L, 2L, 2L); ListenableFuture<Void> future = msgRepository.save(msg, nodeId, 2L, 2L, 2L);
future.get(); future.get();
@ -67,7 +68,7 @@ public class CassandraMsgRepositoryTest extends AbstractServiceTest {
TbMsgMetaData metaData = new TbMsgMetaData(); TbMsgMetaData metaData = new TbMsgMetaData();
metaData.putValue("key", "value"); metaData.putValue("key", "value");
String dataStr = "someContent"; 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(); UUID nodeId = UUIDs.timeBased();
ListenableFuture<Void> future = msgRepository.save(msg, nodeId, 1L, 1L, 1L); ListenableFuture<Void> future = msgRepository.save(msg, nodeId, 1L, 1L, 1L);
future.get(); future.get();

5
pom.xml

@ -378,6 +378,11 @@
<artifactId>rule-engine-api</artifactId> <artifactId>rule-engine-api</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.thingsboard.rule-engine</groupId>
<artifactId>rule-engine-components</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.thingsboard.common</groupId> <groupId>org.thingsboard.common</groupId>
<artifactId>message</artifactId> <artifactId>message</artifactId>

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

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

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

2
rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java

@ -58,8 +58,6 @@ public interface TbContext {
UserService getUserService(); UserService getUserService();
RuleService getRuleService();
PluginService getPluginService(); PluginService getPluginService();
AssetService getAssetService(); AssetService getAssetService();

4
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. * Created by ashvayka on 19.01.18.
*/ */
@Data @Data
public class TbNodeConfiguration { public final class TbNodeConfiguration {
private JsonNode data; private final JsonNode data;
} }

2
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. * Created by ashvayka on 19.01.18.
*/ */
public class TbNodeState { public final class TbNodeState {
} }

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

2
rule-engine/rule-engine-api/src/main/resources/EmptyNodeDescriptor.json

@ -0,0 +1,2 @@
{
}

5
rule-engine/rule-engine-components/pom.xml

@ -88,11 +88,6 @@
<artifactId>mockito-all</artifactId> <artifactId>mockito-all</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>RELEASE</version>
</dependency>
<!--<dependency>--> <!--<dependency>-->
<!--<groupId>org.springframework.boot</groupId>--> <!--<groupId>org.springframework.boot</groupId>-->

26
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetAttributesNode.java

@ -1,12 +1,12 @@
/** /**
* Copyright © 2016-2018 The Thingsboard Authors * Copyright © 2016-2018 The Thingsboard Authors
* * <p>
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* * <p>
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* * <p>
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -15,6 +15,7 @@
*/ */
package org.thingsboard.rule.engine.metadata; package org.thingsboard.rule.engine.metadata;
import com.google.common.base.Function;
import com.google.common.util.concurrent.AsyncFunction; import com.google.common.util.concurrent.AsyncFunction;
import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
@ -33,6 +34,7 @@ import static org.thingsboard.server.common.data.DataConstants.*;
* Created by ashvayka on 19.01.18. * Created by ashvayka on 19.01.18.
*/ */
@Slf4j @Slf4j
@EnrichmentNode(name = "Get Attributes Node")
public class TbGetAttributesNode implements TbNode { public class TbGetAttributesNode implements TbNode {
private TbGetAttributesNodeConfiguration config; private TbGetAttributesNodeConfiguration config;
@ -52,14 +54,20 @@ public class TbGetAttributesNode implements TbNode {
withCallback(future, i -> ctx.tellNext(msg), t -> ctx.tellError(msg, t)); withCallback(future, i -> ctx.tellNext(msg), t -> ctx.tellError(msg, t));
} }
private ListenableFuture<Void> putAttributesAsync(TbMsg msg, List<AttributeKvEntry> attributes, String prefix) { private Void putAttributesAsync(TbMsg msg, List<AttributeKvEntry> attributes, String prefix) {
attributes.forEach(r -> msg.getMetaData().putValue(prefix + r.getKey(), r.getValueAsString())); if (attributes != null) {
return Futures.immediateFuture(null); attributes.forEach(r -> msg.getMetaData().putValue(prefix + r.getKey(), r.getValueAsString()));
}
return null;
} }
private ListenableFuture<Void> putAttrAsync(TbContext ctx, TbMsg msg, String scope, List<String> attributes, String prefix) { private ListenableFuture<Void> putAttrAsync(TbContext ctx, TbMsg msg, String scope, List<String> attributes, String prefix) {
return Futures.transform(ctx.getAttributesService().find(msg.getOriginator(), scope, attributes), if (attributes != null) {
(AsyncFunction<List<AttributeKvEntry>, Void>) i -> putAttributesAsync(msg, i, prefix)); return Futures.transform(ctx.getAttributesService().find(msg.getOriginator(), scope, attributes),
(Function<List<AttributeKvEntry>, Void>) i -> putAttributesAsync(msg, i, prefix));
} else {
return Futures.immediateFuture(null);
}
} }

2
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetCustomerAttributeNode.java

@ -18,11 +18,13 @@ package org.thingsboard.rule.engine.metadata;
import com.google.common.util.concurrent.AsyncFunction; import com.google.common.util.concurrent.AsyncFunction;
import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture; 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.api.TbContext;
import org.thingsboard.rule.engine.api.TbNodeException; import org.thingsboard.rule.engine.api.TbNodeException;
import org.thingsboard.server.common.data.HasCustomerId; import org.thingsboard.server.common.data.HasCustomerId;
import org.thingsboard.server.common.data.id.*; import org.thingsboard.server.common.data.id.*;
@EnrichmentNode(name="Get Customer Attributes Node")
public class TbGetCustomerAttributeNode extends TbEntityGetAttrNode<CustomerId> { public class TbGetCustomerAttributeNode extends TbEntityGetAttrNode<CustomerId> {
@Override @Override

6
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetRelatedAttributeNode.java

@ -20,10 +20,7 @@ import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.CollectionUtils;
import org.thingsboard.rule.engine.TbNodeUtils; import org.thingsboard.rule.engine.TbNodeUtils;
import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.*;
import org.thingsboard.rule.engine.api.TbNodeConfiguration;
import org.thingsboard.rule.engine.api.TbNodeException;
import org.thingsboard.rule.engine.api.TbNodeState;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.relation.EntitySearchDirection; import org.thingsboard.server.common.data.relation.EntitySearchDirection;
@ -33,6 +30,7 @@ import java.util.List;
import static org.thingsboard.server.common.data.relation.RelationTypeGroup.COMMON; import static org.thingsboard.server.common.data.relation.RelationTypeGroup.COMMON;
@EnrichmentNode(name="Get Related Entity Attributes Node")
public class TbGetRelatedAttributeNode extends TbEntityGetAttrNode<EntityId> { public class TbGetRelatedAttributeNode extends TbEntityGetAttrNode<EntityId> {
private TbGetRelatedAttrNodeConfiguration config; private TbGetRelatedAttrNodeConfiguration config;

4
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTenantAttributeNode.java

@ -19,6 +19,7 @@ import com.google.common.util.concurrent.AsyncFunction;
import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.thingsboard.rule.engine.api.EnrichmentNode;
import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.TbContext;
import org.thingsboard.rule.engine.api.TbNodeException; import org.thingsboard.rule.engine.api.TbNodeException;
import org.thingsboard.server.common.data.HasTenantId; import org.thingsboard.server.common.data.HasTenantId;
@ -26,6 +27,7 @@ import org.thingsboard.server.common.data.alarm.AlarmId;
import org.thingsboard.server.common.data.id.*; import org.thingsboard.server.common.data.id.*;
@Slf4j @Slf4j
@EnrichmentNode(name="Get Tenant Attributes Node")
public class TbGetTenantAttributeNode extends TbEntityGetAttrNode<TenantId> { public class TbGetTenantAttributeNode extends TbEntityGetAttrNode<TenantId> {
@Override @Override
@ -38,8 +40,6 @@ public class TbGetTenantAttributeNode extends TbEntityGetAttrNode<TenantId> {
return getTenantAsync(ctx.getCustomerService().findCustomerByIdAsync((CustomerId) originator)); return getTenantAsync(ctx.getCustomerService().findCustomerByIdAsync((CustomerId) originator));
case USER: case USER:
return getTenantAsync(ctx.getUserService().findUserByIdAsync((UserId) originator)); return getTenantAsync(ctx.getUserService().findUserByIdAsync((UserId) originator));
case RULE:
return getTenantAsync(ctx.getRuleService().findRuleByIdAsync((RuleId) originator));
case PLUGIN: case PLUGIN:
return getTenantAsync(ctx.getPluginService().findPluginByIdAsync((PluginId) originator)); return getTenantAsync(ctx.getPluginService().findPluginByIdAsync((PluginId) originator));
case ASSET: case ASSET:

6
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.setAttrMapping(attrMapping);
config.setTelemetry(false); config.setTelemetry(false);
ObjectMapper mapper = new ObjectMapper(); ObjectMapper mapper = new ObjectMapper();
TbNodeConfiguration nodeConfiguration = new TbNodeConfiguration(); TbNodeConfiguration nodeConfiguration = new TbNodeConfiguration(mapper.valueToTree(config));
nodeConfiguration.setData(mapper.valueToTree(config));
node = new TbGetCustomerAttributeNode(); node = new TbGetCustomerAttributeNode();
node.init(nodeConfiguration, null); node.init(nodeConfiguration, null);
@ -224,8 +223,7 @@ public class TbGetCustomerAttributeNodeTest {
config.setAttrMapping(attrMapping); config.setAttrMapping(attrMapping);
config.setTelemetry(true); config.setTelemetry(true);
ObjectMapper mapper = new ObjectMapper(); ObjectMapper mapper = new ObjectMapper();
TbNodeConfiguration nodeConfiguration = new TbNodeConfiguration(); TbNodeConfiguration nodeConfiguration = new TbNodeConfiguration(mapper.valueToTree(config));
nodeConfiguration.setData(mapper.valueToTree(config));
node = new TbGetCustomerAttributeNode(); node = new TbGetCustomerAttributeNode();
node.init(nodeConfiguration, null); node.init(nodeConfiguration, null);

Loading…
Cancel
Save