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 20 <modelVersion>4.0.0</modelVersion>
21 21 <parent>
22 22 <groupId>org.thingsboard</groupId>
23   - <version>1.4.1-SNAPSHOT</version>
  23 + <version>2.0.0-SNAPSHOT</version>
24 24 <artifactId>thingsboard</artifactId>
25 25 </parent>
26   - <groupId>org.thingsboard</groupId>
27 26 <artifactId>application</artifactId>
28 27 <packaging>jar</packaging>
29 28
... ... @@ -50,12 +49,12 @@
50 49 <classifier>linux-x86_64</classifier>
51 50 </dependency>
52 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 54 </dependency>
56 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 58 </dependency>
60 59 <dependency>
61 60 <groupId>org.thingsboard.common</groupId>
... ... @@ -257,6 +256,10 @@
257 256 <groupId>org.hsqldb</groupId>
258 257 <artifactId>hsqldb</artifactId>
259 258 </dependency>
  259 + <dependency>
  260 + <groupId>org.javadelight</groupId>
  261 + <artifactId>delight-nashorn-sandbox</artifactId>
  262 + </dependency>
260 263 </dependencies>
261 264
262 265 <build>
... ... @@ -464,48 +467,6 @@
464 467 <artifactId>maven-dependency-plugin</artifactId>
465 468 <executions>
466 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 470 <id>copy-winsw-service</id>
510 471 <phase>package</phase>
511 472 <goals>
... ...
... ... @@ -14,7 +14,7 @@
14 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 18 export LOG_FILENAME=${pkg.name}.out
19 19 export LOADER_PATH=${pkg.installFolder}/conf,${pkg.installFolder}/extensions
20 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   -}
\ No newline at end of file
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   -}
\ No newline at end of file
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   -}
\ No newline at end of file
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   -}
\ No newline at end of file
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   -}
\ No newline at end of file
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   -}
\ No newline at end of file
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   -}
\ No newline at end of file
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   -}
\ No newline at end of file
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   -}
\ No newline at end of file
  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 +}
\ No newline at end of file
... ...
  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 21 import com.fasterxml.jackson.databind.JsonNode;
22 22 import com.fasterxml.jackson.databind.ObjectMapper;
23 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 27 import com.typesafe.config.Config;
25 28 import com.typesafe.config.ConfigFactory;
26 29 import lombok.Getter;
27 30 import lombok.Setter;
  31 +import lombok.extern.slf4j.Slf4j;
28 32 import org.springframework.beans.factory.annotation.Autowired;
29 33 import org.springframework.beans.factory.annotation.Value;
30 34 import org.springframework.stereotype.Component;
  35 +import org.thingsboard.rule.engine.api.MailService;
31 36 import org.thingsboard.server.actors.service.ActorService;
32 37 import org.thingsboard.server.common.data.DataConstants;
33 38 import org.thingsboard.server.common.data.Event;
34 39 import org.thingsboard.server.common.data.id.EntityId;
35 40 import org.thingsboard.server.common.data.id.TenantId;
36 41 import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
  42 +import org.thingsboard.server.common.msg.TbMsg;
37 43 import org.thingsboard.server.common.msg.cluster.ServerAddress;
38 44 import org.thingsboard.server.common.transport.auth.DeviceAuthService;
39   -import org.thingsboard.server.controller.plugin.PluginWebSocketMsgEndpoint;
40 45 import org.thingsboard.server.dao.alarm.AlarmService;
41 46 import org.thingsboard.server.dao.asset.AssetService;
42 47 import org.thingsboard.server.dao.attributes.AttributesService;
... ... @@ -44,118 +49,222 @@ import org.thingsboard.server.dao.audit.AuditLogService;
44 49 import org.thingsboard.server.dao.customer.CustomerService;
45 50 import org.thingsboard.server.dao.device.DeviceService;
46 51 import org.thingsboard.server.dao.event.EventService;
47   -import org.thingsboard.server.dao.plugin.PluginService;
48 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 54 import org.thingsboard.server.dao.tenant.TenantService;
51 55 import org.thingsboard.server.dao.timeseries.TimeseriesService;
  56 +import org.thingsboard.server.dao.user.UserService;
