Commit 9fce3e068e448d4da34b3f6307432a363ae0fd8d
Committed by
GitHub
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 | ... | ... |
application/src/main/data/json/demo/plugins/demo_device_messaging_rpc_plugin.json
deleted
100644 → 0
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 |
application/src/main/data/json/demo/plugins/demo_email_plugin.json
deleted
100644 → 0
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 |
application/src/main/data/json/demo/plugins/demo_time_rpc_plugin.json
deleted
100644 → 0
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 |
application/src/main/data/json/demo/rules/demo_alarm_rule.json
deleted
100644 → 0
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 |
application/src/main/data/json/demo/rules/demo_gettime_rpc_rule.json
deleted
100644 → 0
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 |
application/src/main/data/json/demo/rules/demo_messaging_rpc_rule.json
deleted
100644 → 0
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 |
application/src/main/data/json/system/plugins/system_rpc_plugin.json
deleted
100644 → 0
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 |
application/src/main/data/json/system/plugins/system_telemetry_plugin.json
deleted
100644 → 0
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 |
application/src/main/data/json/system/rules/system_telemetry_rule.json
deleted
100644 → 0
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 | } | ... | ... |
application/src/main/java/org/thingsboard/server/actors/device/PendingSessionMsgData.java
0 → 100644
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 | -} |
application/src/main/java/org/thingsboard/server/actors/plugin/PluginActorMessageProcessor.java
deleted
100644 → 0
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 | -} |
application/src/main/java/org/thingsboard/server/actors/plugin/PluginCallbackMessage.java
deleted
100644 → 0
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 | -} |
application/src/main/java/org/thingsboard/server/actors/plugin/PluginProcessingContext.java
deleted
100644 → 0
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 | -} |
application/src/main/java/org/thingsboard/server/actors/plugin/RuleToPluginMsgWrapper.java
deleted
100644 → 0
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 | -} |
application/src/main/java/org/thingsboard/server/actors/plugin/SharedPluginProcessingContext.java
deleted
100644 → 0
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,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 | } | ... | ... |
application/src/main/java/org/thingsboard/server/actors/rule/ChainProcessingContext.java
deleted
100644 → 0
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 | -} |
application/src/main/java/org/thingsboard/server/actors/rule/RuleActor.java
deleted
100644 → 0
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 | -} |
application/src/main/java/org/thingsboard/server/actors/rule/RuleActorMessageProcessor.java
deleted
100644 → 0
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 | -} |
application/src/main/java/org/thingsboard/server/actors/rule/RuleActorMetaData.java
deleted
100644 → 0
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 | -} |
application/src/main/java/org/thingsboard/server/actors/rule/RuleProcessingContext.java
deleted
100644 → 0
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 | +} | ... | ... |