Commit e33a336ed93362a12658611a7b9660b4dba8e5e7

Authored by Viacheslav Klimov
2 parents 3b17d14f f12903ba

Merge remote-tracking branch 'origin/master' into feature/bulk-import/device-credentials

# Conflicts:
#	application/src/main/java/org/thingsboard/server/controller/DeviceController.java
#	application/src/main/java/org/thingsboard/server/controller/EdgeController.java
#	common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceService.java
#	ui-ngx/src/app/core/http/device.service.ts
Showing 100 changed files with 1270 additions and 830 deletions

Too many changes to show.

To preserve performance only 100 of 261 files are displayed.

... ... @@ -66,6 +66,10 @@
66 66 <artifactId>rule-engine-api</artifactId>
67 67 </dependency>
68 68 <dependency>
  69 + <groupId>org.thingsboard.common</groupId>
  70 + <artifactId>cluster-api</artifactId>
  71 + </dependency>
  72 + <dependency>
69 73 <groupId>org.thingsboard.rule-engine</groupId>
70 74 <artifactId>rule-engine-components</artifactId>
71 75 </dependency>
... ... @@ -290,6 +294,11 @@
290 294 <scope>test</scope>
291 295 </dependency>
292 296 <dependency>
  297 + <groupId>org.awaitility</groupId>
  298 + <artifactId>awaitility</artifactId>
  299 + <scope>test</scope>
  300 + </dependency>
  301 + <dependency>
293 302 <groupId>org.mockito</groupId>
294 303 <artifactId>mockito-core</artifactId>
295 304 <scope>test</scope>
... ...
... ... @@ -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;\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}",
  21 + "controllerScript": "var requestTimeout = 500;\nvar requestPersistent = false;\nvar persistentPollingInterval = 5000;\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 if (self.ctx.settings.persistentPollingInterval) {\n persistentPollingInterval = self.ctx.settings.persistentPollingInterval;\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, persistentPollingInterval, 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 self.ctx.controlApi.completedCommand();\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 \"persistentPollingInterval\": {\n \"title\": \"Polling interval in milliseconds to get persistent RPC command response\",\n \"type\": \"number\",\n \"default\": 5000,\n \"minimum\": 1000\n }\n },\n \"required\": [\"requestTimeout\"]\n },\n \"form\": [\n \"requestTimeout\",\n \"requestPersistent\",\n {\n \"key\": \"persistentPollingInterval\",\n \"condition\": \"model.requestPersistent === true\"\n }\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 }
... ... @@ -55,7 +55,7 @@
55 55 "templateHtml": "<tb-knob [ctx]='ctx'></tb-knob>",
56 56 "templateCss": "",
57 57 "controllerScript": "self.onInit = function() {\n}\n\nself.onResize = function() {\n}\n\nself.onDestroy = function() {\n}\n",
58   - "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"minValue\": {\n \"title\": \"Minimum value\",\n \"type\": \"number\",\n \"default\": 0\n },\n \"maxValue\": {\n \"title\": \"Maximum value\",\n \"type\": \"number\",\n \"default\": 100\n },\n \"initialValue\": {\n \"title\": \"Initial value\",\n \"type\": \"number\",\n \"default\": 50\n },\n \"title\": {\n \"title\": \"Knob title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"getValueMethod\": {\n \"title\": \"Get value method\",\n \"type\": \"string\",\n \"default\": \"getValue\"\n },\n \"setValueMethod\": {\n \"title\": \"Set value method\",\n \"type\": \"string\",\n \"default\": \"setValue\"\n },\n \"requestTimeout\": {\n \"title\": \"RPC request timeout\",\n \"type\": \"number\",\n \"default\": 500\n }\n },\n \"required\": [\"minValue\", \"maxValue\", \"getValueMethod\", \"setValueMethod\", \"requestTimeout\"]\n },\n \"form\": [\n \"minValue\",\n \"maxValue\",\n \"initialValue\",\n \"title\",\n \"getValueMethod\",\n \"setValueMethod\",\n \"requestTimeout\"\n ]\n}",
  58 + "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"minValue\": {\n \"title\": \"Minimum value\",\n \"type\": \"number\",\n \"default\": 0\n },\n \"maxValue\": {\n \"title\": \"Maximum value\",\n \"type\": \"number\",\n \"default\": 100\n },\n \"initialValue\": {\n \"title\": \"Initial value\",\n \"type\": \"number\",\n \"default\": 50\n },\n \"title\": {\n \"title\": \"Knob title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"getValueMethod\": {\n \"title\": \"Get value method\",\n \"type\": \"string\",\n \"default\": \"getValue\"\n },\n \"setValueMethod\": {\n \"title\": \"Set value method\",\n \"type\": \"string\",\n \"default\": \"setValue\"\n },\n \"requestTimeout\": {\n \"title\": \"RPC request timeout\",\n \"type\": \"number\",\n \"default\": 500\n },\n \"requestPersistent\": {\n \"title\": \"RPC request persistent\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"persistentPollingInterval\": {\n \"title\": \"Polling interval in milliseconds to get persistent RPC command response\",\n \"type\": \"number\",\n \"default\": 5000,\n \"minimum\": 1000\n }\n },\n \"required\": [\"minValue\", \"maxValue\", \"getValueMethod\", \"setValueMethod\", \"requestTimeout\"]\n },\n \"form\": [\n \"minValue\",\n \"maxValue\",\n \"initialValue\",\n \"title\",\n \"getValueMethod\",\n \"setValueMethod\",\n \"requestTimeout\",\n \"requestPersistent\",\n {\n \"key\": \"persistentPollingInterval\",\n \"condition\": \"model.requestPersistent === true\"\n }\n ]\n}",
59 59 "dataKeySettingsSchema": "{}\n",
60 60 "defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":false,\"backgroundColor\":\"#e6e7e8\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"requestTimeout\":500,\"maxValue\":100,\"initialValue\":50,\"minValue\":0,\"title\":\"Knob control\",\"getValueMethod\":\"getValue\",\"setValueMethod\":\"setValue\"},\"title\":\"Knob Control\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{},\"decimals\":2}"
61 61 }
... ... @@ -73,7 +73,7 @@
73 73 "templateHtml": "<tb-switch [ctx]='ctx'></tb-switch>",
74 74 "templateCss": "",
75 75 "controllerScript": "self.onInit = function() {\n}\n\nself.onResize = function() {\n}\n\nself.onDestroy = function() {\n}\n",
76   - "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"initialValue\": {\n \"title\": \"Initial value\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"title\": {\n \"title\": \"Switch title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"showOnOffLabels\": {\n \"title\": \"Show on/off labels\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"retrieveValueMethod\": {\n \"title\": \"Retrieve on/off value using method\",\n \"type\": \"string\",\n \"default\": \"rpc\"\n },\n \"valueKey\": {\n \"title\": \"Attribute/Timeseries value key (only when subscribe for attribute/timeseries method)\",\n \"type\": \"string\",\n \"default\": \"value\"\n },\n \"getValueMethod\": {\n \"title\": \"RPC get value method\",\n \"type\": \"string\",\n \"default\": \"getValue\"\n },\n \"setValueMethod\": {\n \"title\": \"RPC set value method\",\n \"type\": \"string\",\n \"default\": \"setValue\"\n },\n \"parseValueFunction\": {\n \"title\": \"Parse value function, f(data), returns boolean\",\n \"type\": \"string\",\n \"default\": \"return data ? true : false;\"\n },\n \"convertValueFunction\": {\n \"title\": \"Convert value function, f(value), returns payload used by RPC set value method\",\n \"type\": \"string\",\n \"default\": \"return value;\"\n },\n \"requestTimeout\": {\n \"title\": \"RPC request timeout\",\n \"type\": \"number\",\n \"default\": 500\n }\n },\n \"required\": [\"requestTimeout\"]\n },\n \"form\": [\n \"initialValue\",\n \"title\",\n \"showOnOffLabels\",\n {\n \"key\": \"retrieveValueMethod\",\n \"type\": \"rc-select\",\n \"multiple\": false,\n \"items\": [\n {\n \"value\": \"none\",\n \"label\": \"Don't retrieve\"\n },\n {\n \"value\": \"rpc\",\n \"label\": \"Call RPC get value method\"\n },\n {\n \"value\": \"attribute\",\n \"label\": \"Subscribe for attribute\"\n },\n {\n \"value\": \"timeseries\",\n \"label\": \"Subscribe for timeseries\"\n }\n ]\n },\n \"valueKey\",\n \"getValueMethod\",\n \"setValueMethod\",\n {\n \"key\": \"parseValueFunction\",\n \"type\": \"javascript\"\n },\n {\n \"key\": \"convertValueFunction\",\n \"type\": \"javascript\"\n },\n \"requestTimeout\"\n ]\n}",
  76 + "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"initialValue\": {\n \"title\": \"Initial value\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"title\": {\n \"title\": \"Switch title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"showOnOffLabels\": {\n \"title\": \"Show on/off labels\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"retrieveValueMethod\": {\n \"title\": \"Retrieve on/off value using method\",\n \"type\": \"string\",\n \"default\": \"rpc\"\n },\n \"valueKey\": {\n \"title\": \"Attribute/Timeseries value key (only when subscribe for attribute/timeseries method)\",\n \"type\": \"string\",\n \"default\": \"value\"\n },\n \"getValueMethod\": {\n \"title\": \"RPC get value method\",\n \"type\": \"string\",\n \"default\": \"getValue\"\n },\n \"setValueMethod\": {\n \"title\": \"RPC set value method\",\n \"type\": \"string\",\n \"default\": \"setValue\"\n },\n \"parseValueFunction\": {\n \"title\": \"Parse value function, f(data), returns boolean\",\n \"type\": \"string\",\n \"default\": \"return data ? true : false;\"\n },\n \"convertValueFunction\": {\n \"title\": \"Convert value function, f(value), returns payload used by RPC set value method\",\n \"type\": \"string\",\n \"default\": \"return value;\"\n },\n \"requestTimeout\": {\n \"title\": \"RPC request timeout\",\n \"type\": \"number\",\n \"default\": 500\n },\n \"requestPersistent\": {\n \"title\": \"RPC request persistent\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"persistentPollingInterval\": {\n \"title\": \"Polling interval in milliseconds to get persistent RPC command response\",\n \"type\": \"number\",\n \"default\": 5000,\n \"minimum\": 1000\n }\n },\n \"required\": [\"requestTimeout\"]\n },\n \"form\": [\n \"initialValue\",\n \"title\",\n \"showOnOffLabels\",\n {\n \"key\": \"retrieveValueMethod\",\n \"type\": \"rc-select\",\n \"multiple\": false,\n \"items\": [\n {\n \"value\": \"none\",\n \"label\": \"Don't retrieve\"\n },\n {\n \"value\": \"rpc\",\n \"label\": \"Call RPC get value method\"\n },\n {\n \"value\": \"attribute\",\n \"label\": \"Subscribe for attribute\"\n },\n {\n \"value\": \"timeseries\",\n \"label\": \"Subscribe for timeseries\"\n }\n ]\n },\n \"valueKey\",\n \"getValueMethod\",\n \"setValueMethod\",\n {\n \"key\": \"parseValueFunction\",\n \"type\": \"javascript\"\n },\n {\n \"key\": \"convertValueFunction\",\n \"type\": \"javascript\"\n },\n \"requestTimeout\",\n \"requestPersistent\",\n {\n \"key\": \"persistentPollingInterval\",\n \"condition\": \"model.requestPersistent === true\"\n }\n ]\n}",
77 77 "dataKeySettingsSchema": "{}\n",
78 78 "defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":false,\"backgroundColor\":\"#e6e7e8\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"requestTimeout\":500,\"initialValue\":false,\"getValueMethod\":\"getValue\",\"setValueMethod\":\"setValue\",\"showOnOffLabels\":true,\"title\":\"Switch control\"},\"title\":\"Switch Control\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{},\"decimals\":2}"
79 79 }
... ... @@ -91,7 +91,7 @@
91 91 "templateHtml": "<tb-round-switch [ctx]='ctx'></tb-round-switch>",
92 92 "templateCss": "",
93 93 "controllerScript": "self.onInit = function() {\n}\n\nself.onResize = function() {\n}\n\nself.onDestroy = function() {\n}\n",
94   - "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"initialValue\": {\n \"title\": \"Initial value\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"title\": {\n \"title\": \"Switch title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"retrieveValueMethod\": {\n \"title\": \"Retrieve on/off value using method\",\n \"type\": \"string\",\n \"default\": \"rpc\"\n },\n \"valueKey\": {\n \"title\": \"Attribute/Timeseries value key (only when subscribe for attribute/timeseries method)\",\n \"type\": \"string\",\n \"default\": \"value\"\n },\n \"getValueMethod\": {\n \"title\": \"RPC get value method\",\n \"type\": \"string\",\n \"default\": \"getValue\"\n },\n \"setValueMethod\": {\n \"title\": \"RPC set value method\",\n \"type\": \"string\",\n \"default\": \"setValue\"\n },\n \"parseValueFunction\": {\n \"title\": \"Parse value function, f(data), returns boolean\",\n \"type\": \"string\",\n \"default\": \"return data ? true : false;\"\n },\n \"convertValueFunction\": {\n \"title\": \"Convert value function, f(value), returns payload used by RPC set value method\",\n \"type\": \"string\",\n \"default\": \"return value;\"\n },\n \"requestTimeout\": {\n \"title\": \"RPC request timeout\",\n \"type\": \"number\",\n \"default\": 500\n }\n },\n \"required\": [\"requestTimeout\"]\n },\n \"form\": [\n \"initialValue\",\n \"title\",\n {\n \"key\": \"retrieveValueMethod\",\n \"type\": \"rc-select\",\n \"multiple\": false,\n \"items\": [\n {\n \"value\": \"none\",\n \"label\": \"Don't retrieve\"\n },\n {\n \"value\": \"rpc\",\n \"label\": \"Call RPC get value method\"\n },\n {\n \"value\": \"attribute\",\n \"label\": \"Subscribe for attribute\"\n },\n {\n \"value\": \"timeseries\",\n \"label\": \"Subscribe for timeseries\"\n }\n ]\n },\n \"valueKey\",\n \"getValueMethod\",\n \"setValueMethod\",\n {\n \"key\": \"parseValueFunction\",\n \"type\": \"javascript\"\n },\n {\n \"key\": \"convertValueFunction\",\n \"type\": \"javascript\"\n },\n \"requestTimeout\"\n ]\n}",
  94 + "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"initialValue\": {\n \"title\": \"Initial value\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"title\": {\n \"title\": \"Switch title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"retrieveValueMethod\": {\n \"title\": \"Retrieve on/off value using method\",\n \"type\": \"string\",\n \"default\": \"rpc\"\n },\n \"valueKey\": {\n \"title\": \"Attribute/Timeseries value key (only when subscribe for attribute/timeseries method)\",\n \"type\": \"string\",\n \"default\": \"value\"\n },\n \"getValueMethod\": {\n \"title\": \"RPC get value method\",\n \"type\": \"string\",\n \"default\": \"getValue\"\n },\n \"setValueMethod\": {\n \"title\": \"RPC set value method\",\n \"type\": \"string\",\n \"default\": \"setValue\"\n },\n \"parseValueFunction\": {\n \"title\": \"Parse value function, f(data), returns boolean\",\n \"type\": \"string\",\n \"default\": \"return data ? true : false;\"\n },\n \"convertValueFunction\": {\n \"title\": \"Convert value function, f(value), returns payload used by RPC set value method\",\n \"type\": \"string\",\n \"default\": \"return value;\"\n },\n \"requestTimeout\": {\n \"title\": \"RPC request timeout\",\n \"type\": \"number\",\n \"default\": 500\n },\n \"requestPersistent\": {\n \"title\": \"RPC request persistent\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"persistentPollingInterval\": {\n \"title\": \"Polling interval in milliseconds to get persistent RPC command response\",\n \"type\": \"number\",\n \"default\": 5000,\n \"minimum\": 1000\n }\n },\n \"required\": [\"requestTimeout\"]\n },\n \"form\": [\n \"initialValue\",\n \"title\",\n {\n \"key\": \"retrieveValueMethod\",\n \"type\": \"rc-select\",\n \"multiple\": false,\n \"items\": [\n {\n \"value\": \"none\",\n \"label\": \"Don't retrieve\"\n },\n {\n \"value\": \"rpc\",\n \"label\": \"Call RPC get value method\"\n },\n {\n \"value\": \"attribute\",\n \"label\": \"Subscribe for attribute\"\n },\n {\n \"value\": \"timeseries\",\n \"label\": \"Subscribe for timeseries\"\n }\n ]\n },\n \"valueKey\",\n \"getValueMethod\",\n \"setValueMethod\",\n {\n \"key\": \"parseValueFunction\",\n \"type\": \"javascript\"\n },\n {\n \"key\": \"convertValueFunction\",\n \"type\": \"javascript\"\n },\n \"requestTimeout\",\n \"requestPersistent\",\n {\n \"key\": \"persistentPollingInterval\",\n \"condition\": \"model.requestPersistent === true\"\n }\n ]\n}",
95 95 "dataKeySettingsSchema": "{}\n",
96 96 "defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":false,\"backgroundColor\":\"#e6e7e8\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"requestTimeout\":500,\"initialValue\":false,\"getValueMethod\":\"getValue\",\"setValueMethod\":\"setValue\",\"title\":\"Round switch\",\"retrieveValueMethod\":\"rpc\",\"valueKey\":\"value\",\"parseValueFunction\":\"return data ? true : false;\",\"convertValueFunction\":\"return value;\"},\"title\":\"Round switch\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{},\"decimals\":2}"
97 97 }
... ... @@ -109,7 +109,7 @@
109 109 "templateHtml": "<tb-led-indicator [ctx]='ctx'></tb-led-indicator>",
110 110 "templateCss": "",
111 111 "controllerScript": "self.onInit = function() {\n}\n\nself.onResize = function() {\n}\n\nself.onDestroy = function() {\n}\n",
112   - "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"initialValue\": {\n \"title\": \"Initial value\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"title\": {\n \"title\": \"LED title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"ledColor\": {\n \"title\": \"LED Color\",\n \"type\": \"string\",\n \"default\": \"green\"\n },\n \"performCheckStatus\": {\n \"title\": \"Perform RPC device status check\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"checkStatusMethod\": {\n \"title\": \"RPC check device status method\",\n \"type\": \"string\",\n \"default\": \"checkStatus\"\n },\n \"retrieveValueMethod\": {\n \"title\": \"Retrieve led status value using method\",\n \"type\": \"string\",\n \"default\": \"attribute\"\n },\n \"valueAttribute\": {\n \"title\": \"Device attribute/timeseries containing led status value\",\n \"type\": \"string\",\n \"default\": \"value\"\n },\n \"parseValueFunction\": {\n \"title\": \"Parse led status value function, f(data), returns boolean\",\n \"type\": \"string\",\n \"default\": \"return data ? true : false;\"\n },\n \"requestTimeout\": {\n \"title\": \"RPC request timeout (ms)\",\n \"type\": \"number\",\n \"default\": 500\n }\n },\n \"required\": [\"valueAttribute\", \"requestTimeout\"]\n },\n \"form\": [\n \"initialValue\",\n \"title\",\n {\n \"key\": \"ledColor\",\n \"type\": \"color\"\n },\n \"performCheckStatus\",\n \"checkStatusMethod\",\n {\n \"key\": \"retrieveValueMethod\",\n \"type\": \"rc-select\",\n \"multiple\": false,\n \"items\": [\n {\n \"value\": \"attribute\",\n \"label\": \"Subscribe for attribute\"\n },\n {\n \"value\": \"timeseries\",\n \"label\": \"Subscribe for timeseries\"\n }\n ]\n },\n \"valueAttribute\",\n {\n \"key\": \"parseValueFunction\",\n \"type\": \"javascript\"\n },\n \"requestTimeout\"\n ]\n}",
  112 + "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"initialValue\": {\n \"title\": \"Initial value\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"title\": {\n \"title\": \"LED title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"ledColor\": {\n \"title\": \"LED Color\",\n \"type\": \"string\",\n \"default\": \"green\"\n },\n \"performCheckStatus\": {\n \"title\": \"Perform RPC device status check\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"checkStatusMethod\": {\n \"title\": \"RPC check device status method\",\n \"type\": \"string\",\n \"default\": \"checkStatus\"\n },\n \"retrieveValueMethod\": {\n \"title\": \"Retrieve led status value using method\",\n \"type\": \"string\",\n \"default\": \"attribute\"\n },\n \"valueAttribute\": {\n \"title\": \"Device attribute/timeseries containing led status value\",\n \"type\": \"string\",\n \"default\": \"value\"\n },\n \"parseValueFunction\": {\n \"title\": \"Parse led status value function, f(data), returns boolean\",\n \"type\": \"string\",\n \"default\": \"return data ? true : false;\"\n },\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 \"persistentPollingInterval\": {\n \"title\": \"Polling interval in milliseconds to get persistent RPC command response\",\n \"type\": \"number\",\n \"default\": 5000,\n \"minimum\": 1000\n }\n },\n \"required\": [\"valueAttribute\", \"requestTimeout\"]\n },\n \"form\": [\n \"initialValue\",\n \"title\",\n {\n \"key\": \"ledColor\",\n \"type\": \"color\"\n },\n \"performCheckStatus\",\n \"checkStatusMethod\",\n {\n \"key\": \"retrieveValueMethod\",\n \"type\": \"rc-select\",\n \"multiple\": false,\n \"items\": [\n {\n \"value\": \"attribute\",\n \"label\": \"Subscribe for attribute\"\n },\n {\n \"value\": \"timeseries\",\n \"label\": \"Subscribe for timeseries\"\n }\n ]\n },\n \"valueAttribute\",\n {\n \"key\": \"parseValueFunction\",\n \"type\": \"javascript\"\n },\n \"requestTimeout\",\n \"requestPersistent\",\n {\n \"key\": \"persistentPollingInterval\",\n \"condition\": \"model.requestPersistent === true\"\n }\n ]\n}",
113 113 "dataKeySettingsSchema": "{}\n",
114 114 "defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":false,\"backgroundColor\":\"#e6e7e8\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"requestTimeout\":500,\"initialValue\":true,\"title\":\"Led indicator\",\"ledColor\":\"#4caf50\",\"valueAttribute\":\"value\",\"retrieveValueMethod\":\"attribute\",\"parseValueFunction\":\"return data ? true : false;\",\"performCheckStatus\":true,\"checkStatusMethod\":\"checkStatus\"},\"title\":\"Led indicator\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{},\"decimals\":2}"
115 115 }
... ... @@ -126,8 +126,8 @@
126 126 "resources": [],
127 127 "templateHtml": "<div class=\"tb-rpc-button\" fxLayout=\"column\">\n <div fxFlex=\"20\" class=\"title-container\" fxLayout=\"row\"\n fxLayoutAlign=\"center center\" [fxShow]=\"showTitle\">\n <span class=\"button-title\">{{title}}</span>\n </div>\n <div fxFlex=\"{{showTitle ? 80 : 100}}\" [ngStyle]=\"{paddingTop: showTitle ? '5px': '10px'}\"\n class=\"button-container\" fxLayout=\"column\" fxLayoutAlign=\"center center\">\n <div>\n <button mat-button (click)=\"sendCommand()\"\n [class.mat-raised-button]=\"styleButton?.isRaised\"\n [color]=\"styleButton?.isPrimary ? 'primary' : ''\"\n [ngStyle]=\"customStyle\">\n {{buttonLable}}\n </button>\n </div>\n </div>\n <div class=\"error-container\" [ngStyle]=\"{'background': error?.length ? 'rgba(255,255,255,0.25)' : 'none'}\"\n fxLayout=\"row\" fxLayoutAlign=\"center center\">\n <span class=\"button-error\">{{ error }}</span>\n </div>\n</div>",
128 128 "templateCss": ".tb-rpc-button {\n width: 100%;\n height: 100%;\n}\n\n.tb-rpc-button .title-container {\n font-weight: 500;\n white-space: nowrap;\n margin: 10px 0;\n}\n\n.tb-rpc-button .button-container div{\n min-width: 80%\n}\n\n.tb-rpc-button .button-container .mat-button{\n width: 100%;\n margin: 0;\n}\n\n.tb-rpc-button .error-container {\n position: absolute;\n top: 2%;\n right: 0;\n left: 0;\n z-index: 4;\n height: 14px;\n}\n\n.tb-rpc-button .error-container .button-error {\n color: #ff3315;\n white-space: nowrap;\n}",
129   - "controllerScript": "self.onInit = function() {\n self.ctx.ngZone.run(function() {\n init(); \n self.ctx.detectChanges();\n });\n};\n\nfunction init() {\n let rpcEnabled = self.ctx.defaultSubscription.rpcEnabled;\n\n self.ctx.$scope.buttonLable = self.ctx.settings.buttonText;\n self.ctx.$scope.showTitle = self.ctx.settings.title &&\n self.ctx.settings.title.length ? true : false;\n self.ctx.$scope.title = self.ctx.settings.title;\n self.ctx.$scope.styleButton = self.ctx.settings.styleButton;\n\n if (self.ctx.settings.styleButton.isPrimary ===\n false) {\n self.ctx.$scope.customStyle = {\n 'background-color': self.ctx.$scope.styleButton.bgColor,\n 'color': self.ctx.$scope.styleButton.textColor\n };\n }\n\n if (!rpcEnabled) {\n self.ctx.$scope.error =\n 'Target device is not set!';\n }\n\n self.ctx.$scope.sendCommand = function() {\n var rpcMethod = self.ctx.settings.methodName;\n var rpcParams = self.ctx.settings.methodParams;\n var timeout = self.ctx.settings.requestTimeout;\n var oneWayElseTwoWay = self.ctx.settings.oneWayElseTwoWay ?\n true : false;\n\n var commandPromise;\n if (oneWayElseTwoWay) {\n commandPromise = self.ctx.controlApi.sendOneWayCommand(\n rpcMethod, rpcParams, timeout);\n } else {\n commandPromise = self.ctx.controlApi.sendTwoWayCommand(\n rpcMethod, rpcParams, timeout);\n }\n commandPromise.subscribe(\n function success() {\n self.ctx.$scope.error = \"\";\n self.ctx.detectChanges();\n },\n function fail(rejection) {\n if (self.ctx.settings.showError) {\n self.ctx.$scope.error =\n rejection.status + \": \" +\n rejection.statusText;\n self.ctx.detectChanges();\n }\n }\n );\n };\n}\n",
130   - "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"title\": {\n \"title\": \"Widget title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"buttonText\": {\n \"title\": \"Button label\",\n \"type\": \"string\",\n \"default\": \"Send RPC\"\n },\n \"oneWayElseTwoWay\": {\n \"title\": \"Is One Way Command\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"showError\": {\n \"title\": \"Show RPC command execution error\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"methodName\": {\n \"title\": \"RPC method\",\n \"type\": \"string\",\n \"default\": \"rpcCommand\"\n },\n \"methodParams\": {\n \"title\": \"RPC method params\",\n \"type\": \"string\",\n \"default\": \"{}\"\n },\n \"requestTimeout\": {\n \"title\": \"RPC request timeout\",\n \"type\": \"number\",\n \"default\": 5000\n },\n \"styleButton\": {\n \"type\": \"object\",\n \"title\": \"Button Style\",\n \"properties\": {\n \"isRaised\": {\n \"type\": \"boolean\",\n \"title\": \"Raised\",\n \"default\": true\n },\n \"isPrimary\": {\n \"type\": \"boolean\",\n \"title\": \"Primary color\",\n \"default\": false\n },\n \"bgColor\": {\n \"type\": \"string\",\n \"title\": \"Button background color\",\n \"default\": null\n },\n \"textColor\": {\n \"type\": \"string\",\n \"title\": \"Button text color\",\n \"default\": null\n }\n }\n },\n \"required\": []\n }\n },\n \"form\": [\n \"title\",\n \"buttonText\",\n \"oneWayElseTwoWay\",\n \"showError\",\n \"methodName\",\n {\n \"key\": \"methodParams\",\n \"type\": \"json\"\n },\n \"requestTimeout\",\n {\n \"key\": \"styleButton\",\n \"items\": [\n \"styleButton.isRaised\",\n \"styleButton.isPrimary\",\n {\n \"key\": \"styleButton.bgColor\",\n \"type\": \"color\"\n },\n {\n \"key\": \"styleButton.textColor\",\n \"type\": \"color\"\n }\n ]\n }\n ]\n\n}",
  129 + "controllerScript": "var requestPersistent = false;\nvar persistentPollingInterval = 5000;\n\nself.onInit = function() {\n if (self.ctx.settings.requestPersistent) {\n requestPersistent = self.ctx.settings.requestPersistent;\n }\n if (self.ctx.settings.persistentPollingInterval) {\n persistentPollingInterval = self.ctx.settings.persistentPollingInterval;\n }\n \n self.ctx.ngZone.run(function() {\n init(); \n self.ctx.detectChanges();\n });\n};\n\nfunction init() {\n let rpcEnabled = self.ctx.defaultSubscription.rpcEnabled;\n\n self.ctx.$scope.buttonLable = self.ctx.settings.buttonText;\n self.ctx.$scope.showTitle = self.ctx.settings.title &&\n self.ctx.settings.title.length ? true : false;\n self.ctx.$scope.title = self.ctx.settings.title;\n self.ctx.$scope.styleButton = self.ctx.settings.styleButton;\n\n if (self.ctx.settings.styleButton.isPrimary ===\n false) {\n self.ctx.$scope.customStyle = {\n 'background-color': self.ctx.$scope.styleButton.bgColor,\n 'color': self.ctx.$scope.styleButton.textColor\n };\n }\n\n if (!rpcEnabled) {\n self.ctx.$scope.error =\n 'Target device is not set!';\n }\n\n self.ctx.$scope.sendCommand = function() {\n var rpcMethod = self.ctx.settings.methodName;\n var rpcParams = self.ctx.settings.methodParams;\n var timeout = self.ctx.settings.requestTimeout;\n var oneWayElseTwoWay = self.ctx.settings.oneWayElseTwoWay ?\n true : false;\n\n var commandPromise;\n if (oneWayElseTwoWay) {\n commandPromise = self.ctx.controlApi.sendOneWayCommand(\n rpcMethod, rpcParams, timeout, requestPersistent, persistentPollingInterval);\n } else {\n commandPromise = self.ctx.controlApi.sendTwoWayCommand(\n rpcMethod, rpcParams, timeout, requestPersistent, persistentPollingInterval);\n }\n commandPromise.subscribe(\n function success() {\n self.ctx.$scope.error = \"\";\n self.ctx.detectChanges();\n },\n function fail(rejection) {\n if (self.ctx.settings.showError) {\n self.ctx.$scope.error =\n rejection.status + \": \" +\n rejection.statusText;\n self.ctx.detectChanges();\n }\n }\n );\n };\n}\n\nself.onDestroy = function() {\n self.ctx.controlApi.completedCommand();\n}\n",
  130 + "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"title\": {\n \"title\": \"Widget title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"buttonText\": {\n \"title\": \"Button label\",\n \"type\": \"string\",\n \"default\": \"Send RPC\"\n },\n \"oneWayElseTwoWay\": {\n \"title\": \"Is One Way Command\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"showError\": {\n \"title\": \"Show RPC command execution error\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"methodName\": {\n \"title\": \"RPC method\",\n \"type\": \"string\",\n \"default\": \"rpcCommand\"\n },\n \"methodParams\": {\n \"title\": \"RPC method params\",\n \"type\": \"string\",\n \"default\": \"{}\"\n },\n \"requestTimeout\": {\n \"title\": \"RPC request timeout\",\n \"type\": \"number\",\n \"default\": 5000\n },\n \"requestPersistent\": {\n \"title\": \"RPC request persistent\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"persistentPollingInterval\": {\n \"title\": \"Polling interval in milliseconds to get persistent RPC command response\",\n \"type\": \"number\",\n \"default\": 5000,\n \"minimum\": 1000\n },\n \"styleButton\": {\n \"type\": \"object\",\n \"title\": \"Button Style\",\n \"properties\": {\n \"isRaised\": {\n \"type\": \"boolean\",\n \"title\": \"Raised\",\n \"default\": true\n },\n \"isPrimary\": {\n \"type\": \"boolean\",\n \"title\": \"Primary color\",\n \"default\": false\n },\n \"bgColor\": {\n \"type\": \"string\",\n \"title\": \"Button background color\",\n \"default\": null\n },\n \"textColor\": {\n \"type\": \"string\",\n \"title\": \"Button text color\",\n \"default\": null\n }\n }\n },\n \"required\": []\n }\n },\n \"form\": [\n \"title\",\n \"buttonText\",\n \"oneWayElseTwoWay\",\n \"showError\",\n \"methodName\",\n {\n \"key\": \"methodParams\",\n \"type\": \"json\"\n },\n \"requestTimeout\",\n \"requestPersistent\",\n {\n \"key\": \"persistentPollingInterval\",\n \"condition\": \"model.requestPersistent === true\"\n },\n {\n \"key\": \"styleButton\",\n \"items\": [\n \"styleButton.isRaised\",\n \"styleButton.isPrimary\",\n {\n \"key\": \"styleButton.bgColor\",\n \"type\": \"color\"\n },\n {\n \"key\": \"styleButton.textColor\",\n \"type\": \"color\"\n }\n ]\n }\n ]\n\n}",
131 131 "dataKeySettingsSchema": "{}\n",
132 132 "defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":false,\"backgroundColor\":\"#e6e7e8\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"requestTimeout\":5000,\"oneWayElseTwoWay\":true,\"buttonText\":\"Send RPC\",\"styleButton\":{\"isRaised\":true,\"isPrimary\":false},\"methodName\":\"rpcCommand\",\"methodParams\":\"{}\"},\"title\":\"RPC Button\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
133 133 }
... ...
... ... @@ -209,6 +209,7 @@ CREATE TABLE IF NOT EXISTS rpc (
209 209 expiration_time bigint NOT NULL,
210 210 request varchar(10000000) NOT NULL,
211 211 response varchar(10000000),
  212 + additional_info varchar(10000000),
212 213 status varchar(255) NOT NULL
213 214 );
214 215
... ...
... ... @@ -81,7 +81,7 @@ import org.thingsboard.server.service.executors.ExternalCallExecutorService;
81 81 import org.thingsboard.server.service.executors.SharedEventLoopGroupService;
82 82 import org.thingsboard.server.service.mail.MailExecutorService;
83 83 import org.thingsboard.server.service.profile.TbDeviceProfileCache;
84   -import org.thingsboard.server.service.queue.TbClusterService;
  84 +import org.thingsboard.server.cluster.TbClusterService;
