Commit 9fce3e068e448d4da34b3f6307432a363ae0fd8d

Authored by Andrew Shvayka
Committed by GitHub
2 parents 07e30141 331548bf

Merge pull request #794 from thingsboard/develop/2.0

TB 2.0 merged to master
Showing 43 changed files with 1386 additions and 2741 deletions

Too many changes to show.

To preserve performance only 43 of 931 files are displayed.

@@ -20,10 +20,9 @@ @@ -20,10 +20,9 @@
20 <modelVersion>4.0.0</modelVersion> 20 <modelVersion>4.0.0</modelVersion>
21 <parent> 21 <parent>
22 <groupId>org.thingsboard</groupId> 22 <groupId>org.thingsboard</groupId>
23 - <version>1.4.1-SNAPSHOT</version> 23 + <version>2.0.0-SNAPSHOT</version>
24 <artifactId>thingsboard</artifactId> 24 <artifactId>thingsboard</artifactId>
25 </parent> 25 </parent>
26 - <groupId>org.thingsboard</groupId>  
27 <artifactId>application</artifactId> 26 <artifactId>application</artifactId>
28 <packaging>jar</packaging> 27 <packaging>jar</packaging>
29 28
@@ -50,12 +49,12 @@ @@ -50,12 +49,12 @@
50 <classifier>linux-x86_64</classifier> 49 <classifier>linux-x86_64</classifier>
51 </dependency> 50 </dependency>
52 <dependency> 51 <dependency>
53 - <groupId>org.thingsboard</groupId>  
54 - <artifactId>extensions-api</artifactId> 52 + <groupId>org.thingsboard.rule-engine</groupId>
  53 + <artifactId>rule-engine-api</artifactId>
55 </dependency> 54 </dependency>
56 <dependency> 55 <dependency>
57 - <groupId>org.thingsboard</groupId>  
58 - <artifactId>extensions-core</artifactId> 56 + <groupId>org.thingsboard.rule-engine</groupId>
  57 + <artifactId>rule-engine-components</artifactId>
59 </dependency> 58 </dependency>
60 <dependency> 59 <dependency>
61 <groupId>org.thingsboard.common</groupId> 60 <groupId>org.thingsboard.common</groupId>
@@ -257,6 +256,10 @@ @@ -257,6 +256,10 @@
257 <groupId>org.hsqldb</groupId> 256 <groupId>org.hsqldb</groupId>
258 <artifactId>hsqldb</artifactId> 257 <artifactId>hsqldb</artifactId>
259 </dependency> 258 </dependency>
  259 + <dependency>
  260 + <groupId>org.javadelight</groupId>
  261 + <artifactId>delight-nashorn-sandbox</artifactId>
  262 + </dependency>
260 </dependencies> 263 </dependencies>
261 264
262 <build> 265 <build>
@@ -464,48 +467,6 @@ @@ -464,48 +467,6 @@
464 <artifactId>maven-dependency-plugin</artifactId> 467 <artifactId>maven-dependency-plugin</artifactId>
465 <executions> 468 <executions>
466 <execution> 469 <execution>
467 - <id>copy-extensions</id>  
468 - <phase>package</phase>  
469 - <goals>  
470 - <goal>copy</goal>  
471 - </goals>  
472 - <configuration>  
473 - <outputDirectory>${project.build.directory}/extensions</outputDirectory>  
474 - <artifactItems>  
475 - <artifactItem>  
476 - <groupId>org.thingsboard.extensions</groupId>  
477 - <artifactId>extension-rabbitmq</artifactId>  
478 - <classifier>extension</classifier>  
479 - </artifactItem>  
480 - <artifactItem>  
481 - <groupId>org.thingsboard.extensions</groupId>  
482 - <artifactId>extension-rest-api-call</artifactId>  
483 - <classifier>extension</classifier>  
484 - </artifactItem>  
485 - <artifactItem>  
486 - <groupId>org.thingsboard.extensions</groupId>  
487 - <artifactId>extension-kafka</artifactId>  
488 - <classifier>extension</classifier>  
489 - </artifactItem>  
490 - <artifactItem>  
491 - <groupId>org.thingsboard.extensions</groupId>  
492 - <artifactId>extension-mqtt</artifactId>  
493 - <classifier>extension</classifier>  
494 - </artifactItem>  
495 - <artifactItem>  
496 - <groupId>org.thingsboard.extensions</groupId>  
497 - <artifactId>extension-sqs</artifactId>  
498 - <classifier>extension</classifier>  
499 - </artifactItem>  
500 - <artifactItem>  
501 - <groupId>org.thingsboard.extensions</groupId>  
502 - <artifactId>extension-sns</artifactId>  
503 - <classifier>extension</classifier>  
504 - </artifactItem>  
505 - </artifactItems>  
506 - </configuration>  
507 - </execution>  
508 - <execution>  
509 <id>copy-winsw-service</id> 470 <id>copy-winsw-service</id>
510 <phase>package</phase> 471 <phase>package</phase>
511 <goals> 472 <goals>
@@ -14,7 +14,7 @@ @@ -14,7 +14,7 @@
14 # limitations under the License. 14 # limitations under the License.
15 # 15 #
16 16
17 -export JAVA_OPTS="$JAVA_OPTS -Dplatform=@pkg.platform@" 17 +export JAVA_OPTS="$JAVA_OPTS -Dplatform=@pkg.platform@ -Dinstall.data_dir=@pkg.installFolder@"
18 export LOG_FILENAME=${pkg.name}.out 18 export LOG_FILENAME=${pkg.name}.out
19 export LOADER_PATH=${pkg.installFolder}/conf,${pkg.installFolder}/extensions 19 export LOADER_PATH=${pkg.installFolder}/conf,${pkg.installFolder}/extensions
20 export SQL_DATA_FOLDER=${pkg.installFolder}/data/sql 20 export SQL_DATA_FOLDER=${pkg.installFolder}/data/sql
1 -{  
2 - "apiToken": "messaging",  
3 - "name": "Demo Device Messaging RPC Plugin",  
4 - "clazz": "org.thingsboard.server.extensions.core.plugin.messaging.DeviceMessagingPlugin",  
5 - "publicAccess": false,  
6 - "state": "ACTIVE",  
7 - "configuration": {  
8 - "maxDeviceCountPerCustomer": 1024,  
9 - "defaultTimeout": 20000,  
10 - "maxTimeout": 60000  
11 - },  
12 - "additionalInfo": null  
13 -}  
1 -{  
2 - "apiToken": "mail",  
3 - "name": "Demo Email Plugin",  
4 - "clazz": "org.thingsboard.server.extensions.core.plugin.mail.MailPlugin",  
5 - "publicAccess": true,  
6 - "state": "ACTIVE",  
7 - "configuration": {  
8 - "host": "smtp.sendgrid.net",  
9 - "port": 2525,  
10 - "username": "apikey",  
11 - "password": "your_api_key",  
12 - "otherProperties": [  
13 - {  
14 - "key": "mail.smtp.auth",  
15 - "value": "true"  
16 - },  
17 - {  
18 - "key": "mail.smtp.timeout",  
19 - "value": "10000"  
20 - },  
21 - {  
22 - "key": "mail.smtp.starttls.enable",  
23 - "value": "true"  
24 - }  
25 - ]  
26 - },  
27 - "additionalInfo": null  
28 -}  
1 -{  
2 - "apiToken": "time",  
3 - "name": "Demo Time RPC Plugin",  
4 - "clazz": "org.thingsboard.server.extensions.core.plugin.time.TimePlugin",  
5 - "publicAccess": false,  
6 - "state": "ACTIVE",  
7 - "configuration": {  
8 - "timeFormat": "yyyy MM dd HH:mm:ss.SSS"  
9 - },  
10 - "additionalInfo": null  
11 -}  
1 -{  
2 - "name": "Demo Alarm Rule",  
3 - "state": "ACTIVE",  
4 - "weight": 0,  
5 - "pluginToken": "mail",  
6 - "filters": [  
7 - {  
8 - "clazz": "org.thingsboard.server.extensions.core.filter.MsgTypeFilter",  
9 - "name": "MsgTypeFilter",  
10 - "configuration": {  
11 - "messageTypes": [  
12 - "POST_TELEMETRY",  
13 - "POST_ATTRIBUTES",  
14 - "GET_ATTRIBUTES"  
15 - ]  
16 - }  
17 - },  
18 - {  
19 - "clazz": "org.thingsboard.server.extensions.core.filter.DeviceTelemetryFilter",  
20 - "name": "Temperature filter",  
21 - "configuration": {  
22 - "filter": "typeof temperature !== 'undefined' && temperature >= 100"  
23 - }  
24 - }  
25 - ],  
26 - "processor": {  
27 - "clazz": "org.thingsboard.server.extensions.core.processor.AlarmDeduplicationProcessor",  
28 - "name": "AlarmDeduplicationProcessor",  
29 - "configuration": {  
30 - "alarmIdTemplate": "[$date.get('yyyy-MM-dd HH:mm')] Device $cs.get('serialNumber')($cs.get('model')) temperature is high!",  
31 - "alarmBodyTemplate": "[$date.get('yyyy-MM-dd HH:mm:ss')] Device $cs.get('serialNumber')($cs.get('model')) temperature is $temp.valueAsString!"  
32 - }  
33 - },  
34 - "action": {  
35 - "clazz": "org.thingsboard.server.extensions.core.action.mail.SendMailAction",  
36 - "name": "Send Mail Action",  
37 - "configuration": {  
38 - "sendFlag": "isNewAlarm",  
39 - "fromTemplate": "thingsboard@gmail.com",  
40 - "toTemplate": "thingsboard@gmail.com",  
41 - "subjectTemplate": "$alarmId",  
42 - "bodyTemplate": "$alarmBody"  
43 - }  
44 - },  
45 - "additionalInfo": null  
46 -}  
1 -{  
2 - "name": "Demo getTime RPC Rule",  
3 - "state": "ACTIVE",  
4 - "weight": 0,  
5 - "pluginToken": "time",  
6 - "filters": [  
7 - {  
8 - "configuration": {  
9 - "messageTypes": [  
10 - "RPC_REQUEST"  
11 - ]  
12 - },  
13 - "name": "RPC Request Filter",  
14 - "clazz": "org.thingsboard.server.extensions.core.filter.MsgTypeFilter"  
15 - },  
16 - {  
17 - "configuration": {  
18 - "methodNames": [  
19 - {  
20 - "name": "getTime"  
21 - }  
22 - ]  
23 - },  
24 - "name": "getTime method filter",  
25 - "clazz": "org.thingsboard.server.extensions.core.filter.MethodNameFilter"  
26 - }  
27 - ],  
28 - "processor": null,  
29 - "action": {  
30 - "configuration": {},  
31 - "clazz": "org.thingsboard.server.extensions.core.action.rpc.RpcPluginAction",  
32 - "name": "getTimeAction"  
33 - },  
34 - "additionalInfo": null  
35 -}  
1 -{  
2 - "name": "Demo Messaging RPC Rule",  
3 - "state": "ACTIVE",  
4 - "weight": 0,  
5 - "pluginToken": "messaging",  
6 - "filters": [  
7 - {  
8 - "configuration": {  
9 - "messageTypes": [  
10 - "RPC_REQUEST"  
11 - ]  
12 - },  
13 - "name": "RPC Request Filter",  
14 - "clazz": "org.thingsboard.server.extensions.core.filter.MsgTypeFilter"  
15 - },  
16 - {  
17 - "configuration": {  
18 - "methodNames": [  
19 - {  
20 - "name": "getDevices"  
21 - },  
22 - {  
23 - "name": "sendMsg"  
24 - }  
25 - ]  
26 - },  
27 - "name": "Messaging methods filter",  
28 - "clazz": "org.thingsboard.server.extensions.core.filter.MethodNameFilter"  
29 - }  
30 - ],  
31 - "processor": null,  
32 - "action": {  
33 - "configuration": {},  
34 - "clazz": "org.thingsboard.server.extensions.core.action.rpc.RpcPluginAction",  
35 - "name": "Messaging RPC Action"  
36 - },  
37 - "additionalInfo": null  
38 -}  
1 -{  
2 - "apiToken": "rpc",  
3 - "name": "System RPC Plugin",  
4 - "clazz": "org.thingsboard.server.extensions.core.plugin.rpc.RpcPlugin",  
5 - "publicAccess": true,  
6 - "state": "ACTIVE",  
7 - "configuration": {  
8 - "defaultTimeout": 20000  
9 - },  
10 - "additionalInfo": null  
11 -}  
1 -{  
2 - "apiToken": "telemetry",  
3 - "name": "System Telemetry Plugin",  
4 - "clazz": "org.thingsboard.server.extensions.core.plugin.telemetry.TelemetryStoragePlugin",  
5 - "publicAccess": true,  
6 - "state": "ACTIVE",  
7 - "configuration": {},  
8 - "additionalInfo": null  
9 -}  
1 -{  
2 - "name": "System Telemetry Rule",  
3 - "state": "ACTIVE",  
4 - "weight": 0,  
5 - "pluginToken": "telemetry",  
6 - "filters": [  
7 - {  
8 - "clazz": "org.thingsboard.server.extensions.core.filter.MsgTypeFilter",  
9 - "name": "TelemetryFilter",  
10 - "configuration": {  
11 - "messageTypes": [  
12 - "POST_TELEMETRY",  
13 - "POST_ATTRIBUTES",  
14 - "GET_ATTRIBUTES"  
15 - ]  
16 - }  
17 - }  
18 - ],  
19 - "processor": null,  
20 - "action": {  
21 - "clazz": "org.thingsboard.server.extensions.core.action.telemetry.TelemetryPluginAction",  
22 - "name": "TelemetryMsgConverterAction",  
23 - "configuration": {  
24 - "timeUnit": "DAYS",  
25 - "ttlValue": 365  
26 - }  
27 - },  
28 - "additionalInfo": null  
29 -}  
  1 +{
  2 + "ruleChain": {
  3 + "additionalInfo": null,
  4 + "name": "Root Rule Chain",
  5 + "firstRuleNodeId": null,
  6 + "root": true,
  7 + "debugMode": false,
  8 + "configuration": null
  9 + },
  10 + "metadata": {
  11 + "firstNodeIndex": 2,
  12 + "nodes": [
  13 + {
  14 + "additionalInfo": {
  15 + "layoutX": 824,
  16 + "layoutY": 156
  17 + },
  18 + "type": "org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode",
  19 + "name": "SaveTS",
  20 + "debugMode": false,
  21 + "configuration": {
  22 + "defaultTTL": 0
  23 + }
  24 + },
  25 + {
  26 + "additionalInfo": {
  27 + "layoutX": 825,
  28 + "layoutY": 52
  29 + },
  30 + "type": "org.thingsboard.rule.engine.telemetry.TbMsgAttributesNode",
  31 + "name": "save client attributes",
  32 + "debugMode": false,
  33 + "configuration": {
  34 + "scope": "CLIENT_SCOPE"
  35 + }
  36 + },
  37 + {
  38 + "additionalInfo": {
  39 + "layoutX": 347,
  40 + "layoutY": 149
  41 + },
  42 + "type": "org.thingsboard.rule.engine.filter.TbMsgTypeSwitchNode",
  43 + "name": "Message Type Switch",
  44 + "debugMode": false,
  45 + "configuration": {
  46 + "version": 0
  47 + }
  48 + },
  49 + {
  50 + "additionalInfo": {
  51 + "layoutX": 825,
  52 + "layoutY": 266
  53 + },
  54 + "type": "org.thingsboard.rule.engine.action.TbLogNode",
  55 + "name": "Log RPC",
  56 + "debugMode": false,
  57 + "configuration": {
  58 + "jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);"
  59 + }
  60 + },
  61 + {
  62 + "additionalInfo": {
  63 + "layoutX": 825,
  64 + "layoutY": 379
  65 + },
  66 + "type": "org.thingsboard.rule.engine.action.TbLogNode",
  67 + "name": "Log Other",
  68 + "debugMode": false,
  69 + "configuration": {
  70 + "jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);"
  71 + }
  72 + }
  73 + ],
  74 + "connections": [
  75 + {
  76 + "fromIndex": 2,
  77 + "toIndex": 4,
  78 + "type": "Other"
  79 + },
  80 + {
  81 + "fromIndex": 2,
  82 + "toIndex": 1,
  83 + "type": "Post attributes"
  84 + },
  85 + {
  86 + "fromIndex": 2,
  87 + "toIndex": 0,
  88 + "type": "Post telemetry"
  89 + },
  90 + {
  91 + "fromIndex": 2,
  92 + "toIndex": 3,
  93 + "type": "RPC Request"
  94 + }
  95 + ],
  96 + "ruleChainConnections": null
  97 + }
  98 +}
  1 +--
  2 +-- Copyright © 2016-2018 The Thingsboard Authors
  3 +--
  4 +-- Licensed under the Apache License, Version 2.0 (the "License");
  5 +-- you may not use this file except in compliance with the License.
  6 +-- You may obtain a copy of the License at
  7 +--
  8 +-- http://www.apache.org/licenses/LICENSE-2.0
  9 +--
  10 +-- Unless required by applicable law or agreed to in writing, software
  11 +-- distributed under the License is distributed on an "AS IS" BASIS,
  12 +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 +-- See the License for the specific language governing permissions and
  14 +-- limitations under the License.
  15 +--
  16 +
  17 +CREATE TABLE IF NOT EXISTS thingsboard.msg_queue (
  18 + node_id timeuuid,
  19 + cluster_partition bigint,
  20 + ts_partition bigint,
  21 + ts bigint,
  22 + msg blob,
  23 + PRIMARY KEY ((node_id, cluster_partition, ts_partition), ts))
  24 +WITH CLUSTERING ORDER BY (ts DESC)
  25 +AND compaction = {
  26 + 'class': 'org.apache.cassandra.db.compaction.DateTieredCompactionStrategy',
  27 + 'min_threshold': '5',
  28 + 'base_time_seconds': '43200',
  29 + 'max_window_size_seconds': '43200',
  30 + 'tombstone_threshold': '0.9',
  31 + 'unchecked_tombstone_compaction': 'true'
  32 +};
  33 +
  34 +CREATE TABLE IF NOT EXISTS thingsboard.msg_ack_queue (
  35 + node_id timeuuid,
  36 + cluster_partition bigint,
  37 + ts_partition bigint,
  38 + msg_id timeuuid,
  39 + PRIMARY KEY ((node_id, cluster_partition, ts_partition), msg_id))
  40 +WITH CLUSTERING ORDER BY (msg_id DESC)
  41 +AND compaction = {
  42 + 'class': 'org.apache.cassandra.db.compaction.DateTieredCompactionStrategy',
  43 + 'min_threshold': '5',
  44 + 'base_time_seconds': '43200',
  45 + 'max_window_size_seconds': '43200',
  46 + 'tombstone_threshold': '0.9',
  47 + 'unchecked_tombstone_compaction': 'true'
  48 +};
  49 +
  50 +CREATE TABLE IF NOT EXISTS thingsboard.processed_msg_partitions (
  51 + node_id timeuuid,
  52 + cluster_partition bigint,
  53 + ts_partition bigint,
  54 + PRIMARY KEY ((node_id, cluster_partition), ts_partition))
  55 +WITH CLUSTERING ORDER BY (ts_partition DESC)
  56 +AND compaction = {
  57 + 'class': 'org.apache.cassandra.db.compaction.DateTieredCompactionStrategy',
  58 + 'min_threshold': '5',
  59 + 'base_time_seconds': '43200',
  60 + 'max_window_size_seconds': '43200',
  61 + 'tombstone_threshold': '0.9',
  62 + 'unchecked_tombstone_compaction': 'true'
  63 +};
  64 +
  65 +CREATE TABLE IF NOT EXISTS thingsboard.rule_chain (
  66 + id uuid,
  67 + tenant_id uuid,
  68 + name text,
  69 + search_text text,
  70 + first_rule_node_id uuid,
  71 + root boolean,
  72 + debug_mode boolean,
  73 + configuration text,
  74 + additional_info text,
  75 + PRIMARY KEY (id, tenant_id)
  76 +);
  77 +
  78 +CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.rule_chain_by_tenant_and_search_text AS
  79 + SELECT *
  80 + from thingsboard.rule_chain
  81 + WHERE tenant_id IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL
  82 + PRIMARY KEY ( tenant_id, search_text, id )
  83 + WITH CLUSTERING ORDER BY ( search_text ASC, id DESC );
  84 +
  85 +CREATE TABLE IF NOT EXISTS thingsboard.rule_node (
  86 + id uuid,
  87 + rule_chain_id uuid,
  88 + type text,
  89 + name text,
  90 + debug_mode boolean,
  91 + search_text text,
  92 + configuration text,
  93 + additional_info text,
  94 + PRIMARY KEY (id)
  95 +);
  96 +
  97 +DROP MATERIALIZED VIEW IF EXISTS thingsboard.rule_by_plugin_token;
  98 +DROP MATERIALIZED VIEW IF EXISTS thingsboard.rule_by_tenant_and_search_text;
  99 +DROP MATERIALIZED VIEW IF EXISTS thingsboard.plugin_by_api_token;
  100 +DROP MATERIALIZED VIEW IF EXISTS thingsboard.plugin_by_tenant_and_search_text;
  101 +
  102 +DROP TABLE IF EXISTS thingsboard.rule;
  103 +DROP TABLE IF EXISTS thingsboard.plugin;
  1 +--
  2 +-- Copyright © 2016-2018 The Thingsboard Authors
  3 +--
  4 +-- Licensed under the Apache License, Version 2.0 (the "License");
  5 +-- you may not use this file except in compliance with the License.
  6 +-- You may obtain a copy of the License at
  7 +--
  8 +-- http://www.apache.org/licenses/LICENSE-2.0
  9 +--
  10 +-- Unless required by applicable law or agreed to in writing, software
  11 +-- distributed under the License is distributed on an "AS IS" BASIS,
  12 +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 +-- See the License for the specific language governing permissions and
  14 +-- limitations under the License.
  15 +--
  16 +
  17 +CREATE TABLE IF NOT EXISTS rule_chain (
  18 + id varchar(31) NOT NULL CONSTRAINT rule_chain_pkey PRIMARY KEY,
  19 + additional_info varchar,
  20 + configuration varchar(10000000),
  21 + name varchar(255),
  22 + first_rule_node_id varchar(31),
  23 + root boolean,
  24 + debug_mode boolean,
  25 + search_text varchar(255),
  26 + tenant_id varchar(31)
  27 +);
  28 +
  29 +CREATE TABLE IF NOT EXISTS rule_node (
  30 + id varchar(31) NOT NULL CONSTRAINT rule_node_pkey PRIMARY KEY,
  31 + rule_chain_id varchar(31),
  32 + additional_info varchar,
  33 + configuration varchar(10000000),
  34 + type varchar(255),
  35 + name varchar(255),
  36 + debug_mode boolean,
  37 + search_text varchar(255)
  38 +);
  39 +
  40 +DROP TABLE rule;
  41 +DROP TABLE plugin;
  42 +
  43 +DELETE FROM alarm WHERE originator_type = 3 OR originator_type = 4;
  44 +UPDATE alarm SET originator_type = (originator_type - 2) where originator_type > 2;
@@ -21,22 +21,27 @@ import akka.actor.Scheduler; @@ -21,22 +21,27 @@ import akka.actor.Scheduler;
21 import com.fasterxml.jackson.databind.JsonNode; 21 import com.fasterxml.jackson.databind.JsonNode;
22 import com.fasterxml.jackson.databind.ObjectMapper; 22 import com.fasterxml.jackson.databind.ObjectMapper;
23 import com.fasterxml.jackson.databind.node.ObjectNode; 23 import com.fasterxml.jackson.databind.node.ObjectNode;
  24 +import com.google.common.util.concurrent.FutureCallback;
  25 +import com.google.common.util.concurrent.Futures;
  26 +import com.google.common.util.concurrent.ListenableFuture;
24 import com.typesafe.config.Config; 27 import com.typesafe.config.Config;
25 import com.typesafe.config.ConfigFactory; 28 import com.typesafe.config.ConfigFactory;
26 import lombok.Getter; 29 import lombok.Getter;
27 import lombok.Setter; 30 import lombok.Setter;
  31 +import lombok.extern.slf4j.Slf4j;
28 import org.springframework.beans.factory.annotation.Autowired; 32 import org.springframework.beans.factory.annotation.Autowired;
29 import org.springframework.beans.factory.annotation.Value; 33 import org.springframework.beans.factory.annotation.Value;
30 import org.springframework.stereotype.Component; 34 import org.springframework.stereotype.Component;
  35 +import org.thingsboard.rule.engine.api.MailService;
31 import org.thingsboard.server.actors.service.ActorService; 36 import org.thingsboard.server.actors.service.ActorService;
32 import org.thingsboard.server.common.data.DataConstants; 37 import org.thingsboard.server.common.data.DataConstants;
33 import org.thingsboard.server.common.data.Event; 38 import org.thingsboard.server.common.data.Event;
34 import org.thingsboard.server.common.data.id.EntityId; 39 import org.thingsboard.server.common.data.id.EntityId;
35 import org.thingsboard.server.common.data.id.TenantId; 40 import org.thingsboard.server.common.data.id.TenantId;
36 import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; 41 import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
  42 +import org.thingsboard.server.common.msg.TbMsg;
37 import org.thingsboard.server.common.msg.cluster.ServerAddress; 43 import org.thingsboard.server.common.msg.cluster.ServerAddress;
38 import org.thingsboard.server.common.transport.auth.DeviceAuthService; 44 import org.thingsboard.server.common.transport.auth.DeviceAuthService;
39 -import org.thingsboard.server.controller.plugin.PluginWebSocketMsgEndpoint;  
40 import org.thingsboard.server.dao.alarm.AlarmService; 45 import org.thingsboard.server.dao.alarm.AlarmService;
41 import org.thingsboard.server.dao.asset.AssetService; 46 import org.thingsboard.server.dao.asset.AssetService;
42 import org.thingsboard.server.dao.attributes.AttributesService; 47 import org.thingsboard.server.dao.attributes.AttributesService;
@@ -44,118 +49,222 @@ import org.thingsboard.server.dao.audit.AuditLogService; @@ -44,118 +49,222 @@ import org.thingsboard.server.dao.audit.AuditLogService;
44 import org.thingsboard.server.dao.customer.CustomerService; 49 import org.thingsboard.server.dao.customer.CustomerService;
45 import org.thingsboard.server.dao.device.DeviceService; 50 import org.thingsboard.server.dao.device.DeviceService;
46 import org.thingsboard.server.dao.event.EventService; 51 import org.thingsboard.server.dao.event.EventService;
47 -import org.thingsboard.server.dao.plugin.PluginService;  
48 import org.thingsboard.server.dao.relation.RelationService; 52 import org.thingsboard.server.dao.relation.RelationService;
49 -import org.thingsboard.server.dao.rule.RuleService; 53 +import org.thingsboard.server.dao.rule.RuleChainService;
50 import org.thingsboard.server.dao.tenant.TenantService; 54 import org.thingsboard.server.dao.tenant.TenantService;
51 import org.thingsboard.server.dao.timeseries.TimeseriesService; 55 import org.thingsboard.server.dao.timeseries.TimeseriesService;
  56 +import org.thingsboard.server.dao.user.UserService;
52 import org.thingsboard.server.service.cluster.discovery.DiscoveryService; 57 import org.thingsboard.server.service.cluster.discovery.DiscoveryService;
53 import org.thingsboard.server.service.cluster.routing.ClusterRoutingService; 58 import org.thingsboard.server.service.cluster.routing.ClusterRoutingService;
54 import org.thingsboard.server.service.cluster.rpc.ClusterRpcService; 59 import org.thingsboard.server.service.cluster.rpc.ClusterRpcService;
55 import org.thingsboard.server.service.component.ComponentDiscoveryService; 60 import org.thingsboard.server.service.component.ComponentDiscoveryService;
56 - 61 +import org.thingsboard.server.service.encoding.DataDecodingEncodingService;
  62 +import org.thingsboard.server.service.executors.DbCallbackExecutorService;
  63 +import org.thingsboard.server.service.executors.ExternalCallExecutorService;
  64 +import org.thingsboard.server.service.mail.MailExecutorService;
  65 +import org.thingsboard.server.service.queue.MsgQueueService;
  66 +import org.thingsboard.server.service.rpc.DeviceRpcService;
  67 +import org.thingsboard.server.service.script.JsExecutorService;
  68 +import org.thingsboard.server.service.script.JsSandboxService;
  69 +import org.thingsboard.server.service.state.DeviceStateService;
  70 +import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService;
  71 +
  72 +import javax.annotation.Nullable;
  73 +import java.io.IOException;
57 import java.io.PrintWriter; 74 import java.io.PrintWriter;
58 import java.io.StringWriter; 75 import java.io.StringWriter;
59 import java.util.Optional; 76 import java.util.Optional;
60 77
  78 +@Slf4j
