diff --git a/application/pom.xml b/application/pom.xml
index 8449246059..e0b6b98d9a 100644
--- a/application/pom.xml
+++ b/application/pom.xml
@@ -20,10 +20,9 @@
4.0.0
org.thingsboard
- 1.4.1-SNAPSHOT
+ 2.0.0-SNAPSHOT
thingsboard
- org.thingsboard
application
jar
@@ -50,12 +49,12 @@
linux-x86_64
- org.thingsboard
- extensions-api
+ org.thingsboard.rule-engine
+ rule-engine-api
- org.thingsboard
- extensions-core
+ org.thingsboard.rule-engine
+ rule-engine-components
org.thingsboard.common
@@ -257,6 +256,10 @@
org.hsqldb
hsqldb
+
+ org.javadelight
+ delight-nashorn-sandbox
+
@@ -463,48 +466,6 @@
org.apache.maven.plugins
maven-dependency-plugin
-
- copy-extensions
- package
-
- copy
-
-
- ${project.build.directory}/extensions
-
-
- org.thingsboard.extensions
- extension-rabbitmq
- extension
-
-
- org.thingsboard.extensions
- extension-rest-api-call
- extension
-
-
- org.thingsboard.extensions
- extension-kafka
- extension
-
-
- org.thingsboard.extensions
- extension-mqtt
- extension
-
-
- org.thingsboard.extensions
- extension-sqs
- extension
-
-
- org.thingsboard.extensions
- extension-sns
- extension
-
-
-
-
copy-winsw-service
package
diff --git a/application/src/main/conf/thingsboard.conf b/application/src/main/conf/thingsboard.conf
index a6e404d5ad..2baee7de41 100644
--- a/application/src/main/conf/thingsboard.conf
+++ b/application/src/main/conf/thingsboard.conf
@@ -14,7 +14,7 @@
# limitations under the License.
#
-export JAVA_OPTS="$JAVA_OPTS -Dplatform=@pkg.platform@"
+export JAVA_OPTS="$JAVA_OPTS -Dplatform=@pkg.platform@ -Dinstall.data_dir=@pkg.installFolder@"
export LOG_FILENAME=${pkg.name}.out
export LOADER_PATH=${pkg.installFolder}/conf,${pkg.installFolder}/extensions
export SQL_DATA_FOLDER=${pkg.installFolder}/data/sql
diff --git a/application/src/main/data/json/demo/plugins/demo_device_messaging_rpc_plugin.json b/application/src/main/data/json/demo/plugins/demo_device_messaging_rpc_plugin.json
deleted file mode 100644
index 6181f4920c..0000000000
--- a/application/src/main/data/json/demo/plugins/demo_device_messaging_rpc_plugin.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "apiToken": "messaging",
- "name": "Demo Device Messaging RPC Plugin",
- "clazz": "org.thingsboard.server.extensions.core.plugin.messaging.DeviceMessagingPlugin",
- "publicAccess": false,
- "state": "ACTIVE",
- "configuration": {
- "maxDeviceCountPerCustomer": 1024,
- "defaultTimeout": 20000,
- "maxTimeout": 60000
- },
- "additionalInfo": null
-}
\ No newline at end of file
diff --git a/application/src/main/data/json/demo/plugins/demo_email_plugin.json b/application/src/main/data/json/demo/plugins/demo_email_plugin.json
deleted file mode 100644
index 40225f8c8c..0000000000
--- a/application/src/main/data/json/demo/plugins/demo_email_plugin.json
+++ /dev/null
@@ -1,28 +0,0 @@
-{
- "apiToken": "mail",
- "name": "Demo Email Plugin",
- "clazz": "org.thingsboard.server.extensions.core.plugin.mail.MailPlugin",
- "publicAccess": true,
- "state": "ACTIVE",
- "configuration": {
- "host": "smtp.sendgrid.net",
- "port": 2525,
- "username": "apikey",
- "password": "your_api_key",
- "otherProperties": [
- {
- "key": "mail.smtp.auth",
- "value": "true"
- },
- {
- "key": "mail.smtp.timeout",
- "value": "10000"
- },
- {
- "key": "mail.smtp.starttls.enable",
- "value": "true"
- }
- ]
- },
- "additionalInfo": null
-}
\ No newline at end of file
diff --git a/application/src/main/data/json/demo/plugins/demo_time_rpc_plugin.json b/application/src/main/data/json/demo/plugins/demo_time_rpc_plugin.json
deleted file mode 100644
index d5ac2d4469..0000000000
--- a/application/src/main/data/json/demo/plugins/demo_time_rpc_plugin.json
+++ /dev/null
@@ -1,11 +0,0 @@
-{
- "apiToken": "time",
- "name": "Demo Time RPC Plugin",
- "clazz": "org.thingsboard.server.extensions.core.plugin.time.TimePlugin",
- "publicAccess": false,
- "state": "ACTIVE",
- "configuration": {
- "timeFormat": "yyyy MM dd HH:mm:ss.SSS"
- },
- "additionalInfo": null
-}
\ No newline at end of file
diff --git a/application/src/main/data/json/demo/rules/demo_alarm_rule.json b/application/src/main/data/json/demo/rules/demo_alarm_rule.json
deleted file mode 100644
index 12565d8c16..0000000000
--- a/application/src/main/data/json/demo/rules/demo_alarm_rule.json
+++ /dev/null
@@ -1,46 +0,0 @@
-{
- "name": "Demo Alarm Rule",
- "state": "ACTIVE",
- "weight": 0,
- "pluginToken": "mail",
- "filters": [
- {
- "clazz": "org.thingsboard.server.extensions.core.filter.MsgTypeFilter",
- "name": "MsgTypeFilter",
- "configuration": {
- "messageTypes": [
- "POST_TELEMETRY",
- "POST_ATTRIBUTES",
- "GET_ATTRIBUTES"
- ]
- }
- },
- {
- "clazz": "org.thingsboard.server.extensions.core.filter.DeviceTelemetryFilter",
- "name": "Temperature filter",
- "configuration": {
- "filter": "typeof temperature !== 'undefined' && temperature >= 100"
- }
- }
- ],
- "processor": {
- "clazz": "org.thingsboard.server.extensions.core.processor.AlarmDeduplicationProcessor",
- "name": "AlarmDeduplicationProcessor",
- "configuration": {
- "alarmIdTemplate": "[$date.get('yyyy-MM-dd HH:mm')] Device $cs.get('serialNumber')($cs.get('model')) temperature is high!",
- "alarmBodyTemplate": "[$date.get('yyyy-MM-dd HH:mm:ss')] Device $cs.get('serialNumber')($cs.get('model')) temperature is $temp.valueAsString!"
- }
- },
- "action": {
- "clazz": "org.thingsboard.server.extensions.core.action.mail.SendMailAction",
- "name": "Send Mail Action",
- "configuration": {
- "sendFlag": "isNewAlarm",
- "fromTemplate": "thingsboard@gmail.com",
- "toTemplate": "thingsboard@gmail.com",
- "subjectTemplate": "$alarmId",
- "bodyTemplate": "$alarmBody"
- }
- },
- "additionalInfo": null
-}
\ No newline at end of file
diff --git a/application/src/main/data/json/demo/rules/demo_gettime_rpc_rule.json b/application/src/main/data/json/demo/rules/demo_gettime_rpc_rule.json
deleted file mode 100644
index 391c121ca2..0000000000
--- a/application/src/main/data/json/demo/rules/demo_gettime_rpc_rule.json
+++ /dev/null
@@ -1,35 +0,0 @@
-{
- "name": "Demo getTime RPC Rule",
- "state": "ACTIVE",
- "weight": 0,
- "pluginToken": "time",
- "filters": [
- {
- "configuration": {
- "messageTypes": [
- "RPC_REQUEST"
- ]
- },
- "name": "RPC Request Filter",
- "clazz": "org.thingsboard.server.extensions.core.filter.MsgTypeFilter"
- },
- {
- "configuration": {
- "methodNames": [
- {
- "name": "getTime"
- }
- ]
- },
- "name": "getTime method filter",
- "clazz": "org.thingsboard.server.extensions.core.filter.MethodNameFilter"
- }
- ],
- "processor": null,
- "action": {
- "configuration": {},
- "clazz": "org.thingsboard.server.extensions.core.action.rpc.RpcPluginAction",
- "name": "getTimeAction"
- },
- "additionalInfo": null
-}
\ No newline at end of file
diff --git a/application/src/main/data/json/demo/rules/demo_messaging_rpc_rule.json b/application/src/main/data/json/demo/rules/demo_messaging_rpc_rule.json
deleted file mode 100644
index 50021e4bde..0000000000
--- a/application/src/main/data/json/demo/rules/demo_messaging_rpc_rule.json
+++ /dev/null
@@ -1,38 +0,0 @@
-{
- "name": "Demo Messaging RPC Rule",
- "state": "ACTIVE",
- "weight": 0,
- "pluginToken": "messaging",
- "filters": [
- {
- "configuration": {
- "messageTypes": [
- "RPC_REQUEST"
- ]
- },
- "name": "RPC Request Filter",
- "clazz": "org.thingsboard.server.extensions.core.filter.MsgTypeFilter"
- },
- {
- "configuration": {
- "methodNames": [
- {
- "name": "getDevices"
- },
- {
- "name": "sendMsg"
- }
- ]
- },
- "name": "Messaging methods filter",
- "clazz": "org.thingsboard.server.extensions.core.filter.MethodNameFilter"
- }
- ],
- "processor": null,
- "action": {
- "configuration": {},
- "clazz": "org.thingsboard.server.extensions.core.action.rpc.RpcPluginAction",
- "name": "Messaging RPC Action"
- },
- "additionalInfo": null
-}
\ No newline at end of file
diff --git a/application/src/main/data/json/system/plugins/system_rpc_plugin.json b/application/src/main/data/json/system/plugins/system_rpc_plugin.json
deleted file mode 100644
index 7245faaa5e..0000000000
--- a/application/src/main/data/json/system/plugins/system_rpc_plugin.json
+++ /dev/null
@@ -1,11 +0,0 @@
-{
- "apiToken": "rpc",
- "name": "System RPC Plugin",
- "clazz": "org.thingsboard.server.extensions.core.plugin.rpc.RpcPlugin",
- "publicAccess": true,
- "state": "ACTIVE",
- "configuration": {
- "defaultTimeout": 20000
- },
- "additionalInfo": null
-}
\ No newline at end of file
diff --git a/application/src/main/data/json/system/plugins/system_telemetry_plugin.json b/application/src/main/data/json/system/plugins/system_telemetry_plugin.json
deleted file mode 100644
index 93a40e0ab3..0000000000
--- a/application/src/main/data/json/system/plugins/system_telemetry_plugin.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "apiToken": "telemetry",
- "name": "System Telemetry Plugin",
- "clazz": "org.thingsboard.server.extensions.core.plugin.telemetry.TelemetryStoragePlugin",
- "publicAccess": true,
- "state": "ACTIVE",
- "configuration": {},
- "additionalInfo": null
-}
\ No newline at end of file
diff --git a/application/src/main/data/json/system/rules/system_telemetry_rule.json b/application/src/main/data/json/system/rules/system_telemetry_rule.json
deleted file mode 100644
index c8166bf710..0000000000
--- a/application/src/main/data/json/system/rules/system_telemetry_rule.json
+++ /dev/null
@@ -1,29 +0,0 @@
-{
- "name": "System Telemetry Rule",
- "state": "ACTIVE",
- "weight": 0,
- "pluginToken": "telemetry",
- "filters": [
- {
- "clazz": "org.thingsboard.server.extensions.core.filter.MsgTypeFilter",
- "name": "TelemetryFilter",
- "configuration": {
- "messageTypes": [
- "POST_TELEMETRY",
- "POST_ATTRIBUTES",
- "GET_ATTRIBUTES"
- ]
- }
- }
- ],
- "processor": null,
- "action": {
- "clazz": "org.thingsboard.server.extensions.core.action.telemetry.TelemetryPluginAction",
- "name": "TelemetryMsgConverterAction",
- "configuration": {
- "timeUnit": "DAYS",
- "ttlValue": 365
- }
- },
- "additionalInfo": null
-}
\ No newline at end of file
diff --git a/application/src/main/data/json/tenant/rule_chains/root_rule_chain.json b/application/src/main/data/json/tenant/rule_chains/root_rule_chain.json
new file mode 100644
index 0000000000..225ccdc1f0
--- /dev/null
+++ b/application/src/main/data/json/tenant/rule_chains/root_rule_chain.json
@@ -0,0 +1,98 @@
+{
+ "ruleChain": {
+ "additionalInfo": null,
+ "name": "Root Rule Chain",
+ "firstRuleNodeId": null,
+ "root": true,
+ "debugMode": false,
+ "configuration": null
+ },
+ "metadata": {
+ "firstNodeIndex": 2,
+ "nodes": [
+ {
+ "additionalInfo": {
+ "layoutX": 824,
+ "layoutY": 156
+ },
+ "type": "org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode",
+ "name": "SaveTS",
+ "debugMode": false,
+ "configuration": {
+ "defaultTTL": 0
+ }
+ },
+ {
+ "additionalInfo": {
+ "layoutX": 825,
+ "layoutY": 52
+ },
+ "type": "org.thingsboard.rule.engine.telemetry.TbMsgAttributesNode",
+ "name": "save client attributes",
+ "debugMode": false,
+ "configuration": {
+ "scope": "CLIENT_SCOPE"
+ }
+ },
+ {
+ "additionalInfo": {
+ "layoutX": 347,
+ "layoutY": 149
+ },
+ "type": "org.thingsboard.rule.engine.filter.TbMsgTypeSwitchNode",
+ "name": "Message Type Switch",
+ "debugMode": false,
+ "configuration": {
+ "version": 0
+ }
+ },
+ {
+ "additionalInfo": {
+ "layoutX": 825,
+ "layoutY": 266
+ },
+ "type": "org.thingsboard.rule.engine.action.TbLogNode",
+ "name": "Log RPC",
+ "debugMode": false,
+ "configuration": {
+ "jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);"
+ }
+ },
+ {
+ "additionalInfo": {
+ "layoutX": 825,
+ "layoutY": 379
+ },
+ "type": "org.thingsboard.rule.engine.action.TbLogNode",
+ "name": "Log Other",
+ "debugMode": false,
+ "configuration": {
+ "jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);"
+ }
+ }
+ ],
+ "connections": [
+ {
+ "fromIndex": 2,
+ "toIndex": 4,
+ "type": "Other"
+ },
+ {
+ "fromIndex": 2,
+ "toIndex": 1,
+ "type": "Post attributes"
+ },
+ {
+ "fromIndex": 2,
+ "toIndex": 0,
+ "type": "Post telemetry"
+ },
+ {
+ "fromIndex": 2,
+ "toIndex": 3,
+ "type": "RPC Request"
+ }
+ ],
+ "ruleChainConnections": null
+ }
+}
\ No newline at end of file
diff --git a/application/src/main/data/upgrade/2.0.0/schema_update.cql b/application/src/main/data/upgrade/2.0.0/schema_update.cql
new file mode 100644
index 0000000000..5f46878d6a
--- /dev/null
+++ b/application/src/main/data/upgrade/2.0.0/schema_update.cql
@@ -0,0 +1,103 @@
+--
+-- 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.
+--
+
+CREATE TABLE IF NOT EXISTS thingsboard.msg_queue (
+ node_id timeuuid,
+ cluster_partition bigint,
+ ts_partition bigint,
+ ts bigint,
+ msg blob,
+ PRIMARY KEY ((node_id, cluster_partition, ts_partition), ts))
+WITH CLUSTERING ORDER BY (ts DESC)
+AND compaction = {
+ 'class': 'org.apache.cassandra.db.compaction.DateTieredCompactionStrategy',
+ 'min_threshold': '5',
+ 'base_time_seconds': '43200',
+ 'max_window_size_seconds': '43200',
+ 'tombstone_threshold': '0.9',
+ 'unchecked_tombstone_compaction': 'true'
+};
+
+CREATE TABLE IF NOT EXISTS thingsboard.msg_ack_queue (
+ node_id timeuuid,
+ cluster_partition bigint,
+ ts_partition bigint,
+ msg_id timeuuid,
+ PRIMARY KEY ((node_id, cluster_partition, ts_partition), msg_id))
+WITH CLUSTERING ORDER BY (msg_id DESC)
+AND compaction = {
+ 'class': 'org.apache.cassandra.db.compaction.DateTieredCompactionStrategy',
+ 'min_threshold': '5',
+ 'base_time_seconds': '43200',
+ 'max_window_size_seconds': '43200',
+ 'tombstone_threshold': '0.9',
+ 'unchecked_tombstone_compaction': 'true'
+};
+
+CREATE TABLE IF NOT EXISTS thingsboard.processed_msg_partitions (
+ node_id timeuuid,
+ cluster_partition bigint,
+ ts_partition bigint,
+ PRIMARY KEY ((node_id, cluster_partition), ts_partition))
+WITH CLUSTERING ORDER BY (ts_partition DESC)
+AND compaction = {
+ 'class': 'org.apache.cassandra.db.compaction.DateTieredCompactionStrategy',
+ 'min_threshold': '5',
+ 'base_time_seconds': '43200',
+ 'max_window_size_seconds': '43200',
+ 'tombstone_threshold': '0.9',
+ 'unchecked_tombstone_compaction': 'true'
+};
+
+CREATE TABLE IF NOT EXISTS thingsboard.rule_chain (
+ id uuid,
+ tenant_id uuid,
+ name text,
+ search_text text,
+ first_rule_node_id uuid,
+ root boolean,
+ debug_mode boolean,
+ configuration text,
+ additional_info text,
+ PRIMARY KEY (id, tenant_id)
+);
+
+CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.rule_chain_by_tenant_and_search_text AS
+ SELECT *
+ from thingsboard.rule_chain
+ WHERE tenant_id IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL
+ PRIMARY KEY ( tenant_id, search_text, id )
+ WITH CLUSTERING ORDER BY ( search_text ASC, id DESC );
+
+CREATE TABLE IF NOT EXISTS thingsboard.rule_node (
+ id uuid,
+ rule_chain_id uuid,
+ type text,
+ name text,
+ debug_mode boolean,
+ search_text text,
+ configuration text,
+ additional_info text,
+ PRIMARY KEY (id)
+);
+
+DROP MATERIALIZED VIEW IF EXISTS thingsboard.rule_by_plugin_token;
+DROP MATERIALIZED VIEW IF EXISTS thingsboard.rule_by_tenant_and_search_text;
+DROP MATERIALIZED VIEW IF EXISTS thingsboard.plugin_by_api_token;
+DROP MATERIALIZED VIEW IF EXISTS thingsboard.plugin_by_tenant_and_search_text;
+
+DROP TABLE IF EXISTS thingsboard.rule;
+DROP TABLE IF EXISTS thingsboard.plugin;
diff --git a/application/src/main/data/upgrade/2.0.0/schema_update.sql b/application/src/main/data/upgrade/2.0.0/schema_update.sql
new file mode 100644
index 0000000000..f16e334041
--- /dev/null
+++ b/application/src/main/data/upgrade/2.0.0/schema_update.sql
@@ -0,0 +1,44 @@
+--
+-- 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.
+--
+
+CREATE TABLE IF NOT EXISTS rule_chain (
+ id varchar(31) NOT NULL CONSTRAINT rule_chain_pkey PRIMARY KEY,
+ additional_info varchar,
+ configuration varchar(10000000),
+ name varchar(255),
+ first_rule_node_id varchar(31),
+ root boolean,
+ debug_mode boolean,
+ search_text varchar(255),
+ tenant_id varchar(31)
+);
+
+CREATE TABLE IF NOT EXISTS rule_node (
+ id varchar(31) NOT NULL CONSTRAINT rule_node_pkey PRIMARY KEY,
+ rule_chain_id varchar(31),
+ additional_info varchar,
+ configuration varchar(10000000),
+ type varchar(255),
+ name varchar(255),
+ debug_mode boolean,
+ search_text varchar(255)
+);
+
+DROP TABLE rule;
+DROP TABLE plugin;
+
+DELETE FROM alarm WHERE originator_type = 3 OR originator_type = 4;
+UPDATE alarm SET originator_type = (originator_type - 2) where originator_type > 2;
diff --git a/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java b/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java
index 77953c9dc1..d33734e14c 100644
--- a/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java
+++ b/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java
@@ -21,22 +21,27 @@ import akka.actor.Scheduler;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
import com.typesafe.config.Config;
import com.typesafe.config.ConfigFactory;
import lombok.Getter;
import lombok.Setter;
+import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
+import org.thingsboard.rule.engine.api.MailService;
import org.thingsboard.server.actors.service.ActorService;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.Event;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
+import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.cluster.ServerAddress;
import org.thingsboard.server.common.transport.auth.DeviceAuthService;
-import org.thingsboard.server.controller.plugin.PluginWebSocketMsgEndpoint;
import org.thingsboard.server.dao.alarm.AlarmService;
import org.thingsboard.server.dao.asset.AssetService;
import org.thingsboard.server.dao.attributes.AttributesService;
@@ -44,118 +49,222 @@ import org.thingsboard.server.dao.audit.AuditLogService;
import org.thingsboard.server.dao.customer.CustomerService;
import org.thingsboard.server.dao.device.DeviceService;
import org.thingsboard.server.dao.event.EventService;
-import org.thingsboard.server.dao.plugin.PluginService;
import org.thingsboard.server.dao.relation.RelationService;
-import org.thingsboard.server.dao.rule.RuleService;
+import org.thingsboard.server.dao.rule.RuleChainService;
import org.thingsboard.server.dao.tenant.TenantService;
import org.thingsboard.server.dao.timeseries.TimeseriesService;
+import org.thingsboard.server.dao.user.UserService;
import org.thingsboard.server.service.cluster.discovery.DiscoveryService;
import org.thingsboard.server.service.cluster.routing.ClusterRoutingService;
import org.thingsboard.server.service.cluster.rpc.ClusterRpcService;
import org.thingsboard.server.service.component.ComponentDiscoveryService;
-
+import org.thingsboard.server.service.encoding.DataDecodingEncodingService;
+import org.thingsboard.server.service.executors.DbCallbackExecutorService;
+import org.thingsboard.server.service.executors.ExternalCallExecutorService;
+import org.thingsboard.server.service.mail.MailExecutorService;
+import org.thingsboard.server.service.queue.MsgQueueService;
+import org.thingsboard.server.service.rpc.DeviceRpcService;
+import org.thingsboard.server.service.script.JsExecutorService;
+import org.thingsboard.server.service.script.JsSandboxService;
+import org.thingsboard.server.service.state.DeviceStateService;
+import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService;
+
+import javax.annotation.Nullable;
+import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Optional;
+@Slf4j
@Component
public class ActorSystemContext {
private static final String AKKA_CONF_FILE_NAME = "actor-system.conf";
protected final ObjectMapper mapper = new ObjectMapper();
- @Getter @Setter private ActorService actorService;
+ @Getter
+ @Setter
+ private ActorService actorService;
+
+ @Autowired
+ @Getter
+ private DiscoveryService discoveryService;
+
+ @Autowired
+ @Getter
+ @Setter
+ private ComponentDiscoveryService componentService;
+
+ @Autowired
+ @Getter
+ private ClusterRoutingService routingService;
+
+ @Autowired
+ @Getter
+ private ClusterRpcService rpcService;
+
+ @Autowired
+ @Getter
+ private DataDecodingEncodingService encodingService;
+
+ @Autowired
+ @Getter
+ private DeviceAuthService deviceAuthService;
+
+ @Autowired
+ @Getter
+ private DeviceService deviceService;
+
+ @Autowired
+ @Getter
+ private AssetService assetService;
+
+ @Autowired
+ @Getter
+ private TenantService tenantService;
+
+ @Autowired
+ @Getter
+ private CustomerService customerService;
@Autowired
- @Getter private DiscoveryService discoveryService;
+ @Getter
+ private UserService userService;
@Autowired
- @Getter @Setter private ComponentDiscoveryService componentService;
+ @Getter
+ private RuleChainService ruleChainService;
@Autowired
- @Getter private ClusterRoutingService routingService;
+ @Getter
+ private TimeseriesService tsService;
@Autowired
- @Getter private ClusterRpcService rpcService;
+ @Getter
+ private AttributesService attributesService;
@Autowired
- @Getter private DeviceAuthService deviceAuthService;
+ @Getter
+ private EventService eventService;
@Autowired
- @Getter private DeviceService deviceService;
+ @Getter
+ private AlarmService alarmService;
@Autowired
- @Getter private AssetService assetService;
+ @Getter
+ private RelationService relationService;
@Autowired
- @Getter private TenantService tenantService;
+ @Getter
+ private AuditLogService auditLogService;
@Autowired
- @Getter private CustomerService customerService;
+ @Getter
+ private TelemetrySubscriptionService tsSubService;
@Autowired
- @Getter private RuleService ruleService;
+ @Getter
+ private DeviceRpcService deviceRpcService;
@Autowired
- @Getter private PluginService pluginService;
+ @Getter
+ private JsSandboxService jsSandbox;
@Autowired
- @Getter private TimeseriesService tsService;
+ @Getter
+ private JsExecutorService jsExecutor;
@Autowired
- @Getter private AttributesService attributesService;
+ @Getter
+ private MailExecutorService mailExecutor;
@Autowired
- @Getter private EventService eventService;
+ @Getter
+ private DbCallbackExecutorService dbCallbackExecutor;
@Autowired
- @Getter private AlarmService alarmService;
+ @Getter
+ private ExternalCallExecutorService externalCallExecutorService;
@Autowired
- @Getter private RelationService relationService;
+ @Getter
+ private MailService mailService;
@Autowired
- @Getter private AuditLogService auditLogService;
+ @Getter
+ private MsgQueueService msgQueueService;
@Autowired
- @Getter @Setter private PluginWebSocketMsgEndpoint wsMsgEndpoint;
+ @Getter
+ private DeviceStateService deviceStateService;
+
+ @Value("${cluster.partition_id}")
+ @Getter
+ private long queuePartitionId;
+
+ @Value("${actors.session.max_concurrent_sessions_per_device:1}")
+ @Getter
+ private long maxConcurrentSessionsPerDevice;
@Value("${actors.session.sync.timeout}")
- @Getter private long syncSessionTimeout;
+ @Getter
+ private long syncSessionTimeout;
- @Value("${actors.plugin.termination.delay}")
- @Getter private long pluginActorTerminationDelay;
+ @Value("${actors.queue.enabled}")
+ @Getter
+ private boolean queuePersistenceEnabled;
- @Value("${actors.plugin.processing.timeout}")
- @Getter private long pluginProcessingTimeout;
+ @Value("${actors.queue.timeout}")
+ @Getter
+ private long queuePersistenceTimeout;
- @Value("${actors.plugin.error_persist_frequency}")
- @Getter private long pluginErrorPersistFrequency;
+ @Value("${actors.client_side_rpc.timeout}")
+ @Getter
+ private long clientSideRpcTimeout;
- @Value("${actors.rule.termination.delay}")
- @Getter private long ruleActorTerminationDelay;
+ @Value("${actors.rule.chain.error_persist_frequency}")
+ @Getter
+ private long ruleChainErrorPersistFrequency;
- @Value("${actors.rule.error_persist_frequency}")
- @Getter private long ruleErrorPersistFrequency;
+ @Value("${actors.rule.node.error_persist_frequency}")
+ @Getter
+ private long ruleNodeErrorPersistFrequency;
@Value("${actors.statistics.enabled}")
- @Getter private boolean statisticsEnabled;
+ @Getter
+ private boolean statisticsEnabled;
@Value("${actors.statistics.persist_frequency}")
- @Getter private long statisticsPersistFrequency;
+ @Getter
+ private long statisticsPersistFrequency;
@Value("${actors.tenant.create_components_on_init}")
- @Getter private boolean tenantComponentsInitEnabled;
+ @Getter
+ private boolean tenantComponentsInitEnabled;
- @Getter @Setter private ActorSystem actorSystem;
+ @Value("${actors.rule.allow_system_mail_service}")
+ @Getter
+ private boolean allowSystemMailService;
- @Getter @Setter private ActorRef appActor;
+ @Getter
+ @Setter
+ private ActorSystem actorSystem;
- @Getter @Setter private ActorRef sessionManagerActor;
+ @Getter
+ @Setter
+ private ActorRef appActor;
- @Getter @Setter private ActorRef statsActor;
+ @Getter
+ @Setter
+ private ActorRef sessionManagerActor;
- @Getter private final Config config;
+ @Getter
+ @Setter
+ private ActorRef statsActor;
+
+ @Getter
+ private final Config config;
public ActorSystemContext() {
config = ConfigFactory.parseResources(AKKA_CONF_FILE_NAME).withFallback(ConfigFactory.load());
@@ -187,7 +296,7 @@ public class ActorSystemContext {
eventService.save(event);
}
- private String toString(Exception e) {
+ private String toString(Throwable e) {
StringWriter sw = new StringWriter();
e.printStackTrace(new PrintWriter(sw));
return sw.toString();
@@ -207,4 +316,72 @@ public class ActorSystemContext {
private JsonNode toBodyJson(ServerAddress server, String method, String body) {
return mapper.createObjectNode().put("server", server.toString()).put("method", method).put("error", body);
}
+
+ public String getServerAddress() {
+ return discoveryService.getCurrentServer().getServerAddress().toString();
+ }
+
+ public void persistDebugInput(TenantId tenantId, EntityId entityId, TbMsg tbMsg, String relationType) {
+ persistDebugAsync(tenantId, entityId, "IN", tbMsg, relationType, null);
+ }
+
+ public void persistDebugInput(TenantId tenantId, EntityId entityId, TbMsg tbMsg, String relationType, Throwable error) {
+ persistDebugAsync(tenantId, entityId, "IN", tbMsg, relationType, error);
+ }
+
+ public void persistDebugOutput(TenantId tenantId, EntityId entityId, TbMsg tbMsg, String relationType, Throwable error) {
+ persistDebugAsync(tenantId, entityId, "OUT", tbMsg, relationType, error);
+ }
+
+ public void persistDebugOutput(TenantId tenantId, EntityId entityId, TbMsg tbMsg, String relationType) {
+ persistDebugAsync(tenantId, entityId, "OUT", tbMsg, relationType, null);
+ }
+
+ private void persistDebugAsync(TenantId tenantId, EntityId entityId, String type, TbMsg tbMsg, String relationType, Throwable error) {
+ try {
+ Event event = new Event();
+ event.setTenantId(tenantId);
+ event.setEntityId(entityId);
+ event.setType(DataConstants.DEBUG_RULE_NODE);
+
+ String metadata = mapper.writeValueAsString(tbMsg.getMetaData().getData());
+
+ 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())
+ .put("relationType", relationType)
+ .put("data", tbMsg.getData())
+ .put("metadata", metadata);
+
+ if (error != null) {
+ node = node.put("error", toString(error));
+ }
+
+ event.setBody(node);
+ ListenableFuture future = eventService.saveAsync(event);
+ Futures.addCallback(future, new FutureCallback() {
+ @Override
+ public void onSuccess(@Nullable Event event) {
+
+ }
+
+ @Override
+ public void onFailure(Throwable th) {
+ log.error("Could not save debug Event for Node", th);
+ }
+ });
+ } catch (IOException ex) {
+ log.warn("Failed to persist rule node debug message", ex);
+ }
+ }
+
+ public static Exception toException(Throwable error) {
+ return Exception.class.isInstance(error) ? (Exception) error : new Exception(error);
+ }
+
}
diff --git a/application/src/main/java/org/thingsboard/server/actors/app/AppActor.java b/application/src/main/java/org/thingsboard/server/actors/app/AppActor.java
index b475277630..d453e594ba 100644
--- a/application/src/main/java/org/thingsboard/server/actors/app/AppActor.java
+++ b/application/src/main/java/org/thingsboard/server/actors/app/AppActor.java
@@ -15,55 +15,51 @@
*/
package org.thingsboard.server.actors.app;
-import akka.actor.*;
+import akka.actor.ActorRef;
+import akka.actor.LocalActorRef;
+import akka.actor.OneForOneStrategy;
+import akka.actor.Props;
+import akka.actor.SupervisorStrategy;
import akka.actor.SupervisorStrategy.Directive;
+import akka.actor.Terminated;
import akka.event.Logging;
import akka.event.LoggingAdapter;
import akka.japi.Function;
import org.thingsboard.server.actors.ActorSystemContext;
-import org.thingsboard.server.actors.plugin.PluginTerminationMsg;
-import org.thingsboard.server.actors.service.ContextAwareActor;
+import org.thingsboard.server.actors.ruleChain.RuleChainManagerActor;
import org.thingsboard.server.actors.service.ContextBasedCreator;
import org.thingsboard.server.actors.service.DefaultActorService;
-import org.thingsboard.server.actors.shared.plugin.PluginManager;
-import org.thingsboard.server.actors.shared.plugin.SystemPluginManager;
-import org.thingsboard.server.actors.shared.rule.RuleManager;
-import org.thingsboard.server.actors.shared.rule.SystemRuleManager;
-import org.thingsboard.server.actors.tenant.RuleChainDeviceMsg;
+import org.thingsboard.server.actors.shared.rulechain.SystemRuleChainManager;
import org.thingsboard.server.actors.tenant.TenantActor;
import org.thingsboard.server.common.data.Tenant;
-import org.thingsboard.server.common.data.id.PluginId;
-import org.thingsboard.server.common.data.id.RuleId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageDataIterable;
-import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;
-import org.thingsboard.server.common.msg.device.ToDeviceActorMsg;
+import org.thingsboard.server.common.msg.TbActorMsg;
+import org.thingsboard.server.common.msg.aware.TenantAwareMsg;
+import org.thingsboard.server.common.msg.cluster.SendToClusterMsg;
+import org.thingsboard.server.common.msg.cluster.ServerAddress;
+import org.thingsboard.server.common.msg.core.BasicActorSystemToDeviceSessionActorMsg;
+import org.thingsboard.server.common.msg.device.DeviceToDeviceActorMsg;
import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
+import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg;
import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.dao.tenant.TenantService;
-import org.thingsboard.server.extensions.api.device.ToDeviceActorNotificationMsg;
-import org.thingsboard.server.extensions.api.plugins.msg.ToPluginActorMsg;
-import org.thingsboard.server.extensions.api.rules.ToRuleActorMsg;
import scala.concurrent.duration.Duration;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
-public class AppActor extends ContextAwareActor {
+public class AppActor extends RuleChainManagerActor {
private final LoggingAdapter logger = Logging.getLogger(getContext().system(), this);
public static final TenantId SYSTEM_TENANT = new TenantId(ModelConstants.NULL_UUID);
- private final RuleManager ruleManager;
- private final PluginManager pluginManager;
private final TenantService tenantService;
private final Map tenantActors;
private AppActor(ActorSystemContext systemContext) {
- super(systemContext);
- this.ruleManager = new SystemRuleManager(systemContext);
- this.pluginManager = new SystemPluginManager(systemContext);
+ super(systemContext, new SystemRuleChainManager(systemContext));
this.tenantService = systemContext.getTenantService();
this.tenantActors = new HashMap<>();
}
@@ -77,8 +73,7 @@ public class AppActor extends ContextAwareActor {
public void preStart() {
logger.info("Starting main system actor.");
try {
- ruleManager.init(this.context());
- pluginManager.init(this.context());
+ initRuleChains();
if (systemContext.isTenantComponentsInitEnabled()) {
PageDataIterable tenantIterator = new PageDataIterable<>(tenantService::findTenants, ENTITY_PACK_LIMIT);
@@ -96,93 +91,89 @@ public class AppActor extends ContextAwareActor {
}
@Override
- public void onReceive(Object msg) throws Exception {
- logger.debug("Received message: {}", msg);
- if (msg instanceof ToDeviceActorMsg) {
- processDeviceMsg((ToDeviceActorMsg) msg);
- } else if (msg instanceof ToPluginActorMsg) {
- onToPluginMsg((ToPluginActorMsg) msg);
- } else if (msg instanceof ToRuleActorMsg) {
- onToRuleMsg((ToRuleActorMsg) msg);
- } else if (msg instanceof ToDeviceActorNotificationMsg) {
- onToDeviceActorMsg((ToDeviceActorNotificationMsg) msg);
- } else if (msg instanceof Terminated) {
- processTermination((Terminated) msg);
- } else if (msg instanceof ClusterEventMsg) {
- broadcast(msg);
- } else if (msg instanceof ComponentLifecycleMsg) {
- onComponentLifecycleMsg((ComponentLifecycleMsg) msg);
- } else if (msg instanceof PluginTerminationMsg) {
- onPluginTerminated((PluginTerminationMsg) msg);
- } else {
- logger.warning("Unknown message: {}!", msg);
+ protected boolean process(TbActorMsg msg) {
+ switch (msg.getMsgType()) {
+ case SEND_TO_CLUSTER_MSG:
+ onPossibleClusterMsg((SendToClusterMsg) msg);
+ break;
+ case CLUSTER_EVENT_MSG:
+ broadcast(msg);
+ break;
+ case COMPONENT_LIFE_CYCLE_MSG:
+ onComponentLifecycleMsg((ComponentLifecycleMsg) msg);
+ break;
+ case SERVICE_TO_RULE_ENGINE_MSG:
+ onServiceToRuleEngineMsg((ServiceToRuleEngineMsg) msg);
+ break;
+ case DEVICE_SESSION_TO_DEVICE_ACTOR_MSG:
+ case DEVICE_ATTRIBUTES_UPDATE_TO_DEVICE_ACTOR_MSG:
+ case DEVICE_CREDENTIALS_UPDATE_TO_DEVICE_ACTOR_MSG:
+ case DEVICE_NAME_OR_TYPE_UPDATE_TO_DEVICE_ACTOR_MSG:
+ case DEVICE_RPC_REQUEST_TO_DEVICE_ACTOR_MSG:
+ case SERVER_RPC_RESPONSE_TO_DEVICE_ACTOR_MSG:
+ onToDeviceActorMsg((TenantAwareMsg) msg);
+ break;
+ case ACTOR_SYSTEM_TO_DEVICE_SESSION_ACTOR_MSG:
+ onToDeviceSessionMsg((BasicActorSystemToDeviceSessionActorMsg) msg);
+ default:
+ return false;
}
+ return true;
}
- private void onPluginTerminated(PluginTerminationMsg msg) {
- pluginManager.remove(msg.getId());
+ private void onToDeviceSessionMsg(BasicActorSystemToDeviceSessionActorMsg msg) {
+ systemContext.getSessionManagerActor().tell(msg, self());
}
- private void broadcast(Object msg) {
- pluginManager.broadcast(msg);
- tenantActors.values().forEach(actorRef -> actorRef.tell(msg, ActorRef.noSender()));
+ private void onPossibleClusterMsg(SendToClusterMsg msg) {
+ Optional address = systemContext.getRoutingService().resolveById(msg.getEntityId());
+ if (address.isPresent()) {
+ systemContext.getRpcService().tell(
+ systemContext.getEncodingService().convertToProtoDataMessage(address.get(), msg.getMsg()));
+ } else {
+ self().tell(msg.getMsg(), ActorRef.noSender());
+ }
}
- private void onToRuleMsg(ToRuleActorMsg msg) {
- ActorRef target;
+ private void onServiceToRuleEngineMsg(ServiceToRuleEngineMsg msg) {
if (SYSTEM_TENANT.equals(msg.getTenantId())) {
- target = ruleManager.getOrCreateRuleActor(this.context(), msg.getRuleId());
+ //TODO: ashvayka handle this.
} else {
- target = getOrCreateTenantActor(msg.getTenantId());
+ getOrCreateTenantActor(msg.getTenantId()).tell(msg, self());
}
- target.tell(msg, ActorRef.noSender());
}
- private void onToPluginMsg(ToPluginActorMsg msg) {
- ActorRef target;
- if (SYSTEM_TENANT.equals(msg.getPluginTenantId())) {
- target = pluginManager.getOrCreatePluginActor(this.context(), msg.getPluginId());
- } else {
- target = getOrCreateTenantActor(msg.getPluginTenantId());
- }
- target.tell(msg, ActorRef.noSender());
+ @Override
+ protected void broadcast(Object msg) {
+ super.broadcast(msg);
+ tenantActors.values().forEach(actorRef -> actorRef.tell(msg, ActorRef.noSender()));
}
private void onComponentLifecycleMsg(ComponentLifecycleMsg msg) {
- ActorRef target = null;
+ ActorRef target;
if (SYSTEM_TENANT.equals(msg.getTenantId())) {
- Optional pluginId = msg.getPluginId();
- Optional ruleId = msg.getRuleId();
- if (pluginId.isPresent()) {
- target = pluginManager.getOrCreatePluginActor(this.context(), pluginId.get());
- } else if (ruleId.isPresent()) {
- Optional ref = ruleManager.update(this.context(), ruleId.get(), msg.getEvent());
- if (ref.isPresent()) {
- target = ref.get();
- } else {
- logger.debug("Failed to find actor for rule: [{}]", ruleId);
- return;
- }
- }
+ target = getEntityActorRef(msg.getEntityId());
} else {
target = getOrCreateTenantActor(msg.getTenantId());
}
if (target != null) {
target.tell(msg, ActorRef.noSender());
+ } else {
+ logger.debug("Invalid component lifecycle msg: {}", msg);
}
}
- private void onToDeviceActorMsg(ToDeviceActorNotificationMsg msg) {
+ private void onToDeviceActorMsg(TenantAwareMsg msg) {
getOrCreateTenantActor(msg.getTenantId()).tell(msg, ActorRef.noSender());
}
- private void processDeviceMsg(ToDeviceActorMsg toDeviceActorMsg) {
- TenantId tenantId = toDeviceActorMsg.getTenantId();
+ private void processDeviceMsg(DeviceToDeviceActorMsg deviceToDeviceActorMsg) {
+ TenantId tenantId = deviceToDeviceActorMsg.getTenantId();
ActorRef tenantActor = getOrCreateTenantActor(tenantId);
- if (toDeviceActorMsg.getPayload().getMsgType().requiresRulesProcessing()) {
- tenantActor.tell(new RuleChainDeviceMsg(toDeviceActorMsg, ruleManager.getRuleChain(this.context())), context().self());
+ if (deviceToDeviceActorMsg.getPayload().getMsgType().requiresRulesProcessing()) {
+// tenantActor.tell(new RuleChainDeviceMsg(deviceToDeviceActorMsg, ruleManager.getRuleChain(this.context())), context().self());
} else {
- tenantActor.tell(toDeviceActorMsg, context().self());
+ tenantActor.tell(deviceToDeviceActorMsg, context().self());
}
}
diff --git a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActor.java b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActor.java
index 861c405e08..99d004574e 100644
--- a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActor.java
+++ b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActor.java
@@ -17,61 +17,73 @@ package org.thingsboard.server.actors.device;
import akka.event.Logging;
import akka.event.LoggingAdapter;
+import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg;
+import org.thingsboard.rule.engine.api.msg.DeviceNameOrTypeUpdateMsg;
import org.thingsboard.server.actors.ActorSystemContext;
-import org.thingsboard.server.actors.rule.RulesProcessedMsg;
import org.thingsboard.server.actors.service.ContextAwareActor;
import org.thingsboard.server.actors.service.ContextBasedCreator;
-import org.thingsboard.server.actors.tenant.RuleChainDeviceMsg;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.TenantId;
+import org.thingsboard.server.common.msg.TbActorMsg;
import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;
-import org.thingsboard.server.common.msg.device.ToDeviceActorMsg;
-import org.thingsboard.server.extensions.api.device.DeviceAttributesEventNotificationMsg;
-import org.thingsboard.server.extensions.api.device.DeviceCredentialsUpdateNotificationMsg;
-import org.thingsboard.server.extensions.api.device.DeviceNameOrTypeUpdateMsg;
-import org.thingsboard.server.extensions.api.device.ToDeviceActorNotificationMsg;
-import org.thingsboard.server.extensions.api.plugins.msg.*;
+import org.thingsboard.server.common.msg.device.DeviceToDeviceActorMsg;
+import org.thingsboard.server.common.msg.timeout.DeviceActorClientSideRpcTimeoutMsg;
+import org.thingsboard.server.common.msg.timeout.DeviceActorQueueTimeoutMsg;
+import org.thingsboard.server.common.msg.timeout.DeviceActorServerSideRpcTimeoutMsg;
+import org.thingsboard.server.service.rpc.ToDeviceRpcRequestActorMsg;
+import org.thingsboard.server.service.rpc.ToServerRpcResponseActorMsg;
public class DeviceActor extends ContextAwareActor {
private final LoggingAdapter logger = Logging.getLogger(getContext().system(), this);
- private final TenantId tenantId;
- private final DeviceId deviceId;
private final DeviceActorMessageProcessor processor;
private DeviceActor(ActorSystemContext systemContext, TenantId tenantId, DeviceId deviceId) {
super(systemContext);
- this.tenantId = tenantId;
- this.deviceId = deviceId;
- this.processor = new DeviceActorMessageProcessor(systemContext, logger, deviceId);
+ this.processor = new DeviceActorMessageProcessor(systemContext, logger, tenantId, deviceId);
}
@Override
- public void onReceive(Object msg) throws Exception {
- if (msg instanceof RuleChainDeviceMsg) {
- processor.process(context(), (RuleChainDeviceMsg) msg);
- } else if (msg instanceof RulesProcessedMsg) {
- processor.onRulesProcessedMsg(context(), (RulesProcessedMsg) msg);
- } else if (msg instanceof ToDeviceActorMsg) {
- processor.process(context(), (ToDeviceActorMsg) msg);
- } else if (msg instanceof ToDeviceActorNotificationMsg) {
- if (msg instanceof DeviceAttributesEventNotificationMsg) {
+ protected boolean process(TbActorMsg msg) {
+ switch (msg.getMsgType()) {
+ case CLUSTER_EVENT_MSG:
+ processor.processClusterEventMsg((ClusterEventMsg) msg);
+ break;
+ case DEVICE_SESSION_TO_DEVICE_ACTOR_MSG:
+ processor.process(context(), (DeviceToDeviceActorMsg) msg);
+ break;
+ case DEVICE_ATTRIBUTES_UPDATE_TO_DEVICE_ACTOR_MSG:
processor.processAttributesUpdate(context(), (DeviceAttributesEventNotificationMsg) msg);
- } else if (msg instanceof ToDeviceRpcRequestPluginMsg) {
- processor.processRpcRequest(context(), (ToDeviceRpcRequestPluginMsg) msg);
- } else if (msg instanceof DeviceCredentialsUpdateNotificationMsg){
+ break;
+ case DEVICE_CREDENTIALS_UPDATE_TO_DEVICE_ACTOR_MSG:
processor.processCredentialsUpdate();
- } else if (msg instanceof DeviceNameOrTypeUpdateMsg){
+ break;
+ case DEVICE_NAME_OR_TYPE_UPDATE_TO_DEVICE_ACTOR_MSG:
processor.processNameOrTypeUpdate((DeviceNameOrTypeUpdateMsg) msg);
- }
- } else if (msg instanceof TimeoutMsg) {
- processor.processTimeout(context(), (TimeoutMsg) msg);
- } else if (msg instanceof ClusterEventMsg) {
- processor.processClusterEventMsg((ClusterEventMsg) msg);
- } else {
- logger.debug("[{}][{}] Unknown msg type.", tenantId, deviceId, msg.getClass().getName());
+ break;
+ case DEVICE_RPC_REQUEST_TO_DEVICE_ACTOR_MSG:
+ processor.processRpcRequest(context(), (ToDeviceRpcRequestActorMsg) msg);
+ break;
+ case SERVER_RPC_RESPONSE_TO_DEVICE_ACTOR_MSG:
+ processor.processToServerRPCResponse(context(), (ToServerRpcResponseActorMsg) msg);
+ break;
+ case DEVICE_ACTOR_SERVER_SIDE_RPC_TIMEOUT_MSG:
+ processor.processServerSideRpcTimeout(context(), (DeviceActorServerSideRpcTimeoutMsg) msg);
+ break;
+ case DEVICE_ACTOR_CLIENT_SIDE_RPC_TIMEOUT_MSG:
+ processor.processClientSideRpcTimeout(context(), (DeviceActorClientSideRpcTimeoutMsg) msg);
+ break;
+ case DEVICE_ACTOR_QUEUE_TIMEOUT_MSG:
+ processor.processQueueTimeout(context(), (DeviceActorQueueTimeoutMsg) msg);
+ break;
+ case RULE_ENGINE_QUEUE_PUT_ACK_MSG:
+ processor.processQueueAck(context(), (RuleEngineQueuePutAckMsg) msg);
+ break;
+ default:
+ return false;
}
+ return true;
}
public static class ActorCreator extends ContextBasedCreator {
diff --git a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java
index 21112bf4df..85b32857f1 100644
--- a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java
+++ b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java
@@ -18,37 +18,75 @@ package org.thingsboard.server.actors.device;
import akka.actor.ActorContext;
import akka.actor.ActorRef;
import akka.event.LoggingAdapter;
+import com.datastax.driver.core.utils.UUIDs;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.gson.Gson;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import org.thingsboard.rule.engine.api.RpcError;
+import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg;
+import org.thingsboard.rule.engine.api.msg.DeviceNameOrTypeUpdateMsg;
import org.thingsboard.server.actors.ActorSystemContext;
-import org.thingsboard.server.actors.rule.*;
import org.thingsboard.server.actors.shared.AbstractContextAwareMsgProcessor;
-import org.thingsboard.server.actors.tenant.RuleChainDeviceMsg;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.SessionId;
+import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.kv.AttributeKey;
import org.thingsboard.server.common.data.kv.AttributeKvEntry;
+import org.thingsboard.server.common.data.kv.KvEntry;
+import org.thingsboard.server.common.data.rpc.ToDeviceRpcRequestBody;
+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.cluster.ClusterEventMsg;
import org.thingsboard.server.common.msg.cluster.ServerAddress;
-import org.thingsboard.server.common.msg.core.*;
-import org.thingsboard.server.common.msg.device.ToDeviceActorMsg;
+import org.thingsboard.server.common.msg.core.ActorSystemToDeviceSessionActorMsg;
+import org.thingsboard.server.common.msg.core.AttributesUpdateNotification;
+import org.thingsboard.server.common.msg.core.AttributesUpdateRequest;
+import org.thingsboard.server.common.msg.core.BasicActorSystemToDeviceSessionActorMsg;
+import org.thingsboard.server.common.msg.core.BasicCommandAckResponse;
+import org.thingsboard.server.common.msg.core.BasicGetAttributesResponse;
+import org.thingsboard.server.common.msg.core.BasicStatusCodeResponse;
+import org.thingsboard.server.common.msg.core.GetAttributesRequest;
+import org.thingsboard.server.common.msg.core.RuleEngineError;
+import org.thingsboard.server.common.msg.core.RuleEngineErrorMsg;
+import org.thingsboard.server.common.msg.core.SessionCloseMsg;
+import org.thingsboard.server.common.msg.core.SessionCloseNotification;
+import org.thingsboard.server.common.msg.core.SessionOpenMsg;
+import org.thingsboard.server.common.msg.core.TelemetryUploadRequest;
+import org.thingsboard.server.common.msg.core.ToDeviceRpcRequestMsg;
+import org.thingsboard.server.common.msg.core.ToDeviceRpcResponseMsg;
+import org.thingsboard.server.common.msg.core.ToServerRpcRequestMsg;
+import org.thingsboard.server.common.msg.device.DeviceToDeviceActorMsg;
import org.thingsboard.server.common.msg.kv.BasicAttributeKVMsg;
+import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest;
import org.thingsboard.server.common.msg.session.FromDeviceMsg;
-import org.thingsboard.server.common.msg.session.MsgType;
+import org.thingsboard.server.common.msg.session.SessionMsgType;
import org.thingsboard.server.common.msg.session.SessionType;
import org.thingsboard.server.common.msg.session.ToDeviceMsg;
-import org.thingsboard.server.extensions.api.device.*;
-import org.thingsboard.server.extensions.api.plugins.msg.FromDeviceRpcResponse;
-import org.thingsboard.server.extensions.api.plugins.msg.RpcError;
-import org.thingsboard.server.extensions.api.plugins.msg.TimeoutIntMsg;
-import org.thingsboard.server.extensions.api.plugins.msg.TimeoutMsg;
-import org.thingsboard.server.extensions.api.plugins.msg.ToDeviceRpcRequest;
-import org.thingsboard.server.extensions.api.plugins.msg.ToDeviceRpcRequestBody;
-import org.thingsboard.server.extensions.api.plugins.msg.ToDeviceRpcRequestPluginMsg;
-import org.thingsboard.server.extensions.api.plugins.msg.ToPluginRpcResponseDeviceMsg;
-
-import java.util.*;
-import java.util.concurrent.ExecutionException;
+import org.thingsboard.server.common.msg.timeout.DeviceActorClientSideRpcTimeoutMsg;
+import org.thingsboard.server.common.msg.timeout.DeviceActorQueueTimeoutMsg;
+import org.thingsboard.server.common.msg.timeout.DeviceActorServerSideRpcTimeoutMsg;
+import org.thingsboard.server.service.rpc.FromDeviceRpcResponse;
+import org.thingsboard.server.service.rpc.ToDeviceRpcRequestActorMsg;
+import org.thingsboard.server.service.rpc.ToServerRpcResponseActorMsg;
+
+import javax.annotation.Nullable;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.UUID;
import java.util.concurrent.TimeoutException;
import java.util.function.Consumer;
import java.util.function.Predicate;
@@ -59,46 +97,46 @@ import java.util.stream.Collectors;
*/
public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
+ private final TenantId tenantId;
private final DeviceId deviceId;
private final Map sessions;
private final Map attributeSubscriptions;
private final Map rpcSubscriptions;
+ private final Map toDeviceRpcPendingMap;
+ private final Map toServerRpcPendingMap;
+ private final Map pendingMsgs;
- private final Map rpcPendingMap;
+ private final Gson gson = new Gson();
+ private final JsonParser jsonParser = new JsonParser();
private int rpcSeq = 0;
private String deviceName;
private String deviceType;
- private DeviceAttributes deviceAttributes;
+ private TbMsgMetaData defaultMetaData;
- public DeviceActorMessageProcessor(ActorSystemContext systemContext, LoggingAdapter logger, DeviceId deviceId) {
+ public DeviceActorMessageProcessor(ActorSystemContext systemContext, LoggingAdapter logger, TenantId tenantId, DeviceId deviceId) {
super(systemContext, logger);
+ this.tenantId = tenantId;
this.deviceId = deviceId;
- this.sessions = new HashMap<>();
+ this.sessions = new LinkedHashMap<>();
this.attributeSubscriptions = new HashMap<>();
this.rpcSubscriptions = new HashMap<>();
- this.rpcPendingMap = new HashMap<>();
+ this.toDeviceRpcPendingMap = new HashMap<>();
+ this.toServerRpcPendingMap = new HashMap<>();
+ this.pendingMsgs = new HashMap<>();
initAttributes();
}
private void initAttributes() {
- //TODO: add invalidation of deviceType cache.
Device device = systemContext.getDeviceService().findDeviceById(deviceId);
this.deviceName = device.getName();
this.deviceType = device.getType();
- this.deviceAttributes = new DeviceAttributes(fetchAttributes(DataConstants.CLIENT_SCOPE),
- fetchAttributes(DataConstants.SERVER_SCOPE), fetchAttributes(DataConstants.SHARED_SCOPE));
+ this.defaultMetaData = new TbMsgMetaData();
+ this.defaultMetaData.putValue("deviceName", deviceName);
+ this.defaultMetaData.putValue("deviceType", deviceType);
}
- private void refreshAttributes(DeviceAttributesEventNotificationMsg msg) {
- if (msg.isDeleted()) {
- msg.getDeletedKeys().forEach(key -> deviceAttributes.remove(key));
- } else {
- deviceAttributes.update(msg.getScope(), msg.getValues());
- }
- }
-
- void processRpcRequest(ActorContext context, ToDeviceRpcRequestPluginMsg msg) {
+ void processRpcRequest(ActorContext context, ToDeviceRpcRequestActorMsg msg) {
ToDeviceRpcRequest request = msg.getMsg();
ToDeviceRpcRequestBody body = request.getBody();
ToDeviceRpcRequestMsg rpcRequest = new ToDeviceRpcRequestMsg(
@@ -116,7 +154,7 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
boolean sent = rpcSubscriptions.size() > 0;
Set syncSessionSet = new HashSet<>();
rpcSubscriptions.entrySet().forEach(sub -> {
- ToDeviceSessionActorMsg response = new BasicToDeviceSessionActorMsg(rpcRequest, sub.getKey());
+ ActorSystemToDeviceSessionActorMsg response = new BasicActorSystemToDeviceSessionActorMsg(rpcRequest, sub.getKey());
sendMsgToSessionActor(response, sub.getValue().getServer());
if (SessionType.SYNC == sub.getValue().getType()) {
syncSessionSet.add(sub.getKey());
@@ -125,9 +163,8 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
syncSessionSet.forEach(rpcSubscriptions::remove);
if (request.isOneway() && sent) {
- ToPluginRpcResponseDeviceMsg responsePluginMsg = toPluginRpcResponseMsg(msg, (String) null);
- context.parent().tell(responsePluginMsg, ActorRef.noSender());
logger.debug("[{}] Rpc command response sent [{}]!", deviceId, request.getId());
+ systemContext.getDeviceRpcService().processRpcResponseFromDevice(new FromDeviceRpcResponse(msg.getMsg().getId(), msg.getServerAddress(), null, null));
} else {
registerPendingRpcRequest(context, msg, sent, rpcRequest, timeout);
}
@@ -139,24 +176,46 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
}
- private void registerPendingRpcRequest(ActorContext context, ToDeviceRpcRequestPluginMsg msg, boolean sent, ToDeviceRpcRequestMsg rpcRequest, long timeout) {
- rpcPendingMap.put(rpcRequest.getRequestId(), new ToDeviceRpcRequestMetadata(msg, sent));
- TimeoutIntMsg timeoutMsg = new TimeoutIntMsg(rpcRequest.getRequestId(), timeout);
+ private void registerPendingRpcRequest(ActorContext context, ToDeviceRpcRequestActorMsg msg, boolean sent, ToDeviceRpcRequestMsg rpcRequest, long timeout) {
+ toDeviceRpcPendingMap.put(rpcRequest.getRequestId(), new ToDeviceRpcRequestMetadata(msg, sent));
+ DeviceActorServerSideRpcTimeoutMsg timeoutMsg = new DeviceActorServerSideRpcTimeoutMsg(rpcRequest.getRequestId(), timeout);
scheduleMsgWithDelay(context, timeoutMsg, timeoutMsg.getTimeout());
}
- public void processTimeout(ActorContext context, TimeoutMsg msg) {
- ToDeviceRpcRequestMetadata requestMd = rpcPendingMap.remove(msg.getId());
+ void processServerSideRpcTimeout(ActorContext context, DeviceActorServerSideRpcTimeoutMsg msg) {
+ ToDeviceRpcRequestMetadata requestMd = toDeviceRpcPendingMap.remove(msg.getId());
if (requestMd != null) {
logger.debug("[{}] RPC request [{}] timeout detected!", deviceId, msg.getId());
- ToPluginRpcResponseDeviceMsg responsePluginMsg = toPluginRpcResponseMsg(requestMd.getMsg(), requestMd.isSent() ? RpcError.TIMEOUT : RpcError.NO_ACTIVE_CONNECTION);
- context.parent().tell(responsePluginMsg, ActorRef.noSender());
+ systemContext.getDeviceRpcService().processRpcResponseFromDevice(new FromDeviceRpcResponse(requestMd.getMsg().getMsg().getId(),
+ requestMd.getMsg().getServerAddress(), null, requestMd.isSent() ? RpcError.TIMEOUT : RpcError.NO_ACTIVE_CONNECTION));
+ }
+ }
+
+ void processQueueTimeout(ActorContext context, DeviceActorQueueTimeoutMsg msg) {
+ PendingSessionMsgData data = pendingMsgs.remove(msg.getId());
+ if (data != null) {
+ logger.debug("[{}] Queue put [{}] timeout detected!", deviceId, msg.getId());
+ ToDeviceMsg toDeviceMsg = new RuleEngineErrorMsg(data.getSessionMsgType(), RuleEngineError.QUEUE_PUT_TIMEOUT);
+ sendMsgToSessionActor(new BasicActorSystemToDeviceSessionActorMsg(toDeviceMsg, data.getSessionId()), data.getServerAddress());
+ }
+ }
+
+ void processQueueAck(ActorContext context, RuleEngineQueuePutAckMsg msg) {
+ PendingSessionMsgData data = pendingMsgs.remove(msg.getId());
+ if (data != null && data.isReplyOnQueueAck()) {
+ int remainingAcks = data.getAckMsgCount() - 1;
+ data.setAckMsgCount(remainingAcks);
+ logger.debug("[{}] Queue put [{}] ack detected. Remaining acks: {}!", deviceId, msg.getId(), remainingAcks);
+ if (remainingAcks == 0) {
+ ToDeviceMsg toDeviceMsg = BasicStatusCodeResponse.onSuccess(data.getSessionMsgType(), data.getRequestId());
+ sendMsgToSessionActor(new BasicActorSystemToDeviceSessionActorMsg(toDeviceMsg, data.getSessionId()), data.getServerAddress());
+ }
}
}
private void sendPendingRequests(ActorContext context, SessionId sessionId, SessionType type, Optional server) {
- if (!rpcPendingMap.isEmpty()) {
- logger.debug("[{}] Pushing {} pending RPC messages to new async session [{}]", deviceId, rpcPendingMap.size(), sessionId);
+ if (!toDeviceRpcPendingMap.isEmpty()) {
+ logger.debug("[{}] Pushing {} pending RPC messages to new async session [{}]", deviceId, toDeviceRpcPendingMap.size(), sessionId);
if (type == SessionType.SYNC) {
logger.debug("[{}] Cleanup sync rpc session [{}]", deviceId, sessionId);
rpcSubscriptions.remove(sessionId);
@@ -166,41 +225,196 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
}
Set sentOneWayIds = new HashSet<>();
if (type == SessionType.ASYNC) {
- rpcPendingMap.entrySet().forEach(processPendingRpc(context, sessionId, server, sentOneWayIds));
+ toDeviceRpcPendingMap.entrySet().forEach(processPendingRpc(context, sessionId, server, sentOneWayIds));
} else {
- rpcPendingMap.entrySet().stream().findFirst().ifPresent(processPendingRpc(context, sessionId, server, sentOneWayIds));
+ toDeviceRpcPendingMap.entrySet().stream().findFirst().ifPresent(processPendingRpc(context, sessionId, server, sentOneWayIds));
}
- sentOneWayIds.forEach(rpcPendingMap::remove);
+ sentOneWayIds.forEach(toDeviceRpcPendingMap::remove);
}
private Consumer> processPendingRpc(ActorContext context, SessionId sessionId, Optional server, Set sentOneWayIds) {
return entry -> {
+ ToDeviceRpcRequestActorMsg requestActorMsg = entry.getValue().getMsg();
ToDeviceRpcRequest request = entry.getValue().getMsg().getMsg();
ToDeviceRpcRequestBody body = request.getBody();
if (request.isOneway()) {
sentOneWayIds.add(entry.getKey());
- ToPluginRpcResponseDeviceMsg responsePluginMsg = toPluginRpcResponseMsg(entry.getValue().getMsg(), (String) null);
- context.parent().tell(responsePluginMsg, ActorRef.noSender());
+ systemContext.getDeviceRpcService().processRpcResponseFromDevice(new FromDeviceRpcResponse(request.getId(), requestActorMsg.getServerAddress(), null, null));
}
ToDeviceRpcRequestMsg rpcRequest = new ToDeviceRpcRequestMsg(
entry.getKey(),
body.getMethod(),
body.getParams()
);
- ToDeviceSessionActorMsg response = new BasicToDeviceSessionActorMsg(rpcRequest, sessionId);
+ ActorSystemToDeviceSessionActorMsg response = new BasicActorSystemToDeviceSessionActorMsg(rpcRequest, sessionId);
sendMsgToSessionActor(response, server);
};
}
- void process(ActorContext context, ToDeviceActorMsg msg) {
+ void process(ActorContext context, DeviceToDeviceActorMsg msg) {
processSubscriptionCommands(context, msg);
processRpcResponses(context, msg);
processSessionStateMsgs(msg);
+
+ SessionMsgType sessionMsgType = msg.getPayload().getMsgType();
+ if (sessionMsgType.requiresRulesProcessing()) {
+ switch (sessionMsgType) {
+ case GET_ATTRIBUTES_REQUEST:
+ handleGetAttributesRequest(msg);
+ break;
+ case POST_ATTRIBUTES_REQUEST:
+ handlePostAttributesRequest(context, msg);
+ reportActivity();
+ break;
+ case POST_TELEMETRY_REQUEST:
+ handlePostTelemetryRequest(context, msg);
+ reportActivity();
+ break;
+ case TO_SERVER_RPC_REQUEST:
+ handleClientSideRPCRequest(context, msg);
+ reportActivity();
+ break;
+ }
+ }
+ }
+
+ private void reportActivity() {
+ systemContext.getDeviceStateService().onDeviceActivity(deviceId);
+ }
+
+ private void reportSessionOpen() {
+ systemContext.getDeviceStateService().onDeviceConnect(deviceId);
+ }
+
+ private void reportSessionClose() {
+ systemContext.getDeviceStateService().onDeviceDisconnect(deviceId);
+ }
+
+ private void handleGetAttributesRequest(DeviceToDeviceActorMsg src) {
+ GetAttributesRequest request = (GetAttributesRequest) src.getPayload();
+ ListenableFuture> clientAttributesFuture = getAttributeKvEntries(deviceId, DataConstants.CLIENT_SCOPE, request.getClientAttributeNames());
+ ListenableFuture> sharedAttributesFuture = getAttributeKvEntries(deviceId, DataConstants.SHARED_SCOPE, request.getClientAttributeNames());
+
+ Futures.addCallback(Futures.allAsList(Arrays.asList(clientAttributesFuture, sharedAttributesFuture)), new FutureCallback>>() {
+ @Override
+ public void onSuccess(@Nullable List> result) {
+ BasicGetAttributesResponse response = BasicGetAttributesResponse.onSuccess(request.getMsgType(),
+ request.getRequestId(), BasicAttributeKVMsg.from(result.get(0), result.get(1)));
+ sendMsgToSessionActor(new BasicActorSystemToDeviceSessionActorMsg(response, src.getSessionId()), src.getServerAddress());
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ if (t instanceof Exception) {
+ ToDeviceMsg toDeviceMsg = BasicStatusCodeResponse.onError(SessionMsgType.GET_ATTRIBUTES_REQUEST, request.getRequestId(), (Exception) t);
+ sendMsgToSessionActor(new BasicActorSystemToDeviceSessionActorMsg(toDeviceMsg, src.getSessionId()), src.getServerAddress());
+ } else {
+ logger.error("[{}] Failed to process attributes request", deviceId, t);
+ }
+ }
+ });
+ }
+
+ private ListenableFuture> getAttributeKvEntries(DeviceId deviceId, String scope, Optional> names) {
+ if (names.isPresent()) {
+ if (!names.get().isEmpty()) {
+ return systemContext.getAttributesService().find(deviceId, scope, names.get());
+ } else {
+ return systemContext.getAttributesService().findAll(deviceId, scope);
+ }
+ } else {
+ return Futures.immediateFuture(Collections.emptyList());
+ }
+ }
+
+ private void handlePostAttributesRequest(ActorContext context, DeviceToDeviceActorMsg src) {
+ AttributesUpdateRequest request = (AttributesUpdateRequest) src.getPayload();
+
+ JsonObject json = new JsonObject();
+ for (AttributeKvEntry kv : request.getAttributes()) {
+ kv.getBooleanValue().ifPresent(v -> json.addProperty(kv.getKey(), v));
+ kv.getLongValue().ifPresent(v -> json.addProperty(kv.getKey(), v));
+ kv.getDoubleValue().ifPresent(v -> json.addProperty(kv.getKey(), v));
+ kv.getStrValue().ifPresent(v -> json.addProperty(kv.getKey(), v));
+ }
+
+ TbMsg tbMsg = new TbMsg(UUIDs.timeBased(), SessionMsgType.POST_ATTRIBUTES_REQUEST.name(), deviceId, defaultMetaData.copy(), TbMsgDataType.JSON, gson.toJson(json), null, null, 0L);
+ PendingSessionMsgData msgData = new PendingSessionMsgData(src.getSessionId(), src.getServerAddress(),
+ SessionMsgType.POST_ATTRIBUTES_REQUEST, request.getRequestId(), true, 1);
+ pushToRuleEngineWithTimeout(context, tbMsg, msgData);
+ }
+
+ private void handlePostTelemetryRequest(ActorContext context, DeviceToDeviceActorMsg src) {
+ TelemetryUploadRequest request = (TelemetryUploadRequest) src.getPayload();
+
+ Map> tsData = request.getData();
+
+ PendingSessionMsgData msgData = new PendingSessionMsgData(src.getSessionId(), src.getServerAddress(),
+ SessionMsgType.POST_TELEMETRY_REQUEST, request.getRequestId(), true, tsData.size());
+
+ for (Map.Entry> entry : tsData.entrySet()) {
+ JsonObject json = new JsonObject();
+ for (KvEntry kv : entry.getValue()) {
+ kv.getBooleanValue().ifPresent(v -> json.addProperty(kv.getKey(), v));
+ kv.getLongValue().ifPresent(v -> json.addProperty(kv.getKey(), v));
+ kv.getDoubleValue().ifPresent(v -> json.addProperty(kv.getKey(), v));
+ kv.getStrValue().ifPresent(v -> json.addProperty(kv.getKey(), v));
+ }
+ TbMsgMetaData metaData = defaultMetaData.copy();
+ metaData.putValue("ts", entry.getKey() + "");
+ TbMsg tbMsg = new TbMsg(UUIDs.timeBased(), SessionMsgType.POST_TELEMETRY_REQUEST.name(), deviceId, metaData, TbMsgDataType.JSON, gson.toJson(json), null, null, 0L);
+ pushToRuleEngineWithTimeout(context, tbMsg, msgData);
+ }
+ }
+
+ private void handleClientSideRPCRequest(ActorContext context, DeviceToDeviceActorMsg src) {
+ ToServerRpcRequestMsg request = (ToServerRpcRequestMsg) src.getPayload();
+
+ JsonObject json = new JsonObject();
+ json.addProperty("method", request.getMethod());
+ json.add("params", jsonParser.parse(request.getParams()));
+
+ TbMsgMetaData requestMetaData = defaultMetaData.copy();
+ requestMetaData.putValue("requestId", Integer.toString(request.getRequestId()));
+ TbMsg tbMsg = new TbMsg(UUIDs.timeBased(), SessionMsgType.TO_SERVER_RPC_REQUEST.name(), deviceId, requestMetaData, TbMsgDataType.JSON, gson.toJson(json), null, null, 0L);
+ PendingSessionMsgData msgData = new PendingSessionMsgData(src.getSessionId(), src.getServerAddress(), SessionMsgType.TO_SERVER_RPC_REQUEST, request.getRequestId(), false, 1);
+ pushToRuleEngineWithTimeout(context, tbMsg, msgData);
+
+ scheduleMsgWithDelay(context, new DeviceActorClientSideRpcTimeoutMsg(request.getRequestId(), systemContext.getClientSideRpcTimeout()), systemContext.getClientSideRpcTimeout());
+ toServerRpcPendingMap.put(request.getRequestId(), new ToServerRpcRequestMetadata(src.getSessionId(), src.getSessionType(), src.getServerAddress()));
+ }
+
+ public void processClientSideRpcTimeout(ActorContext context, DeviceActorClientSideRpcTimeoutMsg msg) {
+ ToServerRpcRequestMetadata data = toServerRpcPendingMap.remove(msg.getId());
+ if (data != null) {
+ logger.debug("[{}] Client side RPC request [{}] timeout detected!", deviceId, msg.getId());
+ ToDeviceMsg toDeviceMsg = new RuleEngineErrorMsg(SessionMsgType.TO_SERVER_RPC_REQUEST, RuleEngineError.TIMEOUT);
+ sendMsgToSessionActor(new BasicActorSystemToDeviceSessionActorMsg(toDeviceMsg, data.getSessionId()), data.getServer());
+ }
+ }
+
+ void processToServerRPCResponse(ActorContext context, ToServerRpcResponseActorMsg msg) {
+ ToServerRpcRequestMetadata data = toServerRpcPendingMap.remove(msg.getMsg().getRequestId());
+ if (data != null) {
+ sendMsgToSessionActor(new BasicActorSystemToDeviceSessionActorMsg(msg.getMsg(), data.getSessionId()), data.getServer());
+ }
+ }
+
+ private void pushToRuleEngineWithTimeout(ActorContext context, TbMsg tbMsg, PendingSessionMsgData pendingMsgData) {
+ SessionMsgType sessionMsgType = pendingMsgData.getSessionMsgType();
+ int requestId = pendingMsgData.getRequestId();
+ if (systemContext.isQueuePersistenceEnabled()) {
+ pendingMsgs.put(tbMsg.getId(), pendingMsgData);
+ scheduleMsgWithDelay(context, new DeviceActorQueueTimeoutMsg(tbMsg.getId(), systemContext.getQueuePersistenceTimeout()), systemContext.getQueuePersistenceTimeout());
+ } else {
+ ActorSystemToDeviceSessionActorMsg response = new BasicActorSystemToDeviceSessionActorMsg(BasicStatusCodeResponse.onSuccess(sessionMsgType, requestId), pendingMsgData.getSessionId());
+ sendMsgToSessionActor(response, pendingMsgData.getServerAddress());
+ }
+ context.parent().tell(new DeviceActorToRuleEngineMsg(context.self(), tbMsg), context.self());
}
void processAttributesUpdate(ActorContext context, DeviceAttributesEventNotificationMsg msg) {
- refreshAttributes(msg);
if (attributeSubscriptions.size() > 0) {
ToDeviceMsg notification = null;
if (msg.isDeleted()) {
@@ -221,7 +435,7 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
if (notification != null) {
ToDeviceMsg finalNotification = notification;
attributeSubscriptions.entrySet().forEach(sub -> {
- ToDeviceSessionActorMsg response = new BasicToDeviceSessionActorMsg(finalNotification, sub.getKey());
+ ActorSystemToDeviceSessionActorMsg response = new BasicActorSystemToDeviceSessionActorMsg(finalNotification, sub.getKey());
sendMsgToSessionActor(response, sub.getValue().getServer());
});
}
@@ -230,50 +444,30 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
}
}
- void process(ActorContext context, RuleChainDeviceMsg srcMsg) {
- ChainProcessingMetaData md = new ChainProcessingMetaData(srcMsg.getRuleChain(),
- srcMsg.getToDeviceActorMsg(), new DeviceMetaData(deviceId, deviceName, deviceType, deviceAttributes), context.self());
- ChainProcessingContext ctx = new ChainProcessingContext(md);
- if (ctx.getChainLength() > 0) {
- RuleProcessingMsg msg = new RuleProcessingMsg(ctx);
- ActorRef ruleActorRef = ctx.getCurrentActor();
- ruleActorRef.tell(msg, ActorRef.noSender());
- } else {
- context.self().tell(new RulesProcessedMsg(ctx), context.self());
- }
- }
-
- void processRpcResponses(ActorContext context, ToDeviceActorMsg msg) {
+ private void processRpcResponses(ActorContext context, DeviceToDeviceActorMsg msg) {
SessionId sessionId = msg.getSessionId();
FromDeviceMsg inMsg = msg.getPayload();
- if (inMsg.getMsgType() == MsgType.TO_DEVICE_RPC_RESPONSE) {
+ if (inMsg.getMsgType() == SessionMsgType.TO_DEVICE_RPC_RESPONSE) {
logger.debug("[{}] Processing rpc command response [{}]", deviceId, sessionId);
ToDeviceRpcResponseMsg responseMsg = (ToDeviceRpcResponseMsg) inMsg;
- ToDeviceRpcRequestMetadata requestMd = rpcPendingMap.remove(responseMsg.getRequestId());
+ ToDeviceRpcRequestMetadata requestMd = toDeviceRpcPendingMap.remove(responseMsg.getRequestId());
boolean success = requestMd != null;
if (success) {
- ToPluginRpcResponseDeviceMsg responsePluginMsg = toPluginRpcResponseMsg(requestMd.getMsg(), responseMsg.getData());
- Optional pluginServerAddress = requestMd.getMsg().getServerAddress();
- if (pluginServerAddress.isPresent()) {
- systemContext.getRpcService().tell(pluginServerAddress.get(), responsePluginMsg);
- logger.debug("[{}] Rpc command response sent to remote plugin actor [{}]!", deviceId, requestMd.getMsg().getMsg().getId());
- } else {
- context.parent().tell(responsePluginMsg, ActorRef.noSender());
- logger.debug("[{}] Rpc command response sent to local plugin actor [{}]!", deviceId, requestMd.getMsg().getMsg().getId());
- }
+ systemContext.getDeviceRpcService().processRpcResponseFromDevice(new FromDeviceRpcResponse(requestMd.getMsg().getMsg().getId(),
+ requestMd.getMsg().getServerAddress(), responseMsg.getData(), null));
} else {
logger.debug("[{}] Rpc command response [{}] is stale!", deviceId, responseMsg.getRequestId());
}
if (msg.getSessionType() == SessionType.SYNC) {
BasicCommandAckResponse response = success
- ? BasicCommandAckResponse.onSuccess(MsgType.TO_DEVICE_RPC_REQUEST, responseMsg.getRequestId())
- : BasicCommandAckResponse.onError(MsgType.TO_DEVICE_RPC_REQUEST, responseMsg.getRequestId(), new TimeoutException());
- sendMsgToSessionActor(new BasicToDeviceSessionActorMsg(response, msg.getSessionId()), msg.getServerAddress());
+ ? BasicCommandAckResponse.onSuccess(SessionMsgType.TO_DEVICE_RPC_REQUEST, responseMsg.getRequestId())
+ : BasicCommandAckResponse.onError(SessionMsgType.TO_DEVICE_RPC_REQUEST, responseMsg.getRequestId(), new TimeoutException());
+ sendMsgToSessionActor(new BasicActorSystemToDeviceSessionActorMsg(response, msg.getSessionId()), msg.getServerAddress());
}
}
}
- public void processClusterEventMsg(ClusterEventMsg msg) {
+ void processClusterEventMsg(ClusterEventMsg msg) {
if (!msg.isAdded()) {
logger.debug("[{}] Clearing attributes/rpc subscription for server [{}]", deviceId, msg.getServerAddress());
Predicate> filter = e -> e.getValue().getServer()
@@ -283,102 +477,79 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
}
}
- private ToPluginRpcResponseDeviceMsg toPluginRpcResponseMsg(ToDeviceRpcRequestPluginMsg requestMsg, String data) {
- return toPluginRpcResponseMsg(requestMsg, data, null);
- }
-
- private ToPluginRpcResponseDeviceMsg toPluginRpcResponseMsg(ToDeviceRpcRequestPluginMsg requestMsg, RpcError error) {
- return toPluginRpcResponseMsg(requestMsg, null, error);
- }
-
- private ToPluginRpcResponseDeviceMsg toPluginRpcResponseMsg(ToDeviceRpcRequestPluginMsg requestMsg, String data, RpcError error) {
- return new ToPluginRpcResponseDeviceMsg(
- requestMsg.getPluginId(),
- requestMsg.getPluginTenantId(),
- new FromDeviceRpcResponse(requestMsg.getMsg().getId(),
- data,
- error
- )
- );
- }
-
- void onRulesProcessedMsg(ActorContext context, RulesProcessedMsg msg) {
- ChainProcessingContext ctx = msg.getCtx();
- ToDeviceActorMsg inMsg = ctx.getInMsg();
- SessionId sid = inMsg.getSessionId();
- ToDeviceSessionActorMsg response;
- if (ctx.getResponse() != null) {
- response = new BasicToDeviceSessionActorMsg(ctx.getResponse(), sid);
- } else {
- response = new BasicToDeviceSessionActorMsg(ctx.getError(), sid);
- }
- sendMsgToSessionActor(response, inMsg.getServerAddress());
- }
-
- private void processSubscriptionCommands(ActorContext context, ToDeviceActorMsg msg) {
+ private void processSubscriptionCommands(ActorContext context, DeviceToDeviceActorMsg msg) {
SessionId sessionId = msg.getSessionId();
SessionType sessionType = msg.getSessionType();
FromDeviceMsg inMsg = msg.getPayload();
- if (inMsg.getMsgType() == MsgType.SUBSCRIBE_ATTRIBUTES_REQUEST) {
+ if (inMsg.getMsgType() == SessionMsgType.SUBSCRIBE_ATTRIBUTES_REQUEST) {
logger.debug("[{}] Registering attributes subscription for session [{}]", deviceId, sessionId);
attributeSubscriptions.put(sessionId, new SessionInfo(sessionType, msg.getServerAddress()));
- } else if (inMsg.getMsgType() == MsgType.UNSUBSCRIBE_ATTRIBUTES_REQUEST) {
+ } else if (inMsg.getMsgType() == SessionMsgType.UNSUBSCRIBE_ATTRIBUTES_REQUEST) {
logger.debug("[{}] Canceling attributes subscription for session [{}]", deviceId, sessionId);
attributeSubscriptions.remove(sessionId);
- } else if (inMsg.getMsgType() == MsgType.SUBSCRIBE_RPC_COMMANDS_REQUEST) {
+ } else if (inMsg.getMsgType() == SessionMsgType.SUBSCRIBE_RPC_COMMANDS_REQUEST) {
logger.debug("[{}] Registering rpc subscription for session [{}][{}]", deviceId, sessionId, sessionType);
rpcSubscriptions.put(sessionId, new SessionInfo(sessionType, msg.getServerAddress()));
sendPendingRequests(context, sessionId, sessionType, msg.getServerAddress());
- } else if (inMsg.getMsgType() == MsgType.UNSUBSCRIBE_RPC_COMMANDS_REQUEST) {
+ } else if (inMsg.getMsgType() == SessionMsgType.UNSUBSCRIBE_RPC_COMMANDS_REQUEST) {
logger.debug("[{}] Canceling rpc subscription for session [{}][{}]", deviceId, sessionId, sessionType);
rpcSubscriptions.remove(sessionId);
}
}
- private void processSessionStateMsgs(ToDeviceActorMsg msg) {
+ private void processSessionStateMsgs(DeviceToDeviceActorMsg msg) {
SessionId sessionId = msg.getSessionId();
FromDeviceMsg inMsg = msg.getPayload();
if (inMsg instanceof SessionOpenMsg) {
logger.debug("[{}] Processing new session [{}]", deviceId, sessionId);
+ if (sessions.size() >= systemContext.getMaxConcurrentSessionsPerDevice()) {
+ SessionId sessionIdToRemove = sessions.keySet().stream().findFirst().orElse(null);
+ if (sessionIdToRemove != null) {
+ closeSession(sessionIdToRemove, sessions.remove(sessionIdToRemove));
+ }
+ }
sessions.put(sessionId, new SessionInfo(SessionType.ASYNC, msg.getServerAddress()));
+ if (sessions.size() == 1) {
+ reportSessionOpen();
+ }
} else if (inMsg instanceof SessionCloseMsg) {
logger.debug("[{}] Canceling subscriptions for closed session [{}]", deviceId, sessionId);
sessions.remove(sessionId);
attributeSubscriptions.remove(sessionId);
rpcSubscriptions.remove(sessionId);
+ if (sessions.isEmpty()) {
+ reportSessionClose();
+ }
}
}
- private void sendMsgToSessionActor(ToDeviceSessionActorMsg response, Optional sessionAddress) {
+ private void sendMsgToSessionActor(ActorSystemToDeviceSessionActorMsg response, Optional sessionAddress) {
if (sessionAddress.isPresent()) {
ServerAddress address = sessionAddress.get();
logger.debug("{} Forwarding msg: {}", address, response);
- systemContext.getRpcService().tell(sessionAddress.get(), response);
+ systemContext.getRpcService().tell(systemContext.getEncodingService()
+ .convertToProtoDataMessage(sessionAddress.get(), response));
} else {
systemContext.getSessionManagerActor().tell(response, ActorRef.noSender());
}
}
- private List fetchAttributes(String scope) {
- try {
- //TODO: replace this with async operation. Happens only during actor creation, but is still criticla for performance,
- return systemContext.getAttributesService().findAll(this.deviceId, scope).get();
- } catch (InterruptedException | ExecutionException e) {
- logger.warning("[{}] Failed to fetch attributes for scope: {}", deviceId, scope);
- throw new RuntimeException(e);
- }
- }
-
- public void processCredentialsUpdate() {
- sessions.forEach((k, v) -> {
- sendMsgToSessionActor(new BasicToDeviceSessionActorMsg(new SessionCloseNotification(), k), v.getServer());
- });
+ void processCredentialsUpdate() {
+ sessions.forEach(this::closeSession);
attributeSubscriptions.clear();
rpcSubscriptions.clear();
}
- public void processNameOrTypeUpdate(DeviceNameOrTypeUpdateMsg msg) {
+ private void closeSession(SessionId sessionId, SessionInfo sessionInfo) {
+ sendMsgToSessionActor(new BasicActorSystemToDeviceSessionActorMsg(new SessionCloseNotification(), sessionId), sessionInfo.getServer());
+ }
+
+ void processNameOrTypeUpdate(DeviceNameOrTypeUpdateMsg msg) {
this.deviceName = msg.getDeviceName();
this.deviceType = msg.getDeviceType();
+ this.defaultMetaData = new TbMsgMetaData();
+ this.defaultMetaData.putValue("deviceName", deviceName);
+ this.defaultMetaData.putValue("deviceType", deviceType);
}
+
}
diff --git a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorToRuleEngineMsg.java b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorToRuleEngineMsg.java
new file mode 100644
index 0000000000..adc5531da5
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorToRuleEngineMsg.java
@@ -0,0 +1,37 @@
+/**
+ * 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.device;
+
+import akka.actor.ActorRef;
+import lombok.Data;
+import org.thingsboard.server.common.msg.MsgType;
+import org.thingsboard.server.common.msg.TbActorMsg;
+import org.thingsboard.server.common.msg.TbMsg;
+
+/**
+ * Created by ashvayka on 15.03.18.
+ */
+@Data
+public final class DeviceActorToRuleEngineMsg implements TbActorMsg {
+
+ private final ActorRef callbackRef;
+ private final TbMsg tbMsg;
+
+ @Override
+ public MsgType getMsgType() {
+ return MsgType.DEVICE_ACTOR_TO_RULE_ENGINE_MSG;
+ }
+}
diff --git a/application/src/main/java/org/thingsboard/server/actors/device/PendingSessionMsgData.java b/application/src/main/java/org/thingsboard/server/actors/device/PendingSessionMsgData.java
new file mode 100644
index 0000000000..23ad966049
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/actors/device/PendingSessionMsgData.java
@@ -0,0 +1,40 @@
+/**
+ * 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.device;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import org.thingsboard.server.common.data.id.SessionId;
+import org.thingsboard.server.common.msg.cluster.ServerAddress;
+import org.thingsboard.server.common.msg.session.SessionMsgType;
+
+import java.util.Optional;
+
+/**
+ * Created by ashvayka on 17.04.18.
+ */
+@Data
+@AllArgsConstructor
+public final class PendingSessionMsgData {
+
+ private final SessionId sessionId;
+ private final Optional serverAddress;
+ private final SessionMsgType sessionMsgType;
+ private final int requestId;
+ private final boolean replyOnQueueAck;
+ private int ackMsgCount;
+
+}
diff --git a/application/src/main/java/org/thingsboard/server/actors/device/RuleEngineQueuePutAckMsg.java b/application/src/main/java/org/thingsboard/server/actors/device/RuleEngineQueuePutAckMsg.java
new file mode 100644
index 0000000000..0b6fe54c4a
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/actors/device/RuleEngineQueuePutAckMsg.java
@@ -0,0 +1,36 @@
+/**
+ * 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.device;
+
+import lombok.Data;
+import org.thingsboard.server.common.msg.MsgType;
+import org.thingsboard.server.common.msg.TbActorMsg;
+
+import java.util.UUID;
+
+/**
+ * Created by ashvayka on 15.03.18.
+ */
+@Data
+public final class RuleEngineQueuePutAckMsg implements TbActorMsg {
+
+ private final UUID id;
+
+ @Override
+ public MsgType getMsgType() {
+ return MsgType.RULE_ENGINE_QUEUE_PUT_ACK_MSG;
+ }
+}
diff --git a/application/src/main/java/org/thingsboard/server/actors/device/SessionInfo.java b/application/src/main/java/org/thingsboard/server/actors/device/SessionInfo.java
index e039b9634f..04c457c92e 100644
--- a/application/src/main/java/org/thingsboard/server/actors/device/SessionInfo.java
+++ b/application/src/main/java/org/thingsboard/server/actors/device/SessionInfo.java
@@ -16,7 +16,6 @@
package org.thingsboard.server.actors.device;
import lombok.Data;
-import org.thingsboard.server.common.data.id.SessionId;
import org.thingsboard.server.common.msg.cluster.ServerAddress;
import org.thingsboard.server.common.msg.session.SessionType;
diff --git a/application/src/main/java/org/thingsboard/server/actors/device/ToDeviceRpcRequestMetadata.java b/application/src/main/java/org/thingsboard/server/actors/device/ToDeviceRpcRequestMetadata.java
index 01342fd866..8a4262c5b2 100644
--- a/application/src/main/java/org/thingsboard/server/actors/device/ToDeviceRpcRequestMetadata.java
+++ b/application/src/main/java/org/thingsboard/server/actors/device/ToDeviceRpcRequestMetadata.java
@@ -16,13 +16,13 @@
package org.thingsboard.server.actors.device;
import lombok.Data;
-import org.thingsboard.server.extensions.api.plugins.msg.ToDeviceRpcRequestPluginMsg;
+import org.thingsboard.server.service.rpc.ToDeviceRpcRequestActorMsg;
/**
* @author Andrew Shvayka
*/
@Data
public class ToDeviceRpcRequestMetadata {
- private final ToDeviceRpcRequestPluginMsg msg;
+ private final ToDeviceRpcRequestActorMsg msg;
private final boolean sent;
}
diff --git a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/rpc/RpcMsg.java b/application/src/main/java/org/thingsboard/server/actors/device/ToServerRpcRequestMetadata.java
similarity index 65%
rename from extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/rpc/RpcMsg.java
rename to application/src/main/java/org/thingsboard/server/actors/device/ToServerRpcRequestMetadata.java
index 91abaaec9d..f82a8c2481 100644
--- a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/rpc/RpcMsg.java
+++ b/application/src/main/java/org/thingsboard/server/actors/device/ToServerRpcRequestMetadata.java
@@ -13,23 +13,21 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.thingsboard.server.extensions.api.plugins.rpc;
+package org.thingsboard.server.actors.device;
-import lombok.Getter;
-import lombok.RequiredArgsConstructor;
-import lombok.ToString;
+import lombok.Data;
+import org.thingsboard.server.common.data.id.SessionId;
import org.thingsboard.server.common.msg.cluster.ServerAddress;
+import org.thingsboard.server.common.msg.session.SessionType;
+
+import java.util.Optional;
/**
* @author Andrew Shvayka
*/
-@ToString
-@RequiredArgsConstructor
-public class RpcMsg {
- @Getter
- private final ServerAddress serverAddress;
- @Getter
- private final int msgClazz;
- @Getter
- private final byte[] msgData;
+@Data
+public class ToServerRpcRequestMetadata {
+ private final SessionId sessionId;
+ private final SessionType type;
+ private final Optional server;
}
diff --git a/application/src/main/java/org/thingsboard/server/actors/plugin/PluginActor.java b/application/src/main/java/org/thingsboard/server/actors/plugin/PluginActor.java
deleted file mode 100644
index 265da386ff..0000000000
--- a/application/src/main/java/org/thingsboard/server/actors/plugin/PluginActor.java
+++ /dev/null
@@ -1,151 +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.plugin;
-
-import akka.actor.ActorContext;
-import akka.actor.ActorRef;
-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.PluginId;
-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.TimeoutMsg;
-import org.thingsboard.server.extensions.api.plugins.msg.ToPluginRpcResponseDeviceMsg;
-import org.thingsboard.server.extensions.api.plugins.rest.PluginRestMsg;
-import org.thingsboard.server.extensions.api.plugins.rpc.PluginRpcMsg;
-import org.thingsboard.server.extensions.api.plugins.ws.msg.PluginWebsocketMsg;
-import org.thingsboard.server.extensions.api.rules.RuleException;
-
-public class PluginActor extends ComponentActor {
-
- private PluginActor(ActorSystemContext systemContext, TenantId tenantId, PluginId pluginId) {
- super(systemContext, tenantId, pluginId);
- setProcessor(new PluginActorMessageProcessor(tenantId, pluginId, systemContext,
- logger, context().parent(), context().self()));
- }
-
- @Override
- public void onReceive(Object msg) throws Exception {
- if (msg instanceof PluginWebsocketMsg) {
- onWebsocketMsg((PluginWebsocketMsg>) msg);
- } else if (msg instanceof PluginRestMsg) {
- onRestMsg((PluginRestMsg) msg);
- } else if (msg instanceof PluginCallbackMessage) {
- onPluginCallback((PluginCallbackMessage) msg);
- } else if (msg instanceof RuleToPluginMsgWrapper) {
- onRuleToPluginMsg((RuleToPluginMsgWrapper) msg);
- } else if (msg instanceof PluginRpcMsg) {
- onRpcMsg((PluginRpcMsg) msg);
- } else if (msg instanceof ClusterEventMsg) {
- onClusterEventMsg((ClusterEventMsg) msg);
- } else if (msg instanceof ComponentLifecycleMsg) {
- onComponentLifecycleMsg((ComponentLifecycleMsg) msg);
- } else if (msg instanceof ToPluginRpcResponseDeviceMsg) {
- onRpcResponse((ToPluginRpcResponseDeviceMsg) msg);
- } else if (msg instanceof PluginTerminationMsg) {
- logger.info("[{}][{}] Going to terminate plugin actor.", tenantId, id);
- context().parent().tell(msg, ActorRef.noSender());
- context().stop(self());
- } else if (msg instanceof TimeoutMsg) {
- onTimeoutMsg(context(), (TimeoutMsg) msg);
- } else if (msg instanceof StatsPersistTick) {
- onStatsPersistTick(id);
- } else {
- logger.debug("[{}][{}] Unknown msg type.", tenantId, id, msg.getClass().getName());
- }
- }
-
- private void onPluginCallback(PluginCallbackMessage msg) {
- try {
- processor.onPluginCallbackMsg(msg);
- } catch (Exception e) {
- logAndPersist("onPluginCallbackMsg", e);
- }
- }
-
- private void onTimeoutMsg(ActorContext context, TimeoutMsg msg) {
- processor.onTimeoutMsg(context, msg);
- }
-
- private void onRpcResponse(ToPluginRpcResponseDeviceMsg msg) {
- processor.onDeviceRpcMsg(msg.getResponse());
- }
-
- private void onRuleToPluginMsg(RuleToPluginMsgWrapper msg) throws RuleException {
- logger.debug("[{}] Going to process rule msg: {}", id, msg.getMsg());
- try {
- processor.onRuleToPluginMsg(msg);
- increaseMessagesProcessedCount();
- } catch (Exception e) {
- logAndPersist("onRuleMsg", e);
- }
- }
-
- private void onWebsocketMsg(PluginWebsocketMsg> msg) {
- logger.debug("[{}] Going to process web socket msg: {}", id, msg);
- try {
- processor.onWebsocketMsg(msg);
- increaseMessagesProcessedCount();
- } catch (Exception e) {
- logAndPersist("onWebsocketMsg", e);
- }
- }
-
- private void onRestMsg(PluginRestMsg msg) {
- logger.debug("[{}] Going to process rest msg: {}", id, msg);
- try {
- processor.onRestMsg(msg);
- increaseMessagesProcessedCount();
- } catch (Exception e) {
- logAndPersist("onRestMsg", e);
- }
- }
-
- private void onRpcMsg(PluginRpcMsg msg) {
- try {
- logger.debug("[{}] Going to process rpc msg: {}", id, msg);
- processor.onRpcMsg(msg);
- } catch (Exception e) {
- logAndPersist("onRpcMsg", e);
- }
- }
-
- public static class ActorCreator extends ContextBasedCreator {
- private static final long serialVersionUID = 1L;
-
- private final TenantId tenantId;
- private final PluginId pluginId;
-
- public ActorCreator(ActorSystemContext context, TenantId tenantId, PluginId pluginId) {
- super(context);
- this.tenantId = tenantId;
- this.pluginId = pluginId;
- }
-
- @Override
- public PluginActor create() throws Exception {
- return new PluginActor(context, tenantId, pluginId);
- }
- }
-
- @Override
- protected long getErrorPersistFrequency() {
- return systemContext.getPluginErrorPersistFrequency();
- }
-}
diff --git a/application/src/main/java/org/thingsboard/server/actors/plugin/PluginActorMessageProcessor.java b/application/src/main/java/org/thingsboard/server/actors/plugin/PluginActorMessageProcessor.java
deleted file mode 100644
index 2af30d4b49..0000000000
--- a/application/src/main/java/org/thingsboard/server/actors/plugin/PluginActorMessageProcessor.java
+++ /dev/null
@@ -1,251 +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.plugin;
-
-import akka.actor.ActorContext;
-import akka.actor.ActorRef;
-import akka.event.LoggingAdapter;
-import com.fasterxml.jackson.core.JsonProcessingException;
-import org.thingsboard.server.actors.ActorSystemContext;
-import org.thingsboard.server.actors.shared.ComponentMsgProcessor;
-import org.thingsboard.server.common.data.id.PluginId;
-import org.thingsboard.server.common.data.id.TenantId;
-import org.thingsboard.server.common.data.plugin.ComponentLifecycleState;
-import org.thingsboard.server.common.data.plugin.ComponentType;
-import org.thingsboard.server.common.data.plugin.PluginMetaData;
-import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;
-import org.thingsboard.server.common.msg.cluster.ServerAddress;
-import org.thingsboard.server.common.msg.core.BasicStatusCodeResponse;
-import org.thingsboard.server.common.msg.session.FromDeviceRequestMsg;
-import org.thingsboard.server.common.msg.session.MsgType;
-import org.thingsboard.server.extensions.api.plugins.Plugin;
-import org.thingsboard.server.extensions.api.plugins.PluginInitializationException;
-import org.thingsboard.server.extensions.api.plugins.msg.FromDeviceRpcResponse;
-import org.thingsboard.server.extensions.api.plugins.msg.ResponsePluginToRuleMsg;
-import org.thingsboard.server.extensions.api.plugins.msg.RuleToPluginMsg;
-import org.thingsboard.server.extensions.api.plugins.msg.TimeoutMsg;
-import org.thingsboard.server.extensions.api.plugins.rest.PluginRestMsg;
-import org.thingsboard.server.extensions.api.plugins.rpc.PluginRpcMsg;
-import org.thingsboard.server.extensions.api.plugins.ws.msg.PluginWebsocketMsg;
-import org.thingsboard.server.extensions.api.rules.RuleException;
-
-/**
- * @author Andrew Shvayka
- */
-public class PluginActorMessageProcessor extends ComponentMsgProcessor {
-
- private final SharedPluginProcessingContext pluginCtx;
- private final PluginProcessingContext trustedCtx;
- private PluginMetaData pluginMd;
- private Plugin pluginImpl;
- private ComponentLifecycleState state;
-
-
- protected PluginActorMessageProcessor(TenantId tenantId, PluginId pluginId, ActorSystemContext systemContext
- , LoggingAdapter logger, ActorRef parent, ActorRef self) {
- super(systemContext, logger, tenantId, pluginId);
- this.pluginCtx = new SharedPluginProcessingContext(systemContext, tenantId, pluginId, parent, self);
- this.trustedCtx = new PluginProcessingContext(pluginCtx, null);
- }
-
- @Override
- public void start() throws Exception {
- logger.info("[{}] Going to start plugin actor.", entityId);
- pluginMd = systemContext.getPluginService().findPluginById(entityId);
- if (pluginMd == null) {
- throw new PluginInitializationException("Plugin not found!");
- }
- if (pluginMd.getConfiguration() == null) {
- throw new PluginInitializationException("Plugin metadata is empty!");
- }
- state = pluginMd.getState();
- if (state == ComponentLifecycleState.ACTIVE) {
- logger.info("[{}] Plugin is active. Going to initialize plugin.", entityId);
- initComponent();
- } else {
- logger.info("[{}] Plugin is suspended. Skipping plugin initialization.", entityId);
- }
- }
-
- @Override
- public void stop() throws Exception {
- onStop();
- }
-
- private void initComponent() {
- try {
- pluginImpl = initComponent(pluginMd.getClazz(), ComponentType.PLUGIN, mapper.writeValueAsString(pluginMd.getConfiguration()));
- } catch (InstantiationException e) {
- throw new PluginInitializationException("No default constructor for plugin implementation!", e);
- } catch (IllegalAccessException e) {
- throw new PluginInitializationException("Illegal Access Exception during plugin initialization!", e);
- } catch (ClassNotFoundException e) {
- throw new PluginInitializationException("Plugin Class not found!", e);
- } catch (JsonProcessingException e) {
- throw new PluginInitializationException("Plugin Configuration is invalid!", e);
- } catch (Exception e) {
- throw new PluginInitializationException(e.getMessage(), e);
- }
- }
-
- public void onRuleToPluginMsg(RuleToPluginMsgWrapper msg) throws RuleException {
- if (state == ComponentLifecycleState.ACTIVE) {
- try {
- pluginImpl.process(trustedCtx, msg.getRuleTenantId(), msg.getRuleId(), msg.getMsg());
- } catch (Exception ex) {
- logger.debug("[{}] Failed to process RuleToPlugin msg: [{}] [{}]", tenantId, msg.getMsg(), ex);
- RuleToPluginMsg ruleMsg = msg.getMsg();
- MsgType responceMsgType = MsgType.RULE_ENGINE_ERROR;
- Integer requestId = 0;
- if (ruleMsg.getPayload() instanceof FromDeviceRequestMsg) {
- requestId = ((FromDeviceRequestMsg) ruleMsg.getPayload()).getRequestId();
- }
- trustedCtx.reply(
- new ResponsePluginToRuleMsg(ruleMsg.getUid(), tenantId, msg.getRuleId(),
- BasicStatusCodeResponse.onError(responceMsgType, requestId, ex)));
- }
- } else {
- //TODO: reply with plugin suspended message
- }
- }
-
- public void onWebsocketMsg(PluginWebsocketMsg> msg) {
- if (state == ComponentLifecycleState.ACTIVE) {
- pluginImpl.process(new PluginProcessingContext(pluginCtx, msg.getSecurityCtx()), msg);
- } else {
- //TODO: reply with plugin suspended message
- }
- }
-
- public void onRestMsg(PluginRestMsg msg) {
- if (state == ComponentLifecycleState.ACTIVE) {
- pluginImpl.process(new PluginProcessingContext(pluginCtx, msg.getSecurityCtx()), msg);
- }
- }
-
- public void onRpcMsg(PluginRpcMsg msg) {
- if (state == ComponentLifecycleState.ACTIVE) {
- pluginImpl.process(trustedCtx, msg.getRpcMsg());
- } else {
- //TODO: reply with plugin suspended message
- }
- }
-
- public void onPluginCallbackMsg(PluginCallbackMessage msg) {
- if (state == ComponentLifecycleState.ACTIVE) {
- if (msg.isSuccess()) {
- msg.getCallback().onSuccess(trustedCtx, msg.getV());
- } else {
- msg.getCallback().onFailure(trustedCtx, msg.getE());
- }
- } else {
- //TODO: reply with plugin suspended message
- }
- }
-
-
- public void onTimeoutMsg(ActorContext context, TimeoutMsg> msg) {
- if (state == ComponentLifecycleState.ACTIVE) {
- pluginImpl.process(trustedCtx, msg);
- }
- }
-
-
- public void onDeviceRpcMsg(FromDeviceRpcResponse response) {
- if (state == ComponentLifecycleState.ACTIVE) {
- pluginImpl.process(trustedCtx, response);
- }
- }
-
- @Override
- public void onClusterEventMsg(ClusterEventMsg msg) {
- if (state == ComponentLifecycleState.ACTIVE) {
- ServerAddress address = msg.getServerAddress();
- if (msg.isAdded()) {
- logger.debug("[{}] Going to process server add msg: {}", entityId, address);
- pluginImpl.onServerAdded(trustedCtx, address);
- } else {
- logger.debug("[{}] Going to process server remove msg: {}", entityId, address);
- pluginImpl.onServerRemoved(trustedCtx, address);
- }
- }
- }
-
- @Override
- public void onCreated(ActorContext context) {
- logger.info("[{}] Going to process onCreated plugin.", entityId);
- }
-
- @Override
- public void onUpdate(ActorContext context) throws Exception {
- PluginMetaData oldPluginMd = pluginMd;
- pluginMd = systemContext.getPluginService().findPluginById(entityId);
- boolean requiresRestart = false;
- logger.info("[{}] Plugin configuration was updated from {} to {}.", entityId, oldPluginMd, pluginMd);
- if (!oldPluginMd.getClazz().equals(pluginMd.getClazz())) {
- logger.info("[{}] Plugin requires restart due to clazz change from {} to {}.",
- entityId, oldPluginMd.getClazz(), pluginMd.getClazz());
- requiresRestart = true;
- } else if (!oldPluginMd.getConfiguration().equals(pluginMd.getConfiguration())) {
- logger.info("[{}] Plugin requires restart due to configuration change from {} to {}.",
- entityId, oldPluginMd.getConfiguration(), pluginMd.getConfiguration());
- requiresRestart = true;
- }
- if (requiresRestart) {
- this.state = ComponentLifecycleState.SUSPENDED;
- if (pluginImpl != null) {
- pluginImpl.stop(trustedCtx);
- }
- start();
- }
- }
-
- @Override
- public void onStop(ActorContext context) {
- onStop();
- scheduleMsgWithDelay(context, new PluginTerminationMsg(entityId), systemContext.getPluginActorTerminationDelay());
- }
-
- private void onStop() {
- logger.info("[{}] Going to process onStop plugin.", entityId);
- this.state = ComponentLifecycleState.SUSPENDED;
- if (pluginImpl != null) {
- pluginImpl.stop(trustedCtx);
- }
- }
-
- @Override
- public void onActivate(ActorContext context) throws Exception {
- logger.info("[{}] Going to process onActivate plugin.", entityId);
- this.state = ComponentLifecycleState.ACTIVE;
- if (pluginImpl != null) {
- pluginImpl.resume(trustedCtx);
- logger.info("[{}] Plugin resumed.", entityId);
- } else {
- start();
- }
- }
-
- @Override
- public void onSuspend(ActorContext context) {
- logger.info("[{}] Going to process onSuspend plugin.", entityId);
- this.state = ComponentLifecycleState.SUSPENDED;
- if (pluginImpl != null) {
- pluginImpl.suspend(trustedCtx);
- }
- }
-
-}
diff --git a/application/src/main/java/org/thingsboard/server/actors/plugin/PluginCallbackMessage.java b/application/src/main/java/org/thingsboard/server/actors/plugin/PluginCallbackMessage.java
deleted file mode 100644
index 539a874d77..0000000000
--- a/application/src/main/java/org/thingsboard/server/actors/plugin/PluginCallbackMessage.java
+++ /dev/null
@@ -1,53 +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.plugin;
-
-import lombok.Data;
-import lombok.Getter;
-import lombok.ToString;
-import org.thingsboard.server.extensions.api.plugins.PluginCallback;
-
-import java.util.Optional;
-
-/**
- * @author Andrew Shvayka
- */
-@ToString
-public final class PluginCallbackMessage {
- @Getter
- private final PluginCallback callback;
- @Getter
- private final boolean success;
- @Getter
- private final V v;
- @Getter
- private final Exception e;
-
- public static PluginCallbackMessage onSuccess(PluginCallback callback, V data) {
- return new PluginCallbackMessage(true, callback, data, null);
- }
-
- public static PluginCallbackMessage onError(PluginCallback callback, Exception e) {
- return new PluginCallbackMessage(false, callback, null, e);
- }
-
- private PluginCallbackMessage(boolean success, PluginCallback callback, V v, Exception e) {
- this.success = success;
- this.callback = callback;
- this.v = v;
- this.e = e;
- }
-}
diff --git a/application/src/main/java/org/thingsboard/server/actors/plugin/PluginProcessingContext.java b/application/src/main/java/org/thingsboard/server/actors/plugin/PluginProcessingContext.java
deleted file mode 100644
index ce95ee4629..0000000000
--- a/application/src/main/java/org/thingsboard/server/actors/plugin/PluginProcessingContext.java
+++ /dev/null
@@ -1,546 +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.plugin;
-
-import akka.actor.ActorRef;
-import com.google.common.base.Function;
-import com.google.common.util.concurrent.FutureCallback;
-import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
-import lombok.extern.slf4j.Slf4j;
-import org.thingsboard.server.common.data.Customer;
-import org.thingsboard.server.common.data.Device;
-import org.thingsboard.server.common.data.EntityType;
-import org.thingsboard.server.common.data.Tenant;
-import org.thingsboard.server.common.data.asset.Asset;
-import org.thingsboard.server.common.data.audit.ActionType;
-import org.thingsboard.server.common.data.id.*;
-import org.thingsboard.server.common.data.kv.AttributeKey;
-import org.thingsboard.server.common.data.kv.AttributeKvEntry;
-import org.thingsboard.server.common.data.kv.TsKvEntry;
-import org.thingsboard.server.common.data.kv.TsKvQuery;
-import org.thingsboard.server.common.data.page.TextPageLink;
-import org.thingsboard.server.common.data.plugin.PluginMetaData;
-import org.thingsboard.server.common.data.relation.EntityRelation;
-import org.thingsboard.server.common.data.relation.RelationTypeGroup;
-import org.thingsboard.server.common.data.rule.RuleMetaData;
-import org.thingsboard.server.common.msg.cluster.ServerAddress;
-import org.thingsboard.server.extensions.api.device.DeviceAttributesEventNotificationMsg;
-import org.thingsboard.server.extensions.api.plugins.PluginApiCallSecurityContext;
-import org.thingsboard.server.extensions.api.plugins.PluginCallback;
-import org.thingsboard.server.extensions.api.plugins.PluginContext;
-import org.thingsboard.server.extensions.api.plugins.msg.*;
-import org.thingsboard.server.extensions.api.plugins.rpc.PluginRpcMsg;
-import org.thingsboard.server.extensions.api.plugins.rpc.RpcMsg;
-import org.thingsboard.server.extensions.api.plugins.ws.PluginWebsocketSessionRef;
-import org.thingsboard.server.extensions.api.plugins.ws.msg.PluginWebsocketMsg;
-
-import javax.annotation.Nullable;
-import java.io.IOException;
-import java.util.*;
-import java.util.concurrent.Executor;
-import java.util.concurrent.Executors;
-import java.util.stream.Collectors;
-
-@Slf4j
-public final class PluginProcessingContext implements PluginContext {
-
- private static final Executor executor = Executors.newSingleThreadExecutor();
- public static final String CUSTOMER_USER_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION = "Customer user is not allowed to perform this operation!";
- public static final String SYSTEM_ADMINISTRATOR_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION = "System administrator is not allowed to perform this operation!";
- public static final String DEVICE_WITH_REQUESTED_ID_NOT_FOUND = "Device with requested id wasn't found!";
-
- private final SharedPluginProcessingContext pluginCtx;
- private final Optional securityCtx;
-
- public PluginProcessingContext(SharedPluginProcessingContext pluginCtx, PluginApiCallSecurityContext securityCtx) {
- super();
- this.pluginCtx = pluginCtx;
- this.securityCtx = Optional.ofNullable(securityCtx);
- }
-
- public void persistError(String method, Exception e) {
- pluginCtx.persistError(method, e);
- }
-
- @Override
- public void sendPluginRpcMsg(RpcMsg msg) {
- this.pluginCtx.rpcService.tell(new PluginRpcMsg(pluginCtx.tenantId, pluginCtx.pluginId, msg));
- }
-
- @Override
- public void send(PluginWebsocketMsg> wsMsg) throws IOException {
- pluginCtx.msgEndpoint.send(wsMsg);
- }
-
- @Override
- public void close(PluginWebsocketSessionRef sessionRef) throws IOException {
- pluginCtx.msgEndpoint.close(sessionRef);
- }
-
- @Override
- public void saveAttributes(final TenantId tenantId, final EntityId entityId, final String scope, final List attributes, final PluginCallback callback) {
- validate(entityId, new ValidationCallback(callback, ctx -> {
- ListenableFuture> futures = pluginCtx.attributesService.save(entityId, scope, attributes);
- Futures.addCallback(futures, getListCallback(callback, v -> {
- if (entityId.getEntityType() == EntityType.DEVICE) {
- onDeviceAttributesChanged(tenantId, new DeviceId(entityId.getId()), scope, attributes);
- }
- return null;
- }), executor);
- }));
- }
-
- @Override
- public void removeAttributes(final TenantId tenantId, final EntityId entityId, final String scope, final List keys, final PluginCallback callback) {
- validate(entityId, new ValidationCallback(callback, ctx -> {
- ListenableFuture> futures = pluginCtx.attributesService.removeAll(entityId, scope, keys);
- Futures.addCallback(futures, getCallback(callback, v -> null), executor);
- if (entityId.getEntityType() == EntityType.DEVICE) {
- onDeviceAttributesDeleted(tenantId, new DeviceId(entityId.getId()), keys.stream().map(key -> new AttributeKey(scope, key)).collect(Collectors.toSet()));
- }
- }));
- }
-
- @Override
- public void loadAttribute(EntityId entityId, String attributeType, String attributeKey, final PluginCallback> callback) {
- validate(entityId, new ValidationCallback(callback, ctx -> {
- ListenableFuture> future = pluginCtx.attributesService.find(entityId, attributeType, attributeKey);
- Futures.addCallback(future, getCallback(callback, v -> v), executor);
- }));
- }
-
- @Override
- public void loadAttributes(EntityId entityId, String attributeType, Collection attributeKeys, final PluginCallback> callback) {
- validate(entityId, new ValidationCallback(callback, ctx -> {
- ListenableFuture> future = pluginCtx.attributesService.find(entityId, attributeType, attributeKeys);
- Futures.addCallback(future, getCallback(callback, v -> v), executor);
- }));
- }
-
- @Override
- public void loadAttributes(EntityId entityId, String attributeType, PluginCallback> callback) {
- validate(entityId, new ValidationCallback(callback, ctx -> {
- ListenableFuture> future = pluginCtx.attributesService.findAll(entityId, attributeType);
- Futures.addCallback(future, getCallback(callback, v -> v), executor);
- }));
- }
-
- @Override
- public void loadAttributes(final EntityId entityId, final Collection attributeTypes, final PluginCallback> callback) {
- validate(entityId, new ValidationCallback(callback, ctx -> {
- List>> futures = new ArrayList<>();
- attributeTypes.forEach(attributeType -> futures.add(pluginCtx.attributesService.findAll(entityId, attributeType)));
- convertFuturesAndAddCallback(callback, futures);
- }));
- }
-
- @Override
- public void loadAttributes(final EntityId entityId, final Collection attributeTypes, final Collection attributeKeys, final PluginCallback> callback) {
- validate(entityId, new ValidationCallback(callback, ctx -> {
- List>> futures = new ArrayList<>();
- attributeTypes.forEach(attributeType -> futures.add(pluginCtx.attributesService.find(entityId, attributeType, attributeKeys)));
- convertFuturesAndAddCallback(callback, futures);
- }));
- }
-
- @Override
- public void saveTsData(final EntityId entityId, final TsKvEntry entry, final PluginCallback callback) {
- validate(entityId, new ValidationCallback(callback, ctx -> {
- ListenableFuture> rsListFuture = pluginCtx.tsService.save(entityId, entry);
- Futures.addCallback(rsListFuture, getListCallback(callback, v -> null), executor);
- }));
- }
-
- @Override
- public void saveTsData(final EntityId entityId, final List entries, final PluginCallback callback) {
- saveTsData(entityId, entries, 0L, callback);
- }
-
- @Override
- public void saveTsData(final EntityId entityId, final List entries, long ttl, final PluginCallback callback) {
- validate(entityId, new ValidationCallback(callback, ctx -> {
- ListenableFuture> rsListFuture = pluginCtx.tsService.save(entityId, entries, ttl);
- Futures.addCallback(rsListFuture, getListCallback(callback, v -> null), executor);
- }));
- }
-
-
- @Override
- public void loadTimeseries(final EntityId entityId, final List queries, final PluginCallback> callback) {
- validate(entityId, new ValidationCallback(callback, ctx -> {
- ListenableFuture> future = pluginCtx.tsService.findAll(entityId, queries);
- Futures.addCallback(future, getCallback(callback, v -> v), executor);
- }));
- }
-
- @Override
- public void loadLatestTimeseries(final EntityId entityId, final PluginCallback> callback) {
- validate(entityId, new ValidationCallback(callback, ctx -> {
- ListenableFuture> future = pluginCtx.tsService.findAllLatest(entityId);
- Futures.addCallback(future, getCallback(callback, v -> v), executor);
- }));
- }
-
- @Override
- public void logAttributesUpdated(PluginApiCallSecurityContext ctx, EntityId entityId, String attributeType,
- List attributes, Exception e) {
- pluginCtx.auditLogService.logEntityAction(
- ctx.getTenantId(),
- ctx.getCustomerId(),
- ctx.getUserId(),
- ctx.getUserName(),
- (UUIDBased & EntityId)entityId,
- null,
- ActionType.ATTRIBUTES_UPDATED,
- e,
- attributeType,
- attributes);
- }
-
- @Override
- public void logAttributesDeleted(PluginApiCallSecurityContext ctx, EntityId entityId, String attributeType, List keys, Exception e) {
- pluginCtx.auditLogService.logEntityAction(
- ctx.getTenantId(),
- ctx.getCustomerId(),
- ctx.getUserId(),
- ctx.getUserName(),
- (UUIDBased & EntityId)entityId,
- null,
- ActionType.ATTRIBUTES_DELETED,
- e,
- attributeType,
- keys);
- }
-
- @Override
- public void logAttributesRead(PluginApiCallSecurityContext ctx, EntityId entityId, String attributeType, List keys, Exception e) {
- pluginCtx.auditLogService.logEntityAction(
- ctx.getTenantId(),
- ctx.getCustomerId(),
- ctx.getUserId(),
- ctx.getUserName(),
- (UUIDBased & EntityId)entityId,
- null,
- ActionType.ATTRIBUTES_READ,
- e,
- attributeType,
- keys);
- }
-
- @Override
- public void loadLatestTimeseries(final EntityId entityId, final Collection keys, final PluginCallback> callback) {
- validate(entityId, new ValidationCallback(callback, ctx -> {
- ListenableFuture> rsListFuture = pluginCtx.tsService.findLatest(entityId, keys);
- Futures.addCallback(rsListFuture, getCallback(callback, v -> v), executor);
- }));
- }
-
- @Override
- public void reply(PluginToRuleMsg> msg) {
- pluginCtx.parentActor.tell(msg, ActorRef.noSender());
- }
-
- @Override
- public PluginId getPluginId() {
- return pluginCtx.pluginId;
- }
-
- @Override
- public Optional getSecurityCtx() {
- return securityCtx;
- }
-
- private void onDeviceAttributesDeleted(TenantId tenantId, DeviceId deviceId, Set keys) {
- pluginCtx.toDeviceActor(DeviceAttributesEventNotificationMsg.onDelete(tenantId, deviceId, keys));
- }
-
- private void onDeviceAttributesChanged(TenantId tenantId, DeviceId deviceId, String scope, List values) {
- pluginCtx.toDeviceActor(DeviceAttributesEventNotificationMsg.onUpdate(tenantId, deviceId, scope, values));
- }
-
- private FutureCallback> getListCallback(final PluginCallback callback, Function, R> transformer) {
- return new FutureCallback>() {
- @Override
- public void onSuccess(@Nullable List result) {
- pluginCtx.self().tell(PluginCallbackMessage.onSuccess(callback, transformer.apply(result)), ActorRef.noSender());
- }
-
- @Override
- public void onFailure(Throwable t) {
- if (t instanceof Exception) {
- pluginCtx.self().tell(PluginCallbackMessage.onError(callback, (Exception) t), ActorRef.noSender());
- } else {
- log.error("Critical error: {}", t.getMessage(), t);
- }
- }
- };
- }
-
- private FutureCallback getCallback(final PluginCallback callback, Function transformer) {
- return new FutureCallback() {
- @Override
- public void onSuccess(@Nullable R result) {
- try {
- pluginCtx.self().tell(PluginCallbackMessage.onSuccess(callback, transformer.apply(result)), ActorRef.noSender());
- } catch (Exception e) {
- pluginCtx.self().tell(PluginCallbackMessage.onError(callback, e), ActorRef.noSender());
- }
- }
-
- @Override
- public void onFailure(Throwable t) {
- if (t instanceof Exception) {
- pluginCtx.self().tell(PluginCallbackMessage.onError(callback, (Exception) t), ActorRef.noSender());
- } else {
- log.error("Critical error: {}", t.getMessage(), t);
- }
- }
- };
- }
-
- @Override
- public void checkAccess(DeviceId deviceId, PluginCallback callback) {
- validate(deviceId, new ValidationCallback(callback, ctx -> callback.onSuccess(ctx, null)));
- }
-
- private void validate(EntityId entityId, ValidationCallback callback) {
- if (securityCtx.isPresent()) {
- final PluginApiCallSecurityContext ctx = securityCtx.get();
- switch (entityId.getEntityType()) {
- case DEVICE:
- validateDevice(ctx, entityId, callback);
- return;
- case ASSET:
- validateAsset(ctx, entityId, callback);
- return;
- case RULE:
- validateRule(ctx, entityId, callback);
- return;
- case PLUGIN:
- validatePlugin(ctx, entityId, callback);
- return;
- case CUSTOMER:
- validateCustomer(ctx, entityId, callback);
- return;
- case TENANT:
- validateTenant(ctx, entityId, callback);
- return;
- default:
- //TODO: add support of other entities
- throw new IllegalStateException("Not Implemented!");
- }
- } else {
- callback.onSuccess(this, ValidationResult.ok());
- }
- }
-
- private void validateDevice(final PluginApiCallSecurityContext ctx, EntityId entityId, ValidationCallback callback) {
- if (ctx.isSystemAdmin()) {
- callback.onSuccess(this, ValidationResult.accessDenied(SYSTEM_ADMINISTRATOR_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION));
- } else {
- ListenableFuture deviceFuture = pluginCtx.deviceService.findDeviceByIdAsync(new DeviceId(entityId.getId()));
- Futures.addCallback(deviceFuture, getCallback(callback, device -> {
- if (device == null) {
- return ValidationResult.entityNotFound(DEVICE_WITH_REQUESTED_ID_NOT_FOUND);
- } else {
- if (!device.getTenantId().equals(ctx.getTenantId())) {
- return ValidationResult.accessDenied("Device doesn't belong to the current Tenant!");
- } else if (ctx.isCustomerUser() && !device.getCustomerId().equals(ctx.getCustomerId())) {
- return ValidationResult.accessDenied("Device doesn't belong to the current Customer!");
- } else {
- return ValidationResult.ok();
- }
- }
- }));
- }
- }
-
- private void validateAsset(final PluginApiCallSecurityContext ctx, EntityId entityId, ValidationCallback callback) {
- if (ctx.isSystemAdmin()) {
- callback.onSuccess(this, ValidationResult.accessDenied(SYSTEM_ADMINISTRATOR_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION));
- } else {
- ListenableFuture assetFuture = pluginCtx.assetService.findAssetByIdAsync(new AssetId(entityId.getId()));
- Futures.addCallback(assetFuture, getCallback(callback, asset -> {
- if (asset == null) {
- return ValidationResult.entityNotFound("Asset with requested id wasn't found!");
- } else {
- if (!asset.getTenantId().equals(ctx.getTenantId())) {
- return ValidationResult.accessDenied("Asset doesn't belong to the current Tenant!");
- } else if (ctx.isCustomerUser() && !asset.getCustomerId().equals(ctx.getCustomerId())) {
- return ValidationResult.accessDenied("Asset doesn't belong to the current Customer!");
- } else {
- return ValidationResult.ok();
- }
- }
- }));
- }
- }
-
- private void validateRule(final PluginApiCallSecurityContext ctx, EntityId entityId, ValidationCallback callback) {
- if (ctx.isCustomerUser()) {
- callback.onSuccess(this, ValidationResult.accessDenied(CUSTOMER_USER_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION));
- } else {
- ListenableFuture ruleFuture = pluginCtx.ruleService.findRuleByIdAsync(new RuleId(entityId.getId()));
- Futures.addCallback(ruleFuture, getCallback(callback, rule -> {
- if (rule == null) {
- return ValidationResult.entityNotFound("Rule with requested id wasn't found!");
- } else {
- if (ctx.isTenantAdmin() && !rule.getTenantId().equals(ctx.getTenantId())) {
- return ValidationResult.accessDenied("Rule doesn't belong to the current Tenant!");
- } else if (ctx.isSystemAdmin() && !rule.getTenantId().isNullUid()) {
- return ValidationResult.accessDenied("Rule is not in system scope!");
- } else {
- return ValidationResult.ok();
- }
- }
- }));
- }
- }
-
- private void validatePlugin(final PluginApiCallSecurityContext ctx, EntityId entityId, ValidationCallback callback) {
- if (ctx.isCustomerUser()) {
- callback.onSuccess(this, ValidationResult.accessDenied(CUSTOMER_USER_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION));
- } else {
- ListenableFuture pluginFuture = pluginCtx.pluginService.findPluginByIdAsync(new PluginId(entityId.getId()));
- Futures.addCallback(pluginFuture, getCallback(callback, plugin -> {
- if (plugin == null) {
- return ValidationResult.entityNotFound("Plugin with requested id wasn't found!");
- } else {
- if (ctx.isTenantAdmin() && !plugin.getTenantId().equals(ctx.getTenantId())) {
- return ValidationResult.accessDenied("Plugin doesn't belong to the current Tenant!");
- } else if (ctx.isSystemAdmin() && !plugin.getTenantId().isNullUid()) {
- return ValidationResult.accessDenied("Plugin is not in system scope!");
- } else {
- return ValidationResult.ok();
- }
- }
- }));
- }
- }
-
- private void validateCustomer(final PluginApiCallSecurityContext ctx, EntityId entityId, ValidationCallback callback) {
- if (ctx.isSystemAdmin()) {
- callback.onSuccess(this, ValidationResult.accessDenied(SYSTEM_ADMINISTRATOR_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION));
- } else {
- ListenableFuture customerFuture = pluginCtx.customerService.findCustomerByIdAsync(new CustomerId(entityId.getId()));
- Futures.addCallback(customerFuture, getCallback(callback, customer -> {
- if (customer == null) {
- return ValidationResult.entityNotFound("Customer with requested id wasn't found!");
- } else {
- if (!customer.getTenantId().equals(ctx.getTenantId())) {
- return ValidationResult.accessDenied("Customer doesn't belong to the current Tenant!");
- } else if (ctx.isCustomerUser() && !customer.getId().equals(ctx.getCustomerId())) {
- return ValidationResult.accessDenied("Customer doesn't relate to the currently authorized customer user!");
- } else {
- return ValidationResult.ok();
- }
- }
- }));
- }
- }
-
- private void validateTenant(final PluginApiCallSecurityContext ctx, EntityId entityId, ValidationCallback callback) {
- if (ctx.isCustomerUser()) {
- callback.onSuccess(this, ValidationResult.accessDenied(CUSTOMER_USER_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION));
- } else if (ctx.isSystemAdmin()) {
- callback.onSuccess(this, ValidationResult.ok());
- } else {
- ListenableFuture tenantFuture = pluginCtx.tenantService.findTenantByIdAsync(new TenantId(entityId.getId()));
- Futures.addCallback(tenantFuture, getCallback(callback, tenant -> {
- if (tenant == null) {
- return ValidationResult.entityNotFound("Tenant with requested id wasn't found!");
- } else if (!tenant.getId().equals(ctx.getTenantId())) {
- return ValidationResult.accessDenied("Tenant doesn't relate to the currently authorized user!");
- } else {
- return ValidationResult.ok();
- }
- }));
- }
- }
-
- @Override
- public ListenableFuture> findByFromAndType(EntityId from, String relationType) {
- return this.pluginCtx.relationService.findByFromAndTypeAsync(from, relationType, RelationTypeGroup.COMMON);
- }
-
- @Override
- public ListenableFuture> findByToAndType(EntityId from, String relationType) {
- return this.pluginCtx.relationService.findByToAndTypeAsync(from, relationType, RelationTypeGroup.COMMON);
- }
-
- @Override
- public Optional resolve(EntityId entityId) {
- return pluginCtx.routingService.resolveById(entityId);
- }
-
- @Override
- public void getDevice(DeviceId deviceId, PluginCallback callback) {
- ListenableFuture deviceFuture = pluginCtx.deviceService.findDeviceByIdAsync(deviceId);
- Futures.addCallback(deviceFuture, getCallback(callback, v -> v));
- }
-
- @Override
- public void getCustomerDevices(TenantId tenantId, CustomerId customerId, int limit, PluginCallback> callback) {
- //TODO: add caching here with async api.
- List devices = pluginCtx.deviceService.findDevicesByTenantIdAndCustomerId(tenantId, customerId, new TextPageLink(limit)).getData();
- pluginCtx.self().tell(PluginCallbackMessage.onSuccess(callback, devices), ActorRef.noSender());
- }
-
- @Override
- public void sendRpcRequest(ToDeviceRpcRequest msg) {
- pluginCtx.sendRpcRequest(msg);
- }
-
- @Override
- public void logRpcRequest(PluginApiCallSecurityContext ctx, DeviceId deviceId, ToDeviceRpcRequestBody body, boolean oneWay, Optional rpcError, Exception e) {
- String rpcErrorStr = "";
- if (rpcError.isPresent()) {
- rpcErrorStr = "RPC Error: " + rpcError.get().name();
- }
- String method = body.getMethod();
- String params = body.getParams();
- pluginCtx.auditLogService.logEntityAction(
- ctx.getTenantId(),
- ctx.getCustomerId(),
- ctx.getUserId(),
- ctx.getUserName(),
- deviceId,
- null,
- ActionType.RPC_CALL,
- e,
- rpcErrorStr,
- new Boolean(oneWay),
- method,
- params);
- }
-
- @Override
- public void scheduleTimeoutMsg(TimeoutMsg msg) {
- pluginCtx.scheduleTimeoutMsg(msg);
- }
-
-
- private void convertFuturesAndAddCallback(PluginCallback> callback, List>> futures) {
- ListenableFuture> future = Futures.transform(Futures.successfulAsList(futures),
- (Function super List>, ? extends List>) input -> {
- List result = new ArrayList<>();
- input.forEach(r -> result.addAll(r));
- return result;
- }, executor);
- Futures.addCallback(future, getCallback(callback, v -> v), executor);
- }
-}
diff --git a/application/src/main/java/org/thingsboard/server/actors/plugin/PluginTerminationMsg.java b/application/src/main/java/org/thingsboard/server/actors/plugin/PluginTerminationMsg.java
deleted file mode 100644
index 99b1e64d0a..0000000000
--- a/application/src/main/java/org/thingsboard/server/actors/plugin/PluginTerminationMsg.java
+++ /dev/null
@@ -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.plugin;
-
-import org.thingsboard.server.actors.shared.ActorTerminationMsg;
-import org.thingsboard.server.common.data.id.PluginId;
-import org.thingsboard.server.common.data.id.SessionId;
-
-/**
- * @author Andrew Shvayka
- */
-public class PluginTerminationMsg extends ActorTerminationMsg {
-
- public PluginTerminationMsg(PluginId id) {
- super(id);
- }
-}
diff --git a/application/src/main/java/org/thingsboard/server/actors/plugin/RuleToPluginMsgWrapper.java b/application/src/main/java/org/thingsboard/server/actors/plugin/RuleToPluginMsgWrapper.java
deleted file mode 100644
index 4b2268cd57..0000000000
--- a/application/src/main/java/org/thingsboard/server/actors/plugin/RuleToPluginMsgWrapper.java
+++ /dev/null
@@ -1,66 +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.plugin;
-
-import org.thingsboard.server.common.data.id.PluginId;
-import org.thingsboard.server.common.data.id.RuleId;
-import org.thingsboard.server.common.data.id.TenantId;
-import org.thingsboard.server.common.msg.aware.RuleAwareMsg;
-import org.thingsboard.server.extensions.api.plugins.msg.RuleToPluginMsg;
-import org.thingsboard.server.extensions.api.plugins.msg.ToPluginActorMsg;
-
-public class RuleToPluginMsgWrapper implements ToPluginActorMsg, RuleAwareMsg {
-
- private final TenantId pluginTenantId;
- private final PluginId pluginId;
- private final TenantId ruleTenantId;
- private final RuleId ruleId;
- private final RuleToPluginMsg> msg;
-
- public RuleToPluginMsgWrapper(TenantId pluginTenantId, PluginId pluginId, TenantId ruleTenantId, RuleId ruleId, RuleToPluginMsg> msg) {
- super();
- this.pluginTenantId = pluginTenantId;
- this.pluginId = pluginId;
- this.ruleTenantId = ruleTenantId;
- this.ruleId = ruleId;
- this.msg = msg;
- }
-
- @Override
- public TenantId getPluginTenantId() {
- return pluginTenantId;
- }
-
- @Override
- public PluginId getPluginId() {
- return pluginId;
- }
-
- public TenantId getRuleTenantId() {
- return ruleTenantId;
- }
-
- @Override
- public RuleId getRuleId() {
- return ruleId;
- }
-
-
- public RuleToPluginMsg getMsg() {
- return msg;
- }
-
-}
diff --git a/application/src/main/java/org/thingsboard/server/actors/plugin/SharedPluginProcessingContext.java b/application/src/main/java/org/thingsboard/server/actors/plugin/SharedPluginProcessingContext.java
deleted file mode 100644
index 06138a3308..0000000000
--- a/application/src/main/java/org/thingsboard/server/actors/plugin/SharedPluginProcessingContext.java
+++ /dev/null
@@ -1,141 +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.plugin;
-
-import akka.actor.ActorRef;
-import lombok.extern.slf4j.Slf4j;
-import org.thingsboard.server.actors.ActorSystemContext;
-import org.thingsboard.server.common.data.Device;
-import org.thingsboard.server.common.data.id.DeviceId;
-import org.thingsboard.server.common.data.id.EntityId;
-import org.thingsboard.server.common.data.id.TenantId;
-import org.thingsboard.server.common.msg.cluster.ServerAddress;
-import org.thingsboard.server.controller.plugin.PluginWebSocketMsgEndpoint;
-import org.thingsboard.server.common.data.id.PluginId;
-import org.thingsboard.server.dao.asset.AssetService;
-import org.thingsboard.server.dao.attributes.AttributesService;
-import org.thingsboard.server.dao.audit.AuditLogService;
-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.RuleService;
-import org.thingsboard.server.dao.tenant.TenantService;
-import org.thingsboard.server.dao.timeseries.TimeseriesService;
-import org.thingsboard.server.extensions.api.device.DeviceAttributesEventNotificationMsg;
-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.ToDeviceRpcRequestPluginMsg;
-import org.thingsboard.server.service.cluster.routing.ClusterRoutingService;
-import org.thingsboard.server.service.cluster.rpc.ClusterRpcService;
-import scala.concurrent.duration.Duration;
-
-import java.util.Optional;
-import java.util.concurrent.TimeUnit;
-import java.util.function.BiConsumer;
-
-@Slf4j
-public final class SharedPluginProcessingContext {
- final ActorRef parentActor;
- final ActorRef currentActor;
- final ActorSystemContext systemContext;
- final PluginWebSocketMsgEndpoint msgEndpoint;
- final AssetService assetService;
- final DeviceService deviceService;
- final RuleService ruleService;
- final PluginService pluginService;
- final CustomerService customerService;
- final TenantService tenantService;
- final TimeseriesService tsService;
- final AttributesService attributesService;
- final ClusterRpcService rpcService;
- final ClusterRoutingService routingService;
- final RelationService relationService;
- final AuditLogService auditLogService;
- final PluginId pluginId;
- final TenantId tenantId;
-
- public SharedPluginProcessingContext(ActorSystemContext sysContext, TenantId tenantId, PluginId pluginId,
- ActorRef parentActor, ActorRef self) {
- super();
- this.tenantId = tenantId;
- this.pluginId = pluginId;
- this.parentActor = parentActor;
- this.currentActor = self;
- this.systemContext = sysContext;
- this.msgEndpoint = sysContext.getWsMsgEndpoint();
- this.tsService = sysContext.getTsService();
- this.attributesService = sysContext.getAttributesService();
- this.assetService = sysContext.getAssetService();
- this.deviceService = sysContext.getDeviceService();
- this.rpcService = sysContext.getRpcService();
- this.routingService = sysContext.getRoutingService();
- this.ruleService = sysContext.getRuleService();
- this.pluginService = sysContext.getPluginService();
- this.customerService = sysContext.getCustomerService();
- this.tenantService = sysContext.getTenantService();
- this.relationService = sysContext.getRelationService();
- this.auditLogService = sysContext.getAuditLogService();
- }
-
- public PluginId getPluginId() {
- return pluginId;
- }
-
- public TenantId getPluginTenantId() {
- return tenantId;
- }
-
- public void toDeviceActor(DeviceAttributesEventNotificationMsg msg) {
- forward(msg.getDeviceId(), msg, rpcService::tell);
- }
-
- public void sendRpcRequest(ToDeviceRpcRequest msg) {
- log.trace("[{}] Forwarding msg {} to device actor!", pluginId, msg);
- ToDeviceRpcRequestPluginMsg rpcMsg = new ToDeviceRpcRequestPluginMsg(pluginId, tenantId, msg);
- forward(msg.getDeviceId(), rpcMsg, rpcService::tell);
- }
-
- private void forward(DeviceId deviceId, T msg, BiConsumer rpcFunction) {
- Optional instance = routingService.resolveById(deviceId);
- if (instance.isPresent()) {
- log.trace("[{}] Forwarding msg {} to remote device actor!", pluginId, msg);
- rpcFunction.accept(instance.get(), msg);
- } else {
- log.trace("[{}] Forwarding msg {} to local device actor!", pluginId, msg);
- parentActor.tell(msg, ActorRef.noSender());
- }
- }
-
- public void scheduleTimeoutMsg(TimeoutMsg msg) {
- log.debug("Scheduling msg {} with delay {} ms", msg, msg.getTimeout());
- systemContext.getScheduler().scheduleOnce(
- Duration.create(msg.getTimeout(), TimeUnit.MILLISECONDS),
- currentActor,
- msg,
- systemContext.getActorSystem().dispatcher(),
- ActorRef.noSender());
-
- }
-
- public void persistError(String method, Exception e) {
- systemContext.persistError(tenantId, pluginId, method, e);
- }
-
- public ActorRef self() {
- return currentActor;
- }
-}
diff --git a/application/src/main/java/org/thingsboard/server/actors/plugin/ValidationResult.java b/application/src/main/java/org/thingsboard/server/actors/plugin/ValidationResult.java
deleted file mode 100644
index f135ffbd53..0000000000
--- a/application/src/main/java/org/thingsboard/server/actors/plugin/ValidationResult.java
+++ /dev/null
@@ -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.plugin;
-
-import lombok.AllArgsConstructor;
-import lombok.Data;
-
-@Data
-@AllArgsConstructor
-public class ValidationResult {
-
- private final ValidationResultCode resultCode;
- private final String message;
-
- public static ValidationResult ok() {
- return new ValidationResult(ValidationResultCode.OK, "Ok");
- }
-
- public static ValidationResult accessDenied(String message) {
- return new ValidationResult(ValidationResultCode.ACCESS_DENIED, message);
- }
-
- public static ValidationResult entityNotFound(String message) {
- return new ValidationResult(ValidationResultCode.ENTITY_NOT_FOUND, message);
- }
-
- public static ValidationResult unauthorized(String message) {
- return new ValidationResult(ValidationResultCode.UNAUTHORIZED, message);
- }
-
- public static ValidationResult internalError(String message) {
- return new ValidationResult(ValidationResultCode.INTERNAL_ERROR, message);
- }
-
-}
diff --git a/application/src/main/java/org/thingsboard/server/actors/rpc/BasicRpcSessionListener.java b/application/src/main/java/org/thingsboard/server/actors/rpc/BasicRpcSessionListener.java
index 4fa222755d..14bb636830 100644
--- a/application/src/main/java/org/thingsboard/server/actors/rpc/BasicRpcSessionListener.java
+++ b/application/src/main/java/org/thingsboard/server/actors/rpc/BasicRpcSessionListener.java
@@ -17,43 +17,23 @@ package org.thingsboard.server.actors.rpc;
import akka.actor.ActorRef;
import lombok.extern.slf4j.Slf4j;
-import org.springframework.util.SerializationUtils;
-import org.springframework.util.StringUtils;
-import org.thingsboard.server.actors.ActorSystemContext;
import org.thingsboard.server.actors.service.ActorService;
-import org.thingsboard.server.common.data.id.DeviceId;
-import org.thingsboard.server.common.data.id.PluginId;
-import org.thingsboard.server.common.data.id.TenantId;
-import org.thingsboard.server.common.msg.cluster.ServerAddress;
-import org.thingsboard.server.common.msg.cluster.ToAllNodesMsg;
-import org.thingsboard.server.common.msg.core.ToDeviceSessionActorMsg;
-import org.thingsboard.server.common.msg.device.ToDeviceActorMsg;
-import org.thingsboard.server.extensions.api.device.ToDeviceActorNotificationMsg;
-import org.thingsboard.server.extensions.api.plugins.msg.*;
-import org.thingsboard.server.extensions.api.plugins.rpc.PluginRpcMsg;
-import org.thingsboard.server.extensions.api.plugins.rpc.RpcMsg;
import org.thingsboard.server.gen.cluster.ClusterAPIProtos;
import org.thingsboard.server.service.cluster.rpc.GrpcSession;
import org.thingsboard.server.service.cluster.rpc.GrpcSessionListener;
-import java.io.Serializable;
-import java.util.UUID;
-
/**
* @author Andrew Shvayka
*/
@Slf4j
public class BasicRpcSessionListener implements GrpcSessionListener {
- public static final String SESSION_RECEIVED_SESSION_ACTOR_MSG = "{} session [{}] received session actor msg {}";
- private final ActorSystemContext context;
private final ActorService service;
private final ActorRef manager;
private final ActorRef self;
- public BasicRpcSessionListener(ActorSystemContext context, ActorRef manager, ActorRef self) {
- this.context = context;
- this.service = context.getActorService();
+ public BasicRpcSessionListener(ActorService service, ActorRef manager, ActorRef self) {
+ this.service = service;
this.manager = manager;
this.self = self;
}
@@ -73,47 +53,11 @@ public class BasicRpcSessionListener implements GrpcSessionListener {
}
@Override
- public void onToPluginRpcMsg(GrpcSession session, ClusterAPIProtos.ToPluginRpcMessage msg) {
- if (log.isTraceEnabled()) {
- log.trace("{} session [{}] received plugin msg {}", getType(session), session.getRemoteServer(), msg);
- }
- service.onMsg(convert(session.getRemoteServer(), msg));
- }
-
- @Override
- public void onToDeviceActorRpcMsg(GrpcSession session, ClusterAPIProtos.ToDeviceActorRpcMessage msg) {
- log.trace("{} session [{}] received device actor msg {}", getType(session), session.getRemoteServer(), msg);
- service.onMsg((ToDeviceActorMsg) deserialize(msg.getData().toByteArray()));
- }
-
- @Override
- public void onToDeviceActorNotificationRpcMsg(GrpcSession session, ClusterAPIProtos.ToDeviceActorNotificationRpcMessage msg) {
- log.trace("{} session [{}] received device actor notification msg {}", getType(session), session.getRemoteServer(), msg);
- service.onMsg((ToDeviceActorNotificationMsg) deserialize(msg.getData().toByteArray()));
- }
-
- @Override
- public void onToDeviceSessionActorRpcMsg(GrpcSession session, ClusterAPIProtos.ToDeviceSessionActorRpcMessage msg) {
- log.trace(SESSION_RECEIVED_SESSION_ACTOR_MSG, getType(session), session.getRemoteServer(), msg);
- service.onMsg((ToDeviceSessionActorMsg) deserialize(msg.getData().toByteArray()));
- }
-
- @Override
- public void onToDeviceRpcRequestRpcMsg(GrpcSession session, ClusterAPIProtos.ToDeviceRpcRequestRpcMessage msg) {
- log.trace(SESSION_RECEIVED_SESSION_ACTOR_MSG, getType(session), session.getRemoteServer(), msg);
- service.onMsg(deserialize(session.getRemoteServer(), msg));
- }
-
- @Override
- public void onFromDeviceRpcResponseRpcMsg(GrpcSession session, ClusterAPIProtos.ToPluginRpcResponseRpcMessage msg) {
- log.trace(SESSION_RECEIVED_SESSION_ACTOR_MSG, getType(session), session.getRemoteServer(), msg);
- service.onMsg(deserialize(session.getRemoteServer(), msg));
- }
-
- @Override
- public void onToAllNodesRpcMessage(GrpcSession session, ClusterAPIProtos.ToAllNodesRpcMessage msg) {
- log.trace(SESSION_RECEIVED_SESSION_ACTOR_MSG, getType(session), session.getRemoteServer(), msg);
- service.onMsg((ToAllNodesMsg) deserialize(msg.getData().toByteArray()));
+ public void onReceiveClusterGrpcMsg(GrpcSession session, ClusterAPIProtos.ClusterMessage clusterMessage) {
+ log.trace("{} Service [{}] received session actor msg {}", getType(session),
+ session.getRemoteServer(),
+ clusterMessage);
+ service.onReceivedMsg(session.getRemoteServer(), clusterMessage);
}
@Override
@@ -127,45 +71,5 @@ public class BasicRpcSessionListener implements GrpcSessionListener {
return session.isClient() ? "Client" : "Server";
}
- private static PluginRpcMsg convert(ServerAddress serverAddress, ClusterAPIProtos.ToPluginRpcMessage msg) {
- ClusterAPIProtos.PluginAddress address = msg.getAddress();
- TenantId tenantId = new TenantId(toUUID(address.getTenantId()));
- PluginId pluginId = new PluginId(toUUID(address.getPluginId()));
- RpcMsg rpcMsg = new RpcMsg(serverAddress, msg.getClazz(), msg.getData().toByteArray());
- return new PluginRpcMsg(tenantId, pluginId, rpcMsg);
- }
-
- private static UUID toUUID(ClusterAPIProtos.Uid uid) {
- return new UUID(uid.getPluginUuidMsb(), uid.getPluginUuidLsb());
- }
-
- private static ToDeviceRpcRequestPluginMsg deserialize(ServerAddress serverAddress, ClusterAPIProtos.ToDeviceRpcRequestRpcMessage msg) {
- ClusterAPIProtos.PluginAddress address = msg.getAddress();
- TenantId pluginTenantId = new TenantId(toUUID(address.getTenantId()));
- PluginId pluginId = new PluginId(toUUID(address.getPluginId()));
-
- TenantId deviceTenantId = new TenantId(toUUID(msg.getDeviceTenantId()));
- DeviceId deviceId = new DeviceId(toUUID(msg.getDeviceId()));
-
- ToDeviceRpcRequestBody requestBody = new ToDeviceRpcRequestBody(msg.getMethod(), msg.getParams());
- ToDeviceRpcRequest request = new ToDeviceRpcRequest(toUUID(msg.getMsgId()), null, deviceTenantId, deviceId, msg.getOneway(), msg.getExpTime(), requestBody);
-
- return new ToDeviceRpcRequestPluginMsg(serverAddress, pluginId, pluginTenantId, request);
- }
-
- private static ToPluginRpcResponseDeviceMsg deserialize(ServerAddress serverAddress, ClusterAPIProtos.ToPluginRpcResponseRpcMessage msg) {
- ClusterAPIProtos.PluginAddress address = msg.getAddress();
- TenantId pluginTenantId = new TenantId(toUUID(address.getTenantId()));
- PluginId pluginId = new PluginId(toUUID(address.getPluginId()));
-
- RpcError error = !StringUtils.isEmpty(msg.getError()) ? RpcError.valueOf(msg.getError()) : null;
- FromDeviceRpcResponse response = new FromDeviceRpcResponse(toUUID(msg.getMsgId()), msg.getResponse(), error);
- return new ToPluginRpcResponseDeviceMsg(pluginId, pluginTenantId, response);
- }
-
- @SuppressWarnings("unchecked")
- private static T deserialize(byte[] data) {
- return (T) SerializationUtils.deserialize(data);
- }
}
diff --git a/application/src/main/java/org/thingsboard/server/actors/rpc/RpcBroadcastMsg.java b/application/src/main/java/org/thingsboard/server/actors/rpc/RpcBroadcastMsg.java
index 3718a22dfe..2dd949eb7f 100644
--- a/application/src/main/java/org/thingsboard/server/actors/rpc/RpcBroadcastMsg.java
+++ b/application/src/main/java/org/thingsboard/server/actors/rpc/RpcBroadcastMsg.java
@@ -23,5 +23,5 @@ import org.thingsboard.server.gen.cluster.ClusterAPIProtos;
*/
@Data
public final class RpcBroadcastMsg {
- private final ClusterAPIProtos.ToRpcServerMessage msg;
+ private final ClusterAPIProtos.ClusterMessage msg;
}
diff --git a/application/src/main/java/org/thingsboard/server/actors/rpc/RpcManagerActor.java b/application/src/main/java/org/thingsboard/server/actors/rpc/RpcManagerActor.java
index 9290a8fe58..3f3f70b424 100644
--- a/application/src/main/java/org/thingsboard/server/actors/rpc/RpcManagerActor.java
+++ b/application/src/main/java/org/thingsboard/server/actors/rpc/RpcManagerActor.java
@@ -23,12 +23,17 @@ import org.thingsboard.server.actors.ActorSystemContext;
import org.thingsboard.server.actors.service.ContextAwareActor;
import org.thingsboard.server.actors.service.ContextBasedCreator;
import org.thingsboard.server.actors.service.DefaultActorService;
+import org.thingsboard.server.common.msg.TbActorMsg;
import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;
import org.thingsboard.server.common.msg.cluster.ServerAddress;
import org.thingsboard.server.gen.cluster.ClusterAPIProtos;
import org.thingsboard.server.service.cluster.discovery.ServerInstance;
-import java.util.*;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.Queue;
+import java.util.UUID;
/**
* @author Andrew Shvayka
@@ -39,7 +44,7 @@ public class RpcManagerActor extends ContextAwareActor {
private final Map sessionActors;
- private final Map> pendingMsgs;
+ private final Map> pendingMsgs;
private final ServerAddress instance;
@@ -56,10 +61,16 @@ public class RpcManagerActor extends ContextAwareActor {
}
+ @Override
+ protected boolean process(TbActorMsg msg) {
+ //TODO Move everything here, to work with TbActorMsg
+ return false;
+ }
+
@Override
public void onReceive(Object msg) throws Exception {
- if (msg instanceof RpcSessionTellMsg) {
- onMsg((RpcSessionTellMsg) msg);
+ if (msg instanceof ClusterAPIProtos.ClusterMessage) {
+ onMsg((ClusterAPIProtos.ClusterMessage) msg);
} else if (msg instanceof RpcBroadcastMsg) {
onMsg((RpcBroadcastMsg) msg);
} else if (msg instanceof RpcSessionCreateRequestMsg) {
@@ -77,24 +88,30 @@ public class RpcManagerActor extends ContextAwareActor {
private void onMsg(RpcBroadcastMsg msg) {
log.debug("Forwarding msg to session actors {}", msg);
- sessionActors.keySet().forEach(address -> onMsg(new RpcSessionTellMsg(address, msg.getMsg())));
+ sessionActors.keySet().forEach(address -> onMsg(msg.getMsg()));
pendingMsgs.values().forEach(queue -> queue.add(msg.getMsg()));
}
- private void onMsg(RpcSessionTellMsg msg) {
- ServerAddress address = msg.getServerAddress();
- SessionActorInfo session = sessionActors.get(address);
- if (session != null) {
- log.debug("{} Forwarding msg to session actor", address);
- session.actor.tell(msg, ActorRef.noSender());
- } else {
- log.debug("{} Storing msg to pending queue", address);
- Queue queue = pendingMsgs.get(address);
- if (queue == null) {
- queue = new LinkedList<>();
- pendingMsgs.put(address, queue);
+ private void onMsg(ClusterAPIProtos.ClusterMessage msg) {
+ if (msg.hasServerAddress()) {
+ ServerAddress address = new ServerAddress(msg.getServerAddress().getHost(),
+ msg.getServerAddress().getPort());
+ SessionActorInfo session = sessionActors.get(address);
+ if (session != null) {
+ log.debug("{} Forwarding msg to session actor", address);
+ session.getActor().tell(msg, ActorRef.noSender());
+ } else {
+ log.debug("{} Storing msg to pending queue", address);
+ Queue queue = pendingMsgs.get(address);
+ if (queue == null) {
+ queue = new LinkedList<>();
+ pendingMsgs.put(new ServerAddress(
+ msg.getServerAddress().getHost(), msg.getServerAddress().getPort()), queue);
+ }
+ queue.add(msg);
}
- queue.add(msg.getMsg());
+ } else {
+ logger.warning("Cluster msg doesn't have set Server Address [{}]", msg);
}
}
@@ -141,7 +158,7 @@ public class RpcManagerActor extends ContextAwareActor {
private void onSessionClose(boolean reconnect, ServerAddress remoteAddress) {
log.debug("[{}] session closed. Should reconnect: {}", remoteAddress, reconnect);
SessionActorInfo sessionRef = sessionActors.get(remoteAddress);
- if (context().sender().equals(sessionRef.actor)) {
+ if (context().sender() != null && context().sender().equals(sessionRef.actor)) {
sessionActors.remove(remoteAddress);
pendingMsgs.remove(remoteAddress);
if (reconnect) {
@@ -160,10 +177,10 @@ public class RpcManagerActor extends ContextAwareActor {
private void register(ServerAddress remoteAddress, UUID uuid, ActorRef sender) {
sessionActors.put(remoteAddress, new SessionActorInfo(uuid, sender));
log.debug("[{}][{}] Registering session actor.", remoteAddress, uuid);
- Queue data = pendingMsgs.remove(remoteAddress);
+ Queue data = pendingMsgs.remove(remoteAddress);
if (data != null) {
log.debug("[{}][{}] Forwarding {} pending messages.", remoteAddress, uuid, data.size());
- data.forEach(msg -> sender.tell(new RpcSessionTellMsg(remoteAddress, msg), ActorRef.noSender()));
+ data.forEach(msg -> sender.tell(new RpcSessionTellMsg(msg), ActorRef.noSender()));
} else {
log.debug("[{}][{}] No pending messages to forward.", remoteAddress, uuid);
}
diff --git a/application/src/main/java/org/thingsboard/server/actors/rpc/RpcSessionActor.java b/application/src/main/java/org/thingsboard/server/actors/rpc/RpcSessionActor.java
index db029fa13b..c9cf8696e5 100644
--- a/application/src/main/java/org/thingsboard/server/actors/rpc/RpcSessionActor.java
+++ b/application/src/main/java/org/thingsboard/server/actors/rpc/RpcSessionActor.java
@@ -23,6 +23,7 @@ import io.grpc.stub.StreamObserver;
import org.thingsboard.server.actors.ActorSystemContext;
import org.thingsboard.server.actors.service.ContextAwareActor;
import org.thingsboard.server.actors.service.ContextBasedCreator;
+import org.thingsboard.server.common.msg.TbActorMsg;
import org.thingsboard.server.common.msg.cluster.ServerAddress;
import org.thingsboard.server.gen.cluster.ClusterAPIProtos;
import org.thingsboard.server.gen.cluster.ClusterRpcServiceGrpc;
@@ -31,6 +32,8 @@ import org.thingsboard.server.service.cluster.rpc.GrpcSessionListener;
import java.util.UUID;
+import static org.thingsboard.server.gen.cluster.ClusterAPIProtos.MessageType.CONNECT_RPC_MESSAGE;
+
/**
* @author Andrew Shvayka
*/
@@ -47,17 +50,23 @@ public class RpcSessionActor extends ContextAwareActor {
this.sessionId = sessionId;
}
+ @Override
+ protected boolean process(TbActorMsg msg) {
+ //TODO Move everything here, to work with TbActorMsg
+ return false;
+ }
+
@Override
public void onReceive(Object msg) throws Exception {
- if (msg instanceof RpcSessionTellMsg) {
- tell((RpcSessionTellMsg) msg);
+ if (msg instanceof ClusterAPIProtos.ClusterMessage) {
+ tell((ClusterAPIProtos.ClusterMessage) msg);
} else if (msg instanceof RpcSessionCreateRequestMsg) {
initSession((RpcSessionCreateRequestMsg) msg);
}
}
- private void tell(RpcSessionTellMsg msg) {
- session.sendMsg(msg.getMsg());
+ private void tell(ClusterAPIProtos.ClusterMessage msg) {
+ session.sendMsg(msg);
}
@Override
@@ -69,7 +78,7 @@ public class RpcSessionActor extends ContextAwareActor {
private void initSession(RpcSessionCreateRequestMsg msg) {
log.info("[{}] Initializing session", context().self());
ServerAddress remoteServer = msg.getRemoteAddress();
- listener = new BasicRpcSessionListener(systemContext, context().parent(), context().self());
+ listener = new BasicRpcSessionListener(systemContext.getActorService(), context().parent(), context().self());
if (msg.getRemoteAddress() == null) {
// Server session
session = new GrpcSession(listener);
@@ -84,7 +93,7 @@ public class RpcSessionActor extends ContextAwareActor {
session.initInputStream();
ClusterRpcServiceGrpc.ClusterRpcServiceStub stub = ClusterRpcServiceGrpc.newStub(channel);
- StreamObserver outputStream = stub.handlePluginMsgs(session.getInputStream());
+ StreamObserver outputStream = stub.handleMsgs(session.getInputStream());
session.setOutputStream(outputStream);
session.initOutputStream();
@@ -108,11 +117,10 @@ public class RpcSessionActor extends ContextAwareActor {
}
}
- private ClusterAPIProtos.ToRpcServerMessage toConnectMsg() {
+ private ClusterAPIProtos.ClusterMessage toConnectMsg() {
ServerAddress instance = systemContext.getDiscoveryService().getCurrentServer().getServerAddress();
- return ClusterAPIProtos.ToRpcServerMessage.newBuilder().setConnectMsg(
- ClusterAPIProtos.ConnectRpcMessage.newBuilder().setServerAddress(
- ClusterAPIProtos.ServerAddress.newBuilder().setHost(instance.getHost()).setPort(instance.getPort()).build()).build()).build();
-
+ return ClusterAPIProtos.ClusterMessage.newBuilder().setMessageType(CONNECT_RPC_MESSAGE).setServerAddress(
+ ClusterAPIProtos.ServerAddress.newBuilder().setHost(instance.getHost())
+ .setPort(instance.getPort()).build()).build();
}
}
diff --git a/application/src/main/java/org/thingsboard/server/actors/rpc/RpcSessionCreateRequestMsg.java b/application/src/main/java/org/thingsboard/server/actors/rpc/RpcSessionCreateRequestMsg.java
index 5bcf1d621b..0c1136eb0b 100644
--- a/application/src/main/java/org/thingsboard/server/actors/rpc/RpcSessionCreateRequestMsg.java
+++ b/application/src/main/java/org/thingsboard/server/actors/rpc/RpcSessionCreateRequestMsg.java
@@ -30,6 +30,6 @@ public final class RpcSessionCreateRequestMsg {
private final UUID msgUid;
private final ServerAddress remoteAddress;
- private final StreamObserver responseObserver;
+ private final StreamObserver responseObserver;
}
diff --git a/application/src/main/java/org/thingsboard/server/actors/rpc/RpcSessionTellMsg.java b/application/src/main/java/org/thingsboard/server/actors/rpc/RpcSessionTellMsg.java
index 5a61044f49..50db43f7fb 100644
--- a/application/src/main/java/org/thingsboard/server/actors/rpc/RpcSessionTellMsg.java
+++ b/application/src/main/java/org/thingsboard/server/actors/rpc/RpcSessionTellMsg.java
@@ -16,7 +16,6 @@
package org.thingsboard.server.actors.rpc;
import lombok.Data;
-import org.thingsboard.server.common.msg.cluster.ServerAddress;
import org.thingsboard.server.gen.cluster.ClusterAPIProtos;
/**
@@ -24,6 +23,5 @@ import org.thingsboard.server.gen.cluster.ClusterAPIProtos;
*/
@Data
public final class RpcSessionTellMsg {
- private final ServerAddress serverAddress;
- private final ClusterAPIProtos.ToRpcServerMessage msg;
+ private final ClusterAPIProtos.ClusterMessage msg;
}
diff --git a/application/src/main/java/org/thingsboard/server/actors/rule/ChainProcessingContext.java b/application/src/main/java/org/thingsboard/server/actors/rule/ChainProcessingContext.java
deleted file mode 100644
index 8723a68516..0000000000
--- a/application/src/main/java/org/thingsboard/server/actors/rule/ChainProcessingContext.java
+++ /dev/null
@@ -1,117 +0,0 @@
-/**
- * Copyright © 2016-2018 The Thingsboard Authors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.thingsboard.server.actors.rule;
-
-import akka.actor.ActorRef;
-import org.thingsboard.server.common.msg.core.RuleEngineError;
-import org.thingsboard.server.common.msg.core.RuleEngineErrorMsg;
-import org.thingsboard.server.common.msg.device.ToDeviceActorMsg;
-import org.thingsboard.server.common.msg.session.ToDeviceMsg;
-import org.thingsboard.server.extensions.api.device.DeviceAttributes;
-import org.thingsboard.server.extensions.api.device.DeviceMetaData;
-
-public class ChainProcessingContext {
-
- private final ChainProcessingMetaData md;
- private final int index;
- private final RuleEngineError error;
- private ToDeviceMsg response;
-
-
- public ChainProcessingContext(ChainProcessingMetaData md) {
- super();
- this.md = md;
- this.index = 0;
- this.error = RuleEngineError.NO_RULES;
- }
-
- private ChainProcessingContext(ChainProcessingContext other, int indexOffset, RuleEngineError error) {
- super();
- this.md = other.md;
- this.index = other.index + indexOffset;
- this.error = error;
- this.response = other.response;
-
- if (this.index < 0 || this.index >= this.md.chain.size()) {
- throw new IllegalArgumentException("Can't apply offset " + indexOffset + " to the chain!");
- }
- }
-
- public ActorRef getDeviceActor() {
- return md.originator;
- }
-
- public ActorRef getCurrentActor() {
- return md.chain.getRuleActorMd(index).getActorRef();
- }
-
- public boolean hasNext() {
- return (getChainLength() - 1) > index;
- }
-
- public boolean isFailure() {
- return (error != null && error.isCritical()) || (response != null && !response.isSuccess());
- }
-
- public ChainProcessingContext getNext() {
- return new ChainProcessingContext(this, 1, this.error);
- }
-
- public ChainProcessingContext withError(RuleEngineError error) {
- if (error != null && (this.error == null || this.error.getPriority() < error.getPriority())) {
- return new ChainProcessingContext(this, 0, error);
- } else {
- return this;
- }
- }
-
- public int getChainLength() {
- return md.chain.size();
- }
-
- public ToDeviceActorMsg getInMsg() {
- return md.inMsg;
- }
-
- public DeviceMetaData getDeviceMetaData() {
- return md.deviceMetaData;
- }
-
- public String getDeviceName() {
- return md.deviceMetaData.getDeviceName();
- }
-
- public String getDeviceType() {
- return md.deviceMetaData.getDeviceType();
- }
-
- public DeviceAttributes getAttributes() {
- return md.deviceMetaData.getDeviceAttributes();
- }
-
- public ToDeviceMsg getResponse() {
- return response;
- }
-
- public void mergeResponse(ToDeviceMsg response) {
- // TODO add merge logic
- this.response = response;
- }
-
- public RuleEngineErrorMsg getError() {
- return new RuleEngineErrorMsg(md.inMsg.getPayload().getMsgType(), error);
- }
-}
diff --git a/application/src/main/java/org/thingsboard/server/actors/rule/ChainProcessingMetaData.java b/application/src/main/java/org/thingsboard/server/actors/rule/ChainProcessingMetaData.java
deleted file mode 100644
index 8080d4ecc9..0000000000
--- a/application/src/main/java/org/thingsboard/server/actors/rule/ChainProcessingMetaData.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/**
- * Copyright © 2016-2018 The Thingsboard Authors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.thingsboard.server.actors.rule;
-
-import akka.actor.ActorRef;
-import org.thingsboard.server.common.msg.device.ToDeviceActorMsg;
-import org.thingsboard.server.extensions.api.device.DeviceMetaData;
-
-/**
- * Immutable part of chain processing data;
- *
- * @author ashvayka
- */
-public final class ChainProcessingMetaData {
-
- final RuleActorChain chain;
- final ToDeviceActorMsg inMsg;
- final ActorRef originator;
- final DeviceMetaData deviceMetaData;
-
- public ChainProcessingMetaData(RuleActorChain chain, ToDeviceActorMsg inMsg, DeviceMetaData deviceMetaData, ActorRef originator) {
- super();
- this.chain = chain;
- this.inMsg = inMsg;
- this.originator = originator;
- this.deviceMetaData = deviceMetaData;
- }
-}
diff --git a/application/src/main/java/org/thingsboard/server/actors/rule/ComplexRuleActorChain.java b/application/src/main/java/org/thingsboard/server/actors/rule/ComplexRuleActorChain.java
deleted file mode 100644
index 89d31bdc84..0000000000
--- a/application/src/main/java/org/thingsboard/server/actors/rule/ComplexRuleActorChain.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/**
- * Copyright © 2016-2018 The Thingsboard Authors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.thingsboard.server.actors.rule;
-
-public class ComplexRuleActorChain implements RuleActorChain {
-
- private final RuleActorChain systemChain;
- private final RuleActorChain tenantChain;
-
- public ComplexRuleActorChain(RuleActorChain systemChain, RuleActorChain tenantChain) {
- super();
- this.systemChain = systemChain;
- this.tenantChain = tenantChain;
- }
-
- @Override
- public int size() {
- return systemChain.size() + tenantChain.size();
- }
-
- @Override
- public RuleActorMetaData getRuleActorMd(int index) {
- if (index < systemChain.size()) {
- return systemChain.getRuleActorMd(index);
- } else {
- return tenantChain.getRuleActorMd(index - systemChain.size());
- }
- }
-
-}
diff --git a/application/src/main/java/org/thingsboard/server/actors/rule/RuleActor.java b/application/src/main/java/org/thingsboard/server/actors/rule/RuleActor.java
deleted file mode 100644
index 062da2a3bb..0000000000
--- a/application/src/main/java/org/thingsboard/server/actors/rule/RuleActor.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/**
- * Copyright © 2016-2018 The Thingsboard Authors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.thingsboard.server.actors.rule;
-
-import org.thingsboard.server.actors.ActorSystemContext;
-import org.thingsboard.server.actors.service.ComponentActor;
-import org.thingsboard.server.actors.service.ContextBasedCreator;
-import org.thingsboard.server.actors.stats.StatsPersistTick;
-import org.thingsboard.server.common.data.id.RuleId;
-import org.thingsboard.server.common.data.id.TenantId;
-import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;
-import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
-import org.thingsboard.server.extensions.api.plugins.msg.PluginToRuleMsg;
-
-public class RuleActor extends ComponentActor {
-
- private RuleActor(ActorSystemContext systemContext, TenantId tenantId, RuleId ruleId) {
- super(systemContext, tenantId, ruleId);
- setProcessor(new RuleActorMessageProcessor(tenantId, ruleId, systemContext, logger));
- }
-
- @Override
- public void onReceive(Object msg) throws Exception {
- logger.debug("[{}] Received message: {}", id, msg);
- if (msg instanceof RuleProcessingMsg) {
- try {
- processor.onRuleProcessingMsg(context(), (RuleProcessingMsg) msg);
- increaseMessagesProcessedCount();
- } catch (Exception e) {
- logAndPersist("onDeviceMsg", e);
- }
- } else if (msg instanceof PluginToRuleMsg>) {
- try {
- processor.onPluginMsg(context(), (PluginToRuleMsg>) msg);
- } catch (Exception e) {
- logAndPersist("onPluginMsg", e);
- }
- } else if (msg instanceof ComponentLifecycleMsg) {
- onComponentLifecycleMsg((ComponentLifecycleMsg) msg);
- } else if (msg instanceof ClusterEventMsg) {
- onClusterEventMsg((ClusterEventMsg) msg);
- } else if (msg instanceof RuleToPluginTimeoutMsg) {
- try {
- processor.onTimeoutMsg(context(), (RuleToPluginTimeoutMsg) msg);
- } catch (Exception e) {
- logAndPersist("onTimeoutMsg", e);
- }
- } else if (msg instanceof StatsPersistTick) {
- onStatsPersistTick(id);
- } else {
- logger.debug("[{}][{}] Unknown msg type.", tenantId, id, msg.getClass().getName());
- }
- }
-
- public static class ActorCreator extends ContextBasedCreator {
- private static final long serialVersionUID = 1L;
-
- private final TenantId tenantId;
- private final RuleId ruleId;
-
- public ActorCreator(ActorSystemContext context, TenantId tenantId, RuleId ruleId) {
- super(context);
- this.tenantId = tenantId;
- this.ruleId = ruleId;
- }
-
- @Override
- public RuleActor create() throws Exception {
- return new RuleActor(context, tenantId, ruleId);
- }
- }
-
- @Override
- protected long getErrorPersistFrequency() {
- return systemContext.getRuleErrorPersistFrequency();
- }
-}
diff --git a/application/src/main/java/org/thingsboard/server/actors/rule/RuleActorMessageProcessor.java b/application/src/main/java/org/thingsboard/server/actors/rule/RuleActorMessageProcessor.java
deleted file mode 100644
index fd84fe978f..0000000000
--- a/application/src/main/java/org/thingsboard/server/actors/rule/RuleActorMessageProcessor.java
+++ /dev/null
@@ -1,354 +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.ActorContext;
-import akka.actor.ActorRef;
-import akka.event.LoggingAdapter;
-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.BasicStatusCodeResponse;
-import org.thingsboard.server.common.msg.core.RuleEngineError;
-import org.thingsboard.server.common.msg.device.ToDeviceActorMsg;
-import org.thingsboard.server.common.msg.session.ToDeviceMsg;
-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.ResponsePluginToRuleMsg;
-import org.thingsboard.server.extensions.api.plugins.msg.RuleToPluginMsg;
-import org.thingsboard.server.extensions.api.rules.*;
-
-import java.util.*;
-
-class RuleActorMessageProcessor extends ComponentMsgProcessor {
-
- private final RuleProcessingContext ruleCtx;
- private final Map pendingMsgMap;
-
- private RuleMetaData ruleMd;
- private ComponentLifecycleState state;
- private List filters;
- private RuleProcessor processor;
- private PluginAction action;
-
- private TenantId pluginTenantId;
- private PluginId pluginId;
-
- protected RuleActorMessageProcessor(TenantId tenantId, RuleId ruleId, ActorSystemContext systemContext, LoggingAdapter logger) {
- super(systemContext, logger, tenantId, ruleId);
- this.pendingMsgMap = new HashMap<>();
- this.ruleCtx = new RuleProcessingContext(systemContext, ruleId);
- }
-
- @Override
- public void start() throws Exception {
- logger.info("[{}][{}] Starting rule actor.", entityId, tenantId);
- ruleMd = systemContext.getRuleService().findRuleById(entityId);
- if (ruleMd == null) {
- throw new RuleInitializationException("Rule not found!");
- }
- state = ruleMd.getState();
- if (state == ComponentLifecycleState.ACTIVE) {
- logger.info("[{}] Rule is active. Going to initialize rule components.", entityId);
- initComponent();
- } else {
- logger.info("[{}] Rule is suspended. Skipping rule components initialization.", entityId);
- }
-
- logger.info("[{}][{}] Started rule actor.", entityId, tenantId);
- }
-
- @Override
- public void stop() throws Exception {
- onStop();
- }
-
-
- private void initComponent() throws RuleException {
- try {
- if (!ruleMd.getFilters().isArray()) {
- throw new RuntimeException("Filters are not array!");
- }
- fetchPluginInfo();
- initFilters();
- initProcessor();
- initAction();
- } catch (RuntimeException e) {
- throw new RuleInitializationException("Unknown runtime exception!", e);
- } catch (InstantiationException e) {
- throw new RuleInitializationException("No default constructor for rule implementation!", e);
- } catch (IllegalAccessException e) {
- throw new RuleInitializationException("Illegal Access Exception during rule initialization!", e);
- } catch (ClassNotFoundException e) {
- throw new RuleInitializationException("Rule Class not found!", e);
- } catch (Exception e) {
- throw new RuleException(e.getMessage(), e);
- }
- }
-
- private void initAction() throws Exception {
- if (ruleMd.getAction() != null && !ruleMd.getAction().isNull()) {
- action = initComponent(ruleMd.getAction());
- }
- }
-
- private void initProcessor() throws Exception {
- if (ruleMd.getProcessor() != null && !ruleMd.getProcessor().isNull()) {
- processor = initComponent(ruleMd.getProcessor());
- }
- }
-
- private void initFilters() throws Exception {
- filters = new ArrayList<>(ruleMd.getFilters().size());
- for (int i = 0; i < ruleMd.getFilters().size(); i++) {
- filters.add(initComponent(ruleMd.getFilters().get(i)));
- }
- }
-
- private void fetchPluginInfo() {
- if (!StringUtils.isEmpty(ruleMd.getPluginToken())) {
- PluginMetaData pluginMd = systemContext.getPluginService().findPluginByApiToken(ruleMd.getPluginToken());
- pluginTenantId = pluginMd.getTenantId();
- pluginId = pluginMd.getId();
- }
- }
-
- protected void onRuleProcessingMsg(ActorContext context, RuleProcessingMsg msg) throws RuleException {
- if (state != ComponentLifecycleState.ACTIVE) {
- pushToNextRule(context, msg.getCtx(), RuleEngineError.NO_ACTIVE_RULES);
- return;
- }
- ChainProcessingContext chainCtx = msg.getCtx();
- ToDeviceActorMsg inMsg = chainCtx.getInMsg();
-
- ruleCtx.update(inMsg, chainCtx.getDeviceMetaData());
-
- logger.debug("[{}] Going to filter in msg: {}", entityId, inMsg);
- for (RuleFilter filter : filters) {
- if (!filter.filter(ruleCtx, inMsg)) {
- logger.debug("[{}] In msg is NOT valid for processing by current rule: {}", entityId, inMsg);
- pushToNextRule(context, msg.getCtx(), RuleEngineError.NO_FILTERS_MATCHED);
- return;
- }
- }
- RuleProcessingMetaData inMsgMd;
- if (processor != null) {
- logger.debug("[{}] Going to process in msg: {}", entityId, inMsg);
- inMsgMd = processor.process(ruleCtx, inMsg);
- } else {
- inMsgMd = new RuleProcessingMetaData();
- }
- logger.debug("[{}] Going to convert in msg: {}", entityId, inMsg);
- if (action != null) {
- Optional> ruleToPluginMsgOptional = action.convert(ruleCtx, inMsg, inMsgMd);
- if (ruleToPluginMsgOptional.isPresent()) {
- RuleToPluginMsg> ruleToPluginMsg = ruleToPluginMsgOptional.get();
- logger.debug("[{}] Device msg is converted to: {}", entityId, ruleToPluginMsg);
- context.parent().tell(new RuleToPluginMsgWrapper(pluginTenantId, pluginId, tenantId, entityId, ruleToPluginMsg), context.self());
- if (action.isOneWayAction()) {
- pushToNextRule(context, msg.getCtx(), RuleEngineError.NO_TWO_WAY_ACTIONS);
- return;
- } else {
- pendingMsgMap.put(ruleToPluginMsg.getUid(), msg);
- scheduleMsgWithDelay(context, new RuleToPluginTimeoutMsg(ruleToPluginMsg.getUid()), systemContext.getPluginProcessingTimeout());
- return;
- }
- }
- }
- logger.debug("[{}] Nothing to send to plugin: {}", entityId, pluginId);
- pushToNextRule(context, msg.getCtx(), RuleEngineError.NO_TWO_WAY_ACTIONS);
- }
-
- void onPluginMsg(ActorContext context, PluginToRuleMsg> msg) {
- RuleProcessingMsg pendingMsg = pendingMsgMap.remove(msg.getUid());
- if (pendingMsg != null) {
- ChainProcessingContext ctx = pendingMsg.getCtx();
- if (isErrorResponce(msg)) {
- pushToNextRule(context, ctx, RuleEngineError.NO_RESPONSE_FROM_ACTIONS);
- } else {
- Optional ruleResponseOptional = action.convert(msg);
- if (ruleResponseOptional.isPresent()) {
- ctx.mergeResponse(ruleResponseOptional.get());
- pushToNextRule(context, ctx, null);
- } else {
- pushToNextRule(context, ctx, RuleEngineError.NO_RESPONSE_FROM_ACTIONS);
- }
- }
- } else {
- logger.warning("[{}] Processing timeout detected: [{}]", entityId, msg.getUid());
- }
- }
-
- private boolean isErrorResponce(PluginToRuleMsg> msg) {
- if (msg instanceof ResponsePluginToRuleMsg) {
- if (((ResponsePluginToRuleMsg) msg).getPayload() instanceof BasicStatusCodeResponse) {
- BasicStatusCodeResponse responce = (BasicStatusCodeResponse) ((ResponsePluginToRuleMsg) msg).getPayload();
- return !responce.isSuccess();
- }
- }
- return false;
- }
-
- void onTimeoutMsg(ActorContext context, RuleToPluginTimeoutMsg msg) {
- RuleProcessingMsg pendingMsg = pendingMsgMap.remove(msg.getMsgId());
- if (pendingMsg != null) {
- logger.debug("[{}] Processing timeout detected [{}]: {}", entityId, msg.getMsgId(), pendingMsg);
- ChainProcessingContext ctx = pendingMsg.getCtx();
- pushToNextRule(context, ctx, RuleEngineError.PLUGIN_TIMEOUT);
- }
- }
-
- private void pushToNextRule(ActorContext context, ChainProcessingContext ctx, RuleEngineError error) {
- if (error != null) {
- ctx = ctx.withError(error);
- }
- if (ctx.isFailure()) {
- logger.debug("[{}][{}] Forwarding processing chain to device actor due to failure.", ruleMd.getId(), ctx.getInMsg().getDeviceId());
- ctx.getDeviceActor().tell(new RulesProcessedMsg(ctx), ActorRef.noSender());
- } else if (!ctx.hasNext()) {
- logger.debug("[{}][{}] Forwarding processing chain to device actor due to end of chain.", ruleMd.getId(), ctx.getInMsg().getDeviceId());
- ctx.getDeviceActor().tell(new RulesProcessedMsg(ctx), ActorRef.noSender());
- } else {
- logger.debug("[{}][{}] Forwarding processing chain to next rule actor.", ruleMd.getId(), ctx.getInMsg().getDeviceId());
- ChainProcessingContext nextTask = ctx.getNext();
- nextTask.getCurrentActor().tell(new RuleProcessingMsg(nextTask), context.self());
- }
- }
-
- @Override
- public void onCreated(ActorContext context) {
- logger.info("[{}] Going to process onCreated rule.", entityId);
- }
-
- @Override
- public void onUpdate(ActorContext context) throws RuleException {
- RuleMetaData oldRuleMd = ruleMd;
- ruleMd = systemContext.getRuleService().findRuleById(entityId);
- logger.info("[{}] Rule configuration was updated from {} to {}.", entityId, oldRuleMd, ruleMd);
- try {
- fetchPluginInfo();
- if (filters == null || !Objects.equals(oldRuleMd.getFilters(), ruleMd.getFilters())) {
- logger.info("[{}] Rule filters require restart due to json change from {} to {}.",
- entityId, mapper.writeValueAsString(oldRuleMd.getFilters()), mapper.writeValueAsString(ruleMd.getFilters()));
- stopFilters();
- initFilters();
- }
- if (processor == null || !Objects.equals(oldRuleMd.getProcessor(), ruleMd.getProcessor())) {
- logger.info("[{}] Rule processor require restart due to configuration change.", entityId);
- stopProcessor();
- initProcessor();
- }
- if (action == null || !Objects.equals(oldRuleMd.getAction(), ruleMd.getAction())) {
- logger.info("[{}] Rule action require restart due to configuration change.", entityId);
- stopAction();
- initAction();
- }
- } catch (RuntimeException e) {
- throw new RuleInitializationException("Unknown runtime exception!", e);
- } catch (InstantiationException e) {
- throw new RuleInitializationException("No default constructor for rule implementation!", e);
- } catch (IllegalAccessException e) {
- throw new RuleInitializationException("Illegal Access Exception during rule initialization!", e);
- } catch (ClassNotFoundException e) {
- throw new RuleInitializationException("Rule Class not found!", e);
- } catch (JsonProcessingException e) {
- throw new RuleInitializationException("Rule configuration is invalid!", e);
- } catch (Exception e) {
- throw new RuleInitializationException(e.getMessage(), e);
- }
- }
-
- @Override
- public void onActivate(ActorContext context) throws Exception {
- logger.info("[{}] Going to process onActivate rule.", entityId);
- this.state = ComponentLifecycleState.ACTIVE;
- if (filters != null) {
- filters.forEach(RuleLifecycleComponent::resume);
- if (processor != null) {
- processor.resume();
- } else {
- initProcessor();
- }
- if (action != null) {
- action.resume();
- }
- logger.info("[{}] Rule resumed.", entityId);
- } else {
- start();
- }
- }
-
- @Override
- public void onSuspend(ActorContext context) {
- logger.info("[{}] Going to process onSuspend rule.", entityId);
- this.state = ComponentLifecycleState.SUSPENDED;
- if (filters != null) {
- filters.forEach(f -> f.suspend());
- }
- if (processor != null) {
- processor.suspend();
- }
- if (action != null) {
- action.suspend();
- }
- }
-
- @Override
- public void onStop(ActorContext context) {
- logger.info("[{}] Going to process onStop rule.", entityId);
- onStop();
- scheduleMsgWithDelay(context, new RuleTerminationMsg(entityId), systemContext.getRuleActorTerminationDelay());
- }
-
- private void onStop() {
- this.state = ComponentLifecycleState.SUSPENDED;
- stopFilters();
- stopProcessor();
- stopAction();
- }
-
- @Override
- public void onClusterEventMsg(ClusterEventMsg msg) throws Exception {
- //Do nothing
- }
-
- private void stopAction() {
- if (action != null) {
- action.stop();
- }
- }
-
- private void stopProcessor() {
- if (processor != null) {
- processor.stop();
- }
- }
-
- private void stopFilters() {
- if (filters != null) {
- filters.forEach(f -> f.stop());
- }
- }
-}
diff --git a/application/src/main/java/org/thingsboard/server/actors/rule/RuleActorMetaData.java b/application/src/main/java/org/thingsboard/server/actors/rule/RuleActorMetaData.java
deleted file mode 100644
index df7adac396..0000000000
--- a/application/src/main/java/org/thingsboard/server/actors/rule/RuleActorMetaData.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/**
- * Copyright © 2016-2018 The Thingsboard Authors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.thingsboard.server.actors.rule;
-
-import java.util.Comparator;
-
-import org.thingsboard.server.common.data.id.RuleId;
-
-import akka.actor.ActorRef;
-
-public class RuleActorMetaData {
-
- private final RuleId ruleId;
- private final boolean systemRule;
- private final int weight;
- private final ActorRef actorRef;
-
- public static final Comparator RULE_ACTOR_MD_COMPARATOR = new Comparator() {
-
- @Override
- public int compare(RuleActorMetaData r1, RuleActorMetaData r2) {
- if (r1.isSystemRule() && !r2.isSystemRule()) {
- return 1;
- } else if (!r1.isSystemRule() && r2.isSystemRule()) {
- return -1;
- } else {
- return Integer.compare(r2.getWeight(), r1.getWeight());
- }
- }
- };
-
- public static RuleActorMetaData systemRule(RuleId ruleId, int weight, ActorRef actorRef) {
- return new RuleActorMetaData(ruleId, true, weight, actorRef);
- }
-
- public static RuleActorMetaData tenantRule(RuleId ruleId, int weight, ActorRef actorRef) {
- return new RuleActorMetaData(ruleId, false, weight, actorRef);
- }
-
- private RuleActorMetaData(RuleId ruleId, boolean systemRule, int weight, ActorRef actorRef) {
- super();
- this.ruleId = ruleId;
- this.systemRule = systemRule;
- this.weight = weight;
- this.actorRef = actorRef;
- }
-
- public RuleId getRuleId() {
- return ruleId;
- }
-
- public boolean isSystemRule() {
- return systemRule;
- }
-
- public int getWeight() {
- return weight;
- }
-
- public ActorRef getActorRef() {
- return actorRef;
- }
-
- @Override
- public int hashCode() {
- final int prime = 31;
- int result = 1;
- result = prime * result + ((ruleId == null) ? 0 : ruleId.hashCode());
- return result;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj)
- return true;
- if (obj == null)
- return false;
- if (getClass() != obj.getClass())
- return false;
- RuleActorMetaData other = (RuleActorMetaData) obj;
- if (ruleId == null) {
- if (other.ruleId != null)
- return false;
- } else if (!ruleId.equals(other.ruleId))
- return false;
- return true;
- }
-
- @Override
- public String toString() {
- return "RuleActorMetaData [ruleId=" + ruleId + ", systemRule=" + systemRule + ", weight=" + weight + ", actorRef=" + actorRef + "]";
- }
-
-}
diff --git a/application/src/main/java/org/thingsboard/server/actors/rule/RuleProcessingContext.java b/application/src/main/java/org/thingsboard/server/actors/rule/RuleProcessingContext.java
deleted file mode 100644
index 179307bb05..0000000000
--- a/application/src/main/java/org/thingsboard/server/actors/rule/RuleProcessingContext.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/**
- * Copyright © 2016-2018 The Thingsboard Authors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.thingsboard.server.actors.rule;
-
-import com.google.common.util.concurrent.ListenableFuture;
-import org.thingsboard.server.actors.ActorSystemContext;
-import org.thingsboard.server.common.data.Event;
-import org.thingsboard.server.common.data.alarm.Alarm;
-import org.thingsboard.server.common.data.alarm.AlarmId;
-import org.thingsboard.server.common.data.id.*;
-import org.thingsboard.server.dao.alarm.AlarmService;
-import org.thingsboard.server.dao.event.EventService;
-import org.thingsboard.server.dao.timeseries.TimeseriesService;
-import org.thingsboard.server.common.msg.device.ToDeviceActorMsg;
-import org.thingsboard.server.extensions.api.device.DeviceMetaData;
-import org.thingsboard.server.extensions.api.rules.RuleContext;
-
-import java.util.Optional;
-import java.util.concurrent.ExecutionException;
-
-public class RuleProcessingContext implements RuleContext {
-
- private final TimeseriesService tsService;
- private final EventService eventService;
- private final AlarmService alarmService;
- private final RuleId ruleId;
- private TenantId tenantId;
- private CustomerId customerId;
- private DeviceId deviceId;
- private DeviceMetaData deviceMetaData;
-
- RuleProcessingContext(ActorSystemContext systemContext, RuleId ruleId) {
- this.tsService = systemContext.getTsService();
- this.eventService = systemContext.getEventService();
- this.alarmService = systemContext.getAlarmService();
- this.ruleId = ruleId;
- }
-
- void update(ToDeviceActorMsg toDeviceActorMsg, DeviceMetaData deviceMetaData) {
- this.tenantId = toDeviceActorMsg.getTenantId();
- this.customerId = toDeviceActorMsg.getCustomerId();
- this.deviceId = toDeviceActorMsg.getDeviceId();
- this.deviceMetaData = deviceMetaData;
- }
-
- @Override
- public RuleId getRuleId() {
- return ruleId;
- }
-
- @Override
- public DeviceMetaData getDeviceMetaData() {
- return deviceMetaData;
- }
-
- @Override
- public Event save(Event event) {
- checkEvent(event);
- return eventService.save(event);
- }
-
- @Override
- public Optional saveIfNotExists(Event event) {
- checkEvent(event);
- return eventService.saveIfNotExists(event);
- }
-
- @Override
- public Optional findEvent(String eventType, String eventUid) {
- return eventService.findEvent(tenantId, deviceId, eventType, eventUid);
- }
-
- @Override
- public Alarm createOrUpdateAlarm(Alarm alarm) {
- alarm.setTenantId(tenantId);
- return alarmService.createOrUpdateAlarm(alarm);
- }
-
- public Optional findLatestAlarm(EntityId originator, String alarmType) {
- try {
- return Optional.ofNullable(alarmService.findLatestByOriginatorAndType(tenantId, originator, alarmType).get());
- } catch (InterruptedException | ExecutionException e) {
- throw new RuntimeException("Failed to lookup alarm!", e);
- }
- }
-
- @Override
- public ListenableFuture clearAlarm(AlarmId alarmId, long clearTs) {
- return alarmService.clearAlarm(alarmId, clearTs);
- }
-
- private void checkEvent(Event event) {
- if (event.getTenantId() == null) {
- event.setTenantId(tenantId);
- } else if (!tenantId.equals(event.getTenantId())) {
- throw new IllegalArgumentException("Invalid Tenant id!");
- }
- if (event.getEntityId() == null) {
- event.setEntityId(deviceId);
- }
- }
-}
diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java
new file mode 100644
index 0000000000..bcadd0c8aa
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java
@@ -0,0 +1,241 @@
+/**
+ * Copyright © 2016-2018 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.actors.ruleChain;
+
+import akka.actor.ActorRef;
+import com.datastax.driver.core.utils.UUIDs;
+import org.thingsboard.rule.engine.api.ListeningExecutor;
+import org.thingsboard.rule.engine.api.MailService;
+import org.thingsboard.rule.engine.api.RuleEngineDeviceRpcRequest;
+import org.thingsboard.rule.engine.api.RuleEngineDeviceRpcResponse;
+import org.thingsboard.rule.engine.api.RuleEngineRpcService;
+import org.thingsboard.rule.engine.api.RuleEngineTelemetryService;
+import org.thingsboard.rule.engine.api.ScriptEngine;
+import org.thingsboard.rule.engine.api.TbContext;
+import org.thingsboard.rule.engine.api.TbRelationTypes;
+import org.thingsboard.server.actors.ActorSystemContext;
+import org.thingsboard.server.common.data.id.DeviceId;
+import org.thingsboard.server.common.data.id.EntityId;
+import org.thingsboard.server.common.data.id.RuleNodeId;
+import org.thingsboard.server.common.data.id.TenantId;
+import org.thingsboard.server.common.data.rpc.ToDeviceRpcRequestBody;
+import org.thingsboard.server.common.data.rule.RuleNode;
+import org.thingsboard.server.common.msg.TbMsg;
+import org.thingsboard.server.common.msg.TbMsgMetaData;
+import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest;
+import org.thingsboard.server.dao.alarm.AlarmService;
+import org.thingsboard.server.dao.asset.AssetService;
+import org.thingsboard.server.dao.attributes.AttributesService;
+import org.thingsboard.server.dao.customer.CustomerService;
+import org.thingsboard.server.dao.device.DeviceService;
+import org.thingsboard.server.dao.relation.RelationService;
+import org.thingsboard.server.dao.rule.RuleChainService;
+import org.thingsboard.server.dao.timeseries.TimeseriesService;
+import org.thingsboard.server.dao.user.UserService;
+import org.thingsboard.server.service.script.RuleNodeJsScriptEngine;
+import scala.concurrent.duration.Duration;
+
+import java.util.Collections;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
+
+/**
+ * Created by ashvayka on 19.03.18.
+ */
+class DefaultTbContext implements TbContext {
+
+ private final ActorSystemContext mainCtx;
+ private final RuleNodeCtx nodeCtx;
+
+ public DefaultTbContext(ActorSystemContext mainCtx, RuleNodeCtx nodeCtx) {
+ this.mainCtx = mainCtx;
+ this.nodeCtx = nodeCtx;
+ }
+
+ @Override
+ public void tellNext(TbMsg msg, String relationType) {
+ tellNext(msg, Collections.singleton(relationType), null);
+ }
+
+ @Override
+ public void tellNext(TbMsg msg, Set relationTypes) {
+ tellNext(msg, relationTypes, null);
+ }
+
+ @Override
+ public void tellNext(TbMsg msg, String relationType, Throwable th) {
+ tellNext(msg, Collections.singleton(relationType), th);
+ }
+
+ private void tellNext(TbMsg msg, Set relationTypes, Throwable th) {
+ if (nodeCtx.getSelf().isDebugMode()) {
+ relationTypes.forEach(relationType -> mainCtx.persistDebugOutput(nodeCtx.getTenantId(), nodeCtx.getSelf().getId(), msg, relationType, th));
+ }
+ nodeCtx.getChainActor().tell(new RuleNodeToRuleChainTellNextMsg(nodeCtx.getSelf().getId(), relationTypes, msg), nodeCtx.getSelfActor());
+ }
+
+ @Override
+ public void tellSelf(TbMsg msg, long delayMs) {
+ //TODO: add persistence layer
+ scheduleMsgWithDelay(new RuleNodeToSelfMsg(msg), delayMs, nodeCtx.getSelfActor());
+ }
+
+ private void scheduleMsgWithDelay(Object msg, long delayInMs, ActorRef target) {
+ mainCtx.getScheduler().scheduleOnce(Duration.create(delayInMs, TimeUnit.MILLISECONDS), target, msg, mainCtx.getActorSystem().dispatcher(), nodeCtx.getSelfActor());
+ }
+
+ @Override
+ public void tellFailure(TbMsg msg, Throwable th) {
+ if (nodeCtx.getSelf().isDebugMode()) {
+ mainCtx.persistDebugOutput(nodeCtx.getTenantId(), nodeCtx.getSelf().getId(), msg, TbRelationTypes.FAILURE, th);
+ }
+ nodeCtx.getChainActor().tell(new RuleNodeToRuleChainTellNextMsg(nodeCtx.getSelf().getId(), Collections.singleton(TbRelationTypes.FAILURE), msg), nodeCtx.getSelfActor());
+ }
+
+ @Override
+ public void updateSelf(RuleNode self) {
+ nodeCtx.setSelf(self);
+ }
+
+ @Override
+ public TbMsg newMsg(String type, EntityId originator, TbMsgMetaData metaData, String data) {
+ return new TbMsg(UUIDs.timeBased(), type, originator, metaData.copy(), data, nodeCtx.getSelf().getRuleChainId(), nodeCtx.getSelf().getId(), mainCtx.getQueuePartitionId());
+ }
+
+ @Override
+ public TbMsg transformMsg(TbMsg origMsg, String type, EntityId originator, TbMsgMetaData metaData, String data) {
+ return new TbMsg(origMsg.getId(), type, originator, metaData.copy(), data, origMsg.getRuleChainId(), origMsg.getRuleNodeId(), mainCtx.getQueuePartitionId());
+ }
+
+ @Override
+ public RuleNodeId getSelfId() {
+ return nodeCtx.getSelf().getId();
+ }
+
+ @Override
+ public TenantId getTenantId() {
+ return nodeCtx.getTenantId();
+ }
+
+ @Override
+ public ListeningExecutor getJsExecutor() {
+ return mainCtx.getJsExecutor();
+ }
+
+ @Override
+ public ListeningExecutor getMailExecutor() {
+ return mainCtx.getMailExecutor();
+ }
+
+ @Override
+ public ListeningExecutor getDbCallbackExecutor() {
+ return mainCtx.getDbCallbackExecutor();
+ }
+
+ @Override
+ public ListeningExecutor getExternalCallExecutor() {
+ return mainCtx.getExternalCallExecutorService();
+ }
+
+ @Override
+ public ScriptEngine createJsScriptEngine(String script, String... argNames) {
+ return new RuleNodeJsScriptEngine(mainCtx.getJsSandbox(), script, argNames);
+ }
+
+ @Override
+ public AttributesService getAttributesService() {
+ return mainCtx.getAttributesService();
+ }
+
+ @Override
+ public CustomerService getCustomerService() {
+ return mainCtx.getCustomerService();
+ }
+
+ @Override
+ public UserService getUserService() {
+ return mainCtx.getUserService();
+ }
+
+ @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 RuleEngineTelemetryService getTelemetryService() {
+ return mainCtx.getTsSubService();
+ }
+
+ @Override
+ public RelationService getRelationService() {
+ return mainCtx.getRelationService();
+ }
+
+ @Override
+ public MailService getMailService() {
+ if (mainCtx.isAllowSystemMailService()) {
+ return mainCtx.getMailService();
+ } else {
+ throw new RuntimeException("Access to System Mail Service is forbidden!");
+ }
+ }
+
+ @Override
+ public RuleEngineRpcService getRpcService() {
+ return new RuleEngineRpcService() {
+ @Override
+ public void sendRpcReply(DeviceId deviceId, int requestId, String body) {
+ mainCtx.getDeviceRpcService().sendRpcReplyToDevice(nodeCtx.getTenantId(), deviceId, requestId, body);
+ }
+
+ @Override
+ public void sendRpcRequest(RuleEngineDeviceRpcRequest src, Consumer consumer) {
+ ToDeviceRpcRequest request = new ToDeviceRpcRequest(UUIDs.timeBased(), nodeCtx.getTenantId(), src.getDeviceId(),
+ src.isOneway(), System.currentTimeMillis() + src.getTimeout(), new ToDeviceRpcRequestBody(src.getMethod(), src.getBody()));
+ mainCtx.getDeviceRpcService().processRpcRequestToDevice(request, response -> {
+ consumer.accept(RuleEngineDeviceRpcResponse.builder()
+ .deviceId(src.getDeviceId())
+ .requestId(src.getRequestId())
+ .error(response.getError())
+ .response(response.getResponse())
+ .build());
+ });
+ }
+ };
+ }
+}
diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActor.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActor.java
new file mode 100644
index 0000000000..3ba646aaf8
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActor.java
@@ -0,0 +1,97 @@
+/**
+ * Copyright © 2016-2018 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.actors.ruleChain;
+
+import akka.actor.OneForOneStrategy;
+import akka.actor.SupervisorStrategy;
+import org.thingsboard.server.actors.ActorSystemContext;
+import org.thingsboard.server.actors.device.DeviceActorToRuleEngineMsg;
+import org.thingsboard.server.actors.service.ComponentActor;
+import org.thingsboard.server.actors.service.ContextBasedCreator;
+import org.thingsboard.server.common.data.id.RuleChainId;
+import org.thingsboard.server.common.data.id.TenantId;
+import org.thingsboard.server.common.msg.TbActorMsg;
+import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
+import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg;
+import scala.concurrent.duration.Duration;
+
+public class RuleChainActor extends ComponentActor {
+
+ private RuleChainActor(ActorSystemContext systemContext, TenantId tenantId, RuleChainId ruleChainId) {
+ super(systemContext, tenantId, ruleChainId);
+ setProcessor(new RuleChainActorMessageProcessor(tenantId, ruleChainId, systemContext,
+ logger, context().parent(), context().self()));
+ }
+
+ @Override
+ protected boolean process(TbActorMsg msg) {
+ switch (msg.getMsgType()) {
+ case COMPONENT_LIFE_CYCLE_MSG:
+ onComponentLifecycleMsg((ComponentLifecycleMsg) msg);
+ break;
+ case SERVICE_TO_RULE_ENGINE_MSG:
+ processor.onServiceToRuleEngineMsg((ServiceToRuleEngineMsg) msg);
+ break;
+ case DEVICE_ACTOR_TO_RULE_ENGINE_MSG:
+ processor.onDeviceActorToRuleEngineMsg((DeviceActorToRuleEngineMsg) msg);
+ break;
+ case RULE_TO_RULE_CHAIN_TELL_NEXT_MSG:
+ processor.onTellNext((RuleNodeToRuleChainTellNextMsg) msg);
+ break;
+ case RULE_CHAIN_TO_RULE_CHAIN_MSG:
+ processor.onRuleChainToRuleChainMsg((RuleChainToRuleChainMsg) msg);
+ break;
+ case CLUSTER_EVENT_MSG:
+ break;
+ default:
+ return false;
+ }
+ return true;
+ }
+
+ public static class ActorCreator extends ContextBasedCreator {
+ private static final long serialVersionUID = 1L;
+
+ private final TenantId tenantId;
+ private final RuleChainId ruleChainId;
+
+ public ActorCreator(ActorSystemContext context, TenantId tenantId, RuleChainId pluginId) {
+ super(context);
+ this.tenantId = tenantId;
+ this.ruleChainId = pluginId;
+ }
+
+ @Override
+ public RuleChainActor create() throws Exception {
+ return new RuleChainActor(context, tenantId, ruleChainId);
+ }
+ }
+
+ @Override
+ protected long getErrorPersistFrequency() {
+ return systemContext.getRuleChainErrorPersistFrequency();
+ }
+
+ @Override
+ public SupervisorStrategy supervisorStrategy() {
+ return strategy;
+ }
+
+ private final SupervisorStrategy strategy = new OneForOneStrategy(3, Duration.create("1 minute"), t -> {
+ logAndPersist("Unknown Failure", ActorSystemContext.toException(t));
+ return SupervisorStrategy.resume();
+ });
+}
diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java
new file mode 100644
index 0000000000..7d560db061
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java
@@ -0,0 +1,297 @@
+/**
+ * Copyright © 2016-2018 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.actors.ruleChain;
+
+import akka.actor.ActorContext;
+import akka.actor.ActorRef;
+import akka.actor.Props;
+import akka.event.LoggingAdapter;
+import com.datastax.driver.core.utils.UUIDs;
+import org.thingsboard.server.actors.ActorSystemContext;
+import org.thingsboard.server.actors.device.DeviceActorToRuleEngineMsg;
+import org.thingsboard.server.actors.device.RuleEngineQueuePutAckMsg;
+import org.thingsboard.server.actors.service.DefaultActorService;
+import org.thingsboard.server.actors.shared.ComponentMsgProcessor;
+import org.thingsboard.server.common.data.EntityType;
+import org.thingsboard.server.common.data.id.EntityId;
+import org.thingsboard.server.common.data.id.RuleChainId;
+import org.thingsboard.server.common.data.id.RuleNodeId;
+import org.thingsboard.server.common.data.id.TenantId;
+import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
+import org.thingsboard.server.common.data.plugin.ComponentLifecycleState;
+import org.thingsboard.server.common.data.relation.EntityRelation;
+import org.thingsboard.server.common.data.rule.RuleChain;
+import org.thingsboard.server.common.data.rule.RuleNode;
+import org.thingsboard.server.common.msg.TbMsg;
+import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;
+import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
+import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg;
+import org.thingsboard.server.dao.rule.RuleChainService;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * @author Andrew Shvayka
+ */
+public class RuleChainActorMessageProcessor extends ComponentMsgProcessor {
+
+ private static final long DEFAULT_CLUSTER_PARTITION = 0L;
+ private final ActorRef parent;
+ private final ActorRef self;
+ private final Map nodeActors;
+ private final Map> nodeRoutes;
+ private final RuleChainService service;
+
+ private RuleNodeId firstId;
+ private RuleNodeCtx firstNode;
+ private boolean started;
+
+ RuleChainActorMessageProcessor(TenantId tenantId, RuleChainId ruleChainId, ActorSystemContext systemContext
+ , LoggingAdapter logger, ActorRef parent, ActorRef self) {
+ super(systemContext, logger, tenantId, ruleChainId);
+ this.parent = parent;
+ this.self = self;
+ this.nodeActors = new HashMap<>();
+ this.nodeRoutes = new HashMap<>();
+ this.service = systemContext.getRuleChainService();
+ }
+
+ @Override
+ public void start(ActorContext context) throws Exception {
+ if (!started) {
+ RuleChain ruleChain = service.findRuleChainById(entityId);
+ List ruleNodeList = service.getRuleChainNodes(entityId);
+ // Creating and starting the actors;
+ for (RuleNode ruleNode : ruleNodeList) {
+ ActorRef ruleNodeActor = createRuleNodeActor(context, ruleNode);
+ nodeActors.put(ruleNode.getId(), new RuleNodeCtx(tenantId, self, ruleNodeActor, ruleNode));
+ }
+ initRoutes(ruleChain, ruleNodeList);
+ reprocess(ruleNodeList);
+ started = true;
+ } else {
+ onUpdate(context);
+ }
+ }
+
+ private void reprocess(List ruleNodeList) {
+ for (RuleNode ruleNode : ruleNodeList) {
+ for (TbMsg tbMsg : queue.findUnprocessed(tenantId, ruleNode.getId().getId(), systemContext.getQueuePartitionId())) {
+ pushMsgToNode(nodeActors.get(ruleNode.getId()), tbMsg, "");
+ }
+ }
+ if (firstNode != null) {
+ for (TbMsg tbMsg : queue.findUnprocessed(tenantId, entityId.getId(), systemContext.getQueuePartitionId())) {
+ pushMsgToNode(firstNode, tbMsg, "");
+ }
+ }
+ }
+
+ @Override
+ public void onUpdate(ActorContext context) throws Exception {
+ RuleChain ruleChain = service.findRuleChainById(entityId);
+ List ruleNodeList = service.getRuleChainNodes(entityId);
+
+ for (RuleNode ruleNode : ruleNodeList) {
+ RuleNodeCtx existing = nodeActors.get(ruleNode.getId());
+ if (existing == null) {
+ ActorRef ruleNodeActor = createRuleNodeActor(context, ruleNode);
+ nodeActors.put(ruleNode.getId(), new RuleNodeCtx(tenantId, self, ruleNodeActor, ruleNode));
+ } else {
+ existing.setSelf(ruleNode);
+ existing.getSelfActor().tell(new ComponentLifecycleMsg(tenantId, existing.getSelf().getId(), ComponentLifecycleEvent.UPDATED), self);
+ }
+ }
+
+ Set existingNodes = ruleNodeList.stream().map(RuleNode::getId).collect(Collectors.toSet());
+ List removedRules = nodeActors.keySet().stream().filter(node -> !existingNodes.contains(node)).collect(Collectors.toList());
+ removedRules.forEach(ruleNodeId -> {
+ RuleNodeCtx removed = nodeActors.remove(ruleNodeId);
+ removed.getSelfActor().tell(new ComponentLifecycleMsg(tenantId, removed.getSelf().getId(), ComponentLifecycleEvent.DELETED), self);
+ });
+
+ initRoutes(ruleChain, ruleNodeList);
+ reprocess(ruleNodeList);
+ }
+
+ @Override
+ public void stop(ActorContext context) throws Exception {
+ nodeActors.values().stream().map(RuleNodeCtx::getSelfActor).forEach(context::stop);
+ nodeActors.clear();
+ nodeRoutes.clear();
+ context.stop(self);
+ started = false;
+ }
+
+ @Override
+ public void onClusterEventMsg(ClusterEventMsg msg) throws Exception {
+
+ }
+
+ private ActorRef createRuleNodeActor(ActorContext context, RuleNode ruleNode) {
+ String dispatcherName = tenantId.getId().equals(EntityId.NULL_UUID) ?
+ DefaultActorService.SYSTEM_RULE_DISPATCHER_NAME : DefaultActorService.TENANT_RULE_DISPATCHER_NAME;
+ return context.actorOf(
+ Props.create(new RuleNodeActor.ActorCreator(systemContext, tenantId, entityId, ruleNode.getId()))
+ .withDispatcher(dispatcherName), ruleNode.getId().toString());
+ }
+
+ private void initRoutes(RuleChain ruleChain, List ruleNodeList) {
+ nodeRoutes.clear();
+ // Populating the routes map;
+ for (RuleNode ruleNode : ruleNodeList) {
+ List relations = service.getRuleNodeRelations(ruleNode.getId());
+ if (relations.size() == 0) {
+ nodeRoutes.put(ruleNode.getId(), Collections.emptyList());
+ } else {
+ for (EntityRelation relation : relations) {
+ if (relation.getTo().getEntityType() == EntityType.RULE_NODE) {
+ RuleNodeCtx ruleNodeCtx = nodeActors.get(new RuleNodeId(relation.getTo().getId()));
+ if (ruleNodeCtx == null) {
+ throw new IllegalArgumentException("Rule Node [" + relation.getFrom() + "] has invalid relation to Rule node [" + relation.getTo() + "]");
+ }
+ }
+ nodeRoutes.computeIfAbsent(ruleNode.getId(), k -> new ArrayList<>())
+ .add(new RuleNodeRelation(ruleNode.getId(), relation.getTo(), relation.getType()));
+ }
+ }
+ }
+
+ firstId = ruleChain.getFirstRuleNodeId();
+ firstNode = nodeActors.get(ruleChain.getFirstRuleNodeId());
+ state = ComponentLifecycleState.ACTIVE;
+ }
+
+ void onServiceToRuleEngineMsg(ServiceToRuleEngineMsg envelope) {
+ checkActive();
+ if (firstNode != null) {
+ putToQueue(enrichWithRuleChainId(envelope.getTbMsg()), msg -> pushMsgToNode(firstNode, msg, ""));
+ }
+ }
+
+ void onDeviceActorToRuleEngineMsg(DeviceActorToRuleEngineMsg envelope) {
+ checkActive();
+ if (firstNode != null) {
+ putToQueue(enrichWithRuleChainId(envelope.getTbMsg()), msg -> {
+ pushMsgToNode(firstNode, msg, "");
+ envelope.getCallbackRef().tell(new RuleEngineQueuePutAckMsg(msg.getId()), self);
+ });
+ }
+ }
+
+ void onRuleChainToRuleChainMsg(RuleChainToRuleChainMsg envelope) {
+ checkActive();
+ if (envelope.isEnqueue()) {
+ if (firstNode != null) {
+ putToQueue(enrichWithRuleChainId(envelope.getMsg()), msg -> pushMsgToNode(firstNode, msg, envelope.getFromRelationType()));
+ }
+ } else {
+ if (firstNode != null) {
+ pushMsgToNode(firstNode, envelope.getMsg(), envelope.getFromRelationType());
+ } else {
+ TbMsg msg = envelope.getMsg();
+ EntityId ackId = msg.getRuleNodeId() != null ? msg.getRuleNodeId() : msg.getRuleChainId();
+ queue.ack(tenantId, envelope.getMsg(), ackId.getId(), msg.getClusterPartition());
+ }
+ }
+ }
+
+ void onTellNext(RuleNodeToRuleChainTellNextMsg envelope) {
+ checkActive();
+ RuleNodeId originator = envelope.getOriginator();
+ List relations = nodeRoutes.get(originator).stream()
+ .filter(r -> contains(envelope.getRelationTypes(), r.getType()))
+ .collect(Collectors.toList());
+
+ TbMsg msg = envelope.getMsg();
+ int relationsCount = relations.size();
+ EntityId ackId = msg.getRuleNodeId() != null ? msg.getRuleNodeId() : msg.getRuleChainId();
+ if (relationsCount == 0) {
+ queue.ack(tenantId, msg, ackId.getId(), msg.getClusterPartition());
+ } else if (relationsCount == 1) {
+ for (RuleNodeRelation relation : relations) {
+ pushToTarget(msg, relation.getOut(), relation.getType());
+ }
+ } else {
+ for (RuleNodeRelation relation : relations) {
+ EntityId target = relation.getOut();
+ switch (target.getEntityType()) {
+ case RULE_NODE:
+ enqueueAndForwardMsgCopyToNode(msg, target, relation.getType());
+ break;
+ case RULE_CHAIN:
+ enqueueAndForwardMsgCopyToChain(msg, target, relation.getType());
+ break;
+ }
+ }
+ //TODO: Ideally this should happen in async way when all targets confirm that the copied messages are successfully written to corresponding target queues.
+ queue.ack(tenantId, msg, ackId.getId(), msg.getClusterPartition());
+ }
+ }
+
+ private boolean contains(Set relationTypes, String type) {
+ if (relationTypes == null) {
+ return true;
+ }
+ for (String relationType : relationTypes) {
+ if (relationType.equalsIgnoreCase(type)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void enqueueAndForwardMsgCopyToChain(TbMsg msg, EntityId target, String fromRelationType) {
+ RuleChainId targetRCId = new RuleChainId(target.getId());
+ TbMsg copyMsg = msg.copy(UUIDs.timeBased(), targetRCId, null, DEFAULT_CLUSTER_PARTITION);
+ parent.tell(new RuleChainToRuleChainMsg(new RuleChainId(target.getId()), entityId, copyMsg, fromRelationType, true), self);
+ }
+
+ private void enqueueAndForwardMsgCopyToNode(TbMsg msg, EntityId target, String fromRelationType) {
+ RuleNodeId targetId = new RuleNodeId(target.getId());
+ RuleNodeCtx targetNodeCtx = nodeActors.get(targetId);
+ TbMsg copy = msg.copy(UUIDs.timeBased(), entityId, targetId, DEFAULT_CLUSTER_PARTITION);
+ putToQueue(copy, queuedMsg -> pushMsgToNode(targetNodeCtx, queuedMsg, fromRelationType));
+ }
+
+ private void pushToTarget(TbMsg msg, EntityId target, String fromRelationType) {
+ switch (target.getEntityType()) {
+ case RULE_NODE:
+ pushMsgToNode(nodeActors.get(new RuleNodeId(target.getId())), msg, fromRelationType);
+ break;
+ case RULE_CHAIN:
+ parent.tell(new RuleChainToRuleChainMsg(new RuleChainId(target.getId()), entityId, msg, fromRelationType, false), self);
+ break;
+ }
+ }
+
+ private void pushMsgToNode(RuleNodeCtx nodeCtx, TbMsg msg, String fromRelationType) {
+ if (nodeCtx != null) {
+ nodeCtx.getSelfActor().tell(new RuleChainToRuleNodeMsg(new DefaultTbContext(systemContext, nodeCtx), msg, fromRelationType), self);
+ }
+ }
+
+ private TbMsg enrichWithRuleChainId(TbMsg tbMsg) {
+ // We don't put firstNodeId because it may change over time;
+ return new TbMsg(tbMsg.getId(), tbMsg.getType(), tbMsg.getOriginator(), tbMsg.getMetaData().copy(), tbMsg.getData(), entityId, null, systemContext.getQueuePartitionId());
+ }
+}
diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainManagerActor.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainManagerActor.java
new file mode 100644
index 0000000000..47ee796141
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainManagerActor.java
@@ -0,0 +1,57 @@
+/**
+ * Copyright © 2016-2018 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.actors.ruleChain;
+
+import akka.actor.ActorRef;
+import org.thingsboard.server.actors.ActorSystemContext;
+import org.thingsboard.server.actors.service.ContextAwareActor;
+import org.thingsboard.server.actors.shared.rulechain.RuleChainManager;
+import org.thingsboard.server.common.data.id.EntityId;
+import org.thingsboard.server.common.data.id.RuleChainId;
+import org.thingsboard.server.dao.rule.RuleChainService;
+
+/**
+ * Created by ashvayka on 15.03.18.
+ */
+public abstract class RuleChainManagerActor extends ContextAwareActor {
+
+ protected final RuleChainManager ruleChainManager;
+ protected final RuleChainService ruleChainService;
+
+ public RuleChainManagerActor(ActorSystemContext systemContext, RuleChainManager ruleChainManager) {
+ super(systemContext);
+ this.ruleChainManager = ruleChainManager;
+ this.ruleChainService = systemContext.getRuleChainService();
+ }
+
+ protected void initRuleChains() {
+ ruleChainManager.init(this.context());
+ }
+
+ protected ActorRef getEntityActorRef(EntityId entityId) {
+ ActorRef target = null;
+ switch (entityId.getEntityType()) {
+ case RULE_CHAIN:
+ target = ruleChainManager.getOrCreateActor(this.context(), (RuleChainId) entityId);
+ break;
+ }
+ return target;
+ }
+
+ protected void broadcast(Object msg) {
+ ruleChainManager.broadcast(msg);
+ }
+}
diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainToRuleChainMsg.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainToRuleChainMsg.java
new file mode 100644
index 0000000000..8b13747fb6
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainToRuleChainMsg.java
@@ -0,0 +1,40 @@
+/**
+ * Copyright © 2016-2018 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.actors.ruleChain;
+
+import lombok.Data;
+import org.thingsboard.server.common.data.id.RuleChainId;
+import org.thingsboard.server.common.msg.MsgType;
+import org.thingsboard.server.common.msg.TbActorMsg;
+import org.thingsboard.server.common.msg.TbMsg;
+
+/**
+ * Created by ashvayka on 19.03.18.
+ */
+@Data
+public final class RuleChainToRuleChainMsg implements TbActorMsg {
+
+ private final RuleChainId target;
+ private final RuleChainId source;
+ private final TbMsg msg;
+ private final String fromRelationType;
+ private final boolean enqueue;
+
+ @Override
+ public MsgType getMsgType() {
+ return MsgType.RULE_CHAIN_TO_RULE_CHAIN_MSG;
+ }
+}
diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainToRuleNodeMsg.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainToRuleNodeMsg.java
new file mode 100644
index 0000000000..abcddc91fc
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainToRuleNodeMsg.java
@@ -0,0 +1,38 @@
+/**
+ * Copyright © 2016-2018 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.actors.ruleChain;
+
+import lombok.Data;
+import org.thingsboard.rule.engine.api.TbContext;
+import org.thingsboard.server.common.msg.MsgType;
+import org.thingsboard.server.common.msg.TbActorMsg;
+import org.thingsboard.server.common.msg.TbMsg;
+
+/**
+ * Created by ashvayka on 19.03.18.
+ */
+@Data
+final class RuleChainToRuleNodeMsg implements TbActorMsg {
+
+ private final TbContext ctx;
+ private final TbMsg msg;
+ private final String fromRelationType;
+
+ @Override
+ public MsgType getMsgType() {
+ return MsgType.RULE_CHAIN_TO_RULE_MSG;
+ }
+}
diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeActor.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeActor.java
new file mode 100644
index 0000000000..f7ca0d8cc7
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeActor.java
@@ -0,0 +1,109 @@
+/**
+ * Copyright © 2016-2018 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.actors.ruleChain;
+
+import org.thingsboard.server.actors.ActorSystemContext;
+import org.thingsboard.server.actors.service.ComponentActor;
+import org.thingsboard.server.actors.service.ContextBasedCreator;
+import org.thingsboard.server.common.data.id.RuleChainId;
+import org.thingsboard.server.common.data.id.RuleNodeId;
+import org.thingsboard.server.common.data.id.TenantId;
+import org.thingsboard.server.common.msg.TbActorMsg;
+import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
+
+public class RuleNodeActor extends ComponentActor {
+
+ private final RuleChainId ruleChainId;
+
+ private RuleNodeActor(ActorSystemContext systemContext, TenantId tenantId, RuleChainId ruleChainId, RuleNodeId ruleNodeId) {
+ super(systemContext, tenantId, ruleNodeId);
+ this.ruleChainId = ruleChainId;
+ setProcessor(new RuleNodeActorMessageProcessor(tenantId, ruleChainId, ruleNodeId, systemContext,
+ logger, context().parent(), context().self()));
+ }
+
+ @Override
+ protected boolean process(TbActorMsg msg) {
+ switch (msg.getMsgType()) {
+ case COMPONENT_LIFE_CYCLE_MSG:
+ onComponentLifecycleMsg((ComponentLifecycleMsg) msg);
+ break;
+ case RULE_CHAIN_TO_RULE_MSG:
+ onRuleChainToRuleNodeMsg((RuleChainToRuleNodeMsg) msg);
+ break;
+ case RULE_TO_SELF_ERROR_MSG:
+ onRuleNodeToSelfErrorMsg((RuleNodeToSelfErrorMsg) msg);
+ break;
+ case RULE_TO_SELF_MSG:
+ onRuleNodeToSelfMsg((RuleNodeToSelfMsg) msg);
+ break;
+ default:
+ return false;
+ }
+ return true;
+ }
+
+ private void onRuleNodeToSelfMsg(RuleNodeToSelfMsg msg) {
+ logger.debug("[{}] Going to process rule msg: {}", id, msg.getMsg());
+ try {
+ processor.onRuleToSelfMsg(msg);
+ increaseMessagesProcessedCount();
+ } catch (Exception e) {
+ logAndPersist("onRuleMsg", e);
+ }
+ }
+
+ private void onRuleChainToRuleNodeMsg(RuleChainToRuleNodeMsg msg) {
+ logger.debug("[{}] Going to process rule msg: {}", id, msg.getMsg());
+ try {
+ processor.onRuleChainToRuleNodeMsg(msg);
+ increaseMessagesProcessedCount();
+ } catch (Exception e) {
+ logAndPersist("onRuleMsg", e);
+ }
+ }
+
+ private void onRuleNodeToSelfErrorMsg(RuleNodeToSelfErrorMsg msg) {
+ logAndPersist("onRuleMsg", ActorSystemContext.toException(msg.getError()));
+ }
+
+ public static class ActorCreator extends ContextBasedCreator {
+ private static final long serialVersionUID = 1L;
+
+ private final TenantId tenantId;
+ private final RuleChainId ruleChainId;
+ private final RuleNodeId ruleNodeId;
+
+ public ActorCreator(ActorSystemContext context, TenantId tenantId, RuleChainId ruleChainId, RuleNodeId ruleNodeId) {
+ super(context);
+ this.tenantId = tenantId;
+ this.ruleChainId = ruleChainId;
+ this.ruleNodeId = ruleNodeId;
+
+ }
+
+ @Override
+ public RuleNodeActor create() throws Exception {
+ return new RuleNodeActor(context, tenantId, ruleChainId, ruleNodeId);
+ }
+ }
+
+ @Override
+ protected long getErrorPersistFrequency() {
+ return systemContext.getRuleNodeErrorPersistFrequency();
+ }
+
+}
diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeActorMessageProcessor.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeActorMessageProcessor.java
new file mode 100644
index 0000000000..acb171d959
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeActorMessageProcessor.java
@@ -0,0 +1,121 @@
+/**
+ * Copyright © 2016-2018 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.actors.ruleChain;
+
+import akka.actor.ActorContext;
+import akka.actor.ActorRef;
+import akka.event.LoggingAdapter;
+import org.thingsboard.rule.engine.api.TbContext;
+import org.thingsboard.rule.engine.api.TbNode;
+import org.thingsboard.rule.engine.api.TbNodeConfiguration;
+import org.thingsboard.server.actors.ActorSystemContext;
+import org.thingsboard.server.actors.shared.ComponentMsgProcessor;
+import org.thingsboard.server.common.data.id.RuleChainId;
+import org.thingsboard.server.common.data.id.RuleNodeId;
+import org.thingsboard.server.common.data.id.TenantId;
+import org.thingsboard.server.common.data.plugin.ComponentLifecycleState;
+import org.thingsboard.server.common.data.rule.RuleNode;
+import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;
+import org.thingsboard.server.dao.rule.RuleChainService;
+
+/**
+ * @author Andrew Shvayka
+ */
+public class RuleNodeActorMessageProcessor extends ComponentMsgProcessor {
+
+ private final ActorRef parent;
+ private final ActorRef self;
+ private final RuleChainService service;
+ private RuleNode ruleNode;
+ private TbNode tbNode;
+ private TbContext defaultCtx;
+
+ RuleNodeActorMessageProcessor(TenantId tenantId, RuleChainId ruleChainId, RuleNodeId ruleNodeId, ActorSystemContext systemContext
+ , LoggingAdapter logger, ActorRef parent, ActorRef self) {
+ super(systemContext, logger, tenantId, ruleNodeId);
+ this.parent = parent;
+ this.self = self;
+ this.service = systemContext.getRuleChainService();
+ this.ruleNode = systemContext.getRuleChainService().findRuleNodeById(entityId);
+ this.defaultCtx = new DefaultTbContext(systemContext, new RuleNodeCtx(tenantId, parent, self, ruleNode));
+ }
+
+ @Override
+ public void start(ActorContext context) throws Exception {
+ tbNode = initComponent(ruleNode);
+ state = ComponentLifecycleState.ACTIVE;
+ }
+
+ @Override
+ public void onUpdate(ActorContext context) throws Exception {
+ RuleNode newRuleNode = systemContext.getRuleChainService().findRuleNodeById(entityId);
+ boolean restartRequired = !(ruleNode.getType().equals(newRuleNode.getType())
+ && ruleNode.getConfiguration().equals(newRuleNode.getConfiguration()));
+ this.ruleNode = newRuleNode;
+ this.defaultCtx.updateSelf(newRuleNode);
+ if (restartRequired) {
+ if (tbNode != null) {
+ tbNode.destroy();
+ }
+ start(context);
+ }
+ }
+
+ @Override
+ public void stop(ActorContext context) throws Exception {
+ if (tbNode != null) {
+ tbNode.destroy();
+ }
+ context.stop(self);
+ }
+
+ @Override
+ public void onClusterEventMsg(ClusterEventMsg msg) throws Exception {
+
+ }
+
+ public void onRuleToSelfMsg(RuleNodeToSelfMsg msg) throws Exception {
+ checkActive();
+ if (ruleNode.isDebugMode()) {
+ systemContext.persistDebugInput(tenantId, entityId, msg.getMsg(), "Self");
+ }
+ try {
+ tbNode.onMsg(defaultCtx, msg.getMsg());
+ } catch (Exception e) {
+ defaultCtx.tellFailure(msg.getMsg(), e);
+ }
+ }
+
+ void onRuleChainToRuleNodeMsg(RuleChainToRuleNodeMsg msg) throws Exception {
+ checkActive();
+ if (ruleNode.isDebugMode()) {
+ systemContext.persistDebugInput(tenantId, entityId, msg.getMsg(), msg.getFromRelationType());
+ }
+ try {
+ tbNode.onMsg(msg.getCtx(), msg.getMsg());
+ } catch (Exception e) {
+ msg.getCtx().tellFailure(msg.getMsg(), e);
+ }
+ }
+
+ private TbNode initComponent(RuleNode ruleNode) throws Exception {
+ Class> componentClazz = Class.forName(ruleNode.getType());
+ TbNode tbNode = (TbNode) (componentClazz.newInstance());
+ tbNode.init(defaultCtx, new TbNodeConfiguration(ruleNode.getConfiguration()));
+ return tbNode;
+ }
+
+}
diff --git a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/msg/ToPluginRpcResponseDeviceMsg.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeCtx.java
similarity index 64%
rename from extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/msg/ToPluginRpcResponseDeviceMsg.java
rename to application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeCtx.java
index 1122bad9f6..10fcc8b606 100644
--- a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/msg/ToPluginRpcResponseDeviceMsg.java
+++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeCtx.java
@@ -13,19 +13,22 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.thingsboard.server.extensions.api.plugins.msg;
+package org.thingsboard.server.actors.ruleChain;
+import akka.actor.ActorRef;
+import lombok.AllArgsConstructor;
import lombok.Data;
-import org.thingsboard.server.common.data.id.PluginId;
import org.thingsboard.server.common.data.id.TenantId;
+import org.thingsboard.server.common.data.rule.RuleNode;
/**
- * @author Andrew Shvayka
+ * Created by ashvayka on 19.03.18.
*/
@Data
-public class ToPluginRpcResponseDeviceMsg implements ToPluginActorMsg {
- private final PluginId pluginId;
- private final TenantId pluginTenantId;
- private final FromDeviceRpcResponse response;
+@AllArgsConstructor
+final class RuleNodeCtx {
+ private final TenantId tenantId;
+ private final ActorRef chainActor;
+ private final ActorRef selfActor;
+ private RuleNode self;
}
-
diff --git a/extensions-core/src/main/java/org/thingsboard/server/extensions/core/filter/MethodNameFilterConfiguration.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeRelation.java
similarity index 71%
rename from extensions-core/src/main/java/org/thingsboard/server/extensions/core/filter/MethodNameFilterConfiguration.java
rename to application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeRelation.java
index a018af198f..7861e5473c 100644
--- a/extensions-core/src/main/java/org/thingsboard/server/extensions/core/filter/MethodNameFilterConfiguration.java
+++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeRelation.java
@@ -13,21 +13,20 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.thingsboard.server.extensions.core.filter;
+package org.thingsboard.server.actors.ruleChain;
import lombok.Data;
+import org.thingsboard.server.common.data.id.EntityId;
/**
- * @author Andrew Shvayka
+ * Created by ashvayka on 19.03.18.
*/
-@Data
-public class MethodNameFilterConfiguration {
- private MethodName[] methodNames;
+@Data
+final class RuleNodeRelation {
- @Data
- public static class MethodName {
- private String name;
- }
+ private final EntityId in;
+ private final EntityId out;
+ private final String type;
}
diff --git a/extensions/extension-rabbitmq/src/main/java/org/thingsboard/server/extensions/rabbitmq/plugin/RabbitMqPluginConfiguration.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeToRuleChainTellNextMsg.java
similarity index 52%
rename from extensions/extension-rabbitmq/src/main/java/org/thingsboard/server/extensions/rabbitmq/plugin/RabbitMqPluginConfiguration.java
rename to application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeToRuleChainTellNextMsg.java
index d98d3dcabb..c0a475c953 100644
--- a/extensions/extension-rabbitmq/src/main/java/org/thingsboard/server/extensions/rabbitmq/plugin/RabbitMqPluginConfiguration.java
+++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeToRuleChainTellNextMsg.java
@@ -13,35 +13,29 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.thingsboard.server.extensions.rabbitmq.plugin;
+package org.thingsboard.server.actors.ruleChain;
import lombok.Data;
+import org.thingsboard.server.common.data.id.RuleNodeId;
+import org.thingsboard.server.common.msg.MsgType;
+import org.thingsboard.server.common.msg.TbActorMsg;
+import org.thingsboard.server.common.msg.TbMsg;
-import java.util.List;
+import java.util.Set;
/**
- * @author Andrew Shvayka
+ * Created by ashvayka on 19.03.18.
*/
@Data
-public class RabbitMqPluginConfiguration {
- private String host;
- private int port;
- private String virtualHost;
+final class RuleNodeToRuleChainTellNextMsg implements TbActorMsg {
- private String userName;
- private String password;
+ private final RuleNodeId originator;
+ private final Set relationTypes;
+ private final TbMsg msg;
- private Boolean automaticRecoveryEnabled;
-
- private Integer connectionTimeout;
- private Integer handshakeTimeout;
-
- private List clientProperties;
-
- @Data
- public static class RabbitMqPluginProperties {
- private String key;
- private String value;
+ @Override
+ public MsgType getMsgType() {
+ return MsgType.RULE_TO_RULE_CHAIN_TELL_NEXT_MSG;
}
}
diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeToSelfErrorMsg.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeToSelfErrorMsg.java
new file mode 100644
index 0000000000..e6248f199f
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeToSelfErrorMsg.java
@@ -0,0 +1,37 @@
+/**
+ * Copyright © 2016-2018 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.actors.ruleChain;
+
+import lombok.Data;
+import org.thingsboard.server.common.msg.MsgType;
+import org.thingsboard.server.common.msg.TbActorMsg;
+import org.thingsboard.server.common.msg.TbMsg;
+
+/**
+ * Created by ashvayka on 19.03.18.
+ */
+@Data
+final class RuleNodeToSelfErrorMsg implements TbActorMsg {
+
+ private final TbMsg msg;
+ private final Throwable error;
+
+ @Override
+ public MsgType getMsgType() {
+ return MsgType.RULE_TO_SELF_ERROR_MSG;
+ }
+
+}
diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeToSelfMsg.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeToSelfMsg.java
new file mode 100644
index 0000000000..d1bac4d7c5
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeToSelfMsg.java
@@ -0,0 +1,36 @@
+/**
+ * Copyright © 2016-2018 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.actors.ruleChain;
+
+import lombok.Data;
+import org.thingsboard.server.common.msg.MsgType;
+import org.thingsboard.server.common.msg.TbActorMsg;
+import org.thingsboard.server.common.msg.TbMsg;
+
+/**
+ * Created by ashvayka on 19.03.18.
+ */
+@Data
+final class RuleNodeToSelfMsg implements TbActorMsg {
+
+ private final TbMsg msg;
+
+ @Override
+ public MsgType getMsgType() {
+ return MsgType.RULE_TO_SELF_MSG;
+ }
+
+}
diff --git a/application/src/main/java/org/thingsboard/server/actors/service/ActorService.java b/application/src/main/java/org/thingsboard/server/actors/service/ActorService.java
index baae376c64..f7e80e4baa 100644
--- a/application/src/main/java/org/thingsboard/server/actors/service/ActorService.java
+++ b/application/src/main/java/org/thingsboard/server/actors/service/ActorService.java
@@ -16,21 +16,24 @@
package org.thingsboard.server.actors.service;
import org.thingsboard.server.common.data.id.DeviceId;
-import org.thingsboard.server.common.data.id.PluginId;
-import org.thingsboard.server.common.data.id.RuleId;
+import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
+import org.thingsboard.server.common.msg.cluster.SendToClusterMsg;
+import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg;
import org.thingsboard.server.common.transport.SessionMsgProcessor;
import org.thingsboard.server.service.cluster.discovery.DiscoveryServiceListener;
import org.thingsboard.server.service.cluster.rpc.RpcMsgListener;
-public interface ActorService extends SessionMsgProcessor, WebSocketMsgProcessor, RestMsgProcessor, RpcMsgListener, DiscoveryServiceListener {
+public interface ActorService extends SessionMsgProcessor, RpcMsgListener, DiscoveryServiceListener {
- void onPluginStateChange(TenantId tenantId, PluginId pluginId, ComponentLifecycleEvent state);
+ void onEntityStateChange(TenantId tenantId, EntityId entityId, ComponentLifecycleEvent state);
- void onRuleStateChange(TenantId tenantId, RuleId ruleId, ComponentLifecycleEvent state);
+ void onMsg(SendToClusterMsg msg);
void onCredentialsUpdate(TenantId tenantId, DeviceId deviceId);
void onDeviceNameOrTypeUpdate(TenantId tenantId, DeviceId deviceId, String deviceName, String deviceType);
+
+ void onMsg(ServiceToRuleEngineMsg serviceToRuleEngineMsg);
}
diff --git a/application/src/main/java/org/thingsboard/server/actors/service/ComponentActor.java b/application/src/main/java/org/thingsboard/server/actors/service/ComponentActor.java
index 76b9be96b3..6aa68d3c5b 100644
--- a/application/src/main/java/org/thingsboard/server/actors/service/ComponentActor.java
+++ b/application/src/main/java/org/thingsboard/server/actors/service/ComponentActor.java
@@ -54,7 +54,7 @@ public abstract class ComponentActor implements Creator {
diff --git a/application/src/main/java/org/thingsboard/server/actors/service/DefaultActorService.java b/application/src/main/java/org/thingsboard/server/actors/service/DefaultActorService.java
index bb84a30229..3507f244e9 100644
--- a/application/src/main/java/org/thingsboard/server/actors/service/DefaultActorService.java
+++ b/application/src/main/java/org/thingsboard/server/actors/service/DefaultActorService.java
@@ -19,35 +19,32 @@ import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import akka.actor.Props;
import akka.actor.Terminated;
+import com.google.protobuf.ByteString;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
+import org.thingsboard.rule.engine.api.msg.DeviceCredentialsUpdateNotificationMsg;
+import org.thingsboard.rule.engine.api.msg.DeviceNameOrTypeUpdateMsg;
import org.thingsboard.server.actors.ActorSystemContext;
import org.thingsboard.server.actors.app.AppActor;
import org.thingsboard.server.actors.rpc.RpcBroadcastMsg;
import org.thingsboard.server.actors.rpc.RpcManagerActor;
import org.thingsboard.server.actors.rpc.RpcSessionCreateRequestMsg;
-import org.thingsboard.server.actors.rpc.RpcSessionTellMsg;
import org.thingsboard.server.actors.session.SessionManagerActor;
import org.thingsboard.server.actors.stats.StatsActor;
import org.thingsboard.server.common.data.id.DeviceId;
-import org.thingsboard.server.common.data.id.PluginId;
-import org.thingsboard.server.common.data.id.RuleId;
+import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
+import org.thingsboard.server.common.msg.TbActorMsg;
import org.thingsboard.server.common.msg.aware.SessionAwareMsg;
import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;
+import org.thingsboard.server.common.msg.cluster.SendToClusterMsg;
import org.thingsboard.server.common.msg.cluster.ServerAddress;
import org.thingsboard.server.common.msg.cluster.ToAllNodesMsg;
-import org.thingsboard.server.common.msg.core.ToDeviceSessionActorMsg;
-import org.thingsboard.server.extensions.api.device.DeviceNameOrTypeUpdateMsg;
-import org.thingsboard.server.common.msg.device.ToDeviceActorMsg;
import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
-import org.thingsboard.server.extensions.api.device.DeviceCredentialsUpdateNotificationMsg;
-import org.thingsboard.server.extensions.api.device.ToDeviceActorNotificationMsg;
-import org.thingsboard.server.extensions.api.plugins.msg.ToPluginActorMsg;
-import org.thingsboard.server.extensions.api.plugins.rest.PluginRestMsg;
-import org.thingsboard.server.extensions.api.plugins.ws.msg.PluginWebsocketMsg;
+import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg;
+import org.thingsboard.server.gen.cluster.ClusterAPIProtos;
import org.thingsboard.server.service.cluster.discovery.DiscoveryService;
import org.thingsboard.server.service.cluster.discovery.ServerInstance;
import org.thingsboard.server.service.cluster.rpc.ClusterRpcService;
@@ -57,7 +54,8 @@ import scala.concurrent.duration.Duration;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
-import java.util.Optional;
+
+import static org.thingsboard.server.gen.cluster.ClusterAPIProtos.MessageType.CLUSTER_ACTOR_MESSAGE;
@Service
@Slf4j
@@ -93,7 +91,7 @@ public class DefaultActorService implements ActorService {
@PostConstruct
public void initActorSystem() {
- log.info("Initializing Actor system. {}", actorContext.getRuleService());
+ log.info("Initializing Actor system. {}", actorContext.getRuleChainService());
actorContext.setActorService(this);
system = ActorSystem.create(ACTOR_SYSTEM_NAME, actorContext.getConfig());
actorContext.setActorSystem(system);
@@ -129,71 +127,16 @@ public class DefaultActorService implements ActorService {
}
@Override
- public void process(SessionAwareMsg msg) {
- log.debug("Processing session aware msg: {}", msg);
- sessionManagerActor.tell(msg, ActorRef.noSender());
- }
-
- @Override
- public void process(PluginWebsocketMsg> msg) {
- log.debug("Processing websocket msg: {}", msg);
- appActor.tell(msg, ActorRef.noSender());
- }
-
- @Override
- public void process(PluginRestMsg msg) {
- log.debug("Processing rest msg: {}", msg);
- appActor.tell(msg, ActorRef.noSender());
- }
-
- @Override
- public void onMsg(ToPluginActorMsg msg) {
- log.trace("Processing plugin rpc msg: {}", msg);
- appActor.tell(msg, ActorRef.noSender());
- }
-
- @Override
- public void onMsg(ToDeviceActorMsg msg) {
- log.trace("Processing device rpc msg: {}", msg);
+ public void onMsg(SendToClusterMsg msg) {
appActor.tell(msg, ActorRef.noSender());
}
@Override
- public void onMsg(ToDeviceActorNotificationMsg msg) {
- log.trace("Processing notification rpc msg: {}", msg);
- appActor.tell(msg, ActorRef.noSender());
- }
-
- @Override
- public void onMsg(ToDeviceSessionActorMsg msg) {
- log.trace("Processing session rpc msg: {}", msg);
+ public void process(SessionAwareMsg msg) {
+ log.debug("Processing session aware msg: {}", msg);
sessionManagerActor.tell(msg, ActorRef.noSender());
}
- @Override
- public void onMsg(ToAllNodesMsg msg) {
- log.trace("Processing broadcast rpc msg: {}", msg);
- appActor.tell(msg, ActorRef.noSender());
- }
-
- @Override
- public void onMsg(RpcSessionCreateRequestMsg msg) {
- log.trace("Processing session create msg: {}", msg);
- rpcManagerActor.tell(msg, ActorRef.noSender());
- }
-
- @Override
- public void onMsg(RpcSessionTellMsg msg) {
- log.trace("Processing session rpc msg: {}", msg);
- rpcManagerActor.tell(msg, ActorRef.noSender());
- }
-
- @Override
- public void onMsg(RpcBroadcastMsg msg) {
- log.trace("Processing broadcast rpc msg: {}", msg);
- rpcManagerActor.tell(msg, ActorRef.noSender());
- }
-
@Override
public void onServerAdded(ServerInstance server) {
log.trace("Processing onServerAdded msg: {}", server);
@@ -212,42 +155,37 @@ public class DefaultActorService implements ActorService {
}
@Override
- public void onPluginStateChange(TenantId tenantId, PluginId pluginId, ComponentLifecycleEvent state) {
- log.trace("[{}] Processing onPluginStateChange event: {}", pluginId, state);
- broadcast(ComponentLifecycleMsg.forPlugin(tenantId, pluginId, state));
- }
-
- @Override
- public void onRuleStateChange(TenantId tenantId, RuleId ruleId, ComponentLifecycleEvent state) {
- log.trace("[{}] Processing onRuleStateChange event: {}", ruleId, state);
- broadcast(ComponentLifecycleMsg.forRule(tenantId, ruleId, state));
+ public void onEntityStateChange(TenantId tenantId, EntityId entityId, ComponentLifecycleEvent state) {
+ log.trace("[{}] Processing {} state change event: {}", tenantId, entityId.getEntityType(), state);
+ broadcast(new ComponentLifecycleMsg(tenantId, entityId, state));
}
@Override
public void onCredentialsUpdate(TenantId tenantId, DeviceId deviceId) {
DeviceCredentialsUpdateNotificationMsg msg = new DeviceCredentialsUpdateNotificationMsg(tenantId, deviceId);
- Optional address = actorContext.getRoutingService().resolveById(deviceId);
- if (address.isPresent()) {
- rpcService.tell(address.get(), msg);
- } else {
- onMsg(msg);
- }
+ appActor.tell(new SendToClusterMsg(deviceId, msg), ActorRef.noSender());
}
@Override
public void onDeviceNameOrTypeUpdate(TenantId tenantId, DeviceId deviceId, String deviceName, String deviceType) {
log.trace("[{}] Processing onDeviceNameOrTypeUpdate event, deviceName: {}, deviceType: {}", deviceId, deviceName, deviceType);
DeviceNameOrTypeUpdateMsg msg = new DeviceNameOrTypeUpdateMsg(tenantId, deviceId, deviceName, deviceType);
- Optional address = actorContext.getRoutingService().resolveById(deviceId);
- if (address.isPresent()) {
- rpcService.tell(address.get(), msg);
- } else {
- onMsg(msg);
- }
+ appActor.tell(new SendToClusterMsg(deviceId, msg), ActorRef.noSender());
+ }
+
+ @Override
+ public void onMsg(ServiceToRuleEngineMsg msg) {
+ appActor.tell(msg, ActorRef.noSender());
}
public void broadcast(ToAllNodesMsg msg) {
- rpcService.broadcast(msg);
+ actorContext.getEncodingService().encode(msg);
+ rpcService.broadcast(new RpcBroadcastMsg(ClusterAPIProtos.ClusterMessage
+ .newBuilder()
+ .setPayload(ByteString
+ .copyFrom(actorContext.getEncodingService().encode(msg)))
+ .setMessageType(CLUSTER_ACTOR_MESSAGE)
+ .build()));
appActor.tell(msg, ActorRef.noSender());
}
@@ -256,4 +194,64 @@ public class DefaultActorService implements ActorService {
this.sessionManagerActor.tell(msg, ActorRef.noSender());
this.rpcManagerActor.tell(msg, ActorRef.noSender());
}
+
+ @Override
+ public void onReceivedMsg(ServerAddress source, ClusterAPIProtos.ClusterMessage msg) {
+ ServerAddress serverAddress = new ServerAddress(source.getHost(), source.getPort());
+ log.info("Received msg [{}] from [{}]", msg.getMessageType().name(), serverAddress);
+ if(log.isDebugEnabled()){
+ log.info("MSG: ", msg);
+ }
+ switch (msg.getMessageType()) {
+ case CLUSTER_ACTOR_MESSAGE:
+ java.util.Optional decodedMsg = actorContext.getEncodingService()
+ .decode(msg.getPayload().toByteArray());
+ if (decodedMsg.isPresent()) {
+ appActor.tell(decodedMsg.get(), ActorRef.noSender());
+ } else {
+ log.error("Error during decoding cluster proto message");
+ }
+ break;
+ case TO_ALL_NODES_MSG:
+ //TODO
+ break;
+ case CLUSTER_TELEMETRY_SUBSCRIPTION_CREATE_MESSAGE:
+ actorContext.getTsSubService().onNewRemoteSubscription(serverAddress, msg.getPayload().toByteArray());
+ break;
+ case CLUSTER_TELEMETRY_SUBSCRIPTION_UPDATE_MESSAGE:
+ actorContext.getTsSubService().onRemoteSubscriptionUpdate(serverAddress, msg.getPayload().toByteArray());
+ break;
+ case CLUSTER_TELEMETRY_SUBSCRIPTION_CLOSE_MESSAGE:
+ actorContext.getTsSubService().onRemoteSubscriptionClose(serverAddress, msg.getPayload().toByteArray());
+ break;
+ case CLUSTER_TELEMETRY_SESSION_CLOSE_MESSAGE:
+ actorContext.getTsSubService().onRemoteSessionClose(serverAddress, msg.getPayload().toByteArray());
+ break;
+ case CLUSTER_TELEMETRY_ATTR_UPDATE_MESSAGE:
+ actorContext.getTsSubService().onRemoteAttributesUpdate(serverAddress, msg.getPayload().toByteArray());
+ break;
+ case CLUSTER_TELEMETRY_TS_UPDATE_MESSAGE:
+ actorContext.getTsSubService().onRemoteTsUpdate(serverAddress, msg.getPayload().toByteArray());
+ break;
+ case CLUSTER_RPC_FROM_DEVICE_RESPONSE_MESSAGE:
+ actorContext.getDeviceRpcService().processRemoteResponseFromDevice(serverAddress, msg.getPayload().toByteArray());
+ break;
+ }
+ }
+
+ @Override
+ public void onSendMsg(ClusterAPIProtos.ClusterMessage msg) {
+ rpcManagerActor.tell(msg, ActorRef.noSender());
+ }
+
+ @Override
+ public void onRpcSessionCreateRequestMsg(RpcSessionCreateRequestMsg msg) {
+ rpcManagerActor.tell(msg, ActorRef.noSender());
+ }
+
+ @Override
+ public void onBroadcastMsg(RpcBroadcastMsg msg) {
+ rpcManagerActor.tell(msg, ActorRef.noSender());
+ }
+
}
diff --git a/application/src/main/java/org/thingsboard/server/actors/service/WebSocketMsgProcessor.java b/application/src/main/java/org/thingsboard/server/actors/service/WebSocketMsgProcessor.java
deleted file mode 100644
index d0c9ca6202..0000000000
--- a/application/src/main/java/org/thingsboard/server/actors/service/WebSocketMsgProcessor.java
+++ /dev/null
@@ -1,24 +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.service;
-
-import org.thingsboard.server.extensions.api.plugins.ws.msg.PluginWebsocketMsg;
-
-public interface WebSocketMsgProcessor {
-
- void process(PluginWebsocketMsg> msg);
-
-}
diff --git a/application/src/main/java/org/thingsboard/server/actors/session/ASyncMsgProcessor.java b/application/src/main/java/org/thingsboard/server/actors/session/ASyncMsgProcessor.java
index ab75cdb9fe..a8f14fedf5 100644
--- a/application/src/main/java/org/thingsboard/server/actors/session/ASyncMsgProcessor.java
+++ b/application/src/main/java/org/thingsboard/server/actors/session/ASyncMsgProcessor.java
@@ -15,19 +15,26 @@
*/
package org.thingsboard.server.actors.session;
+import akka.actor.ActorContext;
+import akka.event.LoggingAdapter;
import org.thingsboard.server.actors.ActorSystemContext;
import org.thingsboard.server.actors.shared.SessionTimeoutMsg;
import org.thingsboard.server.common.data.id.SessionId;
import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;
import org.thingsboard.server.common.msg.cluster.ServerAddress;
-import org.thingsboard.server.common.msg.core.*;
+import org.thingsboard.server.common.msg.core.AttributesSubscribeMsg;
+import org.thingsboard.server.common.msg.core.ResponseMsg;
+import org.thingsboard.server.common.msg.core.RpcSubscribeMsg;
import org.thingsboard.server.common.msg.core.SessionCloseMsg;
-import org.thingsboard.server.common.msg.device.ToDeviceActorMsg;
-import org.thingsboard.server.common.msg.session.*;
-
-import akka.actor.ActorContext;
-import akka.event.LoggingAdapter;
-import org.thingsboard.server.common.msg.session.ctrl.*;
+import org.thingsboard.server.common.msg.core.SessionOpenMsg;
+import org.thingsboard.server.common.msg.device.DeviceToDeviceActorMsg;
+import org.thingsboard.server.common.msg.session.BasicSessionActorToAdaptorMsg;
+import org.thingsboard.server.common.msg.session.FromDeviceMsg;
+import org.thingsboard.server.common.msg.session.FromDeviceRequestMsg;
+import org.thingsboard.server.common.msg.session.SessionMsgType;
+import org.thingsboard.server.common.msg.session.SessionType;
+import org.thingsboard.server.common.msg.session.ToDeviceMsg;
+import org.thingsboard.server.common.msg.session.TransportToDeviceSessionActorMsg;
import org.thingsboard.server.common.msg.session.ex.SessionException;
import java.util.HashMap;
@@ -37,7 +44,7 @@ import java.util.Optional;
class ASyncMsgProcessor extends AbstractSessionActorMsgProcessor {
private boolean firstMsg = true;
- private Map pendingMap = new HashMap<>();
+ private Map pendingMap = new HashMap<>();
private Optional currentTargetServer;
private boolean subscribedToAttributeUpdates;
private boolean subscribedToRpcCommands;
@@ -47,14 +54,16 @@ class ASyncMsgProcessor extends AbstractSessionActorMsgProcessor {
}
@Override
- protected void processToDeviceActorMsg(ActorContext ctx, ToDeviceActorSessionMsg msg) {
+ protected void processToDeviceActorMsg(ActorContext ctx, TransportToDeviceSessionActorMsg msg) {
updateSessionCtx(msg, SessionType.ASYNC);
+ DeviceToDeviceActorMsg pendingMsg = toDeviceMsg(msg);
+ FromDeviceMsg fromDeviceMsg = pendingMsg.getPayload();
if (firstMsg) {
- toDeviceMsg(new SessionOpenMsg()).ifPresent(m -> forwardToAppActor(ctx, m));
+ if (fromDeviceMsg.getMsgType() != SessionMsgType.SESSION_OPEN) {
+ toDeviceMsg(new SessionOpenMsg()).ifPresent(m -> forwardToAppActor(ctx, m));
+ }
firstMsg = false;
}
- ToDeviceActorMsg pendingMsg = toDeviceMsg(msg);
- FromDeviceMsg fromDeviceMsg = pendingMsg.getPayload();
switch (fromDeviceMsg.getMsgType()) {
case POST_TELEMETRY_REQUEST:
case POST_ATTRIBUTES_REQUEST:
@@ -86,8 +95,8 @@ class ASyncMsgProcessor extends AbstractSessionActorMsgProcessor {
@Override
public void processToDeviceMsg(ActorContext context, ToDeviceMsg msg) {
try {
- if (msg.getMsgType() != MsgType.SESSION_CLOSE) {
- switch (msg.getMsgType()) {
+ if (msg.getSessionMsgType() != SessionMsgType.SESSION_CLOSE) {
+ switch (msg.getSessionMsgType()) {
case STATUS_CODE_RESPONSE:
case GET_ATTRIBUTES_RESPONSE:
ResponseMsg responseMsg = (ResponseMsg) msg;
diff --git a/application/src/main/java/org/thingsboard/server/actors/session/AbstractSessionActorMsgProcessor.java b/application/src/main/java/org/thingsboard/server/actors/session/AbstractSessionActorMsgProcessor.java
index 96526dda88..469cda9c5f 100644
--- a/application/src/main/java/org/thingsboard/server/actors/session/AbstractSessionActorMsgProcessor.java
+++ b/application/src/main/java/org/thingsboard/server/actors/session/AbstractSessionActorMsgProcessor.java
@@ -15,36 +15,42 @@
*/
package org.thingsboard.server.actors.session;
+import akka.actor.ActorContext;
+import akka.actor.ActorRef;
+import akka.event.LoggingAdapter;
import org.thingsboard.server.actors.ActorSystemContext;
import org.thingsboard.server.actors.shared.AbstractContextAwareMsgProcessor;
import org.thingsboard.server.actors.shared.SessionTimeoutMsg;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.SessionId;
import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;
+import org.thingsboard.server.common.msg.cluster.SendToClusterMsg;
import org.thingsboard.server.common.msg.cluster.ServerAddress;
-import org.thingsboard.server.common.msg.device.BasicToDeviceActorMsg;
-import org.thingsboard.server.common.msg.device.ToDeviceActorMsg;
-import org.thingsboard.server.common.msg.session.*;
+import org.thingsboard.server.common.msg.device.BasicDeviceToDeviceActorMsg;
+import org.thingsboard.server.common.msg.device.DeviceToDeviceActorMsg;
+import org.thingsboard.server.common.msg.session.AdaptorToSessionActorMsg;
+import org.thingsboard.server.common.msg.session.FromDeviceMsg;
+import org.thingsboard.server.common.msg.session.SessionContext;
+import org.thingsboard.server.common.msg.session.SessionCtrlMsg;
+import org.thingsboard.server.common.msg.session.SessionType;
+import org.thingsboard.server.common.msg.session.ToDeviceMsg;
+import org.thingsboard.server.common.msg.session.TransportToDeviceSessionActorMsg;
import org.thingsboard.server.common.msg.session.ctrl.SessionCloseMsg;
-import akka.actor.ActorContext;
-import akka.actor.ActorRef;
-import akka.event.LoggingAdapter;
-
import java.util.Optional;
abstract class AbstractSessionActorMsgProcessor extends AbstractContextAwareMsgProcessor {
protected final SessionId sessionId;
protected SessionContext sessionCtx;
- protected ToDeviceActorMsg toDeviceActorMsgPrototype;
+ protected DeviceToDeviceActorMsg deviceToDeviceActorMsgPrototype;
protected AbstractSessionActorMsgProcessor(ActorSystemContext ctx, LoggingAdapter logger, SessionId sessionId) {
super(ctx, logger);
this.sessionId = sessionId;
}
- protected abstract void processToDeviceActorMsg(ActorContext ctx, ToDeviceActorSessionMsg msg);
+ protected abstract void processToDeviceActorMsg(ActorContext ctx, TransportToDeviceSessionActorMsg msg);
protected abstract void processTimeoutMsg(ActorContext context, SessionTimeoutMsg msg);
@@ -62,47 +68,44 @@ abstract class AbstractSessionActorMsgProcessor extends AbstractContextAwareMsgP
protected void cleanupSession(ActorContext ctx) {
}
- protected void updateSessionCtx(ToDeviceActorSessionMsg msg, SessionType type) {
+ protected void updateSessionCtx(TransportToDeviceSessionActorMsg msg, SessionType type) {
sessionCtx = msg.getSessionMsg().getSessionContext();
- toDeviceActorMsgPrototype = new BasicToDeviceActorMsg(msg, type);
+ deviceToDeviceActorMsgPrototype = new BasicDeviceToDeviceActorMsg(msg, type);
}
- protected ToDeviceActorMsg toDeviceMsg(ToDeviceActorSessionMsg msg) {
+ protected DeviceToDeviceActorMsg toDeviceMsg(TransportToDeviceSessionActorMsg msg) {
AdaptorToSessionActorMsg adaptorMsg = msg.getSessionMsg();
- return new BasicToDeviceActorMsg(toDeviceActorMsgPrototype, adaptorMsg.getMsg());
+ return new BasicDeviceToDeviceActorMsg(deviceToDeviceActorMsgPrototype, adaptorMsg.getMsg());
}
- protected Optional toDeviceMsg(FromDeviceMsg msg) {
- if (toDeviceActorMsgPrototype != null) {
- return Optional.of(new BasicToDeviceActorMsg(toDeviceActorMsgPrototype, msg));
+ protected Optional toDeviceMsg(FromDeviceMsg msg) {
+ if (deviceToDeviceActorMsgPrototype != null) {
+ return Optional.of(new BasicDeviceToDeviceActorMsg(deviceToDeviceActorMsgPrototype, msg));
} else {
return Optional.empty();
}
}
- protected Optional forwardToAppActor(ActorContext ctx, ToDeviceActorMsg toForward) {
+ protected Optional forwardToAppActor(ActorContext ctx, DeviceToDeviceActorMsg toForward) {
Optional address = systemContext.getRoutingService().resolveById(toForward.getDeviceId());
forwardToAppActor(ctx, toForward, address);
return address;
}
- protected Optional forwardToAppActorIfAdressChanged(ActorContext ctx, ToDeviceActorMsg toForward, Optional oldAddress) {
+ protected Optional forwardToAppActorIfAddressChanged(ActorContext ctx, DeviceToDeviceActorMsg toForward, Optional oldAddress) {
+
Optional newAddress = systemContext.getRoutingService().resolveById(toForward.getDeviceId());
if (!newAddress.equals(oldAddress)) {
- if (newAddress.isPresent()) {
- systemContext.getRpcService().tell(newAddress.get(),
- toForward.toOtherAddress(systemContext.getRoutingService().getCurrentServer()));
- } else {
- getAppActor().tell(toForward, ctx.self());
- }
+ getAppActor().tell(new SendToClusterMsg(toForward.getDeviceId(), toForward
+ .toOtherAddress(systemContext.getRoutingService().getCurrentServer())), ctx.self());
}
return newAddress;
}
- protected void forwardToAppActor(ActorContext ctx, ToDeviceActorMsg toForward, Optional address) {
+ protected void forwardToAppActor(ActorContext ctx, DeviceToDeviceActorMsg toForward, Optional address) {
if (address.isPresent()) {
- systemContext.getRpcService().tell(address.get(),
- toForward.toOtherAddress(systemContext.getRoutingService().getCurrentServer()));
+ systemContext.getRpcService().tell(systemContext.getEncodingService().convertToProtoDataMessage(address.get(),
+ toForward.toOtherAddress(systemContext.getRoutingService().getCurrentServer())));
} else {
getAppActor().tell(toForward, ctx.self());
}
@@ -114,6 +117,6 @@ abstract class AbstractSessionActorMsgProcessor extends AbstractContextAwareMsgP
}
public DeviceId getDeviceId() {
- return toDeviceActorMsgPrototype.getDeviceId();
+ return deviceToDeviceActorMsgPrototype.getDeviceId();
}
}
diff --git a/application/src/main/java/org/thingsboard/server/actors/session/SessionActor.java b/application/src/main/java/org/thingsboard/server/actors/session/SessionActor.java
index 37827d61fb..05926c1825 100644
--- a/application/src/main/java/org/thingsboard/server/actors/session/SessionActor.java
+++ b/application/src/main/java/org/thingsboard/server/actors/session/SessionActor.java
@@ -17,22 +17,21 @@ package org.thingsboard.server.actors.session;
import akka.actor.OneForOneStrategy;
import akka.actor.SupervisorStrategy;
-import akka.japi.Function;
+import akka.event.Logging;
+import akka.event.LoggingAdapter;
import org.thingsboard.server.actors.ActorSystemContext;
import org.thingsboard.server.actors.service.ContextAwareActor;
import org.thingsboard.server.actors.service.ContextBasedCreator;
import org.thingsboard.server.actors.shared.SessionTimeoutMsg;
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.core.ToDeviceSessionActorMsg;
-import org.thingsboard.server.common.msg.session.ToDeviceActorSessionMsg;
+import org.thingsboard.server.common.msg.core.ActorSystemToDeviceSessionActorMsg;
import org.thingsboard.server.common.msg.session.SessionCtrlMsg;
import org.thingsboard.server.common.msg.session.SessionMsg;
import org.thingsboard.server.common.msg.session.SessionType;
+import org.thingsboard.server.common.msg.session.TransportToDeviceSessionActorMsg;
import org.thingsboard.server.common.msg.session.ctrl.SessionCloseMsg;
-
-import akka.event.Logging;
-import akka.event.LoggingAdapter;
import scala.concurrent.duration.Duration;
public class SessionActor extends ContextAwareActor {
@@ -61,33 +60,38 @@ public class SessionActor extends ContextAwareActor {
}
@Override
- public void onReceive(Object msg) throws Exception {
- logger.debug("[{}] Processing: {}.", sessionId, msg);
- if (msg instanceof ToDeviceActorSessionMsg) {
- processDeviceMsg((ToDeviceActorSessionMsg) msg);
- } else if (msg instanceof ToDeviceSessionActorMsg) {
- processToDeviceMsg((ToDeviceSessionActorMsg) msg);
- } else if (msg instanceof SessionTimeoutMsg) {
- processTimeoutMsg((SessionTimeoutMsg) msg);
- } else if (msg instanceof SessionCtrlMsg) {
- processSessionCtrlMsg((SessionCtrlMsg) msg);
- } else if (msg instanceof ClusterEventMsg) {
- processClusterEvent((ClusterEventMsg) msg);
- } else {
- logger.warning("[{}] Unknown msg: {}", sessionId, msg);
+ protected boolean process(TbActorMsg msg) {
+ switch (msg.getMsgType()) {
+ case TRANSPORT_TO_DEVICE_SESSION_ACTOR_MSG:
+ processTransportToSessionMsg((TransportToDeviceSessionActorMsg) msg);
+ break;
+ case ACTOR_SYSTEM_TO_DEVICE_SESSION_ACTOR_MSG:
+ processActorsToSessionMsg((ActorSystemToDeviceSessionActorMsg) msg);
+ break;
+ case SESSION_TIMEOUT_MSG:
+ processTimeoutMsg((SessionTimeoutMsg) msg);
+ break;
+ case SESSION_CTRL_MSG:
+ processSessionCloseMsg((SessionCtrlMsg) msg);
+ break;
+ case CLUSTER_EVENT_MSG:
+ processClusterEvent((ClusterEventMsg) msg);
+ break;
+ default: return false;
}
+ return true;
}
private void processClusterEvent(ClusterEventMsg msg) {
processor.processClusterEvent(context(), msg);
}
- private void processDeviceMsg(ToDeviceActorSessionMsg msg) {
+ private void processTransportToSessionMsg(TransportToDeviceSessionActorMsg msg) {
initProcessor(msg);
processor.processToDeviceActorMsg(context(), msg);
}
- private void processToDeviceMsg(ToDeviceSessionActorMsg msg) {
+ private void processActorsToSessionMsg(ActorSystemToDeviceSessionActorMsg msg) {
processor.processToDeviceMsg(context(), msg.getMsg());
}
@@ -99,7 +103,7 @@ public class SessionActor extends ContextAwareActor {
}
}
- private void processSessionCtrlMsg(SessionCtrlMsg msg) {
+ private void processSessionCloseMsg(SessionCtrlMsg msg) {
if (processor != null) {
processor.processSessionCtrlMsg(context(), msg);
} else if (msg instanceof SessionCloseMsg) {
@@ -109,7 +113,7 @@ public class SessionActor extends ContextAwareActor {
}
}
- private void initProcessor(ToDeviceActorSessionMsg msg) {
+ private void initProcessor(TransportToDeviceSessionActorMsg msg) {
if (processor == null) {
SessionMsg sessionMsg = (SessionMsg) msg.getSessionMsg();
if (sessionMsg.getSessionContext().getSessionType() == SessionType.SYNC) {
diff --git a/application/src/main/java/org/thingsboard/server/actors/session/SessionManagerActor.java b/application/src/main/java/org/thingsboard/server/actors/session/SessionManagerActor.java
index 9d67dabeb8..624a9f49c7 100644
--- a/application/src/main/java/org/thingsboard/server/actors/session/SessionManagerActor.java
+++ b/application/src/main/java/org/thingsboard/server/actors/session/SessionManagerActor.java
@@ -15,26 +15,29 @@
*/
package org.thingsboard.server.actors.session;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.UUID;
-
-import akka.actor.*;
+import akka.actor.ActorRef;
+import akka.actor.InvalidActorNameException;
+import akka.actor.LocalActorRef;
+import akka.actor.Props;
+import akka.actor.Terminated;
+import akka.event.Logging;
+import akka.event.LoggingAdapter;
import org.thingsboard.server.actors.ActorSystemContext;
import org.thingsboard.server.actors.service.ContextAwareActor;
import org.thingsboard.server.actors.service.ContextBasedCreator;
import org.thingsboard.server.actors.service.DefaultActorService;
import org.thingsboard.server.actors.shared.SessionTimeoutMsg;
import org.thingsboard.server.common.data.id.SessionId;
+import org.thingsboard.server.common.msg.TbActorMsg;
import org.thingsboard.server.common.msg.aware.SessionAwareMsg;
-
-import akka.event.Logging;
-import akka.event.LoggingAdapter;
import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;
+import org.thingsboard.server.common.msg.core.ActorSystemToDeviceSessionActorMsg;
import org.thingsboard.server.common.msg.core.SessionCloseMsg;
-import org.thingsboard.server.common.msg.core.ToDeviceSessionActorMsg;
import org.thingsboard.server.common.msg.session.SessionCtrlMsg;
+import java.util.HashMap;
+import java.util.Map;
+
public class SessionManagerActor extends ContextAwareActor {
private static final int INITIAL_SESSION_MAP_SIZE = 1024;
@@ -48,6 +51,12 @@ public class SessionManagerActor extends ContextAwareActor {
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
public void onReceive(Object msg) throws Exception {
if (msg instanceof SessionCtrlMsg) {
@@ -97,7 +106,7 @@ public class SessionManagerActor extends ContextAwareActor {
}
private void forwardToSessionActor(SessionAwareMsg msg) {
- if (msg instanceof ToDeviceSessionActorMsg || msg instanceof SessionCloseMsg) {
+ if (msg instanceof ActorSystemToDeviceSessionActorMsg || msg instanceof SessionCloseMsg) {
String sessionIdStr = msg.getSessionId().toUidStr();
ActorRef sessionActor = sessionActors.get(sessionIdStr);
if (sessionActor != null) {
diff --git a/application/src/main/java/org/thingsboard/server/actors/session/SyncMsgProcessor.java b/application/src/main/java/org/thingsboard/server/actors/session/SyncMsgProcessor.java
index d69650347b..cf8df1385f 100644
--- a/application/src/main/java/org/thingsboard/server/actors/session/SyncMsgProcessor.java
+++ b/application/src/main/java/org/thingsboard/server/actors/session/SyncMsgProcessor.java
@@ -15,24 +15,26 @@
*/
package org.thingsboard.server.actors.session;
+import akka.actor.ActorContext;
+import akka.event.LoggingAdapter;
import org.thingsboard.server.actors.ActorSystemContext;
import org.thingsboard.server.actors.shared.SessionTimeoutMsg;
import org.thingsboard.server.common.data.id.SessionId;
import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;
import org.thingsboard.server.common.msg.cluster.ServerAddress;
-import org.thingsboard.server.common.msg.device.ToDeviceActorMsg;
-import org.thingsboard.server.common.msg.session.*;
-import org.thingsboard.server.common.msg.session.ToDeviceActorSessionMsg;
+import org.thingsboard.server.common.msg.device.DeviceToDeviceActorMsg;
+import org.thingsboard.server.common.msg.session.BasicSessionActorToAdaptorMsg;
+import org.thingsboard.server.common.msg.session.SessionContext;
+import org.thingsboard.server.common.msg.session.SessionType;
+import org.thingsboard.server.common.msg.session.ToDeviceMsg;
+import org.thingsboard.server.common.msg.session.TransportToDeviceSessionActorMsg;
import org.thingsboard.server.common.msg.session.ctrl.SessionCloseMsg;
import org.thingsboard.server.common.msg.session.ex.SessionException;
-import akka.actor.ActorContext;
-import akka.event.LoggingAdapter;
-
import java.util.Optional;
class SyncMsgProcessor extends AbstractSessionActorMsgProcessor {
- private ToDeviceActorMsg pendingMsg;
+ private DeviceToDeviceActorMsg pendingMsg;
private Optional currentTargetServer;
private boolean pendingResponse;
@@ -41,7 +43,7 @@ class SyncMsgProcessor extends AbstractSessionActorMsgProcessor {
}
@Override
- protected void processToDeviceActorMsg(ActorContext ctx, ToDeviceActorSessionMsg msg) {
+ protected void processToDeviceActorMsg(ActorContext ctx, TransportToDeviceSessionActorMsg msg) {
updateSessionCtx(msg, SessionType.SYNC);
pendingMsg = toDeviceMsg(msg);
pendingResponse = true;
@@ -73,7 +75,7 @@ class SyncMsgProcessor extends AbstractSessionActorMsgProcessor {
@Override
public void processClusterEvent(ActorContext context, ClusterEventMsg msg) {
if (pendingResponse) {
- Optional newTargetServer = forwardToAppActorIfAdressChanged(context, pendingMsg, currentTargetServer);
+ Optional newTargetServer = forwardToAppActorIfAddressChanged(context, pendingMsg, currentTargetServer);
if (logger.isDebugEnabled()) {
if (!newTargetServer.equals(currentTargetServer)) {
if (newTargetServer.isPresent()) {
diff --git a/application/src/main/java/org/thingsboard/server/actors/shared/AbstractContextAwareMsgProcessor.java b/application/src/main/java/org/thingsboard/server/actors/shared/AbstractContextAwareMsgProcessor.java
index 73b221ffc5..8864486150 100644
--- a/application/src/main/java/org/thingsboard/server/actors/shared/AbstractContextAwareMsgProcessor.java
+++ b/application/src/main/java/org/thingsboard/server/actors/shared/AbstractContextAwareMsgProcessor.java
@@ -19,18 +19,13 @@ import akka.actor.ActorContext;
import akka.actor.ActorRef;
import akka.actor.Scheduler;
import akka.event.LoggingAdapter;
-import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.thingsboard.server.actors.ActorSystemContext;
-import org.thingsboard.server.common.data.plugin.ComponentDescriptor;
-import org.thingsboard.server.common.data.plugin.ComponentType;
-import org.thingsboard.server.extensions.api.component.*;
import scala.concurrent.ExecutionContextExecutor;
import scala.concurrent.duration.Duration;
-import java.io.IOException;
import java.util.concurrent.TimeUnit;
public abstract class AbstractContextAwareMsgProcessor {
@@ -76,53 +71,6 @@ public abstract class AbstractContextAwareMsgProcessor {
getScheduler().scheduleOnce(Duration.create(delayInMs, TimeUnit.MILLISECONDS), target, msg, getSystemDispatcher(), null);
}
- protected T initComponent(JsonNode componentNode) throws Exception {
- ComponentConfiguration configuration = new ComponentConfiguration(
- componentNode.get("clazz").asText(),
- componentNode.get("name").asText(),
- mapper.writeValueAsString(componentNode.get("configuration"))
- );
- logger.info("Initializing [{}][{}] component", configuration.getName(), configuration.getClazz());
- ComponentDescriptor componentDescriptor = systemContext.getComponentService().getComponent(configuration.getClazz())
- .orElseThrow(() -> new InstantiationException("Component Not found!"));
- return initComponent(componentDescriptor, configuration);
- }
-
- protected T initComponent(ComponentDescriptor componentDefinition, ComponentConfiguration configuration)
- throws Exception {
- return initComponent(componentDefinition.getClazz(), componentDefinition.getType(), configuration.getConfiguration());
- }
-
- protected T initComponent(String clazz, ComponentType type, String configuration)
- throws Exception {
- Class> componentClazz = Class.forName(clazz);
- T component = (T) (componentClazz.newInstance());
- Class> configurationClazz;
- switch (type) {
- case FILTER:
- configurationClazz = ((Filter) componentClazz.getAnnotation(Filter.class)).configuration();
- break;
- case PROCESSOR:
- configurationClazz = ((Processor) componentClazz.getAnnotation(Processor.class)).configuration();
- break;
- case ACTION:
- configurationClazz = ((Action) componentClazz.getAnnotation(Action.class)).configuration();
- break;
- case PLUGIN:
- configurationClazz = ((Plugin) componentClazz.getAnnotation(Plugin.class)).configuration();
- break;
- default:
- throw new IllegalStateException("Component with type: " + type + " is not supported!");
- }
- component.init(decode(configuration, configurationClazz));
- return component;
- }
-
- public C decode(String configuration, Class configurationClazz) throws IOException, RuntimeException {
- logger.info("Initializing using configuration: {}", configuration);
- return mapper.readValue(configuration, configurationClazz);
- }
-
@Data
@AllArgsConstructor
private static class ComponentConfiguration {
diff --git a/application/src/main/java/org/thingsboard/server/actors/shared/ComponentMsgProcessor.java b/application/src/main/java/org/thingsboard/server/actors/shared/ComponentMsgProcessor.java
index 18d32d9476..1cf83397fc 100644
--- a/application/src/main/java/org/thingsboard/server/actors/shared/ComponentMsgProcessor.java
+++ b/application/src/main/java/org/thingsboard/server/actors/shared/ComponentMsgProcessor.java
@@ -17,39 +17,87 @@ package org.thingsboard.server.actors.shared;
import akka.actor.ActorContext;
import akka.event.LoggingAdapter;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
import org.thingsboard.server.actors.ActorSystemContext;
import org.thingsboard.server.actors.stats.StatsPersistTick;
+import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
+import org.thingsboard.server.common.data.plugin.ComponentLifecycleState;
+import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;
+import org.thingsboard.server.service.queue.MsgQueueService;
-public abstract class ComponentMsgProcessor extends AbstractContextAwareMsgProcessor {
+import javax.annotation.Nullable;
+import java.util.function.Consumer;
+
+public abstract class ComponentMsgProcessor extends AbstractContextAwareMsgProcessor {
protected final TenantId tenantId;
protected final T entityId;
+ protected final MsgQueueService queue;
+ protected ComponentLifecycleState state;
protected ComponentMsgProcessor(ActorSystemContext systemContext, LoggingAdapter logger, TenantId tenantId, T id) {
super(systemContext, logger);
this.tenantId = tenantId;
this.entityId = id;
+ this.queue = systemContext.getMsgQueueService();
}
- public abstract void start() throws Exception;
+ public abstract void start(ActorContext context) throws Exception;
- public abstract void stop() throws Exception;
+ public abstract void stop(ActorContext context) throws Exception;
- public abstract void onCreated(ActorContext context) throws Exception;
+ public abstract void onClusterEventMsg(ClusterEventMsg msg) throws Exception;
- public abstract void onUpdate(ActorContext context) throws Exception;
+ public void onCreated(ActorContext context) throws Exception {
+ start(context);
+ }
- public abstract void onActivate(ActorContext context) throws Exception;
+ public void onUpdate(ActorContext context) throws Exception {
+ restart(context);
+ }
- public abstract void onSuspend(ActorContext context) throws Exception;
+ public void onActivate(ActorContext context) throws Exception {
+ restart(context);
+ }
- public abstract void onStop(ActorContext context) throws Exception;
+ public void onSuspend(ActorContext context) throws Exception {
+ stop(context);
+ }
- public abstract void onClusterEventMsg(ClusterEventMsg msg) throws Exception;
+ public void onStop(ActorContext context) throws Exception {
+ stop(context);
+ }
+
+ private void restart(ActorContext context) throws Exception {
+ stop(context);
+ start(context);
+ }
public void scheduleStatsPersistTick(ActorContext context, long statsPersistFrequency) {
schedulePeriodicMsgWithDelay(context, new StatsPersistTick(), statsPersistFrequency, statsPersistFrequency);
}
+
+ protected void checkActive() {
+ if (state != ComponentLifecycleState.ACTIVE) {
+ throw new IllegalStateException("Rule chain is not active!");
+ }
+ }
+
+ protected void putToQueue(final TbMsg tbMsg, final Consumer onSuccess) {
+ EntityId entityId = tbMsg.getRuleNodeId() != null ? tbMsg.getRuleNodeId() : tbMsg.getRuleChainId();
+ Futures.addCallback(queue.put(this.tenantId, tbMsg, entityId.getId(), tbMsg.getClusterPartition()), new FutureCallback() {
+ @Override
+ public void onSuccess(@Nullable Void result) {
+ onSuccess.accept(tbMsg);
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ logger.debug("Failed to push message [{}] to queue due to [{}]", tbMsg, t);
+ }
+ });
+ }
}
diff --git a/application/src/main/java/org/thingsboard/server/actors/shared/EntityActorsManager.java b/application/src/main/java/org/thingsboard/server/actors/shared/EntityActorsManager.java
new file mode 100644
index 0000000000..1b9e6a8074
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/actors/shared/EntityActorsManager.java
@@ -0,0 +1,84 @@
+/**
+ * Copyright © 2016-2018 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.actors.shared;
+
+import akka.actor.ActorContext;
+import akka.actor.ActorRef;
+import akka.actor.Props;
+import akka.actor.UntypedActor;
+import akka.japi.Creator;
+import lombok.extern.slf4j.Slf4j;
+import org.thingsboard.server.actors.ActorSystemContext;
+import org.thingsboard.server.actors.service.ContextAwareActor;
+import org.thingsboard.server.common.data.SearchTextBased;
+import org.thingsboard.server.common.data.id.EntityId;
+import org.thingsboard.server.common.data.id.TenantId;
+import org.thingsboard.server.common.data.id.UUIDBased;
+import org.thingsboard.server.common.data.page.PageDataIterable;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Created by ashvayka on 15.03.18.
+ */
+@Slf4j
+public abstract class EntityActorsManager> {
+
+ protected final ActorSystemContext systemContext;
+ protected final Map actors;
+
+ public EntityActorsManager(ActorSystemContext systemContext) {
+ this.systemContext = systemContext;
+ this.actors = new HashMap<>();
+ }
+
+ protected abstract TenantId getTenantId();
+
+ protected abstract String getDispatcherName();
+
+ protected abstract Creator creator(T entityId);
+
+ protected abstract PageDataIterable.FetchFunction getFetchEntitiesFunction();
+
+ public void init(ActorContext context) {
+ for (M entity : new PageDataIterable<>(getFetchEntitiesFunction(), ContextAwareActor.ENTITY_PACK_LIMIT)) {
+ T entityId = (T) entity.getId();
+ log.debug("[{}|{}] Creating entity actor", entityId.getEntityType(), entityId.getId());
+ //TODO: remove this cast making UUIDBased subclass of EntityId an interface and vice versa.
+ ActorRef actorRef = getOrCreateActor(context, entityId);
+ visit(entity, actorRef);
+ log.debug("[{}|{}] Entity actor created.", entityId.getEntityType(), entityId.getId());
+ }
+ }
+
+ public void visit(M entity, ActorRef actorRef) {}
+
+ public ActorRef getOrCreateActor(ActorContext context, T entityId) {
+ return actors.computeIfAbsent(entityId, eId ->
+ context.actorOf(Props.create(creator(eId))
+ .withDispatcher(getDispatcherName()), eId.toString()));
+ }
+
+ public void broadcast(Object msg) {
+ actors.values().forEach(actorRef -> actorRef.tell(msg, ActorRef.noSender()));
+ }
+
+ public void remove(T id) {
+ actors.remove(id);
+ }
+
+}
diff --git a/application/src/main/java/org/thingsboard/server/actors/shared/SessionTimeoutMsg.java b/application/src/main/java/org/thingsboard/server/actors/shared/SessionTimeoutMsg.java
index 7d6dbca6cd..d015fe01de 100644
--- a/application/src/main/java/org/thingsboard/server/actors/shared/SessionTimeoutMsg.java
+++ b/application/src/main/java/org/thingsboard/server/actors/shared/SessionTimeoutMsg.java
@@ -17,13 +17,20 @@ package org.thingsboard.server.actors.shared;
import lombok.Data;
import org.thingsboard.server.common.data.id.SessionId;
+import org.thingsboard.server.common.msg.MsgType;
+import org.thingsboard.server.common.msg.TbActorMsg;
import java.io.Serializable;
@Data
-public class SessionTimeoutMsg implements Serializable {
+public class SessionTimeoutMsg implements Serializable, TbActorMsg {
private static final long serialVersionUID = 1L;
private final SessionId sessionId;
+
+ @Override
+ public MsgType getMsgType() {
+ return MsgType.SESSION_TIMEOUT_MSG;
+ }
}
diff --git a/application/src/main/java/org/thingsboard/server/actors/shared/plugin/PluginManager.java b/application/src/main/java/org/thingsboard/server/actors/shared/plugin/PluginManager.java
deleted file mode 100644
index 4f5871f585..0000000000
--- a/application/src/main/java/org/thingsboard/server/actors/shared/plugin/PluginManager.java
+++ /dev/null
@@ -1,77 +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.plugin;
-
-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.plugin.PluginActor;
-import org.thingsboard.server.actors.service.ContextAwareActor;
-import org.thingsboard.server.common.data.id.PluginId;
-import org.thingsboard.server.common.data.id.TenantId;
-import org.thingsboard.server.common.data.page.PageDataIterable;
-import org.thingsboard.server.common.data.page.PageDataIterable.FetchFunction;
-import org.thingsboard.server.common.data.plugin.PluginMetaData;
-import org.thingsboard.server.dao.plugin.PluginService;
-
-import java.util.HashMap;
-import java.util.Map;
-
-@Slf4j
-public abstract class PluginManager {
-
- protected final ActorSystemContext systemContext;
- protected final PluginService pluginService;
- protected final Map pluginActors;
-
- public PluginManager(ActorSystemContext systemContext) {
- this.systemContext = systemContext;
- this.pluginService = systemContext.getPluginService();
- this.pluginActors = new HashMap<>();
- }
-
- public void init(ActorContext context) {
- PageDataIterable pluginIterator = new PageDataIterable<>(getFetchPluginsFunction(),
- ContextAwareActor.ENTITY_PACK_LIMIT);
- for (PluginMetaData plugin : pluginIterator) {
- log.debug("[{}] Creating plugin actor", plugin.getId());
- getOrCreatePluginActor(context, plugin.getId());
- log.debug("Plugin actor created.");
- }
- }
-
- abstract FetchFunction getFetchPluginsFunction();
-
- abstract TenantId getTenantId();
-
- abstract String getDispatcherName();
-
- public ActorRef getOrCreatePluginActor(ActorContext context, PluginId pluginId) {
- return pluginActors.computeIfAbsent(pluginId, pId ->
- context.actorOf(Props.create(new PluginActor.ActorCreator(systemContext, getTenantId(), pId))
- .withDispatcher(getDispatcherName()), pId.toString()));
- }
-
- public void broadcast(Object msg) {
- pluginActors.values().forEach(actorRef -> actorRef.tell(msg, ActorRef.noSender()));
- }
-
- public void remove(PluginId id) {
- pluginActors.remove(id);
- }
-}
diff --git a/application/src/main/java/org/thingsboard/server/actors/shared/plugin/SystemPluginManager.java b/application/src/main/java/org/thingsboard/server/actors/shared/plugin/SystemPluginManager.java
deleted file mode 100644
index 0888e23f25..0000000000
--- a/application/src/main/java/org/thingsboard/server/actors/shared/plugin/SystemPluginManager.java
+++ /dev/null
@@ -1,45 +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.plugin;
-
-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.plugin.PluginMetaData;
-import org.thingsboard.server.dao.plugin.BasePluginService;
-
-public class SystemPluginManager extends PluginManager {
-
- public SystemPluginManager(ActorSystemContext systemContext) {
- super(systemContext);
- }
-
- @Override
- FetchFunction getFetchPluginsFunction() {
- return pluginService::findSystemPlugins;
- }
-
- @Override
- TenantId getTenantId() {
- return BasePluginService.SYSTEM_TENANT;
- }
-
- @Override
- protected String getDispatcherName() {
- return DefaultActorService.SYSTEM_PLUGIN_DISPATCHER_NAME;
- }
-}
diff --git a/application/src/main/java/org/thingsboard/server/actors/shared/rule/RuleManager.java b/application/src/main/java/org/thingsboard/server/actors/shared/rule/RuleManager.java
deleted file mode 100644
index 95d762afdf..0000000000
--- a/application/src/main/java/org/thingsboard/server/actors/shared/rule/RuleManager.java
+++ /dev/null
@@ -1,135 +0,0 @@
-/**
- * Copyright © 2016-2018 The Thingsboard Authors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.thingsboard.server.actors.shared.rule;
-
-import akka.actor.ActorContext;
-import akka.actor.ActorRef;
-import akka.actor.Props;
-import lombok.extern.slf4j.Slf4j;
-import org.thingsboard.server.actors.ActorSystemContext;
-import org.thingsboard.server.actors.rule.RuleActor;
-import org.thingsboard.server.actors.rule.RuleActorChain;
-import org.thingsboard.server.actors.rule.RuleActorMetaData;
-import org.thingsboard.server.actors.rule.SimpleRuleActorChain;
-import org.thingsboard.server.actors.service.ContextAwareActor;
-import org.thingsboard.server.common.data.id.RuleId;
-import org.thingsboard.server.common.data.id.TenantId;
-import org.thingsboard.server.common.data.page.PageDataIterable;
-import org.thingsboard.server.common.data.page.PageDataIterable.FetchFunction;
-import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
-import org.thingsboard.server.common.data.plugin.ComponentLifecycleState;
-import org.thingsboard.server.common.data.rule.RuleMetaData;
-import org.thingsboard.server.dao.rule.RuleService;
-
-import java.util.*;
-
-@Slf4j
-public abstract class RuleManager {
-
- protected final ActorSystemContext systemContext;
- protected final RuleService ruleService;
- protected final Map ruleActors;
- protected final TenantId tenantId;
-
- private Map ruleMap;
- private RuleActorChain ruleChain;
-
- public RuleManager(ActorSystemContext systemContext, TenantId tenantId) {
- this.systemContext = systemContext;
- this.ruleService = systemContext.getRuleService();
- this.ruleActors = new HashMap<>();
- this.tenantId = tenantId;
- }
-
- public void init(ActorContext context) {
- doInit(context);
- }
-
- private void doInit(ActorContext context) {
- PageDataIterable ruleIterator = new PageDataIterable<>(getFetchRulesFunction(),
- ContextAwareActor.ENTITY_PACK_LIMIT);
- ruleMap = new HashMap<>();
-
- for (RuleMetaData rule : ruleIterator) {
- log.debug("[{}] Creating rule actor {}", rule.getId(), rule);
- ActorRef ref = getOrCreateRuleActor(context, rule.getId());
- ruleMap.put(rule, RuleActorMetaData.systemRule(rule.getId(), rule.getWeight(), ref));
- log.debug("[{}] Rule actor created.", rule.getId());
- }
-
- refreshRuleChain();
- }
-
- public Optional update(ActorContext context, RuleId ruleId, ComponentLifecycleEvent event) {
- if (ruleMap == null) {
- doInit(context);
- }
- RuleMetaData rule;
- if (event != ComponentLifecycleEvent.DELETED) {
- rule = systemContext.getRuleService().findRuleById(ruleId);
- } else {
- rule = ruleMap.keySet().stream()
- .filter(r -> r.getId().equals(ruleId))
- .peek(r -> r.setState(ComponentLifecycleState.SUSPENDED))
- .findFirst()
- .orElse(null);
- if (rule != null) {
- ruleMap.remove(rule);
- ruleActors.remove(ruleId);
- }
- }
- if (rule != null) {
- RuleActorMetaData actorMd = ruleMap.get(rule);
- if (actorMd == null) {
- ActorRef ref = getOrCreateRuleActor(context, rule.getId());
- actorMd = RuleActorMetaData.systemRule(rule.getId(), rule.getWeight(), ref);
- ruleMap.put(rule, actorMd);
- }
- refreshRuleChain();
- return Optional.of(actorMd.getActorRef());
- } else {
- log.warn("[{}] Can't process unknown rule!", ruleId);
- return Optional.empty();
- }
- }
-
- abstract FetchFunction getFetchRulesFunction();
-
- abstract String getDispatcherName();
-
- public ActorRef getOrCreateRuleActor(ActorContext context, RuleId ruleId) {
- return ruleActors.computeIfAbsent(ruleId, rId ->
- context.actorOf(Props.create(new RuleActor.ActorCreator(systemContext, tenantId, rId))
- .withDispatcher(getDispatcherName()), rId.toString()));
- }
-
- public RuleActorChain getRuleChain(ActorContext context) {
- if (ruleChain == null) {
- doInit(context);
- }
- return ruleChain;
- }
-
- private void refreshRuleChain() {
- Set activeRuleSet = new HashSet<>();
- for (Map.Entry rule : ruleMap.entrySet()) {
- if (rule.getKey().getState() == ComponentLifecycleState.ACTIVE) {
- activeRuleSet.add(rule.getValue());
- }
- }
- ruleChain = new SimpleRuleActorChain(activeRuleSet);
- }
-}
diff --git a/application/src/main/java/org/thingsboard/server/actors/shared/rule/TenantRuleManager.java b/application/src/main/java/org/thingsboard/server/actors/shared/rule/TenantRuleManager.java
deleted file mode 100644
index e4d023ca90..0000000000
--- a/application/src/main/java/org/thingsboard/server/actors/shared/rule/TenantRuleManager.java
+++ /dev/null
@@ -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 getFetchRulesFunction() {
- return link -> ruleService.findTenantRules(tenantId, link);
- }
-
- @Override
- String getDispatcherName() {
- return DefaultActorService.TENANT_RULE_DISPATCHER_NAME;
- }
-
-}
diff --git a/application/src/main/java/org/thingsboard/server/actors/shared/rulechain/RuleChainManager.java b/application/src/main/java/org/thingsboard/server/actors/shared/rulechain/RuleChainManager.java
new file mode 100644
index 0000000000..11ed5a3363
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/actors/shared/rulechain/RuleChainManager.java
@@ -0,0 +1,59 @@
+/**
+ * Copyright © 2016-2018 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.actors.shared.rulechain;
+
+import akka.actor.ActorRef;
+import akka.japi.Creator;
+import lombok.Getter;
+import lombok.extern.slf4j.Slf4j;
+import org.thingsboard.server.actors.ActorSystemContext;
+import org.thingsboard.server.actors.ruleChain.RuleChainActor;
+import org.thingsboard.server.actors.shared.EntityActorsManager;
+import org.thingsboard.server.common.data.id.RuleChainId;
+import org.thingsboard.server.common.data.rule.RuleChain;
+import org.thingsboard.server.dao.rule.RuleChainService;
+
+/**
+ * Created by ashvayka on 15.03.18.
+ */
+@Slf4j
+public abstract class RuleChainManager extends EntityActorsManager {
+
+ protected final RuleChainService service;
+ @Getter
+ protected RuleChain rootChain;
+ @Getter
+ protected ActorRef rootChainActor;
+
+ public RuleChainManager(ActorSystemContext systemContext) {
+ super(systemContext);
+ this.service = systemContext.getRuleChainService();
+ }
+
+ @Override
+ public Creator creator(RuleChainId entityId) {
+ return new RuleChainActor.ActorCreator(systemContext, getTenantId(), entityId);
+ }
+
+ @Override
+ public void visit(RuleChain entity, ActorRef actorRef) {
+ if (entity.isRoot()) {
+ rootChain = entity;
+ rootChainActor = actorRef;
+ }
+ }
+
+}
diff --git a/application/src/main/java/org/thingsboard/server/actors/shared/rule/SystemRuleManager.java b/application/src/main/java/org/thingsboard/server/actors/shared/rulechain/SystemRuleChainManager.java
similarity index 61%
rename from application/src/main/java/org/thingsboard/server/actors/shared/rule/SystemRuleManager.java
rename to application/src/main/java/org/thingsboard/server/actors/shared/rulechain/SystemRuleChainManager.java
index d10731caed..ce1747fc6c 100644
--- a/application/src/main/java/org/thingsboard/server/actors/shared/rule/SystemRuleManager.java
+++ b/application/src/main/java/org/thingsboard/server/actors/shared/rulechain/SystemRuleChainManager.java
@@ -13,28 +13,36 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.thingsboard.server.actors.shared.rule;
+package org.thingsboard.server.actors.shared.rulechain;
import org.thingsboard.server.actors.ActorSystemContext;
import org.thingsboard.server.actors.service.DefaultActorService;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageDataIterable.FetchFunction;
-import org.thingsboard.server.common.data.rule.RuleMetaData;
+import org.thingsboard.server.common.data.page.TextPageData;
+import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.dao.model.ModelConstants;
-public class SystemRuleManager extends RuleManager {
+import java.util.Collections;
- public SystemRuleManager(ActorSystemContext systemContext) {
- super(systemContext, new TenantId(ModelConstants.NULL_UUID));
+public class SystemRuleChainManager extends RuleChainManager {
+
+ public SystemRuleChainManager(ActorSystemContext systemContext) {
+ super(systemContext);
+ }
+
+ @Override
+ protected FetchFunction getFetchEntitiesFunction() {
+ return link -> new TextPageData<>(Collections.emptyList(), link);
}
@Override
- FetchFunction getFetchRulesFunction() {
- return ruleService::findSystemRules;
+ protected TenantId getTenantId() {
+ return ModelConstants.SYSTEM_TENANT;
}
@Override
- String getDispatcherName() {
+ protected String getDispatcherName() {
return DefaultActorService.SYSTEM_RULE_DISPATCHER_NAME;
}
}
diff --git a/application/src/main/java/org/thingsboard/server/actors/shared/plugin/TenantPluginManager.java b/application/src/main/java/org/thingsboard/server/actors/shared/rulechain/TenantRuleChainManager.java
similarity index 72%
rename from application/src/main/java/org/thingsboard/server/actors/shared/plugin/TenantPluginManager.java
rename to application/src/main/java/org/thingsboard/server/actors/shared/rulechain/TenantRuleChainManager.java
index 14ea2aa7f8..731d8d8e6c 100644
--- a/application/src/main/java/org/thingsboard/server/actors/shared/plugin/TenantPluginManager.java
+++ b/application/src/main/java/org/thingsboard/server/actors/shared/rulechain/TenantRuleChainManager.java
@@ -13,20 +13,20 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.thingsboard.server.actors.shared.plugin;
+package org.thingsboard.server.actors.shared.rulechain;
import akka.actor.ActorContext;
import org.thingsboard.server.actors.ActorSystemContext;
import org.thingsboard.server.actors.service.DefaultActorService;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageDataIterable.FetchFunction;
-import org.thingsboard.server.common.data.plugin.PluginMetaData;
+import org.thingsboard.server.common.data.rule.RuleChain;
-public class TenantPluginManager extends PluginManager {
+public class TenantRuleChainManager extends RuleChainManager {
private final TenantId tenantId;
- public TenantPluginManager(ActorSystemContext systemContext, TenantId tenantId) {
+ public TenantRuleChainManager(ActorSystemContext systemContext, TenantId tenantId) {
super(systemContext);
this.tenantId = tenantId;
}
@@ -39,18 +39,17 @@ public class TenantPluginManager extends PluginManager {
}
@Override
- FetchFunction getFetchPluginsFunction() {
- return link -> pluginService.findTenantPlugins(tenantId, link);
- }
-
- @Override
- TenantId getTenantId() {
+ protected TenantId getTenantId() {
return tenantId;
}
@Override
protected String getDispatcherName() {
- return DefaultActorService.TENANT_PLUGIN_DISPATCHER_NAME;
+ return DefaultActorService.TENANT_RULE_DISPATCHER_NAME;
}
+ @Override
+ protected FetchFunction getFetchEntitiesFunction() {
+ return link -> service.findTenantRuleChains(tenantId, link);
+ }
}
diff --git a/application/src/main/java/org/thingsboard/server/actors/stats/StatsActor.java b/application/src/main/java/org/thingsboard/server/actors/stats/StatsActor.java
index ccc31cca29..8623370896 100644
--- a/application/src/main/java/org/thingsboard/server/actors/stats/StatsActor.java
+++ b/application/src/main/java/org/thingsboard/server/actors/stats/StatsActor.java
@@ -24,6 +24,7 @@ import org.thingsboard.server.actors.service.ContextAwareActor;
import org.thingsboard.server.actors.service.ContextBasedCreator;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.Event;
+import org.thingsboard.server.common.msg.TbActorMsg;
import org.thingsboard.server.common.msg.cluster.ServerAddress;
public class StatsActor extends ContextAwareActor {
@@ -35,6 +36,12 @@ public class StatsActor extends ContextAwareActor {
super(context);
}
+ @Override
+ protected boolean process(TbActorMsg msg) {
+ //TODO Move everything here, to work with TbActorMsg\
+ return false;
+ }
+
@Override
public void onReceive(Object msg) throws Exception {
logger.debug("Received message: {}", msg);
diff --git a/application/src/main/java/org/thingsboard/server/actors/tenant/RuleChainDeviceMsg.java b/application/src/main/java/org/thingsboard/server/actors/tenant/RuleChainDeviceMsg.java
deleted file mode 100644
index a84e0b5fd4..0000000000
--- a/application/src/main/java/org/thingsboard/server/actors/tenant/RuleChainDeviceMsg.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/**
- * Copyright © 2016-2018 The Thingsboard Authors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.thingsboard.server.actors.tenant;
-
-import org.thingsboard.server.actors.rule.RuleActorChain;
-import org.thingsboard.server.common.msg.device.ToDeviceActorMsg;
-
-public class RuleChainDeviceMsg {
-
- private final ToDeviceActorMsg toDeviceActorMsg;
- private final RuleActorChain ruleChain;
-
- public RuleChainDeviceMsg(ToDeviceActorMsg toDeviceActorMsg, RuleActorChain ruleChain) {
- super();
- this.toDeviceActorMsg = toDeviceActorMsg;
- this.ruleChain = ruleChain;
- }
-
- public ToDeviceActorMsg getToDeviceActorMsg() {
- return toDeviceActorMsg;
- }
-
- public RuleActorChain getRuleChain() {
- return ruleChain;
- }
-
-}
diff --git a/application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java b/application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java
index b923fe15a3..7a3127dd36 100644
--- a/application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java
+++ b/application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java
@@ -15,61 +15,55 @@
*/
package org.thingsboard.server.actors.tenant;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Optional;
-
+import akka.actor.ActorRef;
+import akka.actor.OneForOneStrategy;
+import akka.actor.Props;
+import akka.actor.SupervisorStrategy;
+import akka.japi.Function;
import org.thingsboard.server.actors.ActorSystemContext;
import org.thingsboard.server.actors.device.DeviceActor;
-import org.thingsboard.server.actors.plugin.PluginTerminationMsg;
-import org.thingsboard.server.actors.rule.ComplexRuleActorChain;
-import org.thingsboard.server.actors.rule.RuleActorChain;
-import org.thingsboard.server.actors.service.ContextAwareActor;
+import org.thingsboard.server.actors.device.DeviceActorToRuleEngineMsg;
+import org.thingsboard.server.actors.ruleChain.RuleChainManagerActor;
+import org.thingsboard.server.actors.ruleChain.RuleChainToRuleChainMsg;
import org.thingsboard.server.actors.service.ContextBasedCreator;
import org.thingsboard.server.actors.service.DefaultActorService;
-import org.thingsboard.server.actors.shared.plugin.PluginManager;
-import org.thingsboard.server.actors.shared.plugin.TenantPluginManager;
-import org.thingsboard.server.actors.shared.rule.RuleManager;
-import org.thingsboard.server.actors.shared.rule.TenantRuleManager;
+import org.thingsboard.server.actors.shared.rulechain.TenantRuleChainManager;
+import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.id.DeviceId;
-import org.thingsboard.server.common.data.id.PluginId;
-import org.thingsboard.server.common.data.id.RuleId;
+import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.TenantId;
-import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;
-import org.thingsboard.server.common.msg.device.ToDeviceActorMsg;
-
-import akka.actor.ActorRef;
-import akka.actor.Props;
-import akka.event.Logging;
-import akka.event.LoggingAdapter;
+import org.thingsboard.server.common.data.rule.RuleChain;
+import org.thingsboard.server.common.msg.TbActorMsg;
+import org.thingsboard.server.common.msg.aware.DeviceAwareMsg;
import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
-import org.thingsboard.server.extensions.api.device.ToDeviceActorNotificationMsg;
-import org.thingsboard.server.extensions.api.plugins.msg.ToPluginActorMsg;
-import org.thingsboard.server.extensions.api.rules.ToRuleActorMsg;
+import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg;
+import scala.concurrent.duration.Duration;
-public class TenantActor extends ContextAwareActor {
+import java.util.HashMap;
+import java.util.Map;
- private final LoggingAdapter logger = Logging.getLogger(getContext().system(), this);
+public class TenantActor extends RuleChainManagerActor {
private final TenantId tenantId;
- private final RuleManager ruleManager;
- private final PluginManager pluginManager;
private final Map deviceActors;
private TenantActor(ActorSystemContext systemContext, TenantId tenantId) {
- super(systemContext);
+ super(systemContext, new TenantRuleChainManager(systemContext, tenantId));
this.tenantId = tenantId;
- this.ruleManager = new TenantRuleManager(systemContext, tenantId);
- this.pluginManager = new TenantPluginManager(systemContext, tenantId);
this.deviceActors = new HashMap<>();
}
+
+ @Override
+ public SupervisorStrategy supervisorStrategy() {
+ return strategy;
+ }
+
@Override
public void preStart() {
logger.info("[{}] Starting tenant actor.", tenantId);
try {
- ruleManager.init(this.context());
- pluginManager.init(this.context());
+ initRuleChains();
logger.info("[{}] Tenant actor started.", tenantId);
} catch (Exception e) {
logger.error(e, "[{}] Unknown failure", tenantId);
@@ -77,89 +71,74 @@ public class TenantActor extends ContextAwareActor {
}
@Override
- public void onReceive(Object msg) throws Exception {
- logger.debug("[{}] Received message: {}", tenantId, msg);
- if (msg instanceof RuleChainDeviceMsg) {
- process((RuleChainDeviceMsg) msg);
- } else if (msg instanceof ToDeviceActorMsg) {
- onToDeviceActorMsg((ToDeviceActorMsg) msg);
- } else if (msg instanceof ToPluginActorMsg) {
- onToPluginMsg((ToPluginActorMsg) msg);
- } else if (msg instanceof ToRuleActorMsg) {
- onToRuleMsg((ToRuleActorMsg) msg);
- } else if (msg instanceof ToDeviceActorNotificationMsg) {
- onToDeviceActorMsg((ToDeviceActorNotificationMsg) msg);
- } else if (msg instanceof ClusterEventMsg) {
- broadcast(msg);
- } else if (msg instanceof ComponentLifecycleMsg) {
- onComponentLifecycleMsg((ComponentLifecycleMsg) msg);
- } else if (msg instanceof PluginTerminationMsg) {
- onPluginTerminated((PluginTerminationMsg) msg);
- } else {
- logger.warning("[{}] Unknown message: {}!", tenantId, msg);
+ protected boolean process(TbActorMsg msg) {
+ switch (msg.getMsgType()) {
+ case CLUSTER_EVENT_MSG:
+ broadcast(msg);
+ break;
+ case COMPONENT_LIFE_CYCLE_MSG:
+ onComponentLifecycleMsg((ComponentLifecycleMsg) msg);
+ break;
+ case SERVICE_TO_RULE_ENGINE_MSG:
+ onServiceToRuleEngineMsg((ServiceToRuleEngineMsg) msg);
+ break;
+ case DEVICE_ACTOR_TO_RULE_ENGINE_MSG:
+ onDeviceActorToRuleEngineMsg((DeviceActorToRuleEngineMsg) msg);
+ break;
+ case DEVICE_SESSION_TO_DEVICE_ACTOR_MSG:
+ case DEVICE_ATTRIBUTES_UPDATE_TO_DEVICE_ACTOR_MSG:
+ case DEVICE_CREDENTIALS_UPDATE_TO_DEVICE_ACTOR_MSG:
+ case DEVICE_NAME_OR_TYPE_UPDATE_TO_DEVICE_ACTOR_MSG:
+ case DEVICE_RPC_REQUEST_TO_DEVICE_ACTOR_MSG:
+ case SERVER_RPC_RESPONSE_TO_DEVICE_ACTOR_MSG:
+ onToDeviceActorMsg((DeviceAwareMsg) msg);
+ break;
+ case RULE_CHAIN_TO_RULE_CHAIN_MSG:
+ onRuleChainMsg((RuleChainToRuleChainMsg) msg);
+ break;
+ default:
+ return false;
}
+ return true;
}
- private void broadcast(Object msg) {
- pluginManager.broadcast(msg);
+ @Override
+ protected void broadcast(Object msg) {
+ super.broadcast(msg);
deviceActors.values().forEach(actorRef -> actorRef.tell(msg, ActorRef.noSender()));
}
- private void onToDeviceActorMsg(ToDeviceActorMsg msg) {
- getOrCreateDeviceActor(msg.getDeviceId()).tell(msg, ActorRef.noSender());
+ private void onServiceToRuleEngineMsg(ServiceToRuleEngineMsg msg) {
+ ruleChainManager.getRootChainActor().tell(msg, self());
}
- private void onToDeviceActorMsg(ToDeviceActorNotificationMsg msg) {
- getOrCreateDeviceActor(msg.getDeviceId()).tell(msg, ActorRef.noSender());
+ private void onDeviceActorToRuleEngineMsg(DeviceActorToRuleEngineMsg msg) {
+ ruleChainManager.getRootChainActor().tell(msg, self());
}
- private void onToRuleMsg(ToRuleActorMsg msg) {
- ActorRef target = ruleManager.getOrCreateRuleActor(this.context(), msg.getRuleId());
- target.tell(msg, ActorRef.noSender());
+ private void onRuleChainMsg(RuleChainToRuleChainMsg msg) {
+ ruleChainManager.getOrCreateActor(context(), msg.getTarget()).tell(msg, self());
}
- private void onToPluginMsg(ToPluginActorMsg msg) {
- if (msg.getPluginTenantId().equals(tenantId)) {
- ActorRef pluginActor = pluginManager.getOrCreatePluginActor(this.context(), msg.getPluginId());
- pluginActor.tell(msg, ActorRef.noSender());
- } else {
- context().parent().tell(msg, ActorRef.noSender());
- }
+
+ private void onToDeviceActorMsg(DeviceAwareMsg msg) {
+ getOrCreateDeviceActor(msg.getDeviceId()).tell(msg, ActorRef.noSender());
}
private void onComponentLifecycleMsg(ComponentLifecycleMsg msg) {
- Optional pluginId = msg.getPluginId();
- Optional ruleId = msg.getRuleId();
- if (pluginId.isPresent()) {
- ActorRef pluginActor = pluginManager.getOrCreatePluginActor(this.context(), pluginId.get());
- pluginActor.tell(msg, ActorRef.noSender());
- } else if (ruleId.isPresent()) {
- ActorRef target;
- Optional ref = ruleManager.update(this.context(), ruleId.get(), msg.getEvent());
- if (ref.isPresent()) {
- target = ref.get();
- } else {
- logger.debug("Failed to find actor for rule: [{}]", ruleId);
- return;
+ ActorRef target = getEntityActorRef(msg.getEntityId());
+ if (target != null) {
+ if (msg.getEntityId().getEntityType() == EntityType.RULE_CHAIN) {
+ RuleChain ruleChain = systemContext.getRuleChainService().
+ findRuleChainById(new RuleChainId(msg.getEntityId().getId()));
+ ruleChainManager.visit(ruleChain, target);
}
target.tell(msg, ActorRef.noSender());
} else {
- logger.debug("[{}] Invalid component lifecycle msg.", tenantId);
+ logger.debug("Invalid component lifecycle msg: {}", msg);
}
}
- private void onPluginTerminated(PluginTerminationMsg msg) {
- pluginManager.remove(msg.getId());
- }
-
- private void process(RuleChainDeviceMsg msg) {
- ToDeviceActorMsg toDeviceActorMsg = msg.getToDeviceActorMsg();
- ActorRef deviceActor = getOrCreateDeviceActor(toDeviceActorMsg.getDeviceId());
- RuleActorChain tenantChain = ruleManager.getRuleChain(this.context());
- RuleActorChain chain = new ComplexRuleActorChain(msg.getRuleChain(), tenantChain);
- deviceActor.tell(new RuleChainDeviceMsg(toDeviceActorMsg, chain), context().self());
- }
-
private ActorRef getOrCreateDeviceActor(DeviceId deviceId) {
return deviceActors.computeIfAbsent(deviceId, k -> context().actorOf(Props.create(new DeviceActor.ActorCreator(systemContext, tenantId, deviceId))
.withDispatcher(DefaultActorService.CORE_DISPATCHER_NAME), deviceId.toString()));
@@ -181,4 +160,12 @@ public class TenantActor extends ContextAwareActor {
}
}
+ private final SupervisorStrategy strategy = new OneForOneStrategy(3, Duration.create("1 minute"), new Function() {
+ @Override
+ public SupervisorStrategy.Directive apply(Throwable t) {
+ logger.error(t, "Unknown failure");
+ return SupervisorStrategy.resume();
+ }
+ });
+
}
diff --git a/application/src/main/java/org/thingsboard/server/config/AuditLogLevelProperties.java b/application/src/main/java/org/thingsboard/server/config/AuditLogLevelProperties.java
index 34afd35ba2..4c36a15758 100644
--- a/application/src/main/java/org/thingsboard/server/config/AuditLogLevelProperties.java
+++ b/application/src/main/java/org/thingsboard/server/config/AuditLogLevelProperties.java
@@ -17,8 +17,6 @@ package org.thingsboard.server.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
-import org.thingsboard.server.common.data.EntityType;
-import org.thingsboard.server.common.data.audit.ActionType;
import java.util.HashMap;
import java.util.Map;
diff --git a/application/src/main/java/org/thingsboard/server/config/SwaggerConfiguration.java b/application/src/main/java/org/thingsboard/server/config/SwaggerConfiguration.java
index 87b187e98c..2254cf3da0 100644
--- a/application/src/main/java/org/thingsboard/server/config/SwaggerConfiguration.java
+++ b/application/src/main/java/org/thingsboard/server/config/SwaggerConfiguration.java
@@ -24,7 +24,11 @@ import org.springframework.context.annotation.Configuration;
import org.thingsboard.server.common.data.security.Authority;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.schema.AlternateTypeRule;
-import springfox.documentation.service.*;
+import springfox.documentation.service.ApiInfo;
+import springfox.documentation.service.ApiKey;
+import springfox.documentation.service.AuthorizationScope;
+import springfox.documentation.service.Contact;
+import springfox.documentation.service.SecurityReference;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;
diff --git a/application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java b/application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java
index 2952529812..6afa6b2460 100644
--- a/application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java
+++ b/application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java
@@ -20,7 +20,6 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
-import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
@@ -37,15 +36,18 @@ import org.springframework.security.web.authentication.AuthenticationFailureHand
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
-import org.springframework.web.cors.CorsUtils;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
import org.thingsboard.server.dao.audit.AuditLogLevelFilter;
import org.thingsboard.server.exception.ThingsboardErrorResponseHandler;
+import org.thingsboard.server.service.security.auth.jwt.JwtAuthenticationProvider;
+import org.thingsboard.server.service.security.auth.jwt.JwtTokenAuthenticationProcessingFilter;
+import org.thingsboard.server.service.security.auth.jwt.RefreshTokenAuthenticationProvider;
+import org.thingsboard.server.service.security.auth.jwt.RefreshTokenProcessingFilter;
+import org.thingsboard.server.service.security.auth.jwt.SkipPathRequestMatcher;
+import org.thingsboard.server.service.security.auth.jwt.extractor.TokenExtractor;
import org.thingsboard.server.service.security.auth.rest.RestAuthenticationProvider;
import org.thingsboard.server.service.security.auth.rest.RestLoginProcessingFilter;
-import org.thingsboard.server.service.security.auth.jwt.*;
-import org.thingsboard.server.service.security.auth.jwt.extractor.TokenExtractor;
import org.thingsboard.server.service.security.auth.rest.RestPublicLoginProcessingFilter;
import java.util.ArrayList;
diff --git a/application/src/main/java/org/thingsboard/server/config/WebSocketConfiguration.java b/application/src/main/java/org/thingsboard/server/config/WebSocketConfiguration.java
index a75ecb1353..59b7da2e76 100644
--- a/application/src/main/java/org/thingsboard/server/config/WebSocketConfiguration.java
+++ b/application/src/main/java/org/thingsboard/server/config/WebSocketConfiguration.java
@@ -15,12 +15,6 @@
*/
package org.thingsboard.server.config;
-import java.util.Map;
-
-import org.thingsboard.server.exception.ThingsboardErrorCode;
-import org.thingsboard.server.exception.ThingsboardException;
-import org.thingsboard.server.controller.plugin.PluginWebSocketHandler;
-import org.thingsboard.server.service.security.model.SecurityUser;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
@@ -35,6 +29,12 @@ import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry
import org.springframework.web.socket.server.HandshakeInterceptor;
import org.springframework.web.socket.server.standard.ServletServerContainerFactoryBean;
import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor;
+import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
+import org.thingsboard.server.common.data.exception.ThingsboardException;
+import org.thingsboard.server.controller.plugin.TbWebSocketHandler;
+import org.thingsboard.server.service.security.model.SecurityUser;
+
+import java.util.Map;
@Configuration
@EnableWebSocket
@@ -54,7 +54,7 @@ public class WebSocketConfiguration implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
- registry.addHandler(pluginWsHandler(), WS_PLUGIN_MAPPING).setAllowedOrigins("*")
+ registry.addHandler(wsHandler(), WS_PLUGIN_MAPPING).setAllowedOrigins("*")
.addInterceptors(new HttpSessionHandshakeInterceptor(), new HandshakeInterceptor() {
@Override
@@ -82,8 +82,8 @@ public class WebSocketConfiguration implements WebSocketConfigurer {
}
@Bean
- public WebSocketHandler pluginWsHandler() {
- return new PluginWebSocketHandler();
+ public WebSocketHandler wsHandler() {
+ return new TbWebSocketHandler();
}
protected SecurityUser getCurrentUser() throws ThingsboardException {
diff --git a/application/src/main/java/org/thingsboard/server/controller/AdminController.java b/application/src/main/java/org/thingsboard/server/controller/AdminController.java
index e9a6ba3816..0cf149114a 100644
--- a/application/src/main/java/org/thingsboard/server/controller/AdminController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/AdminController.java
@@ -17,11 +17,16 @@ package org.thingsboard.server.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
-import org.springframework.web.bind.annotation.*;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.RestController;
+import org.thingsboard.rule.engine.api.MailService;
import org.thingsboard.server.common.data.AdminSettings;
+import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.dao.settings.AdminSettingsService;
-import org.thingsboard.server.exception.ThingsboardException;
-import org.thingsboard.server.service.mail.MailService;
import org.thingsboard.server.service.update.UpdateService;
import org.thingsboard.server.service.update.model.UpdateMessage;
diff --git a/application/src/main/java/org/thingsboard/server/controller/AlarmController.java b/application/src/main/java/org/thingsboard/server/controller/AlarmController.java
index 1959f4e528..28cc2fdf09 100644
--- a/application/src/main/java/org/thingsboard/server/controller/AlarmController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/AlarmController.java
@@ -18,13 +18,27 @@ package org.thingsboard.server.controller;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize;
-import org.springframework.web.bind.annotation.*;
-import org.thingsboard.server.common.data.alarm.*;
-import org.thingsboard.server.common.data.id.*;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestController;
+import org.thingsboard.server.common.data.alarm.Alarm;
+import org.thingsboard.server.common.data.alarm.AlarmId;
+import org.thingsboard.server.common.data.alarm.AlarmInfo;
+import org.thingsboard.server.common.data.alarm.AlarmQuery;
+import org.thingsboard.server.common.data.alarm.AlarmSearchStatus;
+import org.thingsboard.server.common.data.alarm.AlarmSeverity;
+import org.thingsboard.server.common.data.alarm.AlarmStatus;
+import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
+import org.thingsboard.server.common.data.exception.ThingsboardException;
+import org.thingsboard.server.common.data.id.EntityId;
+import org.thingsboard.server.common.data.id.EntityIdFactory;
import org.thingsboard.server.common.data.page.TimePageData;
import org.thingsboard.server.common.data.page.TimePageLink;
-import org.thingsboard.server.exception.ThingsboardErrorCode;
-import org.thingsboard.server.exception.ThingsboardException;
@RestController
@RequestMapping("/api")
@@ -93,7 +107,7 @@ public class AlarmController extends BaseController {
try {
AlarmId alarmId = new AlarmId(toUUID(strAlarmId));
checkAlarmId(alarmId);
- alarmService.clearAlarm(alarmId, System.currentTimeMillis()).get();
+ alarmService.clearAlarm(alarmId, null, System.currentTimeMillis()).get();
} catch (Exception e) {
throw handleException(e);
}
diff --git a/application/src/main/java/org/thingsboard/server/controller/AssetController.java b/application/src/main/java/org/thingsboard/server/controller/AssetController.java
index 9b439133d3..35865ab2df 100644
--- a/application/src/main/java/org/thingsboard/server/controller/AssetController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/AssetController.java
@@ -18,23 +18,30 @@ package org.thingsboard.server.controller;
import com.google.common.util.concurrent.ListenableFuture;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize;
-import org.springframework.web.bind.annotation.*;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestController;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.EntitySubtype;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.asset.Asset;
+import org.thingsboard.server.common.data.asset.AssetSearchQuery;
import org.thingsboard.server.common.data.audit.ActionType;
+import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
+import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.AssetId;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
-import org.thingsboard.server.common.data.asset.AssetSearchQuery;
import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.dao.exception.IncorrectParameterException;
import org.thingsboard.server.dao.model.ModelConstants;
-import org.thingsboard.server.exception.ThingsboardErrorCode;
-import org.thingsboard.server.exception.ThingsboardException;
import org.thingsboard.server.service.security.model.SecurityUser;
import java.util.ArrayList;
diff --git a/application/src/main/java/org/thingsboard/server/controller/AuditLogController.java b/application/src/main/java/org/thingsboard/server/controller/AuditLogController.java
index 75bcf2ab15..34bcf8456f 100644
--- a/application/src/main/java/org/thingsboard/server/controller/AuditLogController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/AuditLogController.java
@@ -16,15 +16,20 @@
package org.thingsboard.server.controller;
import org.springframework.security.access.prepost.PreAuthorize;
-import org.springframework.web.bind.annotation.*;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.RestController;
import org.thingsboard.server.common.data.audit.AuditLog;
+import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EntityIdFactory;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.page.TimePageData;
import org.thingsboard.server.common.data.page.TimePageLink;
-import org.thingsboard.server.exception.ThingsboardException;
import java.util.UUID;
diff --git a/application/src/main/java/org/thingsboard/server/controller/AuthController.java b/application/src/main/java/org/thingsboard/server/controller/AuthController.java
index ef38d802ec..af24c9c24b 100644
--- a/application/src/main/java/org/thingsboard/server/controller/AuthController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/AuthController.java
@@ -25,12 +25,18 @@ import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
-import org.springframework.web.bind.annotation.*;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestController;
+import org.thingsboard.rule.engine.api.MailService;
import org.thingsboard.server.common.data.User;
+import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
+import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.security.UserCredentials;
-import org.thingsboard.server.exception.ThingsboardErrorCode;
-import org.thingsboard.server.exception.ThingsboardException;
-import org.thingsboard.server.service.mail.MailService;
import org.thingsboard.server.service.security.auth.jwt.RefreshTokenRepository;
import org.thingsboard.server.service.security.model.SecurityUser;
import org.thingsboard.server.service.security.model.UserPrincipal;
diff --git a/application/src/main/java/org/thingsboard/server/controller/BaseController.java b/application/src/main/java/org/thingsboard/server/controller/BaseController.java
index 3264af40b7..f04422898c 100644
--- a/application/src/main/java/org/thingsboard/server/controller/BaseController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/BaseController.java
@@ -15,12 +15,13 @@
*/
package org.thingsboard.server.controller;
-import com.fasterxml.jackson.databind.JsonNode;
+import com.datastax.driver.core.utils.UUIDs;
import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.ExceptionHandler;
@@ -30,18 +31,24 @@ import org.thingsboard.server.common.data.alarm.Alarm;
import org.thingsboard.server.common.data.alarm.AlarmId;
import org.thingsboard.server.common.data.alarm.AlarmInfo;
import org.thingsboard.server.common.data.asset.Asset;
-import org.thingsboard.server.common.data.audit.ActionStatus;
import org.thingsboard.server.common.data.audit.ActionType;
+import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
+import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.*;
+import org.thingsboard.server.common.data.kv.AttributeKvEntry;
+import org.thingsboard.server.common.data.kv.DataType;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.common.data.plugin.ComponentDescriptor;
import org.thingsboard.server.common.data.plugin.ComponentType;
-import org.thingsboard.server.common.data.plugin.PluginMetaData;
-import org.thingsboard.server.common.data.rule.RuleMetaData;
+import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.common.data.widget.WidgetType;
import org.thingsboard.server.common.data.widget.WidgetsBundle;
+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.system.ServiceToRuleEngineMsg;
import org.thingsboard.server.dao.alarm.AlarmService;
import org.thingsboard.server.dao.asset.AssetService;
import org.thingsboard.server.dao.audit.AuditLogService;
@@ -52,23 +59,23 @@ import org.thingsboard.server.dao.device.DeviceService;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.exception.IncorrectParameterException;
import org.thingsboard.server.dao.model.ModelConstants;
-import org.thingsboard.server.dao.plugin.PluginService;
import org.thingsboard.server.dao.relation.RelationService;
-import org.thingsboard.server.dao.rule.RuleService;
+import org.thingsboard.server.dao.rule.RuleChainService;
+import org.thingsboard.server.dao.tenant.TenantService;
import org.thingsboard.server.dao.user.UserService;
import org.thingsboard.server.dao.widget.WidgetTypeService;
import org.thingsboard.server.dao.widget.WidgetsBundleService;
-import org.thingsboard.server.exception.ThingsboardErrorCode;
import org.thingsboard.server.exception.ThingsboardErrorResponseHandler;
-import org.thingsboard.server.exception.ThingsboardException;
import org.thingsboard.server.service.component.ComponentDiscoveryService;
import org.thingsboard.server.service.security.model.SecurityUser;
+import org.thingsboard.server.service.state.DeviceStateService;
import javax.mail.MessagingException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
import java.util.Optional;
+import java.util.Set;
import java.util.UUID;
import static org.thingsboard.server.dao.service.Validator.validateId;
@@ -79,9 +86,14 @@ public abstract class BaseController {
public static final String INCORRECT_TENANT_ID = "Incorrect tenantId ";
public static final String YOU_DON_T_HAVE_PERMISSION_TO_PERFORM_THIS_OPERATION = "You don't have permission to perform this operation!";
+ private static final ObjectMapper json = new ObjectMapper();
+
@Autowired
private ThingsboardErrorResponseHandler errorResponseHandler;
+ @Autowired
+ protected TenantService tenantService;
+
@Autowired
protected CustomerService customerService;
@@ -113,10 +125,7 @@ public abstract class BaseController {
protected ComponentDiscoveryService componentDescriptorService;
@Autowired
- protected RuleService ruleService;
-
- @Autowired
- protected PluginService pluginService;
+ protected RuleChainService ruleChainService;
@Autowired
protected ActorService actorService;
@@ -127,6 +136,9 @@ public abstract class BaseController {
@Autowired
protected AuditLogService auditLogService;
+ @Autowired
+ protected DeviceStateService deviceStateService;
+
@ExceptionHandler(ThingsboardException.class)
public void handleThingsboardException(ThingsboardException ex, HttpServletResponse response) {
errorResponseHandler.handle(ex, response);
@@ -289,11 +301,8 @@ public abstract class BaseController {
case TENANT:
checkTenantId(new TenantId(entityId.getId()));
return;
- case PLUGIN:
- checkPlugin(new PluginId(entityId.getId()));
- return;
- case RULE:
- checkRule(new RuleId(entityId.getId()));
+ case RULE_CHAIN:
+ checkRuleChain(new RuleChainId(entityId.getId()));
return;
case ASSET:
checkAsset(assetService.findAssetById(new AssetId(entityId.getId())));
@@ -472,60 +481,34 @@ public abstract class BaseController {
}
}
- List checkPluginActionsByPluginClazz(String pluginClazz) throws ThingsboardException {
+ List checkComponentDescriptorsByTypes(Set types) throws ThingsboardException {
try {
- checkComponentDescriptorByClazz(pluginClazz);
- log.debug("[{}] Lookup plugin actions", pluginClazz);
- return componentDescriptorService.getPluginActions(pluginClazz);
+ log.debug("[{}] Lookup component descriptors", types);
+ return componentDescriptorService.getComponents(types);
} catch (Exception e) {
throw handleException(e, false);
}
}
- protected PluginMetaData checkPlugin(PluginMetaData plugin) throws ThingsboardException {
- checkNotNull(plugin);
- SecurityUser authUser = getCurrentUser();
- TenantId tenantId = plugin.getTenantId();
- validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
- if (authUser.getAuthority() != Authority.SYS_ADMIN) {
- if (authUser.getTenantId() == null ||
- !tenantId.getId().equals(ModelConstants.NULL_UUID) && !authUser.getTenantId().equals(tenantId)) {
- throw new ThingsboardException(YOU_DON_T_HAVE_PERMISSION_TO_PERFORM_THIS_OPERATION,
- ThingsboardErrorCode.PERMISSION_DENIED);
-
- } else if (tenantId.getId().equals(ModelConstants.NULL_UUID)) {
- plugin.setConfiguration(null);
- }
- }
- return plugin;
- }
-
- protected PluginMetaData checkPlugin(PluginId pluginId) throws ThingsboardException {
- checkNotNull(pluginId);
- return checkPlugin(pluginService.findPluginById(pluginId));
- }
-
- protected RuleMetaData checkRule(RuleId ruleId) throws ThingsboardException {
- checkNotNull(ruleId);
- return checkRule(ruleService.findRuleById(ruleId));
+ protected RuleChain checkRuleChain(RuleChainId ruleChainId) throws ThingsboardException {
+ checkNotNull(ruleChainId);
+ return checkRuleChain(ruleChainService.findRuleChainById(ruleChainId));
}
- protected RuleMetaData checkRule(RuleMetaData rule) throws ThingsboardException {
- checkNotNull(rule);
+ protected RuleChain checkRuleChain(RuleChain ruleChain) throws ThingsboardException {
+ checkNotNull(ruleChain);
SecurityUser authUser = getCurrentUser();
- TenantId tenantId = rule.getTenantId();
+ TenantId tenantId = ruleChain.getTenantId();
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
- if (authUser.getAuthority() != Authority.SYS_ADMIN) {
- if (authUser.getTenantId() == null ||
- !tenantId.getId().equals(ModelConstants.NULL_UUID) && !authUser.getTenantId().equals(tenantId)) {
- throw new ThingsboardException(YOU_DON_T_HAVE_PERMISSION_TO_PERFORM_THIS_OPERATION,
- ThingsboardErrorCode.PERMISSION_DENIED);
-
- }
+ if (authUser.getAuthority() != Authority.TENANT_ADMIN ||
+ !authUser.getTenantId().equals(tenantId)) {
+ throw new ThingsboardException(YOU_DON_T_HAVE_PERMISSION_TO_PERFORM_THIS_OPERATION,
+ ThingsboardErrorCode.PERMISSION_DENIED);
}
- return rule;
+ return ruleChain;
}
+
protected String constructBaseUrl(HttpServletRequest request) {
String scheme = request.getScheme();
if (request.getHeader("x-forwarded-proto") != null) {
@@ -553,12 +536,127 @@ public abstract class BaseController {
protected & HasName,
I extends UUIDBased & EntityId> void logEntityAction(I entityId, E entity, CustomerId customerId,
ActionType actionType, Exception e, Object... additionalInfo) throws ThingsboardException {
- User user = getCurrentUser();
+ logEntityAction(getCurrentUser(), entityId, entity, customerId, actionType, e, additionalInfo);
+ }
+
+ protected & HasName,
+ I extends UUIDBased & EntityId> void logEntityAction(User user, I entityId, E entity, CustomerId customerId,
+ ActionType actionType, Exception e, Object... additionalInfo) throws ThingsboardException {
if (customerId == null || customerId.isNullUid()) {
customerId = user.getCustomerId();
}
+ if (e == null) {
+ pushEntityActionToRuleEngine(entityId, entity, user, customerId, actionType, additionalInfo);
+ }
auditLogService.logEntityAction(user.getTenantId(), customerId, user.getId(), user.getName(), entityId, entity, actionType, e, additionalInfo);
}
+ public static Exception toException(Throwable error) {
+ return error != null ? (Exception.class.isInstance(error) ? (Exception) error : new Exception(error)) : null;
+ }
+
+ private & HasName,
+ I extends UUIDBased & EntityId> void pushEntityActionToRuleEngine(I entityId, E entity, User user, CustomerId customerId,
+ ActionType actionType, Object... additionalInfo) {
+ String msgType = null;
+ switch (actionType) {
+ case ADDED:
+ msgType = DataConstants.ENTITY_CREATED;
+ break;
+ case DELETED:
+ msgType = DataConstants.ENTITY_DELETED;
+ break;
+ case UPDATED:
+ msgType = DataConstants.ENTITY_UPDATED;
+ break;
+ case ASSIGNED_TO_CUSTOMER:
+ msgType = DataConstants.ENTITY_ASSIGNED;
+ break;
+ case UNASSIGNED_FROM_CUSTOMER:
+ msgType = DataConstants.ENTITY_UNASSIGNED;
+ break;
+ case ATTRIBUTES_UPDATED:
+ msgType = DataConstants.ATTRIBUTES_UPDATED;
+ break;
+ case ATTRIBUTES_DELETED:
+ msgType = DataConstants.ATTRIBUTES_DELETED;
+ break;
+ }
+ if (!StringUtils.isEmpty(msgType)) {
+ try {
+ TbMsgMetaData metaData = new TbMsgMetaData();
+ metaData.putValue("userId", user.getId().toString());
+ metaData.putValue("userName", user.getName());
+ if (customerId != null && !customerId.isNullUid()) {
+ metaData.putValue("customerId", customerId.toString());
+ }
+ if (actionType == ActionType.ASSIGNED_TO_CUSTOMER) {
+ String strCustomerId = extractParameter(String.class, 1, additionalInfo);
+ String strCustomerName = extractParameter(String.class, 2, additionalInfo);
+ metaData.putValue("assignedCustomerId", strCustomerId);
+ metaData.putValue("assignedCustomerName", strCustomerName);
+ } else if (actionType == ActionType.UNASSIGNED_FROM_CUSTOMER) {
+ String strCustomerId = extractParameter(String.class, 1, additionalInfo);
+ String strCustomerName = extractParameter(String.class, 2, additionalInfo);
+ metaData.putValue("unassignedCustomerId", strCustomerId);
+ metaData.putValue("unassignedCustomerName", strCustomerName);
+ }
+ ObjectNode entityNode;
+ if (entity != null) {
+ entityNode = json.valueToTree(entity);
+ if (entityId.getEntityType() == EntityType.DASHBOARD) {
+ entityNode.put("configuration", "");
+ }
+ } else {
+ entityNode = json.createObjectNode();
+ if (actionType == ActionType.ATTRIBUTES_UPDATED) {
+ String scope = extractParameter(String.class, 0, additionalInfo);
+ List attributes = extractParameter(List.class, 1, additionalInfo);
+ metaData.putValue("scope", scope);
+ if (attributes != null) {
+ for (AttributeKvEntry attr : attributes) {
+ if (attr.getDataType() == DataType.BOOLEAN) {
+ entityNode.put(attr.getKey(), attr.getBooleanValue().get());
+ } else if (attr.getDataType() == DataType.DOUBLE) {
+ entityNode.put(attr.getKey(), attr.getDoubleValue().get());
+ } else if (attr.getDataType() == DataType.LONG) {
+ entityNode.put(attr.getKey(), attr.getLongValue().get());
+ } else {
+ entityNode.put(attr.getKey(), attr.getValueAsString());
+ }
+ }
+ }
+ } else if (actionType == ActionType.ATTRIBUTES_DELETED) {
+ String scope = extractParameter(String.class, 0, additionalInfo);
+ List keys = extractParameter(List.class, 1, additionalInfo);
+ metaData.putValue("scope", scope);
+ ArrayNode attrsArrayNode = entityNode.putArray("attributes");
+ if (keys != null) {
+ keys.forEach(attrsArrayNode::add);
+ }
+ }
+ }
+ TbMsg tbMsg = new TbMsg(UUIDs.timeBased(), msgType, entityId, metaData, TbMsgDataType.JSON
+ , json.writeValueAsString(entityNode)
+ , null, null, 0L);
+ actorService.onMsg(new ServiceToRuleEngineMsg(user.getTenantId(), tbMsg));
+ } catch (Exception e) {
+ log.warn("[{}] Failed to push entity action to rule engine: {}", entityId, actionType, e);
+ }
+ }
+ }
+
+ private T extractParameter(Class clazz, int index, Object... additionalInfo) {
+ T result = null;
+ if (additionalInfo != null && additionalInfo.length > index) {
+ Object paramObject = additionalInfo[index];
+ if (clazz.isInstance(paramObject)) {
+ result = clazz.cast(paramObject);
+ }
+ }
+ return result;
+ }
+
+
}
diff --git a/application/src/main/java/org/thingsboard/server/controller/ComponentDescriptorController.java b/application/src/main/java/org/thingsboard/server/controller/ComponentDescriptorController.java
index e63a443769..fe4829f789 100644
--- a/application/src/main/java/org/thingsboard/server/controller/ComponentDescriptorController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/ComponentDescriptorController.java
@@ -16,12 +16,19 @@
package org.thingsboard.server.controller;
import org.springframework.security.access.prepost.PreAuthorize;
-import org.springframework.web.bind.annotation.*;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.RestController;
+import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.plugin.ComponentDescriptor;
import org.thingsboard.server.common.data.plugin.ComponentType;
-import org.thingsboard.server.exception.ThingsboardException;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
@RestController
@RequestMapping("/api")
@@ -52,12 +59,16 @@ public class ComponentDescriptorController extends BaseController {
}
@PreAuthorize("hasAnyAuthority('SYS_ADMIN','TENANT_ADMIN')")
- @RequestMapping(value = "/components/actions/{pluginClazz:.+}", method = RequestMethod.GET)
+ @RequestMapping(value = "/components", params = {"componentTypes"}, method = RequestMethod.GET)
@ResponseBody
- public List getPluginActionsByPluginClazz(@PathVariable("pluginClazz") String pluginClazz) throws ThingsboardException {
- checkParameter("pluginClazz", pluginClazz);
+ public List getComponentDescriptorsByTypes(@RequestParam("componentTypes") String[] strComponentTypes) throws ThingsboardException {
+ checkArrayParameter("componentTypes", strComponentTypes);
try {
- return checkPluginActionsByPluginClazz(pluginClazz);
+ Set componentTypes = new HashSet<>();
+ for (String strComponentType : strComponentTypes) {
+ componentTypes.add(ComponentType.valueOf(strComponentType));
+ }
+ return checkComponentDescriptorsByTypes(componentTypes);
} catch (Exception e) {
throw handleException(e);
}
diff --git a/application/src/main/java/org/thingsboard/server/controller/CustomerController.java b/application/src/main/java/org/thingsboard/server/controller/CustomerController.java
index b16470216a..34843ceb01 100644
--- a/application/src/main/java/org/thingsboard/server/controller/CustomerController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/CustomerController.java
@@ -20,15 +20,22 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize;
-import org.springframework.web.bind.annotation.*;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestController;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.audit.ActionType;
+import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
-import org.thingsboard.server.exception.ThingsboardException;
@RestController
@RequestMapping("/api")
diff --git a/application/src/main/java/org/thingsboard/server/controller/DashboardController.java b/application/src/main/java/org/thingsboard/server/controller/DashboardController.java
index d2952a16cb..85227e76a9 100644
--- a/application/src/main/java/org/thingsboard/server/controller/DashboardController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/DashboardController.java
@@ -17,9 +17,21 @@ package org.thingsboard.server.controller;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize;
-import org.springframework.web.bind.annotation.*;
-import org.thingsboard.server.common.data.*;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestController;
+import org.thingsboard.server.common.data.Customer;
+import org.thingsboard.server.common.data.Dashboard;
+import org.thingsboard.server.common.data.DashboardInfo;
+import org.thingsboard.server.common.data.EntityType;
+import org.thingsboard.server.common.data.ShortCustomerInfo;
import org.thingsboard.server.common.data.audit.ActionType;
+import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DashboardId;
import org.thingsboard.server.common.data.id.TenantId;
@@ -27,9 +39,6 @@ import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.common.data.page.TimePageData;
import org.thingsboard.server.common.data.page.TimePageLink;
-import org.thingsboard.server.dao.exception.IncorrectParameterException;
-import org.thingsboard.server.dao.model.ModelConstants;
-import org.thingsboard.server.exception.ThingsboardException;
import java.util.HashSet;
import java.util.Set;
diff --git a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java
index bceea54135..a6daeecf4a 100644
--- a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java
@@ -18,14 +18,22 @@ package org.thingsboard.server.controller;
import com.google.common.util.concurrent.ListenableFuture;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize;
-import org.springframework.web.bind.annotation.*;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestController;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.EntitySubtype;
import org.thingsboard.server.common.data.EntityType;
-import org.thingsboard.server.common.data.audit.ActionStatus;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.device.DeviceSearchQuery;
+import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
+import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.TenantId;
@@ -35,8 +43,6 @@ import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.dao.exception.IncorrectParameterException;
import org.thingsboard.server.dao.model.ModelConstants;
-import org.thingsboard.server.exception.ThingsboardErrorCode;
-import org.thingsboard.server.exception.ThingsboardException;
import org.thingsboard.server.service.security.model.SecurityUser;
import java.util.ArrayList;
@@ -90,6 +96,11 @@ public class DeviceController extends BaseController {
savedDevice.getCustomerId(),
device.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null);
+ if (device.getId() == null) {
+ deviceStateService.onDeviceAdded(savedDevice);
+ } else {
+ deviceStateService.onDeviceUpdated(savedDevice);
+ }
return savedDevice;
} catch (Exception e) {
logEntityAction(emptyId(EntityType.DEVICE), device,
@@ -112,6 +123,7 @@ public class DeviceController extends BaseController {
device.getCustomerId(),
ActionType.DELETED, null, strDeviceId);
+ deviceStateService.onDeviceDeleted(device);
} catch (Exception e) {
logEntityAction(emptyId(EntityType.DEVICE),
null,
diff --git a/application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java b/application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java
index 03054df985..844dbd35a9 100644
--- a/application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java
@@ -17,15 +17,21 @@ package org.thingsboard.server.controller;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize;
-import org.springframework.web.bind.annotation.*;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestController;
+import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
+import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.EntityIdFactory;
import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.relation.EntityRelationInfo;
-import org.thingsboard.server.common.data.relation.RelationTypeGroup;
import org.thingsboard.server.common.data.relation.EntityRelationsQuery;
-import org.thingsboard.server.exception.ThingsboardErrorCode;
-import org.thingsboard.server.exception.ThingsboardException;
+import org.thingsboard.server.common.data.relation.RelationTypeGroup;
import java.util.List;
diff --git a/application/src/main/java/org/thingsboard/server/controller/EventController.java b/application/src/main/java/org/thingsboard/server/controller/EventController.java
index 331b15eb09..7c8b2ae45e 100644
--- a/application/src/main/java/org/thingsboard/server/controller/EventController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/EventController.java
@@ -17,15 +17,21 @@ package org.thingsboard.server.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
-import org.springframework.web.bind.annotation.*;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.RestController;
import org.thingsboard.server.common.data.Event;
-import org.thingsboard.server.common.data.id.*;
+import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
+import org.thingsboard.server.common.data.exception.ThingsboardException;
+import org.thingsboard.server.common.data.id.EntityIdFactory;
+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.dao.event.EventService;
import org.thingsboard.server.dao.model.ModelConstants;
-import org.thingsboard.server.exception.ThingsboardErrorCode;
-import org.thingsboard.server.exception.ThingsboardException;
@RestController
@RequestMapping("/api")
diff --git a/application/src/main/java/org/thingsboard/server/controller/HttpValidationCallback.java b/application/src/main/java/org/thingsboard/server/controller/HttpValidationCallback.java
new file mode 100644
index 0000000000..fb1f3e7bee
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/controller/HttpValidationCallback.java
@@ -0,0 +1,32 @@
+/**
+ * Copyright © 2016-2018 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.controller;
+
+import com.google.common.util.concurrent.FutureCallback;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.context.request.async.DeferredResult;
+import org.thingsboard.server.service.security.ValidationCallback;
+
+/**
+ * Created by ashvayka on 21.02.17.
+ */
+public class HttpValidationCallback extends ValidationCallback> {
+
+ public HttpValidationCallback(DeferredResult response, FutureCallback> action) {
+ super(response, action);
+ }
+
+}
diff --git a/application/src/main/java/org/thingsboard/server/controller/PluginController.java b/application/src/main/java/org/thingsboard/server/controller/PluginController.java
deleted file mode 100644
index 2c69248faa..0000000000
--- a/application/src/main/java/org/thingsboard/server/controller/PluginController.java
+++ /dev/null
@@ -1,240 +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.controller;
-
-import org.springframework.http.HttpStatus;
-import org.springframework.security.access.prepost.PreAuthorize;
-import org.springframework.web.bind.annotation.*;
-import org.thingsboard.server.common.data.EntityType;
-import org.thingsboard.server.common.data.audit.ActionType;
-import org.thingsboard.server.common.data.id.PluginId;
-import org.thingsboard.server.common.data.id.TenantId;
-import org.thingsboard.server.common.data.page.TextPageData;
-import org.thingsboard.server.common.data.page.TextPageLink;
-import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
-import org.thingsboard.server.common.data.plugin.PluginMetaData;
-import org.thingsboard.server.common.data.security.Authority;
-import org.thingsboard.server.dao.model.ModelConstants;
-import org.thingsboard.server.exception.ThingsboardException;
-
-import java.util.List;
-
-@RestController
-@RequestMapping("/api")
-public class PluginController extends BaseController {
-
- public static final String PLUGIN_ID = "pluginId";
-
- @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
- @RequestMapping(value = "/plugin/{pluginId}", method = RequestMethod.GET)
- @ResponseBody
- public PluginMetaData getPluginById(@PathVariable(PLUGIN_ID) String strPluginId) throws ThingsboardException {
- checkParameter(PLUGIN_ID, strPluginId);
- try {
- PluginId pluginId = new PluginId(toUUID(strPluginId));
- return checkPlugin(pluginService.findPluginById(pluginId));
- } catch (Exception e) {
- throw handleException(e);
- }
- }
-
- @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
- @RequestMapping(value = "/plugin/token/{pluginToken}", method = RequestMethod.GET)
- @ResponseBody
- public PluginMetaData getPluginByToken(@PathVariable("pluginToken") String pluginToken) throws ThingsboardException {
- checkParameter("pluginToken", pluginToken);
- try {
- return checkPlugin(pluginService.findPluginByApiToken(pluginToken));
- } catch (Exception e) {
- throw handleException(e);
- }
- }
-
- @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
- @RequestMapping(value = "/plugin", method = RequestMethod.POST)
- @ResponseBody
- public PluginMetaData savePlugin(@RequestBody PluginMetaData source) throws ThingsboardException {
- try {
- boolean created = source.getId() == null;
- source.setTenantId(getCurrentUser().getTenantId());
- PluginMetaData plugin = checkNotNull(pluginService.savePlugin(source));
- actorService.onPluginStateChange(plugin.getTenantId(), plugin.getId(),
- created ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED);
-
- logEntityAction(plugin.getId(), plugin,
- null,
- created ? ActionType.ADDED : ActionType.UPDATED, null);
-
- return plugin;
- } catch (Exception e) {
-
- logEntityAction(emptyId(EntityType.PLUGIN), source,
- null, source.getId() == null ? ActionType.ADDED : ActionType.UPDATED, e);
-
- throw handleException(e);
- }
- }
-
- @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
- @RequestMapping(value = "/plugin/{pluginId}/activate", method = RequestMethod.POST)
- @ResponseStatus(value = HttpStatus.OK)
- public void activatePluginById(@PathVariable(PLUGIN_ID) String strPluginId) throws ThingsboardException {
- checkParameter(PLUGIN_ID, strPluginId);
- try {
- PluginId pluginId = new PluginId(toUUID(strPluginId));
- PluginMetaData plugin = checkPlugin(pluginService.findPluginById(pluginId));
- pluginService.activatePluginById(pluginId);
- actorService.onPluginStateChange(plugin.getTenantId(), plugin.getId(), ComponentLifecycleEvent.ACTIVATED);
-
- logEntityAction(plugin.getId(), plugin,
- null,
- ActionType.ACTIVATED, null, strPluginId);
-
- } catch (Exception e) {
-
- logEntityAction(emptyId(EntityType.PLUGIN),
- null,
- null,
- ActionType.ACTIVATED, e, strPluginId);
-
- throw handleException(e);
- }
- }
-
- @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
- @RequestMapping(value = "/plugin/{pluginId}/suspend", method = RequestMethod.POST)
- @ResponseStatus(value = HttpStatus.OK)
- public void suspendPluginById(@PathVariable(PLUGIN_ID) String strPluginId) throws ThingsboardException {
- checkParameter(PLUGIN_ID, strPluginId);
- try {
- PluginId pluginId = new PluginId(toUUID(strPluginId));
- PluginMetaData plugin = checkPlugin(pluginService.findPluginById(pluginId));
- pluginService.suspendPluginById(pluginId);
- actorService.onPluginStateChange(plugin.getTenantId(), plugin.getId(), ComponentLifecycleEvent.SUSPENDED);
-
- logEntityAction(plugin.getId(), plugin,
- null,
- ActionType.SUSPENDED, null, strPluginId);
-
- } catch (Exception e) {
-
- logEntityAction(emptyId(EntityType.PLUGIN),
- null,
- null,
- ActionType.SUSPENDED, e, strPluginId);
-
- throw handleException(e);
- }
- }
-
- @PreAuthorize("hasAuthority('SYS_ADMIN')")
- @RequestMapping(value = "/plugin/system", params = {"limit"}, method = RequestMethod.GET)
- @ResponseBody
- public TextPageData