85 85 import org.thingsboard.server.service.rpc.TbCoreDeviceRpcService;
86 86 import org.thingsboard.server.service.rpc.TbRpcService;
87 87 import org.thingsboard.server.service.rpc.TbRuleEngineDeviceRpcService;
... ...
... ... @@ -55,7 +55,7 @@ public class AppActor extends ContextAwareActor {
55 55 private final TbTenantProfileCache tenantProfileCache;
56 56 private final TenantService tenantService;
57 57 private final Set<TenantId> deletedTenants;
58   - private boolean ruleChainsInitialized;
  58 + private volatile boolean ruleChainsInitialized;
59 59
60 60 private AppActor(ActorSystemContext systemContext) {
61 61 super(systemContext);
... ... @@ -69,7 +69,7 @@ public class AppActor extends ContextAwareActor {
69 69 if (!ruleChainsInitialized) {
70 70 initTenantActors();
71 71 ruleChainsInitialized = true;
72   - if (msg.getMsgType() != MsgType.APP_INIT_MSG) {
  72 + if (msg.getMsgType() != MsgType.APP_INIT_MSG && msg.getMsgType() != MsgType.PARTITION_CHANGE_MSG) {
73 73 log.warn("Rule Chains initialized by unexpected message: {}", msg);
74 74 }
75 75 }
... ... @@ -95,6 +95,7 @@ public class AppActor extends ContextAwareActor {
95 95 case DEVICE_RPC_REQUEST_TO_DEVICE_ACTOR_MSG:
96 96 case DEVICE_RPC_RESPONSE_TO_DEVICE_ACTOR_MSG:
97 97 case SERVER_RPC_RESPONSE_TO_DEVICE_ACTOR_MSG:
  98 + case REMOVE_RPC_TO_DEVICE_ACTOR_MSG:
98 99 onToDeviceActorMsg((TenantAwareMsg) msg, true);
99 100 break;
100 101 case EDGE_EVENT_UPDATE_TO_EDGE_SESSION_MSG:
... ...
... ... @@ -28,6 +28,7 @@ import org.thingsboard.server.common.data.id.TenantId;
28 28 import org.thingsboard.server.common.msg.TbActorMsg;
29 29 import org.thingsboard.server.common.msg.timeout.DeviceActorServerSideRpcTimeoutMsg;
30 30 import org.thingsboard.server.service.rpc.FromDeviceRpcResponseActorMsg;
  31 +import org.thingsboard.server.service.rpc.RemoveRpcActorMsg;
31 32 import org.thingsboard.server.service.rpc.ToDeviceRpcRequestActorMsg;
32 33 import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper;
33 34
... ... @@ -84,6 +85,9 @@ public class DeviceActor extends ContextAwareActor {
84 85 case DEVICE_EDGE_UPDATE_TO_DEVICE_ACTOR_MSG:
85 86 processor.processEdgeUpdate((DeviceEdgeUpdateMsg) msg);
86 87 break;
  88 + case REMOVE_RPC_TO_DEVICE_ACTOR_MSG:
  89 + processor.processRemoveRpc(ctx, (RemoveRpcActorMsg) msg);
  90 + break;
87 91 default:
88 92 return false;
89 93 }
... ...
... ... @@ -26,7 +26,7 @@ import lombok.extern.slf4j.Slf4j;
26 26 import org.apache.commons.collections.CollectionUtils;
27 27 import org.thingsboard.common.util.JacksonUtil;
28 28 import org.thingsboard.common.util.LinkedHashMapRemoveEldest;
29   -import org.thingsboard.rule.engine.api.RpcError;
  29 +import org.thingsboard.server.common.data.rpc.RpcError;
30 30 import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg;
31 31 import org.thingsboard.rule.engine.api.msg.DeviceCredentialsUpdateNotificationMsg;
32 32 import org.thingsboard.rule.engine.api.msg.DeviceEdgeUpdateMsg;
... ... @@ -86,8 +86,9 @@ import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg;
86 86 import org.thingsboard.server.gen.transport.TransportProtos.ToTransportUpdateCredentialsProto;
87 87 import org.thingsboard.server.gen.transport.TransportProtos.TransportToDeviceActorMsg;
88 88 import org.thingsboard.server.gen.transport.TransportProtos.TsKvProto;
89   -import org.thingsboard.server.service.rpc.FromDeviceRpcResponse;
  89 +import org.thingsboard.server.common.msg.rpc.FromDeviceRpcResponse;
90 90 import org.thingsboard.server.service.rpc.FromDeviceRpcResponseActorMsg;
  91 +import org.thingsboard.server.service.rpc.RemoveRpcActorMsg;
91 92 import org.thingsboard.server.service.rpc.ToDeviceRpcRequestActorMsg;
92 93 import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper;
93 94
... ... @@ -97,11 +98,9 @@ import java.util.Arrays;
97 98 import java.util.Collections;
98 99 import java.util.HashMap;
99 100 import java.util.HashSet;
100   -import java.util.LinkedHashMap;
101 101 import java.util.List;
102 102 import java.util.Map;
103 103 import java.util.Objects;
104   -import java.util.Optional;
105 104 import java.util.Set;
106 105 import java.util.UUID;
107 106 import java.util.function.Consumer;
... ... @@ -236,6 +235,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
236 235 rpc.setExpirationTime(request.getExpirationTime());
237 236 rpc.setRequest(JacksonUtil.valueToTree(request));
238 237 rpc.setStatus(status);
  238 + rpc.setAdditionalInfo(JacksonUtil.valueToTree(request.getAdditionalInfo()));
239 239 return systemContext.getTbRpcService().save(tenantId, rpc);
240 240 }
241 241
... ... @@ -264,6 +264,21 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
264 264 }
265 265 }
266 266
  267 + void processRemoveRpc(TbActorCtx context, RemoveRpcActorMsg msg) {
  268 + log.debug("[{}] Processing remove rpc command", msg.getRequestId());
  269 + Integer requestId = null;
  270 + for (Map.Entry<Integer, ToDeviceRpcRequestMetadata> entry : toDeviceRpcPendingMap.entrySet()) {
  271 + if (entry.getValue().getMsg().getMsg().getId().equals(msg.getRequestId())) {
  272 + requestId = entry.getKey();
  273 + break;
  274 + }
  275 + }
  276 +
  277 + if (requestId != null) {
  278 + toDeviceRpcPendingMap.remove(requestId);
  279 + }
  280 + }
  281 +
267 282 private void registerPendingRpcRequest(TbActorCtx context, ToDeviceRpcRequestActorMsg msg, boolean sent, ToDeviceRpcRequestMsg rpcRequest, long timeout) {
268 283 toDeviceRpcPendingMap.put(rpcRequest.getRequestId(), new ToDeviceRpcRequestMetadata(msg, sent));
269 284 DeviceActorServerSideRpcTimeoutMsg timeoutMsg = new DeviceActorServerSideRpcTimeoutMsg(rpcRequest.getRequestId(), timeout);
... ... @@ -383,11 +398,11 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
383 398 }
384 399
385 400 private void reportSessionOpen() {
386   - systemContext.getDeviceStateService().onDeviceConnect(deviceId);
  401 + systemContext.getDeviceStateService().onDeviceConnect(tenantId, deviceId);
387 402 }
388 403
389 404 private void reportSessionClose() {
390   - systemContext.getDeviceStateService().onDeviceDisconnect(deviceId);
  405 + systemContext.getDeviceStateService().onDeviceDisconnect(tenantId, deviceId);
391 406 }
392 407
393 408 private void handleGetAttributesRequest(TbActorCtx context, SessionInfoProto sessionInfo, GetAttributeRequestMsg request) {
... ... @@ -586,13 +601,13 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
586 601 log.debug("[{}] Received duplicate session open event [{}]", deviceId, sessionId);
587 602 return;
588 603 }
589   - log.info("[{}] Processing new session [{}]. Current sessions size {}", deviceId, sessionId, sessions.size());
  604 + log.debug("[{}] Processing new session [{}]. Current sessions size {}", deviceId, sessionId, sessions.size());
590 605
591 606 sessions.put(sessionId, new SessionInfoMetaData(new SessionInfo(SessionType.ASYNC, sessionInfo.getNodeId())));
592 607 if (sessions.size() == 1) {
593 608 reportSessionOpen();
594 609 }
595   - systemContext.getDeviceStateService().onDeviceActivity(deviceId, System.currentTimeMillis());
  610 + systemContext.getDeviceStateService().onDeviceActivity(tenantId, deviceId, System.currentTimeMillis());
596 611 dumpSessions();
597 612 } else if (msg.getEvent() == SessionEvent.CLOSED) {
598 613 log.debug("[{}] Canceling subscriptions for closed session [{}]", deviceId, sessionId);
... ... @@ -622,7 +637,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
622 637 if (subscriptionInfo.getRpcSubscription()) {
623 638 rpcSubscriptions.putIfAbsent(sessionId, sessionMD.getSessionInfo());
624 639 }
625   - systemContext.getDeviceStateService().onDeviceActivity(deviceId, subscriptionInfo.getLastActivityTime());
  640 + systemContext.getDeviceStateService().onDeviceActivity(tenantId, deviceId, subscriptionInfo.getLastActivityTime());
626 641 dumpSessions();
627 642 }
628 643
... ... @@ -729,18 +744,8 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
729 744 edgeEvent.setBody(body);
730 745
731 746 edgeEvent.setEdgeId(edgeId);
732   - ListenableFuture<EdgeEvent> future = systemContext.getEdgeEventService().saveAsync(edgeEvent);
733   - Futures.addCallback(future, new FutureCallback<EdgeEvent>() {
734   - @Override
735   - public void onSuccess(EdgeEvent result) {
736   - systemContext.getClusterService().onEdgeEventUpdate(tenantId, edgeId);
737   - }
738   -
739   - @Override
740   - public void onFailure(Throwable t) {
741   - log.warn("[{}] Can't save edge event [{}] for edge [{}]", tenantId.getId(), edgeEvent, edgeId.getId(), t);
742   - }
743   - }, systemContext.getDbCallbackExecutor());
  747 + systemContext.getEdgeEventService().save(edgeEvent);
  748 + systemContext.getClusterService().onEdgeEventUpdate(tenantId, edgeId);
744 749 }
745 750
746 751 private List<TsKvProto> toTsKvProtos(@Nullable List<AttributeKvEntry> result) {
... ...
... ... @@ -33,6 +33,7 @@ import org.thingsboard.rule.engine.api.TbRelationTypes;
33 33 import org.thingsboard.rule.engine.api.sms.SmsSenderFactory;
34 34 import org.thingsboard.server.actors.ActorSystemContext;
35 35 import org.thingsboard.server.actors.TbActorRef;
  36 +import org.thingsboard.server.cluster.TbClusterService;
36 37 import org.thingsboard.server.common.data.Customer;
37 38 import org.thingsboard.server.common.data.DataConstants;
38 39 import org.thingsboard.server.common.data.Device;
... ... @@ -454,6 +455,11 @@ class DefaultTbContext implements TbContext {
454 455 }
455 456
456 457 @Override
  458 + public TbClusterService getClusterService() {
  459 + return mainCtx.getClusterService();
  460 + }
  461 +
  462 + @Override
457 463 public DashboardService getDashboardService() {
458 464 return mainCtx.getDashboardService();
459 465 }
... ...
... ... @@ -49,7 +49,7 @@ import org.thingsboard.server.queue.TbQueueCallback;
49 49 import org.thingsboard.server.queue.common.MultipleTbQueueTbMsgCallbackWrapper;
50 50 import org.thingsboard.server.queue.common.TbQueueTbMsgCallbackWrapper;
51 51 import org.thingsboard.server.queue.usagestats.TbApiUsageClient;
52   -import org.thingsboard.server.service.queue.TbClusterService;
  52 +import org.thingsboard.server.cluster.TbClusterService;
53 53
54 54 import java.util.ArrayList;
55 55 import java.util.Collections;
... ...
... ... @@ -165,6 +165,7 @@ public class TenantActor extends RuleChainManagerActor {
165 165 case DEVICE_RPC_REQUEST_TO_DEVICE_ACTOR_MSG:
166 166 case DEVICE_RPC_RESPONSE_TO_DEVICE_ACTOR_MSG:
167 167 case SERVER_RPC_RESPONSE_TO_DEVICE_ACTOR_MSG:
  168 + case REMOVE_RPC_TO_DEVICE_ACTOR_MSG:
168 169 onToDeviceActorMsg((DeviceAwareMsg) msg, true);
169 170 break;
170 171 case RULE_CHAIN_TO_RULE_CHAIN_MSG:
... ...
  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.controller;
  17 +
  18 +import com.fasterxml.jackson.databind.JsonNode;
  19 +import com.google.common.util.concurrent.FutureCallback;
  20 +import lombok.extern.slf4j.Slf4j;
  21 +import org.springframework.beans.factory.annotation.Autowired;
  22 +import org.springframework.beans.factory.annotation.Value;
  23 +import org.springframework.http.HttpStatus;
  24 +import org.springframework.http.ResponseEntity;
  25 +import org.springframework.util.StringUtils;
  26 +import org.springframework.web.context.request.async.DeferredResult;
  27 +import org.thingsboard.common.util.JacksonUtil;
  28 +import org.thingsboard.server.common.data.rpc.RpcError;
  29 +import org.thingsboard.server.common.data.DataConstants;
  30 +import org.thingsboard.server.common.data.audit.ActionType;
  31 +import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
  32 +import org.thingsboard.server.common.data.exception.ThingsboardException;
  33 +import org.thingsboard.server.common.data.id.DeviceId;
  34 +import org.thingsboard.server.common.data.id.EntityId;
  35 +import org.thingsboard.server.common.data.id.TenantId;
  36 +import org.thingsboard.server.common.data.id.UUIDBased;
  37 +import org.thingsboard.server.common.data.rpc.ToDeviceRpcRequestBody;
  38 +import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest;
  39 +import org.thingsboard.server.queue.util.TbCoreComponent;
  40 +import org.thingsboard.server.common.msg.rpc.FromDeviceRpcResponse;
  41 +import org.thingsboard.server.service.rpc.LocalRequestMetaData;
  42 +import org.thingsboard.server.service.rpc.TbCoreDeviceRpcService;
  43 +import org.thingsboard.server.service.security.AccessValidator;
  44 +import org.thingsboard.server.service.security.model.SecurityUser;
  45 +import org.thingsboard.server.service.security.permission.Operation;
  46 +import org.thingsboard.server.service.telemetry.exception.ToErrorResponseEntity;
  47 +
  48 +import javax.annotation.Nullable;
  49 +import java.util.Optional;
  50 +import java.util.UUID;
  51 +
  52 +/**
  53 + * Created by ashvayka on 22.03.18.
  54 + */
  55 +@TbCoreComponent
  56 +@Slf4j
  57 +public abstract class AbstractRpcController extends BaseController {
  58 +
  59 + @Autowired
  60 + protected TbCoreDeviceRpcService deviceRpcService;
  61 +
  62 + @Autowired
  63 + private AccessValidator accessValidator;
  64 +
  65 + @Value("${server.rest.server_side_rpc.min_timeout:5000}")
  66 + protected long minTimeout;
  67 +
  68 + @Value("${server.rest.server_side_rpc.default_timeout:10000}")
  69 + protected long defaultTimeout;
  70 +
  71 + protected DeferredResult<ResponseEntity> handleDeviceRPCRequest(boolean oneWay, DeviceId deviceId, String requestBody, HttpStatus timeoutStatus, HttpStatus noActiveConnectionStatus) throws ThingsboardException {
  72 + try {
  73 + JsonNode rpcRequestBody = JacksonUtil.toJsonNode(requestBody);
  74 + ToDeviceRpcRequestBody body = new ToDeviceRpcRequestBody(rpcRequestBody.get("method").asText(), JacksonUtil.toString(rpcRequestBody.get("params")));
  75 + SecurityUser currentUser = getCurrentUser();
  76 + TenantId tenantId = currentUser.getTenantId();
  77 + final DeferredResult<ResponseEntity> response = new DeferredResult<>();
  78 + long timeout = rpcRequestBody.has("timeout") ? rpcRequestBody.get("timeout").asLong() : defaultTimeout;
  79 + long expTime = System.currentTimeMillis() + Math.max(minTimeout, timeout);
  80 + UUID rpcRequestUUID = rpcRequestBody.has("requestUUID") ? UUID.fromString(rpcRequestBody.get("requestUUID").asText()) : UUID.randomUUID();
  81 + boolean persisted = rpcRequestBody.has(DataConstants.PERSISTENT) && rpcRequestBody.get(DataConstants.PERSISTENT).asBoolean();
  82 + String additionalInfo = JacksonUtil.toString(rpcRequestBody.get(DataConstants.ADDITIONAL_INFO));
  83 + accessValidator.validate(currentUser, Operation.RPC_CALL, deviceId, new HttpValidationCallback(response, new FutureCallback<>() {
  84 + @Override
  85 + public void onSuccess(@Nullable DeferredResult<ResponseEntity> result) {
  86 + ToDeviceRpcRequest rpcRequest = new ToDeviceRpcRequest(rpcRequestUUID,
  87 + tenantId,
  88 + deviceId,
  89 + oneWay,
  90 + expTime,
  91 + body,
  92 + persisted,
  93 + additionalInfo
  94 + );
  95 + deviceRpcService.processRestApiRpcRequest(rpcRequest, fromDeviceRpcResponse -> reply(new LocalRequestMetaData(rpcRequest, currentUser, result), fromDeviceRpcResponse, timeoutStatus, noActiveConnectionStatus), currentUser);
  96 + }
  97 +
  98 + @Override
  99 + public void onFailure(Throwable e) {
  100 + ResponseEntity entity;
  101 + if (e instanceof ToErrorResponseEntity) {
  102 + entity = ((ToErrorResponseEntity) e).toErrorResponseEntity();
  103 + } else {
  104 + entity = new ResponseEntity(HttpStatus.UNAUTHORIZED);
  105 + }
  106 + logRpcCall(currentUser, deviceId, body, oneWay, Optional.empty(), e);
  107 + response.setResult(entity);
  108 + }
  109 + }));
  110 + return response;
  111 + } catch (IllegalArgumentException ioe) {
  112 + throw new ThingsboardException("Invalid request body", ioe, ThingsboardErrorCode.BAD_REQUEST_PARAMS);
  113 + }
  114 + }
  115 +
  116 + public void reply(LocalRequestMetaData rpcRequest, FromDeviceRpcResponse response, HttpStatus timeoutStatus, HttpStatus noActiveConnectionStatus) {
  117 + Optional<RpcError> rpcError = response.getError();
  118 + DeferredResult<ResponseEntity> responseWriter = rpcRequest.getResponseWriter();
  119 + if (rpcError.isPresent()) {
  120 + logRpcCall(rpcRequest, rpcError, null);
  121 + RpcError error = rpcError.get();
  122 + switch (error) {
  123 + case TIMEOUT:
  124 + responseWriter.setResult(new ResponseEntity<>(timeoutStatus));
  125 + break;
  126 + case NO_ACTIVE_CONNECTION:
  127 + responseWriter.setResult(new ResponseEntity<>(noActiveConnectionStatus));
  128 + break;
  129 + default:
  130 + responseWriter.setResult(new ResponseEntity<>(timeoutStatus));
  131 + break;
  132 + }
  133 + } else {
  134 + Optional<String> responseData = response.getResponse();
  135 + if (responseData.isPresent() && !StringUtils.isEmpty(responseData.get())) {
  136 + String data = responseData.get();
  137 + try {
  138 + logRpcCall(rpcRequest, rpcError, null);
  139 + responseWriter.setResult(new ResponseEntity<>(JacksonUtil.toJsonNode(data), HttpStatus.OK));
  140 + } catch (IllegalArgumentException e) {
  141 + log.debug("Failed to decode device response: {}", data, e);
  142 + logRpcCall(rpcRequest, rpcError, e);
  143 + responseWriter.setResult(new ResponseEntity<>(HttpStatus.NOT_ACCEPTABLE));
  144 + }
  145 + } else {
  146 + logRpcCall(rpcRequest, rpcError, null);
  147 + responseWriter.setResult(new ResponseEntity<>(HttpStatus.OK));
  148 + }
  149 + }
  150 + }
  151 +
  152 + private void logRpcCall(LocalRequestMetaData rpcRequest, Optional<RpcError> rpcError, Throwable e) {
  153 + logRpcCall(rpcRequest.getUser(), rpcRequest.getRequest().getDeviceId(), rpcRequest.getRequest().getBody(), rpcRequest.getRequest().isOneway(), rpcError, null);
  154 + }
  155 +
  156 +
  157 + private void logRpcCall(SecurityUser user, EntityId entityId, ToDeviceRpcRequestBody body, boolean oneWay, Optional<RpcError> rpcError, Throwable e) {
  158 + String rpcErrorStr = "";
  159 + if (rpcError.isPresent()) {
  160 + rpcErrorStr = "RPC Error: " + rpcError.get().name();
  161 + }
  162 + String method = body.getMethod();
  163 + String params = body.getParams();
  164 +
  165 + auditLogService.logEntityAction(
  166 + user.getTenantId(),
  167 + user.getCustomerId(),
  168 + user.getId(),
  169 + user.getName(),
  170 + (UUIDBased & EntityId) entityId,
  171 + null,
  172 + ActionType.RPC_CALL,
  173 + BaseController.toException(e),
  174 + rpcErrorStr,
  175 + oneWay,
  176 + method,
  177 + params);
  178 + }
  179 +
  180 +
  181 +}
... ...
... ... @@ -26,12 +26,12 @@ import org.springframework.web.bind.annotation.ResponseBody;
26 26 import org.springframework.web.bind.annotation.RestController;
27 27 import org.thingsboard.rule.engine.api.MailService;
28 28 import org.thingsboard.rule.engine.api.SmsService;
29   -import org.thingsboard.server.common.data.sms.config.TestSmsRequest;
30 29 import org.thingsboard.server.common.data.AdminSettings;
31 30 import org.thingsboard.server.common.data.UpdateMessage;
32 31 import org.thingsboard.server.common.data.exception.ThingsboardException;
33 32 import org.thingsboard.server.common.data.id.TenantId;
34 33 import org.thingsboard.server.common.data.security.model.SecuritySettings;
  34 +import org.thingsboard.server.common.data.sms.config.TestSmsRequest;
35 35 import org.thingsboard.server.dao.settings.AdminSettingsService;
36 36 import org.thingsboard.server.queue.util.TbCoreComponent;
37 37 import org.thingsboard.server.service.security.permission.Operation;
... ... @@ -67,7 +67,7 @@ public class AdminController extends BaseController {
67 67 accessControlService.checkPermission(getCurrentUser(), Resource.ADMIN_SETTINGS, Operation.READ);
68 68 AdminSettings adminSettings = checkNotNull(adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, key));
69 69 if (adminSettings.getKey().equals("mail")) {
70   - ((ObjectNode) adminSettings.getJsonValue()).put("password", "");
  70 + ((ObjectNode) adminSettings.getJsonValue()).remove("password");
71 71 }
72 72 return adminSettings;
73 73 } catch (Exception e) {
... ... @@ -84,7 +84,7 @@ public class AdminController extends BaseController {
84 84 adminSettings = checkNotNull(adminSettingsService.saveAdminSettings(TenantId.SYS_TENANT_ID, adminSettings));
85 85 if (adminSettings.getKey().equals("mail")) {
86 86 mailService.updateMailConfiguration();
87   - ((ObjectNode) adminSettings.getJsonValue()).put("password", "");
  87 + ((ObjectNode) adminSettings.getJsonValue()).remove("password");
88 88 } else if (adminSettings.getKey().equals("sms")) {
89 89 smsService.updateSmsConfiguration();
90 90 }
... ... @@ -126,6 +126,10 @@ public class AdminController extends BaseController {
126 126 accessControlService.checkPermission(getCurrentUser(), Resource.ADMIN_SETTINGS, Operation.READ);
127 127 adminSettings = checkNotNull(adminSettings);
128 128 if (adminSettings.getKey().equals("mail")) {
  129 + if(!adminSettings.getJsonValue().has("password")) {
  130 + AdminSettings mailSettings = checkNotNull(adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, "mail"));
  131 + ((ObjectNode) adminSettings.getJsonValue()).put("password", mailSettings.getJsonValue().get("password").asText());
  132 + }
129 133 String email = getCurrentUser().getEmail();
130 134 mailService.sendTestMail(adminSettings.getJsonValue(), email);
131 135 }
... ...
... ... @@ -38,6 +38,7 @@ import org.thingsboard.server.common.data.edge.EdgeEventActionType;
38 38 import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
39 39 import org.thingsboard.server.common.data.exception.ThingsboardException;
40 40 import org.thingsboard.server.common.data.id.AlarmId;
  41 +import org.thingsboard.server.common.data.id.EdgeId;
41 42 import org.thingsboard.server.common.data.id.EntityId;
42 43 import org.thingsboard.server.common.data.id.EntityIdFactory;
43 44 import org.thingsboard.server.common.data.page.PageData;
... ... @@ -46,6 +47,8 @@ import org.thingsboard.server.queue.util.TbCoreComponent;
46 47 import org.thingsboard.server.service.security.permission.Operation;
47 48 import org.thingsboard.server.service.security.permission.Resource;
48 49
  50 +import java.util.List;
  51 +
49 52 @RestController
50 53 @TbCoreComponent
51 54 @RequestMapping("/api")
... ... @@ -112,10 +115,13 @@ public class AlarmController extends BaseController {
112 115 AlarmId alarmId = new AlarmId(toUUID(strAlarmId));
113 116 Alarm alarm = checkAlarmId(alarmId, Operation.WRITE);
114 117
  118 + List<EdgeId> relatedEdgeIds = findRelatedEdgeIds(getTenantId(), alarm.getOriginator());
  119 +
115 120 logEntityAction(alarm.getOriginator(), alarm,
116 121 getCurrentUser().getCustomerId(),
117 122 ActionType.ALARM_DELETE, null);
118   - sendEntityNotificationMsg(getTenantId(), alarmId, EdgeEventActionType.DELETED);
  123 +
  124 + sendAlarmDeleteNotificationMsg(getTenantId(), alarmId, relatedEdgeIds, alarm);
119 125
120 126 return alarmService.deleteAlarm(getTenantId(), alarmId);
121 127 } catch (Exception e) {
... ...
... ... @@ -33,7 +33,6 @@ import org.thingsboard.server.common.data.DashboardInfo;
33 33 import org.thingsboard.server.common.data.Device;
34 34 import org.thingsboard.server.common.data.DeviceInfo;
35 35 import org.thingsboard.server.common.data.DeviceProfile;
36   -import org.thingsboard.server.common.data.EdgeUtils;
37 36 import org.thingsboard.server.common.data.EntityType;
38 37 import org.thingsboard.server.common.data.EntityView;
39 38 import org.thingsboard.server.common.data.EntityViewInfo;
... ... @@ -118,7 +117,6 @@ import org.thingsboard.server.dao.user.UserService;
118 117 import org.thingsboard.server.dao.widget.WidgetTypeService;
119 118 import org.thingsboard.server.dao.widget.WidgetsBundleService;
120 119 import org.thingsboard.server.exception.ThingsboardErrorResponseHandler;
121   -import org.thingsboard.server.gen.transport.TransportProtos;
122 120 import org.thingsboard.server.queue.discovery.PartitionService;
123 121 import org.thingsboard.server.queue.provider.TbQueueProducerProvider;
124 122 import org.thingsboard.server.queue.util.TbCoreComponent;
... ... @@ -129,7 +127,7 @@ import org.thingsboard.server.service.edge.rpc.EdgeRpcService;
129 127 import org.thingsboard.server.service.lwm2m.LwM2MServerSecurityInfoRepository;
130 128 import org.thingsboard.server.service.ota.OtaPackageStateService;
131 129 import org.thingsboard.server.service.profile.TbDeviceProfileCache;
132   -import org.thingsboard.server.service.queue.TbClusterService;
  130 +import org.thingsboard.server.cluster.TbClusterService;
133 131 import org.thingsboard.server.service.resource.TbResourceService;
134 132 import org.thingsboard.server.service.security.model.SecurityUser;
135 133 import org.thingsboard.server.service.security.permission.AccessControlService;
... ... @@ -846,13 +844,25 @@ public abstract class BaseController {
846 844 }
847 845
848 846 protected void sendDeleteNotificationMsg(TenantId tenantId, EntityId entityId, List<EdgeId> edgeIds) {
  847 + sendDeleteNotificationMsg(tenantId, entityId, edgeIds, null);
  848 + }
  849 +
  850 + protected void sendDeleteNotificationMsg(TenantId tenantId, EntityId entityId, List<EdgeId> edgeIds, String body) {
849 851 if (edgeIds != null && !edgeIds.isEmpty()) {
850 852 for (EdgeId edgeId : edgeIds) {
851   - sendNotificationMsgToEdgeService(tenantId, edgeId, entityId, null, null, EdgeEventActionType.DELETED);
  853 + sendNotificationMsgToEdgeService(tenantId, edgeId, entityId, body, null, EdgeEventActionType.DELETED);
852 854 }
853 855 }
854 856 }
855 857
  858 + protected void sendAlarmDeleteNotificationMsg(TenantId tenantId, EntityId entityId, List<EdgeId> edgeIds, Alarm alarm) {
  859 + try {
  860 + sendDeleteNotificationMsg(tenantId, entityId, edgeIds, json.writeValueAsString(alarm));
  861 + } catch (Exception e) {
  862 + log.warn("Failed to push delete alarm msg to core: {}", alarm, e);
  863 + }
  864 + }
  865 +
856 866 protected void sendEntityAssignToCustomerNotificationMsg(TenantId tenantId, EntityId entityId, CustomerId customerId, EdgeEventActionType action) {
857 867 try {
858 868 sendNotificationMsgToEdgeService(tenantId, null, entityId, json.writeValueAsString(customerId), null, action);
... ... @@ -870,42 +880,7 @@ public abstract class BaseController {
870 880 }
871 881
872 882 private void sendNotificationMsgToEdgeService(TenantId tenantId, EdgeId edgeId, EntityId entityId, String body, EdgeEventType type, EdgeEventActionType action) {
873   - if (!edgesEnabled) {
874   - return;
875   - }
876   - if (type == null) {
877   - if (entityId != null) {
878   - type = EdgeUtils.getEdgeEventTypeByEntityType(entityId.getEntityType());
879   - } else {
880   - log.trace("[{}] entity id and type are null. Ignoring this notification", tenantId);
881   - return;
882   - }
883   - if (type == null) {
884   - log.trace("[{}] edge event type is null. Ignoring this notification [{}]", tenantId, entityId);
885   - return;
886   - }
887   - }
888   - TransportProtos.EdgeNotificationMsgProto.Builder builder = TransportProtos.EdgeNotificationMsgProto.newBuilder();
889   - builder.setTenantIdMSB(tenantId.getId().getMostSignificantBits());
890   - builder.setTenantIdLSB(tenantId.getId().getLeastSignificantBits());
891   - builder.setType(type.name());
892   - builder.setAction(action.name());
893   - if (entityId != null) {
894   - builder.setEntityIdMSB(entityId.getId().getMostSignificantBits());
895   - builder.setEntityIdLSB(entityId.getId().getLeastSignificantBits());
896   - builder.setEntityType(entityId.getEntityType().name());
897   - }
898   - if (edgeId != null) {
899   - builder.setEdgeIdMSB(edgeId.getId().getMostSignificantBits());
900   - builder.setEdgeIdLSB(edgeId.getId().getLeastSignificantBits());
901   - }
902   - if (body != null) {
903   - builder.setBody(body);
904   - }
905   - TransportProtos.EdgeNotificationMsgProto msg = builder.build();
906   - log.trace("[{}] sending notification to edge service {}", tenantId.getId(), msg);
907   - tbClusterService.pushMsgToCore(tenantId, entityId != null ? entityId : tenantId,
908   - TransportProtos.ToCoreMsg.newBuilder().setEdgeNotificationMsg(msg).build(), null);
  883 + tbClusterService.sendNotificationMsgToEdgeService(tenantId, edgeId, entityId, body, type, action);
909 884 }
910 885
911 886 protected List<EdgeId> findRelatedEdgeIds(TenantId tenantId, EntityId entityId) {
... ...
... ... @@ -149,7 +149,7 @@ public class CustomerController extends BaseController {
149 149 ActionType.DELETED, null, strCustomerId);
150 150
151 151 sendDeleteNotificationMsg(getTenantId(), customerId, relatedEdgeIds);
152   - tbClusterService.onEntityStateChange(getTenantId(), customerId, ComponentLifecycleEvent.DELETED);
  152 + tbClusterService.broadcastEntityStateChangeEvent(getTenantId(), customerId, ComponentLifecycleEvent.DELETED);
153 153 } catch (Exception e) {
154 154
155 155 logEntityAction(emptyId(EntityType.CUSTOMER),
... ...
... ... @@ -36,7 +36,6 @@ import org.springframework.web.bind.annotation.RestController;
36 36 import org.springframework.web.context.request.async.DeferredResult;
37 37 import org.thingsboard.rule.engine.api.msg.DeviceCredentialsUpdateNotificationMsg;
38 38 import org.thingsboard.rule.engine.api.msg.DeviceEdgeUpdateMsg;
39   -import org.thingsboard.rule.engine.api.msg.DeviceNameOrTypeUpdateMsg;
40 39 import org.thingsboard.server.common.data.ClaimRequest;
41 40 import org.thingsboard.server.common.data.Customer;
42 41 import org.thingsboard.server.common.data.DataConstants;
... ... @@ -60,7 +59,6 @@ import org.thingsboard.server.common.data.ota.OtaPackageType;
60 59 import org.thingsboard.server.common.data.page.PageData;
61 60 import org.thingsboard.server.common.data.page.PageLink;
62 61 import org.thingsboard.server.common.data.page.TimePageLink;
63   -import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
64 62 import org.thingsboard.server.common.data.security.DeviceCredentials;
65 63 import org.thingsboard.server.common.msg.TbMsg;
66 64 import org.thingsboard.server.common.msg.TbMsgDataType;
... ... @@ -134,20 +132,16 @@ public class DeviceController extends BaseController {
134 132 try {
135 133 device.setTenantId(getCurrentUser().getTenantId());
136 134
137   - checkEntity(device.getId(), device, Resource.DEVICE);
138   -
139   - Device oldDevice;
  135 + Device oldDevice = null;
140 136 if (!created) {
141   - oldDevice = deviceService.findDeviceById(getTenantId(), device.getId());
  137 + oldDevice = checkDeviceId(device.getId(), Operation.WRITE);
142 138 } else {
143   - oldDevice = null;
  139 + checkEntity(null, device, Resource.DEVICE);
144 140 }
145 141
146 142 Device savedDevice = checkNotNull(deviceService.saveDeviceWithAccessToken(device, accessToken));
147 143
148   - onDeviceCreatedOrUpdated(savedDevice, !created);
149   -
150   - otaPackageStateService.update(savedDevice, oldDevice);
  144 + onDeviceCreatedOrUpdated(savedDevice, oldDevice, !created);
151 145
152 146 return savedDevice;
153 147 } catch (Exception e) {
... ... @@ -158,29 +152,16 @@ public class DeviceController extends BaseController {
158 152
159 153 }
160 154
161   - private void onDeviceCreatedOrUpdated(Device device, boolean updated) {
162   - tbClusterService.onDeviceChange(device, null);
163   - tbClusterService.pushMsgToCore(new DeviceNameOrTypeUpdateMsg(device.getTenantId(),
164   - device.getId(), device.getName(), device.getType()), null);
165   - tbClusterService.onEntityStateChange(device.getTenantId(), device.getId(), updated ? ComponentLifecycleEvent.UPDATED : ComponentLifecycleEvent.CREATED);
166   -
167   - if (updated) {
168   - sendEntityNotificationMsg(device.getTenantId(), device.getId(), EdgeEventActionType.UPDATED);
169   - }
  155 + private void onDeviceCreatedOrUpdated(Device savedDevice, Device oldDevice, boolean updated) {
  156 + tbClusterService.onDeviceUpdated(savedDevice, oldDevice);
170 157
171 158 try {
172   - logEntityAction(device.getId(), device,
173   - device.getCustomerId(),
  159 + logEntityAction(savedDevice.getId(), savedDevice,
  160 + savedDevice.getCustomerId(),
174 161 updated ? ActionType.UPDATED : ActionType.ADDED, null);
175 162 } catch (ThingsboardException e) {
176 163 log.error("Failed to log entity action", e);
177 164 }
178   -
179   - if (updated) {
180   - deviceStateService.onDeviceUpdated(device);
181   - } else {
182   - deviceStateService.onDeviceAdded(device);
183   - }
184 165 }
185 166
186 167 @PreAuthorize("hasAuthority('TENANT_ADMIN')")
... ... @@ -197,15 +178,12 @@ public class DeviceController extends BaseController {
197 178 deviceService.deleteDevice(getCurrentUser().getTenantId(), deviceId);
198 179
199 180 tbClusterService.onDeviceDeleted(device, null);
200   - tbClusterService.onEntityStateChange(device.getTenantId(), deviceId, ComponentLifecycleEvent.DELETED);
201 181
202 182 logEntityAction(deviceId, device,
203 183 device.getCustomerId(),
204 184 ActionType.DELETED, null, strDeviceId);
205 185
206 186 sendDeleteNotificationMsg(getTenantId(), deviceId, relatedEdgeIds);
207   -
208   - deviceStateService.onDeviceDeleted(device);
209 187 } catch (Exception e) {
210 188 logEntityAction(emptyId(EntityType.DEVICE),
211 189 null,
... ... @@ -819,8 +797,7 @@ public class DeviceController extends BaseController {
819 797 @PostMapping("/device/bulk_import")
820 798 public BulkImportResult<Device> processDevicesBulkImport(@RequestBody BulkImportRequest request) throws Exception {
821 799 return deviceBulkImportService.processBulkImport(request, getCurrentUser(), importedDeviceInfo -> {
822   - onDeviceCreatedOrUpdated(importedDeviceInfo.getEntity(), importedDeviceInfo.isUpdated());
823   - otaPackageStateService.update(importedDeviceInfo.getEntity(), importedDeviceInfo.getOldEntity());
  800 + onDeviceCreatedOrUpdated(importedDeviceInfo.getEntity(), importedDeviceInfo.getOldEntity(), importedDeviceInfo.isUpdated());
824 801 });
825 802 }
826 803
... ...
... ... @@ -161,7 +161,7 @@ public class DeviceProfileController extends BaseController {
161 161 DeviceProfile savedDeviceProfile = checkNotNull(deviceProfileService.saveDeviceProfile(deviceProfile));
162 162
163 163 tbClusterService.onDeviceProfileChange(savedDeviceProfile, null);
164   - tbClusterService.onEntityStateChange(deviceProfile.getTenantId(), savedDeviceProfile.getId(),
  164 + tbClusterService.broadcastEntityStateChangeEvent(deviceProfile.getTenantId(), savedDeviceProfile.getId(),
165 165 created ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED);
166 166
167 167 logEntityAction(savedDeviceProfile.getId(), savedDeviceProfile,
... ... @@ -191,7 +191,7 @@ public class DeviceProfileController extends BaseController {
191 191 deviceProfileService.deleteDeviceProfile(getTenantId(), deviceProfileId);
192 192
193 193 tbClusterService.onDeviceProfileDelete(deviceProfile, null);
194   - tbClusterService.onEntityStateChange(deviceProfile.getTenantId(), deviceProfile.getId(), ComponentLifecycleEvent.DELETED);
  194 + tbClusterService.broadcastEntityStateChangeEvent(deviceProfile.getTenantId(), deviceProfile.getId(), ComponentLifecycleEvent.DELETED);
195 195
196 196 logEntityAction(deviceProfileId, deviceProfile,
197 197 null,
... ...
... ... @@ -153,7 +153,7 @@ public class EdgeController extends BaseController {
153 153 edgeService.assignDefaultRuleChainsToEdge(tenantId, edge.getId());
154 154 }
155 155
156   - tbClusterService.onEntityStateChange(edge.getTenantId(), edge.getId(),
  156 + tbClusterService.broadcastEntityStateChangeEvent(edge.getTenantId(), edge.getId(),
157 157 updated ? ComponentLifecycleEvent.UPDATED : ComponentLifecycleEvent.CREATED);
158 158
159 159 logEntityAction(edge.getId(), edge, null, updated ? ActionType.UPDATED : ActionType.ADDED, null);
... ... @@ -169,7 +169,7 @@ public class EdgeController extends BaseController {
169 169 Edge edge = checkEdgeId(edgeId, Operation.DELETE);
170 170 edgeService.deleteEdge(getTenantId(), edgeId);
171 171
172   - tbClusterService.onEntityStateChange(getTenantId(), edgeId,
  172 + tbClusterService.broadcastEntityStateChangeEvent(getTenantId(), edgeId,
173 173 ComponentLifecycleEvent.DELETED);
174 174
175 175 logEntityAction(edgeId, edge,
... ... @@ -220,7 +220,7 @@ public class EdgeController extends BaseController {
220 220
221 221 Edge savedEdge = checkNotNull(edgeService.assignEdgeToCustomer(getCurrentUser().getTenantId(), edgeId, customerId));
222 222
223   - tbClusterService.onEntityStateChange(getTenantId(), edgeId,
  223 + tbClusterService.broadcastEntityStateChangeEvent(getTenantId(), edgeId,
224 224 ComponentLifecycleEvent.UPDATED);
225 225
226 226 logEntityAction(edgeId, savedEdge,
... ... @@ -254,7 +254,7 @@ public class EdgeController extends BaseController {
254 254
255 255 Edge savedEdge = checkNotNull(edgeService.unassignEdgeFromCustomer(getCurrentUser().getTenantId(), edgeId));
256 256
257   - tbClusterService.onEntityStateChange(getTenantId(), edgeId,
  257 + tbClusterService.broadcastEntityStateChangeEvent(getTenantId(), edgeId,
258 258 ComponentLifecycleEvent.UPDATED);
259 259
260 260 logEntityAction(edgeId, edge,
... ... @@ -284,7 +284,7 @@ public class EdgeController extends BaseController {
284 284 Customer publicCustomer = customerService.findOrCreatePublicCustomer(edge.getTenantId());
285 285 Edge savedEdge = checkNotNull(edgeService.assignEdgeToCustomer(getCurrentUser().getTenantId(), edgeId, publicCustomer.getId()));
286 286
287   - tbClusterService.onEntityStateChange(getTenantId(), edgeId,
  287 + tbClusterService.broadcastEntityStateChangeEvent(getTenantId(), edgeId,
288 288 ComponentLifecycleEvent.UPDATED);
289 289
290 290 logEntityAction(edgeId, savedEdge,
... ... @@ -376,7 +376,7 @@ public class EdgeController extends BaseController {
376 376
377 377 Edge updatedEdge = edgeNotificationService.setEdgeRootRuleChain(getTenantId(), edge, ruleChainId);
378 378
379   - tbClusterService.onEntityStateChange(updatedEdge.getTenantId(), updatedEdge.getId(), ComponentLifecycleEvent.UPDATED);
  379 + tbClusterService.broadcastEntityStateChangeEvent(updatedEdge.getTenantId(), updatedEdge.getId(), ComponentLifecycleEvent.UPDATED);
380 380
381 381 logEntityAction(updatedEdge.getId(), updatedEdge, null, ActionType.UPDATED, null);
382 382
... ...
... ... @@ -24,13 +24,11 @@ import org.springframework.web.bind.annotation.RequestMapping;
24 24 import org.springframework.web.bind.annotation.RequestMethod;
25 25 import org.springframework.web.bind.annotation.ResponseBody;
26 26 import org.springframework.web.bind.annotation.RestController;
27   -import org.thingsboard.rule.engine.api.msg.DeviceNameOrTypeUpdateMsg;
28 27 import org.thingsboard.server.common.data.Device;
29 28 import org.thingsboard.server.common.data.EntityType;
30 29 import org.thingsboard.server.common.data.audit.ActionType;
31 30 import org.thingsboard.server.common.data.exception.ThingsboardException;
32 31 import org.thingsboard.server.common.data.lwm2m.ServerSecurityConfig;
33   -import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
34 32 import org.thingsboard.server.common.data.security.DeviceCredentials;
35 33 import org.thingsboard.server.queue.util.TbCoreComponent;
36 34 import org.thingsboard.server.service.security.permission.Resource;
... ... @@ -66,22 +64,11 @@ public class Lwm2mController extends BaseController {
66 64 checkEntity(device.getId(), device, Resource.DEVICE);
67 65 Device savedDevice = deviceService.saveDeviceWithCredentials(device, credentials);
68 66 checkNotNull(savedDevice);
69   -
70   - tbClusterService.onDeviceChange(savedDevice, null);
71   - tbClusterService.pushMsgToCore(new DeviceNameOrTypeUpdateMsg(savedDevice.getTenantId(),
72   - savedDevice.getId(), savedDevice.getName(), savedDevice.getType()), null);
73   - tbClusterService.onEntityStateChange(savedDevice.getTenantId(), savedDevice.getId(),
74   - device.getId() == null ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED);
75   -
  67 + tbClusterService.onDeviceUpdated(savedDevice, device);
76 68 logEntityAction(savedDevice.getId(), savedDevice,
77 69 savedDevice.getCustomerId(),
78 70 device.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null);
79 71
80   - if (device.getId() == null) {
81   - deviceStateService.onDeviceAdded(savedDevice);
82   - } else {
83   - deviceStateService.onDeviceUpdated(savedDevice);
84   - }
85 72 return savedDevice;
86 73 } catch (Exception e) {
87 74 logEntityAction(emptyId(EntityType.DEVICE), device,
... ...
  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.controller;
  17 +
  18 +import lombok.extern.slf4j.Slf4j;
  19 +import org.springframework.http.HttpStatus;
  20 +import org.springframework.http.ResponseEntity;
  21 +import org.springframework.security.access.prepost.PreAuthorize;
  22 +import org.springframework.web.bind.annotation.PathVariable;
  23 +import org.springframework.web.bind.annotation.RequestBody;
  24 +import org.springframework.web.bind.annotation.RequestMapping;
  25 +import org.springframework.web.bind.annotation.RequestMethod;
  26 +import org.springframework.web.bind.annotation.ResponseBody;
  27 +import org.springframework.web.bind.annotation.RestController;
  28 +import org.springframework.web.context.request.async.DeferredResult;
  29 +import org.thingsboard.server.common.data.exception.ThingsboardException;
  30 +import org.thingsboard.server.common.data.id.DeviceId;
  31 +import org.thingsboard.server.queue.util.TbCoreComponent;
  32 +
  33 +import java.util.UUID;
  34 +
  35 +@RestController
  36 +@TbCoreComponent
  37 +@RequestMapping(TbUrlConstants.RPC_V1_URL_PREFIX)
  38 +@Slf4j
  39 +public class RpcV1Controller extends AbstractRpcController {
  40 +
  41 + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
  42 + @RequestMapping(value = "/oneway/{deviceId}", method = RequestMethod.POST)
  43 + @ResponseBody
  44 + public DeferredResult<ResponseEntity> handleOneWayDeviceRPCRequest(@PathVariable("deviceId") String deviceIdStr, @RequestBody String requestBody) throws ThingsboardException {
  45 + return handleDeviceRPCRequest(true, new DeviceId(UUID.fromString(deviceIdStr)), requestBody, HttpStatus.REQUEST_TIMEOUT, HttpStatus.CONFLICT);
  46 + }
  47 +
  48 + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
  49 + @RequestMapping(value = "/twoway/{deviceId}", method = RequestMethod.POST)
  50 + @ResponseBody
  51 + public DeferredResult<ResponseEntity> handleTwoWayDeviceRPCRequest(@PathVariable("deviceId") String deviceIdStr, @RequestBody String requestBody) throws ThingsboardException {
  52 + return handleDeviceRPCRequest(false, new DeviceId(UUID.fromString(deviceIdStr)), requestBody, HttpStatus.REQUEST_TIMEOUT, HttpStatus.CONFLICT);
  53 + }
  54 +
  55 +}
... ...
application/src/main/java/org/thingsboard/server/controller/RpcV2Controller.java renamed from application/src/main/java/org/thingsboard/server/controller/RpcController.java
... ... @@ -15,16 +15,10 @@
15 15 */
16 16 package org.thingsboard.server.controller;
17 17
18   -import com.fasterxml.jackson.databind.JsonNode;
19   -import com.fasterxml.jackson.databind.ObjectMapper;
20   -import com.google.common.util.concurrent.FutureCallback;
21 18 import lombok.extern.slf4j.Slf4j;
22   -import org.springframework.beans.factory.annotation.Autowired;
23   -import org.springframework.beans.factory.annotation.Value;
24 19 import org.springframework.http.HttpStatus;
25 20 import org.springframework.http.ResponseEntity;
26 21 import org.springframework.security.access.prepost.PreAuthorize;
27   -import org.springframework.util.StringUtils;
28 22 import org.springframework.web.bind.annotation.PathVariable;
29 23 import org.springframework.web.bind.annotation.RequestBody;
30 24 import org.springframework.web.bind.annotation.RequestMapping;
... ... @@ -33,71 +27,43 @@ import org.springframework.web.bind.annotation.RequestParam;
33 27 import org.springframework.web.bind.annotation.ResponseBody;
34 28 import org.springframework.web.bind.annotation.RestController;
35 29 import org.springframework.web.context.request.async.DeferredResult;
36   -import org.thingsboard.rule.engine.api.RpcError;
37   -import org.thingsboard.server.common.data.DataConstants;
38   -import org.thingsboard.server.common.data.audit.ActionType;
39   -import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
  30 +import org.thingsboard.common.util.JacksonUtil;
40 31 import org.thingsboard.server.common.data.exception.ThingsboardException;
41 32 import org.thingsboard.server.common.data.id.DeviceId;
42   -import org.thingsboard.server.common.data.id.EntityId;
43 33 import org.thingsboard.server.common.data.id.RpcId;
44 34 import org.thingsboard.server.common.data.id.TenantId;
45   -import org.thingsboard.server.common.data.id.UUIDBased;
46 35 import org.thingsboard.server.common.data.page.PageData;
47 36 import org.thingsboard.server.common.data.page.PageLink;
48 37 import org.thingsboard.server.common.data.rpc.Rpc;
49 38 import org.thingsboard.server.common.data.rpc.RpcStatus;
50   -import org.thingsboard.server.common.data.rpc.ToDeviceRpcRequestBody;
51   -import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest;
  39 +import org.thingsboard.server.common.msg.TbMsg;
  40 +import org.thingsboard.server.common.msg.TbMsgMetaData;
52 41 import org.thingsboard.server.queue.util.TbCoreComponent;
53   -import org.thingsboard.server.service.rpc.FromDeviceRpcResponse;
54   -import org.thingsboard.server.service.rpc.LocalRequestMetaData;
55   -import org.thingsboard.server.service.rpc.TbCoreDeviceRpcService;
56   -import org.thingsboard.server.service.security.AccessValidator;
57   -import org.thingsboard.server.service.security.model.SecurityUser;
  42 +import org.thingsboard.server.service.rpc.RemoveRpcActorMsg;
58 43 import org.thingsboard.server.service.security.permission.Operation;
59   -import org.thingsboard.server.service.telemetry.exception.ToErrorResponseEntity;
60 44
61   -import javax.annotation.Nullable;
62   -import java.io.IOException;
63   -import java.util.Optional;
64 45 import java.util.UUID;
65 46
66   -/**
67   - * Created by ashvayka on 22.03.18.
68   - */
  47 +import static org.thingsboard.server.common.data.DataConstants.RPC_DELETED;
  48 +
69 49 @RestController
70 50 @TbCoreComponent
71   -@RequestMapping(TbUrlConstants.RPC_URL_PREFIX)
  51 +@RequestMapping(TbUrlConstants.RPC_V2_URL_PREFIX)
72 52 @Slf4j
73   -public class RpcController extends BaseController {
74   -
75   - protected final ObjectMapper jsonMapper = new ObjectMapper();
76   -
77   - @Autowired
78   - private TbCoreDeviceRpcService deviceRpcService;
79   -
80   - @Autowired
81   - private AccessValidator accessValidator;
82   -
83   - @Value("${server.rest.server_side_rpc.min_timeout:5000}")
84   - private long minTimeout;
85   -
86   - @Value("${server.rest.server_side_rpc.default_timeout:10000}")
87   - private long defaultTimeout;
  53 +public class RpcV2Controller extends AbstractRpcController {
88 54
89 55 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
90 56 @RequestMapping(value = "/oneway/{deviceId}", method = RequestMethod.POST)
91 57 @ResponseBody
92 58 public DeferredResult<ResponseEntity> handleOneWayDeviceRPCRequest(@PathVariable("deviceId") String deviceIdStr, @RequestBody String requestBody) throws ThingsboardException {
93   - return handleDeviceRPCRequest(true, new DeviceId(UUID.fromString(deviceIdStr)), requestBody);
  59 + return handleDeviceRPCRequest(true, new DeviceId(UUID.fromString(deviceIdStr)), requestBody, HttpStatus.GATEWAY_TIMEOUT, HttpStatus.GATEWAY_TIMEOUT);
94 60 }
95 61
96 62 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
97 63 @RequestMapping(value = "/twoway/{deviceId}", method = RequestMethod.POST)
98 64 @ResponseBody
99 65 public DeferredResult<ResponseEntity> handleTwoWayDeviceRPCRequest(@PathVariable("deviceId") String deviceIdStr, @RequestBody String requestBody) throws ThingsboardException {
100   - return handleDeviceRPCRequest(false, new DeviceId(UUID.fromString(deviceIdStr)), requestBody);
  66 + return handleDeviceRPCRequest(false, new DeviceId(UUID.fromString(deviceIdStr)), requestBody, HttpStatus.GATEWAY_TIMEOUT, HttpStatus.GATEWAY_TIMEOUT);
101 67 }
102 68
103 69 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
... ... @@ -140,118 +106,23 @@ public class RpcController extends BaseController {
140 106 public void deleteResource(@PathVariable("rpcId") String strRpc) throws ThingsboardException {
141 107 checkParameter("RpcId", strRpc);
142 108 try {
143   - rpcService.deleteRpc(getTenantId(), new RpcId(UUID.fromString(strRpc)));
144   - } catch (Exception e) {
145   - throw handleException(e);
146   - }
147   - }
  109 + RpcId rpcId = new RpcId(UUID.fromString(strRpc));
  110 + Rpc rpc = checkRpcId(rpcId, Operation.DELETE);
148 111
149   - private DeferredResult<ResponseEntity> handleDeviceRPCRequest(boolean oneWay, DeviceId deviceId, String requestBody) throws ThingsboardException {
150   - try {
151   - JsonNode rpcRequestBody = jsonMapper.readTree(requestBody);
152   - ToDeviceRpcRequestBody body = new ToDeviceRpcRequestBody(rpcRequestBody.get("method").asText(), jsonMapper.writeValueAsString(rpcRequestBody.get("params")));
153   - SecurityUser currentUser = getCurrentUser();
154   - TenantId tenantId = currentUser.getTenantId();
155   - final DeferredResult<ResponseEntity> response = new DeferredResult<>();
156   - long timeout = rpcRequestBody.has("timeout") ? rpcRequestBody.get("timeout").asLong() : defaultTimeout;
157   - long expTime = System.currentTimeMillis() + Math.max(minTimeout, timeout);
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();
160   - accessValidator.validate(currentUser, Operation.RPC_CALL, deviceId, new HttpValidationCallback(response, new FutureCallback<DeferredResult<ResponseEntity>>() {
161   - @Override
162   - public void onSuccess(@Nullable DeferredResult<ResponseEntity> result) {
163   - ToDeviceRpcRequest rpcRequest = new ToDeviceRpcRequest(rpcRequestUUID,
164   - tenantId,
165   - deviceId,
166   - oneWay,
167   - expTime,
168   - body,
169   - persisted
170   - );
171   - deviceRpcService.processRestApiRpcRequest(rpcRequest, fromDeviceRpcResponse -> reply(new LocalRequestMetaData(rpcRequest, currentUser, result), fromDeviceRpcResponse), currentUser);
  112 + if (rpc != null) {
  113 + if (rpc.getStatus().equals(RpcStatus.QUEUED)) {
  114 + RemoveRpcActorMsg removeMsg = new RemoveRpcActorMsg(getTenantId(), rpc.getDeviceId(), rpc.getUuidId());
  115 + log.trace("[{}] Forwarding msg {} to queue actor!", rpc.getDeviceId(), rpc);
  116 + tbClusterService.pushMsgToCore(removeMsg, null);
172 117 }
173 118
174   - @Override
175   - public void onFailure(Throwable e) {
176   - ResponseEntity entity;
177   - if (e instanceof ToErrorResponseEntity) {
178   - entity = ((ToErrorResponseEntity) e).toErrorResponseEntity();
179   - } else {
180   - entity = new ResponseEntity(HttpStatus.UNAUTHORIZED);
181   - }
182   - logRpcCall(currentUser, deviceId, body, oneWay, Optional.empty(), e);
183   - response.setResult(entity);
184   - }
185   - }));
186   - return response;
187   - } catch (IOException ioe) {
188   - throw new ThingsboardException("Invalid request body", ioe, ThingsboardErrorCode.BAD_REQUEST_PARAMS);
189   - }
190   - }
  119 + rpcService.deleteRpc(getTenantId(), rpcId);
191 120
192   - public void reply(LocalRequestMetaData rpcRequest, FromDeviceRpcResponse response) {
193   - Optional<RpcError> rpcError = response.getError();
194   - DeferredResult<ResponseEntity> responseWriter = rpcRequest.getResponseWriter();
195   - if (rpcError.isPresent()) {
196   - logRpcCall(rpcRequest, rpcError, null);
197   - RpcError error = rpcError.get();
198   - switch (error) {
199   - case TIMEOUT:
200   - responseWriter.setResult(new ResponseEntity<>(HttpStatus.REQUEST_TIMEOUT));
201   - break;
202   - case NO_ACTIVE_CONNECTION:
203   - responseWriter.setResult(new ResponseEntity<>(HttpStatus.CONFLICT));
204   - break;
205   - default:
206   - responseWriter.setResult(new ResponseEntity<>(HttpStatus.REQUEST_TIMEOUT));
207   - break;
208   - }
209   - } else {
210   - Optional<String> responseData = response.getResponse();
211   - if (responseData.isPresent() && !StringUtils.isEmpty(responseData.get())) {
212   - String data = responseData.get();
213   - try {
214   - logRpcCall(rpcRequest, rpcError, null);
215   - responseWriter.setResult(new ResponseEntity<>(jsonMapper.readTree(data), HttpStatus.OK));
216   - } catch (IOException e) {
217   - log.debug("Failed to decode device response: {}", data, e);
218   - logRpcCall(rpcRequest, rpcError, e);
219   - responseWriter.setResult(new ResponseEntity<>(HttpStatus.NOT_ACCEPTABLE));
220   - }
221   - } else {
222   - logRpcCall(rpcRequest, rpcError, null);
223   - responseWriter.setResult(new ResponseEntity<>(HttpStatus.OK));
  121 + TbMsg msg = TbMsg.newMsg(RPC_DELETED, rpc.getDeviceId(), TbMsgMetaData.EMPTY, JacksonUtil.toString(rpc));
  122 + tbClusterService.pushMsgToRuleEngine(getTenantId(), rpc.getDeviceId(), msg, null);
224 123 }
  124 + } catch (Exception e) {
  125 + throw handleException(e);
225 126 }
226 127 }
227   -
228   - private void logRpcCall(LocalRequestMetaData rpcRequest, Optional<RpcError> rpcError, Throwable e) {
229   - logRpcCall(rpcRequest.getUser(), rpcRequest.getRequest().getDeviceId(), rpcRequest.getRequest().getBody(), rpcRequest.getRequest().isOneway(), rpcError, null);
230   - }
231   -
232   -
233   - private void logRpcCall(SecurityUser user, EntityId entityId, ToDeviceRpcRequestBody body, boolean oneWay, Optional<RpcError> rpcError, Throwable e) {
234   - String rpcErrorStr = "";
235   - if (rpcError.isPresent()) {
236   - rpcErrorStr = "RPC Error: " + rpcError.get().name();
237   - }
238   - String method = body.getMethod();
239   - String params = body.getParams();
240   -
241   - auditLogService.logEntityAction(
242   - user.getTenantId(),
243   - user.getCustomerId(),
244   - user.getId(),
245   - user.getName(),
246   - (UUIDBased & EntityId) entityId,
247   - null,
248   - ActionType.RPC_CALL,
249   - BaseController.toException(e),
250   - rpcErrorStr,
251   - oneWay,
252   - method,
253   - params);
254   - }
255   -
256   -
257 128 }
... ...
... ... @@ -149,7 +149,7 @@ public class RuleChainController extends BaseController {
149 149 RuleChain savedRuleChain = checkNotNull(ruleChainService.saveRuleChain(ruleChain));
150 150
151 151 if (RuleChainType.CORE.equals(savedRuleChain.getType())) {
152   - tbClusterService.onEntityStateChange(ruleChain.getTenantId(), savedRuleChain.getId(),
  152 + tbClusterService.broadcastEntityStateChangeEvent(ruleChain.getTenantId(), savedRuleChain.getId(),
153 153 created ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED);
154 154 }
155 155
... ... @@ -183,7 +183,7 @@ public class RuleChainController extends BaseController {
183 183
184 184 RuleChain savedRuleChain = installScripts.createDefaultRuleChain(getCurrentUser().getTenantId(), request.getName());
185 185
186   - tbClusterService.onEntityStateChange(savedRuleChain.getTenantId(), savedRuleChain.getId(), ComponentLifecycleEvent.CREATED);
  186 + tbClusterService.broadcastEntityStateChangeEvent(savedRuleChain.getTenantId(), savedRuleChain.getId(), ComponentLifecycleEvent.CREATED);
187 187
188 188 logEntityAction(savedRuleChain.getId(), savedRuleChain, null, ActionType.ADDED, null);
189 189
... ... @@ -210,7 +210,7 @@ public class RuleChainController extends BaseController {
210 210 if (previousRootRuleChain != null) {
211 211 previousRootRuleChain = ruleChainService.findRuleChainById(getTenantId(), previousRootRuleChain.getId());
212 212
213   - tbClusterService.onEntityStateChange(previousRootRuleChain.getTenantId(), previousRootRuleChain.getId(),
  213 + tbClusterService.broadcastEntityStateChangeEvent(previousRootRuleChain.getTenantId(), previousRootRuleChain.getId(),
214 214 ComponentLifecycleEvent.UPDATED);
215 215
216 216 logEntityAction(previousRootRuleChain.getId(), previousRootRuleChain,
... ... @@ -218,7 +218,7 @@ public class RuleChainController extends BaseController {
218 218 }
219 219 ruleChain = ruleChainService.findRuleChainById(getTenantId(), ruleChainId);
220 220
221   - tbClusterService.onEntityStateChange(ruleChain.getTenantId(), ruleChain.getId(),
  221 + tbClusterService.broadcastEntityStateChangeEvent(ruleChain.getTenantId(), ruleChain.getId(),
222 222 ComponentLifecycleEvent.UPDATED);
223 223
224 224 logEntityAction(ruleChain.getId(), ruleChain,
... ... @@ -254,7 +254,7 @@ public class RuleChainController extends BaseController {
254 254 RuleChainMetaData savedRuleChainMetaData = checkNotNull(ruleChainService.loadRuleChainMetaData(tenantId, ruleChainMetaData.getRuleChainId()));
255 255
256 256 if (RuleChainType.CORE.equals(ruleChain.getType())) {
257   - tbClusterService.onEntityStateChange(ruleChain.getTenantId(), ruleChain.getId(), ComponentLifecycleEvent.UPDATED);
  257 + tbClusterService.broadcastEntityStateChangeEvent(ruleChain.getTenantId(), ruleChain.getId(), ComponentLifecycleEvent.UPDATED);
258 258 }
259 259
260 260 logEntityAction(ruleChain.getId(), ruleChain,
... ... @@ -323,9 +323,9 @@ public class RuleChainController extends BaseController {
323 323
324 324 if (RuleChainType.CORE.equals(ruleChain.getType())) {
325 325 referencingRuleChainIds.forEach(referencingRuleChainId ->
326   - tbClusterService.onEntityStateChange(ruleChain.getTenantId(), referencingRuleChainId, ComponentLifecycleEvent.UPDATED));
  326 + tbClusterService.broadcastEntityStateChangeEvent(ruleChain.getTenantId(), referencingRuleChainId, ComponentLifecycleEvent.UPDATED));
327 327
328   - tbClusterService.onEntityStateChange(ruleChain.getTenantId(), ruleChain.getId(), ComponentLifecycleEvent.DELETED);
  328 + tbClusterService.broadcastEntityStateChangeEvent(ruleChain.getTenantId(), ruleChain.getId(), ComponentLifecycleEvent.DELETED);
329 329 }
330 330
331 331 logEntityAction(ruleChainId, ruleChain,
... ... @@ -456,7 +456,7 @@ public class RuleChainController extends BaseController {
456 456 List<RuleChainImportResult> importResults = ruleChainService.importTenantRuleChains(tenantId, ruleChainData, RuleChainType.CORE, overwrite);
457 457 if (!CollectionUtils.isEmpty(importResults)) {
458 458 for (RuleChainImportResult importResult : importResults) {
459   - tbClusterService.onEntityStateChange(importResult.getTenantId(), importResult.getRuleChainId(), importResult.getLifecycleEvent());
  459 + tbClusterService.broadcastEntityStateChangeEvent(importResult.getTenantId(), importResult.getRuleChainId(), importResult.getLifecycleEvent());
460 460 }
461 461 }
462 462 } catch (Exception e) {
... ...
... ... @@ -20,5 +20,6 @@ package org.thingsboard.server.controller;
20 20 */
21 21 public class TbUrlConstants {
22 22 public static final String TELEMETRY_URL_PREFIX = "/api/plugins/telemetry";
23   - public static final String RPC_URL_PREFIX = "/api/plugins/rpc";
  23 + public static final String RPC_V1_URL_PREFIX = "/api/plugins/rpc";
  24 + public static final String RPC_V2_URL_PREFIX = "/api/rpc";
24 25 }
... ...
... ... @@ -99,7 +99,7 @@ public class TenantController extends BaseController {
99 99 }
100 100 tenantProfileCache.evict(tenant.getId());
101 101 tbClusterService.onTenantChange(tenant, null);
102   - tbClusterService.onEntityStateChange(tenant.getId(), tenant.getId(),
  102 + tbClusterService.broadcastEntityStateChangeEvent(tenant.getId(), tenant.getId(),
103 103 newTenant ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED);
104 104 return tenant;
105 105 } catch (Exception e) {
... ... @@ -118,7 +118,7 @@ public class TenantController extends BaseController {
118 118 tenantService.deleteTenant(tenantId);
119 119 tenantProfileCache.evict(tenantId);
120 120 tbClusterService.onTenantDelete(tenant, null);
121   - tbClusterService.onEntityStateChange(tenantId, tenantId, ComponentLifecycleEvent.DELETED);
  121 + tbClusterService.broadcastEntityStateChangeEvent(tenantId, tenantId, ComponentLifecycleEvent.DELETED);
122 122 } catch (Exception e) {
123 123 throw handleException(e);
124 124 }
... ...
... ... @@ -34,7 +34,6 @@ import org.thingsboard.server.common.data.id.TenantProfileId;
34 34 import org.thingsboard.server.common.data.page.PageData;
35 35 import org.thingsboard.server.common.data.page.PageLink;
36 36 import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
37   -import org.thingsboard.server.dao.exception.DataValidationException;
38 37 import org.thingsboard.server.queue.util.TbCoreComponent;
39 38 import org.thingsboard.server.service.security.permission.Operation;
40 39 import org.thingsboard.server.service.security.permission.Resource;
... ... @@ -98,7 +97,7 @@ public class TenantProfileController extends BaseController {
98 97 tenantProfile = checkNotNull(tenantProfileService.saveTenantProfile(getTenantId(), tenantProfile));
99 98 tenantProfileCache.put(tenantProfile);
100 99 tbClusterService.onTenantProfileChange(tenantProfile, null);
101   - tbClusterService.onEntityStateChange(TenantId.SYS_TENANT_ID, tenantProfile.getId(),
  100 + tbClusterService.broadcastEntityStateChangeEvent(TenantId.SYS_TENANT_ID, tenantProfile.getId(),
102 101 newTenantProfile ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED);
103 102 return tenantProfile;
104 103 } catch (Exception e) {
... ...
... ... @@ -41,7 +41,7 @@ import org.thingsboard.server.common.msg.TbMsgDataType;
41 41 import org.thingsboard.server.common.msg.TbMsgMetaData;
42 42 import org.thingsboard.server.dao.audit.AuditLogService;
43 43 import org.thingsboard.server.queue.util.TbCoreComponent;
44   -import org.thingsboard.server.service.queue.TbClusterService;
  44 +import org.thingsboard.server.cluster.TbClusterService;
45 45
46 46 import java.util.List;
47 47 import java.util.Map;
... ...
... ... @@ -61,7 +61,7 @@ import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
61 61 import org.thingsboard.server.queue.discovery.PartitionService;
62 62 import org.thingsboard.server.queue.discovery.TbApplicationEventListener;
63 63 import org.thingsboard.server.queue.scheduler.SchedulerComponent;
64   -import org.thingsboard.server.service.queue.TbClusterService;
  64 +import org.thingsboard.server.cluster.TbClusterService;
65 65 import org.thingsboard.server.service.telemetry.InternalTelemetryService;
66 66
67 67 import javax.annotation.PostConstruct;
... ...
... ... @@ -29,6 +29,7 @@ import org.springframework.cache.CacheManager;
29 29 import org.springframework.stereotype.Service;
30 30 import org.springframework.util.StringUtils;
31 31 import org.thingsboard.rule.engine.api.RuleEngineTelemetryService;
  32 +import org.thingsboard.server.cluster.TbClusterService;
32 33 import org.thingsboard.server.common.data.Customer;
33 34 import org.thingsboard.server.common.data.DataConstants;
34 35 import org.thingsboard.server.common.data.Device;
... ... @@ -70,6 +71,8 @@ public class ClaimDevicesServiceImpl implements ClaimDevicesService {
70 71 private static final ObjectMapper mapper = new ObjectMapper();
71 72
72 73 @Autowired
  74 + private TbClusterService clusterService;
  75 + @Autowired
73 76 private DeviceService deviceService;
74 77 @Autowired
75 78 private AttributesService attributesService;
... ... @@ -155,6 +158,7 @@ public class ClaimDevicesServiceImpl implements ClaimDevicesService {
155 158 if (device.getCustomerId().getId().equals(ModelConstants.NULL_UUID)) {
156 159 device.setCustomerId(customerId);
157 160 Device savedDevice = deviceService.saveDevice(device);
  161 + clusterService.onDeviceUpdated(savedDevice, device);
158 162 return Futures.transform(removeClaimingSavedData(cache, claimData, device), result -> new ClaimResult(savedDevice, ClaimResponse.SUCCESS), MoreExecutors.directExecutor());
159 163 }
160 164 return Futures.transform(removeClaimingSavedData(cache, claimData, device), result -> new ClaimResult(null, ClaimResponse.CLAIMED), MoreExecutors.directExecutor());
... ... @@ -179,13 +183,14 @@ public class ClaimDevicesServiceImpl implements ClaimDevicesService {
179 183 cacheEviction(device.getId());
180 184 Customer unassignedCustomer = customerService.findCustomerById(tenantId, device.getCustomerId());
181 185 device.setCustomerId(null);
182   - deviceService.saveDevice(device);
  186 + Device savedDevice = deviceService.saveDevice(device);
  187 + clusterService.onDeviceUpdated(savedDevice, device);
183 188 if (isAllowedClaimingByDefault) {
184 189 return Futures.immediateFuture(new ReclaimResult(unassignedCustomer));
185 190 }
186 191 SettableFuture<ReclaimResult> result = SettableFuture.create();
187 192 telemetryService.saveAndNotify(
188   - tenantId, device.getId(), DataConstants.SERVER_SCOPE, Collections.singletonList(
  193 + tenantId, savedDevice.getId(), DataConstants.SERVER_SCOPE, Collections.singletonList(
189 194 new BaseAttributeKvEntry(new BooleanDataEntry(CLAIM_ATTRIBUTE_NAME, true), System.currentTimeMillis())
190 195 ),
191 196 new FutureCallback<>() {
... ... @@ -198,7 +203,7 @@ public class ClaimDevicesServiceImpl implements ClaimDevicesService {
198 203 public void onFailure(Throwable t) {
199 204 result.setException(t);
200 205 }
201   - });
  206 + });
202 207 return result;
203 208 }
204 209 cacheEviction(device.getId());
... ... @@ -238,7 +243,7 @@ public class ClaimDevicesServiceImpl implements ClaimDevicesService {
238 243 public void onFailure(Throwable t) {
239 244 result.setException(t);
240 245 }
241   - });
  246 + });
242 247 return result;
243 248 }
244 249
... ...
... ... @@ -24,6 +24,7 @@ import org.apache.commons.lang3.RandomStringUtils;
24 24 import org.springframework.beans.factory.annotation.Autowired;
25 25 import org.springframework.stereotype.Service;
26 26 import org.springframework.util.StringUtils;
  27 +import org.thingsboard.server.cluster.TbClusterService;
27 28 import org.thingsboard.server.common.data.DataConstants;
28 29 import org.thingsboard.server.common.data.Device;
29 30 import org.thingsboard.server.common.data.DeviceProfile;
... ... @@ -78,6 +79,9 @@ public class DeviceProvisionServiceImpl implements DeviceProvisionService {
78 79 private static final String PROVISIONED_STATE = "provisioned";
79 80
80 81 @Autowired
  82 + TbClusterService clusterService;
  83 +
  84 + @Autowired
81 85 DeviceDao deviceDao;
82 86
83 87 @Autowired
... ... @@ -190,8 +194,7 @@ public class DeviceProvisionServiceImpl implements DeviceProvisionService {
190 194 provisionRequest.setDeviceName(newDeviceName);
191 195 }
192 196 Device savedDevice = deviceService.saveDevice(provisionRequest, profile);
193   -
194   - deviceStateService.onDeviceAdded(savedDevice);
  197 + clusterService.onDeviceUpdated(savedDevice, null);
195 198 saveProvisionStateAttribute(savedDevice).get();
196 199 pushDeviceCreatedEventToRuleEngine(savedDevice);
197 200 notify(savedDevice, provisionRequest, DataConstants.PROVISION_SUCCESS, true);
... ...
... ... @@ -16,11 +16,7 @@
16 16 package org.thingsboard.server.service.edge;
17 17
18 18 import com.fasterxml.jackson.databind.JsonNode;
19   -import com.google.common.util.concurrent.FutureCallback;
20   -import com.google.common.util.concurrent.Futures;
21   -import com.google.common.util.concurrent.ListenableFuture;
22 19 import lombok.extern.slf4j.Slf4j;
23   -import org.checkerframework.checker.nullness.qual.Nullable;
24 20 import org.springframework.beans.factory.annotation.Autowired;
25 21 import org.springframework.stereotype.Service;
26 22 import org.thingsboard.server.common.data.edge.Edge;
... ... @@ -41,8 +37,7 @@ import org.thingsboard.server.service.edge.rpc.processor.CustomerEdgeProcessor;
41 37 import org.thingsboard.server.service.edge.rpc.processor.EdgeProcessor;
42 38 import org.thingsboard.server.service.edge.rpc.processor.EntityEdgeProcessor;
43 39 import org.thingsboard.server.service.edge.rpc.processor.RelationEdgeProcessor;
44   -import org.thingsboard.server.service.executors.DbCallbackExecutorService;
45   -import org.thingsboard.server.service.queue.TbClusterService;
  40 +import org.thingsboard.server.cluster.TbClusterService;
46 41
47 42 import javax.annotation.PostConstruct;
48 43 import javax.annotation.PreDestroy;
... ... @@ -66,9 +61,6 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService {
66 61 private TbClusterService clusterService;
67 62
68 63 @Autowired
69   - private DbCallbackExecutorService dbCallbackExecutorService;
70   -
71   - @Autowired
72 64 private EdgeProcessor edgeProcessor;
73 65
74 66 @Autowired
... ... @@ -123,23 +115,13 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService {
123 115 edgeEvent.setEntityId(entityId.getId());
124 116 }
125 117 edgeEvent.setBody(body);
126   - ListenableFuture<EdgeEvent> future = edgeEventService.saveAsync(edgeEvent);
127   - Futures.addCallback(future, new FutureCallback<EdgeEvent>() {
128   - @Override
129   - public void onSuccess(@Nullable EdgeEvent result) {
130   - clusterService.onEdgeEventUpdate(tenantId, edgeId);
131   - }
132   -
133   - @Override
134   - public void onFailure(Throwable t) {
135   - log.warn("[{}] Can't save edge event [{}] for edge [{}]", tenantId.getId(), edgeEvent, edgeId.getId(), t);
136   - }
137   - }, dbCallbackExecutorService);
138   -
  118 + edgeEventService.save(edgeEvent);
  119 + clusterService.onEdgeEventUpdate(tenantId, edgeId);
139 120 }
140 121
141 122 @Override
142 123 public void pushNotificationToEdge(TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg, TbCallback callback) {
  124 + log.trace("Pushing notification to edge {}", edgeNotificationMsg);
143 125 try {
144 126 TenantId tenantId = new TenantId(new UUID(edgeNotificationMsg.getTenantIdMSB(), edgeNotificationMsg.getTenantIdLSB()));
145 127 EdgeEventType type = EdgeEventType.valueOf(edgeNotificationMsg.getType());
... ...
... ... @@ -259,7 +259,7 @@ public final class EdgeGrpcSession implements Closeable {
259 259 log.error("[{}] Msg processing failed! Error msg: {}", edge.getRoutingKey(), msg.getErrorMsg());
260 260 }
261 261 if (sessionState.getPendingMsgsMap().isEmpty()) {
262   - log.debug("[{}] Pending msgs map is empty. Stopping current iteration {}", edge.getRoutingKey(), msg);
  262 + log.debug("[{}] Pending msgs map is empty. Stopping current iteration", edge.getRoutingKey());
263 263 if (sessionState.getScheduledSendDownlinkTask() != null) {
264 264 sessionState.getScheduledSendDownlinkTask().cancel(false);
265 265 }
... ... @@ -527,7 +527,7 @@ public final class EdgeGrpcSession implements Closeable {
527 527 case RULE_CHAIN_METADATA:
528 528 return ctx.getRuleChainProcessor().processRuleChainMetadataToEdge(edgeEvent, msgType);
529 529 case ALARM:
530   - return ctx.getAlarmProcessor().processAlarmToEdge(edge, edgeEvent, msgType);
  530 + return ctx.getAlarmProcessor().processAlarmToEdge(edge, edgeEvent, msgType, action);
531 531 case USER:
532 532 return ctx.getUserProcessor().processUserToEdge(edge, edgeEvent, msgType, action);
533 533 case RELATION:
... ...
  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.protobuf.BoolValue;
  19 +import com.google.protobuf.ByteString;
  20 +import com.google.protobuf.BytesValue;
  21 +import com.google.protobuf.Int64Value;
  22 +import com.google.protobuf.StringValue;
  23 +
  24 +public class EdgeProtoUtils {
  25 +
  26 + private EdgeProtoUtils() {
  27 + }
  28 +
  29 + public static BoolValue getBoolValue(Boolean value) {
  30 + BoolValue.Builder builder = BoolValue.newBuilder();
  31 + builder.setValue(value);
  32 + return builder.build();
  33 + }
  34 +
  35 + public static StringValue getStringValue(String value) {
  36 + StringValue.Builder builder = StringValue.newBuilder();
  37 + builder.setValue(value);
  38 + return builder.build();
  39 + }
  40 +
  41 + public static Int64Value getInt64Value(Long value) {
  42 + Int64Value.Builder builder = Int64Value.newBuilder();
  43 + builder.setValue(value);
  44 + return builder.build();
  45 + }
  46 +
  47 + public static BytesValue getBytesValue(ByteString value) {
  48 + BytesValue.Builder builder = BytesValue.newBuilder();
  49 + builder.setValue(value);
  50 + return builder.build();
  51 + }
  52 +}
... ...
... ... @@ -58,6 +58,8 @@ public class AlarmMsgConstructor {
58 58 }
59 59 AlarmUpdateMsg.Builder builder = AlarmUpdateMsg.newBuilder()
60 60 .setMsgType(msgType)
  61 + .setIdMSB(alarm.getId().getId().getMostSignificantBits())
  62 + .setIdLSB(alarm.getId().getId().getLeastSignificantBits())
61 63 .setName(alarm.getName())
62 64 .setType(alarm.getType())
63 65 .setOriginatorName(entityName)
... ...
... ... @@ -24,6 +24,9 @@ import org.thingsboard.server.gen.edge.v1.AssetUpdateMsg;
24 24 import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
25 25 import org.thingsboard.server.queue.util.TbCoreComponent;
26 26
  27 +import static org.thingsboard.server.service.edge.rpc.EdgeProtoUtils.getInt64Value;
  28 +import static org.thingsboard.server.service.edge.rpc.EdgeProtoUtils.getStringValue;
  29 +
27 30 @Component
28 31 @TbCoreComponent
29 32 public class AssetMsgConstructor {
... ... @@ -36,14 +39,14 @@ public class AssetMsgConstructor {
36 39 .setName(asset.getName())
37 40 .setType(asset.getType());
38 41 if (asset.getLabel() != null) {
39   - builder.setLabel(asset.getLabel());
  42 + builder.setLabel(getStringValue(asset.getLabel()));
40 43 }
41 44 if (customerId != null) {
42   - builder.setCustomerIdMSB(customerId.getId().getMostSignificantBits());
43   - builder.setCustomerIdLSB(customerId.getId().getLeastSignificantBits());
  45 + builder.setCustomerIdMSB(getInt64Value(customerId.getId().getMostSignificantBits()));
  46 + builder.setCustomerIdLSB(getInt64Value(customerId.getId().getLeastSignificantBits()));
44 47 }
45 48 if (asset.getAdditionalInfo() != null) {
46   - builder.setAdditionalInfo(JacksonUtil.toString(asset.getAdditionalInfo()));
  49 + builder.setAdditionalInfo(getStringValue(JacksonUtil.toString(asset.getAdditionalInfo())));
47 50 }
48 51 return builder.build();
49 52 }
... ...
... ... @@ -23,6 +23,8 @@ import org.thingsboard.server.gen.edge.v1.CustomerUpdateMsg;
23 23 import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
24 24 import org.thingsboard.server.queue.util.TbCoreComponent;
25 25
  26 +import static org.thingsboard.server.service.edge.rpc.EdgeProtoUtils.getStringValue;
  27 +
26 28 @Component
27 29 @TbCoreComponent
28 30 public class CustomerMsgConstructor {
... ... @@ -34,31 +36,31 @@ public class CustomerMsgConstructor {
34 36 .setIdLSB(customer.getId().getId().getLeastSignificantBits())
35 37 .setTitle(customer.getTitle());
36 38 if (customer.getCountry() != null) {
37   - builder.setCountry(customer.getCountry());
  39 + builder.setCountry(getStringValue(customer.getCountry()));
38 40 }
39 41 if (customer.getState() != null) {
40   - builder.setState(customer.getState());
  42 + builder.setState(getStringValue(customer.getState()));
41 43 }
42 44 if (customer.getCity() != null) {
43   - builder.setCity(customer.getCity());
  45 + builder.setCity(getStringValue(customer.getCity()));
44 46 }
45 47 if (customer.getAddress() != null) {
46   - builder.setAddress(customer.getAddress());
  48 + builder.setAddress(getStringValue(customer.getAddress()));
47 49 }
48 50 if (customer.getAddress2() != null) {
49   - builder.setAddress2(customer.getAddress2());
  51 + builder.setAddress2(getStringValue(customer.getAddress2()));
50 52 }
51 53 if (customer.getZip() != null) {
52   - builder.setZip(customer.getZip());
  54 + builder.setZip(getStringValue(customer.getZip()));
53 55 }
54 56 if (customer.getPhone() != null) {
55   - builder.setPhone(customer.getPhone());
  57 + builder.setPhone(getStringValue(customer.getPhone()));
56 58 }
57 59 if (customer.getEmail() != null) {
58   - builder.setEmail(customer.getEmail());
  60 + builder.setEmail(getStringValue(customer.getEmail()));
59 61 }
60 62 if (customer.getAdditionalInfo() != null) {
61   - builder.setAdditionalInfo(JacksonUtil.toString(customer.getAdditionalInfo()));
  63 + builder.setAdditionalInfo(getStringValue(JacksonUtil.toString(customer.getAdditionalInfo())));
62 64 }
63 65 return builder.build();
64 66 }
... ...
... ... @@ -24,6 +24,8 @@ import org.thingsboard.server.gen.edge.v1.DashboardUpdateMsg;
24 24 import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
25 25 import org.thingsboard.server.queue.util.TbCoreComponent;
26 26
  27 +import static org.thingsboard.server.service.edge.rpc.EdgeProtoUtils.getInt64Value;
  28 +
27 29 @Component
28 30 @TbCoreComponent
29 31 public class DashboardMsgConstructor {
... ... @@ -36,8 +38,8 @@ public class DashboardMsgConstructor {
36 38 .setTitle(dashboard.getTitle())
37 39 .setConfiguration(JacksonUtil.toString(dashboard.getConfiguration()));
38 40 if (customerId != null) {
39   - builder.setCustomerIdMSB(customerId.getId().getMostSignificantBits());
40   - builder.setCustomerIdLSB(customerId.getId().getLeastSignificantBits());
  41 + builder.setCustomerIdMSB(getInt64Value(customerId.getId().getMostSignificantBits()));
  42 + builder.setCustomerIdLSB(getInt64Value(customerId.getId().getLeastSignificantBits()));
41 43 }
42 44 return builder.build();
43 45 }
... ...
... ... @@ -32,6 +32,9 @@ import org.thingsboard.server.queue.util.TbCoreComponent;
32 32
33 33 import java.util.UUID;
34 34
  35 +import static org.thingsboard.server.service.edge.rpc.EdgeProtoUtils.getInt64Value;
  36 +import static org.thingsboard.server.service.edge.rpc.EdgeProtoUtils.getStringValue;
  37 +
35 38 @Component
36 39 @TbCoreComponent
37 40 public class DeviceMsgConstructor {
... ... @@ -46,21 +49,21 @@ public class DeviceMsgConstructor {
46 49 .setName(device.getName())
47 50 .setType(device.getType());
48 51 if (device.getLabel() != null) {
49   - builder.setLabel(device.getLabel());
  52 + builder.setLabel(getStringValue(device.getLabel()));
50 53 }
51 54 if (customerId != null) {
52   - builder.setCustomerIdMSB(customerId.getId().getMostSignificantBits());
53   - builder.setCustomerIdLSB(customerId.getId().getLeastSignificantBits());
  55 + builder.setCustomerIdMSB(getInt64Value(customerId.getId().getMostSignificantBits()));
  56 + builder.setCustomerIdLSB(getInt64Value(customerId.getId().getLeastSignificantBits()));
54 57 }
55 58 if (device.getDeviceProfileId() != null) {
56   - builder.setDeviceProfileIdMSB(device.getDeviceProfileId().getId().getMostSignificantBits());
57   - builder.setDeviceProfileIdLSB(device.getDeviceProfileId().getId().getLeastSignificantBits());
  59 + builder.setDeviceProfileIdMSB(getInt64Value(device.getDeviceProfileId().getId().getMostSignificantBits()));
  60 + builder.setDeviceProfileIdLSB(getInt64Value(device.getDeviceProfileId().getId().getLeastSignificantBits()));
58 61 }
59 62 if (device.getAdditionalInfo() != null) {
60   - builder.setAdditionalInfo(JacksonUtil.toString(device.getAdditionalInfo()));
  63 + builder.setAdditionalInfo(getStringValue(JacksonUtil.toString(device.getAdditionalInfo())));
61 64 }
62 65 if (conflictName != null) {
63   - builder.setConflictName(conflictName);
  66 + builder.setConflictName(getStringValue(conflictName));
64 67 }
65 68 return builder.build();
66 69 }
... ... @@ -74,7 +77,7 @@ public class DeviceMsgConstructor {
74 77 .setCredentialsId(deviceCredentials.getCredentialsId());
75 78 }
76 79 if (deviceCredentials.getCredentialsValue() != null) {
77   - builder.setCredentialsValue(deviceCredentials.getCredentialsValue());
  80 + builder.setCredentialsValue(getStringValue(deviceCredentials.getCredentialsValue()));
78 81 }
79 82 return builder.build();
80 83 }
... ...
... ... @@ -27,6 +27,9 @@ import org.thingsboard.server.queue.util.TbCoreComponent;
27 27
28 28 import java.nio.charset.StandardCharsets;
29 29
  30 +import static org.thingsboard.server.service.edge.rpc.EdgeProtoUtils.getBytesValue;
  31 +import static org.thingsboard.server.service.edge.rpc.EdgeProtoUtils.getStringValue;
  32 +
30 33 @Component
31 34 @TbCoreComponent
32 35 public class DeviceProfileMsgConstructor {
... ... @@ -52,19 +55,19 @@ public class DeviceProfileMsgConstructor {
52 55 // builder.setDefaultQueueName(deviceProfile.getDefaultQueueName());
53 56 // }
54 57 if (deviceProfile.getDescription() != null) {
55   - builder.setDescription(deviceProfile.getDescription());
  58 + builder.setDescription(getStringValue(deviceProfile.getDescription()));
56 59 }
57 60 if (deviceProfile.getTransportType() != null) {
58   - builder.setTransportType(deviceProfile.getTransportType().name());
  61 + builder.setTransportType(getStringValue(deviceProfile.getTransportType().name()));
59 62 }
60 63 if (deviceProfile.getProvisionType() != null) {
61   - builder.setProvisionType(deviceProfile.getProvisionType().name());
  64 + builder.setProvisionType(getStringValue(deviceProfile.getProvisionType().name()));
62 65 }
63 66 if (deviceProfile.getProvisionDeviceKey() != null) {
64   - builder.setProvisionDeviceKey(deviceProfile.getProvisionDeviceKey());
  67 + builder.setProvisionDeviceKey(getStringValue(deviceProfile.getProvisionDeviceKey()));
65 68 }
66 69 if (deviceProfile.getImage() != null) {
67   - builder.setImage(ByteString.copyFrom(deviceProfile.getImage().getBytes(StandardCharsets.UTF_8)));
  70 + builder.setImage(getBytesValue(ByteString.copyFrom(deviceProfile.getImage().getBytes(StandardCharsets.UTF_8))));
68 71 }
69 72 return builder.build();
70 73 }
... ...
... ... @@ -25,6 +25,9 @@ import org.thingsboard.server.gen.edge.v1.EntityViewUpdateMsg;
25 25 import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
26 26 import org.thingsboard.server.queue.util.TbCoreComponent;
27 27
  28 +import static org.thingsboard.server.service.edge.rpc.EdgeProtoUtils.getInt64Value;
  29 +import static org.thingsboard.server.service.edge.rpc.EdgeProtoUtils.getStringValue;
  30 +
28 31 @Component
29 32 @TbCoreComponent
30 33 public class EntityViewMsgConstructor {
... ... @@ -51,11 +54,11 @@ public class EntityViewMsgConstructor {
51 54 .setEntityIdLSB(entityView.getEntityId().getId().getLeastSignificantBits())
52 55 .setEntityType(entityType);
53 56 if (customerId != null) {
54   - builder.setCustomerIdMSB(customerId.getId().getMostSignificantBits());
55   - builder.setCustomerIdLSB(customerId.getId().getLeastSignificantBits());
  57 + builder.setCustomerIdMSB(getInt64Value(customerId.getId().getMostSignificantBits()));
  58 + builder.setCustomerIdLSB(getInt64Value(customerId.getId().getLeastSignificantBits()));
56 59 }
57 60 if (entityView.getAdditionalInfo() != null) {
58   - builder.setAdditionalInfo(JacksonUtil.toString(entityView.getAdditionalInfo()));
  61 + builder.setAdditionalInfo(getStringValue(JacksonUtil.toString(entityView.getAdditionalInfo())));
59 62 }
60 63 return builder.build();
61 64 }
... ...
... ... @@ -22,6 +22,8 @@ import org.thingsboard.server.gen.edge.v1.RelationUpdateMsg;
22 22 import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
23 23 import org.thingsboard.server.queue.util.TbCoreComponent;
24 24
  25 +import static org.thingsboard.server.service.edge.rpc.EdgeProtoUtils.getStringValue;
  26 +
25 27 @Component
26 28 @TbCoreComponent
27 29 public class RelationMsgConstructor {
... ... @@ -35,10 +37,12 @@ public class RelationMsgConstructor {
35 37 .setToIdMSB(entityRelation.getTo().getId().getMostSignificantBits())
36 38 .setToIdLSB(entityRelation.getTo().getId().getLeastSignificantBits())
37 39 .setToEntityType(entityRelation.getTo().getEntityType().name())
38   - .setType(entityRelation.getType())
39   - .setAdditionalInfo(JacksonUtil.toString(entityRelation.getAdditionalInfo()));
  40 + .setType(entityRelation.getType());
  41 + if (entityRelation.getAdditionalInfo() != null) {
  42 + builder.setAdditionalInfo(JacksonUtil.toString(entityRelation.getAdditionalInfo()));
  43 + }
40 44 if (entityRelation.getTypeGroup() != null) {
41   - builder.setTypeGroup(entityRelation.getTypeGroup().name());
  45 + builder.setTypeGroup(getStringValue(entityRelation.getTypeGroup().name()));
42 46 }
43 47 return builder.build();
44 48 }
... ...
... ... @@ -37,6 +37,8 @@ import org.thingsboard.server.queue.util.TbCoreComponent;
37 37 import java.util.ArrayList;
38 38 import java.util.List;
39 39
  40 +import static org.thingsboard.server.service.edge.rpc.EdgeProtoUtils.getInt64Value;
  41 +
40 42 @Component
41 43 @Slf4j
42 44 @TbCoreComponent
... ... @@ -54,8 +56,8 @@ public class RuleChainMsgConstructor {
54 56 .setDebugMode(ruleChain.isDebugMode())
55 57 .setConfiguration(JacksonUtil.toString(ruleChain.getConfiguration()));
56 58 if (ruleChain.getFirstRuleNodeId() != null) {
57   - builder.setFirstRuleNodeIdMSB(ruleChain.getFirstRuleNodeId().getId().getMostSignificantBits())
58   - .setFirstRuleNodeIdLSB(ruleChain.getFirstRuleNodeId().getId().getLeastSignificantBits());
  59 + builder.setFirstRuleNodeIdMSB(getInt64Value(ruleChain.getFirstRuleNodeId().getId().getMostSignificantBits()))
  60 + .setFirstRuleNodeIdLSB(getInt64Value(ruleChain.getFirstRuleNodeId().getId().getLeastSignificantBits()));
59 61 }
60 62 return builder.build();
61 63 }
... ...
... ... @@ -16,16 +16,19 @@
16 16 package org.thingsboard.server.service.edge.rpc.constructor;
17 17
18 18 import org.springframework.stereotype.Component;
  19 +import org.thingsboard.common.util.JacksonUtil;
19 20 import org.thingsboard.server.common.data.User;
20 21 import org.thingsboard.server.common.data.id.CustomerId;
21 22 import org.thingsboard.server.common.data.id.UserId;
22 23 import org.thingsboard.server.common.data.security.UserCredentials;
23   -import org.thingsboard.common.util.JacksonUtil;
24 24 import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
25 25 import org.thingsboard.server.gen.edge.v1.UserCredentialsUpdateMsg;
26 26 import org.thingsboard.server.gen.edge.v1.UserUpdateMsg;
27 27 import org.thingsboard.server.queue.util.TbCoreComponent;
28 28
  29 +import static org.thingsboard.server.service.edge.rpc.EdgeProtoUtils.getInt64Value;
  30 +import static org.thingsboard.server.service.edge.rpc.EdgeProtoUtils.getStringValue;
  31 +
29 32 @Component
30 33 @TbCoreComponent
31 34 public class UserMsgConstructor {
... ... @@ -38,20 +41,17 @@ public class UserMsgConstructor {
38 41 .setEmail(user.getEmail())
39 42 .setAuthority(user.getAuthority().name());
40 43 if (customerId != null) {
41   - builder.setCustomerIdMSB(customerId.getId().getMostSignificantBits());
42   - builder.setCustomerIdLSB(customerId.getId().getLeastSignificantBits());
  44 + builder.setCustomerIdMSB(getInt64Value(customerId.getId().getMostSignificantBits()));
  45 + builder.setCustomerIdLSB(getInt64Value(customerId.getId().getLeastSignificantBits()));
43 46 }
44 47 if (user.getFirstName() != null) {
45   - builder.setFirstName(user.getFirstName());
  48 + builder.setFirstName(getStringValue(user.getFirstName()));
46 49 }
47 50 if (user.getLastName() != null) {
48   - builder.setLastName(user.getLastName());
49   - }
50   - if (user.getAdditionalInfo() != null) {
51   - builder.setAdditionalInfo(JacksonUtil.toString(user.getAdditionalInfo()));
  51 + builder.setLastName(getStringValue(user.getLastName()));
52 52 }
53 53 if (user.getAdditionalInfo() != null) {
54   - builder.setAdditionalInfo(JacksonUtil.toString(user.getAdditionalInfo()));
  54 + builder.setAdditionalInfo(getStringValue(JacksonUtil.toString(user.getAdditionalInfo())));
55 55 }
56 56 return builder.build();
57 57 }
... ...
... ... @@ -16,14 +16,16 @@
16 16 package org.thingsboard.server.service.edge.rpc.constructor;
17 17
18 18 import org.springframework.stereotype.Component;
  19 +import org.thingsboard.common.util.JacksonUtil;
19 20 import org.thingsboard.server.common.data.id.TenantId;
20 21 import org.thingsboard.server.common.data.id.WidgetTypeId;
21 22 import org.thingsboard.server.common.data.widget.WidgetType;
22   -import org.thingsboard.common.util.JacksonUtil;
23 23 import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
24 24 import org.thingsboard.server.gen.edge.v1.WidgetTypeUpdateMsg;
25 25 import org.thingsboard.server.queue.util.TbCoreComponent;
26 26
  27 +import static org.thingsboard.server.service.edge.rpc.EdgeProtoUtils.getStringValue;
  28 +
27 29 @Component
28 30 @TbCoreComponent
29 31 public class WidgetTypeMsgConstructor {
... ... @@ -33,21 +35,21 @@ public class WidgetTypeMsgConstructor {
33 35 .setMsgType(msgType)
34 36 .setIdMSB(widgetType.getId().getId().getMostSignificantBits())
35 37 .setIdLSB(widgetType.getId().getId().getLeastSignificantBits());
36   - if (widgetType.getBundleAlias() != null) {
37   - builder.setBundleAlias(widgetType.getBundleAlias());
38   - }
39   - if (widgetType.getAlias() != null) {
40   - builder.setAlias(widgetType.getAlias());
41   - }
42   - if (widgetType.getName() != null) {
43   - builder.setName(widgetType.getName());
44   - }
45   - if (widgetType.getDescriptor() != null) {
46   - builder.setDescriptorJson(JacksonUtil.toString(widgetType.getDescriptor()));
47   - }
48   - if (widgetType.getTenantId().equals(TenantId.SYS_TENANT_ID)) {
49   - builder.setIsSystem(true);
50   - }
  38 + if (widgetType.getBundleAlias() != null) {
  39 + builder.setBundleAlias(getStringValue(widgetType.getBundleAlias()));
  40 + }
  41 + if (widgetType.getAlias() != null) {
  42 + builder.setAlias(getStringValue(widgetType.getAlias()));
  43 + }
  44 + if (widgetType.getName() != null) {
  45 + builder.setName(getStringValue(widgetType.getName()));
  46 + }
  47 + if (widgetType.getDescriptor() != null) {
  48 + builder.setDescriptorJson(getStringValue(JacksonUtil.toString(widgetType.getDescriptor())));
  49 + }
  50 + if (widgetType.getTenantId().equals(TenantId.SYS_TENANT_ID)) {
  51 + builder.setIsSystem(true);
  52 + }
51 53 return builder.build();
52 54 }
53 55
... ...
... ... @@ -26,6 +26,9 @@ import org.thingsboard.server.queue.util.TbCoreComponent;
26 26
27 27 import java.nio.charset.StandardCharsets;
28 28
  29 +import static org.thingsboard.server.service.edge.rpc.EdgeProtoUtils.getBytesValue;
  30 +import static org.thingsboard.server.service.edge.rpc.EdgeProtoUtils.getStringValue;
  31 +
29 32 @Component
30 33 @TbCoreComponent
31 34 public class WidgetsBundleMsgConstructor {
... ... @@ -38,10 +41,10 @@ public class WidgetsBundleMsgConstructor {
38 41 .setTitle(widgetsBundle.getTitle())
39 42 .setAlias(widgetsBundle.getAlias());
40 43 if (widgetsBundle.getImage() != null) {
41   - builder.setImage(ByteString.copyFrom(widgetsBundle.getImage().getBytes(StandardCharsets.UTF_8)));
  44 + builder.setImage(getBytesValue(ByteString.copyFrom(widgetsBundle.getImage().getBytes(StandardCharsets.UTF_8))));
42 45 }
43 46 if (widgetsBundle.getDescription() != null) {
44   - builder.setDescription(widgetsBundle.getDescription());
  47 + builder.setDescription(getStringValue(widgetsBundle.getDescription()));
45 48 }
46 49 if (widgetsBundle.getTenantId().equals(TenantId.SYS_TENANT_ID)) {
47 50 builder.setIsSystem(true);
... ...
... ... @@ -15,6 +15,7 @@
15 15 */
16 16 package org.thingsboard.server.service.edge.rpc.processor;
17 17
  18 +import com.fasterxml.jackson.core.JsonProcessingException;
18 19 import com.google.common.util.concurrent.FutureCallback;
19 20 import com.google.common.util.concurrent.Futures;
20 21 import com.google.common.util.concurrent.ListenableFuture;
... ... @@ -54,6 +55,7 @@ public class AlarmEdgeProcessor extends BaseEdgeProcessor {
54 55 EntityId originatorId = getAlarmOriginator(tenantId, alarmUpdateMsg.getOriginatorName(),
55 56 EntityType.valueOf(alarmUpdateMsg.getOriginatorType()));
56 57 if (originatorId == null) {
  58 + log.warn("Originator not found for the alarm msg {}", alarmUpdateMsg);
57 59 return Futures.immediateFuture(null);
58 60 }
59 61 try {
... ... @@ -113,59 +115,84 @@ public class AlarmEdgeProcessor extends BaseEdgeProcessor {
113 115 }
114 116 }
115 117
116   - public DownlinkMsg processAlarmToEdge(Edge edge, EdgeEvent edgeEvent, UpdateMsgType msgType) {
  118 + public DownlinkMsg processAlarmToEdge(Edge edge, EdgeEvent edgeEvent, UpdateMsgType msgType, EdgeEventActionType action) {
  119 + AlarmId alarmId = new AlarmId(edgeEvent.getEntityId());
117 120 DownlinkMsg downlinkMsg = null;
118   - try {
119   - AlarmId alarmId = new AlarmId(edgeEvent.getEntityId());
120   - Alarm alarm = alarmService.findAlarmByIdAsync(edgeEvent.getTenantId(), alarmId).get();
121   - if (alarm != null) {
  121 + switch (action) {
  122 + case ADDED:
  123 + case UPDATED:
  124 + case ALARM_ACK:
  125 + case ALARM_CLEAR:
  126 + try {
  127 + Alarm alarm = alarmService.findAlarmByIdAsync(edgeEvent.getTenantId(), alarmId).get();
  128 + if (alarm != null) {
  129 + downlinkMsg = DownlinkMsg.newBuilder()
  130 + .setDownlinkMsgId(EdgeUtils.nextPositiveInt())
  131 + .addAlarmUpdateMsg(alarmMsgConstructor.constructAlarmUpdatedMsg(edge.getTenantId(), msgType, alarm))
  132 + .build();
  133 + }
  134 + } catch (Exception e) {
  135 + log.error("Can't process alarm msg [{}] [{}]", edgeEvent, msgType, e);
  136 + }
  137 + break;
  138 + case DELETED:
  139 + Alarm alarm = mapper.convertValue(edgeEvent.getBody(), Alarm.class);
  140 + AlarmUpdateMsg alarmUpdateMsg =
  141 + alarmMsgConstructor.constructAlarmUpdatedMsg(edge.getTenantId(), msgType, alarm);
122 142 downlinkMsg = DownlinkMsg.newBuilder()
123 143 .setDownlinkMsgId(EdgeUtils.nextPositiveInt())
124   - .addAlarmUpdateMsg(alarmMsgConstructor.constructAlarmUpdatedMsg(edge.getTenantId(), msgType, alarm))
  144 + .addAlarmUpdateMsg(alarmUpdateMsg)
125 145 .build();
126   - }
127   - } catch (Exception e) {
128   - log.error("Can't process alarm msg [{}] [{}]", edgeEvent, msgType, e);
  146 + break;
129 147 }
130 148 return downlinkMsg;
131 149 }
132 150
133   - public void processAlarmNotification(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) {
  151 + public void processAlarmNotification(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) throws JsonProcessingException {
  152 + EdgeEventActionType actionType = EdgeEventActionType.valueOf(edgeNotificationMsg.getAction());
134 153 AlarmId alarmId = new AlarmId(new UUID(edgeNotificationMsg.getEntityIdMSB(), edgeNotificationMsg.getEntityIdLSB()));
135   - ListenableFuture<Alarm> alarmFuture = alarmService.findAlarmByIdAsync(tenantId, alarmId);
136   - Futures.addCallback(alarmFuture, new FutureCallback<Alarm>() {
137   - @Override
138   - public void onSuccess(@Nullable Alarm alarm) {
139   - if (alarm != null) {
140   - EdgeEventType type = EdgeUtils.getEdgeEventTypeByEntityType(alarm.getOriginator().getEntityType());
141   - if (type != null) {
142   - PageLink pageLink = new PageLink(DEFAULT_PAGE_SIZE);
143   - PageData<EdgeId> pageData;
144   - do {
145   - pageData = edgeService.findRelatedEdgeIdsByEntityId(tenantId, alarm.getOriginator(), pageLink);
146   - if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) {
147   - for (EdgeId edgeId : pageData.getData()) {
148   - saveEdgeEvent(tenantId,
149   - edgeId,
150   - EdgeEventType.ALARM,
151   - EdgeEventActionType.valueOf(edgeNotificationMsg.getAction()),
152   - alarmId,
153   - null);
154   - }
155   - if (pageData.hasNext()) {
156   - pageLink = pageLink.nextPageLink();
157   - }
  154 + switch (actionType) {
  155 + case DELETED:
  156 + EdgeId edgeId = new EdgeId(new UUID(edgeNotificationMsg.getEdgeIdMSB(), edgeNotificationMsg.getEdgeIdLSB()));
  157 + Alarm alarm = mapper.readValue(edgeNotificationMsg.getBody(), Alarm.class);
  158 + saveEdgeEvent(tenantId, edgeId, EdgeEventType.ALARM, actionType, alarmId, mapper.valueToTree(alarm));
  159 + break;
  160 + default:
  161 + ListenableFuture<Alarm> alarmFuture = alarmService.findAlarmByIdAsync(tenantId, alarmId);
  162 + Futures.addCallback(alarmFuture, new FutureCallback<Alarm>() {
  163 + @Override
  164 + public void onSuccess(@Nullable Alarm alarm) {
  165 + if (alarm != null) {
  166 + EdgeEventType type = EdgeUtils.getEdgeEventTypeByEntityType(alarm.getOriginator().getEntityType());
  167 + if (type != null) {
  168 + PageLink pageLink = new PageLink(DEFAULT_PAGE_SIZE);
  169 + PageData<EdgeId> pageData;
  170 + do {
  171 + pageData = edgeService.findRelatedEdgeIdsByEntityId(tenantId, alarm.getOriginator(), pageLink);
  172 + if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) {
  173 + for (EdgeId edgeId : pageData.getData()) {
  174 + saveEdgeEvent(tenantId,
  175 + edgeId,
  176 + EdgeEventType.ALARM,
  177 + EdgeEventActionType.valueOf(edgeNotificationMsg.getAction()),
  178 + alarmId,
  179 + null);
  180 + }
  181 + if (pageData.hasNext()) {
  182 + pageLink = pageLink.nextPageLink();
  183 + }
  184 + }
  185 + } while (pageData != null && pageData.hasNext());
158 186 }
159   - } while (pageData != null && pageData.hasNext());
  187 + }
160 188 }
161   - }
162   - }
163 189
164   - @Override
165   - public void onFailure(Throwable t) {
166   - log.warn("[{}] can't find alarm by id [{}] {}", tenantId.getId(), alarmId.getId(), t);
167   - }
168   - }, dbCallbackExecutorService);
  190 + @Override
  191 + public void onFailure(Throwable t) {
  192 + log.warn("[{}] can't find alarm by id [{}] {}", tenantId.getId(), alarmId.getId(), t);
  193 + }
  194 + }, dbCallbackExecutorService);
  195 + }
169 196 }
170 197
171 198 }
... ...
... ... @@ -17,11 +17,7 @@ package org.thingsboard.server.service.edge.rpc.processor;
17 17
18 18 import com.fasterxml.jackson.databind.JsonNode;
19 19 import com.fasterxml.jackson.databind.ObjectMapper;
20   -import com.google.common.util.concurrent.FutureCallback;
21   -import com.google.common.util.concurrent.Futures;
22   -import com.google.common.util.concurrent.ListenableFuture;
23 20 import lombok.extern.slf4j.Slf4j;
24   -import org.checkerframework.checker.nullness.qual.Nullable;
25 21 import org.springframework.beans.factory.annotation.Autowired;
26 22 import org.thingsboard.server.common.data.HasCustomerId;
27 23 import org.thingsboard.server.common.data.edge.Edge;
... ... @@ -66,7 +62,7 @@ import org.thingsboard.server.service.edge.rpc.constructor.WidgetTypeMsgConstruc
66 62 import org.thingsboard.server.service.edge.rpc.constructor.WidgetsBundleMsgConstructor;
67 63 import org.thingsboard.server.service.executors.DbCallbackExecutorService;
68 64 import org.thingsboard.server.service.profile.TbDeviceProfileCache;
69   -import org.thingsboard.server.service.queue.TbClusterService;
  65 +import org.thingsboard.server.cluster.TbClusterService;
70 66 import org.thingsboard.server.service.state.DeviceStateService;
71 67
72 68 @Slf4j
... ... @@ -178,12 +174,12 @@ public abstract class BaseEdgeProcessor {
178 174 @Autowired
179 175 protected DbCallbackExecutorService dbCallbackExecutorService;
180 176
181   - protected ListenableFuture<EdgeEvent> saveEdgeEvent(TenantId tenantId,
182   - EdgeId edgeId,
183   - EdgeEventType type,
184   - EdgeEventActionType action,
185   - EntityId entityId,
186   - JsonNode body) {
  177 + protected void saveEdgeEvent(TenantId tenantId,
  178 + EdgeId edgeId,
  179 + EdgeEventType type,
  180 + EdgeEventActionType action,
  181 + EntityId entityId,
  182 + JsonNode body) {
187 183 log.debug("Pushing event to edge queue. tenantId [{}], edgeId [{}], type[{}], " +
188 184 "action [{}], entityId [{}], body [{}]",
189 185 tenantId, edgeId, type, action, entityId, body);
... ... @@ -197,19 +193,8 @@ public abstract class BaseEdgeProcessor {
197 193 edgeEvent.setEntityId(entityId.getId());
198 194 }
199 195 edgeEvent.setBody(body);
200   - ListenableFuture<EdgeEvent> future = edgeEventService.saveAsync(edgeEvent);
201   - Futures.addCallback(future, new FutureCallback<EdgeEvent>() {
202   - @Override
203   - public void onSuccess(@Nullable EdgeEvent result) {
204   - tbClusterService.onEdgeEventUpdate(tenantId, edgeId);
205   - }
206   -
207   - @Override
208   - public void onFailure(Throwable t) {
209   - log.warn("[{}] Can't save edge event [{}] for edge [{}]", tenantId.getId(), edgeEvent, edgeId.getId(), t);
210   - }
211   - }, dbCallbackExecutorService);
212   - return future;
  196 + edgeEventService.save(edgeEvent);
  197 + tbClusterService.onEdgeEventUpdate(tenantId, edgeId);
213 198 }
214 199
215 200 protected CustomerId getCustomerIdIfEdgeAssignedToCustomer(HasCustomerId hasCustomerIdEntity, Edge edge) {
... ...
... ... @@ -18,7 +18,6 @@ package org.thingsboard.server.service.edge.rpc.processor;
18 18 import com.datastax.oss.driver.api.core.uuid.Uuids;
19 19 import com.fasterxml.jackson.core.JsonProcessingException;
20 20 import com.fasterxml.jackson.databind.node.ObjectNode;
21   -import com.google.common.util.concurrent.FutureCallback;
22 21 import com.google.common.util.concurrent.Futures;
23 22 import com.google.common.util.concurrent.ListenableFuture;
24 23 import com.google.common.util.concurrent.SettableFuture;
... ... @@ -27,7 +26,7 @@ import org.apache.commons.lang3.RandomStringUtils;
27 26 import org.apache.commons.lang3.StringUtils;
28 27 import org.springframework.stereotype.Component;
29 28 import org.thingsboard.common.util.JacksonUtil;
30   -import org.thingsboard.rule.engine.api.RpcError;
  29 +import org.thingsboard.server.common.data.rpc.RpcError;
31 30 import org.thingsboard.server.common.data.Customer;
32 31 import org.thingsboard.server.common.data.DataConstants;
33 32 import org.thingsboard.server.common.data.Device;
... ... @@ -60,7 +59,7 @@ import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
60 59 import org.thingsboard.server.queue.TbQueueCallback;
61 60 import org.thingsboard.server.queue.TbQueueMsgMetadata;
62 61 import org.thingsboard.server.queue.util.TbCoreComponent;
63   -import org.thingsboard.server.service.rpc.FromDeviceRpcResponse;
  62 +import org.thingsboard.server.common.msg.rpc.FromDeviceRpcResponse;
64 63 import org.thingsboard.server.service.rpc.FromDeviceRpcResponseActorMsg;
65 64
66 65 import java.util.UUID;
... ... @@ -105,19 +104,8 @@ public class DeviceEdgeProcessor extends BaseEdgeProcessor {
105 104 Device newDevice = createDevice(tenantId, edge, deviceUpdateMsg, newDeviceName);
106 105 ObjectNode body = mapper.createObjectNode();
107 106 body.put("conflictName", deviceName);
108   - ListenableFuture<EdgeEvent> future =
109   - saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE, EdgeEventActionType.ENTITY_MERGE_REQUEST, newDevice.getId(), body);
110   - Futures.addCallback(future, new FutureCallback<>() {
111   - @Override
112   - public void onSuccess(EdgeEvent edgeEvent) {
113   - saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE, EdgeEventActionType.CREDENTIALS_REQUEST, newDevice.getId(), null);
114   - }
115   -
116   - @Override
117   - public void onFailure(Throwable t) {
118   - log.error("[{}] Failed to save ENTITY_MERGE_REQUEST edge event [{}][{}]", tenantId, deviceUpdateMsg, edge.getId(), t);
119   - }
120   - }, dbCallbackExecutorService);
  107 + saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE, EdgeEventActionType.ENTITY_MERGE_REQUEST, newDevice.getId(), body);
  108 + saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE, EdgeEventActionType.CREDENTIALS_REQUEST, newDevice.getId(), null);
121 109 }
122 110 } while (pageData != null && pageData.hasNext());
123 111 } else {
... ... @@ -155,7 +143,9 @@ public class DeviceEdgeProcessor extends BaseEdgeProcessor {
155 143 DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(tenantId, device.getId());
156 144 deviceCredentials.setCredentialsType(DeviceCredentialsType.valueOf(deviceCredentialsUpdateMsg.getCredentialsType()));
157 145 deviceCredentials.setCredentialsId(deviceCredentialsUpdateMsg.getCredentialsId());
158   - deviceCredentials.setCredentialsValue(deviceCredentialsUpdateMsg.getCredentialsValue());
  146 + if (deviceCredentialsUpdateMsg.hasCredentialsValue()) {
  147 + deviceCredentials.setCredentialsValue(deviceCredentialsUpdateMsg.getCredentialsValue().getValue());
  148 + }
159 149 deviceCredentialsService.updateDeviceCredentials(tenantId, deviceCredentials);
160 150 } catch (Exception e) {
161 151 log.error("Can't update device credentials for device [{}], deviceCredentialsUpdateMsg [{}]", device.getName(), deviceCredentialsUpdateMsg, e);
... ... @@ -173,14 +163,20 @@ public class DeviceEdgeProcessor extends BaseEdgeProcessor {
173 163 if (device != null) {
174 164 device.setName(deviceUpdateMsg.getName());
175 165 device.setType(deviceUpdateMsg.getType());
176   - device.setLabel(deviceUpdateMsg.getLabel());
177   - device.setAdditionalInfo(JacksonUtil.toJsonNode(deviceUpdateMsg.getAdditionalInfo()));
178   - if (deviceUpdateMsg.getDeviceProfileIdMSB() != 0 && deviceUpdateMsg.getDeviceProfileIdLSB() != 0) {
  166 + if (deviceUpdateMsg.hasLabel()) {
  167 + device.setLabel(deviceUpdateMsg.getLabel().getValue());
  168 + }
  169 + if (deviceUpdateMsg.hasAdditionalInfo()) {
  170 + device.setAdditionalInfo(JacksonUtil.toJsonNode(deviceUpdateMsg.getAdditionalInfo().getValue()));
  171 + }
  172 + if (deviceUpdateMsg.hasDeviceProfileIdMSB() && deviceUpdateMsg.hasDeviceProfileIdLSB()) {
179 173 DeviceProfileId deviceProfileId = new DeviceProfileId(
180   - new UUID(deviceUpdateMsg.getDeviceProfileIdMSB(), deviceUpdateMsg.getDeviceProfileIdLSB()));
  174 + new UUID(deviceUpdateMsg.getDeviceProfileIdMSB().getValue(),
  175 + deviceUpdateMsg.getDeviceProfileIdLSB().getValue()));
181 176 device.setDeviceProfileId(deviceProfileId);
182 177 }
183   - deviceService.saveDevice(device);
  178 + Device savedDevice = deviceService.saveDevice(device);
  179 + tbClusterService.onDeviceUpdated(savedDevice, device);
184 180 saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE, EdgeEventActionType.CREDENTIALS_REQUEST, deviceId, null);
185 181 } else {
186 182 log.warn("[{}] can't find device [{}], edge [{}]", tenantId, deviceUpdateMsg, edge.getId());
... ... @@ -206,22 +202,26 @@ public class DeviceEdgeProcessor extends BaseEdgeProcessor {
206 202 device.setCustomerId(getCustomerId(edge));
207 203 device.setName(deviceName);
208 204 device.setType(deviceUpdateMsg.getType());
209   - device.setLabel(deviceUpdateMsg.getLabel());
210   - device.setAdditionalInfo(JacksonUtil.toJsonNode(deviceUpdateMsg.getAdditionalInfo()));
211   - if (deviceUpdateMsg.getDeviceProfileIdMSB() != 0 && deviceUpdateMsg.getDeviceProfileIdLSB() != 0) {
  205 + if (deviceUpdateMsg.hasLabel()) {
  206 + device.setLabel(deviceUpdateMsg.getLabel().getValue());
  207 + }
  208 + if (deviceUpdateMsg.hasAdditionalInfo()) {
  209 + device.setAdditionalInfo(JacksonUtil.toJsonNode(deviceUpdateMsg.getAdditionalInfo().getValue()));
  210 + }
  211 + if (deviceUpdateMsg.hasDeviceProfileIdMSB() && deviceUpdateMsg.hasDeviceProfileIdLSB()) {
212 212 DeviceProfileId deviceProfileId = new DeviceProfileId(
213   - new UUID(deviceUpdateMsg.getDeviceProfileIdMSB(), deviceUpdateMsg.getDeviceProfileIdLSB()));
  213 + new UUID(deviceUpdateMsg.getDeviceProfileIdMSB().getValue(),
  214 + deviceUpdateMsg.getDeviceProfileIdLSB().getValue()));
214 215 device.setDeviceProfileId(deviceProfileId);
215 216 }
216 217 Device savedDevice = deviceService.saveDevice(device, false);
  218 + tbClusterService.onDeviceUpdated(savedDevice, device);
217 219 if (created) {
218 220 DeviceCredentials deviceCredentials = new DeviceCredentials();
219 221 deviceCredentials.setDeviceId(new DeviceId(savedDevice.getUuidId()));
220 222 deviceCredentials.setCredentialsType(DeviceCredentialsType.ACCESS_TOKEN);
221 223 deviceCredentials.setCredentialsId(org.apache.commons.lang3.RandomStringUtils.randomAlphanumeric(20));
222 224 deviceCredentialsService.createDeviceCredentials(device.getTenantId(), deviceCredentials);
223   -
224   - deviceStateService.onDeviceAdded(savedDevice);
225 225 }
226 226 createRelationFromEdge(tenantId, edge.getId(), device.getId());
227 227 pushDeviceCreatedEventToRuleEngine(tenantId, edge, device);
... ...
... ... @@ -72,7 +72,9 @@ public class RelationEdgeProcessor extends BaseEdgeProcessor {
72 72 entityRelation.setTo(toId);
73 73
74 74 entityRelation.setType(relationUpdateMsg.getType());
75   - entityRelation.setTypeGroup(RelationTypeGroup.valueOf(relationUpdateMsg.getTypeGroup()));
  75 + if (relationUpdateMsg.hasTypeGroup()) {
  76 + entityRelation.setTypeGroup(RelationTypeGroup.valueOf(relationUpdateMsg.getTypeGroup().getValue()));
  77 + }
76 78 entityRelation.setAdditionalInfo(mapper.readTree(relationUpdateMsg.getAdditionalInfo()));
77 79 switch (relationUpdateMsg.getMsgType()) {
78 80 case ENTITY_CREATED_RPC_MESSAGE:
... ...
... ... @@ -73,7 +73,7 @@ import org.thingsboard.server.gen.edge.v1.UserCredentialsRequestMsg;
73 73 import org.thingsboard.server.gen.edge.v1.WidgetBundleTypesRequestMsg;
74 74 import org.thingsboard.server.service.edge.rpc.EdgeEventUtils;
75 75 import org.thingsboard.server.service.executors.DbCallbackExecutorService;
76   -import org.thingsboard.server.service.queue.TbClusterService;
  76 +import org.thingsboard.server.cluster.TbClusterService;
77 77
78 78 import java.util.ArrayList;
79 79 import java.util.HashMap;
... ... @@ -122,26 +122,13 @@ public class DefaultEdgeRequestsService implements EdgeRequestsService {
122 122 @Override
123 123 public ListenableFuture<Void> processRuleChainMetadataRequestMsg(TenantId tenantId, Edge edge, RuleChainMetadataRequestMsg ruleChainMetadataRequestMsg) {
124 124 log.trace("[{}] processRuleChainMetadataRequestMsg [{}][{}]", tenantId, edge.getName(), ruleChainMetadataRequestMsg);
125   - SettableFuture<Void> futureToSet = SettableFuture.create();
126 125 if (ruleChainMetadataRequestMsg.getRuleChainIdMSB() != 0 && ruleChainMetadataRequestMsg.getRuleChainIdLSB() != 0) {
127 126 RuleChainId ruleChainId =
128 127 new RuleChainId(new UUID(ruleChainMetadataRequestMsg.getRuleChainIdMSB(), ruleChainMetadataRequestMsg.getRuleChainIdLSB()));
129   - ListenableFuture<EdgeEvent> future = saveEdgeEvent(tenantId, edge.getId(),
  128 + saveEdgeEvent(tenantId, edge.getId(),
130 129 EdgeEventType.RULE_CHAIN_METADATA, EdgeEventActionType.ADDED, ruleChainId, null);
131   - Futures.addCallback(future, new FutureCallback<EdgeEvent>() {
132   - @Override
133   - public void onSuccess(@Nullable EdgeEvent result) {
134   - futureToSet.set(null);
135   - }
136   -
137   - @Override
138   - public void onFailure(Throwable t) {
139   - log.error("Can't save edge event [{}]", ruleChainMetadataRequestMsg, t);
140   - futureToSet.setException(t);
141   - }
142   - }, dbCallbackExecutorService);
143 130 }
144   - return futureToSet;
  131 + return Futures.immediateFuture(null);
145 132 }
146 133
147 134 @Override
... ... @@ -154,8 +141,8 @@ public class DefaultEdgeRequestsService implements EdgeRequestsService {
154 141 if (type != null) {
155 142 SettableFuture<Void> futureToSet = SettableFuture.create();
156 143 String scope = attributesRequestMsg.getScope();
157   - ListenableFuture<List<AttributeKvEntry>> ssAttrFuture = attributesService.findAll(tenantId, entityId, scope);
158   - Futures.addCallback(ssAttrFuture, new FutureCallback<List<AttributeKvEntry>>() {
  144 + ListenableFuture<List<AttributeKvEntry>> findAttrFuture = attributesService.findAll(tenantId, entityId, scope);
  145 + Futures.addCallback(findAttrFuture, new FutureCallback<List<AttributeKvEntry>>() {
159 146 @Override
160 147 public void onSuccess(@Nullable List<AttributeKvEntry> ssAttributes) {
161 148 if (ssAttributes != null && !ssAttributes.isEmpty()) {
... ... @@ -184,8 +171,9 @@ public class DefaultEdgeRequestsService implements EdgeRequestsService {
184 171 entityId,
185 172 body);
186 173 } catch (Exception e) {
187   - log.error("[{}] Failed to send attribute updates to the edge", edge.getName(), e);
188   - throw new RuntimeException("[" + edge.getName() + "] Failed to send attribute updates to the edge", e);
  174 + log.error("[{}] Failed to save attribute updates to the edge", edge.getName(), e);
  175 + futureToSet.setException(new RuntimeException("[" + edge.getName() + "] Failed to send attribute updates to the edge", e));
  176 + return;
189 177 }
190 178 } else {
191 179 log.trace("[{}][{}] No attributes found for entity {} [{}]", tenantId,
... ... @@ -198,7 +186,7 @@ public class DefaultEdgeRequestsService implements EdgeRequestsService {
198 186
199 187 @Override
200 188 public void onFailure(Throwable t) {
201   - log.error("Can't save attributes [{}]", attributesRequestMsg, t);
  189 + log.error("Can't find attributes [{}]", attributesRequestMsg, t);
202 190 futureToSet.setException(t);
203 191 }
204 192 }, dbCallbackExecutorService);
... ... @@ -273,82 +261,39 @@ public class DefaultEdgeRequestsService implements EdgeRequestsService {
273 261 @Override
274 262 public ListenableFuture<Void> processDeviceCredentialsRequestMsg(TenantId tenantId, Edge edge, DeviceCredentialsRequestMsg deviceCredentialsRequestMsg) {
275 263 log.trace("[{}] processDeviceCredentialsRequestMsg [{}][{}]", tenantId, edge.getName(), deviceCredentialsRequestMsg);
276   - SettableFuture<Void> futureToSet = SettableFuture.create();
277 264 if (deviceCredentialsRequestMsg.getDeviceIdMSB() != 0 && deviceCredentialsRequestMsg.getDeviceIdLSB() != 0) {
278 265 DeviceId deviceId = new DeviceId(new UUID(deviceCredentialsRequestMsg.getDeviceIdMSB(), deviceCredentialsRequestMsg.getDeviceIdLSB()));
279   - ListenableFuture<EdgeEvent> future = saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE,
  266 + saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE,
280 267 EdgeEventActionType.CREDENTIALS_UPDATED, deviceId, null);
281   - Futures.addCallback(future, new FutureCallback<EdgeEvent>() {
282   - @Override
283   - public void onSuccess(@Nullable EdgeEvent result) {
284   - futureToSet.set(null);
285   - }
286   -
287   - @Override
288   - public void onFailure(Throwable t) {
289   - log.error("Can't save edge event [{}]", deviceCredentialsRequestMsg, t);
290   - futureToSet.setException(t);
291   - }
292   - }, dbCallbackExecutorService);
293 268 }
294   - return futureToSet;
  269 + return Futures.immediateFuture(null);
295 270 }
296 271
297 272 @Override
298 273 public ListenableFuture<Void> processUserCredentialsRequestMsg(TenantId tenantId, Edge edge, UserCredentialsRequestMsg userCredentialsRequestMsg) {
299 274 log.trace("[{}] processUserCredentialsRequestMsg [{}][{}]", tenantId, edge.getName(), userCredentialsRequestMsg);
300   - SettableFuture<Void> futureToSet = SettableFuture.create();
301 275 if (userCredentialsRequestMsg.getUserIdMSB() != 0 && userCredentialsRequestMsg.getUserIdLSB() != 0) {
302 276 UserId userId = new UserId(new UUID(userCredentialsRequestMsg.getUserIdMSB(), userCredentialsRequestMsg.getUserIdLSB()));
303   - ListenableFuture<EdgeEvent> future = saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.USER,
  277 + saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.USER,
304 278 EdgeEventActionType.CREDENTIALS_UPDATED, userId, null);
305   - Futures.addCallback(future, new FutureCallback<>() {
306   - @Override
307   - public void onSuccess(@Nullable EdgeEvent result) {
308   - futureToSet.set(null);
309   - }
310   -
311   - @Override
312   - public void onFailure(Throwable t) {
313   - log.error("Can't save edge event [{}]", userCredentialsRequestMsg, t);
314   - futureToSet.setException(t);
315   - }
316   - }, dbCallbackExecutorService);
317 279 }
318   - return futureToSet;
  280 + return Futures.immediateFuture(null);
319 281 }
320 282
321 283 @Override
322 284 public ListenableFuture<Void> processDeviceProfileDevicesRequestMsg(TenantId tenantId, Edge edge, DeviceProfileDevicesRequestMsg deviceProfileDevicesRequestMsg) {
323 285 log.trace("[{}] processDeviceProfileDevicesRequestMsg [{}][{}]", tenantId, edge.getName(), deviceProfileDevicesRequestMsg);
324   - SettableFuture<Void> futureToSet = SettableFuture.create();
325 286 if (deviceProfileDevicesRequestMsg.getDeviceProfileIdMSB() != 0 && deviceProfileDevicesRequestMsg.getDeviceProfileIdLSB() != 0) {
326 287 DeviceProfileId deviceProfileId = new DeviceProfileId(new UUID(deviceProfileDevicesRequestMsg.getDeviceProfileIdMSB(), deviceProfileDevicesRequestMsg.getDeviceProfileIdLSB()));
327 288 DeviceProfile deviceProfileById = deviceProfileService.findDeviceProfileById(tenantId, deviceProfileId);
328   - List<ListenableFuture<EdgeEvent>> futures;
329 289 if (deviceProfileById != null) {
330   - futures = syncDevices(tenantId, edge, deviceProfileById.getName());
331   - } else {
332   - futures = new ArrayList<>();
  290 + syncDevices(tenantId, edge, deviceProfileById.getName());
333 291 }
334   - Futures.addCallback(Futures.allAsList(futures), new FutureCallback<>() {
335   - @Override
336   - public void onSuccess(@Nullable List<EdgeEvent> result) {
337   - futureToSet.set(null);
338   - }
339   -
340   - @Override
341   - public void onFailure(Throwable t) {
342   - log.error("Can't sync devices by device profile [{}]", deviceProfileDevicesRequestMsg, t);
343   - futureToSet.setException(t);
344   - }
345   - }, dbCallbackExecutorService);
346 292 }
347   - return futureToSet;
  293 + return Futures.immediateFuture(null);
348 294 }
349 295
350   - private List<ListenableFuture<EdgeEvent>> syncDevices(TenantId tenantId, Edge edge, String deviceType) {
351   - List<ListenableFuture<EdgeEvent>> futures = new ArrayList<>();
  296 + private void syncDevices(TenantId tenantId, Edge edge, String deviceType) {
352 297 log.trace("[{}] syncDevices [{}][{}]", tenantId, edge.getName(), deviceType);
353 298 try {
354 299 PageLink pageLink = new PageLink(DEFAULT_PAGE_SIZE);
... ... @@ -358,7 +303,7 @@ public class DefaultEdgeRequestsService implements EdgeRequestsService {
358 303 if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) {
359 304 log.trace("[{}] [{}] device(s) are going to be pushed to edge.", edge.getId(), pageData.getData().size());
360 305 for (Device device : pageData.getData()) {
361   - futures.add(saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE, EdgeEventActionType.ADDED, device.getId(), null));
  306 + saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE, EdgeEventActionType.ADDED, device.getId(), null);
362 307 }
363 308 if (pageData.hasNext()) {
364 309 pageLink = pageLink.nextPageLink();
... ... @@ -368,40 +313,25 @@ public class DefaultEdgeRequestsService implements EdgeRequestsService {
368 313 } catch (Exception e) {
369 314 log.error("Exception during loading edge device(s) on sync!", e);
370 315 }
371   - return futures;
372 316 }
373 317
374 318 @Override
375 319 public ListenableFuture<Void> processWidgetBundleTypesRequestMsg(TenantId tenantId, Edge edge,
376 320 WidgetBundleTypesRequestMsg widgetBundleTypesRequestMsg) {
377 321 log.trace("[{}] processWidgetBundleTypesRequestMsg [{}][{}]", tenantId, edge.getName(), widgetBundleTypesRequestMsg);
378   - SettableFuture<Void> futureToSet = SettableFuture.create();
379 322 if (widgetBundleTypesRequestMsg.getWidgetBundleIdMSB() != 0 && widgetBundleTypesRequestMsg.getWidgetBundleIdLSB() != 0) {
380 323 WidgetsBundleId widgetsBundleId = new WidgetsBundleId(new UUID(widgetBundleTypesRequestMsg.getWidgetBundleIdMSB(), widgetBundleTypesRequestMsg.getWidgetBundleIdLSB()));
381 324 WidgetsBundle widgetsBundleById = widgetsBundleService.findWidgetsBundleById(tenantId, widgetsBundleId);
382   - List<ListenableFuture<EdgeEvent>> futures = new ArrayList<>();
383 325 if (widgetsBundleById != null) {
384 326 List<WidgetType> widgetTypesToPush =
385 327 widgetTypeService.findWidgetTypesByTenantIdAndBundleAlias(widgetsBundleById.getTenantId(), widgetsBundleById.getAlias());
386 328
387 329 for (WidgetType widgetType : widgetTypesToPush) {
388   - futures.add(saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.WIDGET_TYPE, EdgeEventActionType.ADDED, widgetType.getId(), null));
  330 + saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.WIDGET_TYPE, EdgeEventActionType.ADDED, widgetType.getId(), null);
389 331 }
390 332 }
391   - Futures.addCallback(Futures.allAsList(futures), new FutureCallback<>() {
392   - @Override
393   - public void onSuccess(@Nullable List<EdgeEvent> result) {
394   - futureToSet.set(null);
395   - }
396   -
397   - @Override
398   - public void onFailure(Throwable t) {
399   - log.error("Can't sync widget types by widget bundle [{}]", widgetBundleTypesRequestMsg, t);
400   - futureToSet.setException(t);
401   - }
402   - }, dbCallbackExecutorService);
403 333 }
404   - return futureToSet;
  334 + return Futures.immediateFuture(null);
405 335 }
406 336
407 337 @Override
... ... @@ -416,9 +346,12 @@ public class DefaultEdgeRequestsService implements EdgeRequestsService {
416 346 public void onSuccess(@Nullable List<EntityView> entityViews) {
417 347 try {
418 348 if (entityViews != null && !entityViews.isEmpty()) {
  349 + List<ListenableFuture<Boolean>> futures = new ArrayList<>();
419 350 for (EntityView entityView : entityViews) {
420   - Futures.addCallback(relationService.checkRelation(tenantId, edge.getId(), entityView.getId(),
421   - EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE), new FutureCallback<>() {
  351 + ListenableFuture<Boolean> future = relationService.checkRelation(tenantId, edge.getId(), entityView.getId(),
  352 + EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE);
  353 + futures.add(future);
  354 + Futures.addCallback(future, new FutureCallback<>() {
422 355 @Override
423 356 public void onSuccess(@Nullable Boolean result) {
424 357 if (Boolean.TRUE.equals(result)) {
... ... @@ -426,16 +359,27 @@ public class DefaultEdgeRequestsService implements EdgeRequestsService {
426 359 EdgeEventActionType.ADDED, entityView.getId(), null);
427 360 }
428 361 }
429   -
430 362 @Override
431 363 public void onFailure(Throwable t) {
432   - log.error("Exception during loading relation [{}] to edge on sync!", t, t);
433   - futureToSet.setException(t);
  364 + // Do nothing - error handles in allAsList
434 365 }
435 366 }, dbCallbackExecutorService);
436 367 }
  368 + Futures.addCallback(Futures.allAsList(futures), new FutureCallback<>() {
  369 + @Override
  370 + public void onSuccess(@Nullable List<Boolean> result) {
  371 + futureToSet.set(null);
  372 + }
  373 +
  374 + @Override
  375 + public void onFailure(Throwable t) {
  376 + log.error("Exception during loading relation [{}] to edge on sync!", t, t);
  377 + futureToSet.setException(t);
  378 + }
  379 + }, dbCallbackExecutorService);
  380 + } else {
  381 + futureToSet.set(null);
437 382 }
438   - futureToSet.set(null);
439 383 } catch (Exception e) {
440 384 log.error("Exception during loading relation(s) to edge on sync!", e);
441 385 futureToSet.setException(e);
... ... @@ -451,30 +395,19 @@ public class DefaultEdgeRequestsService implements EdgeRequestsService {
451 395 return futureToSet;
452 396 }
453 397
454   - private ListenableFuture<EdgeEvent> saveEdgeEvent(TenantId tenantId,
455   - EdgeId edgeId,
456   - EdgeEventType type,
457   - EdgeEventActionType action,
458   - EntityId entityId,
459   - JsonNode body) {
  398 + private void saveEdgeEvent(TenantId tenantId,
  399 + EdgeId edgeId,
  400 + EdgeEventType type,
  401 + EdgeEventActionType action,
  402 + EntityId entityId,
  403 + JsonNode body) {
460 404 log.trace("Pushing edge event to edge queue. tenantId [{}], edgeId [{}], type [{}], action[{}], entityId [{}], body [{}]",
461 405 tenantId, edgeId, type, action, entityId, body);
462 406
463 407 EdgeEvent edgeEvent = EdgeEventUtils.constructEdgeEvent(tenantId, edgeId, type, action, entityId, body);
464 408
465   - ListenableFuture<EdgeEvent> future = edgeEventService.saveAsync(edgeEvent);
466   - Futures.addCallback(future, new FutureCallback<>() {
467   - @Override
468   - public void onSuccess(@Nullable EdgeEvent result) {
469   - tbClusterService.onEdgeEventUpdate(tenantId, edgeId);
470   - }
471   -
472   - @Override
473   - public void onFailure(Throwable t) {
474   - log.warn("[{}] Can't save edge event [{}] for edge [{}]", tenantId.getId(), edgeEvent, edgeId.getId(), t);
475   - }
476   - }, dbCallbackExecutorService);
477   - return future;
  409 + edgeEventService.save(edgeEvent);
  410 + tbClusterService.onEdgeEventUpdate(tenantId, edgeId);
478 411 }
479 412
480 413 }
... ...
... ... @@ -215,6 +215,7 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
215 215 node.put("password", "");
216 216 node.put("tlsVersion", "TLSv1.2");//NOSONAR, key used to identify password field (not password value itself)
217 217 node.put("enableProxy", false);
  218 + node.put("showChangePassword", false);
218 219 mailSettings.setJsonValue(node);
219 220 adminSettingsService.saveAdminSettings(TenantId.SYS_TENANT_ID, mailSettings);
220 221 }
... ... @@ -480,6 +481,7 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
480 481 device.setAdditionalInfo(additionalInfo);
481 482 }
482 483 device = deviceService.saveDevice(device);
  484 + //TODO: No access to cluster service, so we should manually update the status of device.
483 485 DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(TenantId.SYS_TENANT_ID, device.getId());
484 486 deviceCredentials.setCredentialsId(accessToken);
485 487 deviceCredentialsService.updateDeviceCredentials(TenantId.SYS_TENANT_ID, deviceCredentials);
... ...
... ... @@ -17,6 +17,7 @@ package org.thingsboard.server.service.ota;
17 17
18 18 import com.google.common.util.concurrent.FutureCallback;
19 19 import lombok.extern.slf4j.Slf4j;
  20 +import org.springframework.context.annotation.Lazy;
20 21 import org.springframework.stereotype.Service;
21 22 import org.thingsboard.rule.engine.api.RuleEngineTelemetryService;
22 23 import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg;
... ... @@ -49,7 +50,7 @@ import org.thingsboard.server.queue.TbQueueProducer;
49 50 import org.thingsboard.server.queue.common.TbProtoQueueMsg;
50 51 import org.thingsboard.server.queue.provider.TbCoreQueueFactory;
51 52 import org.thingsboard.server.queue.util.TbCoreComponent;
52   -import org.thingsboard.server.service.queue.TbClusterService;
  53 +import org.thingsboard.server.cluster.TbClusterService;
53 54
54 55 import javax.annotation.Nullable;
55 56 import java.util.ArrayList;
... ... @@ -87,10 +88,11 @@ public class DefaultOtaPackageStateService implements OtaPackageStateService {
87 88 private final RuleEngineTelemetryService telemetryService;
88 89 private final TbQueueProducer<TbProtoQueueMsg<ToOtaPackageStateServiceMsg>> otaPackageStateMsgProducer;
89 90
90   - public DefaultOtaPackageStateService(TbClusterService tbClusterService, OtaPackageService otaPackageService,
  91 + public DefaultOtaPackageStateService(@Lazy TbClusterService tbClusterService,
  92 + OtaPackageService otaPackageService,
91 93 DeviceService deviceService,
92 94 DeviceProfileService deviceProfileService,
93   - RuleEngineTelemetryService telemetryService,
  95 + @Lazy RuleEngineTelemetryService telemetryService,
94 96 TbCoreQueueFactory coreQueueFactory) {
95 97 this.tbClusterService = tbClusterService;
96 98 this.otaPackageService = otaPackageService;
... ...
... ... @@ -16,11 +16,17 @@
16 16 package org.thingsboard.server.service.queue;
17 17
18 18 import com.google.protobuf.ByteString;
  19 +import lombok.RequiredArgsConstructor;
19 20 import lombok.extern.slf4j.Slf4j;
20 21 import org.springframework.beans.factory.annotation.Value;
21 22 import org.springframework.scheduling.annotation.Scheduled;
22 23 import org.springframework.stereotype.Service;
23   -import org.thingsboard.rule.engine.api.msg.ToDeviceActorNotificationMsg;
  24 +import org.thingsboard.rule.engine.api.msg.DeviceNameOrTypeUpdateMsg;
  25 +import org.thingsboard.server.cluster.TbClusterService;
  26 +import org.thingsboard.server.common.data.EdgeUtils;
  27 +import org.thingsboard.server.common.data.edge.EdgeEventActionType;
  28 +import org.thingsboard.server.common.data.edge.EdgeEventType;
  29 +import org.thingsboard.server.common.msg.ToDeviceActorNotificationMsg;
24 30 import org.thingsboard.server.common.data.ApiUsageState;
25 31 import org.thingsboard.server.common.data.Device;
26 32 import org.thingsboard.server.common.data.DeviceProfile;
... ... @@ -29,7 +35,6 @@ import org.thingsboard.server.common.data.HasName;
29 35 import org.thingsboard.server.common.data.TbResource;
30 36 import org.thingsboard.server.common.data.Tenant;
31 37 import org.thingsboard.server.common.data.TenantProfile;
32   -import org.thingsboard.server.common.data.id.CustomerId;
33 38 import org.thingsboard.server.common.data.id.DeviceId;
34 39 import org.thingsboard.server.common.data.id.DeviceProfileId;
35 40 import org.thingsboard.server.common.data.id.EdgeId;
... ... @@ -56,8 +61,9 @@ import org.thingsboard.server.queue.common.MultipleTbQueueCallbackWrapper;
56 61 import org.thingsboard.server.queue.common.TbProtoQueueMsg;
57 62 import org.thingsboard.server.queue.discovery.PartitionService;
58 63 import org.thingsboard.server.queue.provider.TbQueueProducerProvider;
  64 +import org.thingsboard.server.service.ota.OtaPackageStateService;
59 65 import org.thingsboard.server.service.profile.TbDeviceProfileCache;
60   -import org.thingsboard.server.service.rpc.FromDeviceRpcResponse;
  66 +import org.thingsboard.server.common.msg.rpc.FromDeviceRpcResponse;
61 67
62 68 import java.util.HashSet;
63 69 import java.util.Set;
... ... @@ -66,10 +72,13 @@ import java.util.concurrent.atomic.AtomicInteger;
66 72
67 73 @Service
68 74 @Slf4j
  75 +@RequiredArgsConstructor
69 76 public class DefaultTbClusterService implements TbClusterService {
70 77
71 78 @Value("${cluster.stats.enabled:false}")
72 79 private boolean statsEnabled;
  80 + @Value("${edges.enabled}")
  81 + protected boolean edgesEnabled;
73 82
74 83 private final AtomicInteger toCoreMsgs = new AtomicInteger(0);
75 84 private final AtomicInteger toCoreNfs = new AtomicInteger(0);
... ... @@ -81,13 +90,7 @@ public class DefaultTbClusterService implements TbClusterService {
81 90 private final PartitionService partitionService;
82 91 private final DataDecodingEncodingService encodingService;
83 92 private final TbDeviceProfileCache deviceProfileCache;
84   -
85   - public DefaultTbClusterService(TbQueueProducerProvider producerProvider, PartitionService partitionService, DataDecodingEncodingService encodingService, TbDeviceProfileCache deviceProfileCache) {
86   - this.producerProvider = producerProvider;
87   - this.partitionService = partitionService;
88   - this.encodingService = encodingService;
89   - this.deviceProfileCache = deviceProfileCache;
90   - }
  93 + private final OtaPackageStateService otaPackageStateService;
91 94
92 95 @Override
93 96 public void pushMsgToCore(TenantId tenantId, EntityId entityId, ToCoreMsg msg, TbQueueCallback callback) {
... ... @@ -200,55 +203,52 @@ public class DefaultTbClusterService implements TbClusterService {
200 203 }
201 204
202 205 @Override
203   - public void onEntityStateChange(TenantId tenantId, EntityId entityId, ComponentLifecycleEvent state) {
  206 + public void broadcastEntityStateChangeEvent(TenantId tenantId, EntityId entityId, ComponentLifecycleEvent state) {
204 207 log.trace("[{}] Processing {} state change event: {}", tenantId, entityId.getEntityType(), state);
205 208 broadcast(new ComponentLifecycleMsg(tenantId, entityId, state));
206 209 }
207 210
208 211 @Override
209 212 public void onDeviceProfileChange(DeviceProfile deviceProfile, TbQueueCallback callback) {
210   - onEntityChange(deviceProfile.getTenantId(), deviceProfile.getId(), deviceProfile, callback);
  213 + broadcastEntityChangeToTransport(deviceProfile.getTenantId(), deviceProfile.getId(), deviceProfile, callback);
211 214 }
212 215
213 216 @Override
214 217 public void onTenantProfileChange(TenantProfile tenantProfile, TbQueueCallback callback) {
215   - onEntityChange(TenantId.SYS_TENANT_ID, tenantProfile.getId(), tenantProfile, callback);
  218 + broadcastEntityChangeToTransport(TenantId.SYS_TENANT_ID, tenantProfile.getId(), tenantProfile, callback);
216 219 }
217 220
218 221 @Override
219 222 public void onTenantChange(Tenant tenant, TbQueueCallback callback) {
220   - onEntityChange(TenantId.SYS_TENANT_ID, tenant.getId(), tenant, callback);
  223 + broadcastEntityChangeToTransport(TenantId.SYS_TENANT_ID, tenant.getId(), tenant, callback);
221 224 }
222 225
223 226 @Override
224 227 public void onApiStateChange(ApiUsageState apiUsageState, TbQueueCallback callback) {
225   - onEntityChange(apiUsageState.getTenantId(), apiUsageState.getId(), apiUsageState, callback);
  228 + broadcastEntityChangeToTransport(apiUsageState.getTenantId(), apiUsageState.getId(), apiUsageState, callback);
226 229 broadcast(new ComponentLifecycleMsg(apiUsageState.getTenantId(), apiUsageState.getId(), ComponentLifecycleEvent.UPDATED));
227 230 }
228 231
229 232 @Override
230 233 public void onDeviceProfileDelete(DeviceProfile entity, TbQueueCallback callback) {
231   - onEntityDelete(entity.getTenantId(), entity.getId(), entity.getName(), callback);
  234 + broadcastEntityDeleteToTransport(entity.getTenantId(), entity.getId(), entity.getName(), callback);
232 235 }
233 236
234 237 @Override
235 238 public void onTenantProfileDelete(TenantProfile entity, TbQueueCallback callback) {
236   - onEntityDelete(TenantId.SYS_TENANT_ID, entity.getId(), entity.getName(), callback);
  239 + broadcastEntityDeleteToTransport(TenantId.SYS_TENANT_ID, entity.getId(), entity.getName(), callback);
237 240 }
238 241
239 242 @Override
240 243 public void onTenantDelete(Tenant entity, TbQueueCallback callback) {
241   - onEntityDelete(TenantId.SYS_TENANT_ID, entity.getId(), entity.getName(), callback);
  244 + broadcastEntityDeleteToTransport(TenantId.SYS_TENANT_ID, entity.getId(), entity.getName(), callback);
242 245 }
243 246
244 247 @Override
245   - public void onDeviceChange(Device entity, TbQueueCallback callback) {
246   - onEntityChange(entity.getTenantId(), entity.getId(), entity, callback);
247   - }
248   -
249   - @Override
250   - public void onDeviceDeleted(Device entity, TbQueueCallback callback) {
251   - onEntityDelete(entity.getTenantId(), entity.getId(), entity.getName(), callback);
  248 + public void onDeviceDeleted(Device device, TbQueueCallback callback) {
  249 + broadcastEntityDeleteToTransport(device.getTenantId(), device.getId(), device.getName(), callback);
  250 + sendDeviceStateServiceEvent(device.getTenantId(), device.getId(), false, false, true);
  251 + broadcastEntityStateChangeEvent(device.getTenantId(), device.getId(), ComponentLifecycleEvent.DELETED);
252 252 }
253 253
254 254 @Override
... ... @@ -278,7 +278,7 @@ public class DefaultTbClusterService implements TbClusterService {
278 278 broadcast(transportMsg, callback);
279 279 }
280 280
281   - public <T> void onEntityChange(TenantId tenantId, EntityId entityid, T entity, TbQueueCallback callback) {
  281 + public <T> void broadcastEntityChangeToTransport(TenantId tenantId, EntityId entityid, T entity, TbQueueCallback callback) {
282 282 String entityName = (entity instanceof HasName) ? ((HasName) entity).getName() : entity.getClass().getName();
283 283 log.trace("[{}][{}][{}] Processing [{}] change event", tenantId, entityid.getEntityType(), entityid.getId(), entityName);
284 284 TransportProtos.EntityUpdateMsg entityUpdateMsg = TransportProtos.EntityUpdateMsg.newBuilder()
... ... @@ -288,7 +288,7 @@ public class DefaultTbClusterService implements TbClusterService {
288 288 broadcast(transportMsg, callback);
289 289 }
290 290
291   - private void onEntityDelete(TenantId tenantId, EntityId entityId, String name, TbQueueCallback callback) {
  291 + private void broadcastEntityDeleteToTransport(TenantId tenantId, EntityId entityId, String name, TbQueueCallback callback) {
292 292 log.trace("[{}][{}][{}] Processing [{}] delete event", tenantId, entityId.getEntityType(), entityId.getId(), name);
293 293 TransportProtos.EntityDeleteMsg entityDeleteMsg = TransportProtos.EntityDeleteMsg.newBuilder()
294 294 .setEntityType(entityId.getEntityType().name())
... ... @@ -369,4 +369,72 @@ public class DefaultTbClusterService implements TbClusterService {
369 369 }
370 370 }
371 371 }
  372 +
  373 + private void sendDeviceStateServiceEvent(TenantId tenantId, DeviceId deviceId, boolean added, boolean updated, boolean deleted) {
  374 + TransportProtos.DeviceStateServiceMsgProto.Builder builder = TransportProtos.DeviceStateServiceMsgProto.newBuilder();
  375 + builder.setTenantIdMSB(tenantId.getId().getMostSignificantBits());
  376 + builder.setTenantIdLSB(tenantId.getId().getLeastSignificantBits());
  377 + builder.setDeviceIdMSB(deviceId.getId().getMostSignificantBits());
  378 + builder.setDeviceIdLSB(deviceId.getId().getLeastSignificantBits());
  379 + builder.setAdded(added);
  380 + builder.setUpdated(updated);
  381 + builder.setDeleted(deleted);
  382 + TransportProtos.DeviceStateServiceMsgProto msg = builder.build();
  383 + pushMsgToCore(tenantId, deviceId, TransportProtos.ToCoreMsg.newBuilder().setDeviceStateServiceMsg(msg).build(), null);
  384 + }
  385 +
  386 + @Override
  387 + public void onDeviceUpdated(Device device, Device old) {
  388 + var created = old == null;
  389 + broadcastEntityChangeToTransport(device.getTenantId(), device.getId(), device, null);
  390 + if (old != null && (!device.getName().equals(old.getName()) || !device.getType().equals(old.getType()))) {
  391 + pushMsgToCore(new DeviceNameOrTypeUpdateMsg(device.getTenantId(), device.getId(), device.getName(), device.getType()), null);
  392 + }
  393 + broadcastEntityStateChangeEvent(device.getTenantId(), device.getId(), created ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED);
  394 + sendDeviceStateServiceEvent(device.getTenantId(), device.getId(), created, !created, false);
  395 + otaPackageStateService.update(device, old);
  396 + if (!created) {
  397 + sendNotificationMsgToEdgeService(device.getTenantId(), null, device.getId(), null, null, EdgeEventActionType.UPDATED);
  398 + }
  399 + }
  400 +
  401 + @Override
  402 + public void sendNotificationMsgToEdgeService(TenantId tenantId, EdgeId edgeId, EntityId entityId, String body, EdgeEventType type, EdgeEventActionType action) {
  403 + if (!edgesEnabled) {
  404 + return;
  405 + }
  406 + if (type == null) {
  407 + if (entityId != null) {
  408 + type = EdgeUtils.getEdgeEventTypeByEntityType(entityId.getEntityType());
  409 + } else {
  410 + log.trace("[{}] entity id and type are null. Ignoring this notification", tenantId);
  411 + return;
  412 + }
  413 + if (type == null) {
  414 + log.trace("[{}] edge event type is null. Ignoring this notification [{}]", tenantId, entityId);
  415 + return;
  416 + }
  417 + }
  418 + TransportProtos.EdgeNotificationMsgProto.Builder builder = TransportProtos.EdgeNotificationMsgProto.newBuilder();
  419 + builder.setTenantIdMSB(tenantId.getId().getMostSignificantBits());
  420 + builder.setTenantIdLSB(tenantId.getId().getLeastSignificantBits());
  421 + builder.setType(type.name());
  422 + builder.setAction(action.name());
  423 + if (entityId != null) {
  424 + builder.setEntityIdMSB(entityId.getId().getMostSignificantBits());
  425 + builder.setEntityIdLSB(entityId.getId().getLeastSignificantBits());
  426 + builder.setEntityType(entityId.getEntityType().name());
  427 + }
  428 + if (edgeId != null) {
  429 + builder.setEdgeIdMSB(edgeId.getId().getMostSignificantBits());
  430 + builder.setEdgeIdLSB(edgeId.getId().getLeastSignificantBits());
  431 + }
  432 + if (body != null) {
  433 + builder.setBody(body);
  434 + }
  435 + TransportProtos.EdgeNotificationMsgProto msg = builder.build();
  436 + log.trace("[{}] sending notification to edge service {}", tenantId.getId(), msg);
  437 + pushMsgToCore(tenantId, entityId != null ? entityId : tenantId, TransportProtos.ToCoreMsg.newBuilder().setEdgeNotificationMsg(msg).build(), null);
  438 + }
  439 +
372 440 }
... ...
... ... @@ -26,7 +26,7 @@ import org.springframework.scheduling.annotation.Scheduled;
26 26 import org.springframework.stereotype.Service;
27 27 import org.thingsboard.common.util.JacksonUtil;
28 28 import org.thingsboard.common.util.ThingsBoardThreadFactory;
29   -import org.thingsboard.rule.engine.api.RpcError;
  29 +import org.thingsboard.server.common.data.rpc.RpcError;
30 30 import org.thingsboard.server.actors.ActorSystemContext;
31 31 import org.thingsboard.server.common.data.alarm.Alarm;
32 32 import org.thingsboard.server.common.data.id.TenantId;
... ... @@ -64,7 +64,7 @@ import org.thingsboard.server.service.ota.OtaPackageStateService;
64 64 import org.thingsboard.server.service.profile.TbDeviceProfileCache;
65 65 import org.thingsboard.server.service.queue.processing.AbstractConsumerService;
66 66 import org.thingsboard.server.service.queue.processing.IdMsgPair;
67   -import org.thingsboard.server.service.rpc.FromDeviceRpcResponse;
  67 +import org.thingsboard.server.common.msg.rpc.FromDeviceRpcResponse;
68 68 import org.thingsboard.server.service.rpc.TbCoreDeviceRpcService;
69 69 import org.thingsboard.server.service.rpc.ToDeviceRpcRequestActorMsg;
70 70 import org.thingsboard.server.service.state.DeviceStateService;
... ...
... ... @@ -21,7 +21,7 @@ import org.springframework.beans.factory.annotation.Value;
21 21 import org.springframework.scheduling.annotation.Scheduled;
22 22 import org.springframework.stereotype.Service;
23 23 import org.thingsboard.common.util.ThingsBoardThreadFactory;
24   -import org.thingsboard.rule.engine.api.RpcError;
  24 +import org.thingsboard.server.common.data.rpc.RpcError;
25 25 import org.thingsboard.server.actors.ActorSystemContext;
26 26 import org.thingsboard.server.common.data.id.TenantId;
27 27 import org.thingsboard.server.common.msg.TbMsg;
... ... @@ -55,7 +55,7 @@ import org.thingsboard.server.service.queue.processing.TbRuleEngineProcessingStr
55 55 import org.thingsboard.server.service.queue.processing.TbRuleEngineProcessingStrategyFactory;
56 56 import org.thingsboard.server.service.queue.processing.TbRuleEngineSubmitStrategy;
57 57 import org.thingsboard.server.service.queue.processing.TbRuleEngineSubmitStrategyFactory;
58   -import org.thingsboard.server.service.rpc.FromDeviceRpcResponse;
  58 +import org.thingsboard.server.common.msg.rpc.FromDeviceRpcResponse;
59 59 import org.thingsboard.server.service.rpc.TbRuleEngineDeviceRpcService;
60 60 import org.thingsboard.server.service.stats.RuleEngineStatisticsService;
61 61
... ...
... ... @@ -22,18 +22,19 @@ import lombok.extern.slf4j.Slf4j;
22 22 import org.springframework.beans.factory.annotation.Autowired;
23 23 import org.springframework.stereotype.Service;
24 24 import org.thingsboard.common.util.ThingsBoardThreadFactory;
25   -import org.thingsboard.rule.engine.api.RpcError;
  25 +import org.thingsboard.server.common.data.rpc.RpcError;
26 26 import org.thingsboard.server.actors.ActorSystemContext;
27 27 import org.thingsboard.server.common.data.DataConstants;
28 28 import org.thingsboard.server.common.data.Device;
29 29 import org.thingsboard.server.common.msg.TbMsg;
30 30 import org.thingsboard.server.common.msg.TbMsgDataType;
31 31 import org.thingsboard.server.common.msg.TbMsgMetaData;
  32 +import org.thingsboard.server.common.msg.rpc.FromDeviceRpcResponse;
32 33 import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest;
33 34 import org.thingsboard.server.dao.device.DeviceService;
34 35 import org.thingsboard.server.queue.discovery.TbServiceInfoProvider;
35 36 import org.thingsboard.server.queue.util.TbCoreComponent;
36   -import org.thingsboard.server.service.queue.TbClusterService;
  37 +import org.thingsboard.server.cluster.TbClusterService;
37 38 import org.thingsboard.server.service.security.model.SecurityUser;
38 39
39 40 import javax.annotation.PostConstruct;
... ... @@ -138,6 +139,12 @@ public class DefaultTbCoreDeviceRpcService implements TbCoreDeviceRpcService {
138 139 }
139 140 }
140 141
  142 + @Override
  143 + public void processRemoveRpc(RemoveRpcActorMsg removeRpcMsg) {
  144 + log.trace("[{}][{}] Processing remove RPC [{}]", removeRpcMsg.getTenantId(), removeRpcMsg.getRequestId(), removeRpcMsg.getDeviceId());
  145 + actorContext.tellWithHighPriority(removeRpcMsg);
  146 + }
  147 +
141 148 private void sendRpcResponseToTbRuleEngine(String originServiceId, FromDeviceRpcResponse response) {
142 149 if (serviceId.equals(originServiceId)) {
143 150 if (tbRuleEngineRpcService.isPresent()) {
... ... @@ -168,6 +175,8 @@ public class DefaultTbCoreDeviceRpcService implements TbCoreDeviceRpcService {
168 175 entityNode.put("method", msg.getBody().getMethod());
169 176 entityNode.put("params", msg.getBody().getParams());
170 177
  178 + entityNode.put(DataConstants.ADDITIONAL_INFO, msg.getAdditionalInfo());
  179 +
171 180 try {
172 181 TbMsg tbMsg = TbMsg.newMsg(DataConstants.RPC_CALL_FROM_SERVER_TO_DEVICE, msg.getDeviceId(), currentUser.getCustomerId(), metaData, TbMsgDataType.JSON, json.writeValueAsString(entityNode));
173 182 clusterService.pushMsgToRuleEngine(msg.getTenantId(), msg.getDeviceId(), tbMsg, null);
... ...
... ... @@ -19,18 +19,19 @@ import lombok.extern.slf4j.Slf4j;
19 19 import org.springframework.beans.factory.annotation.Autowired;
20 20 import org.springframework.stereotype.Service;
21 21 import org.thingsboard.common.util.ThingsBoardThreadFactory;
22   -import org.thingsboard.rule.engine.api.RpcError;
  22 +import org.thingsboard.server.common.data.rpc.RpcError;
23 23 import org.thingsboard.rule.engine.api.RuleEngineDeviceRpcRequest;
24 24 import org.thingsboard.rule.engine.api.RuleEngineDeviceRpcResponse;
25 25 import org.thingsboard.server.common.data.rpc.ToDeviceRpcRequestBody;
26 26 import org.thingsboard.server.common.msg.queue.ServiceType;
27 27 import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
  28 +import org.thingsboard.server.common.msg.rpc.FromDeviceRpcResponse;
28 29 import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest;
29 30 import org.thingsboard.server.gen.transport.TransportProtos;
30 31 import org.thingsboard.server.queue.discovery.PartitionService;
31 32 import org.thingsboard.server.queue.discovery.TbServiceInfoProvider;
32 33 import org.thingsboard.server.queue.util.TbRuleEngineComponent;
33   -import org.thingsboard.server.service.queue.TbClusterService;
  34 +import org.thingsboard.server.cluster.TbClusterService;
34 35
35 36 import javax.annotation.PostConstruct;
36 37 import javax.annotation.PreDestroy;
... ... @@ -100,7 +101,7 @@ public class DefaultTbRuleEngineRpcService implements TbRuleEngineDeviceRpcServi
100 101 @Override
101 102 public void sendRpcRequestToDevice(RuleEngineDeviceRpcRequest src, Consumer<RuleEngineDeviceRpcResponse> consumer) {
102 103 ToDeviceRpcRequest request = new ToDeviceRpcRequest(src.getRequestUUID(), src.getTenantId(), src.getDeviceId(),
103   - src.isOneway(), src.getExpirationTime(), new ToDeviceRpcRequestBody(src.getMethod(), src.getBody()), src.isPersisted());
  104 + src.isOneway(), src.getExpirationTime(), new ToDeviceRpcRequestBody(src.getMethod(), src.getBody()), src.isPersisted(), src.getAdditionalInfo());
104 105 forwardRpcRequestToDeviceActor(request, response -> {
105 106 if (src.isRestApiCall()) {
106 107 sendRpcResponseToTbCore(src.getOriginServiceId(), response);
... ...
... ... @@ -18,10 +18,11 @@ package org.thingsboard.server.service.rpc;
18 18 import lombok.Getter;
19 19 import lombok.RequiredArgsConstructor;
20 20 import lombok.ToString;
21   -import org.thingsboard.rule.engine.api.msg.ToDeviceActorNotificationMsg;
  21 +import org.thingsboard.server.common.msg.ToDeviceActorNotificationMsg;
22 22 import org.thingsboard.server.common.data.id.DeviceId;
23 23 import org.thingsboard.server.common.data.id.TenantId;
24 24 import org.thingsboard.server.common.msg.MsgType;
  25 +import org.thingsboard.server.common.msg.rpc.FromDeviceRpcResponse;
25 26
26 27 @ToString
27 28 @RequiredArgsConstructor
... ...
  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.rpc;
  17 +
  18 +import lombok.Getter;
  19 +import lombok.RequiredArgsConstructor;
  20 +import lombok.ToString;
  21 +import org.thingsboard.server.common.data.id.DeviceId;
  22 +import org.thingsboard.server.common.data.id.TenantId;
  23 +import org.thingsboard.server.common.msg.MsgType;
  24 +import org.thingsboard.server.common.msg.ToDeviceActorNotificationMsg;
  25 +
  26 +import java.util.UUID;
  27 +
  28 +@ToString
  29 +@RequiredArgsConstructor
  30 +public class RemoveRpcActorMsg implements ToDeviceActorNotificationMsg {
  31 +
  32 + @Getter
  33 + private final TenantId tenantId;
  34 + @Getter
  35 + private final DeviceId deviceId;
  36 +
  37 + @Getter
  38 + private final UUID requestId;
  39 +
  40 + @Override
  41 + public MsgType getMsgType() {
  42 + return MsgType.REMOVE_RPC_TO_DEVICE_ACTOR_MSG;
  43 + }
  44 +}
... ...
... ... @@ -15,6 +15,7 @@
15 15 */
16 16 package org.thingsboard.server.service.rpc;
17 17
  18 +import org.thingsboard.server.common.msg.rpc.FromDeviceRpcResponse;
18 19 import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest;
19 20 import org.thingsboard.server.service.security.model.SecurityUser;
20 21
... ... @@ -55,4 +56,6 @@ public interface TbCoreDeviceRpcService {
55 56 */
56 57 void processRpcResponseFromDeviceActor(FromDeviceRpcResponse response);
57 58
  59 + void processRemoveRpc(RemoveRpcActorMsg removeRpcMsg);
  60 +
58 61 }
... ...
... ... @@ -31,7 +31,7 @@ import org.thingsboard.server.common.msg.TbMsg;
31 31 import org.thingsboard.server.common.msg.TbMsgMetaData;
32 32 import org.thingsboard.server.dao.rpc.RpcService;
33 33 import org.thingsboard.server.queue.util.TbCoreComponent;
34   -import org.thingsboard.server.service.queue.TbClusterService;
  34 +import org.thingsboard.server.cluster.TbClusterService;
35 35
36 36 @TbCoreComponent
37 37 @Service
... ...
... ... @@ -16,6 +16,7 @@
16 16 package org.thingsboard.server.service.rpc;
17 17
18 18 import org.thingsboard.rule.engine.api.RuleEngineRpcService;
  19 +import org.thingsboard.server.common.msg.rpc.FromDeviceRpcResponse;
19 20
20 21 /**
21 22 * Created by ashvayka on 16.04.18.
... ...
... ... @@ -18,7 +18,7 @@ package org.thingsboard.server.service.rpc;
18 18 import lombok.Getter;
19 19 import lombok.RequiredArgsConstructor;
20 20 import lombok.ToString;
21   -import org.thingsboard.rule.engine.api.msg.ToDeviceActorNotificationMsg;
  21 +import org.thingsboard.server.common.msg.ToDeviceActorNotificationMsg;
22 22 import org.thingsboard.server.common.data.id.DeviceId;
23 23 import org.thingsboard.server.common.data.id.TenantId;
24 24 import org.thingsboard.server.common.msg.MsgType;
... ...
... ... @@ -47,7 +47,7 @@ import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
47 47 import org.thingsboard.server.dao.tenant.TenantService;
48 48 import org.thingsboard.server.dao.user.UserService;
49 49 import org.thingsboard.server.service.install.InstallScripts;
50   -import org.thingsboard.server.service.queue.TbClusterService;
  50 +import org.thingsboard.server.cluster.TbClusterService;
51 51 import org.thingsboard.server.service.security.model.SecurityUser;
52 52 import org.thingsboard.server.service.security.model.UserPrincipal;
53 53
... ... @@ -180,7 +180,7 @@ public abstract class AbstractOAuth2ClientMapper {
180 180 installScripts.createDefaultEdgeRuleChains(tenant.getId());
181 181 tenantProfileCache.evict(tenant.getId());
182 182 tbClusterService.onTenantChange(tenant, null);
183   - tbClusterService.onEntityStateChange(tenant.getId(), tenant.getId(),
  183 + tbClusterService.broadcastEntityStateChangeEvent(tenant.getId(), tenant.getId(),
184 184 ComponentLifecycleEvent.CREATED);
185 185 } else {
186 186 tenant = tenants.get(0);
... ...
... ... @@ -17,6 +17,7 @@ package org.thingsboard.server.service.security.auth.oauth2;
17 17
18 18 import com.fasterxml.jackson.core.JsonProcessingException;
19 19 import com.fasterxml.jackson.databind.ObjectMapper;
  20 +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
20 21 import lombok.extern.slf4j.Slf4j;
21 22 import org.springframework.boot.web.client.RestTemplateBuilder;
22 23 import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
... ... @@ -29,6 +30,7 @@ import org.thingsboard.server.common.data.oauth2.OAuth2Registration;
29 30 import org.thingsboard.server.dao.oauth2.OAuth2User;
30 31 import org.thingsboard.server.service.security.model.SecurityUser;
31 32
  33 +import javax.annotation.PostConstruct;
32 34 import javax.servlet.http.HttpServletRequest;
33 35
34 36 @Service(value = "customOAuth2ClientMapper")
... ... @@ -40,6 +42,15 @@ public class CustomOAuth2ClientMapper extends AbstractOAuth2ClientMapper impleme
40 42
41 43 private RestTemplateBuilder restTemplateBuilder = new RestTemplateBuilder();
42 44
  45 + @PostConstruct
  46 + public void init() {
  47 + // Register time module to parse Instant objects.
  48 + // com.fasterxml.jackson.databind.exc.InvalidDefinitionException:
  49 + // Java 8 date/time type `java.time.Instant` not supported by default:
  50 + // add Module "com.fasterxml.jackson.datatype:jackson-datatype-jsr310" to enable handling
  51 + json.registerModule(new JavaTimeModule());
  52 + }
  53 +
43 54 @Override
44 55 public SecurityUser getOrCreateUserByClientPrincipal(HttpServletRequest request, OAuth2AuthenticationToken token, String providerAccessToken, OAuth2Registration registration) {
45 56 OAuth2MapperConfig config = registration.getMapperConfig();
... ...
... ... @@ -15,6 +15,7 @@
15 15 */
16 16 package org.thingsboard.server.service.security.auth.oauth2;
17 17
  18 +import lombok.extern.slf4j.Slf4j;
18 19 import org.springframework.beans.factory.annotation.Autowired;
19 20 import org.springframework.security.core.Authentication;
20 21 import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
... ... @@ -42,6 +43,7 @@ import java.net.URLEncoder;
42 43 import java.nio.charset.StandardCharsets;
43 44 import java.util.UUID;
44 45
  46 +@Slf4j
45 47 @Component(value = "oauth2AuthenticationSuccessHandler")
46 48 public class Oauth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
47 49
... ... @@ -99,6 +101,8 @@ public class Oauth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationS
99 101 clearAuthenticationAttributes(request, response);
100 102 getRedirectStrategy().sendRedirect(request, response, baseUrl + "/?accessToken=" + accessToken.getToken() + "&refreshToken=" + refreshToken.getToken());
101 103 } catch (Exception e) {
  104 + log.debug("Error occurred during processing authentication success result. " +
  105 + "request [{}], response [{}], authentication [{}]", request, response, authentication, e);
102 106 clearAuthenticationAttributes(request, response);
103 107 String errorPrefix;
104 108 if (!StringUtils.isEmpty(callbackUrlScheme)) {
... ...
... ... @@ -22,7 +22,6 @@ import com.google.common.util.concurrent.Futures;
22 22 import com.google.common.util.concurrent.ListenableFuture;
23 23 import com.google.common.util.concurrent.ListeningScheduledExecutorService;
24 24 import com.google.common.util.concurrent.MoreExecutors;
25   -import com.google.common.util.concurrent.SettableFuture;
26 25 import lombok.Getter;
27 26 import lombok.extern.slf4j.Slf4j;
28 27 import org.springframework.beans.factory.annotation.Autowired;
... ... @@ -59,7 +58,7 @@ import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
59 58 import org.thingsboard.server.queue.discovery.PartitionService;
60 59 import org.thingsboard.server.queue.discovery.TbApplicationEventListener;
61 60 import org.thingsboard.server.queue.util.TbCoreComponent;
62   -import org.thingsboard.server.service.queue.TbClusterService;
  61 +import org.thingsboard.server.cluster.TbClusterService;
63 62 import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService;
64 63
65 64 import javax.annotation.Nonnull;
... ... @@ -71,7 +70,6 @@ import java.util.Arrays;
71 70 import java.util.Collections;
72 71 import java.util.HashSet;
73 72 import java.util.List;
74   -import java.util.Optional;
75 73 import java.util.Queue;
76 74 import java.util.Random;
77 75 import java.util.Set;
... ... @@ -137,7 +135,6 @@ public class DefaultDeviceStateService extends TbApplicationEventListener<Partit
137 135 private ExecutorService deviceStateExecutor;
138 136 private final ConcurrentMap<TopicPartitionInfo, Set<DeviceId>> partitionedDevices = new ConcurrentHashMap<>();
139 137 final ConcurrentMap<DeviceId, DeviceStateData> deviceStates = new ConcurrentHashMap<>();
140   - private final ConcurrentMap<DeviceId, Long> deviceLastSavedActivity = new ConcurrentHashMap<>();
141 138
142 139 final Queue<Set<TopicPartitionInfo>> subscribeQueue = new ConcurrentLinkedQueue<>();
143 140
... ... @@ -177,22 +174,7 @@ public class DefaultDeviceStateService extends TbApplicationEventListener<Partit
177 174 }
178 175
179 176 @Override
180   - public void onDeviceAdded(Device device) {
181   - sendDeviceEvent(device.getTenantId(), device.getId(), true, false, false);
182   - }
183   -
184   - @Override
185   - public void onDeviceUpdated(Device device) {
186   - sendDeviceEvent(device.getTenantId(), device.getId(), false, true, false);
187   - }
188   -
189   - @Override
190   - public void onDeviceDeleted(Device device) {
191   - sendDeviceEvent(device.getTenantId(), device.getId(), false, false, true);
192   - }
193   -
194   - @Override
195   - public void onDeviceConnect(DeviceId deviceId) {
  177 + public void onDeviceConnect(TenantId tenantId, DeviceId deviceId) {
196 178 log.trace("on Device Connect [{}]", deviceId.getId());
197 179 DeviceStateData stateData = getOrFetchDeviceStateData(deviceId);
198 180 long ts = System.currentTimeMillis();
... ... @@ -200,23 +182,23 @@ public class DefaultDeviceStateService extends TbApplicationEventListener<Partit
200 182 save(deviceId, LAST_CONNECT_TIME, ts);
201 183 pushRuleEngineMessage(stateData, CONNECT_EVENT);
202 184 checkAndUpdateState(deviceId, stateData);
  185 + cleanDeviceStateIfBelongsExternalPartition(tenantId, deviceId);
203 186 }
204 187
205 188 @Override
206   - public void onDeviceActivity(DeviceId deviceId, long lastReportedActivity) {
  189 + public void onDeviceActivity(TenantId tenantId, DeviceId deviceId, long lastReportedActivity) {
207 190 log.trace("on Device Activity [{}], lastReportedActivity [{}]", deviceId.getId(), lastReportedActivity);
208   - long lastSavedActivity = deviceLastSavedActivity.getOrDefault(deviceId, 0L);
209   - if (lastReportedActivity > 0 && lastReportedActivity > lastSavedActivity) {
210   - final DeviceStateData stateData = getOrFetchDeviceStateData(deviceId);
  191 + final DeviceStateData stateData = getOrFetchDeviceStateData(deviceId);
  192 + if (lastReportedActivity > 0 && lastReportedActivity > stateData.getState().getLastActivityTime()) {
211 193 updateActivityState(deviceId, stateData, lastReportedActivity);
212 194 }
  195 + cleanDeviceStateIfBelongsExternalPartition(tenantId, deviceId);
213 196 }
214 197
215 198 void updateActivityState(DeviceId deviceId, DeviceStateData stateData, long lastReportedActivity) {
216 199 log.trace("updateActivityState - fetched state {} for device {}, lastReportedActivity {}", stateData, deviceId, lastReportedActivity);
217 200 if (stateData != null) {
218 201 save(deviceId, LAST_ACTIVITY_TIME, lastReportedActivity);
219   - deviceLastSavedActivity.put(deviceId, lastReportedActivity);
220 202 DeviceState state = stateData.getState();
221 203 state.setLastActivityTime(lastReportedActivity);
222 204 if (!state.isActive()) {
... ... @@ -225,21 +207,23 @@ public class DefaultDeviceStateService extends TbApplicationEventListener<Partit
225 207 pushRuleEngineMessage(stateData, ACTIVITY_EVENT);
226 208 }
227 209 } else {
228   - log.warn("updateActivityState - fetched state IN NULL for device {}, lastReportedActivity {}", deviceId, lastReportedActivity);
  210 + log.debug("updateActivityState - fetched state IN NULL for device {}, lastReportedActivity {}", deviceId, lastReportedActivity);
  211 + cleanUpDeviceStateMap(deviceId);
229 212 }
230 213 }
231 214
232 215 @Override
233   - public void onDeviceDisconnect(DeviceId deviceId) {
  216 + public void onDeviceDisconnect(TenantId tenantId, DeviceId deviceId) {
234 217 DeviceStateData stateData = getOrFetchDeviceStateData(deviceId);
235 218 long ts = System.currentTimeMillis();
236 219 stateData.getState().setLastDisconnectTime(ts);
237 220 save(deviceId, LAST_DISCONNECT_TIME, ts);
238 221 pushRuleEngineMessage(stateData, DISCONNECT_EVENT);
  222 + cleanDeviceStateIfBelongsExternalPartition(tenantId, deviceId);
239 223 }
240 224
241 225 @Override
242   - public void onDeviceInactivityTimeoutUpdate(DeviceId deviceId, long inactivityTimeout) {
  226 + public void onDeviceInactivityTimeoutUpdate(TenantId tenantId, DeviceId deviceId, long inactivityTimeout) {
243 227 if (inactivityTimeout <= 0L) {
244 228 return;
245 229 }
... ... @@ -247,6 +231,7 @@ public class DefaultDeviceStateService extends TbApplicationEventListener<Partit
247 231 DeviceStateData stateData = getOrFetchDeviceStateData(deviceId);
248 232 stateData.getState().setInactivityTimeout(inactivityTimeout);
249 233 checkAndUpdateState(deviceId, stateData);
  234 + cleanDeviceStateIfBelongsExternalPartition(tenantId, deviceId);
250 235 }
251 236
252 237 @Override
... ... @@ -267,6 +252,7 @@ public class DefaultDeviceStateService extends TbApplicationEventListener<Partit
267 252 TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_CORE, tenantId, device.getId());
268 253 if (partitionedDevices.containsKey(tpi)) {
269 254 addDeviceUsingState(tpi, state);
  255 + save(deviceId, ACTIVITY_STATE, false);
270 256 callback.onSuccess();
271 257 } else {
272 258 log.warn("[{}][{}] Device belongs to external partition. Probably rebalancing is in progress. Topic: {}"
... ... @@ -283,12 +269,11 @@ public class DefaultDeviceStateService extends TbApplicationEventListener<Partit
283 269 }, deviceStateExecutor);
284 270 } else if (proto.getUpdated()) {
285 271 DeviceStateData stateData = getOrFetchDeviceStateData(device.getId());
286   - if (stateData != null) {
287   - TbMsgMetaData md = new TbMsgMetaData();
288   - md.putValue("deviceName", device.getName());
289   - md.putValue("deviceType", device.getType());
290   - stateData.setMetaData(md);
291   - }
  272 + TbMsgMetaData md = new TbMsgMetaData();
  273 + md.putValue("deviceName", device.getName());
  274 + md.putValue("deviceType", device.getType());
  275 + stateData.setMetaData(md);
  276 + callback.onSuccess();
292 277 }
293 278 } else {
294 279 //Device was probably deleted while message was in queue;
... ... @@ -356,10 +341,7 @@ public class DefaultDeviceStateService extends TbApplicationEventListener<Partit
356 341 // We no longer manage current partition of devices;
357 342 removedPartitions.forEach(partition -> {
358 343 Set<DeviceId> devices = partitionedDevices.remove(partition);
359   - devices.forEach(deviceId -> {
360   - deviceStates.remove(deviceId);
361   - deviceLastSavedActivity.remove(deviceId);
362   - });
  344 + devices.forEach(this::cleanUpDeviceStateMap);
363 345 });
364 346
365 347 addedPartitions.forEach(tpi -> partitionedDevices.computeIfAbsent(tpi, key -> ConcurrentHashMap.newKeySet()));
... ... @@ -457,17 +439,18 @@ public class DefaultDeviceStateService extends TbApplicationEventListener<Partit
457 439 deviceStates.put(state.getDeviceId(), state);
458 440 } else {
459 441 log.warn("Device belongs to external partition {}" + tpi.getFullTopicName());
460   - new RuntimeException("Device belongs to external partition " + tpi.getFullTopicName() + "!");
  442 + throw new RuntimeException("Device belongs to external partition " + tpi.getFullTopicName() + "!");
461 443 }
462 444 }
463 445
464 446 void updateInactivityStateIfExpired() {
465 447 final long ts = System.currentTimeMillis();
466   - log.debug("Calculating state updates for {} devices", deviceStates.size());
467   - Set<DeviceId> deviceIds = new HashSet<>(deviceStates.keySet());
468   - for (DeviceId deviceId : deviceIds) {
469   - updateInactivityStateIfExpired(ts, deviceId);
470   - }
  448 + partitionedDevices.forEach((tpi, deviceIds) -> {
  449 + log.debug("Calculating state updates. tpi {} for {} devices", tpi.getFullTopicName(), deviceIds.size());
  450 + for (DeviceId deviceId : deviceIds) {
  451 + updateInactivityStateIfExpired(ts, deviceId);
  452 + }
  453 + });
471 454 }
472 455
473 456 void updateInactivityStateIfExpired(long ts, DeviceId deviceId) {
... ... @@ -488,8 +471,7 @@ public class DefaultDeviceStateService extends TbApplicationEventListener<Partit
488 471 }
489 472 } else {
490 473 log.debug("[{}] Device that belongs to other server is detected and removed.", deviceId);
491   - deviceStates.remove(deviceId);
492   - deviceLastSavedActivity.remove(deviceId);
  474 + cleanUpDeviceStateMap(deviceId);
493 475 }
494 476 }
495 477
... ... @@ -522,27 +504,26 @@ public class DefaultDeviceStateService extends TbApplicationEventListener<Partit
522 504 }
523 505 }
524 506
525   - private void sendDeviceEvent(TenantId tenantId, DeviceId deviceId, boolean added, boolean updated, boolean deleted) {
526   - TransportProtos.DeviceStateServiceMsgProto.Builder builder = TransportProtos.DeviceStateServiceMsgProto.newBuilder();
527   - builder.setTenantIdMSB(tenantId.getId().getMostSignificantBits());
528   - builder.setTenantIdLSB(tenantId.getId().getLeastSignificantBits());
529   - builder.setDeviceIdMSB(deviceId.getId().getMostSignificantBits());
530   - builder.setDeviceIdLSB(deviceId.getId().getLeastSignificantBits());
531   - builder.setAdded(added);
532   - builder.setUpdated(updated);
533   - builder.setDeleted(deleted);
534   - TransportProtos.DeviceStateServiceMsgProto msg = builder.build();
535   - clusterService.pushMsgToCore(tenantId, deviceId, TransportProtos.ToCoreMsg.newBuilder().setDeviceStateServiceMsg(msg).build(), null);
  507 + private void cleanDeviceStateIfBelongsExternalPartition(TenantId tenantId, final DeviceId deviceId) {
  508 + TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_CORE, tenantId, deviceId);
  509 + if (!partitionedDevices.containsKey(tpi)) {
  510 + cleanUpDeviceStateMap(deviceId);
  511 + log.debug("[{}][{}] device belongs to external partition. Probably rebalancing is in progress. Topic: {}"
  512 + , tenantId, deviceId, tpi.getFullTopicName());
  513 + }
536 514 }
537 515
538 516 private void onDeviceDeleted(TenantId tenantId, DeviceId deviceId) {
539   - deviceStates.remove(deviceId);
540   - deviceLastSavedActivity.remove(deviceId);
  517 + cleanUpDeviceStateMap(deviceId);
541 518 TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_CORE, tenantId, deviceId);
542 519 Set<DeviceId> deviceIdSet = partitionedDevices.get(tpi);
543 520 deviceIdSet.remove(deviceId);
544 521 }
545 522
  523 + private void cleanUpDeviceStateMap(DeviceId deviceId) {
  524 + deviceStates.remove(deviceId);
  525 + }
  526 +
546 527 private ListenableFuture<DeviceStateData> fetchDeviceState(Device device) {
547 528 ListenableFuture<DeviceStateData> future;
548 529 if (persistToTelemetry) {
... ... @@ -573,7 +554,7 @@ public class DefaultDeviceStateService extends TbApplicationEventListener<Partit
573 554 }
574 555
575 556 private <T extends KvEntry> Function<List<T>, DeviceStateData> extractDeviceStateData(Device device) {
576   - return new Function<List<T>, DeviceStateData>() {
  557 + return new Function<>() {
577 558 @Nonnull
578 559 @Override
579 560 public DeviceStateData apply(@Nullable List<T> data) {
... ... @@ -660,9 +641,9 @@ public class DefaultDeviceStateService extends TbApplicationEventListener<Partit
660 641 tsSubService.saveAndNotifyInternal(
661 642 TenantId.SYS_TENANT_ID, deviceId,
662 643 Collections.singletonList(new BasicTsKvEntry(System.currentTimeMillis(), new LongDataEntry(key, value))),
663   - new AttributeSaveCallback<>(deviceId, key, value));
  644 + new TelemetrySaveCallback<>(deviceId, key, value));
664 645 } else {
665   - tsSubService.saveAttrAndNotify(TenantId.SYS_TENANT_ID, deviceId, DataConstants.SERVER_SCOPE, key, value, new AttributeSaveCallback<>(deviceId, key, value));
  646 + tsSubService.saveAttrAndNotify(TenantId.SYS_TENANT_ID, deviceId, DataConstants.SERVER_SCOPE, key, value, new TelemetrySaveCallback<>(deviceId, key, value));
666 647 }
667 648 }
668 649
... ... @@ -671,18 +652,18 @@ public class DefaultDeviceStateService extends TbApplicationEventListener<Partit
671 652 tsSubService.saveAndNotifyInternal(
672 653 TenantId.SYS_TENANT_ID, deviceId,
673 654 Collections.singletonList(new BasicTsKvEntry(System.currentTimeMillis(), new BooleanDataEntry(key, value))),
674   - new AttributeSaveCallback<>(deviceId, key, value));
  655 + new TelemetrySaveCallback<>(deviceId, key, value));
675 656 } else {
676   - tsSubService.saveAttrAndNotify(TenantId.SYS_TENANT_ID, deviceId, DataConstants.SERVER_SCOPE, key, value, new AttributeSaveCallback<>(deviceId, key, value));
  657 + tsSubService.saveAttrAndNotify(TenantId.SYS_TENANT_ID, deviceId, DataConstants.SERVER_SCOPE, key, value, new TelemetrySaveCallback<>(deviceId, key, value));
677 658 }
678 659 }
679 660
680   - private static class AttributeSaveCallback<T> implements FutureCallback<T> {
  661 + private static class TelemetrySaveCallback<T> implements FutureCallback<T> {
681 662 private final DeviceId deviceId;
682 663 private final String key;
683 664 private final Object value;
684 665
685   - AttributeSaveCallback(DeviceId deviceId, String key, Object value) {
  666 + TelemetrySaveCallback(DeviceId deviceId, String key, Object value) {
686 667 this.deviceId = deviceId;
687 668 this.key = key;
688 669 this.value = value;
... ...
... ... @@ -18,6 +18,7 @@ package org.thingsboard.server.service.state;
18 18 import org.springframework.context.ApplicationListener;
19 19 import org.thingsboard.server.common.data.Device;
20 20 import org.thingsboard.server.common.data.id.DeviceId;
  21 +import org.thingsboard.server.common.data.id.TenantId;
21 22 import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
22 23 import org.thingsboard.server.gen.transport.TransportProtos;
23 24 import org.thingsboard.server.common.msg.queue.TbCallback;
... ... @@ -27,19 +28,13 @@ import org.thingsboard.server.common.msg.queue.TbCallback;
27 28 */
28 29 public interface DeviceStateService extends ApplicationListener<PartitionChangeEvent> {
29 30
30   - void onDeviceAdded(Device device);
  31 + void onDeviceConnect(TenantId tenantId, DeviceId deviceId);
31 32
32   - void onDeviceUpdated(Device device);
  33 + void onDeviceActivity(TenantId tenantId, DeviceId deviceId, long lastReportedActivityTime);
33 34
34   - void onDeviceDeleted(Device device);
  35 + void onDeviceDisconnect(TenantId tenantId, DeviceId deviceId);
35 36
36   - void onDeviceConnect(DeviceId deviceId);
37   -
38   - void onDeviceActivity(DeviceId deviceId, long lastReportedActivityTime);
39   -
40   - void onDeviceDisconnect(DeviceId deviceId);
41   -
42   - void onDeviceInactivityTimeoutUpdate(DeviceId deviceId, long inactivityTimeout);
  37 + void onDeviceInactivityTimeoutUpdate(TenantId tenantId, DeviceId deviceId, long inactivityTimeout);
43 38
44 39 void onQueueMsg(TransportProtos.DeviceStateServiceMsgProto proto, TbCallback bytes);
45 40
... ...
... ... @@ -53,7 +53,7 @@ import org.thingsboard.server.queue.discovery.TbApplicationEventListener;
53 53 import org.thingsboard.server.queue.discovery.TbServiceInfoProvider;
54 54 import org.thingsboard.server.queue.provider.TbQueueProducerProvider;
55 55 import org.thingsboard.server.queue.util.TbCoreComponent;
56   -import org.thingsboard.server.service.queue.TbClusterService;
  56 +import org.thingsboard.server.cluster.TbClusterService;
57 57 import org.thingsboard.server.service.state.DefaultDeviceStateService;
58 58 import org.thingsboard.server.service.state.DeviceStateService;
59 59 import org.thingsboard.server.service.telemetry.sub.AlarmSubscriptionUpdate;
... ... @@ -224,7 +224,7 @@ public class DefaultSubscriptionManagerService extends TbApplicationEventListene
224 224 return subscriptionUpdate;
225 225 });
226 226 if (entityId.getEntityType() == EntityType.DEVICE) {
227   - updateDeviceInactivityTimeout(entityId, ts);
  227 + updateDeviceInactivityTimeout(tenantId, entityId, ts);
228 228 }
229 229 callback.onSuccess();
230 230 }
... ... @@ -259,7 +259,7 @@ public class DefaultSubscriptionManagerService extends TbApplicationEventListene
259 259 });
260 260 if (entityId.getEntityType() == EntityType.DEVICE) {
261 261 if (TbAttributeSubscriptionScope.SERVER_SCOPE.name().equalsIgnoreCase(scope)) {
262   - updateDeviceInactivityTimeout(entityId, attributes);
  262 + updateDeviceInactivityTimeout(tenantId, entityId, attributes);
263 263 } else if (TbAttributeSubscriptionScope.SHARED_SCOPE.name().equalsIgnoreCase(scope) && notifyDevice) {
264 264 clusterService.pushMsgToCore(DeviceAttributesEventNotificationMsg.onUpdate(tenantId,
265 265 new DeviceId(entityId.getId()), DataConstants.SHARED_SCOPE, new ArrayList<>(attributes))
... ... @@ -269,10 +269,10 @@ public class DefaultSubscriptionManagerService extends TbApplicationEventListene
269 269 callback.onSuccess();
270 270 }
271 271
272   - private void updateDeviceInactivityTimeout(EntityId entityId, List<? extends KvEntry> kvEntries) {
  272 + private void updateDeviceInactivityTimeout(TenantId tenantId, EntityId entityId, List<? extends KvEntry> kvEntries) {
273 273 for (KvEntry kvEntry : kvEntries) {
274 274 if (kvEntry.getKey().equals(DefaultDeviceStateService.INACTIVITY_TIMEOUT)) {
275   - deviceStateService.onDeviceInactivityTimeoutUpdate(new DeviceId(entityId.getId()), kvEntry.getLongValue().orElse(0L));
  275 + deviceStateService.onDeviceInactivityTimeoutUpdate(tenantId, new DeviceId(entityId.getId()), kvEntry.getLongValue().orElse(0L));
276 276 }
277 277 }
278 278 }
... ...
... ... @@ -30,7 +30,7 @@ import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
30 30 import org.thingsboard.server.common.msg.queue.TbCallback;
31 31 import org.thingsboard.server.queue.discovery.TbApplicationEventListener;
32 32 import org.thingsboard.server.queue.util.TbCoreComponent;
33   -import org.thingsboard.server.service.queue.TbClusterService;
  33 +import org.thingsboard.server.cluster.TbClusterService;
34 34 import org.thingsboard.server.service.telemetry.sub.AlarmSubscriptionUpdate;
35 35 import org.thingsboard.server.service.telemetry.sub.TelemetrySubscriptionUpdate;
36 36
... ... @@ -42,7 +42,6 @@ import java.util.Map;
42 42 import java.util.Set;
43 43 import java.util.concurrent.ConcurrentHashMap;
44 44 import java.util.concurrent.ExecutorService;
45   -import java.util.concurrent.Executors;
46 45
47 46 @Slf4j
48 47 @TbCoreComponent
... ...
... ... @@ -20,15 +20,13 @@ import com.google.common.util.concurrent.Futures;
20 20 import com.google.common.util.concurrent.ListenableFuture;
21 21 import lombok.extern.slf4j.Slf4j;
22 22 import org.springframework.beans.factory.annotation.Autowired;
23   -import org.springframework.context.ApplicationListener;
24   -import org.springframework.context.event.EventListener;
25 23 import org.thingsboard.common.util.ThingsBoardThreadFactory;
26 24 import org.thingsboard.server.common.msg.queue.ServiceType;
27 25 import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
28 26 import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
29 27 import org.thingsboard.server.queue.discovery.PartitionService;
30 28 import org.thingsboard.server.queue.discovery.TbApplicationEventListener;
31   -import org.thingsboard.server.service.queue.TbClusterService;
  29 +import org.thingsboard.server.cluster.TbClusterService;
32 30 import org.thingsboard.server.service.subscription.SubscriptionManagerService;
33 31
34 32 import javax.annotation.Nullable;
... ...
... ... @@ -46,7 +46,7 @@ import org.thingsboard.server.gen.transport.TransportProtos;
46 46 import org.thingsboard.server.queue.discovery.PartitionService;
47 47 import org.thingsboard.server.queue.usagestats.TbApiUsageClient;
48 48 import org.thingsboard.server.service.apiusage.TbApiUsageStateService;
49   -import org.thingsboard.server.service.queue.TbClusterService;
  49 +import org.thingsboard.server.cluster.TbClusterService;
50 50 import org.thingsboard.server.service.subscription.SubscriptionManagerService;
51 51 import org.thingsboard.server.service.subscription.TbSubscriptionUtils;
52 52
... ...
... ... @@ -45,7 +45,7 @@ import org.thingsboard.server.gen.transport.TransportProtos;
45 45 import org.thingsboard.server.queue.discovery.PartitionService;
46 46 import org.thingsboard.server.queue.usagestats.TbApiUsageClient;
47 47 import org.thingsboard.server.service.apiusage.TbApiUsageStateService;
48   -import org.thingsboard.server.service.queue.TbClusterService;
  48 +import org.thingsboard.server.cluster.TbClusterService;
49 49 import org.thingsboard.server.service.subscription.TbSubscriptionUtils;
50 50
51 51 import javax.annotation.Nullable;
... ...
... ... @@ -95,7 +95,7 @@ import org.thingsboard.server.queue.util.TbCoreComponent;
95 95 import org.thingsboard.server.service.apiusage.TbApiUsageStateService;
96 96 import org.thingsboard.server.service.executors.DbCallbackExecutorService;
97 97 import org.thingsboard.server.service.profile.TbDeviceProfileCache;
98   -import org.thingsboard.server.service.queue.TbClusterService;
  98 +import org.thingsboard.server.cluster.TbClusterService;
99 99 import org.thingsboard.server.service.resource.TbResourceService;
100 100 import org.thingsboard.server.service.state.DeviceStateService;
101 101
... ... @@ -265,9 +265,11 @@ public class DefaultTransportApiService implements TransportApiService {
265 265 device.setCustomerId(gateway.getCustomerId());
266 266 DeviceProfile deviceProfile = deviceProfileCache.findOrCreateDeviceProfile(gateway.getTenantId(), requestMsg.getDeviceType());
267 267 device.setDeviceProfileId(deviceProfile.getId());
268   - device = deviceService.saveDevice(device);
  268 + Device savedDevice = deviceService.saveDevice(device);
  269 + tbClusterService.onDeviceUpdated(savedDevice, device);
  270 + device = savedDevice;
  271 +
269 272 relationService.saveRelationAsync(TenantId.SYS_TENANT_ID, new EntityRelation(gateway.getId(), device.getId(), "Created"));
270   - deviceStateService.onDeviceAdded(device);
271 273
272 274 TbMsgMetaData metaData = new TbMsgMetaData();
273 275 CustomerId customerId = gateway.getCustomerId();
... ... @@ -581,7 +583,7 @@ public class DefaultTransportApiService implements TransportApiService {
581 583 device.setName(deviceName);
582 584 device.setType("LwM2M");
583 585 device = deviceService.saveDevice(device);
584   - deviceStateService.onDeviceAdded(device);
  586 + tbClusterService.onDeviceUpdated(device, null);
585 587 }
586 588 TransportProtos.LwM2MRegistrationResponseMsg registrationResponseMsg =
587 589 TransportProtos.LwM2MRegistrationResponseMsg.newBuilder()
... ...
... ... @@ -588,6 +588,7 @@ transport:
588 588 bind_address: "${MQTT_BIND_ADDRESS:0.0.0.0}"
589 589 bind_port: "${MQTT_BIND_PORT:1883}"
590 590 timeout: "${MQTT_TIMEOUT:10000}"
  591 + msg_queue_size_per_device_limit: "${MQTT_MSG_QUEUE_SIZE_PER_DEVICE_LIMIT:100}" # messages await in the queue before device connected state. This limit works on low level before TenantProfileLimits mechanism
591 592 netty:
592 593 leak_detector_level: "${NETTY_LEAK_DETECTOR_LVL:DISABLED}"
593 594 boss_group_thread_count: "${NETTY_BOSS_GROUP_THREADS:1}"
... ... @@ -747,6 +748,8 @@ queue:
747 748 compression.type: "${TB_KAFKA_COMPRESSION_TYPE:none}" # none or gzip
748 749 batch.size: "${TB_KAFKA_BATCH_SIZE:16384}"
749 750 linger.ms: "${TB_KAFKA_LINGER_MS:1}"
  751 + max.request.size: "${TB_KAFKA_MAX_REQUEST_SIZE:1048576}"
  752 + max.in.flight.requests.per.connection: "${TB_KAFKA_MAX_IN_FLIGHT_REQUESTS_PER_CONNECTION:5}"
750 753 buffer.memory: "${TB_BUFFER_MEMORY:33554432}"
751 754 replication_factor: "${TB_QUEUE_KAFKA_REPLICATION_FACTOR:1}"
752 755 max_poll_interval_ms: "${TB_QUEUE_KAFKA_MAX_POLL_INTERVAL_MS:300000}"
... ... @@ -763,7 +766,11 @@ queue:
763 766 tb_ota_package:
764 767 - key: max.poll.records
765 768 value: 10
766   - other:
  769 + other: # In this section you can specify custom parameters for Kafka consumer/producer and expose the env variables to configure outside
  770 + - key: "request.timeout.ms" # refer to https://docs.confluent.io/platform/current/installation/configuration/producer-configs.html#producerconfigs_request.timeout.ms
  771 + value: "${TB_QUEUE_KAFKA_REQUEST_TIMEOUT_MS:30000}" # (30 seconds)
  772 + - key: "session.timeout.ms" # refer to https://docs.confluent.io/platform/current/installation/configuration/consumer-configs.html#consumerconfigs_session.timeout.ms
  773 + value: "${TB_QUEUE_KAFKA_SESSION_TIMEOUT_MS:10000}" # (10 seconds)
767 774 topic-properties:
768 775 rule-engine: "${TB_QUEUE_KAFKA_RE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}"
769 776 core: "${TB_QUEUE_KAFKA_CORE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}"
... ...
... ... @@ -17,6 +17,7 @@ package org.thingsboard.server.controller;
17 17
18 18 import com.fasterxml.jackson.core.type.TypeReference;
19 19 import lombok.extern.slf4j.Slf4j;
  20 +import org.awaitility.Awaitility;
20 21 import org.junit.After;
21 22 import org.junit.Assert;
22 23 import org.junit.Before;
... ... @@ -28,13 +29,14 @@ import org.thingsboard.server.common.data.asset.Asset;
28 29 import org.thingsboard.server.common.data.edge.Edge;
29 30 import org.thingsboard.server.common.data.edge.EdgeEvent;
30 31 import org.thingsboard.server.common.data.edge.EdgeEventType;
31   -import org.thingsboard.server.common.data.id.TenantId;
  32 +import org.thingsboard.server.common.data.id.EdgeId;
32 33 import org.thingsboard.server.common.data.page.PageData;
33 34 import org.thingsboard.server.common.data.page.TimePageLink;
34 35 import org.thingsboard.server.common.data.relation.EntityRelation;
35 36 import org.thingsboard.server.common.data.security.Authority;
36 37
37 38 import java.util.List;
  39 +import java.util.concurrent.TimeUnit;
38 40
39 41 import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
40 42
... ... @@ -83,34 +85,37 @@ public class BaseEdgeEventControllerTest extends AbstractControllerTest {
83 85 Device device = constructDevice("TestDevice", "default");
84 86 Device savedDevice = doPost("/api/device", device, Device.class);
85 87
86   - doPost("/api/edge/" + edge.getId().toString() + "/device/" + savedDevice.getId().toString(), Device.class);
  88 + final EdgeId edgeId = edge.getId();
  89 + doPost("/api/edge/" + edgeId.toString() + "/device/" + savedDevice.getId().toString(), Device.class);
87 90
88 91 Asset asset = constructAsset("TestAsset", "default");
89 92 Asset savedAsset = doPost("/api/asset", asset, Asset.class);
90 93
91   - doPost("/api/edge/" + edge.getId().toString() + "/asset/" + savedAsset.getId().toString(), Asset.class);
  94 + doPost("/api/edge/" + edgeId.toString() + "/asset/" + savedAsset.getId().toString(), Asset.class);
92 95
93 96 EntityRelation relation = new EntityRelation(savedAsset.getId(), savedDevice.getId(), EntityRelation.CONTAINS_TYPE);
94 97
95 98 doPost("/api/relation", relation);
96 99
97   - // wait while edge event for the relation entity persisted to DB
98   - Thread.sleep(100);
99   - List<EdgeEvent> edgeEvents;
100   - int attempt = 1;
101   - do {
102   - edgeEvents = doGetTypedWithTimePageLink("/api/edge/" + edge.getId().toString() + "/events?",
103   - new TypeReference<PageData<EdgeEvent>>() {}, new TimePageLink(4)).getData();
104   - attempt++;
105   - Thread.sleep(100);
106   - } while (edgeEvents.size() != 4 && attempt < 5);
107   - Assert.assertEquals(4, edgeEvents.size());
  100 + Awaitility.await()
  101 + .atMost(30, TimeUnit.SECONDS)
  102 + .until(() -> {
  103 + List<EdgeEvent> edgeEvents = findEdgeEvents(edgeId);
  104 + return edgeEvents.size() == 4;
  105 + });
  106 + List<EdgeEvent> edgeEvents = findEdgeEvents(edgeId);
108 107 Assert.assertTrue(edgeEvents.stream().anyMatch(ee -> EdgeEventType.RULE_CHAIN.equals(ee.getType())));
109 108 Assert.assertTrue(edgeEvents.stream().anyMatch(ee -> EdgeEventType.DEVICE.equals(ee.getType())));
110 109 Assert.assertTrue(edgeEvents.stream().anyMatch(ee -> EdgeEventType.ASSET.equals(ee.getType())));
111 110 Assert.assertTrue(edgeEvents.stream().anyMatch(ee -> EdgeEventType.RELATION.equals(ee.getType())));
112 111 }
113 112
  113 + private List<EdgeEvent> findEdgeEvents(EdgeId edgeId) throws Exception {
  114 + return doGetTypedWithTimePageLink("/api/edge/" + edgeId.toString() + "/events?",
  115 + new TypeReference<PageData<EdgeEvent>>() {
  116 + }, new TimePageLink(10)).getData();
  117 + }
  118 +
114 119 private Device constructDevice(String name, String type) {
115 120 Device device = new Device();
116 121 device.setName(name);
... ...
... ... @@ -27,6 +27,7 @@ import com.google.protobuf.InvalidProtocolBufferException;
27 27 import com.google.protobuf.MessageLite;
28 28 import lombok.extern.slf4j.Slf4j;
29 29 import org.apache.commons.lang3.RandomStringUtils;
  30 +import org.awaitility.Awaitility;
30 31 import org.junit.After;
31 32 import org.junit.Assert;
32 33 import org.junit.Before;
... ... @@ -114,7 +115,7 @@ import org.thingsboard.server.gen.edge.v1.UserUpdateMsg;
114 115 import org.thingsboard.server.gen.edge.v1.WidgetTypeUpdateMsg;
115 116 import org.thingsboard.server.gen.edge.v1.WidgetsBundleUpdateMsg;
116 117 import org.thingsboard.server.gen.transport.TransportProtos;
117   -import org.thingsboard.server.service.queue.TbClusterService;
  118 +import org.thingsboard.server.cluster.TbClusterService;
118 119
119 120 import java.util.ArrayList;
120 121 import java.util.List;
... ... @@ -126,6 +127,7 @@ import java.util.UUID;
126 127 import java.util.concurrent.TimeUnit;
127 128
128 129 import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
  130 +import static org.thingsboard.server.service.edge.rpc.EdgeProtoUtils.getStringValue;
129 131
130 132 @Slf4j
131 133 abstract public class BaseEdgeTest extends AbstractControllerTest {
... ... @@ -648,7 +650,7 @@ abstract public class BaseEdgeTest extends AbstractControllerTest {
648 650 Assert.assertEquals(relationUpdateMsg.getFromIdMSB(), relation.getFrom().getId().getMostSignificantBits());
649 651 Assert.assertEquals(relationUpdateMsg.getToIdLSB(), relation.getTo().getId().getLeastSignificantBits());
650 652 Assert.assertEquals(relationUpdateMsg.getToEntityType(), relation.getTo().getEntityType().name());
651   - Assert.assertEquals(relationUpdateMsg.getTypeGroup(), relation.getTypeGroup().name());
  653 + Assert.assertEquals(relationUpdateMsg.getTypeGroup().getValue(), relation.getTypeGroup().name());
652 654
653 655 // 2
654 656 edgeImitator.expectMessageAmount(1);
... ... @@ -672,7 +674,7 @@ abstract public class BaseEdgeTest extends AbstractControllerTest {
672 674 Assert.assertEquals(relationUpdateMsg.getFromIdMSB(), relation.getFrom().getId().getMostSignificantBits());
673 675 Assert.assertEquals(relationUpdateMsg.getToIdLSB(), relation.getTo().getId().getLeastSignificantBits());
674 676 Assert.assertEquals(relationUpdateMsg.getToEntityType(), relation.getTo().getEntityType().name());
675   - Assert.assertEquals(relationUpdateMsg.getTypeGroup(), relation.getTypeGroup().name());
  677 + Assert.assertEquals(relationUpdateMsg.getTypeGroup().getValue(), relation.getTypeGroup().name());
676 678
677 679 log.info("Relations tested successfully");
678 680 }
... ... @@ -730,7 +732,15 @@ abstract public class BaseEdgeTest extends AbstractControllerTest {
730 732 edgeImitator.expectMessageAmount(1);
731 733 doDelete("/api/alarm/" + savedAlarm.getId().getId().toString())
732 734 .andExpect(status().isOk());
733   - Assert.assertFalse(edgeImitator.waitForMessages(1));
  735 + Assert.assertTrue(edgeImitator.waitForMessages(1));
  736 + latestMessage = edgeImitator.getLatestMessage();
  737 + Assert.assertTrue(latestMessage instanceof AlarmUpdateMsg);
  738 + alarmUpdateMsg = (AlarmUpdateMsg) latestMessage;
  739 + Assert.assertEquals(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE, alarmUpdateMsg.getMsgType());
  740 + Assert.assertEquals(alarmUpdateMsg.getType(), savedAlarm.getType());
  741 + Assert.assertEquals(alarmUpdateMsg.getName(), savedAlarm.getName());
  742 + Assert.assertEquals(alarmUpdateMsg.getOriginatorName(), device.getName());
  743 + Assert.assertEquals(alarmUpdateMsg.getStatus(), AlarmStatus.CLEARED_ACK.name());
734 744
735 745 log.info("Alarms tested successfully");
736 746 }
... ... @@ -900,9 +910,9 @@ abstract public class BaseEdgeTest extends AbstractControllerTest {
900 910 Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, widgetTypeUpdateMsg.getMsgType());
901 911 Assert.assertEquals(widgetTypeUpdateMsg.getIdMSB(), savedWidgetType.getUuidId().getMostSignificantBits());
902 912 Assert.assertEquals(widgetTypeUpdateMsg.getIdLSB(), savedWidgetType.getUuidId().getLeastSignificantBits());
903   - Assert.assertEquals(widgetTypeUpdateMsg.getAlias(), savedWidgetType.getAlias());
904   - Assert.assertEquals(widgetTypeUpdateMsg.getName(), savedWidgetType.getName());
905   - Assert.assertEquals(JacksonUtil.toJsonNode(widgetTypeUpdateMsg.getDescriptorJson()), savedWidgetType.getDescriptor());
  913 + Assert.assertEquals(widgetTypeUpdateMsg.getAlias().getValue(), savedWidgetType.getAlias());
  914 + Assert.assertEquals(widgetTypeUpdateMsg.getName().getValue(), savedWidgetType.getName());
  915 + Assert.assertEquals(JacksonUtil.toJsonNode(widgetTypeUpdateMsg.getDescriptorJson().getValue()), savedWidgetType.getDescriptor());
906 916
907 917 // 3
908 918 edgeImitator.expectMessageAmount(1);
... ... @@ -939,7 +949,7 @@ abstract public class BaseEdgeTest extends AbstractControllerTest {
939 949 String timeseriesData = "{\"data\":{\"temperature\":25},\"ts\":" + System.currentTimeMillis() + "}";
940 950 JsonNode timeseriesEntityData = mapper.readTree(timeseriesData);
941 951 EdgeEvent edgeEvent = constructEdgeEvent(tenantId, edge.getId(), EdgeEventActionType.TIMESERIES_UPDATED, device.getId().getId(), EdgeEventType.DEVICE, timeseriesEntityData);
942   - edgeEventService.saveAsync(edgeEvent);
  952 + edgeEventService.save(edgeEvent);
943 953 clusterService.onEdgeEventUpdate(tenantId, edge.getId());
944 954 Assert.assertTrue(edgeImitator.waitForMessages());
945 955
... ... @@ -978,7 +988,7 @@ abstract public class BaseEdgeTest extends AbstractControllerTest {
978 988 JsonNode attributesEntityData = mapper.readTree(attributesData);
979 989 EdgeEvent edgeEvent1 = constructEdgeEvent(tenantId, edge.getId(), EdgeEventActionType.ATTRIBUTES_UPDATED, device.getId().getId(), EdgeEventType.DEVICE, attributesEntityData);
980 990 edgeImitator.expectMessageAmount(1);
981   - edgeEventService.saveAsync(edgeEvent1);
  991 + edgeEventService.save(edgeEvent1);
982 992 clusterService.onEdgeEventUpdate(tenantId, edge.getId());
983 993 Assert.assertTrue(edgeImitator.waitForMessages());
984 994
... ... @@ -1003,7 +1013,7 @@ abstract public class BaseEdgeTest extends AbstractControllerTest {
1003 1013 JsonNode postAttributesEntityData = mapper.readTree(postAttributesData);
1004 1014 EdgeEvent edgeEvent = constructEdgeEvent(tenantId, edge.getId(), EdgeEventActionType.POST_ATTRIBUTES, device.getId().getId(), EdgeEventType.DEVICE, postAttributesEntityData);
1005 1015 edgeImitator.expectMessageAmount(1);
1006   - edgeEventService.saveAsync(edgeEvent);
  1016 + edgeEventService.save(edgeEvent);
1007 1017 clusterService.onEdgeEventUpdate(tenantId, edge.getId());
1008 1018 Assert.assertTrue(edgeImitator.waitForMessages());
1009 1019
... ... @@ -1028,7 +1038,7 @@ abstract public class BaseEdgeTest extends AbstractControllerTest {
1028 1038 JsonNode deleteAttributesEntityData = mapper.readTree(deleteAttributesData);
1029 1039 EdgeEvent edgeEvent = constructEdgeEvent(tenantId, edge.getId(), EdgeEventActionType.ATTRIBUTES_DELETED, device.getId().getId(), EdgeEventType.DEVICE, deleteAttributesEntityData);
1030 1040 edgeImitator.expectMessageAmount(1);
1031   - edgeEventService.saveAsync(edgeEvent);
  1041 + edgeEventService.save(edgeEvent);
1032 1042 clusterService.onEdgeEventUpdate(tenantId, edge.getId());
1033 1043 Assert.assertTrue(edgeImitator.waitForMessages());
1034 1044
... ... @@ -1062,7 +1072,7 @@ abstract public class BaseEdgeTest extends AbstractControllerTest {
1062 1072
1063 1073 EdgeEvent edgeEvent = constructEdgeEvent(tenantId, edge.getId(), EdgeEventActionType.RPC_CALL, device.getId().getId(), EdgeEventType.DEVICE, body);
1064 1074 edgeImitator.expectMessageAmount(1);
1065   - edgeEventService.saveAsync(edgeEvent);
  1075 + edgeEventService.save(edgeEvent);
1066 1076 clusterService.onEdgeEventUpdate(tenantId, edge.getId());
1067 1077 Assert.assertTrue(edgeImitator.waitForMessages());
1068 1078
... ... @@ -1088,7 +1098,7 @@ abstract public class BaseEdgeTest extends AbstractControllerTest {
1088 1098 JsonNode timeseriesEntityData = mapper.readTree(timeseriesData);
1089 1099 EdgeEvent edgeEvent = constructEdgeEvent(tenantId, edge.getId(), EdgeEventActionType.TIMESERIES_UPDATED,
1090 1100 device.getId().getId(), EdgeEventType.DEVICE, timeseriesEntityData);
1091   - edgeEventService.saveAsync(edgeEvent);
  1101 + edgeEventService.save(edgeEvent);
1092 1102 clusterService.onEdgeEventUpdate(tenantId, edge.getId());
1093 1103 }
1094 1104
... ... @@ -1202,7 +1212,7 @@ abstract public class BaseEdgeTest extends AbstractControllerTest {
1202 1212 Assert.assertTrue(latestMessage instanceof DeviceUpdateMsg);
1203 1213 DeviceUpdateMsg latestDeviceUpdateMsg = (DeviceUpdateMsg) latestMessage;
1204 1214 Assert.assertNotEquals(deviceOnCloudName, latestDeviceUpdateMsg.getName());
1205   - Assert.assertEquals(deviceOnCloudName, latestDeviceUpdateMsg.getConflictName());
  1215 + Assert.assertEquals(deviceOnCloudName, latestDeviceUpdateMsg.getConflictName().getValue());
1206 1216
1207 1217 UUID newDeviceId = new UUID(latestDeviceUpdateMsg.getIdMSB(), latestDeviceUpdateMsg.getIdLSB());
1208 1218
... ... @@ -1269,7 +1279,7 @@ abstract public class BaseEdgeTest extends AbstractControllerTest {
1269 1279 EntityId toEntityId = EntityIdFactory.getByTypeAndUuid(relationUpdateMsg.getToEntityType(), toUUID);
1270 1280 Assert.assertEquals(relation.getTo(), toEntityId);
1271 1281
1272   - Assert.assertEquals(relation.getTypeGroup().name(), relationUpdateMsg.getTypeGroup());
  1282 + Assert.assertEquals(relation.getTypeGroup().name(), relationUpdateMsg.getTypeGroup().getValue());
1273 1283 }
1274 1284
1275 1285 private void sendAlarm() throws Exception {
... ... @@ -1348,15 +1358,11 @@ abstract public class BaseEdgeTest extends AbstractControllerTest {
1348 1358 edgeImitator.sendUplinkMsg(uplinkMsgBuilder2.build());
1349 1359 Assert.assertTrue(edgeImitator.waitForResponses());
1350 1360
1351   - int attempt = 0;
1352   - Map<String, List<Map<String, String>>> timeseries;
1353   - do {
1354   - timeseries = doGetAsyncTyped("/api/plugins/telemetry/DEVICE/" + device.getUuidId() + "/values/timeseries?keys=" + timeseriesKey,
1355   - new TypeReference<>() {});
1356   - // Wait before device attributes saved to database before requesting them from controller
1357   - Thread.sleep(100);
1358   - attempt++;
1359   - } while (!timeseries.containsKey(timeseriesKey) || attempt < 10);
  1361 + Awaitility.await()
  1362 + .atMost(2, TimeUnit.SECONDS)
  1363 + .until(() -> loadDeviceTimeseries(device, timeseriesKey).containsKey(timeseriesKey));
  1364 +
  1365 + Map<String, List<Map<String, String>>> timeseries = loadDeviceTimeseries(device, timeseriesKey);
1360 1366 Assert.assertTrue(timeseries.containsKey(timeseriesKey));
1361 1367 Assert.assertEquals(1, timeseries.get(timeseriesKey).size());
1362 1368 Assert.assertEquals(timeseriesValue, timeseries.get(timeseriesKey).get(0).get("value"));
... ... @@ -1365,7 +1371,11 @@ abstract public class BaseEdgeTest extends AbstractControllerTest {
1365 1371 Assert.assertEquals(1, attributes.size());
1366 1372 Assert.assertEquals(attributes.get(0).get("key"), attributesKey);
1367 1373 Assert.assertEquals(attributes.get(0).get("value"), attributesValue);
  1374 + }
1368 1375
  1376 + private Map<String, List<Map<String, String>>> loadDeviceTimeseries(Device device, String timeseriesKey) throws Exception {
  1377 + return doGetAsyncTyped("/api/plugins/telemetry/DEVICE/" + device.getUuidId() + "/values/timeseries?keys=" + timeseriesKey,
  1378 + new TypeReference<>() {});
1369 1379 }
1370 1380
1371 1381 private void sendRelation() throws Exception {
... ... @@ -1381,7 +1391,7 @@ abstract public class BaseEdgeTest extends AbstractControllerTest {
1381 1391 UplinkMsg.Builder uplinkMsgBuilder = UplinkMsg.newBuilder();
1382 1392 RelationUpdateMsg.Builder relationUpdateMsgBuilder = RelationUpdateMsg.newBuilder();
1383 1393 relationUpdateMsgBuilder.setType("test");
1384   - relationUpdateMsgBuilder.setTypeGroup(RelationTypeGroup.COMMON.name());
  1394 + relationUpdateMsgBuilder.setTypeGroup(getStringValue(RelationTypeGroup.COMMON.name()));
1385 1395 relationUpdateMsgBuilder.setToIdMSB(device1.getId().getId().getMostSignificantBits());
1386 1396 relationUpdateMsgBuilder.setToIdLSB(device1.getId().getId().getLeastSignificantBits());
1387 1397 relationUpdateMsgBuilder.setToEntityType(device1.getId().getEntityType().name());
... ... @@ -1447,7 +1457,7 @@ abstract public class BaseEdgeTest extends AbstractControllerTest {
1447 1457 edgeImitator.expectMessageAmount(1);
1448 1458 edgeImitator.sendUplinkMsg(uplinkMsgBuilder.build());
1449 1459 Assert.assertTrue(edgeImitator.waitForResponses());
1450   - Assert.assertTrue(edgeImitator.waitForMessages());;
  1460 + Assert.assertTrue(edgeImitator.waitForMessages());
1451 1461
1452 1462 AbstractMessage latestMessage = edgeImitator.getLatestMessage();
1453 1463 Assert.assertTrue(latestMessage instanceof RuleChainMetadataUpdateMsg);
... ...
... ... @@ -27,7 +27,7 @@ import org.thingsboard.server.dao.device.DeviceService;
27 27 import org.thingsboard.server.dao.tenant.TenantService;
28 28 import org.thingsboard.server.dao.timeseries.TimeseriesService;
29 29 import org.thingsboard.server.queue.discovery.PartitionService;
30   -import org.thingsboard.server.service.queue.TbClusterService;
  30 +import org.thingsboard.server.cluster.TbClusterService;
31 31
32 32 import static org.hamcrest.CoreMatchers.is;
33 33 import static org.hamcrest.MatcherAssert.assertThat;
... ...
... ... @@ -43,7 +43,7 @@ public abstract class AbstractCoapServerSideRpcDefaultIntegrationTest extends Ab
43 43 String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"24\",\"value\": 1},\"timeout\": 6000}";
44 44 String deviceId = savedDevice.getId().getId().toString();
45 45
46   - doPostAsync("/api/plugins/rpc/oneway/" + deviceId, setGpioRequest, String.class, status().is(409),
  46 + doPostAsync("/api/rpc/oneway/" + deviceId, setGpioRequest, String.class, status().is(504),
47 47 asyncContextTimeoutToUseRpcPlugin);
48 48 }
49 49
... ... @@ -52,7 +52,7 @@ public abstract class AbstractCoapServerSideRpcDefaultIntegrationTest extends Ab
52 52 String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"25\",\"value\": 1}}";
53 53 String nonExistentDeviceId = Uuids.timeBased().toString();
54 54
55   - String result = doPostAsync("/api/plugins/rpc/oneway/" + nonExistentDeviceId, setGpioRequest, String.class,
  55 + String result = doPostAsync("/api/rpc/oneway/" + nonExistentDeviceId, setGpioRequest, String.class,
56 56 status().isNotFound());
57 57 Assert.assertEquals(AccessValidator.DEVICE_WITH_REQUESTED_ID_NOT_FOUND, result);
58 58 }
... ... @@ -62,7 +62,7 @@ public abstract class AbstractCoapServerSideRpcDefaultIntegrationTest extends Ab
62 62 String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"27\",\"value\": 1},\"timeout\": 6000}";
63 63 String deviceId = savedDevice.getId().getId().toString();
64 64
65   - doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().is(409),
  65 + doPostAsync("/api/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().is(504),
66 66 asyncContextTimeoutToUseRpcPlugin);
67 67 }
68 68
... ... @@ -71,7 +71,7 @@ public abstract class AbstractCoapServerSideRpcDefaultIntegrationTest extends Ab
71 71 String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"28\",\"value\": 1}}";
72 72 String nonExistentDeviceId = Uuids.timeBased().toString();
73 73
74   - String result = doPostAsync("/api/plugins/rpc/twoway/" + nonExistentDeviceId, setGpioRequest, String.class,
  74 + String result = doPostAsync("/api/rpc/twoway/" + nonExistentDeviceId, setGpioRequest, String.class,
75 75 status().isNotFound());
76 76 Assert.assertEquals(AccessValidator.DEVICE_WITH_REQUESTED_ID_NOT_FOUND, result);
77 77 }
... ...
... ... @@ -71,7 +71,7 @@ public abstract class AbstractCoapServerSideRpcIntegrationTest extends AbstractC
71 71
72 72 String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"23\",\"value\": 1}}";
73 73 String deviceId = savedDevice.getId().getId().toString();
74   - String result = doPostAsync("/api/plugins/rpc/oneway/" + deviceId, setGpioRequest, String.class, status().isOk());
  74 + String result = doPostAsync("/api/rpc/oneway/" + deviceId, setGpioRequest, String.class, status().isOk());
75 75
76 76 latch.await(3, TimeUnit.SECONDS);
77 77
... ... @@ -99,14 +99,14 @@ public abstract class AbstractCoapServerSideRpcIntegrationTest extends AbstractC
99 99 String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"26\",\"value\": 1}}";
100 100 String deviceId = savedDevice.getId().getId().toString();
101 101
102   - String actualResult = doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().isOk());
  102 + String actualResult = doPostAsync("/api/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().isOk());
103 103 latch.await(3, TimeUnit.SECONDS);
104 104
105 105 validateTwoWayStateChangedNotification(callback, 1, expectedResponseResult, actualResult);
106 106
107 107 latch = new CountDownLatch(1);
108 108
109   - actualResult = doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().isOk());
  109 + actualResult = doPostAsync("/api/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().isOk());
110 110 latch.await(3, TimeUnit.SECONDS);
111 111
112 112 validateTwoWayStateChangedNotification(callback, 2, expectedResponseResult, actualResult);
... ...
... ... @@ -27,6 +27,7 @@ import org.springframework.mock.web.MockMultipartFile;
27 27 import org.springframework.test.web.servlet.request.MockMultipartHttpServletRequestBuilder;
28 28 import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
29 29 import org.thingsboard.common.util.JacksonUtil;
  30 +import org.thingsboard.common.util.ThingsBoardThreadFactory;
30 31 import org.thingsboard.server.common.data.Device;
31 32 import org.thingsboard.server.common.data.DeviceProfile;
32 33 import org.thingsboard.server.common.data.DeviceProfileProvisionType;
... ... @@ -261,7 +262,7 @@ public class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest {
261 262
262 263 @Before
263 264 public void beforeTest() throws Exception {
264   - executor = Executors.newScheduledThreadPool(10);
  265 + executor = Executors.newScheduledThreadPool(10, ThingsBoardThreadFactory.forName("test-lwm2m-scheduled"));
265 266 loginTenantAdmin();
266 267
267 268 String[] resources = new String[]{"1.xml", "2.xml", "3.xml", "5.xml", "9.xml"};
... ...
... ... @@ -16,6 +16,8 @@
16 16 package org.thingsboard.server.transport.lwm2m;
17 17
18 18 import com.fasterxml.jackson.core.type.TypeReference;
  19 +import lombok.extern.slf4j.Slf4j;
  20 +import org.junit.After;
19 21 import org.junit.Assert;
20 22 import org.junit.Test;
21 23 import org.thingsboard.server.common.data.Device;
... ... @@ -23,16 +25,18 @@ import org.thingsboard.server.common.data.device.credentials.lwm2m.NoSecClientCr
23 25 import org.thingsboard.server.common.data.kv.KvEntry;
24 26 import org.thingsboard.server.common.data.kv.TsKvEntry;
25 27 import org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus;
26   -import org.thingsboard.server.common.data.query.EntityKey;
27   -import org.thingsboard.server.common.data.query.EntityKeyType;
28 28 import org.thingsboard.server.transport.lwm2m.client.LwM2MTestClient;
29 29
30 30 import java.util.Arrays;
31 31 import java.util.Collections;
32 32 import java.util.Comparator;
33 33 import java.util.List;
  34 +import java.util.UUID;
  35 +import java.util.concurrent.TimeUnit;
34 36 import java.util.stream.Collectors;
35 37
  38 +import static org.awaitility.Awaitility.await;
  39 +import static org.hamcrest.Matchers.is;
36 40 import static org.thingsboard.rest.client.utils.RestJsonConverter.toTimeseries;
37 41 import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.DOWNLOADED;
38 42 import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.DOWNLOADING;
... ... @@ -43,8 +47,10 @@ import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.UPDA
43 47 import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.UPDATING;
44 48 import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.VERIFIED;
45 49
  50 +@Slf4j
46 51 public class NoSecLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest {
47 52
  53 + public static final int TIMEOUT = 30;
48 54 private final String OTA_TRANSPORT_CONFIGURATION = "{\n" +
49 55 " \"observeAttr\": {\n" +
50 56 " \"keyName\": {\n" +
... ... @@ -122,6 +128,15 @@ public class NoSecLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest {
122 128 " \"type\": \"LWM2M\"\n" +
123 129 "}";
124 130
  131 + LwM2MTestClient client = null;
  132 +
  133 + @After
  134 + public void tearDown() {
  135 + if (client != null) {
  136 + client.destroy();
  137 + }
  138 + }
  139 +
125 140 @Test
126 141 public void testConnectAndObserveTelemetry() throws Exception {
127 142 NoSecClientCredentials clientCredentials = new NoSecClientCredentials();
... ... @@ -196,37 +211,68 @@ public class NoSecLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest {
196 211 }
197 212 }
198 213
  214 + /**
  215 + * This is the example how to use the AWAITILITY instead Thread.sleep()
  216 + * Test will finish as fast as possible, but will await until TIMEOUT if a build machine is busy or slow
  217 + * Check the detailed log output to learn how Awaitility polling the API and when exactly expected result appears
  218 + * */
199 219 @Test
200 220 public void testSoftwareUpdateByObject9() throws Exception {
201   - LwM2MTestClient client = null;
202   - try {
203   - createDeviceProfile(OTA_TRANSPORT_CONFIGURATION);
204   - NoSecClientCredentials clientCredentials = new NoSecClientCredentials();
205   - clientCredentials.setEndpoint("OTA_" + ENDPOINT);
206   - Device device = createDevice(clientCredentials);
207   -
208   - device.setSoftwareId(createSoftware().getId());
209   - device = doPost("/api/device", device, Device.class);
  221 + //given
  222 + final List<OtaPackageUpdateStatus> expectedStatuses = Collections.unmodifiableList(Arrays.asList(
  223 + QUEUED, INITIATED, DOWNLOADING, DOWNLOADING, DOWNLOADING, DOWNLOADED, VERIFIED, UPDATED));
210 224
211   - Thread.sleep(1000);
212   -
213   - client = new LwM2MTestClient(executor, "OTA_" + ENDPOINT);
214   - client.init(SECURITY, COAP_CONFIG);
215   -
216   - Thread.sleep(3000);
217   -
218   - List<TsKvEntry> ts = toTimeseries(doGetAsyncTyped("/api/plugins/telemetry/DEVICE/" + device.getId().getId() + "/values/timeseries?orderBy=ASC&keys=sw_state&startTs=0&endTs=" + System.currentTimeMillis(), new TypeReference<>() {
219   - }));
220   -
221   - List<OtaPackageUpdateStatus> statuses = ts.stream().sorted(Comparator.comparingLong(TsKvEntry::getTs)).map(KvEntry::getValueAsString).map(OtaPackageUpdateStatus::valueOf).collect(Collectors.toList());
  225 + createDeviceProfile(OTA_TRANSPORT_CONFIGURATION);
  226 + NoSecClientCredentials clientCredentials = new NoSecClientCredentials();
  227 + clientCredentials.setEndpoint("OTA_" + ENDPOINT);
  228 + final Device device = createDevice(clientCredentials);
  229 + device.setSoftwareId(createSoftware().getId());
  230 +
  231 + log.warn("Saving by API " + device);
  232 + final Device savedDevice = doPost("/api/device", device, Device.class);
  233 + Assert.assertNotNull(savedDevice);
  234 + log.warn("Device saved by API {}", savedDevice);
  235 +
  236 + log.warn("AWAIT atMost {} SECONDS on get device by API...", TIMEOUT);
  237 + await()
  238 + .atMost(TIMEOUT, TimeUnit.SECONDS)
  239 + .until(() -> getDeviceFromAPI(device.getId().getId()), is(savedDevice));
  240 + log.warn("Got device by API.");
  241 +
  242 + //when
  243 + log.warn("Init the client...");
  244 + client = new LwM2MTestClient(executor, "OTA_" + ENDPOINT);
  245 + client.init(SECURITY, COAP_CONFIG);
  246 + log.warn("Init done");
  247 +
  248 + log.warn("AWAIT atMost {} SECONDS on timeseries List<TsKvEntry> by API with list size {}...", TIMEOUT, expectedStatuses.size());
  249 + await()
  250 + .atMost(30, TimeUnit.SECONDS)
  251 + .until(() -> getSwStateTelemetryFromAPI(device.getId().getId())
  252 + .size(), is(expectedStatuses.size()));
  253 + log.warn("Got an expected await condition!");
  254 +
  255 + //then
  256 + log.warn("Fetching ts for the final asserts");
  257 + List<TsKvEntry> ts = getSwStateTelemetryFromAPI(device.getId().getId());
  258 + log.warn("Got an ts {}", ts);
  259 +
  260 + List<OtaPackageUpdateStatus> statuses = ts.stream().sorted(Comparator.comparingLong(TsKvEntry::getTs)).map(KvEntry::getValueAsString).map(OtaPackageUpdateStatus::valueOf).collect(Collectors.toList());
  261 + log.warn("Converted ts to statuses {}", statuses);
  262 +
  263 + Assert.assertEquals(expectedStatuses, statuses);
  264 + }
222 265
223   - List<OtaPackageUpdateStatus> expectedStatuses = Arrays.asList(QUEUED, INITIATED, DOWNLOADING, DOWNLOADING, DOWNLOADING, DOWNLOADED, VERIFIED, UPDATED);
  266 + private Device getDeviceFromAPI(UUID deviceId) throws Exception {
  267 + final Device device = doGet("/api/device/" + deviceId, Device.class);
  268 + log.warn("Fetched device by API for deviceId {}, device is {}", deviceId, device);
  269 + return device;
  270 + }
224 271
225   - Assert.assertEquals(expectedStatuses, statuses);
226   - } finally {
227   - if (client != null) {
228   - client.destroy();
229   - }
230   - }
  272 + private List<TsKvEntry> getSwStateTelemetryFromAPI(UUID deviceId) throws Exception {
  273 + final List<TsKvEntry> tsKvEntries = toTimeseries(doGetAsyncTyped("/api/plugins/telemetry/DEVICE/" + deviceId + "/values/timeseries?orderBy=ASC&keys=sw_state&startTs=0&endTs=" + System.currentTimeMillis(), new TypeReference<>() {
  274 + }));
  275 + log.warn("Fetched telemetry by API for deviceId {}, list size {}, tsKvEntries {}", deviceId, tsKvEntries.size(), tsKvEntries);
  276 + return tsKvEntries;
231 277 }
232 278 }
... ...
... ... @@ -46,7 +46,7 @@ public abstract class AbstractMqttServerSideRpcDefaultIntegrationTest extends Ab
46 46 String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"24\",\"value\": 1},\"timeout\": 6000}";
47 47 String deviceId = savedDevice.getId().getId().toString();
48 48
49   - doPostAsync("/api/plugins/rpc/oneway/" + deviceId, setGpioRequest, String.class, status().is(409),
  49 + doPostAsync("/api/rpc/oneway/" + deviceId, setGpioRequest, String.class, status().is(504),
50 50 asyncContextTimeoutToUseRpcPlugin);
51 51 }
52 52
... ... @@ -55,7 +55,7 @@ public abstract class AbstractMqttServerSideRpcDefaultIntegrationTest extends Ab
55 55 String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"25\",\"value\": 1}}";
56 56 String nonExistentDeviceId = Uuids.timeBased().toString();
57 57
58   - String result = doPostAsync("/api/plugins/rpc/oneway/" + nonExistentDeviceId, setGpioRequest, String.class,
  58 + String result = doPostAsync("/api/rpc/oneway/" + nonExistentDeviceId, setGpioRequest, String.class,
59 59 status().isNotFound());
60 60 Assert.assertEquals(AccessValidator.DEVICE_WITH_REQUESTED_ID_NOT_FOUND, result);
61 61 }
... ... @@ -65,7 +65,7 @@ public abstract class AbstractMqttServerSideRpcDefaultIntegrationTest extends Ab
65 65 String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"27\",\"value\": 1},\"timeout\": 6000}";
66 66 String deviceId = savedDevice.getId().getId().toString();
67 67
68   - doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().is(409),
  68 + doPostAsync("/api/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().is(504),
69 69 asyncContextTimeoutToUseRpcPlugin);
70 70 }
71 71
... ... @@ -74,7 +74,7 @@ public abstract class AbstractMqttServerSideRpcDefaultIntegrationTest extends Ab
74 74 String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"28\",\"value\": 1}}";
75 75 String nonExistentDeviceId = Uuids.timeBased().toString();
76 76
77   - String result = doPostAsync("/api/plugins/rpc/twoway/" + nonExistentDeviceId, setGpioRequest, String.class,
  77 + String result = doPostAsync("/api/rpc/twoway/" + nonExistentDeviceId, setGpioRequest, String.class,
78 78 status().isNotFound());
79 79 Assert.assertEquals(AccessValidator.DEVICE_WITH_REQUESTED_ID_NOT_FOUND, result);
80 80 }
... ...
... ... @@ -69,7 +69,7 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractM
69 69
70 70 String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"23\",\"value\": 1}}";
71 71 String deviceId = savedDevice.getId().getId().toString();
72   - String result = doPostAsync("/api/plugins/rpc/oneway/" + deviceId, setGpioRequest, String.class, status().isOk());
  72 + String result = doPostAsync("/api/rpc/oneway/" + deviceId, setGpioRequest, String.class, status().isOk());
73 73 Assert.assertTrue(StringUtils.isEmpty(result));
74 74 latch.await(3, TimeUnit.SECONDS);
75 75 assertEquals(MqttQoS.AT_MOST_ONCE.value(), callback.getQoS());
... ... @@ -95,7 +95,7 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractM
95 95 String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"26\",\"value\": 1}}";
96 96 String deviceId = savedDevice.getId().getId().toString();
97 97
98   - String result = doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().isOk());
  98 + String result = doPostAsync("/api/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().isOk());
99 99 String expected = "{\"value1\":\"A\",\"value2\":\"B\"}";
100 100 latch.await(3, TimeUnit.SECONDS);
101 101 Assert.assertEquals(expected, result);
... ... @@ -130,7 +130,7 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractM
130 130
131 131 String setGpioRequest = "{\"method\": \"toggle_gpio\", \"params\": {\"pin\":1}}";
132 132 String deviceId = savedDevice.getId().getId().toString();
133   - String result = doPostAsync("/api/plugins/rpc/oneway/" + deviceId, setGpioRequest, String.class, status().isOk());
  133 + String result = doPostAsync("/api/rpc/oneway/" + deviceId, setGpioRequest, String.class, status().isOk());
134 134 Assert.assertTrue(StringUtils.isEmpty(result));
135 135 latch.await(3, TimeUnit.SECONDS);
136 136 assertEquals(MqttQoS.AT_MOST_ONCE.value(), callback.getQoS());
... ... @@ -156,7 +156,7 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractM
156 156
157 157 String setGpioRequest = "{\"method\": \"toggle_gpio\", \"params\": {\"pin\":1}}";
158 158 String deviceId = savedDevice.getId().getId().toString();
159   - String result = doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().isOk());
  159 + String result = doPostAsync("/api/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().isOk());
160 160 latch.await(3, TimeUnit.SECONDS);
161 161 String expected = "{\"success\":true}";
162 162 assertEquals(expected, result);
... ...
... ... @@ -131,7 +131,7 @@ public abstract class AbstractMqttServerSideRpcProtoIntegrationTest extends Abst
131 131 String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"26\",\"value\": 1}}";
132 132 String deviceId = savedDevice.getId().getId().toString();
133 133
134   - String result = doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().isOk());
  134 + String result = doPostAsync("/api/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().isOk());
135 135 String expected = "{\"payload\":\"{\\\"value1\\\":\\\"A\\\",\\\"value2\\\":\\\"B\\\"}\"}";
136 136 latch.await(3, TimeUnit.SECONDS);
137 137 Assert.assertEquals(expected, result);
... ...
  1 +<!--
  2 +
  3 + Copyright © 2016-2021 The Thingsboard Authors
  4 +
  5 + Licensed under the Apache License, Version 2.0 (the "License");
  6 + you may not use this file except in compliance with the License.
  7 + You may obtain a copy of the License at
  8 +
  9 + http://www.apache.org/licenses/LICENSE-2.0
  10 +
  11 + Unless required by applicable law or agreed to in writing, software
  12 + distributed under the License is distributed on an "AS IS" BASIS,
  13 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14 + See the License for the specific language governing permissions and
  15 + limitations under the License.
  16 +
  17 +-->
  18 +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  19 + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  20 + <modelVersion>4.0.0</modelVersion>
  21 + <parent>
  22 + <groupId>org.thingsboard</groupId>
  23 + <version>3.3.0-SNAPSHOT</version>
  24 + <artifactId>common</artifactId>
  25 + </parent>
  26 + <groupId>org.thingsboard.common</groupId>
  27 + <artifactId>cluster-api</artifactId>
  28 + <packaging>jar</packaging>
  29 +
  30 + <name>Thingsboard Server Common Cluster API</name>
  31 + <url>https://thingsboard.io</url>
  32 +
  33 + <properties>
  34 + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  35 + <main.dir>${basedir}/../..</main.dir>
  36 + </properties>
  37 +
  38 + <dependencies>
  39 + <dependency>
  40 + <groupId>org.thingsboard.common</groupId>
  41 + <artifactId>data</artifactId>
  42 + </dependency>
  43 + <dependency>
  44 + <groupId>org.thingsboard.common</groupId>
  45 + <artifactId>message</artifactId>
  46 + </dependency>
  47 + <dependency>
  48 + <groupId>org.thingsboard.common</groupId>
  49 + <artifactId>stats</artifactId>
  50 + </dependency>
  51 + <dependency>
  52 + <groupId>com.google.guava</groupId>
  53 + <artifactId>guava</artifactId>
  54 + </dependency>
  55 + <dependency>
  56 + <groupId>javax.annotation</groupId>
  57 + <artifactId>javax.annotation-api</artifactId>
  58 + </dependency>
  59 + <dependency>
  60 + <groupId>com.github.fge</groupId>
  61 + <artifactId>json-schema-validator</artifactId>
  62 + </dependency>
  63 + <dependency>
  64 + <groupId>org.slf4j</groupId>
  65 + <artifactId>slf4j-api</artifactId>
  66 + </dependency>
  67 + <dependency>
  68 + <groupId>org.slf4j</groupId>
  69 + <artifactId>log4j-over-slf4j</artifactId>
  70 + </dependency>
  71 + <dependency>
  72 + <groupId>ch.qos.logback</groupId>
  73 + <artifactId>logback-core</artifactId>
  74 + </dependency>
  75 + <dependency>
  76 + <groupId>ch.qos.logback</groupId>
  77 + <artifactId>logback-classic</artifactId>
  78 + </dependency>
  79 + <dependency>
  80 + <groupId>com.fasterxml.jackson.core</groupId>
  81 + <artifactId>jackson-databind</artifactId>
  82 + </dependency>
  83 + <dependency>
  84 + <groupId>org.springframework.boot</groupId>
  85 + <artifactId>spring-boot-autoconfigure</artifactId>
  86 + <scope>provided</scope>
  87 + </dependency>
  88 + <dependency>
  89 + <groupId>com.datastax.oss</groupId>
  90 + <artifactId>java-driver-core</artifactId>
  91 + <scope>provided</scope>
  92 + </dependency>
  93 + <dependency>
  94 + <groupId>io.dropwizard.metrics</groupId>
  95 + <artifactId>metrics-jmx</artifactId>
  96 + <scope>provided</scope>
  97 + </dependency>
  98 + <dependency>
  99 + <groupId>org.apache.commons</groupId>
  100 + <artifactId>commons-lang3</artifactId>
  101 + <scope>provided</scope>
  102 + </dependency>
  103 + <dependency>
  104 + <groupId>junit</groupId>
  105 + <artifactId>junit</artifactId>
  106 + <scope>test</scope>
  107 + </dependency>
  108 + <dependency>
  109 + <groupId>org.mockito</groupId>
  110 + <artifactId>mockito-core</artifactId>
  111 + <scope>test</scope>
  112 + </dependency>
  113 + </dependencies>
  114 +
  115 + <build>
  116 + <plugins>
  117 + <plugin>
  118 + <groupId>org.xolstice.maven.plugins</groupId>
  119 + <artifactId>protobuf-maven-plugin</artifactId>
  120 + </plugin>
  121 + <plugin>
  122 + <groupId>org.apache.maven.plugins</groupId>
  123 + <artifactId>maven-source-plugin</artifactId>
  124 + <executions>
  125 + <execution>
  126 + <id>attach-sources</id>
  127 + <goals>
  128 + <goal>jar</goal>
  129 + </goals>
  130 + </execution>
  131 + </executions>
  132 + </plugin>
  133 + <plugin>
  134 + <groupId>org.apache.maven.plugins</groupId>
  135 + <artifactId>maven-deploy-plugin</artifactId>
  136 + <configuration>
  137 + <skip>false</skip>
  138 + </configuration>
  139 + </plugin>
  140 + </plugins>
  141 + </build>
  142 +
  143 +
  144 +</project>
... ...
common/cluster-api/src/main/java/org/thingsboard/server/cluster/TbClusterService.java renamed from application/src/main/java/org/thingsboard/server/service/queue/TbClusterService.java
... ... @@ -13,26 +13,29 @@
13 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
16   -package org.thingsboard.server.service.queue;
  16 +package org.thingsboard.server.cluster;
17 17
18   -import org.thingsboard.rule.engine.api.msg.ToDeviceActorNotificationMsg;
  18 +import org.thingsboard.server.common.data.edge.EdgeEventActionType;
  19 +import org.thingsboard.server.common.data.edge.EdgeEventType;
  20 +import org.thingsboard.server.common.msg.ToDeviceActorNotificationMsg;
19 21 import org.thingsboard.server.common.data.ApiUsageState;
20 22 import org.thingsboard.server.common.data.Device;
21 23 import org.thingsboard.server.common.data.DeviceProfile;
22 24 import org.thingsboard.server.common.data.TbResource;
23 25 import org.thingsboard.server.common.data.Tenant;
24 26 import org.thingsboard.server.common.data.TenantProfile;
  27 +import org.thingsboard.server.common.data.id.DeviceId;
25 28 import org.thingsboard.server.common.data.id.EdgeId;
26 29 import org.thingsboard.server.common.data.id.EntityId;
27 30 import org.thingsboard.server.common.data.id.TenantId;
28 31 import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
29 32 import org.thingsboard.server.common.msg.TbMsg;
30 33 import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
  34 +import org.thingsboard.server.common.msg.rpc.FromDeviceRpcResponse;
31 35 import org.thingsboard.server.gen.transport.TransportProtos;
32 36 import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg;
33 37 import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg;
34 38 import org.thingsboard.server.queue.TbQueueCallback;
35   -import org.thingsboard.server.service.rpc.FromDeviceRpcResponse;
36 39
37 40 import java.util.UUID;
38 41
... ... @@ -54,7 +57,7 @@ public interface TbClusterService {
54 57
55 58 void pushNotificationToTransport(String targetServiceId, ToTransportMsg response, TbQueueCallback callback);
56 59
57   - void onEntityStateChange(TenantId tenantId, EntityId entityId, ComponentLifecycleEvent state);
  60 + void broadcastEntityStateChangeEvent(TenantId tenantId, EntityId entityId, ComponentLifecycleEvent state);
58 61
59 62 void onDeviceProfileChange(DeviceProfile deviceProfile, TbQueueCallback callback);
60 63
... ... @@ -70,7 +73,7 @@ public interface TbClusterService {
70 73
71 74 void onApiStateChange(ApiUsageState apiUsageState, TbQueueCallback callback);
72 75
73   - void onDeviceChange(Device device, TbQueueCallback callback);
  76 + void onDeviceUpdated(Device device, Device old);
74 77
75 78 void onDeviceDeleted(Device device, TbQueueCallback callback);
76 79
... ... @@ -79,4 +82,6 @@ public interface TbClusterService {
79 82 void onResourceDeleted(TbResource resource, TbQueueCallback callback);
80 83
81 84 void onEdgeEventUpdate(TenantId tenantId, EdgeId edgeId);
  85 +
  86 + void sendNotificationMsgToEdgeService(TenantId tenantId, EdgeId edgeId, EntityId entityId, String body, EdgeEventType type, EdgeEventActionType action);
82 87 }
... ...
common/cluster-api/src/main/java/org/thingsboard/server/queue/TbQueueAdmin.java renamed from common/queue/src/main/java/org/thingsboard/server/queue/TbQueueAdmin.java
common/cluster-api/src/main/java/org/thingsboard/server/queue/TbQueueCallback.java renamed from common/queue/src/main/java/org/thingsboard/server/queue/TbQueueCallback.java
common/cluster-api/src/main/java/org/thingsboard/server/queue/TbQueueConsumer.java renamed from common/queue/src/main/java/org/thingsboard/server/queue/TbQueueConsumer.java
common/cluster-api/src/main/java/org/thingsboard/server/queue/TbQueueHandler.java renamed from common/queue/src/main/java/org/thingsboard/server/queue/TbQueueHandler.java
common/cluster-api/src/main/java/org/thingsboard/server/queue/TbQueueMsg.java renamed from common/queue/src/main/java/org/thingsboard/server/queue/TbQueueMsg.java
common/cluster-api/src/main/java/org/thingsboard/server/queue/TbQueueMsgDecoder.java renamed from common/queue/src/main/java/org/thingsboard/server/queue/TbQueueMsgDecoder.java
common/cluster-api/src/main/java/org/thingsboard/server/queue/TbQueueMsgHeaders.java renamed from common/queue/src/main/java/org/thingsboard/server/queue/TbQueueMsgHeaders.java
common/cluster-api/src/main/java/org/thingsboard/server/queue/TbQueueMsgMetadata.java renamed from common/queue/src/main/java/org/thingsboard/server/queue/TbQueueMsgMetadata.java
common/cluster-api/src/main/java/org/thingsboard/server/queue/TbQueueProducer.java renamed from common/queue/src/main/java/org/thingsboard/server/queue/TbQueueProducer.java
common/cluster-api/src/main/java/org/thingsboard/server/queue/TbQueueRequestTemplate.java renamed from common/queue/src/main/java/org/thingsboard/server/queue/TbQueueRequestTemplate.java
common/cluster-api/src/main/java/org/thingsboard/server/queue/TbQueueResponseTemplate.java renamed from common/queue/src/main/java/org/thingsboard/server/queue/TbQueueResponseTemplate.java
common/cluster-api/src/main/proto/jsinvoke.proto renamed from common/queue/src/main/proto/jsinvoke.proto
common/cluster-api/src/main/proto/queue.proto renamed from common/queue/src/main/proto/queue.proto
... ... @@ -54,6 +54,8 @@ public interface DeviceService {
54 54
55 55 Device saveDeviceWithCredentials(Device device, DeviceCredentials deviceCredentials);
56 56
  57 + Device saveDevice(ProvisionRequest provisionRequest, DeviceProfile profile);
  58 +
57 59 void createAccessTokenCredentials(Device device, String accessToken);
58 60
59 61 Device assignDeviceToCustomer(TenantId tenantId, DeviceId deviceId, CustomerId customerId);
... ... @@ -100,8 +102,6 @@ public interface DeviceService {
100 102
101 103 Device assignDeviceToTenant(TenantId tenantId, Device device);
102 104
103   - Device saveDevice(ProvisionRequest provisionRequest, DeviceProfile profile);
104   -
105 105 PageData<UUID> findDevicesIdsByDeviceProfileTransportType(DeviceTransportType transportType, PageLink pageLink);
106 106
107 107 Device assignDeviceToEdge(TenantId tenantId, DeviceId deviceId, EdgeId edgeId);
... ...