61 @Component 79 @Component
62 public class ActorSystemContext { 80 public class ActorSystemContext {
63 private static final String AKKA_CONF_FILE_NAME = "actor-system.conf"; 81 private static final String AKKA_CONF_FILE_NAME = "actor-system.conf";
64 82
65 protected final ObjectMapper mapper = new ObjectMapper(); 83 protected final ObjectMapper mapper = new ObjectMapper();
66 84
67 - @Getter @Setter private ActorService actorService; 85 + @Getter
  86 + @Setter
  87 + private ActorService actorService;
  88 +
  89 + @Autowired
  90 + @Getter
  91 + private DiscoveryService discoveryService;
  92 +
  93 + @Autowired
  94 + @Getter
  95 + @Setter
  96 + private ComponentDiscoveryService componentService;
  97 +
  98 + @Autowired
  99 + @Getter
  100 + private ClusterRoutingService routingService;
  101 +
  102 + @Autowired
  103 + @Getter
  104 + private ClusterRpcService rpcService;
  105 +
  106 + @Autowired
  107 + @Getter
  108 + private DataDecodingEncodingService encodingService;
  109 +
  110 + @Autowired
  111 + @Getter
  112 + private DeviceAuthService deviceAuthService;
  113 +
  114 + @Autowired
  115 + @Getter
  116 + private DeviceService deviceService;
  117 +
  118 + @Autowired
  119 + @Getter
  120 + private AssetService assetService;
  121 +
  122 + @Autowired
  123 + @Getter
  124 + private TenantService tenantService;
  125 +
  126 + @Autowired
  127 + @Getter
  128 + private CustomerService customerService;
68 129
69 @Autowired 130 @Autowired
70 - @Getter private DiscoveryService discoveryService; 131 + @Getter
  132 + private UserService userService;
71 133
72 @Autowired 134 @Autowired
73 - @Getter @Setter private ComponentDiscoveryService componentService; 135 + @Getter
  136 + private RuleChainService ruleChainService;
74 137
75 @Autowired 138 @Autowired
76 - @Getter private ClusterRoutingService routingService; 139 + @Getter
  140 + private TimeseriesService tsService;
77 141
78 @Autowired 142 @Autowired
79 - @Getter private ClusterRpcService rpcService; 143 + @Getter
  144 + private AttributesService attributesService;
80 145
81 @Autowired 146 @Autowired
82 - @Getter private DeviceAuthService deviceAuthService; 147 + @Getter
  148 + private EventService eventService;
83 149
84 @Autowired 150 @Autowired
85 - @Getter private DeviceService deviceService; 151 + @Getter
  152 + private AlarmService alarmService;
86 153
87 @Autowired 154 @Autowired
88 - @Getter private AssetService assetService; 155 + @Getter
  156 + private RelationService relationService;
89 157
90 @Autowired 158 @Autowired
91 - @Getter private TenantService tenantService; 159 + @Getter
  160 + private AuditLogService auditLogService;
92 161
93 @Autowired 162 @Autowired
94 - @Getter private CustomerService customerService; 163 + @Getter
  164 + private TelemetrySubscriptionService tsSubService;
95 165
96 @Autowired 166 @Autowired
97 - @Getter private RuleService ruleService; 167 + @Getter
  168 + private DeviceRpcService deviceRpcService;
98 169
99 @Autowired 170 @Autowired
100 - @Getter private PluginService pluginService; 171 + @Getter
  172 + private JsSandboxService jsSandbox;
101 173
102 @Autowired 174 @Autowired
103 - @Getter private TimeseriesService tsService; 175 + @Getter
  176 + private JsExecutorService jsExecutor;
104 177
105 @Autowired 178 @Autowired
106 - @Getter private AttributesService attributesService; 179 + @Getter
  180 + private MailExecutorService mailExecutor;
107 181
108 @Autowired 182 @Autowired
109 - @Getter private EventService eventService; 183 + @Getter
  184 + private DbCallbackExecutorService dbCallbackExecutor;
110 185
111 @Autowired 186 @Autowired
112 - @Getter private AlarmService alarmService; 187 + @Getter
  188 + private ExternalCallExecutorService externalCallExecutorService;
113 189
114 @Autowired 190 @Autowired
115 - @Getter private RelationService relationService; 191 + @Getter
  192 + private MailService mailService;
116 193
117 @Autowired 194 @Autowired
118 - @Getter private AuditLogService auditLogService; 195 + @Getter
  196 + private MsgQueueService msgQueueService;
119 197
120 @Autowired 198 @Autowired
121 - @Getter @Setter private PluginWebSocketMsgEndpoint wsMsgEndpoint; 199 + @Getter
  200 + private DeviceStateService deviceStateService;
  201 +
  202 + @Value("${cluster.partition_id}")
  203 + @Getter
  204 + private long queuePartitionId;
  205 +
  206 + @Value("${actors.session.max_concurrent_sessions_per_device:1}")
  207 + @Getter
  208 + private long maxConcurrentSessionsPerDevice;
122 209
123 @Value("${actors.session.sync.timeout}") 210 @Value("${actors.session.sync.timeout}")
124 - @Getter private long syncSessionTimeout; 211 + @Getter
  212 + private long syncSessionTimeout;
125 213
126 - @Value("${actors.plugin.termination.delay}")  
127 - @Getter private long pluginActorTerminationDelay; 214 + @Value("${actors.queue.enabled}")
  215 + @Getter
  216 + private boolean queuePersistenceEnabled;
128 217
129 - @Value("${actors.plugin.processing.timeout}")  
130 - @Getter private long pluginProcessingTimeout; 218 + @Value("${actors.queue.timeout}")
  219 + @Getter
  220 + private long queuePersistenceTimeout;
131 221
132 - @Value("${actors.plugin.error_persist_frequency}")  
133 - @Getter private long pluginErrorPersistFrequency; 222 + @Value("${actors.client_side_rpc.timeout}")
  223 + @Getter
  224 + private long clientSideRpcTimeout;
134 225
135 - @Value("${actors.rule.termination.delay}")  
136 - @Getter private long ruleActorTerminationDelay; 226 + @Value("${actors.rule.chain.error_persist_frequency}")
  227 + @Getter
  228 + private long ruleChainErrorPersistFrequency;
137 229
138 - @Value("${actors.rule.error_persist_frequency}")  
139 - @Getter private long ruleErrorPersistFrequency; 230 + @Value("${actors.rule.node.error_persist_frequency}")
  231 + @Getter
  232 + private long ruleNodeErrorPersistFrequency;
140 233
141 @Value("${actors.statistics.enabled}") 234 @Value("${actors.statistics.enabled}")
142 - @Getter private boolean statisticsEnabled; 235 + @Getter
  236 + private boolean statisticsEnabled;
143 237
144 @Value("${actors.statistics.persist_frequency}") 238 @Value("${actors.statistics.persist_frequency}")
145 - @Getter private long statisticsPersistFrequency; 239 + @Getter
  240 + private long statisticsPersistFrequency;
146 241
147 @Value("${actors.tenant.create_components_on_init}") 242 @Value("${actors.tenant.create_components_on_init}")
148 - @Getter private boolean tenantComponentsInitEnabled; 243 + @Getter
  244 + private boolean tenantComponentsInitEnabled;
149 245
150 - @Getter @Setter private ActorSystem actorSystem; 246 + @Value("${actors.rule.allow_system_mail_service}")
  247 + @Getter
  248 + private boolean allowSystemMailService;
151 249
152 - @Getter @Setter private ActorRef appActor; 250 + @Getter
  251 + @Setter
  252 + private ActorSystem actorSystem;
153 253
154 - @Getter @Setter private ActorRef sessionManagerActor; 254 + @Getter
  255 + @Setter
  256 + private ActorRef appActor;
155 257
156 - @Getter @Setter private ActorRef statsActor; 258 + @Getter
  259 + @Setter
  260 + private ActorRef sessionManagerActor;
157 261
158 - @Getter private final Config config; 262 + @Getter
  263 + @Setter
  264 + private ActorRef statsActor;
  265 +
  266 + @Getter
  267 + private final Config config;
159 268
160 public ActorSystemContext() { 269 public ActorSystemContext() {
161 config = ConfigFactory.parseResources(AKKA_CONF_FILE_NAME).withFallback(ConfigFactory.load()); 270 config = ConfigFactory.parseResources(AKKA_CONF_FILE_NAME).withFallback(ConfigFactory.load());
@@ -187,7 +296,7 @@ public class ActorSystemContext { @@ -187,7 +296,7 @@ public class ActorSystemContext {
187 eventService.save(event); 296 eventService.save(event);
188 } 297 }
189 298
190 - private String toString(Exception e) { 299 + private String toString(Throwable e) {
191 StringWriter sw = new StringWriter(); 300 StringWriter sw = new StringWriter();
192 e.printStackTrace(new PrintWriter(sw)); 301 e.printStackTrace(new PrintWriter(sw));
193 return sw.toString(); 302 return sw.toString();
@@ -207,4 +316,72 @@ public class ActorSystemContext { @@ -207,4 +316,72 @@ public class ActorSystemContext {
207 private JsonNode toBodyJson(ServerAddress server, String method, String body) { 316 private JsonNode toBodyJson(ServerAddress server, String method, String body) {
208 return mapper.createObjectNode().put("server", server.toString()).put("method", method).put("error", body); 317 return mapper.createObjectNode().put("server", server.toString()).put("method", method).put("error", body);
209 } 318 }
  319 +
  320 + public String getServerAddress() {
  321 + return discoveryService.getCurrentServer().getServerAddress().toString();
  322 + }
  323 +
  324 + public void persistDebugInput(TenantId tenantId, EntityId entityId, TbMsg tbMsg, String relationType) {
  325 + persistDebugAsync(tenantId, entityId, "IN", tbMsg, relationType, null);
  326 + }
  327 +
  328 + public void persistDebugInput(TenantId tenantId, EntityId entityId, TbMsg tbMsg, String relationType, Throwable error) {
  329 + persistDebugAsync(tenantId, entityId, "IN", tbMsg, relationType, error);
  330 + }
  331 +
  332 + public void persistDebugOutput(TenantId tenantId, EntityId entityId, TbMsg tbMsg, String relationType, Throwable error) {
  333 + persistDebugAsync(tenantId, entityId, "OUT", tbMsg, relationType, error);
  334 + }
  335 +
  336 + public void persistDebugOutput(TenantId tenantId, EntityId entityId, TbMsg tbMsg, String relationType) {
  337 + persistDebugAsync(tenantId, entityId, "OUT", tbMsg, relationType, null);
  338 + }
  339 +
  340 + private void persistDebugAsync(TenantId tenantId, EntityId entityId, String type, TbMsg tbMsg, String relationType, Throwable error) {
  341 + try {
  342 + Event event = new Event();
  343 + event.setTenantId(tenantId);
  344 + event.setEntityId(entityId);
  345 + event.setType(DataConstants.DEBUG_RULE_NODE);
  346 +
  347 + String metadata = mapper.writeValueAsString(tbMsg.getMetaData().getData());
  348 +
  349 + ObjectNode node = mapper.createObjectNode()
  350 + .put("type", type)
  351 + .put("server", getServerAddress())
  352 + .put("entityId", tbMsg.getOriginator().getId().toString())
  353 + .put("entityName", tbMsg.getOriginator().getEntityType().name())
  354 + .put("msgId", tbMsg.getId().toString())
  355 + .put("msgType", tbMsg.getType())
  356 + .put("dataType", tbMsg.getDataType().name())
  357 + .put("relationType", relationType)
  358 + .put("data", tbMsg.getData())
  359 + .put("metadata", metadata);
  360 +
  361 + if (error != null) {
  362 + node = node.put("error", toString(error));
  363 + }
  364 +
  365 + event.setBody(node);
  366 + ListenableFuture<Event> future = eventService.saveAsync(event);
  367 + Futures.addCallback(future, new FutureCallback<Event>() {
  368 + @Override
  369 + public void onSuccess(@Nullable Event event) {
  370 +
  371 + }
  372 +
  373 + @Override
  374 + public void onFailure(Throwable th) {
  375 + log.error("Could not save debug Event for Node", th);
  376 + }
  377 + });
  378 + } catch (IOException ex) {
  379 + log.warn("Failed to persist rule node debug message", ex);
  380 + }
  381 + }
  382 +
  383 + public static Exception toException(Throwable error) {
  384 + return Exception.class.isInstance(error) ? (Exception) error : new Exception(error);
  385 + }
  386 +
210 } 387 }
@@ -15,55 +15,51 @@ @@ -15,55 +15,51 @@
15 */ 15 */
16 package org.thingsboard.server.actors.app; 16 package org.thingsboard.server.actors.app;
17 17
18 -import akka.actor.*; 18 +import akka.actor.ActorRef;
  19 +import akka.actor.LocalActorRef;
  20 +import akka.actor.OneForOneStrategy;
  21 +import akka.actor.Props;
  22 +import akka.actor.SupervisorStrategy;
19 import akka.actor.SupervisorStrategy.Directive; 23 import akka.actor.SupervisorStrategy.Directive;
  24 +import akka.actor.Terminated;
20 import akka.event.Logging; 25 import akka.event.Logging;
21 import akka.event.LoggingAdapter; 26 import akka.event.LoggingAdapter;
22 import akka.japi.Function; 27 import akka.japi.Function;
23 import org.thingsboard.server.actors.ActorSystemContext; 28 import org.thingsboard.server.actors.ActorSystemContext;
24 -import org.thingsboard.server.actors.plugin.PluginTerminationMsg;  
25 -import org.thingsboard.server.actors.service.ContextAwareActor; 29 +import org.thingsboard.server.actors.ruleChain.RuleChainManagerActor;
26 import org.thingsboard.server.actors.service.ContextBasedCreator; 30 import org.thingsboard.server.actors.service.ContextBasedCreator;
27 import org.thingsboard.server.actors.service.DefaultActorService; 31 import org.thingsboard.server.actors.service.DefaultActorService;
28 -import org.thingsboard.server.actors.shared.plugin.PluginManager;  
29 -import org.thingsboard.server.actors.shared.plugin.SystemPluginManager;  
30 -import org.thingsboard.server.actors.shared.rule.RuleManager;  
31 -import org.thingsboard.server.actors.shared.rule.SystemRuleManager;  
32 -import org.thingsboard.server.actors.tenant.RuleChainDeviceMsg; 32 +import org.thingsboard.server.actors.shared.rulechain.SystemRuleChainManager;
33 import org.thingsboard.server.actors.tenant.TenantActor; 33 import org.thingsboard.server.actors.tenant.TenantActor;
34 import org.thingsboard.server.common.data.Tenant; 34 import org.thingsboard.server.common.data.Tenant;
35 -import org.thingsboard.server.common.data.id.PluginId;  
36 -import org.thingsboard.server.common.data.id.RuleId;  
37 import org.thingsboard.server.common.data.id.TenantId; 35 import org.thingsboard.server.common.data.id.TenantId;
38 import org.thingsboard.server.common.data.page.PageDataIterable; 36 import org.thingsboard.server.common.data.page.PageDataIterable;
39 -import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;  
40 -import org.thingsboard.server.common.msg.device.ToDeviceActorMsg; 37 +import org.thingsboard.server.common.msg.TbActorMsg;
  38 +import org.thingsboard.server.common.msg.aware.TenantAwareMsg;
  39 +import org.thingsboard.server.common.msg.cluster.SendToClusterMsg;
  40 +import org.thingsboard.server.common.msg.cluster.ServerAddress;
  41 +import org.thingsboard.server.common.msg.core.BasicActorSystemToDeviceSessionActorMsg;
  42 +import org.thingsboard.server.common.msg.device.DeviceToDeviceActorMsg;
41 import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; 43 import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
  44 +import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg;
42 import org.thingsboard.server.dao.model.ModelConstants; 45 import org.thingsboard.server.dao.model.ModelConstants;
43 import org.thingsboard.server.dao.tenant.TenantService; 46 import org.thingsboard.server.dao.tenant.TenantService;
44 -import org.thingsboard.server.extensions.api.device.ToDeviceActorNotificationMsg;  
45 -import org.thingsboard.server.extensions.api.plugins.msg.ToPluginActorMsg;  
46 -import org.thingsboard.server.extensions.api.rules.ToRuleActorMsg;  
47 import scala.concurrent.duration.Duration; 47 import scala.concurrent.duration.Duration;
48 48
49 import java.util.HashMap; 49 import java.util.HashMap;
50 import java.util.Map; 50 import java.util.Map;
51 import java.util.Optional; 51 import java.util.Optional;
52 52
53 -public class AppActor extends ContextAwareActor { 53 +public class AppActor extends RuleChainManagerActor {
54 54
55 private final LoggingAdapter logger = Logging.getLogger(getContext().system(), this); 55 private final LoggingAdapter logger = Logging.getLogger(getContext().system(), this);
56 56
57 public static final TenantId SYSTEM_TENANT = new TenantId(ModelConstants.NULL_UUID); 57 public static final TenantId SYSTEM_TENANT = new TenantId(ModelConstants.NULL_UUID);
58 - private final RuleManager ruleManager;  
59 - private final PluginManager pluginManager;  
60 private final TenantService tenantService; 58 private final TenantService tenantService;
61 private final Map<TenantId, ActorRef> tenantActors; 59 private final Map<TenantId, ActorRef> tenantActors;
62 60
63 private AppActor(ActorSystemContext systemContext) { 61 private AppActor(ActorSystemContext systemContext) {
64 - super(systemContext);  
65 - this.ruleManager = new SystemRuleManager(systemContext);  
66 - this.pluginManager = new SystemPluginManager(systemContext); 62 + super(systemContext, new SystemRuleChainManager(systemContext));
67 this.tenantService = systemContext.getTenantService(); 63 this.tenantService = systemContext.getTenantService();
68 this.tenantActors = new HashMap<>(); 64 this.tenantActors = new HashMap<>();
69 } 65 }
@@ -77,8 +73,7 @@ public class AppActor extends ContextAwareActor { @@ -77,8 +73,7 @@ public class AppActor extends ContextAwareActor {
77 public void preStart() { 73 public void preStart() {
78 logger.info("Starting main system actor."); 74 logger.info("Starting main system actor.");
79 try { 75 try {
80 - ruleManager.init(this.context());  
81 - pluginManager.init(this.context()); 76 + initRuleChains();
82 77
83 if (systemContext.isTenantComponentsInitEnabled()) { 78 if (systemContext.isTenantComponentsInitEnabled()) {
84 PageDataIterable<Tenant> tenantIterator = new PageDataIterable<>(tenantService::findTenants, ENTITY_PACK_LIMIT); 79 PageDataIterable<Tenant> tenantIterator = new PageDataIterable<>(tenantService::findTenants, ENTITY_PACK_LIMIT);
@@ -96,93 +91,89 @@ public class AppActor extends ContextAwareActor { @@ -96,93 +91,89 @@ public class AppActor extends ContextAwareActor {
96 } 91 }
97 92
98 @Override 93 @Override
99 - public void onReceive(Object msg) throws Exception {  
100 - logger.debug("Received message: {}", msg);  
101 - if (msg instanceof ToDeviceActorMsg) {  
102 - processDeviceMsg((ToDeviceActorMsg) msg);  
103 - } else if (msg instanceof ToPluginActorMsg) {  
104 - onToPluginMsg((ToPluginActorMsg) msg);  
105 - } else if (msg instanceof ToRuleActorMsg) {  
106 - onToRuleMsg((ToRuleActorMsg) msg);  
107 - } else if (msg instanceof ToDeviceActorNotificationMsg) {  
108 - onToDeviceActorMsg((ToDeviceActorNotificationMsg) msg);  
109 - } else if (msg instanceof Terminated) {  
110 - processTermination((Terminated) msg);  
111 - } else if (msg instanceof ClusterEventMsg) {  
112 - broadcast(msg);  
113 - } else if (msg instanceof ComponentLifecycleMsg) {  
114 - onComponentLifecycleMsg((ComponentLifecycleMsg) msg);  
115 - } else if (msg instanceof PluginTerminationMsg) {  
116 - onPluginTerminated((PluginTerminationMsg) msg);  
117 - } else {  
118 - logger.warning("Unknown message: {}!", msg); 94 + protected boolean process(TbActorMsg msg) {
  95 + switch (msg.getMsgType()) {
  96 + case SEND_TO_CLUSTER_MSG:
  97 + onPossibleClusterMsg((SendToClusterMsg) msg);
  98 + break;
  99 + case CLUSTER_EVENT_MSG:
  100 + broadcast(msg);
  101 + break;
  102 + case COMPONENT_LIFE_CYCLE_MSG:
  103 + onComponentLifecycleMsg((ComponentLifecycleMsg) msg);
  104 + break;
  105 + case SERVICE_TO_RULE_ENGINE_MSG:
  106 + onServiceToRuleEngineMsg((ServiceToRuleEngineMsg) msg);
  107 + break;
  108 + case DEVICE_SESSION_TO_DEVICE_ACTOR_MSG:
  109 + case DEVICE_ATTRIBUTES_UPDATE_TO_DEVICE_ACTOR_MSG:
  110 + case DEVICE_CREDENTIALS_UPDATE_TO_DEVICE_ACTOR_MSG:
  111 + case DEVICE_NAME_OR_TYPE_UPDATE_TO_DEVICE_ACTOR_MSG:
  112 + case DEVICE_RPC_REQUEST_TO_DEVICE_ACTOR_MSG:
  113 + case SERVER_RPC_RESPONSE_TO_DEVICE_ACTOR_MSG:
  114 + onToDeviceActorMsg((TenantAwareMsg) msg);
  115 + break;
  116 + case ACTOR_SYSTEM_TO_DEVICE_SESSION_ACTOR_MSG:
  117 + onToDeviceSessionMsg((BasicActorSystemToDeviceSessionActorMsg) msg);
  118 + default:
  119 + return false;
119 } 120 }
  121 + return true;
120 } 122 }
121 123
122 - private void onPluginTerminated(PluginTerminationMsg msg) {  
123 - pluginManager.remove(msg.getId()); 124 + private void onToDeviceSessionMsg(BasicActorSystemToDeviceSessionActorMsg msg) {
  125 + systemContext.getSessionManagerActor().tell(msg, self());
124 } 126 }
125 127
126 - private void broadcast(Object msg) {  
127 - pluginManager.broadcast(msg);  
128 - tenantActors.values().forEach(actorRef -> actorRef.tell(msg, ActorRef.noSender())); 128 + private void onPossibleClusterMsg(SendToClusterMsg msg) {
  129 + Optional<ServerAddress> address = systemContext.getRoutingService().resolveById(msg.getEntityId());
  130 + if (address.isPresent()) {
  131 + systemContext.getRpcService().tell(
  132 + systemContext.getEncodingService().convertToProtoDataMessage(address.get(), msg.getMsg()));
  133 + } else {
  134 + self().tell(msg.getMsg(), ActorRef.noSender());
  135 + }
129 } 136 }
130 137
131 - private void onToRuleMsg(ToRuleActorMsg msg) {  
132 - ActorRef target; 138 + private void onServiceToRuleEngineMsg(ServiceToRuleEngineMsg msg) {
133 if (SYSTEM_TENANT.equals(msg.getTenantId())) { 139 if (SYSTEM_TENANT.equals(msg.getTenantId())) {
134 - target = ruleManager.getOrCreateRuleActor(this.context(), msg.getRuleId()); 140 + //TODO: ashvayka handle this.
135 } else { 141 } else {
136 - target = getOrCreateTenantActor(msg.getTenantId()); 142 + getOrCreateTenantActor(msg.getTenantId()).tell(msg, self());
137 } 143 }
138 - target.tell(msg, ActorRef.noSender());  
139 } 144 }
140 145
141 - private void onToPluginMsg(ToPluginActorMsg msg) {  
142 - ActorRef target;  
143 - if (SYSTEM_TENANT.equals(msg.getPluginTenantId())) {  
144 - target = pluginManager.getOrCreatePluginActor(this.context(), msg.getPluginId());  
145 - } else {  
146 - target = getOrCreateTenantActor(msg.getPluginTenantId());  
147 - }  
148 - target.tell(msg, ActorRef.noSender()); 146 + @Override
  147 + protected void broadcast(Object msg) {
  148 + super.broadcast(msg);
  149 + tenantActors.values().forEach(actorRef -> actorRef.tell(msg, ActorRef.noSender()));
149 } 150 }
150 151
151 private void onComponentLifecycleMsg(ComponentLifecycleMsg msg) { 152 private void onComponentLifecycleMsg(ComponentLifecycleMsg msg) {
152 - ActorRef target = null; 153 + ActorRef target;
153 if (SYSTEM_TENANT.equals(msg.getTenantId())) { 154 if (SYSTEM_TENANT.equals(msg.getTenantId())) {
154 - Optional<PluginId> pluginId = msg.getPluginId();  
155 - Optional<RuleId> ruleId = msg.getRuleId();  
156 - if (pluginId.isPresent()) {  
157 - target = pluginManager.getOrCreatePluginActor(this.context(), pluginId.get());  
158 - } else if (ruleId.isPresent()) {  
159 - Optional<ActorRef> ref = ruleManager.update(this.context(), ruleId.get(), msg.getEvent());  
160 - if (ref.isPresent()) {  
161 - target = ref.get();  
162 - } else {  
163 - logger.debug("Failed to find actor for rule: [{}]", ruleId);  
164 - return;  
165 - }  
166 - } 155 + target = getEntityActorRef(msg.getEntityId());
167 } else { 156 } else {
168 target = getOrCreateTenantActor(msg.getTenantId()); 157 target = getOrCreateTenantActor(msg.getTenantId());
169 } 158 }
170 if (target != null) { 159 if (target != null) {
171 target.tell(msg, ActorRef.noSender()); 160 target.tell(msg, ActorRef.noSender());
  161 + } else {
  162 + logger.debug("Invalid component lifecycle msg: {}", msg);
172 } 163 }
173 } 164 }
174 165
175 - private void onToDeviceActorMsg(ToDeviceActorNotificationMsg msg) { 166 + private void onToDeviceActorMsg(TenantAwareMsg msg) {
176 getOrCreateTenantActor(msg.getTenantId()).tell(msg, ActorRef.noSender()); 167 getOrCreateTenantActor(msg.getTenantId()).tell(msg, ActorRef.noSender());
177 } 168 }
178 169
179 - private void processDeviceMsg(ToDeviceActorMsg toDeviceActorMsg) {  
180 - TenantId tenantId = toDeviceActorMsg.getTenantId(); 170 + private void processDeviceMsg(DeviceToDeviceActorMsg deviceToDeviceActorMsg) {
  171 + TenantId tenantId = deviceToDeviceActorMsg.getTenantId();
181 ActorRef tenantActor = getOrCreateTenantActor(tenantId); 172 ActorRef tenantActor = getOrCreateTenantActor(tenantId);
182 - if (toDeviceActorMsg.getPayload().getMsgType().requiresRulesProcessing()) {  
183 - tenantActor.tell(new RuleChainDeviceMsg(toDeviceActorMsg, ruleManager.getRuleChain(this.context())), context().self()); 173 + if (deviceToDeviceActorMsg.getPayload().getMsgType().requiresRulesProcessing()) {
  174 +// tenantActor.tell(new RuleChainDeviceMsg(deviceToDeviceActorMsg, ruleManager.getRuleChain(this.context())), context().self());
184 } else { 175 } else {
185 - tenantActor.tell(toDeviceActorMsg, context().self()); 176 + tenantActor.tell(deviceToDeviceActorMsg, context().self());
186 } 177 }
187 } 178 }
188 179
@@ -17,61 +17,73 @@ package org.thingsboard.server.actors.device; @@ -17,61 +17,73 @@ package org.thingsboard.server.actors.device;
17 17
18 import akka.event.Logging; 18 import akka.event.Logging;
19 import akka.event.LoggingAdapter; 19 import akka.event.LoggingAdapter;
  20 +import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg;
  21 +import org.thingsboard.rule.engine.api.msg.DeviceNameOrTypeUpdateMsg;
20 import org.thingsboard.server.actors.ActorSystemContext; 22 import org.thingsboard.server.actors.ActorSystemContext;
21 -import org.thingsboard.server.actors.rule.RulesProcessedMsg;  
22 import org.thingsboard.server.actors.service.ContextAwareActor; 23 import org.thingsboard.server.actors.service.ContextAwareActor;
23 import org.thingsboard.server.actors.service.ContextBasedCreator; 24 import org.thingsboard.server.actors.service.ContextBasedCreator;
24 -import org.thingsboard.server.actors.tenant.RuleChainDeviceMsg;  
25 import org.thingsboard.server.common.data.id.DeviceId; 25 import org.thingsboard.server.common.data.id.DeviceId;
26 import org.thingsboard.server.common.data.id.TenantId; 26 import org.thingsboard.server.common.data.id.TenantId;
  27 +import org.thingsboard.server.common.msg.TbActorMsg;
27 import org.thingsboard.server.common.msg.cluster.ClusterEventMsg; 28 import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;
28 -import org.thingsboard.server.common.msg.device.ToDeviceActorMsg;  
29 -import org.thingsboard.server.extensions.api.device.DeviceAttributesEventNotificationMsg;  
30 -import org.thingsboard.server.extensions.api.device.DeviceCredentialsUpdateNotificationMsg;  
31 -import org.thingsboard.server.extensions.api.device.DeviceNameOrTypeUpdateMsg;  
32 -import org.thingsboard.server.extensions.api.device.ToDeviceActorNotificationMsg;  
33 -import org.thingsboard.server.extensions.api.plugins.msg.*; 29 +import org.thingsboard.server.common.msg.device.DeviceToDeviceActorMsg;
  30 +import org.thingsboard.server.common.msg.timeout.DeviceActorClientSideRpcTimeoutMsg;
  31 +import org.thingsboard.server.common.msg.timeout.DeviceActorQueueTimeoutMsg;
  32 +import org.thingsboard.server.common.msg.timeout.DeviceActorServerSideRpcTimeoutMsg;
  33 +import org.thingsboard.server.service.rpc.ToDeviceRpcRequestActorMsg;
  34 +import org.thingsboard.server.service.rpc.ToServerRpcResponseActorMsg;
34 35
35 public class DeviceActor extends ContextAwareActor { 36 public class DeviceActor extends ContextAwareActor {
36 37
37 private final LoggingAdapter logger = Logging.getLogger(getContext().system(), this); 38 private final LoggingAdapter logger = Logging.getLogger(getContext().system(), this);
38 39
39 - private final TenantId tenantId;  
40 - private final DeviceId deviceId;  
41 private final DeviceActorMessageProcessor processor; 40 private final DeviceActorMessageProcessor processor;
42 41
43 private DeviceActor(ActorSystemContext systemContext, TenantId tenantId, DeviceId deviceId) { 42 private DeviceActor(ActorSystemContext systemContext, TenantId tenantId, DeviceId deviceId) {
44 super(systemContext); 43 super(systemContext);
45 - this.tenantId = tenantId;  
46 - this.deviceId = deviceId;  
47 - this.processor = new DeviceActorMessageProcessor(systemContext, logger, deviceId); 44 + this.processor = new DeviceActorMessageProcessor(systemContext, logger, tenantId, deviceId);
48 } 45 }
49 46
50 @Override 47 @Override
51 - public void onReceive(Object msg) throws Exception {  
52 - if (msg instanceof RuleChainDeviceMsg) {  
53 - processor.process(context(), (RuleChainDeviceMsg) msg);  
54 - } else if (msg instanceof RulesProcessedMsg) {  
55 - processor.onRulesProcessedMsg(context(), (RulesProcessedMsg) msg);  
56 - } else if (msg instanceof ToDeviceActorMsg) {  
57 - processor.process(context(), (ToDeviceActorMsg) msg);  
58 - } else if (msg instanceof ToDeviceActorNotificationMsg) {  
59 - if (msg instanceof DeviceAttributesEventNotificationMsg) { 48 + protected boolean process(TbActorMsg msg) {
  49 + switch (msg.getMsgType()) {
  50 + case CLUSTER_EVENT_MSG:
  51 + processor.processClusterEventMsg((ClusterEventMsg) msg);
  52 + break;
  53 + case DEVICE_SESSION_TO_DEVICE_ACTOR_MSG:
  54 + processor.process(context(), (DeviceToDeviceActorMsg) msg);
  55 + break;
  56 + case DEVICE_ATTRIBUTES_UPDATE_TO_DEVICE_ACTOR_MSG:
60 processor.processAttributesUpdate(context(), (DeviceAttributesEventNotificationMsg) msg); 57 processor.processAttributesUpdate(context(), (DeviceAttributesEventNotificationMsg) msg);
61 - } else if (msg instanceof ToDeviceRpcRequestPluginMsg) {  
62 - processor.processRpcRequest(context(), (ToDeviceRpcRequestPluginMsg) msg);  
63 - } else if (msg instanceof DeviceCredentialsUpdateNotificationMsg){ 58 + break;
  59 + case DEVICE_CREDENTIALS_UPDATE_TO_DEVICE_ACTOR_MSG:
64 processor.processCredentialsUpdate(); 60 processor.processCredentialsUpdate();
65 - } else if (msg instanceof DeviceNameOrTypeUpdateMsg){ 61 + break;
  62 + case DEVICE_NAME_OR_TYPE_UPDATE_TO_DEVICE_ACTOR_MSG:
66 processor.processNameOrTypeUpdate((DeviceNameOrTypeUpdateMsg) msg); 63 processor.processNameOrTypeUpdate((DeviceNameOrTypeUpdateMsg) msg);
67 - }  
68 - } else if (msg instanceof TimeoutMsg) {  
69 - processor.processTimeout(context(), (TimeoutMsg) msg);  
70 - } else if (msg instanceof ClusterEventMsg) {  
71 - processor.processClusterEventMsg((ClusterEventMsg) msg);  
72 - } else {  
73 - logger.debug("[{}][{}] Unknown msg type.", tenantId, deviceId, msg.getClass().getName()); 64 + break;
  65 + case DEVICE_RPC_REQUEST_TO_DEVICE_ACTOR_MSG:
  66 + processor.processRpcRequest(context(), (ToDeviceRpcRequestActorMsg) msg);
  67 + break;
  68 + case SERVER_RPC_RESPONSE_TO_DEVICE_ACTOR_MSG:
  69 + processor.processToServerRPCResponse(context(), (ToServerRpcResponseActorMsg) msg);
  70 + break;
  71 + case DEVICE_ACTOR_SERVER_SIDE_RPC_TIMEOUT_MSG:
  72 + processor.processServerSideRpcTimeout(context(), (DeviceActorServerSideRpcTimeoutMsg) msg);
  73 + break;
  74 + case DEVICE_ACTOR_CLIENT_SIDE_RPC_TIMEOUT_MSG:
  75 + processor.processClientSideRpcTimeout(context(), (DeviceActorClientSideRpcTimeoutMsg) msg);
  76 + break;
  77 + case DEVICE_ACTOR_QUEUE_TIMEOUT_MSG:
  78 + processor.processQueueTimeout(context(), (DeviceActorQueueTimeoutMsg) msg);
  79 + break;
  80 + case RULE_ENGINE_QUEUE_PUT_ACK_MSG:
  81 + processor.processQueueAck(context(), (RuleEngineQueuePutAckMsg) msg);
  82 + break;
  83 + default:
  84 + return false;
74 } 85 }
  86 + return true;
75 } 87 }
76 88
77 public static class ActorCreator extends ContextBasedCreator<DeviceActor> { 89 public static class ActorCreator extends ContextBasedCreator<DeviceActor> {
@@ -18,37 +18,75 @@ package org.thingsboard.server.actors.device; @@ -18,37 +18,75 @@ package org.thingsboard.server.actors.device;
18 import akka.actor.ActorContext; 18 import akka.actor.ActorContext;
19 import akka.actor.ActorRef; 19 import akka.actor.ActorRef;
20 import akka.event.LoggingAdapter; 20 import akka.event.LoggingAdapter;
  21 +import com.datastax.driver.core.utils.UUIDs;
  22 +import com.google.common.util.concurrent.FutureCallback;
  23 +import com.google.common.util.concurrent.Futures;
  24 +import com.google.common.util.concurrent.ListenableFuture;
  25 +import com.google.gson.Gson;
  26 +import com.google.gson.JsonObject;
  27 +import com.google.gson.JsonParser;
  28 +import org.thingsboard.rule.engine.api.RpcError;
  29 +import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg;
  30 +import org.thingsboard.rule.engine.api.msg.DeviceNameOrTypeUpdateMsg;
21 import org.thingsboard.server.actors.ActorSystemContext; 31 import org.thingsboard.server.actors.ActorSystemContext;
22 -import org.thingsboard.server.actors.rule.*;  
23 import org.thingsboard.server.actors.shared.AbstractContextAwareMsgProcessor; 32 import org.thingsboard.server.actors.shared.AbstractContextAwareMsgProcessor;
24 -import org.thingsboard.server.actors.tenant.RuleChainDeviceMsg;  
25 import org.thingsboard.server.common.data.DataConstants; 33 import org.thingsboard.server.common.data.DataConstants;
26 import org.thingsboard.server.common.data.Device; 34 import org.thingsboard.server.common.data.Device;
27 import org.thingsboard.server.common.data.id.DeviceId; 35 import org.thingsboard.server.common.data.id.DeviceId;
28 import org.thingsboard.server.common.data.id.SessionId; 36 import org.thingsboard.server.common.data.id.SessionId;
  37 +import org.thingsboard.server.common.data.id.TenantId;
29 import org.thingsboard.server.common.data.kv.AttributeKey; 38 import org.thingsboard.server.common.data.kv.AttributeKey;
30 import org.thingsboard.server.common.data.kv.AttributeKvEntry; 39 import org.thingsboard.server.common.data.kv.AttributeKvEntry;
  40 +import org.thingsboard.server.common.data.kv.KvEntry;
  41 +import org.thingsboard.server.common.data.rpc.ToDeviceRpcRequestBody;
  42 +import org.thingsboard.server.common.msg.TbMsg;
  43 +import org.thingsboard.server.common.msg.TbMsgDataType;
  44 +import org.thingsboard.server.common.msg.TbMsgMetaData;
31 import org.thingsboard.server.common.msg.cluster.ClusterEventMsg; 45 import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;
32 import org.thingsboard.server.common.msg.cluster.ServerAddress; 46 import org.thingsboard.server.common.msg.cluster.ServerAddress;
33 -import org.thingsboard.server.common.msg.core.*;  
34 -import org.thingsboard.server.common.msg.device.ToDeviceActorMsg; 47 +import org.thingsboard.server.common.msg.core.ActorSystemToDeviceSessionActorMsg;
  48 +import org.thingsboard.server.common.msg.core.AttributesUpdateNotification;
  49 +import org.thingsboard.server.common.msg.core.AttributesUpdateRequest;
  50 +import org.thingsboard.server.common.msg.core.BasicActorSystemToDeviceSessionActorMsg;
  51 +import org.thingsboard.server.common.msg.core.BasicCommandAckResponse;
  52 +import org.thingsboard.server.common.msg.core.BasicGetAttributesResponse;
  53 +import org.thingsboard.server.common.msg.core.BasicStatusCodeResponse;
  54 +import org.thingsboard.server.common.msg.core.GetAttributesRequest;
  55 +import org.thingsboard.server.common.msg.core.RuleEngineError;
  56 +import org.thingsboard.server.common.msg.core.RuleEngineErrorMsg;
  57 +import org.thingsboard.server.common.msg.core.SessionCloseMsg;
  58 +import org.thingsboard.server.common.msg.core.SessionCloseNotification;
  59 +import org.thingsboard.server.common.msg.core.SessionOpenMsg;
  60 +import org.thingsboard.server.common.msg.core.TelemetryUploadRequest;
  61 +import org.thingsboard.server.common.msg.core.ToDeviceRpcRequestMsg;
  62 +import org.thingsboard.server.common.msg.core.ToDeviceRpcResponseMsg;
  63 +import org.thingsboard.server.common.msg.core.ToServerRpcRequestMsg;
  64 +import org.thingsboard.server.common.msg.device.DeviceToDeviceActorMsg;
35 import org.thingsboard.server.common.msg.kv.BasicAttributeKVMsg; 65 import org.thingsboard.server.common.msg.kv.BasicAttributeKVMsg;
  66 +import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest;
36 import org.thingsboard.server.common.msg.session.FromDeviceMsg; 67 import org.thingsboard.server.common.msg.session.FromDeviceMsg;
37 -import org.thingsboard.server.common.msg.session.MsgType; 68 +import org.thingsboard.server.common.msg.session.SessionMsgType;
38 import org.thingsboard.server.common.msg.session.SessionType; 69 import org.thingsboard.server.common.msg.session.SessionType;
39 import org.thingsboard.server.common.msg.session.ToDeviceMsg; 70 import org.thingsboard.server.common.msg.session.ToDeviceMsg;
40 -import org.thingsboard.server.extensions.api.device.*;  
41 -import org.thingsboard.server.extensions.api.plugins.msg.FromDeviceRpcResponse;  
42 -import org.thingsboard.server.extensions.api.plugins.msg.RpcError;  
43 -import org.thingsboard.server.extensions.api.plugins.msg.TimeoutIntMsg;  
44 -import org.thingsboard.server.extensions.api.plugins.msg.TimeoutMsg;  
45 -import org.thingsboard.server.extensions.api.plugins.msg.ToDeviceRpcRequest;  
46 -import org.thingsboard.server.extensions.api.plugins.msg.ToDeviceRpcRequestBody;  
47 -import org.thingsboard.server.extensions.api.plugins.msg.ToDeviceRpcRequestPluginMsg;  
48 -import org.thingsboard.server.extensions.api.plugins.msg.ToPluginRpcResponseDeviceMsg;  
49 -  
50 -import java.util.*;  
51 -import java.util.concurrent.ExecutionException; 71 +import org.thingsboard.server.common.msg.timeout.DeviceActorClientSideRpcTimeoutMsg;
  72 +import org.thingsboard.server.common.msg.timeout.DeviceActorQueueTimeoutMsg;
  73 +import org.thingsboard.server.common.msg.timeout.DeviceActorServerSideRpcTimeoutMsg;
  74 +import org.thingsboard.server.service.rpc.FromDeviceRpcResponse;
  75 +import org.thingsboard.server.service.rpc.ToDeviceRpcRequestActorMsg;
  76 +import org.thingsboard.server.service.rpc.ToServerRpcResponseActorMsg;
  77 +
  78 +import javax.annotation.Nullable;
  79 +import java.util.ArrayList;
  80 +import java.util.Arrays;
  81 +import java.util.Collections;
  82 +import java.util.HashMap;
  83 +import java.util.HashSet;
  84 +import java.util.LinkedHashMap;
  85 +import java.util.List;
  86 +import java.util.Map;
  87 +import java.util.Optional;
  88 +import java.util.Set;
  89 +import java.util.UUID;
52 import java.util.concurrent.TimeoutException; 90 import java.util.concurrent.TimeoutException;
53 import java.util.function.Consumer; 91 import java.util.function.Consumer;
54 import java.util.function.Predicate; 92 import java.util.function.Predicate;
@@ -59,46 +97,46 @@ import java.util.stream.Collectors; @@ -59,46 +97,46 @@ import java.util.stream.Collectors;
59 */ 97 */
60 public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { 98 public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
61 99
  100 + private final TenantId tenantId;
62 private final DeviceId deviceId; 101 private final DeviceId deviceId;
63 private final Map<SessionId, SessionInfo> sessions; 102 private final Map<SessionId, SessionInfo> sessions;
64 private final Map<SessionId, SessionInfo> attributeSubscriptions; 103 private final Map<SessionId, SessionInfo> attributeSubscriptions;
65 private final Map<SessionId, SessionInfo> rpcSubscriptions; 104 private final Map<SessionId, SessionInfo> rpcSubscriptions;
  105 + private final Map<Integer, ToDeviceRpcRequestMetadata> toDeviceRpcPendingMap;
  106 + private final Map<Integer, ToServerRpcRequestMetadata> toServerRpcPendingMap;
  107 + private final Map<UUID, PendingSessionMsgData> pendingMsgs;
66 108
67 - private final Map<Integer, ToDeviceRpcRequestMetadata> rpcPendingMap; 109 + private final Gson gson = new Gson();
  110 + private final JsonParser jsonParser = new JsonParser();
68 111
69 private int rpcSeq = 0; 112 private int rpcSeq = 0;
70 private String deviceName; 113 private String deviceName;
71 private String deviceType; 114 private String deviceType;
72 - private DeviceAttributes deviceAttributes; 115 + private TbMsgMetaData defaultMetaData;
73 116
74 - public DeviceActorMessageProcessor(ActorSystemContext systemContext, LoggingAdapter logger, DeviceId deviceId) { 117 + public DeviceActorMessageProcessor(ActorSystemContext systemContext, LoggingAdapter logger, TenantId tenantId, DeviceId deviceId) {
75 super(systemContext, logger); 118 super(systemContext, logger);
  119 + this.tenantId = tenantId;
76 this.deviceId = deviceId; 120 this.deviceId = deviceId;
77 - this.sessions = new HashMap<>(); 121 + this.sessions = new LinkedHashMap<>();
78 this.attributeSubscriptions = new HashMap<>(); 122 this.attributeSubscriptions = new HashMap<>();
79 this.rpcSubscriptions = new HashMap<>(); 123 this.rpcSubscriptions = new HashMap<>();
80 - this.rpcPendingMap = new HashMap<>(); 124 + this.toDeviceRpcPendingMap = new HashMap<>();
  125 + this.toServerRpcPendingMap = new HashMap<>();
  126 + this.pendingMsgs = new HashMap<>();
81 initAttributes(); 127 initAttributes();
82 } 128 }
83 129
84 private void initAttributes() { 130 private void initAttributes() {
85 - //TODO: add invalidation of deviceType cache.  
86 Device device = systemContext.getDeviceService().findDeviceById(deviceId); 131 Device device = systemContext.getDeviceService().findDeviceById(deviceId);
87 this.deviceName = device.getName(); 132 this.deviceName = device.getName();
88 this.deviceType = device.getType(); 133 this.deviceType = device.getType();
89 - this.deviceAttributes = new DeviceAttributes(fetchAttributes(DataConstants.CLIENT_SCOPE),  
90 - fetchAttributes(DataConstants.SERVER_SCOPE), fetchAttributes(DataConstants.SHARED_SCOPE)); 134 + this.defaultMetaData = new TbMsgMetaData();
  135 + this.defaultMetaData.putValue("deviceName", deviceName);
  136 + this.defaultMetaData.putValue("deviceType", deviceType);
91 } 137 }
92 138
93 - private void refreshAttributes(DeviceAttributesEventNotificationMsg msg) {  
94 - if (msg.isDeleted()) {  
95 - msg.getDeletedKeys().forEach(key -> deviceAttributes.remove(key));  
96 - } else {  
97 - deviceAttributes.update(msg.getScope(), msg.getValues());  
98 - }  
99 - }  
100 -  
101 - void processRpcRequest(ActorContext context, ToDeviceRpcRequestPluginMsg msg) { 139 + void processRpcRequest(ActorContext context, ToDeviceRpcRequestActorMsg msg) {
102 ToDeviceRpcRequest request = msg.getMsg(); 140 ToDeviceRpcRequest request = msg.getMsg();
103 ToDeviceRpcRequestBody body = request.getBody(); 141 ToDeviceRpcRequestBody body = request.getBody();
104 ToDeviceRpcRequestMsg rpcRequest = new ToDeviceRpcRequestMsg( 142 ToDeviceRpcRequestMsg rpcRequest = new ToDeviceRpcRequestMsg(
@@ -116,7 +154,7 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso @@ -116,7 +154,7 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
116 boolean sent = rpcSubscriptions.size() > 0; 154 boolean sent = rpcSubscriptions.size() > 0;
117 Set<SessionId> syncSessionSet = new HashSet<>(); 155 Set<SessionId> syncSessionSet = new HashSet<>();
118 rpcSubscriptions.entrySet().forEach(sub -> { 156 rpcSubscriptions.entrySet().forEach(sub -> {
119 - ToDeviceSessionActorMsg response = new BasicToDeviceSessionActorMsg(rpcRequest, sub.getKey()); 157 + ActorSystemToDeviceSessionActorMsg response = new BasicActorSystemToDeviceSessionActorMsg(rpcRequest, sub.getKey());
120 sendMsgToSessionActor(response, sub.getValue().getServer()); 158 sendMsgToSessionActor(response, sub.getValue().getServer());
121 if (SessionType.SYNC == sub.getValue().getType()) { 159 if (SessionType.SYNC == sub.getValue().getType()) {
122 syncSessionSet.add(sub.getKey()); 160 syncSessionSet.add(sub.getKey());
@@ -125,9 +163,8 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso @@ -125,9 +163,8 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
125 syncSessionSet.forEach(rpcSubscriptions::remove); 163 syncSessionSet.forEach(rpcSubscriptions::remove);
126 164
127 if (request.isOneway() && sent) { 165 if (request.isOneway() && sent) {
128 - ToPluginRpcResponseDeviceMsg responsePluginMsg = toPluginRpcResponseMsg(msg, (String) null);  
129 - context.parent().tell(responsePluginMsg, ActorRef.noSender());  
130 logger.debug("[{}] Rpc command response sent [{}]!", deviceId, request.getId()); 166 logger.debug("[{}] Rpc command response sent [{}]!", deviceId, request.getId());
  167 + systemContext.getDeviceRpcService().processRpcResponseFromDevice(new FromDeviceRpcResponse(msg.getMsg().getId(), msg.getServerAddress(), null, null));
131 } else { 168 } else {
132 registerPendingRpcRequest(context, msg, sent, rpcRequest, timeout); 169 registerPendingRpcRequest(context, msg, sent, rpcRequest, timeout);
133 } 170 }
@@ -139,24 +176,46 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso @@ -139,24 +176,46 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
139 176
140 } 177 }
141 178
142 - private void registerPendingRpcRequest(ActorContext context, ToDeviceRpcRequestPluginMsg msg, boolean sent, ToDeviceRpcRequestMsg rpcRequest, long timeout) {  
143 - rpcPendingMap.put(rpcRequest.getRequestId(), new ToDeviceRpcRequestMetadata(msg, sent));  
144 - TimeoutIntMsg timeoutMsg = new TimeoutIntMsg(rpcRequest.getRequestId(), timeout); 179 + private void registerPendingRpcRequest(ActorContext context, ToDeviceRpcRequestActorMsg msg, boolean sent, ToDeviceRpcRequestMsg rpcRequest, long timeout) {
  180 + toDeviceRpcPendingMap.put(rpcRequest.getRequestId(), new ToDeviceRpcRequestMetadata(msg, sent));
  181 + DeviceActorServerSideRpcTimeoutMsg timeoutMsg = new DeviceActorServerSideRpcTimeoutMsg(rpcRequest.getRequestId(), timeout);
145 scheduleMsgWithDelay(context, timeoutMsg, timeoutMsg.getTimeout()); 182 scheduleMsgWithDelay(context, timeoutMsg, timeoutMsg.getTimeout());
146 } 183 }
147 184
148 - public void processTimeout(ActorContext context, TimeoutMsg msg) {  
149 - ToDeviceRpcRequestMetadata requestMd = rpcPendingMap.remove(msg.getId()); 185 + void processServerSideRpcTimeout(ActorContext context, DeviceActorServerSideRpcTimeoutMsg msg) {
  186 + ToDeviceRpcRequestMetadata requestMd = toDeviceRpcPendingMap.remove(msg.getId());
150 if (requestMd != null) { 187 if (requestMd != null) {
151 logger.debug("[{}] RPC request [{}] timeout detected!", deviceId, msg.getId()); 188 logger.debug("[{}] RPC request [{}] timeout detected!", deviceId, msg.getId());
152 - ToPluginRpcResponseDeviceMsg responsePluginMsg = toPluginRpcResponseMsg(requestMd.getMsg(), requestMd.isSent() ? RpcError.TIMEOUT : RpcError.NO_ACTIVE_CONNECTION);  
153 - context.parent().tell(responsePluginMsg, ActorRef.noSender()); 189 + systemContext.getDeviceRpcService().processRpcResponseFromDevice(new FromDeviceRpcResponse(requestMd.getMsg().getMsg().getId(),
  190 + requestMd.getMsg().getServerAddress(), null, requestMd.isSent() ? RpcError.TIMEOUT : RpcError.NO_ACTIVE_CONNECTION));
  191 + }
  192 + }
  193 +
  194 + void processQueueTimeout(ActorContext context, DeviceActorQueueTimeoutMsg msg) {
  195 + PendingSessionMsgData data = pendingMsgs.remove(msg.getId());
  196 + if (data != null) {
  197 + logger.debug("[{}] Queue put [{}] timeout detected!", deviceId, msg.getId());
  198 + ToDeviceMsg toDeviceMsg = new RuleEngineErrorMsg(data.getSessionMsgType(), RuleEngineError.QUEUE_PUT_TIMEOUT);
  199 + sendMsgToSessionActor(new BasicActorSystemToDeviceSessionActorMsg(toDeviceMsg, data.getSessionId()), data.getServerAddress());
  200 + }
  201 + }
  202 +
  203 + void processQueueAck(ActorContext context, RuleEngineQueuePutAckMsg msg) {
  204 + PendingSessionMsgData data = pendingMsgs.remove(msg.getId());
  205 + if (data != null && data.isReplyOnQueueAck()) {
  206 + int remainingAcks = data.getAckMsgCount() - 1;
  207 + data.setAckMsgCount(remainingAcks);
  208 + logger.debug("[{}] Queue put [{}] ack detected. Remaining acks: {}!", deviceId, msg.getId(), remainingAcks);
  209 + if (remainingAcks == 0) {
  210 + ToDeviceMsg toDeviceMsg = BasicStatusCodeResponse.onSuccess(data.getSessionMsgType(), data.getRequestId());
  211 + sendMsgToSessionActor(new BasicActorSystemToDeviceSessionActorMsg(toDeviceMsg, data.getSessionId()), data.getServerAddress());
  212 + }
154 } 213 }
155 } 214 }
156 215
157 private void sendPendingRequests(ActorContext context, SessionId sessionId, SessionType type, Optional<ServerAddress> server) { 216 private void sendPendingRequests(ActorContext context, SessionId sessionId, SessionType type, Optional<ServerAddress> server) {
158 - if (!rpcPendingMap.isEmpty()) {  
159 - logger.debug("[{}] Pushing {} pending RPC messages to new async session [{}]", deviceId, rpcPendingMap.size(), sessionId); 217 + if (!toDeviceRpcPendingMap.isEmpty()) {
  218 + logger.debug("[{}] Pushing {} pending RPC messages to new async session [{}]", deviceId, toDeviceRpcPendingMap.size(), sessionId);
160 if (type == SessionType.SYNC) { 219 if (type == SessionType.SYNC) {
161 logger.debug("[{}] Cleanup sync rpc session [{}]", deviceId, sessionId); 220 logger.debug("[{}] Cleanup sync rpc session [{}]", deviceId, sessionId);
162 rpcSubscriptions.remove(sessionId); 221 rpcSubscriptions.remove(sessionId);
@@ -166,41 +225,196 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso @@ -166,41 +225,196 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
166 } 225 }
167 Set<Integer> sentOneWayIds = new HashSet<>(); 226 Set<Integer> sentOneWayIds = new HashSet<>();
168 if (type == SessionType.ASYNC) { 227 if (type == SessionType.ASYNC) {
169 - rpcPendingMap.entrySet().forEach(processPendingRpc(context, sessionId, server, sentOneWayIds)); 228 + toDeviceRpcPendingMap.entrySet().forEach(processPendingRpc(context, sessionId, server, sentOneWayIds));
170 } else { 229 } else {
171 - rpcPendingMap.entrySet().stream().findFirst().ifPresent(processPendingRpc(context, sessionId, server, sentOneWayIds)); 230 + toDeviceRpcPendingMap.entrySet().stream().findFirst().ifPresent(processPendingRpc(context, sessionId, server, sentOneWayIds));
172 } 231 }
173 232
174 - sentOneWayIds.forEach(rpcPendingMap::remove); 233 + sentOneWayIds.forEach(toDeviceRpcPendingMap::remove);
175 } 234 }
176 235
177 private Consumer<Map.Entry<Integer, ToDeviceRpcRequestMetadata>> processPendingRpc(ActorContext context, SessionId sessionId, Optional<ServerAddress> server, Set<Integer> sentOneWayIds) { 236 private Consumer<Map.Entry<Integer, ToDeviceRpcRequestMetadata>> processPendingRpc(ActorContext context, SessionId sessionId, Optional<ServerAddress> server, Set<Integer> sentOneWayIds) {
178 return entry -> { 237 return entry -> {
  238 + ToDeviceRpcRequestActorMsg requestActorMsg = entry.getValue().getMsg();
179 ToDeviceRpcRequest request = entry.getValue().getMsg().getMsg(); 239 ToDeviceRpcRequest request = entry.getValue().getMsg().getMsg();
180 ToDeviceRpcRequestBody body = request.getBody(); 240 ToDeviceRpcRequestBody body = request.getBody();
181 if (request.isOneway()) { 241 if (request.isOneway()) {
182 sentOneWayIds.add(entry.getKey()); 242 sentOneWayIds.add(entry.getKey());
183 - ToPluginRpcResponseDeviceMsg responsePluginMsg = toPluginRpcResponseMsg(entry.getValue().getMsg(), (String) null);  
184 - context.parent().tell(responsePluginMsg, ActorRef.noSender()); 243 + systemContext.getDeviceRpcService().processRpcResponseFromDevice(new FromDeviceRpcResponse(request.getId(), requestActorMsg.getServerAddress(), null, null));
185 } 244 }
186 ToDeviceRpcRequestMsg rpcRequest = new ToDeviceRpcRequestMsg( 245 ToDeviceRpcRequestMsg rpcRequest = new ToDeviceRpcRequestMsg(
187 entry.getKey(), 246 entry.getKey(),
188 body.getMethod(), 247 body.getMethod(),
189 body.getParams() 248 body.getParams()
190 ); 249 );
191 - ToDeviceSessionActorMsg response = new BasicToDeviceSessionActorMsg(rpcRequest, sessionId); 250 + ActorSystemToDeviceSessionActorMsg response = new BasicActorSystemToDeviceSessionActorMsg(rpcRequest, sessionId);
192 sendMsgToSessionActor(response, server); 251 sendMsgToSessionActor(response, server);
193 }; 252 };
194 } 253 }
195 254
196 - void process(ActorContext context, ToDeviceActorMsg msg) { 255 + void process(ActorContext context, DeviceToDeviceActorMsg msg) {
197 processSubscriptionCommands(context, msg); 256 processSubscriptionCommands(context, msg);
198 processRpcResponses(context, msg); 257 processRpcResponses(context, msg);
199 processSessionStateMsgs(msg); 258 processSessionStateMsgs(msg);
  259 +
  260 + SessionMsgType sessionMsgType = msg.getPayload().getMsgType();
  261 + if (sessionMsgType.requiresRulesProcessing()) {
  262 + switch (sessionMsgType) {
  263 + case GET_ATTRIBUTES_REQUEST:
  264 + handleGetAttributesRequest(msg);
  265 + break;
  266 + case POST_ATTRIBUTES_REQUEST:
  267 + handlePostAttributesRequest(context, msg);
  268 + reportActivity();
  269 + break;
  270 + case POST_TELEMETRY_REQUEST:
  271 + handlePostTelemetryRequest(context, msg);
  272 + reportActivity();
  273 + break;
  274 + case TO_SERVER_RPC_REQUEST:
  275 + handleClientSideRPCRequest(context, msg);
  276 + reportActivity();
  277 + break;
  278 + }
  279 + }
  280 + }
  281 +
  282 + private void reportActivity() {
  283 + systemContext.getDeviceStateService().onDeviceActivity(deviceId);
  284 + }
  285 +
  286 + private void reportSessionOpen() {
  287 + systemContext.getDeviceStateService().onDeviceConnect(deviceId);
  288 + }
  289 +
  290 + private void reportSessionClose() {
  291 + systemContext.getDeviceStateService().onDeviceDisconnect(deviceId);
  292 + }
  293 +
  294 + private void handleGetAttributesRequest(DeviceToDeviceActorMsg src) {
  295 + GetAttributesRequest request = (GetAttributesRequest) src.getPayload();
  296 + ListenableFuture<List<AttributeKvEntry>> clientAttributesFuture = getAttributeKvEntries(deviceId, DataConstants.CLIENT_SCOPE, request.getClientAttributeNames());
  297 + ListenableFuture<List<AttributeKvEntry>> sharedAttributesFuture = getAttributeKvEntries(deviceId, DataConstants.SHARED_SCOPE, request.getClientAttributeNames());
  298 +
  299 + Futures.addCallback(Futures.allAsList(Arrays.asList(clientAttributesFuture, sharedAttributesFuture)), new FutureCallback<List<List<AttributeKvEntry>>>() {
  300 + @Override
  301 + public void onSuccess(@Nullable List<List<AttributeKvEntry>> result) {
  302 + BasicGetAttributesResponse response = BasicGetAttributesResponse.onSuccess(request.getMsgType(),
  303 + request.getRequestId(), BasicAttributeKVMsg.from(result.get(0), result.get(1)));
  304 + sendMsgToSessionActor(new BasicActorSystemToDeviceSessionActorMsg(response, src.getSessionId()), src.getServerAddress());
  305 + }
  306 +
  307 + @Override
  308 + public void onFailure(Throwable t) {
  309 + if (t instanceof Exception) {
  310 + ToDeviceMsg toDeviceMsg = BasicStatusCodeResponse.onError(SessionMsgType.GET_ATTRIBUTES_REQUEST, request.getRequestId(), (Exception) t);
  311 + sendMsgToSessionActor(new BasicActorSystemToDeviceSessionActorMsg(toDeviceMsg, src.getSessionId()), src.getServerAddress());
  312 + } else {
  313 + logger.error("[{}] Failed to process attributes request", deviceId, t);
  314 + }
  315 + }
  316 + });
  317 + }
  318 +
  319 + private ListenableFuture<List<AttributeKvEntry>> getAttributeKvEntries(DeviceId deviceId, String scope, Optional<Set<String>> names) {
  320 + if (names.isPresent()) {
  321 + if (!names.get().isEmpty()) {
  322 + return systemContext.getAttributesService().find(deviceId, scope, names.get());
  323 + } else {
  324 + return systemContext.getAttributesService().findAll(deviceId, scope);
  325 + }
  326 + } else {
  327 + return Futures.immediateFuture(Collections.emptyList());
  328 + }
  329 + }
  330 +
  331 + private void handlePostAttributesRequest(ActorContext context, DeviceToDeviceActorMsg src) {
  332 + AttributesUpdateRequest request = (AttributesUpdateRequest) src.getPayload();
  333 +
  334 + JsonObject json = new JsonObject();
  335 + for (AttributeKvEntry kv : request.getAttributes()) {
  336 + kv.getBooleanValue().ifPresent(v -> json.addProperty(kv.getKey(), v));
  337 + kv.getLongValue().ifPresent(v -> json.addProperty(kv.getKey(), v));
  338 + kv.getDoubleValue().ifPresent(v -> json.addProperty(kv.getKey(), v));
  339 + kv.getStrValue().ifPresent(v -> json.addProperty(kv.getKey(), v));
  340 + }
  341 +
  342 + TbMsg tbMsg = new TbMsg(UUIDs.timeBased(), SessionMsgType.POST_ATTRIBUTES_REQUEST.name(), deviceId, defaultMetaData.copy(), TbMsgDataType.JSON, gson.toJson(json), null, null, 0L);
  343 + PendingSessionMsgData msgData = new PendingSessionMsgData(src.getSessionId(), src.getServerAddress(),
  344 + SessionMsgType.POST_ATTRIBUTES_REQUEST, request.getRequestId(), true, 1);
  345 + pushToRuleEngineWithTimeout(context, tbMsg, msgData);
  346 + }
  347 +
  348 + private void handlePostTelemetryRequest(ActorContext context, DeviceToDeviceActorMsg src) {
  349 + TelemetryUploadRequest request = (TelemetryUploadRequest) src.getPayload();
  350 +
  351 + Map<Long, List<KvEntry>> tsData = request.getData();
  352 +
  353 + PendingSessionMsgData msgData = new PendingSessionMsgData(src.getSessionId(), src.getServerAddress(),
  354 + SessionMsgType.POST_TELEMETRY_REQUEST, request.getRequestId(), true, tsData.size());
  355 +
  356 + for (Map.Entry<Long, List<KvEntry>> entry : tsData.entrySet()) {
  357 + JsonObject json = new JsonObject();
  358 + for (KvEntry kv : entry.getValue()) {
  359 + kv.getBooleanValue().ifPresent(v -> json.addProperty(kv.getKey(), v));
  360 + kv.getLongValue().ifPresent(v -> json.addProperty(kv.getKey(), v));
  361 + kv.getDoubleValue().ifPresent(v -> json.addProperty(kv.getKey(), v));
  362 + kv.getStrValue().ifPresent(v -> json.addProperty(kv.getKey(), v));
  363 + }
  364 + TbMsgMetaData metaData = defaultMetaData.copy();
  365 + metaData.putValue("ts", entry.getKey() + "");
  366 + TbMsg tbMsg = new TbMsg(UUIDs.timeBased(), SessionMsgType.POST_TELEMETRY_REQUEST.name(), deviceId, metaData, TbMsgDataType.JSON, gson.toJson(json), null, null, 0L);
  367 + pushToRuleEngineWithTimeout(context, tbMsg, msgData);
  368 + }
  369 + }
  370 +
  371 + private void handleClientSideRPCRequest(ActorContext context, DeviceToDeviceActorMsg src) {
  372 + ToServerRpcRequestMsg request = (ToServerRpcRequestMsg) src.getPayload();
  373 +
  374 + JsonObject json = new JsonObject();
  375 + json.addProperty("method", request.getMethod());
  376 + json.add("params", jsonParser.parse(request.getParams()));
  377 +
  378 + TbMsgMetaData requestMetaData = defaultMetaData.copy();
  379 + requestMetaData.putValue("requestId", Integer.toString(request.getRequestId()));
  380 + TbMsg tbMsg = new TbMsg(UUIDs.timeBased(), SessionMsgType.TO_SERVER_RPC_REQUEST.name(), deviceId, requestMetaData, TbMsgDataType.JSON, gson.toJson(json), null, null, 0L);
  381 + PendingSessionMsgData msgData = new PendingSessionMsgData(src.getSessionId(), src.getServerAddress(), SessionMsgType.TO_SERVER_RPC_REQUEST, request.getRequestId(), false, 1);
  382 + pushToRuleEngineWithTimeout(context, tbMsg, msgData);
  383 +
  384 + scheduleMsgWithDelay(context, new DeviceActorClientSideRpcTimeoutMsg(request.getRequestId(), systemContext.getClientSideRpcTimeout()), systemContext.getClientSideRpcTimeout());
  385 + toServerRpcPendingMap.put(request.getRequestId(), new ToServerRpcRequestMetadata(src.getSessionId(), src.getSessionType(), src.getServerAddress()));
  386 + }
  387 +
  388 + public void processClientSideRpcTimeout(ActorContext context, DeviceActorClientSideRpcTimeoutMsg msg) {
  389 + ToServerRpcRequestMetadata data = toServerRpcPendingMap.remove(msg.getId());
  390 + if (data != null) {
  391 + logger.debug("[{}] Client side RPC request [{}] timeout detected!", deviceId, msg.getId());
  392 + ToDeviceMsg toDeviceMsg = new RuleEngineErrorMsg(SessionMsgType.TO_SERVER_RPC_REQUEST, RuleEngineError.TIMEOUT);
  393 + sendMsgToSessionActor(new BasicActorSystemToDeviceSessionActorMsg(toDeviceMsg, data.getSessionId()), data.getServer());
  394 + }
  395 + }
  396 +
  397 + void processToServerRPCResponse(ActorContext context, ToServerRpcResponseActorMsg msg) {
  398 + ToServerRpcRequestMetadata data = toServerRpcPendingMap.remove(msg.getMsg().getRequestId());
  399 + if (data != null) {
  400 + sendMsgToSessionActor(new BasicActorSystemToDeviceSessionActorMsg(msg.getMsg(), data.getSessionId()), data.getServer());
  401 + }
  402 + }
  403 +
  404 + private void pushToRuleEngineWithTimeout(ActorContext context, TbMsg tbMsg, PendingSessionMsgData pendingMsgData) {
  405 + SessionMsgType sessionMsgType = pendingMsgData.getSessionMsgType();
  406 + int requestId = pendingMsgData.getRequestId();
  407 + if (systemContext.isQueuePersistenceEnabled()) {
  408 + pendingMsgs.put(tbMsg.getId(), pendingMsgData);
  409 + scheduleMsgWithDelay(context, new DeviceActorQueueTimeoutMsg(tbMsg.getId(), systemContext.getQueuePersistenceTimeout()), systemContext.getQueuePersistenceTimeout());
  410 + } else {
  411 + ActorSystemToDeviceSessionActorMsg response = new BasicActorSystemToDeviceSessionActorMsg(BasicStatusCodeResponse.onSuccess(sessionMsgType, requestId), pendingMsgData.getSessionId());
  412 + sendMsgToSessionActor(response, pendingMsgData.getServerAddress());
  413 + }
  414 + context.parent().tell(new DeviceActorToRuleEngineMsg(context.self(), tbMsg), context.self());
200 } 415 }
201 416
202 void processAttributesUpdate(ActorContext context, DeviceAttributesEventNotificationMsg msg) { 417 void processAttributesUpdate(ActorContext context, DeviceAttributesEventNotificationMsg msg) {
203 - refreshAttributes(msg);  
204 if (attributeSubscriptions.size() > 0) { 418 if (attributeSubscriptions.size() > 0) {
205 ToDeviceMsg notification = null; 419 ToDeviceMsg notification = null;
206 if (msg.isDeleted()) { 420 if (msg.isDeleted()) {
@@ -221,7 +435,7 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso @@ -221,7 +435,7 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
221 if (notification != null) { 435 if (notification != null) {
222 ToDeviceMsg finalNotification = notification; 436 ToDeviceMsg finalNotification = notification;
223 attributeSubscriptions.entrySet().forEach(sub -> { 437 attributeSubscriptions.entrySet().forEach(sub -> {
224 - ToDeviceSessionActorMsg response = new BasicToDeviceSessionActorMsg(finalNotification, sub.getKey()); 438 + ActorSystemToDeviceSessionActorMsg response = new BasicActorSystemToDeviceSessionActorMsg(finalNotification, sub.getKey());
225 sendMsgToSessionActor(response, sub.getValue().getServer()); 439 sendMsgToSessionActor(response, sub.getValue().getServer());
226 }); 440 });
227 } 441 }
@@ -230,50 +444,30 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso @@ -230,50 +444,30 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
230 } 444 }
231 } 445 }
232 446
233 - void process(ActorContext context, RuleChainDeviceMsg srcMsg) {  
234 - ChainProcessingMetaData md = new ChainProcessingMetaData(srcMsg.getRuleChain(),  
235 - srcMsg.getToDeviceActorMsg(), new DeviceMetaData(deviceId, deviceName, deviceType, deviceAttributes), context.self());  
236 - ChainProcessingContext ctx = new ChainProcessingContext(md);  
237 - if (ctx.getChainLength() > 0) {  
238 - RuleProcessingMsg msg = new RuleProcessingMsg(ctx);  
239 - ActorRef ruleActorRef = ctx.getCurrentActor();  
240 - ruleActorRef.tell(msg, ActorRef.noSender());  
241 - } else {  
242 - context.self().tell(new RulesProcessedMsg(ctx), context.self());  
243 - }  
244 - }  
245 -  
246 - void processRpcResponses(ActorContext context, ToDeviceActorMsg msg) { 447 + private void processRpcResponses(ActorContext context, DeviceToDeviceActorMsg msg) {
247 SessionId sessionId = msg.getSessionId(); 448 SessionId sessionId = msg.getSessionId();
248 FromDeviceMsg inMsg = msg.getPayload(); 449 FromDeviceMsg inMsg = msg.getPayload();
249 - if (inMsg.getMsgType() == MsgType.TO_DEVICE_RPC_RESPONSE) { 450 + if (inMsg.getMsgType() == SessionMsgType.TO_DEVICE_RPC_RESPONSE) {
250 logger.debug("[{}] Processing rpc command response [{}]", deviceId, sessionId); 451 logger.debug("[{}] Processing rpc command response [{}]", deviceId, sessionId);
251 ToDeviceRpcResponseMsg responseMsg = (ToDeviceRpcResponseMsg) inMsg; 452 ToDeviceRpcResponseMsg responseMsg = (ToDeviceRpcResponseMsg) inMsg;
252 - ToDeviceRpcRequestMetadata requestMd = rpcPendingMap.remove(responseMsg.getRequestId()); 453 + ToDeviceRpcRequestMetadata requestMd = toDeviceRpcPendingMap.remove(responseMsg.getRequestId());
253 boolean success = requestMd != null; 454 boolean success = requestMd != null;
254 if (success) { 455 if (success) {
255 - ToPluginRpcResponseDeviceMsg responsePluginMsg = toPluginRpcResponseMsg(requestMd.getMsg(), responseMsg.getData());  
256 - Optional<ServerAddress> pluginServerAddress = requestMd.getMsg().getServerAddress();  
257 - if (pluginServerAddress.isPresent()) {  
258 - systemContext.getRpcService().tell(pluginServerAddress.get(), responsePluginMsg);  
259 - logger.debug("[{}] Rpc command response sent to remote plugin actor [{}]!", deviceId, requestMd.getMsg().getMsg().getId());  
260 - } else {  
261 - context.parent().tell(responsePluginMsg, ActorRef.noSender());  
262 - logger.debug("[{}] Rpc command response sent to local plugin actor [{}]!", deviceId, requestMd.getMsg().getMsg().getId());  
263 - } 456 + systemContext.getDeviceRpcService().processRpcResponseFromDevice(new FromDeviceRpcResponse(requestMd.getMsg().getMsg().getId(),
  457 + requestMd.getMsg().getServerAddress(), responseMsg.getData(), null));
264 } else { 458 } else {
265 logger.debug("[{}] Rpc command response [{}] is stale!", deviceId, responseMsg.getRequestId()); 459 logger.debug("[{}] Rpc command response [{}] is stale!", deviceId, responseMsg.getRequestId());
266 } 460 }
267 if (msg.getSessionType() == SessionType.SYNC) { 461 if (msg.getSessionType() == SessionType.SYNC) {
268 BasicCommandAckResponse response = success 462 BasicCommandAckResponse response = success
269 - ? BasicCommandAckResponse.onSuccess(MsgType.TO_DEVICE_RPC_REQUEST, responseMsg.getRequestId())  
270 - : BasicCommandAckResponse.onError(MsgType.TO_DEVICE_RPC_REQUEST, responseMsg.getRequestId(), new TimeoutException());  
271 - sendMsgToSessionActor(new BasicToDeviceSessionActorMsg(response, msg.getSessionId()), msg.getServerAddress()); 463 + ? BasicCommandAckResponse.onSuccess(SessionMsgType.TO_DEVICE_RPC_REQUEST, responseMsg.getRequestId())
  464 + : BasicCommandAckResponse.onError(SessionMsgType.TO_DEVICE_RPC_REQUEST, responseMsg.getRequestId(), new TimeoutException());
  465 + sendMsgToSessionActor(new BasicActorSystemToDeviceSessionActorMsg(response, msg.getSessionId()), msg.getServerAddress());
272 } 466 }
273 } 467 }
274 } 468 }
275 469
276 - public void processClusterEventMsg(ClusterEventMsg msg) { 470 + void processClusterEventMsg(ClusterEventMsg msg) {
277 if (!msg.isAdded()) { 471 if (!msg.isAdded()) {
278 logger.debug("[{}] Clearing attributes/rpc subscription for server [{}]", deviceId, msg.getServerAddress()); 472 logger.debug("[{}] Clearing attributes/rpc subscription for server [{}]", deviceId, msg.getServerAddress());
279 Predicate<Map.Entry<SessionId, SessionInfo>> filter = e -> e.getValue().getServer() 473 Predicate<Map.Entry<SessionId, SessionInfo>> filter = e -> e.getValue().getServer()
@@ -283,102 +477,79 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso @@ -283,102 +477,79 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
283 } 477 }
284 } 478 }
285 479
286 - private ToPluginRpcResponseDeviceMsg toPluginRpcResponseMsg(ToDeviceRpcRequestPluginMsg requestMsg, String data) {  
287 - return toPluginRpcResponseMsg(requestMsg, data, null);  
288 - }  
289 -  
290 - private ToPluginRpcResponseDeviceMsg toPluginRpcResponseMsg(ToDeviceRpcRequestPluginMsg requestMsg, RpcError error) {  
291 - return toPluginRpcResponseMsg(requestMsg, null, error);  
292 - }  
293 -  
294 - private ToPluginRpcResponseDeviceMsg toPluginRpcResponseMsg(ToDeviceRpcRequestPluginMsg requestMsg, String data, RpcError error) {  
295 - return new ToPluginRpcResponseDeviceMsg(  
296 - requestMsg.getPluginId(),  
297 - requestMsg.getPluginTenantId(),  
298 - new FromDeviceRpcResponse(requestMsg.getMsg().getId(),  
299 - data,  
300 - error  
301 - )  
302 - );  
303 - }  
304 -  
305 - void onRulesProcessedMsg(ActorContext context, RulesProcessedMsg msg) {  
306 - ChainProcessingContext ctx = msg.getCtx();  
307 - ToDeviceActorMsg inMsg = ctx.getInMsg();  
308 - SessionId sid = inMsg.getSessionId();  
309 - ToDeviceSessionActorMsg response;  
310 - if (ctx.getResponse() != null) {  
311 - response = new BasicToDeviceSessionActorMsg(ctx.getResponse(), sid);  
312 - } else {  
313 - response = new BasicToDeviceSessionActorMsg(ctx.getError(), sid);  
314 - }  
315 - sendMsgToSessionActor(response, inMsg.getServerAddress());  
316 - }  
317 -  
318 - private void processSubscriptionCommands(ActorContext context, ToDeviceActorMsg msg) { 480 + private void processSubscriptionCommands(ActorContext context, DeviceToDeviceActorMsg msg) {
319 SessionId sessionId = msg.getSessionId(); 481 SessionId sessionId = msg.getSessionId();
320 SessionType sessionType = msg.getSessionType(); 482 SessionType sessionType = msg.getSessionType();
321 FromDeviceMsg inMsg = msg.getPayload(); 483 FromDeviceMsg inMsg = msg.getPayload();
322 - if (inMsg.getMsgType() == MsgType.SUBSCRIBE_ATTRIBUTES_REQUEST) { 484 + if (inMsg.getMsgType() == SessionMsgType.SUBSCRIBE_ATTRIBUTES_REQUEST) {
323 logger.debug("[{}] Registering attributes subscription for session [{}]", deviceId, sessionId); 485 logger.debug("[{}] Registering attributes subscription for session [{}]", deviceId, sessionId);
324 attributeSubscriptions.put(sessionId, new SessionInfo(sessionType, msg.getServerAddress())); 486 attributeSubscriptions.put(sessionId, new SessionInfo(sessionType, msg.getServerAddress()));
325 - } else if (inMsg.getMsgType() == MsgType.UNSUBSCRIBE_ATTRIBUTES_REQUEST) { 487 + } else if (inMsg.getMsgType() == SessionMsgType.UNSUBSCRIBE_ATTRIBUTES_REQUEST) {
326 logger.debug("[{}] Canceling attributes subscription for session [{}]", deviceId, sessionId); 488 logger.debug("[{}] Canceling attributes subscription for session [{}]", deviceId, sessionId);
327 attributeSubscriptions.remove(sessionId); 489 attributeSubscriptions.remove(sessionId);
328 - } else if (inMsg.getMsgType() == MsgType.SUBSCRIBE_RPC_COMMANDS_REQUEST) { 490 + } else if (inMsg.getMsgType() == SessionMsgType.SUBSCRIBE_RPC_COMMANDS_REQUEST) {
329 logger.debug("[{}] Registering rpc subscription for session [{}][{}]", deviceId, sessionId, sessionType); 491 logger.debug("[{}] Registering rpc subscription for session [{}][{}]", deviceId, sessionId, sessionType);
330 rpcSubscriptions.put(sessionId, new SessionInfo(sessionType, msg.getServerAddress())); 492 rpcSubscriptions.put(sessionId, new SessionInfo(sessionType, msg.getServerAddress()));
331 sendPendingRequests(context, sessionId, sessionType, msg.getServerAddress()); 493 sendPendingRequests(context, sessionId, sessionType, msg.getServerAddress());
332 - } else if (inMsg.getMsgType() == MsgType.UNSUBSCRIBE_RPC_COMMANDS_REQUEST) { 494 + } else if (inMsg.getMsgType() == SessionMsgType.UNSUBSCRIBE_RPC_COMMANDS_REQUEST) {
333 logger.debug("[{}] Canceling rpc subscription for session [{}][{}]", deviceId, sessionId, sessionType); 495 logger.debug("[{}] Canceling rpc subscription for session [{}][{}]", deviceId, sessionId, sessionType);
334 rpcSubscriptions.remove(sessionId); 496 rpcSubscriptions.remove(sessionId);
335 } 497 }
336 } 498 }
337 499
338 - private void processSessionStateMsgs(ToDeviceActorMsg msg) { 500 + private void processSessionStateMsgs(DeviceToDeviceActorMsg msg) {
339 SessionId sessionId = msg.getSessionId(); 501 SessionId sessionId = msg.getSessionId();
340 FromDeviceMsg inMsg = msg.getPayload(); 502 FromDeviceMsg inMsg = msg.getPayload();
341 if (inMsg instanceof SessionOpenMsg) { 503 if (inMsg instanceof SessionOpenMsg) {
342 logger.debug("[{}] Processing new session [{}]", deviceId, sessionId); 504 logger.debug("[{}] Processing new session [{}]", deviceId, sessionId);
  505 + if (sessions.size() >= systemContext.getMaxConcurrentSessionsPerDevice()) {
  506 + SessionId sessionIdToRemove = sessions.keySet().stream().findFirst().orElse(null);
  507 + if (sessionIdToRemove != null) {
  508 + closeSession(sessionIdToRemove, sessions.remove(sessionIdToRemove));
  509 + }
  510 + }
343 sessions.put(sessionId, new SessionInfo(SessionType.ASYNC, msg.getServerAddress())); 511 sessions.put(sessionId, new SessionInfo(SessionType.ASYNC, msg.getServerAddress()));
  512 + if (sessions.size() == 1) {
  513 + reportSessionOpen();
  514 + }
344 } else if (inMsg instanceof SessionCloseMsg) { 515 } else if (inMsg instanceof SessionCloseMsg) {
345 logger.debug("[{}] Canceling subscriptions for closed session [{}]", deviceId, sessionId); 516 logger.debug("[{}] Canceling subscriptions for closed session [{}]", deviceId, sessionId);
346 sessions.remove(sessionId); 517 sessions.remove(sessionId);
347 attributeSubscriptions.remove(sessionId); 518 attributeSubscriptions.remove(sessionId);
348 rpcSubscriptions.remove(sessionId); 519 rpcSubscriptions.remove(sessionId);
  520 + if (sessions.isEmpty()) {
  521 + reportSessionClose();
  522 + }
349 } 523 }
350 } 524 }
351 525
352 - private void sendMsgToSessionActor(ToDeviceSessionActorMsg response, Optional<ServerAddress> sessionAddress) { 526 + private void sendMsgToSessionActor(ActorSystemToDeviceSessionActorMsg response, Optional<ServerAddress> sessionAddress) {
353 if (sessionAddress.isPresent()) { 527 if (sessionAddress.isPresent()) {
354 ServerAddress address = sessionAddress.get(); 528 ServerAddress address = sessionAddress.get();
355 logger.debug("{} Forwarding msg: {}", address, response); 529 logger.debug("{} Forwarding msg: {}", address, response);
356 - systemContext.getRpcService().tell(sessionAddress.get(), response); 530 + systemContext.getRpcService().tell(systemContext.getEncodingService()
  531 + .convertToProtoDataMessage(sessionAddress.get(), response));
357 } else { 532 } else {
358 systemContext.getSessionManagerActor().tell(response, ActorRef.noSender()); 533 systemContext.getSessionManagerActor().tell(response, ActorRef.noSender());
359 } 534 }
360 } 535 }
361 536
362 - private List<AttributeKvEntry> fetchAttributes(String scope) {  
363 - try {  
364 - //TODO: replace this with async operation. Happens only during actor creation, but is still criticla for performance,  
365 - return systemContext.getAttributesService().findAll(this.deviceId, scope).get();  
366 - } catch (InterruptedException | ExecutionException e) {  
367 - logger.warning("[{}] Failed to fetch attributes for scope: {}", deviceId, scope);  
368 - throw new RuntimeException(e);  
369 - }  
370 - }  
371 -  
372 - public void processCredentialsUpdate() {  
373 - sessions.forEach((k, v) -> {  
374 - sendMsgToSessionActor(new BasicToDeviceSessionActorMsg(new SessionCloseNotification(), k), v.getServer());  
375 - }); 537 + void processCredentialsUpdate() {
  538 + sessions.forEach(this::closeSession);
376 attributeSubscriptions.clear(); 539 attributeSubscriptions.clear();
377 rpcSubscriptions.clear(); 540 rpcSubscriptions.clear();
378 } 541 }
379 542
380 - public void processNameOrTypeUpdate(DeviceNameOrTypeUpdateMsg msg) { 543 + private void closeSession(SessionId sessionId, SessionInfo sessionInfo) {
  544 + sendMsgToSessionActor(new BasicActorSystemToDeviceSessionActorMsg(new SessionCloseNotification(), sessionId), sessionInfo.getServer());
  545 + }
  546 +
  547 + void processNameOrTypeUpdate(DeviceNameOrTypeUpdateMsg msg) {
381 this.deviceName = msg.getDeviceName(); 548 this.deviceName = msg.getDeviceName();
382 this.deviceType = msg.getDeviceType(); 549 this.deviceType = msg.getDeviceType();
  550 + this.defaultMetaData = new TbMsgMetaData();
  551 + this.defaultMetaData.putValue("deviceName", deviceName);
  552 + this.defaultMetaData.putValue("deviceType", deviceType);
383 } 553 }
  554 +
384 } 555 }
application/src/main/java/org/thingsboard/server/actors/device/DeviceActorToRuleEngineMsg.java renamed from application/src/main/java/org/thingsboard/server/actors/rule/ChainProcessingMetaData.java
@@ -13,29 +13,25 @@ @@ -13,29 +13,25 @@
13 * See the License for the specific language governing permissions and 13 * See the License for the specific language governing permissions and
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 -package org.thingsboard.server.actors.rule; 16 +package org.thingsboard.server.actors.device;
17 17
18 import akka.actor.ActorRef; 18 import akka.actor.ActorRef;
19 -import org.thingsboard.server.common.msg.device.ToDeviceActorMsg;  
20 -import org.thingsboard.server.extensions.api.device.DeviceMetaData; 19 +import lombok.Data;
  20 +import org.thingsboard.server.common.msg.MsgType;
  21 +import org.thingsboard.server.common.msg.TbActorMsg;
  22 +import org.thingsboard.server.common.msg.TbMsg;
21 23
22 /** 24 /**
23 - * Immutable part of chain processing data;  
24 - *  
25 - * @author ashvayka 25 + * Created by ashvayka on 15.03.18.
26 */ 26 */
27 -public final class ChainProcessingMetaData { 27 +@Data
  28 +public final class DeviceActorToRuleEngineMsg implements TbActorMsg {
28 29
29 - final RuleActorChain chain;  
30 - final ToDeviceActorMsg inMsg;  
31 - final ActorRef originator;  
32 - final DeviceMetaData deviceMetaData; 30 + private final ActorRef callbackRef;
  31 + private final TbMsg tbMsg;
33 32
34 - public ChainProcessingMetaData(RuleActorChain chain, ToDeviceActorMsg inMsg, DeviceMetaData deviceMetaData, ActorRef originator) {  
35 - super();  
36 - this.chain = chain;  
37 - this.inMsg = inMsg;  
38 - this.originator = originator;  
39 - this.deviceMetaData = deviceMetaData; 33 + @Override
  34 + public MsgType getMsgType() {
  35 + return MsgType.DEVICE_ACTOR_TO_RULE_ENGINE_MSG;
40 } 36 }
41 } 37 }
  1 +/**
  2 + * Copyright © 2016-2018 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.actors.device;
  17 +
  18 +import lombok.AllArgsConstructor;
  19 +import lombok.Data;
  20 +import org.thingsboard.server.common.data.id.SessionId;
  21 +import org.thingsboard.server.common.msg.cluster.ServerAddress;
  22 +import org.thingsboard.server.common.msg.session.SessionMsgType;
  23 +
  24 +import java.util.Optional;
  25 +
  26 +/**
  27 + * Created by ashvayka on 17.04.18.
  28 + */
  29 +@Data
  30 +@AllArgsConstructor
  31 +public final class PendingSessionMsgData {
  32 +
  33 + private final SessionId sessionId;
  34 + private final Optional<ServerAddress> serverAddress;
  35 + private final SessionMsgType sessionMsgType;
  36 + private final int requestId;
  37 + private final boolean replyOnQueueAck;
  38 + private int ackMsgCount;
  39 +
  40 +}
application/src/main/java/org/thingsboard/server/actors/device/RuleEngineQueuePutAckMsg.java renamed from application/src/main/java/org/thingsboard/server/actors/rule/RuleToPluginTimeoutMsg.java
@@ -13,24 +13,24 @@ @@ -13,24 +13,24 @@
13 * See the License for the specific language governing permissions and 13 * See the License for the specific language governing permissions and
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 -package org.thingsboard.server.actors.rule; 16 +package org.thingsboard.server.actors.device;
17 17
18 -import java.io.Serializable;  
19 -import java.util.UUID;  
20 -  
21 -public class RuleToPluginTimeoutMsg implements Serializable { 18 +import lombok.Data;
  19 +import org.thingsboard.server.common.msg.MsgType;
  20 +import org.thingsboard.server.common.msg.TbActorMsg;
22 21
23 - private static final long serialVersionUID = 1L; 22 +import java.util.UUID;
24 23
25 - private final UUID msgId; 24 +/**
  25 + * Created by ashvayka on 15.03.18.
  26 + */
  27 +@Data
  28 +public final class RuleEngineQueuePutAckMsg implements TbActorMsg {
26 29
27 - public RuleToPluginTimeoutMsg(UUID msgId) {  
28 - super();  
29 - this.msgId = msgId;  
30 - } 30 + private final UUID id;
31 31
32 - public UUID getMsgId() {  
33 - return msgId; 32 + @Override
  33 + public MsgType getMsgType() {
  34 + return MsgType.RULE_ENGINE_QUEUE_PUT_ACK_MSG;
34 } 35 }
35 -  
36 } 36 }
@@ -16,7 +16,6 @@ @@ -16,7 +16,6 @@
16 package org.thingsboard.server.actors.device; 16 package org.thingsboard.server.actors.device;
17 17
18 import lombok.Data; 18 import lombok.Data;
19 -import org.thingsboard.server.common.data.id.SessionId;  
20 import org.thingsboard.server.common.msg.cluster.ServerAddress; 19 import org.thingsboard.server.common.msg.cluster.ServerAddress;
21 import org.thingsboard.server.common.msg.session.SessionType; 20 import org.thingsboard.server.common.msg.session.SessionType;
22 21
@@ -16,13 +16,13 @@ @@ -16,13 +16,13 @@
16 package org.thingsboard.server.actors.device; 16 package org.thingsboard.server.actors.device;
17 17
18 import lombok.Data; 18 import lombok.Data;
19 -import org.thingsboard.server.extensions.api.plugins.msg.ToDeviceRpcRequestPluginMsg; 19 +import org.thingsboard.server.service.rpc.ToDeviceRpcRequestActorMsg;
20 20
21 /** 21 /**
22 * @author Andrew Shvayka 22 * @author Andrew Shvayka
23 */ 23 */
24 @Data 24 @Data
25 public class ToDeviceRpcRequestMetadata { 25 public class ToDeviceRpcRequestMetadata {
26 - private final ToDeviceRpcRequestPluginMsg msg; 26 + private final ToDeviceRpcRequestActorMsg msg;
27 private final boolean sent; 27 private final boolean sent;
28 } 28 }
application/src/main/java/org/thingsboard/server/actors/device/ToServerRpcRequestMetadata.java renamed from extensions-core/src/main/java/org/thingsboard/server/extensions/core/action/rpc/ServerSideRpcCallActionConfiguration.java
@@ -13,22 +13,21 @@ @@ -13,22 +13,21 @@
13 * See the License for the specific language governing permissions and 13 * See the License for the specific language governing permissions and
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 -package org.thingsboard.server.extensions.core.action.rpc; 16 +package org.thingsboard.server.actors.device;
17 17
18 import lombok.Data; 18 import lombok.Data;
  19 +import org.thingsboard.server.common.data.id.SessionId;
  20 +import org.thingsboard.server.common.msg.cluster.ServerAddress;
  21 +import org.thingsboard.server.common.msg.session.SessionType;
  22 +
  23 +import java.util.Optional;
19 24
20 /** 25 /**
21 * @author Andrew Shvayka 26 * @author Andrew Shvayka
22 */ 27 */
23 @Data 28 @Data
24 -public class ServerSideRpcCallActionConfiguration {  
25 -  
26 - private String sendFlag;  
27 -  
28 - private String deviceIdTemplate;  
29 - private String rpcCallMethodTemplate;  
30 - private String rpcCallBodyTemplate;  
31 - private long rpcCallTimeoutInSec;  
32 - private String fromDeviceRelationTemplate;  
33 - private String toDeviceRelationTemplate; 29 +public class ToServerRpcRequestMetadata {
  30 + private final SessionId sessionId;
  31 + private final SessionType type;
  32 + private final Optional<ServerAddress> server;
34 } 33 }
1 -/**  
2 - * Copyright © 2016-2018 The Thingsboard Authors  
3 - *  
4 - * Licensed under the Apache License, Version 2.0 (the "License");  
5 - * you may not use this file except in compliance with the License.  
6 - * You may obtain a copy of the License at  
7 - *  
8 - * http://www.apache.org/licenses/LICENSE-2.0  
9 - *  
10 - * Unless required by applicable law or agreed to in writing, software  
11 - * distributed under the License is distributed on an "AS IS" BASIS,  
12 - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  
13 - * See the License for the specific language governing permissions and  
14 - * limitations under the License.  
15 - */  
16 -package org.thingsboard.server.actors.plugin;  
17 -  
18 -import akka.actor.ActorContext;  
19 -import akka.actor.ActorRef;  
20 -import org.thingsboard.server.actors.ActorSystemContext;  
21 -import org.thingsboard.server.actors.service.ComponentActor;  
22 -import org.thingsboard.server.actors.service.ContextBasedCreator;  
23 -import org.thingsboard.server.actors.stats.StatsPersistTick;  
24 -import org.thingsboard.server.common.data.id.PluginId;  
25 -import org.thingsboard.server.common.data.id.TenantId;  
26 -import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;  
27 -import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;  
28 -import org.thingsboard.server.extensions.api.plugins.msg.TimeoutMsg;  
29 -import org.thingsboard.server.extensions.api.plugins.msg.ToPluginRpcResponseDeviceMsg;  
30 -import org.thingsboard.server.extensions.api.plugins.rest.PluginRestMsg;  
31 -import org.thingsboard.server.extensions.api.plugins.rpc.PluginRpcMsg;  
32 -import org.thingsboard.server.extensions.api.plugins.ws.msg.PluginWebsocketMsg;  
33 -import org.thingsboard.server.extensions.api.rules.RuleException;  
34 -  
35 -public class PluginActor extends ComponentActor<PluginId, PluginActorMessageProcessor> {  
36 -  
37 - private PluginActor(ActorSystemContext systemContext, TenantId tenantId, PluginId pluginId) {  
38 - super(systemContext, tenantId, pluginId);  
39 - setProcessor(new PluginActorMessageProcessor(tenantId, pluginId, systemContext,  
40 - logger, context().parent(), context().self()));  
41 - }  
42 -  
43 - @Override  
44 - public void onReceive(Object msg) throws Exception {  
45 - if (msg instanceof PluginWebsocketMsg) {  
46 - onWebsocketMsg((PluginWebsocketMsg<?>) msg);  
47 - } else if (msg instanceof PluginRestMsg) {  
48 - onRestMsg((PluginRestMsg) msg);  
49 - } else if (msg instanceof PluginCallbackMessage) {  
50 - onPluginCallback((PluginCallbackMessage) msg);  
51 - } else if (msg instanceof RuleToPluginMsgWrapper) {  
52 - onRuleToPluginMsg((RuleToPluginMsgWrapper) msg);  
53 - } else if (msg instanceof PluginRpcMsg) {  
54 - onRpcMsg((PluginRpcMsg) msg);  
55 - } else if (msg instanceof ClusterEventMsg) {  
56 - onClusterEventMsg((ClusterEventMsg) msg);  
57 - } else if (msg instanceof ComponentLifecycleMsg) {  
58 - onComponentLifecycleMsg((ComponentLifecycleMsg) msg);  
59 - } else if (msg instanceof ToPluginRpcResponseDeviceMsg) {  
60 - onRpcResponse((ToPluginRpcResponseDeviceMsg) msg);  
61 - } else if (msg instanceof PluginTerminationMsg) {  
62 - logger.info("[{}][{}] Going to terminate plugin actor.", tenantId, id);  
63 - context().parent().tell(msg, ActorRef.noSender());  
64 - context().stop(self());  
65 - } else if (msg instanceof TimeoutMsg) {  
66 - onTimeoutMsg(context(), (TimeoutMsg) msg);  
67 - } else if (msg instanceof StatsPersistTick) {  
68 - onStatsPersistTick(id);  
69 - } else {  
70 - logger.debug("[{}][{}] Unknown msg type.", tenantId, id, msg.getClass().getName());  
71 - }  
72 - }  
73 -  
74 - private void onPluginCallback(PluginCallbackMessage msg) {  
75 - try {  
76 - processor.onPluginCallbackMsg(msg);  
77 - } catch (Exception e) {  
78 - logAndPersist("onPluginCallbackMsg", e);  
79 - }  
80 - }  
81 -  
82 - private void onTimeoutMsg(ActorContext context, TimeoutMsg msg) {  
83 - processor.onTimeoutMsg(context, msg);  
84 - }  
85 -  
86 - private void onRpcResponse(ToPluginRpcResponseDeviceMsg msg) {  
87 - processor.onDeviceRpcMsg(msg.getResponse());  
88 - }  
89 -  
90 - private void onRuleToPluginMsg(RuleToPluginMsgWrapper msg) throws RuleException {  
91 - logger.debug("[{}] Going to process rule msg: {}", id, msg.getMsg());  
92 - try {  
93 - processor.onRuleToPluginMsg(msg);  
94 - increaseMessagesProcessedCount();  
95 - } catch (Exception e) {  
96 - logAndPersist("onRuleMsg", e);  
97 - }  
98 - }  
99 -  
100 - private void onWebsocketMsg(PluginWebsocketMsg<?> msg) {  
101 - logger.debug("[{}] Going to process web socket msg: {}", id, msg);  
102 - try {  
103 - processor.onWebsocketMsg(msg);  
104 - increaseMessagesProcessedCount();  
105 - } catch (Exception e) {  
106 - logAndPersist("onWebsocketMsg", e);  
107 - }  
108 - }  
109 -  
110 - private void onRestMsg(PluginRestMsg msg) {  
111 - logger.debug("[{}] Going to process rest msg: {}", id, msg);  
112 - try {  
113 - processor.onRestMsg(msg);  
114 - increaseMessagesProcessedCount();  
115 - } catch (Exception e) {  
116 - logAndPersist("onRestMsg", e);  
117 - }  
118 - }  
119 -  
120 - private void onRpcMsg(PluginRpcMsg msg) {  
121 - try {  
122 - logger.debug("[{}] Going to process rpc msg: {}", id, msg);  
123 - processor.onRpcMsg(msg);  
124 - } catch (Exception e) {  
125 - logAndPersist("onRpcMsg", e);  
126 - }  
127 - }  
128 -  
129 - public static class ActorCreator extends ContextBasedCreator<PluginActor> {  
130 - private static final long serialVersionUID = 1L;  
131 -  
132 - private final TenantId tenantId;  
133 - private final PluginId pluginId;  
134 -  
135 - public ActorCreator(ActorSystemContext context, TenantId tenantId, PluginId pluginId) {  
136 - super(context);  
137 - this.tenantId = tenantId;  
138 - this.pluginId = pluginId;  
139 - }  
140 -  
141 - @Override  
142 - public PluginActor create() throws Exception {  
143 - return new PluginActor(context, tenantId, pluginId);  
144 - }  
145 - }  
146 -  
147 - @Override  
148 - protected long getErrorPersistFrequency() {  
149 - return systemContext.getPluginErrorPersistFrequency();  
150 - }  
151 -}  
1 -/**  
2 - * Copyright © 2016-2018 The Thingsboard Authors  
3 - *  
4 - * Licensed under the Apache License, Version 2.0 (the "License");  
5 - * you may not use this file except in compliance with the License.  
6 - * You may obtain a copy of the License at  
7 - *  
8 - * http://www.apache.org/licenses/LICENSE-2.0  
9 - *  
10 - * Unless required by applicable law or agreed to in writing, software  
11 - * distributed under the License is distributed on an "AS IS" BASIS,  
12 - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  
13 - * See the License for the specific language governing permissions and  
14 - * limitations under the License.  
15 - */  
16 -package org.thingsboard.server.actors.plugin;  
17 -  
18 -import akka.actor.ActorContext;  
19 -import akka.actor.ActorRef;  
20 -import akka.event.LoggingAdapter;  
21 -import com.fasterxml.jackson.core.JsonProcessingException;  
22 -import org.thingsboard.server.actors.ActorSystemContext;  
23 -import org.thingsboard.server.actors.shared.ComponentMsgProcessor;  
24 -import org.thingsboard.server.common.data.id.PluginId;  
25 -import org.thingsboard.server.common.data.id.TenantId;  
26 -import org.thingsboard.server.common.data.plugin.ComponentLifecycleState;  
27 -import org.thingsboard.server.common.data.plugin.ComponentType;  
28 -import org.thingsboard.server.common.data.plugin.PluginMetaData;  
29 -import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;  
30 -import org.thingsboard.server.common.msg.cluster.ServerAddress;  
31 -import org.thingsboard.server.common.msg.core.BasicStatusCodeResponse;  
32 -import org.thingsboard.server.common.msg.session.FromDeviceRequestMsg;  
33 -import org.thingsboard.server.common.msg.session.MsgType;  
34 -import org.thingsboard.server.extensions.api.plugins.Plugin;  
35 -import org.thingsboard.server.extensions.api.plugins.PluginInitializationException;  
36 -import org.thingsboard.server.extensions.api.plugins.msg.FromDeviceRpcResponse;  
37 -import org.thingsboard.server.extensions.api.plugins.msg.ResponsePluginToRuleMsg;  
38 -import org.thingsboard.server.extensions.api.plugins.msg.RuleToPluginMsg;  
39 -import org.thingsboard.server.extensions.api.plugins.msg.TimeoutMsg;  
40 -import org.thingsboard.server.extensions.api.plugins.rest.PluginRestMsg;  
41 -import org.thingsboard.server.extensions.api.plugins.rpc.PluginRpcMsg;  
42 -import org.thingsboard.server.extensions.api.plugins.ws.msg.PluginWebsocketMsg;  
43 -import org.thingsboard.server.extensions.api.rules.RuleException;  
44 -  
45 -/**  
46 - * @author Andrew Shvayka  
47 - */  
48 -public class PluginActorMessageProcessor extends ComponentMsgProcessor<PluginId> {  
49 -  
50 - private final SharedPluginProcessingContext pluginCtx;  
51 - private final PluginProcessingContext trustedCtx;  
52 - private PluginMetaData pluginMd;  
53 - private Plugin pluginImpl;  
54 - private ComponentLifecycleState state;  
55 -  
56 -  
57 - protected PluginActorMessageProcessor(TenantId tenantId, PluginId pluginId, ActorSystemContext systemContext  
58 - , LoggingAdapter logger, ActorRef parent, ActorRef self) {  
59 - super(systemContext, logger, tenantId, pluginId);  
60 - this.pluginCtx = new SharedPluginProcessingContext(systemContext, tenantId, pluginId, parent, self);  
61 - this.trustedCtx = new PluginProcessingContext(pluginCtx, null);  
62 - }  
63 -  
64 - @Override  
65 - public void start() throws Exception {  
66 - logger.info("[{}] Going to start plugin actor.", entityId);  
67 - pluginMd = systemContext.getPluginService().findPluginById(entityId);  
68 - if (pluginMd == null) {  
69 - throw new PluginInitializationException("Plugin not found!");  
70 - }  
71 - if (pluginMd.getConfiguration() == null) {  
72 - throw new PluginInitializationException("Plugin metadata is empty!");  
73 - }  
74 - state = pluginMd.getState();  
75 - if (state == ComponentLifecycleState.ACTIVE) {  
76 - logger.info("[{}] Plugin is active. Going to initialize plugin.", entityId);  
77 - initComponent();  
78 - } else {  
79 - logger.info("[{}] Plugin is suspended. Skipping plugin initialization.", entityId);  
80 - }  
81 - }  
82 -  
83 - @Override  
84 - public void stop() throws Exception {  
85 - onStop();  
86 - }  
87 -  
88 - private void initComponent() {  
89 - try {  
90 - pluginImpl = initComponent(pluginMd.getClazz(), ComponentType.PLUGIN, mapper.writeValueAsString(pluginMd.getConfiguration()));  
91 - } catch (InstantiationException e) {  
92 - throw new PluginInitializationException("No default constructor for plugin implementation!", e);  
93 - } catch (IllegalAccessException e) {  
94 - throw new PluginInitializationException("Illegal Access Exception during plugin initialization!", e);  
95 - } catch (ClassNotFoundException e) {  
96 - throw new PluginInitializationException("Plugin Class not found!", e);  
97 - } catch (JsonProcessingException e) {  
98 - throw new PluginInitializationException("Plugin Configuration is invalid!", e);  
99 - } catch (Exception e) {  
100 - throw new PluginInitializationException(e.getMessage(), e);  
101 - }  
102 - }  
103 -  
104 - public void onRuleToPluginMsg(RuleToPluginMsgWrapper msg) throws RuleException {  
105 - if (state == ComponentLifecycleState.ACTIVE) {  
106 - try {  
107 - pluginImpl.process(trustedCtx, msg.getRuleTenantId(), msg.getRuleId(), msg.getMsg());  
108 - } catch (Exception ex) {  
109 - logger.debug("[{}] Failed to process RuleToPlugin msg: [{}] [{}]", tenantId, msg.getMsg(), ex);  
110 - RuleToPluginMsg ruleMsg = msg.getMsg();  
111 - MsgType responceMsgType = MsgType.RULE_ENGINE_ERROR;  
112 - Integer requestId = 0;  
113 - if (ruleMsg.getPayload() instanceof FromDeviceRequestMsg) {  
114 - requestId = ((FromDeviceRequestMsg) ruleMsg.getPayload()).getRequestId();  
115 - }  
116 - trustedCtx.reply(  
117 - new ResponsePluginToRuleMsg(ruleMsg.getUid(), tenantId, msg.getRuleId(),  
118 - BasicStatusCodeResponse.onError(responceMsgType, requestId, ex)));  
119 - }  
120 - } else {  
121 - //TODO: reply with plugin suspended message  
122 - }  
123 - }  
124 -  
125 - public void onWebsocketMsg(PluginWebsocketMsg<?> msg) {  
126 - if (state == ComponentLifecycleState.ACTIVE) {  
127 - pluginImpl.process(new PluginProcessingContext(pluginCtx, msg.getSecurityCtx()), msg);  
128 - } else {  
129 - //TODO: reply with plugin suspended message  
130 - }  
131 - }  
132 -  
133 - public void onRestMsg(PluginRestMsg msg) {  
134 - if (state == ComponentLifecycleState.ACTIVE) {  
135 - pluginImpl.process(new PluginProcessingContext(pluginCtx, msg.getSecurityCtx()), msg);  
136 - }  
137 - }  
138 -  
139 - public void onRpcMsg(PluginRpcMsg msg) {  
140 - if (state == ComponentLifecycleState.ACTIVE) {  
141 - pluginImpl.process(trustedCtx, msg.getRpcMsg());  
142 - } else {  
143 - //TODO: reply with plugin suspended message  
144 - }  
145 - }  
146 -  
147 - public void onPluginCallbackMsg(PluginCallbackMessage msg) {  
148 - if (state == ComponentLifecycleState.ACTIVE) {  
149 - if (msg.isSuccess()) {  
150 - msg.getCallback().onSuccess(trustedCtx, msg.getV());  
151 - } else {  
152 - msg.getCallback().onFailure(trustedCtx, msg.getE());  
153 - }  
154 - } else {  
155 - //TODO: reply with plugin suspended message  
156 - }  
157 - }  
158 -  
159 -  
160 - public void onTimeoutMsg(ActorContext context, TimeoutMsg<?> msg) {  
161 - if (state == ComponentLifecycleState.ACTIVE) {  
162 - pluginImpl.process(trustedCtx, msg);  
163 - }  
164 - }  
165 -  
166 -  
167 - public void onDeviceRpcMsg(FromDeviceRpcResponse response) {  
168 - if (state == ComponentLifecycleState.ACTIVE) {  
169 - pluginImpl.process(trustedCtx, response);  
170 - }  
171 - }  
172 -  
173 - @Override  
174 - public void onClusterEventMsg(ClusterEventMsg msg) {  
175 - if (state == ComponentLifecycleState.ACTIVE) {  
176 - ServerAddress address = msg.getServerAddress();  
177 - if (msg.isAdded()) {  
178 - logger.debug("[{}] Going to process server add msg: {}", entityId, address);  
179 - pluginImpl.onServerAdded(trustedCtx, address);  
180 - } else {  
181 - logger.debug("[{}] Going to process server remove msg: {}", entityId, address);  
182 - pluginImpl.onServerRemoved(trustedCtx, address);  
183 - }  
184 - }  
185 - }  
186 -  
187 - @Override  
188 - public void onCreated(ActorContext context) {  
189 - logger.info("[{}] Going to process onCreated plugin.", entityId);  
190 - }  
191 -  
192 - @Override  
193 - public void onUpdate(ActorContext context) throws Exception {  
194 - PluginMetaData oldPluginMd = pluginMd;  
195 - pluginMd = systemContext.getPluginService().findPluginById(entityId);  
196 - boolean requiresRestart = false;  
197 - logger.info("[{}] Plugin configuration was updated from {} to {}.", entityId, oldPluginMd, pluginMd);  
198 - if (!oldPluginMd.getClazz().equals(pluginMd.getClazz())) {  
199 - logger.info("[{}] Plugin requires restart due to clazz change from {} to {}.",  
200 - entityId, oldPluginMd.getClazz(), pluginMd.getClazz());  
201 - requiresRestart = true;  
202 - } else if (!oldPluginMd.getConfiguration().equals(pluginMd.getConfiguration())) {  
203 - logger.info("[{}] Plugin requires restart due to configuration change from {} to {}.",  
204 - entityId, oldPluginMd.getConfiguration(), pluginMd.getConfiguration());  
205 - requiresRestart = true;  
206 - }  
207 - if (requiresRestart) {  
208 - this.state = ComponentLifecycleState.SUSPENDED;  
209 - if (pluginImpl != null) {  
210 - pluginImpl.stop(trustedCtx);  
211 - }  
212 - start();  
213 - }  
214 - }  
215 -  
216 - @Override  
217 - public void onStop(ActorContext context) {  
218 - onStop();  
219 - scheduleMsgWithDelay(context, new PluginTerminationMsg(entityId), systemContext.getPluginActorTerminationDelay());  
220 - }  
221 -  
222 - private void onStop() {  
223 - logger.info("[{}] Going to process onStop plugin.", entityId);  
224 - this.state = ComponentLifecycleState.SUSPENDED;  
225 - if (pluginImpl != null) {  
226 - pluginImpl.stop(trustedCtx);  
227 - }  
228 - }  
229 -  
230 - @Override  
231 - public void onActivate(ActorContext context) throws Exception {  
232 - logger.info("[{}] Going to process onActivate plugin.", entityId);  
233 - this.state = ComponentLifecycleState.ACTIVE;  
234 - if (pluginImpl != null) {  
235 - pluginImpl.resume(trustedCtx);  
236 - logger.info("[{}] Plugin resumed.", entityId);  
237 - } else {  
238 - start();  
239 - }  
240 - }  
241 -  
242 - @Override  
243 - public void onSuspend(ActorContext context) {  
244 - logger.info("[{}] Going to process onSuspend plugin.", entityId);  
245 - this.state = ComponentLifecycleState.SUSPENDED;  
246 - if (pluginImpl != null) {  
247 - pluginImpl.suspend(trustedCtx);  
248 - }  
249 - }  
250 -  
251 -}  
1 -/**  
2 - * Copyright © 2016-2018 The Thingsboard Authors  
3 - *  
4 - * Licensed under the Apache License, Version 2.0 (the "License");  
5 - * you may not use this file except in compliance with the License.  
6 - * You may obtain a copy of the License at  
7 - *  
8 - * http://www.apache.org/licenses/LICENSE-2.0  
9 - *  
10 - * Unless required by applicable law or agreed to in writing, software  
11 - * distributed under the License is distributed on an "AS IS" BASIS,  
12 - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  
13 - * See the License for the specific language governing permissions and  
14 - * limitations under the License.  
15 - */  
16 -package org.thingsboard.server.actors.plugin;  
17 -  
18 -import lombok.Data;  
19 -import lombok.Getter;  
20 -import lombok.ToString;  
21 -import org.thingsboard.server.extensions.api.plugins.PluginCallback;  
22 -  
23 -import java.util.Optional;  
24 -  
25 -/**  
26 - * @author Andrew Shvayka  
27 - */  
28 -@ToString  
29 -public final class PluginCallbackMessage<V> {  
30 - @Getter  
31 - private final PluginCallback<V> callback;  
32 - @Getter  
33 - private final boolean success;  
34 - @Getter  
35 - private final V v;  
36 - @Getter  
37 - private final Exception e;  
38 -  
39 - public static <V> PluginCallbackMessage<V> onSuccess(PluginCallback<V> callback, V data) {  
40 - return new PluginCallbackMessage<V>(true, callback, data, null);  
41 - }  
42 -  
43 - public static <V> PluginCallbackMessage<V> onError(PluginCallback<V> callback, Exception e) {  
44 - return new PluginCallbackMessage<V>(false, callback, null, e);  
45 - }  
46 -  
47 - private PluginCallbackMessage(boolean success, PluginCallback<V> callback, V v, Exception e) {  
48 - this.success = success;  
49 - this.callback = callback;  
50 - this.v = v;  
51 - this.e = e;  
52 - }  
53 -}  
1 -/**  
2 - * Copyright © 2016-2018 The Thingsboard Authors  
3 - *  
4 - * Licensed under the Apache License, Version 2.0 (the "License");  
5 - * you may not use this file except in compliance with the License.  
6 - * You may obtain a copy of the License at  
7 - *  
8 - * http://www.apache.org/licenses/LICENSE-2.0  
9 - *  
10 - * Unless required by applicable law or agreed to in writing, software  
11 - * distributed under the License is distributed on an "AS IS" BASIS,  
12 - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  
13 - * See the License for the specific language governing permissions and  
14 - * limitations under the License.  
15 - */  
16 -package org.thingsboard.server.actors.plugin;  
17 -  
18 -import akka.actor.ActorRef;  
19 -import com.google.common.base.Function;  
20 -import com.google.common.util.concurrent.FutureCallback;  
21 -import com.google.common.util.concurrent.Futures;  
22 -import com.google.common.util.concurrent.ListenableFuture;  
23 -import lombok.extern.slf4j.Slf4j;  
24 -import org.thingsboard.server.common.data.Customer;  
25 -import org.thingsboard.server.common.data.Device;  
26 -import org.thingsboard.server.common.data.EntityType;  
27 -import org.thingsboard.server.common.data.Tenant;  
28 -import org.thingsboard.server.common.data.asset.Asset;  
29 -import org.thingsboard.server.common.data.audit.ActionType;  
30 -import org.thingsboard.server.common.data.id.*;  
31 -import org.thingsboard.server.common.data.kv.AttributeKey;  
32 -import org.thingsboard.server.common.data.kv.AttributeKvEntry;  
33 -import org.thingsboard.server.common.data.kv.TsKvEntry;  
34 -import org.thingsboard.server.common.data.kv.TsKvQuery;  
35 -import org.thingsboard.server.common.data.page.TextPageLink;  
36 -import org.thingsboard.server.common.data.plugin.PluginMetaData;  
37 -import org.thingsboard.server.common.data.relation.EntityRelation;  
38 -import org.thingsboard.server.common.data.relation.RelationTypeGroup;  
39 -import org.thingsboard.server.common.data.rule.RuleMetaData;  
40 -import org.thingsboard.server.common.msg.cluster.ServerAddress;  
41 -import org.thingsboard.server.extensions.api.device.DeviceAttributesEventNotificationMsg;  
42 -import org.thingsboard.server.extensions.api.plugins.PluginApiCallSecurityContext;  
43 -import org.thingsboard.server.extensions.api.plugins.PluginCallback;  
44 -import org.thingsboard.server.extensions.api.plugins.PluginContext;  
45 -import org.thingsboard.server.extensions.api.plugins.msg.*;  
46 -import org.thingsboard.server.extensions.api.plugins.rpc.PluginRpcMsg;  
47 -import org.thingsboard.server.extensions.api.plugins.rpc.RpcMsg;  
48 -import org.thingsboard.server.extensions.api.plugins.ws.PluginWebsocketSessionRef;  
49 -import org.thingsboard.server.extensions.api.plugins.ws.msg.PluginWebsocketMsg;  
50 -  
51 -import javax.annotation.Nullable;  
52 -import java.io.IOException;  
53 -import java.util.*;  
54 -import java.util.concurrent.Executor;  
55 -import java.util.concurrent.Executors;  
56 -import java.util.stream.Collectors;  
57 -  
58 -@Slf4j  
59 -public final class PluginProcessingContext implements PluginContext {  
60 -  
61 - private static final Executor executor = Executors.newSingleThreadExecutor();  
62 - public static final String CUSTOMER_USER_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION = "Customer user is not allowed to perform this operation!";  
63 - public static final String SYSTEM_ADMINISTRATOR_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION = "System administrator is not allowed to perform this operation!";  
64 - public static final String DEVICE_WITH_REQUESTED_ID_NOT_FOUND = "Device with requested id wasn't found!";  
65 -  
66 - private final SharedPluginProcessingContext pluginCtx;  
67 - private final Optional<PluginApiCallSecurityContext> securityCtx;  
68 -  
69 - public PluginProcessingContext(SharedPluginProcessingContext pluginCtx, PluginApiCallSecurityContext securityCtx) {  
70 - super();  
71 - this.pluginCtx = pluginCtx;  
72 - this.securityCtx = Optional.ofNullable(securityCtx);  
73 - }  
74 -  
75 - public void persistError(String method, Exception e) {  
76 - pluginCtx.persistError(method, e);  
77 - }  
78 -  
79 - @Override  
80 - public void sendPluginRpcMsg(RpcMsg msg) {  
81 - this.pluginCtx.rpcService.tell(new PluginRpcMsg(pluginCtx.tenantId, pluginCtx.pluginId, msg));  
82 - }  
83 -  
84 - @Override  
85 - public void send(PluginWebsocketMsg<?> wsMsg) throws IOException {  
86 - pluginCtx.msgEndpoint.send(wsMsg);  
87 - }  
88 -  
89 - @Override  
90 - public void close(PluginWebsocketSessionRef sessionRef) throws IOException {  
91 - pluginCtx.msgEndpoint.close(sessionRef);  
92 - }  
93 -  
94 - @Override  
95 - public void saveAttributes(final TenantId tenantId, final EntityId entityId, final String scope, final List<AttributeKvEntry> attributes, final PluginCallback<Void> callback) {  
96 - validate(entityId, new ValidationCallback(callback, ctx -> {  
97 - ListenableFuture<List<Void>> futures = pluginCtx.attributesService.save(entityId, scope, attributes);  
98 - Futures.addCallback(futures, getListCallback(callback, v -> {  
99 - if (entityId.getEntityType() == EntityType.DEVICE) {  
100 - onDeviceAttributesChanged(tenantId, new DeviceId(entityId.getId()), scope, attributes);  
101 - }  
102 - return null;  
103 - }), executor);  
104 - }));  
105 - }  
106 -  
107 - @Override  
108 - public void removeAttributes(final TenantId tenantId, final EntityId entityId, final String scope, final List<String> keys, final PluginCallback<Void> callback) {  
109 - validate(entityId, new ValidationCallback(callback, ctx -> {  
110 - ListenableFuture<List<Void>> futures = pluginCtx.attributesService.removeAll(entityId, scope, keys);  
111 - Futures.addCallback(futures, getCallback(callback, v -> null), executor);  
112 - if (entityId.getEntityType() == EntityType.DEVICE) {  
113 - onDeviceAttributesDeleted(tenantId, new DeviceId(entityId.getId()), keys.stream().map(key -> new AttributeKey(scope, key)).collect(Collectors.toSet()));  
114 - }  
115 - }));  
116 - }  
117 -  
118 - @Override  
119 - public void loadAttribute(EntityId entityId, String attributeType, String attributeKey, final PluginCallback<Optional<AttributeKvEntry>> callback) {  
120 - validate(entityId, new ValidationCallback(callback, ctx -> {  
121 - ListenableFuture<Optional<AttributeKvEntry>> future = pluginCtx.attributesService.find(entityId, attributeType, attributeKey);  
122 - Futures.addCallback(future, getCallback(callback, v -> v), executor);  
123 - }));  
124 - }  
125 -  
126 - @Override  
127 - public void loadAttributes(EntityId entityId, String attributeType, Collection<String> attributeKeys, final PluginCallback<List<AttributeKvEntry>> callback) {  
128 - validate(entityId, new ValidationCallback(callback, ctx -> {  
129 - ListenableFuture<List<AttributeKvEntry>> future = pluginCtx.attributesService.find(entityId, attributeType, attributeKeys);  
130 - Futures.addCallback(future, getCallback(callback, v -> v), executor);  
131 - }));  
132 - }  
133 -  
134 - @Override  
135 - public void loadAttributes(EntityId entityId, String attributeType, PluginCallback<List<AttributeKvEntry>> callback) {  
136 - validate(entityId, new ValidationCallback(callback, ctx -> {  
137 - ListenableFuture<List<AttributeKvEntry>> future = pluginCtx.attributesService.findAll(entityId, attributeType);  
138 - Futures.addCallback(future, getCallback(callback, v -> v), executor);  
139 - }));  
140 - }  
141 -  
142 - @Override  
143 - public void loadAttributes(final EntityId entityId, final Collection<String> attributeTypes, final PluginCallback<List<AttributeKvEntry>> callback) {  
144 - validate(entityId, new ValidationCallback(callback, ctx -> {  
145 - List<ListenableFuture<List<AttributeKvEntry>>> futures = new ArrayList<>();  
146 - attributeTypes.forEach(attributeType -> futures.add(pluginCtx.attributesService.findAll(entityId, attributeType)));  
147 - convertFuturesAndAddCallback(callback, futures);  
148 - }));  
149 - }  
150 -  
151 - @Override  
152 - public void loadAttributes(final EntityId entityId, final Collection<String> attributeTypes, final Collection<String> attributeKeys, final PluginCallback<List<AttributeKvEntry>> callback) {  
153 - validate(entityId, new ValidationCallback(callback, ctx -> {  
154 - List<ListenableFuture<List<AttributeKvEntry>>> futures = new ArrayList<>();  
155 - attributeTypes.forEach(attributeType -> futures.add(pluginCtx.attributesService.find(entityId, attributeType, attributeKeys)));  
156 - convertFuturesAndAddCallback(callback, futures);  
157 - }));  
158 - }  
159 -  
160 - @Override  
161 - public void saveTsData(final EntityId entityId, final TsKvEntry entry, final PluginCallback<Void> callback) {  
162 - validate(entityId, new ValidationCallback(callback, ctx -> {  
163 - ListenableFuture<List<Void>> rsListFuture = pluginCtx.tsService.save(entityId, entry);  
164 - Futures.addCallback(rsListFuture, getListCallback(callback, v -> null), executor);  
165 - }));  
166 - }  
167 -  
168 - @Override  
169 - public void saveTsData(final EntityId entityId, final List<TsKvEntry> entries, final PluginCallback<Void> callback) {  
170 - saveTsData(entityId, entries, 0L, callback);  
171 - }  
172 -  
173 - @Override  
174 - public void saveTsData(final EntityId entityId, final List<TsKvEntry> entries, long ttl, final PluginCallback<Void> callback) {  
175 - validate(entityId, new ValidationCallback(callback, ctx -> {  
176 - ListenableFuture<List<Void>> rsListFuture = pluginCtx.tsService.save(entityId, entries, ttl);  
177 - Futures.addCallback(rsListFuture, getListCallback(callback, v -> null), executor);  
178 - }));  
179 - }  
180 -  
181 -  
182 - @Override  
183 - public void loadTimeseries(final EntityId entityId, final List<TsKvQuery> queries, final PluginCallback<List<TsKvEntry>> callback) {  
184 - validate(entityId, new ValidationCallback(callback, ctx -> {  
185 - ListenableFuture<List<TsKvEntry>> future = pluginCtx.tsService.findAll(entityId, queries);  
186 - Futures.addCallback(future, getCallback(callback, v -> v), executor);  
187 - }));  
188 - }  
189 -  
190 - @Override  
191 - public void loadLatestTimeseries(final EntityId entityId, final PluginCallback<List<TsKvEntry>> callback) {  
192 - validate(entityId, new ValidationCallback(callback, ctx -> {  
193 - ListenableFuture<List<TsKvEntry>> future = pluginCtx.tsService.findAllLatest(entityId);  
194 - Futures.addCallback(future, getCallback(callback, v -> v), executor);  
195 - }));  
196 - }  
197 -  
198 - @Override  
199 - public void logAttributesUpdated(PluginApiCallSecurityContext ctx, EntityId entityId, String attributeType,  
200 - List<AttributeKvEntry> attributes, Exception e) {  
201 - pluginCtx.auditLogService.logEntityAction(  
202 - ctx.getTenantId(),  
203 - ctx.getCustomerId(),  
204 - ctx.getUserId(),  
205 - ctx.getUserName(),  
206 - (UUIDBased & EntityId)entityId,  
207 - null,  
208 - ActionType.ATTRIBUTES_UPDATED,  
209 - e,  
210 - attributeType,  
211 - attributes);  
212 - }  
213 -  
214 - @Override  
215 - public void logAttributesDeleted(PluginApiCallSecurityContext ctx, EntityId entityId, String attributeType, List<String> keys, Exception e) {  
216 - pluginCtx.auditLogService.logEntityAction(  
217 - ctx.getTenantId(),  
218 - ctx.getCustomerId(),  
219 - ctx.getUserId(),  
220 - ctx.getUserName(),  
221 - (UUIDBased & EntityId)entityId,  
222 - null,  
223 - ActionType.ATTRIBUTES_DELETED,  
224 - e,  
225 - attributeType,  
226 - keys);  
227 - }  
228 -  
229 - @Override  
230 - public void logAttributesRead(PluginApiCallSecurityContext ctx, EntityId entityId, String attributeType, List<String> keys, Exception e) {  
231 - pluginCtx.auditLogService.logEntityAction(  
232 - ctx.getTenantId(),  
233 - ctx.getCustomerId(),  
234 - ctx.getUserId(),  
235 - ctx.getUserName(),  
236 - (UUIDBased & EntityId)entityId,  
237 - null,  
238 - ActionType.ATTRIBUTES_READ,  
239 - e,  
240 - attributeType,  
241 - keys);  
242 - }  
243 -  
244 - @Override  
245 - public void loadLatestTimeseries(final EntityId entityId, final Collection<String> keys, final PluginCallback<List<TsKvEntry>> callback) {  
246 - validate(entityId, new ValidationCallback(callback, ctx -> {  
247 - ListenableFuture<List<TsKvEntry>> rsListFuture = pluginCtx.tsService.findLatest(entityId, keys);  
248 - Futures.addCallback(rsListFuture, getCallback(callback, v -> v), executor);  
249 - }));  
250 - }  
251 -  
252 - @Override  
253 - public void reply(PluginToRuleMsg<?> msg) {  
254 - pluginCtx.parentActor.tell(msg, ActorRef.noSender());  
255 - }  
256 -  
257 - @Override  
258 - public PluginId getPluginId() {  
259 - return pluginCtx.pluginId;  
260 - }  
261 -  
262 - @Override  
263 - public Optional<PluginApiCallSecurityContext> getSecurityCtx() {  
264 - return securityCtx;  
265 - }  
266 -  
267 - private void onDeviceAttributesDeleted(TenantId tenantId, DeviceId deviceId, Set<AttributeKey> keys) {  
268 - pluginCtx.toDeviceActor(DeviceAttributesEventNotificationMsg.onDelete(tenantId, deviceId, keys));  
269 - }  
270 -  
271 - private void onDeviceAttributesChanged(TenantId tenantId, DeviceId deviceId, String scope, List<AttributeKvEntry> values) {  
272 - pluginCtx.toDeviceActor(DeviceAttributesEventNotificationMsg.onUpdate(tenantId, deviceId, scope, values));  
273 - }  
274 -  
275 - private <T, R> FutureCallback<List<T>> getListCallback(final PluginCallback<R> callback, Function<List<T>, R> transformer) {  
276 - return new FutureCallback<List<T>>() {  
277 - @Override  
278 - public void onSuccess(@Nullable List<T> result) {  
279 - pluginCtx.self().tell(PluginCallbackMessage.onSuccess(callback, transformer.apply(result)), ActorRef.noSender());  
280 - }  
281 -  
282 - @Override  
283 - public void onFailure(Throwable t) {  
284 - if (t instanceof Exception) {  
285 - pluginCtx.self().tell(PluginCallbackMessage.onError(callback, (Exception) t), ActorRef.noSender());  
286 - } else {  
287 - log.error("Critical error: {}", t.getMessage(), t);  
288 - }  
289 - }  
290 - };  
291 - }  
292 -  
293 - private <T, R> FutureCallback<R> getCallback(final PluginCallback<T> callback, Function<R, T> transformer) {  
294 - return new FutureCallback<R>() {  
295 - @Override  
296 - public void onSuccess(@Nullable R result) {  
297 - try {  
298 - pluginCtx.self().tell(PluginCallbackMessage.onSuccess(callback, transformer.apply(result)), ActorRef.noSender());  
299 - } catch (Exception e) {  
300 - pluginCtx.self().tell(PluginCallbackMessage.onError(callback, e), ActorRef.noSender());  
301 - }  
302 - }  
303 -  
304 - @Override  
305 - public void onFailure(Throwable t) {  
306 - if (t instanceof Exception) {  
307 - pluginCtx.self().tell(PluginCallbackMessage.onError(callback, (Exception) t), ActorRef.noSender());  
308 - } else {  
309 - log.error("Critical error: {}", t.getMessage(), t);  
310 - }  
311 - }  
312 - };  
313 - }  
314 -  
315 - @Override  
316 - public void checkAccess(DeviceId deviceId, PluginCallback<Void> callback) {  
317 - validate(deviceId, new ValidationCallback(callback, ctx -> callback.onSuccess(ctx, null)));  
318 - }  
319 -  
320 - private void validate(EntityId entityId, ValidationCallback callback) {  
321 - if (securityCtx.isPresent()) {  
322 - final PluginApiCallSecurityContext ctx = securityCtx.get();  
323 - switch (entityId.getEntityType()) {  
324 - case DEVICE:  
325 - validateDevice(ctx, entityId, callback);  
326 - return;  
327 - case ASSET:  
328 - validateAsset(ctx, entityId, callback);  
329 - return;  
330 - case RULE:  
331 - validateRule(ctx, entityId, callback);  
332 - return;  
333 - case PLUGIN:  
334 - validatePlugin(ctx, entityId, callback);  
335 - return;  
336 - case CUSTOMER:  
337 - validateCustomer(ctx, entityId, callback);  
338 - return;  
339 - case TENANT:  
340 - validateTenant(ctx, entityId, callback);  
341 - return;  
342 - default:  
343 - //TODO: add support of other entities  
344 - throw new IllegalStateException("Not Implemented!");  
345 - }  
346 - } else {  
347 - callback.onSuccess(this, ValidationResult.ok());  
348 - }  
349 - }  
350 -  
351 - private void validateDevice(final PluginApiCallSecurityContext ctx, EntityId entityId, ValidationCallback callback) {  
352 - if (ctx.isSystemAdmin()) {  
353 - callback.onSuccess(this, ValidationResult.accessDenied(SYSTEM_ADMINISTRATOR_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION));  
354 - } else {  
355 - ListenableFuture<Device> deviceFuture = pluginCtx.deviceService.findDeviceByIdAsync(new DeviceId(entityId.getId()));  
356 - Futures.addCallback(deviceFuture, getCallback(callback, device -> {  
357 - if (device == null) {  
358 - return ValidationResult.entityNotFound(DEVICE_WITH_REQUESTED_ID_NOT_FOUND);  
359 - } else {  
360 - if (!device.getTenantId().equals(ctx.getTenantId())) {  
361 - return ValidationResult.accessDenied("Device doesn't belong to the current Tenant!");  
362 - } else if (ctx.isCustomerUser() && !device.getCustomerId().equals(ctx.getCustomerId())) {  
363 - return ValidationResult.accessDenied("Device doesn't belong to the current Customer!");  
364 - } else {  
365 - return ValidationResult.ok();  
366 - }  
367 - }  
368 - }));  
369 - }  
370 - }  
371 -  
372 - private void validateAsset(final PluginApiCallSecurityContext ctx, EntityId entityId, ValidationCallback callback) {  
373 - if (ctx.isSystemAdmin()) {  
374 - callback.onSuccess(this, ValidationResult.accessDenied(SYSTEM_ADMINISTRATOR_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION));  
375 - } else {  
376 - ListenableFuture<Asset> assetFuture = pluginCtx.assetService.findAssetByIdAsync(new AssetId(entityId.getId()));  
377 - Futures.addCallback(assetFuture, getCallback(callback, asset -> {  
378 - if (asset == null) {  
379 - return ValidationResult.entityNotFound("Asset with requested id wasn't found!");  
380 - } else {  
381 - if (!asset.getTenantId().equals(ctx.getTenantId())) {  
382 - return ValidationResult.accessDenied("Asset doesn't belong to the current Tenant!");  
383 - } else if (ctx.isCustomerUser() && !asset.getCustomerId().equals(ctx.getCustomerId())) {  
384 - return ValidationResult.accessDenied("Asset doesn't belong to the current Customer!");  
385 - } else {  
386 - return ValidationResult.ok();  
387 - }  
388 - }  
389 - }));  
390 - }  
391 - }  
392 -  
393 - private void validateRule(final PluginApiCallSecurityContext ctx, EntityId entityId, ValidationCallback callback) {  
394 - if (ctx.isCustomerUser()) {  
395 - callback.onSuccess(this, ValidationResult.accessDenied(CUSTOMER_USER_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION));  
396 - } else {  
397 - ListenableFuture<RuleMetaData> ruleFuture = pluginCtx.ruleService.findRuleByIdAsync(new RuleId(entityId.getId()));  
398 - Futures.addCallback(ruleFuture, getCallback(callback, rule -> {  
399 - if (rule == null) {  
400 - return ValidationResult.entityNotFound("Rule with requested id wasn't found!");  
401 - } else {  
402 - if (ctx.isTenantAdmin() && !rule.getTenantId().equals(ctx.getTenantId())) {  
403 - return ValidationResult.accessDenied("Rule doesn't belong to the current Tenant!");  
404 - } else if (ctx.isSystemAdmin() && !rule.getTenantId().isNullUid()) {  
405 - return ValidationResult.accessDenied("Rule is not in system scope!");  
406 - } else {  
407 - return ValidationResult.ok();  
408 - }  
409 - }  
410 - }));  
411 - }  
412 - }  
413 -  
414 - private void validatePlugin(final PluginApiCallSecurityContext ctx, EntityId entityId, ValidationCallback callback) {  
415 - if (ctx.isCustomerUser()) {  
416 - callback.onSuccess(this, ValidationResult.accessDenied(CUSTOMER_USER_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION));  
417 - } else {  
418 - ListenableFuture<PluginMetaData> pluginFuture = pluginCtx.pluginService.findPluginByIdAsync(new PluginId(entityId.getId()));  
419 - Futures.addCallback(pluginFuture, getCallback(callback, plugin -> {  
420 - if (plugin == null) {  
421 - return ValidationResult.entityNotFound("Plugin with requested id wasn't found!");  
422 - } else {  
423 - if (ctx.isTenantAdmin() && !plugin.getTenantId().equals(ctx.getTenantId())) {  
424 - return ValidationResult.accessDenied("Plugin doesn't belong to the current Tenant!");  
425 - } else if (ctx.isSystemAdmin() && !plugin.getTenantId().isNullUid()) {  
426 - return ValidationResult.accessDenied("Plugin is not in system scope!");  
427 - } else {  
428 - return ValidationResult.ok();  
429 - }  
430 - }  
431 - }));  
432 - }  
433 - }  
434 -  
435 - private void validateCustomer(final PluginApiCallSecurityContext ctx, EntityId entityId, ValidationCallback callback) {  
436 - if (ctx.isSystemAdmin()) {  
437 - callback.onSuccess(this, ValidationResult.accessDenied(SYSTEM_ADMINISTRATOR_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION));  
438 - } else {  
439 - ListenableFuture<Customer> customerFuture = pluginCtx.customerService.findCustomerByIdAsync(new CustomerId(entityId.getId()));  
440 - Futures.addCallback(customerFuture, getCallback(callback, customer -> {  
441 - if (customer == null) {  
442 - return ValidationResult.entityNotFound("Customer with requested id wasn't found!");  
443 - } else {  
444 - if (!customer.getTenantId().equals(ctx.getTenantId())) {  
445 - return ValidationResult.accessDenied("Customer doesn't belong to the current Tenant!");  
446 - } else if (ctx.isCustomerUser() && !customer.getId().equals(ctx.getCustomerId())) {  
447 - return ValidationResult.accessDenied("Customer doesn't relate to the currently authorized customer user!");  
448 - } else {  
449 - return ValidationResult.ok();  
450 - }  
451 - }  
452 - }));  
453 - }  
454 - }  
455 -  
456 - private void validateTenant(final PluginApiCallSecurityContext ctx, EntityId entityId, ValidationCallback callback) {  
457 - if (ctx.isCustomerUser()) {  
458 - callback.onSuccess(this, ValidationResult.accessDenied(CUSTOMER_USER_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION));  
459 - } else if (ctx.isSystemAdmin()) {  
460 - callback.onSuccess(this, ValidationResult.ok());  
461 - } else {  
462 - ListenableFuture<Tenant> tenantFuture = pluginCtx.tenantService.findTenantByIdAsync(new TenantId(entityId.getId()));  
463 - Futures.addCallback(tenantFuture, getCallback(callback, tenant -> {  
464 - if (tenant == null) {  
465 - return ValidationResult.entityNotFound("Tenant with requested id wasn't found!");  
466 - } else if (!tenant.getId().equals(ctx.getTenantId())) {  
467 - return ValidationResult.accessDenied("Tenant doesn't relate to the currently authorized user!");  
468 - } else {  
469 - return ValidationResult.ok();  
470 - }  
471 - }));  
472 - }  
473 - }  
474 -  
475 - @Override  
476 - public ListenableFuture<List<EntityRelation>> findByFromAndType(EntityId from, String relationType) {  
477 - return this.pluginCtx.relationService.findByFromAndTypeAsync(from, relationType, RelationTypeGroup.COMMON);  
478 - }  
479 -  
480 - @Override  
481 - public ListenableFuture<List<EntityRelation>> findByToAndType(EntityId from, String relationType) {  
482 - return this.pluginCtx.relationService.findByToAndTypeAsync(from, relationType, RelationTypeGroup.COMMON);  
483 - }  
484 -  
485 - @Override  
486 - public Optional<ServerAddress> resolve(EntityId entityId) {  
487 - return pluginCtx.routingService.resolveById(entityId);  
488 - }  
489 -  
490 - @Override  
491 - public void getDevice(DeviceId deviceId, PluginCallback<Device> callback) {  
492 - ListenableFuture<Device> deviceFuture = pluginCtx.deviceService.findDeviceByIdAsync(deviceId);  
493 - Futures.addCallback(deviceFuture, getCallback(callback, v -> v));  
494 - }  
495 -  
496 - @Override  
497 - public void getCustomerDevices(TenantId tenantId, CustomerId customerId, int limit, PluginCallback<List<Device>> callback) {  
498 - //TODO: add caching here with async api.  
499 - List<Device> devices = pluginCtx.deviceService.findDevicesByTenantIdAndCustomerId(tenantId, customerId, new TextPageLink(limit)).getData();  
500 - pluginCtx.self().tell(PluginCallbackMessage.onSuccess(callback, devices), ActorRef.noSender());  
501 - }  
502 -  
503 - @Override  
504 - public void sendRpcRequest(ToDeviceRpcRequest msg) {  
505 - pluginCtx.sendRpcRequest(msg);  
506 - }  
507 -  
508 - @Override  
509 - public void logRpcRequest(PluginApiCallSecurityContext ctx, DeviceId deviceId, ToDeviceRpcRequestBody body, boolean oneWay, Optional<RpcError> rpcError, Exception e) {  
510 - String rpcErrorStr = "";  
511 - if (rpcError.isPresent()) {  
512 - rpcErrorStr = "RPC Error: " + rpcError.get().name();  
513 - }  
514 - String method = body.getMethod();  
515 - String params = body.getParams();  
516 - pluginCtx.auditLogService.logEntityAction(  
517 - ctx.getTenantId(),  
518 - ctx.getCustomerId(),  
519 - ctx.getUserId(),  
520 - ctx.getUserName(),  
521 - deviceId,  
522 - null,  
523 - ActionType.RPC_CALL,  
524 - e,  
525 - rpcErrorStr,  
526 - new Boolean(oneWay),  
527 - method,  
528 - params);  
529 - }  
530 -  
531 - @Override  
532 - public void scheduleTimeoutMsg(TimeoutMsg msg) {  
533 - pluginCtx.scheduleTimeoutMsg(msg);  
534 - }  
535 -  
536 -  
537 - private void convertFuturesAndAddCallback(PluginCallback<List<AttributeKvEntry>> callback, List<ListenableFuture<List<AttributeKvEntry>>> futures) {  
538 - ListenableFuture<List<AttributeKvEntry>> future = Futures.transform(Futures.successfulAsList(futures),  
539 - (Function<? super List<List<AttributeKvEntry>>, ? extends List<AttributeKvEntry>>) input -> {  
540 - List<AttributeKvEntry> result = new ArrayList<>();  
541 - input.forEach(r -> result.addAll(r));  
542 - return result;  
543 - }, executor);  
544 - Futures.addCallback(future, getCallback(callback, v -> v), executor);  
545 - }  
546 -}  
1 -/**  
2 - * Copyright © 2016-2018 The Thingsboard Authors  
3 - *  
4 - * Licensed under the Apache License, Version 2.0 (the "License");  
5 - * you may not use this file except in compliance with the License.  
6 - * You may obtain a copy of the License at  
7 - *  
8 - * http://www.apache.org/licenses/LICENSE-2.0  
9 - *  
10 - * Unless required by applicable law or agreed to in writing, software  
11 - * distributed under the License is distributed on an "AS IS" BASIS,  
12 - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  
13 - * See the License for the specific language governing permissions and  
14 - * limitations under the License.  
15 - */  
16 -package org.thingsboard.server.actors.plugin;  
17 -  
18 -import org.thingsboard.server.common.data.id.PluginId;  
19 -import org.thingsboard.server.common.data.id.RuleId;  
20 -import org.thingsboard.server.common.data.id.TenantId;  
21 -import org.thingsboard.server.common.msg.aware.RuleAwareMsg;  
22 -import org.thingsboard.server.extensions.api.plugins.msg.RuleToPluginMsg;  
23 -import org.thingsboard.server.extensions.api.plugins.msg.ToPluginActorMsg;  
24 -  
25 -public class RuleToPluginMsgWrapper implements ToPluginActorMsg, RuleAwareMsg {  
26 -  
27 - private final TenantId pluginTenantId;  
28 - private final PluginId pluginId;  
29 - private final TenantId ruleTenantId;  
30 - private final RuleId ruleId;  
31 - private final RuleToPluginMsg<?> msg;  
32 -  
33 - public RuleToPluginMsgWrapper(TenantId pluginTenantId, PluginId pluginId, TenantId ruleTenantId, RuleId ruleId, RuleToPluginMsg<?> msg) {  
34 - super();  
35 - this.pluginTenantId = pluginTenantId;  
36 - this.pluginId = pluginId;  
37 - this.ruleTenantId = ruleTenantId;  
38 - this.ruleId = ruleId;  
39 - this.msg = msg;  
40 - }  
41 -  
42 - @Override  
43 - public TenantId getPluginTenantId() {  
44 - return pluginTenantId;  
45 - }  
46 -  
47 - @Override  
48 - public PluginId getPluginId() {  
49 - return pluginId;  
50 - }  
51 -  
52 - public TenantId getRuleTenantId() {  
53 - return ruleTenantId;  
54 - }  
55 -  
56 - @Override  
57 - public RuleId getRuleId() {  
58 - return ruleId;  
59 - }  
60 -  
61 -  
62 - public RuleToPluginMsg getMsg() {  
63 - return msg;  
64 - }  
65 -  
66 -}  
1 -/**  
2 - * Copyright © 2016-2018 The Thingsboard Authors  
3 - *  
4 - * Licensed under the Apache License, Version 2.0 (the "License");  
5 - * you may not use this file except in compliance with the License.  
6 - * You may obtain a copy of the License at  
7 - *  
8 - * http://www.apache.org/licenses/LICENSE-2.0  
9 - *  
10 - * Unless required by applicable law or agreed to in writing, software  
11 - * distributed under the License is distributed on an "AS IS" BASIS,  
12 - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  
13 - * See the License for the specific language governing permissions and  
14 - * limitations under the License.  
15 - */  
16 -package org.thingsboard.server.actors.plugin;  
17 -  
18 -import akka.actor.ActorRef;  
19 -import lombok.extern.slf4j.Slf4j;  
20 -import org.thingsboard.server.actors.ActorSystemContext;  
21 -import org.thingsboard.server.common.data.Device;  
22 -import org.thingsboard.server.common.data.id.DeviceId;  
23 -import org.thingsboard.server.common.data.id.EntityId;  
24 -import org.thingsboard.server.common.data.id.TenantId;  
25 -import org.thingsboard.server.common.msg.cluster.ServerAddress;  
26 -import org.thingsboard.server.controller.plugin.PluginWebSocketMsgEndpoint;  
27 -import org.thingsboard.server.common.data.id.PluginId;  
28 -import org.thingsboard.server.dao.asset.AssetService;  
29 -import org.thingsboard.server.dao.attributes.AttributesService;  
30 -import org.thingsboard.server.dao.audit.AuditLogService;  
31 -import org.thingsboard.server.dao.customer.CustomerService;  
32 -import org.thingsboard.server.dao.device.DeviceService;  
33 -import org.thingsboard.server.dao.plugin.PluginService;  
34 -import org.thingsboard.server.dao.relation.RelationService;  
35 -import org.thingsboard.server.dao.rule.RuleService;  
36 -import org.thingsboard.server.dao.tenant.TenantService;  
37 -import org.thingsboard.server.dao.timeseries.TimeseriesService;  
38 -import org.thingsboard.server.extensions.api.device.DeviceAttributesEventNotificationMsg;  
39 -import org.thingsboard.server.extensions.api.plugins.msg.TimeoutMsg;  
40 -import org.thingsboard.server.extensions.api.plugins.msg.ToDeviceRpcRequest;  
41 -import org.thingsboard.server.extensions.api.plugins.msg.ToDeviceRpcRequestPluginMsg;  
42 -import org.thingsboard.server.service.cluster.routing.ClusterRoutingService;  
43 -import org.thingsboard.server.service.cluster.rpc.ClusterRpcService;  
44 -import scala.concurrent.duration.Duration;  
45 -  
46 -import java.util.Optional;  
47 -import java.util.concurrent.TimeUnit;  
48 -import java.util.function.BiConsumer;  
49 -  
50 -@Slf4j  
51 -public final class SharedPluginProcessingContext {  
52 - final ActorRef parentActor;  
53 - final ActorRef currentActor;  
54 - final ActorSystemContext systemContext;  
55 - final PluginWebSocketMsgEndpoint msgEndpoint;  
56 - final AssetService assetService;  
57 - final DeviceService deviceService;  
58 - final RuleService ruleService;  
59 - final PluginService pluginService;  
60 - final CustomerService customerService;  
61 - final TenantService tenantService;  
62 - final TimeseriesService tsService;  
63 - final AttributesService attributesService;  
64 - final ClusterRpcService rpcService;  
65 - final ClusterRoutingService routingService;  
66 - final RelationService relationService;  
67 - final AuditLogService auditLogService;  
68 - final PluginId pluginId;  
69 - final TenantId tenantId;  
70 -  
71 - public SharedPluginProcessingContext(ActorSystemContext sysContext, TenantId tenantId, PluginId pluginId,  
72 - ActorRef parentActor, ActorRef self) {  
73 - super();  
74 - this.tenantId = tenantId;  
75 - this.pluginId = pluginId;  
76 - this.parentActor = parentActor;  
77 - this.currentActor = self;  
78 - this.systemContext = sysContext;  
79 - this.msgEndpoint = sysContext.getWsMsgEndpoint();  
80 - this.tsService = sysContext.getTsService();  
81 - this.attributesService = sysContext.getAttributesService();  
82 - this.assetService = sysContext.getAssetService();  
83 - this.deviceService = sysContext.getDeviceService();  
84 - this.rpcService = sysContext.getRpcService();  
85 - this.routingService = sysContext.getRoutingService();  
86 - this.ruleService = sysContext.getRuleService();  
87 - this.pluginService = sysContext.getPluginService();  
88 - this.customerService = sysContext.getCustomerService();  
89 - this.tenantService = sysContext.getTenantService();  
90 - this.relationService = sysContext.getRelationService();  
91 - this.auditLogService = sysContext.getAuditLogService();  
92 - }  
93 -  
94 - public PluginId getPluginId() {  
95 - return pluginId;  
96 - }  
97 -  
98 - public TenantId getPluginTenantId() {  
99 - return tenantId;  
100 - }  
101 -  
102 - public void toDeviceActor(DeviceAttributesEventNotificationMsg msg) {  
103 - forward(msg.getDeviceId(), msg, rpcService::tell);  
104 - }  
105 -  
106 - public void sendRpcRequest(ToDeviceRpcRequest msg) {  
107 - log.trace("[{}] Forwarding msg {} to device actor!", pluginId, msg);  
108 - ToDeviceRpcRequestPluginMsg rpcMsg = new ToDeviceRpcRequestPluginMsg(pluginId, tenantId, msg);  
109 - forward(msg.getDeviceId(), rpcMsg, rpcService::tell);  
110 - }  
111 -  
112 - private <T> void forward(DeviceId deviceId, T msg, BiConsumer<ServerAddress, T> rpcFunction) {  
113 - Optional<ServerAddress> instance = routingService.resolveById(deviceId);  
114 - if (instance.isPresent()) {  
115 - log.trace("[{}] Forwarding msg {} to remote device actor!", pluginId, msg);  
116 - rpcFunction.accept(instance.get(), msg);  
117 - } else {  
118 - log.trace("[{}] Forwarding msg {} to local device actor!", pluginId, msg);  
119 - parentActor.tell(msg, ActorRef.noSender());  
120 - }  
121 - }  
122 -  
123 - public void scheduleTimeoutMsg(TimeoutMsg msg) {  
124 - log.debug("Scheduling msg {} with delay {} ms", msg, msg.getTimeout());  
125 - systemContext.getScheduler().scheduleOnce(  
126 - Duration.create(msg.getTimeout(), TimeUnit.MILLISECONDS),  
127 - currentActor,  
128 - msg,  
129 - systemContext.getActorSystem().dispatcher(),  
130 - ActorRef.noSender());  
131 -  
132 - }  
133 -  
134 - public void persistError(String method, Exception e) {  
135 - systemContext.persistError(tenantId, pluginId, method, e);  
136 - }  
137 -  
138 - public ActorRef self() {  
139 - return currentActor;  
140 - }  
141 -}  
@@ -17,43 +17,23 @@ package org.thingsboard.server.actors.rpc; @@ -17,43 +17,23 @@ package org.thingsboard.server.actors.rpc;
17 17
18 import akka.actor.ActorRef; 18 import akka.actor.ActorRef;
19 import lombok.extern.slf4j.Slf4j; 19 import lombok.extern.slf4j.Slf4j;
20 -import org.springframework.util.SerializationUtils;  
21 -import org.springframework.util.StringUtils;  
22 -import org.thingsboard.server.actors.ActorSystemContext;  
23 import org.thingsboard.server.actors.service.ActorService; 20 import org.thingsboard.server.actors.service.ActorService;
24 -import org.thingsboard.server.common.data.id.DeviceId;  
25 -import org.thingsboard.server.common.data.id.PluginId;  
26 -import org.thingsboard.server.common.data.id.TenantId;  
27 -import org.thingsboard.server.common.msg.cluster.ServerAddress;  
28 -import org.thingsboard.server.common.msg.cluster.ToAllNodesMsg;  
29 -import org.thingsboard.server.common.msg.core.ToDeviceSessionActorMsg;  
30 -import org.thingsboard.server.common.msg.device.ToDeviceActorMsg;  
31 -import org.thingsboard.server.extensions.api.device.ToDeviceActorNotificationMsg;  
32 -import org.thingsboard.server.extensions.api.plugins.msg.*;  
33 -import org.thingsboard.server.extensions.api.plugins.rpc.PluginRpcMsg;  
34 -import org.thingsboard.server.extensions.api.plugins.rpc.RpcMsg;  
35 import org.thingsboard.server.gen.cluster.ClusterAPIProtos; 21 import org.thingsboard.server.gen.cluster.ClusterAPIProtos;
36 import org.thingsboard.server.service.cluster.rpc.GrpcSession; 22 import org.thingsboard.server.service.cluster.rpc.GrpcSession;
37 import org.thingsboard.server.service.cluster.rpc.GrpcSessionListener; 23 import org.thingsboard.server.service.cluster.rpc.GrpcSessionListener;
38 24
39 -import java.io.Serializable;  
40 -import java.util.UUID;  
41 -  
42 /** 25 /**
43 * @author Andrew Shvayka 26 * @author Andrew Shvayka
44 */ 27 */
45 @Slf4j 28 @Slf4j
46 public class BasicRpcSessionListener implements GrpcSessionListener { 29 public class BasicRpcSessionListener implements GrpcSessionListener {
47 30
48 - public static final String SESSION_RECEIVED_SESSION_ACTOR_MSG = "{} session [{}] received session actor msg {}";  
49 - private final ActorSystemContext context;  
50 private final ActorService service; 31 private final ActorService service;
51 private final ActorRef manager; 32 private final ActorRef manager;
52 private final ActorRef self; 33 private final ActorRef self;
53 34
54 - public BasicRpcSessionListener(ActorSystemContext context, ActorRef manager, ActorRef self) {  
55 - this.context = context;  
56 - this.service = context.getActorService(); 35 + public BasicRpcSessionListener(ActorService service, ActorRef manager, ActorRef self) {
  36 + this.service = service;
57 this.manager = manager; 37 this.manager = manager;
58 this.self = self; 38 this.self = self;
59 } 39 }
@@ -73,47 +53,11 @@ public class BasicRpcSessionListener implements GrpcSessionListener { @@ -73,47 +53,11 @@ public class BasicRpcSessionListener implements GrpcSessionListener {
73 } 53 }
74 54
75 @Override 55 @Override
76 - public void onToPluginRpcMsg(GrpcSession session, ClusterAPIProtos.ToPluginRpcMessage msg) {  
77 - if (log.isTraceEnabled()) {  
78 - log.trace("{} session [{}] received plugin msg {}", getType(session), session.getRemoteServer(), msg);  
79 - }  
80 - service.onMsg(convert(session.getRemoteServer(), msg));  
81 - }  
82 -  
83 - @Override  
84 - public void onToDeviceActorRpcMsg(GrpcSession session, ClusterAPIProtos.ToDeviceActorRpcMessage msg) {  
85 - log.trace("{} session [{}] received device actor msg {}", getType(session), session.getRemoteServer(), msg);  
86 - service.onMsg((ToDeviceActorMsg) deserialize(msg.getData().toByteArray()));  
87 - }  
88 -  
89 - @Override  
90 - public void onToDeviceActorNotificationRpcMsg(GrpcSession session, ClusterAPIProtos.ToDeviceActorNotificationRpcMessage msg) {  
91 - log.trace("{} session [{}] received device actor notification msg {}", getType(session), session.getRemoteServer(), msg);  
92 - service.onMsg((ToDeviceActorNotificationMsg) deserialize(msg.getData().toByteArray()));  
93 - }  
94 -  
95 - @Override  
96 - public void onToDeviceSessionActorRpcMsg(GrpcSession session, ClusterAPIProtos.ToDeviceSessionActorRpcMessage msg) {  
97 - log.trace(SESSION_RECEIVED_SESSION_ACTOR_MSG, getType(session), session.getRemoteServer(), msg);  
98 - service.onMsg((ToDeviceSessionActorMsg) deserialize(msg.getData().toByteArray()));  
99 - }  
100 -  
101 - @Override  
102 - public void onToDeviceRpcRequestRpcMsg(GrpcSession session, ClusterAPIProtos.ToDeviceRpcRequestRpcMessage msg) {  
103 - log.trace(SESSION_RECEIVED_SESSION_ACTOR_MSG, getType(session), session.getRemoteServer(), msg);  
104 - service.onMsg(deserialize(session.getRemoteServer(), msg));  
105 - }  
106 -  
107 - @Override  
108 - public void onFromDeviceRpcResponseRpcMsg(GrpcSession session, ClusterAPIProtos.ToPluginRpcResponseRpcMessage msg) {  
109 - log.trace(SESSION_RECEIVED_SESSION_ACTOR_MSG, getType(session), session.getRemoteServer(), msg);  
110 - service.onMsg(deserialize(session.getRemoteServer(), msg));  
111 - }  
112 -  
113 - @Override  
114 - public void onToAllNodesRpcMessage(GrpcSession session, ClusterAPIProtos.ToAllNodesRpcMessage msg) {  
115 - log.trace(SESSION_RECEIVED_SESSION_ACTOR_MSG, getType(session), session.getRemoteServer(), msg);  
116 - service.onMsg((ToAllNodesMsg) deserialize(msg.getData().toByteArray())); 56 + public void onReceiveClusterGrpcMsg(GrpcSession session, ClusterAPIProtos.ClusterMessage clusterMessage) {
  57 + log.trace("{} Service [{}] received session actor msg {}", getType(session),
  58 + session.getRemoteServer(),
  59 + clusterMessage);
  60 + service.onReceivedMsg(session.getRemoteServer(), clusterMessage);
117 } 61 }
118 62
119 @Override 63 @Override
@@ -127,45 +71,5 @@ public class BasicRpcSessionListener implements GrpcSessionListener { @@ -127,45 +71,5 @@ public class BasicRpcSessionListener implements GrpcSessionListener {
127 return session.isClient() ? "Client" : "Server"; 71 return session.isClient() ? "Client" : "Server";
128 } 72 }
129 73
130 - private static PluginRpcMsg convert(ServerAddress serverAddress, ClusterAPIProtos.ToPluginRpcMessage msg) {  
131 - ClusterAPIProtos.PluginAddress address = msg.getAddress();  
132 - TenantId tenantId = new TenantId(toUUID(address.getTenantId()));  
133 - PluginId pluginId = new PluginId(toUUID(address.getPluginId()));  
134 - RpcMsg rpcMsg = new RpcMsg(serverAddress, msg.getClazz(), msg.getData().toByteArray());  
135 - return new PluginRpcMsg(tenantId, pluginId, rpcMsg);  
136 - }  
137 -  
138 - private static UUID toUUID(ClusterAPIProtos.Uid uid) {  
139 - return new UUID(uid.getPluginUuidMsb(), uid.getPluginUuidLsb());  
140 - }  
141 -  
142 - private static ToDeviceRpcRequestPluginMsg deserialize(ServerAddress serverAddress, ClusterAPIProtos.ToDeviceRpcRequestRpcMessage msg) {  
143 - ClusterAPIProtos.PluginAddress address = msg.getAddress();  
144 - TenantId pluginTenantId = new TenantId(toUUID(address.getTenantId()));  
145 - PluginId pluginId = new PluginId(toUUID(address.getPluginId()));  
146 -  
147 - TenantId deviceTenantId = new TenantId(toUUID(msg.getDeviceTenantId()));  
148 - DeviceId deviceId = new DeviceId(toUUID(msg.getDeviceId()));  
149 -  
150 - ToDeviceRpcRequestBody requestBody = new ToDeviceRpcRequestBody(msg.getMethod(), msg.getParams());  
151 - ToDeviceRpcRequest request = new ToDeviceRpcRequest(toUUID(msg.getMsgId()), null, deviceTenantId, deviceId, msg.getOneway(), msg.getExpTime(), requestBody);  
152 -  
153 - return new ToDeviceRpcRequestPluginMsg(serverAddress, pluginId, pluginTenantId, request);  
154 - }  
155 -  
156 - private static ToPluginRpcResponseDeviceMsg deserialize(ServerAddress serverAddress, ClusterAPIProtos.ToPluginRpcResponseRpcMessage msg) {  
157 - ClusterAPIProtos.PluginAddress address = msg.getAddress();  
158 - TenantId pluginTenantId = new TenantId(toUUID(address.getTenantId()));  
159 - PluginId pluginId = new PluginId(toUUID(address.getPluginId()));  
160 -  
161 - RpcError error = !StringUtils.isEmpty(msg.getError()) ? RpcError.valueOf(msg.getError()) : null;  
162 - FromDeviceRpcResponse response = new FromDeviceRpcResponse(toUUID(msg.getMsgId()), msg.getResponse(), error);  
163 - return new ToPluginRpcResponseDeviceMsg(pluginId, pluginTenantId, response);  
164 - }  
165 -  
166 - @SuppressWarnings("unchecked")  
167 - private static <T extends Serializable> T deserialize(byte[] data) {  
168 - return (T) SerializationUtils.deserialize(data);  
169 - }  
170 74
171 } 75 }
@@ -23,5 +23,5 @@ import org.thingsboard.server.gen.cluster.ClusterAPIProtos; @@ -23,5 +23,5 @@ import org.thingsboard.server.gen.cluster.ClusterAPIProtos;
23 */ 23 */
24 @Data 24 @Data
25 public final class RpcBroadcastMsg { 25 public final class RpcBroadcastMsg {
26 - private final ClusterAPIProtos.ToRpcServerMessage msg; 26 + private final ClusterAPIProtos.ClusterMessage msg;
27 } 27 }
@@ -23,12 +23,17 @@ import org.thingsboard.server.actors.ActorSystemContext; @@ -23,12 +23,17 @@ import org.thingsboard.server.actors.ActorSystemContext;
23 import org.thingsboard.server.actors.service.ContextAwareActor; 23 import org.thingsboard.server.actors.service.ContextAwareActor;
24 import org.thingsboard.server.actors.service.ContextBasedCreator; 24 import org.thingsboard.server.actors.service.ContextBasedCreator;
25 import org.thingsboard.server.actors.service.DefaultActorService; 25 import org.thingsboard.server.actors.service.DefaultActorService;
  26 +import org.thingsboard.server.common.msg.TbActorMsg;
26 import org.thingsboard.server.common.msg.cluster.ClusterEventMsg; 27 import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;
27 import org.thingsboard.server.common.msg.cluster.ServerAddress; 28 import org.thingsboard.server.common.msg.cluster.ServerAddress;
28 import org.thingsboard.server.gen.cluster.ClusterAPIProtos; 29 import org.thingsboard.server.gen.cluster.ClusterAPIProtos;
29 import org.thingsboard.server.service.cluster.discovery.ServerInstance; 30 import org.thingsboard.server.service.cluster.discovery.ServerInstance;
30 31
31 -import java.util.*; 32 +import java.util.HashMap;
  33 +import java.util.LinkedList;
  34 +import java.util.Map;
  35 +import java.util.Queue;
  36 +import java.util.UUID;
32 37
33 /** 38 /**
34 * @author Andrew Shvayka 39 * @author Andrew Shvayka
@@ -39,7 +44,7 @@ public class RpcManagerActor extends ContextAwareActor { @@ -39,7 +44,7 @@ public class RpcManagerActor extends ContextAwareActor {
39 44
40 private final Map<ServerAddress, SessionActorInfo> sessionActors; 45 private final Map<ServerAddress, SessionActorInfo> sessionActors;
41 46
42 - private final Map<ServerAddress, Queue<ClusterAPIProtos.ToRpcServerMessage>> pendingMsgs; 47 + private final Map<ServerAddress, Queue<ClusterAPIProtos.ClusterMessage>> pendingMsgs;
43 48
44 private final ServerAddress instance; 49 private final ServerAddress instance;
45 50
@@ -57,9 +62,15 @@ public class RpcManagerActor extends ContextAwareActor { @@ -57,9 +62,15 @@ public class RpcManagerActor extends ContextAwareActor {
57 } 62 }
58 63
59 @Override 64 @Override
  65 + protected boolean process(TbActorMsg msg) {
  66 + //TODO Move everything here, to work with TbActorMsg
  67 + return false;
  68 + }
  69 +
  70 + @Override
60 public void onReceive(Object msg) throws Exception { 71 public void onReceive(Object msg) throws Exception {
61 - if (msg instanceof RpcSessionTellMsg) {  
62 - onMsg((RpcSessionTellMsg) msg); 72 + if (msg instanceof ClusterAPIProtos.ClusterMessage) {
  73 + onMsg((ClusterAPIProtos.ClusterMessage) msg);
63 } else if (msg instanceof RpcBroadcastMsg) { 74 } else if (msg instanceof RpcBroadcastMsg) {
64 onMsg((RpcBroadcastMsg) msg); 75 onMsg((RpcBroadcastMsg) msg);
65 } else if (msg instanceof RpcSessionCreateRequestMsg) { 76 } else if (msg instanceof RpcSessionCreateRequestMsg) {
@@ -77,24 +88,30 @@ public class RpcManagerActor extends ContextAwareActor { @@ -77,24 +88,30 @@ public class RpcManagerActor extends ContextAwareActor {
77 88
78 private void onMsg(RpcBroadcastMsg msg) { 89 private void onMsg(RpcBroadcastMsg msg) {
79 log.debug("Forwarding msg to session actors {}", msg); 90 log.debug("Forwarding msg to session actors {}", msg);
80 - sessionActors.keySet().forEach(address -> onMsg(new RpcSessionTellMsg(address, msg.getMsg()))); 91 + sessionActors.keySet().forEach(address -> onMsg(msg.getMsg()));
81 pendingMsgs.values().forEach(queue -> queue.add(msg.getMsg())); 92 pendingMsgs.values().forEach(queue -> queue.add(msg.getMsg()));
82 } 93 }
83 94
84 - private void onMsg(RpcSessionTellMsg msg) {  
85 - ServerAddress address = msg.getServerAddress();  
86 - SessionActorInfo session = sessionActors.get(address);  
87 - if (session != null) {  
88 - log.debug("{} Forwarding msg to session actor", address);  
89 - session.actor.tell(msg, ActorRef.noSender());  
90 - } else {  
91 - log.debug("{} Storing msg to pending queue", address);  
92 - Queue<ClusterAPIProtos.ToRpcServerMessage> queue = pendingMsgs.get(address);  
93 - if (queue == null) {  
94 - queue = new LinkedList<>();  
95 - pendingMsgs.put(address, queue); 95 + private void onMsg(ClusterAPIProtos.ClusterMessage msg) {
  96 + if (msg.hasServerAddress()) {
  97 + ServerAddress address = new ServerAddress(msg.getServerAddress().getHost(),
  98 + msg.getServerAddress().getPort());
  99 + SessionActorInfo session = sessionActors.get(address);
  100 + if (session != null) {
  101 + log.debug("{} Forwarding msg to session actor", address);
  102 + session.getActor().tell(msg, ActorRef.noSender());
  103 + } else {
  104 + log.debug("{} Storing msg to pending queue", address);
  105 + Queue<ClusterAPIProtos.ClusterMessage> queue = pendingMsgs.get(address);
  106 + if (queue == null) {
  107 + queue = new LinkedList<>();
  108 + pendingMsgs.put(new ServerAddress(
  109 + msg.getServerAddress().getHost(), msg.getServerAddress().getPort()), queue);
  110 + }
  111 + queue.add(msg);
96 } 112 }
97 - queue.add(msg.getMsg()); 113 + } else {
  114 + logger.warning("Cluster msg doesn't have set Server Address [{}]", msg);
98 } 115 }
99 } 116 }
100 117
@@ -141,7 +158,7 @@ public class RpcManagerActor extends ContextAwareActor { @@ -141,7 +158,7 @@ public class RpcManagerActor extends ContextAwareActor {
141 private void onSessionClose(boolean reconnect, ServerAddress remoteAddress) { 158 private void onSessionClose(boolean reconnect, ServerAddress remoteAddress) {
142 log.debug("[{}] session closed. Should reconnect: {}", remoteAddress, reconnect); 159 log.debug("[{}] session closed. Should reconnect: {}", remoteAddress, reconnect);
143 SessionActorInfo sessionRef = sessionActors.get(remoteAddress); 160 SessionActorInfo sessionRef = sessionActors.get(remoteAddress);
144 - if (context().sender().equals(sessionRef.actor)) { 161 + if (context().sender() != null && context().sender().equals(sessionRef.actor)) {
145 sessionActors.remove(remoteAddress); 162 sessionActors.remove(remoteAddress);
146 pendingMsgs.remove(remoteAddress); 163 pendingMsgs.remove(remoteAddress);
147 if (reconnect) { 164 if (reconnect) {
@@ -160,10 +177,10 @@ public class RpcManagerActor extends ContextAwareActor { @@ -160,10 +177,10 @@ public class RpcManagerActor extends ContextAwareActor {
160 private void register(ServerAddress remoteAddress, UUID uuid, ActorRef sender) { 177 private void register(ServerAddress remoteAddress, UUID uuid, ActorRef sender) {
161 sessionActors.put(remoteAddress, new SessionActorInfo(uuid, sender)); 178 sessionActors.put(remoteAddress, new SessionActorInfo(uuid, sender));
162 log.debug("[{}][{}] Registering session actor.", remoteAddress, uuid); 179 log.debug("[{}][{}] Registering session actor.", remoteAddress, uuid);
163 - Queue<ClusterAPIProtos.ToRpcServerMessage> data = pendingMsgs.remove(remoteAddress); 180 + Queue<ClusterAPIProtos.ClusterMessage> data = pendingMsgs.remove(remoteAddress);
164 if (data != null) { 181 if (data != null) {
165 log.debug("[{}][{}] Forwarding {} pending messages.", remoteAddress, uuid, data.size()); 182 log.debug("[{}][{}] Forwarding {} pending messages.", remoteAddress, uuid, data.size());
166 - data.forEach(msg -> sender.tell(new RpcSessionTellMsg(remoteAddress, msg), ActorRef.noSender())); 183 + data.forEach(msg -> sender.tell(new RpcSessionTellMsg(msg), ActorRef.noSender()));
167 } else { 184 } else {
168 log.debug("[{}][{}] No pending messages to forward.", remoteAddress, uuid); 185 log.debug("[{}][{}] No pending messages to forward.", remoteAddress, uuid);
169 } 186 }
@@ -23,6 +23,7 @@ import io.grpc.stub.StreamObserver; @@ -23,6 +23,7 @@ import io.grpc.stub.StreamObserver;
23 import org.thingsboard.server.actors.ActorSystemContext; 23 import org.thingsboard.server.actors.ActorSystemContext;
24 import org.thingsboard.server.actors.service.ContextAwareActor; 24 import org.thingsboard.server.actors.service.ContextAwareActor;
25 import org.thingsboard.server.actors.service.ContextBasedCreator; 25 import org.thingsboard.server.actors.service.ContextBasedCreator;
  26 +import org.thingsboard.server.common.msg.TbActorMsg;
26 import org.thingsboard.server.common.msg.cluster.ServerAddress; 27 import org.thingsboard.server.common.msg.cluster.ServerAddress;
27 import org.thingsboard.server.gen.cluster.ClusterAPIProtos; 28 import org.thingsboard.server.gen.cluster.ClusterAPIProtos;
28 import org.thingsboard.server.gen.cluster.ClusterRpcServiceGrpc; 29 import org.thingsboard.server.gen.cluster.ClusterRpcServiceGrpc;
@@ -31,6 +32,8 @@ import org.thingsboard.server.service.cluster.rpc.GrpcSessionListener; @@ -31,6 +32,8 @@ import org.thingsboard.server.service.cluster.rpc.GrpcSessionListener;
31 32
32 import java.util.UUID; 33 import java.util.UUID;
33 34
  35 +import static org.thingsboard.server.gen.cluster.ClusterAPIProtos.MessageType.CONNECT_RPC_MESSAGE;
  36 +
34 /** 37 /**
35 * @author Andrew Shvayka 38 * @author Andrew Shvayka
36 */ 39 */
@@ -48,16 +51,22 @@ public class RpcSessionActor extends ContextAwareActor { @@ -48,16 +51,22 @@ public class RpcSessionActor extends ContextAwareActor {
48 } 51 }
49 52
50 @Override 53 @Override
  54 + protected boolean process(TbActorMsg msg) {
  55 + //TODO Move everything here, to work with TbActorMsg
  56 + return false;
  57 + }
  58 +
  59 + @Override
51 public void onReceive(Object msg) throws Exception { 60 public void onReceive(Object msg) throws Exception {
52 - if (msg instanceof RpcSessionTellMsg) {  
53 - tell((RpcSessionTellMsg) msg); 61 + if (msg instanceof ClusterAPIProtos.ClusterMessage) {
  62 + tell((ClusterAPIProtos.ClusterMessage) msg);
54 } else if (msg instanceof RpcSessionCreateRequestMsg) { 63 } else if (msg instanceof RpcSessionCreateRequestMsg) {
55 initSession((RpcSessionCreateRequestMsg) msg); 64 initSession((RpcSessionCreateRequestMsg) msg);
56 } 65 }
57 } 66 }
58 67
59 - private void tell(RpcSessionTellMsg msg) {  
60 - session.sendMsg(msg.getMsg()); 68 + private void tell(ClusterAPIProtos.ClusterMessage msg) {
  69 + session.sendMsg(msg);
61 } 70 }
62 71
63 @Override 72 @Override
@@ -69,7 +78,7 @@ public class RpcSessionActor extends ContextAwareActor { @@ -69,7 +78,7 @@ public class RpcSessionActor extends ContextAwareActor {
69 private void initSession(RpcSessionCreateRequestMsg msg) { 78 private void initSession(RpcSessionCreateRequestMsg msg) {
70 log.info("[{}] Initializing session", context().self()); 79 log.info("[{}] Initializing session", context().self());
71 ServerAddress remoteServer = msg.getRemoteAddress(); 80 ServerAddress remoteServer = msg.getRemoteAddress();
72 - listener = new BasicRpcSessionListener(systemContext, context().parent(), context().self()); 81 + listener = new BasicRpcSessionListener(systemContext.getActorService(), context().parent(), context().self());
73 if (msg.getRemoteAddress() == null) { 82 if (msg.getRemoteAddress() == null) {
74 // Server session 83 // Server session
75 session = new GrpcSession(listener); 84 session = new GrpcSession(listener);
@@ -84,7 +93,7 @@ public class RpcSessionActor extends ContextAwareActor { @@ -84,7 +93,7 @@ public class RpcSessionActor extends ContextAwareActor {
84 session.initInputStream(); 93 session.initInputStream();
85 94
86 ClusterRpcServiceGrpc.ClusterRpcServiceStub stub = ClusterRpcServiceGrpc.newStub(channel); 95 ClusterRpcServiceGrpc.ClusterRpcServiceStub stub = ClusterRpcServiceGrpc.newStub(channel);
87 - StreamObserver<ClusterAPIProtos.ToRpcServerMessage> outputStream = stub.handlePluginMsgs(session.getInputStream()); 96 + StreamObserver<ClusterAPIProtos.ClusterMessage> outputStream = stub.handleMsgs(session.getInputStream());
88 97
89 session.setOutputStream(outputStream); 98 session.setOutputStream(outputStream);
90 session.initOutputStream(); 99 session.initOutputStream();
@@ -108,11 +117,10 @@ public class RpcSessionActor extends ContextAwareActor { @@ -108,11 +117,10 @@ public class RpcSessionActor extends ContextAwareActor {
108 } 117 }
109 } 118 }
110 119
111 - private ClusterAPIProtos.ToRpcServerMessage toConnectMsg() { 120 + private ClusterAPIProtos.ClusterMessage toConnectMsg() {
112 ServerAddress instance = systemContext.getDiscoveryService().getCurrentServer().getServerAddress(); 121 ServerAddress instance = systemContext.getDiscoveryService().getCurrentServer().getServerAddress();
113 - return ClusterAPIProtos.ToRpcServerMessage.newBuilder().setConnectMsg(  
114 - ClusterAPIProtos.ConnectRpcMessage.newBuilder().setServerAddress(  
115 - ClusterAPIProtos.ServerAddress.newBuilder().setHost(instance.getHost()).setPort(instance.getPort()).build()).build()).build();  
116 - 122 + return ClusterAPIProtos.ClusterMessage.newBuilder().setMessageType(CONNECT_RPC_MESSAGE).setServerAddress(
  123 + ClusterAPIProtos.ServerAddress.newBuilder().setHost(instance.getHost())
  124 + .setPort(instance.getPort()).build()).build();
117 } 125 }
118 } 126 }
@@ -30,6 +30,6 @@ public final class RpcSessionCreateRequestMsg { @@ -30,6 +30,6 @@ public final class RpcSessionCreateRequestMsg {
30 30
31 private final UUID msgUid; 31 private final UUID msgUid;
32 private final ServerAddress remoteAddress; 32 private final ServerAddress remoteAddress;
33 - private final StreamObserver<ClusterAPIProtos.ToRpcServerMessage> responseObserver; 33 + private final StreamObserver<ClusterAPIProtos.ClusterMessage> responseObserver;
34 34
35 } 35 }
@@ -16,7 +16,6 @@ @@ -16,7 +16,6 @@
16 package org.thingsboard.server.actors.rpc; 16 package org.thingsboard.server.actors.rpc;
17 17
18 import lombok.Data; 18 import lombok.Data;
19 -import org.thingsboard.server.common.msg.cluster.ServerAddress;  
20 import org.thingsboard.server.gen.cluster.ClusterAPIProtos; 19 import org.thingsboard.server.gen.cluster.ClusterAPIProtos;
21 20
22 /** 21 /**
@@ -24,6 +23,5 @@ import org.thingsboard.server.gen.cluster.ClusterAPIProtos; @@ -24,6 +23,5 @@ import org.thingsboard.server.gen.cluster.ClusterAPIProtos;
24 */ 23 */
25 @Data 24 @Data
26 public final class RpcSessionTellMsg { 25 public final class RpcSessionTellMsg {
27 - private final ServerAddress serverAddress;  
28 - private final ClusterAPIProtos.ToRpcServerMessage msg; 26 + private final ClusterAPIProtos.ClusterMessage msg;
29 } 27 }
1 -/**  
2 - * Copyright © 2016-2018 The Thingsboard Authors  
3 - *  
4 - * Licensed under the Apache License, Version 2.0 (the "License");  
5 - * you may not use this file except in compliance with the License.  
6 - * You may obtain a copy of the License at  
7 - *  
8 - * http://www.apache.org/licenses/LICENSE-2.0  
9 - *  
10 - * Unless required by applicable law or agreed to in writing, software  
11 - * distributed under the License is distributed on an "AS IS" BASIS,  
12 - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  
13 - * See the License for the specific language governing permissions and  
14 - * limitations under the License.  
15 - */  
16 -package org.thingsboard.server.actors.rule;  
17 -  
18 -import akka.actor.ActorRef;  
19 -import org.thingsboard.server.common.msg.core.RuleEngineError;  
20 -import org.thingsboard.server.common.msg.core.RuleEngineErrorMsg;  
21 -import org.thingsboard.server.common.msg.device.ToDeviceActorMsg;  
22 -import org.thingsboard.server.common.msg.session.ToDeviceMsg;  
23 -import org.thingsboard.server.extensions.api.device.DeviceAttributes;  
24 -import org.thingsboard.server.extensions.api.device.DeviceMetaData;  
25 -  
26 -public class ChainProcessingContext {  
27 -  
28 - private final ChainProcessingMetaData md;  
29 - private final int index;  
30 - private final RuleEngineError error;  
31 - private ToDeviceMsg response;  
32 -  
33 -  
34 - public ChainProcessingContext(ChainProcessingMetaData md) {  
35 - super();  
36 - this.md = md;  
37 - this.index = 0;  
38 - this.error = RuleEngineError.NO_RULES;  
39 - }  
40 -  
41 - private ChainProcessingContext(ChainProcessingContext other, int indexOffset, RuleEngineError error) {  
42 - super();  
43 - this.md = other.md;  
44 - this.index = other.index + indexOffset;  
45 - this.error = error;  
46 - this.response = other.response;  
47 -  
48 - if (this.index < 0 || this.index >= this.md.chain.size()) {  
49 - throw new IllegalArgumentException("Can't apply offset " + indexOffset + " to the chain!");  
50 - }  
51 - }  
52 -  
53 - public ActorRef getDeviceActor() {  
54 - return md.originator;  
55 - }  
56 -  
57 - public ActorRef getCurrentActor() {  
58 - return md.chain.getRuleActorMd(index).getActorRef();  
59 - }  
60 -  
61 - public boolean hasNext() {  
62 - return (getChainLength() - 1) > index;  
63 - }  
64 -  
65 - public boolean isFailure() {  
66 - return (error != null && error.isCritical()) || (response != null && !response.isSuccess());  
67 - }  
68 -  
69 - public ChainProcessingContext getNext() {  
70 - return new ChainProcessingContext(this, 1, this.error);  
71 - }  
72 -  
73 - public ChainProcessingContext withError(RuleEngineError error) {  
74 - if (error != null && (this.error == null || this.error.getPriority() < error.getPriority())) {  
75 - return new ChainProcessingContext(this, 0, error);  
76 - } else {  
77 - return this;  
78 - }  
79 - }  
80 -  
81 - public int getChainLength() {  
82 - return md.chain.size();  
83 - }  
84 -  
85 - public ToDeviceActorMsg getInMsg() {  
86 - return md.inMsg;  
87 - }  
88 -  
89 - public DeviceMetaData getDeviceMetaData() {  
90 - return md.deviceMetaData;  
91 - }  
92 -  
93 - public String getDeviceName() {  
94 - return md.deviceMetaData.getDeviceName();  
95 - }  
96 -  
97 - public String getDeviceType() {  
98 - return md.deviceMetaData.getDeviceType();  
99 - }  
100 -  
101 - public DeviceAttributes getAttributes() {  
102 - return md.deviceMetaData.getDeviceAttributes();  
103 - }  
104 -  
105 - public ToDeviceMsg getResponse() {  
106 - return response;  
107 - }  
108 -  
109 - public void mergeResponse(ToDeviceMsg response) {  
110 - // TODO add merge logic  
111 - this.response = response;  
112 - }  
113 -  
114 - public RuleEngineErrorMsg getError() {  
115 - return new RuleEngineErrorMsg(md.inMsg.getPayload().getMsgType(), error);  
116 - }  
117 -}  
1 -/**  
2 - * Copyright © 2016-2018 The Thingsboard Authors  
3 - *  
4 - * Licensed under the Apache License, Version 2.0 (the "License");  
5 - * you may not use this file except in compliance with the License.  
6 - * You may obtain a copy of the License at  
7 - *  
8 - * http://www.apache.org/licenses/LICENSE-2.0  
9 - *  
10 - * Unless required by applicable law or agreed to in writing, software  
11 - * distributed under the License is distributed on an "AS IS" BASIS,  
12 - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  
13 - * See the License for the specific language governing permissions and  
14 - * limitations under the License.  
15 - */  
16 -package org.thingsboard.server.actors.rule;  
17 -  
18 -import org.thingsboard.server.actors.ActorSystemContext;  
19 -import org.thingsboard.server.actors.service.ComponentActor;  
20 -import org.thingsboard.server.actors.service.ContextBasedCreator;  
21 -import org.thingsboard.server.actors.stats.StatsPersistTick;  
22 -import org.thingsboard.server.common.data.id.RuleId;  
23 -import org.thingsboard.server.common.data.id.TenantId;  
24 -import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;  
25 -import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;  
26 -import org.thingsboard.server.extensions.api.plugins.msg.PluginToRuleMsg;  
27 -  
28 -public class RuleActor extends ComponentActor<RuleId, RuleActorMessageProcessor> {  
29 -  
30 - private RuleActor(ActorSystemContext systemContext, TenantId tenantId, RuleId ruleId) {  
31 - super(systemContext, tenantId, ruleId);  
32 - setProcessor(new RuleActorMessageProcessor(tenantId, ruleId, systemContext, logger));  
33 - }  
34 -  
35 - @Override  
36 - public void onReceive(Object msg) throws Exception {  
37 - logger.debug("[{}] Received message: {}", id, msg);  
38 - if (msg instanceof RuleProcessingMsg) {  
39 - try {  
40 - processor.onRuleProcessingMsg(context(), (RuleProcessingMsg) msg);  
41 - increaseMessagesProcessedCount();  
42 - } catch (Exception e) {  
43 - logAndPersist("onDeviceMsg", e);  
44 - }  
45 - } else if (msg instanceof PluginToRuleMsg<?>) {  
46 - try {  
47 - processor.onPluginMsg(context(), (PluginToRuleMsg<?>) msg);  
48 - } catch (Exception e) {  
49 - logAndPersist("onPluginMsg", e);  
50 - }  
51 - } else if (msg instanceof ComponentLifecycleMsg) {  
52 - onComponentLifecycleMsg((ComponentLifecycleMsg) msg);  
53 - } else if (msg instanceof ClusterEventMsg) {  
54 - onClusterEventMsg((ClusterEventMsg) msg);  
55 - } else if (msg instanceof RuleToPluginTimeoutMsg) {  
56 - try {  
57 - processor.onTimeoutMsg(context(), (RuleToPluginTimeoutMsg) msg);  
58 - } catch (Exception e) {  
59 - logAndPersist("onTimeoutMsg", e);  
60 - }  
61 - } else if (msg instanceof StatsPersistTick) {  
62 - onStatsPersistTick(id);  
63 - } else {  
64 - logger.debug("[{}][{}] Unknown msg type.", tenantId, id, msg.getClass().getName());  
65 - }  
66 - }  
67 -  
68 - public static class ActorCreator extends ContextBasedCreator<RuleActor> {  
69 - private static final long serialVersionUID = 1L;  
70 -  
71 - private final TenantId tenantId;  
72 - private final RuleId ruleId;  
73 -  
74 - public ActorCreator(ActorSystemContext context, TenantId tenantId, RuleId ruleId) {  
75 - super(context);  
76 - this.tenantId = tenantId;  
77 - this.ruleId = ruleId;  
78 - }  
79 -  
80 - @Override  
81 - public RuleActor create() throws Exception {  
82 - return new RuleActor(context, tenantId, ruleId);  
83 - }  
84 - }  
85 -  
86 - @Override  
87 - protected long getErrorPersistFrequency() {  
88 - return systemContext.getRuleErrorPersistFrequency();  
89 - }  
90 -}  
1 -/**  
2 - * Copyright © 2016-2018 The Thingsboard Authors  
3 - *  
4 - * Licensed under the Apache License, Version 2.0 (the "License");  
5 - * you may not use this file except in compliance with the License.  
6 - * You may obtain a copy of the License at  
7 - *  
8 - * http://www.apache.org/licenses/LICENSE-2.0  
9 - *  
10 - * Unless required by applicable law or agreed to in writing, software  
11 - * distributed under the License is distributed on an "AS IS" BASIS,  
12 - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  
13 - * See the License for the specific language governing permissions and  
14 - * limitations under the License.  
15 - */  
16 -package org.thingsboard.server.actors.rule;  
17 -  
18 -import akka.actor.ActorContext;  
19 -import akka.actor.ActorRef;  
20 -import akka.event.LoggingAdapter;  
21 -import com.fasterxml.jackson.core.JsonProcessingException;  
22 -import org.springframework.util.StringUtils;  
23 -import org.thingsboard.server.actors.ActorSystemContext;  
24 -import org.thingsboard.server.actors.plugin.RuleToPluginMsgWrapper;  
25 -import org.thingsboard.server.actors.shared.ComponentMsgProcessor;  
26 -import org.thingsboard.server.common.data.id.PluginId;  
27 -import org.thingsboard.server.common.data.id.RuleId;  
28 -import org.thingsboard.server.common.data.id.TenantId;  
29 -import org.thingsboard.server.common.data.plugin.ComponentLifecycleState;  
30 -import org.thingsboard.server.common.data.plugin.PluginMetaData;  
31 -import org.thingsboard.server.common.data.rule.RuleMetaData;  
32 -import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;  
33 -import org.thingsboard.server.common.msg.core.BasicStatusCodeResponse;  
34 -import org.thingsboard.server.common.msg.core.RuleEngineError;  
35 -import org.thingsboard.server.common.msg.device.ToDeviceActorMsg;  
36 -import org.thingsboard.server.common.msg.session.ToDeviceMsg;  
37 -import org.thingsboard.server.extensions.api.plugins.PluginAction;  
38 -import org.thingsboard.server.extensions.api.plugins.msg.PluginToRuleMsg;  
39 -import org.thingsboard.server.extensions.api.plugins.msg.ResponsePluginToRuleMsg;  
40 -import org.thingsboard.server.extensions.api.plugins.msg.RuleToPluginMsg;  
41 -import org.thingsboard.server.extensions.api.rules.*;  
42 -  
43 -import java.util.*;  
44 -  
45 -class RuleActorMessageProcessor extends ComponentMsgProcessor<RuleId> {  
46 -  
47 - private final RuleProcessingContext ruleCtx;  
48 - private final Map<UUID, RuleProcessingMsg> pendingMsgMap;  
49 -  
50 - private RuleMetaData ruleMd;  
51 - private ComponentLifecycleState state;  
52 - private List<RuleFilter> filters;  
53 - private RuleProcessor processor;  
54 - private PluginAction action;  
55 -  
56 - private TenantId pluginTenantId;  
57 - private PluginId pluginId;  
58 -  
59 - protected RuleActorMessageProcessor(TenantId tenantId, RuleId ruleId, ActorSystemContext systemContext, LoggingAdapter logger) {  
60 - super(systemContext, logger, tenantId, ruleId);  
61 - this.pendingMsgMap = new HashMap<>();  
62 - this.ruleCtx = new RuleProcessingContext(systemContext, ruleId);  
63 - }  
64 -  
65 - @Override  
66 - public void start() throws Exception {  
67 - logger.info("[{}][{}] Starting rule actor.", entityId, tenantId);  
68 - ruleMd = systemContext.getRuleService().findRuleById(entityId);  
69 - if (ruleMd == null) {  
70 - throw new RuleInitializationException("Rule not found!");  
71 - }  
72 - state = ruleMd.getState();  
73 - if (state == ComponentLifecycleState.ACTIVE) {  
74 - logger.info("[{}] Rule is active. Going to initialize rule components.", entityId);  
75 - initComponent();  
76 - } else {  
77 - logger.info("[{}] Rule is suspended. Skipping rule components initialization.", entityId);  
78 - }  
79 -  
80 - logger.info("[{}][{}] Started rule actor.", entityId, tenantId);  
81 - }  
82 -  
83 - @Override  
84 - public void stop() throws Exception {  
85 - onStop();  
86 - }  
87 -  
88 -  
89 - private void initComponent() throws RuleException {  
90 - try {  
91 - if (!ruleMd.getFilters().isArray()) {  
92 - throw new RuntimeException("Filters are not array!");  
93 - }  
94 - fetchPluginInfo();  
95 - initFilters();  
96 - initProcessor();  
97 - initAction();  
98 - } catch (RuntimeException e) {  
99 - throw new RuleInitializationException("Unknown runtime exception!", e);  
100 - } catch (InstantiationException e) {  
101 - throw new RuleInitializationException("No default constructor for rule implementation!", e);  
102 - } catch (IllegalAccessException e) {  
103 - throw new RuleInitializationException("Illegal Access Exception during rule initialization!", e);  
104 - } catch (ClassNotFoundException e) {  
105 - throw new RuleInitializationException("Rule Class not found!", e);  
106 - } catch (Exception e) {  
107 - throw new RuleException(e.getMessage(), e);  
108 - }  
109 - }  
110 -  
111 - private void initAction() throws Exception {  
112 - if (ruleMd.getAction() != null && !ruleMd.getAction().isNull()) {  
113 - action = initComponent(ruleMd.getAction());  
114 - }  
115 - }  
116 -  
117 - private void initProcessor() throws Exception {  
118 - if (ruleMd.getProcessor() != null && !ruleMd.getProcessor().isNull()) {  
119 - processor = initComponent(ruleMd.getProcessor());  
120 - }  
121 - }  
122 -  
123 - private void initFilters() throws Exception {  
124 - filters = new ArrayList<>(ruleMd.getFilters().size());  
125 - for (int i = 0; i < ruleMd.getFilters().size(); i++) {  
126 - filters.add(initComponent(ruleMd.getFilters().get(i)));  
127 - }  
128 - }  
129 -  
130 - private void fetchPluginInfo() {  
131 - if (!StringUtils.isEmpty(ruleMd.getPluginToken())) {  
132 - PluginMetaData pluginMd = systemContext.getPluginService().findPluginByApiToken(ruleMd.getPluginToken());  
133 - pluginTenantId = pluginMd.getTenantId();  
134 - pluginId = pluginMd.getId();  
135 - }  
136 - }  
137 -  
138 - protected void onRuleProcessingMsg(ActorContext context, RuleProcessingMsg msg) throws RuleException {  
139 - if (state != ComponentLifecycleState.ACTIVE) {  
140 - pushToNextRule(context, msg.getCtx(), RuleEngineError.NO_ACTIVE_RULES);  
141 - return;  
142 - }  
143 - ChainProcessingContext chainCtx = msg.getCtx();  
144 - ToDeviceActorMsg inMsg = chainCtx.getInMsg();  
145 -  
146 - ruleCtx.update(inMsg, chainCtx.getDeviceMetaData());  
147 -  
148 - logger.debug("[{}] Going to filter in msg: {}", entityId, inMsg);  
149 - for (RuleFilter filter : filters) {  
150 - if (!filter.filter(ruleCtx, inMsg)) {  
151 - logger.debug("[{}] In msg is NOT valid for processing by current rule: {}", entityId, inMsg);  
152 - pushToNextRule(context, msg.getCtx(), RuleEngineError.NO_FILTERS_MATCHED);  
153 - return;  
154 - }  
155 - }  
156 - RuleProcessingMetaData inMsgMd;  
157 - if (processor != null) {  
158 - logger.debug("[{}] Going to process in msg: {}", entityId, inMsg);  
159 - inMsgMd = processor.process(ruleCtx, inMsg);  
160 - } else {  
161 - inMsgMd = new RuleProcessingMetaData();  
162 - }  
163 - logger.debug("[{}] Going to convert in msg: {}", entityId, inMsg);  
164 - if (action != null) {  
165 - Optional<RuleToPluginMsg<?>> ruleToPluginMsgOptional = action.convert(ruleCtx, inMsg, inMsgMd);  
166 - if (ruleToPluginMsgOptional.isPresent()) {  
167 - RuleToPluginMsg<?> ruleToPluginMsg = ruleToPluginMsgOptional.get();  
168 - logger.debug("[{}] Device msg is converted to: {}", entityId, ruleToPluginMsg);  
169 - context.parent().tell(new RuleToPluginMsgWrapper(pluginTenantId, pluginId, tenantId, entityId, ruleToPluginMsg), context.self());  
170 - if (action.isOneWayAction()) {  
171 - pushToNextRule(context, msg.getCtx(), RuleEngineError.NO_TWO_WAY_ACTIONS);  
172 - return;  
173 - } else {  
174 - pendingMsgMap.put(ruleToPluginMsg.getUid(), msg);  
175 - scheduleMsgWithDelay(context, new RuleToPluginTimeoutMsg(ruleToPluginMsg.getUid()), systemContext.getPluginProcessingTimeout());  
176 - return;  
177 - }  
178 - }  
179 - }  
180 - logger.debug("[{}] Nothing to send to plugin: {}", entityId, pluginId);  
181 - pushToNextRule(context, msg.getCtx(), RuleEngineError.NO_TWO_WAY_ACTIONS);  
182 - }  
183 -  
184 - void onPluginMsg(ActorContext context, PluginToRuleMsg<?> msg) {  
185 - RuleProcessingMsg pendingMsg = pendingMsgMap.remove(msg.getUid());  
186 - if (pendingMsg != null) {  
187 - ChainProcessingContext ctx = pendingMsg.getCtx();  
188 - if (isErrorResponce(msg)) {  
189 - pushToNextRule(context, ctx, RuleEngineError.NO_RESPONSE_FROM_ACTIONS);  
190 - } else {  
191 - Optional<ToDeviceMsg> ruleResponseOptional = action.convert(msg);  
192 - if (ruleResponseOptional.isPresent()) {  
193 - ctx.mergeResponse(ruleResponseOptional.get());  
194 - pushToNextRule(context, ctx, null);  
195 - } else {  
196 - pushToNextRule(context, ctx, RuleEngineError.NO_RESPONSE_FROM_ACTIONS);  
197 - }  
198 - }  
199 - } else {  
200 - logger.warning("[{}] Processing timeout detected: [{}]", entityId, msg.getUid());  
201 - }  
202 - }  
203 -  
204 - private boolean isErrorResponce(PluginToRuleMsg<?> msg) {  
205 - if (msg instanceof ResponsePluginToRuleMsg) {  
206 - if (((ResponsePluginToRuleMsg) msg).getPayload() instanceof BasicStatusCodeResponse) {  
207 - BasicStatusCodeResponse responce = (BasicStatusCodeResponse) ((ResponsePluginToRuleMsg) msg).getPayload();  
208 - return !responce.isSuccess();  
209 - }  
210 - }  
211 - return false;  
212 - }  
213 -  
214 - void onTimeoutMsg(ActorContext context, RuleToPluginTimeoutMsg msg) {  
215 - RuleProcessingMsg pendingMsg = pendingMsgMap.remove(msg.getMsgId());  
216 - if (pendingMsg != null) {  
217 - logger.debug("[{}] Processing timeout detected [{}]: {}", entityId, msg.getMsgId(), pendingMsg);  
218 - ChainProcessingContext ctx = pendingMsg.getCtx();  
219 - pushToNextRule(context, ctx, RuleEngineError.PLUGIN_TIMEOUT);  
220 - }  
221 - }  
222 -  
223 - private void pushToNextRule(ActorContext context, ChainProcessingContext ctx, RuleEngineError error) {  
224 - if (error != null) {  
225 - ctx = ctx.withError(error);  
226 - }  
227 - if (ctx.isFailure()) {  
228 - logger.debug("[{}][{}] Forwarding processing chain to device actor due to failure.", ruleMd.getId(), ctx.getInMsg().getDeviceId());  
229 - ctx.getDeviceActor().tell(new RulesProcessedMsg(ctx), ActorRef.noSender());  
230 - } else if (!ctx.hasNext()) {  
231 - logger.debug("[{}][{}] Forwarding processing chain to device actor due to end of chain.", ruleMd.getId(), ctx.getInMsg().getDeviceId());  
232 - ctx.getDeviceActor().tell(new RulesProcessedMsg(ctx), ActorRef.noSender());  
233 - } else {  
234 - logger.debug("[{}][{}] Forwarding processing chain to next rule actor.", ruleMd.getId(), ctx.getInMsg().getDeviceId());  
235 - ChainProcessingContext nextTask = ctx.getNext();  
236 - nextTask.getCurrentActor().tell(new RuleProcessingMsg(nextTask), context.self());  
237 - }  
238 - }  
239 -  
240 - @Override  
241 - public void onCreated(ActorContext context) {  
242 - logger.info("[{}] Going to process onCreated rule.", entityId);  
243 - }  
244 -  
245 - @Override  
246 - public void onUpdate(ActorContext context) throws RuleException {  
247 - RuleMetaData oldRuleMd = ruleMd;  
248 - ruleMd = systemContext.getRuleService().findRuleById(entityId);  
249 - logger.info("[{}] Rule configuration was updated from {} to {}.", entityId, oldRuleMd, ruleMd);  
250 - try {  
251 - fetchPluginInfo();  
252 - if (filters == null || !Objects.equals(oldRuleMd.getFilters(), ruleMd.getFilters())) {  
253 - logger.info("[{}] Rule filters require restart due to json change from {} to {}.",  
254 - entityId, mapper.writeValueAsString(oldRuleMd.getFilters()), mapper.writeValueAsString(ruleMd.getFilters()));  
255 - stopFilters();  
256 - initFilters();  
257 - }  
258 - if (processor == null || !Objects.equals(oldRuleMd.getProcessor(), ruleMd.getProcessor())) {  
259 - logger.info("[{}] Rule processor require restart due to configuration change.", entityId);  
260 - stopProcessor();  
261 - initProcessor();  
262 - }  
263 - if (action == null || !Objects.equals(oldRuleMd.getAction(), ruleMd.getAction())) {  
264 - logger.info("[{}] Rule action require restart due to configuration change.", entityId);  
265 - stopAction();  
266 - initAction();  
267 - }  
268 - } catch (RuntimeException e) {  
269 - throw new RuleInitializationException("Unknown runtime exception!", e);  
270 - } catch (InstantiationException e) {  
271 - throw new RuleInitializationException("No default constructor for rule implementation!", e);  
272 - } catch (IllegalAccessException e) {  
273 - throw new RuleInitializationException("Illegal Access Exception during rule initialization!", e);  
274 - } catch (ClassNotFoundException e) {  
275 - throw new RuleInitializationException("Rule Class not found!", e);  
276 - } catch (JsonProcessingException e) {  
277 - throw new RuleInitializationException("Rule configuration is invalid!", e);  
278 - } catch (Exception e) {  
279 - throw new RuleInitializationException(e.getMessage(), e);  
280 - }  
281 - }  
282 -  
283 - @Override  
284 - public void onActivate(ActorContext context) throws Exception {  
285 - logger.info("[{}] Going to process onActivate rule.", entityId);  
286 - this.state = ComponentLifecycleState.ACTIVE;  
287 - if (filters != null) {  
288 - filters.forEach(RuleLifecycleComponent::resume);  
289 - if (processor != null) {  
290 - processor.resume();  
291 - } else {  
292 - initProcessor();  
293 - }  
294 - if (action != null) {  
295 - action.resume();  
296 - }  
297 - logger.info("[{}] Rule resumed.", entityId);  
298 - } else {  
299 - start();  
300 - }  
301 - }  
302 -  
303 - @Override  
304 - public void onSuspend(ActorContext context) {  
305 - logger.info("[{}] Going to process onSuspend rule.", entityId);  
306 - this.state = ComponentLifecycleState.SUSPENDED;  
307 - if (filters != null) {  
308 - filters.forEach(f -> f.suspend());  
309 - }  
310 - if (processor != null) {  
311 - processor.suspend();  
312 - }  
313 - if (action != null) {  
314 - action.suspend();  
315 - }  
316 - }  
317 -  
318 - @Override  
319 - public void onStop(ActorContext context) {  
320 - logger.info("[{}] Going to process onStop rule.", entityId);  
321 - onStop();  
322 - scheduleMsgWithDelay(context, new RuleTerminationMsg(entityId), systemContext.getRuleActorTerminationDelay());  
323 - }  
324 -  
325 - private void onStop() {  
326 - this.state = ComponentLifecycleState.SUSPENDED;  
327 - stopFilters();  
328 - stopProcessor();  
329 - stopAction();  
330 - }  
331 -  
332 - @Override  
333 - public void onClusterEventMsg(ClusterEventMsg msg) throws Exception {  
334 - //Do nothing  
335 - }  
336 -  
337 - private void stopAction() {  
338 - if (action != null) {  
339 - action.stop();  
340 - }  
341 - }  
342 -  
343 - private void stopProcessor() {  
344 - if (processor != null) {  
345 - processor.stop();  
346 - }  
347 - }  
348 -  
349 - private void stopFilters() {  
350 - if (filters != null) {  
351 - filters.forEach(f -> f.stop());  
352 - }  
353 - }  
354 -}  
1 -/**  
2 - * Copyright © 2016-2018 The Thingsboard Authors  
3 - *  
4 - * Licensed under the Apache License, Version 2.0 (the "License");  
5 - * you may not use this file except in compliance with the License.  
6 - * You may obtain a copy of the License at  
7 - *  
8 - * http://www.apache.org/licenses/LICENSE-2.0  
9 - *  
10 - * Unless required by applicable law or agreed to in writing, software  
11 - * distributed under the License is distributed on an "AS IS" BASIS,  
12 - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  
13 - * See the License for the specific language governing permissions and  
14 - * limitations under the License.  
15 - */  
16 -package org.thingsboard.server.actors.rule;  
17 -  
18 -import java.util.Comparator;  
19 -  
20 -import org.thingsboard.server.common.data.id.RuleId;  
21 -  
22 -import akka.actor.ActorRef;  
23 -  
24 -public class RuleActorMetaData {  
25 -  
26 - private final RuleId ruleId;  
27 - private final boolean systemRule;  
28 - private final int weight;  
29 - private final ActorRef actorRef;  
30 -  
31 - public static final Comparator<RuleActorMetaData> RULE_ACTOR_MD_COMPARATOR = new Comparator<RuleActorMetaData>() {  
32 -  
33 - @Override  
34 - public int compare(RuleActorMetaData r1, RuleActorMetaData r2) {  
35 - if (r1.isSystemRule() && !r2.isSystemRule()) {  
36 - return 1;  
37 - } else if (!r1.isSystemRule() && r2.isSystemRule()) {  
38 - return -1;  
39 - } else {  
40 - return Integer.compare(r2.getWeight(), r1.getWeight());  
41 - }  
42 - }  
43 - };  
44 -  
45 - public static RuleActorMetaData systemRule(RuleId ruleId, int weight, ActorRef actorRef) {  
46 - return new RuleActorMetaData(ruleId, true, weight, actorRef);  
47 - }  
48 -  
49 - public static RuleActorMetaData tenantRule(RuleId ruleId, int weight, ActorRef actorRef) {  
50 - return new RuleActorMetaData(ruleId, false, weight, actorRef);  
51 - }  
52 -  
53 - private RuleActorMetaData(RuleId ruleId, boolean systemRule, int weight, ActorRef actorRef) {  
54 - super();  
55 - this.ruleId = ruleId;  
56 - this.systemRule = systemRule;  
57 - this.weight = weight;  
58 - this.actorRef = actorRef;  
59 - }  
60 -  
61 - public RuleId getRuleId() {  
62 - return ruleId;  
63 - }  
64 -  
65 - public boolean isSystemRule() {  
66 - return systemRule;  
67 - }  
68 -  
69 - public int getWeight() {  
70 - return weight;  
71 - }  
72 -  
73 - public ActorRef getActorRef() {  
74 - return actorRef;  
75 - }  
76 -  
77 - @Override  
78 - public int hashCode() {  
79 - final int prime = 31;  
80 - int result = 1;  
81 - result = prime * result + ((ruleId == null) ? 0 : ruleId.hashCode());  
82 - return result;  
83 - }  
84 -  
85 - @Override  
86 - public boolean equals(Object obj) {  
87 - if (this == obj)  
88 - return true;  
89 - if (obj == null)  
90 - return false;  
91 - if (getClass() != obj.getClass())  
92 - return false;  
93 - RuleActorMetaData other = (RuleActorMetaData) obj;  
94 - if (ruleId == null) {  
95 - if (other.ruleId != null)  
96 - return false;  
97 - } else if (!ruleId.equals(other.ruleId))  
98 - return false;  
99 - return true;  
100 - }  
101 -  
102 - @Override  
103 - public String toString() {  
104 - return "RuleActorMetaData [ruleId=" + ruleId + ", systemRule=" + systemRule + ", weight=" + weight + ", actorRef=" + actorRef + "]";  
105 - }  
106 -  
107 -}  
1 -/**  
2 - * Copyright © 2016-2018 The Thingsboard Authors  
3 - *  
4 - * Licensed under the Apache License, Version 2.0 (the "License");  
5 - * you may not use this file except in compliance with the License.  
6 - * You may obtain a copy of the License at  
7 - *  
8 - * http://www.apache.org/licenses/LICENSE-2.0  
9 - *  
10 - * Unless required by applicable law or agreed to in writing, software  
11 - * distributed under the License is distributed on an "AS IS" BASIS,  
12 - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  
13 - * See the License for the specific language governing permissions and  
14 - * limitations under the License.  
15 - */  
16 -package org.thingsboard.server.actors.rule;  
17 -  
18 -import com.google.common.util.concurrent.ListenableFuture;  
19 -import org.thingsboard.server.actors.ActorSystemContext;  
20 -import org.thingsboard.server.common.data.Event;  
21 -import org.thingsboard.server.common.data.alarm.Alarm;  
22 -import org.thingsboard.server.common.data.alarm.AlarmId;  
23 -import org.thingsboard.server.common.data.id.*;  
24 -import org.thingsboard.server.dao.alarm.AlarmService;  
25 -import org.thingsboard.server.dao.event.EventService;  
26 -import org.thingsboard.server.dao.timeseries.TimeseriesService;  
27 -import org.thingsboard.server.common.msg.device.ToDeviceActorMsg;  
28 -import org.thingsboard.server.extensions.api.device.DeviceMetaData;  
29 -import org.thingsboard.server.extensions.api.rules.RuleContext;  
30 -  
31 -import java.util.Optional;  
32 -import java.util.concurrent.ExecutionException;  
33 -  
34 -public class RuleProcessingContext implements RuleContext {  
35 -  
36 - private final TimeseriesService tsService;  
37 - private final EventService eventService;  
38 - private final AlarmService alarmService;  
39 - private final RuleId ruleId;  
40 - private TenantId tenantId;  
41 - private CustomerId customerId;  
42 - private DeviceId deviceId;  
43 - private DeviceMetaData deviceMetaData;  
44 -  
45 - RuleProcessingContext(ActorSystemContext systemContext, RuleId ruleId) {  
46 - this.tsService = systemContext.getTsService();  
47 - this.eventService = systemContext.getEventService();  
48 - this.alarmService = systemContext.getAlarmService();  
49 - this.ruleId = ruleId;  
50 - }  
51 -  
52 - void update(ToDeviceActorMsg toDeviceActorMsg, DeviceMetaData deviceMetaData) {  
53 - this.tenantId = toDeviceActorMsg.getTenantId();  
54 - this.customerId = toDeviceActorMsg.getCustomerId();  
55 - this.deviceId = toDeviceActorMsg.getDeviceId();  
56 - this.deviceMetaData = deviceMetaData;  
57 - }  
58 -  
59 - @Override  
60 - public RuleId getRuleId() {  
61 - return ruleId;  
62 - }  
63 -  
64 - @Override  
65 - public DeviceMetaData getDeviceMetaData() {  
66 - return deviceMetaData;  
67 - }  
68 -  
69 - @Override  
70 - public Event save(Event event) {  
71 - checkEvent(event);  
72 - return eventService.save(event);  
73 - }  
74 -  
75 - @Override  
76 - public Optional<Event> saveIfNotExists(Event event) {  
77 - checkEvent(event);  
78 - return eventService.saveIfNotExists(event);  
79 - }  
80 -  
81 - @Override  
82 - public Optional<Event> findEvent(String eventType, String eventUid) {  
83 - return eventService.findEvent(tenantId, deviceId, eventType, eventUid);  
84 - }  
85 -  
86 - @Override  
87 - public Alarm createOrUpdateAlarm(Alarm alarm) {  
88 - alarm.setTenantId(tenantId);  
89 - return alarmService.createOrUpdateAlarm(alarm);  
90 - }  
91 -  
92 - public Optional<Alarm> findLatestAlarm(EntityId originator, String alarmType) {  
93 - try {  
94 - return Optional.ofNullable(alarmService.findLatestByOriginatorAndType(tenantId, originator, alarmType).get());  
95 - } catch (InterruptedException | ExecutionException e) {  
96 - throw new RuntimeException("Failed to lookup alarm!", e);  
97 - }  
98 - }  
99 -  
100 - @Override  
101 - public ListenableFuture<Boolean> clearAlarm(AlarmId alarmId, long clearTs) {  
102 - return alarmService.clearAlarm(alarmId, clearTs);  
103 - }  
104 -  
105 - private void checkEvent(Event event) {  
106 - if (event.getTenantId() == null) {  
107 - event.setTenantId(tenantId);  
108 - } else if (!tenantId.equals(event.getTenantId())) {  
109 - throw new IllegalArgumentException("Invalid Tenant id!");  
110 - }  
111 - if (event.getEntityId() == null) {  
112 - event.setEntityId(deviceId);  
113 - }  
114 - }  
115 -}  
  1 +/**
  2 + * Copyright © 2016-2018 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.actors.ruleChain;
  17 +
  18 +import akka.actor.ActorRef;
  19 +import com.datastax.driver.core.utils.UUIDs;
  20 +import org.thingsboard.rule.engine.api.ListeningExecutor;
  21 +import org.thingsboard.rule.engine.api.MailService;
  22 +import org.thingsboard.rule.engine.api.RuleEngineDeviceRpcRequest;
  23 +import org.thingsboard.rule.engine.api.RuleEngineDeviceRpcResponse;
  24 +import org.thingsboard.rule.engine.api.RuleEngineRpcService;
  25 +import org.thingsboard.rule.engine.api.RuleEngineTelemetryService;
  26 +import org.thingsboard.rule.engine.api.ScriptEngine;
  27 +import org.thingsboard.rule.engine.api.TbContext;
  28 +import org.thingsboard.rule.engine.api.TbRelationTypes;
  29 +import org.thingsboard.server.actors.ActorSystemContext;
  30 +import org.thingsboard.server.common.data.id.DeviceId;
  31 +import org.thingsboard.server.common.data.id.EntityId;
  32 +import org.thingsboard.server.common.data.id.RuleNodeId;
  33 +import org.thingsboard.server.common.data.id.TenantId;
  34 +import org.thingsboard.server.common.data.rpc.ToDeviceRpcRequestBody;
  35 +import org.thingsboard.server.common.data.rule.RuleNode;
  36 +import org.thingsboard.server.common.msg.TbMsg;
  37 +import org.thingsboard.server.common.msg.TbMsgMetaData;
  38 +import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest;
  39 +import org.thingsboard.server.dao.alarm.AlarmService;
  40 +import org.thingsboard.server.dao.asset.AssetService;
  41 +import org.thingsboard.server.dao.attributes.AttributesService;
  42 +import org.thingsboard.server.dao.customer.CustomerService;
  43 +import org.thingsboard.server.dao.device.DeviceService;
  44 +import org.thingsboard.server.dao.relation.RelationService;
  45 +import org.thingsboard.server.dao.rule.RuleChainService;
  46 +import org.thingsboard.server.dao.timeseries.TimeseriesService;
  47 +import org.thingsboard.server.dao.user.UserService;
  48 +import org.thingsboard.server.service.script.RuleNodeJsScriptEngine;
  49 +import scala.concurrent.duration.Duration;
  50 +
  51 +import java.util.Collections;
  52 +import java.util.Set;
  53 +import java.util.concurrent.TimeUnit;
  54 +import java.util.function.Consumer;
  55 +
  56 +/**
  57 + * Created by ashvayka on 19.03.18.
  58 + */
  59 +class DefaultTbContext implements TbContext {
  60 +
  61 + private final ActorSystemContext mainCtx;
  62 + private final RuleNodeCtx nodeCtx;
  63 +
  64 + public DefaultTbContext(ActorSystemContext mainCtx, RuleNodeCtx nodeCtx) {
  65 + this.mainCtx = mainCtx;
  66 + this.nodeCtx = nodeCtx;
  67 + }
  68 +
  69 + @Override
  70 + public void tellNext(TbMsg msg, String relationType) {
  71 + tellNext(msg, Collections.singleton(relationType), null);
  72 + }
  73 +
  74 + @Override
  75 + public void tellNext(TbMsg msg, Set<String> relationTypes) {
  76 + tellNext(msg, relationTypes, null);
  77 + }
  78 +
  79 + @Override
  80 + public void tellNext(TbMsg msg, String relationType, Throwable th) {
  81 + tellNext(msg, Collections.singleton(relationType), th);
  82 + }
  83 +
  84 + private void tellNext(TbMsg msg, Set<String> relationTypes, Throwable th) {
  85 + if (nodeCtx.getSelf().isDebugMode()) {
  86 + relationTypes.forEach(relationType -> mainCtx.persistDebugOutput(nodeCtx.getTenantId(), nodeCtx.getSelf().getId(), msg, relationType, th));
  87 + }
  88 + nodeCtx.getChainActor().tell(new RuleNodeToRuleChainTellNextMsg(nodeCtx.getSelf().getId(), relationTypes, msg), nodeCtx.getSelfActor());
  89 + }
  90 +
  91 + @Override
  92 + public void tellSelf(TbMsg msg, long delayMs) {
  93 + //TODO: add persistence layer
  94 + scheduleMsgWithDelay(new RuleNodeToSelfMsg(msg), delayMs, nodeCtx.getSelfActor());
  95 + }
  96 +
  97 + private void scheduleMsgWithDelay(Object msg, long delayInMs, ActorRef target) {
  98 + mainCtx.getScheduler().scheduleOnce(Duration.create(delayInMs, TimeUnit.MILLISECONDS), target, msg, mainCtx.getActorSystem().dispatcher(), nodeCtx.getSelfActor());
  99 + }
  100 +
  101 + @Override
  102 + public void tellFailure(TbMsg msg, Throwable th) {
  103 + if (nodeCtx.getSelf().isDebugMode()) {
  104 + mainCtx.persistDebugOutput(nodeCtx.getTenantId(), nodeCtx.getSelf().getId(), msg, TbRelationTypes.FAILURE, th);
  105 + }
  106 + nodeCtx.getChainActor().tell(new RuleNodeToRuleChainTellNextMsg(nodeCtx.getSelf().getId(), Collections.singleton(TbRelationTypes.FAILURE), msg), nodeCtx.getSelfActor());
  107 + }
  108 +
  109 + @Override
  110 + public void updateSelf(RuleNode self) {
  111 + nodeCtx.setSelf(self);
  112 + }
  113 +
  114 + @Override
  115 + public TbMsg newMsg(String type, EntityId originator, TbMsgMetaData metaData, String data) {
  116 + return new TbMsg(UUIDs.timeBased(), type, originator, metaData.copy(), data, nodeCtx.getSelf().getRuleChainId(), nodeCtx.getSelf().getId(), mainCtx.getQueuePartitionId());
  117 + }
  118 +
  119 + @Override
  120 + public TbMsg transformMsg(TbMsg origMsg, String type, EntityId originator, TbMsgMetaData metaData, String data) {
  121 + return new TbMsg(origMsg.getId(), type, originator, metaData.copy(), data, origMsg.getRuleChainId(), origMsg.getRuleNodeId(), mainCtx.getQueuePartitionId());
  122 + }
  123 +
  124 + @Override
  125 + public RuleNodeId getSelfId() {
  126 + return nodeCtx.getSelf().getId();
  127 + }
  128 +
  129 + @Override
  130 + public TenantId getTenantId() {
  131 + return nodeCtx.getTenantId();
  132 + }
  133 +
  134 + @Override
  135 + public ListeningExecutor getJsExecutor() {
  136 + return mainCtx.getJsExecutor();
  137 + }
  138 +
  139 + @Override
  140 + public ListeningExecutor getMailExecutor() {
  141 + return mainCtx.getMailExecutor();
  142 + }
  143 +
  144 + @Override
  145 + public ListeningExecutor getDbCallbackExecutor() {
  146 + return mainCtx.getDbCallbackExecutor();
  147 + }
  148 +
  149 + @Override
  150 + public ListeningExecutor getExternalCallExecutor() {
  151 + return mainCtx.getExternalCallExecutorService();
  152 + }
  153 +
  154 + @Override
  155 + public ScriptEngine createJsScriptEngine(String script, String... argNames) {
  156 + return new RuleNodeJsScriptEngine(mainCtx.getJsSandbox(), script, argNames);
  157 + }
  158 +
  159 + @Override
  160 + public AttributesService getAttributesService() {
  161 + return mainCtx.getAttributesService();
  162 + }
  163 +
  164 + @Override
  165 + public CustomerService getCustomerService() {
  166 + return mainCtx.getCustomerService();
  167 + }
  168 +
  169 + @Override
  170 + public UserService getUserService() {
  171 + return mainCtx.getUserService();
  172 + }
  173 +
  174 + @Override
  175 + public AssetService getAssetService() {
  176 + return mainCtx.getAssetService();
  177 + }
  178 +
  179 + @Override
  180 + public DeviceService getDeviceService() {
  181 + return mainCtx.getDeviceService();
  182 + }
  183 +
  184 + @Override
  185 + public AlarmService getAlarmService() {
  186 + return mainCtx.getAlarmService();
  187 + }
  188 +
  189 + @Override
  190 + public RuleChainService getRuleChainService() {
  191 + return mainCtx.getRuleChainService();
  192 + }
  193 +
  194 + @Override
  195 + public TimeseriesService getTimeseriesService() {
  196 + return mainCtx.getTsService();
  197 + }
  198 +
  199 + @Override
  200 + public RuleEngineTelemetryService getTelemetryService() {
  201 + return mainCtx.getTsSubService();
  202 + }
  203 +
  204 + @Override
  205 + public RelationService getRelationService() {
  206 + return mainCtx.getRelationService();
  207 + }
  208 +
  209 + @Override
  210 + public MailService getMailService() {
  211 + if (mainCtx.isAllowSystemMailService()) {
  212 + return mainCtx.getMailService();
  213 + } else {
  214 + throw new RuntimeException("Access to System Mail Service is forbidden!");
  215 + }
  216 + }
  217 +
  218 + @Override
  219 + public RuleEngineRpcService getRpcService() {
  220 + return new RuleEngineRpcService() {
  221 + @Override
  222 + public void sendRpcReply(DeviceId deviceId, int requestId, String body) {
  223 + mainCtx.getDeviceRpcService().sendRpcReplyToDevice(nodeCtx.getTenantId(), deviceId, requestId, body);
  224 + }
  225 +
  226 + @Override
  227 + public void sendRpcRequest(RuleEngineDeviceRpcRequest src, Consumer<RuleEngineDeviceRpcResponse> consumer) {
  228 + ToDeviceRpcRequest request = new ToDeviceRpcRequest(UUIDs.timeBased(), nodeCtx.getTenantId(), src.getDeviceId(),
  229 + src.isOneway(), System.currentTimeMillis() + src.getTimeout(), new ToDeviceRpcRequestBody(src.getMethod(), src.getBody()));
  230 + mainCtx.getDeviceRpcService().processRpcRequestToDevice(request, response -> {
  231 + consumer.accept(RuleEngineDeviceRpcResponse.builder()
  232 + .deviceId(src.getDeviceId())
  233 + .requestId(src.getRequestId())
  234 + .error(response.getError())
  235 + .response(response.getResponse())
  236 + .build());
  237 + });
  238 + }
  239 + };
  240 + }
  241 +}
  1 +/**
  2 + * Copyright © 2016-2018 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.actors.ruleChain;
  17 +
  18 +import akka.actor.OneForOneStrategy;
  19 +import akka.actor.SupervisorStrategy;
  20 +import org.thingsboard.server.actors.ActorSystemContext;
  21 +import org.thingsboard.server.actors.device.DeviceActorToRuleEngineMsg;
  22 +import org.thingsboard.server.actors.service.ComponentActor;
  23 +import org.thingsboard.server.actors.service.ContextBasedCreator;
  24 +import org.thingsboard.server.common.data.id.RuleChainId;
  25 +import org.thingsboard.server.common.data.id.TenantId;
  26 +import org.thingsboard.server.common.msg.TbActorMsg;
  27 +import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
  28 +import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg;
  29 +import scala.concurrent.duration.Duration;
  30 +
  31 +public class RuleChainActor extends ComponentActor<RuleChainId, RuleChainActorMessageProcessor> {
  32 +
  33 + private RuleChainActor(ActorSystemContext systemContext, TenantId tenantId, RuleChainId ruleChainId) {
  34 + super(systemContext, tenantId, ruleChainId);
  35 + setProcessor(new RuleChainActorMessageProcessor(tenantId, ruleChainId, systemContext,
  36 + logger, context().parent(), context().self()));
  37 + }
  38 +
  39 + @Override
  40 + protected boolean process(TbActorMsg msg) {
  41 + switch (msg.getMsgType()) {
  42 + case COMPONENT_LIFE_CYCLE_MSG:
  43 + onComponentLifecycleMsg((ComponentLifecycleMsg) msg);
  44 + break;
  45 + case SERVICE_TO_RULE_ENGINE_MSG:
  46 + processor.onServiceToRuleEngineMsg((ServiceToRuleEngineMsg) msg);
  47 + break;
  48 + case DEVICE_ACTOR_TO_RULE_ENGINE_MSG:
  49 + processor.onDeviceActorToRuleEngineMsg((DeviceActorToRuleEngineMsg) msg);
  50 + break;
  51 + case RULE_TO_RULE_CHAIN_TELL_NEXT_MSG:
  52 + processor.onTellNext((RuleNodeToRuleChainTellNextMsg) msg);
  53 + break;
  54 + case RULE_CHAIN_TO_RULE_CHAIN_MSG:
  55 + processor.onRuleChainToRuleChainMsg((RuleChainToRuleChainMsg) msg);
  56 + break;
  57 + case CLUSTER_EVENT_MSG:
  58 + break;
  59 + default:
  60 + return false;
  61 + }
  62 + return true;
  63 + }
  64 +
  65 + public static class ActorCreator extends ContextBasedCreator<RuleChainActor> {
  66 + private static final long serialVersionUID = 1L;
  67 +
  68 + private final TenantId tenantId;
  69 + private final RuleChainId ruleChainId;
  70 +
  71 + public ActorCreator(ActorSystemContext context, TenantId tenantId, RuleChainId pluginId) {
  72 + super(context);
  73 + this.tenantId = tenantId;
  74 + this.ruleChainId = pluginId;
  75 + }
  76 +
  77 + @Override
  78 + public RuleChainActor create() throws Exception {
  79 + return new RuleChainActor(context, tenantId, ruleChainId);
  80 + }
  81 + }
  82 +
  83 + @Override
  84 + protected long getErrorPersistFrequency() {
  85 + return systemContext.getRuleChainErrorPersistFrequency();
  86 + }
  87 +
  88 + @Override
  89 + public SupervisorStrategy supervisorStrategy() {
  90 + return strategy;
  91 + }
  92 +
  93 + private final SupervisorStrategy strategy = new OneForOneStrategy(3, Duration.create("1 minute"), t -> {
  94 + logAndPersist("Unknown Failure", ActorSystemContext.toException(t));
  95 + return SupervisorStrategy.resume();
  96 + });
  97 +}