Commit 711ff2ab53af0e07d07535342eaca271db6b2bcc
Merge remote-tracking branch 'origin/develop/1.5' into develop/cluter-refactoring
Showing
70 changed files
with
2452 additions
and
851 deletions
Too many changes to show.
To preserve performance only 70 of 269 files are displayed.
... | ... | @@ -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": true, | |
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": true, | |
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 | ... | ... |
... | ... | @@ -28,6 +28,7 @@ CREATE TABLE IF NOT EXISTS rule_chain ( |
28 | 28 | |
29 | 29 | CREATE TABLE IF NOT EXISTS rule_node ( |
30 | 30 | id varchar(31) NOT NULL CONSTRAINT rule_node_pkey PRIMARY KEY, |
31 | + rule_chain_id varchar(31), | |
31 | 32 | additional_info varchar, |
32 | 33 | configuration varchar(10000000), |
33 | 34 | type varchar(255), | ... | ... |
... | ... | @@ -49,6 +49,7 @@ import org.thingsboard.server.dao.customer.CustomerService; |
49 | 49 | import org.thingsboard.server.dao.device.DeviceService; |
50 | 50 | import org.thingsboard.server.dao.event.EventService; |
51 | 51 | import org.thingsboard.server.dao.plugin.PluginService; |
52 | +import org.thingsboard.server.dao.queue.MsgQueue; | |
52 | 53 | import org.thingsboard.server.dao.relation.RelationService; |
53 | 54 | import org.thingsboard.server.dao.rule.RuleChainService; |
54 | 55 | import org.thingsboard.server.dao.rule.RuleService; |
... | ... | @@ -60,8 +61,11 @@ import org.thingsboard.server.service.cluster.routing.ClusterRoutingService; |
60 | 61 | import org.thingsboard.server.service.cluster.rpc.ClusterRpcService; |
61 | 62 | import org.thingsboard.server.service.component.ComponentDiscoveryService; |
62 | 63 | import org.thingsboard.server.service.executors.DbCallbackExecutorService; |
64 | +import org.thingsboard.server.service.executors.ExternalCallExecutorService; | |
63 | 65 | import org.thingsboard.server.service.mail.MailExecutorService; |
66 | +import org.thingsboard.server.service.rpc.DeviceRpcService; | |
64 | 67 | import org.thingsboard.server.service.script.JsExecutorService; |
68 | +import org.thingsboard.server.service.state.DeviceStateService; | |
65 | 69 | import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService; |
66 | 70 | |
67 | 71 | import java.io.IOException; |
... | ... | @@ -163,6 +167,10 @@ public class ActorSystemContext { |
163 | 167 | |
164 | 168 | @Autowired |
165 | 169 | @Getter |
170 | + private DeviceRpcService deviceRpcService; | |
171 | + | |
172 | + @Autowired | |
173 | + @Getter | |
166 | 174 | @Setter |
167 | 175 | private PluginWebSocketMsgEndpoint wsMsgEndpoint; |
168 | 176 | |
... | ... | @@ -180,23 +188,35 @@ public class ActorSystemContext { |
180 | 188 | |
181 | 189 | @Autowired |
182 | 190 | @Getter |
191 | + private ExternalCallExecutorService externalCallExecutorService; | |
192 | + | |
193 | + @Autowired | |
194 | + @Getter | |
183 | 195 | private MailService mailService; |
184 | 196 | |
197 | + @Autowired | |
198 | + @Getter | |
199 | + private MsgQueue msgQueue; | |
200 | + | |
201 | + @Autowired | |
202 | + @Getter | |
203 | + private DeviceStateService deviceStateService; | |
204 | + | |
185 | 205 | @Value("${actors.session.sync.timeout}") |
186 | 206 | @Getter |
187 | 207 | private long syncSessionTimeout; |
188 | 208 | |
189 | - @Value("${actors.plugin.termination.delay}") | |
209 | + @Value("${actors.queue.enabled}") | |
190 | 210 | @Getter |
191 | - private long pluginActorTerminationDelay; | |
211 | + private boolean queuePersistenceEnabled; | |
192 | 212 | |
193 | - @Value("${actors.plugin.processing.timeout}") | |
213 | + @Value("${actors.queue.timeout}") | |
194 | 214 | @Getter |
195 | - private long pluginProcessingTimeout; | |
215 | + private long queuePersistenceTimeout; | |
196 | 216 | |
197 | - @Value("${actors.plugin.error_persist_frequency}") | |
217 | + @Value("${actors.client_side_rpc.timeout}") | |
198 | 218 | @Getter |
199 | - private long pluginErrorPersistFrequency; | |
219 | + private long clientSideRpcTimeout; | |
200 | 220 | |
201 | 221 | @Value("${actors.rule.chain.error_persist_frequency}") |
202 | 222 | @Getter |
... | ... | @@ -206,14 +226,6 @@ public class ActorSystemContext { |
206 | 226 | @Getter |
207 | 227 | private long ruleNodeErrorPersistFrequency; |
208 | 228 | |
209 | - @Value("${actors.rule.termination.delay}") | |
210 | - @Getter | |
211 | - private long ruleActorTerminationDelay; | |
212 | - | |
213 | - @Value("${actors.rule.error_persist_frequency}") | |
214 | - @Getter | |
215 | - private long ruleErrorPersistFrequency; | |
216 | - | |
217 | 229 | @Value("${actors.statistics.enabled}") |
218 | 230 | @Getter |
219 | 231 | private boolean statisticsEnabled; | ... | ... |
... | ... | @@ -29,13 +29,12 @@ import org.thingsboard.server.actors.shared.plugin.SystemPluginManager; |
29 | 29 | import org.thingsboard.server.actors.shared.rulechain.SystemRuleChainManager; |
30 | 30 | import org.thingsboard.server.actors.tenant.TenantActor; |
31 | 31 | import org.thingsboard.server.common.data.Tenant; |
32 | -import org.thingsboard.server.common.data.id.PluginId; | |
33 | -import org.thingsboard.server.common.data.id.RuleChainId; | |
34 | 32 | import org.thingsboard.server.common.data.id.TenantId; |
35 | 33 | import org.thingsboard.server.common.data.page.PageDataIterable; |
36 | 34 | import org.thingsboard.server.common.msg.TbActorMsg; |
37 | -import org.thingsboard.server.common.msg.cluster.ClusterEventMsg; | |
38 | -import org.thingsboard.server.common.msg.device.ToDeviceActorMsg; | |
35 | +import org.thingsboard.server.common.msg.aware.DeviceAwareMsg; | |
36 | +import org.thingsboard.server.common.msg.aware.TenantAwareMsg; | |
37 | +import org.thingsboard.server.common.msg.device.DeviceToDeviceActorMsg; | |
39 | 38 | import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; |
40 | 39 | import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg; |
41 | 40 | import org.thingsboard.server.dao.model.ModelConstants; |
... | ... | @@ -90,12 +89,23 @@ public class AppActor extends RuleChainManagerActor { |
90 | 89 | @Override |
91 | 90 | protected boolean process(TbActorMsg msg) { |
92 | 91 | switch (msg.getMsgType()) { |
92 | + case CLUSTER_EVENT_MSG: | |
93 | + broadcast(msg); | |
94 | + break; | |
93 | 95 | case COMPONENT_LIFE_CYCLE_MSG: |
94 | 96 | onComponentLifecycleMsg((ComponentLifecycleMsg) msg); |
95 | 97 | break; |
96 | 98 | case SERVICE_TO_RULE_ENGINE_MSG: |
97 | 99 | onServiceToRuleEngineMsg((ServiceToRuleEngineMsg) msg); |
98 | 100 | break; |
101 | + case DEVICE_SESSION_TO_DEVICE_ACTOR_MSG: | |
102 | + case DEVICE_ATTRIBUTES_UPDATE_TO_DEVICE_ACTOR_MSG: | |
103 | + case DEVICE_CREDENTIALS_UPDATE_TO_DEVICE_ACTOR_MSG: | |
104 | + case DEVICE_NAME_OR_TYPE_UPDATE_TO_DEVICE_ACTOR_MSG: | |
105 | + case DEVICE_RPC_REQUEST_TO_DEVICE_ACTOR_MSG: | |
106 | + case SERVER_RPC_RESPONSE_TO_DEVICE_ACTOR_MSG: | |
107 | + onToDeviceActorMsg((TenantAwareMsg) msg); | |
108 | + break; | |
99 | 109 | default: |
100 | 110 | return false; |
101 | 111 | } |
... | ... | @@ -110,48 +120,12 @@ public class AppActor extends RuleChainManagerActor { |
110 | 120 | } |
111 | 121 | } |
112 | 122 | |
113 | - | |
114 | -// @Override | |
115 | -// public void onReceive(Object msg) throws Exception { | |
116 | -// logger.debug("Received message: {}", msg); | |
117 | -// if (msg instanceof ToDeviceActorMsg) { | |
118 | -// processDeviceMsg((ToDeviceActorMsg) msg); | |
119 | -// } else if (msg instanceof ToPluginActorMsg) { | |
120 | -// onToPluginMsg((ToPluginActorMsg) msg); | |
121 | -// } else if (msg instanceof ToDeviceActorNotificationMsg) { | |
122 | -// onToDeviceActorMsg((ToDeviceActorNotificationMsg) msg); | |
123 | -// } else if (msg instanceof Terminated) { | |
124 | -// processTermination((Terminated) msg); | |
125 | -// } else if (msg instanceof ClusterEventMsg) { | |
126 | -// broadcast(msg); | |
127 | -// } else if (msg instanceof ComponentLifecycleMsg) { | |
128 | -// onComponentLifecycleMsg((ComponentLifecycleMsg) msg); | |
129 | -// } else if (msg instanceof PluginTerminationMsg) { | |
130 | -// onPluginTerminated((PluginTerminationMsg) msg); | |
131 | -// } else { | |
132 | -// logger.warning("Unknown message: {}!", msg); | |
133 | -// } | |
134 | -// } | |
135 | - | |
136 | - private void onPluginTerminated(PluginTerminationMsg msg) { | |
137 | - pluginManager.remove(msg.getId()); | |
138 | - } | |
139 | - | |
140 | - private void broadcast(Object msg) { | |
141 | - pluginManager.broadcast(msg); | |
123 | + @Override | |
124 | + protected void broadcast(Object msg) { | |
125 | + super.broadcast(msg); | |
142 | 126 | tenantActors.values().forEach(actorRef -> actorRef.tell(msg, ActorRef.noSender())); |
143 | 127 | } |
144 | 128 | |
145 | - private void onToPluginMsg(ToPluginActorMsg msg) { | |
146 | - ActorRef target; | |
147 | - if (SYSTEM_TENANT.equals(msg.getPluginTenantId())) { | |
148 | - target = pluginManager.getOrCreateActor(this.context(), msg.getPluginId()); | |
149 | - } else { | |
150 | - target = getOrCreateTenantActor(msg.getPluginTenantId()); | |
151 | - } | |
152 | - target.tell(msg, ActorRef.noSender()); | |
153 | - } | |
154 | - | |
155 | 129 | private void onComponentLifecycleMsg(ComponentLifecycleMsg msg) { |
156 | 130 | ActorRef target; |
157 | 131 | if (SYSTEM_TENANT.equals(msg.getTenantId())) { |
... | ... | @@ -166,17 +140,17 @@ public class AppActor extends RuleChainManagerActor { |
166 | 140 | } |
167 | 141 | } |
168 | 142 | |
169 | - private void onToDeviceActorMsg(ToDeviceActorNotificationMsg msg) { | |
143 | + private void onToDeviceActorMsg(TenantAwareMsg msg) { | |
170 | 144 | getOrCreateTenantActor(msg.getTenantId()).tell(msg, ActorRef.noSender()); |
171 | 145 | } |
172 | 146 | |
173 | - private void processDeviceMsg(ToDeviceActorMsg toDeviceActorMsg) { | |
174 | - TenantId tenantId = toDeviceActorMsg.getTenantId(); | |
147 | + private void processDeviceMsg(DeviceToDeviceActorMsg deviceToDeviceActorMsg) { | |
148 | + TenantId tenantId = deviceToDeviceActorMsg.getTenantId(); | |
175 | 149 | ActorRef tenantActor = getOrCreateTenantActor(tenantId); |
176 | - if (toDeviceActorMsg.getPayload().getMsgType().requiresRulesProcessing()) { | |
177 | -// tenantActor.tell(new RuleChainDeviceMsg(toDeviceActorMsg, ruleManager.getRuleChain(this.context())), context().self()); | |
150 | + if (deviceToDeviceActorMsg.getPayload().getMsgType().requiresRulesProcessing()) { | |
151 | +// tenantActor.tell(new RuleChainDeviceMsg(deviceToDeviceActorMsg, ruleManager.getRuleChain(this.context())), context().self()); | |
178 | 152 | } else { |
179 | - tenantActor.tell(toDeviceActorMsg, context().self()); | |
153 | + tenantActor.tell(deviceToDeviceActorMsg, context().self()); | |
180 | 154 | } |
181 | 155 | } |
182 | 156 | ... | ... |
... | ... | @@ -24,59 +24,66 @@ import org.thingsboard.server.common.data.id.DeviceId; |
24 | 24 | import org.thingsboard.server.common.data.id.TenantId; |
25 | 25 | import org.thingsboard.server.common.msg.TbActorMsg; |
26 | 26 | import org.thingsboard.server.common.msg.cluster.ClusterEventMsg; |
27 | -import org.thingsboard.server.common.msg.device.ToDeviceActorMsg; | |
27 | +import org.thingsboard.server.common.msg.device.DeviceToDeviceActorMsg; | |
28 | +import org.thingsboard.server.common.msg.timeout.DeviceActorClientSideRpcTimeoutMsg; | |
29 | +import org.thingsboard.server.common.msg.timeout.DeviceActorQueueTimeoutMsg; | |
30 | +import org.thingsboard.server.common.msg.timeout.DeviceActorServerSideRpcTimeoutMsg; | |
28 | 31 | import org.thingsboard.server.extensions.api.device.DeviceAttributesEventNotificationMsg; |
29 | -import org.thingsboard.server.extensions.api.device.DeviceCredentialsUpdateNotificationMsg; | |
30 | 32 | import org.thingsboard.server.extensions.api.device.DeviceNameOrTypeUpdateMsg; |
31 | -import org.thingsboard.server.extensions.api.device.ToDeviceActorNotificationMsg; | |
32 | -import org.thingsboard.server.extensions.api.plugins.msg.TimeoutMsg; | |
33 | -import org.thingsboard.server.extensions.api.plugins.msg.ToDeviceRpcRequestPluginMsg; | |
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 | 48 | protected boolean process(TbActorMsg msg) { |
52 | - return false; | |
53 | - } | |
54 | - | |
55 | - @Override | |
56 | - public void onReceive(Object msg) throws Exception { | |
57 | -// if (msg instanceof RuleChainDeviceMsg) { | |
58 | -// processor.process(context(), (RuleChainDeviceMsg) msg); | |
59 | -// } else if (msg instanceof RulesProcessedMsg) { | |
60 | -// processor.onRulesProcessedMsg(context(), (RulesProcessedMsg) msg); | |
61 | - if (msg instanceof ToDeviceActorMsg) { | |
62 | - processor.process(context(), (ToDeviceActorMsg) msg); | |
63 | - } else if (msg instanceof ToDeviceActorNotificationMsg) { | |
64 | - if (msg instanceof DeviceAttributesEventNotificationMsg) { | |
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: | |
65 | 57 | processor.processAttributesUpdate(context(), (DeviceAttributesEventNotificationMsg) msg); |
66 | - } else if (msg instanceof ToDeviceRpcRequestPluginMsg) { | |
67 | - processor.processRpcRequest(context(), (ToDeviceRpcRequestPluginMsg) msg); | |
68 | - } else if (msg instanceof DeviceCredentialsUpdateNotificationMsg){ | |
58 | + break; | |
59 | + case DEVICE_CREDENTIALS_UPDATE_TO_DEVICE_ACTOR_MSG: | |
69 | 60 | processor.processCredentialsUpdate(); |
70 | - } else if (msg instanceof DeviceNameOrTypeUpdateMsg){ | |
61 | + break; | |
62 | + case DEVICE_NAME_OR_TYPE_UPDATE_TO_DEVICE_ACTOR_MSG: | |
71 | 63 | processor.processNameOrTypeUpdate((DeviceNameOrTypeUpdateMsg) msg); |
72 | - } | |
73 | - } else if (msg instanceof TimeoutMsg) { | |
74 | - processor.processTimeout(context(), (TimeoutMsg) msg); | |
75 | - } else if (msg instanceof ClusterEventMsg) { | |
76 | - processor.processClusterEventMsg((ClusterEventMsg) msg); | |
77 | - } else { | |
78 | - 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; | |
79 | 85 | } |
86 | + return true; | |
80 | 87 | } |
81 | 88 | |
82 | 89 | public static class ActorCreator extends ContextBasedCreator<DeviceActor> { | ... | ... |
... | ... | @@ -18,30 +18,76 @@ 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.JsonArray; | |
27 | +import com.google.gson.JsonObject; | |
28 | +import com.google.gson.JsonParser; | |
21 | 29 | import org.thingsboard.server.actors.ActorSystemContext; |
22 | 30 | import org.thingsboard.server.actors.shared.AbstractContextAwareMsgProcessor; |
23 | 31 | import org.thingsboard.server.common.data.DataConstants; |
24 | 32 | import org.thingsboard.server.common.data.Device; |
25 | 33 | import org.thingsboard.server.common.data.id.DeviceId; |
26 | 34 | import org.thingsboard.server.common.data.id.SessionId; |
35 | +import org.thingsboard.server.common.data.id.TenantId; | |
27 | 36 | import org.thingsboard.server.common.data.kv.AttributeKey; |
28 | 37 | import org.thingsboard.server.common.data.kv.AttributeKvEntry; |
38 | +import org.thingsboard.server.common.data.kv.KvEntry; | |
39 | +import org.thingsboard.server.common.data.rpc.ToDeviceRpcRequestBody; | |
40 | +import org.thingsboard.server.common.msg.TbMsg; | |
41 | +import org.thingsboard.server.common.msg.TbMsgDataType; | |
42 | +import org.thingsboard.server.common.msg.TbMsgMetaData; | |
29 | 43 | import org.thingsboard.server.common.msg.cluster.ClusterEventMsg; |
30 | 44 | import org.thingsboard.server.common.msg.cluster.ServerAddress; |
31 | -import org.thingsboard.server.common.msg.core.*; | |
32 | -import org.thingsboard.server.common.msg.device.ToDeviceActorMsg; | |
45 | +import org.thingsboard.server.common.msg.core.AttributesUpdateNotification; | |
46 | +import org.thingsboard.server.common.msg.core.AttributesUpdateRequest; | |
47 | +import org.thingsboard.server.common.msg.core.BasicCommandAckResponse; | |
48 | +import org.thingsboard.server.common.msg.core.BasicGetAttributesResponse; | |
49 | +import org.thingsboard.server.common.msg.core.BasicStatusCodeResponse; | |
50 | +import org.thingsboard.server.common.msg.core.BasicToDeviceSessionActorMsg; | |
51 | +import org.thingsboard.server.common.msg.core.GetAttributesRequest; | |
52 | +import org.thingsboard.server.common.msg.core.RuleEngineError; | |
53 | +import org.thingsboard.server.common.msg.core.RuleEngineErrorMsg; | |
54 | +import org.thingsboard.server.common.msg.core.SessionCloseMsg; | |
55 | +import org.thingsboard.server.common.msg.core.SessionCloseNotification; | |
56 | +import org.thingsboard.server.common.msg.core.SessionOpenMsg; | |
57 | +import org.thingsboard.server.common.msg.core.TelemetryUploadRequest; | |
58 | +import org.thingsboard.server.common.msg.core.ToDeviceRpcRequestMsg; | |
59 | +import org.thingsboard.server.common.msg.core.ToDeviceRpcResponseMsg; | |
60 | +import org.thingsboard.server.common.msg.core.ToDeviceSessionActorMsg; | |
61 | +import org.thingsboard.server.common.msg.core.ToServerRpcRequestMsg; | |
62 | +import org.thingsboard.server.common.msg.device.DeviceToDeviceActorMsg; | |
33 | 63 | import org.thingsboard.server.common.msg.kv.BasicAttributeKVMsg; |
64 | +import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest; | |
34 | 65 | import org.thingsboard.server.common.msg.session.FromDeviceMsg; |
35 | -import org.thingsboard.server.common.msg.session.MsgType; | |
66 | +import org.thingsboard.server.common.msg.session.FromDeviceRequestMsg; | |
67 | +import org.thingsboard.server.common.msg.session.SessionMsgType; | |
36 | 68 | import org.thingsboard.server.common.msg.session.SessionType; |
37 | 69 | import org.thingsboard.server.common.msg.session.ToDeviceMsg; |
38 | -import org.thingsboard.server.extensions.api.device.DeviceAttributes; | |
70 | +import org.thingsboard.server.common.msg.timeout.DeviceActorClientSideRpcTimeoutMsg; | |
71 | +import org.thingsboard.server.common.msg.timeout.DeviceActorQueueTimeoutMsg; | |
72 | +import org.thingsboard.server.common.msg.timeout.DeviceActorServerSideRpcTimeoutMsg; | |
39 | 73 | import org.thingsboard.server.extensions.api.device.DeviceAttributesEventNotificationMsg; |
40 | 74 | import org.thingsboard.server.extensions.api.device.DeviceNameOrTypeUpdateMsg; |
41 | -import org.thingsboard.server.extensions.api.plugins.msg.*; | |
42 | - | |
43 | -import java.util.*; | |
44 | -import java.util.concurrent.ExecutionException; | |
75 | +import org.thingsboard.server.extensions.api.plugins.msg.FromDeviceRpcResponse; | |
76 | +import org.thingsboard.server.extensions.api.plugins.msg.RpcError; | |
77 | +import org.thingsboard.server.service.rpc.ToDeviceRpcRequestActorMsg; | |
78 | +import org.thingsboard.server.service.rpc.ToServerRpcResponseActorMsg; | |
79 | + | |
80 | +import javax.annotation.Nullable; | |
81 | +import java.util.ArrayList; | |
82 | +import java.util.Arrays; | |
83 | +import java.util.Collections; | |
84 | +import java.util.HashMap; | |
85 | +import java.util.HashSet; | |
86 | +import java.util.List; | |
87 | +import java.util.Map; | |
88 | +import java.util.Optional; | |
89 | +import java.util.Set; | |
90 | +import java.util.UUID; | |
45 | 91 | import java.util.concurrent.TimeoutException; |
46 | 92 | import java.util.function.Consumer; |
47 | 93 | import java.util.function.Predicate; |
... | ... | @@ -52,46 +98,46 @@ import java.util.stream.Collectors; |
52 | 98 | */ |
53 | 99 | public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { |
54 | 100 | |
101 | + private final TenantId tenantId; | |
55 | 102 | private final DeviceId deviceId; |
56 | 103 | private final Map<SessionId, SessionInfo> sessions; |
57 | 104 | private final Map<SessionId, SessionInfo> attributeSubscriptions; |
58 | 105 | private final Map<SessionId, SessionInfo> rpcSubscriptions; |
106 | + private final Map<Integer, ToDeviceRpcRequestMetadata> toDeviceRpcPendingMap; | |
107 | + private final Map<Integer, ToServerRpcRequestMetadata> toServerRpcPendingMap; | |
108 | + private final Map<UUID, PendingSessionMsgData> pendingMsgs; | |
59 | 109 | |
60 | - private final Map<Integer, ToDeviceRpcRequestMetadata> rpcPendingMap; | |
110 | + private final Gson gson = new Gson(); | |
111 | + private final JsonParser jsonParser = new JsonParser(); | |
61 | 112 | |
62 | 113 | private int rpcSeq = 0; |
63 | 114 | private String deviceName; |
64 | 115 | private String deviceType; |
65 | - private DeviceAttributes deviceAttributes; | |
116 | + private TbMsgMetaData defaultMetaData; | |
66 | 117 | |
67 | - public DeviceActorMessageProcessor(ActorSystemContext systemContext, LoggingAdapter logger, DeviceId deviceId) { | |
118 | + public DeviceActorMessageProcessor(ActorSystemContext systemContext, LoggingAdapter logger, TenantId tenantId, DeviceId deviceId) { | |
68 | 119 | super(systemContext, logger); |
120 | + this.tenantId = tenantId; | |
69 | 121 | this.deviceId = deviceId; |
70 | 122 | this.sessions = new HashMap<>(); |
71 | 123 | this.attributeSubscriptions = new HashMap<>(); |
72 | 124 | this.rpcSubscriptions = new HashMap<>(); |
73 | - this.rpcPendingMap = new HashMap<>(); | |
125 | + this.toDeviceRpcPendingMap = new HashMap<>(); | |
126 | + this.toServerRpcPendingMap = new HashMap<>(); | |
127 | + this.pendingMsgs = new HashMap<>(); | |
74 | 128 | initAttributes(); |
75 | 129 | } |
76 | 130 | |
77 | 131 | private void initAttributes() { |
78 | - //TODO: add invalidation of deviceType cache. | |
79 | 132 | Device device = systemContext.getDeviceService().findDeviceById(deviceId); |
80 | 133 | this.deviceName = device.getName(); |
81 | 134 | this.deviceType = device.getType(); |
82 | - this.deviceAttributes = new DeviceAttributes(fetchAttributes(DataConstants.CLIENT_SCOPE), | |
83 | - fetchAttributes(DataConstants.SERVER_SCOPE), fetchAttributes(DataConstants.SHARED_SCOPE)); | |
84 | - } | |
85 | - | |
86 | - private void refreshAttributes(DeviceAttributesEventNotificationMsg msg) { | |
87 | - if (msg.isDeleted()) { | |
88 | - msg.getDeletedKeys().forEach(key -> deviceAttributes.remove(key)); | |
89 | - } else { | |
90 | - deviceAttributes.update(msg.getScope(), msg.getValues()); | |
91 | - } | |
135 | + this.defaultMetaData = new TbMsgMetaData(); | |
136 | + this.defaultMetaData.putValue("deviceName", deviceName); | |
137 | + this.defaultMetaData.putValue("deviceType", deviceType); | |
92 | 138 | } |
93 | 139 | |
94 | - void processRpcRequest(ActorContext context, ToDeviceRpcRequestPluginMsg msg) { | |
140 | + void processRpcRequest(ActorContext context, ToDeviceRpcRequestActorMsg msg) { | |
95 | 141 | ToDeviceRpcRequest request = msg.getMsg(); |
96 | 142 | ToDeviceRpcRequestBody body = request.getBody(); |
97 | 143 | ToDeviceRpcRequestMsg rpcRequest = new ToDeviceRpcRequestMsg( |
... | ... | @@ -118,9 +164,8 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso |
118 | 164 | syncSessionSet.forEach(rpcSubscriptions::remove); |
119 | 165 | |
120 | 166 | if (request.isOneway() && sent) { |
121 | - ToPluginRpcResponseDeviceMsg responsePluginMsg = toPluginRpcResponseMsg(msg, (String) null); | |
122 | - context.parent().tell(responsePluginMsg, ActorRef.noSender()); | |
123 | 167 | logger.debug("[{}] Rpc command response sent [{}]!", deviceId, request.getId()); |
168 | + systemContext.getDeviceRpcService().process(new FromDeviceRpcResponse(msg.getMsg().getId(), null, null)); | |
124 | 169 | } else { |
125 | 170 | registerPendingRpcRequest(context, msg, sent, rpcRequest, timeout); |
126 | 171 | } |
... | ... | @@ -132,24 +177,46 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso |
132 | 177 | |
133 | 178 | } |
134 | 179 | |
135 | - private void registerPendingRpcRequest(ActorContext context, ToDeviceRpcRequestPluginMsg msg, boolean sent, ToDeviceRpcRequestMsg rpcRequest, long timeout) { | |
136 | - rpcPendingMap.put(rpcRequest.getRequestId(), new ToDeviceRpcRequestMetadata(msg, sent)); | |
137 | - TimeoutIntMsg timeoutMsg = new TimeoutIntMsg(rpcRequest.getRequestId(), timeout); | |
180 | + private void registerPendingRpcRequest(ActorContext context, ToDeviceRpcRequestActorMsg msg, boolean sent, ToDeviceRpcRequestMsg rpcRequest, long timeout) { | |
181 | + toDeviceRpcPendingMap.put(rpcRequest.getRequestId(), new ToDeviceRpcRequestMetadata(msg, sent)); | |
182 | + DeviceActorServerSideRpcTimeoutMsg timeoutMsg = new DeviceActorServerSideRpcTimeoutMsg(rpcRequest.getRequestId(), timeout); | |
138 | 183 | scheduleMsgWithDelay(context, timeoutMsg, timeoutMsg.getTimeout()); |
139 | 184 | } |
140 | 185 | |
141 | - public void processTimeout(ActorContext context, TimeoutMsg msg) { | |
142 | - ToDeviceRpcRequestMetadata requestMd = rpcPendingMap.remove(msg.getId()); | |
186 | + void processServerSideRpcTimeout(ActorContext context, DeviceActorServerSideRpcTimeoutMsg msg) { | |
187 | + ToDeviceRpcRequestMetadata requestMd = toDeviceRpcPendingMap.remove(msg.getId()); | |
143 | 188 | if (requestMd != null) { |
144 | 189 | logger.debug("[{}] RPC request [{}] timeout detected!", deviceId, msg.getId()); |
145 | - ToPluginRpcResponseDeviceMsg responsePluginMsg = toPluginRpcResponseMsg(requestMd.getMsg(), requestMd.isSent() ? RpcError.TIMEOUT : RpcError.NO_ACTIVE_CONNECTION); | |
146 | - context.parent().tell(responsePluginMsg, ActorRef.noSender()); | |
190 | + systemContext.getDeviceRpcService().process(new FromDeviceRpcResponse(requestMd.getMsg().getMsg().getId(), | |
191 | + null, requestMd.isSent() ? RpcError.TIMEOUT : RpcError.NO_ACTIVE_CONNECTION)); | |
192 | + } | |
193 | + } | |
194 | + | |
195 | + void processQueueTimeout(ActorContext context, DeviceActorQueueTimeoutMsg msg) { | |
196 | + PendingSessionMsgData data = pendingMsgs.remove(msg.getId()); | |
197 | + if (data != null) { | |
198 | + logger.debug("[{}] Queue put [{}] timeout detected!", deviceId, msg.getId()); | |
199 | + ToDeviceMsg toDeviceMsg = new RuleEngineErrorMsg(data.getSessionMsgType(), RuleEngineError.QUEUE_PUT_TIMEOUT); | |
200 | + sendMsgToSessionActor(new BasicToDeviceSessionActorMsg(toDeviceMsg, data.getSessionId()), data.getServerAddress()); | |
201 | + } | |
202 | + } | |
203 | + | |
204 | + void processQueueAck(ActorContext context, RuleEngineQueuePutAckMsg msg) { | |
205 | + PendingSessionMsgData data = pendingMsgs.remove(msg.getId()); | |
206 | + if (data != null && data.isReplyOnQueueAck()) { | |
207 | + int remainingAcks = data.getAckMsgCount() - 1; | |
208 | + data.setAckMsgCount(remainingAcks); | |
209 | + logger.debug("[{}] Queue put [{}] ack detected. Remaining acks: {}!", deviceId, msg.getId(), remainingAcks); | |
210 | + if (remainingAcks == 0) { | |
211 | + ToDeviceMsg toDeviceMsg = BasicStatusCodeResponse.onSuccess(data.getSessionMsgType(), data.getRequestId()); | |
212 | + sendMsgToSessionActor(new BasicToDeviceSessionActorMsg(toDeviceMsg, data.getSessionId()), data.getServerAddress()); | |
213 | + } | |
147 | 214 | } |
148 | 215 | } |
149 | 216 | |
150 | 217 | private void sendPendingRequests(ActorContext context, SessionId sessionId, SessionType type, Optional<ServerAddress> server) { |
151 | - if (!rpcPendingMap.isEmpty()) { | |
152 | - logger.debug("[{}] Pushing {} pending RPC messages to new async session [{}]", deviceId, rpcPendingMap.size(), sessionId); | |
218 | + if (!toDeviceRpcPendingMap.isEmpty()) { | |
219 | + logger.debug("[{}] Pushing {} pending RPC messages to new async session [{}]", deviceId, toDeviceRpcPendingMap.size(), sessionId); | |
153 | 220 | if (type == SessionType.SYNC) { |
154 | 221 | logger.debug("[{}] Cleanup sync rpc session [{}]", deviceId, sessionId); |
155 | 222 | rpcSubscriptions.remove(sessionId); |
... | ... | @@ -159,12 +226,12 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso |
159 | 226 | } |
160 | 227 | Set<Integer> sentOneWayIds = new HashSet<>(); |
161 | 228 | if (type == SessionType.ASYNC) { |
162 | - rpcPendingMap.entrySet().forEach(processPendingRpc(context, sessionId, server, sentOneWayIds)); | |
229 | + toDeviceRpcPendingMap.entrySet().forEach(processPendingRpc(context, sessionId, server, sentOneWayIds)); | |
163 | 230 | } else { |
164 | - rpcPendingMap.entrySet().stream().findFirst().ifPresent(processPendingRpc(context, sessionId, server, sentOneWayIds)); | |
231 | + toDeviceRpcPendingMap.entrySet().stream().findFirst().ifPresent(processPendingRpc(context, sessionId, server, sentOneWayIds)); | |
165 | 232 | } |
166 | 233 | |
167 | - sentOneWayIds.forEach(rpcPendingMap::remove); | |
234 | + sentOneWayIds.forEach(toDeviceRpcPendingMap::remove); | |
168 | 235 | } |
169 | 236 | |
170 | 237 | private Consumer<Map.Entry<Integer, ToDeviceRpcRequestMetadata>> processPendingRpc(ActorContext context, SessionId sessionId, Optional<ServerAddress> server, Set<Integer> sentOneWayIds) { |
... | ... | @@ -173,8 +240,7 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso |
173 | 240 | ToDeviceRpcRequestBody body = request.getBody(); |
174 | 241 | if (request.isOneway()) { |
175 | 242 | sentOneWayIds.add(entry.getKey()); |
176 | - ToPluginRpcResponseDeviceMsg responsePluginMsg = toPluginRpcResponseMsg(entry.getValue().getMsg(), (String) null); | |
177 | - context.parent().tell(responsePluginMsg, ActorRef.noSender()); | |
243 | + systemContext.getDeviceRpcService().process(new FromDeviceRpcResponse(request.getId(), null, null)); | |
178 | 244 | } |
179 | 245 | ToDeviceRpcRequestMsg rpcRequest = new ToDeviceRpcRequestMsg( |
180 | 246 | entry.getKey(), |
... | ... | @@ -186,14 +252,170 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso |
186 | 252 | }; |
187 | 253 | } |
188 | 254 | |
189 | - void process(ActorContext context, ToDeviceActorMsg msg) { | |
255 | + void process(ActorContext context, DeviceToDeviceActorMsg msg) { | |
190 | 256 | processSubscriptionCommands(context, msg); |
191 | 257 | processRpcResponses(context, msg); |
192 | 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 BasicToDeviceSessionActorMsg(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 BasicToDeviceSessionActorMsg(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, 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 | + json.addProperty("ts", entry.getKey()); | |
359 | + JsonObject values = new JsonObject(); | |
360 | + for (KvEntry kv : entry.getValue()) { | |
361 | + kv.getBooleanValue().ifPresent(v -> values.addProperty(kv.getKey(), v)); | |
362 | + kv.getLongValue().ifPresent(v -> values.addProperty(kv.getKey(), v)); | |
363 | + kv.getDoubleValue().ifPresent(v -> values.addProperty(kv.getKey(), v)); | |
364 | + kv.getStrValue().ifPresent(v -> values.addProperty(kv.getKey(), v)); | |
365 | + } | |
366 | + json.add("values", values); | |
367 | + TbMsg tbMsg = new TbMsg(UUIDs.timeBased(), SessionMsgType.POST_TELEMETRY_REQUEST.name(), deviceId, defaultMetaData, TbMsgDataType.JSON, gson.toJson(json), null, null, 0L); | |
368 | + pushToRuleEngineWithTimeout(context, tbMsg, msgData); | |
369 | + } | |
370 | + } | |
371 | + | |
372 | + private void handleClientSideRPCRequest(ActorContext context, DeviceToDeviceActorMsg src) { | |
373 | + ToServerRpcRequestMsg request = (ToServerRpcRequestMsg) src.getPayload(); | |
374 | + | |
375 | + JsonObject json = new JsonObject(); | |
376 | + json.addProperty("method", request.getMethod()); | |
377 | + json.add("params", jsonParser.parse(request.getParams())); | |
378 | + | |
379 | + TbMsgMetaData requestMetaData = defaultMetaData.copy(); | |
380 | + requestMetaData.putValue("requestId", Integer.toString(request.getRequestId())); | |
381 | + TbMsg tbMsg = new TbMsg(UUIDs.timeBased(), SessionMsgType.TO_SERVER_RPC_REQUEST.name(), deviceId, requestMetaData, TbMsgDataType.JSON, gson.toJson(json), null, null, 0L); | |
382 | + PendingSessionMsgData msgData = new PendingSessionMsgData(src.getSessionId(), src.getServerAddress(), SessionMsgType.TO_SERVER_RPC_REQUEST, request.getRequestId(), false, 1); | |
383 | + pushToRuleEngineWithTimeout(context, tbMsg, msgData); | |
384 | + | |
385 | + scheduleMsgWithDelay(context, new DeviceActorClientSideRpcTimeoutMsg(request.getRequestId(), systemContext.getClientSideRpcTimeout()), systemContext.getClientSideRpcTimeout()); | |
386 | + toServerRpcPendingMap.put(request.getRequestId(), new ToServerRpcRequestMetadata(src.getSessionId(), src.getSessionType(), src.getServerAddress())); | |
387 | + } | |
388 | + | |
389 | + public void processClientSideRpcTimeout(ActorContext context, DeviceActorClientSideRpcTimeoutMsg msg) { | |
390 | + ToServerRpcRequestMetadata data = toServerRpcPendingMap.remove(msg.getId()); | |
391 | + if (data != null) { | |
392 | + logger.debug("[{}] Client side RPC request [{}] timeout detected!", deviceId, msg.getId()); | |
393 | + ToDeviceMsg toDeviceMsg = new RuleEngineErrorMsg(SessionMsgType.TO_SERVER_RPC_REQUEST, RuleEngineError.TIMEOUT); | |
394 | + sendMsgToSessionActor(new BasicToDeviceSessionActorMsg(toDeviceMsg, data.getSessionId()), data.getServer()); | |
395 | + } | |
396 | + } | |
397 | + | |
398 | + void processToServerRPCResponse(ActorContext context, ToServerRpcResponseActorMsg msg) { | |
399 | + ToServerRpcRequestMetadata data = toServerRpcPendingMap.remove(msg.getMsg().getRequestId()); | |
400 | + if (data != null) { | |
401 | + sendMsgToSessionActor(new BasicToDeviceSessionActorMsg(msg.getMsg(), data.getSessionId()), data.getServer()); | |
402 | + } | |
403 | + } | |
404 | + | |
405 | + private void pushToRuleEngineWithTimeout(ActorContext context, TbMsg tbMsg, PendingSessionMsgData pendingMsgData) { | |
406 | + SessionMsgType sessionMsgType = pendingMsgData.getSessionMsgType(); | |
407 | + int requestId = pendingMsgData.getRequestId(); | |
408 | + if (systemContext.isQueuePersistenceEnabled()) { | |
409 | + pendingMsgs.put(tbMsg.getId(), pendingMsgData); | |
410 | + scheduleMsgWithDelay(context, new DeviceActorQueueTimeoutMsg(tbMsg.getId(), systemContext.getQueuePersistenceTimeout()), systemContext.getQueuePersistenceTimeout()); | |
411 | + } else { | |
412 | + ToDeviceSessionActorMsg response = new BasicToDeviceSessionActorMsg(BasicStatusCodeResponse.onSuccess(sessionMsgType, requestId), pendingMsgData.getSessionId()); | |
413 | + sendMsgToSessionActor(response, pendingMsgData.getServerAddress()); | |
414 | + } | |
415 | + context.parent().tell(new DeviceActorToRuleEngineMsg(context.self(), tbMsg), context.self()); | |
193 | 416 | } |
194 | 417 | |
195 | 418 | void processAttributesUpdate(ActorContext context, DeviceAttributesEventNotificationMsg msg) { |
196 | - refreshAttributes(msg); | |
197 | 419 | if (attributeSubscriptions.size() > 0) { |
198 | 420 | ToDeviceMsg notification = null; |
199 | 421 | if (msg.isDeleted()) { |
... | ... | @@ -223,50 +445,29 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso |
223 | 445 | } |
224 | 446 | } |
225 | 447 | |
226 | -// void process(ActorContext context, RuleChainDeviceMsg srcMsg) { | |
227 | -// ChainProcessingMetaData md = new ChainProcessingMetaData(srcMsg.getRuleChain(), | |
228 | -// srcMsg.getToDeviceActorMsg(), new DeviceMetaData(deviceId, deviceName, deviceType, deviceAttributes), context.self()); | |
229 | -// ChainProcessingContext ctx = new ChainProcessingContext(md); | |
230 | -// if (ctx.getChainLength() > 0) { | |
231 | -// RuleProcessingMsg msg = new RuleProcessingMsg(ctx); | |
232 | -// ActorRef ruleActorRef = ctx.getCurrentActor(); | |
233 | -// ruleActorRef.tell(msg, ActorRef.noSender()); | |
234 | -// } else { | |
235 | -// context.self().tell(new RulesProcessedMsg(ctx), context.self()); | |
236 | -// } | |
237 | -// } | |
238 | - | |
239 | - void processRpcResponses(ActorContext context, ToDeviceActorMsg msg) { | |
448 | + private void processRpcResponses(ActorContext context, DeviceToDeviceActorMsg msg) { | |
240 | 449 | SessionId sessionId = msg.getSessionId(); |
241 | 450 | FromDeviceMsg inMsg = msg.getPayload(); |
242 | - if (inMsg.getMsgType() == MsgType.TO_DEVICE_RPC_RESPONSE) { | |
451 | + if (inMsg.getMsgType() == SessionMsgType.TO_DEVICE_RPC_RESPONSE) { | |
243 | 452 | logger.debug("[{}] Processing rpc command response [{}]", deviceId, sessionId); |
244 | 453 | ToDeviceRpcResponseMsg responseMsg = (ToDeviceRpcResponseMsg) inMsg; |
245 | - ToDeviceRpcRequestMetadata requestMd = rpcPendingMap.remove(responseMsg.getRequestId()); | |
454 | + ToDeviceRpcRequestMetadata requestMd = toDeviceRpcPendingMap.remove(responseMsg.getRequestId()); | |
246 | 455 | boolean success = requestMd != null; |
247 | 456 | if (success) { |
248 | - ToPluginRpcResponseDeviceMsg responsePluginMsg = toPluginRpcResponseMsg(requestMd.getMsg(), responseMsg.getData()); | |
249 | - Optional<ServerAddress> pluginServerAddress = requestMd.getMsg().getServerAddress(); | |
250 | - if (pluginServerAddress.isPresent()) { | |
251 | - systemContext.getRpcService().tell(pluginServerAddress.get(), responsePluginMsg); | |
252 | - logger.debug("[{}] Rpc command response sent to remote plugin actor [{}]!", deviceId, requestMd.getMsg().getMsg().getId()); | |
253 | - } else { | |
254 | - context.parent().tell(responsePluginMsg, ActorRef.noSender()); | |
255 | - logger.debug("[{}] Rpc command response sent to local plugin actor [{}]!", deviceId, requestMd.getMsg().getMsg().getId()); | |
256 | - } | |
457 | + systemContext.getDeviceRpcService().process(new FromDeviceRpcResponse(requestMd.getMsg().getMsg().getId(), responseMsg.getData(), null)); | |
257 | 458 | } else { |
258 | 459 | logger.debug("[{}] Rpc command response [{}] is stale!", deviceId, responseMsg.getRequestId()); |
259 | 460 | } |
260 | 461 | if (msg.getSessionType() == SessionType.SYNC) { |
261 | 462 | BasicCommandAckResponse response = success |
262 | - ? BasicCommandAckResponse.onSuccess(MsgType.TO_DEVICE_RPC_REQUEST, responseMsg.getRequestId()) | |
263 | - : BasicCommandAckResponse.onError(MsgType.TO_DEVICE_RPC_REQUEST, responseMsg.getRequestId(), new TimeoutException()); | |
463 | + ? BasicCommandAckResponse.onSuccess(SessionMsgType.TO_DEVICE_RPC_REQUEST, responseMsg.getRequestId()) | |
464 | + : BasicCommandAckResponse.onError(SessionMsgType.TO_DEVICE_RPC_REQUEST, responseMsg.getRequestId(), new TimeoutException()); | |
264 | 465 | sendMsgToSessionActor(new BasicToDeviceSessionActorMsg(response, msg.getSessionId()), msg.getServerAddress()); |
265 | 466 | } |
266 | 467 | } |
267 | 468 | } |
268 | 469 | |
269 | - public void processClusterEventMsg(ClusterEventMsg msg) { | |
470 | + void processClusterEventMsg(ClusterEventMsg msg) { | |
270 | 471 | if (!msg.isAdded()) { |
271 | 472 | logger.debug("[{}] Clearing attributes/rpc subscription for server [{}]", deviceId, msg.getServerAddress()); |
272 | 473 | Predicate<Map.Entry<SessionId, SessionInfo>> filter = e -> e.getValue().getServer() |
... | ... | @@ -276,69 +477,43 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso |
276 | 477 | } |
277 | 478 | } |
278 | 479 | |
279 | - private ToPluginRpcResponseDeviceMsg toPluginRpcResponseMsg(ToDeviceRpcRequestPluginMsg requestMsg, String data) { | |
280 | - return toPluginRpcResponseMsg(requestMsg, data, null); | |
281 | - } | |
282 | - | |
283 | - private ToPluginRpcResponseDeviceMsg toPluginRpcResponseMsg(ToDeviceRpcRequestPluginMsg requestMsg, RpcError error) { | |
284 | - return toPluginRpcResponseMsg(requestMsg, null, error); | |
285 | - } | |
286 | - | |
287 | - private ToPluginRpcResponseDeviceMsg toPluginRpcResponseMsg(ToDeviceRpcRequestPluginMsg requestMsg, String data, RpcError error) { | |
288 | - return new ToPluginRpcResponseDeviceMsg( | |
289 | - requestMsg.getPluginId(), | |
290 | - requestMsg.getPluginTenantId(), | |
291 | - new FromDeviceRpcResponse(requestMsg.getMsg().getId(), | |
292 | - data, | |
293 | - error | |
294 | - ) | |
295 | - ); | |
296 | - } | |
297 | - | |
298 | -// void onRulesProcessedMsg(ActorContext context, RulesProcessedMsg msg) { | |
299 | -// ChainProcessingContext ctx = msg.getCtx(); | |
300 | -// ToDeviceActorMsg inMsg = ctx.getInMsg(); | |
301 | -// SessionId sid = inMsg.getSessionId(); | |
302 | -// ToDeviceSessionActorMsg response; | |
303 | -// if (ctx.getResponse() != null) { | |
304 | -// response = new BasicToDeviceSessionActorMsg(ctx.getResponse(), sid); | |
305 | -// } else { | |
306 | -// response = new BasicToDeviceSessionActorMsg(ctx.getError(), sid); | |
307 | -// } | |
308 | -// sendMsgToSessionActor(response, inMsg.getServerAddress()); | |
309 | -// } | |
310 | - | |
311 | - private void processSubscriptionCommands(ActorContext context, ToDeviceActorMsg msg) { | |
480 | + private void processSubscriptionCommands(ActorContext context, DeviceToDeviceActorMsg msg) { | |
312 | 481 | SessionId sessionId = msg.getSessionId(); |
313 | 482 | SessionType sessionType = msg.getSessionType(); |
314 | 483 | FromDeviceMsg inMsg = msg.getPayload(); |
315 | - if (inMsg.getMsgType() == MsgType.SUBSCRIBE_ATTRIBUTES_REQUEST) { | |
484 | + if (inMsg.getMsgType() == SessionMsgType.SUBSCRIBE_ATTRIBUTES_REQUEST) { | |
316 | 485 | logger.debug("[{}] Registering attributes subscription for session [{}]", deviceId, sessionId); |
317 | 486 | attributeSubscriptions.put(sessionId, new SessionInfo(sessionType, msg.getServerAddress())); |
318 | - } else if (inMsg.getMsgType() == MsgType.UNSUBSCRIBE_ATTRIBUTES_REQUEST) { | |
487 | + } else if (inMsg.getMsgType() == SessionMsgType.UNSUBSCRIBE_ATTRIBUTES_REQUEST) { | |
319 | 488 | logger.debug("[{}] Canceling attributes subscription for session [{}]", deviceId, sessionId); |
320 | 489 | attributeSubscriptions.remove(sessionId); |
321 | - } else if (inMsg.getMsgType() == MsgType.SUBSCRIBE_RPC_COMMANDS_REQUEST) { | |
490 | + } else if (inMsg.getMsgType() == SessionMsgType.SUBSCRIBE_RPC_COMMANDS_REQUEST) { | |
322 | 491 | logger.debug("[{}] Registering rpc subscription for session [{}][{}]", deviceId, sessionId, sessionType); |
323 | 492 | rpcSubscriptions.put(sessionId, new SessionInfo(sessionType, msg.getServerAddress())); |
324 | 493 | sendPendingRequests(context, sessionId, sessionType, msg.getServerAddress()); |
325 | - } else if (inMsg.getMsgType() == MsgType.UNSUBSCRIBE_RPC_COMMANDS_REQUEST) { | |
494 | + } else if (inMsg.getMsgType() == SessionMsgType.UNSUBSCRIBE_RPC_COMMANDS_REQUEST) { | |
326 | 495 | logger.debug("[{}] Canceling rpc subscription for session [{}][{}]", deviceId, sessionId, sessionType); |
327 | 496 | rpcSubscriptions.remove(sessionId); |
328 | 497 | } |
329 | 498 | } |
330 | 499 | |
331 | - private void processSessionStateMsgs(ToDeviceActorMsg msg) { | |
500 | + private void processSessionStateMsgs(DeviceToDeviceActorMsg msg) { | |
332 | 501 | SessionId sessionId = msg.getSessionId(); |
333 | 502 | FromDeviceMsg inMsg = msg.getPayload(); |
334 | 503 | if (inMsg instanceof SessionOpenMsg) { |
335 | 504 | logger.debug("[{}] Processing new session [{}]", deviceId, sessionId); |
336 | 505 | sessions.put(sessionId, new SessionInfo(SessionType.ASYNC, msg.getServerAddress())); |
506 | + if (sessions.size() == 1) { | |
507 | + reportSessionOpen(); | |
508 | + } | |
337 | 509 | } else if (inMsg instanceof SessionCloseMsg) { |
338 | 510 | logger.debug("[{}] Canceling subscriptions for closed session [{}]", deviceId, sessionId); |
339 | 511 | sessions.remove(sessionId); |
340 | 512 | attributeSubscriptions.remove(sessionId); |
341 | 513 | rpcSubscriptions.remove(sessionId); |
514 | + if (sessions.isEmpty()) { | |
515 | + reportSessionClose(); | |
516 | + } | |
342 | 517 | } |
343 | 518 | } |
344 | 519 | |
... | ... | @@ -352,17 +527,7 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso |
352 | 527 | } |
353 | 528 | } |
354 | 529 | |
355 | - private List<AttributeKvEntry> fetchAttributes(String scope) { | |
356 | - try { | |
357 | - //TODO: replace this with async operation. Happens only during actor creation, but is still criticla for performance, | |
358 | - return systemContext.getAttributesService().findAll(this.deviceId, scope).get(); | |
359 | - } catch (InterruptedException | ExecutionException e) { | |
360 | - logger.warning("[{}] Failed to fetch attributes for scope: {}", deviceId, scope); | |
361 | - throw new RuntimeException(e); | |
362 | - } | |
363 | - } | |
364 | - | |
365 | - public void processCredentialsUpdate() { | |
530 | + void processCredentialsUpdate() { | |
366 | 531 | sessions.forEach((k, v) -> { |
367 | 532 | sendMsgToSessionActor(new BasicToDeviceSessionActorMsg(new SessionCloseNotification(), k), v.getServer()); |
368 | 533 | }); |
... | ... | @@ -370,8 +535,12 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso |
370 | 535 | rpcSubscriptions.clear(); |
371 | 536 | } |
372 | 537 | |
373 | - public void processNameOrTypeUpdate(DeviceNameOrTypeUpdateMsg msg) { | |
538 | + void processNameOrTypeUpdate(DeviceNameOrTypeUpdateMsg msg) { | |
374 | 539 | this.deviceName = msg.getDeviceName(); |
375 | 540 | this.deviceType = msg.getDeviceType(); |
541 | + this.defaultMetaData = new TbMsgMetaData(); | |
542 | + this.defaultMetaData.putValue("deviceName", deviceName); | |
543 | + this.defaultMetaData.putValue("deviceType", deviceType); | |
376 | 544 | } |
545 | + | |
377 | 546 | } | ... | ... |
application/src/main/java/org/thingsboard/server/actors/device/DeviceActorToRuleEngineMsg.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 akka.actor.ActorRef; | |
19 | +import lombok.Data; | |
20 | +import org.thingsboard.server.common.data.id.TenantId; | |
21 | +import org.thingsboard.server.common.msg.MsgType; | |
22 | +import org.thingsboard.server.common.msg.TbActorMsg; | |
23 | +import org.thingsboard.server.common.msg.TbMsg; | |
24 | + | |
25 | +/** | |
26 | + * Created by ashvayka on 15.03.18. | |
27 | + */ | |
28 | +@Data | |
29 | +public final class DeviceActorToRuleEngineMsg implements TbActorMsg { | |
30 | + | |
31 | + private final ActorRef callbackRef; | |
32 | + private final TbMsg tbMsg; | |
33 | + | |
34 | + @Override | |
35 | + public MsgType getMsgType() { | |
36 | + return MsgType.DEVICE_ACTOR_TO_RULE_ENGINE_MSG; | |
37 | + } | |
38 | +} | ... | ... |
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
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.Data; | |
19 | +import org.thingsboard.server.common.msg.MsgType; | |
20 | +import org.thingsboard.server.common.msg.TbActorMsg; | |
21 | + | |
22 | +import java.util.UUID; | |
23 | + | |
24 | +/** | |
25 | + * Created by ashvayka on 15.03.18. | |
26 | + */ | |
27 | +@Data | |
28 | +public final class RuleEngineQueuePutAckMsg implements TbActorMsg { | |
29 | + | |
30 | + private final UUID id; | |
31 | + | |
32 | + @Override | |
33 | + public MsgType getMsgType() { | |
34 | + return MsgType.RULE_ENGINE_QUEUE_PUT_ACK_MSG; | |
35 | + } | |
36 | +} | ... | ... |
... | ... | @@ -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
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.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; | |
24 | + | |
25 | +/** | |
26 | + * @author Andrew Shvayka | |
27 | + */ | |
28 | +@Data | |
29 | +public class ToServerRpcRequestMetadata { | |
30 | + private final SessionId sessionId; | |
31 | + private final SessionType type; | |
32 | + private final Optional<ServerAddress> server; | |
33 | +} | ... | ... |
... | ... | @@ -26,7 +26,7 @@ import org.thingsboard.server.common.data.id.TenantId; |
26 | 26 | import org.thingsboard.server.common.msg.TbActorMsg; |
27 | 27 | import org.thingsboard.server.common.msg.cluster.ClusterEventMsg; |
28 | 28 | import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; |
29 | -import org.thingsboard.server.extensions.api.plugins.msg.TimeoutMsg; | |
29 | +import org.thingsboard.server.common.msg.timeout.TimeoutMsg; | |
30 | 30 | import org.thingsboard.server.extensions.api.plugins.msg.ToPluginRpcResponseDeviceMsg; |
31 | 31 | import org.thingsboard.server.extensions.api.plugins.rest.PluginRestMsg; |
32 | 32 | import org.thingsboard.server.extensions.api.plugins.rpc.PluginRpcMsg; |
... | ... | @@ -153,6 +153,7 @@ public class PluginActor extends ComponentActor<PluginId, PluginActorMessageProc |
153 | 153 | |
154 | 154 | @Override |
155 | 155 | protected long getErrorPersistFrequency() { |
156 | - return systemContext.getPluginErrorPersistFrequency(); | |
156 | + return 0; | |
157 | +// return systemContext.getPluginErrorPersistFrequency(); | |
157 | 158 | } |
158 | 159 | } | ... | ... |
... | ... | @@ -30,13 +30,14 @@ import org.thingsboard.server.common.msg.cluster.ClusterEventMsg; |
30 | 30 | import org.thingsboard.server.common.msg.cluster.ServerAddress; |
31 | 31 | import org.thingsboard.server.common.msg.core.BasicStatusCodeResponse; |
32 | 32 | import org.thingsboard.server.common.msg.session.FromDeviceRequestMsg; |
33 | -import org.thingsboard.server.common.msg.session.MsgType; | |
33 | +import org.thingsboard.server.common.msg.session.SessionMsgType; | |
34 | +import org.thingsboard.server.common.msg.session.SessionMsgType; | |
34 | 35 | import org.thingsboard.server.extensions.api.plugins.Plugin; |
35 | 36 | import org.thingsboard.server.extensions.api.plugins.PluginInitializationException; |
36 | 37 | import org.thingsboard.server.extensions.api.plugins.msg.FromDeviceRpcResponse; |
37 | 38 | import org.thingsboard.server.extensions.api.plugins.msg.ResponsePluginToRuleMsg; |
38 | 39 | import org.thingsboard.server.extensions.api.plugins.msg.RuleToPluginMsg; |
39 | -import org.thingsboard.server.extensions.api.plugins.msg.TimeoutMsg; | |
40 | +import org.thingsboard.server.common.msg.timeout.TimeoutMsg; | |
40 | 41 | import org.thingsboard.server.extensions.api.plugins.rest.PluginRestMsg; |
41 | 42 | import org.thingsboard.server.extensions.api.plugins.rpc.PluginRpcMsg; |
42 | 43 | import org.thingsboard.server.extensions.api.plugins.ws.msg.PluginWebsocketMsg; |
... | ... | @@ -108,7 +109,7 @@ public class PluginActorMessageProcessor extends ComponentMsgProcessor<PluginId> |
108 | 109 | } catch (Exception ex) { |
109 | 110 | logger.debug("[{}] Failed to process RuleToPlugin msg: [{}] [{}]", tenantId, msg.getMsg(), ex); |
110 | 111 | RuleToPluginMsg ruleMsg = msg.getMsg(); |
111 | - MsgType responceMsgType = MsgType.RULE_ENGINE_ERROR; | |
112 | + SessionMsgType responceMsgType = SessionMsgType.RULE_ENGINE_ERROR; | |
112 | 113 | Integer requestId = 0; |
113 | 114 | if (ruleMsg.getPayload() instanceof FromDeviceRequestMsg) { |
114 | 115 | requestId = ((FromDeviceRequestMsg) ruleMsg.getPayload()).getRequestId(); |
... | ... | @@ -216,7 +217,7 @@ public class PluginActorMessageProcessor extends ComponentMsgProcessor<PluginId> |
216 | 217 | @Override |
217 | 218 | public void onStop(ActorContext context) { |
218 | 219 | onStop(); |
219 | - scheduleMsgWithDelay(context, new PluginTerminationMsg(entityId), systemContext.getPluginActorTerminationDelay()); | |
220 | +// scheduleMsgWithDelay(context, new PluginTerminationMsg(entityId), systemContext.getPluginActorTerminationDelay()); | |
220 | 221 | } |
221 | 222 | |
222 | 223 | private void onStop() { | ... | ... |
... | ... | @@ -36,9 +36,12 @@ import org.thingsboard.server.common.data.page.TextPageLink; |
36 | 36 | import org.thingsboard.server.common.data.plugin.PluginMetaData; |
37 | 37 | import org.thingsboard.server.common.data.relation.EntityRelation; |
38 | 38 | import org.thingsboard.server.common.data.relation.RelationTypeGroup; |
39 | +import org.thingsboard.server.common.data.rpc.ToDeviceRpcRequestBody; | |
39 | 40 | import org.thingsboard.server.common.data.rule.RuleChain; |
40 | 41 | import org.thingsboard.server.common.data.rule.RuleMetaData; |
41 | 42 | import org.thingsboard.server.common.msg.cluster.ServerAddress; |
43 | +import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest; | |
44 | +import org.thingsboard.server.common.msg.timeout.TimeoutMsg; | |
42 | 45 | import org.thingsboard.server.extensions.api.device.DeviceAttributesEventNotificationMsg; |
43 | 46 | import org.thingsboard.server.extensions.api.plugins.PluginApiCallSecurityContext; |
44 | 47 | import org.thingsboard.server.extensions.api.plugins.PluginCallback; |
... | ... | @@ -348,7 +351,7 @@ public final class PluginProcessingContext implements PluginContext { |
348 | 351 | throw new IllegalStateException("Not Implemented!"); |
349 | 352 | } |
350 | 353 | } else { |
351 | - callback.onSuccess(this, ValidationResult.ok()); | |
354 | + callback.onSuccess(this, ValidationResult.ok(null)); | |
352 | 355 | } |
353 | 356 | } |
354 | 357 | |
... | ... | @@ -366,7 +369,7 @@ public final class PluginProcessingContext implements PluginContext { |
366 | 369 | } else if (ctx.isCustomerUser() && !device.getCustomerId().equals(ctx.getCustomerId())) { |
367 | 370 | return ValidationResult.accessDenied("Device doesn't belong to the current Customer!"); |
368 | 371 | } else { |
369 | - return ValidationResult.ok(); | |
372 | + return ValidationResult.ok(null); | |
370 | 373 | } |
371 | 374 | } |
372 | 375 | })); |
... | ... | @@ -387,7 +390,7 @@ public final class PluginProcessingContext implements PluginContext { |
387 | 390 | } else if (ctx.isCustomerUser() && !asset.getCustomerId().equals(ctx.getCustomerId())) { |
388 | 391 | return ValidationResult.accessDenied("Asset doesn't belong to the current Customer!"); |
389 | 392 | } else { |
390 | - return ValidationResult.ok(); | |
393 | + return ValidationResult.ok(null); | |
391 | 394 | } |
392 | 395 | } |
393 | 396 | })); |
... | ... | @@ -408,7 +411,7 @@ public final class PluginProcessingContext implements PluginContext { |
408 | 411 | } else if (ctx.isSystemAdmin() && !rule.getTenantId().isNullUid()) { |
409 | 412 | return ValidationResult.accessDenied("Rule is not in system scope!"); |
410 | 413 | } else { |
411 | - return ValidationResult.ok(); | |
414 | + return ValidationResult.ok(null); | |
412 | 415 | } |
413 | 416 | } |
414 | 417 | })); |
... | ... | @@ -429,7 +432,7 @@ public final class PluginProcessingContext implements PluginContext { |
429 | 432 | } else if (ctx.isSystemAdmin() && !ruleChain.getTenantId().isNullUid()) { |
430 | 433 | return ValidationResult.accessDenied("Rule chain is not in system scope!"); |
431 | 434 | } else { |
432 | - return ValidationResult.ok(); | |
435 | + return ValidationResult.ok(null); | |
433 | 436 | } |
434 | 437 | } |
435 | 438 | })); |
... | ... | @@ -451,7 +454,7 @@ public final class PluginProcessingContext implements PluginContext { |
451 | 454 | } else if (ctx.isSystemAdmin() && !plugin.getTenantId().isNullUid()) { |
452 | 455 | return ValidationResult.accessDenied("Plugin is not in system scope!"); |
453 | 456 | } else { |
454 | - return ValidationResult.ok(); | |
457 | + return ValidationResult.ok(null); | |
455 | 458 | } |
456 | 459 | } |
457 | 460 | })); |
... | ... | @@ -472,7 +475,7 @@ public final class PluginProcessingContext implements PluginContext { |
472 | 475 | } else if (ctx.isCustomerUser() && !customer.getId().equals(ctx.getCustomerId())) { |
473 | 476 | return ValidationResult.accessDenied("Customer doesn't relate to the currently authorized customer user!"); |
474 | 477 | } else { |
475 | - return ValidationResult.ok(); | |
478 | + return ValidationResult.ok(null); | |
476 | 479 | } |
477 | 480 | } |
478 | 481 | })); |
... | ... | @@ -483,7 +486,7 @@ public final class PluginProcessingContext implements PluginContext { |
483 | 486 | if (ctx.isCustomerUser()) { |
484 | 487 | callback.onSuccess(this, ValidationResult.accessDenied(CUSTOMER_USER_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION)); |
485 | 488 | } else if (ctx.isSystemAdmin()) { |
486 | - callback.onSuccess(this, ValidationResult.ok()); | |
489 | + callback.onSuccess(this, ValidationResult.ok(null)); | |
487 | 490 | } else { |
488 | 491 | ListenableFuture<Tenant> tenantFuture = pluginCtx.tenantService.findTenantByIdAsync(new TenantId(entityId.getId())); |
489 | 492 | Futures.addCallback(tenantFuture, getCallback(callback, tenant -> { |
... | ... | @@ -492,7 +495,7 @@ public final class PluginProcessingContext implements PluginContext { |
492 | 495 | } else if (!tenant.getId().equals(ctx.getTenantId())) { |
493 | 496 | return ValidationResult.accessDenied("Tenant doesn't relate to the currently authorized user!"); |
494 | 497 | } else { |
495 | - return ValidationResult.ok(); | |
498 | + return ValidationResult.ok(null); | |
496 | 499 | } |
497 | 500 | })); |
498 | 501 | } | ... | ... |
... | ... | @@ -18,13 +18,13 @@ package org.thingsboard.server.actors.plugin; |
18 | 18 | import akka.actor.ActorRef; |
19 | 19 | import lombok.extern.slf4j.Slf4j; |
20 | 20 | import org.thingsboard.server.actors.ActorSystemContext; |
21 | -import org.thingsboard.server.common.data.Device; | |
22 | 21 | import org.thingsboard.server.common.data.id.DeviceId; |
23 | -import org.thingsboard.server.common.data.id.EntityId; | |
22 | +import org.thingsboard.server.common.data.id.PluginId; | |
24 | 23 | import org.thingsboard.server.common.data.id.TenantId; |
25 | 24 | import org.thingsboard.server.common.msg.cluster.ServerAddress; |
25 | +import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest; | |
26 | +import org.thingsboard.server.common.msg.timeout.TimeoutMsg; | |
26 | 27 | import org.thingsboard.server.controller.plugin.PluginWebSocketMsgEndpoint; |
27 | -import org.thingsboard.server.common.data.id.PluginId; | |
28 | 28 | import org.thingsboard.server.dao.asset.AssetService; |
29 | 29 | import org.thingsboard.server.dao.attributes.AttributesService; |
30 | 30 | import org.thingsboard.server.dao.audit.AuditLogService; |
... | ... | @@ -37,9 +37,6 @@ import org.thingsboard.server.dao.rule.RuleService; |
37 | 37 | import org.thingsboard.server.dao.tenant.TenantService; |
38 | 38 | import org.thingsboard.server.dao.timeseries.TimeseriesService; |
39 | 39 | import org.thingsboard.server.extensions.api.device.DeviceAttributesEventNotificationMsg; |
40 | -import org.thingsboard.server.extensions.api.plugins.msg.TimeoutMsg; | |
41 | -import org.thingsboard.server.extensions.api.plugins.msg.ToDeviceRpcRequest; | |
42 | -import org.thingsboard.server.extensions.api.plugins.msg.ToDeviceRpcRequestPluginMsg; | |
43 | 40 | import org.thingsboard.server.service.cluster.routing.ClusterRoutingService; |
44 | 41 | import org.thingsboard.server.service.cluster.rpc.ClusterRpcService; |
45 | 42 | import scala.concurrent.duration.Duration; |
... | ... | @@ -108,8 +105,8 @@ public final class SharedPluginProcessingContext { |
108 | 105 | |
109 | 106 | public void sendRpcRequest(ToDeviceRpcRequest msg) { |
110 | 107 | log.trace("[{}] Forwarding msg {} to device actor!", pluginId, msg); |
111 | - ToDeviceRpcRequestPluginMsg rpcMsg = new ToDeviceRpcRequestPluginMsg(pluginId, tenantId, msg); | |
112 | - forward(msg.getDeviceId(), rpcMsg, rpcService::tell); | |
108 | +// ToDeviceRpcRequestPluginMsg rpcMsg = new ToDeviceRpcRequestPluginMsg(pluginId, tenantId, msg); | |
109 | +// forward(msg.getDeviceId(), rpcMsg, rpcService::tell); | |
113 | 110 | } |
114 | 111 | |
115 | 112 | private <T> void forward(DeviceId deviceId, T msg, BiConsumer<ServerAddress, T> rpcFunction) { | ... | ... |
... | ... | @@ -20,29 +20,30 @@ import lombok.Data; |
20 | 20 | |
21 | 21 | @Data |
22 | 22 | @AllArgsConstructor |
23 | -public class ValidationResult { | |
23 | +public class ValidationResult<V> { | |
24 | 24 | |
25 | 25 | private final ValidationResultCode resultCode; |
26 | 26 | private final String message; |
27 | + private final V v; | |
27 | 28 | |
28 | - public static ValidationResult ok() { | |
29 | - return new ValidationResult(ValidationResultCode.OK, "Ok"); | |
29 | + public static <V> ValidationResult<V> ok(V v) { | |
30 | + return new ValidationResult<>(ValidationResultCode.OK, "Ok", v); | |
30 | 31 | } |
31 | 32 | |
32 | - public static ValidationResult accessDenied(String message) { | |
33 | - return new ValidationResult(ValidationResultCode.ACCESS_DENIED, message); | |
33 | + public static <V> ValidationResult<V> accessDenied(String message) { | |
34 | + return new ValidationResult<>(ValidationResultCode.ACCESS_DENIED, message, null); | |
34 | 35 | } |
35 | 36 | |
36 | - public static ValidationResult entityNotFound(String message) { | |
37 | - return new ValidationResult(ValidationResultCode.ENTITY_NOT_FOUND, message); | |
37 | + public static <V> ValidationResult<V> entityNotFound(String message) { | |
38 | + return new ValidationResult<>(ValidationResultCode.ENTITY_NOT_FOUND, message, null); | |
38 | 39 | } |
39 | 40 | |
40 | - public static ValidationResult unauthorized(String message) { | |
41 | - return new ValidationResult(ValidationResultCode.UNAUTHORIZED, message); | |
41 | + public static <V> ValidationResult<V> unauthorized(String message) { | |
42 | + return new ValidationResult<>(ValidationResultCode.UNAUTHORIZED, message, null); | |
42 | 43 | } |
43 | 44 | |
44 | - public static ValidationResult internalError(String message) { | |
45 | - return new ValidationResult(ValidationResultCode.INTERNAL_ERROR, message); | |
45 | + public static <V> ValidationResult<V> internalError(String message) { | |
46 | + return new ValidationResult<>(ValidationResultCode.INTERNAL_ERROR, message, null); | |
46 | 47 | } |
47 | 48 | |
48 | 49 | } | ... | ... |
... | ... | @@ -24,10 +24,12 @@ import org.thingsboard.server.actors.service.ActorService; |
24 | 24 | import org.thingsboard.server.common.data.id.DeviceId; |
25 | 25 | import org.thingsboard.server.common.data.id.PluginId; |
26 | 26 | import org.thingsboard.server.common.data.id.TenantId; |
27 | +import org.thingsboard.server.common.data.rpc.ToDeviceRpcRequestBody; | |
27 | 28 | import org.thingsboard.server.common.msg.cluster.ServerAddress; |
28 | 29 | import org.thingsboard.server.common.msg.cluster.ToAllNodesMsg; |
29 | 30 | import org.thingsboard.server.common.msg.core.ToDeviceSessionActorMsg; |
30 | -import org.thingsboard.server.common.msg.device.ToDeviceActorMsg; | |
31 | +import org.thingsboard.server.common.msg.device.DeviceToDeviceActorMsg; | |
32 | +import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest; | |
31 | 33 | import org.thingsboard.server.extensions.api.device.ToDeviceActorNotificationMsg; |
32 | 34 | import org.thingsboard.server.extensions.api.plugins.msg.*; |
33 | 35 | import org.thingsboard.server.extensions.api.plugins.rpc.PluginRpcMsg; |
... | ... | @@ -35,6 +37,7 @@ import org.thingsboard.server.extensions.api.plugins.rpc.RpcMsg; |
35 | 37 | import org.thingsboard.server.gen.cluster.ClusterAPIProtos; |
36 | 38 | import org.thingsboard.server.service.cluster.rpc.GrpcSession; |
37 | 39 | import org.thingsboard.server.service.cluster.rpc.GrpcSessionListener; |
40 | +import org.thingsboard.server.service.rpc.ToDeviceRpcRequestActorMsg; | |
38 | 41 | |
39 | 42 | import java.io.Serializable; |
40 | 43 | import java.util.UUID; |
... | ... | @@ -83,7 +86,7 @@ public class BasicRpcSessionListener implements GrpcSessionListener { |
83 | 86 | @Override |
84 | 87 | public void onToDeviceActorRpcMsg(GrpcSession session, ClusterAPIProtos.ToDeviceActorRpcMessage msg) { |
85 | 88 | log.trace("{} session [{}] received device actor msg {}", getType(session), session.getRemoteServer(), msg); |
86 | - service.onMsg((ToDeviceActorMsg) deserialize(msg.getData().toByteArray())); | |
89 | + service.onMsg((DeviceToDeviceActorMsg) deserialize(msg.getData().toByteArray())); | |
87 | 90 | } |
88 | 91 | |
89 | 92 | @Override |
... | ... | @@ -139,28 +142,20 @@ public class BasicRpcSessionListener implements GrpcSessionListener { |
139 | 142 | return new UUID(uid.getPluginUuidMsb(), uid.getPluginUuidLsb()); |
140 | 143 | } |
141 | 144 | |
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 | - | |
145 | + private static ToDeviceRpcRequestActorMsg deserialize(ServerAddress serverAddress, ClusterAPIProtos.ToDeviceRpcRequestRpcMessage msg) { | |
147 | 146 | TenantId deviceTenantId = new TenantId(toUUID(msg.getDeviceTenantId())); |
148 | 147 | DeviceId deviceId = new DeviceId(toUUID(msg.getDeviceId())); |
149 | 148 | |
150 | 149 | ToDeviceRpcRequestBody requestBody = new ToDeviceRpcRequestBody(msg.getMethod(), msg.getParams()); |
151 | - ToDeviceRpcRequest request = new ToDeviceRpcRequest(toUUID(msg.getMsgId()), null, deviceTenantId, deviceId, msg.getOneway(), msg.getExpTime(), requestBody); | |
150 | + ToDeviceRpcRequest request = new ToDeviceRpcRequest(toUUID(msg.getMsgId()), deviceTenantId, deviceId, msg.getOneway(), msg.getExpTime(), requestBody); | |
152 | 151 | |
153 | - return new ToDeviceRpcRequestPluginMsg(serverAddress, pluginId, pluginTenantId, request); | |
152 | + return new ToDeviceRpcRequestActorMsg(serverAddress, request); | |
154 | 153 | } |
155 | 154 | |
156 | 155 | 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 | 156 | RpcError error = !StringUtils.isEmpty(msg.getError()) ? RpcError.valueOf(msg.getError()) : null; |
162 | 157 | FromDeviceRpcResponse response = new FromDeviceRpcResponse(toUUID(msg.getMsgId()), msg.getResponse(), error); |
163 | - return new ToPluginRpcResponseDeviceMsg(pluginId, pluginTenantId, response); | |
158 | + return new ToPluginRpcResponseDeviceMsg(null, null, response); | |
164 | 159 | } |
165 | 160 | |
166 | 161 | @SuppressWarnings("unchecked") | ... | ... |
... | ... | @@ -16,15 +16,20 @@ |
16 | 16 | package org.thingsboard.server.actors.ruleChain; |
17 | 17 | |
18 | 18 | import akka.actor.ActorRef; |
19 | -import akka.actor.Cancellable; | |
19 | +import com.datastax.driver.core.utils.UUIDs; | |
20 | 20 | import com.google.common.base.Function; |
21 | 21 | import org.thingsboard.rule.engine.api.*; |
22 | 22 | import org.thingsboard.server.actors.ActorSystemContext; |
23 | +import org.thingsboard.server.common.data.id.DeviceId; | |
24 | +import org.thingsboard.server.common.data.id.EntityId; | |
23 | 25 | import org.thingsboard.server.common.data.id.RuleNodeId; |
24 | 26 | import org.thingsboard.server.common.data.id.TenantId; |
27 | +import org.thingsboard.server.common.data.rpc.ToDeviceRpcRequestBody; | |
25 | 28 | import org.thingsboard.server.common.data.rule.RuleNode; |
26 | 29 | import org.thingsboard.server.common.msg.TbMsg; |
30 | +import org.thingsboard.server.common.msg.TbMsgMetaData; | |
27 | 31 | import org.thingsboard.server.common.msg.cluster.ServerAddress; |
32 | +import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest; | |
28 | 33 | import org.thingsboard.server.dao.alarm.AlarmService; |
29 | 34 | import org.thingsboard.server.dao.asset.AssetService; |
30 | 35 | import org.thingsboard.server.dao.attributes.AttributesService; |
... | ... | @@ -41,13 +46,13 @@ import scala.concurrent.duration.Duration; |
41 | 46 | import java.util.List; |
42 | 47 | import java.util.Set; |
43 | 48 | import java.util.concurrent.TimeUnit; |
49 | +import java.util.function.Consumer; | |
44 | 50 | |
45 | 51 | /** |
46 | 52 | * Created by ashvayka on 19.03.18. |
47 | 53 | */ |
48 | 54 | class DefaultTbContext implements TbContext { |
49 | 55 | |
50 | - private static final Function<? super List<Void>, ? extends Void> LIST_VOID_FUNCTION = v -> null; | |
51 | 56 | private final ActorSystemContext mainCtx; |
52 | 57 | private final RuleNodeCtx nodeCtx; |
53 | 58 | |
... | ... | @@ -63,8 +68,13 @@ class DefaultTbContext implements TbContext { |
63 | 68 | |
64 | 69 | @Override |
65 | 70 | public void tellNext(TbMsg msg, String relationType) { |
71 | + tellNext(msg, relationType, null); | |
72 | + } | |
73 | + | |
74 | + @Override | |
75 | + public void tellNext(TbMsg msg, String relationType, Throwable th) { | |
66 | 76 | if (nodeCtx.getSelf().isDebugMode()) { |
67 | - mainCtx.persistDebugOutput(nodeCtx.getTenantId(), nodeCtx.getSelf().getId(), msg); | |
77 | + mainCtx.persistDebugOutput(nodeCtx.getTenantId(), nodeCtx.getSelf().getId(), msg, th); | |
68 | 78 | } |
69 | 79 | nodeCtx.getChainActor().tell(new RuleNodeToRuleChainTellNextMsg(nodeCtx.getSelf().getId(), relationType, msg), nodeCtx.getSelfActor()); |
70 | 80 | } |
... | ... | @@ -113,6 +123,16 @@ class DefaultTbContext implements TbContext { |
113 | 123 | } |
114 | 124 | |
115 | 125 | @Override |
126 | + public TbMsg newMsg(String type, EntityId originator, TbMsgMetaData metaData, String data) { | |
127 | + return new TbMsg(UUIDs.timeBased(), type, originator, metaData, data, nodeCtx.getSelf().getRuleChainId(), nodeCtx.getSelf().getId(), 0L); | |
128 | + } | |
129 | + | |
130 | + @Override | |
131 | + public TbMsg transformMsg(TbMsg origMsg, String type, EntityId originator, TbMsgMetaData metaData, String data) { | |
132 | + return new TbMsg(origMsg.getId(), type, originator, metaData, data, nodeCtx.getSelf().getRuleChainId(), nodeCtx.getSelf().getId(), 0L); | |
133 | + } | |
134 | + | |
135 | + @Override | |
116 | 136 | public RuleNodeId getSelfId() { |
117 | 137 | return nodeCtx.getSelf().getId(); |
118 | 138 | } |
... | ... | @@ -143,6 +163,11 @@ class DefaultTbContext implements TbContext { |
143 | 163 | } |
144 | 164 | |
145 | 165 | @Override |
166 | + public ListeningExecutor getExternalCallExecutor() { | |
167 | + return mainCtx.getExternalCallExecutorService(); | |
168 | + } | |
169 | + | |
170 | + @Override | |
146 | 171 | public ScriptEngine createJsScriptEngine(String script, String functionName, String... argNames) { |
147 | 172 | return new NashornJsEngine(script, functionName, argNames); |
148 | 173 | } |
... | ... | @@ -206,4 +231,28 @@ class DefaultTbContext implements TbContext { |
206 | 231 | public MailService getMailService() { |
207 | 232 | return mainCtx.getMailService(); |
208 | 233 | } |
234 | + | |
235 | + @Override | |
236 | + public RuleEngineRpcService getRpcService() { | |
237 | + return new RuleEngineRpcService() { | |
238 | + @Override | |
239 | + public void sendRpcReply(DeviceId deviceId, int requestId, String body) { | |
240 | + mainCtx.getDeviceRpcService().sendRpcReplyToDevice(nodeCtx.getTenantId(), deviceId, requestId, body); | |
241 | + } | |
242 | + | |
243 | + @Override | |
244 | + public void sendRpcRequest(RuleEngineDeviceRpcRequest src, Consumer<RuleEngineDeviceRpcResponse> consumer) { | |
245 | + ToDeviceRpcRequest request = new ToDeviceRpcRequest(UUIDs.timeBased(), nodeCtx.getTenantId(), src.getDeviceId(), | |
246 | + src.isOneway(), System.currentTimeMillis() + src.getTimeout(), new ToDeviceRpcRequestBody(src.getMethod(), src.getBody())); | |
247 | + mainCtx.getDeviceRpcService().process(request, response -> { | |
248 | + consumer.accept(RuleEngineDeviceRpcResponse.builder() | |
249 | + .deviceId(src.getDeviceId()) | |
250 | + .requestId(src.getRequestId()) | |
251 | + .error(response.getError()) | |
252 | + .response(response.getResponse()) | |
253 | + .build()); | |
254 | + }); | |
255 | + } | |
256 | + }; | |
257 | + } | |
209 | 258 | } | ... | ... |
... | ... | @@ -18,6 +18,7 @@ package org.thingsboard.server.actors.ruleChain; |
18 | 18 | import akka.actor.OneForOneStrategy; |
19 | 19 | import akka.actor.SupervisorStrategy; |
20 | 20 | import org.thingsboard.server.actors.ActorSystemContext; |
21 | +import org.thingsboard.server.actors.device.DeviceActorToRuleEngineMsg; | |
21 | 22 | import org.thingsboard.server.actors.service.ComponentActor; |
22 | 23 | import org.thingsboard.server.actors.service.ContextBasedCreator; |
23 | 24 | import org.thingsboard.server.common.data.id.RuleChainId; |
... | ... | @@ -44,9 +45,15 @@ public class RuleChainActor extends ComponentActor<RuleChainId, RuleChainActorMe |
44 | 45 | case SERVICE_TO_RULE_ENGINE_MSG: |
45 | 46 | processor.onServiceToRuleEngineMsg((ServiceToRuleEngineMsg) msg); |
46 | 47 | break; |
48 | + case DEVICE_ACTOR_TO_RULE_ENGINE_MSG: | |
49 | + processor.onDeviceActorToRuleEngineMsg((DeviceActorToRuleEngineMsg) msg); | |
50 | + break; | |
47 | 51 | case RULE_TO_RULE_CHAIN_TELL_NEXT_MSG: |
48 | 52 | processor.onTellNext((RuleNodeToRuleChainTellNextMsg) msg); |
49 | 53 | break; |
54 | + case RULE_CHAIN_TO_RULE_CHAIN_MSG: | |
55 | + processor.onRuleChainToRuleChainMsg((RuleChainToRuleChainMsg) msg); | |
56 | + break; | |
50 | 57 | default: |
51 | 58 | return false; |
52 | 59 | } | ... | ... |
... | ... | @@ -19,7 +19,10 @@ import akka.actor.ActorContext; |
19 | 19 | import akka.actor.ActorRef; |
20 | 20 | import akka.actor.Props; |
21 | 21 | import akka.event.LoggingAdapter; |
22 | +import com.datastax.driver.core.utils.UUIDs; | |
22 | 23 | import org.thingsboard.server.actors.ActorSystemContext; |
24 | +import org.thingsboard.server.actors.device.DeviceActorToRuleEngineMsg; | |
25 | +import org.thingsboard.server.actors.device.RuleEngineQueuePutAckMsg; | |
23 | 26 | import org.thingsboard.server.actors.service.DefaultActorService; |
24 | 27 | import org.thingsboard.server.actors.shared.ComponentMsgProcessor; |
25 | 28 | import org.thingsboard.server.common.data.EntityType; |
... | ... | @@ -39,6 +42,7 @@ import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg; |
39 | 42 | import org.thingsboard.server.dao.rule.RuleChainService; |
40 | 43 | |
41 | 44 | import java.util.ArrayList; |
45 | +import java.util.Collections; | |
42 | 46 | import java.util.HashMap; |
43 | 47 | import java.util.List; |
44 | 48 | import java.util.Map; |
... | ... | @@ -50,6 +54,7 @@ import java.util.stream.Collectors; |
50 | 54 | */ |
51 | 55 | public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleChainId> { |
52 | 56 | |
57 | + private static final long DEFAULT_CLUSTER_PARTITION = 0L; | |
53 | 58 | private final ActorRef parent; |
54 | 59 | private final ActorRef self; |
55 | 60 | private final Map<RuleNodeId, RuleNodeCtx> nodeActors; |
... | ... | @@ -58,6 +63,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh |
58 | 63 | |
59 | 64 | private RuleNodeId firstId; |
60 | 65 | private RuleNodeCtx firstNode; |
66 | + private boolean started; | |
61 | 67 | |
62 | 68 | RuleChainActorMessageProcessor(TenantId tenantId, RuleChainId ruleChainId, ActorSystemContext systemContext |
63 | 69 | , LoggingAdapter logger, ActorRef parent, ActorRef self) { |
... | ... | @@ -71,14 +77,33 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh |
71 | 77 | |
72 | 78 | @Override |
73 | 79 | public void start(ActorContext context) throws Exception { |
74 | - RuleChain ruleChain = service.findRuleChainById(entityId); | |
75 | - List<RuleNode> ruleNodeList = service.getRuleChainNodes(entityId); | |
76 | - // Creating and starting the actors; | |
80 | + if (!started) { | |
81 | + RuleChain ruleChain = service.findRuleChainById(entityId); | |
82 | + List<RuleNode> ruleNodeList = service.getRuleChainNodes(entityId); | |
83 | + // Creating and starting the actors; | |
84 | + for (RuleNode ruleNode : ruleNodeList) { | |
85 | + ActorRef ruleNodeActor = createRuleNodeActor(context, ruleNode); | |
86 | + nodeActors.put(ruleNode.getId(), new RuleNodeCtx(tenantId, self, ruleNodeActor, ruleNode)); | |
87 | + } | |
88 | + initRoutes(ruleChain, ruleNodeList); | |
89 | + reprocess(ruleNodeList); | |
90 | + started = true; | |
91 | + } else { | |
92 | + onUpdate(context); | |
93 | + } | |
94 | + } | |
95 | + | |
96 | + private void reprocess(List<RuleNode> ruleNodeList) { | |
77 | 97 | for (RuleNode ruleNode : ruleNodeList) { |
78 | - ActorRef ruleNodeActor = createRuleNodeActor(context, ruleNode); | |
79 | - nodeActors.put(ruleNode.getId(), new RuleNodeCtx(tenantId, self, ruleNodeActor, ruleNode)); | |
98 | + for (TbMsg tbMsg : queue.findUnprocessed(ruleNode.getId().getId(), 0L)) { | |
99 | + pushMsgToNode(nodeActors.get(ruleNode.getId()), tbMsg); | |
100 | + } | |
101 | + } | |
102 | + if (firstNode != null) { | |
103 | + for (TbMsg tbMsg : queue.findUnprocessed(entityId.getId(), 0L)) { | |
104 | + pushMsgToNode(firstNode, tbMsg); | |
105 | + } | |
80 | 106 | } |
81 | - initRoutes(ruleChain, ruleNodeList); | |
82 | 107 | } |
83 | 108 | |
84 | 109 | @Override |
... | ... | @@ -105,6 +130,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh |
105 | 130 | }); |
106 | 131 | |
107 | 132 | initRoutes(ruleChain, ruleNodeList); |
133 | + reprocess(ruleNodeList); | |
108 | 134 | } |
109 | 135 | |
110 | 136 | @Override |
... | ... | @@ -113,6 +139,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh |
113 | 139 | nodeActors.clear(); |
114 | 140 | nodeRoutes.clear(); |
115 | 141 | context.stop(self); |
142 | + started = false; | |
116 | 143 | } |
117 | 144 | |
118 | 145 | @Override |
... | ... | @@ -133,15 +160,19 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh |
133 | 160 | // Populating the routes map; |
134 | 161 | for (RuleNode ruleNode : ruleNodeList) { |
135 | 162 | List<EntityRelation> relations = service.getRuleNodeRelations(ruleNode.getId()); |
136 | - for (EntityRelation relation : relations) { | |
137 | - if (relation.getTo().getEntityType() == EntityType.RULE_NODE) { | |
138 | - RuleNodeCtx ruleNodeCtx = nodeActors.get(new RuleNodeId(relation.getTo().getId())); | |
139 | - if (ruleNodeCtx == null) { | |
140 | - throw new IllegalArgumentException("Rule Node [" + relation.getFrom() + "] has invalid relation to Rule node [" + relation.getTo() + "]"); | |
163 | + if (relations.size() == 0) { | |
164 | + nodeRoutes.put(ruleNode.getId(), Collections.emptyList()); | |
165 | + } else { | |
166 | + for (EntityRelation relation : relations) { | |
167 | + if (relation.getTo().getEntityType() == EntityType.RULE_NODE) { | |
168 | + RuleNodeCtx ruleNodeCtx = nodeActors.get(new RuleNodeId(relation.getTo().getId())); | |
169 | + if (ruleNodeCtx == null) { | |
170 | + throw new IllegalArgumentException("Rule Node [" + relation.getFrom() + "] has invalid relation to Rule node [" + relation.getTo() + "]"); | |
171 | + } | |
141 | 172 | } |
173 | + nodeRoutes.computeIfAbsent(ruleNode.getId(), k -> new ArrayList<>()) | |
174 | + .add(new RuleNodeRelation(ruleNode.getId(), relation.getTo(), relation.getType())); | |
142 | 175 | } |
143 | - nodeRoutes.computeIfAbsent(ruleNode.getId(), k -> new ArrayList<>()) | |
144 | - .add(new RuleNodeRelation(ruleNode.getId(), relation.getTo(), relation.getType())); | |
145 | 176 | } |
146 | 177 | } |
147 | 178 | |
... | ... | @@ -152,37 +183,81 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh |
152 | 183 | |
153 | 184 | void onServiceToRuleEngineMsg(ServiceToRuleEngineMsg envelope) { |
154 | 185 | checkActive(); |
155 | - TbMsg tbMsg = envelope.getTbMsg(); | |
156 | - //TODO: push to queue and act on ack in async way | |
157 | - pushMsgToNode(firstNode, tbMsg); | |
186 | + putToQueue(enrichWithRuleChainId(envelope.getTbMsg()), msg -> pushMsgToNode(firstNode, msg)); | |
187 | + } | |
188 | + | |
189 | + void onDeviceActorToRuleEngineMsg(DeviceActorToRuleEngineMsg envelope) { | |
190 | + checkActive(); | |
191 | + putToQueue(enrichWithRuleChainId(envelope.getTbMsg()), msg -> { | |
192 | + pushMsgToNode(firstNode, msg); | |
193 | + envelope.getCallbackRef().tell(new RuleEngineQueuePutAckMsg(msg.getId()), self); | |
194 | + }); | |
195 | + } | |
196 | + | |
197 | + void onRuleChainToRuleChainMsg(RuleChainToRuleChainMsg envelope) { | |
198 | + checkActive(); | |
199 | + if (envelope.isEnqueue()) { | |
200 | + putToQueue(enrichWithRuleChainId(envelope.getMsg()), msg -> pushMsgToNode(firstNode, msg)); | |
201 | + } else { | |
202 | + pushMsgToNode(firstNode, envelope.getMsg()); | |
203 | + } | |
158 | 204 | } |
159 | 205 | |
160 | 206 | void onTellNext(RuleNodeToRuleChainTellNextMsg envelope) { |
161 | 207 | checkActive(); |
162 | 208 | RuleNodeId originator = envelope.getOriginator(); |
163 | 209 | String targetRelationType = envelope.getRelationType(); |
164 | - List<RuleNodeRelation> relations = nodeRoutes.get(originator); | |
165 | - if (relations == null) { | |
166 | - return; | |
167 | - } | |
168 | - boolean copy = relations.size() > 1; | |
169 | - for (RuleNodeRelation relation : relations) { | |
170 | - TbMsg msg = envelope.getMsg(); | |
171 | - if (copy) { | |
172 | - msg = msg.copy(); | |
210 | + List<RuleNodeRelation> relations = nodeRoutes.get(originator).stream() | |
211 | + .filter(r -> targetRelationType == null || targetRelationType.equalsIgnoreCase(r.getType())) | |
212 | + .collect(Collectors.toList()); | |
213 | + | |
214 | + TbMsg msg = envelope.getMsg(); | |
215 | + int relationsCount = relations.size(); | |
216 | + EntityId ackId = msg.getRuleNodeId() != null ? msg.getRuleNodeId() : msg.getRuleChainId(); | |
217 | + if (relationsCount == 0) { | |
218 | + queue.ack(msg, ackId.getId(), msg.getClusterPartition()); | |
219 | + } else if (relationsCount == 1) { | |
220 | + for (RuleNodeRelation relation : relations) { | |
221 | + pushToTarget(msg, relation.getOut()); | |
173 | 222 | } |
174 | - if (targetRelationType == null || targetRelationType.equalsIgnoreCase(relation.getType())) { | |
175 | - switch (relation.getOut().getEntityType()) { | |
223 | + } else { | |
224 | + for (RuleNodeRelation relation : relations) { | |
225 | + EntityId target = relation.getOut(); | |
226 | + switch (target.getEntityType()) { | |
176 | 227 | case RULE_NODE: |
177 | - RuleNodeId targetRuleNodeId = new RuleNodeId(relation.getOut().getId()); | |
178 | - RuleNodeCtx targetRuleNode = nodeActors.get(targetRuleNodeId); | |
179 | - pushMsgToNode(targetRuleNode, msg); | |
228 | + enqueueAndForwardMsgCopyToNode(msg, target); | |
180 | 229 | break; |
181 | 230 | case RULE_CHAIN: |
182 | -// TODO: implement | |
231 | + enqueueAndForwardMsgCopyToChain(msg, target); | |
183 | 232 | break; |
184 | 233 | } |
185 | 234 | } |
235 | + //TODO: Ideally this should happen in async way when all targets confirm that the copied messages are successfully written to corresponding target queues. | |
236 | + queue.ack(msg, ackId.getId(), msg.getClusterPartition()); | |
237 | + } | |
238 | + } | |
239 | + | |
240 | + private void enqueueAndForwardMsgCopyToChain(TbMsg msg, EntityId target) { | |
241 | + RuleChainId targetRCId = new RuleChainId(target.getId()); | |
242 | + TbMsg copyMsg = msg.copy(UUIDs.timeBased(), targetRCId, null, DEFAULT_CLUSTER_PARTITION); | |
243 | + parent.tell(new RuleChainToRuleChainMsg(new RuleChainId(target.getId()), entityId, copyMsg, true), self); | |
244 | + } | |
245 | + | |
246 | + private void enqueueAndForwardMsgCopyToNode(TbMsg msg, EntityId target) { | |
247 | + RuleNodeId targetId = new RuleNodeId(target.getId()); | |
248 | + RuleNodeCtx targetNodeCtx = nodeActors.get(targetId); | |
249 | + TbMsg copy = msg.copy(UUIDs.timeBased(), entityId, targetId, DEFAULT_CLUSTER_PARTITION); | |
250 | + putToQueue(copy, queuedMsg -> pushMsgToNode(targetNodeCtx, queuedMsg)); | |
251 | + } | |
252 | + | |
253 | + private void pushToTarget(TbMsg msg, EntityId target) { | |
254 | + switch (target.getEntityType()) { | |
255 | + case RULE_NODE: | |
256 | + pushMsgToNode(nodeActors.get(new RuleNodeId(target.getId())), msg); | |
257 | + break; | |
258 | + case RULE_CHAIN: | |
259 | + parent.tell(new RuleChainToRuleChainMsg(new RuleChainId(target.getId()), entityId, msg, false), self); | |
260 | + break; | |
186 | 261 | } |
187 | 262 | } |
188 | 263 | |
... | ... | @@ -192,4 +267,8 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh |
192 | 267 | } |
193 | 268 | } |
194 | 269 | |
270 | + private TbMsg enrichWithRuleChainId(TbMsg tbMsg) { | |
271 | + // We don't put firstNodeId because it may change over time; | |
272 | + return new TbMsg(tbMsg.getId(), tbMsg.getType(), tbMsg.getOriginator(), tbMsg.getMetaData(), tbMsg.getData(), entityId, null, 0L); | |
273 | + } | |
195 | 274 | } | ... | ... |
... | ... | @@ -31,31 +31,29 @@ import org.thingsboard.server.dao.rule.RuleChainService; |
31 | 31 | public abstract class RuleChainManagerActor extends ContextAwareActor { |
32 | 32 | |
33 | 33 | protected final RuleChainManager ruleChainManager; |
34 | - protected final PluginManager pluginManager; | |
35 | 34 | protected final RuleChainService ruleChainService; |
36 | 35 | |
37 | 36 | public RuleChainManagerActor(ActorSystemContext systemContext, RuleChainManager ruleChainManager, PluginManager pluginManager) { |
38 | 37 | super(systemContext); |
39 | 38 | this.ruleChainManager = ruleChainManager; |
40 | - this.pluginManager = pluginManager; | |
41 | 39 | this.ruleChainService = systemContext.getRuleChainService(); |
42 | 40 | } |
43 | 41 | |
44 | 42 | protected void initRuleChains() { |
45 | - pluginManager.init(this.context()); | |
46 | 43 | ruleChainManager.init(this.context()); |
47 | 44 | } |
48 | 45 | |
49 | 46 | protected ActorRef getEntityActorRef(EntityId entityId) { |
50 | 47 | ActorRef target = null; |
51 | 48 | switch (entityId.getEntityType()) { |
52 | - case PLUGIN: | |
53 | - target = pluginManager.getOrCreateActor(this.context(), (PluginId) entityId); | |
54 | - break; | |
55 | 49 | case RULE_CHAIN: |
56 | 50 | target = ruleChainManager.getOrCreateActor(this.context(), (RuleChainId) entityId); |
57 | 51 | break; |
58 | 52 | } |
59 | 53 | return target; |
60 | 54 | } |
55 | + | |
56 | + protected void broadcast(Object msg) { | |
57 | + ruleChainManager.broadcast(msg); | |
58 | + } | |
61 | 59 | } | ... | ... |
application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainToRuleChainMsg.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.ruleChain; | |
17 | + | |
18 | +import lombok.Data; | |
19 | +import org.thingsboard.rule.engine.api.TbContext; | |
20 | +import org.thingsboard.server.common.data.id.RuleChainId; | |
21 | +import org.thingsboard.server.common.msg.MsgType; | |
22 | +import org.thingsboard.server.common.msg.TbActorMsg; | |
23 | +import org.thingsboard.server.common.msg.TbMsg; | |
24 | + | |
25 | +/** | |
26 | + * Created by ashvayka on 19.03.18. | |
27 | + */ | |
28 | +@Data | |
29 | +public final class RuleChainToRuleChainMsg implements TbActorMsg { | |
30 | + | |
31 | + private final RuleChainId target; | |
32 | + private final RuleChainId source; | |
33 | + private final TbMsg msg; | |
34 | + private final boolean enqueue; | |
35 | + | |
36 | + @Override | |
37 | + public MsgType getMsgType() { | |
38 | + return MsgType.RULE_CHAIN_TO_RULE_CHAIN_MSG; | |
39 | + } | |
40 | +} | ... | ... |
... | ... | @@ -32,4 +32,5 @@ public interface ActorService extends SessionMsgProcessor, WebSocketMsgProcessor |
32 | 32 | void onCredentialsUpdate(TenantId tenantId, DeviceId deviceId); |
33 | 33 | |
34 | 34 | void onDeviceNameOrTypeUpdate(TenantId tenantId, DeviceId deviceId, String deviceName, String deviceType); |
35 | + | |
35 | 36 | } | ... | ... |
... | ... | @@ -39,7 +39,7 @@ import org.thingsboard.server.common.msg.cluster.ToAllNodesMsg; |
39 | 39 | import org.thingsboard.server.common.msg.core.ToDeviceSessionActorMsg; |
40 | 40 | import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg; |
41 | 41 | import org.thingsboard.server.extensions.api.device.DeviceNameOrTypeUpdateMsg; |
42 | -import org.thingsboard.server.common.msg.device.ToDeviceActorMsg; | |
42 | +import org.thingsboard.server.common.msg.device.DeviceToDeviceActorMsg; | |
43 | 43 | import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; |
44 | 44 | import org.thingsboard.server.extensions.api.device.DeviceCredentialsUpdateNotificationMsg; |
45 | 45 | import org.thingsboard.server.extensions.api.device.ToDeviceActorNotificationMsg; |
... | ... | @@ -156,7 +156,7 @@ public class DefaultActorService implements ActorService { |
156 | 156 | } |
157 | 157 | |
158 | 158 | @Override |
159 | - public void onMsg(ToDeviceActorMsg msg) { | |
159 | + public void onMsg(DeviceToDeviceActorMsg msg) { | |
160 | 160 | log.trace("Processing device rpc msg: {}", msg); |
161 | 161 | appActor.tell(msg, ActorRef.noSender()); |
162 | 162 | } | ... | ... |
... | ... | @@ -22,12 +22,11 @@ import org.thingsboard.server.common.msg.cluster.ClusterEventMsg; |
22 | 22 | import org.thingsboard.server.common.msg.cluster.ServerAddress; |
23 | 23 | import org.thingsboard.server.common.msg.core.*; |
24 | 24 | import org.thingsboard.server.common.msg.core.SessionCloseMsg; |
25 | -import org.thingsboard.server.common.msg.device.ToDeviceActorMsg; | |
25 | +import org.thingsboard.server.common.msg.device.DeviceToDeviceActorMsg; | |
26 | 26 | import org.thingsboard.server.common.msg.session.*; |
27 | 27 | |
28 | 28 | import akka.actor.ActorContext; |
29 | 29 | import akka.event.LoggingAdapter; |
30 | -import org.thingsboard.server.common.msg.session.ctrl.*; | |
31 | 30 | import org.thingsboard.server.common.msg.session.ex.SessionException; |
32 | 31 | |
33 | 32 | import java.util.HashMap; |
... | ... | @@ -37,7 +36,7 @@ import java.util.Optional; |
37 | 36 | class ASyncMsgProcessor extends AbstractSessionActorMsgProcessor { |
38 | 37 | |
39 | 38 | private boolean firstMsg = true; |
40 | - private Map<Integer, ToDeviceActorMsg> pendingMap = new HashMap<>(); | |
39 | + private Map<Integer, DeviceToDeviceActorMsg> pendingMap = new HashMap<>(); | |
41 | 40 | private Optional<ServerAddress> currentTargetServer; |
42 | 41 | private boolean subscribedToAttributeUpdates; |
43 | 42 | private boolean subscribedToRpcCommands; |
... | ... | @@ -53,7 +52,7 @@ class ASyncMsgProcessor extends AbstractSessionActorMsgProcessor { |
53 | 52 | toDeviceMsg(new SessionOpenMsg()).ifPresent(m -> forwardToAppActor(ctx, m)); |
54 | 53 | firstMsg = false; |
55 | 54 | } |
56 | - ToDeviceActorMsg pendingMsg = toDeviceMsg(msg); | |
55 | + DeviceToDeviceActorMsg pendingMsg = toDeviceMsg(msg); | |
57 | 56 | FromDeviceMsg fromDeviceMsg = pendingMsg.getPayload(); |
58 | 57 | switch (fromDeviceMsg.getMsgType()) { |
59 | 58 | case POST_TELEMETRY_REQUEST: |
... | ... | @@ -86,8 +85,8 @@ class ASyncMsgProcessor extends AbstractSessionActorMsgProcessor { |
86 | 85 | @Override |
87 | 86 | public void processToDeviceMsg(ActorContext context, ToDeviceMsg msg) { |
88 | 87 | try { |
89 | - if (msg.getMsgType() != MsgType.SESSION_CLOSE) { | |
90 | - switch (msg.getMsgType()) { | |
88 | + if (msg.getSessionMsgType() != SessionMsgType.SESSION_CLOSE) { | |
89 | + switch (msg.getSessionMsgType()) { | |
91 | 90 | case STATUS_CODE_RESPONSE: |
92 | 91 | case GET_ATTRIBUTES_RESPONSE: |
93 | 92 | ResponseMsg responseMsg = (ResponseMsg) msg; | ... | ... |
... | ... | @@ -22,8 +22,8 @@ import org.thingsboard.server.common.data.id.DeviceId; |
22 | 22 | import org.thingsboard.server.common.data.id.SessionId; |
23 | 23 | import org.thingsboard.server.common.msg.cluster.ClusterEventMsg; |
24 | 24 | import org.thingsboard.server.common.msg.cluster.ServerAddress; |
25 | -import org.thingsboard.server.common.msg.device.BasicToDeviceActorMsg; | |
26 | -import org.thingsboard.server.common.msg.device.ToDeviceActorMsg; | |
25 | +import org.thingsboard.server.common.msg.device.BasicDeviceToDeviceActorMsg; | |
26 | +import org.thingsboard.server.common.msg.device.DeviceToDeviceActorMsg; | |
27 | 27 | import org.thingsboard.server.common.msg.session.*; |
28 | 28 | import org.thingsboard.server.common.msg.session.ctrl.SessionCloseMsg; |
29 | 29 | |
... | ... | @@ -37,7 +37,7 @@ abstract class AbstractSessionActorMsgProcessor extends AbstractContextAwareMsgP |
37 | 37 | |
38 | 38 | protected final SessionId sessionId; |
39 | 39 | protected SessionContext sessionCtx; |
40 | - protected ToDeviceActorMsg toDeviceActorMsgPrototype; | |
40 | + protected DeviceToDeviceActorMsg deviceToDeviceActorMsgPrototype; | |
41 | 41 | |
42 | 42 | protected AbstractSessionActorMsgProcessor(ActorSystemContext ctx, LoggingAdapter logger, SessionId sessionId) { |
43 | 43 | super(ctx, logger); |
... | ... | @@ -64,29 +64,29 @@ abstract class AbstractSessionActorMsgProcessor extends AbstractContextAwareMsgP |
64 | 64 | |
65 | 65 | protected void updateSessionCtx(ToDeviceActorSessionMsg msg, SessionType type) { |
66 | 66 | sessionCtx = msg.getSessionMsg().getSessionContext(); |
67 | - toDeviceActorMsgPrototype = new BasicToDeviceActorMsg(msg, type); | |
67 | + deviceToDeviceActorMsgPrototype = new BasicDeviceToDeviceActorMsg(msg, type); | |
68 | 68 | } |
69 | 69 | |
70 | - protected ToDeviceActorMsg toDeviceMsg(ToDeviceActorSessionMsg msg) { | |
70 | + protected DeviceToDeviceActorMsg toDeviceMsg(ToDeviceActorSessionMsg msg) { | |
71 | 71 | AdaptorToSessionActorMsg adaptorMsg = msg.getSessionMsg(); |
72 | - return new BasicToDeviceActorMsg(toDeviceActorMsgPrototype, adaptorMsg.getMsg()); | |
72 | + return new BasicDeviceToDeviceActorMsg(deviceToDeviceActorMsgPrototype, adaptorMsg.getMsg()); | |
73 | 73 | } |
74 | 74 | |
75 | - protected Optional<ToDeviceActorMsg> toDeviceMsg(FromDeviceMsg msg) { | |
76 | - if (toDeviceActorMsgPrototype != null) { | |
77 | - return Optional.of(new BasicToDeviceActorMsg(toDeviceActorMsgPrototype, msg)); | |
75 | + protected Optional<DeviceToDeviceActorMsg> toDeviceMsg(FromDeviceMsg msg) { | |
76 | + if (deviceToDeviceActorMsgPrototype != null) { | |
77 | + return Optional.of(new BasicDeviceToDeviceActorMsg(deviceToDeviceActorMsgPrototype, msg)); | |
78 | 78 | } else { |
79 | 79 | return Optional.empty(); |
80 | 80 | } |
81 | 81 | } |
82 | 82 | |
83 | - protected Optional<ServerAddress> forwardToAppActor(ActorContext ctx, ToDeviceActorMsg toForward) { | |
83 | + protected Optional<ServerAddress> forwardToAppActor(ActorContext ctx, DeviceToDeviceActorMsg toForward) { | |
84 | 84 | Optional<ServerAddress> address = systemContext.getRoutingService().resolveById(toForward.getDeviceId()); |
85 | 85 | forwardToAppActor(ctx, toForward, address); |
86 | 86 | return address; |
87 | 87 | } |
88 | 88 | |
89 | - protected Optional<ServerAddress> forwardToAppActorIfAdressChanged(ActorContext ctx, ToDeviceActorMsg toForward, Optional<ServerAddress> oldAddress) { | |
89 | + protected Optional<ServerAddress> forwardToAppActorIfAdressChanged(ActorContext ctx, DeviceToDeviceActorMsg toForward, Optional<ServerAddress> oldAddress) { | |
90 | 90 | Optional<ServerAddress> newAddress = systemContext.getRoutingService().resolveById(toForward.getDeviceId()); |
91 | 91 | if (!newAddress.equals(oldAddress)) { |
92 | 92 | if (newAddress.isPresent()) { |
... | ... | @@ -99,7 +99,7 @@ abstract class AbstractSessionActorMsgProcessor extends AbstractContextAwareMsgP |
99 | 99 | return newAddress; |
100 | 100 | } |
101 | 101 | |
102 | - protected void forwardToAppActor(ActorContext ctx, ToDeviceActorMsg toForward, Optional<ServerAddress> address) { | |
102 | + protected void forwardToAppActor(ActorContext ctx, DeviceToDeviceActorMsg toForward, Optional<ServerAddress> address) { | |
103 | 103 | if (address.isPresent()) { |
104 | 104 | systemContext.getRpcService().tell(address.get(), |
105 | 105 | toForward.toOtherAddress(systemContext.getRoutingService().getCurrentServer())); |
... | ... | @@ -114,6 +114,6 @@ abstract class AbstractSessionActorMsgProcessor extends AbstractContextAwareMsgP |
114 | 114 | } |
115 | 115 | |
116 | 116 | public DeviceId getDeviceId() { |
117 | - return toDeviceActorMsgPrototype.getDeviceId(); | |
117 | + return deviceToDeviceActorMsgPrototype.getDeviceId(); | |
118 | 118 | } |
119 | 119 | } | ... | ... |
... | ... | @@ -20,7 +20,7 @@ import org.thingsboard.server.actors.shared.SessionTimeoutMsg; |
20 | 20 | import org.thingsboard.server.common.data.id.SessionId; |
21 | 21 | import org.thingsboard.server.common.msg.cluster.ClusterEventMsg; |
22 | 22 | import org.thingsboard.server.common.msg.cluster.ServerAddress; |
23 | -import org.thingsboard.server.common.msg.device.ToDeviceActorMsg; | |
23 | +import org.thingsboard.server.common.msg.device.DeviceToDeviceActorMsg; | |
24 | 24 | import org.thingsboard.server.common.msg.session.*; |
25 | 25 | import org.thingsboard.server.common.msg.session.ToDeviceActorSessionMsg; |
26 | 26 | import org.thingsboard.server.common.msg.session.ctrl.SessionCloseMsg; |
... | ... | @@ -32,7 +32,7 @@ import akka.event.LoggingAdapter; |
32 | 32 | import java.util.Optional; |
33 | 33 | |
34 | 34 | class SyncMsgProcessor extends AbstractSessionActorMsgProcessor { |
35 | - private ToDeviceActorMsg pendingMsg; | |
35 | + private DeviceToDeviceActorMsg pendingMsg; | |
36 | 36 | private Optional<ServerAddress> currentTargetServer; |
37 | 37 | private boolean pendingResponse; |
38 | 38 | ... | ... |
... | ... | @@ -17,22 +17,32 @@ package org.thingsboard.server.actors.shared; |
17 | 17 | |
18 | 18 | import akka.actor.ActorContext; |
19 | 19 | import akka.event.LoggingAdapter; |
20 | +import com.google.common.util.concurrent.FutureCallback; | |
21 | +import com.google.common.util.concurrent.Futures; | |
20 | 22 | import org.thingsboard.server.actors.ActorSystemContext; |
21 | 23 | import org.thingsboard.server.actors.stats.StatsPersistTick; |
24 | +import org.thingsboard.server.common.data.id.EntityId; | |
22 | 25 | import org.thingsboard.server.common.data.id.TenantId; |
23 | 26 | import org.thingsboard.server.common.data.plugin.ComponentLifecycleState; |
27 | +import org.thingsboard.server.common.msg.TbMsg; | |
24 | 28 | import org.thingsboard.server.common.msg.cluster.ClusterEventMsg; |
29 | +import org.thingsboard.server.dao.queue.MsgQueue; | |
25 | 30 | |
26 | -public abstract class ComponentMsgProcessor<T> extends AbstractContextAwareMsgProcessor { | |
31 | +import javax.annotation.Nullable; | |
32 | +import java.util.function.Consumer; | |
33 | + | |
34 | +public abstract class ComponentMsgProcessor<T extends EntityId> extends AbstractContextAwareMsgProcessor { | |
27 | 35 | |
28 | 36 | protected final TenantId tenantId; |
29 | 37 | protected final T entityId; |
38 | + protected final MsgQueue queue; | |
30 | 39 | protected ComponentLifecycleState state; |
31 | 40 | |
32 | 41 | protected ComponentMsgProcessor(ActorSystemContext systemContext, LoggingAdapter logger, TenantId tenantId, T id) { |
33 | 42 | super(systemContext, logger); |
34 | 43 | this.tenantId = tenantId; |
35 | 44 | this.entityId = id; |
45 | + this.queue = systemContext.getMsgQueue(); | |
36 | 46 | } |
37 | 47 | |
38 | 48 | public abstract void start(ActorContext context) throws Exception; |
... | ... | @@ -75,4 +85,19 @@ public abstract class ComponentMsgProcessor<T> extends AbstractContextAwareMsgPr |
75 | 85 | throw new IllegalStateException("Rule chain is not active!"); |
76 | 86 | } |
77 | 87 | } |
88 | + | |
89 | + protected void putToQueue(final TbMsg tbMsg, final Consumer<TbMsg> onSuccess) { | |
90 | + EntityId entityId = tbMsg.getRuleNodeId() != null ? tbMsg.getRuleNodeId() : tbMsg.getRuleChainId(); | |
91 | + Futures.addCallback(queue.put(tbMsg, entityId.getId(), 0), new FutureCallback<Void>() { | |
92 | + @Override | |
93 | + public void onSuccess(@Nullable Void result) { | |
94 | + onSuccess.accept(tbMsg); | |
95 | + } | |
96 | + | |
97 | + @Override | |
98 | + public void onFailure(Throwable t) { | |
99 | + logger.debug("Failed to push message [{}] to queue due to [{}]", tbMsg, t); | |
100 | + } | |
101 | + }); | |
102 | + } | |
78 | 103 | } | ... | ... |
... | ... | @@ -16,13 +16,16 @@ |
16 | 16 | package org.thingsboard.server.actors.tenant; |
17 | 17 | |
18 | 18 | import akka.actor.ActorRef; |
19 | +import akka.actor.OneForOneStrategy; | |
19 | 20 | import akka.actor.Props; |
20 | -import akka.event.Logging; | |
21 | -import akka.event.LoggingAdapter; | |
21 | +import akka.actor.SupervisorStrategy; | |
22 | +import akka.japi.Function; | |
22 | 23 | import org.thingsboard.server.actors.ActorSystemContext; |
23 | 24 | import org.thingsboard.server.actors.device.DeviceActor; |
25 | +import org.thingsboard.server.actors.device.DeviceActorToRuleEngineMsg; | |
24 | 26 | import org.thingsboard.server.actors.plugin.PluginTerminationMsg; |
25 | 27 | import org.thingsboard.server.actors.ruleChain.RuleChainManagerActor; |
28 | +import org.thingsboard.server.actors.ruleChain.RuleChainToRuleChainMsg; | |
26 | 29 | import org.thingsboard.server.actors.service.ContextBasedCreator; |
27 | 30 | import org.thingsboard.server.actors.service.DefaultActorService; |
28 | 31 | import org.thingsboard.server.actors.shared.plugin.TenantPluginManager; |
... | ... | @@ -30,11 +33,13 @@ import org.thingsboard.server.actors.shared.rulechain.TenantRuleChainManager; |
30 | 33 | import org.thingsboard.server.common.data.id.DeviceId; |
31 | 34 | import org.thingsboard.server.common.data.id.TenantId; |
32 | 35 | import org.thingsboard.server.common.msg.TbActorMsg; |
33 | -import org.thingsboard.server.common.msg.device.ToDeviceActorMsg; | |
36 | +import org.thingsboard.server.common.msg.aware.DeviceAwareMsg; | |
37 | +import org.thingsboard.server.common.msg.device.DeviceToDeviceActorMsg; | |
34 | 38 | import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; |
35 | 39 | import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg; |
36 | 40 | import org.thingsboard.server.extensions.api.device.ToDeviceActorNotificationMsg; |
37 | 41 | import org.thingsboard.server.extensions.api.plugins.msg.ToPluginActorMsg; |
42 | +import scala.concurrent.duration.Duration; | |
38 | 43 | |
39 | 44 | import java.util.HashMap; |
40 | 45 | import java.util.Map; |
... | ... | @@ -50,6 +55,12 @@ public class TenantActor extends RuleChainManagerActor { |
50 | 55 | this.deviceActors = new HashMap<>(); |
51 | 56 | } |
52 | 57 | |
58 | + | |
59 | + @Override | |
60 | + public SupervisorStrategy supervisorStrategy() { | |
61 | + return strategy; | |
62 | + } | |
63 | + | |
53 | 64 | @Override |
54 | 65 | public void preStart() { |
55 | 66 | logger.info("[{}] Starting tenant actor.", tenantId); |
... | ... | @@ -64,63 +75,56 @@ public class TenantActor extends RuleChainManagerActor { |
64 | 75 | @Override |
65 | 76 | protected boolean process(TbActorMsg msg) { |
66 | 77 | switch (msg.getMsgType()) { |
78 | + case CLUSTER_EVENT_MSG: | |
79 | + broadcast(msg); | |
80 | + break; | |
67 | 81 | case COMPONENT_LIFE_CYCLE_MSG: |
68 | 82 | onComponentLifecycleMsg((ComponentLifecycleMsg) msg); |
69 | 83 | break; |
70 | 84 | case SERVICE_TO_RULE_ENGINE_MSG: |
71 | 85 | onServiceToRuleEngineMsg((ServiceToRuleEngineMsg) msg); |
72 | 86 | break; |
87 | + case DEVICE_ACTOR_TO_RULE_ENGINE_MSG: | |
88 | + onDeviceActorToRuleEngineMsg((DeviceActorToRuleEngineMsg) msg); | |
89 | + break; | |
90 | + case DEVICE_SESSION_TO_DEVICE_ACTOR_MSG: | |
91 | + case DEVICE_ATTRIBUTES_UPDATE_TO_DEVICE_ACTOR_MSG: | |
92 | + case DEVICE_CREDENTIALS_UPDATE_TO_DEVICE_ACTOR_MSG: | |
93 | + case DEVICE_NAME_OR_TYPE_UPDATE_TO_DEVICE_ACTOR_MSG: | |
94 | + case DEVICE_RPC_REQUEST_TO_DEVICE_ACTOR_MSG: | |
95 | + case SERVER_RPC_RESPONSE_TO_DEVICE_ACTOR_MSG: | |
96 | + onToDeviceActorMsg((DeviceAwareMsg) msg); | |
97 | + break; | |
98 | + case RULE_CHAIN_TO_RULE_CHAIN_MSG: | |
99 | + onRuleChainMsg((RuleChainToRuleChainMsg) msg); | |
100 | + break; | |
73 | 101 | default: |
74 | 102 | return false; |
75 | 103 | } |
76 | 104 | return true; |
77 | 105 | } |
78 | 106 | |
107 | + @Override | |
108 | + protected void broadcast(Object msg) { | |
109 | + super.broadcast(msg); | |
110 | + deviceActors.values().forEach(actorRef -> actorRef.tell(msg, ActorRef.noSender())); | |
111 | + } | |
112 | + | |
79 | 113 | private void onServiceToRuleEngineMsg(ServiceToRuleEngineMsg msg) { |
80 | 114 | ruleChainManager.getRootChainActor().tell(msg, self()); |
81 | 115 | } |
82 | 116 | |
83 | - | |
84 | -// @Override | |
85 | -// public void onReceive(Object msg) throws Exception { | |
86 | -// logger.debug("[{}] Received message: {}", tenantId, msg); | |
87 | -// if (msg instanceof ToDeviceActorMsg) { | |
88 | -// onToDeviceActorMsg((ToDeviceActorMsg) msg); | |
89 | -// } else if (msg instanceof ToPluginActorMsg) { | |
90 | -// onToPluginMsg((ToPluginActorMsg) msg); | |
91 | -// } else if (msg instanceof ToDeviceActorNotificationMsg) { | |
92 | -// onToDeviceActorMsg((ToDeviceActorNotificationMsg) msg); | |
93 | -// } else if (msg instanceof ClusterEventMsg) { | |
94 | -// broadcast(msg); | |
95 | -// } else if (msg instanceof ComponentLifecycleMsg) { | |
96 | -// onComponentLifecycleMsg((ComponentLifecycleMsg) msg); | |
97 | -// } else if (msg instanceof PluginTerminationMsg) { | |
98 | -// onPluginTerminated((PluginTerminationMsg) msg); | |
99 | -// } else { | |
100 | -// logger.warning("[{}] Unknown message: {}!", tenantId, msg); | |
101 | -// } | |
102 | -// } | |
103 | - | |
104 | - private void broadcast(Object msg) { | |
105 | - pluginManager.broadcast(msg); | |
106 | - deviceActors.values().forEach(actorRef -> actorRef.tell(msg, ActorRef.noSender())); | |
117 | + private void onDeviceActorToRuleEngineMsg(DeviceActorToRuleEngineMsg msg) { | |
118 | + ruleChainManager.getRootChainActor().tell(msg, self()); | |
107 | 119 | } |
108 | 120 | |
109 | - private void onToDeviceActorMsg(ToDeviceActorMsg msg) { | |
110 | - getOrCreateDeviceActor(msg.getDeviceId()).tell(msg, ActorRef.noSender()); | |
121 | + private void onRuleChainMsg(RuleChainToRuleChainMsg msg) { | |
122 | + ruleChainManager.getOrCreateActor(context(), msg.getTarget()).tell(msg, self()); | |
111 | 123 | } |
112 | 124 | |
113 | - private void onToDeviceActorMsg(ToDeviceActorNotificationMsg msg) { | |
114 | - getOrCreateDeviceActor(msg.getDeviceId()).tell(msg, ActorRef.noSender()); | |
115 | - } | |
116 | 125 | |
117 | - private void onToPluginMsg(ToPluginActorMsg msg) { | |
118 | - if (msg.getPluginTenantId().equals(tenantId)) { | |
119 | - ActorRef pluginActor = pluginManager.getOrCreateActor(this.context(), msg.getPluginId()); | |
120 | - pluginActor.tell(msg, ActorRef.noSender()); | |
121 | - } else { | |
122 | - context().parent().tell(msg, ActorRef.noSender()); | |
123 | - } | |
126 | + private void onToDeviceActorMsg(DeviceAwareMsg msg) { | |
127 | + getOrCreateDeviceActor(msg.getDeviceId()).tell(msg, ActorRef.noSender()); | |
124 | 128 | } |
125 | 129 | |
126 | 130 | private void onComponentLifecycleMsg(ComponentLifecycleMsg msg) { |
... | ... | @@ -132,11 +136,6 @@ public class TenantActor extends RuleChainManagerActor { |
132 | 136 | } |
133 | 137 | } |
134 | 138 | |
135 | - private void onPluginTerminated(PluginTerminationMsg msg) { | |
136 | - pluginManager.remove(msg.getId()); | |
137 | - } | |
138 | - | |
139 | - | |
140 | 139 | private ActorRef getOrCreateDeviceActor(DeviceId deviceId) { |
141 | 140 | return deviceActors.computeIfAbsent(deviceId, k -> context().actorOf(Props.create(new DeviceActor.ActorCreator(systemContext, tenantId, deviceId)) |
142 | 141 | .withDispatcher(DefaultActorService.CORE_DISPATCHER_NAME), deviceId.toString())); |
... | ... | @@ -158,4 +157,12 @@ public class TenantActor extends RuleChainManagerActor { |
158 | 157 | } |
159 | 158 | } |
160 | 159 | |
160 | + private final SupervisorStrategy strategy = new OneForOneStrategy(3, Duration.create("1 minute"), new Function<Throwable, SupervisorStrategy.Directive>() { | |
161 | + @Override | |
162 | + public SupervisorStrategy.Directive apply(Throwable t) { | |
163 | + logger.error(t, "Unknown failure"); | |
164 | + return SupervisorStrategy.resume(); | |
165 | + } | |
166 | + }); | |
167 | + | |
161 | 168 | } | ... | ... |
... | ... | @@ -63,6 +63,7 @@ import org.thingsboard.server.exception.ThingsboardErrorResponseHandler; |
63 | 63 | import org.thingsboard.server.common.data.exception.ThingsboardException; |
64 | 64 | import org.thingsboard.server.service.component.ComponentDiscoveryService; |
65 | 65 | import org.thingsboard.server.service.security.model.SecurityUser; |
66 | +import org.thingsboard.server.service.state.DeviceStateService; | |
66 | 67 | |
67 | 68 | import javax.mail.MessagingException; |
68 | 69 | import javax.servlet.http.HttpServletRequest; |
... | ... | @@ -137,6 +138,9 @@ public abstract class BaseController { |
137 | 138 | @Autowired |
138 | 139 | protected DeviceOfflineService offlineService; |
139 | 140 | |
141 | + @Autowired | |
142 | + protected DeviceStateService deviceStateService; | |
143 | + | |
140 | 144 | @ExceptionHandler(ThingsboardException.class) |
141 | 145 | public void handleThingsboardException(ThingsboardException ex, HttpServletResponse response) { |
142 | 146 | errorResponseHandler.handle(ex, response); |
... | ... | @@ -606,7 +610,7 @@ public abstract class BaseController { |
606 | 610 | auditLogService.logEntityAction(user.getTenantId(), customerId, user.getId(), user.getName(), entityId, entity, actionType, e, additionalInfo); |
607 | 611 | } |
608 | 612 | |
609 | - protected static Exception toException(Throwable error) { | |
613 | + public static Exception toException(Throwable error) { | |
610 | 614 | return Exception.class.isInstance(error) ? (Exception) error : new Exception(error); |
611 | 615 | } |
612 | 616 | ... | ... |
... | ... | @@ -90,6 +90,11 @@ public class DeviceController extends BaseController { |
90 | 90 | savedDevice.getCustomerId(), |
91 | 91 | device.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null); |
92 | 92 | |
93 | + if (device.getId() == null) { | |
94 | + deviceStateService.onDeviceAdded(savedDevice); | |
95 | + } else { | |
96 | + deviceStateService.onDeviceUpdated(savedDevice); | |
97 | + } | |
93 | 98 | return savedDevice; |
94 | 99 | } catch (Exception e) { |
95 | 100 | logEntityAction(emptyId(EntityType.DEVICE), device, |
... | ... | @@ -112,6 +117,7 @@ public class DeviceController extends BaseController { |
112 | 117 | device.getCustomerId(), |
113 | 118 | ActionType.DELETED, null, strDeviceId); |
114 | 119 | |
120 | + deviceStateService.onDeviceDeleted(device); | |
115 | 121 | } catch (Exception e) { |
116 | 122 | logEntityAction(emptyId(EntityType.DEVICE), |
117 | 123 | null, |
... | ... | @@ -387,7 +393,7 @@ public class DeviceController extends BaseController { |
387 | 393 | @RequestMapping(value = "/device/online", method = RequestMethod.GET) |
388 | 394 | @ResponseBody |
389 | 395 | public List<Device> getOnlineDevices(@RequestParam("contactType") DeviceStatusQuery.ContactType contactType, |
390 | - @RequestParam("threshold") long threshold) throws ThingsboardException { | |
396 | + @RequestParam("threshold") long threshold) throws ThingsboardException { | |
391 | 397 | try { |
392 | 398 | TenantId tenantId = getCurrentUser().getTenantId(); |
393 | 399 | ListenableFuture<List<Device>> offlineDevices = offlineService.findOnlineDevices(tenantId.getId(), contactType, threshold); | ... | ... |
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.controller; | |
17 | + | |
18 | +import com.fasterxml.jackson.databind.JsonNode; | |
19 | +import com.fasterxml.jackson.databind.ObjectMapper; | |
20 | +import com.google.common.util.concurrent.FutureCallback; | |
21 | +import lombok.extern.slf4j.Slf4j; | |
22 | +import org.springframework.beans.factory.annotation.Autowired; | |
23 | +import org.springframework.http.HttpStatus; | |
24 | +import org.springframework.http.ResponseEntity; | |
25 | +import org.springframework.security.access.prepost.PreAuthorize; | |
26 | +import org.springframework.util.StringUtils; | |
27 | +import org.springframework.web.bind.annotation.PathVariable; | |
28 | +import org.springframework.web.bind.annotation.RequestBody; | |
29 | +import org.springframework.web.bind.annotation.RequestMapping; | |
30 | +import org.springframework.web.bind.annotation.RequestMethod; | |
31 | +import org.springframework.web.bind.annotation.ResponseBody; | |
32 | +import org.springframework.web.bind.annotation.RestController; | |
33 | +import org.springframework.web.context.request.async.DeferredResult; | |
34 | +import org.thingsboard.server.common.data.audit.ActionType; | |
35 | +import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; | |
36 | +import org.thingsboard.server.common.data.exception.ThingsboardException; | |
37 | +import org.thingsboard.server.common.data.id.DeviceId; | |
38 | +import org.thingsboard.server.common.data.id.EntityId; | |
39 | +import org.thingsboard.server.common.data.id.TenantId; | |
40 | +import org.thingsboard.server.common.data.id.UUIDBased; | |
41 | +import org.thingsboard.server.common.data.rpc.RpcRequest; | |
42 | +import org.thingsboard.server.common.data.rpc.ToDeviceRpcRequestBody; | |
43 | +import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest; | |
44 | +import org.thingsboard.server.extensions.api.exception.ToErrorResponseEntity; | |
45 | +import org.thingsboard.server.extensions.api.plugins.PluginConstants; | |
46 | +import org.thingsboard.server.extensions.api.plugins.msg.FromDeviceRpcResponse; | |
47 | +import org.thingsboard.server.extensions.api.plugins.msg.RpcError; | |
48 | +import org.thingsboard.server.service.rpc.DeviceRpcService; | |
49 | +import org.thingsboard.server.service.rpc.LocalRequestMetaData; | |
50 | +import org.thingsboard.server.service.security.AccessValidator; | |
51 | +import org.thingsboard.server.service.security.model.SecurityUser; | |
52 | + | |
53 | +import javax.annotation.Nullable; | |
54 | +import javax.annotation.PostConstruct; | |
55 | +import javax.annotation.PreDestroy; | |
56 | +import java.io.IOException; | |
57 | +import java.util.Optional; | |
58 | +import java.util.UUID; | |
59 | +import java.util.concurrent.ExecutorService; | |
60 | +import java.util.concurrent.Executors; | |
61 | +import java.util.function.Consumer; | |
62 | + | |
63 | +/** | |
64 | + * Created by ashvayka on 22.03.18. | |
65 | + */ | |
66 | +@RestController | |
67 | +@RequestMapping(PluginConstants.RPC_URL_PREFIX) | |
68 | +@Slf4j | |
69 | +public class RpcController extends BaseController { | |
70 | + | |
71 | + public static final int DEFAULT_TIMEOUT = 10000; | |
72 | + protected final ObjectMapper jsonMapper = new ObjectMapper(); | |
73 | + | |
74 | + @Autowired | |
75 | + private DeviceRpcService deviceRpcService; | |
76 | + | |
77 | + @Autowired | |
78 | + private AccessValidator accessValidator; | |
79 | + | |
80 | + private ExecutorService executor; | |
81 | + | |
82 | + @PostConstruct | |
83 | + public void initExecutor() { | |
84 | + executor = Executors.newSingleThreadExecutor(); | |
85 | + } | |
86 | + | |
87 | + @PreDestroy | |
88 | + public void shutdownExecutor() { | |
89 | + if (executor != null) { | |
90 | + executor.shutdownNow(); | |
91 | + } | |
92 | + } | |
93 | + | |
94 | + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") | |
95 | + @RequestMapping(value = "/oneway/{deviceId}", method = RequestMethod.POST) | |
96 | + @ResponseBody | |
97 | + public DeferredResult<ResponseEntity> handleOneWayDeviceRPCRequest(@PathVariable("deviceId") String deviceIdStr, @RequestBody String requestBody) throws ThingsboardException { | |
98 | + return handleDeviceRPCRequest(true, new DeviceId(UUID.fromString(deviceIdStr)), requestBody); | |
99 | + } | |
100 | + | |
101 | + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") | |
102 | + @RequestMapping(value = "/twoway/{deviceId}", method = RequestMethod.POST) | |
103 | + @ResponseBody | |
104 | + public DeferredResult<ResponseEntity> handleTwoWayDeviceRPCRequest(@PathVariable("deviceId") String deviceIdStr, @RequestBody String requestBody) throws ThingsboardException { | |
105 | + return handleDeviceRPCRequest(false, new DeviceId(UUID.fromString(deviceIdStr)), requestBody); | |
106 | + } | |
107 | + | |
108 | + | |
109 | + private DeferredResult<ResponseEntity> handleDeviceRPCRequest(boolean oneWay, DeviceId deviceId, String requestBody) throws ThingsboardException { | |
110 | + try { | |
111 | + JsonNode rpcRequestBody = jsonMapper.readTree(requestBody); | |
112 | + RpcRequest cmd = new RpcRequest(rpcRequestBody.get("method").asText(), | |
113 | + jsonMapper.writeValueAsString(rpcRequestBody.get("params"))); | |
114 | + | |
115 | + if (rpcRequestBody.has("timeout")) { | |
116 | + cmd.setTimeout(rpcRequestBody.get("timeout").asLong()); | |
117 | + } | |
118 | + SecurityUser currentUser = getCurrentUser(); | |
119 | + TenantId tenantId = currentUser.getTenantId(); | |
120 | + final DeferredResult<ResponseEntity> response = new DeferredResult<>(); | |
121 | + long timeout = System.currentTimeMillis() + (cmd.getTimeout() != null ? cmd.getTimeout() : DEFAULT_TIMEOUT); | |
122 | + ToDeviceRpcRequestBody body = new ToDeviceRpcRequestBody(cmd.getMethodName(), cmd.getRequestData()); | |
123 | + accessValidator.validate(currentUser, deviceId, new HttpValidationCallback(response, new FutureCallback<DeferredResult<ResponseEntity>>() { | |
124 | + @Override | |
125 | + public void onSuccess(@Nullable DeferredResult<ResponseEntity> result) { | |
126 | + ToDeviceRpcRequest rpcRequest = new ToDeviceRpcRequest(UUID.randomUUID(), | |
127 | + tenantId, | |
128 | + deviceId, | |
129 | + oneWay, | |
130 | + timeout, | |
131 | + body | |
132 | + ); | |
133 | + deviceRpcService.process(rpcRequest, new Consumer<FromDeviceRpcResponse>(){ | |
134 | + | |
135 | + @Override | |
136 | + public void accept(FromDeviceRpcResponse fromDeviceRpcResponse) { | |
137 | + reply(new LocalRequestMetaData(rpcRequest, currentUser, result), fromDeviceRpcResponse); | |
138 | + } | |
139 | + }); | |
140 | + } | |
141 | + | |
142 | + @Override | |
143 | + public void onFailure(Throwable e) { | |
144 | + ResponseEntity entity; | |
145 | + if (e instanceof ToErrorResponseEntity) { | |
146 | + entity = ((ToErrorResponseEntity) e).toErrorResponseEntity(); | |
147 | + } else { | |
148 | + entity = new ResponseEntity(HttpStatus.UNAUTHORIZED); | |
149 | + } | |
150 | + logRpcCall(currentUser, deviceId, body, oneWay, Optional.empty(), e); | |
151 | + response.setResult(entity); | |
152 | + } | |
153 | + })); | |
154 | + return response; | |
155 | + } catch (IOException ioe) { | |
156 | + throw new ThingsboardException("Invalid request body", ioe, ThingsboardErrorCode.BAD_REQUEST_PARAMS); | |
157 | + } | |
158 | + } | |
159 | + | |
160 | + public void reply(LocalRequestMetaData rpcRequest, FromDeviceRpcResponse response) { | |
161 | + Optional<RpcError> rpcError = response.getError(); | |
162 | + DeferredResult<ResponseEntity> responseWriter = rpcRequest.getResponseWriter(); | |
163 | + if (rpcError.isPresent()) { | |
164 | + logRpcCall(rpcRequest, rpcError, null); | |
165 | + RpcError error = rpcError.get(); | |
166 | + switch (error) { | |
167 | + case TIMEOUT: | |
168 | + responseWriter.setResult(new ResponseEntity<>(HttpStatus.REQUEST_TIMEOUT)); | |
169 | + break; | |
170 | + case NO_ACTIVE_CONNECTION: | |
171 | + responseWriter.setResult(new ResponseEntity<>(HttpStatus.CONFLICT)); | |
172 | + break; | |
173 | + default: | |
174 | + responseWriter.setResult(new ResponseEntity<>(HttpStatus.REQUEST_TIMEOUT)); | |
175 | + break; | |
176 | + } | |
177 | + } else { | |
178 | + Optional<String> responseData = response.getResponse(); | |
179 | + if (responseData.isPresent() && !StringUtils.isEmpty(responseData.get())) { | |
180 | + String data = responseData.get(); | |
181 | + try { | |
182 | + logRpcCall(rpcRequest, rpcError, null); | |
183 | + responseWriter.setResult(new ResponseEntity<>(jsonMapper.readTree(data), HttpStatus.OK)); | |
184 | + } catch (IOException e) { | |
185 | + log.debug("Failed to decode device response: {}", data, e); | |
186 | + logRpcCall(rpcRequest, rpcError, e); | |
187 | + responseWriter.setResult(new ResponseEntity<>(HttpStatus.NOT_ACCEPTABLE)); | |
188 | + } | |
189 | + } else { | |
190 | + logRpcCall(rpcRequest, rpcError, null); | |
191 | + responseWriter.setResult(new ResponseEntity<>(HttpStatus.OK)); | |
192 | + } | |
193 | + } | |
194 | + } | |
195 | + | |
196 | + private void logRpcCall(LocalRequestMetaData rpcRequest, Optional<RpcError> rpcError, Throwable e) { | |
197 | + logRpcCall(rpcRequest.getUser(), rpcRequest.getRequest().getDeviceId(), rpcRequest.getRequest().getBody(), rpcRequest.getRequest().isOneway(), rpcError, null); | |
198 | + } | |
199 | + | |
200 | + | |
201 | + private void logRpcCall(SecurityUser user, EntityId entityId, ToDeviceRpcRequestBody body, boolean oneWay, Optional<RpcError> rpcError, Throwable e) { | |
202 | + String rpcErrorStr = ""; | |
203 | + if (rpcError.isPresent()) { | |
204 | + rpcErrorStr = "RPC Error: " + rpcError.get().name(); | |
205 | + } | |
206 | + String method = body.getMethod(); | |
207 | + String params = body.getParams(); | |
208 | + | |
209 | + auditLogService.logEntityAction( | |
210 | + user.getTenantId(), | |
211 | + user.getCustomerId(), | |
212 | + user.getId(), | |
213 | + user.getName(), | |
214 | + (UUIDBased & EntityId) entityId, | |
215 | + null, | |
216 | + ActionType.RPC_CALL, | |
217 | + BaseController.toException(e), | |
218 | + rpcErrorStr, | |
219 | + oneWay, | |
220 | + method, | |
221 | + params); | |
222 | + } | |
223 | + | |
224 | + | |
225 | +} | ... | ... |
... | ... | @@ -237,7 +237,7 @@ public class RuleChainController extends BaseController { |
237 | 237 | ScriptEngine engine = null; |
238 | 238 | try { |
239 | 239 | engine = new NashornJsEngine(script, functionName, argNames); |
240 | - TbMsg inMsg = new TbMsg(UUIDs.timeBased(), msgType, null, new TbMsgMetaData(metadata), data); | |
240 | + TbMsg inMsg = new TbMsg(UUIDs.timeBased(), msgType, null, new TbMsgMetaData(metadata), data, null, null, 0L); | |
241 | 241 | switch (scriptType) { |
242 | 242 | case "update": |
243 | 243 | output = msgToOutput(engine.executeUpdate(inMsg)); | ... | ... |
... | ... | @@ -15,21 +15,34 @@ |
15 | 15 | */ |
16 | 16 | package org.thingsboard.server.controller; |
17 | 17 | |
18 | +import lombok.extern.slf4j.Slf4j; | |
18 | 19 | import org.springframework.beans.factory.annotation.Autowired; |
19 | 20 | import org.springframework.http.HttpStatus; |
20 | 21 | import org.springframework.security.access.prepost.PreAuthorize; |
21 | -import org.springframework.web.bind.annotation.*; | |
22 | +import org.springframework.web.bind.annotation.PathVariable; | |
23 | +import org.springframework.web.bind.annotation.RequestBody; | |
24 | +import org.springframework.web.bind.annotation.RequestMapping; | |
25 | +import org.springframework.web.bind.annotation.RequestMethod; | |
26 | +import org.springframework.web.bind.annotation.RequestParam; | |
27 | +import org.springframework.web.bind.annotation.ResponseBody; | |
28 | +import org.springframework.web.bind.annotation.ResponseStatus; | |
29 | +import org.springframework.web.bind.annotation.RestController; | |
22 | 30 | import org.thingsboard.server.common.data.Tenant; |
31 | +import org.thingsboard.server.common.data.exception.ThingsboardException; | |
23 | 32 | import org.thingsboard.server.common.data.id.TenantId; |
24 | 33 | import org.thingsboard.server.common.data.page.TextPageData; |
25 | 34 | import org.thingsboard.server.common.data.page.TextPageLink; |
26 | 35 | import org.thingsboard.server.dao.tenant.TenantService; |
27 | -import org.thingsboard.server.common.data.exception.ThingsboardException; | |
36 | +import org.thingsboard.server.service.install.InstallScripts; | |
28 | 37 | |
29 | 38 | @RestController |
30 | 39 | @RequestMapping("/api") |
40 | +@Slf4j | |
31 | 41 | public class TenantController extends BaseController { |
32 | - | |
42 | + | |
43 | + @Autowired | |
44 | + private InstallScripts installScripts; | |
45 | + | |
33 | 46 | @Autowired |
34 | 47 | private TenantService tenantService; |
35 | 48 | |
... | ... | @@ -49,10 +62,15 @@ public class TenantController extends BaseController { |
49 | 62 | |
50 | 63 | @PreAuthorize("hasAuthority('SYS_ADMIN')") |
51 | 64 | @RequestMapping(value = "/tenant", method = RequestMethod.POST) |
52 | - @ResponseBody | |
65 | + @ResponseBody | |
53 | 66 | public Tenant saveTenant(@RequestBody Tenant tenant) throws ThingsboardException { |
54 | 67 | try { |
55 | - return checkNotNull(tenantService.saveTenant(tenant)); | |
68 | + boolean newTenant = tenant.getId() == null; | |
69 | + tenant = checkNotNull(tenantService.saveTenant(tenant)); | |
70 | + if (newTenant) { | |
71 | + installScripts.createDefaultRuleChains(tenant.getId()); | |
72 | + } | |
73 | + return tenant; | |
56 | 74 | } catch (Exception e) { |
57 | 75 | throw handleException(e); |
58 | 76 | } |
... | ... | @@ -72,7 +90,7 @@ public class TenantController extends BaseController { |
72 | 90 | } |
73 | 91 | |
74 | 92 | @PreAuthorize("hasAuthority('SYS_ADMIN')") |
75 | - @RequestMapping(value = "/tenants", params = { "limit" }, method = RequestMethod.GET) | |
93 | + @RequestMapping(value = "/tenants", params = {"limit"}, method = RequestMethod.GET) | |
76 | 94 | @ResponseBody |
77 | 95 | public TextPageData<Tenant> getTenants(@RequestParam int limit, |
78 | 96 | @RequestParam(required = false) String textSearch, |
... | ... | @@ -85,5 +103,5 @@ public class TenantController extends BaseController { |
85 | 103 | throw handleException(e); |
86 | 104 | } |
87 | 105 | } |
88 | - | |
106 | + | |
89 | 107 | } | ... | ... |
... | ... | @@ -23,9 +23,7 @@ import org.springframework.context.ApplicationContext; |
23 | 23 | import org.springframework.context.annotation.Profile; |
24 | 24 | import org.springframework.stereotype.Service; |
25 | 25 | import org.thingsboard.server.service.component.ComponentDiscoveryService; |
26 | -import org.thingsboard.server.service.install.DatabaseSchemaService; | |
27 | -import org.thingsboard.server.service.install.DatabaseUpgradeService; | |
28 | -import org.thingsboard.server.service.install.SystemDataLoaderService; | |
26 | +import org.thingsboard.server.service.install.*; | |
29 | 27 | |
30 | 28 | import java.nio.file.Paths; |
31 | 29 | |
... | ... | @@ -40,9 +38,6 @@ public class ThingsboardInstallService { |
40 | 38 | @Value("${install.upgrade.from_version:1.2.3}") |
41 | 39 | private String upgradeFromVersion; |
42 | 40 | |
43 | - @Value("${install.data_dir}") | |
44 | - private String dataDir; | |
45 | - | |
46 | 41 | @Value("${install.load_demo:false}") |
47 | 42 | private Boolean loadDemo; |
48 | 43 | |
... | ... | @@ -61,6 +56,9 @@ public class ThingsboardInstallService { |
61 | 56 | @Autowired |
62 | 57 | private SystemDataLoaderService systemDataLoaderService; |
63 | 58 | |
59 | + @Autowired | |
60 | + private DataUpdateService dataUpdateService; | |
61 | + | |
64 | 62 | public void performInstall() { |
65 | 63 | try { |
66 | 64 | if (isUpgrade) { |
... | ... | @@ -87,6 +85,8 @@ public class ThingsboardInstallService { |
87 | 85 | |
88 | 86 | databaseUpgradeService.upgradeDatabase("1.4.0"); |
89 | 87 | |
88 | + dataUpdateService.updateData("1.4.0"); | |
89 | + | |
90 | 90 | log.info("Updating system data..."); |
91 | 91 | |
92 | 92 | systemDataLoaderService.deleteSystemWidgetBundle("charts"); |
... | ... | @@ -113,13 +113,6 @@ public class ThingsboardInstallService { |
113 | 113 | |
114 | 114 | log.info("Starting ThingsBoard Installation..."); |
115 | 115 | |
116 | - if (this.dataDir == null) { | |
117 | - throw new RuntimeException("'install.data_dir' property should specified!"); | |
118 | - } | |
119 | - if (!Paths.get(this.dataDir).toFile().isDirectory()) { | |
120 | - throw new RuntimeException("'install.data_dir' property value is not a valid directory!"); | |
121 | - } | |
122 | - | |
123 | 116 | log.info("Installing DataBase schema..."); |
124 | 117 | |
125 | 118 | databaseSchemaService.createDatabaseSchema(); |
... | ... | @@ -131,8 +124,8 @@ public class ThingsboardInstallService { |
131 | 124 | systemDataLoaderService.createSysAdmin(); |
132 | 125 | systemDataLoaderService.createAdminSettings(); |
133 | 126 | systemDataLoaderService.loadSystemWidgets(); |
134 | - systemDataLoaderService.loadSystemPlugins(); | |
135 | - systemDataLoaderService.loadSystemRules(); | |
127 | +// systemDataLoaderService.loadSystemPlugins(); | |
128 | +// systemDataLoaderService.loadSystemRules(); | |
136 | 129 | |
137 | 130 | if (loadDemo) { |
138 | 131 | log.info("Loading demo data..."); | ... | ... |
... | ... | @@ -19,7 +19,6 @@ import com.google.protobuf.ByteString; |
19 | 19 | import io.grpc.Server; |
20 | 20 | import io.grpc.ServerBuilder; |
21 | 21 | import io.grpc.stub.StreamObserver; |
22 | -import lombok.Setter; | |
23 | 22 | import lombok.extern.slf4j.Slf4j; |
24 | 23 | import org.springframework.beans.factory.annotation.Autowired; |
25 | 24 | import org.springframework.stereotype.Service; |
... | ... | @@ -27,29 +26,24 @@ import org.springframework.util.SerializationUtils; |
27 | 26 | import org.thingsboard.server.actors.rpc.RpcBroadcastMsg; |
28 | 27 | import org.thingsboard.server.actors.rpc.RpcSessionCreateRequestMsg; |
29 | 28 | import org.thingsboard.server.actors.rpc.RpcSessionTellMsg; |
30 | -import org.thingsboard.server.actors.service.ActorService; | |
31 | 29 | import org.thingsboard.server.common.data.id.EntityId; |
32 | 30 | import org.thingsboard.server.common.msg.cluster.ServerAddress; |
33 | 31 | import org.thingsboard.server.common.msg.cluster.ToAllNodesMsg; |
34 | 32 | import org.thingsboard.server.common.msg.core.ToDeviceSessionActorMsg; |
35 | -import org.thingsboard.server.common.msg.device.ToDeviceActorMsg; | |
33 | +import org.thingsboard.server.common.msg.device.DeviceToDeviceActorMsg; | |
36 | 34 | import org.thingsboard.server.extensions.api.device.ToDeviceActorNotificationMsg; |
37 | 35 | import org.thingsboard.server.extensions.api.plugins.msg.FromDeviceRpcResponse; |
38 | -import org.thingsboard.server.extensions.api.plugins.msg.ToDeviceRpcRequest; | |
39 | -import org.thingsboard.server.extensions.api.plugins.msg.ToDeviceRpcRequestPluginMsg; | |
36 | +import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest; | |
40 | 37 | import org.thingsboard.server.extensions.api.plugins.msg.ToPluginRpcResponseDeviceMsg; |
41 | 38 | import org.thingsboard.server.extensions.api.plugins.rpc.PluginRpcMsg; |
42 | 39 | import org.thingsboard.server.gen.cluster.ClusterAPIProtos; |
43 | 40 | import org.thingsboard.server.gen.cluster.ClusterRpcServiceGrpc; |
44 | -import org.thingsboard.server.service.cluster.discovery.DiscoveryService; | |
45 | 41 | import org.thingsboard.server.service.cluster.discovery.ServerInstance; |
46 | 42 | import org.thingsboard.server.service.cluster.discovery.ServerInstanceService; |
47 | -import org.thingsboard.server.service.cluster.routing.ClusterRoutingService; | |
43 | +import org.thingsboard.server.service.rpc.ToDeviceRpcRequestActorMsg; | |
48 | 44 | |
49 | -import javax.annotation.PostConstruct; | |
50 | 45 | import javax.annotation.PreDestroy; |
51 | 46 | import java.io.IOException; |
52 | -import java.util.Set; | |
53 | 47 | import java.util.UUID; |
54 | 48 | import java.util.concurrent.ConcurrentHashMap; |
55 | 49 | import java.util.concurrent.ConcurrentMap; |
... | ... | @@ -124,7 +118,7 @@ public class ClusterGrpcService extends ClusterRpcServiceGrpc.ClusterRpcServiceI |
124 | 118 | } |
125 | 119 | |
126 | 120 | @Override |
127 | - public void tell(ServerAddress serverAddress, ToDeviceActorMsg toForward) { | |
121 | + public void tell(ServerAddress serverAddress, DeviceToDeviceActorMsg toForward) { | |
128 | 122 | ClusterAPIProtos.ToRpcServerMessage msg = ClusterAPIProtos.ToRpcServerMessage.newBuilder() |
129 | 123 | .setToDeviceActorRpcMsg(toProtoMsg(toForward)).build(); |
130 | 124 | tell(serverAddress, msg); |
... | ... | @@ -138,7 +132,7 @@ public class ClusterGrpcService extends ClusterRpcServiceGrpc.ClusterRpcServiceI |
138 | 132 | } |
139 | 133 | |
140 | 134 | @Override |
141 | - public void tell(ServerAddress serverAddress, ToDeviceRpcRequestPluginMsg toForward) { | |
135 | + public void tell(ServerAddress serverAddress, ToDeviceRpcRequestActorMsg toForward) { | |
142 | 136 | ClusterAPIProtos.ToRpcServerMessage msg = ClusterAPIProtos.ToRpcServerMessage.newBuilder() |
143 | 137 | .setToDeviceRpcRequestRpcMsg(toProtoMsg(toForward)).build(); |
144 | 138 | tell(serverAddress, msg); |
... | ... | @@ -190,7 +184,7 @@ public class ClusterGrpcService extends ClusterRpcServiceGrpc.ClusterRpcServiceI |
190 | 184 | } |
191 | 185 | } |
192 | 186 | |
193 | - private static ClusterAPIProtos.ToDeviceActorRpcMessage toProtoMsg(ToDeviceActorMsg msg) { | |
187 | + private static ClusterAPIProtos.ToDeviceActorRpcMessage toProtoMsg(DeviceToDeviceActorMsg msg) { | |
194 | 188 | return ClusterAPIProtos.ToDeviceActorRpcMessage.newBuilder().setData( |
195 | 189 | ByteString.copyFrom(SerializationUtils.serialize(msg)) |
196 | 190 | ).build(); |
... | ... | @@ -202,15 +196,10 @@ public class ClusterGrpcService extends ClusterRpcServiceGrpc.ClusterRpcServiceI |
202 | 196 | ).build(); |
203 | 197 | } |
204 | 198 | |
205 | - private static ClusterAPIProtos.ToDeviceRpcRequestRpcMessage toProtoMsg(ToDeviceRpcRequestPluginMsg msg) { | |
199 | + private static ClusterAPIProtos.ToDeviceRpcRequestRpcMessage toProtoMsg(ToDeviceRpcRequestActorMsg msg) { | |
206 | 200 | ClusterAPIProtos.ToDeviceRpcRequestRpcMessage.Builder builder = ClusterAPIProtos.ToDeviceRpcRequestRpcMessage.newBuilder(); |
207 | 201 | ToDeviceRpcRequest request = msg.getMsg(); |
208 | 202 | |
209 | - builder.setAddress(ClusterAPIProtos.PluginAddress.newBuilder() | |
210 | - .setTenantId(toUid(msg.getPluginTenantId().getId())) | |
211 | - .setPluginId(toUid(msg.getPluginId().getId())) | |
212 | - .build()); | |
213 | - | |
214 | 203 | builder.setDeviceTenantId(toUid(msg.getTenantId())); |
215 | 204 | builder.setDeviceId(toUid(msg.getDeviceId())); |
216 | 205 | |
... | ... | @@ -227,11 +216,6 @@ public class ClusterGrpcService extends ClusterRpcServiceGrpc.ClusterRpcServiceI |
227 | 216 | ClusterAPIProtos.ToPluginRpcResponseRpcMessage.Builder builder = ClusterAPIProtos.ToPluginRpcResponseRpcMessage.newBuilder(); |
228 | 217 | FromDeviceRpcResponse request = msg.getResponse(); |
229 | 218 | |
230 | - builder.setAddress(ClusterAPIProtos.PluginAddress.newBuilder() | |
231 | - .setTenantId(toUid(msg.getPluginTenantId().getId())) | |
232 | - .setPluginId(toUid(msg.getPluginId().getId())) | |
233 | - .build()); | |
234 | - | |
235 | 219 | builder.setMsgId(toUid(request.getId())); |
236 | 220 | request.getResponse().ifPresent(builder::setResponse); |
237 | 221 | request.getError().ifPresent(e -> builder.setError(e.name())); | ... | ... |
... | ... | @@ -19,12 +19,12 @@ import io.grpc.stub.StreamObserver; |
19 | 19 | import org.thingsboard.server.common.msg.cluster.ServerAddress; |
20 | 20 | import org.thingsboard.server.common.msg.cluster.ToAllNodesMsg; |
21 | 21 | import org.thingsboard.server.common.msg.core.ToDeviceSessionActorMsg; |
22 | -import org.thingsboard.server.common.msg.device.ToDeviceActorMsg; | |
22 | +import org.thingsboard.server.common.msg.device.DeviceToDeviceActorMsg; | |
23 | 23 | import org.thingsboard.server.extensions.api.device.ToDeviceActorNotificationMsg; |
24 | -import org.thingsboard.server.extensions.api.plugins.msg.ToDeviceRpcRequestPluginMsg; | |
25 | 24 | import org.thingsboard.server.extensions.api.plugins.msg.ToPluginRpcResponseDeviceMsg; |
26 | 25 | import org.thingsboard.server.extensions.api.plugins.rpc.PluginRpcMsg; |
27 | 26 | import org.thingsboard.server.gen.cluster.ClusterAPIProtos; |
27 | +import org.thingsboard.server.service.rpc.ToDeviceRpcRequestActorMsg; | |
28 | 28 | |
29 | 29 | import java.util.UUID; |
30 | 30 | |
... | ... | @@ -35,13 +35,13 @@ public interface ClusterRpcService { |
35 | 35 | |
36 | 36 | void init(RpcMsgListener listener); |
37 | 37 | |
38 | - void tell(ServerAddress serverAddress, ToDeviceActorMsg toForward); | |
38 | + void tell(ServerAddress serverAddress, DeviceToDeviceActorMsg toForward); | |
39 | 39 | |
40 | 40 | void tell(ServerAddress serverAddress, ToDeviceSessionActorMsg toForward); |
41 | 41 | |
42 | 42 | void tell(ServerAddress serverAddress, ToDeviceActorNotificationMsg toForward); |
43 | 43 | |
44 | - void tell(ServerAddress serverAddress, ToDeviceRpcRequestPluginMsg toForward); | |
44 | + void tell(ServerAddress serverAddress, ToDeviceRpcRequestActorMsg toForward); | |
45 | 45 | |
46 | 46 | void tell(ServerAddress serverAddress, ToPluginRpcResponseDeviceMsg toForward); |
47 | 47 | |
... | ... | @@ -50,4 +50,5 @@ public interface ClusterRpcService { |
50 | 50 | void broadcast(ToAllNodesMsg msg); |
51 | 51 | |
52 | 52 | void onSessionCreated(UUID msgUid, StreamObserver<ClusterAPIProtos.ToRpcServerMessage> inputStream); |
53 | + | |
53 | 54 | } | ... | ... |
... | ... | @@ -20,18 +20,16 @@ import org.thingsboard.server.actors.rpc.RpcSessionCreateRequestMsg; |
20 | 20 | import org.thingsboard.server.actors.rpc.RpcSessionTellMsg; |
21 | 21 | import org.thingsboard.server.common.msg.cluster.ToAllNodesMsg; |
22 | 22 | import org.thingsboard.server.common.msg.core.ToDeviceSessionActorMsg; |
23 | -import org.thingsboard.server.common.msg.device.ToDeviceActorMsg; | |
23 | +import org.thingsboard.server.common.msg.device.DeviceToDeviceActorMsg; | |
24 | 24 | import org.thingsboard.server.extensions.api.device.ToDeviceActorNotificationMsg; |
25 | 25 | import org.thingsboard.server.extensions.api.plugins.msg.ToPluginActorMsg; |
26 | -import org.thingsboard.server.extensions.api.plugins.rpc.PluginRpcMsg; | |
27 | -import org.thingsboard.server.gen.cluster.ClusterAPIProtos; | |
28 | 26 | |
29 | 27 | /** |
30 | 28 | * @author Andrew Shvayka |
31 | 29 | */ |
32 | 30 | public interface RpcMsgListener { |
33 | 31 | |
34 | - void onMsg(ToDeviceActorMsg msg); | |
32 | + void onMsg(DeviceToDeviceActorMsg msg); | |
35 | 33 | |
36 | 34 | void onMsg(ToDeviceActorNotificationMsg msg); |
37 | 35 | ... | ... |
application/src/main/java/org/thingsboard/server/service/executors/ExternalCallExecutorService.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.service.executors; | |
17 | + | |
18 | +import org.springframework.beans.factory.annotation.Value; | |
19 | +import org.springframework.stereotype.Component; | |
20 | + | |
21 | +@Component | |
22 | +public class ExternalCallExecutorService extends AbstractListeningExecutor { | |
23 | + | |
24 | + @Value("${actors.rule.external_call_thread_pool_size}") | |
25 | + private int externalCallExecutorThreadPoolSize; | |
26 | + | |
27 | + @Override | |
28 | + protected int getThreadPollSize() { | |
29 | + return externalCallExecutorThreadPoolSize; | |
30 | + } | |
31 | + | |
32 | +} | |
33 | + | ... | ... |
application/src/main/java/org/thingsboard/server/service/install/CassandraDatabaseSchemaService.java
... | ... | @@ -37,17 +37,16 @@ public class CassandraDatabaseSchemaService implements DatabaseSchemaService { |
37 | 37 | private static final String CASSANDRA_DIR = "cassandra"; |
38 | 38 | private static final String SCHEMA_CQL = "schema.cql"; |
39 | 39 | |
40 | - @Value("${install.data_dir}") | |
41 | - private String dataDir; | |
42 | - | |
43 | 40 | @Autowired |
44 | 41 | private CassandraInstallCluster cluster; |
45 | 42 | |
43 | + @Autowired | |
44 | + private InstallScripts installScripts; | |
45 | + | |
46 | 46 | @Override |
47 | 47 | public void createDatabaseSchema() throws Exception { |
48 | 48 | log.info("Installing Cassandra DataBase schema..."); |
49 | - | |
50 | - Path schemaFile = Paths.get(this.dataDir, CASSANDRA_DIR, SCHEMA_CQL); | |
49 | + Path schemaFile = Paths.get(installScripts.getDataDir(), CASSANDRA_DIR, SCHEMA_CQL); | |
51 | 50 | loadCql(schemaFile); |
52 | 51 | |
53 | 52 | } | ... | ... |
... | ... | @@ -43,9 +43,6 @@ public class CassandraDatabaseUpgradeService implements DatabaseUpgradeService { |
43 | 43 | |
44 | 44 | private static final String SCHEMA_UPDATE_CQL = "schema_update.cql"; |
45 | 45 | |
46 | - @Value("${install.data_dir}") | |
47 | - private String dataDir; | |
48 | - | |
49 | 46 | @Autowired |
50 | 47 | private CassandraCluster cluster; |
51 | 48 | |
... | ... | @@ -55,6 +52,9 @@ public class CassandraDatabaseUpgradeService implements DatabaseUpgradeService { |
55 | 52 | @Autowired |
56 | 53 | private DashboardService dashboardService; |
57 | 54 | |
55 | + @Autowired | |
56 | + private InstallScripts installScripts; | |
57 | + | |
58 | 58 | @Override |
59 | 59 | public void upgradeDatabase(String fromVersion) throws Exception { |
60 | 60 | |
... | ... | @@ -91,7 +91,7 @@ public class CassandraDatabaseUpgradeService implements DatabaseUpgradeService { |
91 | 91 | log.info("Relations dumped."); |
92 | 92 | |
93 | 93 | log.info("Updating schema ..."); |
94 | - Path schemaUpdateFile = Paths.get(this.dataDir, "upgrade", "1.3.0", SCHEMA_UPDATE_CQL); | |
94 | + Path schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "1.3.0", SCHEMA_UPDATE_CQL); | |
95 | 95 | loadCql(schemaUpdateFile); |
96 | 96 | log.info("Schema updated."); |
97 | 97 | |
... | ... | @@ -173,7 +173,7 @@ public class CassandraDatabaseUpgradeService implements DatabaseUpgradeService { |
173 | 173 | |
174 | 174 | |
175 | 175 | log.info("Updating schema ..."); |
176 | - schemaUpdateFile = Paths.get(this.dataDir, "upgrade", "1.4.0", SCHEMA_UPDATE_CQL); | |
176 | + schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "1.4.0", SCHEMA_UPDATE_CQL); | |
177 | 177 | loadCql(schemaUpdateFile); |
178 | 178 | log.info("Schema updated."); |
179 | 179 | |
... | ... | @@ -189,7 +189,7 @@ public class CassandraDatabaseUpgradeService implements DatabaseUpgradeService { |
189 | 189 | case "1.4.0": |
190 | 190 | |
191 | 191 | log.info("Updating schema ..."); |
192 | - schemaUpdateFile = Paths.get(this.dataDir, "upgrade", "1.5.0", SCHEMA_UPDATE_CQL); | |
192 | + schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "1.5.0", SCHEMA_UPDATE_CQL); | |
193 | 193 | loadCql(schemaUpdateFile); |
194 | 194 | log.info("Schema updated."); |
195 | 195 | ... | ... |
application/src/main/java/org/thingsboard/server/service/install/DataUpdateService.java
renamed from
application/src/test/java/org/thingsboard/server/controller/sql/RuleControllerSqlTest.java
... | ... | @@ -13,14 +13,10 @@ |
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.controller.sql; | |
16 | +package org.thingsboard.server.service.install; | |
17 | 17 | |
18 | -import org.thingsboard.server.controller.BaseRuleControllerTest; | |
19 | -import org.thingsboard.server.dao.service.DaoSqlTest; | |
18 | +public interface DataUpdateService { | |
19 | + | |
20 | + void updateData(String fromVersion) throws Exception; | |
20 | 21 | |
21 | -/** | |
22 | - * Created by Valerii Sosliuk on 6/28/2017. | |
23 | - */ | |
24 | -@DaoSqlTest | |
25 | -public class RuleControllerSqlTest extends BaseRuleControllerTest { | |
26 | 22 | } | ... | ... |
application/src/main/java/org/thingsboard/server/service/install/DefaultDataUpdateService.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.service.install; | |
17 | + | |
18 | +import lombok.extern.slf4j.Slf4j; | |
19 | +import org.springframework.beans.factory.annotation.Autowired; | |
20 | +import org.springframework.context.annotation.Profile; | |
21 | +import org.springframework.stereotype.Service; | |
22 | +import org.thingsboard.server.common.data.Tenant; | |
23 | +import org.thingsboard.server.common.data.id.IdBased; | |
24 | +import org.thingsboard.server.common.data.page.TextPageLink; | |
25 | +import org.thingsboard.server.common.data.rule.RuleChain; | |
26 | +import org.thingsboard.server.dao.rule.RuleChainService; | |
27 | +import org.thingsboard.server.dao.tenant.TenantService; | |
28 | + | |
29 | +import java.util.List; | |
30 | +import java.util.UUID; | |
31 | + | |
32 | +@Service | |
33 | +@Profile("install") | |
34 | +@Slf4j | |
35 | +public class DefaultDataUpdateService implements DataUpdateService { | |
36 | + | |
37 | + @Autowired | |
38 | + private TenantService tenantService; | |
39 | + | |
40 | + @Autowired | |
41 | + private RuleChainService ruleChainService; | |
42 | + | |
43 | + @Autowired | |
44 | + private InstallScripts installScripts; | |
45 | + | |
46 | + @Override | |
47 | + public void updateData(String fromVersion) throws Exception { | |
48 | + switch (fromVersion) { | |
49 | + case "1.4.0": | |
50 | + log.info("Updating data from version 1.4.0 to 1.5.0 ..."); | |
51 | + tenantsDefaultRuleChainUpdater.updateEntities(null); | |
52 | + break; | |
53 | + default: | |
54 | + throw new RuntimeException("Unable to update data, unsupported fromVersion: " + fromVersion); | |
55 | + } | |
56 | + } | |
57 | + | |
58 | + private PaginatedUpdater<String, Tenant> tenantsDefaultRuleChainUpdater = | |
59 | + new PaginatedUpdater<String, Tenant>() { | |
60 | + | |
61 | + @Override | |
62 | + protected List<Tenant> findEntities(String region, TextPageLink pageLink) { | |
63 | + return tenantService.findTenants(pageLink).getData(); | |
64 | + } | |
65 | + | |
66 | + @Override | |
67 | + protected void updateEntity(Tenant tenant) { | |
68 | + try { | |
69 | + RuleChain ruleChain = ruleChainService.getRootTenantRuleChain(tenant.getId()); | |
70 | + if (ruleChain == null) { | |
71 | + installScripts.createDefaultRuleChains(tenant.getId()); | |
72 | + } | |
73 | + } catch (Exception e) { | |
74 | + log.error("Unable to update Tenant", e); | |
75 | + } | |
76 | + } | |
77 | + }; | |
78 | + | |
79 | + public abstract class PaginatedUpdater<I, D extends IdBased<?>> { | |
80 | + | |
81 | + private static final int DEFAULT_LIMIT = 100; | |
82 | + | |
83 | + public void updateEntities(I id) { | |
84 | + TextPageLink pageLink = new TextPageLink(DEFAULT_LIMIT); | |
85 | + boolean hasNext = true; | |
86 | + while (hasNext) { | |
87 | + List<D> entities = findEntities(id, pageLink); | |
88 | + for (D entity : entities) { | |
89 | + updateEntity(entity); | |
90 | + } | |
91 | + hasNext = entities.size() == pageLink.getLimit(); | |
92 | + if (hasNext) { | |
93 | + int index = entities.size() - 1; | |
94 | + UUID idOffset = entities.get(index).getUuidId(); | |
95 | + pageLink.setIdOffset(idOffset); | |
96 | + } | |
97 | + } | |
98 | + } | |
99 | + | |
100 | + protected abstract List<D> findEntities(I id, TextPageLink pageLink); | |
101 | + | |
102 | + protected abstract void updateEntity(D entity); | |
103 | + | |
104 | + } | |
105 | + | |
106 | +} | |
\ No newline at end of file | ... | ... |
application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java
... | ... | @@ -60,21 +60,12 @@ import java.nio.file.Paths; |
60 | 60 | @Slf4j |
61 | 61 | public class DefaultSystemDataLoaderService implements SystemDataLoaderService { |
62 | 62 | |
63 | - private static final String JSON_DIR = "json"; | |
64 | - private static final String SYSTEM_DIR = "system"; | |
65 | - private static final String DEMO_DIR = "demo"; | |
66 | - private static final String WIDGET_BUNDLES_DIR = "widget_bundles"; | |
67 | - private static final String PLUGINS_DIR = "plugins"; | |
68 | - private static final String RULES_DIR = "rules"; | |
69 | - private static final String DASHBOARDS_DIR = "dashboards"; | |
70 | - | |
71 | 63 | private static final ObjectMapper objectMapper = new ObjectMapper(); |
72 | - public static final String JSON_EXT = ".json"; | |
73 | 64 | public static final String CUSTOMER_CRED = "customer"; |
74 | 65 | public static final String DEFAULT_DEVICE_TYPE = "default"; |
75 | 66 | |
76 | - @Value("${install.data_dir}") | |
77 | - private String dataDir; | |
67 | + @Autowired | |
68 | + private InstallScripts installScripts; | |
78 | 69 | |
79 | 70 | @Autowired |
80 | 71 | private BCryptPasswordEncoder passwordEncoder; |
... | ... | @@ -89,15 +80,6 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { |
89 | 80 | private WidgetsBundleService widgetsBundleService; |
90 | 81 | |
91 | 82 | @Autowired |
92 | - private WidgetTypeService widgetTypeService; | |
93 | - | |
94 | - @Autowired | |
95 | - private PluginService pluginService; | |
96 | - | |
97 | - @Autowired | |
98 | - private RuleService ruleService; | |
99 | - | |
100 | - @Autowired | |
101 | 83 | private TenantService tenantService; |
102 | 84 | |
103 | 85 | @Autowired |
... | ... | @@ -109,9 +91,6 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { |
109 | 91 | @Autowired |
110 | 92 | private DeviceCredentialsService deviceCredentialsService; |
111 | 93 | |
112 | - @Autowired | |
113 | - private DashboardService dashboardService; | |
114 | - | |
115 | 94 | @Bean |
116 | 95 | protected BCryptPasswordEncoder passwordEncoder() { |
117 | 96 | return new BCryptPasswordEncoder(); |
... | ... | @@ -147,55 +126,12 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { |
147 | 126 | } |
148 | 127 | |
149 | 128 | @Override |
150 | - public void loadSystemWidgets() throws Exception { | |
151 | - Path widgetBundlesDir = Paths.get(dataDir, JSON_DIR, SYSTEM_DIR, WIDGET_BUNDLES_DIR); | |
152 | - try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(widgetBundlesDir, path -> path.toString().endsWith(JSON_EXT))) { | |
153 | - dirStream.forEach( | |
154 | - path -> { | |
155 | - try { | |
156 | - JsonNode widgetsBundleDescriptorJson = objectMapper.readTree(path.toFile()); | |
157 | - JsonNode widgetsBundleJson = widgetsBundleDescriptorJson.get("widgetsBundle"); | |
158 | - WidgetsBundle widgetsBundle = objectMapper.treeToValue(widgetsBundleJson, WidgetsBundle.class); | |
159 | - WidgetsBundle savedWidgetsBundle = widgetsBundleService.saveWidgetsBundle(widgetsBundle); | |
160 | - JsonNode widgetTypesArrayJson = widgetsBundleDescriptorJson.get("widgetTypes"); | |
161 | - widgetTypesArrayJson.forEach( | |
162 | - widgetTypeJson -> { | |
163 | - try { | |
164 | - WidgetType widgetType = objectMapper.treeToValue(widgetTypeJson, WidgetType.class); | |
165 | - widgetType.setBundleAlias(savedWidgetsBundle.getAlias()); | |
166 | - widgetTypeService.saveWidgetType(widgetType); | |
167 | - } catch (Exception e) { | |
168 | - log.error("Unable to load widget type from json: [{}]", path.toString()); | |
169 | - throw new RuntimeException("Unable to load widget type from json", e); | |
170 | - } | |
171 | - } | |
172 | - ); | |
173 | - } catch (Exception e) { | |
174 | - log.error("Unable to load widgets bundle from json: [{}]", path.toString()); | |
175 | - throw new RuntimeException("Unable to load widgets bundle from json", e); | |
176 | - } | |
177 | - } | |
178 | - ); | |
179 | - } | |
180 | - } | |
181 | - | |
182 | - @Override | |
183 | - public void loadSystemPlugins() throws Exception { | |
184 | - loadPlugins(Paths.get(dataDir, JSON_DIR, SYSTEM_DIR, PLUGINS_DIR), null); | |
185 | - } | |
186 | - | |
187 | - | |
188 | - @Override | |
189 | - public void loadSystemRules() throws Exception { | |
190 | -// loadRules(Paths.get(dataDir, JSON_DIR, SYSTEM_DIR, RULES_DIR), null); | |
191 | - } | |
192 | - | |
193 | - @Override | |
194 | 129 | public void loadDemoData() throws Exception { |
195 | 130 | Tenant demoTenant = new Tenant(); |
196 | 131 | demoTenant.setRegion("Global"); |
197 | 132 | demoTenant.setTitle("Tenant"); |
198 | 133 | demoTenant = tenantService.saveTenant(demoTenant); |
134 | + installScripts.createDefaultRuleChains(demoTenant.getId()); | |
199 | 135 | createUser(Authority.TENANT_ADMIN, demoTenant.getId(), null, "tenant@thingsboard.org", "tenant"); |
200 | 136 | |
201 | 137 | Customer customerA = new Customer(); |
... | ... | @@ -227,9 +163,7 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { |
227 | 163 | createDevice(demoTenant.getId(), null, DEFAULT_DEVICE_TYPE, "Raspberry Pi Demo Device", "RASPBERRY_PI_DEMO_TOKEN", "Demo device that is used in " + |
228 | 164 | "Raspberry Pi GPIO control sample application"); |
229 | 165 | |
230 | - loadPlugins(Paths.get(dataDir, JSON_DIR, DEMO_DIR, PLUGINS_DIR), demoTenant.getId()); | |
231 | -// loadRules(Paths.get(dataDir, JSON_DIR, DEMO_DIR, RULES_DIR), demoTenant.getId()); | |
232 | - loadDashboards(Paths.get(dataDir, JSON_DIR, DEMO_DIR, DASHBOARDS_DIR), demoTenant.getId(), null); | |
166 | + installScripts.loadDashboards(demoTenant.getId(), null); | |
233 | 167 | } |
234 | 168 | |
235 | 169 | @Override |
... | ... | @@ -240,6 +174,11 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { |
240 | 174 | } |
241 | 175 | } |
242 | 176 | |
177 | + @Override | |
178 | + public void loadSystemWidgets() throws Exception { | |
179 | + installScripts.loadSystemWidgets(); | |
180 | + } | |
181 | + | |
243 | 182 | private User createUser(Authority authority, |
244 | 183 | TenantId tenantId, |
245 | 184 | CustomerId customerId, |
... | ... | @@ -282,72 +221,4 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { |
282 | 221 | return device; |
283 | 222 | } |
284 | 223 | |
285 | - private void loadPlugins(Path pluginsDir, TenantId tenantId) throws Exception{ | |
286 | - try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(pluginsDir, path -> path.toString().endsWith(JSON_EXT))) { | |
287 | - dirStream.forEach( | |
288 | - path -> { | |
289 | - try { | |
290 | - JsonNode pluginJson = objectMapper.readTree(path.toFile()); | |
291 | - PluginMetaData plugin = objectMapper.treeToValue(pluginJson, PluginMetaData.class); | |
292 | - plugin.setTenantId(tenantId); | |
293 | - if (plugin.getState() == ComponentLifecycleState.ACTIVE) { | |
294 | - plugin.setState(ComponentLifecycleState.SUSPENDED); | |
295 | - PluginMetaData savedPlugin = pluginService.savePlugin(plugin); | |
296 | - pluginService.activatePluginById(savedPlugin.getId()); | |
297 | - } else { | |
298 | - pluginService.savePlugin(plugin); | |
299 | - } | |
300 | - } catch (Exception e) { | |
301 | - log.error("Unable to load plugin from json: [{}]", path.toString()); | |
302 | - throw new RuntimeException("Unable to load plugin from json", e); | |
303 | - } | |
304 | - } | |
305 | - ); | |
306 | - } | |
307 | - } | |
308 | - | |
309 | - private void loadRules(Path rulesDir, TenantId tenantId) throws Exception { | |
310 | - try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(rulesDir, path -> path.toString().endsWith(JSON_EXT))) { | |
311 | - dirStream.forEach( | |
312 | - path -> { | |
313 | - try { | |
314 | - JsonNode ruleJson = objectMapper.readTree(path.toFile()); | |
315 | - RuleMetaData rule = objectMapper.treeToValue(ruleJson, RuleMetaData.class); | |
316 | - rule.setTenantId(tenantId); | |
317 | - if (rule.getState() == ComponentLifecycleState.ACTIVE) { | |
318 | - rule.setState(ComponentLifecycleState.SUSPENDED); | |
319 | - RuleMetaData savedRule = ruleService.saveRule(rule); | |
320 | - ruleService.activateRuleById(savedRule.getId()); | |
321 | - } else { | |
322 | - ruleService.saveRule(rule); | |
323 | - } | |
324 | - } catch (Exception e) { | |
325 | - log.error("Unable to load rule from json: [{}]", path.toString()); | |
326 | - throw new RuntimeException("Unable to load rule from json", e); | |
327 | - } | |
328 | - } | |
329 | - ); | |
330 | - } | |
331 | - } | |
332 | - | |
333 | - private void loadDashboards(Path dashboardsDir, TenantId tenantId, CustomerId customerId) throws Exception { | |
334 | - try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(dashboardsDir, path -> path.toString().endsWith(JSON_EXT))) { | |
335 | - dirStream.forEach( | |
336 | - path -> { | |
337 | - try { | |
338 | - JsonNode dashboardJson = objectMapper.readTree(path.toFile()); | |
339 | - Dashboard dashboard = objectMapper.treeToValue(dashboardJson, Dashboard.class); | |
340 | - dashboard.setTenantId(tenantId); | |
341 | - Dashboard savedDashboard = dashboardService.saveDashboard(dashboard); | |
342 | - if (customerId != null && !customerId.isNullUid()) { | |
343 | - dashboardService.assignDashboardToCustomer(savedDashboard.getId(), customerId); | |
344 | - } | |
345 | - } catch (Exception e) { | |
346 | - log.error("Unable to load dashboard from json: [{}]", path.toString()); | |
347 | - throw new RuntimeException("Unable to load dashboard from json", e); | |
348 | - } | |
349 | - } | |
350 | - ); | |
351 | - } | |
352 | - } | |
353 | 224 | } | ... | ... |
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.service.install; | |
17 | + | |
18 | +import com.fasterxml.jackson.databind.JsonNode; | |
19 | +import lombok.Getter; | |
20 | +import lombok.extern.slf4j.Slf4j; | |
21 | +import org.springframework.beans.factory.annotation.Autowired; | |
22 | +import org.springframework.beans.factory.annotation.Value; | |
23 | +import org.springframework.stereotype.Component; | |
24 | +import org.springframework.util.StringUtils; | |
25 | +import org.thingsboard.server.common.data.Dashboard; | |
26 | +import org.thingsboard.server.common.data.id.CustomerId; | |
27 | +import org.thingsboard.server.common.data.id.TenantId; | |
28 | +import org.thingsboard.server.common.data.rule.RuleChain; | |
29 | +import org.thingsboard.server.common.data.rule.RuleChainMetaData; | |
30 | +import org.thingsboard.server.common.data.widget.WidgetType; | |
31 | +import org.thingsboard.server.common.data.widget.WidgetsBundle; | |
32 | +import org.thingsboard.server.dao.dashboard.DashboardService; | |
33 | +import org.thingsboard.server.dao.rule.RuleChainService; | |
34 | +import org.thingsboard.server.dao.widget.WidgetTypeService; | |
35 | +import org.thingsboard.server.dao.widget.WidgetsBundleService; | |
36 | + | |
37 | +import java.io.IOException; | |
38 | +import java.nio.file.DirectoryStream; | |
39 | +import java.nio.file.Files; | |
40 | +import java.nio.file.Path; | |
41 | +import java.nio.file.Paths; | |
42 | + | |
43 | +import static org.thingsboard.server.service.install.DatabaseHelper.objectMapper; | |
44 | + | |
45 | +/** | |
46 | + * Created by ashvayka on 18.04.18. | |
47 | + */ | |
48 | +@Component | |
49 | +@Slf4j | |
50 | +public class InstallScripts { | |
51 | + | |
52 | + public static final String APP_DIR = "application"; | |
53 | + public static final String SRC_DIR = "src"; | |
54 | + public static final String MAIN_DIR = "main"; | |
55 | + public static final String DATA_DIR = "data"; | |
56 | + public static final String JSON_DIR = "json"; | |
57 | + public static final String SYSTEM_DIR = "system"; | |
58 | + public static final String TENANT_DIR = "tenant"; | |
59 | + public static final String DEMO_DIR = "demo"; | |
60 | + public static final String RULE_CHAINS_DIR = "rule_chains"; | |
61 | + public static final String WIDGET_BUNDLES_DIR = "widget_bundles"; | |
62 | + public static final String DASHBOARDS_DIR = "dashboards"; | |
63 | + | |
64 | + public static final String JSON_EXT = ".json"; | |
65 | + | |
66 | + @Value("${install.data_dir:}") | |
67 | + private String dataDir; | |
68 | + | |
69 | + @Autowired | |
70 | + private RuleChainService ruleChainService; | |
71 | + | |
72 | + @Autowired | |
73 | + private DashboardService dashboardService; | |
74 | + | |
75 | + @Autowired | |
76 | + private WidgetTypeService widgetTypeService; | |
77 | + | |
78 | + @Autowired | |
79 | + private WidgetsBundleService widgetsBundleService; | |
80 | + | |
81 | + public Path getTenantRuleChainsDir() { | |
82 | + return Paths.get(getDataDir(), JSON_DIR, TENANT_DIR, RULE_CHAINS_DIR); | |
83 | + } | |
84 | + | |
85 | + public String getDataDir() { | |
86 | + if (!StringUtils.isEmpty(dataDir)) { | |
87 | + if (!Paths.get(this.dataDir).toFile().isDirectory()) { | |
88 | + throw new RuntimeException("'install.data_dir' property value is not a valid directory!"); | |
89 | + } | |
90 | + return dataDir; | |
91 | + } else { | |
92 | + String workDir = System.getProperty("user.dir"); | |
93 | + if (workDir.endsWith("application")) { | |
94 | + return Paths.get(workDir, SRC_DIR, MAIN_DIR, DATA_DIR).toString(); | |
95 | + } else { | |
96 | + Path dataDirPath = Paths.get(workDir, APP_DIR, SRC_DIR, MAIN_DIR, DATA_DIR); | |
97 | + if (Files.exists(dataDirPath)) { | |
98 | + return dataDirPath.toString(); | |
99 | + } else { | |
100 | + throw new RuntimeException("Not valid working directory: " + workDir + ". Please use either root project directory, application module directory or specify valid \"install.data_dir\" ENV variable to avoid automatic data directory lookup!"); | |
101 | + } | |
102 | + } | |
103 | + } | |
104 | + } | |
105 | + | |
106 | + public void createDefaultRuleChains(TenantId tenantId) throws IOException { | |
107 | + Path tenantChainsDir = getTenantRuleChainsDir(); | |
108 | + try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(tenantChainsDir, path -> path.toString().endsWith(InstallScripts.JSON_EXT))) { | |
109 | + dirStream.forEach( | |
110 | + path -> { | |
111 | + try { | |
112 | + JsonNode ruleChainJson = objectMapper.readTree(path.toFile()); | |
113 | + RuleChain ruleChain = objectMapper.treeToValue(ruleChainJson.get("ruleChain"), RuleChain.class); | |
114 | + RuleChainMetaData ruleChainMetaData = objectMapper.treeToValue(ruleChainJson.get("metadata"), RuleChainMetaData.class); | |
115 | + | |
116 | + ruleChain.setTenantId(tenantId); | |
117 | + ruleChain = ruleChainService.saveRuleChain(ruleChain); | |
118 | + | |
119 | + ruleChainMetaData.setRuleChainId(ruleChain.getId()); | |
120 | + ruleChainService.saveRuleChainMetaData(ruleChainMetaData); | |
121 | + } catch (Exception e) { | |
122 | + log.error("Unable to load rule chain from json: [{}]", path.toString()); | |
123 | + throw new RuntimeException("Unable to load rule chain from json", e); | |
124 | + } | |
125 | + } | |
126 | + ); | |
127 | + } | |
128 | + } | |
129 | + | |
130 | + public void loadSystemWidgets() throws Exception { | |
131 | + Path widgetBundlesDir = Paths.get(getDataDir(), JSON_DIR, SYSTEM_DIR, WIDGET_BUNDLES_DIR); | |
132 | + try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(widgetBundlesDir, path -> path.toString().endsWith(JSON_EXT))) { | |
133 | + dirStream.forEach( | |
134 | + path -> { | |
135 | + try { | |
136 | + JsonNode widgetsBundleDescriptorJson = objectMapper.readTree(path.toFile()); | |
137 | + JsonNode widgetsBundleJson = widgetsBundleDescriptorJson.get("widgetsBundle"); | |
138 | + WidgetsBundle widgetsBundle = objectMapper.treeToValue(widgetsBundleJson, WidgetsBundle.class); | |
139 | + WidgetsBundle savedWidgetsBundle = widgetsBundleService.saveWidgetsBundle(widgetsBundle); | |
140 | + JsonNode widgetTypesArrayJson = widgetsBundleDescriptorJson.get("widgetTypes"); | |
141 | + widgetTypesArrayJson.forEach( | |
142 | + widgetTypeJson -> { | |
143 | + try { | |
144 | + WidgetType widgetType = objectMapper.treeToValue(widgetTypeJson, WidgetType.class); | |
145 | + widgetType.setBundleAlias(savedWidgetsBundle.getAlias()); | |
146 | + widgetTypeService.saveWidgetType(widgetType); | |
147 | + } catch (Exception e) { | |
148 | + log.error("Unable to load widget type from json: [{}]", path.toString()); | |
149 | + throw new RuntimeException("Unable to load widget type from json", e); | |
150 | + } | |
151 | + } | |
152 | + ); | |
153 | + } catch (Exception e) { | |
154 | + log.error("Unable to load widgets bundle from json: [{}]", path.toString()); | |
155 | + throw new RuntimeException("Unable to load widgets bundle from json", e); | |
156 | + } | |
157 | + } | |
158 | + ); | |
159 | + } | |
160 | + } | |
161 | + | |
162 | + public void loadDashboards(TenantId tenantId, CustomerId customerId) throws Exception { | |
163 | + Path dashboardsDir = Paths.get(getDataDir(), JSON_DIR, DEMO_DIR, DASHBOARDS_DIR); | |
164 | + try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(dashboardsDir, path -> path.toString().endsWith(JSON_EXT))) { | |
165 | + dirStream.forEach( | |
166 | + path -> { | |
167 | + try { | |
168 | + JsonNode dashboardJson = objectMapper.readTree(path.toFile()); | |
169 | + Dashboard dashboard = objectMapper.treeToValue(dashboardJson, Dashboard.class); | |
170 | + dashboard.setTenantId(tenantId); | |
171 | + Dashboard savedDashboard = dashboardService.saveDashboard(dashboard); | |
172 | + if (customerId != null && !customerId.isNullUid()) { | |
173 | + dashboardService.assignDashboardToCustomer(savedDashboard.getId(), customerId); | |
174 | + } | |
175 | + } catch (Exception e) { | |
176 | + log.error("Unable to load dashboard from json: [{}]", path.toString()); | |
177 | + throw new RuntimeException("Unable to load dashboard from json", e); | |
178 | + } | |
179 | + } | |
180 | + ); | |
181 | + } | |
182 | + } | |
183 | + | |
184 | + | |
185 | +} | ... | ... |
... | ... | @@ -16,6 +16,7 @@ |
16 | 16 | package org.thingsboard.server.service.install; |
17 | 17 | |
18 | 18 | import lombok.extern.slf4j.Slf4j; |
19 | +import org.springframework.beans.factory.annotation.Autowired; | |
19 | 20 | import org.springframework.beans.factory.annotation.Value; |
20 | 21 | import org.springframework.context.annotation.Profile; |
21 | 22 | import org.springframework.stereotype.Service; |
... | ... | @@ -38,9 +39,6 @@ public class SqlDatabaseSchemaService implements DatabaseSchemaService { |
38 | 39 | private static final String SQL_DIR = "sql"; |
39 | 40 | private static final String SCHEMA_SQL = "schema.sql"; |
40 | 41 | |
41 | - @Value("${install.data_dir}") | |
42 | - private String dataDir; | |
43 | - | |
44 | 42 | @Value("${spring.datasource.url}") |
45 | 43 | private String dbUrl; |
46 | 44 | |
... | ... | @@ -50,12 +48,15 @@ public class SqlDatabaseSchemaService implements DatabaseSchemaService { |
50 | 48 | @Value("${spring.datasource.password}") |
51 | 49 | private String dbPassword; |
52 | 50 | |
51 | + @Autowired | |
52 | + private InstallScripts installScripts; | |
53 | + | |
53 | 54 | @Override |
54 | 55 | public void createDatabaseSchema() throws Exception { |
55 | 56 | |
56 | 57 | log.info("Installing SQL DataBase schema..."); |
57 | 58 | |
58 | - Path schemaFile = Paths.get(this.dataDir, SQL_DIR, SCHEMA_SQL); | |
59 | + Path schemaFile = Paths.get(installScripts.getDataDir(), SQL_DIR, SCHEMA_SQL); | |
59 | 60 | try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { |
60 | 61 | String sql = new String(Files.readAllBytes(schemaFile), Charset.forName("UTF-8")); |
61 | 62 | conn.createStatement().execute(sql); //NOSONAR, ignoring because method used to load initial thingsboard database schema | ... | ... |
... | ... | @@ -44,9 +44,6 @@ public class SqlDatabaseUpgradeService implements DatabaseUpgradeService { |
44 | 44 | |
45 | 45 | private static final String SCHEMA_UPDATE_SQL = "schema_update.sql"; |
46 | 46 | |
47 | - @Value("${install.data_dir}") | |
48 | - private String dataDir; | |
49 | - | |
50 | 47 | @Value("${spring.datasource.url}") |
51 | 48 | private String dbUrl; |
52 | 49 | |
... | ... | @@ -59,12 +56,15 @@ public class SqlDatabaseUpgradeService implements DatabaseUpgradeService { |
59 | 56 | @Autowired |
60 | 57 | private DashboardService dashboardService; |
61 | 58 | |
59 | + @Autowired | |
60 | + private InstallScripts installScripts; | |
61 | + | |
62 | 62 | @Override |
63 | 63 | public void upgradeDatabase(String fromVersion) throws Exception { |
64 | 64 | switch (fromVersion) { |
65 | 65 | case "1.3.0": |
66 | 66 | log.info("Updating schema ..."); |
67 | - Path schemaUpdateFile = Paths.get(this.dataDir, "upgrade", "1.3.1", SCHEMA_UPDATE_SQL); | |
67 | + Path schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "1.3.1", SCHEMA_UPDATE_SQL); | |
68 | 68 | try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { |
69 | 69 | String sql = new String(Files.readAllBytes(schemaUpdateFile), Charset.forName("UTF-8")); |
70 | 70 | conn.createStatement().execute(sql); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script |
... | ... | @@ -82,7 +82,7 @@ public class SqlDatabaseUpgradeService implements DatabaseUpgradeService { |
82 | 82 | log.info("Dashboards dumped."); |
83 | 83 | |
84 | 84 | log.info("Updating schema ..."); |
85 | - schemaUpdateFile = Paths.get(this.dataDir, "upgrade", "1.4.0", SCHEMA_UPDATE_SQL); | |
85 | + schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "1.4.0", SCHEMA_UPDATE_SQL); | |
86 | 86 | String sql = new String(Files.readAllBytes(schemaUpdateFile), Charset.forName("UTF-8")); |
87 | 87 | conn.createStatement().execute(sql); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script |
88 | 88 | log.info("Schema updated."); |
... | ... | @@ -100,7 +100,7 @@ public class SqlDatabaseUpgradeService implements DatabaseUpgradeService { |
100 | 100 | case "1.4.0": |
101 | 101 | try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { |
102 | 102 | log.info("Updating schema ..."); |
103 | - schemaUpdateFile = Paths.get(this.dataDir, "upgrade", "1.5.0", SCHEMA_UPDATE_SQL); | |
103 | + schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "1.5.0", SCHEMA_UPDATE_SQL); | |
104 | 104 | String sql = new String(Files.readAllBytes(schemaUpdateFile), Charset.forName("UTF-8")); |
105 | 105 | conn.createStatement().execute(sql); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script |
106 | 106 | log.info("Schema updated."); | ... | ... |
... | ... | @@ -23,10 +23,6 @@ public interface SystemDataLoaderService { |
23 | 23 | |
24 | 24 | void loadSystemWidgets() throws Exception; |
25 | 25 | |
26 | - void loadSystemPlugins() throws Exception; | |
27 | - | |
28 | - void loadSystemRules() throws Exception; | |
29 | - | |
30 | 26 | void loadDemoData() throws Exception; |
31 | 27 | |
32 | 28 | void deleteSystemWidgetBundle(String bundleAlias) throws Exception; | ... | ... |
application/src/main/java/org/thingsboard/server/service/rpc/DefaultDeviceRpcService.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.service.rpc; | |
17 | + | |
18 | +import com.fasterxml.jackson.databind.ObjectMapper; | |
19 | +import lombok.extern.slf4j.Slf4j; | |
20 | +import org.springframework.beans.factory.annotation.Autowired; | |
21 | +import org.springframework.http.HttpStatus; | |
22 | +import org.springframework.http.ResponseEntity; | |
23 | +import org.springframework.stereotype.Service; | |
24 | +import org.springframework.util.StringUtils; | |
25 | +import org.springframework.web.context.request.async.DeferredResult; | |
26 | +import org.thingsboard.server.actors.service.ActorService; | |
27 | +import org.thingsboard.server.common.data.audit.ActionType; | |
28 | +import org.thingsboard.server.common.data.id.DeviceId; | |
29 | +import org.thingsboard.server.common.data.id.EntityId; | |
30 | +import org.thingsboard.server.common.data.id.TenantId; | |
31 | +import org.thingsboard.server.common.data.id.UUIDBased; | |
32 | +import org.thingsboard.server.common.data.rpc.ToDeviceRpcRequestBody; | |
33 | +import org.thingsboard.server.common.msg.cluster.ServerAddress; | |
34 | +import org.thingsboard.server.common.msg.core.ToServerRpcResponseMsg; | |
35 | +import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest; | |
36 | +import org.thingsboard.server.controller.BaseController; | |
37 | +import org.thingsboard.server.dao.audit.AuditLogService; | |
38 | +import org.thingsboard.server.extensions.api.device.ToDeviceActorNotificationMsg; | |
39 | +import org.thingsboard.server.extensions.api.plugins.msg.FromDeviceRpcResponse; | |
40 | +import org.thingsboard.server.extensions.api.plugins.msg.RpcError; | |
41 | +import org.thingsboard.server.service.cluster.routing.ClusterRoutingService; | |
42 | +import org.thingsboard.server.service.cluster.rpc.ClusterRpcService; | |
43 | +import org.thingsboard.server.service.security.model.SecurityUser; | |
44 | + | |
45 | +import javax.annotation.PostConstruct; | |
46 | +import javax.annotation.PreDestroy; | |
47 | +import java.io.IOException; | |
48 | +import java.util.Optional; | |
49 | +import java.util.UUID; | |
50 | +import java.util.concurrent.ConcurrentHashMap; | |
51 | +import java.util.concurrent.ConcurrentMap; | |
52 | +import java.util.concurrent.Executors; | |
53 | +import java.util.concurrent.ScheduledExecutorService; | |
54 | +import java.util.concurrent.TimeUnit; | |
55 | +import java.util.function.BiConsumer; | |
56 | +import java.util.function.Consumer; | |
57 | + | |
58 | +/** | |
59 | + * Created by ashvayka on 27.03.18. | |
60 | + */ | |
61 | +@Service | |
62 | +@Slf4j | |
63 | +public class DefaultDeviceRpcService implements DeviceRpcService { | |
64 | + | |
65 | + @Autowired | |
66 | + private ClusterRoutingService routingService; | |
67 | + | |
68 | + @Autowired | |
69 | + private ClusterRpcService rpcService; | |
70 | + | |
71 | + @Autowired | |
72 | + private ActorService actorService; | |
73 | + | |
74 | + @Autowired | |
75 | + private AuditLogService auditLogService; | |
76 | + | |
77 | + private ScheduledExecutorService rpcCallBackExecutor; | |
78 | + | |
79 | + private final ConcurrentMap<UUID, Consumer<FromDeviceRpcResponse>> localRpcRequests = new ConcurrentHashMap<>(); | |
80 | + | |
81 | + | |
82 | + @PostConstruct | |
83 | + public void initExecutor() { | |
84 | + rpcCallBackExecutor = Executors.newSingleThreadScheduledExecutor(); | |
85 | + } | |
86 | + | |
87 | + @PreDestroy | |
88 | + public void shutdownExecutor() { | |
89 | + if (rpcCallBackExecutor != null) { | |
90 | + rpcCallBackExecutor.shutdownNow(); | |
91 | + } | |
92 | + } | |
93 | + | |
94 | + @Override | |
95 | + public void process(ToDeviceRpcRequest request, Consumer<FromDeviceRpcResponse> responseConsumer) { | |
96 | + log.trace("[{}] Processing local rpc call for device [{}]", request.getTenantId(), request.getDeviceId()); | |
97 | + sendRpcRequest(request); | |
98 | + UUID requestId = request.getId(); | |
99 | + localRpcRequests.put(requestId, responseConsumer); | |
100 | + long timeout = Math.max(0, request.getExpirationTime() - System.currentTimeMillis()); | |
101 | + log.error("[{}] processing the request: [{}]", this.hashCode(), requestId); | |
102 | + rpcCallBackExecutor.schedule(() -> { | |
103 | + log.error("[{}] timeout the request: [{}]", this.hashCode(), requestId); | |
104 | + Consumer<FromDeviceRpcResponse> consumer = localRpcRequests.remove(requestId); | |
105 | + if (consumer != null) { | |
106 | + consumer.accept(new FromDeviceRpcResponse(requestId, null, RpcError.TIMEOUT)); | |
107 | + } | |
108 | + }, timeout, TimeUnit.MILLISECONDS); | |
109 | + } | |
110 | + | |
111 | + @Override | |
112 | + public void process(ToDeviceRpcRequest request, ServerAddress originator) { | |
113 | +// if (pluginServerAddress.isPresent()) { | |
114 | +// systemContext.getRpcService().tell(pluginServerAddress.get(), responsePluginMsg); | |
115 | +// logger.debug("[{}] Rpc command response sent to remote plugin actor [{}]!", deviceId, requestMd.getMsg().getMsg().getId()); | |
116 | +// } else { | |
117 | +// context.parent().tell(responsePluginMsg, ActorRef.noSender()); | |
118 | +// logger.debug("[{}] Rpc command response sent to local plugin actor [{}]!", deviceId, requestMd.getMsg().getMsg().getId()); | |
119 | +// } | |
120 | + } | |
121 | + | |
122 | + @Override | |
123 | + public void process(FromDeviceRpcResponse response) { | |
124 | + log.error("[{}] response the request: [{}]", this.hashCode(), response.getId()); | |
125 | + //TODO: send to another server if needed. | |
126 | + UUID requestId = response.getId(); | |
127 | + Consumer<FromDeviceRpcResponse> consumer = localRpcRequests.remove(requestId); | |
128 | + if (consumer != null) { | |
129 | + consumer.accept(response); | |
130 | + } else { | |
131 | + log.trace("[{}] Unknown or stale rpc response received [{}]", requestId, response); | |
132 | + } | |
133 | + } | |
134 | + | |
135 | + @Override | |
136 | + public void sendRpcReplyToDevice(TenantId tenantId, DeviceId deviceId, int requestId, String body) { | |
137 | + ToServerRpcResponseActorMsg rpcMsg = new ToServerRpcResponseActorMsg(tenantId, deviceId, new ToServerRpcResponseMsg(requestId, body)); | |
138 | + forward(deviceId, rpcMsg, rpcService::tell); | |
139 | + } | |
140 | + | |
141 | + private void sendRpcRequest(ToDeviceRpcRequest msg) { | |
142 | + log.trace("[{}] Forwarding msg {} to device actor!", msg.getDeviceId(), msg); | |
143 | + ToDeviceRpcRequestActorMsg rpcMsg = new ToDeviceRpcRequestActorMsg(msg); | |
144 | + forward(msg.getDeviceId(), rpcMsg, rpcService::tell); | |
145 | + } | |
146 | + | |
147 | + private <T extends ToDeviceActorNotificationMsg> void forward(DeviceId deviceId, T msg, BiConsumer<ServerAddress, T> rpcFunction) { | |
148 | + Optional<ServerAddress> instance = routingService.resolveById(deviceId); | |
149 | + if (instance.isPresent()) { | |
150 | + log.trace("[{}] Forwarding msg {} to remote device actor!", msg.getTenantId(), msg); | |
151 | + rpcFunction.accept(instance.get(), msg); | |
152 | + } else { | |
153 | + log.trace("[{}] Forwarding msg {} to local device actor!", msg.getTenantId(), msg); | |
154 | + actorService.onMsg(msg); | |
155 | + } | |
156 | + } | |
157 | +} | ... | ... |
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.service.rpc; | |
17 | + | |
18 | +import org.thingsboard.server.common.data.id.DeviceId; | |
19 | +import org.thingsboard.server.common.data.id.TenantId; | |
20 | +import org.thingsboard.server.common.msg.cluster.ServerAddress; | |
21 | +import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest; | |
22 | +import org.thingsboard.server.extensions.api.plugins.msg.FromDeviceRpcResponse; | |
23 | + | |
24 | +import java.util.function.Consumer; | |
25 | + | |
26 | +/** | |
27 | + * Created by ashvayka on 16.04.18. | |
28 | + */ | |
29 | +public interface DeviceRpcService { | |
30 | + | |
31 | + void process(ToDeviceRpcRequest request, Consumer<FromDeviceRpcResponse> responseConsumer); | |
32 | + | |
33 | + void process(ToDeviceRpcRequest request, ServerAddress originator); | |
34 | + | |
35 | + void process(FromDeviceRpcResponse response); | |
36 | + | |
37 | + void sendRpcReplyToDevice(TenantId tenantId, DeviceId deviceId, int requestId, String body); | |
38 | + | |
39 | +} | ... | ... |
application/src/main/java/org/thingsboard/server/service/rpc/LocalRequestMetaData.java
renamed from
extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/rpc/LocalRequestMetaData.java
... | ... | @@ -13,18 +13,20 @@ |
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.plugin.rpc; | |
16 | +package org.thingsboard.server.service.rpc; | |
17 | 17 | |
18 | 18 | import lombok.Data; |
19 | 19 | import org.springframework.http.ResponseEntity; |
20 | 20 | import org.springframework.web.context.request.async.DeferredResult; |
21 | -import org.thingsboard.server.extensions.api.plugins.msg.ToDeviceRpcRequest; | |
21 | +import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest; | |
22 | +import org.thingsboard.server.service.security.model.SecurityUser; | |
22 | 23 | |
23 | 24 | /** |
24 | - * @author Andrew Shvayka | |
25 | + * Created by ashvayka on 16.04.18. | |
25 | 26 | */ |
26 | 27 | @Data |
27 | 28 | public class LocalRequestMetaData { |
28 | 29 | private final ToDeviceRpcRequest request; |
30 | + private final SecurityUser user; | |
29 | 31 | private final DeferredResult<ResponseEntity> responseWriter; |
30 | 32 | } | ... | ... |
application/src/main/java/org/thingsboard/server/service/rpc/ToDeviceRpcRequestActorMsg.java
renamed from
extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/msg/ToDeviceRpcRequestPluginMsg.java
... | ... | @@ -13,36 +13,33 @@ |
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.api.plugins.msg; | |
16 | +package org.thingsboard.server.service.rpc; | |
17 | 17 | |
18 | 18 | import lombok.Getter; |
19 | 19 | import lombok.RequiredArgsConstructor; |
20 | 20 | import lombok.ToString; |
21 | 21 | import org.thingsboard.server.common.data.id.DeviceId; |
22 | -import org.thingsboard.server.common.data.id.PluginId; | |
23 | 22 | import org.thingsboard.server.common.data.id.TenantId; |
23 | +import org.thingsboard.server.common.msg.MsgType; | |
24 | 24 | import org.thingsboard.server.common.msg.cluster.ServerAddress; |
25 | +import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest; | |
25 | 26 | import org.thingsboard.server.extensions.api.device.ToDeviceActorNotificationMsg; |
26 | 27 | |
27 | 28 | import java.util.Optional; |
28 | 29 | |
29 | 30 | /** |
30 | - * @author Andrew Shvayka | |
31 | + * Created by ashvayka on 16.04.18. | |
31 | 32 | */ |
32 | 33 | @ToString |
33 | 34 | @RequiredArgsConstructor |
34 | -public class ToDeviceRpcRequestPluginMsg implements ToDeviceActorNotificationMsg { | |
35 | +public class ToDeviceRpcRequestActorMsg implements ToDeviceActorNotificationMsg { | |
35 | 36 | |
36 | 37 | private final ServerAddress serverAddress; |
37 | 38 | @Getter |
38 | - private final PluginId pluginId; | |
39 | - @Getter | |
40 | - private final TenantId pluginTenantId; | |
41 | - @Getter | |
42 | 39 | private final ToDeviceRpcRequest msg; |
43 | 40 | |
44 | - public ToDeviceRpcRequestPluginMsg(PluginId pluginId, TenantId pluginTenantId, ToDeviceRpcRequest msg) { | |
45 | - this(null, pluginId, pluginTenantId, msg); | |
41 | + public ToDeviceRpcRequestActorMsg(ToDeviceRpcRequest msg) { | |
42 | + this(null, msg); | |
46 | 43 | } |
47 | 44 | |
48 | 45 | public Optional<ServerAddress> getServerAddress() { |
... | ... | @@ -58,5 +55,9 @@ public class ToDeviceRpcRequestPluginMsg implements ToDeviceActorNotificationMsg |
58 | 55 | public TenantId getTenantId() { |
59 | 56 | return msg.getTenantId(); |
60 | 57 | } |
61 | -} | |
62 | 58 | |
59 | + @Override | |
60 | + public MsgType getMsgType() { | |
61 | + return MsgType.DEVICE_RPC_REQUEST_TO_DEVICE_ACTOR_MSG; | |
62 | + } | |
63 | +} | ... | ... |
application/src/main/java/org/thingsboard/server/service/rpc/ToServerRpcResponseActorMsg.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.service.rpc; | |
17 | + | |
18 | +import lombok.Getter; | |
19 | +import lombok.RequiredArgsConstructor; | |
20 | +import lombok.ToString; | |
21 | +import org.thingsboard.server.common.data.id.DeviceId; | |
22 | +import org.thingsboard.server.common.data.id.TenantId; | |
23 | +import org.thingsboard.server.common.msg.MsgType; | |
24 | +import org.thingsboard.server.common.msg.cluster.ServerAddress; | |
25 | +import org.thingsboard.server.common.msg.core.ToServerRpcResponseMsg; | |
26 | +import org.thingsboard.server.extensions.api.device.ToDeviceActorNotificationMsg; | |
27 | + | |
28 | +import java.util.Optional; | |
29 | + | |
30 | +/** | |
31 | + * Created by ashvayka on 16.04.18. | |
32 | + */ | |
33 | +@ToString | |
34 | +@RequiredArgsConstructor | |
35 | +public class ToServerRpcResponseActorMsg implements ToDeviceActorNotificationMsg { | |
36 | + | |
37 | + private final ServerAddress serverAddress; | |
38 | + | |
39 | + @Getter | |
40 | + private final TenantId tenantId; | |
41 | + | |
42 | + @Getter | |
43 | + private final DeviceId deviceId; | |
44 | + | |
45 | + @Getter | |
46 | + private final ToServerRpcResponseMsg msg; | |
47 | + | |
48 | + public ToServerRpcResponseActorMsg(TenantId tenantId, DeviceId deviceId, ToServerRpcResponseMsg msg) { | |
49 | + this(null, tenantId, deviceId, msg); | |
50 | + } | |
51 | + | |
52 | + public Optional<ServerAddress> getServerAddress() { | |
53 | + return Optional.ofNullable(serverAddress); | |
54 | + } | |
55 | + | |
56 | + @Override | |
57 | + public MsgType getMsgType() { | |
58 | + return MsgType.SERVER_RPC_RESPONSE_TO_DEVICE_ACTOR_MSG; | |
59 | + } | |
60 | +} | ... | ... |
... | ... | @@ -118,7 +118,7 @@ public class NashornJsEngine implements org.thingsboard.rule.engine.api.ScriptEn |
118 | 118 | String newData = data != null ? data : msg.getData(); |
119 | 119 | TbMsgMetaData newMetadata = metadata != null ? new TbMsgMetaData(metadata) : msg.getMetaData(); |
120 | 120 | String newMessageType = !StringUtils.isEmpty(messageType) ? messageType : msg.getType(); |
121 | - return new TbMsg(msg.getId(), newMessageType, msg.getOriginator(), newMetadata, newData); | |
121 | + return new TbMsg(msg.getId(), newMessageType, msg.getOriginator(), newMetadata, newData, msg.getRuleChainId(), msg.getRuleNodeId(), msg.getClusterPartition()); | |
122 | 122 | } catch (Throwable th) { |
123 | 123 | th.printStackTrace(); |
124 | 124 | throw new RuntimeException("Failed to unbind message data from javascript result", th); | ... | ... |
... | ... | @@ -25,6 +25,7 @@ import org.springframework.http.ResponseEntity; |
25 | 25 | import org.springframework.stereotype.Component; |
26 | 26 | import org.springframework.web.context.request.async.DeferredResult; |
27 | 27 | import org.thingsboard.server.actors.plugin.ValidationResult; |
28 | +import org.thingsboard.server.common.data.BaseData; | |
28 | 29 | import org.thingsboard.server.common.data.Customer; |
29 | 30 | import org.thingsboard.server.common.data.Device; |
30 | 31 | import org.thingsboard.server.common.data.Tenant; |
... | ... | @@ -35,8 +36,10 @@ import org.thingsboard.server.common.data.id.DeviceId; |
35 | 36 | import org.thingsboard.server.common.data.id.EntityId; |
36 | 37 | import org.thingsboard.server.common.data.id.EntityIdFactory; |
37 | 38 | import org.thingsboard.server.common.data.id.RuleChainId; |
39 | +import org.thingsboard.server.common.data.id.RuleNodeId; | |
38 | 40 | import org.thingsboard.server.common.data.id.TenantId; |
39 | 41 | import org.thingsboard.server.common.data.rule.RuleChain; |
42 | +import org.thingsboard.server.common.data.rule.RuleNode; | |
40 | 43 | import org.thingsboard.server.controller.HttpValidationCallback; |
41 | 44 | import org.thingsboard.server.dao.alarm.AlarmService; |
42 | 45 | import org.thingsboard.server.dao.asset.AssetService; |
... | ... | @@ -140,7 +143,7 @@ public class AccessValidator { |
140 | 143 | return response; |
141 | 144 | } |
142 | 145 | |
143 | - public <T> void validate(SecurityUser currentUser, EntityId entityId, FutureCallback<ValidationResult> callback) { | |
146 | + public void validate(SecurityUser currentUser, EntityId entityId, FutureCallback<ValidationResult> callback) { | |
144 | 147 | switch (entityId.getEntityType()) { |
145 | 148 | case DEVICE: |
146 | 149 | validateDevice(currentUser, entityId, callback); |
... | ... | @@ -177,14 +180,14 @@ public class AccessValidator { |
177 | 180 | } else if (currentUser.isCustomerUser() && !device.getCustomerId().equals(currentUser.getCustomerId())) { |
178 | 181 | return ValidationResult.accessDenied("Device doesn't belong to the current Customer!"); |
179 | 182 | } else { |
180 | - return ValidationResult.ok(); | |
183 | + return ValidationResult.ok(device); | |
181 | 184 | } |
182 | 185 | } |
183 | 186 | }), executor); |
184 | 187 | } |
185 | 188 | } |
186 | 189 | |
187 | - private <T> void validateAsset(final SecurityUser currentUser, EntityId entityId, FutureCallback<ValidationResult> callback) { | |
190 | + private void validateAsset(final SecurityUser currentUser, EntityId entityId, FutureCallback<ValidationResult> callback) { | |
188 | 191 | if (currentUser.isSystemAdmin()) { |
189 | 192 | callback.onSuccess(ValidationResult.accessDenied(SYSTEM_ADMINISTRATOR_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION)); |
190 | 193 | } else { |
... | ... | @@ -198,15 +201,14 @@ public class AccessValidator { |
198 | 201 | } else if (currentUser.isCustomerUser() && !asset.getCustomerId().equals(currentUser.getCustomerId())) { |
199 | 202 | return ValidationResult.accessDenied("Asset doesn't belong to the current Customer!"); |
200 | 203 | } else { |
201 | - return ValidationResult.ok(); | |
204 | + return ValidationResult.ok(asset); | |
202 | 205 | } |
203 | 206 | } |
204 | 207 | }), executor); |
205 | 208 | } |
206 | 209 | } |
207 | 210 | |
208 | - | |
209 | - private <T> void validateRuleChain(final SecurityUser currentUser, EntityId entityId, FutureCallback<ValidationResult> callback) { | |
211 | + private void validateRuleChain(final SecurityUser currentUser, EntityId entityId, FutureCallback<ValidationResult> callback) { | |
210 | 212 | if (currentUser.isCustomerUser()) { |
211 | 213 | callback.onSuccess(ValidationResult.accessDenied(CUSTOMER_USER_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION)); |
212 | 214 | } else { |
... | ... | @@ -220,14 +222,40 @@ public class AccessValidator { |
220 | 222 | } else if (currentUser.isSystemAdmin() && !ruleChain.getTenantId().isNullUid()) { |
221 | 223 | return ValidationResult.accessDenied("Rule chain is not in system scope!"); |
222 | 224 | } else { |
223 | - return ValidationResult.ok(); | |
225 | + return ValidationResult.ok(ruleChain); | |
226 | + } | |
227 | + } | |
228 | + }), executor); | |
229 | + } | |
230 | + } | |
231 | + | |
232 | + private void validateRule(final SecurityUser currentUser, EntityId entityId, FutureCallback<ValidationResult> callback) { | |
233 | + if (currentUser.isCustomerUser()) { | |
234 | + callback.onSuccess(ValidationResult.accessDenied(CUSTOMER_USER_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION)); | |
235 | + } else { | |
236 | + ListenableFuture<RuleNode> ruleNodeFuture = ruleChainService.findRuleNodeByIdAsync(new RuleNodeId(entityId.getId())); | |
237 | + Futures.addCallback(ruleNodeFuture, getCallback(callback, ruleNodeTmp -> { | |
238 | + RuleNode ruleNode = ruleNodeTmp; | |
239 | + if (ruleNode == null) { | |
240 | + return ValidationResult.entityNotFound("Rule node with requested id wasn't found!"); | |
241 | + } else if (ruleNode.getRuleChainId() == null) { | |
242 | + return ValidationResult.entityNotFound("Rule chain with requested node id wasn't found!"); | |
243 | + } else { | |
244 | + //TODO: make async | |
245 | + RuleChain ruleChain = ruleChainService.findRuleChainById(ruleNode.getRuleChainId()); | |
246 | + if (currentUser.isTenantAdmin() && !ruleChain.getTenantId().equals(currentUser.getTenantId())) { | |
247 | + return ValidationResult.accessDenied("Rule chain doesn't belong to the current Tenant!"); | |
248 | + } else if (currentUser.isSystemAdmin() && !ruleChain.getTenantId().isNullUid()) { | |
249 | + return ValidationResult.accessDenied("Rule chain is not in system scope!"); | |
250 | + } else { | |
251 | + return ValidationResult.ok(ruleNode); | |
224 | 252 | } |
225 | 253 | } |
226 | 254 | }), executor); |
227 | 255 | } |
228 | 256 | } |
229 | 257 | |
230 | - private <T> void validateCustomer(final SecurityUser currentUser, EntityId entityId, FutureCallback<ValidationResult> callback) { | |
258 | + private void validateCustomer(final SecurityUser currentUser, EntityId entityId, FutureCallback<ValidationResult> callback) { | |
231 | 259 | if (currentUser.isSystemAdmin()) { |
232 | 260 | callback.onSuccess(ValidationResult.accessDenied(SYSTEM_ADMINISTRATOR_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION)); |
233 | 261 | } else { |
... | ... | @@ -241,18 +269,18 @@ public class AccessValidator { |
241 | 269 | } else if (currentUser.isCustomerUser() && !customer.getId().equals(currentUser.getCustomerId())) { |
242 | 270 | return ValidationResult.accessDenied("Customer doesn't relate to the currently authorized customer user!"); |
243 | 271 | } else { |
244 | - return ValidationResult.ok(); | |
272 | + return ValidationResult.ok(customer); | |
245 | 273 | } |
246 | 274 | } |
247 | 275 | }), executor); |
248 | 276 | } |
249 | 277 | } |
250 | 278 | |
251 | - private <T> void validateTenant(final SecurityUser currentUser, EntityId entityId, FutureCallback<ValidationResult> callback) { | |
279 | + private void validateTenant(final SecurityUser currentUser, EntityId entityId, FutureCallback<ValidationResult> callback) { | |
252 | 280 | if (currentUser.isCustomerUser()) { |
253 | 281 | callback.onSuccess(ValidationResult.accessDenied(CUSTOMER_USER_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION)); |
254 | 282 | } else if (currentUser.isSystemAdmin()) { |
255 | - callback.onSuccess(ValidationResult.ok()); | |
283 | + callback.onSuccess(ValidationResult.ok(null)); | |
256 | 284 | } else { |
257 | 285 | ListenableFuture<Tenant> tenantFuture = tenantService.findTenantByIdAsync(new TenantId(entityId.getId())); |
258 | 286 | Futures.addCallback(tenantFuture, getCallback(callback, tenant -> { |
... | ... | @@ -261,13 +289,13 @@ public class AccessValidator { |
261 | 289 | } else if (!tenant.getId().equals(currentUser.getTenantId())) { |
262 | 290 | return ValidationResult.accessDenied("Tenant doesn't relate to the currently authorized user!"); |
263 | 291 | } else { |
264 | - return ValidationResult.ok(); | |
292 | + return ValidationResult.ok(tenant); | |
265 | 293 | } |
266 | 294 | }), executor); |
267 | 295 | } |
268 | 296 | } |
269 | 297 | |
270 | - private <T> FutureCallback<T> getCallback(FutureCallback<ValidationResult> callback, Function<T, ValidationResult> transformer) { | |
298 | + private <T, V> FutureCallback<T> getCallback(FutureCallback<ValidationResult> callback, Function<T, ValidationResult<V>> transformer) { | |
271 | 299 | return new FutureCallback<T>() { |
272 | 300 | @Override |
273 | 301 | public void onSuccess(@Nullable T result) { | ... | ... |
application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.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.service.state; | |
17 | + | |
18 | +import com.datastax.driver.core.utils.UUIDs; | |
19 | +import com.fasterxml.jackson.databind.ObjectMapper; | |
20 | +import com.google.common.base.Function; | |
21 | +import com.google.common.util.concurrent.FutureCallback; | |
22 | +import com.google.common.util.concurrent.Futures; | |
23 | +import com.google.common.util.concurrent.ListenableFuture; | |
24 | +import com.google.common.util.concurrent.ListeningScheduledExecutorService; | |
25 | +import com.google.common.util.concurrent.MoreExecutors; | |
26 | +import lombok.Getter; | |
27 | +import lombok.extern.slf4j.Slf4j; | |
28 | +import org.springframework.beans.factory.annotation.Autowired; | |
29 | +import org.springframework.beans.factory.annotation.Value; | |
30 | +import org.springframework.stereotype.Service; | |
31 | +import org.thingsboard.server.actors.service.ActorService; | |
32 | +import org.thingsboard.server.common.data.DataConstants; | |
33 | +import org.thingsboard.server.common.data.Device; | |
34 | +import org.thingsboard.server.common.data.Tenant; | |
35 | +import org.thingsboard.server.common.data.id.DeviceId; | |
36 | +import org.thingsboard.server.common.data.id.TenantId; | |
37 | +import org.thingsboard.server.common.data.kv.AttributeKvEntry; | |
38 | +import org.thingsboard.server.common.data.page.TextPageLink; | |
39 | +import org.thingsboard.server.common.msg.TbMsg; | |
40 | +import org.thingsboard.server.common.msg.TbMsgDataType; | |
41 | +import org.thingsboard.server.common.msg.TbMsgMetaData; | |
42 | +import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg; | |
43 | +import org.thingsboard.server.dao.attributes.AttributesService; | |
44 | +import org.thingsboard.server.dao.device.DeviceService; | |
45 | +import org.thingsboard.server.dao.tenant.TenantService; | |
46 | +import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService; | |
47 | + | |
48 | +import javax.annotation.Nullable; | |
49 | +import javax.annotation.PostConstruct; | |
50 | +import javax.annotation.PreDestroy; | |
51 | +import java.util.ArrayList; | |
52 | +import java.util.Arrays; | |
53 | +import java.util.HashSet; | |
54 | +import java.util.List; | |
55 | +import java.util.Optional; | |
56 | +import java.util.Set; | |
57 | +import java.util.concurrent.ConcurrentHashMap; | |
58 | +import java.util.concurrent.ConcurrentMap; | |
59 | +import java.util.concurrent.ExecutionException; | |
60 | +import java.util.concurrent.Executors; | |
61 | +import java.util.concurrent.TimeUnit; | |
62 | + | |
63 | +import static org.thingsboard.server.common.data.DataConstants.ACTIVITY_EVENT; | |
64 | +import static org.thingsboard.server.common.data.DataConstants.CONNECT_EVENT; | |
65 | +import static org.thingsboard.server.common.data.DataConstants.DISCONNECT_EVENT; | |
66 | +import static org.thingsboard.server.common.data.DataConstants.INACTIVITY_EVENT; | |
67 | + | |
68 | +/** | |
69 | + * Created by ashvayka on 01.05.18. | |
70 | + */ | |
71 | +@Service | |
72 | +@Slf4j | |
73 | +//TODO: refactor to use page links as cursor and not fetch all | |
74 | +public class DefaultDeviceStateService implements DeviceStateService { | |
75 | + | |
76 | + private static final ObjectMapper json = new ObjectMapper(); | |
77 | + public static final String ACTIVITY_STATE = "active"; | |
78 | + public static final String LAST_CONNECT_TIME = "lastConnectTime"; | |
79 | + public static final String LAST_DISCONNECT_TIME = "lastDisconnectTime"; | |
80 | + public static final String LAST_ACTIVITY_TIME = "lastActivityTime"; | |
81 | + public static final String INACTIVITY_ALARM_TIME = "inactivityAlarmTime"; | |
82 | + public static final String INACTIVITY_TIMEOUT = "inactivityTimeout"; | |
83 | + | |
84 | + public static final List<String> PERSISTENT_ATTRIBUTES = Arrays.asList(ACTIVITY_STATE, LAST_CONNECT_TIME, LAST_DISCONNECT_TIME, LAST_ACTIVITY_TIME, INACTIVITY_ALARM_TIME, INACTIVITY_TIMEOUT); | |
85 | + | |
86 | + @Autowired | |
87 | + private TenantService tenantService; | |
88 | + | |
89 | + @Autowired | |
90 | + private DeviceService deviceService; | |
91 | + | |
92 | + @Autowired | |
93 | + private AttributesService attributesService; | |
94 | + | |
95 | + @Autowired | |
96 | + private ActorService actorService; | |
97 | + | |
98 | + @Autowired | |
99 | + private TelemetrySubscriptionService tsSubService; | |
100 | + | |
101 | + @Value("${state.defaultInactivityTimeoutInSec}") | |
102 | + @Getter | |
103 | + private long defaultInactivityTimeoutInSec; | |
104 | + | |
105 | + @Value("${state.defaultStateCheckIntervalInSec}") | |
106 | + @Getter | |
107 | + private long defaultStateCheckIntervalInSec; | |
108 | + | |
109 | +// TODO in v2.1 | |
110 | +// @Value("${state.defaultStatePersistenceIntervalInSec}") | |
111 | +// @Getter | |
112 | +// private long defaultStatePersistenceIntervalInSec; | |
113 | +// | |
114 | +// @Value("${state.defaultStatePersistencePack}") | |
115 | +// @Getter | |
116 | +// private long defaultStatePersistencePack; | |
117 | + | |
118 | + private ListeningScheduledExecutorService queueExecutor; | |
119 | + | |
120 | + private ConcurrentMap<TenantId, Set<DeviceId>> tenantDevices = new ConcurrentHashMap<>(); | |
121 | + private ConcurrentMap<DeviceId, DeviceStateData> deviceStates = new ConcurrentHashMap<>(); | |
122 | + | |
123 | + @PostConstruct | |
124 | + public void init() { | |
125 | + // Should be always single threaded due to absence of locks. | |
126 | + queueExecutor = MoreExecutors.listeningDecorator(Executors.newSingleThreadScheduledExecutor()); | |
127 | + queueExecutor.submit(this::initStateFromDB); | |
128 | + queueExecutor.scheduleAtFixedRate(this::updateState, defaultStateCheckIntervalInSec, defaultStateCheckIntervalInSec, TimeUnit.SECONDS); | |
129 | + //TODO: schedule persistence in v2.1; | |
130 | + } | |
131 | + | |
132 | + @PreDestroy | |
133 | + public void stop() { | |
134 | + if (queueExecutor != null) { | |
135 | + queueExecutor.shutdownNow(); | |
136 | + } | |
137 | + } | |
138 | + | |
139 | + @Override | |
140 | + public void onDeviceAdded(Device device) { | |
141 | + queueExecutor.submit(() -> onDeviceAddedSync(device)); | |
142 | + } | |
143 | + | |
144 | + @Override | |
145 | + public void onDeviceUpdated(Device device) { | |
146 | + queueExecutor.submit(() -> onDeviceUpdatedSync(device)); | |
147 | + } | |
148 | + | |
149 | + @Override | |
150 | + public void onDeviceConnect(DeviceId deviceId) { | |
151 | + queueExecutor.submit(() -> onDeviceConnectSync(deviceId)); | |
152 | + } | |
153 | + | |
154 | + @Override | |
155 | + public void onDeviceActivity(DeviceId deviceId) { | |
156 | + queueExecutor.submit(() -> onDeviceActivitySync(deviceId)); | |
157 | + } | |
158 | + | |
159 | + @Override | |
160 | + public void onDeviceDisconnect(DeviceId deviceId) { | |
161 | + queueExecutor.submit(() -> onDeviceDisconnectSync(deviceId)); | |
162 | + } | |
163 | + | |
164 | + @Override | |
165 | + public void onDeviceDeleted(Device device) { | |
166 | + queueExecutor.submit(() -> onDeviceDeleted(device.getTenantId(), device.getId())); | |
167 | + } | |
168 | + | |
169 | + @Override | |
170 | + public void onDeviceInactivityTimeoutUpdate(DeviceId deviceId, long inactivityTimeout) { | |
171 | + queueExecutor.submit(() -> onInactivityTimeoutUpdate(deviceId, inactivityTimeout)); | |
172 | + } | |
173 | + | |
174 | + @Override | |
175 | + public Optional<DeviceState> getDeviceState(DeviceId deviceId) { | |
176 | + DeviceStateData state = deviceStates.get(deviceId); | |
177 | + if (state != null) { | |
178 | + return Optional.of(state.getState()); | |
179 | + } else { | |
180 | + return Optional.empty(); | |
181 | + } | |
182 | + } | |
183 | + | |
184 | + private void initStateFromDB() { | |
185 | + List<Tenant> tenants = tenantService.findTenants(new TextPageLink(Integer.MAX_VALUE)).getData(); | |
186 | + for (Tenant tenant : tenants) { | |
187 | + List<ListenableFuture<DeviceStateData>> fetchFutures = new ArrayList<>(); | |
188 | + List<Device> devices = deviceService.findDevicesByTenantId(tenant.getId(), new TextPageLink(Integer.MAX_VALUE)).getData(); | |
189 | + for (Device device : devices) { | |
190 | + fetchFutures.add(fetchDeviceState(device)); | |
191 | + } | |
192 | + try { | |
193 | + Futures.successfulAsList(fetchFutures).get().forEach(this::addDeviceUsingState); | |
194 | + } catch (InterruptedException | ExecutionException e) { | |
195 | + log.warn("Failed to init device state service from DB", e); | |
196 | + } | |
197 | + } | |
198 | + } | |
199 | + | |
200 | + private void addDeviceUsingState(DeviceStateData state) { | |
201 | + tenantDevices.computeIfAbsent(state.getTenantId(), id -> ConcurrentHashMap.newKeySet()).add(state.getDeviceId()); | |
202 | + deviceStates.put(state.getDeviceId(), state); | |
203 | + } | |
204 | + | |
205 | + private void updateState() { | |
206 | + long ts = System.currentTimeMillis(); | |
207 | + Set<DeviceId> deviceIds = new HashSet<>(deviceStates.keySet()); | |
208 | + for (DeviceId deviceId : deviceIds) { | |
209 | + DeviceStateData stateData = deviceStates.get(deviceId); | |
210 | + DeviceState state = stateData.getState(); | |
211 | + state.setActive(ts < state.getLastActivityTime() + state.getInactivityTimeout()); | |
212 | + if (!state.isActive() && state.getLastInactivityAlarmTime() < state.getLastActivityTime()) { | |
213 | + state.setLastInactivityAlarmTime(ts); | |
214 | + pushRuleEngineMessage(stateData, INACTIVITY_EVENT); | |
215 | + saveAttribute(deviceId, INACTIVITY_ALARM_TIME, ts); | |
216 | + saveAttribute(deviceId, ACTIVITY_STATE, state.isActive()); | |
217 | + } | |
218 | + } | |
219 | + } | |
220 | + | |
221 | + private void onDeviceConnectSync(DeviceId deviceId) { | |
222 | + DeviceStateData stateData = deviceStates.get(deviceId); | |
223 | + if (stateData != null) { | |
224 | + long ts = System.currentTimeMillis(); | |
225 | + stateData.getState().setLastConnectTime(ts); | |
226 | + pushRuleEngineMessage(stateData, CONNECT_EVENT); | |
227 | + saveAttribute(deviceId, LAST_CONNECT_TIME, ts); | |
228 | + } | |
229 | + } | |
230 | + | |
231 | + private void onDeviceDisconnectSync(DeviceId deviceId) { | |
232 | + DeviceStateData stateData = deviceStates.get(deviceId); | |
233 | + if (stateData != null) { | |
234 | + long ts = System.currentTimeMillis(); | |
235 | + stateData.getState().setLastDisconnectTime(ts); | |
236 | + pushRuleEngineMessage(stateData, DISCONNECT_EVENT); | |
237 | + saveAttribute(deviceId, LAST_DISCONNECT_TIME, ts); | |
238 | + } | |
239 | + } | |
240 | + | |
241 | + private void onDeviceActivitySync(DeviceId deviceId) { | |
242 | + DeviceStateData stateData = deviceStates.get(deviceId); | |
243 | + if (stateData != null) { | |
244 | + DeviceState state = stateData.getState(); | |
245 | + long ts = System.currentTimeMillis(); | |
246 | + state.setActive(true); | |
247 | + stateData.getState().setLastActivityTime(ts); | |
248 | + pushRuleEngineMessage(stateData, ACTIVITY_EVENT); | |
249 | + saveAttribute(deviceId, LAST_ACTIVITY_TIME, ts); | |
250 | + saveAttribute(deviceId, ACTIVITY_STATE, state.isActive()); | |
251 | + } | |
252 | + } | |
253 | + | |
254 | + private void onInactivityTimeoutUpdate(DeviceId deviceId, long inactivityTimeout) { | |
255 | + if (inactivityTimeout == 0L) { | |
256 | + return; | |
257 | + } | |
258 | + DeviceStateData stateData = deviceStates.get(deviceId); | |
259 | + if (stateData != null) { | |
260 | + long ts = System.currentTimeMillis(); | |
261 | + DeviceState state = stateData.getState(); | |
262 | + state.setInactivityTimeout(inactivityTimeout); | |
263 | + boolean oldActive = state.isActive(); | |
264 | + state.setActive(ts < state.getLastActivityTime() + state.getInactivityTimeout()); | |
265 | + if (!oldActive && state.isActive()) { | |
266 | + saveAttribute(deviceId, ACTIVITY_STATE, state.isActive()); | |
267 | + } | |
268 | + } | |
269 | + } | |
270 | + | |
271 | + private void onDeviceAddedSync(Device device) { | |
272 | + Futures.addCallback(fetchDeviceState(device), new FutureCallback<DeviceStateData>() { | |
273 | + @Override | |
274 | + public void onSuccess(@Nullable DeviceStateData state) { | |
275 | + addDeviceUsingState(state); | |
276 | + } | |
277 | + | |
278 | + @Override | |
279 | + public void onFailure(Throwable t) { | |
280 | + log.warn("Failed to register device to the state service", t); | |
281 | + } | |
282 | + }); | |
283 | + } | |
284 | + | |
285 | + private void onDeviceUpdatedSync(Device device) { | |
286 | + DeviceStateData stateData = deviceStates.get(device.getId()); | |
287 | + if (stateData != null) { | |
288 | + TbMsgMetaData md = new TbMsgMetaData(); | |
289 | + md.putValue("deviceName", device.getName()); | |
290 | + md.putValue("deviceType", device.getType()); | |
291 | + stateData.setMetaData(md); | |
292 | + } | |
293 | + } | |
294 | + | |
295 | + private void onDeviceDeleted(TenantId tenantId, DeviceId deviceId) { | |
296 | + deviceStates.remove(deviceId); | |
297 | + Set<DeviceId> deviceIds = tenantDevices.get(tenantId); | |
298 | + if (deviceIds != null) { | |
299 | + deviceIds.remove(deviceId); | |
300 | + if (deviceIds.isEmpty()) { | |
301 | + tenantDevices.remove(tenantId); | |
302 | + } | |
303 | + } | |
304 | + } | |
305 | + | |
306 | + private ListenableFuture<DeviceStateData> fetchDeviceState(Device device) { | |
307 | + ListenableFuture<List<AttributeKvEntry>> attributes = attributesService.find(device.getId(), DataConstants.SERVER_SCOPE, PERSISTENT_ATTRIBUTES); | |
308 | + return Futures.transform(attributes, new Function<List<AttributeKvEntry>, DeviceStateData>() { | |
309 | + @Nullable | |
310 | + @Override | |
311 | + public DeviceStateData apply(@Nullable List<AttributeKvEntry> attributes) { | |
312 | + long lastActivityTime = getAttributeValue(attributes, LAST_ACTIVITY_TIME, 0L); | |
313 | + long inactivityAlarmTime = getAttributeValue(attributes, INACTIVITY_ALARM_TIME, 0L); | |
314 | + long inactivityTimeout = getAttributeValue(attributes, INACTIVITY_TIMEOUT, TimeUnit.SECONDS.toMillis(defaultInactivityTimeoutInSec)); | |
315 | + boolean active = System.currentTimeMillis() < lastActivityTime + inactivityTimeout; | |
316 | + DeviceState deviceState = DeviceState.builder() | |
317 | + .active(active) | |
318 | + .lastConnectTime(getAttributeValue(attributes, LAST_CONNECT_TIME, 0L)) | |
319 | + .lastDisconnectTime(getAttributeValue(attributes, LAST_DISCONNECT_TIME, 0L)) | |
320 | + .lastActivityTime(lastActivityTime) | |
321 | + .lastInactivityAlarmTime(inactivityAlarmTime) | |
322 | + .inactivityTimeout(inactivityTimeout) | |
323 | + .build(); | |
324 | + TbMsgMetaData md = new TbMsgMetaData(); | |
325 | + md.putValue("deviceName", device.getName()); | |
326 | + md.putValue("deviceType", device.getType()); | |
327 | + return DeviceStateData.builder() | |
328 | + .tenantId(device.getTenantId()) | |
329 | + .deviceId(device.getId()) | |
330 | + .metaData(md) | |
331 | + .state(deviceState).build(); | |
332 | + } | |
333 | + }); | |
334 | + } | |
335 | + | |
336 | + private long getLastPersistTime(List<AttributeKvEntry> attributes) { | |
337 | + return attributes.stream().map(AttributeKvEntry::getLastUpdateTs).max(Long::compare).orElse(0L); | |
338 | + } | |
339 | + | |
340 | + private long getAttributeValue(List<AttributeKvEntry> attributes, String attributeName, long defaultValue) { | |
341 | + for (AttributeKvEntry attribute : attributes) { | |
342 | + if (attribute.getKey().equals(attributeName)) { | |
343 | + return attribute.getLongValue().orElse(defaultValue); | |
344 | + } | |
345 | + } | |
346 | + return defaultValue; | |
347 | + } | |
348 | + | |
349 | + private void pushRuleEngineMessage(DeviceStateData stateData, String msgType) { | |
350 | + DeviceState state = stateData.getState(); | |
351 | + try { | |
352 | + TbMsg tbMsg = new TbMsg(UUIDs.timeBased(), msgType, stateData.getDeviceId(), stateData.getMetaData(), TbMsgDataType.JSON | |
353 | + , json.writeValueAsString(state) | |
354 | + , null, null, 0L); | |
355 | + actorService.onMsg(new ServiceToRuleEngineMsg(stateData.getTenantId(), tbMsg)); | |
356 | + } catch (Exception e) { | |
357 | + log.warn("[{}] Failed to push inactivity alarm: {}", stateData.getDeviceId(), state, e); | |
358 | + } | |
359 | + } | |
360 | + | |
361 | + private void saveAttribute(DeviceId deviceId, String key, long value) { | |
362 | + tsSubService.saveAttrAndNotify(deviceId, DataConstants.SERVER_SCOPE, key, value, new AttributeSaveCallback(deviceId, key, value)); | |
363 | + } | |
364 | + | |
365 | + private void saveAttribute(DeviceId deviceId, String key, boolean value) { | |
366 | + tsSubService.saveAttrAndNotify(deviceId, DataConstants.SERVER_SCOPE, key, value, new AttributeSaveCallback(deviceId, key, value)); | |
367 | + } | |
368 | + | |
369 | + private class AttributeSaveCallback implements FutureCallback<Void> { | |
370 | + private final DeviceId deviceId; | |
371 | + private final String key; | |
372 | + private final Object value; | |
373 | + | |
374 | + AttributeSaveCallback(DeviceId deviceId, String key, Object value) { | |
375 | + this.deviceId = deviceId; | |
376 | + this.key = key; | |
377 | + this.value = value; | |
378 | + } | |
379 | + | |
380 | + @Override | |
381 | + public void onSuccess(@Nullable Void result) { | |
382 | + log.trace("[{}] Successfully updated attribute [{}] with value [{}]", deviceId, key, value); | |
383 | + } | |
384 | + | |
385 | + @Override | |
386 | + public void onFailure(Throwable t) { | |
387 | + log.warn("[{}] Failed to update attribute [{}] with value [{}]", deviceId, key, value, t); | |
388 | + } | |
389 | + } | |
390 | +} | ... | ... |
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.service.state; | |
17 | + | |
18 | +import lombok.Builder; | |
19 | +import lombok.Data; | |
20 | + | |
21 | +/** | |
22 | + * Created by ashvayka on 01.05.18. | |
23 | + */ | |
24 | +@Data | |
25 | +@Builder | |
26 | +public class DeviceState { | |
27 | + | |
28 | + private boolean active; | |
29 | + private long lastConnectTime; | |
30 | + private long lastActivityTime; | |
31 | + private long lastDisconnectTime; | |
32 | + private long lastInactivityAlarmTime; | |
33 | + private long inactivityTimeout; | |
34 | + | |
35 | +} | ... | ... |
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.service.state; | |
17 | + | |
18 | +import lombok.Builder; | |
19 | +import lombok.Data; | |
20 | +import org.thingsboard.server.common.data.id.DeviceId; | |
21 | +import org.thingsboard.server.common.data.id.TenantId; | |
22 | +import org.thingsboard.server.common.msg.TbMsgMetaData; | |
23 | + | |
24 | +/** | |
25 | + * Created by ashvayka on 01.05.18. | |
26 | + */ | |
27 | +@Data | |
28 | +@Builder | |
29 | +class DeviceStateData { | |
30 | + | |
31 | + private final TenantId tenantId; | |
32 | + private final DeviceId deviceId; | |
33 | + | |
34 | + private TbMsgMetaData metaData; | |
35 | + private final DeviceState state; | |
36 | + | |
37 | +} | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016-2018 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.server.service.state; | |
17 | + | |
18 | +import org.thingsboard.server.common.data.Device; | |
19 | +import org.thingsboard.server.common.data.id.DeviceId; | |
20 | + | |
21 | +import java.util.Optional; | |
22 | + | |
23 | +/** | |
24 | + * Created by ashvayka on 01.05.18. | |
25 | + */ | |
26 | +public interface DeviceStateService { | |
27 | + | |
28 | + void onDeviceAdded(Device device); | |
29 | + | |
30 | + void onDeviceUpdated(Device device); | |
31 | + | |
32 | + void onDeviceDeleted(Device device); | |
33 | + | |
34 | + void onDeviceConnect(DeviceId deviceId); | |
35 | + | |
36 | + void onDeviceActivity(DeviceId deviceId); | |
37 | + | |
38 | + void onDeviceDisconnect(DeviceId deviceId); | |
39 | + | |
40 | + void onDeviceInactivityTimeoutUpdate(DeviceId deviceId, long inactivityTimeout); | |
41 | + | |
42 | + Optional<DeviceState> getDeviceState(DeviceId deviceId); | |
43 | + | |
44 | +} | ... | ... |