Commit 9317281f573a965c4a1b90af696514716425f4f0

Authored by Artem Babak
2 parents cbe5a264 0c3d1556

Merge remote-tracking branch 'thingsboard/master' into edge/cleanups-fixes

Showing 53 changed files with 1789 additions and 1561 deletions

Too many changes to show.

To preserve performance only 53 of 613 files are displayed.

... ... @@ -33,15 +33,15 @@
33 33 "enableSearch": true,
34 34 "displayPagination": true,
35 35 "defaultPageSize": 10,
36   - "defaultSortOrder": "entityLabel",
37   - "displayEntityName": false,
  36 + "defaultSortOrder": "entityName",
  37 + "displayEntityName": true,
38 38 "displayEntityType": false,
39 39 "enableSelectColumnDisplay": false,
40 40 "enableStickyHeader": true,
41 41 "enableStickyAction": false,
42 42 "entitiesTitle": "Devices",
43   - "displayEntityLabel": true,
44   - "entityLabelColumnTitle": "Device"
  43 + "displayEntityLabel": false,
  44 + "entityNameColumnTitle": "Device"
45 45 },
46 46 "title": "New Entities table",
47 47 "dropShadow": true,
... ... @@ -828,15 +828,15 @@
828 828 "enableSearch": true,
829 829 "displayPagination": true,
830 830 "defaultPageSize": 10,
831   - "defaultSortOrder": "entityLabel",
832   - "displayEntityName": false,
  831 + "defaultSortOrder": "entityName",
  832 + "displayEntityName": true,
833 833 "displayEntityType": false,
834 834 "enableSelectColumnDisplay": false,
835 835 "enableStickyHeader": true,
836 836 "enableStickyAction": true,
837 837 "entitiesTitle": "Devices",
838   - "displayEntityLabel": true,
839   - "entityLabelColumnTitle": "Device"
  838 + "displayEntityLabel": false,
  839 + "entityNameColumnTitle": "Device"
840 840 },
841 841 "title": "New Entities table",
842 842 "dropShadow": true,
... ... @@ -1125,15 +1125,15 @@
1125 1125 "enableSearch": true,
1126 1126 "displayPagination": true,
1127 1127 "defaultPageSize": 10,
1128   - "defaultSortOrder": "entityLabel",
1129   - "displayEntityName": false,
  1128 + "defaultSortOrder": "entityName",
  1129 + "displayEntityName": true,
1130 1130 "displayEntityType": false,
1131 1131 "enableSelectColumnDisplay": false,
1132 1132 "enableStickyHeader": true,
1133 1133 "enableStickyAction": true,
1134 1134 "entitiesTitle": "Devices",
1135   - "displayEntityLabel": true,
1136   - "entityLabelColumnTitle": "Device"
  1135 + "displayEntityLabel": false,
  1136 + "entityNameColumnTitle": "Device"
1137 1137 },
1138 1138 "title": "New Entities table",
1139 1139 "dropShadow": true,
... ... @@ -1422,15 +1422,15 @@
1422 1422 "enableSearch": true,
1423 1423 "displayPagination": true,
1424 1424 "defaultPageSize": 10,
1425   - "defaultSortOrder": "entityLabel",
1426   - "displayEntityName": false,
  1425 + "defaultSortOrder": "entityName",
  1426 + "displayEntityName": true,
1427 1427 "displayEntityType": false,
1428 1428 "enableSelectColumnDisplay": false,
1429 1429 "enableStickyHeader": true,
1430 1430 "enableStickyAction": true,
1431 1431 "entitiesTitle": "Devices",
1432   - "displayEntityLabel": true,
1433   - "entityLabelColumnTitle": "Device"
  1432 + "displayEntityLabel": false,
  1433 + "entityNameColumnTitle": "Device"
1434 1434 },
1435 1435 "title": "New Entities table",
1436 1436 "dropShadow": true,
... ... @@ -1719,15 +1719,15 @@
1719 1719 "enableSearch": true,
1720 1720 "displayPagination": true,
1721 1721 "defaultPageSize": 10,
1722   - "defaultSortOrder": "entityLabel",
1723   - "displayEntityName": false,
  1722 + "defaultSortOrder": "entityName",
  1723 + "displayEntityName": true,
1724 1724 "displayEntityType": false,
1725 1725 "enableSelectColumnDisplay": false,
1726 1726 "enableStickyHeader": true,
1727 1727 "enableStickyAction": true,
1728 1728 "entitiesTitle": "Devices",
1729   - "displayEntityLabel": true,
1730   - "entityLabelColumnTitle": "Device"
  1729 + "displayEntityLabel": false,
  1730 + "entityNameColumnTitle": "Device"
1731 1731 },
1732 1732 "title": "New Entities table",
1733 1733 "dropShadow": true,
... ...
... ... @@ -33,15 +33,15 @@
33 33 "enableSearch": true,
34 34 "displayPagination": true,
35 35 "defaultPageSize": 10,
36   - "defaultSortOrder": "entityLabel",
37   - "displayEntityName": false,
  36 + "defaultSortOrder": "entityName",
  37 + "displayEntityName": true,
38 38 "displayEntityType": false,
39 39 "enableSelectColumnDisplay": false,
40 40 "enableStickyHeader": true,
41 41 "enableStickyAction": false,
42 42 "entitiesTitle": "Devices",
43   - "displayEntityLabel": true,
44   - "entityLabelColumnTitle": "Device"
  43 + "displayEntityLabel": false,
  44 + "entityNameColumnTitle": "Device"
45 45 },
46 46 "title": "New Entities table",
47 47 "dropShadow": true,
... ... @@ -828,15 +828,15 @@
828 828 "enableSearch": true,
829 829 "displayPagination": true,
830 830 "defaultPageSize": 10,
831   - "defaultSortOrder": "entityLabel",
832   - "displayEntityName": false,
  831 + "defaultSortOrder": "entityName",
  832 + "displayEntityName": true,
833 833 "displayEntityType": false,
834 834 "enableSelectColumnDisplay": false,
835 835 "enableStickyHeader": true,
836 836 "enableStickyAction": true,
837 837 "entitiesTitle": "Devices",
838   - "displayEntityLabel": true,
839   - "entityLabelColumnTitle": "Device"
  838 + "displayEntityLabel": false,
  839 + "entityNameColumnTitle": "Device"
840 840 },
841 841 "title": "New Entities table",
842 842 "dropShadow": true,
... ... @@ -1125,15 +1125,15 @@
1125 1125 "enableSearch": true,
1126 1126 "displayPagination": true,
1127 1127 "defaultPageSize": 10,
1128   - "defaultSortOrder": "entityLabel",
1129   - "displayEntityName": false,
  1128 + "defaultSortOrder": "entityName",
  1129 + "displayEntityName": true,
1130 1130 "displayEntityType": false,
1131 1131 "enableSelectColumnDisplay": false,
1132 1132 "enableStickyHeader": true,
1133 1133 "enableStickyAction": true,
1134 1134 "entitiesTitle": "Devices",
1135   - "displayEntityLabel": true,
1136   - "entityLabelColumnTitle": "Device"
  1135 + "displayEntityLabel": false,
  1136 + "entityNameColumnTitle": "Device"
1137 1137 },
1138 1138 "title": "New Entities table",
1139 1139 "dropShadow": true,
... ... @@ -1422,15 +1422,15 @@
1422 1422 "enableSearch": true,
1423 1423 "displayPagination": true,
1424 1424 "defaultPageSize": 10,
1425   - "defaultSortOrder": "entityLabel",
1426   - "displayEntityName": false,
  1425 + "defaultSortOrder": "entityName",
  1426 + "displayEntityName": true,
1427 1427 "displayEntityType": false,
1428 1428 "enableSelectColumnDisplay": false,
1429 1429 "enableStickyHeader": true,
1430 1430 "enableStickyAction": true,
1431 1431 "entitiesTitle": "Devices",
1432   - "displayEntityLabel": true,
1433   - "entityLabelColumnTitle": "Device"
  1432 + "displayEntityLabel": false,
  1433 + "entityNameColumnTitle": "Device"
1434 1434 },
1435 1435 "title": "New Entities table",
1436 1436 "dropShadow": true,
... ... @@ -1719,15 +1719,15 @@
1719 1719 "enableSearch": true,
1720 1720 "displayPagination": true,
1721 1721 "defaultPageSize": 10,
1722   - "defaultSortOrder": "entityLabel",
1723   - "displayEntityName": false,
  1722 + "defaultSortOrder": "entityName",
  1723 + "displayEntityName": true,
1724 1724 "displayEntityType": false,
1725 1725 "enableSelectColumnDisplay": false,
1726 1726 "enableStickyHeader": true,
1727 1727 "enableStickyAction": true,
1728 1728 "entitiesTitle": "Devices",
1729   - "displayEntityLabel": true,
1730   - "entityLabelColumnTitle": "Device"
  1729 + "displayEntityLabel": false,
  1730 + "entityNameColumnTitle": "Device"
1731 1731 },
1732 1732 "title": "New Entities table",
1733 1733 "dropShadow": true,
... ...
1   -{
2   - "ruleChain": {
3   - "additionalInfo": null,
4   - "name": "Edge Root Rule Chain",
5   - "type": "EDGE",
6   - "firstRuleNodeId": null,
7   - "root": true,
8   - "debugMode": false,
9   - "configuration": null
10   - },
11   - "metadata": {
12   - "firstNodeIndex": 0,
13   - "nodes": [
14   - {
15   - "additionalInfo": {
16   - "description": "Process incoming messages from devices with the alarm rules defined in the device profile. Dispatch all incoming messages with \"Success\" relation type.",
17   - "layoutX": 203,
18   - "layoutY": 259
19   - },
20   - "type": "org.thingsboard.rule.engine.profile.TbDeviceProfileNode",
21   - "name": "Device Profile Node",
22   - "debugMode": false,
23   - "configuration": {
24   - "persistAlarmRulesState": false,
25   - "fetchAlarmRulesStateOnStart": false
26   - }
27   - },
28   - {
29   - "additionalInfo": {
30   - "layoutX": 823,
31   - "layoutY": 157
32   - },
33   - "type": "org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode",
34   - "name": "Save Timeseries",
35   - "debugMode": false,
36   - "configuration": {
37   - "defaultTTL": 0
38   - }
39   - },
40   - {
41   - "additionalInfo": {
42   - "layoutX": 824,
43   - "layoutY": 52
44   - },
45   - "type": "org.thingsboard.rule.engine.telemetry.TbMsgAttributesNode",
46   - "name": "Save Client Attributes",
47   - "debugMode": false,
48   - "configuration": {
49   - "scope": "CLIENT_SCOPE"
50   - }
51   - },
52   - {
53   - "additionalInfo": {
54   - "layoutX": 347,
55   - "layoutY": 149
56   - },
57   - "type": "org.thingsboard.rule.engine.filter.TbMsgTypeSwitchNode",
58   - "name": "Message Type Switch",
59   - "debugMode": false,
60   - "configuration": {
61   - "version": 0
62   - }
63   - },
64   - {
65   - "additionalInfo": {
66   - "layoutX": 825,
67   - "layoutY": 266
68   - },
69   - "type": "org.thingsboard.rule.engine.action.TbLogNode",
70   - "name": "Log RPC from Device",
71   - "debugMode": false,
72   - "configuration": {
73   - "jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);"
74   - }
75   - },
76   - {
77   - "additionalInfo": {
78   - "layoutX": 824,
79   - "layoutY": 378
80   - },
81   - "type": "org.thingsboard.rule.engine.action.TbLogNode",
82   - "name": "Log Other",
83   - "debugMode": false,
84   - "configuration": {
85   - "jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);"
86   - }
87   - },
88   - {
89   - "additionalInfo": {
90   - "layoutX": 824,
91   - "layoutY": 466
92   - },
93   - "type": "org.thingsboard.rule.engine.rpc.TbSendRPCRequestNode",
94   - "name": "RPC Call Request",
95   - "debugMode": false,
96   - "configuration": {
97   - "timeoutInSeconds": 60
98   - }
99   - },
100   - {
101   - "additionalInfo": {
102   - "layoutX": 1134,
103   - "layoutY": 132
104   - },
105   - "type": "org.thingsboard.rule.engine.edge.TbMsgPushToCloudNode",
106   - "name": "Push to cloud",
107   - "debugMode": false,
108   - "configuration": {
109   - "version": 0
110   - }
111   - }
112   - ],
113   - "connections": [
114   - {
115   - "fromIndex": 0,
116   - "toIndex": 3,
117   - "type": "Success"
118   - },
119   - {
120   - "fromIndex": 1,
121   - "toIndex": 7,
122   - "type": "Success"
123   - },
124   - {
125   - "fromIndex": 2,
126   - "toIndex": 7,
127   - "type": "Success"
128   - },
129   - {
130   - "fromIndex": 3,
131   - "toIndex": 6,
132   - "type": "RPC Request to Device"
133   - },
134   - {
135   - "fromIndex": 3,
136   - "toIndex": 5,
137   - "type": "Other"
138   - },
139   - {
140   - "fromIndex": 3,
141   - "toIndex": 2,
142   - "type": "Post attributes"
143   - },
144   - {
145   - "fromIndex": 3,
146   - "toIndex": 1,
147   - "type": "Post telemetry"
148   - },
149   - {
150   - "fromIndex": 3,
151   - "toIndex": 4,
152   - "type": "RPC Request from Device"
153   - },
154   - {
155   - "fromIndex": 4,
156   - "toIndex": 7,
157   - "type": "Success"
158   - }
159   - ],
160   - "ruleChainConnections": null
161   - }
162   -}
\ No newline at end of file
... ... @@ -18,8 +18,8 @@
18 18 "resources": [],
19 19 "templateHtml": "<div style=\"height: 100%; overflow-y: auto;\" id=\"device-terminal\"></div>",
20 20 "templateCss": ".cmd .cursor.blink {\n -webkit-animation-name: terminal-underline;\n -moz-animation-name: terminal-underline;\n -ms-animation-name: terminal-underline;\n animation-name: terminal-underline;\n}\n.terminal .inverted, .cmd .inverted {\n border-bottom-color: #aaa;\n}\n\n",
21   - "controllerScript": "var requestTimeout = 500;\n\nself.onInit = function() {\n var subscription = self.ctx.defaultSubscription;\n var rpcEnabled = subscription.rpcEnabled;\n var deviceName = 'Simulated';\n var prompt;\n if (subscription.targetDeviceName && subscription.targetDeviceName.length) {\n deviceName = subscription.targetDeviceName;\n }\n if (self.ctx.settings.requestTimeout) {\n requestTimeout = self.ctx.settings.requestTimeout;\n }\n var greetings = 'Welcome to ThingsBoard RPC debug terminal.\\n\\n';\n if (!rpcEnabled) {\n greetings += 'Target device is not set!\\n\\n';\n prompt = '';\n } else {\n greetings += 'Current target device for RPC commands: [[b;#fff;]' + deviceName + ']\\n\\n';\n greetings += 'Please type [[b;#fff;]\\'help\\'] to see usage.\\n';\n prompt = '[[b;#8bc34a;]' + deviceName +']> ';\n }\n \n var terminal = $('#device-terminal', self.ctx.$container).terminal(\n function(command) {\n if (command !== '') {\n try {\n var localCommand = command.trim();\n var requestUUID = uuidv4();\n if (localCommand === 'help') {\n printUsage(this);\n } else {\n var spaceIndex = localCommand.indexOf(' ');\n if (spaceIndex === -1 && !localCommand.length) {\n this.error(\"Wrong number of arguments!\");\n this.echo(' ');\n } else {\n var params;\n if (spaceIndex === -1) {\n spaceIndex = localCommand.length;\n }\n var name = localCommand.substr(0, spaceIndex);\n var args = localCommand.substr(spaceIndex + 1);\n if (args.length) {\n try {\n params = JSON.parse(args);\n } catch (e) {\n params = args;\n }\n }\n performRpc(this, name, params, requestUUID);\n }\n }\n } catch(e) {\n this.error(new String(e));\n }\n } else {\n this.echo('');\n }\n }, {\n greetings: greetings,\n prompt: prompt,\n enabled: rpcEnabled\n });\n \n if (!rpcEnabled) {\n terminal.error('No RPC target detected!').pause();\n }\n}\n\n\nfunction printUsage(terminal) {\n var commandsListText = '\\n[[b;#fff;]Usage:]\\n';\n commandsListText += ' <method> [params body]]\\n\\n';\n commandsListText += '[[b;#fff;]Example 1:]\\n'; \n commandsListText += ' myRemoteMethod1 myText\\n\\n'; \n commandsListText += '[[b;#fff;]Example 2:]\\n'; \n commandsListText += ' myOtherRemoteMethod \"{\\\\\"key1\\\\\": 2, \\\\\"key2\\\\\": \\\\\"myVal\\\\\"}\"\\n'; \n terminal.echo(new String(commandsListText));\n}\n\n\nfunction performRpc(terminal, method, params, requestUUID) {\n terminal.pause();\n self.ctx.controlApi.sendTwoWayCommand(method, params, requestTimeout, requestUUID).subscribe(\n function success(responseBody) {\n terminal.echo(JSON.stringify(responseBody));\n terminal.echo(' ');\n terminal.resume();\n },\n function fail() {\n var errorText = self.ctx.defaultSubscription.rpcErrorText;\n terminal.error(errorText);\n terminal.echo(' ');\n terminal.resume();\n }\n );\n}\n\n\nfunction uuidv4() {\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {\n var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);\n return v.toString(16);\n });\n}\n\n \nself.onDestroy = function() {\n}",
22   - "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"requestTimeout\": {\n \"title\": \"RPC request timeout (ms)\",\n \"type\": \"number\",\n \"default\": 500\n }\n },\n \"required\": [\"requestTimeout\"]\n },\n \"form\": [\n \"requestTimeout\"\n ]\n}",
  21 + "controllerScript": "var requestTimeout = 500;\nvar requestPersistent = false;\n\nself.onInit = function() {\n var subscription = self.ctx.defaultSubscription;\n var rpcEnabled = subscription.rpcEnabled;\n var deviceName = 'Simulated';\n var prompt;\n if (subscription.targetDeviceName && subscription.targetDeviceName.length) {\n deviceName = subscription.targetDeviceName;\n }\n if (self.ctx.settings.requestTimeout) {\n requestTimeout = self.ctx.settings.requestTimeout;\n }\n if (self.ctx.settings.requestPersistent) {\n requestPersistent = self.ctx.settings.requestPersistent;\n }\n var greetings = 'Welcome to ThingsBoard RPC debug terminal.\\n\\n';\n if (!rpcEnabled) {\n greetings += 'Target device is not set!\\n\\n';\n prompt = '';\n } else {\n greetings += 'Current target device for RPC commands: [[b;#fff;]' + deviceName + ']\\n\\n';\n greetings += 'Please type [[b;#fff;]\\'help\\'] to see usage.\\n';\n prompt = '[[b;#8bc34a;]' + deviceName +']> ';\n }\n \n var terminal = $('#device-terminal', self.ctx.$container).terminal(\n function(command) {\n if (command !== '') {\n try {\n var localCommand = command.trim();\n var requestUUID = uuidv4();\n if (localCommand === 'help') {\n printUsage(this);\n } else {\n var spaceIndex = localCommand.indexOf(' ');\n if (spaceIndex === -1 && !localCommand.length) {\n this.error(\"Wrong number of arguments!\");\n this.echo(' ');\n } else {\n var params;\n if (spaceIndex === -1) {\n spaceIndex = localCommand.length;\n }\n var name = localCommand.substr(0, spaceIndex);\n var args = localCommand.substr(spaceIndex + 1);\n if (args.length) {\n try {\n params = JSON.parse(args);\n } catch (e) {\n params = args;\n }\n }\n performRpc(this, name, params, requestUUID);\n }\n }\n } catch(e) {\n this.error(new String(e));\n }\n } else {\n this.echo('');\n }\n }, {\n greetings: greetings,\n prompt: prompt,\n enabled: rpcEnabled\n });\n \n if (!rpcEnabled) {\n terminal.error('No RPC target detected!').pause();\n }\n}\n\n\nfunction printUsage(terminal) {\n var commandsListText = '\\n[[b;#fff;]Usage:]\\n';\n commandsListText += ' <method> [params body]]\\n\\n';\n commandsListText += '[[b;#fff;]Example 1:]\\n'; \n commandsListText += ' myRemoteMethod1 myText\\n\\n'; \n commandsListText += '[[b;#fff;]Example 2:]\\n'; \n commandsListText += ' myOtherRemoteMethod \"{\\\\\"key1\\\\\": 2, \\\\\"key2\\\\\": \\\\\"myVal\\\\\"}\"\\n'; \n terminal.echo(new String(commandsListText));\n}\n\n\nfunction performRpc(terminal, method, params, requestUUID) {\n terminal.pause();\n self.ctx.controlApi.sendTwoWayCommand(method, params, requestTimeout, requestPersistent, requestUUID).subscribe(\n function success(responseBody) {\n terminal.echo(JSON.stringify(responseBody));\n terminal.echo(' ');\n terminal.resume();\n },\n function fail() {\n var errorText = self.ctx.defaultSubscription.rpcErrorText;\n terminal.error(errorText);\n terminal.echo(' ');\n terminal.resume();\n }\n );\n}\n\n\nfunction uuidv4() {\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {\n var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);\n return v.toString(16);\n });\n}\n\n \nself.onDestroy = function() {\n}",
  22 + "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"requestTimeout\": {\n \"title\": \"RPC request timeout (ms)\",\n \"type\": \"number\",\n \"default\": 500\n },\n \"requestPersistent\": {\n \"title\": \"RPC request persistent\",\n \"type\": \"boolean\",\n \"default\": false\n }\n },\n \"required\": [\"requestTimeout\"]\n },\n \"form\": [\n \"requestTimeout\",\n \"requestPersistent\"\n ]\n}",