52 57 import org.thingsboard.server.service.cluster.discovery.DiscoveryService;
53 58 import org.thingsboard.server.service.cluster.routing.ClusterRoutingService;
54 59 import org.thingsboard.server.service.cluster.rpc.ClusterRpcService;
55 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 74 import java.io.PrintWriter;
58 75 import java.io.StringWriter;
59 76 import java.util.Optional;
60 77
  78 +@Slf4j
61 79 @Component
62 80 public class ActorSystemContext {
63 81 private static final String AKKA_CONF_FILE_NAME = "actor-system.conf";
64 82
65 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 130 @Autowired
70   - @Getter private DiscoveryService discoveryService;
  131 + @Getter
  132 + private UserService userService;
71 133
72 134 @Autowired
73   - @Getter @Setter private ComponentDiscoveryService componentService;
  135 + @Getter
  136 + private RuleChainService ruleChainService;
74 137
75 138 @Autowired
76   - @Getter private ClusterRoutingService routingService;
  139 + @Getter
  140 + private TimeseriesService tsService;
77 141
78 142 @Autowired
79   - @Getter private ClusterRpcService rpcService;
  143 + @Getter
  144 + private AttributesService attributesService;
80 145
81 146 @Autowired
82   - @Getter private DeviceAuthService deviceAuthService;
  147 + @Getter
  148 + private EventService eventService;
83 149
84 150 @Autowired
85   - @Getter private DeviceService deviceService;
  151 + @Getter
  152 + private AlarmService alarmService;
86 153
87 154 @Autowired
88   - @Getter private AssetService assetService;
  155 + @Getter
  156 + private RelationService relationService;
89 157
90 158 @Autowired
91   - @Getter private TenantService tenantService;
  159 + @Getter
  160 + private AuditLogService auditLogService;
92 161
93 162 @Autowired
94   - @Getter private CustomerService customerService;
  163 + @Getter
  164 + private TelemetrySubscriptionService tsSubService;
95 165
96 166 @Autowired
97   - @Getter private RuleService ruleService;
  167 + @Getter
  168 + private DeviceRpcService deviceRpcService;
98 169
99 170 @Autowired
100   - @Getter private PluginService pluginService;
  171 + @Getter
  172 + private JsSandboxService jsSandbox;
101 173
102 174 @Autowired
103   - @Getter private TimeseriesService tsService;
  175 + @Getter
  176 + private JsExecutorService jsExecutor;
104 177
105 178 @Autowired
106   - @Getter private AttributesService attributesService;
  179 + @Getter
  180 + private MailExecutorService mailExecutor;
107 181
108 182 @Autowired
109   - @Getter private EventService eventService;
  183 + @Getter
  184 + private DbCallbackExecutorService dbCallbackExecutor;
110 185
111 186 @Autowired
112   - @Getter private AlarmService alarmService;
  187 + @Getter
  188 + private ExternalCallExecutorService externalCallExecutorService;
113 189
114 190 @Autowired
115   - @Getter private RelationService relationService;
  191 + @Getter
  192 + private MailService mailService;
116 193
117 194 @Autowired
118   - @Getter private AuditLogService auditLogService;
  195 + @Getter
  196 + private MsgQueueService msgQueueService;
119 197
120 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 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 234 @Value("${actors.statistics.enabled}")
142   - @Getter private boolean statisticsEnabled;
  235 + @Getter
  236 + private boolean statisticsEnabled;
143 237
144 238 @Value("${actors.statistics.persist_frequency}")
145   - @Getter private long statisticsPersistFrequency;
  239 + @Getter
  240 + private long statisticsPersistFrequency;
146 241
147 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 269 public ActorSystemContext() {
161 270 config = ConfigFactory.parseResources(AKKA_CONF_FILE_NAME).withFallback(ConfigFactory.load());
... ... @@ -187,7 +296,7 @@ public class ActorSystemContext {
187 296 eventService.save(event);
188 297 }
189 298
190   - private String toString(Exception e) {
  299 + private String toString(Throwable e) {
191 300 StringWriter sw = new StringWriter();
192 301 e.printStackTrace(new PrintWriter(sw));
193 302 return sw.toString();
... ... @@ -207,4 +316,72 @@ public class ActorSystemContext {
207 316 private JsonNode toBodyJson(ServerAddress server, String method, String body) {
208 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 15 */
16 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 23 import akka.actor.SupervisorStrategy.Directive;
  24 +import akka.actor.Terminated;
20 25 import akka.event.Logging;
21 26 import akka.event.LoggingAdapter;
22 27 import akka.japi.Function;
23 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 30 import org.thingsboard.server.actors.service.ContextBasedCreator;
27 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 33 import org.thingsboard.server.actors.tenant.TenantActor;
34 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 35 import org.thingsboard.server.common.data.id.TenantId;
38 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 43 import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
  44 +import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg;
42 45 import org.thingsboard.server.dao.model.ModelConstants;
43 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 47 import scala.concurrent.duration.Duration;
48 48
49 49 import java.util.HashMap;
50 50 import java.util.Map;
51 51 import java.util.Optional;
52 52
53   -public class AppActor extends ContextAwareActor {
  53 +public class AppActor extends RuleChainManagerActor {
54 54
55 55 private final LoggingAdapter logger = Logging.getLogger(getContext().system(), this);
56 56
57 57 public static final TenantId SYSTEM_TENANT = new TenantId(ModelConstants.NULL_UUID);
58   - private final RuleManager ruleManager;
59   - private final PluginManager pluginManager;
60 58 private final TenantService tenantService;
61 59 private final Map<TenantId, ActorRef> tenantActors;
62 60
63 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 63 this.tenantService = systemContext.getTenantService();
68 64 this.tenantActors = new HashMap<>();
69 65 }
... ... @@ -77,8 +73,7 @@ public class AppActor extends ContextAwareActor {
77 73 public void preStart() {
78 74 logger.info("Starting main system actor.");
79 75 try {
80   - ruleManager.init(this.context());
81   - pluginManager.init(this.context());
  76 + initRuleChains();
82 77
83 78 if (systemContext.isTenantComponentsInitEnabled()) {
84 79 PageDataIterable<Tenant> tenantIterator = new PageDataIterable<>(tenantService::findTenants, ENTITY_PACK_LIMIT);
... ... @@ -96,93 +91,89 @@ public class AppActor extends ContextAwareActor {
96 91 }
97 92
98 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 139 if (SYSTEM_TENANT.equals(msg.getTenantId())) {
134   - target = ruleManager.getOrCreateRuleActor(this.context(), msg.getRuleId());
  140 + //TODO: ashvayka handle this.
135 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 152 private void onComponentLifecycleMsg(ComponentLifecycleMsg msg) {
152   - ActorRef target = null;
  153 + ActorRef target;
153 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 156 } else {
168 157 target = getOrCreateTenantActor(msg.getTenantId());
169 158 }
170 159 if (target != null) {
171 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 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 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 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 17
18 18 import akka.event.Logging;
19 19 import akka.event.LoggingAdapter;
  20 +import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg;
  21 +import org.thingsboard.rule.engine.api.msg.DeviceNameOrTypeUpdateMsg;
20 22 import org.thingsboard.server.actors.ActorSystemContext;
21   -import org.thingsboard.server.actors.rule.RulesProcessedMsg;
22 23 import org.thingsboard.server.actors.service.ContextAwareActor;
23 24 import org.thingsboard.server.actors.service.ContextBasedCreator;
24   -import org.thingsboard.server.actors.tenant.RuleChainDeviceMsg;
25 25 import org.thingsboard.server.common.data.id.DeviceId;
26 26 import org.thingsboard.server.common.data.id.TenantId;
  27 +import org.thingsboard.server.common.msg.TbActorMsg;
27 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 36 public class DeviceActor extends ContextAwareActor {
36 37
37 38 private final LoggingAdapter logger = Logging.getLogger(getContext().system(), this);
38 39
39   - private final TenantId tenantId;
40   - private final DeviceId deviceId;
41 40 private final DeviceActorMessageProcessor processor;
42 41
43 42 private DeviceActor(ActorSystemContext systemContext, TenantId tenantId, DeviceId deviceId) {
44 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 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 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 60 processor.processCredentialsUpdate();
65   - } else if (msg instanceof DeviceNameOrTypeUpdateMsg){
  61 + break;
  62 + case DEVICE_NAME_OR_TYPE_UPDATE_TO_DEVICE_ACTOR_MSG:
66 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 89 public static class ActorCreator extends ContextBasedCreator<DeviceActor> {
... ...
... ... @@ -18,37 +18,75 @@ package org.thingsboard.server.actors.device;
18 18 import akka.actor.ActorContext;
19 19 import akka.actor.ActorRef;
20 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 31 import org.thingsboard.server.actors.ActorSystemContext;
22   -import org.thingsboard.server.actors.rule.*;
23 32 import org.thingsboard.server.actors.shared.AbstractContextAwareMsgProcessor;
24   -import org.thingsboard.server.actors.tenant.RuleChainDeviceMsg;
25 33 import org.thingsboard.server.common.data.DataConstants;
26 34 import org.thingsboard.server.common.data.Device;
27 35 import org.thingsboard.server.common.data.id.DeviceId;
28 36 import org.thingsboard.server.common.data.id.SessionId;
  37 +import org.thingsboard.server.common.data.id.TenantId;
29 38 import org.thingsboard.server.common.data.kv.AttributeKey;
30 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 45 import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;
32 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 65 import org.thingsboard.server.common.msg.kv.BasicAttributeKVMsg;
  66 +import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest;
36 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 69 import org.thingsboard.server.common.msg.session.SessionType;
39 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 90 import java.util.concurrent.TimeoutException;
53 91 import java.util.function.Consumer;
54 92 import java.util.function.Predicate;
... ... @@ -59,46 +97,46 @@ import java.util.stream.Collectors;
59 97 */
60 98 public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
61 99
  100 + private final TenantId tenantId;
62 101 private final DeviceId deviceId;
63 102 private final Map<SessionId, SessionInfo> sessions;
64 103 private final Map<SessionId, SessionInfo> attributeSubscriptions;
65 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 112 private int rpcSeq = 0;
70 113 private String deviceName;
71 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 118 super(systemContext, logger);
  119 + this.tenantId = tenantId;
76 120 this.deviceId = deviceId;
77   - this.sessions = new HashMap<>();
  121 + this.sessions = new LinkedHashMap<>();
78 122 this.attributeSubscriptions = new HashMap<>();
79 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 127 initAttributes();
82 128 }
83 129
84 130 private void initAttributes() {
85   - //TODO: add invalidation of deviceType cache.
86 131 Device device = systemContext.getDeviceService().findDeviceById(deviceId);
87 132 this.deviceName = device.getName();
88 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 140 ToDeviceRpcRequest request = msg.getMsg();
103 141 ToDeviceRpcRequestBody body = request.getBody();
104 142 ToDeviceRpcRequestMsg rpcRequest = new ToDeviceRpcRequestMsg(
... ... @@ -116,7 +154,7 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
116 154 boolean sent = rpcSubscriptions.size() > 0;
117 155 Set<SessionId> syncSessionSet = new HashSet<>();
118 156 rpcSubscriptions.entrySet().forEach(sub -> {
119   - ToDeviceSessionActorMsg response = new BasicToDeviceSessionActorMsg(rpcRequest, sub.getKey());
  157 + ActorSystemToDeviceSessionActorMsg response = new BasicActorSystemToDeviceSessionActorMsg(rpcRequest, sub.getKey());
120 158 sendMsgToSessionActor(response, sub.getValue().getServer());
121 159 if (SessionType.SYNC == sub.getValue().getType()) {
122 160 syncSessionSet.add(sub.getKey());
... ... @@ -125,9 +163,8 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
125 163 syncSessionSet.forEach(rpcSubscriptions::remove);
126 164
127 165 if (request.isOneway() && sent) {
128   - ToPluginRpcResponseDeviceMsg responsePluginMsg = toPluginRpcResponseMsg(msg, (String) null);
129   - context.parent().tell(responsePluginMsg, ActorRef.noSender());
130 166 logger.debug("[{}] Rpc command response sent [{}]!", deviceId, request.getId());
  167 + systemContext.getDeviceRpcService().processRpcResponseFromDevice(new FromDeviceRpcResponse(msg.getMsg().getId(), msg.getServerAddress(), null, null));
131 168 } else {
132 169 registerPendingRpcRequest(context, msg, sent, rpcRequest, timeout);
133 170 }
... ... @@ -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 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 187 if (requestMd != null) {
151 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 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 219 if (type == SessionType.SYNC) {
161 220 logger.debug("[{}] Cleanup sync rpc session [{}]", deviceId, sessionId);
162 221 rpcSubscriptions.remove(sessionId);
... ... @@ -166,41 +225,196 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
166 225 }
167 226 Set<Integer> sentOneWayIds = new HashSet<>();
168 227 if (type == SessionType.ASYNC) {
169   - rpcPendingMap.entrySet().forEach(processPendingRpc(context, sessionId, server, sentOneWayIds));
  228 + toDeviceRpcPendingMap.entrySet().forEach(processPendingRpc(context, sessionId, server, sentOneWayIds));
170 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 236 private Consumer<Map.Entry<Integer, ToDeviceRpcRequestMetadata>> processPendingRpc(ActorContext context, SessionId sessionId, Optional<ServerAddress> server, Set<Integer> sentOneWayIds) {
178 237 return entry -> {
  238 + ToDeviceRpcRequestActorMsg requestActorMsg = entry.getValue().getMsg();
179 239 ToDeviceRpcRequest request = entry.getValue().getMsg().getMsg();
180 240 ToDeviceRpcRequestBody body = request.getBody();
181 241 if (request.isOneway()) {
182 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 245 ToDeviceRpcRequestMsg rpcRequest = new ToDeviceRpcRequestMsg(
187 246 entry.getKey(),
188 247 body.getMethod(),
189 248 body.getParams()
190 249 );
191   - ToDeviceSessionActorMsg response = new BasicToDeviceSessionActorMsg(rpcRequest, sessionId);
  250 + ActorSystemToDeviceSessionActorMsg response = new BasicActorSystemToDeviceSessionActorMsg(rpcRequest, sessionId);
192 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 256 processSubscriptionCommands(context, msg);
198 257 processRpcResponses(context, msg);
199 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 417 void processAttributesUpdate(ActorContext context, DeviceAttributesEventNotificationMsg msg) {
203   - refreshAttributes(msg);
204 418 if (attributeSubscriptions.size() > 0) {
205 419 ToDeviceMsg notification = null;
206 420 if (msg.isDeleted()) {
... ... @@ -221,7 +435,7 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
221 435 if (notification != null) {
222 436 ToDeviceMsg finalNotification = notification;
223 437 attributeSubscriptions.entrySet().forEach(sub -> {
224   - ToDeviceSessionActorMsg response = new BasicToDeviceSessionActorMsg(finalNotification, sub.getKey());
  438 + ActorSystemToDeviceSessionActorMsg response = new BasicActorSystemToDeviceSessionActorMsg(finalNotification, sub.getKey());
225 439 sendMsgToSessionActor(response, sub.getValue().getServer());
226 440 });
227 441 }
... ... @@ -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 448 SessionId sessionId = msg.getSessionId();
248 449 FromDeviceMsg inMsg = msg.getPayload();
249   - if (inMsg.getMsgType() == MsgType.TO_DEVICE_RPC_RESPONSE) {
  450 + if (inMsg.getMsgType() == SessionMsgType.TO_DEVICE_RPC_RESPONSE) {
250 451 logger.debug("[{}] Processing rpc command response [{}]", deviceId, sessionId);
251 452 ToDeviceRpcResponseMsg responseMsg = (ToDeviceRpcResponseMsg) inMsg;
252   - ToDeviceRpcRequestMetadata requestMd = rpcPendingMap.remove(responseMsg.getRequestId());
  453 + ToDeviceRpcRequestMetadata requestMd = toDeviceRpcPendingMap.remove(responseMsg.getRequestId());
253 454 boolean success = requestMd != null;
254 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 458 } else {
265 459 logger.debug("[{}] Rpc command response [{}] is stale!", deviceId, responseMsg.getRequestId());
266 460 }
267 461 if (msg.getSessionType() == SessionType.SYNC) {
268 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 471 if (!msg.isAdded()) {
278 472 logger.debug("[{}] Clearing attributes/rpc subscription for server [{}]", deviceId, msg.getServerAddress());
279 473 Predicate<Map.Entry<SessionId, SessionInfo>> filter = e -> e.getValue().getServer()
... ... @@ -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 481 SessionId sessionId = msg.getSessionId();
320 482 SessionType sessionType = msg.getSessionType();
321 483 FromDeviceMsg inMsg = msg.getPayload();
322   - if (inMsg.getMsgType() == MsgType.SUBSCRIBE_ATTRIBUTES_REQUEST) {
  484 + if (inMsg.getMsgType() == SessionMsgType.SUBSCRIBE_ATTRIBUTES_REQUEST) {
323 485 logger.debug("[{}] Registering attributes subscription for session [{}]", deviceId, sessionId);
324 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 488 logger.debug("[{}] Canceling attributes subscription for session [{}]", deviceId, sessionId);
327 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 491 logger.debug("[{}] Registering rpc subscription for session [{}][{}]", deviceId, sessionId, sessionType);
330 492 rpcSubscriptions.put(sessionId, new SessionInfo(sessionType, msg.getServerAddress()));
331 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 495 logger.debug("[{}] Canceling rpc subscription for session [{}][{}]", deviceId, sessionId, sessionType);
334 496 rpcSubscriptions.remove(sessionId);
335 497 }
336 498 }
337 499
338   - private void processSessionStateMsgs(ToDeviceActorMsg msg) {
  500 + private void processSessionStateMsgs(DeviceToDeviceActorMsg msg) {
339 501 SessionId sessionId = msg.getSessionId();
340 502 FromDeviceMsg inMsg = msg.getPayload();
341 503 if (inMsg instanceof SessionOpenMsg) {
342 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 511 sessions.put(sessionId, new SessionInfo(SessionType.ASYNC, msg.getServerAddress()));
  512 + if (sessions.size() == 1) {
  513 + reportSessionOpen();
  514 + }
344 515 } else if (inMsg instanceof SessionCloseMsg) {
345 516 logger.debug("[{}] Canceling subscriptions for closed session [{}]", deviceId, sessionId);
346 517 sessions.remove(sessionId);
347 518 attributeSubscriptions.remove(sessionId);
348 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 527 if (sessionAddress.isPresent()) {
354 528 ServerAddress address = sessionAddress.get();
355 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 532 } else {
358 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 539 attributeSubscriptions.clear();
377 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 548 this.deviceName = msg.getDeviceName();
382 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 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
16   -package org.thingsboard.server.actors.rule;
  16 +package org.thingsboard.server.actors.device;
17 17
18 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 13 * See the License for the specific language governing permissions and
14 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 16 package org.thingsboard.server.actors.device;
17 17
18 18 import lombok.Data;
19   -import org.thingsboard.server.common.data.id.SessionId;
20 19 import org.thingsboard.server.common.msg.cluster.ServerAddress;
21 20 import org.thingsboard.server.common.msg.session.SessionType;
22 21
... ...
... ... @@ -16,13 +16,13 @@
16 16 package org.thingsboard.server.actors.device;
17 17
18 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 22 * @author Andrew Shvayka
23 23 */
24 24 @Data
25 25 public class ToDeviceRpcRequestMetadata {
26   - private final ToDeviceRpcRequestPluginMsg msg;
  26 + private final ToDeviceRpcRequestActorMsg msg;
27 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 13 * See the License for the specific language governing permissions and
14 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 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 26 * @author Andrew Shvayka
22 27 */
23 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 17
18 18 import akka.actor.ActorRef;
19 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 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 21 import org.thingsboard.server.gen.cluster.ClusterAPIProtos;
36 22 import org.thingsboard.server.service.cluster.rpc.GrpcSession;
37 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 26 * @author Andrew Shvayka
44 27 */
45 28 @Slf4j
46 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 31 private final ActorService service;
51 32 private final ActorRef manager;
52 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 37 this.manager = manager;
58 38 this.self = self;
59 39 }
... ... @@ -73,47 +53,11 @@ public class BasicRpcSessionListener implements GrpcSessionListener {
73 53 }
74 54
75 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 63 @Override
... ... @@ -127,45 +71,5 @@ public class BasicRpcSessionListener implements GrpcSessionListener {
127 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 23 */
24 24 @Data
25 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 23 import org.thingsboard.server.actors.service.ContextAwareActor;
24 24 import org.thingsboard.server.actors.service.ContextBasedCreator;
25 25 import org.thingsboard.server.actors.service.DefaultActorService;
  26 +import org.thingsboard.server.common.msg.TbActorMsg;
26 27 import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;
27 28 import org.thingsboard.server.common.msg.cluster.ServerAddress;
28 29 import org.thingsboard.server.gen.cluster.ClusterAPIProtos;
29 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 39 * @author Andrew Shvayka
... ... @@ -39,7 +44,7 @@ public class RpcManagerActor extends ContextAwareActor {
39 44
40 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 49 private final ServerAddress instance;
45 50
... ... @@ -57,9 +62,15 @@ public class RpcManagerActor extends ContextAwareActor {
57 62 }
58 63
59 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 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 74 } else if (msg instanceof RpcBroadcastMsg) {
64 75 onMsg((RpcBroadcastMsg) msg);
65 76 } else if (msg instanceof RpcSessionCreateRequestMsg) {
... ... @@ -77,24 +88,30 @@ public class RpcManagerActor extends ContextAwareActor {
77 88
78 89 private void onMsg(RpcBroadcastMsg msg) {
79 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 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 158 private void onSessionClose(boolean reconnect, ServerAddress remoteAddress) {
142 159 log.debug("[{}] session closed. Should reconnect: {}", remoteAddress, reconnect);
143 160 SessionActorInfo sessionRef = sessionActors.get(remoteAddress);
144   - if (context().sender().equals(sessionRef.actor)) {
  161 + if (context().sender() != null && context().sender().equals(sessionRef.actor)) {
145 162 sessionActors.remove(remoteAddress);
146 163 pendingMsgs.remove(remoteAddress);
147 164 if (reconnect) {
... ... @@ -160,10 +177,10 @@ public class RpcManagerActor extends ContextAwareActor {
160 177 private void register(ServerAddress remoteAddress, UUID uuid, ActorRef sender) {
161 178 sessionActors.put(remoteAddress, new SessionActorInfo(uuid, sender));
162 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 181 if (data != null) {
165 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 184 } else {
168 185 log.debug("[{}][{}] No pending messages to forward.", remoteAddress, uuid);
169 186 }
... ...
... ... @@ -23,6 +23,7 @@ import io.grpc.stub.StreamObserver;
23 23 import org.thingsboard.server.actors.ActorSystemContext;
24 24 import org.thingsboard.server.actors.service.ContextAwareActor;
25 25 import org.thingsboard.server.actors.service.ContextBasedCreator;
  26 +import org.thingsboard.server.common.msg.TbActorMsg;
26 27 import org.thingsboard.server.common.msg.cluster.ServerAddress;
27 28 import org.thingsboard.server.gen.cluster.ClusterAPIProtos;
28 29 import org.thingsboard.server.gen.cluster.ClusterRpcServiceGrpc;
... ... @@ -31,6 +32,8 @@ import org.thingsboard.server.service.cluster.rpc.GrpcSessionListener;
31 32
32 33 import java.util.UUID;
33 34
  35 +import static org.thingsboard.server.gen.cluster.ClusterAPIProtos.MessageType.CONNECT_RPC_MESSAGE;
  36 +
34 37 /**
35 38 * @author Andrew Shvayka
36 39 */
... ... @@ -48,16 +51,22 @@ public class RpcSessionActor extends ContextAwareActor {
48 51 }
49 52
50 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 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 63 } else if (msg instanceof RpcSessionCreateRequestMsg) {
55 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 72 @Override
... ... @@ -69,7 +78,7 @@ public class RpcSessionActor extends ContextAwareActor {
69 78 private void initSession(RpcSessionCreateRequestMsg msg) {
70 79 log.info("[{}] Initializing session", context().self());
71 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 82 if (msg.getRemoteAddress() == null) {
74 83 // Server session
75 84 session = new GrpcSession(listener);
... ... @@ -84,7 +93,7 @@ public class RpcSessionActor extends ContextAwareActor {
84 93 session.initInputStream();
85 94
86 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 98 session.setOutputStream(outputStream);
90 99 session.initOutputStream();
... ... @@ -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 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 30
31 31 private final UUID msgUid;
32 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 16 package org.thingsboard.server.actors.rpc;
17 17
18 18 import lombok.Data;
19   -import org.thingsboard.server.common.msg.cluster.ServerAddress;
20 19 import org.thingsboard.server.gen.cluster.ClusterAPIProtos;
21 20
22 21 /**
... ... @@ -24,6 +23,5 @@ import org.thingsboard.server.gen.cluster.ClusterAPIProtos;
24 23 */
25 24 @Data
26 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 +}
... ...