23 23 "dataKeySettingsSchema": "{}\n",
24 24 "defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":true,\"backgroundColor\":\"#010101\",\"color\":\"rgba(255, 254, 254, 0.87)\",\"padding\":\"0px\",\"settings\":{\"parseGpioStatusFunction\":\"return body[pin] === true;\",\"gpioStatusChangeRequest\":{\"method\":\"setGpioStatus\",\"paramsBody\":\"{\\n \\\"pin\\\": \\\"{$pin}\\\",\\n \\\"enabled\\\": \\\"{$enabled}\\\"\\n}\"},\"requestTimeout\":500,\"switchPanelBackgroundColor\":\"#b71c1c\",\"gpioStatusRequest\":{\"method\":\"getGpioStatus\",\"paramsBody\":\"{}\"},\"gpioList\":[{\"pin\":1,\"label\":\"GPIO 1\",\"row\":0,\"col\":0,\"_uniqueKey\":0},{\"pin\":2,\"label\":\"GPIO 2\",\"row\":0,\"col\":1,\"_uniqueKey\":1},{\"pin\":3,\"label\":\"GPIO 3\",\"row\":1,\"col\":0,\"_uniqueKey\":2}]},\"title\":\"RPC debug terminal\",\"dropShadow\":true,\"enableFullscreen\":true,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
25 25 }
... ...
... ... @@ -14,8 +14,8 @@
14 14 {
15 15 "additionalInfo": {
16 16 "description": "Process incoming messages from devices with the alarm rules defined in the device profile. Dispatch all incoming messages with \"Success\" relation type.",
17   - "layoutX": 203,
18   - "layoutY": 259
  17 + "layoutX": 187,
  18 + "layoutY": 468
19 19 },
20 20 "type": "org.thingsboard.rule.engine.profile.TbDeviceProfileNode",
21 21 "name": "Device Profile Node",
... ... @@ -99,14 +99,14 @@
99 99 },
100 100 {
101 101 "additionalInfo": {
102   - "layoutX": 1134,
103   - "layoutY": 132
  102 + "layoutX": 1129,
  103 + "layoutY": 52
104 104 },
105 105 "type": "org.thingsboard.rule.engine.edge.TbMsgPushToCloudNode",
106 106 "name": "Push to cloud",
107 107 "debugMode": false,
108 108 "configuration": {
109   - "version": 0
  109 + "scope": "SERVER_SCOPE"
110 110 }
111 111 }
112 112 ],
... ... @@ -152,6 +152,11 @@
152 152 "type": "RPC Request from Device"
153 153 },
154 154 {
  155 + "fromIndex": 3,
  156 + "toIndex": 7,
  157 + "type": "Attributes Updated"
  158 + },
  159 + {
155 160 "fromIndex": 4,
156 161 "toIndex": 7,
157 162 "type": "Success"
... ...
... ... @@ -18,17 +18,18 @@ CREATE OR REPLACE PROCEDURE drop_partitions_by_max_ttl(IN partition_type varchar
18 18 LANGUAGE plpgsql AS
19 19 $$
20 20 DECLARE
21   - max_tenant_ttl bigint;
22   - max_customer_ttl bigint;
23   - max_ttl bigint;
24   - date timestamp;
25   - partition_by_max_ttl_date varchar;
26   - partition_month varchar;
27   - partition_day varchar;
28   - partition_year varchar;
29   - partition varchar;
30   - partition_to_delete varchar;
31   -
  21 + max_tenant_ttl bigint;
  22 + max_customer_ttl bigint;
  23 + max_ttl bigint;
  24 + date timestamp;
  25 + partition_by_max_ttl_date varchar;
  26 + partition_by_max_ttl_month varchar;
  27 + partition_by_max_ttl_day varchar;
  28 + partition_by_max_ttl_year varchar;
  29 + partition varchar;
  30 + partition_year integer;
  31 + partition_month integer;
  32 + partition_day integer;
32 33
33 34 BEGIN
34 35 SELECT max(attribute_kv.long_v)
... ... @@ -45,53 +46,138 @@ BEGIN
45 46 if max_ttl IS NOT NULL AND max_ttl > 0 THEN
46 47 date := to_timestamp(EXTRACT(EPOCH FROM current_timestamp) - max_ttl);
47 48 partition_by_max_ttl_date := get_partition_by_max_ttl_date(partition_type, date);
  49 + RAISE NOTICE 'Date by max ttl: %', date;
48 50 RAISE NOTICE 'Partition by max ttl: %', partition_by_max_ttl_date;
49 51 IF partition_by_max_ttl_date IS NOT NULL THEN
50 52 CASE
51 53 WHEN partition_type = 'DAYS' THEN
52   - partition_year := SPLIT_PART(partition_by_max_ttl_date, '_', 3);
53   - partition_month := SPLIT_PART(partition_by_max_ttl_date, '_', 4);
54   - partition_day := SPLIT_PART(partition_by_max_ttl_date, '_', 5);
  54 + partition_by_max_ttl_year := SPLIT_PART(partition_by_max_ttl_date, '_', 3);
  55 + partition_by_max_ttl_month := SPLIT_PART(partition_by_max_ttl_date, '_', 4);
  56 + partition_by_max_ttl_day := SPLIT_PART(partition_by_max_ttl_date, '_', 5);
55 57 WHEN partition_type = 'MONTHS' THEN
56   - partition_year := SPLIT_PART(partition_by_max_ttl_date, '_', 3);
57   - partition_month := SPLIT_PART(partition_by_max_ttl_date, '_', 4);
  58 + partition_by_max_ttl_year := SPLIT_PART(partition_by_max_ttl_date, '_', 3);
  59 + partition_by_max_ttl_month := SPLIT_PART(partition_by_max_ttl_date, '_', 4);
58 60 ELSE
59   - partition_year := SPLIT_PART(partition_by_max_ttl_date, '_', 3);
  61 + partition_by_max_ttl_year := SPLIT_PART(partition_by_max_ttl_date, '_', 3);
60 62 END CASE;
61   - FOR partition IN SELECT tablename
62   - FROM pg_tables
63   - WHERE schemaname = 'public'
64   - AND tablename like 'ts_kv_' || '%'
65   - AND tablename != 'ts_kv_latest'
66   - AND tablename != 'ts_kv_dictionary'
67   - AND tablename != 'ts_kv_indefinite'
68   - LOOP
69   - IF partition != partition_by_max_ttl_date THEN
70   - IF partition_year IS NOT NULL THEN
71   - IF SPLIT_PART(partition, '_', 3)::integer < partition_year::integer THEN
72   - partition_to_delete := partition;
73   - ELSE
74   - IF partition_month IS NOT NULL THEN
75   - IF SPLIT_PART(partition, '_', 4)::integer < partition_month::integer THEN
76   - partition_to_delete := partition;
  63 + IF partition_by_max_ttl_year IS NULL THEN
  64 + RAISE NOTICE 'Failed to remove partitions by max ttl date due to partition_by_max_ttl_year is null!';
  65 + ELSE
  66 + IF partition_type = 'YEARS' THEN
  67 + FOR partition IN SELECT tablename
  68 + FROM pg_tables
  69 + WHERE schemaname = 'public'
  70 + AND tablename like 'ts_kv_' || '%'
  71 + AND tablename != 'ts_kv_latest'
  72 + AND tablename != 'ts_kv_dictionary'
  73 + AND tablename != 'ts_kv_indefinite'
  74 + AND tablename != partition_by_max_ttl_date
  75 + LOOP
  76 + partition_year := SPLIT_PART(partition, '_', 3)::integer;
  77 + IF partition_year < partition_by_max_ttl_year::integer THEN
  78 + RAISE NOTICE 'Partition to delete by max ttl: %', partition;
  79 + EXECUTE format('DROP TABLE IF EXISTS %I', partition);
  80 + deleted := deleted + 1;
  81 + END IF;
  82 + END LOOP;
  83 + ELSE
  84 + IF partition_type = 'MONTHS' THEN
  85 + IF partition_by_max_ttl_month IS NULL THEN
  86 + RAISE NOTICE 'Failed to remove months partitions by max ttl date due to partition_by_max_ttl_month is null!';
  87 + ELSE
  88 + FOR partition IN SELECT tablename
  89 + FROM pg_tables
  90 + WHERE schemaname = 'public'
  91 + AND tablename like 'ts_kv_' || '%'
  92 + AND tablename != 'ts_kv_latest'
  93 + AND tablename != 'ts_kv_dictionary'
  94 + AND tablename != 'ts_kv_indefinite'
  95 + AND tablename != partition_by_max_ttl_date
  96 + LOOP
  97 + partition_year := SPLIT_PART(partition, '_', 3)::integer;
  98 + IF partition_year > partition_by_max_ttl_year::integer THEN
  99 + RAISE NOTICE 'Skip iteration! Partition: % is valid!', partition;
  100 + CONTINUE;
77 101 ELSE
78   - IF partition_day IS NOT NULL THEN
79   - IF SPLIT_PART(partition, '_', 5)::integer < partition_day::integer THEN
80   - partition_to_delete := partition;
  102 + IF partition_year < partition_by_max_ttl_year::integer THEN
  103 + RAISE NOTICE 'Partition to delete by max ttl: %', partition;
  104 + EXECUTE format('DROP TABLE IF EXISTS %I', partition);
  105 + deleted := deleted + 1;
  106 + ELSE
  107 + partition_month := SPLIT_PART(partition, '_', 4)::integer;
  108 + IF partition_year = partition_by_max_ttl_year::integer THEN
  109 + IF partition_month >= partition_by_max_ttl_month::integer THEN
  110 + RAISE NOTICE 'Skip iteration! Partition: % is valid!', partition;
  111 + CONTINUE;
  112 + ELSE
  113 + RAISE NOTICE 'Partition to delete by max ttl: %', partition;
  114 + EXECUTE format('DROP TABLE IF EXISTS %I', partition);
  115 + deleted := deleted + 1;
  116 + END IF;
81 117 END IF;
82 118 END IF;
83 119 END IF;
  120 + END LOOP;
  121 + END IF;
  122 + ELSE
  123 + IF partition_type = 'DAYS' THEN
  124 + IF partition_by_max_ttl_month IS NULL THEN
  125 + RAISE NOTICE 'Failed to remove days partitions by max ttl date due to partition_by_max_ttl_month is null!';
  126 + ELSE
  127 + IF partition_by_max_ttl_day IS NULL THEN
  128 + RAISE NOTICE 'Failed to remove days partitions by max ttl date due to partition_by_max_ttl_day is null!';
  129 + ELSE
  130 + FOR partition IN SELECT tablename
  131 + FROM pg_tables
  132 + WHERE schemaname = 'public'
  133 + AND tablename like 'ts_kv_' || '%'
  134 + AND tablename != 'ts_kv_latest'
  135 + AND tablename != 'ts_kv_dictionary'
  136 + AND tablename != 'ts_kv_indefinite'
  137 + AND tablename != partition_by_max_ttl_date
  138 + LOOP
  139 + partition_year := SPLIT_PART(partition, '_', 3)::integer;
  140 + IF partition_year > partition_by_max_ttl_year::integer THEN
  141 + RAISE NOTICE 'Skip iteration! Partition: % is valid!', partition;
  142 + CONTINUE;
  143 + ELSE
  144 + IF partition_year < partition_by_max_ttl_year::integer THEN
  145 + RAISE NOTICE 'Partition to delete by max ttl: %', partition;
  146 + EXECUTE format('DROP TABLE IF EXISTS %I', partition);
  147 + deleted := deleted + 1;
  148 + ELSE
  149 + partition_month := SPLIT_PART(partition, '_', 4)::integer;
  150 + IF partition_month > partition_by_max_ttl_month::integer THEN
  151 + RAISE NOTICE 'Skip iteration! Partition: % is valid!', partition;
  152 + CONTINUE;
  153 + ELSE
  154 + IF partition_month < partition_by_max_ttl_month::integer THEN
  155 + RAISE NOTICE 'Partition to delete by max ttl: %', partition;
  156 + EXECUTE format('DROP TABLE IF EXISTS %I', partition);
  157 + deleted := deleted + 1;
  158 + ELSE
  159 + partition_day := SPLIT_PART(partition, '_', 5)::integer;
  160 + IF partition_day >= partition_by_max_ttl_day::integer THEN
  161 + RAISE NOTICE 'Skip iteration! Partition: % is valid!', partition;
  162 + CONTINUE;
  163 + ELSE
  164 + IF partition_day < partition_by_max_ttl_day::integer THEN
  165 + RAISE NOTICE 'Partition to delete by max ttl: %', partition;
  166 + EXECUTE format('DROP TABLE IF EXISTS %I', partition);
  167 + deleted := deleted + 1;
  168 + END IF;
  169 + END IF;
  170 + END IF;
  171 + END IF;
  172 + END IF;
  173 + END IF;
  174 + END LOOP;
84 175 END IF;
85 176 END IF;
86 177 END IF;
87   - IF partition_to_delete IS NOT NULL THEN
88   - RAISE NOTICE 'Partition to delete by max ttl: %', partition_to_delete;
89   - EXECUTE format('DROP TABLE IF EXISTS %I', partition_to_delete);
90   - partition_to_delete := NULL;
91   - deleted := deleted + 1;
92   - END IF;
93 178 END IF;
94   - END LOOP;
  179 + END IF;
  180 + END IF;
95 181 END IF;
96 182 END IF;
97 183 END
... ... @@ -107,8 +193,6 @@ BEGIN
107 193 partition := 'ts_kv_' || to_char(date, 'yyyy') || '_' || to_char(date, 'MM');
108 194 WHEN partition_type = 'YEARS' THEN
109 195 partition := 'ts_kv_' || to_char(date, 'yyyy');
110   - WHEN partition_type = 'INDEFINITE' THEN
111   - partition := NULL;
112 196 ELSE
113 197 partition := NULL;
114 198 END CASE;
... ...
... ... @@ -197,3 +197,17 @@ $$;
197 197 ALTER TABLE api_usage_state
198 198 ADD COLUMN IF NOT EXISTS alarm_exec VARCHAR(32);
199 199 UPDATE api_usage_state SET alarm_exec = 'ENABLED' WHERE alarm_exec IS NULL;
  200 +
  201 +CREATE TABLE IF NOT EXISTS rpc (
  202 + id uuid NOT NULL CONSTRAINT rpc_pkey PRIMARY KEY,
  203 + created_time bigint NOT NULL,
  204 + tenant_id uuid NOT NULL,
  205 + device_id uuid NOT NULL,
  206 + expiration_time bigint NOT NULL,
  207 + request varchar(10000000) NOT NULL,
  208 + response varchar(10000000),
  209 + status varchar(255) NOT NULL
  210 +);
  211 +
  212 +CREATE INDEX IF NOT EXISTS idx_rpc_tenant_id_device_id ON rpc(tenant_id, device_id);
  213 +
... ...
  1 +--
  2 +-- Copyright © 2016-2021 The Thingsboard Authors
  3 +--
  4 +-- Licensed under the Apache License, Version 2.0 (the "License");
  5 +-- you may not use this file except in compliance with the License.
  6 +-- You may obtain a copy of the License at
  7 +--
  8 +-- http://www.apache.org/licenses/LICENSE-2.0
  9 +--
  10 +-- Unless required by applicable law or agreed to in writing, software
  11 +-- distributed under the License is distributed on an "AS IS" BASIS,
  12 +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 +-- See the License for the specific language governing permissions and
  14 +-- limitations under the License.
  15 +--
  16 +
  17 +-- PROCEDURE: public.cleanup_events_by_ttl(bigint, bigint, bigint)
  18 +
  19 +DROP PROCEDURE IF EXISTS public.cleanup_events_by_ttl(bigint, bigint, bigint);
  20 +
  21 +CREATE OR REPLACE PROCEDURE public.cleanup_events_by_ttl(
  22 + ttl bigint,
  23 + debug_ttl bigint,
  24 + INOUT deleted bigint)
  25 +LANGUAGE 'plpgsql'
  26 +AS $BODY$
  27 +DECLARE
  28 + ttl_ts bigint;
  29 + debug_ttl_ts bigint;
  30 + ttl_deleted_count bigint DEFAULT 0;
  31 + debug_ttl_deleted_count bigint DEFAULT 0;
  32 +BEGIN
  33 + IF ttl > 0 THEN
  34 + ttl_ts := (EXTRACT(EPOCH FROM current_timestamp) * 1000 - ttl::bigint * 1000)::bigint;
  35 +
  36 + DELETE FROM event
  37 + WHERE ts < ttl_ts
  38 + AND NOT event_type IN ('DEBUG_RULE_NODE', 'DEBUG_RULE_CHAIN', 'DEBUG_CONVERTER', 'DEBUG_INTEGRATION');
  39 +
  40 + GET DIAGNOSTICS ttl_deleted_count = ROW_COUNT;
  41 + END IF;
  42 +
  43 + IF debug_ttl > 0 THEN
  44 + debug_ttl_ts := (EXTRACT(EPOCH FROM current_timestamp) * 1000 - debug_ttl::bigint * 1000)::bigint;
  45 +
  46 + DELETE FROM event
  47 + WHERE ts < debug_ttl_ts
  48 + AND event_type IN ('DEBUG_RULE_NODE', 'DEBUG_RULE_CHAIN', 'DEBUG_CONVERTER', 'DEBUG_INTEGRATION');
  49 +
  50 + GET DIAGNOSTICS debug_ttl_deleted_count = ROW_COUNT;
  51 + END IF;
  52 +
  53 + RAISE NOTICE 'Events removed by ttl: %', ttl_deleted_count;
  54 + RAISE NOTICE 'Debug Events removed by ttl: %', debug_ttl_deleted_count;
  55 + deleted := ttl_deleted_count + debug_ttl_deleted_count;
  56 +END
  57 +$BODY$;
  58 +
  59 +
  60 +-- Index: idx_event_ts
  61 +
  62 +DROP INDEX IF EXISTS public.idx_event_ts;
  63 +
  64 +-- Hint: add CONCURRENTLY to CREATE INDEX query in case of more then 1 million records or during live update
  65 +-- CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_event_ts
  66 +CREATE INDEX IF NOT EXISTS idx_event_ts
  67 + ON public.event
  68 + (ts DESC NULLS LAST)
  69 + WITH (FILLFACTOR=95);
  70 +
  71 +COMMENT ON INDEX public.idx_event_ts
  72 + IS 'This index helps to delete events by TTL using timestamp';
  73 +
  74 +
  75 +-- Index: idx_event_tenant_entity_type_entity_event_type_created_time_des
  76 +
  77 +DROP INDEX IF EXISTS public.idx_event_tenant_entity_type_entity_event_type_created_time_des;
  78 +
  79 +-- CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_event_tenant_entity_type_entity_event_type_created_time_des
  80 +CREATE INDEX IF NOT EXISTS idx_event_tenant_entity_type_entity_event_type_created_time_des
  81 + ON public.event
  82 + (tenant_id ASC, entity_type ASC, entity_id ASC, event_type ASC, created_time DESC NULLS LAST)
  83 + WITH (FILLFACTOR=95);
  84 +
  85 +COMMENT ON INDEX public.idx_event_tenant_entity_type_entity_event_type_created_time_des
  86 + IS 'This index helps to open latest events on UI fast';
  87 +
  88 +-- Index: idx_event_type_entity_id
  89 +-- Description: replaced with more suitable idx_event_tenant_entity_type_entity_event_type_created_time_des
  90 +DROP INDEX IF EXISTS public.idx_event_type_entity_id;
\ No newline at end of file
... ...
... ... @@ -65,6 +65,7 @@ import org.thingsboard.server.dao.relation.RelationService;
65 65 import org.thingsboard.server.dao.resource.ResourceService;
66 66 import org.thingsboard.server.dao.rule.RuleChainService;
67 67 import org.thingsboard.server.dao.rule.RuleNodeStateService;
  68 +import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
68 69 import org.thingsboard.server.dao.tenant.TenantProfileService;
69 70 import org.thingsboard.server.dao.tenant.TenantService;
70 71 import org.thingsboard.server.dao.timeseries.TimeseriesService;
... ... @@ -80,9 +81,9 @@ import org.thingsboard.server.service.executors.ExternalCallExecutorService;
80 81 import org.thingsboard.server.service.executors.SharedEventLoopGroupService;
81 82 import org.thingsboard.server.service.mail.MailExecutorService;
82 83 import org.thingsboard.server.service.profile.TbDeviceProfileCache;
83   -import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
84 84 import org.thingsboard.server.service.queue.TbClusterService;
85 85 import org.thingsboard.server.service.rpc.TbCoreDeviceRpcService;
  86 +import org.thingsboard.server.service.rpc.TbRpcService;
86 87 import org.thingsboard.server.service.rpc.TbRuleEngineDeviceRpcService;
87 88 import org.thingsboard.server.service.script.JsExecutorService;
88 89 import org.thingsboard.server.service.script.JsInvokeService;
... ... @@ -303,23 +304,33 @@ public class ActorSystemContext {
303 304
304 305 @Lazy
305 306 @Autowired(required = false)
306   - @Getter private EdgeService edgeService;
  307 + @Getter
  308 + private EdgeService edgeService;
307 309
308 310 @Lazy
309 311 @Autowired(required = false)
310   - @Getter private EdgeEventService edgeEventService;
  312 + @Getter
  313 + private EdgeEventService edgeEventService;
311 314
312 315 @Lazy
313 316 @Autowired(required = false)
314   - @Getter private EdgeRpcService edgeRpcService;
  317 + @Getter
  318 + private EdgeRpcService edgeRpcService;
315 319
316 320 @Lazy
317 321 @Autowired(required = false)
318   - @Getter private ResourceService resourceService;
  322 + @Getter
  323 + private ResourceService resourceService;
319 324
320 325 @Lazy
321 326 @Autowired(required = false)
322   - @Getter private OtaPackageService otaPackageService;
  327 + @Getter
  328 + private OtaPackageService otaPackageService;
  329 +
  330 + @Lazy
  331 + @Autowired(required = false)
  332 + @Getter
  333 + private TbRpcService tbRpcService;
323 334
324 335 @Value("${actors.session.max_concurrent_sessions_per_device:1}")
325 336 @Getter
... ...
... ... @@ -46,7 +46,7 @@ public class DeviceActor extends ContextAwareActor {
46 46 super.init(ctx);
47 47 log.debug("[{}][{}] Starting device actor.", processor.tenantId, processor.deviceId);
48 48 try {
49   - processor.initSessionTimeout(ctx);
  49 + processor.init(ctx);
50 50 log.debug("[{}][{}] Device actor started.", processor.tenantId, processor.deviceId);
51 51 } catch (Exception e) {
52 52 log.warn("[{}][{}] Unknown failure", processor.tenantId, processor.deviceId, e);
... ...
... ... @@ -23,6 +23,8 @@ import com.google.common.util.concurrent.MoreExecutors;
23 23 import com.google.protobuf.InvalidProtocolBufferException;
24 24 import lombok.extern.slf4j.Slf4j;
25 25 import org.apache.commons.collections.CollectionUtils;
  26 +import org.thingsboard.common.util.JacksonUtil;
  27 +import org.thingsboard.common.util.LinkedHashMapRemoveEldest;
26 28 import org.thingsboard.rule.engine.api.RpcError;
27 29 import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg;
28 30 import org.thingsboard.rule.engine.api.msg.DeviceCredentialsUpdateNotificationMsg;
... ... @@ -38,12 +40,17 @@ import org.thingsboard.server.common.data.edge.EdgeEventActionType;
38 40 import org.thingsboard.server.common.data.edge.EdgeEventType;
39 41 import org.thingsboard.server.common.data.id.DeviceId;
40 42 import org.thingsboard.server.common.data.id.EdgeId;
  43 +import org.thingsboard.server.common.data.id.RpcId;
41 44 import org.thingsboard.server.common.data.id.TenantId;
42 45 import org.thingsboard.server.common.data.kv.AttributeKey;
43 46 import org.thingsboard.server.common.data.kv.AttributeKvEntry;
44 47 import org.thingsboard.server.common.data.kv.KvEntry;
  48 +import org.thingsboard.server.common.data.page.PageData;
  49 +import org.thingsboard.server.common.data.page.PageLink;
45 50 import org.thingsboard.server.common.data.relation.EntityRelation;
46 51 import org.thingsboard.server.common.data.relation.RelationTypeGroup;
  52 +import org.thingsboard.server.common.data.rpc.Rpc;
  53 +import org.thingsboard.server.common.data.rpc.RpcStatus;
47 54 import org.thingsboard.server.common.data.rpc.ToDeviceRpcRequestBody;
48 55 import org.thingsboard.server.common.data.security.DeviceCredentials;
49 56 import org.thingsboard.server.common.data.security.DeviceCredentialsType;
... ... @@ -52,8 +59,8 @@ import org.thingsboard.server.common.msg.TbMsgMetaData;
52 59 import org.thingsboard.server.common.msg.queue.TbCallback;
53 60 import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest;
54 61 import org.thingsboard.server.common.msg.timeout.DeviceActorServerSideRpcTimeoutMsg;
55   -import org.thingsboard.server.gen.transport.TransportProtos;
56 62 import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg;
  63 +import org.thingsboard.server.gen.transport.TransportProtos.ClaimDeviceMsg;
57 64 import org.thingsboard.server.gen.transport.TransportProtos.DeviceSessionsCacheEntry;
58 65 import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeRequestMsg;
59 66 import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeResponseMsg;
... ... @@ -68,10 +75,12 @@ import org.thingsboard.server.gen.transport.TransportProtos.SessionType;
68 75 import org.thingsboard.server.gen.transport.TransportProtos.SubscribeToAttributeUpdatesMsg;
69 76 import org.thingsboard.server.gen.transport.TransportProtos.SubscribeToRPCMsg;
70 77 import org.thingsboard.server.gen.transport.TransportProtos.SubscriptionInfoProto;
  78 +import org.thingsboard.server.gen.transport.TransportProtos.ToDevicePersistedRpcResponseMsg;
71 79 import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcRequestMsg;
72 80 import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcResponseMsg;
73 81 import org.thingsboard.server.gen.transport.TransportProtos.ToServerRpcResponseMsg;
74 82 import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg;
  83 +import org.thingsboard.server.gen.transport.TransportProtos.ToTransportUpdateCredentialsProto;
75 84 import org.thingsboard.server.gen.transport.TransportProtos.TransportToDeviceActorMsg;
76 85 import org.thingsboard.server.gen.transport.TransportProtos.TsKvProto;
77 86 import org.thingsboard.server.service.rpc.FromDeviceRpcResponse;
... ... @@ -88,6 +97,8 @@ import java.util.HashSet;
88 97 import java.util.LinkedHashMap;
89 98 import java.util.List;
90 99 import java.util.Map;
  100 +import java.util.Objects;
  101 +import java.util.Optional;
91 102 import java.util.Set;
92 103 import java.util.UUID;
93 104 import java.util.function.Consumer;
... ... @@ -102,7 +113,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
102 113
103 114 final TenantId tenantId;
104 115 final DeviceId deviceId;
105   - private final Map<UUID, SessionInfoMetaData> sessions;
  116 + final LinkedHashMapRemoveEldest<UUID, SessionInfoMetaData> sessions;
106 117 private final Map<UUID, SessionInfo> attributeSubscriptions;
107 118 private final Map<UUID, SessionInfo> rpcSubscriptions;
108 119 private final Map<Integer, ToDeviceRpcRequestMetadata> toDeviceRpcPendingMap;
... ... @@ -117,16 +128,16 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
117 128 super(systemContext);
118 129 this.tenantId = tenantId;
119 130 this.deviceId = deviceId;
120   - this.sessions = new LinkedHashMap<>();
121 131 this.attributeSubscriptions = new HashMap<>();
122 132 this.rpcSubscriptions = new HashMap<>();
123 133 this.toDeviceRpcPendingMap = new HashMap<>();
  134 + this.sessions = new LinkedHashMapRemoveEldest<>(systemContext.getMaxConcurrentSessionsPerDevice(), this::notifyTransportAboutClosedSessionMaxSessionsLimit);
124 135 if (initAttributes()) {
125 136 restoreSessions();
126 137 }
127 138 }
128 139
129   - private boolean initAttributes() {
  140 + boolean initAttributes() {
130 141 Device device = systemContext.getDeviceService().findDeviceById(tenantId, deviceId);
131 142 if (device != null) {
132 143 this.deviceName = device.getName();
... ... @@ -162,20 +173,19 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
162 173
163 174 void processRpcRequest(TbActorCtx context, ToDeviceRpcRequestActorMsg msg) {
164 175 ToDeviceRpcRequest request = msg.getMsg();
165   - ToDeviceRpcRequestBody body = request.getBody();
166   - ToDeviceRpcRequestMsg rpcRequest = ToDeviceRpcRequestMsg.newBuilder()
167   - .setRequestId(rpcSeq++)
168   - .setMethodName(body.getMethod())
169   - .setParams(body.getParams())
170   - .setExpirationTime(request.getExpirationTime())
171   - .setRequestIdMSB(request.getId().getMostSignificantBits())
172   - .setRequestIdLSB(request.getId().getLeastSignificantBits())
173   - .build();
  176 + ToDeviceRpcRequestMsg rpcRequest = creteToDeviceRpcRequestMsg(request);
174 177
175 178 long timeout = request.getExpirationTime() - System.currentTimeMillis();
  179 + boolean persisted = request.isPersisted();
  180 +
176 181 if (timeout <= 0) {
177 182 log.debug("[{}][{}] Ignoring message due to exp time reached, {}", deviceId, request.getId(), request.getExpirationTime());
  183 + if (persisted) {
  184 + createRpc(request, RpcStatus.TIMEOUT);
  185 + }
178 186 return;
  187 + } else if (persisted) {
  188 + createRpc(request, RpcStatus.QUEUED);
179 189 }
180 190
181 191 boolean sent;
... ... @@ -192,11 +202,17 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
192 202 syncSessionSet.add(key);
193 203 }
194 204 });
195   - log.trace("46) Rpc syncSessionSet [{}] subscription after sent [{}]",syncSessionSet, rpcSubscriptions);
  205 + log.trace("46) Rpc syncSessionSet [{}] subscription after sent [{}]", syncSessionSet, rpcSubscriptions);
196 206 syncSessionSet.forEach(rpcSubscriptions::remove);
197 207 }
198 208
199   - if (request.isOneway() && sent) {
  209 + if (persisted) {
  210 + ObjectNode response = JacksonUtil.newObjectNode();
  211 + response.put("rpcId", request.getId().toString());
  212 + systemContext.getTbCoreDeviceRpcService().processRpcResponseFromDeviceActor(new FromDeviceRpcResponse(msg.getMsg().getId(), JacksonUtil.toString(response), null));
  213 + }
  214 +
  215 + if (!persisted && request.isOneway() && sent) {
200 216 log.debug("[{}] Rpc command response sent [{}]!", deviceId, request.getId());
201 217 systemContext.getTbCoreDeviceRpcService().processRpcResponseFromDeviceActor(new FromDeviceRpcResponse(msg.getMsg().getId(), null, null));
202 218 } else {
... ... @@ -209,6 +225,31 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
209 225 }
210 226 }
211 227
  228 + private Rpc createRpc(ToDeviceRpcRequest request, RpcStatus status) {
  229 + Rpc rpc = new Rpc(new RpcId(request.getId()));
  230 + rpc.setCreatedTime(System.currentTimeMillis());
  231 + rpc.setTenantId(tenantId);
  232 + rpc.setDeviceId(deviceId);
  233 + rpc.setExpirationTime(request.getExpirationTime());
  234 + rpc.setRequest(JacksonUtil.valueToTree(request));
  235 + rpc.setStatus(status);
  236 + return systemContext.getTbRpcService().save(tenantId, rpc);
  237 + }
  238 +
  239 + private ToDeviceRpcRequestMsg creteToDeviceRpcRequestMsg(ToDeviceRpcRequest request) {
  240 + ToDeviceRpcRequestBody body = request.getBody();
  241 + return ToDeviceRpcRequestMsg.newBuilder()
  242 + .setRequestId(rpcSeq++)
  243 + .setMethodName(body.getMethod())
  244 + .setParams(body.getParams())
  245 + .setExpirationTime(request.getExpirationTime())
  246 + .setRequestIdMSB(request.getId().getMostSignificantBits())
  247 + .setRequestIdLSB(request.getId().getLeastSignificantBits())
  248 + .setOneway(request.isOneway())
  249 + .setPersisted(request.isPersisted())
  250 + .build();
  251 + }
  252 +
212 253 void processRpcResponsesFromEdge(TbActorCtx context, FromDeviceRpcResponseActorMsg responseMsg) {
213 254 log.debug("[{}] Processing rpc command response from edge session", deviceId);
214 255 ToDeviceRpcRequestMetadata requestMd = toDeviceRpcPendingMap.remove(responseMsg.getRequestId());
... ... @@ -230,6 +271,9 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
230 271 ToDeviceRpcRequestMetadata requestMd = toDeviceRpcPendingMap.remove(msg.getId());
231 272 if (requestMd != null) {
232 273 log.debug("[{}] RPC request [{}] timeout detected!", deviceId, msg.getId());
  274 + if (requestMd.getMsg().getMsg().isPersisted()) {
  275 + systemContext.getTbRpcService().save(tenantId, new RpcId(requestMd.getMsg().getMsg().getId()), RpcStatus.TIMEOUT, null);
  276 + }
233 277 systemContext.getTbCoreDeviceRpcService().processRpcResponseFromDeviceActor(new FromDeviceRpcResponse(requestMd.getMsg().getMsg().getId(),
234 278 null, requestMd.isSent() ? RpcError.TIMEOUT : RpcError.NO_ACTIVE_CONNECTION));
235 279 }
... ... @@ -253,7 +297,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
253 297 toDeviceRpcPendingMap.entrySet().stream().findFirst().ifPresent(processPendingRpc(context, sessionId, sessionInfo.getNodeId(), sentOneWayIds));
254 298 }
255 299
256   - sentOneWayIds.forEach(toDeviceRpcPendingMap::remove);
  300 + sentOneWayIds.stream().filter(id -> !toDeviceRpcPendingMap.get(id).getMsg().getMsg().isPersisted()).forEach(toDeviceRpcPendingMap::remove);
257 301 }
258 302
259 303 private Consumer<Map.Entry<Integer, ToDeviceRpcRequestMetadata>> processPendingRpc(TbActorCtx context, UUID sessionId, String nodeId, Set<Integer> sentOneWayIds) {
... ... @@ -271,7 +315,10 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
271 315 .setExpirationTime(request.getExpirationTime())
272 316 .setRequestIdMSB(request.getId().getMostSignificantBits())
273 317 .setRequestIdLSB(request.getId().getLeastSignificantBits())
  318 + .setOneway(request.isOneway())
  319 + .setPersisted(request.isPersisted())
274 320 .build();
  321 +
275 322 sendToTransport(rpcRequest, sessionId, nodeId);
276 323 };
277 324 }
... ... @@ -279,31 +326,39 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
279 326 void process(TbActorCtx context, TransportToDeviceActorMsgWrapper wrapper) {
280 327 TransportToDeviceActorMsg msg = wrapper.getMsg();
281 328 TbCallback callback = wrapper.getCallback();
  329 + var sessionInfo = msg.getSessionInfo();
  330 +
282 331 if (msg.hasSessionEvent()) {
283   - processSessionStateMsgs(msg.getSessionInfo(), msg.getSessionEvent());
  332 + processSessionStateMsgs(sessionInfo, msg.getSessionEvent());
284 333 }
285 334 if (msg.hasSubscribeToAttributes()) {
286   - processSubscriptionCommands(context, msg.getSessionInfo(), msg.getSubscribeToAttributes());
  335 + processSubscriptionCommands(context, sessionInfo, msg.getSubscribeToAttributes());
287 336 }
288 337 if (msg.hasSubscribeToRPC()) {
289   - processSubscriptionCommands(context, msg.getSessionInfo(), msg.getSubscribeToRPC());
  338 + processSubscriptionCommands(context, sessionInfo, msg.getSubscribeToRPC());
  339 + }
  340 + if (msg.hasSendPendingRPC()) {
  341 + sendPendingRequests(context, getSessionId(sessionInfo), sessionInfo);
290 342 }
291 343 if (msg.hasGetAttributes()) {
292   - handleGetAttributesRequest(context, msg.getSessionInfo(), msg.getGetAttributes());
  344 + handleGetAttributesRequest(context, sessionInfo, msg.getGetAttributes());
293 345 }
294 346 if (msg.hasToDeviceRPCCallResponse()) {
295   - processRpcResponses(context, msg.getSessionInfo(), msg.getToDeviceRPCCallResponse());
  347 + processRpcResponses(context, sessionInfo, msg.getToDeviceRPCCallResponse());
296 348 }
297 349 if (msg.hasSubscriptionInfo()) {
298   - handleSessionActivity(context, msg.getSessionInfo(), msg.getSubscriptionInfo());
  350 + handleSessionActivity(context, sessionInfo, msg.getSubscriptionInfo());
299 351 }
300 352 if (msg.hasClaimDevice()) {
301   - handleClaimDeviceMsg(context, msg.getSessionInfo(), msg.getClaimDevice());
  353 + handleClaimDeviceMsg(context, sessionInfo, msg.getClaimDevice());
  354 + }
  355 + if (msg.hasPersistedRpcResponseMsg()) {
  356 + processPersistedRpcResponses(context, sessionInfo, msg.getPersistedRpcResponseMsg());
302 357 }
303 358 callback.onSuccess();
304 359 }
305 360
306   - private void handleClaimDeviceMsg(TbActorCtx context, SessionInfoProto sessionInfo, TransportProtos.ClaimDeviceMsg msg) {
  361 + private void handleClaimDeviceMsg(TbActorCtx context, SessionInfoProto sessionInfo, ClaimDeviceMsg msg) {
307 362 DeviceId deviceId = new DeviceId(new UUID(msg.getDeviceIdMSB(), msg.getDeviceIdLSB()));
308 363 systemContext.getClaimDevicesService().registerClaimingInfo(tenantId, deviceId, msg.getSecretKey(), msg.getDurationMs());
309 364 }
... ... @@ -442,11 +497,19 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
442 497 if (success) {
443 498 systemContext.getTbCoreDeviceRpcService().processRpcResponseFromDeviceActor(new FromDeviceRpcResponse(requestMd.getMsg().getMsg().getId(),
444 499 responseMsg.getPayload(), null));
  500 + if (requestMd.getMsg().getMsg().isPersisted()) {
  501 + systemContext.getTbRpcService().save(tenantId, new RpcId(requestMd.getMsg().getMsg().getId()), RpcStatus.SUCCESSFUL, JacksonUtil.toJsonNode(responseMsg.getPayload()));
  502 + }
445 503 } else {
446 504 log.debug("[{}] Rpc command response [{}] is stale!", deviceId, responseMsg.getRequestId());
447 505 }
448 506 }
449 507
  508 + private void processPersistedRpcResponses(TbActorCtx context, SessionInfoProto sessionInfo, ToDevicePersistedRpcResponseMsg responseMsg) {
  509 + UUID rpcId = new UUID(responseMsg.getRequestIdMSB(), responseMsg.getRequestIdLSB());
  510 + systemContext.getTbRpcService().save(tenantId, new RpcId(rpcId), RpcStatus.valueOf(responseMsg.getStatus()), null);
  511 + }
  512 +
450 513 private void processSubscriptionCommands(TbActorCtx context, SessionInfoProto sessionInfo, SubscribeToAttributeUpdatesMsg subscribeCmd) {
451 514 UUID sessionId = getSessionId(sessionInfo);
452 515 if (subscribeCmd.getUnsubscribe()) {
... ... @@ -488,18 +551,14 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
488 551
489 552 private void processSessionStateMsgs(SessionInfoProto sessionInfo, SessionEventMsg msg) {
490 553 UUID sessionId = getSessionId(sessionInfo);
  554 + Objects.requireNonNull(sessionId);
491 555 if (msg.getEvent() == SessionEvent.OPEN) {
492 556 if (sessions.containsKey(sessionId)) {
493 557 log.debug("[{}] Received duplicate session open event [{}]", deviceId, sessionId);
494 558 return;
495 559 }
496   - log.debug("[{}] Processing new session [{}]", deviceId, sessionId);
497   - if (sessions.size() >= systemContext.getMaxConcurrentSessionsPerDevice()) {
498   - UUID sessionIdToRemove = sessions.keySet().stream().findFirst().orElse(null);
499   - if (sessionIdToRemove != null) {
500   - notifyTransportAboutClosedSession(sessionIdToRemove, sessions.remove(sessionIdToRemove), "max concurrent sessions limit reached per device!");
501   - }
502   - }
  560 + log.info("[{}] Processing new session [{}]. Current sessions size {}", deviceId, sessionId, sessions.size());
  561 +
503 562 sessions.put(sessionId, new SessionInfoMetaData(new SessionInfo(SessionType.ASYNC, sessionInfo.getNodeId())));
504 563 if (sessions.size() == 1) {
505 564 reportSessionOpen();
... ... @@ -520,8 +579,10 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
520 579
521 580 private void handleSessionActivity(TbActorCtx context, SessionInfoProto sessionInfoProto, SubscriptionInfoProto subscriptionInfo) {
522 581 UUID sessionId = getSessionId(sessionInfoProto);
  582 + Objects.requireNonNull(sessionId);
  583 +
523 584 SessionInfoMetaData sessionMD = sessions.computeIfAbsent(sessionId,
524   - id -> new SessionInfoMetaData(new SessionInfo(SessionType.ASYNC, sessionInfoProto.getNodeId()), 0L));
  585 + id -> new SessionInfoMetaData(new SessionInfo(SessionType.ASYNC, sessionInfoProto.getNodeId()), subscriptionInfo.getLastActivityTime()));
525 586
526 587 sessionMD.setLastActivityTime(subscriptionInfo.getLastActivityTime());
527 588 sessionMD.setSubscribedToAttributes(subscriptionInfo.getAttributeSubscription());
... ... @@ -551,6 +612,11 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
551 612 }
552 613 }
553 614
  615 + private void notifyTransportAboutClosedSessionMaxSessionsLimit(UUID sessionId, SessionInfoMetaData sessionMd) {
  616 + notifyTransportAboutClosedSession(sessionId, sessionMd, "max concurrent sessions limit reached per device!");
  617 + }
  618 +
  619 +
554 620 private void notifyTransportAboutClosedSession(UUID sessionId, SessionInfoMetaData sessionMd, String message) {
555 621 SessionCloseNotificationProto sessionCloseNotificationProto = SessionCloseNotificationProto
556 622 .newBuilder()
... ... @@ -565,7 +631,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
565 631
566 632 void notifyTransportAboutProfileUpdate(UUID sessionId, SessionInfoMetaData sessionMd, DeviceCredentials deviceCredentials) {
567 633 log.info("2) LwM2Mtype: ");
568   - TransportProtos.ToTransportUpdateCredentialsProto.Builder notification = TransportProtos.ToTransportUpdateCredentialsProto.newBuilder();
  634 + ToTransportUpdateCredentialsProto.Builder notification = ToTransportUpdateCredentialsProto.newBuilder();
569 635 notification.addCredentialsId(deviceCredentials.getCredentialsId());
570 636 notification.addCredentialsValue(deviceCredentials.getCredentialsValue());
571 637 ToTransportMsg msg = ToTransportMsg.newBuilder()
... ... @@ -640,7 +706,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
640 706 ListenableFuture<EdgeEvent> future = systemContext.getEdgeEventService().saveAsync(edgeEvent);
641 707 Futures.addCallback(future, new FutureCallback<EdgeEvent>() {
642 708 @Override
643   - public void onSuccess( EdgeEvent result) {
  709 + public void onSuccess(EdgeEvent result) {
644 710 systemContext.getClusterService().onEdgeEventUpdate(tenantId, edgeId);
645 711 }
646 712
... ... @@ -697,7 +763,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
697 763 return builder.build();
698 764 }
699 765
700   - private void restoreSessions() {
  766 + void restoreSessions() {
701 767 log.debug("[{}] Restoring sessions from cache", deviceId);
702 768 DeviceSessionsCacheEntry sessionsDump = null;
703 769 try {
... ... @@ -710,6 +776,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
710 776 log.debug("[{}] No session information found", deviceId);
711 777 return;
712 778 }
  779 + // TODO: Take latest max allowed sessions size from cache
713 780 for (SessionSubscriptionInfoProto sessionSubscriptionInfoProto : sessionsDump.getSessionsList()) {
714 781 SessionInfoProto sessionInfoProto = sessionSubscriptionInfoProto.getSessionInfo();
715 782 UUID sessionId = getSessionId(sessionInfoProto);
... ... @@ -756,11 +823,30 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
756 823 .addAllSessions(sessionsList).build().toByteArray());
757 824 }
758 825
759   - void initSessionTimeout(TbActorCtx ctx) {
  826 + void init(TbActorCtx ctx) {
760 827 schedulePeriodicMsgWithDelay(ctx, SessionTimeoutCheckMsg.instance(), systemContext.getSessionReportTimeout(), systemContext.getSessionReportTimeout());
  828 + PageLink pageLink = new PageLink(1024);
  829 + PageData<Rpc> pageData;
  830 + do {
  831 + pageData = systemContext.getTbRpcService().findAllByDeviceIdAndStatus(tenantId, deviceId, RpcStatus.QUEUED, pageLink);
  832 + pageData.getData().forEach(rpc -> {
  833 + ToDeviceRpcRequest msg = JacksonUtil.convertValue(rpc.getRequest(), ToDeviceRpcRequest.class);
  834 + long timeout = rpc.getExpirationTime() - System.currentTimeMillis();
  835 + if (timeout <= 0) {
  836 + rpc.setStatus(RpcStatus.TIMEOUT);
  837 + systemContext.getTbRpcService().save(tenantId, rpc);
  838 + } else {
  839 + registerPendingRpcRequest(ctx, new ToDeviceRpcRequestActorMsg(systemContext.getServiceId(), msg), false, creteToDeviceRpcRequestMsg(msg), timeout);
  840 + }
  841 + });
  842 + if (pageData.hasNext()) {
  843 + pageLink = pageLink.nextPageLink();
  844 + }
  845 + } while (pageData.hasNext());
761 846 }
762 847
763 848 void checkSessionsTimeout() {
  849 + log.debug("[{}] checkSessionsTimeout started. Size before check {}", deviceId, sessions.size());
764 850 long expTime = System.currentTimeMillis() - systemContext.getSessionInactivityTimeout();
765 851 Map<UUID, SessionInfoMetaData> sessionsToRemove = sessions.entrySet().stream().filter(kv -> kv.getValue().getLastActivityTime() < expTime).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
766 852 sessionsToRemove.forEach((sessionId, sessionMD) -> {
... ... @@ -772,6 +858,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
772 858 if (!sessionsToRemove.isEmpty()) {
773 859 dumpSessions();
774 860 }
  861 + log.debug("[{}] checkSessionsTimeout finished. Size after check {}", deviceId, sessions.size());
775 862 }
776 863
777 864 }
... ...
... ... @@ -25,5 +25,4 @@ import org.thingsboard.server.gen.transport.TransportProtos.SessionType;
25 25 public class SessionInfo {
26 26 private final SessionType type;
27 27 private final String nodeId;
28   - private long lastActivityTime;
29 28 }
... ...
... ... @@ -245,11 +245,11 @@ public class TenantActor extends RuleChainManagerActor {
245 245 EdgeId edgeId = new EdgeId(msg.getEntityId().getId());
246 246 EdgeRpcService edgeRpcService = systemContext.getEdgeRpcService();
247 247 if (msg.getEvent() == ComponentLifecycleEvent.DELETED) {
248   - edgeRpcService.deleteEdge(edgeId);
  248 + edgeRpcService.deleteEdge(tenantId, edgeId);
249 249 } else {
250 250 Edge edge = systemContext.getEdgeService().findEdgeById(tenantId, edgeId);
251 251 if (msg.getEvent() == ComponentLifecycleEvent.UPDATED) {
252   - edgeRpcService.updateEdge(edge);
  252 + edgeRpcService.updateEdge(tenantId, edge);
253 253 }
254 254 }
255 255 } else if (isRuleEngineForCurrentTenant) {
... ... @@ -277,7 +277,7 @@ public class TenantActor extends RuleChainManagerActor {
277 277
278 278 private void onToEdgeSessionMsg(EdgeEventUpdateMsg msg) {
279 279 log.trace("[{}] onToEdgeSessionMsg [{}]", msg.getTenantId(), msg);
280   - systemContext.getEdgeRpcService().onEdgeEvent(msg.getEdgeId());
  280 + systemContext.getEdgeRpcService().onEdgeEvent(tenantId, msg.getEdgeId());
281 281 }
282 282
283 283 public static class ActorCreator extends ContextBasedCreator {
... ...
... ... @@ -135,7 +135,7 @@ public class AuthController extends BaseController {
135 135 }
136 136 }
137 137
138   - @RequestMapping(value = "/noauth/activate", params = { "activateToken" }, method = RequestMethod.GET)
  138 + @RequestMapping(value = "/noauth/activate", params = {"activateToken"}, method = RequestMethod.GET)
139 139 public ResponseEntity<String> checkActivateToken(
140 140 @RequestParam(value = "activateToken") String activateToken) {
141 141 HttpHeaders headers = new HttpHeaders();
... ... @@ -159,7 +159,7 @@ public class AuthController extends BaseController {
159 159
160 160 @RequestMapping(value = "/noauth/resetPasswordByEmail", method = RequestMethod.POST)
161 161 @ResponseStatus(value = HttpStatus.OK)
162   - public void requestResetPasswordByEmail (
  162 + public void requestResetPasswordByEmail(
163 163 @RequestBody JsonNode resetPasswordByEmailRequest,
164 164 HttpServletRequest request) throws ThingsboardException {
165 165 try {
... ... @@ -170,13 +170,13 @@ public class AuthController extends BaseController {
170 170 String resetUrl = String.format("%s/api/noauth/resetPassword?resetToken=%s", baseUrl,
171 171 userCredentials.getResetToken());
172 172
173   - mailService.sendResetPasswordEmail(resetUrl, email);
  173 + mailService.sendResetPasswordEmailAsync(resetUrl, email);
174 174 } catch (Exception e) {
175   - throw handleException(e);
  175 + log.warn("Error occurred: {}", e.getMessage());
176 176 }
177 177 }
178 178
179   - @RequestMapping(value = "/noauth/resetPassword", params = { "resetToken" }, method = RequestMethod.GET)
  179 + @RequestMapping(value = "/noauth/resetPassword", params = {"resetToken"}, method = RequestMethod.GET)
180 180 public ResponseEntity<String> checkResetToken(
181 181 @RequestParam(value = "resetToken") String resetToken) {
182 182 HttpHeaders headers = new HttpHeaders();
... ...
... ... @@ -37,12 +37,12 @@ import org.thingsboard.server.common.data.EdgeUtils;
37 37 import org.thingsboard.server.common.data.EntityType;
38 38 import org.thingsboard.server.common.data.EntityView;
39 39 import org.thingsboard.server.common.data.EntityViewInfo;
40   -import org.thingsboard.server.common.data.OtaPackage;
41   -import org.thingsboard.server.common.data.OtaPackageInfo;
42 40 import org.thingsboard.server.common.data.HasName;
43 41 import org.thingsboard.server.common.data.HasTenantId;
44   -import org.thingsboard.server.common.data.TbResourceInfo;
  42 +import org.thingsboard.server.common.data.OtaPackage;
  43 +import org.thingsboard.server.common.data.OtaPackageInfo;
45 44 import org.thingsboard.server.common.data.TbResource;
  45 +import org.thingsboard.server.common.data.TbResourceInfo;
46 46 import org.thingsboard.server.common.data.Tenant;
47 47 import org.thingsboard.server.common.data.TenantInfo;
48 48 import org.thingsboard.server.common.data.TenantProfile;
... ... @@ -69,20 +69,23 @@ import org.thingsboard.server.common.data.id.EntityId;
69 69 import org.thingsboard.server.common.data.id.EntityIdFactory;
70 70 import org.thingsboard.server.common.data.id.EntityViewId;
71 71 import org.thingsboard.server.common.data.id.OtaPackageId;
72   -import org.thingsboard.server.common.data.id.TbResourceId;
  72 +import org.thingsboard.server.common.data.id.RpcId;
73 73 import org.thingsboard.server.common.data.id.RuleChainId;
74 74 import org.thingsboard.server.common.data.id.RuleNodeId;
  75 +import org.thingsboard.server.common.data.id.TbResourceId;
75 76 import org.thingsboard.server.common.data.id.TenantId;
76 77 import org.thingsboard.server.common.data.id.TenantProfileId;
77 78 import org.thingsboard.server.common.data.id.UserId;
78 79 import org.thingsboard.server.common.data.id.WidgetTypeId;
79 80 import org.thingsboard.server.common.data.id.WidgetsBundleId;
  81 +import org.thingsboard.server.common.data.page.PageDataIterableByTenantIdEntityId;
80 82 import org.thingsboard.server.common.data.page.PageLink;
81 83 import org.thingsboard.server.common.data.page.SortOrder;
82 84 import org.thingsboard.server.common.data.page.TimePageLink;
83 85 import org.thingsboard.server.common.data.plugin.ComponentDescriptor;
84 86 import org.thingsboard.server.common.data.plugin.ComponentType;
85 87 import org.thingsboard.server.common.data.relation.EntityRelation;
  88 +import org.thingsboard.server.common.data.rpc.Rpc;
86 89 import org.thingsboard.server.common.data.rule.RuleChain;
87 90 import org.thingsboard.server.common.data.rule.RuleChainType;
88 91 import org.thingsboard.server.common.data.rule.RuleNode;
... ... @@ -101,11 +104,12 @@ import org.thingsboard.server.dao.edge.EdgeService;
101 104 import org.thingsboard.server.dao.entityview.EntityViewService;
102 105 import org.thingsboard.server.dao.exception.DataValidationException;
103 106 import org.thingsboard.server.dao.exception.IncorrectParameterException;
104   -import org.thingsboard.server.dao.ota.OtaPackageService;
105 107 import org.thingsboard.server.dao.model.ModelConstants;
106 108 import org.thingsboard.server.dao.oauth2.OAuth2ConfigTemplateService;
107 109 import org.thingsboard.server.dao.oauth2.OAuth2Service;
  110 +import org.thingsboard.server.dao.ota.OtaPackageService;
108 111 import org.thingsboard.server.dao.relation.RelationService;
  112 +import org.thingsboard.server.dao.rpc.RpcService;
109 113 import org.thingsboard.server.dao.rule.RuleChainService;
110 114 import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
111 115 import org.thingsboard.server.dao.tenant.TenantProfileService;
... ... @@ -120,11 +124,10 @@ import org.thingsboard.server.queue.provider.TbQueueProducerProvider;
120 124 import org.thingsboard.server.queue.util.TbCoreComponent;
121 125 import org.thingsboard.server.service.action.RuleEngineEntityActionService;
122 126 import org.thingsboard.server.service.component.ComponentDiscoveryService;
123   -import org.thingsboard.server.service.ota.OtaPackageStateService;
124 127 import org.thingsboard.server.service.edge.EdgeNotificationService;
125   -import org.thingsboard.server.service.edge.rpc.EdgeGrpcService;
126   -import org.thingsboard.server.service.edge.rpc.init.SyncEdgeService;
  128 +import org.thingsboard.server.service.edge.rpc.EdgeRpcService;
127 129 import org.thingsboard.server.service.lwm2m.LwM2MServerSecurityInfoRepository;
  130 +import org.thingsboard.server.service.ota.OtaPackageStateService;
128 131 import org.thingsboard.server.service.profile.TbDeviceProfileCache;
129 132 import org.thingsboard.server.service.queue.TbClusterService;
130 133 import org.thingsboard.server.service.resource.TbResourceService;
... ... @@ -138,6 +141,8 @@ import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService;
138 141
139 142 import javax.mail.MessagingException;
140 143 import javax.servlet.http.HttpServletResponse;
  144 +import java.util.ArrayList;
  145 +import java.util.Collections;
141 146 import java.util.List;
142 147 import java.util.Optional;
143 148 import java.util.Set;
... ... @@ -153,6 +158,8 @@ public abstract class BaseController {
153 158 protected static final String DEFAULT_DASHBOARD = "defaultDashboardId";
154 159 protected static final String HOME_DASHBOARD = "homeDashboardId";
155 160
  161 + private static final int DEFAULT_PAGE_SIZE = 1000;
  162 +
156 163 private static final ObjectMapper json = new ObjectMapper();
157 164
158 165 @Autowired
... ... @@ -246,6 +253,9 @@ public abstract class BaseController {
246 253 protected OtaPackageStateService otaPackageStateService;
247 254
248 255 @Autowired
  256 + protected RpcService rpcService;
  257 +
  258 + @Autowired
249 259 protected TbQueueProducerProvider producerProvider;
250 260
251 261 @Autowired
... ... @@ -264,10 +274,7 @@ public abstract class BaseController {
264 274 protected EdgeNotificationService edgeNotificationService;
265 275
266 276 @Autowired(required = false)
267   - protected SyncEdgeService syncEdgeService;
268   -
269   - @Autowired(required = false)
270   - protected EdgeGrpcService edgeGrpcService;
  277 + protected EdgeRpcService edgeGrpcService;
271 278
272 279 @Autowired
273 280 protected RuleEngineEntityActionService ruleEngineEntityActionService;
... ... @@ -786,6 +793,18 @@ public abstract class BaseController {
786 793 }
787 794 }
788 795
  796 + Rpc checkRpcId(RpcId rpcId, Operation operation) throws ThingsboardException {
  797 + try {
  798 + validateId(rpcId, "Incorrect rpcId " + rpcId);
  799 + Rpc rpc = rpcService.findById(getCurrentUser().getTenantId(), rpcId);
  800 + checkNotNull(rpc);
  801 + accessControlService.checkPermission(getCurrentUser(), Resource.RPC, operation, rpcId, rpc);
  802 + return rpc;
  803 + } catch (Exception e) {
  804 + throw handleException(e, false);
  805 + }
  806 + }
  807 +
789 808 @SuppressWarnings("unchecked")
790 809 protected <I extends EntityId> I emptyId(EntityType entityType) {
791 810 return (I) EntityIdFactory.getByTypeAndUuid(entityType, ModelConstants.NULL_UUID);
... ... @@ -899,11 +918,14 @@ public abstract class BaseController {
899 918 if (!edgesEnabled) {
900 919 return null;
901 920 }
902   - List<EdgeId> result = null;
903   - try {
904   - result = edgeService.findRelatedEdgeIdsByEntityId(tenantId, entityId).get();
905   - } catch (Exception e) {
906   - log.error("[{}] can't find related edge ids for entity [{}]", tenantId, entityId, e);
  921 + if (EntityType.EDGE.equals(entityId.getEntityType())) {
  922 + return Collections.singletonList(new EdgeId(entityId.getId()));
  923 + }
  924 + PageDataIterableByTenantIdEntityId<EdgeId> relatedEdgeIdsIterator =
  925 + new PageDataIterableByTenantIdEntityId<>(edgeService::findRelatedEdgeIdsByEntityId, tenantId, entityId, DEFAULT_PAGE_SIZE);
  926 + List<EdgeId> result = new ArrayList<>();
  927 + for(EdgeId edgeId : relatedEdgeIdsIterator) {
  928 + result.add(edgeId);
907 929 }
908 930 return result;
909 931 }
... ...
... ... @@ -782,15 +782,17 @@ public class DeviceController extends BaseController {
782 782 }
783 783
784 784 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
785   - @RequestMapping(value = "/devices/count/{otaPackageType}", method = RequestMethod.GET)
  785 + @RequestMapping(value = "/devices/count/{otaPackageType}/{deviceProfileId}", method = RequestMethod.GET)
786 786 @ResponseBody
787   - public Long countDevicesByTenantIdAndDeviceProfileIdAndEmptyOtaPackage(@PathVariable("otaPackageType") String otaPackageType,
788   - @RequestParam String deviceProfileId) throws ThingsboardException {
  787 + public Long countByDeviceProfileAndEmptyOtaPackage(@PathVariable("otaPackageType") String otaPackageType,
  788 + @PathVariable("deviceProfileId") String deviceProfileId) throws ThingsboardException {
789 789 checkParameter("OtaPackageType", otaPackageType);
790 790 checkParameter("DeviceProfileId", deviceProfileId);
791 791 try {
792 792 return deviceService.countDevicesByTenantIdAndDeviceProfileIdAndEmptyOtaPackage(
793   - getCurrentUser().getTenantId(), new DeviceProfileId(UUID.fromString(deviceProfileId)), OtaPackageType.valueOf(otaPackageType));
  793 + getTenantId(),
  794 + new DeviceProfileId(UUID.fromString(deviceProfileId)),
  795 + OtaPackageType.valueOf(otaPackageType));
794 796 } catch (Exception e) {
795 797 throw handleException(e);
796 798 }
... ...
... ... @@ -49,7 +49,6 @@ import org.thingsboard.server.dao.exception.DataValidationException;
49 49 import org.thingsboard.server.dao.exception.IncorrectParameterException;
50 50 import org.thingsboard.server.dao.model.ModelConstants;
51 51 import org.thingsboard.server.queue.util.TbCoreComponent;
52   -import org.thingsboard.server.service.edge.rpc.EdgeGrpcSession;
53 52 import org.thingsboard.server.service.security.model.SecurityUser;
54 53 import org.thingsboard.server.service.security.permission.Operation;
55 54 import org.thingsboard.server.service.security.permission.Resource;
... ... @@ -128,7 +127,7 @@ public class EdgeController extends BaseController {
128 127 accessControlService.checkPermission(getCurrentUser(), Resource.EDGE, operation,
129 128 edge.getId(), edge);
130 129
131   - Edge savedEdge = checkNotNull(edgeService.saveEdge(edge));
  130 + Edge savedEdge = checkNotNull(edgeService.saveEdge(edge, true));
132 131
133 132 if (created) {
134 133 ruleChainService.assignRuleChainToEdge(tenantId, edgeTemplateRootRuleChain.getId(), savedEdge.getId());
... ... @@ -536,9 +535,7 @@ public class EdgeController extends BaseController {
536 535 edgeId = checkNotNull(edgeId);
537 536 SecurityUser user = getCurrentUser();
538 537 TenantId tenantId = user.getTenantId();
539   - EdgeGrpcSession session = edgeGrpcService.getEdgeGrpcSessionById(tenantId, edgeId);
540   - Edge edge = session.getEdge();
541   - syncEdgeService.sync(tenantId, edge);
  538 + edgeGrpcService.startSyncProcess(tenantId, edgeId);
542 539 } else {
543 540 throw new ThingsboardException("Edges support disabled", ThingsboardErrorCode.GENERAL);
544 541 }
... ...
... ... @@ -32,12 +32,13 @@ import org.springframework.web.multipart.MultipartFile;
32 32 import org.thingsboard.server.common.data.EntityType;
33 33 import org.thingsboard.server.common.data.OtaPackage;
34 34 import org.thingsboard.server.common.data.OtaPackageInfo;
  35 +import org.thingsboard.server.common.data.SaveOtaPackageInfoRequest;
35 36 import org.thingsboard.server.common.data.audit.ActionType;
36 37 import org.thingsboard.server.common.data.exception.ThingsboardException;
37   -import org.thingsboard.server.common.data.ota.ChecksumAlgorithm;
38   -import org.thingsboard.server.common.data.ota.OtaPackageType;
39 38 import org.thingsboard.server.common.data.id.DeviceProfileId;
40 39 import org.thingsboard.server.common.data.id.OtaPackageId;
  40 +import org.thingsboard.server.common.data.ota.ChecksumAlgorithm;
  41 +import org.thingsboard.server.common.data.ota.OtaPackageType;
41 42 import org.thingsboard.server.common.data.page.PageData;
42 43 import org.thingsboard.server.common.data.page.PageLink;
43 44 import org.thingsboard.server.queue.util.TbCoreComponent;
... ... @@ -109,12 +110,12 @@ public class OtaPackageController extends BaseController {
109 110 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
110 111 @RequestMapping(value = "/otaPackage", method = RequestMethod.POST)
111 112 @ResponseBody
112   - public OtaPackageInfo saveOtaPackageInfo(@RequestBody OtaPackageInfo otaPackageInfo) throws ThingsboardException {
  113 + public OtaPackageInfo saveOtaPackageInfo(@RequestBody SaveOtaPackageInfoRequest otaPackageInfo) throws ThingsboardException {
113 114 boolean created = otaPackageInfo.getId() == null;
114 115 try {
115 116 otaPackageInfo.setTenantId(getTenantId());
116 117 checkEntity(otaPackageInfo.getId(), otaPackageInfo, Resource.OTA_PACKAGE);
117   - OtaPackageInfo savedOtaPackageInfo = otaPackageService.saveOtaPackageInfo(otaPackageInfo);
  118 + OtaPackageInfo savedOtaPackageInfo = otaPackageService.saveOtaPackageInfo(new OtaPackageInfo(otaPackageInfo), otaPackageInfo.isUsesUrl());
118 119 logEntityAction(savedOtaPackageInfo.getId(), savedOtaPackageInfo,
119 120 null, created ? ActionType.ADDED : ActionType.UPDATED, null);
120 121 return savedOtaPackageInfo;
... ...
... ... @@ -29,17 +29,24 @@ import org.springframework.web.bind.annotation.PathVariable;
29 29 import org.springframework.web.bind.annotation.RequestBody;
30 30 import org.springframework.web.bind.annotation.RequestMapping;
31 31 import org.springframework.web.bind.annotation.RequestMethod;
  32 +import org.springframework.web.bind.annotation.RequestParam;
32 33 import org.springframework.web.bind.annotation.ResponseBody;
33 34 import org.springframework.web.bind.annotation.RestController;
34 35 import org.springframework.web.context.request.async.DeferredResult;
35 36 import org.thingsboard.rule.engine.api.RpcError;
  37 +import org.thingsboard.server.common.data.DataConstants;
36 38 import org.thingsboard.server.common.data.audit.ActionType;
37 39 import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
38 40 import org.thingsboard.server.common.data.exception.ThingsboardException;
39 41 import org.thingsboard.server.common.data.id.DeviceId;
40 42 import org.thingsboard.server.common.data.id.EntityId;
  43 +import org.thingsboard.server.common.data.id.RpcId;
41 44 import org.thingsboard.server.common.data.id.TenantId;
42 45 import org.thingsboard.server.common.data.id.UUIDBased;
  46 +import org.thingsboard.server.common.data.page.PageData;
  47 +import org.thingsboard.server.common.data.page.PageLink;
  48 +import org.thingsboard.server.common.data.rpc.Rpc;
  49 +import org.thingsboard.server.common.data.rpc.RpcStatus;
43 50 import org.thingsboard.server.common.data.rpc.ToDeviceRpcRequestBody;
44 51 import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest;
45 52 import org.thingsboard.server.queue.util.TbCoreComponent;
... ... @@ -93,6 +100,52 @@ public class RpcController extends BaseController {
93 100 return handleDeviceRPCRequest(false, new DeviceId(UUID.fromString(deviceIdStr)), requestBody);
94 101 }
95 102
  103 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
  104 + @RequestMapping(value = "/persistent/{rpcId}", method = RequestMethod.GET)
  105 + @ResponseBody
  106 + public Rpc getPersistedRpc(@PathVariable("rpcId") String strRpc) throws ThingsboardException {
  107 + checkParameter("RpcId", strRpc);
  108 + try {
  109 + RpcId rpcId = new RpcId(UUID.fromString(strRpc));
  110 + return checkRpcId(rpcId, Operation.READ);
  111 + } catch (Exception e) {
  112 + throw handleException(e);
  113 + }
  114 + }
  115 +
  116 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
  117 + @RequestMapping(value = "/persistent/device/{deviceId}", method = RequestMethod.GET)
  118 + @ResponseBody
  119 + public PageData<Rpc> getPersistedRpcByDevice(@PathVariable("deviceId") String strDeviceId,
  120 + @RequestParam int pageSize,
  121 + @RequestParam int page,
  122 + @RequestParam RpcStatus rpcStatus,
  123 + @RequestParam(required = false) String textSearch,
  124 + @RequestParam(required = false) String sortProperty,
  125 + @RequestParam(required = false) String sortOrder) throws ThingsboardException {
  126 + checkParameter("DeviceId", strDeviceId);
  127 + try {
  128 + TenantId tenantId = getCurrentUser().getTenantId();
  129 + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
  130 + DeviceId deviceId = new DeviceId(UUID.fromString(strDeviceId));
  131 + return checkNotNull(rpcService.findAllByDeviceIdAndStatus(tenantId, deviceId, rpcStatus, pageLink));
  132 + } catch (Exception e) {
  133 + throw handleException(e);
  134 + }
  135 + }
  136 +
  137 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
  138 + @RequestMapping(value = "/persistent/{rpcId}", method = RequestMethod.DELETE)
  139 + @ResponseBody
  140 + public void deleteResource(@PathVariable("rpcId") String strRpc) throws ThingsboardException {
  141 + checkParameter("RpcId", strRpc);
  142 + try {
  143 + rpcService.deleteRpc(getTenantId(), new RpcId(UUID.fromString(strRpc)));
  144 + } catch (Exception e) {
  145 + throw handleException(e);
  146 + }
  147 + }
  148 +
96 149 private DeferredResult<ResponseEntity> handleDeviceRPCRequest(boolean oneWay, DeviceId deviceId, String requestBody) throws ThingsboardException {
97 150 try {
98 151 JsonNode rpcRequestBody = jsonMapper.readTree(requestBody);
... ... @@ -103,6 +156,7 @@ public class RpcController extends BaseController {
103 156 long timeout = rpcRequestBody.has("timeout") ? rpcRequestBody.get("timeout").asLong() : defaultTimeout;
104 157 long expTime = System.currentTimeMillis() + Math.max(minTimeout, timeout);
105 158 UUID rpcRequestUUID = rpcRequestBody.has("requestUUID") ? UUID.fromString(rpcRequestBody.get("requestUUID").asText()) : UUID.randomUUID();
  159 + boolean persisted = rpcRequestBody.has(DataConstants.PERSISTENT) && rpcRequestBody.get(DataConstants.PERSISTENT).asBoolean();
106 160 accessValidator.validate(currentUser, Operation.RPC_CALL, deviceId, new HttpValidationCallback(response, new FutureCallback<DeferredResult<ResponseEntity>>() {
107 161 @Override
108 162 public void onSuccess(@Nullable DeferredResult<ResponseEntity> result) {
... ... @@ -111,7 +165,8 @@ public class RpcController extends BaseController {
111 165 deviceId,
112 166 oneWay,
113 167 expTime,
114   - body
  168 + body,
  169 + persisted
115 170 );
116 171 deviceRpcService.processRestApiRpcRequest(rpcRequest, fromDeviceRpcResponse -> reply(new LocalRequestMetaData(rpcRequest, currentUser, result), fromDeviceRpcResponse), currentUser);
117 172 }
... ...
... ... @@ -50,6 +50,7 @@ import org.thingsboard.server.common.data.id.RuleChainId;
50 50 import org.thingsboard.server.common.data.id.RuleNodeId;
51 51 import org.thingsboard.server.common.data.id.TenantId;
52 52 import org.thingsboard.server.common.data.page.PageData;
  53 +import org.thingsboard.server.common.data.page.PageDataIterableByTenant;
53 54 import org.thingsboard.server.common.data.page.PageLink;
54 55 import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
55 56 import org.thingsboard.server.common.data.rule.DefaultRuleChainCreateRequest;
... ... @@ -70,10 +71,12 @@ import org.thingsboard.server.service.script.RuleNodeJsScriptEngine;
70 71 import org.thingsboard.server.service.security.permission.Operation;
71 72 import org.thingsboard.server.service.security.permission.Resource;
72 73
  74 +import java.util.ArrayList;
73 75 import java.util.List;
74 76 import java.util.Map;
75 77 import java.util.Set;
76 78 import java.util.concurrent.ConcurrentMap;
  79 +import java.util.concurrent.TimeUnit;
77 80 import java.util.stream.Collectors;
78 81
79 82 @Slf4j
... ... @@ -85,7 +88,10 @@ public class RuleChainController extends BaseController {
85 88 public static final String RULE_CHAIN_ID = "ruleChainId";
86 89 public static final String RULE_NODE_ID = "ruleNodeId";
87 90
  91 + private static final int DEFAULT_PAGE_SIZE = 1000;
  92 +
88 93 private static final ObjectMapper objectMapper = new ObjectMapper();
  94 + public static final int TIMEOUT = 20;
89 95
90 96 @Autowired
91 97 private InstallScripts installScripts;
... ... @@ -388,25 +394,25 @@ public class RuleChainController extends BaseController {
388 394 TbMsg inMsg = TbMsg.newMsg(msgType, null, new TbMsgMetaData(metadata), TbMsgDataType.JSON, data);
389 395 switch (scriptType) {
390 396 case "update":
391   - output = msgToOutput(engine.executeUpdate(inMsg));
  397 + output = msgToOutput(engine.executeUpdateAsync(inMsg).get(TIMEOUT, TimeUnit.SECONDS));
392 398 break;
393 399 case "generate":
394   - output = msgToOutput(engine.executeGenerate(inMsg));
  400 + output = msgToOutput(engine.executeGenerateAsync(inMsg).get(TIMEOUT, TimeUnit.SECONDS));
395 401 break;
396 402 case "filter":
397   - boolean result = engine.executeFilter(inMsg);
  403 + boolean result = engine.executeFilterAsync(inMsg).get(TIMEOUT, TimeUnit.SECONDS);
398 404 output = Boolean.toString(result);
399 405 break;
400 406 case "switch":
401   - Set<String> states = engine.executeSwitch(inMsg);
  407 + Set<String> states = engine.executeSwitchAsync(inMsg).get(TIMEOUT, TimeUnit.SECONDS);
402 408 output = objectMapper.writeValueAsString(states);
403 409 break;
404 410 case "json":
405   - JsonNode json = engine.executeJson(inMsg);
  411 + JsonNode json = engine.executeJsonAsync(inMsg).get(TIMEOUT, TimeUnit.SECONDS);
406 412 output = objectMapper.writeValueAsString(json);
407 413 break;
408 414 case "string":
409   - output = engine.executeToString(inMsg);
  415 + output = engine.executeToStringAsync(inMsg).get(TIMEOUT, TimeUnit.SECONDS);
410 416 break;
411 417 default:
412 418 throw new IllegalArgumentException("Unsupported script type: " + scriptType);
... ... @@ -477,7 +483,7 @@ public class RuleChainController extends BaseController {
477 483 return objectMapper.writeValueAsString(resultNode);
478 484 }
479 485
480   - private JsonNode convertMsgToOut(TbMsg msg) throws Exception{
  486 + private JsonNode convertMsgToOut(TbMsg msg) throws Exception {
481 487 ObjectNode msgData = objectMapper.createObjectNode();
482 488 if (!StringUtils.isEmpty(msg.getData())) {
483 489 msgData.set("msg", objectMapper.readTree(msg.getData()));
... ... @@ -632,13 +638,20 @@ public class RuleChainController extends BaseController {
632 638 }
633 639 }
634 640
  641 + // TODO: @voba refactor this - add new config to edge rule chain to set it as auto-assign
635 642 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
636 643 @RequestMapping(value = "/ruleChain/autoAssignToEdgeRuleChains", method = RequestMethod.GET)
637 644 @ResponseBody
638 645 public List<RuleChain> getAutoAssignToEdgeRuleChains() throws ThingsboardException {
639 646 try {
640 647 TenantId tenantId = getCurrentUser().getTenantId();
641   - return checkNotNull(ruleChainService.findAutoAssignToEdgeRuleChainsByTenantId(tenantId)).get();
  648 + List<RuleChain> result = new ArrayList<>();
  649 + PageDataIterableByTenant<RuleChain> autoAssignRuleChainsIterator =
  650 + new PageDataIterableByTenant<>(ruleChainService::findAutoAssignToEdgeRuleChainsByTenantId, tenantId, DEFAULT_PAGE_SIZE);
  651 + for (RuleChain ruleChain : autoAssignRuleChainsIterator) {
  652 + result.add(ruleChain);
  653 + }
  654 + return checkNotNull(result);
642 655 } catch (Exception e) {
643 656 throw handleException(e);
644 657 }
... ...
... ... @@ -95,9 +95,7 @@ public class TenantController extends BaseController {
95 95 tenant = checkNotNull(tenantService.saveTenant(tenant));
96 96 if (newTenant) {
97 97 installScripts.createDefaultRuleChains(tenant.getId());
98   - if (edgesEnabled) {
99   - installScripts.createDefaultEdgeRuleChains(tenant.getId());
100   - }
  98 + installScripts.createDefaultEdgeRuleChains(tenant.getId());
101 99 }
102 100 tenantProfileCache.evict(tenant.getId());
103 101 tbClusterService.onTenantChange(tenant, null);
... ...
... ... @@ -25,9 +25,12 @@ import org.springframework.security.authentication.BadCredentialsException;
25 25 import org.springframework.security.authentication.DisabledException;
26 26 import org.springframework.security.authentication.LockedException;
27 27 import org.springframework.security.core.AuthenticationException;
  28 +import org.springframework.security.core.userdetails.UsernameNotFoundException;
28 29 import org.springframework.security.web.access.AccessDeniedHandler;
29   -import org.springframework.stereotype.Component;
  30 +import org.springframework.web.bind.annotation.ExceptionHandler;
  31 +import org.springframework.web.bind.annotation.RestControllerAdvice;
30 32 import org.springframework.web.client.HttpClientErrorException;
  33 +import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
31 34 import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
32 35 import org.thingsboard.server.common.data.exception.ThingsboardException;
33 36 import org.thingsboard.server.common.msg.tools.TbRateLimitsException;
... ... @@ -40,14 +43,15 @@ import javax.servlet.http.HttpServletRequest;
40 43 import javax.servlet.http.HttpServletResponse;
41 44 import java.io.IOException;
42 45
43   -@Component
44 46 @Slf4j
45   -public class ThingsboardErrorResponseHandler implements AccessDeniedHandler {
  47 +@RestControllerAdvice
  48 +public class ThingsboardErrorResponseHandler extends ResponseEntityExceptionHandler implements AccessDeniedHandler {
46 49
47 50 @Autowired
48 51 private ObjectMapper mapper;
49 52
50 53 @Override
  54 + @ExceptionHandler(AccessDeniedException.class)
51 55 public void handle(HttpServletRequest request, HttpServletResponse response,
52 56 AccessDeniedException accessDeniedException) throws IOException,
53 57 ServletException {
... ... @@ -60,6 +64,7 @@ public class ThingsboardErrorResponseHandler implements AccessDeniedHandler {
60 64 }
61 65 }
62 66
  67 + @ExceptionHandler(Exception.class)
63 68 public void handle(Exception exception, HttpServletResponse response) {
64 69 log.debug("Processing exception {}", exception.getMessage(), exception);
65 70 if (!response.isCommitted()) {
... ... @@ -148,7 +153,7 @@ public class ThingsboardErrorResponseHandler implements AccessDeniedHandler {
148 153
149 154 private void handleAuthenticationException(AuthenticationException authenticationException, HttpServletResponse response) throws IOException {
150 155 response.setStatus(HttpStatus.UNAUTHORIZED.value());
151   - if (authenticationException instanceof BadCredentialsException) {
  156 + if (authenticationException instanceof BadCredentialsException || authenticationException instanceof UsernameNotFoundException) {
152 157 mapper.writeValue(response.getWriter(), ThingsboardErrorResponse.of("Invalid username or password", ThingsboardErrorCode.AUTHENTICATION, HttpStatus.UNAUTHORIZED));
153 158 } else if (authenticationException instanceof DisabledException) {
154 159 mapper.writeValue(response.getWriter(), ThingsboardErrorResponse.of("User account is not active", ThingsboardErrorCode.AUTHENTICATION, HttpStatus.UNAUTHORIZED));
... ... @@ -159,7 +164,7 @@ public class ThingsboardErrorResponseHandler implements AccessDeniedHandler {
159 164 } else if (authenticationException instanceof AuthMethodNotSupportedException) {
160 165 mapper.writeValue(response.getWriter(), ThingsboardErrorResponse.of(authenticationException.getMessage(), ThingsboardErrorCode.AUTHENTICATION, HttpStatus.UNAUTHORIZED));
161 166 } else if (authenticationException instanceof UserPasswordExpiredException) {
162   - UserPasswordExpiredException expiredException = (UserPasswordExpiredException)authenticationException;
  167 + UserPasswordExpiredException expiredException = (UserPasswordExpiredException) authenticationException;
163 168 String resetToken = expiredException.getResetToken();
164 169 mapper.writeValue(response.getWriter(), ThingsboardCredentialsExpiredResponse.of(expiredException.getMessage(), resetToken));
165 170 } else {
... ...
... ... @@ -15,9 +15,7 @@
15 15 */
16 16 package org.thingsboard.server.service.edge;
17 17
18   -import com.fasterxml.jackson.core.JsonProcessingException;
19 18 import com.fasterxml.jackson.databind.JsonNode;
20   -import com.fasterxml.jackson.databind.ObjectMapper;
21 19 import com.google.common.util.concurrent.FutureCallback;
22 20 import com.google.common.util.concurrent.Futures;
23 21 import com.google.common.util.concurrent.ListenableFuture;
... ... @@ -25,45 +23,30 @@ import lombok.extern.slf4j.Slf4j;
25 23 import org.checkerframework.checker.nullness.qual.Nullable;
26 24 import org.springframework.beans.factory.annotation.Autowired;
27 25 import org.springframework.stereotype.Service;
28   -import org.thingsboard.server.common.data.EdgeUtils;
29   -import org.thingsboard.server.common.data.EntityType;
30   -import org.thingsboard.server.common.data.User;
31   -import org.thingsboard.server.common.data.alarm.Alarm;
32 26 import org.thingsboard.server.common.data.edge.Edge;
33 27 import org.thingsboard.server.common.data.edge.EdgeEvent;
34 28 import org.thingsboard.server.common.data.edge.EdgeEventActionType;
35 29 import org.thingsboard.server.common.data.edge.EdgeEventType;
36   -import org.thingsboard.server.common.data.id.AlarmId;
37   -import org.thingsboard.server.common.data.id.CustomerId;
38 30 import org.thingsboard.server.common.data.id.EdgeId;
39 31 import org.thingsboard.server.common.data.id.EntityId;
40   -import org.thingsboard.server.common.data.id.EntityIdFactory;
41 32 import org.thingsboard.server.common.data.id.RuleChainId;
42 33 import org.thingsboard.server.common.data.id.TenantId;
43   -import org.thingsboard.server.common.data.page.PageData;
44   -import org.thingsboard.server.common.data.page.PageLink;
45   -import org.thingsboard.server.common.data.page.TimePageLink;
46   -import org.thingsboard.server.common.data.relation.EntityRelation;
47   -import org.thingsboard.server.common.data.rule.RuleChain;
48   -import org.thingsboard.server.common.data.rule.RuleChainConnectionInfo;
49 34 import org.thingsboard.server.common.msg.queue.TbCallback;
50   -import org.thingsboard.server.dao.alarm.AlarmService;
51 35 import org.thingsboard.server.dao.edge.EdgeEventService;
52 36 import org.thingsboard.server.dao.edge.EdgeService;
53   -import org.thingsboard.server.dao.rule.RuleChainService;
54   -import org.thingsboard.server.dao.user.UserService;
55 37 import org.thingsboard.server.gen.transport.TransportProtos;
56 38 import org.thingsboard.server.queue.util.TbCoreComponent;
  39 +import org.thingsboard.server.service.edge.rpc.processor.AlarmEdgeProcessor;
  40 +import org.thingsboard.server.service.edge.rpc.processor.CustomerEdgeProcessor;
  41 +import org.thingsboard.server.service.edge.rpc.processor.EdgeProcessor;
  42 +import org.thingsboard.server.service.edge.rpc.processor.EntityEdgeProcessor;
  43 +import org.thingsboard.server.service.edge.rpc.processor.RelationEdgeProcessor;
57 44 import org.thingsboard.server.service.executors.DbCallbackExecutorService;
58 45 import org.thingsboard.server.service.queue.TbClusterService;
59 46
60 47 import javax.annotation.PostConstruct;
61 48 import javax.annotation.PreDestroy;
62 49 import java.io.IOException;
63   -import java.util.ArrayList;
64   -import java.util.HashSet;
65   -import java.util.List;
66   -import java.util.Set;
67 50 import java.util.UUID;
68 51 import java.util.concurrent.ExecutorService;
69 52 import java.util.concurrent.Executors;
... ... @@ -73,30 +56,32 @@ import java.util.concurrent.Executors;
73 56 @Slf4j
74 57 public class DefaultEdgeNotificationService implements EdgeNotificationService {
75 58
76   - private static final ObjectMapper mapper = new ObjectMapper();
  59 + @Autowired
  60 + private EdgeService edgeService;
77 61
78   - private static final int DEFAULT_LIMIT = 100;
  62 + @Autowired
  63 + private EdgeEventService edgeEventService;
79 64
80 65 @Autowired
81   - private EdgeService edgeService;
  66 + private TbClusterService clusterService;
82 67
83 68 @Autowired
84   - private AlarmService alarmService;
  69 + private DbCallbackExecutorService dbCallbackExecutorService;
85 70
86 71 @Autowired
87   - private UserService userService;
  72 + private EdgeProcessor edgeProcessor;
88 73
89 74 @Autowired
90   - private RuleChainService ruleChainService;
  75 + private EntityEdgeProcessor entityProcessor;
91 76
92 77 @Autowired
93   - private EdgeEventService edgeEventService;
  78 + private AlarmEdgeProcessor alarmProcessor;
94 79
95 80 @Autowired
96   - private TbClusterService clusterService;
  81 + private RelationEdgeProcessor relationProcessor;
97 82
98 83 @Autowired
99   - private DbCallbackExecutorService dbCallbackExecutorService;
  84 + private CustomerEdgeProcessor customerProcessor;
100 85
101 86 private ExecutorService tsCallBackExecutor;
102 87
... ... @@ -115,7 +100,7 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService {
115 100 @Override
116 101 public Edge setEdgeRootRuleChain(TenantId tenantId, Edge edge, RuleChainId ruleChainId) throws IOException {
117 102 edge.setRootRuleChainId(ruleChainId);
118   - Edge savedEdge = edgeService.saveEdge(edge);
  103 + Edge savedEdge = edgeService.saveEdge(edge, true);
119 104 saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.RULE_CHAIN, EdgeEventActionType.UPDATED, ruleChainId, null);
120 105 return savedEdge;
121 106 }
... ... @@ -160,7 +145,7 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService {
160 145 EdgeEventType type = EdgeEventType.valueOf(edgeNotificationMsg.getType());
161 146 switch (type) {
162 147 case EDGE:
163   - processEdge(tenantId, edgeNotificationMsg);
  148 + edgeProcessor.processEdgeNotification(tenantId, edgeNotificationMsg);
164 149 break;
165 150 case USER:
166 151 case ASSET:
... ... @@ -169,20 +154,20 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService {
169 154 case ENTITY_VIEW:
170 155 case DASHBOARD:
171 156 case RULE_CHAIN:
172   - processEntity(tenantId, edgeNotificationMsg);
  157 + entityProcessor.processEntityNotification(tenantId, edgeNotificationMsg);
173 158 break;
174 159 case CUSTOMER:
175   - processCustomer(tenantId, edgeNotificationMsg);
  160 + customerProcessor.processCustomerNotification(tenantId, edgeNotificationMsg);
176 161 break;
177 162 case WIDGETS_BUNDLE:
178 163 case WIDGET_TYPE:
179   - processWidgetBundleOrWidgetType(tenantId, edgeNotificationMsg);
  164 + entityProcessor.processEntityNotificationForAllEdges(tenantId, edgeNotificationMsg);
180 165 break;
181 166 case ALARM:
182   - processAlarm(tenantId, edgeNotificationMsg);
  167 + alarmProcessor.processAlarmNotification(tenantId, edgeNotificationMsg);
183 168 break;
184 169 case RELATION:
185   - processRelation(tenantId, edgeNotificationMsg);
  170 + relationProcessor.processRelationNotification(tenantId, edgeNotificationMsg);
186 171 break;
187 172 default:
188 173 log.debug("Edge event type [{}] is not designed to be pushed to edge", type);
... ... @@ -195,311 +180,6 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService {
195 180 }
196 181 }
197 182
198   - private void processEdge(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) {
199   - try {
200   - EdgeEventActionType actionType = EdgeEventActionType.valueOf(edgeNotificationMsg.getAction());
201   - EdgeId edgeId = new EdgeId(new UUID(edgeNotificationMsg.getEntityIdMSB(), edgeNotificationMsg.getEntityIdLSB()));
202   - ListenableFuture<Edge> edgeFuture;
203   - switch (actionType) {
204   - case ASSIGNED_TO_CUSTOMER:
205   - CustomerId customerId = mapper.readValue(edgeNotificationMsg.getBody(), CustomerId.class);
206   - edgeFuture = edgeService.findEdgeByIdAsync(tenantId, edgeId);
207   - Futures.addCallback(edgeFuture, new FutureCallback<Edge>() {
208   - @Override
209   - public void onSuccess(@Nullable Edge edge) {
210   - if (edge != null && !customerId.isNullUid()) {
211   - saveEdgeEvent(edge.getTenantId(), edge.getId(), EdgeEventType.CUSTOMER, EdgeEventActionType.ADDED, customerId, null);
212   - PageLink pageLink = new PageLink(DEFAULT_LIMIT);
213   - PageData<User> pageData;
214   - do {
215   - pageData = userService.findCustomerUsers(tenantId, customerId, pageLink);
216   - if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) {
217   - log.trace("[{}] [{}] user(s) are going to be added to edge.", edge.getId(), pageData.getData().size());
218   - for (User user : pageData.getData()) {
219   - saveEdgeEvent(edge.getTenantId(), edge.getId(), EdgeEventType.USER, EdgeEventActionType.ADDED, user.getId(), null);
220   - }
221   - if (pageData.hasNext()) {
222   - pageLink = pageLink.nextPageLink();
223   - }
224   - }
225   - } while (pageData != null && pageData.hasNext());
226   - }
227   - }
228   -
229   - @Override
230   - public void onFailure(Throwable t) {
231   - log.error("Can't find edge by id [{}]", edgeNotificationMsg, t);
232   - }
233   - }, dbCallbackExecutorService);
234   - break;
235   - case UNASSIGNED_FROM_CUSTOMER:
236   - CustomerId customerIdToDelete = mapper.readValue(edgeNotificationMsg.getBody(), CustomerId.class);
237   - edgeFuture = edgeService.findEdgeByIdAsync(tenantId, edgeId);
238   - Futures.addCallback(edgeFuture, new FutureCallback<Edge>() {
239   - @Override
240   - public void onSuccess(@Nullable Edge edge) {
241   - if (edge != null && !customerIdToDelete.isNullUid()) {
242   - saveEdgeEvent(edge.getTenantId(), edge.getId(), EdgeEventType.CUSTOMER, EdgeEventActionType.DELETED, customerIdToDelete, null);
243   - }
244   - }
245   -
246   - @Override
247   - public void onFailure(Throwable t) {
248   - log.error("Can't find edge by id [{}]", edgeNotificationMsg, t);
249   - }
250   - }, dbCallbackExecutorService);
251   - break;
252   - }
253   - } catch (Exception e) {
254   - log.error("Exception during processing edge event", e);
255   - }
256   - }
257   -
258   - private void processWidgetBundleOrWidgetType(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) {
259   - EdgeEventActionType actionType = EdgeEventActionType.valueOf(edgeNotificationMsg.getAction());
260   - EdgeEventType type = EdgeEventType.valueOf(edgeNotificationMsg.getType());
261   - EntityId entityId = EntityIdFactory.getByEdgeEventTypeAndUuid(type, new UUID(edgeNotificationMsg.getEntityIdMSB(), edgeNotificationMsg.getEntityIdLSB()));
262   - switch (actionType) {
263   - case ADDED:
264   - case UPDATED:
265   - case DELETED:
266   - processActionForAllEdges(tenantId, type, actionType, entityId);
267   - break;
268   - }
269   - }
270   -
271   - private void processCustomer(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) {
272   - EdgeEventActionType actionType = EdgeEventActionType.valueOf(edgeNotificationMsg.getAction());
273   - EdgeEventType type = EdgeEventType.valueOf(edgeNotificationMsg.getType());
274   - UUID uuid = new UUID(edgeNotificationMsg.getEntityIdMSB(), edgeNotificationMsg.getEntityIdLSB());
275   - CustomerId customerId = new CustomerId(EntityIdFactory.getByEdgeEventTypeAndUuid(type, uuid).getId());
276   - switch (actionType) {
277   - case UPDATED:
278   - PageLink pageLink = new PageLink(DEFAULT_LIMIT);
279   - PageData<Edge> pageData;
280   - do {
281   - pageData = edgeService.findEdgesByTenantIdAndCustomerId(tenantId, customerId, pageLink);
282   - if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) {
283   - for (Edge edge : pageData.getData()) {
284   - saveEdgeEvent(tenantId, edge.getId(), type, actionType, customerId, null);
285   - }
286   - if (pageData.hasNext()) {
287   - pageLink = pageLink.nextPageLink();
288   - }
289   - }
290   - } while (pageData != null && pageData.hasNext());
291   - break;
292   - case DELETED:
293   - EdgeId edgeId = new EdgeId(new UUID(edgeNotificationMsg.getEdgeIdMSB(), edgeNotificationMsg.getEdgeIdLSB()));
294   - saveEdgeEvent(tenantId, edgeId, type, actionType, customerId, null);
295   - break;
296   - }
297   - }
298   -
299   - private void processEntity(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) {
300   - EdgeEventActionType actionType = EdgeEventActionType.valueOf(edgeNotificationMsg.getAction());
301   - EdgeEventType type = EdgeEventType.valueOf(edgeNotificationMsg.getType());
302   - EntityId entityId = EntityIdFactory.getByEdgeEventTypeAndUuid(type,
303   - new UUID(edgeNotificationMsg.getEntityIdMSB(), edgeNotificationMsg.getEntityIdLSB()));
304   - EdgeId edgeId = new EdgeId(new UUID(edgeNotificationMsg.getEdgeIdMSB(), edgeNotificationMsg.getEdgeIdLSB()));
305   - ListenableFuture<List<EdgeId>> edgeIdsFuture;
306   - switch (actionType) {
307   - case ADDED: // used only for USER entity
308   - case UPDATED:
309   - case CREDENTIALS_UPDATED:
310   - edgeIdsFuture = edgeService.findRelatedEdgeIdsByEntityId(tenantId, entityId);
311   - Futures.addCallback(edgeIdsFuture, new FutureCallback<List<EdgeId>>() {
312   - @Override
313   - public void onSuccess(@Nullable List<EdgeId> edgeIds) {
314   - if (edgeIds != null && !edgeIds.isEmpty()) {
315   - for (EdgeId edgeId : edgeIds) {
316   - saveEdgeEvent(tenantId, edgeId, type, actionType, entityId, null);
317   - }
318   - }
319   - }
320   - @Override
321   - public void onFailure(Throwable throwable) {
322   - log.error("Failed to find related edge ids [{}]", edgeNotificationMsg, throwable);
323   - }
324   - }, dbCallbackExecutorService);
325   - break;
326   - case ASSIGNED_TO_CUSTOMER:
327   - case UNASSIGNED_FROM_CUSTOMER:
328   - edgeIdsFuture = edgeService.findRelatedEdgeIdsByEntityId(tenantId, entityId);
329   - Futures.addCallback(edgeIdsFuture, new FutureCallback<List<EdgeId>>() {
330   - @Override
331   - public void onSuccess(@Nullable List<EdgeId> edgeIds) {
332   - if (edgeIds != null && !edgeIds.isEmpty()) {
333   - for (EdgeId edgeId : edgeIds) {
334   - try {
335   - CustomerId customerId = mapper.readValue(edgeNotificationMsg.getBody(), CustomerId.class);
336   - ListenableFuture<Edge> future = edgeService.findEdgeByIdAsync(tenantId, edgeId);
337   - Futures.addCallback(future, new FutureCallback<Edge>() {
338   - @Override
339   - public void onSuccess(@Nullable Edge edge) {
340   - if (edge != null && edge.getCustomerId() != null &&
341   - !edge.getCustomerId().isNullUid() && edge.getCustomerId().equals(customerId)) {
342   - saveEdgeEvent(tenantId, edgeId, type, actionType, entityId, null);
343   - }
344   - }
345   - @Override
346   - public void onFailure(Throwable throwable) {
347   - log.error("Failed to find edge by id [{}]", edgeNotificationMsg, throwable);
348   - }
349   - }, dbCallbackExecutorService);
350   - } catch (Exception e) {
351   - log.error("Can't parse customer id from entity body [{}]", edgeNotificationMsg, e);
352   - }
353   - }
354   - }
355   - }
356   -
357   - @Override
358   - public void onFailure(Throwable throwable) {
359   - log.error("Failed to find related edge ids [{}]", edgeNotificationMsg, throwable);
360   - }
361   - }, dbCallbackExecutorService);
362   - break;
363   - case DELETED:
364   - saveEdgeEvent(tenantId, edgeId, type, actionType, entityId, null);
365   - break;
366   - case ASSIGNED_TO_EDGE:
367   - case UNASSIGNED_FROM_EDGE:
368   - saveEdgeEvent(tenantId, edgeId, type, actionType, entityId, null);
369   - if (type.equals(EdgeEventType.RULE_CHAIN)) {
370   - updateDependentRuleChains(tenantId, new RuleChainId(entityId.getId()), edgeId);
371   - }
372   - break;
373   - }
374   - }
375   -
376   - private void updateDependentRuleChains(TenantId tenantId, RuleChainId processingRuleChainId, EdgeId edgeId) {
377   - PageLink pageLink = new PageLink(DEFAULT_LIMIT);
378   - PageData<RuleChain> pageData;
379   - do {
380   - pageData = ruleChainService.findRuleChainsByTenantIdAndEdgeId(tenantId, edgeId, pageLink);
381   - if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) {
382   - for (RuleChain ruleChain : pageData.getData()) {
383   - if (!ruleChain.getId().equals(processingRuleChainId)) {
384   - List<RuleChainConnectionInfo> connectionInfos =
385   - ruleChainService.loadRuleChainMetaData(ruleChain.getTenantId(), ruleChain.getId()).getRuleChainConnections();
386   - if (connectionInfos != null && !connectionInfos.isEmpty()) {
387   - for (RuleChainConnectionInfo connectionInfo : connectionInfos) {
388   - if (connectionInfo.getTargetRuleChainId().equals(processingRuleChainId)) {
389   - saveEdgeEvent(tenantId,
390   - edgeId,
391   - EdgeEventType.RULE_CHAIN_METADATA,
392   - EdgeEventActionType.UPDATED,
393   - ruleChain.getId(),
394   - null);
395   - }
396   - }
397   - }
398   - }
399   - }
400   - if (pageData.hasNext()) {
401   - pageLink = pageLink.nextPageLink();
402   - }
403   - }
404   - } while (pageData != null && pageData.hasNext());
405   - }
406   -
407   - private void processAlarm(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) {
408   - AlarmId alarmId = new AlarmId(new UUID(edgeNotificationMsg.getEntityIdMSB(), edgeNotificationMsg.getEntityIdLSB()));
409   - ListenableFuture<Alarm> alarmFuture = alarmService.findAlarmByIdAsync(tenantId, alarmId);
410   - Futures.addCallback(alarmFuture, new FutureCallback<Alarm>() {
411   - @Override
412   - public void onSuccess(@Nullable Alarm alarm) {
413   - if (alarm != null) {
414   - EdgeEventType type = EdgeUtils.getEdgeEventTypeByEntityType(alarm.getOriginator().getEntityType());
415   - if (type != null) {
416   - ListenableFuture<List<EdgeId>> relatedEdgeIdsByEntityIdFuture = edgeService.findRelatedEdgeIdsByEntityId(tenantId, alarm.getOriginator());
417   - Futures.addCallback(relatedEdgeIdsByEntityIdFuture, new FutureCallback<List<EdgeId>>() {
418   - @Override
419   - public void onSuccess(@Nullable List<EdgeId> relatedEdgeIdsByEntityId) {
420   - if (relatedEdgeIdsByEntityId != null) {
421   - for (EdgeId edgeId : relatedEdgeIdsByEntityId) {
422   - saveEdgeEvent(tenantId,
423   - edgeId,
424   - EdgeEventType.ALARM,
425   - EdgeEventActionType.valueOf(edgeNotificationMsg.getAction()),
426   - alarmId,
427   - null);
428   - }
429   - }
430   - }
431   -
432   - @Override
433   - public void onFailure(Throwable t) {
434   - log.warn("[{}] can't find related edge ids by entity id [{}]", tenantId.getId(), alarm.getOriginator(), t);
435   - }
436   - }, dbCallbackExecutorService);
437   - }
438   - }
439   - }
440   -
441   - @Override
442   - public void onFailure(Throwable t) {
443   - log.warn("[{}] can't find alarm by id [{}]", tenantId.getId(), alarmId.getId(), t);
444   - }
445   - }, dbCallbackExecutorService);
446   - }
447   -
448   - private void processRelation(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) throws JsonProcessingException {
449   - EntityRelation relation = mapper.readValue(edgeNotificationMsg.getBody(), EntityRelation.class);
450   - if (!relation.getFrom().getEntityType().equals(EntityType.EDGE) &&
451   - !relation.getTo().getEntityType().equals(EntityType.EDGE)) {
452   - List<ListenableFuture<List<EdgeId>>> futures = new ArrayList<>();
453   - futures.add(edgeService.findRelatedEdgeIdsByEntityId(tenantId, relation.getTo()));
454   - futures.add(edgeService.findRelatedEdgeIdsByEntityId(tenantId, relation.getFrom()));
455   - ListenableFuture<List<List<EdgeId>>> combinedFuture = Futures.allAsList(futures);
456   - Futures.addCallback(combinedFuture, new FutureCallback<List<List<EdgeId>>>() {
457   - @Override
458   - public void onSuccess(@Nullable List<List<EdgeId>> listOfListsEdgeIds) {
459   - Set<EdgeId> uniqueEdgeIds = new HashSet<>();
460   - if (listOfListsEdgeIds != null && !listOfListsEdgeIds.isEmpty()) {
461   - for (List<EdgeId> listOfListsEdgeId : listOfListsEdgeIds) {
462   - if (listOfListsEdgeId != null) {
463   - uniqueEdgeIds.addAll(listOfListsEdgeId);
464   - }
465   - }
466   - }
467   - if (!uniqueEdgeIds.isEmpty()) {
468   - for (EdgeId edgeId : uniqueEdgeIds) {
469   - saveEdgeEvent(tenantId,
470   - edgeId,
471   - EdgeEventType.RELATION,
472   - EdgeEventActionType.valueOf(edgeNotificationMsg.getAction()),
473   - null,
474   - mapper.valueToTree(relation));
475   - }
476   - }
477   - }
478   -
479   - @Override
480   - public void onFailure(Throwable t) {
481   - log.warn("[{}] can't find related edge ids by relation to id [{}] and relation from id [{}]" ,
482   - tenantId.getId(), relation.getTo().getId(), relation.getFrom().getId(), t);
483   - }
484   - }, dbCallbackExecutorService);
485   - }
486   - }
487   -
488   - private void processActionForAllEdges(TenantId tenantId, EdgeEventType type, EdgeEventActionType actionType, EntityId entityId) {
489   - PageLink pageLink = new PageLink(DEFAULT_LIMIT);
490   - PageData<Edge> pageData;
491   - do {
492   - pageData = edgeService.findEdgesByTenantId(tenantId, pageLink);
493   - if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) {
494   - for (Edge edge : pageData.getData()) {
495   - saveEdgeEvent(tenantId, edge.getId(), type, actionType, entityId, null);
496   - }
497   - if (pageData.hasNext()) {
498   - pageLink = pageLink.nextPageLink();
499   - }
500   - }
501   - } while (pageData != null && pageData.hasNext());
502   - }
503 183 }
504 184
505 185
... ...
... ... @@ -16,218 +16,130 @@
16 16 package org.thingsboard.server.service.edge;
17 17
18 18 import lombok.Data;
19   -import lombok.Getter;
20 19 import org.springframework.beans.factory.annotation.Autowired;
21 20 import org.springframework.context.annotation.Lazy;
22 21 import org.springframework.stereotype.Component;
23   -import org.thingsboard.server.actors.service.ActorService;
24   -import org.thingsboard.server.dao.alarm.AlarmService;
25 22 import org.thingsboard.server.dao.asset.AssetService;
26 23 import org.thingsboard.server.dao.attributes.AttributesService;
27   -import org.thingsboard.server.dao.customer.CustomerService;
28 24 import org.thingsboard.server.dao.dashboard.DashboardService;
29   -import org.thingsboard.server.dao.device.DeviceCredentialsService;
30 25 import org.thingsboard.server.dao.device.DeviceProfileService;
31   -import org.thingsboard.server.dao.device.DeviceService;
32 26 import org.thingsboard.server.dao.edge.EdgeEventService;
33 27 import org.thingsboard.server.dao.edge.EdgeService;
34   -import org.thingsboard.server.dao.entityview.EntityViewService;
35   -import org.thingsboard.server.dao.relation.RelationService;
36 28 import org.thingsboard.server.dao.rule.RuleChainService;
  29 +import org.thingsboard.server.dao.settings.AdminSettingsService;
37 30 import org.thingsboard.server.dao.user.UserService;
38   -import org.thingsboard.server.dao.widget.WidgetTypeService;
39 31 import org.thingsboard.server.dao.widget.WidgetsBundleService;
40   -import org.thingsboard.server.queue.discovery.PartitionService;
41 32 import org.thingsboard.server.queue.util.TbCoreComponent;
42 33 import org.thingsboard.server.service.edge.rpc.EdgeEventStorageSettings;
43   -import org.thingsboard.server.service.edge.rpc.constructor.AdminSettingsMsgConstructor;
44   -import org.thingsboard.server.service.edge.rpc.constructor.AlarmMsgConstructor;
45   -import org.thingsboard.server.service.edge.rpc.constructor.AssetMsgConstructor;
46   -import org.thingsboard.server.service.edge.rpc.constructor.CustomerMsgConstructor;
47   -import org.thingsboard.server.service.edge.rpc.constructor.DashboardMsgConstructor;
48   -import org.thingsboard.server.service.edge.rpc.constructor.DeviceMsgConstructor;
49   -import org.thingsboard.server.service.edge.rpc.constructor.DeviceProfileMsgConstructor;
50   -import org.thingsboard.server.service.edge.rpc.constructor.EntityDataMsgConstructor;
51   -import org.thingsboard.server.service.edge.rpc.constructor.EntityViewMsgConstructor;
52   -import org.thingsboard.server.service.edge.rpc.constructor.RelationMsgConstructor;
53   -import org.thingsboard.server.service.edge.rpc.constructor.RuleChainMsgConstructor;
54   -import org.thingsboard.server.service.edge.rpc.constructor.UserMsgConstructor;
55   -import org.thingsboard.server.service.edge.rpc.constructor.WidgetTypeMsgConstructor;
56   -import org.thingsboard.server.service.edge.rpc.constructor.WidgetsBundleMsgConstructor;
57   -import org.thingsboard.server.service.edge.rpc.init.SyncEdgeService;
58   -import org.thingsboard.server.service.edge.rpc.processor.AlarmProcessor;
59   -import org.thingsboard.server.service.edge.rpc.processor.DeviceProcessor;
60   -import org.thingsboard.server.service.edge.rpc.processor.RelationProcessor;
61   -import org.thingsboard.server.service.edge.rpc.processor.TelemetryProcessor;
  34 +import org.thingsboard.server.service.edge.rpc.processor.AdminSettingsEdgeProcessor;
  35 +import org.thingsboard.server.service.edge.rpc.processor.AlarmEdgeProcessor;
  36 +import org.thingsboard.server.service.edge.rpc.processor.AssetEdgeProcessor;
  37 +import org.thingsboard.server.service.edge.rpc.processor.CustomerEdgeProcessor;
  38 +import org.thingsboard.server.service.edge.rpc.processor.DashboardEdgeProcessor;
  39 +import org.thingsboard.server.service.edge.rpc.processor.DeviceEdgeProcessor;
  40 +import org.thingsboard.server.service.edge.rpc.processor.DeviceProfileEdgeProcessor;
  41 +import org.thingsboard.server.service.edge.rpc.processor.EntityEdgeProcessor;
  42 +import org.thingsboard.server.service.edge.rpc.processor.EntityViewEdgeProcessor;
  43 +import org.thingsboard.server.service.edge.rpc.processor.RelationEdgeProcessor;
  44 +import org.thingsboard.server.service.edge.rpc.processor.RuleChainEdgeProcessor;
  45 +import org.thingsboard.server.service.edge.rpc.processor.TelemetryEdgeProcessor;
  46 +import org.thingsboard.server.service.edge.rpc.processor.UserEdgeProcessor;
  47 +import org.thingsboard.server.service.edge.rpc.processor.WidgetBundleEdgeProcessor;
  48 +import org.thingsboard.server.service.edge.rpc.processor.WidgetTypeEdgeProcessor;
  49 +import org.thingsboard.server.service.edge.rpc.sync.EdgeRequestsService;
62 50 import org.thingsboard.server.service.executors.DbCallbackExecutorService;
63   -import org.thingsboard.server.service.queue.TbClusterService;
64   -import org.thingsboard.server.service.state.DeviceStateService;
  51 +import org.thingsboard.server.service.executors.GrpcCallbackExecutorService;
65 52
66 53 @Component
67 54 @TbCoreComponent
68 55 @Data
  56 +@Lazy
69 57 public class EdgeContextComponent {
70 58
71   - @Lazy
72 59 @Autowired
73 60 private EdgeService edgeService;
74 61
75 62 @Autowired
76   - private PartitionService partitionService;
77   -
78   - @Lazy
79   - @Autowired
80 63 private EdgeEventService edgeEventService;
81 64
82   - @Lazy
83 65 @Autowired
84   - private AssetService assetService;
  66 + private AdminSettingsService adminSettingsService;
85 67
86   - @Lazy
87 68 @Autowired
88   - private DeviceService deviceService;
  69 + private AssetService assetService;
89 70
90   - @Lazy
91 71 @Autowired
92 72 private DeviceProfileService deviceProfileService;
93 73
94   - @Lazy
95   - @Autowired
96   - private DeviceCredentialsService deviceCredentialsService;
97   -
98   - @Lazy
99   - @Autowired
100   - private EntityViewService entityViewService;
101   -
102   - @Lazy
103 74 @Autowired
104 75 private AttributesService attributesService;
105 76
106   - @Lazy
107   - @Autowired
108   - private CustomerService customerService;
109   -
110   - @Lazy
111   - @Autowired
112   - private RelationService relationService;
113   -
114   - @Lazy
115   - @Autowired
116   - private AlarmService alarmService;
117   -
118   - @Lazy
119 77 @Autowired
120 78 private DashboardService dashboardService;
121 79
122   - @Lazy
123 80 @Autowired
124 81 private RuleChainService ruleChainService;
125 82
126   - @Lazy
127 83 @Autowired
128 84 private UserService userService;
129 85
130   - @Lazy
131   - @Autowired
132   - private ActorService actorService;
133   -
134   - @Lazy
135 86 @Autowired
136 87 private WidgetsBundleService widgetsBundleService;
137 88
138   - @Lazy
139   - @Autowired
140   - private WidgetTypeService widgetTypeService;
141   -
142   - @Lazy
143   - @Autowired
144   - private DeviceStateService deviceStateService;
145   -
146   - @Lazy
147   - @Autowired
148   - private TbClusterService tbClusterService;
149   -
150   - @Lazy
151 89 @Autowired
152   - private SyncEdgeService syncEdgeService;
  90 + private EdgeRequestsService edgeRequestsService;
153 91
154   - @Lazy
155 92 @Autowired
156   - private RuleChainMsgConstructor ruleChainMsgConstructor;
  93 + private AlarmEdgeProcessor alarmProcessor;
157 94
158   - @Lazy
159 95 @Autowired
160   - private AlarmMsgConstructor alarmMsgConstructor;
  96 + private DeviceProfileEdgeProcessor deviceProfileProcessor;
161 97
162   - @Lazy
163 98 @Autowired
164   - private DeviceMsgConstructor deviceMsgConstructor;
  99 + private DeviceEdgeProcessor deviceProcessor;
165 100
166   - @Lazy
167 101 @Autowired
168   - private DeviceProfileMsgConstructor deviceProfileMsgConstructor;
  102 + private EntityEdgeProcessor entityProcessor;
169 103
170   - @Lazy
171 104 @Autowired
172   - private AssetMsgConstructor assetMsgConstructor;
  105 + private AssetEdgeProcessor assetProcessor;
173 106
174   - @Lazy
175 107 @Autowired
176   - private EntityViewMsgConstructor entityViewMsgConstructor;
  108 + private EntityViewEdgeProcessor entityViewProcessor;
177 109
178   - @Lazy
179 110 @Autowired
180   - private DashboardMsgConstructor dashboardMsgConstructor;
  111 + private UserEdgeProcessor userProcessor;
181 112
182   - @Lazy
183 113 @Autowired
184   - private CustomerMsgConstructor customerMsgConstructor;
  114 + private RelationEdgeProcessor relationProcessor;
185 115
186   - @Lazy
187 116 @Autowired
188   - private UserMsgConstructor userMsgConstructor;
  117 + private TelemetryEdgeProcessor telemetryProcessor;
189 118
190   - @Lazy
191 119 @Autowired
192   - private RelationMsgConstructor relationMsgConstructor;
  120 + private DashboardEdgeProcessor dashboardProcessor;
193 121
194   - @Lazy
195 122 @Autowired
196   - private WidgetsBundleMsgConstructor widgetsBundleMsgConstructor;
  123 + private RuleChainEdgeProcessor ruleChainProcessor;
197 124
198   - @Lazy
199 125 @Autowired
200   - private WidgetTypeMsgConstructor widgetTypeMsgConstructor;
  126 + private CustomerEdgeProcessor customerProcessor;
201 127
202   - @Lazy
203 128 @Autowired
204   - private AdminSettingsMsgConstructor adminSettingsMsgConstructor;
  129 + private WidgetBundleEdgeProcessor widgetBundleProcessor;
205 130
206   - @Lazy
207 131 @Autowired
208   - private EntityDataMsgConstructor entityDataMsgConstructor;
  132 + private WidgetTypeEdgeProcessor widgetTypeProcessor;
209 133
210   - @Lazy
211 134 @Autowired
212   - private AlarmProcessor alarmProcessor;
  135 + private AdminSettingsEdgeProcessor adminSettingsProcessor;
213 136
214   - @Lazy
215   - @Autowired
216   - private DeviceProcessor deviceProcessor;
217   -
218   - @Lazy
219   - @Autowired
220   - private RelationProcessor relationProcessor;
221   -
222   - @Lazy
223   - @Autowired
224   - private TelemetryProcessor telemetryProcessor;
225   -
226   - @Lazy
227 137 @Autowired
228 138 private EdgeEventStorageSettings edgeEventStorageSettings;
229 139
230 140 @Autowired
231   - @Getter
232 141 private DbCallbackExecutorService dbCallbackExecutor;
  142 +
  143 + @Autowired
  144 + private GrpcCallbackExecutorService grpcCallbackExecutorService;
233 145 }
... ...
  1 +/**
  2 + * Copyright © 2016-2021 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.edge.rpc;
  17 +
  18 +import com.fasterxml.jackson.databind.JsonNode;
  19 +import org.thingsboard.server.common.data.edge.EdgeEvent;
  20 +import org.thingsboard.server.common.data.edge.EdgeEventActionType;
  21 +import org.thingsboard.server.common.data.edge.EdgeEventType;
  22 +import org.thingsboard.server.common.data.id.EdgeId;
  23 +import org.thingsboard.server.common.data.id.EntityId;
  24 +import org.thingsboard.server.common.data.id.TenantId;
  25 +
  26 +public final class EdgeEventUtils {
  27 +
  28 + private EdgeEventUtils() {
  29 + }
  30 +
  31 + public static EdgeEvent constructEdgeEvent(TenantId tenantId,
  32 + EdgeId edgeId,
  33 + EdgeEventType type,
  34 + EdgeEventActionType action,
  35 + EntityId entityId,
  36 + JsonNode body) {
  37 + EdgeEvent edgeEvent = new EdgeEvent();
  38 + edgeEvent.setTenantId(tenantId);
  39 + edgeEvent.setEdgeId(edgeId);
  40 + edgeEvent.setType(type);
  41 + edgeEvent.setAction(action);
  42 + if (entityId != null) {
  43 + edgeEvent.setEntityId(entityId.getId());
  44 + }
  45 + edgeEvent.setBody(body);
  46 + return edgeEvent;
  47 + }
  48 +}
... ...
... ... @@ -16,8 +16,8 @@
16 16 package org.thingsboard.server.service.edge.rpc;
17 17
18 18 import com.fasterxml.jackson.databind.ObjectMapper;
19   -import com.google.common.io.Resources;
20 19 import com.google.common.util.concurrent.FutureCallback;
  20 +import com.google.common.util.concurrent.Futures;
21 21 import io.grpc.Server;
22 22 import io.grpc.netty.NettyServerBuilder;
23 23 import io.grpc.stub.StreamObserver;
... ... @@ -28,16 +28,16 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
28 28 import org.springframework.stereotype.Service;
29 29 import org.thingsboard.common.util.ThingsBoardThreadFactory;
30 30 import org.thingsboard.server.common.data.DataConstants;
31   -import org.thingsboard.server.common.data.Tenant;
  31 +import org.thingsboard.server.common.data.ResourceUtils;
32 32 import org.thingsboard.server.common.data.edge.Edge;
33 33 import org.thingsboard.server.common.data.id.EdgeId;
34 34 import org.thingsboard.server.common.data.id.TenantId;
35 35 import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
36 36 import org.thingsboard.server.common.data.kv.BooleanDataEntry;
37 37 import org.thingsboard.server.common.data.kv.LongDataEntry;
38   -import org.thingsboard.server.gen.edge.EdgeRpcServiceGrpc;
39   -import org.thingsboard.server.gen.edge.RequestMsg;
40   -import org.thingsboard.server.gen.edge.ResponseMsg;
  38 +import org.thingsboard.server.gen.edge.v1.EdgeRpcServiceGrpc;
  39 +import org.thingsboard.server.gen.edge.v1.RequestMsg;
  40 +import org.thingsboard.server.gen.edge.v1.ResponseMsg;
41 41 import org.thingsboard.server.queue.util.TbCoreComponent;
42 42 import org.thingsboard.server.service.edge.EdgeContextComponent;
43 43 import org.thingsboard.server.service.state.DefaultDeviceStateService;
... ... @@ -46,9 +46,10 @@ import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService;
46 46 import javax.annotation.Nullable;
47 47 import javax.annotation.PostConstruct;
48 48 import javax.annotation.PreDestroy;
49   -import java.io.File;
50 49 import java.io.IOException;
  50 +import java.io.InputStream;
51 51 import java.util.Collections;
  52 +import java.util.HashMap;
52 53 import java.util.Map;
53 54 import java.util.UUID;
54 55 import java.util.concurrent.ConcurrentHashMap;
... ... @@ -57,6 +58,8 @@ import java.util.concurrent.Executors;
57 58 import java.util.concurrent.ScheduledExecutorService;
58 59 import java.util.concurrent.ScheduledFuture;
59 60 import java.util.concurrent.TimeUnit;
  61 +import java.util.concurrent.locks.Lock;
  62 +import java.util.concurrent.locks.ReentrantLock;
60 63
61 64 @Service
62 65 @Slf4j
... ... @@ -65,7 +68,8 @@ import java.util.concurrent.TimeUnit;
65 68 public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase implements EdgeRpcService {
66 69
67 70 private final ConcurrentMap<EdgeId, EdgeGrpcSession> sessions = new ConcurrentHashMap<>();
68   - private final ConcurrentMap<EdgeId, Boolean> sessionNewEvents = new ConcurrentHashMap<>();
  71 + private final ConcurrentMap<EdgeId, Lock> sessionNewEventsLocks = new ConcurrentHashMap<>();
  72 + private final Map<EdgeId, Boolean> sessionNewEvents = new HashMap<>();
69 73 private final ConcurrentMap<EdgeId, ScheduledFuture<?>> sessionEdgeEventChecks = new ConcurrentHashMap<>();
70 74 private static final ObjectMapper mapper = new ObjectMapper();
71 75
... ... @@ -81,10 +85,15 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i
81 85 private boolean persistToTelemetry;
82 86 @Value("${edges.rpc.client_max_keep_alive_time_sec}")
83 87 private int clientMaxKeepAliveTimeSec;
  88 + @Value("${edges.rpc.max_inbound_message_size:4194304}")
  89 + private int maxInboundMessageSize;
84 90
85 91 @Value("${edges.scheduler_pool_size}")
86 92 private int schedulerPoolSize;
87 93
  94 + @Value("${edges.send_scheduler_pool_size}")
  95 + private int sendSchedulerPoolSize;
  96 +
88 97 @Autowired
89 98 private EdgeContextComponent ctx;
90 99
... ... @@ -93,19 +102,22 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i
93 102
94 103 private Server server;
95 104
96   - private ScheduledExecutorService scheduler;
  105 + private ScheduledExecutorService edgeEventProcessingExecutorService;
  106 +
  107 + private ScheduledExecutorService sendDownlinkExecutorService;
97 108
98 109 @PostConstruct
99 110 public void init() {
100 111 log.info("Initializing Edge RPC service!");
101 112 NettyServerBuilder builder = NettyServerBuilder.forPort(rpcPort)
102 113 .permitKeepAliveTime(clientMaxKeepAliveTimeSec, TimeUnit.SECONDS)
  114 + .maxInboundMessageSize(maxInboundMessageSize)
103 115 .addService(this);
104 116 if (sslEnabled) {
105 117 try {
106   - File certFile = new File(Resources.getResource(certFileResource).toURI());
107   - File privateKeyFile = new File(Resources.getResource(privateKeyResource).toURI());
108   - builder.useTransportSecurity(certFile, privateKeyFile);
  118 + InputStream certFileIs = ResourceUtils.getInputStream(this, certFileResource);
  119 + InputStream privateKeyFileIs = ResourceUtils.getInputStream(this, privateKeyResource);
  120 + builder.useTransportSecurity(certFileIs, privateKeyFileIs);
109 121 } catch (Exception e) {
110 122 log.error("Unable to set up SSL context. Reason: " + e.getMessage(), e);
111 123 throw new RuntimeException("Unable to set up SSL context!", e);
... ... @@ -119,7 +131,8 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i
119 131 log.error("Failed to start Edge RPC server!", e);
120 132 throw new RuntimeException("Failed to start Edge RPC server!");
121 133 }
122   - this.scheduler = Executors.newScheduledThreadPool(schedulerPoolSize, ThingsBoardThreadFactory.forName("edge-scheduler"));
  134 + this.edgeEventProcessingExecutorService = Executors.newScheduledThreadPool(schedulerPoolSize, ThingsBoardThreadFactory.forName("edge-scheduler"));
  135 + this.sendDownlinkExecutorService = Executors.newScheduledThreadPool(sendSchedulerPoolSize, ThingsBoardThreadFactory.forName("edge-send-scheduler"));
123 136 log.info("Edge RPC service initialized!");
124 137 }
125 138
... ... @@ -136,62 +149,84 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i
136 149 sessionEdgeEventChecks.remove(edgeId);
137 150 }
138 151 }
139   - if (scheduler != null) {
140   - scheduler.shutdownNow();
  152 + if (edgeEventProcessingExecutorService != null) {
  153 + edgeEventProcessingExecutorService.shutdownNow();
  154 + }
  155 + if (sendDownlinkExecutorService != null) {
  156 + sendDownlinkExecutorService.shutdownNow();
141 157 }
142 158 }
143 159
144 160 @Override
145 161 public StreamObserver<RequestMsg> handleMsgs(StreamObserver<ResponseMsg> outputStream) {
146   - return new EdgeGrpcSession(ctx, outputStream, this::onEdgeConnect, this::onEdgeDisconnect, mapper).getInputStream();
  162 + return new EdgeGrpcSession(ctx, outputStream, this::onEdgeConnect, this::onEdgeDisconnect, mapper, sendDownlinkExecutorService).getInputStream();
147 163 }
148 164
149 165 @Override
150   - public void updateEdge(Edge edge) {
  166 + public void updateEdge(TenantId tenantId, Edge edge) {
151 167 EdgeGrpcSession session = sessions.get(edge.getId());
152 168 if (session != null && session.isConnected()) {
153   - log.debug("[{}] Updating configuration for edge [{}] [{}]", edge.getTenantId(), edge.getName(), edge.getId());
  169 + log.debug("[{}] Updating configuration for edge [{}] [{}]", tenantId, edge.getName(), edge.getId());
154 170 session.onConfigurationUpdate(edge);
155 171 } else {
156   - log.debug("[{}] Session doesn't exist for edge [{}] [{}]", edge.getTenantId(), edge.getName(), edge.getId());
  172 + log.debug("[{}] Session doesn't exist for edge [{}] [{}]", tenantId, edge.getName(), edge.getId());
157 173 }
158 174 }
159 175
160 176 @Override
161   - public void deleteEdge(EdgeId edgeId) {
  177 + public void deleteEdge(TenantId tenantId, EdgeId edgeId) {
162 178 EdgeGrpcSession session = sessions.get(edgeId);
163 179 if (session != null && session.isConnected()) {
164   - log.info("Closing and removing session for edge [{}]", edgeId);
  180 + log.info("[{}] Closing and removing session for edge [{}]", tenantId, edgeId);
165 181 session.close();
166 182 sessions.remove(edgeId);
167   - sessionNewEvents.remove(edgeId);
  183 + final Lock newEventLock = sessionNewEventsLocks.computeIfAbsent(edgeId, id -> new ReentrantLock());
  184 + newEventLock.lock();
  185 + try {
  186 + sessionNewEvents.remove(edgeId);
  187 + } finally {
  188 + newEventLock.unlock();
  189 + }
168 190 cancelScheduleEdgeEventsCheck(edgeId);
169 191 }
170 192 }
171 193
172 194 @Override
173   - public void onEdgeEvent(EdgeId edgeId) {
174   - log.trace("[{}] onEdgeEvent", edgeId.getId());
175   - if (Boolean.FALSE.equals(sessionNewEvents.get(edgeId))) {
176   - log.trace("[{}] set session new events flag to true", edgeId.getId());
177   - sessionNewEvents.put(edgeId, true);
  195 + public void onEdgeEvent(TenantId tenantId, EdgeId edgeId) {
  196 + log.trace("[{}] onEdgeEvent [{}]", tenantId, edgeId.getId());
  197 + final Lock newEventLock = sessionNewEventsLocks.computeIfAbsent(edgeId, id -> new ReentrantLock());
  198 + newEventLock.lock();
  199 + try {
  200 + if (Boolean.FALSE.equals(sessionNewEvents.get(edgeId))) {
  201 + log.trace("[{}] set session new events flag to true [{}]", tenantId, edgeId.getId());
  202 + sessionNewEvents.put(edgeId, true);
  203 + }
  204 + } finally {
  205 + newEventLock.unlock();
178 206 }
179 207 }
180 208
181 209 private void onEdgeConnect(EdgeId edgeId, EdgeGrpcSession edgeGrpcSession) {
182 210 log.info("[{}] edge [{}] connected successfully.", edgeGrpcSession.getSessionId(), edgeId);
183 211 sessions.put(edgeId, edgeGrpcSession);
184   - sessionNewEvents.put(edgeId, false);
  212 + final Lock newEventLock = sessionNewEventsLocks.computeIfAbsent(edgeId, id -> new ReentrantLock());
  213 + newEventLock.lock();
  214 + try {
  215 + sessionNewEvents.put(edgeId, true);
  216 + } finally {
  217 + newEventLock.unlock();
  218 + }
185 219 save(edgeId, DefaultDeviceStateService.ACTIVITY_STATE, true);
186 220 save(edgeId, DefaultDeviceStateService.LAST_CONNECT_TIME, System.currentTimeMillis());
187 221 cancelScheduleEdgeEventsCheck(edgeId);
188 222 scheduleEdgeEventsCheck(edgeGrpcSession);
189 223 }
190 224
191   - public EdgeGrpcSession getEdgeGrpcSessionById(TenantId tenantId, EdgeId edgeId) {
  225 + @Override
  226 + public void startSyncProcess(TenantId tenantId, EdgeId edgeId) {
192 227 EdgeGrpcSession session = sessions.get(edgeId);
193 228 if (session != null && session.isConnected()) {
194   - return session;
  229 + session.startSyncProcess(tenantId, edgeId);
195 230 } else {
196 231 log.error("[{}] Edge is not connected [{}]", tenantId, edgeId);
197 232 throw new RuntimeException("Edge is not connected");
... ... @@ -202,19 +237,37 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i
202 237 EdgeId edgeId = session.getEdge().getId();
203 238 UUID tenantId = session.getEdge().getTenantId().getId();
204 239 if (sessions.containsKey(edgeId)) {
205   - ScheduledFuture<?> schedule = scheduler.schedule(() -> {
  240 + ScheduledFuture<?> edgeEventCheckTask = edgeEventProcessingExecutorService.schedule(() -> {
206 241 try {
207   - if (Boolean.TRUE.equals(sessionNewEvents.get(edgeId))) {
208   - log.trace("[{}] Set session new events flag to false", edgeId.getId());
209   - sessionNewEvents.put(edgeId, false);
210   - session.processEdgeEvents();
  242 + final Lock newEventLock = sessionNewEventsLocks.computeIfAbsent(edgeId, id -> new ReentrantLock());
  243 + newEventLock.lock();
  244 + try {
  245 + if (Boolean.TRUE.equals(sessionNewEvents.get(edgeId))) {
  246 + log.trace("[{}] Set session new events flag to false", edgeId.getId());
  247 + sessionNewEvents.put(edgeId, false);
  248 + Futures.addCallback(session.processEdgeEvents(), new FutureCallback<>() {
  249 + @Override
  250 + public void onSuccess(Void result) {
  251 + scheduleEdgeEventsCheck(session);
  252 + }
  253 +
  254 + @Override
  255 + public void onFailure(Throwable t) {
  256 + log.warn("[{}] Failed to process edge events for edge [{}]!", tenantId, session.getEdge().getId().getId(), t);
  257 + scheduleEdgeEventsCheck(session);
  258 + }
  259 + }, ctx.getGrpcCallbackExecutorService());
  260 + } else {
  261 + scheduleEdgeEventsCheck(session);
  262 + }
  263 + } finally {
  264 + newEventLock.unlock();
211 265 }
212 266 } catch (Exception e) {
213 267 log.warn("[{}] Failed to process edge events for edge [{}]!", tenantId, session.getEdge().getId().getId(), e);
214 268 }
215   - scheduleEdgeEventsCheck(session);
216 269 }, ctx.getEdgeEventStorageSettings().getNoRecordsSleepInterval(), TimeUnit.MILLISECONDS);
217   - sessionEdgeEventChecks.put(edgeId, schedule);
  270 + sessionEdgeEventChecks.put(edgeId, edgeEventCheckTask);
218 271 log.trace("[{}] Check edge event scheduled for edge [{}]", tenantId, edgeId.getId());
219 272 } else {
220 273 log.debug("[{}] Session was removed and edge event check schedule must not be started [{}]",
... ... @@ -236,7 +289,13 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i
236 289 private void onEdgeDisconnect(EdgeId edgeId) {
237 290 log.info("[{}] edge disconnected!", edgeId);
238 291 sessions.remove(edgeId);
239   - sessionNewEvents.remove(edgeId);
  292 + final Lock newEventLock = sessionNewEventsLocks.computeIfAbsent(edgeId, id -> new ReentrantLock());
  293 + newEventLock.lock();
  294 + try {
  295 + sessionNewEvents.remove(edgeId);
  296 + } finally {
  297 + newEventLock.unlock();
  298 + }
240 299 save(edgeId, DefaultDeviceStateService.ACTIVITY_STATE, false);
241 300 save(edgeId, DefaultDeviceStateService.LAST_DISCONNECT_TIME, System.currentTimeMillis());
242 301 cancelScheduleEdgeEventsCheck(edgeId);
... ...
... ... @@ -16,111 +16,76 @@
16 16 package org.thingsboard.server.service.edge.rpc;
17 17
18 18 import com.datastax.oss.driver.api.core.uuid.Uuids;
19   -import com.fasterxml.jackson.core.JsonProcessingException;
20 19 import com.fasterxml.jackson.databind.ObjectMapper;
21   -import com.fasterxml.jackson.databind.node.ObjectNode;
22 20 import com.google.common.util.concurrent.FutureCallback;
23 21 import com.google.common.util.concurrent.Futures;
24 22 import com.google.common.util.concurrent.ListenableFuture;
25   -import com.google.common.util.concurrent.MoreExecutors;
26   -import com.google.gson.JsonElement;
  23 +import com.google.common.util.concurrent.SettableFuture;
27 24 import io.grpc.stub.StreamObserver;
28 25 import lombok.Data;
29 26 import lombok.extern.slf4j.Slf4j;
30 27 import org.checkerframework.checker.nullness.qual.Nullable;
31   -import org.thingsboard.server.common.data.AdminSettings;
32   -import org.thingsboard.server.common.data.Customer;
33   -import org.thingsboard.server.common.data.Dashboard;
  28 +import org.thingsboard.common.util.JacksonUtil;
34 29 import org.thingsboard.server.common.data.DataConstants;
35   -import org.thingsboard.server.common.data.Device;
36   -import org.thingsboard.server.common.data.DeviceProfile;
37   -import org.thingsboard.server.common.data.EntityView;
38   -import org.thingsboard.server.common.data.HasCustomerId;
39   -import org.thingsboard.server.common.data.User;
40   -import org.thingsboard.server.common.data.alarm.Alarm;
41   -import org.thingsboard.server.common.data.asset.Asset;
  30 +import org.thingsboard.server.common.data.EdgeUtils;
42 31 import org.thingsboard.server.common.data.edge.Edge;
43 32 import org.thingsboard.server.common.data.edge.EdgeEvent;
44 33 import org.thingsboard.server.common.data.edge.EdgeEventActionType;
45   -import org.thingsboard.server.common.data.edge.EdgeEventType;
46   -import org.thingsboard.server.common.data.id.AlarmId;
47   -import org.thingsboard.server.common.data.id.AssetId;
48   -import org.thingsboard.server.common.data.id.CustomerId;
49   -import org.thingsboard.server.common.data.id.DashboardId;
50   -import org.thingsboard.server.common.data.id.DeviceId;
51   -import org.thingsboard.server.common.data.id.DeviceProfileId;
52 34 import org.thingsboard.server.common.data.id.EdgeId;
53   -import org.thingsboard.server.common.data.id.EntityId;
54   -import org.thingsboard.server.common.data.id.EntityViewId;
55   -import org.thingsboard.server.common.data.id.RuleChainId;
56 35 import org.thingsboard.server.common.data.id.TenantId;
57   -import org.thingsboard.server.common.data.id.UserId;
58   -import org.thingsboard.server.common.data.id.WidgetTypeId;
59   -import org.thingsboard.server.common.data.id.WidgetsBundleId;
60 36 import org.thingsboard.server.common.data.kv.AttributeKvEntry;
61 37 import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry;
62 38 import org.thingsboard.server.common.data.kv.LongDataEntry;
63 39 import org.thingsboard.server.common.data.page.PageData;
64   -import org.thingsboard.server.common.data.page.SortOrder;
65   -import org.thingsboard.server.common.data.page.TimePageLink;
66   -import org.thingsboard.server.common.data.relation.EntityRelation;
67   -import org.thingsboard.server.common.data.rule.RuleChain;
68   -import org.thingsboard.server.common.data.rule.RuleChainMetaData;
69   -import org.thingsboard.server.common.data.security.DeviceCredentials;
70   -import org.thingsboard.server.common.data.security.UserCredentials;
71   -import org.thingsboard.server.common.data.widget.WidgetType;
72   -import org.thingsboard.server.common.data.widget.WidgetsBundle;
73   -import org.thingsboard.server.common.transport.util.JsonUtils;
74   -import org.thingsboard.common.util.JacksonUtil;
75   -import org.thingsboard.server.gen.edge.AdminSettingsUpdateMsg;
76   -import org.thingsboard.server.gen.edge.AlarmUpdateMsg;
77   -import org.thingsboard.server.gen.edge.AssetUpdateMsg;
78   -import org.thingsboard.server.gen.edge.AttributesRequestMsg;
79   -import org.thingsboard.server.gen.edge.ConnectRequestMsg;
80   -import org.thingsboard.server.gen.edge.ConnectResponseCode;
81   -import org.thingsboard.server.gen.edge.ConnectResponseMsg;
82   -import org.thingsboard.server.gen.edge.CustomerUpdateMsg;
83   -import org.thingsboard.server.gen.edge.DashboardUpdateMsg;
84   -import org.thingsboard.server.gen.edge.DeviceCredentialsRequestMsg;
85   -import org.thingsboard.server.gen.edge.DeviceCredentialsUpdateMsg;
86   -import org.thingsboard.server.gen.edge.DeviceProfileUpdateMsg;
87   -import org.thingsboard.server.gen.edge.DeviceRpcCallMsg;
88   -import org.thingsboard.server.gen.edge.DeviceUpdateMsg;
89   -import org.thingsboard.server.gen.edge.DownlinkMsg;
90   -import org.thingsboard.server.gen.edge.DownlinkResponseMsg;
91   -import org.thingsboard.server.gen.edge.EdgeConfiguration;
92   -import org.thingsboard.server.gen.edge.EdgeUpdateMsg;
93   -import org.thingsboard.server.gen.edge.EntityDataProto;
94   -import org.thingsboard.server.gen.edge.EntityViewUpdateMsg;
95   -import org.thingsboard.server.gen.edge.RelationRequestMsg;
96   -import org.thingsboard.server.gen.edge.RelationUpdateMsg;
97   -import org.thingsboard.server.gen.edge.RequestMsg;
98   -import org.thingsboard.server.gen.edge.RequestMsgType;
99   -import org.thingsboard.server.gen.edge.ResponseMsg;
100   -import org.thingsboard.server.gen.edge.RuleChainMetadataRequestMsg;
101   -import org.thingsboard.server.gen.edge.RuleChainMetadataUpdateMsg;
102   -import org.thingsboard.server.gen.edge.RuleChainUpdateMsg;
103   -import org.thingsboard.server.gen.edge.UpdateMsgType;
104   -import org.thingsboard.server.gen.edge.UplinkMsg;
105   -import org.thingsboard.server.gen.edge.UplinkResponseMsg;
106   -import org.thingsboard.server.gen.edge.UserCredentialsRequestMsg;
107   -import org.thingsboard.server.gen.edge.UserCredentialsUpdateMsg;
108   -import org.thingsboard.server.gen.edge.WidgetTypeUpdateMsg;
109   -import org.thingsboard.server.gen.edge.WidgetsBundleUpdateMsg;
  40 +import org.thingsboard.server.common.data.page.PageLink;
  41 +import org.thingsboard.server.gen.edge.v1.AlarmUpdateMsg;
  42 +import org.thingsboard.server.gen.edge.v1.AttributesRequestMsg;
  43 +import org.thingsboard.server.gen.edge.v1.ConnectRequestMsg;
  44 +import org.thingsboard.server.gen.edge.v1.ConnectResponseCode;
  45 +import org.thingsboard.server.gen.edge.v1.ConnectResponseMsg;
  46 +import org.thingsboard.server.gen.edge.v1.DeviceCredentialsRequestMsg;
  47 +import org.thingsboard.server.gen.edge.v1.DeviceCredentialsUpdateMsg;
  48 +import org.thingsboard.server.gen.edge.v1.DeviceProfileDevicesRequestMsg;
  49 +import org.thingsboard.server.gen.edge.v1.DeviceRpcCallMsg;
  50 +import org.thingsboard.server.gen.edge.v1.DeviceUpdateMsg;
  51 +import org.thingsboard.server.gen.edge.v1.DownlinkMsg;
  52 +import org.thingsboard.server.gen.edge.v1.DownlinkResponseMsg;
  53 +import org.thingsboard.server.gen.edge.v1.EdgeConfiguration;
  54 +import org.thingsboard.server.gen.edge.v1.EdgeUpdateMsg;
  55 +import org.thingsboard.server.gen.edge.v1.EntityDataProto;
  56 +import org.thingsboard.server.gen.edge.v1.EntityViewsRequestMsg;
  57 +import org.thingsboard.server.gen.edge.v1.RelationRequestMsg;
  58 +import org.thingsboard.server.gen.edge.v1.RelationUpdateMsg;
  59 +import org.thingsboard.server.gen.edge.v1.RequestMsg;
  60 +import org.thingsboard.server.gen.edge.v1.RequestMsgType;
  61 +import org.thingsboard.server.gen.edge.v1.ResponseMsg;
  62 +import org.thingsboard.server.gen.edge.v1.RuleChainMetadataRequestMsg;
  63 +import org.thingsboard.server.gen.edge.v1.SyncCompletedMsg;
  64 +import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
  65 +import org.thingsboard.server.gen.edge.v1.UplinkMsg;
  66 +import org.thingsboard.server.gen.edge.v1.UplinkResponseMsg;
  67 +import org.thingsboard.server.gen.edge.v1.UserCredentialsRequestMsg;
  68 +import org.thingsboard.server.gen.edge.v1.WidgetBundleTypesRequestMsg;
110 69 import org.thingsboard.server.service.edge.EdgeContextComponent;
  70 +import org.thingsboard.server.service.edge.rpc.fetch.EdgeEventFetcher;
  71 +import org.thingsboard.server.service.edge.rpc.fetch.GeneralEdgeEventFetcher;
111 72
112 73 import java.io.Closeable;
113 74 import java.util.ArrayList;
114 75 import java.util.Collections;
  76 +import java.util.LinkedHashMap;
115 77 import java.util.List;
  78 +import java.util.Map;
  79 +import java.util.Objects;
116 80 import java.util.Optional;
117 81 import java.util.UUID;
118   -import java.util.concurrent.CountDownLatch;
119   -import java.util.concurrent.ExecutionException;
  82 +import java.util.concurrent.ScheduledExecutorService;
  83 +import java.util.concurrent.ScheduledFuture;
120 84 import java.util.concurrent.TimeUnit;
121 85 import java.util.concurrent.locks.ReentrantLock;
122 86 import java.util.function.BiConsumer;
123 87 import java.util.function.Consumer;
  88 +import java.util.stream.Collectors;
124 89
125 90 @Slf4j
126 91 @Data
... ... @@ -135,27 +100,31 @@ public final class EdgeGrpcSession implements Closeable {
135 100 private final Consumer<EdgeId> sessionCloseListener;
136 101 private final ObjectMapper mapper;
137 102
  103 + private final EdgeSessionState sessionState = new EdgeSessionState();
  104 +
138 105 private EdgeContextComponent ctx;
139 106 private Edge edge;
140 107 private StreamObserver<RequestMsg> inputStream;
141 108 private StreamObserver<ResponseMsg> outputStream;
142 109 private boolean connected;
  110 + private boolean syncCompleted;
143 111
144   - private CountDownLatch latch;
  112 + private ScheduledExecutorService sendDownlinkExecutorService;
145 113
146 114 EdgeGrpcSession(EdgeContextComponent ctx, StreamObserver<ResponseMsg> outputStream, BiConsumer<EdgeId, EdgeGrpcSession> sessionOpenListener,
147   - Consumer<EdgeId> sessionCloseListener, ObjectMapper mapper) {
  115 + Consumer<EdgeId> sessionCloseListener, ObjectMapper mapper, ScheduledExecutorService sendDownlinkExecutorService) {
148 116 this.sessionId = UUID.randomUUID();
149 117 this.ctx = ctx;
150 118 this.outputStream = outputStream;
151 119 this.sessionOpenListener = sessionOpenListener;
152 120 this.sessionCloseListener = sessionCloseListener;
153 121 this.mapper = mapper;
  122 + this.sendDownlinkExecutorService = sendDownlinkExecutorService;
154 123 initInputStream();
155 124 }
156 125
157 126 private void initInputStream() {
158   - this.inputStream = new StreamObserver<RequestMsg>() {
  127 + this.inputStream = new StreamObserver<>() {
159 128 @Override
160 129 public void onNext(RequestMsg requestMsg) {
161 130 if (!connected && requestMsg.getMsgType().equals(RequestMsgType.CONNECT_RPC_MESSAGE)) {
... ... @@ -169,15 +138,21 @@ public final class EdgeGrpcSession implements Closeable {
169 138 connected = true;
170 139 }
171 140 }
172   - if (connected && requestMsg.getMsgType().equals(RequestMsgType.SYNC_REQUEST_RPC_MESSAGE)) {
173   - ctx.getSyncEdgeService().sync(edge.getTenantId(), edge);
174   - }
175 141 if (connected) {
176   - if (requestMsg.getMsgType().equals(RequestMsgType.UPLINK_RPC_MESSAGE) && requestMsg.hasUplinkMsg()) {
177   - onUplinkMsg(requestMsg.getUplinkMsg());
  142 + if (requestMsg.getMsgType().equals(RequestMsgType.SYNC_REQUEST_RPC_MESSAGE)) {
  143 + if (requestMsg.hasSyncRequestMsg() && requestMsg.getSyncRequestMsg().getSyncRequired()) {
  144 + startSyncProcess(edge.getTenantId(), edge.getId());
  145 + } else {
  146 + syncCompleted = true;
  147 + }
178 148 }
179   - if (requestMsg.getMsgType().equals(RequestMsgType.UPLINK_RPC_MESSAGE) && requestMsg.hasDownlinkResponseMsg()) {
180   - onDownlinkResponse(requestMsg.getDownlinkResponseMsg());
  149 + if (requestMsg.getMsgType().equals(RequestMsgType.UPLINK_RPC_MESSAGE)) {
  150 + if (requestMsg.hasUplinkMsg()) {
  151 + onUplinkMsg(requestMsg.getUplinkMsg());
  152 + }
  153 + if (requestMsg.hasDownlinkResponseMsg()) {
  154 + onDownlinkResponse(requestMsg.getDownlinkResponseMsg());
  155 + }
181 156 }
182 157 }
183 158 }
... ... @@ -198,21 +173,65 @@ public final class EdgeGrpcSession implements Closeable {
198 173 if (edge != null) {
199 174 try {
200 175 sessionCloseListener.accept(edge.getId());
201   - } catch (Exception ignored) {}
  176 + } catch (Exception ignored) {
  177 + }
202 178 }
203 179 try {
204 180 outputStream.onCompleted();
205   - } catch (Exception ignored) {}
  181 + } catch (Exception ignored) {
  182 + }
206 183 }
207 184 };
208 185 }
209 186
  187 + public void startSyncProcess(TenantId tenantId, EdgeId edgeId) {
  188 + log.trace("[{}][{}] Staring edge sync process", tenantId, edgeId);
  189 + syncCompleted = false;
  190 + doSync(new EdgeSyncCursor(ctx, edge));
  191 + }
  192 +
  193 + private void doSync(EdgeSyncCursor cursor) {
  194 + if (cursor.hasNext()) {
  195 + log.info("[{}][{}] starting sync process, cursor current idx = {}", edge.getTenantId(), edge.getId(), cursor.getCurrentIdx());
  196 + ListenableFuture<UUID> uuidListenableFuture = startProcessingEdgeEvents(cursor.getNext());
  197 + Futures.addCallback(uuidListenableFuture, new FutureCallback<>() {
  198 + @Override
  199 + public void onSuccess(@Nullable UUID result) {
  200 + doSync(cursor);
  201 + }
  202 +
  203 + @Override
  204 + public void onFailure(Throwable t) {
  205 + log.error("[{}][{}] Exception during sync process", edge.getTenantId(), edge.getId(), t);
  206 + }
  207 + }, ctx.getGrpcCallbackExecutorService());
  208 + } else {
  209 + DownlinkMsg syncCompleteDownlinkMsg = DownlinkMsg.newBuilder()
  210 + .setDownlinkMsgId(EdgeUtils.nextPositiveInt())
  211 + .setSyncCompletedMsg(SyncCompletedMsg.newBuilder().build())
  212 + .build();
  213 + Futures.addCallback(sendDownlinkMsgsPack(Collections.singletonList(syncCompleteDownlinkMsg)), new FutureCallback<Void>() {
  214 + @Override
  215 + public void onSuccess(Void result) {
  216 + syncCompleted = true;
  217 + }
  218 +
  219 + @Override
  220 + public void onFailure(Throwable t) {
  221 + log.error("[{}][{}] Exception during sending sync complete", edge.getTenantId(), edge.getId(), t);
  222 + }
  223 + }, ctx.getGrpcCallbackExecutorService());
  224 + }
  225 + }
  226 +
210 227 private void onUplinkMsg(UplinkMsg uplinkMsg) {
211 228 ListenableFuture<List<Void>> future = processUplinkMsg(uplinkMsg);
212   - Futures.addCallback(future, new FutureCallback<List<Void>>() {
  229 + Futures.addCallback(future, new FutureCallback<>() {
213 230 @Override
214 231 public void onSuccess(@Nullable List<Void> result) {
215   - UplinkResponseMsg uplinkResponseMsg = UplinkResponseMsg.newBuilder().setSuccess(true).build();
  232 + UplinkResponseMsg uplinkResponseMsg = UplinkResponseMsg.newBuilder()
  233 + .setUplinkMsgId(uplinkMsg.getUplinkMsgId())
  234 + .setSuccess(true).build();
216 235 sendDownlinkMsg(ResponseMsg.newBuilder()
217 236 .setUplinkResponseMsg(uplinkResponseMsg)
218 237 .build());
... ... @@ -220,22 +239,32 @@ public final class EdgeGrpcSession implements Closeable {
220 239
221 240 @Override
222 241 public void onFailure(Throwable t) {
223   - UplinkResponseMsg uplinkResponseMsg = UplinkResponseMsg.newBuilder().setSuccess(false).setErrorMsg(t.getMessage()).build();
  242 + String errorMsg = t.getMessage() != null ? t.getMessage() : "";
  243 + UplinkResponseMsg uplinkResponseMsg = UplinkResponseMsg.newBuilder()
  244 + .setUplinkMsgId(uplinkMsg.getUplinkMsgId())
  245 + .setSuccess(false).setErrorMsg(errorMsg).build();
224 246 sendDownlinkMsg(ResponseMsg.newBuilder()
225 247 .setUplinkResponseMsg(uplinkResponseMsg)
226 248 .build());
227 249 }
228   - }, MoreExecutors.directExecutor());
  250 + }, ctx.getGrpcCallbackExecutorService());
229 251 }
230 252
231 253 private void onDownlinkResponse(DownlinkResponseMsg msg) {
232 254 try {
233 255 if (msg.getSuccess()) {
  256 + sessionState.getPendingMsgsMap().remove(msg.getDownlinkMsgId());
234 257 log.debug("[{}] Msg has been processed successfully! {}", edge.getRoutingKey(), msg);
235 258 } else {
236 259 log.error("[{}] Msg processing failed! Error msg: {}", edge.getRoutingKey(), msg.getErrorMsg());
237 260 }
238   - latch.countDown();
  261 + if (sessionState.getPendingMsgsMap().isEmpty()) {
  262 + log.debug("[{}] Pending msgs map is empty. Stopping current iteration {}", edge.getRoutingKey(), msg);
  263 + if (sessionState.getScheduledSendDownlinkTask() != null) {
  264 + sessionState.getScheduledSendDownlinkTask().cancel(false);
  265 + }
  266 + sessionState.getSendDownlinkMsgsFuture().set(null);
  267 + }
239 268 } catch (Exception e) {
240 269 log.error("[{}] Can't process downlink response message [{}]", this.sessionId, msg, e);
241 270 }
... ... @@ -244,8 +273,8 @@ public final class EdgeGrpcSession implements Closeable {
244 273 private void sendDownlinkMsg(ResponseMsg downlinkMsg) {
245 274 log.trace("[{}] Sending downlink msg [{}]", this.sessionId, downlinkMsg);
246 275 if (isConnected()) {
  276 + downlinkMsgLock.lock();
247 277 try {
248   - downlinkMsgLock.lock();
249 278 outputStream.onNext(downlinkMsg);
250 279 } catch (Exception e) {
251 280 log.error("[{}] Failed to send downlink message [{}]", this.sessionId, downlinkMsg, e);
... ... @@ -269,149 +298,192 @@ public final class EdgeGrpcSession implements Closeable {
269 298 sendDownlinkMsg(edgeConfigMsg);
270 299 }
271 300
272   - void processEdgeEvents() throws ExecutionException, InterruptedException {
273   - log.trace("[{}] processHandleMessages started", this.sessionId);
274   - if (isConnected()) {
  301 + ListenableFuture<Void> processEdgeEvents() throws Exception {
  302 + SettableFuture<Void> result = SettableFuture.create();
  303 + log.trace("[{}] starting processing edge events", this.sessionId);
  304 + if (isConnected() && isSyncCompleted()) {
275 305 Long queueStartTs = getQueueStartTs().get();
276   - TimePageLink pageLink = new TimePageLink(
277   - ctx.getEdgeEventStorageSettings().getMaxReadRecordsCount(),
278   - 0,
279   - null,
280   - new SortOrder("createdTime", SortOrder.Direction.ASC),
  306 + GeneralEdgeEventFetcher fetcher = new GeneralEdgeEventFetcher(
281 307 queueStartTs,
282   - null);
283   - PageData<EdgeEvent> pageData;
284   - UUID ifOffset = null;
285   - boolean success = true;
286   - do {
287   - pageData = ctx.getEdgeEventService().findEdgeEvents(edge.getTenantId(), edge.getId(), pageLink, true);
288   - if (isConnected() && !pageData.getData().isEmpty()) {
289   - log.trace("[{}] [{}] event(s) are going to be processed.", this.sessionId, pageData.getData().size());
290   - List<DownlinkMsg> downlinkMsgsPack = convertToDownlinkMsgsPack(pageData.getData());
291   - log.trace("[{}] [{}] downlink msg(s) are going to be send.", this.sessionId, downlinkMsgsPack.size());
292   -
293   - latch = new CountDownLatch(downlinkMsgsPack.size());
294   - for (DownlinkMsg downlinkMsg : downlinkMsgsPack) {
295   - sendDownlinkMsg(ResponseMsg.newBuilder()
296   - .setDownlinkMsg(downlinkMsg)
297   - .build());
  308 + ctx.getEdgeEventService());
  309 + ListenableFuture<UUID> ifOffsetFuture = startProcessingEdgeEvents(fetcher);
  310 + Futures.addCallback(ifOffsetFuture, new FutureCallback<>() {
  311 + @Override
  312 + public void onSuccess(@Nullable UUID ifOffset) {
  313 + if (ifOffset != null) {
  314 + Long newStartTs = Uuids.unixTimestamp(ifOffset);
  315 + ListenableFuture<List<Void>> updateFuture = updateQueueStartTs(newStartTs);
  316 + Futures.addCallback(updateFuture, new FutureCallback<>() {
  317 + @Override
  318 + public void onSuccess(@Nullable List<Void> list) {
  319 + log.debug("[{}] queue offset was updated [{}][{}]", sessionId, ifOffset, newStartTs);
  320 + result.set(null);
  321 + }
  322 +
  323 + @Override
  324 + public void onFailure(Throwable t) {
  325 + log.error("[{}] Failed to update queue offset [{}]", sessionId, ifOffset, t);
  326 + result.setException(t);
  327 + }
  328 + }, ctx.getGrpcCallbackExecutorService());
  329 + } else {
  330 + log.trace("[{}] ifOffset is null. Skipping iteration without db update", sessionId);
  331 + result.set(null);
298 332 }
  333 + }
299 334
300   - ifOffset = pageData.getData().get(pageData.getData().size() - 1).getUuidId();
301   -
302   - success = latch.await(10, TimeUnit.SECONDS);
303   - if (!success) {
304   - log.warn("[{}] Failed to deliver the batch: {}", this.sessionId, downlinkMsgsPack);
305   - }
  335 + @Override
  336 + public void onFailure(Throwable t) {
  337 + log.error("[{}] Failed to process events", sessionId, t);
  338 + result.setException(t);
306 339 }
307   - if (isConnected() && (!success || pageData.hasNext())) {
308   - try {
309   - Thread.sleep(ctx.getEdgeEventStorageSettings().getSleepIntervalBetweenBatches());
310   - } catch (InterruptedException e) {
311   - log.error("[{}] Error during sleep between batches", this.sessionId, e);
312   - }
313   - if (success) {
314   - pageLink = pageLink.nextPageLink();
  340 + }, ctx.getGrpcCallbackExecutorService());
  341 + } else {
  342 + log.trace("[{}] edge is not connected or sync is not completed. Skipping iteration", sessionId);
  343 + result.set(null);
  344 + }
  345 + return result;
  346 + }
  347 +
  348 + private ListenableFuture<UUID> startProcessingEdgeEvents(EdgeEventFetcher fetcher) {
  349 + SettableFuture<UUID> result = SettableFuture.create();
  350 + PageLink pageLink = fetcher.getPageLink(ctx.getEdgeEventStorageSettings().getMaxReadRecordsCount());
  351 + processEdgeEvents(fetcher, pageLink, result);
  352 + return result;
  353 + }
  354 +
  355 + private void processEdgeEvents(EdgeEventFetcher fetcher, PageLink pageLink, SettableFuture<UUID> result) {
  356 + try {
  357 + PageData<EdgeEvent> pageData = fetcher.fetchEdgeEvents(edge.getTenantId(), edge, pageLink);
  358 + if (isConnected() && !pageData.getData().isEmpty()) {
  359 + log.trace("[{}] [{}] event(s) are going to be processed.", this.sessionId, pageData.getData().size());
  360 + List<DownlinkMsg> downlinkMsgsPack = convertToDownlinkMsgsPack(pageData.getData());
  361 + Futures.addCallback(sendDownlinkMsgsPack(downlinkMsgsPack), new FutureCallback<Void>() {
  362 + @Override
  363 + public void onSuccess(@Nullable Void tmp) {
  364 + if (isConnected() && pageData.hasNext()) {
  365 + processEdgeEvents(fetcher, pageLink.nextPageLink(), result);
  366 + } else {
  367 + UUID ifOffset = pageData.getData().get(pageData.getData().size() - 1).getUuidId();
  368 + result.set(ifOffset);
  369 + }
315 370 }
316   - }
317   - } while (isConnected() && (!success || pageData.hasNext()));
318 371
319   - if (ifOffset != null) {
320   - Long newStartTs = Uuids.unixTimestamp(ifOffset);
321   - updateQueueStartTs(newStartTs);
  372 + @Override
  373 + public void onFailure(Throwable t) {
  374 + log.error("[{}] Failed to send downlink msgs pack", sessionId, t);
  375 + result.setException(t);
  376 + }
  377 + }, ctx.getGrpcCallbackExecutorService());
  378 + } else {
  379 + log.trace("[{}] no event(s) found. Stop processing edge events", this.sessionId);
  380 + result.set(null);
322 381 }
  382 + } catch (Exception e) {
  383 + log.error("[{}] Failed to fetch edge events", this.sessionId, e);
  384 + result.setException(e);
323 385 }
324   - log.trace("[{}] processHandleMessages finished", this.sessionId);
325 386 }
326 387
327   - private List<DownlinkMsg> convertToDownlinkMsgsPack(List<EdgeEvent> edgeEvents) {
328   - List<DownlinkMsg> result = new ArrayList<>();
329   - for (EdgeEvent edgeEvent : edgeEvents) {
330   - log.trace("[{}] Processing edge event [{}]", this.sessionId, edgeEvent);
  388 + private ListenableFuture<Void> sendDownlinkMsgsPack(List<DownlinkMsg> downlinkMsgsPack) {
  389 + if (sessionState.getSendDownlinkMsgsFuture() != null && !sessionState.getSendDownlinkMsgsFuture().isDone()) {
  390 + String erroMsg = "[" + this.sessionId + "] Previous send downdlink future was not properly completed, stopping it now";
  391 + log.error(erroMsg);
  392 + sessionState.getSendDownlinkMsgsFuture().setException(new RuntimeException(erroMsg));
  393 + }
  394 + sessionState.setSendDownlinkMsgsFuture(SettableFuture.create());
  395 + sessionState.getPendingMsgsMap().clear();
  396 + downlinkMsgsPack.forEach(msg -> sessionState.getPendingMsgsMap().put(msg.getDownlinkMsgId(), msg));
  397 + scheduleDownlinkMsgsPackSend(true);
  398 + return sessionState.getSendDownlinkMsgsFuture();
  399 + }
  400 +
  401 + private void scheduleDownlinkMsgsPackSend(boolean firstRun) {
  402 + Runnable sendDownlinkMsgsTask = () -> {
331 403 try {
332   - DownlinkMsg downlinkMsg = null;
333   - switch (edgeEvent.getAction()) {
334   - case UPDATED:
335   - case ADDED:
336   - case DELETED:
337   - case ASSIGNED_TO_EDGE:
338   - case UNASSIGNED_FROM_EDGE:
339   - case ALARM_ACK:
340   - case ALARM_CLEAR:
341   - case CREDENTIALS_UPDATED:
342   - case RELATION_ADD_OR_UPDATE:
343   - case RELATION_DELETED:
344   - case ASSIGNED_TO_CUSTOMER:
345   - case UNASSIGNED_FROM_CUSTOMER:
346   - downlinkMsg = processEntityMessage(edgeEvent, edgeEvent.getAction());
347   - break;
348   - case ATTRIBUTES_UPDATED:
349   - case POST_ATTRIBUTES:
350   - case ATTRIBUTES_DELETED:
351   - case TIMESERIES_UPDATED:
352   - downlinkMsg = processTelemetryMessage(edgeEvent);
353   - break;
354   - case CREDENTIALS_REQUEST:
355   - downlinkMsg = processCredentialsRequestMessage(edgeEvent);
356   - break;
357   - case ENTITY_MERGE_REQUEST:
358   - downlinkMsg = processEntityMergeRequestMessage(edgeEvent);
359   - break;
360   - case RPC_CALL:
361   - downlinkMsg = processRpcCallMsg(edgeEvent);
362   - break;
363   - }
364   - if (downlinkMsg != null) {
365   - result.add(downlinkMsg);
  404 + if (isConnected() && sessionState.getPendingMsgsMap().values().size() > 0) {
  405 + if (!firstRun) {
  406 + log.warn("[{}] Failed to deliver the batch: {}", this.sessionId, sessionState.getPendingMsgsMap().values());
  407 + }
  408 + log.trace("[{}] [{}] downlink msg(s) are going to be send.", this.sessionId, sessionState.getPendingMsgsMap().values().size());
  409 + List<DownlinkMsg> copy = new ArrayList<>(sessionState.getPendingMsgsMap().values());
  410 + for (DownlinkMsg downlinkMsg : copy) {
  411 + sendDownlinkMsg(ResponseMsg.newBuilder()
  412 + .setDownlinkMsg(downlinkMsg)
  413 + .build());
  414 + }
  415 + scheduleDownlinkMsgsPackSend(false);
  416 + } else {
  417 + sessionState.getSendDownlinkMsgsFuture().set(null);
366 418 }
367 419 } catch (Exception e) {
368   - log.error("Exception during processing records from queue", e);
  420 + sessionState.getSendDownlinkMsgsFuture().setException(e);
369 421 }
  422 + };
  423 +
  424 + if (firstRun) {
  425 + sendDownlinkExecutorService.submit(sendDownlinkMsgsTask);
  426 + } else {
  427 + sessionState.setScheduledSendDownlinkTask(
  428 + sendDownlinkExecutorService.schedule(
  429 + sendDownlinkMsgsTask,
  430 + ctx.getEdgeEventStorageSettings().getSleepIntervalBetweenBatches(),
  431 + TimeUnit.MILLISECONDS)
  432 + );
370 433 }
371   - return result;
  434 +
372 435 }
373 436
374   - private DownlinkMsg processEntityMergeRequestMessage(EdgeEvent edgeEvent) {
  437 + private DownlinkMsg convertToDownlinkMsg(EdgeEvent edgeEvent) {
  438 + log.trace("[{}][{}] converting edge event to downlink msg [{}]", edge.getTenantId(), this.sessionId, edgeEvent);
375 439 DownlinkMsg downlinkMsg = null;
376   - if (EdgeEventType.DEVICE.equals(edgeEvent.getType())) {
377   - DeviceId deviceId = new DeviceId(edgeEvent.getEntityId());
378   - Device device = ctx.getDeviceService().findDeviceById(edge.getTenantId(), deviceId);
379   - CustomerId customerId = getCustomerIdIfEdgeAssignedToCustomer(device);
380   - String conflictName = null;
381   - if(edgeEvent.getBody() != null) {
382   - conflictName = edgeEvent.getBody().get("conflictName").asText();
  440 + try {
  441 + switch (edgeEvent.getAction()) {
  442 + case UPDATED:
  443 + case ADDED:
  444 + case DELETED:
  445 + case ASSIGNED_TO_EDGE:
  446 + case UNASSIGNED_FROM_EDGE:
  447 + case ALARM_ACK:
  448 + case ALARM_CLEAR:
  449 + case CREDENTIALS_UPDATED:
  450 + case RELATION_ADD_OR_UPDATE:
  451 + case RELATION_DELETED:
  452 + case ASSIGNED_TO_CUSTOMER:
  453 + case UNASSIGNED_FROM_CUSTOMER:
  454 + downlinkMsg = processEntityMessage(edgeEvent, edgeEvent.getAction());
  455 + log.trace("[{}][{}] entity message processed [{}]", edgeEvent.getTenantId(), this.sessionId, downlinkMsg);
  456 + break;
  457 + case ATTRIBUTES_UPDATED:
  458 + case POST_ATTRIBUTES:
  459 + case ATTRIBUTES_DELETED:
  460 + case TIMESERIES_UPDATED:
  461 + downlinkMsg = ctx.getTelemetryProcessor().processTelemetryMessageToEdge(edgeEvent);
  462 + break;
  463 + case CREDENTIALS_REQUEST:
  464 + downlinkMsg = ctx.getEntityProcessor().processCredentialsRequestMessageToEdge(edgeEvent);
  465 + break;
  466 + case ENTITY_MERGE_REQUEST:
  467 + downlinkMsg = ctx.getEntityProcessor().processEntityMergeRequestMessageToEdge(edge, edgeEvent);
  468 + break;
  469 + case RPC_CALL:
  470 + downlinkMsg = ctx.getDeviceProcessor().processRpcCallMsgToEdge(edgeEvent);
  471 + break;
  472 + default:
  473 + log.warn("[{}][{}] Unsupported action type [{}]", edge.getTenantId(), this.sessionId, edgeEvent.getAction());
383 474 }
384   - DeviceUpdateMsg d = ctx.getDeviceMsgConstructor()
385   - .constructDeviceUpdatedMsg(UpdateMsgType.ENTITY_MERGE_RPC_MESSAGE, device, customerId, conflictName);
386   - downlinkMsg = DownlinkMsg.newBuilder()
387   - .addAllDeviceUpdateMsg(Collections.singletonList(d))
388   - .build();
  475 + } catch (Exception e) {
  476 + log.error("[{}][{}] Exception during converting edge event to downlink msg", edge.getTenantId(), this.sessionId, e);
389 477 }
390 478 return downlinkMsg;
391 479 }
392 480
393   - private DownlinkMsg processRpcCallMsg(EdgeEvent edgeEvent) {
394   - log.trace("Executing processRpcCall, edgeEvent [{}]", edgeEvent);
395   - DeviceRpcCallMsg deviceRpcCallMsg =
396   - ctx.getDeviceMsgConstructor().constructDeviceRpcCallMsg(edgeEvent.getEntityId(), edgeEvent.getBody());
397   - return DownlinkMsg.newBuilder()
398   - .addAllDeviceRpcCallMsg(Collections.singletonList(deviceRpcCallMsg))
399   - .build();
400   - }
401   -
402   - private DownlinkMsg processCredentialsRequestMessage(EdgeEvent edgeEvent) {
403   - DownlinkMsg downlinkMsg = null;
404   - if (EdgeEventType.DEVICE.equals(edgeEvent.getType())) {
405   - DeviceId deviceId = new DeviceId(edgeEvent.getEntityId());
406   - DeviceCredentialsRequestMsg deviceCredentialsRequestMsg = DeviceCredentialsRequestMsg.newBuilder()
407   - .setDeviceIdMSB(deviceId.getId().getMostSignificantBits())
408   - .setDeviceIdLSB(deviceId.getId().getLeastSignificantBits())
409   - .build();
410   - DownlinkMsg.Builder builder = DownlinkMsg.newBuilder()
411   - .addAllDeviceCredentialsRequestMsg(Collections.singletonList(deviceCredentialsRequestMsg));
412   - downlinkMsg = builder.build();
413   - }
414   - return downlinkMsg;
  481 + private List<DownlinkMsg> convertToDownlinkMsgsPack(List<EdgeEvent> edgeEvents) {
  482 + return edgeEvents
  483 + .stream()
  484 + .map(this::convertToDownlinkMsg)
  485 + .filter(Objects::nonNull)
  486 + .collect(Collectors.toList());
415 487 }
416 488
417 489 private ListenableFuture<Long> getQueueStartTs() {
... ... @@ -424,52 +496,14 @@ public final class EdgeGrpcSession implements Closeable {
424 496 } else {
425 497 return 0L;
426 498 }
427   - }, ctx.getDbCallbackExecutor());
  499 + }, ctx.getGrpcCallbackExecutorService());
428 500 }
429 501
430   - private void updateQueueStartTs(Long newStartTs) {
  502 + private ListenableFuture<List<Void>> updateQueueStartTs(Long newStartTs) {
431 503 log.trace("[{}] updating QueueStartTs [{}][{}]", this.sessionId, edge.getId(), newStartTs);
432 504 newStartTs = ++newStartTs; // increments ts by 1 - next edge event search starts from current offset + 1
433 505 List<AttributeKvEntry> attributes = Collections.singletonList(new BaseAttributeKvEntry(new LongDataEntry(QUEUE_START_TS_ATTR_KEY, newStartTs), System.currentTimeMillis()));
434   - ctx.getAttributesService().save(edge.getTenantId(), edge.getId(), DataConstants.SERVER_SCOPE, attributes);
435   - }
436   -
437   - private DownlinkMsg processTelemetryMessage(EdgeEvent edgeEvent) {
438   - log.trace("[{}] Executing processTelemetryMessage, edgeEvent [{}]", this.sessionId, edgeEvent);
439   - EntityId entityId = null;
440   - switch (edgeEvent.getType()) {
441   - case DEVICE:
442   - entityId = new DeviceId(edgeEvent.getEntityId());
443   - break;
444   - case ASSET:
445   - entityId = new AssetId(edgeEvent.getEntityId());
446   - break;
447   - case ENTITY_VIEW:
448   - entityId = new EntityViewId(edgeEvent.getEntityId());
449   - break;
450   - case DASHBOARD:
451   - entityId = new DashboardId(edgeEvent.getEntityId());
452   - break;
453   - case TENANT:
454   - entityId = new TenantId(edgeEvent.getEntityId());
455   - break;
456   - case CUSTOMER:
457   - entityId = new CustomerId(edgeEvent.getEntityId());
458   - break;
459   - case EDGE:
460   - entityId = new EdgeId(edgeEvent.getEntityId());
461   - break;
462   - }
463   - DownlinkMsg downlinkMsg = null;
464   - if (entityId != null) {
465   - log.debug("[{}] Sending telemetry data msg, entityId [{}], body [{}]", this.sessionId, edgeEvent.getEntityId(), edgeEvent.getBody());
466   - try {
467   - downlinkMsg = constructEntityDataProtoMsg(entityId, edgeEvent.getAction(), JsonUtils.parse(mapper.writeValueAsString(edgeEvent.getBody())));
468   - } catch (Exception e) {
469   - log.warn("[{}] Can't send telemetry data msg, entityId [{}], body [{}]", this.sessionId, edgeEvent.getEntityId(), edgeEvent.getBody(), e);
470   - }
471   - }
472   - return downlinkMsg;
  506 + return ctx.getAttributesService().save(edge.getTenantId(), edge.getId(), DataConstants.SERVER_SCOPE, attributes);
473 507 }
474 508
475 509 private DownlinkMsg processEntityMessage(EdgeEvent edgeEvent, EdgeEventActionType action) {
... ... @@ -477,411 +511,39 @@ public final class EdgeGrpcSession implements Closeable {
477 511 log.trace("Executing processEntityMessage, edgeEvent [{}], action [{}], msgType [{}]", edgeEvent, action, msgType);
478 512 switch (edgeEvent.getType()) {
479 513 case DEVICE:
480   - return processDevice(edgeEvent, msgType, action);
  514 + return ctx.getDeviceProcessor().processDeviceToEdge(edge, edgeEvent, msgType, action);
481 515 case DEVICE_PROFILE:
482   - return processDeviceProfile(edgeEvent, msgType, action);
  516 + return ctx.getDeviceProfileProcessor().processDeviceProfileToEdge(edgeEvent, msgType, action);
483 517 case ASSET:
484   - return processAsset(edgeEvent, msgType, action);
  518 + return ctx.getAssetProcessor().processAssetToEdge(edge, edgeEvent, msgType, action);
485 519 case ENTITY_VIEW:
486   - return processEntityView(edgeEvent, msgType, action);
  520 + return ctx.getEntityViewProcessor().processEntityViewToEdge(edge, edgeEvent, msgType, action);
487 521 case DASHBOARD:
488   - return processDashboard(edgeEvent, msgType, action);
  522 + return ctx.getDashboardProcessor().processDashboardToEdge(edge, edgeEvent, msgType, action);
489 523 case CUSTOMER:
490   - return processCustomer(edgeEvent, msgType, action);
  524 + return ctx.getCustomerProcessor().processCustomerToEdge(edgeEvent, msgType, action);
491 525 case RULE_CHAIN:
492   - return processRuleChain(edgeEvent, msgType, action);
  526 + return ctx.getRuleChainProcessor().processRuleChainToEdge(edge, edgeEvent, msgType, action);
493 527 case RULE_CHAIN_METADATA:
494   - return processRuleChainMetadata(edgeEvent, msgType);
  528 + return ctx.getRuleChainProcessor().processRuleChainMetadataToEdge(edgeEvent, msgType);
495 529 case ALARM:
496   - return processAlarm(edgeEvent, msgType);
  530 + return ctx.getAlarmProcessor().processAlarmToEdge(edge, edgeEvent, msgType);
497 531 case USER:
498   - return processUser(edgeEvent, msgType, action);
  532 + return ctx.getUserProcessor().processUserToEdge(edge, edgeEvent, msgType, action);
499 533 case RELATION:
500   - return processRelation(edgeEvent, msgType);
  534 + return ctx.getRelationProcessor().processRelationToEdge(edgeEvent, msgType);
501 535 case WIDGETS_BUNDLE:
502   - return processWidgetsBundle(edgeEvent, msgType, action);
  536 + return ctx.getWidgetBundleProcessor().processWidgetsBundleToEdge(edgeEvent, msgType, action);
503 537 case WIDGET_TYPE:
504   - return processWidgetType(edgeEvent, msgType, action);
  538 + return ctx.getWidgetTypeProcessor().processWidgetTypeToEdge(edgeEvent, msgType, action);
505 539 case ADMIN_SETTINGS:
506   - return processAdminSettings(edgeEvent);
  540 + return ctx.getAdminSettingsProcessor().processAdminSettingsToEdge(edgeEvent);
507 541 default:
508 542 log.warn("Unsupported edge event type [{}]", edgeEvent);
509 543 return null;
510 544 }
511 545 }
512 546
513   - private DownlinkMsg processDevice(EdgeEvent edgeEvent, UpdateMsgType msgType, EdgeEventActionType edgeEdgeEventActionType) {
514   - DeviceId deviceId = new DeviceId(edgeEvent.getEntityId());
515   - DownlinkMsg downlinkMsg = null;
516   - switch (edgeEdgeEventActionType) {
517   - case ADDED:
518   - case UPDATED:
519   - case ASSIGNED_TO_EDGE:
520   - case ASSIGNED_TO_CUSTOMER:
521   - case UNASSIGNED_FROM_CUSTOMER:
522   - Device device = ctx.getDeviceService().findDeviceById(edgeEvent.getTenantId(), deviceId);
523   - if (device != null) {
524   - CustomerId customerId = getCustomerIdIfEdgeAssignedToCustomer(device);
525   - DeviceUpdateMsg deviceUpdateMsg =
526   - ctx.getDeviceMsgConstructor().constructDeviceUpdatedMsg(msgType, device, customerId, null);
527   - downlinkMsg = DownlinkMsg.newBuilder()
528   - .addAllDeviceUpdateMsg(Collections.singletonList(deviceUpdateMsg))
529   - .build();
530   - }
531   - break;
532   - case DELETED:
533   - case UNASSIGNED_FROM_EDGE:
534   - DeviceUpdateMsg deviceUpdateMsg =
535   - ctx.getDeviceMsgConstructor().constructDeviceDeleteMsg(deviceId);
536   - downlinkMsg = DownlinkMsg.newBuilder()
537   - .addAllDeviceUpdateMsg(Collections.singletonList(deviceUpdateMsg))
538   - .build();
539   - break;
540   - case CREDENTIALS_UPDATED:
541   - DeviceCredentials deviceCredentials = ctx.getDeviceCredentialsService().findDeviceCredentialsByDeviceId(edge.getTenantId(), deviceId);
542   - if (deviceCredentials != null) {
543   - DeviceCredentialsUpdateMsg deviceCredentialsUpdateMsg =
544   - ctx.getDeviceMsgConstructor().constructDeviceCredentialsUpdatedMsg(deviceCredentials);
545   - downlinkMsg = DownlinkMsg.newBuilder()
546   - .addAllDeviceCredentialsUpdateMsg(Collections.singletonList(deviceCredentialsUpdateMsg))
547   - .build();
548   - }
549   - break;
550   - }
551   - log.trace("[{}] device processed [{}]", this.sessionId, downlinkMsg);
552   - return downlinkMsg;
553   - }
554   -
555   - private DownlinkMsg processDeviceProfile(EdgeEvent edgeEvent, UpdateMsgType msgType, EdgeEventActionType action) {
556   - DeviceProfileId deviceProfileId = new DeviceProfileId(edgeEvent.getEntityId());
557   - DownlinkMsg downlinkMsg = null;
558   - switch (action) {
559   - case ADDED:
560   - case UPDATED:
561   - DeviceProfile deviceProfile = ctx.getDeviceProfileService().findDeviceProfileById(edgeEvent.getTenantId(), deviceProfileId);
562   - if (deviceProfile != null) {
563   - DeviceProfileUpdateMsg deviceProfileUpdateMsg =
564   - ctx.getDeviceProfileMsgConstructor().constructDeviceProfileUpdatedMsg(msgType, deviceProfile);
565   - downlinkMsg = DownlinkMsg.newBuilder()
566   - .addAllDeviceProfileUpdateMsg(Collections.singletonList(deviceProfileUpdateMsg))
567   - .build();
568   - }
569   - break;
570   - case DELETED:
571   - DeviceProfileUpdateMsg deviceProfileUpdateMsg =
572   - ctx.getDeviceProfileMsgConstructor().constructDeviceProfileDeleteMsg(deviceProfileId);
573   - downlinkMsg = DownlinkMsg.newBuilder()
574   - .addAllDeviceProfileUpdateMsg(Collections.singletonList(deviceProfileUpdateMsg))
575   - .build();
576   - break;
577   - }
578   - log.trace("[{}] device profile processed [{}]", this.sessionId, downlinkMsg);
579   - return downlinkMsg;
580   - }
581   -
582   - private DownlinkMsg processAsset(EdgeEvent edgeEvent, UpdateMsgType msgType, EdgeEventActionType action) {
583   - AssetId assetId = new AssetId(edgeEvent.getEntityId());
584   - DownlinkMsg downlinkMsg = null;
585   - switch (action) {
586   - case ADDED:
587   - case UPDATED:
588   - case ASSIGNED_TO_EDGE:
589   - case ASSIGNED_TO_CUSTOMER:
590   - case UNASSIGNED_FROM_CUSTOMER:
591   - Asset asset = ctx.getAssetService().findAssetById(edgeEvent.getTenantId(), assetId);
592   - if (asset != null) {
593   - CustomerId customerId = getCustomerIdIfEdgeAssignedToCustomer(asset);
594   - AssetUpdateMsg assetUpdateMsg =
595   - ctx.getAssetMsgConstructor().constructAssetUpdatedMsg(msgType, asset, customerId);
596   - downlinkMsg = DownlinkMsg.newBuilder()
597   - .addAllAssetUpdateMsg(Collections.singletonList(assetUpdateMsg))
598   - .build();
599   - }
600   - break;
601   - case DELETED:
602   - case UNASSIGNED_FROM_EDGE:
603   - AssetUpdateMsg assetUpdateMsg =
604   - ctx.getAssetMsgConstructor().constructAssetDeleteMsg(assetId);
605   - downlinkMsg = DownlinkMsg.newBuilder()
606   - .addAllAssetUpdateMsg(Collections.singletonList(assetUpdateMsg))
607   - .build();
608   - break;
609   - }
610   - log.trace("[{}] asset processed [{}]", this.sessionId, downlinkMsg);
611   - return downlinkMsg;
612   - }
613   -
614   - private DownlinkMsg processEntityView(EdgeEvent edgeEvent, UpdateMsgType msgType, EdgeEventActionType action) {
615   - EntityViewId entityViewId = new EntityViewId(edgeEvent.getEntityId());
616   - DownlinkMsg downlinkMsg = null;
617   - switch (action) {
618   - case ADDED:
619   - case UPDATED:
620   - case ASSIGNED_TO_EDGE:
621   - case ASSIGNED_TO_CUSTOMER:
622   - case UNASSIGNED_FROM_CUSTOMER:
623   - EntityView entityView = ctx.getEntityViewService().findEntityViewById(edgeEvent.getTenantId(), entityViewId);
624   - if (entityView != null) {
625   - CustomerId customerId = getCustomerIdIfEdgeAssignedToCustomer(entityView);
626   - EntityViewUpdateMsg entityViewUpdateMsg =
627   - ctx.getEntityViewMsgConstructor().constructEntityViewUpdatedMsg(msgType, entityView, customerId);
628   - downlinkMsg = DownlinkMsg.newBuilder()
629   - .addAllEntityViewUpdateMsg(Collections.singletonList(entityViewUpdateMsg))
630   - .build();
631   - }
632   - break;
633   - case DELETED:
634   - case UNASSIGNED_FROM_EDGE:
635   - EntityViewUpdateMsg entityViewUpdateMsg =
636   - ctx.getEntityViewMsgConstructor().constructEntityViewDeleteMsg(entityViewId);
637   - downlinkMsg = DownlinkMsg.newBuilder()
638   - .addAllEntityViewUpdateMsg(Collections.singletonList(entityViewUpdateMsg))
639   - .build();
640   - break;
641   - }
642   - log.trace("[{}] entity view processed [{}]", this.sessionId, downlinkMsg);
643   - return downlinkMsg;
644   - }
645   -
646   - private DownlinkMsg processDashboard(EdgeEvent edgeEvent, UpdateMsgType msgType, EdgeEventActionType action) {
647   - DashboardId dashboardId = new DashboardId(edgeEvent.getEntityId());
648   - DownlinkMsg downlinkMsg = null;
649   - switch (action) {
650   - case ADDED:
651   - case UPDATED:
652   - case ASSIGNED_TO_EDGE:
653   - case ASSIGNED_TO_CUSTOMER:
654   - case UNASSIGNED_FROM_CUSTOMER:
655   - Dashboard dashboard = ctx.getDashboardService().findDashboardById(edgeEvent.getTenantId(), dashboardId);
656   - if (dashboard != null) {
657   - CustomerId customerId = null;
658   - if (!edge.getCustomerId().isNullUid() && dashboard.isAssignedToCustomer(edge.getCustomerId())) {
659   - customerId = edge.getCustomerId();
660   - }
661   - DashboardUpdateMsg dashboardUpdateMsg =
662   - ctx.getDashboardMsgConstructor().constructDashboardUpdatedMsg(msgType, dashboard, customerId);
663   - downlinkMsg = DownlinkMsg.newBuilder()
664   - .addAllDashboardUpdateMsg(Collections.singletonList(dashboardUpdateMsg))
665   - .build();
666   - }
667   - break;
668   - case DELETED:
669   - case UNASSIGNED_FROM_EDGE:
670   - DashboardUpdateMsg dashboardUpdateMsg =
671   - ctx.getDashboardMsgConstructor().constructDashboardDeleteMsg(dashboardId);
672   - downlinkMsg = DownlinkMsg.newBuilder()
673   - .addAllDashboardUpdateMsg(Collections.singletonList(dashboardUpdateMsg))
674   - .build();
675   - break;
676   - }
677   - log.trace("[{}] dashboard processed [{}]", this.sessionId, downlinkMsg);
678   - return downlinkMsg;
679   - }
680   -
681   - private DownlinkMsg processCustomer(EdgeEvent edgeEvent, UpdateMsgType msgType, EdgeEventActionType action) {
682   - CustomerId customerId = new CustomerId(edgeEvent.getEntityId());
683   - DownlinkMsg downlinkMsg = null;
684   - switch (action) {
685   - case ADDED:
686   - case UPDATED:
687   - Customer customer = ctx.getCustomerService().findCustomerById(edgeEvent.getTenantId(), customerId);
688   - if (customer != null) {
689   - CustomerUpdateMsg customerUpdateMsg =
690   - ctx.getCustomerMsgConstructor().constructCustomerUpdatedMsg(msgType, customer);
691   - downlinkMsg = DownlinkMsg.newBuilder()
692   - .addAllCustomerUpdateMsg(Collections.singletonList(customerUpdateMsg))
693   - .build();
694   - }
695   - break;
696   - case DELETED:
697   - CustomerUpdateMsg customerUpdateMsg =
698   - ctx.getCustomerMsgConstructor().constructCustomerDeleteMsg(customerId);
699   - downlinkMsg = DownlinkMsg.newBuilder()
700   - .addAllCustomerUpdateMsg(Collections.singletonList(customerUpdateMsg))
701   - .build();
702   - break;
703   - }
704   - log.trace("[{}] customer processed [{}]", this.sessionId, downlinkMsg);
705   - return downlinkMsg;
706   - }
707   -
708   - private DownlinkMsg processRuleChain(EdgeEvent edgeEvent, UpdateMsgType msgType, EdgeEventActionType action) {
709   - RuleChainId ruleChainId = new RuleChainId(edgeEvent.getEntityId());
710   - DownlinkMsg downlinkMsg = null;
711   - switch (action) {
712   - case ADDED:
713   - case UPDATED:
714   - case ASSIGNED_TO_EDGE:
715   - RuleChain ruleChain = ctx.getRuleChainService().findRuleChainById(edgeEvent.getTenantId(), ruleChainId);
716   - if (ruleChain != null) {
717   - RuleChainUpdateMsg ruleChainUpdateMsg =
718   - ctx.getRuleChainMsgConstructor().constructRuleChainUpdatedMsg(edge.getRootRuleChainId(), msgType, ruleChain);
719   - downlinkMsg = DownlinkMsg.newBuilder()
720   - .addAllRuleChainUpdateMsg(Collections.singletonList(ruleChainUpdateMsg))
721   - .build();
722   - }
723   - break;
724   - case DELETED:
725   - case UNASSIGNED_FROM_EDGE:
726   - downlinkMsg = DownlinkMsg.newBuilder()
727   - .addAllRuleChainUpdateMsg(Collections.singletonList(ctx.getRuleChainMsgConstructor().constructRuleChainDeleteMsg(ruleChainId)))
728   - .build();
729   - break;
730   - }
731   - log.trace("[{}] rule chain processed [{}]", this.sessionId, downlinkMsg);
732   - return downlinkMsg;
733   - }
734   -
735   - private DownlinkMsg processRuleChainMetadata(EdgeEvent edgeEvent, UpdateMsgType msgType) {
736   - RuleChainId ruleChainId = new RuleChainId(edgeEvent.getEntityId());
737   - RuleChain ruleChain = ctx.getRuleChainService().findRuleChainById(edgeEvent.getTenantId(), ruleChainId);
738   - DownlinkMsg downlinkMsg = null;
739   - if (ruleChain != null) {
740   - RuleChainMetaData ruleChainMetaData = ctx.getRuleChainService().loadRuleChainMetaData(edgeEvent.getTenantId(), ruleChainId);
741   - RuleChainMetadataUpdateMsg ruleChainMetadataUpdateMsg =
742   - ctx.getRuleChainMsgConstructor().constructRuleChainMetadataUpdatedMsg(msgType, ruleChainMetaData);
743   - if (ruleChainMetadataUpdateMsg != null) {
744   - downlinkMsg = DownlinkMsg.newBuilder()
745   - .addAllRuleChainMetadataUpdateMsg(Collections.singletonList(ruleChainMetadataUpdateMsg))
746   - .build();
747   - }
748   - }
749   - log.trace("[{}] rule chain metadata processed [{}]", this.sessionId, downlinkMsg);
750   - return downlinkMsg;
751   - }
752   -
753   - private DownlinkMsg processUser(EdgeEvent edgeEvent, UpdateMsgType msgType, EdgeEventActionType edgeEdgeEventActionType) {
754   - UserId userId = new UserId(edgeEvent.getEntityId());
755   - DownlinkMsg downlinkMsg = null;
756   - switch (edgeEdgeEventActionType) {
757   - case ADDED:
758   - case UPDATED:
759   - User user = ctx.getUserService().findUserById(edgeEvent.getTenantId(), userId);
760   - if (user != null) {
761   - CustomerId customerId = getCustomerIdIfEdgeAssignedToCustomer(user);
762   - downlinkMsg = DownlinkMsg.newBuilder()
763   - .addAllUserUpdateMsg(Collections.singletonList(ctx.getUserMsgConstructor().constructUserUpdatedMsg(msgType, user, customerId)))
764   - .build();
765   - }
766   - break;
767   - case DELETED:
768   - downlinkMsg = DownlinkMsg.newBuilder()
769   - .addAllUserUpdateMsg(Collections.singletonList(ctx.getUserMsgConstructor().constructUserDeleteMsg(userId)))
770   - .build();
771   - break;
772   - case CREDENTIALS_UPDATED:
773   - UserCredentials userCredentialsByUserId = ctx.getUserService().findUserCredentialsByUserId(edge.getTenantId(), userId);
774   - if (userCredentialsByUserId != null && userCredentialsByUserId.isEnabled()) {
775   - UserCredentialsUpdateMsg userCredentialsUpdateMsg =
776   - ctx.getUserMsgConstructor().constructUserCredentialsUpdatedMsg(userCredentialsByUserId);
777   - downlinkMsg = DownlinkMsg.newBuilder()
778   - .addAllUserCredentialsUpdateMsg(Collections.singletonList(userCredentialsUpdateMsg))
779   - .build();
780   - }
781   - }
782   - log.trace("[{}] user processed [{}]", this.sessionId, downlinkMsg);
783   - return downlinkMsg;
784   - }
785   -
786   - private CustomerId getCustomerIdIfEdgeAssignedToCustomer(HasCustomerId hasCustomerIdEntity) {
787   - if (!edge.getCustomerId().isNullUid() && edge.getCustomerId().equals(hasCustomerIdEntity.getCustomerId())) {
788   - return edge.getCustomerId();
789   - } else {
790   - return null;
791   - }
792   - }
793   -
794   - private DownlinkMsg processRelation(EdgeEvent edgeEvent, UpdateMsgType msgType) {
795   - EntityRelation entityRelation = mapper.convertValue(edgeEvent.getBody(), EntityRelation.class);
796   - RelationUpdateMsg r = ctx.getRelationMsgConstructor().constructRelationUpdatedMsg(msgType, entityRelation);
797   - DownlinkMsg downlinkMsg = DownlinkMsg.newBuilder()
798   - .addAllRelationUpdateMsg(Collections.singletonList(r))
799   - .build();
800   - log.trace("[{}] relation processed [{}]", this.sessionId, downlinkMsg);
801   - return downlinkMsg;
802   - }
803   -
804   - private DownlinkMsg processAlarm(EdgeEvent edgeEvent, UpdateMsgType msgType) {
805   - DownlinkMsg downlinkMsg = null;
806   - try {
807   - AlarmId alarmId = new AlarmId(edgeEvent.getEntityId());
808   - Alarm alarm = ctx.getAlarmService().findAlarmByIdAsync(edgeEvent.getTenantId(), alarmId).get();
809   - if (alarm != null) {
810   - downlinkMsg = DownlinkMsg.newBuilder()
811   - .addAllAlarmUpdateMsg(Collections.singletonList(ctx.getAlarmMsgConstructor().constructAlarmUpdatedMsg(edge.getTenantId(), msgType, alarm)))
812   - .build();
813   - }
814   - } catch (Exception e) {
815   - log.error("Can't process alarm msg [{}] [{}]", edgeEvent, msgType, e);
816   - }
817   - log.trace("[{}] alarm processed [{}]", this.sessionId, downlinkMsg);
818   - return downlinkMsg;
819   - }
820   -
821   - private DownlinkMsg processWidgetsBundle(EdgeEvent edgeEvent, UpdateMsgType msgType, EdgeEventActionType edgeEdgeEventActionType) {
822   - WidgetsBundleId widgetsBundleId = new WidgetsBundleId(edgeEvent.getEntityId());
823   - DownlinkMsg downlinkMsg = null;
824   - switch (edgeEdgeEventActionType) {
825   - case ADDED:
826   - case UPDATED:
827   - WidgetsBundle widgetsBundle = ctx.getWidgetsBundleService().findWidgetsBundleById(edgeEvent.getTenantId(), widgetsBundleId);
828   - if (widgetsBundle != null) {
829   - WidgetsBundleUpdateMsg widgetsBundleUpdateMsg =
830   - ctx.getWidgetsBundleMsgConstructor().constructWidgetsBundleUpdateMsg(msgType, widgetsBundle);
831   - downlinkMsg = DownlinkMsg.newBuilder()
832   - .addAllWidgetsBundleUpdateMsg(Collections.singletonList(widgetsBundleUpdateMsg))
833   - .build();
834   - }
835   - break;
836   - case DELETED:
837   - WidgetsBundleUpdateMsg widgetsBundleUpdateMsg =
838   - ctx.getWidgetsBundleMsgConstructor().constructWidgetsBundleDeleteMsg(widgetsBundleId);
839   - downlinkMsg = DownlinkMsg.newBuilder()
840   - .addAllWidgetsBundleUpdateMsg(Collections.singletonList(widgetsBundleUpdateMsg))
841   - .build();
842   - break;
843   - }
844   - log.trace("[{}] widget bundle processed [{}]", this.sessionId, downlinkMsg);
845   - return downlinkMsg;
846   - }
847   -
848   - private DownlinkMsg processWidgetType(EdgeEvent edgeEvent, UpdateMsgType msgType, EdgeEventActionType edgeEdgeEventActionType) {
849   - WidgetTypeId widgetTypeId = new WidgetTypeId(edgeEvent.getEntityId());
850   - DownlinkMsg downlinkMsg = null;
851   - switch (edgeEdgeEventActionType) {
852   - case ADDED:
853   - case UPDATED:
854   - WidgetType widgetType = ctx.getWidgetTypeService().findWidgetTypeById(edgeEvent.getTenantId(), widgetTypeId);
855   - if (widgetType != null) {
856   - WidgetTypeUpdateMsg widgetTypeUpdateMsg =
857   - ctx.getWidgetTypeMsgConstructor().constructWidgetTypeUpdateMsg(msgType, widgetType);
858   - downlinkMsg = DownlinkMsg.newBuilder()
859   - .addAllWidgetTypeUpdateMsg(Collections.singletonList(widgetTypeUpdateMsg))
860   - .build();
861   - }
862   - break;
863   - case DELETED:
864   - WidgetTypeUpdateMsg widgetTypeUpdateMsg =
865   - ctx.getWidgetTypeMsgConstructor().constructWidgetTypeDeleteMsg(widgetTypeId);
866   - downlinkMsg = DownlinkMsg.newBuilder()
867   - .addAllWidgetTypeUpdateMsg(Collections.singletonList(widgetTypeUpdateMsg))
868   - .build();
869   - break;
870   - }
871   - log.trace("[{}] widget type processed [{}]", this.sessionId, downlinkMsg);
872   - return downlinkMsg;
873   - }
874   -
875   - private DownlinkMsg processAdminSettings(EdgeEvent edgeEvent) {
876   - AdminSettings adminSettings = mapper.convertValue(edgeEvent.getBody(), AdminSettings.class);
877   - AdminSettingsUpdateMsg t = ctx.getAdminSettingsMsgConstructor().constructAdminSettingsUpdateMsg(adminSettings);
878   - DownlinkMsg downlinkMsg = DownlinkMsg.newBuilder()
879   - .addAllAdminSettingsUpdateMsg(Collections.singletonList(t))
880   - .build();
881   - log.trace("[{}] admin settings processed [{}]", this.sessionId, downlinkMsg);
882   - return downlinkMsg;
883   - }
884   -
885 547 private UpdateMsgType getResponseMsgType(EdgeEventActionType actionType) {
886 548 switch (actionType) {
887 549 case UPDATED:
... ... @@ -906,71 +568,77 @@ public final class EdgeGrpcSession implements Closeable {
906 568 }
907 569 }
908 570
909   - private DownlinkMsg constructEntityDataProtoMsg(EntityId entityId, EdgeEventActionType actionType, JsonElement entityData) {
910   - EntityDataProto entityDataProto = ctx.getEntityDataMsgConstructor().constructEntityDataMsg(entityId, actionType, entityData);
911   - DownlinkMsg downlinkMsg = DownlinkMsg.newBuilder()
912   - .addAllEntityData(Collections.singletonList(entityDataProto))
913   - .build();
914   - log.trace("[{}] entity data proto processed [{}]", this.sessionId, downlinkMsg);
915   - return downlinkMsg;
916   - }
917   -
918 571 private ListenableFuture<List<Void>> processUplinkMsg(UplinkMsg uplinkMsg) {
919 572 List<ListenableFuture<Void>> result = new ArrayList<>();
920 573 try {
921 574 if (uplinkMsg.getEntityDataCount() > 0) {
922 575 for (EntityDataProto entityData : uplinkMsg.getEntityDataList()) {
923   - result.addAll(ctx.getTelemetryProcessor().onTelemetryUpdate(edge.getTenantId(), edge.getCustomerId(), entityData));
  576 + result.addAll(ctx.getTelemetryProcessor().processTelemetryFromEdge(edge.getTenantId(), edge.getCustomerId(), entityData));
924 577 }
925 578 }
926 579 if (uplinkMsg.getDeviceUpdateMsgCount() > 0) {
927 580 for (DeviceUpdateMsg deviceUpdateMsg : uplinkMsg.getDeviceUpdateMsgList()) {
928   - result.add(ctx.getDeviceProcessor().onDeviceUpdate(edge.getTenantId(), edge, deviceUpdateMsg));
  581 + result.add(ctx.getDeviceProcessor().processDeviceFromEdge(edge.getTenantId(), edge, deviceUpdateMsg));
929 582 }
930 583 }
931 584 if (uplinkMsg.getDeviceCredentialsUpdateMsgCount() > 0) {
932 585 for (DeviceCredentialsUpdateMsg deviceCredentialsUpdateMsg : uplinkMsg.getDeviceCredentialsUpdateMsgList()) {
933   - result.add(ctx.getDeviceProcessor().onDeviceCredentialsUpdate(edge.getTenantId(), deviceCredentialsUpdateMsg));
  586 + result.add(ctx.getDeviceProcessor().processDeviceCredentialsFromEdge(edge.getTenantId(), deviceCredentialsUpdateMsg));
934 587 }
935 588 }
936 589 if (uplinkMsg.getAlarmUpdateMsgCount() > 0) {
937 590 for (AlarmUpdateMsg alarmUpdateMsg : uplinkMsg.getAlarmUpdateMsgList()) {
938   - result.add(ctx.getAlarmProcessor().onAlarmUpdate(edge.getTenantId(), alarmUpdateMsg));
  591 + result.add(ctx.getAlarmProcessor().processAlarmFromEdge(edge.getTenantId(), alarmUpdateMsg));
939 592 }
940 593 }
941 594 if (uplinkMsg.getRelationUpdateMsgCount() > 0) {
942 595 for (RelationUpdateMsg relationUpdateMsg : uplinkMsg.getRelationUpdateMsgList()) {
943   - result.add(ctx.getRelationProcessor().onRelationUpdate(edge.getTenantId(), relationUpdateMsg));
  596 + result.add(ctx.getRelationProcessor().processRelationFromEdge(edge.getTenantId(), relationUpdateMsg));
944 597 }
945 598 }
946 599 if (uplinkMsg.getRuleChainMetadataRequestMsgCount() > 0) {
947 600 for (RuleChainMetadataRequestMsg ruleChainMetadataRequestMsg : uplinkMsg.getRuleChainMetadataRequestMsgList()) {
948   - result.add(ctx.getSyncEdgeService().processRuleChainMetadataRequestMsg(edge.getTenantId(), edge, ruleChainMetadataRequestMsg));
  601 + result.add(ctx.getEdgeRequestsService().processRuleChainMetadataRequestMsg(edge.getTenantId(), edge, ruleChainMetadataRequestMsg));
949 602 }
950 603 }
951 604 if (uplinkMsg.getAttributesRequestMsgCount() > 0) {
952 605 for (AttributesRequestMsg attributesRequestMsg : uplinkMsg.getAttributesRequestMsgList()) {
953   - result.add(ctx.getSyncEdgeService().processAttributesRequestMsg(edge.getTenantId(), edge, attributesRequestMsg));
  606 + result.add(ctx.getEdgeRequestsService().processAttributesRequestMsg(edge.getTenantId(), edge, attributesRequestMsg));
954 607 }
955 608 }
956 609 if (uplinkMsg.getRelationRequestMsgCount() > 0) {
957 610 for (RelationRequestMsg relationRequestMsg : uplinkMsg.getRelationRequestMsgList()) {
958   - result.add(ctx.getSyncEdgeService().processRelationRequestMsg(edge.getTenantId(), edge, relationRequestMsg));
  611 + result.add(ctx.getEdgeRequestsService().processRelationRequestMsg(edge.getTenantId(), edge, relationRequestMsg));
959 612 }
960 613 }
961 614 if (uplinkMsg.getUserCredentialsRequestMsgCount() > 0) {
962 615 for (UserCredentialsRequestMsg userCredentialsRequestMsg : uplinkMsg.getUserCredentialsRequestMsgList()) {
963   - result.add(ctx.getSyncEdgeService().processUserCredentialsRequestMsg(edge.getTenantId(), edge, userCredentialsRequestMsg));
  616 + result.add(ctx.getEdgeRequestsService().processUserCredentialsRequestMsg(edge.getTenantId(), edge, userCredentialsRequestMsg));
964 617 }
965 618 }
966 619 if (uplinkMsg.getDeviceCredentialsRequestMsgCount() > 0) {
967 620 for (DeviceCredentialsRequestMsg deviceCredentialsRequestMsg : uplinkMsg.getDeviceCredentialsRequestMsgList()) {
968   - result.add(ctx.getSyncEdgeService().processDeviceCredentialsRequestMsg(edge.getTenantId(), edge, deviceCredentialsRequestMsg));
  621 + result.add(ctx.getEdgeRequestsService().processDeviceCredentialsRequestMsg(edge.getTenantId(), edge, deviceCredentialsRequestMsg));
969 622 }
970 623 }
971 624 if (uplinkMsg.getDeviceRpcCallMsgCount() > 0) {
972 625 for (DeviceRpcCallMsg deviceRpcCallMsg : uplinkMsg.getDeviceRpcCallMsgList()) {
973   - result.add(ctx.getDeviceProcessor().processDeviceRpcCallResponseMsg(edge.getTenantId(), deviceRpcCallMsg));
  626 + result.add(ctx.getDeviceProcessor().processDeviceRpcCallResponseFromEdge(edge.getTenantId(), deviceRpcCallMsg));
  627 + }
  628 + }
  629 + if (uplinkMsg.getDeviceProfileDevicesRequestMsgCount() > 0) {
  630 + for (DeviceProfileDevicesRequestMsg deviceProfileDevicesRequestMsg : uplinkMsg.getDeviceProfileDevicesRequestMsgList()) {
  631 + result.add(ctx.getEdgeRequestsService().processDeviceProfileDevicesRequestMsg(edge.getTenantId(), edge, deviceProfileDevicesRequestMsg));
  632 + }
  633 + }
  634 + if (uplinkMsg.getWidgetBundleTypesRequestMsgCount() > 0) {
  635 + for (WidgetBundleTypesRequestMsg widgetBundleTypesRequestMsg : uplinkMsg.getWidgetBundleTypesRequestMsgList()) {
  636 + result.add(ctx.getEdgeRequestsService().processWidgetBundleTypesRequestMsg(edge.getTenantId(), edge, widgetBundleTypesRequestMsg));
  637 + }
  638 + }
  639 + if (uplinkMsg.getEntityViewsRequestMsgCount() > 0) {
  640 + for (EntityViewsRequestMsg entityViewRequestMsg : uplinkMsg.getEntityViewsRequestMsgList()) {
  641 + result.add(ctx.getEdgeRequestsService().processEntityViewsRequestMsg(edge.getTenantId(), edge, entityViewRequestMsg));
974 642 }
975 643 }
976 644 } catch (Exception e) {
... ... @@ -1026,7 +694,7 @@ public final class EdgeGrpcSession implements Closeable {
1026 694 .setCloudType("CE");
1027 695 if (edge.getCustomerId() != null) {
1028 696 builder.setCustomerIdMSB(edge.getCustomerId().getId().getMostSignificantBits())
1029   - .setCustomerIdLSB(edge.getCustomerId().getId().getLeastSignificantBits());
  697 + .setCustomerIdLSB(edge.getCustomerId().getId().getLeastSignificantBits());
1030 698 }
1031 699 return builder
1032 700 .build();
... ...
... ... @@ -17,12 +17,15 @@ package org.thingsboard.server.service.edge.rpc;
17 17
18 18 import org.thingsboard.server.common.data.edge.Edge;
19 19 import org.thingsboard.server.common.data.id.EdgeId;
  20 +import org.thingsboard.server.common.data.id.TenantId;
20 21
21 22 public interface EdgeRpcService {
22 23
23   - void updateEdge(Edge edge);
  24 + void updateEdge(TenantId tenantId, Edge edge);
24 25
25   - void deleteEdge(EdgeId edgeId);
  26 + void deleteEdge(TenantId tenantId, EdgeId edgeId);
26 27
27   - void onEdgeEvent(EdgeId edgeId);
  28 + void onEdgeEvent(TenantId tenantId, EdgeId edgeId);
  29 +
  30 + void startSyncProcess(TenantId tenantId, EdgeId edgeId);
28 31 }
... ...
  1 +/**
  2 + * Copyright © 2016-2021 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.edge.rpc;
  17 +
  18 +import com.google.common.util.concurrent.SettableFuture;
  19 +import lombok.Data;
  20 +import org.thingsboard.server.gen.edge.v1.DownlinkMsg;
  21 +
  22 +import java.util.LinkedHashMap;
  23 +import java.util.Map;
  24 +import java.util.concurrent.ScheduledFuture;
  25 +
  26 +@Data
  27 +public class EdgeSessionState {
  28 +
  29 + private final Map<Integer, DownlinkMsg> pendingMsgsMap = new LinkedHashMap<>();
  30 + private SettableFuture<Void> sendDownlinkMsgsFuture;
  31 + private ScheduledFuture<?> scheduledSendDownlinkTask;
  32 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2021 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.edge.rpc;
  17 +
  18 +import org.thingsboard.server.common.data.edge.Edge;
  19 +import org.thingsboard.server.common.data.id.EntityId;
  20 +import org.thingsboard.server.service.edge.EdgeContextComponent;
  21 +import org.thingsboard.server.service.edge.rpc.fetch.AdminSettingsEdgeEventFetcher;
  22 +import org.thingsboard.server.service.edge.rpc.fetch.AssetsEdgeEventFetcher;
  23 +import org.thingsboard.server.service.edge.rpc.fetch.CustomerEdgeEventFetcher;
  24 +import org.thingsboard.server.service.edge.rpc.fetch.CustomerUsersEdgeEventFetcher;
  25 +import org.thingsboard.server.service.edge.rpc.fetch.DashboardsEdgeEventFetcher;
  26 +import org.thingsboard.server.service.edge.rpc.fetch.DeviceProfilesEdgeEventFetcher;
  27 +import org.thingsboard.server.service.edge.rpc.fetch.EdgeEventFetcher;
  28 +import org.thingsboard.server.service.edge.rpc.fetch.RuleChainsEdgeEventFetcher;
  29 +import org.thingsboard.server.service.edge.rpc.fetch.SystemWidgetsBundlesEdgeEventFetcher;
  30 +import org.thingsboard.server.service.edge.rpc.fetch.TenantAdminUsersEdgeEventFetcher;
  31 +import org.thingsboard.server.service.edge.rpc.fetch.TenantWidgetsBundlesEdgeEventFetcher;
  32 +
  33 +import java.util.LinkedList;
  34 +import java.util.List;
  35 +import java.util.NoSuchElementException;
  36 +
  37 +public class EdgeSyncCursor {
  38 +
  39 + List<EdgeEventFetcher> fetchers = new LinkedList<>();
  40 +
  41 + int currentIdx = 0;
  42 +
  43 + public EdgeSyncCursor(EdgeContextComponent ctx, Edge edge) {
  44 + fetchers.add(new SystemWidgetsBundlesEdgeEventFetcher(ctx.getWidgetsBundleService()));
  45 + fetchers.add(new TenantWidgetsBundlesEdgeEventFetcher(ctx.getWidgetsBundleService()));
  46 + fetchers.add(new DeviceProfilesEdgeEventFetcher(ctx.getDeviceProfileService()));
  47 + fetchers.add(new RuleChainsEdgeEventFetcher(ctx.getRuleChainService()));
  48 + fetchers.add(new TenantAdminUsersEdgeEventFetcher(ctx.getUserService()));
  49 + if (edge.getCustomerId() != null && !EntityId.NULL_UUID.equals(edge.getCustomerId().getId())) {
  50 + fetchers.add(new CustomerEdgeEventFetcher());
  51 + fetchers.add(new CustomerUsersEdgeEventFetcher(ctx.getUserService(), edge.getCustomerId()));
  52 + }
  53 + fetchers.add(new AdminSettingsEdgeEventFetcher(ctx.getAdminSettingsService()));
  54 + fetchers.add(new AssetsEdgeEventFetcher(ctx.getAssetService()));
  55 + fetchers.add(new DashboardsEdgeEventFetcher(ctx.getDashboardService()));
  56 + }
  57 +
  58 + public boolean hasNext() {
  59 + return fetchers.size() > currentIdx;
  60 + }
  61 +
  62 + public EdgeEventFetcher getNext() {
  63 + if (!hasNext()) {
  64 + throw new NoSuchElementException();
  65 + }
  66 + EdgeEventFetcher edgeEventFetcher = fetchers.get(currentIdx);
  67 + currentIdx++;
  68 + return edgeEventFetcher;
  69 + }
  70 +
  71 + public int getCurrentIdx() {
  72 + return currentIdx;
  73 + }
  74 +}
... ...
... ... @@ -18,7 +18,7 @@ package org.thingsboard.server.service.edge.rpc.constructor;
18 18 import org.springframework.stereotype.Component;
19 19 import org.thingsboard.server.common.data.AdminSettings;
20 20 import org.thingsboard.common.util.JacksonUtil;
21   -import org.thingsboard.server.gen.edge.AdminSettingsUpdateMsg;
  21 +import org.thingsboard.server.gen.edge.v1.AdminSettingsUpdateMsg;
22 22 import org.thingsboard.server.queue.util.TbCoreComponent;
23 23
24 24 @Component
... ...
... ... @@ -26,8 +26,8 @@ import org.thingsboard.server.dao.asset.AssetService;
26 26 import org.thingsboard.server.dao.device.DeviceService;
27 27 import org.thingsboard.server.dao.entityview.EntityViewService;
28 28 import org.thingsboard.common.util.JacksonUtil;
29   -import org.thingsboard.server.gen.edge.AlarmUpdateMsg;
30   -import org.thingsboard.server.gen.edge.UpdateMsgType;
  29 +import org.thingsboard.server.gen.edge.v1.AlarmUpdateMsg;
  30 +import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
31 31 import org.thingsboard.server.queue.util.TbCoreComponent;
32 32
33 33 @Component
... ...
... ... @@ -20,8 +20,8 @@ import org.thingsboard.server.common.data.asset.Asset;
20 20 import org.thingsboard.server.common.data.id.AssetId;
21 21 import org.thingsboard.server.common.data.id.CustomerId;
22 22 import org.thingsboard.common.util.JacksonUtil;
23   -import org.thingsboard.server.gen.edge.AssetUpdateMsg;
24   -import org.thingsboard.server.gen.edge.UpdateMsgType;
  23 +import org.thingsboard.server.gen.edge.v1.AssetUpdateMsg;
  24 +import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
25 25 import org.thingsboard.server.queue.util.TbCoreComponent;
26 26
27 27 @Component
... ...
... ... @@ -19,8 +19,8 @@ import org.springframework.stereotype.Component;
19 19 import org.thingsboard.server.common.data.Customer;
20 20 import org.thingsboard.server.common.data.id.CustomerId;
21 21 import org.thingsboard.common.util.JacksonUtil;
22   -import org.thingsboard.server.gen.edge.CustomerUpdateMsg;
23   -import org.thingsboard.server.gen.edge.UpdateMsgType;
  22 +import org.thingsboard.server.gen.edge.v1.CustomerUpdateMsg;
  23 +import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
24 24 import org.thingsboard.server.queue.util.TbCoreComponent;
25 25
26 26 @Component
... ...
... ... @@ -20,8 +20,8 @@ import org.thingsboard.server.common.data.Dashboard;
20 20 import org.thingsboard.server.common.data.id.CustomerId;
21 21 import org.thingsboard.server.common.data.id.DashboardId;
22 22 import org.thingsboard.common.util.JacksonUtil;
23   -import org.thingsboard.server.gen.edge.DashboardUpdateMsg;
24   -import org.thingsboard.server.gen.edge.UpdateMsgType;
  23 +import org.thingsboard.server.gen.edge.v1.DashboardUpdateMsg;
  24 +import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
25 25 import org.thingsboard.server.queue.util.TbCoreComponent;
26 26
27 27 @Component
... ...
... ... @@ -23,11 +23,11 @@ import org.thingsboard.server.common.data.Device;
23 23 import org.thingsboard.server.common.data.id.CustomerId;
24 24 import org.thingsboard.server.common.data.id.DeviceId;
25 25 import org.thingsboard.server.common.data.security.DeviceCredentials;
26   -import org.thingsboard.server.gen.edge.DeviceCredentialsUpdateMsg;
27   -import org.thingsboard.server.gen.edge.DeviceRpcCallMsg;
28   -import org.thingsboard.server.gen.edge.DeviceUpdateMsg;
29   -import org.thingsboard.server.gen.edge.RpcRequestMsg;
30   -import org.thingsboard.server.gen.edge.UpdateMsgType;
  26 +import org.thingsboard.server.gen.edge.v1.DeviceCredentialsUpdateMsg;
  27 +import org.thingsboard.server.gen.edge.v1.DeviceRpcCallMsg;
  28 +import org.thingsboard.server.gen.edge.v1.DeviceUpdateMsg;
  29 +import org.thingsboard.server.gen.edge.v1.RpcRequestMsg;
  30 +import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
31 31 import org.thingsboard.server.queue.util.TbCoreComponent;
32 32
33 33 import java.util.UUID;
... ...
... ... @@ -21,8 +21,8 @@ import org.springframework.stereotype.Component;
21 21 import org.thingsboard.server.common.data.DeviceProfile;
22 22 import org.thingsboard.server.common.data.id.DeviceProfileId;
23 23 import org.thingsboard.server.common.transport.util.DataDecodingEncodingService;
24   -import org.thingsboard.server.gen.edge.DeviceProfileUpdateMsg;
25   -import org.thingsboard.server.gen.edge.UpdateMsgType;
  24 +import org.thingsboard.server.gen.edge.v1.DeviceProfileUpdateMsg;
  25 +import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
26 26 import org.thingsboard.server.queue.util.TbCoreComponent;
27 27
28 28 @Component
... ... @@ -41,7 +41,7 @@ public class DeviceProfileMsgConstructor {
41 41 .setDefault(deviceProfile.isDefault())
42 42 .setType(deviceProfile.getType().name())
43 43 .setProfileDataBytes(ByteString.copyFrom(dataDecodingEncodingService.encode(deviceProfile.getProfileData())));
44   - // TODO: voba - should this be always null at the moment??
  44 + // TODO: @voba - add possibility to setup edge rule chain as device profile default
45 45 // if (deviceProfile.getDefaultRuleChainId() != null) {
46 46 // builder.setDefaultRuleChainIdMSB(deviceProfile.getDefaultRuleChainId().getId().getMostSignificantBits())
47 47 // .setDefaultRuleChainIdLSB(deviceProfile.getDefaultRuleChainId().getId().getLeastSignificantBits());
... ...
... ... @@ -25,8 +25,8 @@ import org.springframework.stereotype.Component;
25 25 import org.thingsboard.server.common.data.edge.EdgeEventActionType;
26 26 import org.thingsboard.server.common.data.id.EntityId;
27 27 import org.thingsboard.server.common.transport.adaptor.JsonConverter;
28   -import org.thingsboard.server.gen.edge.AttributeDeleteMsg;
29   -import org.thingsboard.server.gen.edge.EntityDataProto;
  28 +import org.thingsboard.server.gen.edge.v1.AttributeDeleteMsg;
  29 +import org.thingsboard.server.gen.edge.v1.EntityDataProto;
30 30 import org.thingsboard.server.gen.transport.TransportProtos;
31 31 import org.thingsboard.server.queue.util.TbCoreComponent;
32 32
... ...
... ... @@ -20,9 +20,9 @@ import org.thingsboard.server.common.data.EntityView;
20 20 import org.thingsboard.server.common.data.id.CustomerId;
21 21 import org.thingsboard.server.common.data.id.EntityViewId;
22 22 import org.thingsboard.common.util.JacksonUtil;
23   -import org.thingsboard.server.gen.edge.EdgeEntityType;
24   -import org.thingsboard.server.gen.edge.EntityViewUpdateMsg;
25   -import org.thingsboard.server.gen.edge.UpdateMsgType;
  23 +import org.thingsboard.server.gen.edge.v1.EdgeEntityType;
  24 +import org.thingsboard.server.gen.edge.v1.EntityViewUpdateMsg;
  25 +import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
26 26 import org.thingsboard.server.queue.util.TbCoreComponent;
27 27
28 28 @Component
... ...
... ... @@ -18,8 +18,8 @@ package org.thingsboard.server.service.edge.rpc.constructor;
18 18 import org.springframework.stereotype.Component;
19 19 import org.thingsboard.server.common.data.relation.EntityRelation;
20 20 import org.thingsboard.common.util.JacksonUtil;
21   -import org.thingsboard.server.gen.edge.RelationUpdateMsg;
22   -import org.thingsboard.server.gen.edge.UpdateMsgType;
  21 +import org.thingsboard.server.gen.edge.v1.RelationUpdateMsg;
  22 +import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
23 23 import org.thingsboard.server.queue.util.TbCoreComponent;
24 24
25 25 @Component
... ...
... ... @@ -26,12 +26,12 @@ import org.thingsboard.server.common.data.rule.RuleChainConnectionInfo;
26 26 import org.thingsboard.server.common.data.rule.RuleChainMetaData;
27 27 import org.thingsboard.server.common.data.rule.RuleNode;
28 28 import org.thingsboard.common.util.JacksonUtil;
29   -import org.thingsboard.server.gen.edge.NodeConnectionInfoProto;
30   -import org.thingsboard.server.gen.edge.RuleChainConnectionInfoProto;
31   -import org.thingsboard.server.gen.edge.RuleChainMetadataUpdateMsg;
32   -import org.thingsboard.server.gen.edge.RuleChainUpdateMsg;
33   -import org.thingsboard.server.gen.edge.RuleNodeProto;
34   -import org.thingsboard.server.gen.edge.UpdateMsgType;
  29 +import org.thingsboard.server.gen.edge.v1.NodeConnectionInfoProto;
  30 +import org.thingsboard.server.gen.edge.v1.RuleChainConnectionInfoProto;
  31 +import org.thingsboard.server.gen.edge.v1.RuleChainMetadataUpdateMsg;
  32 +import org.thingsboard.server.gen.edge.v1.RuleChainUpdateMsg;
  33 +import org.thingsboard.server.gen.edge.v1.RuleNodeProto;
  34 +import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
35 35 import org.thingsboard.server.queue.util.TbCoreComponent;
36 36
37 37 import java.util.ArrayList;
... ...
... ... @@ -21,9 +21,9 @@ import org.thingsboard.server.common.data.id.CustomerId;
21 21 import org.thingsboard.server.common.data.id.UserId;
22 22 import org.thingsboard.server.common.data.security.UserCredentials;
23 23 import org.thingsboard.common.util.JacksonUtil;
24   -import org.thingsboard.server.gen.edge.UpdateMsgType;
25   -import org.thingsboard.server.gen.edge.UserCredentialsUpdateMsg;
26   -import org.thingsboard.server.gen.edge.UserUpdateMsg;
  24 +import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
  25 +import org.thingsboard.server.gen.edge.v1.UserCredentialsUpdateMsg;
  26 +import org.thingsboard.server.gen.edge.v1.UserUpdateMsg;
27 27 import org.thingsboard.server.queue.util.TbCoreComponent;
28 28
29 29 @Component
... ...
... ... @@ -20,8 +20,8 @@ import org.thingsboard.server.common.data.id.TenantId;
20 20 import org.thingsboard.server.common.data.id.WidgetTypeId;
21 21 import org.thingsboard.server.common.data.widget.WidgetType;
22 22 import org.thingsboard.common.util.JacksonUtil;
23   -import org.thingsboard.server.gen.edge.UpdateMsgType;
24   -import org.thingsboard.server.gen.edge.WidgetTypeUpdateMsg;
  23 +import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
  24 +import org.thingsboard.server.gen.edge.v1.WidgetTypeUpdateMsg;
25 25 import org.thingsboard.server.queue.util.TbCoreComponent;
26 26
27 27 @Component
... ...
... ... @@ -20,8 +20,8 @@ import org.springframework.stereotype.Component;
20 20 import org.thingsboard.server.common.data.id.TenantId;
21 21 import org.thingsboard.server.common.data.id.WidgetsBundleId;
22 22 import org.thingsboard.server.common.data.widget.WidgetsBundle;
23   -import org.thingsboard.server.gen.edge.UpdateMsgType;
24   -import org.thingsboard.server.gen.edge.WidgetsBundleUpdateMsg;
  23 +import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
  24 +import org.thingsboard.server.gen.edge.v1.WidgetsBundleUpdateMsg;
25 25 import org.thingsboard.server.queue.util.TbCoreComponent;
26 26
27 27 import java.nio.charset.StandardCharsets;
... ...
  1 +/**
  2 + * Copyright © 2016-2021 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.edge.rpc.fetch;
  17 +
  18 +import com.datastax.oss.driver.api.core.uuid.Uuids;
  19 +import com.fasterxml.jackson.databind.JsonNode;
  20 +import com.fasterxml.jackson.databind.ObjectMapper;
  21 +import com.fasterxml.jackson.databind.node.ObjectNode;
  22 +import lombok.AllArgsConstructor;
  23 +import lombok.extern.slf4j.Slf4j;
  24 +import org.apache.commons.io.FileUtils;
  25 +import org.apache.commons.lang3.StringUtils;
  26 +import org.apache.commons.lang3.text.WordUtils;
  27 +import org.springframework.core.io.DefaultResourceLoader;
  28 +import org.thingsboard.server.common.data.AdminSettings;
  29 +import org.thingsboard.server.common.data.edge.Edge;
  30 +import org.thingsboard.server.common.data.edge.EdgeEvent;
  31 +import org.thingsboard.server.common.data.edge.EdgeEventActionType;
  32 +import org.thingsboard.server.common.data.edge.EdgeEventType;
  33 +import org.thingsboard.server.common.data.id.AdminSettingsId;
  34 +import org.thingsboard.server.common.data.id.TenantId;
  35 +import org.thingsboard.server.common.data.page.PageData;
  36 +import org.thingsboard.server.common.data.page.PageLink;
  37 +import org.thingsboard.server.dao.settings.AdminSettingsService;
  38 +import org.thingsboard.server.service.edge.rpc.EdgeEventUtils;
  39 +
  40 +import java.io.File;
  41 +import java.nio.charset.StandardCharsets;
  42 +import java.util.ArrayList;
  43 +import java.util.HashMap;
  44 +import java.util.List;
  45 +import java.util.Map;
  46 +import java.util.regex.Matcher;
  47 +import java.util.regex.Pattern;
  48 +
  49 +@AllArgsConstructor
  50 +@Slf4j
  51 +public class AdminSettingsEdgeEventFetcher implements EdgeEventFetcher {
  52 +
  53 + private static final ObjectMapper mapper = new ObjectMapper();
  54 +
  55 + private final AdminSettingsService adminSettingsService;
  56 +
  57 + @Override
  58 + public PageLink getPageLink(int pageSize) {
  59 + return null;
  60 + }
  61 +
  62 + @Override
  63 + public PageData<EdgeEvent> fetchEdgeEvents(TenantId tenantId, Edge edge, PageLink pageLink) throws Exception {
  64 + List<EdgeEvent> result = new ArrayList<>();
  65 +
  66 + AdminSettings systemMailSettings = adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, "mail");
  67 + result.add(EdgeEventUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.ADMIN_SETTINGS,
  68 + EdgeEventActionType.UPDATED, null, mapper.valueToTree(systemMailSettings)));
  69 +
  70 + AdminSettings tenantMailSettings = convertToTenantAdminSettings(systemMailSettings.getKey(), (ObjectNode) systemMailSettings.getJsonValue());
  71 + result.add(EdgeEventUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.ADMIN_SETTINGS,
  72 + EdgeEventActionType.UPDATED, null, mapper.valueToTree(tenantMailSettings)));
  73 +
  74 + AdminSettings systemMailTemplates = loadMailTemplates();
  75 + result.add(EdgeEventUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.ADMIN_SETTINGS,
  76 + EdgeEventActionType.UPDATED, null, mapper.valueToTree(systemMailTemplates)));
  77 +
  78 + AdminSettings tenantMailTemplates = convertToTenantAdminSettings(systemMailTemplates.getKey(), (ObjectNode) systemMailTemplates.getJsonValue());
  79 + result.add(EdgeEventUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.ADMIN_SETTINGS,
  80 + EdgeEventActionType.UPDATED, null, mapper.valueToTree(tenantMailTemplates)));
  81 +
  82 + // @voba - returns PageData object to be in sync with other fetchers
  83 + return new PageData<>(result, 1, result.size(), false);
  84 + }
  85 +
  86 + private AdminSettings loadMailTemplates() throws Exception {
  87 + Map<String, Object> mailTemplates = new HashMap<>();
  88 + Pattern startPattern = Pattern.compile("<div class=\"content\".*?>");
  89 + Pattern endPattern = Pattern.compile("<div class=\"footer\".*?>");
  90 + File[] files = new DefaultResourceLoader().getResource("classpath:/templates/").getFile().listFiles();
  91 + for (File file : files) {
  92 + Map<String, String> mailTemplate = new HashMap<>();
  93 + String name = validateName(file.getName());
  94 + String stringTemplate = FileUtils.readFileToString(file, StandardCharsets.UTF_8);
  95 + Matcher start = startPattern.matcher(stringTemplate);
  96 + Matcher end = endPattern.matcher(stringTemplate);
  97 + if (start.find() && end.find()) {
  98 + String body = StringUtils.substringBetween(stringTemplate, start.group(), end.group()).replaceAll("\t", "");
  99 + String subject = StringUtils.substringBetween(body, "<h2>", "</h2>");
  100 + mailTemplate.put("subject", subject);
  101 + mailTemplate.put("body", body);
  102 + mailTemplates.put(name, mailTemplate);
  103 + } else {
  104 + log.error("Can't load mail template from file {}", file.getName());
  105 + }
  106 + }
  107 + AdminSettings adminSettings = new AdminSettings();
  108 + adminSettings.setId(new AdminSettingsId(Uuids.timeBased()));
  109 + adminSettings.setKey("mailTemplates");
  110 + adminSettings.setJsonValue(mapper.convertValue(mailTemplates, JsonNode.class));
  111 + return adminSettings;
  112 + }
  113 +
  114 + private String validateName(String name) throws Exception {
  115 + StringBuilder nameBuilder = new StringBuilder();
  116 + name = name.replace(".vm", "");
  117 + String[] nameParts = name.split("\\.");
  118 + if (nameParts.length >= 1) {
  119 + nameBuilder.append(nameParts[0]);
  120 + for (int i = 1; i < nameParts.length; i++) {
  121 + String word = WordUtils.capitalize(nameParts[i]);
  122 + nameBuilder.append(word);
  123 + }
  124 + return nameBuilder.toString();
  125 + } else {
  126 + throw new Exception("Error during filename validation");
  127 + }
  128 + }
  129 +
  130 + private AdminSettings convertToTenantAdminSettings(String key, ObjectNode jsonValue) {
  131 + AdminSettings tenantMailSettings = new AdminSettings();
  132 + jsonValue.put("useSystemMailSettings", true);
  133 + tenantMailSettings.setJsonValue(jsonValue);
  134 + tenantMailSettings.setKey(key);
  135 + return tenantMailSettings;
  136 + }
  137 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2021 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.edge.rpc.fetch;
  17 +
  18 +import lombok.AllArgsConstructor;
  19 +import lombok.extern.slf4j.Slf4j;
  20 +import org.thingsboard.server.common.data.asset.Asset;
  21 +import org.thingsboard.server.common.data.edge.Edge;
  22 +import org.thingsboard.server.common.data.edge.EdgeEvent;
  23 +import org.thingsboard.server.common.data.edge.EdgeEventActionType;
  24 +import org.thingsboard.server.common.data.edge.EdgeEventType;
  25 +import org.thingsboard.server.common.data.id.TenantId;
  26 +import org.thingsboard.server.common.data.page.PageData;
  27 +import org.thingsboard.server.common.data.page.PageLink;
  28 +import org.thingsboard.server.dao.asset.AssetService;
  29 +import org.thingsboard.server.service.edge.rpc.EdgeEventUtils;
  30 +
  31 +@AllArgsConstructor
  32 +@Slf4j
  33 +public class AssetsEdgeEventFetcher extends BasePageableEdgeEventFetcher<Asset> {
  34 +
  35 + private final AssetService assetService;
  36 +
  37 + @Override
  38 + PageData<Asset> fetchPageData(TenantId tenantId, Edge edge, PageLink pageLink) {
  39 + return assetService.findAssetsByTenantIdAndEdgeId(tenantId, edge.getId(), pageLink);
  40 + }
  41 +
  42 + @Override
  43 + EdgeEvent constructEdgeEvent(TenantId tenantId, Edge edge, Asset asset) {
  44 + return EdgeEventUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.ASSET,
  45 + EdgeEventActionType.ADDED, asset.getId(), null);
  46 + }
  47 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2021 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.edge.rpc.fetch;
  17 +
  18 +import com.fasterxml.jackson.databind.ObjectMapper;
  19 +import lombok.extern.slf4j.Slf4j;
  20 +import org.thingsboard.server.common.data.BaseData;
  21 +import org.thingsboard.server.common.data.edge.Edge;
  22 +import org.thingsboard.server.common.data.edge.EdgeEvent;
  23 +import org.thingsboard.server.common.data.edge.EdgeEventActionType;
  24 +import org.thingsboard.server.common.data.edge.EdgeEventType;
  25 +import org.thingsboard.server.common.data.id.EntityId;
  26 +import org.thingsboard.server.common.data.id.EventId;
  27 +import org.thingsboard.server.common.data.id.HasId;
  28 +import org.thingsboard.server.common.data.id.HasUUID;
  29 +import org.thingsboard.server.common.data.id.IdBased;
  30 +import org.thingsboard.server.common.data.id.RuleChainId;
  31 +import org.thingsboard.server.common.data.id.TenantId;
  32 +import org.thingsboard.server.common.data.id.UUIDBased;
  33 +import org.thingsboard.server.common.data.page.PageData;
  34 +import org.thingsboard.server.common.data.page.PageLink;
  35 +import org.thingsboard.server.common.data.rule.RuleChain;
  36 +import org.thingsboard.server.service.edge.rpc.EdgeEventUtils;
  37 +
  38 +import java.util.ArrayList;
  39 +import java.util.List;
  40 +
  41 +@Slf4j
  42 +public abstract class BasePageableEdgeEventFetcher<T> implements EdgeEventFetcher {
  43 +
  44 + @Override
  45 + public PageLink getPageLink(int pageSize) {
  46 + return new PageLink(pageSize);
  47 + }
  48 +
  49 + @Override
  50 + public PageData<EdgeEvent> fetchEdgeEvents(TenantId tenantId, Edge edge, PageLink pageLink) {
  51 + log.trace("[{}] start fetching edge events [{}]", tenantId, edge.getId());
  52 + PageData<T> pageData = fetchPageData(tenantId, edge, pageLink);
  53 + List<EdgeEvent> result = new ArrayList<>();
  54 + if (!pageData.getData().isEmpty()) {
  55 + for (T entity : pageData.getData()) {
  56 + result.add(constructEdgeEvent(tenantId, edge, entity));
  57 + }
  58 + }
  59 + return new PageData<>(result, pageData.getTotalPages(), pageData.getTotalElements(), pageData.hasNext());
  60 + }
  61 +
  62 + abstract PageData<T> fetchPageData(TenantId tenantId, Edge edge, PageLink pageLink);
  63 +
  64 + abstract EdgeEvent constructEdgeEvent(TenantId tenantId, Edge edge, T entity);
  65 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2021 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.edge.rpc.fetch;
  17 +
  18 +import lombok.AllArgsConstructor;
  19 +import lombok.extern.slf4j.Slf4j;
  20 +import org.thingsboard.server.common.data.User;
  21 +import org.thingsboard.server.common.data.edge.Edge;
  22 +import org.thingsboard.server.common.data.edge.EdgeEvent;
  23 +import org.thingsboard.server.common.data.edge.EdgeEventActionType;
  24 +import org.thingsboard.server.common.data.edge.EdgeEventType;
  25 +import org.thingsboard.server.common.data.id.TenantId;
  26 +import org.thingsboard.server.common.data.page.PageData;
  27 +import org.thingsboard.server.common.data.page.PageLink;
  28 +import org.thingsboard.server.dao.user.UserService;
  29 +import org.thingsboard.server.service.edge.rpc.EdgeEventUtils;
  30 +
  31 +@Slf4j
  32 +@AllArgsConstructor
  33 +public abstract class BaseUsersEdgeEventFetcher extends BasePageableEdgeEventFetcher<User> {
  34 +
  35 + protected final UserService userService;
  36 +
  37 + @Override
  38 + PageData<User> fetchPageData(TenantId tenantId, Edge edge, PageLink pageLink) {
  39 + return findUsers(tenantId, pageLink);
  40 + }
  41 +
  42 + @Override
  43 + EdgeEvent constructEdgeEvent(TenantId tenantId, Edge edge, User user) {
  44 + return EdgeEventUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.USER,
  45 + EdgeEventActionType.ADDED, user.getId(), null);
  46 + }
  47 +
  48 + protected abstract PageData<User> findUsers(TenantId tenantId, PageLink pageLink);
  49 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2021 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.edge.rpc.fetch;
  17 +
  18 +import lombok.AllArgsConstructor;
  19 +import lombok.extern.slf4j.Slf4j;
  20 +import org.thingsboard.server.common.data.edge.Edge;
  21 +import org.thingsboard.server.common.data.edge.EdgeEvent;
  22 +import org.thingsboard.server.common.data.edge.EdgeEventActionType;
  23 +import org.thingsboard.server.common.data.edge.EdgeEventType;
  24 +import org.thingsboard.server.common.data.id.TenantId;
  25 +import org.thingsboard.server.common.data.page.PageData;
  26 +import org.thingsboard.server.common.data.page.PageLink;
  27 +import org.thingsboard.server.common.data.widget.WidgetsBundle;
  28 +import org.thingsboard.server.dao.widget.WidgetsBundleService;
  29 +import org.thingsboard.server.service.edge.rpc.EdgeEventUtils;
  30 +
  31 +@Slf4j
  32 +@AllArgsConstructor
  33 +public abstract class BaseWidgetsBundlesEdgeEventFetcher extends BasePageableEdgeEventFetcher<WidgetsBundle> {
  34 +
  35 + protected final WidgetsBundleService widgetsBundleService;
  36 +
  37 + @Override
  38 + PageData<WidgetsBundle> fetchPageData(TenantId tenantId, Edge edge, PageLink pageLink) {
  39 + return findWidgetsBundles(tenantId, pageLink);
  40 + }
  41 +
  42 + @Override
  43 + EdgeEvent constructEdgeEvent(TenantId tenantId, Edge edge, WidgetsBundle widgetsBundle) {
  44 + return EdgeEventUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.WIDGETS_BUNDLE,
  45 + EdgeEventActionType.ADDED, widgetsBundle.getId(), null);
  46 + }
  47 +
  48 + protected abstract PageData<WidgetsBundle> findWidgetsBundles(TenantId tenantId, PageLink pageLink);
  49 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2021 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.edge.rpc.fetch;
  17 +
  18 +import lombok.AllArgsConstructor;
  19 +import lombok.extern.slf4j.Slf4j;
  20 +import org.thingsboard.server.common.data.edge.Edge;
  21 +import org.thingsboard.server.common.data.edge.EdgeEvent;
  22 +import org.thingsboard.server.common.data.edge.EdgeEventActionType;
  23 +import org.thingsboard.server.common.data.edge.EdgeEventType;
  24 +import org.thingsboard.server.common.data.id.TenantId;
  25 +import org.thingsboard.server.common.data.page.PageData;
  26 +import org.thingsboard.server.common.data.page.PageLink;
  27 +import org.thingsboard.server.service.edge.rpc.EdgeEventUtils;
  28 +
  29 +import java.util.ArrayList;
  30 +import java.util.List;
  31 +
  32 +@AllArgsConstructor
  33 +@Slf4j
  34 +public class CustomerEdgeEventFetcher implements EdgeEventFetcher {
  35 +
  36 + @Override
  37 + public PageLink getPageLink(int pageSize) {
  38 + return null;
  39 + }
  40 +
  41 + @Override
  42 + public PageData<EdgeEvent> fetchEdgeEvents(TenantId tenantId, Edge edge, PageLink pageLink) {
  43 + List<EdgeEvent> result = new ArrayList<>();
  44 + result.add(EdgeEventUtils.constructEdgeEvent(edge.getTenantId(), edge.getId(),
  45 + EdgeEventType.CUSTOMER, EdgeEventActionType.ADDED, edge.getCustomerId(), null));
  46 + // @voba - returns PageData object to be in sync with other fetchers
  47 + return new PageData<>(result, 1, result.size(), false);
  48 + }
  49 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2021 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.edge.rpc.fetch;
  17 +
  18 +import org.thingsboard.server.common.data.User;
  19 +import org.thingsboard.server.common.data.edge.Edge;
  20 +import org.thingsboard.server.common.data.edge.EdgeEvent;
  21 +import org.thingsboard.server.common.data.id.CustomerId;
  22 +import org.thingsboard.server.common.data.id.TenantId;
  23 +import org.thingsboard.server.common.data.page.PageData;
  24 +import org.thingsboard.server.common.data.page.PageLink;
  25 +import org.thingsboard.server.dao.user.UserService;
  26 +
  27 +public class CustomerUsersEdgeEventFetcher extends BaseUsersEdgeEventFetcher {
  28 +
  29 + private final CustomerId customerId;
  30 +
  31 + public CustomerUsersEdgeEventFetcher(UserService userService, CustomerId customerId) {
  32 + super(userService);
  33 + this.customerId = customerId;
  34 + }
  35 +
  36 + @Override
  37 + protected PageData<User> findUsers(TenantId tenantId, PageLink pageLink) {
  38 + return userService.findCustomerUsers(tenantId, customerId, pageLink);
  39 + }
  40 +
  41 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2021 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.edge.rpc.fetch;
  17 +
  18 +import lombok.AllArgsConstructor;
  19 +import lombok.extern.slf4j.Slf4j;
  20 +import org.thingsboard.server.common.data.DashboardInfo;
  21 +import org.thingsboard.server.common.data.edge.Edge;
  22 +import org.thingsboard.server.common.data.edge.EdgeEvent;
  23 +import org.thingsboard.server.common.data.edge.EdgeEventActionType;
  24 +import org.thingsboard.server.common.data.edge.EdgeEventType;
  25 +import org.thingsboard.server.common.data.id.TenantId;
  26 +import org.thingsboard.server.common.data.page.PageData;
  27 +import org.thingsboard.server.common.data.page.PageLink;
  28 +import org.thingsboard.server.dao.dashboard.DashboardService;
  29 +import org.thingsboard.server.service.edge.rpc.EdgeEventUtils;
  30 +
  31 +@AllArgsConstructor
  32 +@Slf4j
  33 +public class DashboardsEdgeEventFetcher extends BasePageableEdgeEventFetcher<DashboardInfo> {
  34 +
  35 + private final DashboardService dashboardService;
  36 +
  37 + @Override
  38 + PageData<DashboardInfo> fetchPageData(TenantId tenantId, Edge edge, PageLink pageLink) {
  39 + return dashboardService.findDashboardsByTenantIdAndEdgeId(tenantId, edge.getId(), pageLink);
  40 + }
  41 +
  42 + @Override
  43 + EdgeEvent constructEdgeEvent(TenantId tenantId, Edge edge, DashboardInfo dashboardInfo) {
  44 + return EdgeEventUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.DASHBOARD,
  45 + EdgeEventActionType.ADDED, dashboardInfo.getId(), null);
  46 + }
  47 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2021 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.edge.rpc.fetch;
  17 +
  18 +import lombok.AllArgsConstructor;
  19 +import lombok.extern.slf4j.Slf4j;
  20 +import org.thingsboard.server.common.data.DeviceProfile;
  21 +import org.thingsboard.server.common.data.edge.Edge;
  22 +import org.thingsboard.server.common.data.edge.EdgeEvent;
  23 +import org.thingsboard.server.common.data.edge.EdgeEventActionType;
  24 +import org.thingsboard.server.common.data.edge.EdgeEventType;
  25 +import org.thingsboard.server.common.data.id.TenantId;
  26 +import org.thingsboard.server.common.data.page.PageData;
  27 +import org.thingsboard.server.common.data.page.PageLink;
  28 +import org.thingsboard.server.dao.device.DeviceProfileService;
  29 +import org.thingsboard.server.service.edge.rpc.EdgeEventUtils;
  30 +
  31 +@AllArgsConstructor
  32 +@Slf4j
  33 +public class DeviceProfilesEdgeEventFetcher extends BasePageableEdgeEventFetcher<DeviceProfile> {
  34 +
  35 + private final DeviceProfileService deviceProfileService;
  36 +
  37 + @Override
  38 + PageData<DeviceProfile> fetchPageData(TenantId tenantId, Edge edge, PageLink pageLink) {
  39 + return deviceProfileService.findDeviceProfiles(tenantId, pageLink);
  40 + }
  41 +
  42 + @Override
  43 + EdgeEvent constructEdgeEvent(TenantId tenantId, Edge edge, DeviceProfile deviceProfile) {
  44 + return EdgeEventUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE_PROFILE,
  45 + EdgeEventActionType.ADDED, deviceProfile.getId(), null);
  46 + }
  47 +}
... ...