Commit f673b0944bd08c144b0ffb8d09ce6c29ac25a082

Authored by Andrew Shvayka
Committed by GitHub
2 parents 5a724799 37b88908

Merge pull request #4248 from ViacheslavKlimov/feature/merge-3.3-into-snmp

Merge develop/3.3 into develop/snmp
Showing 100 changed files with 2174 additions and 575 deletions

Too many changes to show.

To preserve performance only 100 of 703 files are displayed.

@@ -20,7 +20,7 @@ @@ -20,7 +20,7 @@
20 <modelVersion>4.0.0</modelVersion> 20 <modelVersion>4.0.0</modelVersion>
21 <parent> 21 <parent>
22 <groupId>org.thingsboard</groupId> 22 <groupId>org.thingsboard</groupId>
23 - <version>3.2.1-SNAPSHOT</version> 23 + <version>3.3.0-SNAPSHOT</version>
24 <artifactId>thingsboard</artifactId> 24 <artifactId>thingsboard</artifactId>
25 </parent> 25 </parent>
26 <artifactId>application</artifactId> 26 <artifactId>application</artifactId>
@@ -87,6 +87,10 @@ @@ -87,6 +87,10 @@
87 </dependency> 87 </dependency>
88 <dependency> 88 <dependency>
89 <groupId>org.thingsboard.common.transport</groupId> 89 <groupId>org.thingsboard.common.transport</groupId>
  90 + <artifactId>lwm2m</artifactId>
  91 + </dependency>
  92 + <dependency>
  93 + <groupId>org.thingsboard.common.transport</groupId>
90 <artifactId>snmp</artifactId> 94 <artifactId>snmp</artifactId>
91 </dependency> 95 </dependency>
92 <dependency> 96 <dependency>
@@ -279,7 +283,7 @@ @@ -279,7 +283,7 @@
279 </dependency> 283 </dependency>
280 <dependency> 284 <dependency>
281 <groupId>org.mockito</groupId> 285 <groupId>org.mockito</groupId>
282 - <artifactId>mockito-all</artifactId> 286 + <artifactId>mockito-core</artifactId>
283 <scope>test</scope> 287 <scope>test</scope>
284 </dependency> 288 </dependency>
285 <dependency> 289 <dependency>
@@ -15,11 +15,10 @@ @@ -15,11 +15,10 @@
15 # 15 #
16 16
17 export JAVA_OPTS="$JAVA_OPTS -Dplatform=@pkg.platform@ -Dinstall.data_dir=@pkg.installFolder@/data" 17 export JAVA_OPTS="$JAVA_OPTS -Dplatform=@pkg.platform@ -Dinstall.data_dir=@pkg.installFolder@/data"
18 -export JAVA_OPTS="$JAVA_OPTS -Xloggc:@pkg.logFolder@/gc.log -XX:+IgnoreUnrecognizedVMOptions -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDetails -XX:+PrintGCDateStamps"  
19 -export JAVA_OPTS="$JAVA_OPTS -XX:+PrintHeapAtGC -XX:+PrintTenuringDistribution -XX:+PrintGCApplicationStoppedTime -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10"  
20 -export JAVA_OPTS="$JAVA_OPTS -XX:GCLogFileSize=10M -XX:-UseBiasedLocking -XX:+UseTLAB -XX:+ResizeTLAB -XX:+PerfDisableSharedMem -XX:+UseCondCardMark"  
21 -export JAVA_OPTS="$JAVA_OPTS -XX:CMSWaitDuration=10000 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+CMSParallelInitialMarkEnabled"  
22 -export JAVA_OPTS="$JAVA_OPTS -XX:+CMSEdenChunksRecordAlways -XX:CMSInitiatingOccupancyFraction=75 -XX:+UseCMSInitiatingOccupancyOnly" 18 +export JAVA_OPTS="$JAVA_OPTS -Xlog:gc*,heap*,age*,safepoint=debug:file=@pkg.logFolder@/gc.log:time,uptime,level,tags:filecount=10,filesize=10M"
  19 +export JAVA_OPTS="$JAVA_OPTS -XX:+IgnoreUnrecognizedVMOptions -XX:+HeapDumpOnOutOfMemoryError"
  20 +export JAVA_OPTS="$JAVA_OPTS -XX:-UseBiasedLocking -XX:+UseTLAB -XX:+ResizeTLAB -XX:+PerfDisableSharedMem -XX:+UseCondCardMark"
  21 +export JAVA_OPTS="$JAVA_OPTS -XX:+UseG1GC -XX:MaxGCPauseMillis=500 -XX:+UseStringDeduplication -XX:+ParallelRefProcEnabled -XX:MaxTenuringThreshold=10"
23 export LOG_FILENAME=${pkg.name}.out 22 export LOG_FILENAME=${pkg.name}.out
24 export LOADER_PATH=${pkg.installFolder}/conf,${pkg.installFolder}/extensions 23 export LOADER_PATH=${pkg.installFolder}/conf,${pkg.installFolder}/extensions
25 export SQL_DATA_FOLDER=${pkg.installFolder}/data/sql 24 export SQL_DATA_FOLDER=${pkg.installFolder}/data/sql
@@ -47,7 +47,7 @@ @@ -47,7 +47,7 @@
47 "resources": [], 47 "resources": [],
48 "templateHtml": "<tb-timeseries-table-widget \n [ctx]=\"ctx\">\n</tb-timeseries-table-widget>", 48 "templateHtml": "<tb-timeseries-table-widget \n [ctx]=\"ctx\">\n</tb-timeseries-table-widget>",
49 "templateCss": "", 49 "templateCss": "",
50 - "controllerScript": "self.onInit = function() {\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.timeseriesTableWidget.onDataUpdated();\n}\n\nself.actionSources = function() {\n return {\n 'actionCellButton': {\n name: 'widget-action.action-cell-button',\n multiple: true\n },\n 'rowClick': {\n name: 'widget-action.row-click',\n multiple: false\n }\n };\n}\n\nself.onDestroy = function() {\n}", 50 + "controllerScript": "self.onInit = function() {\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.timeseriesTableWidget.onDataUpdated();\n}\n\nself.typeParameters = function() {\n return {\n ignoreDataUpdateOnIntervalTick: true\n };\n}\n\nself.actionSources = function() {\n return {\n 'actionCellButton': {\n name: 'widget-action.action-cell-button',\n multiple: true\n },\n 'rowClick': {\n name: 'widget-action.row-click',\n multiple: false\n }\n };\n}\n\nself.onDestroy = function() {\n}",
51 "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"TimeseriesTableSettings\",\n \"properties\": {\n \"showTimestamp\": {\n \"title\": \"Display timestamp column\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"showMilliseconds\": {\n \"title\": \"Display timestamp milliseconds\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"displayPagination\": {\n \"title\": \"Display pagination\",\n \"type\": \"boolean\",\n \"default\": true\n }, \n \"defaultPageSize\": {\n \"title\": \"Default page size\",\n \"type\": \"number\",\n \"default\": 10\n },\n \"hideEmptyLines\": {\n \"title\": \"Hide empty lines\",\n \"type\": \"boolean\",\n \"default\": false\n }\n },\n \"required\": []\n },\n \"form\": [\n \"showTimestamp\",\n \"showMilliseconds\",\n \"displayPagination\",\n \"defaultPageSize\",\n \"hideEmptyLines\"\n ]\n}", 51 "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"TimeseriesTableSettings\",\n \"properties\": {\n \"showTimestamp\": {\n \"title\": \"Display timestamp column\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"showMilliseconds\": {\n \"title\": \"Display timestamp milliseconds\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"displayPagination\": {\n \"title\": \"Display pagination\",\n \"type\": \"boolean\",\n \"default\": true\n }, \n \"defaultPageSize\": {\n \"title\": \"Default page size\",\n \"type\": \"number\",\n \"default\": 10\n },\n \"hideEmptyLines\": {\n \"title\": \"Hide empty lines\",\n \"type\": \"boolean\",\n \"default\": false\n }\n },\n \"required\": []\n },\n \"form\": [\n \"showTimestamp\",\n \"showMilliseconds\",\n \"displayPagination\",\n \"defaultPageSize\",\n \"hideEmptyLines\"\n ]\n}",
52 "dataKeySettingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"DataKeySettings\",\n \"properties\": {\n \"useCellStyleFunction\": {\n \"title\": \"Use cell style function\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"cellStyleFunction\": {\n \"title\": \"Cell style function: f(value)\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"useCellContentFunction\": {\n \"title\": \"Use cell content function\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"cellContentFunction\": {\n \"title\": \"Cell content function: f(value, rowData, ctx)\",\n \"type\": \"string\",\n \"default\": \"\"\n }\n },\n \"required\": []\n },\n \"form\": [\n \"useCellStyleFunction\",\n {\n \"key\": \"cellStyleFunction\",\n \"type\": \"javascript\"\n },\n \"useCellContentFunction\",\n {\n \"key\": \"cellContentFunction\",\n \"type\": \"javascript\"\n }\n ]\n}", 52 "dataKeySettingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"DataKeySettings\",\n \"properties\": {\n \"useCellStyleFunction\": {\n \"title\": \"Use cell style function\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"cellStyleFunction\": {\n \"title\": \"Cell style function: f(value)\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"useCellContentFunction\": {\n \"title\": \"Use cell content function\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"cellContentFunction\": {\n \"title\": \"Cell content function: f(value, rowData, ctx)\",\n \"type\": \"string\",\n \"default\": \"\"\n }\n },\n \"required\": []\n },\n \"form\": [\n \"useCellStyleFunction\",\n {\n \"key\": \"cellStyleFunction\",\n \"type\": \"javascript\"\n },\n \"useCellContentFunction\",\n {\n \"key\": \"cellContentFunction\",\n \"type\": \"javascript\"\n }\n ]\n}",
53 "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temperature °C\",\"color\":\"#2196f3\",\"settings\":{\"useCellStyleFunction\":true,\"cellStyleFunction\":\"if (value) {\\n var percent = (value + 60)/120 * 100;\\n var color = tinycolor.mix('blue', 'red', amount = percent);\\n color.setAlpha(.5);\\n return {\\n paddingLeft: '20px',\\n color: '#ffffff',\\n background: color.toRgbString(),\\n fontSize: '18px'\\n };\\n} else {\\n return {};\\n}\"},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nvar multiplier = Math.pow(10, 1 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Humidity, %\",\"color\":\"#ffc107\",\"settings\":{\"useCellStyleFunction\":true,\"cellStyleFunction\":\"if (value) {\\n var percent = value;\\n var backgroundColor = tinycolor('blue');\\n backgroundColor.setAlpha(value/100);\\n var color = 'blue';\\n if (value > 50) {\\n color = 'white';\\n }\\n \\n return {\\n paddingLeft: '20px',\\n color: color,\\n background: backgroundColor.toRgbString(),\\n fontSize: '18px'\\n };\\n} else {\\n return {};\\n}\",\"useCellContentFunction\":false},\"_hash\":0.12775350966079668,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nvar multiplier = Math.pow(10, 1 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 5) {\\n\\tvalue = 5;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"interval\":1000,\"timewindowMs\":60000},\"aggregation\":{\"type\":\"NONE\",\"limit\":200}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"showTimestamp\":true,\"displayPagination\":true,\"defaultPageSize\":10},\"title\":\"Timeseries table\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400,\"padding\":\"5px 10px 5px 10px\"},\"useDashboardTimewindow\":false,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"showTitleIcon\":false,\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\"}" 53 "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temperature °C\",\"color\":\"#2196f3\",\"settings\":{\"useCellStyleFunction\":true,\"cellStyleFunction\":\"if (value) {\\n var percent = (value + 60)/120 * 100;\\n var color = tinycolor.mix('blue', 'red', amount = percent);\\n color.setAlpha(.5);\\n return {\\n paddingLeft: '20px',\\n color: '#ffffff',\\n background: color.toRgbString(),\\n fontSize: '18px'\\n };\\n} else {\\n return {};\\n}\"},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nvar multiplier = Math.pow(10, 1 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Humidity, %\",\"color\":\"#ffc107\",\"settings\":{\"useCellStyleFunction\":true,\"cellStyleFunction\":\"if (value) {\\n var percent = value;\\n var backgroundColor = tinycolor('blue');\\n backgroundColor.setAlpha(value/100);\\n var color = 'blue';\\n if (value > 50) {\\n color = 'white';\\n }\\n \\n return {\\n paddingLeft: '20px',\\n color: color,\\n background: backgroundColor.toRgbString(),\\n fontSize: '18px'\\n };\\n} else {\\n return {};\\n}\",\"useCellContentFunction\":false},\"_hash\":0.12775350966079668,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nvar multiplier = Math.pow(10, 1 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 5) {\\n\\tvalue = 5;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"interval\":1000,\"timewindowMs\":60000},\"aggregation\":{\"type\":\"NONE\",\"limit\":200}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"showTimestamp\":true,\"displayPagination\":true,\"defaultPageSize\":10},\"title\":\"Timeseries table\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400,\"padding\":\"5px 10px 5px 10px\"},\"useDashboardTimewindow\":false,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"showTitleIcon\":false,\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\"}"
@@ -134,4 +134,4 @@ @@ -134,4 +134,4 @@
134 } 134 }
135 } 135 }
136 ] 136 ]
137 -}  
  137 +}
@@ -26,22 +26,6 @@ @@ -26,22 +26,6 @@
26 } 26 }
27 }, 27 },
28 { 28 {
29 - "alias": "basic_timeseries",  
30 - "name": "Timeseries - Flot",  
31 - "descriptor": {  
32 - "type": "timeseries",  
33 - "sizeX": 8,  
34 - "sizeY": 5,  
35 - "resources": [],  
36 - "templateHtml": "",  
37 - "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n",  
38 - "controllerScript": "self.onInit = function() {\n self.ctx.flot = new TbFlot(self.ctx); \n}\n\nself.onDataUpdated = function() {\n self.ctx.flot.update();\n}\n\nself.onResize = function() {\n self.ctx.flot.resize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.flot.checkMouseEvents();\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.flot.checkMouseEvents();\n}\n\nself.getSettingsSchema = function() {\n return TbFlot.settingsSchema('graph');\n}\n\nself.getDataKeySettingsSchema = function() {\n return TbFlot.datakeySettingsSchema(true, 'graph');\n}\n\nself.onDestroy = function() {\n self.ctx.flot.destroy();\n}\n",  
39 - "settingsSchema": "{}",  
40 - "dataKeySettingsSchema": "{}",  
41 - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#ffc107\",\"settings\":{\"showLines\":true,\"fillLines\":false,\"showPoints\":false},\"_hash\":0.12775350966079668,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"shadowSize\":4,\"fontColor\":\"#545454\",\"fontSize\":10,\"xaxis\":{\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"showLabels\":true,\"color\":\"#545454\"},\"grid\":{\"color\":\"#545454\",\"tickColor\":\"#DDDDDD\",\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1},\"legend\":{\"show\":true,\"position\":\"nw\",\"backgroundColor\":\"#f0f0f0\",\"backgroundOpacity\":0.85,\"labelBoxBorderColor\":\"rgba(1, 1, 1, 0.45)\"},\"decimals\":1,\"stack\":false,\"tooltipIndividual\":false},\"title\":\"Timeseries - Flot\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null}"  
42 - }  
43 - },  
44 - {  
45 "alias": "doughnut_chart_js", 29 "alias": "doughnut_chart_js",
46 "name": "Doughnut - Chart.js", 30 "name": "Doughnut - Chart.js",
47 "descriptor": { 31 "descriptor": {
@@ -71,7 +55,7 @@ @@ -71,7 +55,7 @@
71 "resources": [], 55 "resources": [],
72 "templateHtml": "", 56 "templateHtml": "",
73 "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.pie-label {\n font-size: 12px;\n font-family: 'Roboto';\n font-weight: bold;\n text-align: center;\n padding: 2px;\n color: white;\n}\n", 57 "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.pie-label {\n font-size: 12px;\n font-family: 'Roboto';\n font-weight: bold;\n text-align: center;\n padding: 2px;\n color: white;\n}\n",
74 - "controllerScript": "self.onInit = function() {\n self.ctx.flot = new TbFlot(self.ctx, 'pie'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.flot.update();\n}\n\nself.onResize = function() {\n self.ctx.flot.resize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.flot.checkMouseEvents();\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.flot.checkMouseEvents();\n}\n\nself.getSettingsSchema = function() {\n return TbFlot.pieSettingsSchema();\n}\n\nself.getDataKeySettingsSchema = function() {\n return TbFlot.pieDatakeySettingsSchema();\n}\n\nself.onDestroy = function() {\n self.ctx.flot.destroy();\n}\nself.actionSources = function() {\n return {\n 'sliceClick': {\n name: 'widget-action.pie-slice-click',\n multiple: false\n }\n };\n}\n", 58 + "controllerScript": "self.onInit = function() {\n self.ctx.flot = new TbFlot(self.ctx, 'pie'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.flot.update();\n}\n\nself.onResize = function() {\n self.ctx.flot.resize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.flot.checkMouseEvents();\n}\n\nself.getSettingsSchema = function() {\n return TbFlot.pieSettingsSchema();\n}\n\nself.getDataKeySettingsSchema = function() {\n return TbFlot.pieDatakeySettingsSchema();\n}\n\nself.onDestroy = function() {\n self.ctx.flot.destroy();\n}\nself.actionSources = function() {\n return {\n 'sliceClick': {\n name: 'widget-action.pie-slice-click',\n multiple: false\n }\n };\n}\n",
75 "settingsSchema": "{}\n", 59 "settingsSchema": "{}\n",
76 "dataKeySettingsSchema": "{}\n", 60 "dataKeySettingsSchema": "{}\n",
77 "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.6114638304362894,\"funcBody\":\"var value = (prevValue-20) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+20;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Third\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.9955906536344441,\"funcBody\":\"var value = (prevValue-40) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+40;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Fourth\",\"color\":\"#ffc107\",\"settings\":{},\"_hash\":0.9430835931647599,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"radius\":1,\"fontColor\":\"#545454\",\"fontSize\":10,\"decimals\":1,\"legend\":{\"show\":true,\"position\":\"nw\",\"labelBoxBorderColor\":\"#CCCCCC\",\"backgroundColor\":\"#F0F0F0\",\"backgroundOpacity\":0.85},\"innerRadius\":0,\"showLabels\":true,\"showPercentages\":true,\"stroke\":{\"width\":5},\"tilt\":1,\"animatedPie\":false},\"title\":\"Pie - Flot\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}" 61 "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.6114638304362894,\"funcBody\":\"var value = (prevValue-20) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+20;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Third\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.9955906536344441,\"funcBody\":\"var value = (prevValue-40) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+40;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Fourth\",\"color\":\"#ffc107\",\"settings\":{},\"_hash\":0.9430835931647599,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"radius\":1,\"fontColor\":\"#545454\",\"fontSize\":10,\"decimals\":1,\"legend\":{\"show\":true,\"position\":\"nw\",\"labelBoxBorderColor\":\"#CCCCCC\",\"backgroundColor\":\"#F0F0F0\",\"backgroundOpacity\":0.85},\"innerRadius\":0,\"showLabels\":true,\"showPercentages\":true,\"stroke\":{\"width\":5},\"tilt\":1,\"animatedPie\":false},\"title\":\"Pie - Flot\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}"
@@ -138,8 +122,8 @@ @@ -138,8 +122,8 @@
138 } 122 }
139 }, 123 },
140 { 124 {
141 - "alias": "timeseries_bars_flot",  
142 - "name": "Timeseries Bars - Flot", 125 + "alias": "state_chart",
  126 + "name": "State Chart",
143 "descriptor": { 127 "descriptor": {
144 "type": "timeseries", 128 "type": "timeseries",
145 "sizeX": 8, 129 "sizeX": 8,
@@ -147,15 +131,15 @@ @@ -147,15 +131,15 @@
147 "resources": [], 131 "resources": [],
148 "templateHtml": "", 132 "templateHtml": "",
149 "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", 133 "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n",
150 - "controllerScript": "self.onInit = function() {\n self.ctx.flot = new TbFlot(self.ctx, 'bar'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.flot.update();\n}\n\nself.onResize = function() {\n self.ctx.flot.resize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.flot.checkMouseEvents();\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.flot.checkMouseEvents();\n}\n\nself.getSettingsSchema = function() {\n return TbFlot.settingsSchema('bar');\n}\n\nself.getDataKeySettingsSchema = function() {\n return TbFlot.datakeySettingsSchema(false, 'bar');\n}\n\nself.onDestroy = function() {\n self.ctx.flot.destroy();\n}\n", 134 + "controllerScript": "self.onInit = function() {\n self.ctx.flot = new TbFlot(self.ctx, 'state'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.flot.update();\n}\n\nself.onResize = function() {\n self.ctx.flot.resize();\n}\n\nself.typeParameters = function() {\n return {\n stateData: true\n };\n}\n\nself.onEditModeChanged = function() {\n self.ctx.flot.checkMouseEvents();\n}\n\nself.getSettingsSchema = function() {\n return TbFlot.settingsSchema('graph');\n}\n\nself.getDataKeySettingsSchema = function() {\n return TbFlot.datakeySettingsSchema(true, 'graph');\n}\n\nself.onDestroy = function() {\n self.ctx.flot.destroy();\n}\n",
151 "settingsSchema": "{}", 135 "settingsSchema": "{}",
152 "dataKeySettingsSchema": "{}", 136 "dataKeySettingsSchema": "{}",
153 - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{\"showLines\":false,\"fillLines\":false,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#ffc107\",\"settings\":{\"showLines\":false,\"fillLines\":false,\"showPoints\":false},\"_hash\":0.12775350966079668,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000},\"aggregation\":{\"limit\":200,\"type\":\"AVG\"}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"shadowSize\":4,\"fontColor\":\"#545454\",\"fontSize\":10,\"xaxis\":{\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"showLabels\":true,\"color\":\"#545454\"},\"grid\":{\"color\":\"#545454\",\"tickColor\":\"#DDDDDD\",\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1},\"stack\":true,\"tooltipIndividual\":false,\"defaultBarWidth\":600},\"title\":\"Timeseries Bars - Flot\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"widgetStyle\":{},\"useDashboardTimewindow\":true,\"showLegend\":true,\"actions\":{}}" 137 + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Switch 1\",\"color\":\"#2196f3\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false,\"axisPosition\":\"left\",\"showSeparateAxis\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"return Math.random() > 0.5 ? 1 : 0;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Switch 2\",\"color\":\"#ffc107\",\"settings\":{\"showLines\":true,\"fillLines\":false,\"showPoints\":false,\"axisPosition\":\"left\"},\"_hash\":0.12775350966079668,\"funcBody\":\"return Math.random() <= 0.5 ? 1 : 0;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"shadowSize\":4,\"fontColor\":\"#545454\",\"fontSize\":10,\"xaxis\":{\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"showLabels\":true,\"color\":\"#545454\",\"ticksFormatter\":\"if (value > 0 && value <= 1) {\\n return 'On';\\n} else if (value === 0) {\\n return 'Off';\\n} else {\\n return '';\\n}\"},\"grid\":{\"color\":\"#545454\",\"tickColor\":\"#DDDDDD\",\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1},\"stack\":false,\"tooltipIndividual\":false,\"tooltipValueFormatter\":\"if (value > 0 && value <= 1) {\\n return 'On';\\n} else if (value === 0) {\\n return 'Off';\\n} else {\\n return '';\\n}\",\"smoothLines\":false},\"title\":\"State Chart\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"widgetStyle\":{},\"useDashboardTimewindow\":true,\"showLegend\":true,\"actions\":{},\"legendConfig\":{\"direction\":\"column\",\",position\":\"bottom\",\"showMin\":false,\"showMax\":false,\"showAvg\":false,\"showTotal\":false}}"
154 } 138 }
155 }, 139 },
156 { 140 {
157 - "alias": "state_chart",  
158 - "name": "State Chart", 141 + "alias": "basic_timeseries",
  142 + "name": "Timeseries - Flot",
159 "descriptor": { 143 "descriptor": {
160 "type": "timeseries", 144 "type": "timeseries",
161 "sizeX": 8, 145 "sizeX": 8,
@@ -163,11 +147,27 @@ @@ -163,11 +147,27 @@
163 "resources": [], 147 "resources": [],
164 "templateHtml": "", 148 "templateHtml": "",
165 "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", 149 "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n",
166 - "controllerScript": "self.onInit = function() {\n self.ctx.flot = new TbFlot(self.ctx, 'state'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.flot.update();\n}\n\nself.onResize = function() {\n self.ctx.flot.resize();\n}\n\nself.typeParameters = function() {\n return {\n stateData: true\n };\n}\n\nself.onEditModeChanged = function() {\n self.ctx.flot.checkMouseEvents();\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.flot.checkMouseEvents();\n}\n\nself.getSettingsSchema = function() {\n return TbFlot.settingsSchema('graph');\n}\n\nself.getDataKeySettingsSchema = function() {\n return TbFlot.datakeySettingsSchema(true, 'graph');\n}\n\nself.onDestroy = function() {\n self.ctx.flot.destroy();\n}\n", 150 + "controllerScript": "self.onInit = function() {\n self.ctx.flot = new TbFlot(self.ctx); \n}\n\nself.onDataUpdated = function() {\n self.ctx.flot.update();\n}\n\nself.onResize = function() {\n self.ctx.flot.resize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.flot.checkMouseEvents();\n}\n\nself.getSettingsSchema = function() {\n return TbFlot.settingsSchema('graph');\n}\n\nself.getDataKeySettingsSchema = function() {\n return TbFlot.datakeySettingsSchema(true, 'graph');\n}\n\nself.onDestroy = function() {\n self.ctx.flot.destroy();\n}\n",
167 "settingsSchema": "{}", 151 "settingsSchema": "{}",
168 "dataKeySettingsSchema": "{}", 152 "dataKeySettingsSchema": "{}",
169 - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Switch 1\",\"color\":\"#2196f3\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false,\"axisPosition\":\"left\",\"showSeparateAxis\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"return Math.random() > 0.5 ? 1 : 0;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Switch 2\",\"color\":\"#ffc107\",\"settings\":{\"showLines\":true,\"fillLines\":false,\"showPoints\":false,\"axisPosition\":\"left\"},\"_hash\":0.12775350966079668,\"funcBody\":\"return Math.random() <= 0.5 ? 1 : 0;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"shadowSize\":4,\"fontColor\":\"#545454\",\"fontSize\":10,\"xaxis\":{\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"showLabels\":true,\"color\":\"#545454\",\"ticksFormatter\":\"if (value > 0 && value <= 1) {\\n return 'On';\\n} else if (value === 0) {\\n return 'Off';\\n} else {\\n return '';\\n}\"},\"grid\":{\"color\":\"#545454\",\"tickColor\":\"#DDDDDD\",\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1},\"stack\":false,\"tooltipIndividual\":false,\"tooltipValueFormatter\":\"if (value > 0 && value <= 1) {\\n return 'On';\\n} else if (value === 0) {\\n return 'Off';\\n} else {\\n return '';\\n}\",\"smoothLines\":false},\"title\":\"State Chart\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"widgetStyle\":{},\"useDashboardTimewindow\":true,\"showLegend\":true,\"actions\":{},\"legendConfig\":{\"direction\":\"column\",\",position\":\"bottom\",\"showMin\":false,\"showMax\":false,\"showAvg\":false,\"showTotal\":false}}" 153 + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#ffc107\",\"settings\":{\"showLines\":true,\"fillLines\":false,\"showPoints\":false},\"_hash\":0.12775350966079668,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"shadowSize\":4,\"fontColor\":\"#545454\",\"fontSize\":10,\"xaxis\":{\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"showLabels\":true,\"color\":\"#545454\"},\"grid\":{\"color\":\"#545454\",\"tickColor\":\"#DDDDDD\",\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1},\"legend\":{\"show\":true,\"position\":\"nw\",\"backgroundColor\":\"#f0f0f0\",\"backgroundOpacity\":0.85,\"labelBoxBorderColor\":\"rgba(1, 1, 1, 0.45)\"},\"decimals\":1,\"stack\":false,\"tooltipIndividual\":false},\"title\":\"Timeseries - Flot\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null}"
  154 + }
  155 + },
  156 + {
  157 + "alias": "timeseries_bars_flot",
  158 + "name": "Timeseries Bars - Flot",
  159 + "descriptor": {
  160 + "type": "timeseries",
  161 + "sizeX": 8,
  162 + "sizeY": 5,
  163 + "resources": [],
  164 + "templateHtml": "",
  165 + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n",
  166 + "controllerScript": "self.onInit = function() {\n self.ctx.flot = new TbFlot(self.ctx, 'bar'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.flot.update();\n}\n\nself.onResize = function() {\n self.ctx.flot.resize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.flot.checkMouseEvents();\n}\n\nself.getSettingsSchema = function() {\n return TbFlot.settingsSchema('bar');\n}\n\nself.getDataKeySettingsSchema = function() {\n return TbFlot.datakeySettingsSchema(false, 'bar');\n}\n\nself.onDestroy = function() {\n self.ctx.flot.destroy();\n}\n",
  167 + "settingsSchema": "{}",
  168 + "dataKeySettingsSchema": "{}",
  169 + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{\"showLines\":false,\"fillLines\":false,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#ffc107\",\"settings\":{\"showLines\":false,\"fillLines\":false,\"showPoints\":false},\"_hash\":0.12775350966079668,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000},\"aggregation\":{\"limit\":200,\"type\":\"AVG\"}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"shadowSize\":4,\"fontColor\":\"#545454\",\"fontSize\":10,\"xaxis\":{\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"showLabels\":true,\"color\":\"#545454\"},\"grid\":{\"color\":\"#545454\",\"tickColor\":\"#DDDDDD\",\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1},\"stack\":true,\"tooltipIndividual\":false,\"defaultBarWidth\":600},\"title\":\"Timeseries Bars - Flot\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"widgetStyle\":{},\"useDashboardTimewindow\":true,\"showLegend\":true,\"actions\":{}}"
170 } 170 }
171 } 171 }
172 ] 172 ]
173 -} 173 +}
  1 +{
  2 + "widgetsBundle": {
  3 + "alias": "navigation_widgets",
  4 + "title": "Navigation widgets",
  5 + "image": null
  6 + },
  7 + "widgetTypes": [
  8 + {
  9 + "alias": "navigation_cards",
  10 + "name": "Navigation cards",
  11 + "descriptor": {
  12 + "type": "static",
  13 + "sizeX": 7,
  14 + "sizeY": 6,
  15 + "resources": [],
  16 + "templateHtml": "<tb-navigation-cards-widget [ctx]=\"ctx\"></tb-navigation-cards-widget>",
  17 + "templateCss": "/*#widget-container {\n overflow-y: auto;\n box-sizing: content-box !important;\n cursor: auto;\n}*/\n\n#widget-container #container {\n overflow-y: auto;\n box-sizing: content-box;\n cursor: auto;\n}",
  18 + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.navigationCardsWidget.resize();\n}\n\nself.onResize = function() {\n self.ctx.$scope.navigationCardsWidget.resize();\n}\n\nself.onDestroy = function() {\n}\n",
  19 + "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"filterType\": {\n \"title\": \"Filter type\",\n \"type\": \"string\",\n \"default\": \"all\"\n },\n \"filter\": {\n \"title\": \"Items\",\n \"type\": \"array\"\n }\n },\n \"required\": []\n },\n \"form\": [\n {\n \"key\": \"filterType\",\n \"type\": \"radios\",\n \"direction\": \"row\",\n \"titleMap\": [\n {\n \"value\": \"all\",\n \"name\": \"All items\"\n },\n {\n \"value\": \"include\",\n \"name\": \"Include items\"\n },\n {\n \"value\": \"exclude\",\n \"name\": \"Exclude items\"\n }\n ]\n },\n {\n \"key\": \"filter\",\n \"type\": \"rc-select\",\n \"condition\": \"model.filterType !== 'all'\",\n \"tags\": true,\n \"placeholder\": \"Enter urls to filter\",\n \"items\": [{\"value\": \"/devices\", \"label\": \"/devices\"}, {\"value\": \"/assets\", \"label\": \"/assets\"}, {\"value\": \"/deviceProfies\", \"label\": \"/deviceProfies\"}]\n }\n ]\n}\n",
  20 + "dataKeySettingsSchema": "{}\n",
  21 + "defaultConfig": "{\"datasources\":[{\"type\":\"static\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(255,255,255,0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"filterType\":\"all\"},\"title\":\"Navigation cards\",\"dropShadow\":false,\"showTitleIcon\":false,\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"showLegend\":false}"
  22 + }
  23 + },
  24 + {
  25 + "alias": "navigation_card",
  26 + "name": "Navigation card",
  27 + "descriptor": {
  28 + "type": "static",
  29 + "sizeX": 2.5,
  30 + "sizeY": 2,
  31 + "resources": [],
  32 + "templateHtml": "<tb-navigation-card-widget [ctx]=\"ctx\"></tb-navigation-card-widget>",
  33 + "templateCss": "",
  34 + "controllerScript": "self.onInit = function() {\n\n}\n\n\nself.onDestroy = function() {\n}\n",
  35 + "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"name\": {\n \"title\": \"Title\",\n \"type\": \"string\",\n \"default\": \"{i18n:device.devices}\"\n },\n \"icon\": {\n \"title\": \"icon\",\n \"type\": \"string\",\n \"default\": \"devices_other\"\n },\n \"path\": {\n \"title\": \"Navigation path\",\n \"type\": \"string\",\n \"default\": \"/devices\"\n }\n },\n \"required\": [\"name\", \"icon\", \"path\"]\n },\n \"form\": [\n \"name\",\n {\n \"key\": \"icon\",\n \"type\": \"icon\"\n },\n \"path\"\n ]\n}\n",
  36 + "dataKeySettingsSchema": "{}\n",
  37 + "defaultConfig": "{\"datasources\":[{\"type\":\"static\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(255,255,255,0)\",\"color\":\"rgba(255,255,255,0.87)\",\"padding\":\"8px\",\"settings\":{\"name\":\"{i18n:device.devices}\",\"icon\":\"devices_other\",\"path\":\"/devices\"},\"title\":\"Navigation card\",\"dropShadow\":false,\"showTitleIcon\":false,\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"showLegend\":false}"
  38 + }
  39 + }
  40 + ]
  41 +}
@@ -84,11 +84,12 @@ BEGIN @@ -84,11 +84,12 @@ BEGIN
84 END IF; 84 END IF;
85 END IF; 85 END IF;
86 END IF; 86 END IF;
87 - END IF;  
88 - IF partition_to_delete IS NOT NULL THEN  
89 - RAISE NOTICE 'Partition to delete by max ttl: %', partition_to_delete;  
90 - EXECUTE format('DROP TABLE %I', partition_to_delete);  
91 - deleted := deleted + 1; 87 + IF partition_to_delete IS NOT NULL THEN
  88 + RAISE NOTICE 'Partition to delete by max ttl: %', partition_to_delete;
  89 + EXECUTE format('DROP TABLE IF EXISTS %I', partition_to_delete);
  90 + partition_to_delete := NULL;
  91 + deleted := deleted + 1;
  92 + END IF;
92 END IF; 93 END IF;
93 END LOOP; 94 END LOOP;
94 END IF; 95 END IF;
@@ -30,7 +30,8 @@ import java.util.Arrays; @@ -30,7 +30,8 @@ import java.util.Arrays;
30 "org.thingsboard.server.service.component", 30 "org.thingsboard.server.service.component",
31 "org.thingsboard.server.service.install", 31 "org.thingsboard.server.service.install",
32 "org.thingsboard.server.dao", 32 "org.thingsboard.server.dao",
33 - "org.thingsboard.server.common.stats"}) 33 + "org.thingsboard.server.common.stats",
  34 + "org.thingsboard.server.cache"})
34 public class ThingsboardInstallApplication { 35 public class ThingsboardInstallApplication {
35 36
36 private static final String SPRING_CONFIG_NAME_KEY = "--spring.config.name"; 37 private static final String SPRING_CONFIG_NAME_KEY = "--spring.config.name";
@@ -134,12 +134,12 @@ public class AppActor extends ContextAwareActor { @@ -134,12 +134,12 @@ public class AppActor extends ContextAwareActor {
134 134
135 private void onQueueToRuleEngineMsg(QueueToRuleEngineMsg msg) { 135 private void onQueueToRuleEngineMsg(QueueToRuleEngineMsg msg) {
136 if (TenantId.SYS_TENANT_ID.equals(msg.getTenantId())) { 136 if (TenantId.SYS_TENANT_ID.equals(msg.getTenantId())) {
137 - msg.getTbMsg().getCallback().onFailure(new RuleEngineException("Message has system tenant id!")); 137 + msg.getMsg().getCallback().onFailure(new RuleEngineException("Message has system tenant id!"));
138 } else { 138 } else {
139 if (!deletedTenants.contains(msg.getTenantId())) { 139 if (!deletedTenants.contains(msg.getTenantId())) {
140 getOrCreateTenantActor(msg.getTenantId()).tell(msg); 140 getOrCreateTenantActor(msg.getTenantId()).tell(msg);
141 } else { 141 } else {
142 - msg.getTbMsg().getCallback().onSuccess(); 142 + msg.getMsg().getCallback().onSuccess();
143 } 143 }
144 } 144 }
145 } 145 }
@@ -62,7 +62,7 @@ public class DeviceActor extends ContextAwareActor { @@ -62,7 +62,7 @@ public class DeviceActor extends ContextAwareActor {
62 processor.processAttributesUpdate(ctx, (DeviceAttributesEventNotificationMsg) msg); 62 processor.processAttributesUpdate(ctx, (DeviceAttributesEventNotificationMsg) msg);
63 break; 63 break;
64 case DEVICE_CREDENTIALS_UPDATE_TO_DEVICE_ACTOR_MSG: 64 case DEVICE_CREDENTIALS_UPDATE_TO_DEVICE_ACTOR_MSG:
65 - processor.processCredentialsUpdate(); 65 + processor.processCredentialsUpdate(msg);
66 break; 66 break;
67 case DEVICE_NAME_OR_TYPE_UPDATE_TO_DEVICE_ACTOR_MSG: 67 case DEVICE_NAME_OR_TYPE_UPDATE_TO_DEVICE_ACTOR_MSG:
68 processor.processNameOrTypeUpdate((DeviceNameOrTypeUpdateMsg) msg); 68 processor.processNameOrTypeUpdate((DeviceNameOrTypeUpdateMsg) msg);
@@ -24,6 +24,7 @@ import lombok.extern.slf4j.Slf4j; @@ -24,6 +24,7 @@ import lombok.extern.slf4j.Slf4j;
24 import org.apache.commons.collections.CollectionUtils; 24 import org.apache.commons.collections.CollectionUtils;
25 import org.thingsboard.rule.engine.api.RpcError; 25 import org.thingsboard.rule.engine.api.RpcError;
26 import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg; 26 import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg;
  27 +import org.thingsboard.rule.engine.api.msg.DeviceCredentialsUpdateNotificationMsg;
27 import org.thingsboard.rule.engine.api.msg.DeviceNameOrTypeUpdateMsg; 28 import org.thingsboard.rule.engine.api.msg.DeviceNameOrTypeUpdateMsg;
28 import org.thingsboard.server.actors.ActorSystemContext; 29 import org.thingsboard.server.actors.ActorSystemContext;
29 import org.thingsboard.server.actors.TbActorCtx; 30 import org.thingsboard.server.actors.TbActorCtx;
@@ -36,6 +37,9 @@ import org.thingsboard.server.common.data.kv.AttributeKey; @@ -36,6 +37,9 @@ import org.thingsboard.server.common.data.kv.AttributeKey;
36 import org.thingsboard.server.common.data.kv.AttributeKvEntry; 37 import org.thingsboard.server.common.data.kv.AttributeKvEntry;
37 import org.thingsboard.server.common.data.kv.KvEntry; 38 import org.thingsboard.server.common.data.kv.KvEntry;
38 import org.thingsboard.server.common.data.rpc.ToDeviceRpcRequestBody; 39 import org.thingsboard.server.common.data.rpc.ToDeviceRpcRequestBody;
  40 +import org.thingsboard.server.common.data.security.DeviceCredentials;
  41 +import org.thingsboard.server.common.data.security.DeviceCredentialsType;
  42 +import org.thingsboard.server.common.msg.TbActorMsg;
39 import org.thingsboard.server.common.msg.TbMsgMetaData; 43 import org.thingsboard.server.common.msg.TbMsgMetaData;
40 import org.thingsboard.server.common.msg.queue.TbCallback; 44 import org.thingsboard.server.common.msg.queue.TbCallback;
41 import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest; 45 import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest;
@@ -61,6 +65,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcResponseM @@ -61,6 +65,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcResponseM
61 import org.thingsboard.server.gen.transport.TransportProtos.ToServerRpcResponseMsg; 65 import org.thingsboard.server.gen.transport.TransportProtos.ToServerRpcResponseMsg;
62 import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg; 66 import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg;
63 import org.thingsboard.server.gen.transport.TransportProtos.TransportToDeviceActorMsg; 67 import org.thingsboard.server.gen.transport.TransportProtos.TransportToDeviceActorMsg;
  68 +import org.thingsboard.server.gen.transport.TransportProtos.ToTransportUpdateCredentialsProto;
64 import org.thingsboard.server.gen.transport.TransportProtos.TsKvProto; 69 import org.thingsboard.server.gen.transport.TransportProtos.TsKvProto;
65 import org.thingsboard.server.service.rpc.FromDeviceRpcResponse; 70 import org.thingsboard.server.service.rpc.FromDeviceRpcResponse;
66 import org.thingsboard.server.service.rpc.ToDeviceRpcRequestActorMsg; 71 import org.thingsboard.server.service.rpc.ToDeviceRpcRequestActorMsg;
@@ -450,11 +455,19 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -450,11 +455,19 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
450 dumpSessions(); 455 dumpSessions();
451 } 456 }
452 457
453 - void processCredentialsUpdate() {  
454 - sessions.forEach(this::notifyTransportAboutClosedSession);  
455 - attributeSubscriptions.clear();  
456 - rpcSubscriptions.clear();  
457 - dumpSessions(); 458 + void processCredentialsUpdate(TbActorMsg msg) {
  459 + if (((DeviceCredentialsUpdateNotificationMsg) msg).getDeviceCredentials().getCredentialsType() == DeviceCredentialsType.LWM2M_CREDENTIALS) {
  460 + log.info("1) LwM2Mtype: ");
  461 + sessions.forEach((k, v) -> {
  462 + notifyTransportAboutProfileUpdate(k, v, ((DeviceCredentialsUpdateNotificationMsg) msg).getDeviceCredentials());
  463 + });
  464 + } else {
  465 + sessions.forEach(this::notifyTransportAboutClosedSession);
  466 + attributeSubscriptions.clear();
  467 + rpcSubscriptions.clear();
  468 + dumpSessions();
  469 +
  470 + }
458 } 471 }
459 472
460 private void notifyTransportAboutClosedSession(UUID sessionId, SessionInfoMetaData sessionMd) { 473 private void notifyTransportAboutClosedSession(UUID sessionId, SessionInfoMetaData sessionMd) {
@@ -465,6 +478,18 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -465,6 +478,18 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
465 systemContext.getTbCoreToTransportService().process(sessionMd.getSessionInfo().getNodeId(), msg); 478 systemContext.getTbCoreToTransportService().process(sessionMd.getSessionInfo().getNodeId(), msg);
466 } 479 }
467 480
  481 + void notifyTransportAboutProfileUpdate(UUID sessionId, SessionInfoMetaData sessionMd, DeviceCredentials deviceCredentials) {
  482 + log.info("2) LwM2Mtype: ");
  483 + TransportProtos.ToTransportUpdateCredentialsProto.Builder notification = TransportProtos.ToTransportUpdateCredentialsProto.newBuilder();
  484 + notification.addCredentialsId(deviceCredentials.getCredentialsId());
  485 + notification.addCredentialsValue(deviceCredentials.getCredentialsValue());
  486 + ToTransportMsg msg = ToTransportMsg.newBuilder()
  487 + .setSessionIdMSB(sessionId.getMostSignificantBits())
  488 + .setSessionIdLSB(sessionId.getLeastSignificantBits())
  489 + .setToTransportUpdateCredentialsNotification(notification).build();
  490 + systemContext.getTbCoreToTransportService().process(sessionMd.getSessionInfo().getNodeId(), msg);
  491 + }
  492 +
468 void processNameOrTypeUpdate(DeviceNameOrTypeUpdateMsg msg) { 493 void processNameOrTypeUpdate(DeviceNameOrTypeUpdateMsg msg) {
469 this.deviceName = msg.getDeviceName(); 494 this.deviceName = msg.getDeviceName();
470 this.deviceType = msg.getDeviceType(); 495 this.deviceType = msg.getDeviceType();
@@ -19,7 +19,6 @@ import com.fasterxml.jackson.core.JsonProcessingException; @@ -19,7 +19,6 @@ import com.fasterxml.jackson.core.JsonProcessingException;
19 import com.fasterxml.jackson.databind.ObjectMapper; 19 import com.fasterxml.jackson.databind.ObjectMapper;
20 import io.netty.channel.EventLoopGroup; 20 import io.netty.channel.EventLoopGroup;
21 import lombok.extern.slf4j.Slf4j; 21 import lombok.extern.slf4j.Slf4j;
22 -import org.springframework.data.redis.core.RedisTemplate;  
23 import org.springframework.util.StringUtils; 22 import org.springframework.util.StringUtils;
24 import org.thingsboard.common.util.ListeningExecutor; 23 import org.thingsboard.common.util.ListeningExecutor;
25 import org.thingsboard.rule.engine.api.MailService; 24 import org.thingsboard.rule.engine.api.MailService;
@@ -34,11 +33,11 @@ import org.thingsboard.rule.engine.api.TbRelationTypes; @@ -34,11 +33,11 @@ import org.thingsboard.rule.engine.api.TbRelationTypes;
34 import org.thingsboard.rule.engine.api.sms.SmsSenderFactory; 33 import org.thingsboard.rule.engine.api.sms.SmsSenderFactory;
35 import org.thingsboard.server.actors.ActorSystemContext; 34 import org.thingsboard.server.actors.ActorSystemContext;
36 import org.thingsboard.server.actors.TbActorRef; 35 import org.thingsboard.server.actors.TbActorRef;
37 -import org.thingsboard.server.common.data.ApiUsageRecordKey;  
38 import org.thingsboard.server.common.data.Customer; 36 import org.thingsboard.server.common.data.Customer;
39 import org.thingsboard.server.common.data.DataConstants; 37 import org.thingsboard.server.common.data.DataConstants;
40 import org.thingsboard.server.common.data.Device; 38 import org.thingsboard.server.common.data.Device;
41 import org.thingsboard.server.common.data.DeviceProfile; 39 import org.thingsboard.server.common.data.DeviceProfile;
  40 +import org.thingsboard.server.common.data.EntityType;
42 import org.thingsboard.server.common.data.TenantProfile; 41 import org.thingsboard.server.common.data.TenantProfile;
43 import org.thingsboard.server.common.data.alarm.Alarm; 42 import org.thingsboard.server.common.data.alarm.Alarm;
44 import org.thingsboard.server.common.data.asset.Asset; 43 import org.thingsboard.server.common.data.asset.Asset;
@@ -90,10 +89,12 @@ class DefaultTbContext implements TbContext { @@ -90,10 +89,12 @@ class DefaultTbContext implements TbContext {
90 public final static ObjectMapper mapper = new ObjectMapper(); 89 public final static ObjectMapper mapper = new ObjectMapper();
91 90
92 private final ActorSystemContext mainCtx; 91 private final ActorSystemContext mainCtx;
  92 + private final String ruleChainName;
93 private final RuleNodeCtx nodeCtx; 93 private final RuleNodeCtx nodeCtx;
94 94
95 - public DefaultTbContext(ActorSystemContext mainCtx, RuleNodeCtx nodeCtx) { 95 + public DefaultTbContext(ActorSystemContext mainCtx, String ruleChainName, RuleNodeCtx nodeCtx) {
96 this.mainCtx = mainCtx; 96 this.mainCtx = mainCtx;
  97 + this.ruleChainName = ruleChainName;
97 this.nodeCtx = nodeCtx; 98 this.nodeCtx = nodeCtx;
98 } 99 }
99 100
@@ -117,13 +118,13 @@ class DefaultTbContext implements TbContext { @@ -117,13 +118,13 @@ class DefaultTbContext implements TbContext {
117 relationTypes.forEach(relationType -> mainCtx.persistDebugOutput(nodeCtx.getTenantId(), nodeCtx.getSelf().getId(), msg, relationType, th)); 118 relationTypes.forEach(relationType -> mainCtx.persistDebugOutput(nodeCtx.getTenantId(), nodeCtx.getSelf().getId(), msg, relationType, th));
118 } 119 }
119 msg.getCallback().onProcessingEnd(nodeCtx.getSelf().getId()); 120 msg.getCallback().onProcessingEnd(nodeCtx.getSelf().getId());
120 - nodeCtx.getChainActor().tell(new RuleNodeToRuleChainTellNextMsg(nodeCtx.getSelf().getId(), relationTypes, msg, th != null ? th.getMessage() : null)); 121 + nodeCtx.getChainActor().tell(new RuleNodeToRuleChainTellNextMsg(nodeCtx.getSelf().getRuleChainId(), nodeCtx.getSelf().getId(), relationTypes, msg, th != null ? th.getMessage() : null));
121 } 122 }
122 123
123 @Override 124 @Override
124 public void tellSelf(TbMsg msg, long delayMs) { 125 public void tellSelf(TbMsg msg, long delayMs) {
125 //TODO: add persistence layer 126 //TODO: add persistence layer
126 - scheduleMsgWithDelay(new RuleNodeToSelfMsg(msg), delayMs, nodeCtx.getSelfActor()); 127 + scheduleMsgWithDelay(new RuleNodeToSelfMsg(this, msg), delayMs, nodeCtx.getSelfActor());
127 } 128 }
128 129
129 @Override 130 @Override
@@ -254,7 +255,8 @@ class DefaultTbContext implements TbContext { @@ -254,7 +255,8 @@ class DefaultTbContext implements TbContext {
254 } else { 255 } else {
255 failureMessage = null; 256 failureMessage = null;
256 } 257 }
257 - nodeCtx.getChainActor().tell(new RuleNodeToRuleChainTellNextMsg(nodeCtx.getSelf().getId(), Collections.singleton(TbRelationTypes.FAILURE), 258 + nodeCtx.getChainActor().tell(new RuleNodeToRuleChainTellNextMsg(nodeCtx.getSelf().getRuleChainId(),
  259 + nodeCtx.getSelf().getId(), Collections.singleton(TbRelationTypes.FAILURE),
258 msg, failureMessage)); 260 msg, failureMessage));
259 } 261 }
260 262
@@ -277,7 +279,21 @@ class DefaultTbContext implements TbContext { @@ -277,7 +279,21 @@ class DefaultTbContext implements TbContext {
277 } 279 }
278 280
279 public TbMsg deviceCreatedMsg(Device device, RuleNodeId ruleNodeId) { 281 public TbMsg deviceCreatedMsg(Device device, RuleNodeId ruleNodeId) {
280 - return entityActionMsg(device, device.getId(), ruleNodeId, DataConstants.ENTITY_CREATED); 282 + RuleChainId ruleChainId = null;
  283 + String queueName = ServiceQueue.MAIN;
  284 + if (device.getDeviceProfileId() != null) {
  285 + DeviceProfile deviceProfile = mainCtx.getDeviceProfileCache().find(device.getDeviceProfileId());
  286 + if (deviceProfile == null) {
  287 + log.warn("[{}] Device profile is null!", device.getDeviceProfileId());
  288 + ruleChainId = null;
  289 + queueName = ServiceQueue.MAIN;
  290 + } else {
  291 + ruleChainId = deviceProfile.getDefaultRuleChainId();
  292 + String defaultQueueName = deviceProfile.getDefaultQueueName();
  293 + queueName = defaultQueueName != null ? defaultQueueName : ServiceQueue.MAIN;
  294 + }
  295 + }
  296 + return entityActionMsg(device, device.getId(), ruleNodeId, DataConstants.ENTITY_CREATED, queueName, ruleChainId);
281 } 297 }
282 298
283 public TbMsg assetCreatedMsg(Asset asset, RuleNodeId ruleNodeId) { 299 public TbMsg assetCreatedMsg(Asset asset, RuleNodeId ruleNodeId) {
@@ -285,12 +301,31 @@ class DefaultTbContext implements TbContext { @@ -285,12 +301,31 @@ class DefaultTbContext implements TbContext {
285 } 301 }
286 302
287 public TbMsg alarmActionMsg(Alarm alarm, RuleNodeId ruleNodeId, String action) { 303 public TbMsg alarmActionMsg(Alarm alarm, RuleNodeId ruleNodeId, String action) {
288 - return entityActionMsg(alarm, alarm.getId(), ruleNodeId, action); 304 + RuleChainId ruleChainId = null;
  305 + String queueName = ServiceQueue.MAIN;
  306 + if (EntityType.DEVICE.equals(alarm.getOriginator().getEntityType())) {
  307 + DeviceId deviceId = new DeviceId(alarm.getOriginator().getId());
  308 + DeviceProfile deviceProfile = mainCtx.getDeviceProfileCache().get(getTenantId(), deviceId);
  309 + if (deviceProfile == null) {
  310 + log.warn("[{}] Device profile is null!", deviceId);
  311 + ruleChainId = null;
  312 + queueName = ServiceQueue.MAIN;
  313 + } else {
  314 + ruleChainId = deviceProfile.getDefaultRuleChainId();
  315 + String defaultQueueName = deviceProfile.getDefaultQueueName();
  316 + queueName = defaultQueueName != null ? defaultQueueName : ServiceQueue.MAIN;
  317 + }
  318 + }
  319 + return entityActionMsg(alarm, alarm.getId(), ruleNodeId, action, queueName, ruleChainId);
289 } 320 }
290 321
291 public <E, I extends EntityId> TbMsg entityActionMsg(E entity, I id, RuleNodeId ruleNodeId, String action) { 322 public <E, I extends EntityId> TbMsg entityActionMsg(E entity, I id, RuleNodeId ruleNodeId, String action) {
  323 + return entityActionMsg(entity, id, ruleNodeId, action, ServiceQueue.MAIN, null);
  324 + }
  325 +
  326 + public <E, I extends EntityId> TbMsg entityActionMsg(E entity, I id, RuleNodeId ruleNodeId, String action, String queueName, RuleChainId ruleChainId) {
292 try { 327 try {
293 - return TbMsg.newMsg(action, id, getActionMetaData(ruleNodeId), mapper.writeValueAsString(mapper.valueToTree(entity))); 328 + return TbMsg.newMsg(queueName, action, id, getActionMetaData(ruleNodeId), mapper.writeValueAsString(mapper.valueToTree(entity)), ruleChainId, null);
294 } catch (JsonProcessingException | IllegalArgumentException e) { 329 } catch (JsonProcessingException | IllegalArgumentException e) {
295 throw new RuntimeException("Failed to process " + id.getEntityType().name().toLowerCase() + " " + action + " msg: " + e); 330 throw new RuntimeException("Failed to process " + id.getEntityType().name().toLowerCase() + " " + action + " msg: " + e);
296 } 331 }
@@ -302,6 +337,16 @@ class DefaultTbContext implements TbContext { @@ -302,6 +337,16 @@ class DefaultTbContext implements TbContext {
302 } 337 }
303 338
304 @Override 339 @Override
  340 + public RuleNode getSelf() {
  341 + return nodeCtx.getSelf();
  342 + }
  343 +
  344 + @Override
  345 + public String getRuleChainName() {
  346 + return ruleChainName;
  347 + }
  348 +
  349 + @Override
305 public TenantId getTenantId() { 350 public TenantId getTenantId() {
306 return nodeCtx.getTenantId(); 351 return nodeCtx.getTenantId();
307 } 352 }
@@ -476,11 +521,6 @@ class DefaultTbContext implements TbContext { @@ -476,11 +521,6 @@ class DefaultTbContext implements TbContext {
476 } 521 }
477 522
478 @Override 523 @Override
479 - public RedisTemplate<String, Object> getRedisTemplate() {  
480 - return mainCtx.getRedisTemplate();  
481 - }  
482 -  
483 - @Override  
484 public PageData<RuleNodeState> findRuleNodeStates(PageLink pageLink) { 524 public PageData<RuleNodeState> findRuleNodeStates(PageLink pageLink) {
485 if (log.isDebugEnabled()) { 525 if (log.isDebugEnabled()) {
486 log.debug("[{}][{}] Fetch Rule Node States.", getTenantId(), getSelfId()); 526 log.debug("[{}][{}] Fetch Rule Node States.", getTenantId(), getSelfId());
@@ -23,7 +23,6 @@ import org.thingsboard.server.actors.TbActorRef; @@ -23,7 +23,6 @@ import org.thingsboard.server.actors.TbActorRef;
23 import org.thingsboard.server.actors.TbEntityActorId; 23 import org.thingsboard.server.actors.TbEntityActorId;
24 import org.thingsboard.server.actors.service.DefaultActorService; 24 import org.thingsboard.server.actors.service.DefaultActorService;
25 import org.thingsboard.server.actors.shared.ComponentMsgProcessor; 25 import org.thingsboard.server.actors.shared.ComponentMsgProcessor;
26 -import org.thingsboard.server.common.data.ApiUsageRecordKey;  
27 import org.thingsboard.server.common.data.EntityType; 26 import org.thingsboard.server.common.data.EntityType;
28 import org.thingsboard.server.common.data.id.EntityId; 27 import org.thingsboard.server.common.data.id.EntityId;
29 import org.thingsboard.server.common.data.id.RuleChainId; 28 import org.thingsboard.server.common.data.id.RuleChainId;
@@ -36,6 +35,7 @@ import org.thingsboard.server.common.data.rule.RuleChain; @@ -36,6 +35,7 @@ import org.thingsboard.server.common.data.rule.RuleChain;
36 import org.thingsboard.server.common.data.rule.RuleNode; 35 import org.thingsboard.server.common.data.rule.RuleNode;
37 import org.thingsboard.server.common.msg.TbMsg; 36 import org.thingsboard.server.common.msg.TbMsg;
38 import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; 37 import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
  38 +import org.thingsboard.server.common.msg.plugin.RuleNodeUpdatedMsg;
39 import org.thingsboard.server.common.msg.queue.PartitionChangeMsg; 39 import org.thingsboard.server.common.msg.queue.PartitionChangeMsg;
40 import org.thingsboard.server.common.msg.queue.QueueToRuleEngineMsg; 40 import org.thingsboard.server.common.msg.queue.QueueToRuleEngineMsg;
41 import org.thingsboard.server.common.msg.queue.RuleEngineException; 41 import org.thingsboard.server.common.msg.queue.RuleEngineException;
@@ -132,7 +132,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh @@ -132,7 +132,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
132 } else { 132 } else {
133 log.trace("[{}][{}] Updating rule node [{}]: {}", entityId, ruleNode.getId(), ruleNode.getName(), ruleNode); 133 log.trace("[{}][{}] Updating rule node [{}]: {}", entityId, ruleNode.getId(), ruleNode.getName(), ruleNode);
134 existing.setSelf(ruleNode); 134 existing.setSelf(ruleNode);
135 - existing.getSelfActor().tellWithHighPriority(new ComponentLifecycleMsg(tenantId, existing.getSelf().getId(), ComponentLifecycleEvent.UPDATED)); 135 + existing.getSelfActor().tellWithHighPriority(new RuleNodeUpdatedMsg(tenantId, existing.getSelf().getId()));
136 } 136 }
137 } 137 }
138 138
@@ -197,11 +197,11 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh @@ -197,11 +197,11 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
197 } 197 }
198 198
199 void onQueueToRuleEngineMsg(QueueToRuleEngineMsg envelope) { 199 void onQueueToRuleEngineMsg(QueueToRuleEngineMsg envelope) {
200 - TbMsg msg = envelope.getTbMsg(); 200 + TbMsg msg = envelope.getMsg();
201 log.trace("[{}][{}] Processing message [{}]: {}", entityId, firstId, msg.getId(), msg); 201 log.trace("[{}][{}] Processing message [{}]: {}", entityId, firstId, msg.getId(), msg);
202 if (envelope.getRelationTypes() == null || envelope.getRelationTypes().isEmpty()) { 202 if (envelope.getRelationTypes() == null || envelope.getRelationTypes().isEmpty()) {
203 try { 203 try {
204 - checkActive(envelope.getTbMsg()); 204 + checkActive(envelope.getMsg());
205 RuleNodeId targetId = msg.getRuleNodeId(); 205 RuleNodeId targetId = msg.getRuleNodeId();
206 RuleNodeCtx targetCtx; 206 RuleNodeCtx targetCtx;
207 if (targetId == null) { 207 if (targetId == null) {
@@ -218,12 +218,12 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh @@ -218,12 +218,12 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
218 msg.getCallback().onSuccess(); 218 msg.getCallback().onSuccess();
219 } 219 }
220 } catch (RuleNodeException rne) { 220 } catch (RuleNodeException rne) {
221 - envelope.getTbMsg().getCallback().onFailure(rne); 221 + envelope.getMsg().getCallback().onFailure(rne);
222 } catch (Exception e) { 222 } catch (Exception e) {
223 - envelope.getTbMsg().getCallback().onFailure(new RuleEngineException(e.getMessage())); 223 + envelope.getMsg().getCallback().onFailure(new RuleEngineException(e.getMessage()));
224 } 224 }
225 } else { 225 } else {
226 - onTellNext(envelope.getTbMsg(), envelope.getTbMsg().getRuleNodeId(), envelope.getRelationTypes(), envelope.getFailureMessage()); 226 + onTellNext(envelope.getMsg(), envelope.getMsg().getRuleNodeId(), envelope.getRelationTypes(), envelope.getFailureMessage());
227 } 227 }
228 } 228 }
229 229
@@ -335,7 +335,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh @@ -335,7 +335,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
335 335
336 private void pushMsgToNode(RuleNodeCtx nodeCtx, TbMsg msg, String fromRelationType) { 336 private void pushMsgToNode(RuleNodeCtx nodeCtx, TbMsg msg, String fromRelationType) {
337 if (nodeCtx != null) { 337 if (nodeCtx != null) {
338 - nodeCtx.getSelfActor().tell(new RuleChainToRuleNodeMsg(new DefaultTbContext(systemContext, nodeCtx), msg, fromRelationType)); 338 + nodeCtx.getSelfActor().tell(new RuleChainToRuleNodeMsg(new DefaultTbContext(systemContext, ruleChainName, nodeCtx), msg, fromRelationType));
339 } else { 339 } else {
340 log.error("[{}][{}] RuleNodeCtx is empty", entityId, ruleChainName); 340 log.error("[{}][{}] RuleNodeCtx is empty", entityId, ruleChainName);
341 msg.getCallback().onFailure(new RuleEngineException("Rule Node CTX is empty")); 341 msg.getCallback().onFailure(new RuleEngineException("Rule Node CTX is empty"));
@@ -15,24 +15,44 @@ @@ -15,24 +15,44 @@
15 */ 15 */
16 package org.thingsboard.server.actors.ruleChain; 16 package org.thingsboard.server.actors.ruleChain;
17 17
18 -import lombok.Data; 18 +import lombok.EqualsAndHashCode;
  19 +import lombok.Getter;
  20 +import lombok.ToString;
19 import org.thingsboard.server.common.data.id.RuleChainId; 21 import org.thingsboard.server.common.data.id.RuleChainId;
20 import org.thingsboard.server.common.msg.MsgType; 22 import org.thingsboard.server.common.msg.MsgType;
21 -import org.thingsboard.server.common.msg.TbActorMsg; 23 +import org.thingsboard.server.common.msg.TbActorStopReason;
22 import org.thingsboard.server.common.msg.TbMsg; 24 import org.thingsboard.server.common.msg.TbMsg;
  25 +import org.thingsboard.server.common.msg.TbRuleEngineActorMsg;
23 import org.thingsboard.server.common.msg.aware.RuleChainAwareMsg; 26 import org.thingsboard.server.common.msg.aware.RuleChainAwareMsg;
  27 +import org.thingsboard.server.common.msg.queue.RuleEngineException;
24 28
25 /** 29 /**
26 * Created by ashvayka on 19.03.18. 30 * Created by ashvayka on 19.03.18.
27 */ 31 */
28 -@Data  
29 -public final class RuleChainToRuleChainMsg implements TbActorMsg, RuleChainAwareMsg { 32 +@EqualsAndHashCode(callSuper = true)
  33 +@ToString
  34 +public final class RuleChainToRuleChainMsg extends TbRuleEngineActorMsg implements RuleChainAwareMsg {
30 35
  36 + @Getter
31 private final RuleChainId target; 37 private final RuleChainId target;
  38 + @Getter
32 private final RuleChainId source; 39 private final RuleChainId source;
33 - private final TbMsg msg; 40 + @Getter
34 private final String fromRelationType; 41 private final String fromRelationType;
35 42
  43 + public RuleChainToRuleChainMsg(RuleChainId target, RuleChainId source, TbMsg tbMsg, String fromRelationType) {
  44 + super(tbMsg);
  45 + this.target = target;
  46 + this.source = source;
  47 + this.fromRelationType = fromRelationType;
  48 + }
  49 +
  50 + @Override
  51 + public void onTbActorStopped(TbActorStopReason reason) {
  52 + String message = reason == TbActorStopReason.STOPPED ? String.format("Rule chain [%s] stopped", target.getId()) : String.format("Failed to initialize rule chain [%s]!", target.getId());
  53 + msg.getCallback().onFailure(new RuleEngineException(message));
  54 + }
  55 +
36 @Override 56 @Override
37 public RuleChainId getRuleChainId() { 57 public RuleChainId getRuleChainId() {
38 return target; 58 return target;
@@ -15,22 +15,28 @@ @@ -15,22 +15,28 @@
15 */ 15 */
16 package org.thingsboard.server.actors.ruleChain; 16 package org.thingsboard.server.actors.ruleChain;
17 17
18 -import lombok.Data; 18 +import lombok.EqualsAndHashCode;
  19 +import lombok.Getter;
  20 +import lombok.ToString;
19 import org.thingsboard.rule.engine.api.TbContext; 21 import org.thingsboard.rule.engine.api.TbContext;
20 import org.thingsboard.server.common.msg.MsgType; 22 import org.thingsboard.server.common.msg.MsgType;
21 -import org.thingsboard.server.common.msg.TbActorMsg;  
22 import org.thingsboard.server.common.msg.TbMsg; 23 import org.thingsboard.server.common.msg.TbMsg;
23 24
24 /** 25 /**
25 * Created by ashvayka on 19.03.18. 26 * Created by ashvayka on 19.03.18.
26 */ 27 */
27 -@Data  
28 -final class RuleChainToRuleNodeMsg implements TbActorMsg { 28 +@EqualsAndHashCode(callSuper = true)
  29 +@ToString
  30 +final class RuleChainToRuleNodeMsg extends TbToRuleNodeActorMsg {
29 31
30 - private final TbContext ctx;  
31 - private final TbMsg msg; 32 + @Getter
32 private final String fromRelationType; 33 private final String fromRelationType;
33 34
  35 + public RuleChainToRuleNodeMsg(TbContext ctx, TbMsg tbMsg, String fromRelationType) {
  36 + super(ctx, tbMsg);
  37 + this.fromRelationType = fromRelationType;
  38 + }
  39 +
34 @Override 40 @Override
35 public MsgType getMsgType() { 41 public MsgType getMsgType() {
36 return MsgType.RULE_CHAIN_TO_RULE_MSG; 42 return MsgType.RULE_CHAIN_TO_RULE_MSG;
@@ -26,7 +26,6 @@ import org.thingsboard.server.actors.service.ContextBasedCreator; @@ -26,7 +26,6 @@ import org.thingsboard.server.actors.service.ContextBasedCreator;
26 import org.thingsboard.server.common.data.id.RuleChainId; 26 import org.thingsboard.server.common.data.id.RuleChainId;
27 import org.thingsboard.server.common.data.id.RuleNodeId; 27 import org.thingsboard.server.common.data.id.RuleNodeId;
28 import org.thingsboard.server.common.data.id.TenantId; 28 import org.thingsboard.server.common.data.id.TenantId;
29 -import org.thingsboard.server.common.data.rule.RuleChain;  
30 import org.thingsboard.server.common.msg.TbActorMsg; 29 import org.thingsboard.server.common.msg.TbActorMsg;
31 import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; 30 import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
32 import org.thingsboard.server.common.msg.queue.PartitionChangeMsg; 31 import org.thingsboard.server.common.msg.queue.PartitionChangeMsg;
@@ -54,14 +53,12 @@ public class RuleNodeActor extends ComponentActor<RuleNodeId, RuleNodeActorMessa @@ -54,14 +53,12 @@ public class RuleNodeActor extends ComponentActor<RuleNodeId, RuleNodeActorMessa
54 protected boolean doProcess(TbActorMsg msg) { 53 protected boolean doProcess(TbActorMsg msg) {
55 switch (msg.getMsgType()) { 54 switch (msg.getMsgType()) {
56 case COMPONENT_LIFE_CYCLE_MSG: 55 case COMPONENT_LIFE_CYCLE_MSG:
  56 + case RULE_NODE_UPDATED_MSG:
57 onComponentLifecycleMsg((ComponentLifecycleMsg) msg); 57 onComponentLifecycleMsg((ComponentLifecycleMsg) msg);
58 break; 58 break;
59 case RULE_CHAIN_TO_RULE_MSG: 59 case RULE_CHAIN_TO_RULE_MSG:
60 onRuleChainToRuleNodeMsg((RuleChainToRuleNodeMsg) msg); 60 onRuleChainToRuleNodeMsg((RuleChainToRuleNodeMsg) msg);
61 break; 61 break;
62 - case RULE_TO_SELF_ERROR_MSG:  
63 - onRuleNodeToSelfErrorMsg((RuleNodeToSelfErrorMsg) msg);  
64 - break;  
65 case RULE_TO_SELF_MSG: 62 case RULE_TO_SELF_MSG:
66 onRuleNodeToSelfMsg((RuleNodeToSelfMsg) msg); 63 onRuleNodeToSelfMsg((RuleNodeToSelfMsg) msg);
67 break; 64 break;
@@ -101,10 +98,6 @@ public class RuleNodeActor extends ComponentActor<RuleNodeId, RuleNodeActorMessa @@ -101,10 +98,6 @@ public class RuleNodeActor extends ComponentActor<RuleNodeId, RuleNodeActorMessa
101 } 98 }
102 } 99 }
103 100
104 - private void onRuleNodeToSelfErrorMsg(RuleNodeToSelfErrorMsg msg) {  
105 - logAndPersist("onRuleMsg", ActorSystemContext.toException(msg.getError()));  
106 - }  
107 -  
108 public static class ActorCreator extends ContextBasedCreator { 101 public static class ActorCreator extends ContextBasedCreator {
109 102
110 private final TenantId tenantId; 103 private final TenantId tenantId;
@@ -20,14 +20,13 @@ import org.thingsboard.rule.engine.api.TbNodeConfiguration; @@ -20,14 +20,13 @@ import org.thingsboard.rule.engine.api.TbNodeConfiguration;
20 import org.thingsboard.server.actors.ActorSystemContext; 20 import org.thingsboard.server.actors.ActorSystemContext;
21 import org.thingsboard.server.actors.TbActorCtx; 21 import org.thingsboard.server.actors.TbActorCtx;
22 import org.thingsboard.server.actors.TbActorRef; 22 import org.thingsboard.server.actors.TbActorRef;
  23 +import org.thingsboard.server.actors.TbRuleNodeUpdateException;
23 import org.thingsboard.server.actors.shared.ComponentMsgProcessor; 24 import org.thingsboard.server.actors.shared.ComponentMsgProcessor;
24 import org.thingsboard.server.common.data.ApiUsageRecordKey; 25 import org.thingsboard.server.common.data.ApiUsageRecordKey;
25 -import org.thingsboard.server.common.data.TenantProfile;  
26 import org.thingsboard.server.common.data.id.RuleNodeId; 26 import org.thingsboard.server.common.data.id.RuleNodeId;
27 import org.thingsboard.server.common.data.id.TenantId; 27 import org.thingsboard.server.common.data.id.TenantId;
28 import org.thingsboard.server.common.data.plugin.ComponentLifecycleState; 28 import org.thingsboard.server.common.data.plugin.ComponentLifecycleState;
29 import org.thingsboard.server.common.data.rule.RuleNode; 29 import org.thingsboard.server.common.data.rule.RuleNode;
30 -import org.thingsboard.server.common.data.tenant.profile.TenantProfileConfiguration;  
31 import org.thingsboard.server.common.msg.TbMsg; 30 import org.thingsboard.server.common.msg.TbMsg;
32 import org.thingsboard.server.common.msg.queue.PartitionChangeMsg; 31 import org.thingsboard.server.common.msg.queue.PartitionChangeMsg;
33 import org.thingsboard.server.common.msg.queue.RuleNodeException; 32 import org.thingsboard.server.common.msg.queue.RuleNodeException;
@@ -54,7 +53,7 @@ public class RuleNodeActorMessageProcessor extends ComponentMsgProcessor<RuleNod @@ -54,7 +53,7 @@ public class RuleNodeActorMessageProcessor extends ComponentMsgProcessor<RuleNod
54 this.ruleChainName = ruleChainName; 53 this.ruleChainName = ruleChainName;
55 this.self = self; 54 this.self = self;
56 this.ruleNode = systemContext.getRuleChainService().findRuleNodeById(tenantId, entityId); 55 this.ruleNode = systemContext.getRuleChainService().findRuleNodeById(tenantId, entityId);
57 - this.defaultCtx = new DefaultTbContext(systemContext, new RuleNodeCtx(tenantId, parent, self, ruleNode)); 56 + this.defaultCtx = new DefaultTbContext(systemContext, ruleChainName, new RuleNodeCtx(tenantId, parent, self, ruleNode));
58 this.info = new RuleNodeInfo(ruleNodeId, ruleChainName, ruleNode != null ? ruleNode.getName() : "Unknown"); 57 this.info = new RuleNodeInfo(ruleNodeId, ruleChainName, ruleNode != null ? ruleNode.getName() : "Unknown");
59 } 58 }
60 59
@@ -78,7 +77,11 @@ public class RuleNodeActorMessageProcessor extends ComponentMsgProcessor<RuleNod @@ -78,7 +77,11 @@ public class RuleNodeActorMessageProcessor extends ComponentMsgProcessor<RuleNod
78 if (tbNode != null) { 77 if (tbNode != null) {
79 tbNode.destroy(); 78 tbNode.destroy();
80 } 79 }
81 - start(context); 80 + try {
  81 + start(context);
  82 + } catch (Exception e) {
  83 + throw new TbRuleNodeUpdateException("Failed to update rule node", e);
  84 + }
82 } 85 }
83 } 86 }
84 87
@@ -147,7 +150,7 @@ public class RuleNodeActorMessageProcessor extends ComponentMsgProcessor<RuleNod @@ -147,7 +150,7 @@ public class RuleNodeActorMessageProcessor extends ComponentMsgProcessor<RuleNod
147 TbNode tbNode = null; 150 TbNode tbNode = null;
148 if (ruleNode != null) { 151 if (ruleNode != null) {
149 Class<?> componentClazz = Class.forName(ruleNode.getType()); 152 Class<?> componentClazz = Class.forName(ruleNode.getType());
150 - tbNode = (TbNode) (componentClazz.newInstance()); 153 + tbNode = (TbNode) (componentClazz.getDeclaredConstructor().newInstance());
151 tbNode.init(defaultCtx, new TbNodeConfiguration(ruleNode.getConfiguration())); 154 tbNode.init(defaultCtx, new TbNodeConfiguration(ruleNode.getConfiguration()));
152 } 155 }
153 return tbNode; 156 return tbNode;
@@ -15,11 +15,16 @@ @@ -15,11 +15,16 @@
15 */ 15 */
16 package org.thingsboard.server.actors.ruleChain; 16 package org.thingsboard.server.actors.ruleChain;
17 17
18 -import lombok.Data; 18 +import lombok.EqualsAndHashCode;
  19 +import lombok.Getter;
  20 +import lombok.ToString;
  21 +import org.thingsboard.server.common.data.id.RuleChainId;
19 import org.thingsboard.server.common.data.id.RuleNodeId; 22 import org.thingsboard.server.common.data.id.RuleNodeId;
20 import org.thingsboard.server.common.msg.MsgType; 23 import org.thingsboard.server.common.msg.MsgType;
21 -import org.thingsboard.server.common.msg.TbActorMsg; 24 +import org.thingsboard.server.common.msg.TbActorStopReason;
22 import org.thingsboard.server.common.msg.TbMsg; 25 import org.thingsboard.server.common.msg.TbMsg;
  26 +import org.thingsboard.server.common.msg.TbRuleEngineActorMsg;
  27 +import org.thingsboard.server.common.msg.queue.RuleEngineException;
23 28
24 import java.io.Serializable; 29 import java.io.Serializable;
25 import java.util.Set; 30 import java.util.Set;
@@ -27,15 +32,34 @@ import java.util.Set; @@ -27,15 +32,34 @@ import java.util.Set;
27 /** 32 /**
28 * Created by ashvayka on 19.03.18. 33 * Created by ashvayka on 19.03.18.
29 */ 34 */
30 -@Data  
31 -class RuleNodeToRuleChainTellNextMsg implements TbActorMsg, Serializable { 35 +@EqualsAndHashCode(callSuper = true)
  36 +@ToString
  37 +class RuleNodeToRuleChainTellNextMsg extends TbRuleEngineActorMsg implements Serializable {
32 38
33 private static final long serialVersionUID = 4577026446412871820L; 39 private static final long serialVersionUID = 4577026446412871820L;
  40 + @Getter
  41 + private final RuleChainId ruleChainId;
  42 + @Getter
34 private final RuleNodeId originator; 43 private final RuleNodeId originator;
  44 + @Getter
35 private final Set<String> relationTypes; 45 private final Set<String> relationTypes;
36 - private final TbMsg msg; 46 + @Getter
37 private final String failureMessage; 47 private final String failureMessage;
38 48
  49 + public RuleNodeToRuleChainTellNextMsg(RuleChainId ruleChainId, RuleNodeId originator, Set<String> relationTypes, TbMsg tbMsg, String failureMessage) {
  50 + super(tbMsg);
  51 + this.ruleChainId = ruleChainId;
  52 + this.originator = originator;
  53 + this.relationTypes = relationTypes;
  54 + this.failureMessage = failureMessage;
  55 + }
  56 +
  57 + @Override
  58 + public void onTbActorStopped(TbActorStopReason reason) {
  59 + String message = reason == TbActorStopReason.STOPPED ? String.format("Rule chain [%s] stopped", ruleChainId.getId()) : String.format("Failed to initialize rule chain [%s]!", ruleChainId.getId());
  60 + msg.getCallback().onFailure(new RuleEngineException(message));
  61 + }
  62 +
39 @Override 63 @Override
40 public MsgType getMsgType() { 64 public MsgType getMsgType() {
41 return MsgType.RULE_TO_RULE_CHAIN_TELL_NEXT_MSG; 65 return MsgType.RULE_TO_RULE_CHAIN_TELL_NEXT_MSG;
@@ -15,18 +15,25 @@ @@ -15,18 +15,25 @@
15 */ 15 */
16 package org.thingsboard.server.actors.ruleChain; 16 package org.thingsboard.server.actors.ruleChain;
17 17
18 -import lombok.Data; 18 +import lombok.EqualsAndHashCode;
  19 +import lombok.ToString;
  20 +import org.thingsboard.rule.engine.api.TbContext;
19 import org.thingsboard.server.common.msg.MsgType; 21 import org.thingsboard.server.common.msg.MsgType;
20 -import org.thingsboard.server.common.msg.TbActorMsg; 22 +import org.thingsboard.server.common.msg.TbActorStopReason;
21 import org.thingsboard.server.common.msg.TbMsg; 23 import org.thingsboard.server.common.msg.TbMsg;
  24 +import org.thingsboard.server.common.msg.TbRuleEngineActorMsg;
  25 +import org.thingsboard.server.common.msg.queue.RuleNodeException;
22 26
23 /** 27 /**
24 * Created by ashvayka on 19.03.18. 28 * Created by ashvayka on 19.03.18.
25 */ 29 */
26 -@Data  
27 -final class RuleNodeToSelfMsg implements TbActorMsg { 30 +@EqualsAndHashCode(callSuper = true)
  31 +@ToString
  32 +final class RuleNodeToSelfMsg extends TbToRuleNodeActorMsg {
28 33
29 - private final TbMsg msg; 34 + public RuleNodeToSelfMsg(TbContext ctx, TbMsg tbMsg) {
  35 + super(ctx, tbMsg);
  36 + }
30 37
31 @Override 38 @Override
32 public MsgType getMsgType() { 39 public MsgType getMsgType() {
  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.actors.ruleChain;
  17 +
  18 +import lombok.EqualsAndHashCode;
  19 +import lombok.Getter;
  20 +import org.thingsboard.rule.engine.api.TbContext;
  21 +import org.thingsboard.server.common.msg.TbActorStopReason;
  22 +import org.thingsboard.server.common.msg.TbMsg;
  23 +import org.thingsboard.server.common.msg.TbRuleEngineActorMsg;
  24 +import org.thingsboard.server.common.msg.queue.RuleNodeException;
  25 +
  26 +@EqualsAndHashCode(callSuper = true)
  27 +public abstract class TbToRuleNodeActorMsg extends TbRuleEngineActorMsg {
  28 +
  29 + @Getter
  30 + private final TbContext ctx;
  31 +
  32 + public TbToRuleNodeActorMsg(TbContext ctx, TbMsg tbMsg) {
  33 + super(tbMsg);
  34 + this.ctx = ctx;
  35 + }
  36 +
  37 + @Override
  38 + public void onTbActorStopped(TbActorStopReason reason) {
  39 + String message = reason == TbActorStopReason.STOPPED ? "Rule node stopped" : "Failed to initialize rule node!";
  40 + msg.getCallback().onFailure(new RuleNodeException(message, ctx.getRuleChainName(), ctx.getSelf()));
  41 + }
  42 +}
@@ -20,6 +20,7 @@ import org.thingsboard.server.actors.ActorSystemContext; @@ -20,6 +20,7 @@ import org.thingsboard.server.actors.ActorSystemContext;
20 import org.thingsboard.server.actors.TbActor; 20 import org.thingsboard.server.actors.TbActor;
21 import org.thingsboard.server.actors.TbActorCtx; 21 import org.thingsboard.server.actors.TbActorCtx;
22 import org.thingsboard.server.actors.TbActorException; 22 import org.thingsboard.server.actors.TbActorException;
  23 +import org.thingsboard.server.actors.TbRuleNodeUpdateException;
23 import org.thingsboard.server.actors.shared.ComponentMsgProcessor; 24 import org.thingsboard.server.actors.shared.ComponentMsgProcessor;
24 import org.thingsboard.server.actors.stats.StatsPersistMsg; 25 import org.thingsboard.server.actors.stats.StatsPersistMsg;
25 import org.thingsboard.server.common.data.id.EntityId; 26 import org.thingsboard.server.common.data.id.EntityId;
@@ -123,6 +124,9 @@ public abstract class ComponentActor<T extends EntityId, P extends ComponentMsgP @@ -123,6 +124,9 @@ public abstract class ComponentActor<T extends EntityId, P extends ComponentMsgP
123 } catch (Exception e) { 124 } catch (Exception e) {
124 logAndPersist("onLifecycleMsg", e, true); 125 logAndPersist("onLifecycleMsg", e, true);
125 logLifecycleEvent(msg.getEvent(), e); 126 logLifecycleEvent(msg.getEvent(), e);
  127 + if (e instanceof TbRuleNodeUpdateException) {
  128 + throw (TbRuleNodeUpdateException) e;
  129 + }
126 } 130 }
127 } 131 }
128 132
@@ -34,6 +34,7 @@ import org.thingsboard.server.actors.app.AppInitMsg; @@ -34,6 +34,7 @@ import org.thingsboard.server.actors.app.AppInitMsg;
34 import org.thingsboard.server.actors.stats.StatsActor; 34 import org.thingsboard.server.actors.stats.StatsActor;
35 import org.thingsboard.server.common.msg.queue.PartitionChangeMsg; 35 import org.thingsboard.server.common.msg.queue.PartitionChangeMsg;
36 import org.thingsboard.server.queue.discovery.PartitionChangeEvent; 36 import org.thingsboard.server.queue.discovery.PartitionChangeEvent;
  37 +import org.thingsboard.server.queue.discovery.TbApplicationEventListener;
37 38
38 import javax.annotation.PostConstruct; 39 import javax.annotation.PostConstruct;
39 import javax.annotation.PreDestroy; 40 import javax.annotation.PreDestroy;
@@ -43,7 +44,7 @@ import java.util.concurrent.ScheduledExecutorService; @@ -43,7 +44,7 @@ import java.util.concurrent.ScheduledExecutorService;
43 44
44 @Service 45 @Service
45 @Slf4j 46 @Slf4j
46 -public class DefaultActorService implements ActorService { 47 +public class DefaultActorService extends TbApplicationEventListener<PartitionChangeEvent> implements ActorService {
47 48
48 public static final String APP_DISPATCHER_NAME = "app-dispatcher"; 49 public static final String APP_DISPATCHER_NAME = "app-dispatcher";
49 public static final String TENANT_DISPATCHER_NAME = "tenant-dispatcher"; 50 public static final String TENANT_DISPATCHER_NAME = "tenant-dispatcher";
@@ -120,10 +121,10 @@ public class DefaultActorService implements ActorService { @@ -120,10 +121,10 @@ public class DefaultActorService implements ActorService {
120 appActor.tellWithHighPriority(new AppInitMsg()); 121 appActor.tellWithHighPriority(new AppInitMsg());
121 } 122 }
122 123
123 - @EventListener(PartitionChangeEvent.class)  
124 - public void onApplicationEvent(PartitionChangeEvent partitionChangeEvent) { 124 + @Override
  125 + protected void onTbApplicationEvent(PartitionChangeEvent event) {
125 log.info("Received partition change event."); 126 log.info("Received partition change event.");
126 - this.appActor.tellWithHighPriority(new PartitionChangeMsg(partitionChangeEvent.getServiceQueueKey(), partitionChangeEvent.getPartitions())); 127 + this.appActor.tellWithHighPriority(new PartitionChangeMsg(event.getServiceQueueKey(), event.getPartitions()));
127 } 128 }
128 129
129 @PreDestroy 130 @PreDestroy
@@ -28,10 +28,10 @@ import org.thingsboard.server.common.msg.TbActorMsg; @@ -28,10 +28,10 @@ import org.thingsboard.server.common.msg.TbActorMsg;
28 @ToString 28 @ToString
29 public final class StatsPersistMsg implements TbActorMsg { 29 public final class StatsPersistMsg implements TbActorMsg {
30 30
31 - private long messagesProcessed;  
32 - private long errorsOccurred;  
33 - private TenantId tenantId;  
34 - private EntityId entityId; 31 + private final long messagesProcessed;
  32 + private final long errorsOccurred;
  33 + private final TenantId tenantId;
  34 + private final EntityId entityId;
35 35
36 @Override 36 @Override
37 public MsgType getMsgType() { 37 public MsgType getMsgType() {
@@ -18,7 +18,7 @@ package org.thingsboard.server.actors.stats; @@ -18,7 +18,7 @@ package org.thingsboard.server.actors.stats;
18 import org.thingsboard.server.common.msg.MsgType; 18 import org.thingsboard.server.common.msg.MsgType;
19 import org.thingsboard.server.common.msg.TbActorMsg; 19 import org.thingsboard.server.common.msg.TbActorMsg;
20 20
21 -public final class StatsPersistTick implements TbActorMsg{ 21 +public final class StatsPersistTick implements TbActorMsg {
22 @Override 22 @Override
23 public MsgType getMsgType() { 23 public MsgType getMsgType() {
24 return MsgType.STATS_PERSIST_TICK_MSG; 24 return MsgType.STATS_PERSIST_TICK_MSG;
@@ -119,7 +119,7 @@ public class TenantActor extends RuleChainManagerActor { @@ -119,7 +119,7 @@ public class TenantActor extends RuleChainManagerActor {
119 log.info("[{}] Processing missing Tenant msg: {}", tenantId, msg); 119 log.info("[{}] Processing missing Tenant msg: {}", tenantId, msg);
120 if (msg.getMsgType().equals(MsgType.QUEUE_TO_RULE_ENGINE_MSG)) { 120 if (msg.getMsgType().equals(MsgType.QUEUE_TO_RULE_ENGINE_MSG)) {
121 QueueToRuleEngineMsg queueMsg = (QueueToRuleEngineMsg) msg; 121 QueueToRuleEngineMsg queueMsg = (QueueToRuleEngineMsg) msg;
122 - queueMsg.getTbMsg().getCallback().onSuccess(); 122 + queueMsg.getMsg().getCallback().onSuccess();
123 } else if (msg.getMsgType().equals(MsgType.TRANSPORT_TO_DEVICE_ACTOR_MSG)) { 123 } else if (msg.getMsgType().equals(MsgType.TRANSPORT_TO_DEVICE_ACTOR_MSG)) {
124 TransportToDeviceActorMsgWrapper transportMsg = (TransportToDeviceActorMsgWrapper) msg; 124 TransportToDeviceActorMsgWrapper transportMsg = (TransportToDeviceActorMsgWrapper) msg;
125 transportMsg.getCallback().onSuccess(); 125 transportMsg.getCallback().onSuccess();
@@ -177,7 +177,7 @@ public class TenantActor extends RuleChainManagerActor { @@ -177,7 +177,7 @@ public class TenantActor extends RuleChainManagerActor {
177 log.warn("RECEIVED INVALID MESSAGE: {}", msg); 177 log.warn("RECEIVED INVALID MESSAGE: {}", msg);
178 return; 178 return;
179 } 179 }
180 - TbMsg tbMsg = msg.getTbMsg(); 180 + TbMsg tbMsg = msg.getMsg();
181 if (apiUsageState.isReExecEnabled()) { 181 if (apiUsageState.isReExecEnabled()) {
182 if (tbMsg.getRuleChainId() == null) { 182 if (tbMsg.getRuleChainId() == null) {
183 if (getRootChainActor() != null) { 183 if (getRootChainActor() != null) {
@@ -91,6 +91,7 @@ public class CustomOAuth2AuthorizationRequestResolver implements OAuth2Authoriza @@ -91,6 +91,7 @@ public class CustomOAuth2AuthorizationRequestResolver implements OAuth2Authoriza
91 return action; 91 return action;
92 } 92 }
93 93
  94 + @SuppressWarnings("deprecation")
94 private OAuth2AuthorizationRequest resolve(HttpServletRequest request, String registrationId, String redirectUriAction) { 95 private OAuth2AuthorizationRequest resolve(HttpServletRequest request, String registrationId, String redirectUriAction) {
95 if (registrationId == null) { 96 if (registrationId == null) {
96 return null; 97 return null;
@@ -48,6 +48,7 @@ import org.thingsboard.server.service.security.auth.jwt.RefreshTokenAuthenticati @@ -48,6 +48,7 @@ import org.thingsboard.server.service.security.auth.jwt.RefreshTokenAuthenticati
48 import org.thingsboard.server.service.security.auth.jwt.RefreshTokenProcessingFilter; 48 import org.thingsboard.server.service.security.auth.jwt.RefreshTokenProcessingFilter;
49 import org.thingsboard.server.service.security.auth.jwt.SkipPathRequestMatcher; 49 import org.thingsboard.server.service.security.auth.jwt.SkipPathRequestMatcher;
50 import org.thingsboard.server.service.security.auth.jwt.extractor.TokenExtractor; 50 import org.thingsboard.server.service.security.auth.jwt.extractor.TokenExtractor;
  51 +import org.thingsboard.server.service.security.auth.oauth2.HttpCookieOAuth2AuthorizationRequestRepository;
51 import org.thingsboard.server.service.security.auth.rest.RestAuthenticationProvider; 52 import org.thingsboard.server.service.security.auth.rest.RestAuthenticationProvider;
52 import org.thingsboard.server.service.security.auth.rest.RestLoginProcessingFilter; 53 import org.thingsboard.server.service.security.auth.rest.RestLoginProcessingFilter;
53 import org.thingsboard.server.service.security.auth.rest.RestPublicLoginProcessingFilter; 54 import org.thingsboard.server.service.security.auth.rest.RestPublicLoginProcessingFilter;
@@ -84,6 +85,9 @@ public class ThingsboardSecurityConfiguration extends WebSecurityConfigurerAdapt @@ -84,6 +85,9 @@ public class ThingsboardSecurityConfiguration extends WebSecurityConfigurerAdapt
84 @Qualifier("oauth2AuthenticationFailureHandler") 85 @Qualifier("oauth2AuthenticationFailureHandler")
85 private AuthenticationFailureHandler oauth2AuthenticationFailureHandler; 86 private AuthenticationFailureHandler oauth2AuthenticationFailureHandler;
86 87
  88 + @Autowired(required = false)
  89 + private HttpCookieOAuth2AuthorizationRequestRepository httpCookieOAuth2AuthorizationRequestRepository;
  90 +
87 @Autowired 91 @Autowired
88 @Qualifier("defaultAuthenticationSuccessHandler") 92 @Qualifier("defaultAuthenticationSuccessHandler")
89 private AuthenticationSuccessHandler successHandler; 93 private AuthenticationSuccessHandler successHandler;
@@ -127,7 +131,7 @@ public class ThingsboardSecurityConfiguration extends WebSecurityConfigurerAdapt @@ -127,7 +131,7 @@ public class ThingsboardSecurityConfiguration extends WebSecurityConfigurerAdapt
127 } 131 }
128 132
129 protected JwtTokenAuthenticationProcessingFilter buildJwtTokenAuthenticationProcessingFilter() throws Exception { 133 protected JwtTokenAuthenticationProcessingFilter buildJwtTokenAuthenticationProcessingFilter() throws Exception {
130 - List<String> pathsToSkip = new ArrayList(Arrays.asList(NON_TOKEN_BASED_AUTH_ENTRY_POINTS)); 134 + List<String> pathsToSkip = new ArrayList<>(Arrays.asList(NON_TOKEN_BASED_AUTH_ENTRY_POINTS));
131 pathsToSkip.addAll(Arrays.asList(WS_TOKEN_BASED_AUTH_ENTRY_POINT, TOKEN_REFRESH_ENTRY_POINT, FORM_BASED_LOGIN_ENTRY_POINT, 135 pathsToSkip.addAll(Arrays.asList(WS_TOKEN_BASED_AUTH_ENTRY_POINT, TOKEN_REFRESH_ENTRY_POINT, FORM_BASED_LOGIN_ENTRY_POINT,
132 PUBLIC_LOGIN_ENTRY_POINT, DEVICE_API_ENTRY_POINT, WEBJARS_ENTRY_POINT)); 136 PUBLIC_LOGIN_ENTRY_POINT, DEVICE_API_ENTRY_POINT, WEBJARS_ENTRY_POINT));
133 SkipPathRequestMatcher matcher = new SkipPathRequestMatcher(pathsToSkip, TOKEN_BASED_AUTH_ENTRY_POINT); 137 SkipPathRequestMatcher matcher = new SkipPathRequestMatcher(pathsToSkip, TOKEN_BASED_AUTH_ENTRY_POINT);
@@ -213,7 +217,9 @@ public class ThingsboardSecurityConfiguration extends WebSecurityConfigurerAdapt @@ -213,7 +217,9 @@ public class ThingsboardSecurityConfiguration extends WebSecurityConfigurerAdapt
213 .addFilterAfter(rateLimitProcessingFilter, UsernamePasswordAuthenticationFilter.class); 217 .addFilterAfter(rateLimitProcessingFilter, UsernamePasswordAuthenticationFilter.class);
214 if (oauth2Configuration != null) { 218 if (oauth2Configuration != null) {
215 http.oauth2Login() 219 http.oauth2Login()
216 - .authorizationEndpoint().authorizationRequestResolver(oAuth2AuthorizationRequestResolver) 220 + .authorizationEndpoint()
  221 + .authorizationRequestRepository(httpCookieOAuth2AuthorizationRequestRepository)
  222 + .authorizationRequestResolver(oAuth2AuthorizationRequestResolver)
217 .and() 223 .and()
218 .loginPage("/oauth2Login") 224 .loginPage("/oauth2Login")
219 .loginProcessingUrl(oauth2Configuration.getLoginProcessingUrl()) 225 .loginProcessingUrl(oauth2Configuration.getLoginProcessingUrl())
@@ -215,6 +215,7 @@ public class AuthController extends BaseController { @@ -215,6 +215,7 @@ public class AuthController extends BaseController {
215 User user = userService.findUserById(TenantId.SYS_TENANT_ID, credentials.getUserId()); 215 User user = userService.findUserById(TenantId.SYS_TENANT_ID, credentials.getUserId());
216 UserPrincipal principal = new UserPrincipal(UserPrincipal.Type.USER_NAME, user.getEmail()); 216 UserPrincipal principal = new UserPrincipal(UserPrincipal.Type.USER_NAME, user.getEmail());
217 SecurityUser securityUser = new SecurityUser(user, credentials.isEnabled(), principal); 217 SecurityUser securityUser = new SecurityUser(user, credentials.isEnabled(), principal);
  218 + userService.setUserCredentialsEnabled(user.getTenantId(), user.getId(), true);
218 String baseUrl = systemSecurityService.getBaseUrl(user.getTenantId(), user.getCustomerId(), request); 219 String baseUrl = systemSecurityService.getBaseUrl(user.getTenantId(), user.getCustomerId(), request);
219 String loginUrl = String.format("%s/login", baseUrl); 220 String loginUrl = String.format("%s/login", baseUrl);
220 String email = user.getEmail(); 221 String email = user.getEmail();
@@ -27,7 +27,22 @@ import org.springframework.beans.factory.annotation.Value; @@ -27,7 +27,22 @@ import org.springframework.beans.factory.annotation.Value;
27 import org.springframework.security.core.Authentication; 27 import org.springframework.security.core.Authentication;
28 import org.springframework.security.core.context.SecurityContextHolder; 28 import org.springframework.security.core.context.SecurityContextHolder;
29 import org.springframework.web.bind.annotation.ExceptionHandler; 29 import org.springframework.web.bind.annotation.ExceptionHandler;
30 -import org.thingsboard.server.common.data.*; 30 +import org.thingsboard.server.common.data.Customer;
  31 +import org.thingsboard.server.common.data.Dashboard;
  32 +import org.thingsboard.server.common.data.DashboardInfo;
  33 +import org.thingsboard.server.common.data.DataConstants;
  34 +import org.thingsboard.server.common.data.Device;
  35 +import org.thingsboard.server.common.data.DeviceInfo;
  36 +import org.thingsboard.server.common.data.DeviceProfile;
  37 +import org.thingsboard.server.common.data.EntityType;
  38 +import org.thingsboard.server.common.data.EntityView;
  39 +import org.thingsboard.server.common.data.EntityViewInfo;
  40 +import org.thingsboard.server.common.data.HasName;
  41 +import org.thingsboard.server.common.data.HasTenantId;
  42 +import org.thingsboard.server.common.data.Tenant;
  43 +import org.thingsboard.server.common.data.TenantInfo;
  44 +import org.thingsboard.server.common.data.TenantProfile;
  45 +import org.thingsboard.server.common.data.User;
31 import org.thingsboard.server.common.data.alarm.Alarm; 46 import org.thingsboard.server.common.data.alarm.Alarm;
32 import org.thingsboard.server.common.data.alarm.AlarmInfo; 47 import org.thingsboard.server.common.data.alarm.AlarmInfo;
33 import org.thingsboard.server.common.data.asset.Asset; 48 import org.thingsboard.server.common.data.asset.Asset;
@@ -84,6 +99,7 @@ import org.thingsboard.server.dao.oauth2.OAuth2ConfigTemplateService; @@ -84,6 +99,7 @@ import org.thingsboard.server.dao.oauth2.OAuth2ConfigTemplateService;
84 import org.thingsboard.server.dao.oauth2.OAuth2Service; 99 import org.thingsboard.server.dao.oauth2.OAuth2Service;
85 import org.thingsboard.server.dao.relation.RelationService; 100 import org.thingsboard.server.dao.relation.RelationService;
86 import org.thingsboard.server.dao.rule.RuleChainService; 101 import org.thingsboard.server.dao.rule.RuleChainService;
  102 +import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
87 import org.thingsboard.server.dao.tenant.TenantProfileService; 103 import org.thingsboard.server.dao.tenant.TenantProfileService;
88 import org.thingsboard.server.dao.tenant.TenantService; 104 import org.thingsboard.server.dao.tenant.TenantService;
89 import org.thingsboard.server.dao.user.UserService; 105 import org.thingsboard.server.dao.user.UserService;
@@ -94,8 +110,8 @@ import org.thingsboard.server.queue.discovery.PartitionService; @@ -94,8 +110,8 @@ import org.thingsboard.server.queue.discovery.PartitionService;
94 import org.thingsboard.server.queue.provider.TbQueueProducerProvider; 110 import org.thingsboard.server.queue.provider.TbQueueProducerProvider;
95 import org.thingsboard.server.queue.util.TbCoreComponent; 111 import org.thingsboard.server.queue.util.TbCoreComponent;
96 import org.thingsboard.server.service.component.ComponentDiscoveryService; 112 import org.thingsboard.server.service.component.ComponentDiscoveryService;
  113 +import org.thingsboard.server.service.lwm2m.LwM2MModelsRepository;
97 import org.thingsboard.server.service.profile.TbDeviceProfileCache; 114 import org.thingsboard.server.service.profile.TbDeviceProfileCache;
98 -import org.thingsboard.server.dao.tenant.TbTenantProfileCache;  
99 import org.thingsboard.server.service.queue.TbClusterService; 115 import org.thingsboard.server.service.queue.TbClusterService;
100 import org.thingsboard.server.service.security.model.SecurityUser; 116 import org.thingsboard.server.service.security.model.SecurityUser;
101 import org.thingsboard.server.service.security.permission.AccessControlService; 117 import org.thingsboard.server.service.security.permission.AccessControlService;
@@ -123,6 +139,9 @@ public abstract class BaseController { @@ -123,6 +139,9 @@ public abstract class BaseController {
123 public static final String INCORRECT_TENANT_ID = "Incorrect tenantId "; 139 public static final String INCORRECT_TENANT_ID = "Incorrect tenantId ";
124 public static final String YOU_DON_T_HAVE_PERMISSION_TO_PERFORM_THIS_OPERATION = "You don't have permission to perform this operation!"; 140 public static final String YOU_DON_T_HAVE_PERMISSION_TO_PERFORM_THIS_OPERATION = "You don't have permission to perform this operation!";
125 141
  142 + protected static final String DEFAULT_DASHBOARD = "defaultDashboardId";
  143 + protected static final String HOME_DASHBOARD = "homeDashboardId";
  144 +
126 private static final ObjectMapper json = new ObjectMapper(); 145 private static final ObjectMapper json = new ObjectMapper();
127 146
128 @Autowired 147 @Autowired
@@ -215,6 +234,9 @@ public abstract class BaseController { @@ -215,6 +234,9 @@ public abstract class BaseController {
215 @Autowired 234 @Autowired
216 protected TbDeviceProfileCache deviceProfileCache; 235 protected TbDeviceProfileCache deviceProfileCache;
217 236
  237 + @Autowired
  238 + protected LwM2MModelsRepository lwM2MModelsRepository;
  239 +
218 @Value("${server.log_controller_error_stack_trace}") 240 @Value("${server.log_controller_error_stack_trace}")
219 @Getter 241 @Getter
220 private boolean logControllerErrorStackTrace; 242 private boolean logControllerErrorStackTrace;
@@ -645,6 +667,7 @@ public abstract class BaseController { @@ -645,6 +667,7 @@ public abstract class BaseController {
645 return ruleNode; 667 return ruleNode;
646 } 668 }
647 669
  670 + @SuppressWarnings("unchecked")
648 protected <I extends EntityId> I emptyId(EntityType entityType) { 671 protected <I extends EntityId> I emptyId(EntityType entityType) {
649 return (I) EntityIdFactory.getByTypeAndUuid(entityType, ModelConstants.NULL_UUID); 672 return (I) EntityIdFactory.getByTypeAndUuid(entityType, ModelConstants.NULL_UUID);
650 } 673 }
@@ -759,8 +782,9 @@ public abstract class BaseController { @@ -759,8 +782,9 @@ public abstract class BaseController {
759 entityNode = json.createObjectNode(); 782 entityNode = json.createObjectNode();
760 if (actionType == ActionType.ATTRIBUTES_UPDATED) { 783 if (actionType == ActionType.ATTRIBUTES_UPDATED) {
761 String scope = extractParameter(String.class, 0, additionalInfo); 784 String scope = extractParameter(String.class, 0, additionalInfo);
  785 + @SuppressWarnings("unchecked")
762 List<AttributeKvEntry> attributes = extractParameter(List.class, 1, additionalInfo); 786 List<AttributeKvEntry> attributes = extractParameter(List.class, 1, additionalInfo);
763 - metaData.putValue("scope", scope); 787 + metaData.putValue(DataConstants.SCOPE, scope);
764 if (attributes != null) { 788 if (attributes != null) {
765 for (AttributeKvEntry attr : attributes) { 789 for (AttributeKvEntry attr : attributes) {
766 addKvEntry(entityNode, attr); 790 addKvEntry(entityNode, attr);
@@ -768,16 +792,19 @@ public abstract class BaseController { @@ -768,16 +792,19 @@ public abstract class BaseController {
768 } 792 }
769 } else if (actionType == ActionType.ATTRIBUTES_DELETED) { 793 } else if (actionType == ActionType.ATTRIBUTES_DELETED) {
770 String scope = extractParameter(String.class, 0, additionalInfo); 794 String scope = extractParameter(String.class, 0, additionalInfo);
  795 + @SuppressWarnings("unchecked")
771 List<String> keys = extractParameter(List.class, 1, additionalInfo); 796 List<String> keys = extractParameter(List.class, 1, additionalInfo);
772 - metaData.putValue("scope", scope); 797 + metaData.putValue(DataConstants.SCOPE, scope);
773 ArrayNode attrsArrayNode = entityNode.putArray("attributes"); 798 ArrayNode attrsArrayNode = entityNode.putArray("attributes");
774 if (keys != null) { 799 if (keys != null) {
775 keys.forEach(attrsArrayNode::add); 800 keys.forEach(attrsArrayNode::add);
776 } 801 }
777 } else if (actionType == ActionType.TIMESERIES_UPDATED) { 802 } else if (actionType == ActionType.TIMESERIES_UPDATED) {
  803 + @SuppressWarnings("unchecked")
778 List<TsKvEntry> timeseries = extractParameter(List.class, 0, additionalInfo); 804 List<TsKvEntry> timeseries = extractParameter(List.class, 0, additionalInfo);
779 addTimeseries(entityNode, timeseries); 805 addTimeseries(entityNode, timeseries);
780 } else if (actionType == ActionType.TIMESERIES_DELETED) { 806 } else if (actionType == ActionType.TIMESERIES_DELETED) {
  807 + @SuppressWarnings("unchecked")
781 List<String> keys = extractParameter(List.class, 0, additionalInfo); 808 List<String> keys = extractParameter(List.class, 0, additionalInfo);
782 if (keys != null) { 809 if (keys != null) {
783 ArrayNode timeseriesArrayNode = entityNode.putArray("timeseries"); 810 ArrayNode timeseriesArrayNode = entityNode.putArray("timeseries");
@@ -853,4 +880,14 @@ public abstract class BaseController { @@ -853,4 +880,14 @@ public abstract class BaseController {
853 } 880 }
854 } 881 }
855 } 882 }
  883 +
  884 + protected void processDashboardIdFromAdditionalInfo(ObjectNode additionalInfo, String requiredFields) throws ThingsboardException {
  885 + String dashboardId = additionalInfo.has(requiredFields) ? additionalInfo.get(requiredFields).asText() : null;
  886 + if(dashboardId != null && !dashboardId.equals("null")) {
  887 + if(dashboardService.findDashboardById(getTenantId(), new DashboardId(UUID.fromString(dashboardId))) == null) {
  888 + additionalInfo.remove(requiredFields);
  889 + }
  890 + }
  891 + }
  892 +
856 } 893 }
@@ -55,7 +55,11 @@ public class CustomerController extends BaseController { @@ -55,7 +55,11 @@ public class CustomerController extends BaseController {
55 checkParameter(CUSTOMER_ID, strCustomerId); 55 checkParameter(CUSTOMER_ID, strCustomerId);
56 try { 56 try {
57 CustomerId customerId = new CustomerId(toUUID(strCustomerId)); 57 CustomerId customerId = new CustomerId(toUUID(strCustomerId));
58 - return checkCustomerId(customerId, Operation.READ); 58 + Customer customer = checkCustomerId(customerId, Operation.READ);
  59 + if(!customer.getAdditionalInfo().isNull()) {
  60 + processDashboardIdFromAdditionalInfo((ObjectNode) customer.getAdditionalInfo(), HOME_DASHBOARD);
  61 + }
  62 + return customer;
59 } catch (Exception e) { 63 } catch (Exception e) {
60 throw handleException(e); 64 throw handleException(e);
61 } 65 }
@@ -15,6 +15,8 @@ @@ -15,6 +15,8 @@
15 */ 15 */
16 package org.thingsboard.server.controller; 16 package org.thingsboard.server.controller;
17 17
  18 +import com.fasterxml.jackson.databind.JsonNode;
  19 +import com.fasterxml.jackson.databind.node.ObjectNode;
18 import org.springframework.beans.factory.annotation.Value; 20 import org.springframework.beans.factory.annotation.Value;
19 import org.springframework.http.HttpStatus; 21 import org.springframework.http.HttpStatus;
20 import org.springframework.security.access.prepost.PreAuthorize; 22 import org.springframework.security.access.prepost.PreAuthorize;
@@ -30,7 +32,11 @@ import org.thingsboard.server.common.data.Customer; @@ -30,7 +32,11 @@ import org.thingsboard.server.common.data.Customer;
30 import org.thingsboard.server.common.data.Dashboard; 32 import org.thingsboard.server.common.data.Dashboard;
31 import org.thingsboard.server.common.data.DashboardInfo; 33 import org.thingsboard.server.common.data.DashboardInfo;
32 import org.thingsboard.server.common.data.EntityType; 34 import org.thingsboard.server.common.data.EntityType;
  35 +import org.thingsboard.server.common.data.HomeDashboard;
  36 +import org.thingsboard.server.common.data.HomeDashboardInfo;
33 import org.thingsboard.server.common.data.ShortCustomerInfo; 37 import org.thingsboard.server.common.data.ShortCustomerInfo;
  38 +import org.thingsboard.server.common.data.Tenant;
  39 +import org.thingsboard.server.common.data.User;
34 import org.thingsboard.server.common.data.audit.ActionType; 40 import org.thingsboard.server.common.data.audit.ActionType;
35 import org.thingsboard.server.common.data.exception.ThingsboardException; 41 import org.thingsboard.server.common.data.exception.ThingsboardException;
36 import org.thingsboard.server.common.data.id.CustomerId; 42 import org.thingsboard.server.common.data.id.CustomerId;
@@ -38,8 +44,9 @@ import org.thingsboard.server.common.data.id.DashboardId; @@ -38,8 +44,9 @@ import org.thingsboard.server.common.data.id.DashboardId;
38 import org.thingsboard.server.common.data.id.TenantId; 44 import org.thingsboard.server.common.data.id.TenantId;
39 import org.thingsboard.server.common.data.page.PageData; 45 import org.thingsboard.server.common.data.page.PageData;
40 import org.thingsboard.server.common.data.page.PageLink; 46 import org.thingsboard.server.common.data.page.PageLink;
41 -import org.thingsboard.server.common.data.page.TimePageLink; 47 +import org.thingsboard.common.util.JacksonUtil;
42 import org.thingsboard.server.queue.util.TbCoreComponent; 48 import org.thingsboard.server.queue.util.TbCoreComponent;
  49 +import org.thingsboard.server.service.security.model.SecurityUser;
43 import org.thingsboard.server.service.security.permission.Operation; 50 import org.thingsboard.server.service.security.permission.Operation;
44 import org.thingsboard.server.service.security.permission.Resource; 51 import org.thingsboard.server.service.security.permission.Resource;
45 52
@@ -53,6 +60,9 @@ public class DashboardController extends BaseController { @@ -53,6 +60,9 @@ public class DashboardController extends BaseController {
53 60
54 public static final String DASHBOARD_ID = "dashboardId"; 61 public static final String DASHBOARD_ID = "dashboardId";
55 62
  63 + private static final String HOME_DASHBOARD_ID = "homeDashboardId";
  64 + private static final String HOME_DASHBOARD_HIDE_TOOLBAR = "homeDashboardHideToolbar";
  65 +
56 @Value("${dashboard.max_datapoints_limit}") 66 @Value("${dashboard.max_datapoints_limit}")
57 private long maxDatapointsLimit; 67 private long maxDatapointsLimit;
58 68
@@ -472,4 +482,100 @@ public class DashboardController extends BaseController { @@ -472,4 +482,100 @@ public class DashboardController extends BaseController {
472 throw handleException(e); 482 throw handleException(e);
473 } 483 }
474 } 484 }
  485 +
  486 + @PreAuthorize("isAuthenticated()")
  487 + @RequestMapping(value = "/dashboard/home", method = RequestMethod.GET)
  488 + @ResponseBody
  489 + public HomeDashboard getHomeDashboard() throws ThingsboardException {
  490 + try {
  491 + SecurityUser securityUser = getCurrentUser();
  492 + if (securityUser.isSystemAdmin()) {
  493 + return null;
  494 + }
  495 + User user = userService.findUserById(securityUser.getTenantId(), securityUser.getId());
  496 + JsonNode additionalInfo = user.getAdditionalInfo();
  497 + HomeDashboard homeDashboard;
  498 + homeDashboard = extractHomeDashboardFromAdditionalInfo(additionalInfo);
  499 + if (homeDashboard == null) {
  500 + if (securityUser.isCustomerUser()) {
  501 + Customer customer = customerService.findCustomerById(securityUser.getTenantId(), securityUser.getCustomerId());
  502 + additionalInfo = customer.getAdditionalInfo();
  503 + homeDashboard = extractHomeDashboardFromAdditionalInfo(additionalInfo);
  504 + }
  505 + if (homeDashboard == null) {
  506 + Tenant tenant = tenantService.findTenantById(securityUser.getTenantId());
  507 + additionalInfo = tenant.getAdditionalInfo();
  508 + homeDashboard = extractHomeDashboardFromAdditionalInfo(additionalInfo);
  509 + }
  510 + }
  511 + return homeDashboard;
  512 + } catch (Exception e) {
  513 + throw handleException(e);
  514 + }
  515 + }
  516 +
  517 + @PreAuthorize("hasAuthority('TENANT_ADMIN')")
  518 + @RequestMapping(value = "/tenant/dashboard/home/info", method = RequestMethod.GET)
  519 + @ResponseBody
  520 + public HomeDashboardInfo getTenantHomeDashboardInfo() throws ThingsboardException {
  521 + try {
  522 + Tenant tenant = tenantService.findTenantById(getTenantId());
  523 + JsonNode additionalInfo = tenant.getAdditionalInfo();
  524 + DashboardId dashboardId = null;
  525 + boolean hideDashboardToolbar = true;
  526 + if (additionalInfo != null && additionalInfo.has(HOME_DASHBOARD_ID) && !additionalInfo.get(HOME_DASHBOARD_ID).isNull()) {
  527 + String strDashboardId = additionalInfo.get(HOME_DASHBOARD_ID).asText();
  528 + dashboardId = new DashboardId(toUUID(strDashboardId));
  529 + if (additionalInfo.has(HOME_DASHBOARD_HIDE_TOOLBAR)) {
  530 + hideDashboardToolbar = additionalInfo.get(HOME_DASHBOARD_HIDE_TOOLBAR).asBoolean();
  531 + }
  532 + }
  533 + return new HomeDashboardInfo(dashboardId, hideDashboardToolbar);
  534 + } catch (Exception e) {
  535 + throw handleException(e);
  536 + }
  537 + }
  538 +
  539 + @PreAuthorize("hasAuthority('TENANT_ADMIN')")
  540 + @RequestMapping(value = "/tenant/dashboard/home/info", method = RequestMethod.POST)
  541 + @ResponseStatus(value = HttpStatus.OK)
  542 + public void setTenantHomeDashboardInfo(@RequestBody HomeDashboardInfo homeDashboardInfo) throws ThingsboardException {
  543 + try {
  544 + if (homeDashboardInfo.getDashboardId() != null) {
  545 + checkDashboardId(homeDashboardInfo.getDashboardId(), Operation.READ);
  546 + }
  547 + Tenant tenant = tenantService.findTenantById(getTenantId());
  548 + JsonNode additionalInfo = tenant.getAdditionalInfo();
  549 + if (additionalInfo == null || !(additionalInfo instanceof ObjectNode)) {
  550 + additionalInfo = JacksonUtil.OBJECT_MAPPER.createObjectNode();
  551 + }
  552 + if (homeDashboardInfo.getDashboardId() != null) {
  553 + ((ObjectNode) additionalInfo).put(HOME_DASHBOARD_ID, homeDashboardInfo.getDashboardId().getId().toString());
  554 + ((ObjectNode) additionalInfo).put(HOME_DASHBOARD_HIDE_TOOLBAR, homeDashboardInfo.isHideDashboardToolbar());
  555 + } else {
  556 + ((ObjectNode) additionalInfo).remove(HOME_DASHBOARD_ID);
  557 + ((ObjectNode) additionalInfo).remove(HOME_DASHBOARD_HIDE_TOOLBAR);
  558 + }
  559 + tenant.setAdditionalInfo(additionalInfo);
  560 + tenantService.saveTenant(tenant);
  561 + } catch (Exception e) {
  562 + throw handleException(e);
  563 + }
  564 + }
  565 +
  566 + private HomeDashboard extractHomeDashboardFromAdditionalInfo(JsonNode additionalInfo) {
  567 + try {
  568 + if (additionalInfo != null && additionalInfo.has(HOME_DASHBOARD_ID) && !additionalInfo.get(HOME_DASHBOARD_ID).isNull()) {
  569 + String strDashboardId = additionalInfo.get(HOME_DASHBOARD_ID).asText();
  570 + DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
  571 + Dashboard dashboard = checkDashboardId(dashboardId, Operation.READ);
  572 + boolean hideDashboardToolbar = true;
  573 + if (additionalInfo.has(HOME_DASHBOARD_HIDE_TOOLBAR)) {
  574 + hideDashboardToolbar = additionalInfo.get(HOME_DASHBOARD_HIDE_TOOLBAR).asBoolean();
  575 + }
  576 + return new HomeDashboard(dashboard, hideDashboardToolbar);
  577 + }
  578 + } catch (Exception e) {}
  579 + return null;
  580 + }
475 } 581 }
@@ -278,9 +278,7 @@ public class DeviceController extends BaseController { @@ -278,9 +278,7 @@ public class DeviceController extends BaseController {
278 try { 278 try {
279 Device device = checkDeviceId(deviceCredentials.getDeviceId(), Operation.WRITE_CREDENTIALS); 279 Device device = checkDeviceId(deviceCredentials.getDeviceId(), Operation.WRITE_CREDENTIALS);
280 DeviceCredentials result = checkNotNull(deviceCredentialsService.updateDeviceCredentials(getCurrentUser().getTenantId(), deviceCredentials)); 280 DeviceCredentials result = checkNotNull(deviceCredentialsService.updateDeviceCredentials(getCurrentUser().getTenantId(), deviceCredentials));
281 -  
282 - tbClusterService.pushMsgToCore(new DeviceCredentialsUpdateNotificationMsg(getCurrentUser().getTenantId(), deviceCredentials.getDeviceId()), null);  
283 - 281 + tbClusterService.pushMsgToCore(new DeviceCredentialsUpdateNotificationMsg(getCurrentUser().getTenantId(), deviceCredentials.getDeviceId(), result), null);
284 logEntityAction(device.getId(), device, 282 logEntityAction(device.getId(), device,
285 device.getCustomerId(), 283 device.getCustomerId(),
286 ActionType.CREDENTIALS_UPDATED, null, deviceCredentials); 284 ActionType.CREDENTIALS_UPDATED, null, deviceCredentials);
  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.ObjectMapper;
  19 +import lombok.extern.slf4j.Slf4j;
  20 +import org.springframework.security.access.prepost.PreAuthorize;
  21 +import org.springframework.web.bind.annotation.PathVariable;
  22 +import org.springframework.web.bind.annotation.RequestBody;
  23 +import org.springframework.web.bind.annotation.RequestMapping;
  24 +import org.springframework.web.bind.annotation.RequestMethod;
  25 +import org.springframework.web.bind.annotation.RequestParam;
  26 +import org.springframework.web.bind.annotation.ResponseBody;
  27 +import org.springframework.web.bind.annotation.RestController;
  28 +import org.thingsboard.rule.engine.api.msg.DeviceNameOrTypeUpdateMsg;
  29 +import org.thingsboard.server.common.data.Device;
  30 +import org.thingsboard.server.common.data.EntityType;
  31 +import org.thingsboard.server.common.data.audit.ActionType;
  32 +import org.thingsboard.server.common.data.exception.ThingsboardException;
  33 +import org.thingsboard.server.common.data.lwm2m.LwM2mObject;
  34 +import org.thingsboard.server.common.data.lwm2m.ServerSecurityConfig;
  35 +import org.thingsboard.server.common.data.page.PageData;
  36 +import org.thingsboard.server.common.data.page.PageLink;
  37 +import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
  38 +import org.thingsboard.server.common.data.security.DeviceCredentials;
  39 +import org.thingsboard.server.queue.util.TbCoreComponent;
  40 +import org.thingsboard.server.service.security.permission.Resource;
  41 +
  42 +import java.util.List;
  43 +import java.util.Map;
  44 +
  45 +@Slf4j
  46 +@RestController
  47 +@TbCoreComponent
  48 +@RequestMapping("/api")
  49 +public class DeviceLwm2mController extends BaseController {
  50 +
  51 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
  52 + @RequestMapping(value = "/lwm2m/deviceProfile", params = {"sortOrder", "sortProperty"}, method = RequestMethod.GET)
  53 + @ResponseBody
  54 + public List<LwM2mObject> getLwm2mListObjects(@RequestParam String sortOrder,
  55 + @RequestParam String sortProperty,
  56 + @RequestParam(required = false) int[] objectIds,
  57 + @RequestParam(required = false) String searchText)
  58 + throws ThingsboardException {
  59 + try {
  60 + return lwM2MModelsRepository.getLwm2mObjects(objectIds, searchText, sortProperty, sortOrder);
  61 + } catch (Exception e) {
  62 + throw handleException(e);
  63 + }
  64 + }
  65 +
  66 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
  67 + @RequestMapping(value = "/lwm2m/deviceProfile/objects", params = {"pageSize", "page"}, method = RequestMethod.GET)
  68 + @ResponseBody
  69 + public PageData<LwM2mObject> getLwm2mListObjects(@RequestParam int pageSize,
  70 + @RequestParam int page,
  71 + @RequestParam(required = false) String searchText,
  72 + @RequestParam(required = false) String sortProperty,
  73 + @RequestParam(required = false) String sortOrder) throws ThingsboardException {
  74 + try {
  75 + PageLink pageLink = createPageLink(pageSize, page, searchText, sortProperty, sortOrder);
  76 + return checkNotNull(lwM2MModelsRepository.findDeviceLwm2mObjects(getTenantId(), pageLink));
  77 + } catch (Exception e) {
  78 + throw handleException(e);
  79 + }
  80 + }
  81 +
  82 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
  83 + @RequestMapping(value = "/lwm2m/deviceProfile/bootstrap/{securityMode}/{bootstrapServerIs}", method = RequestMethod.GET)
  84 + @ResponseBody
  85 + public ServerSecurityConfig getLwm2mBootstrapSecurityInfo(@PathVariable("securityMode") String securityMode,
  86 + @PathVariable("bootstrapServerIs") boolean bootstrapServerIs) throws ThingsboardException {
  87 + try {
  88 + return lwM2MModelsRepository.getBootstrapSecurityInfo(securityMode, bootstrapServerIs);
  89 + } catch (Exception e) {
  90 + throw handleException(e);
  91 + }
  92 + }
  93 +
  94 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
  95 + @RequestMapping(value = "/lwm2m/device-credentials", method = RequestMethod.POST)
  96 + @ResponseBody
  97 + public Device saveDeviceWithCredentials(@RequestBody (required=false) Map<Class<?>, Object> deviceWithDeviceCredentials) throws ThingsboardException {
  98 + ObjectMapper mapper = new ObjectMapper();
  99 + Device device = checkNotNull(mapper.convertValue(deviceWithDeviceCredentials.get(Device.class), Device.class));
  100 + DeviceCredentials credentials = checkNotNull(mapper.convertValue( deviceWithDeviceCredentials.get(DeviceCredentials.class), DeviceCredentials.class));
  101 + try {
  102 + device.setTenantId(getCurrentUser().getTenantId());
  103 + checkEntity(device.getId(), device, Resource.DEVICE);
  104 + Device savedDevice = deviceService.saveDeviceWithCredentials(device, credentials);
  105 + checkNotNull(savedDevice);
  106 +
  107 + tbClusterService.onDeviceChange(savedDevice, null);
  108 + tbClusterService.pushMsgToCore(new DeviceNameOrTypeUpdateMsg(savedDevice.getTenantId(),
  109 + savedDevice.getId(), savedDevice.getName(), savedDevice.getType()), null);
  110 + tbClusterService.onEntityStateChange(savedDevice.getTenantId(), savedDevice.getId(),
  111 + device.getId() == null ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED);
  112 +
  113 + logEntityAction(savedDevice.getId(), savedDevice,
  114 + savedDevice.getCustomerId(),
  115 + device.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null);
  116 +
  117 + if (device.getId() == null) {
  118 + deviceStateService.onDeviceAdded(savedDevice);
  119 + } else {
  120 + deviceStateService.onDeviceUpdated(savedDevice);
  121 + }
  122 + return savedDevice;
  123 + } catch (Exception e) {
  124 + logEntityAction(emptyId(EntityType.DEVICE), device,
  125 + null, device.getId() == null ? ActionType.ADDED : ActionType.UPDATED, e);
  126 + throw handleException(e);
  127 + }
  128 + }
  129 +}
@@ -63,7 +63,7 @@ import java.util.List; @@ -63,7 +63,7 @@ import java.util.List;
63 import java.util.concurrent.ExecutionException; 63 import java.util.concurrent.ExecutionException;
64 import java.util.stream.Collectors; 64 import java.util.stream.Collectors;
65 65
66 -import static org.apache.commons.lang.StringUtils.isBlank; 66 +import static org.apache.commons.lang3.StringUtils.isBlank;
67 import static org.thingsboard.server.controller.CustomerController.CUSTOMER_ID; 67 import static org.thingsboard.server.controller.CustomerController.CUSTOMER_ID;
68 68
69 /** 69 /**
  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.security.access.prepost.PreAuthorize;
  20 +import org.springframework.web.bind.annotation.PathVariable;
  21 +import org.springframework.web.bind.annotation.RequestMapping;
  22 +import org.springframework.web.bind.annotation.RequestMethod;
  23 +import org.springframework.web.bind.annotation.RequestParam;
  24 +import org.springframework.web.bind.annotation.ResponseBody;
  25 +import org.springframework.web.bind.annotation.RestController;
  26 +import org.thingsboard.server.common.data.exception.ThingsboardException;
  27 +import org.thingsboard.server.common.data.id.TenantId;
  28 +import org.thingsboard.server.common.data.page.PageData;
  29 +import org.thingsboard.server.common.data.page.PageLink;
  30 +import org.thingsboard.server.common.data.transport.resource.Resource;
  31 +import org.thingsboard.server.common.data.transport.resource.ResourceType;
  32 +import org.thingsboard.server.dao.resource.ResourceService;
  33 +import org.thingsboard.server.queue.util.TbCoreComponent;
  34 +
  35 +@Slf4j
  36 +@RestController
  37 +@TbCoreComponent
  38 +@RequestMapping("/api")
  39 +public class ResourceController extends BaseController {
  40 +
  41 + private final ResourceService resourceService;
  42 +
  43 + public ResourceController(ResourceService resourceService) {
  44 + this.resourceService = resourceService;
  45 + }
  46 +
  47 + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
  48 + @RequestMapping(value = "/resource", method = RequestMethod.POST)
  49 + @ResponseBody
  50 + public Resource saveResource(Resource resource) throws ThingsboardException {
  51 + try {
  52 + resource.setTenantId(getTenantId());
  53 + Resource savedResource = checkNotNull(resourceService.saveResource(resource));
  54 + tbClusterService.onResourceChange(savedResource, null);
  55 + return savedResource;
  56 + } catch (Exception e) {
  57 + throw handleException(e);
  58 + }
  59 + }
  60 +
  61 + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
  62 + @RequestMapping(value = "/resource", method = RequestMethod.GET)
  63 + @ResponseBody
  64 + public PageData<Resource> getResources(@RequestParam(required = false) boolean system,
  65 + @RequestParam int pageSize,
  66 + @RequestParam int page,
  67 + @RequestParam(required = false) String sortProperty,
  68 + @RequestParam(required = false) String sortOrder) throws ThingsboardException {
  69 + try {
  70 + PageLink pageLink = createPageLink(pageSize, page, null, sortProperty, sortOrder);
  71 + return checkNotNull(resourceService.findResourcesByTenantId(system ? TenantId.SYS_TENANT_ID : getTenantId(), pageLink));
  72 + } catch (Exception e) {
  73 + throw handleException(e);
  74 + }
  75 + }
  76 +
  77 + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
  78 + @RequestMapping(value = "/resource/{resourceType}/{resourceId}", method = RequestMethod.DELETE)
  79 + @ResponseBody
  80 + public void deleteResource(@PathVariable("resourceType") ResourceType resourceType,
  81 + @PathVariable("resourceId") String resourceId) throws ThingsboardException {
  82 + try {
  83 + Resource resource = checkNotNull(resourceService.getResource(getTenantId(), resourceType, resourceId));
  84 + resourceService.deleteResource(getTenantId(), resourceType, resourceId);
  85 + tbClusterService.onResourceDeleted(resource, null);
  86 + } catch (Exception e) {
  87 + throw handleException(e);
  88 + }
  89 + }
  90 +}
@@ -15,6 +15,7 @@ @@ -15,6 +15,7 @@
15 */ 15 */
16 package org.thingsboard.server.controller; 16 package org.thingsboard.server.controller;
17 17
  18 +import com.fasterxml.jackson.databind.node.ObjectNode;
18 import lombok.extern.slf4j.Slf4j; 19 import lombok.extern.slf4j.Slf4j;
19 import org.springframework.beans.factory.annotation.Autowired; 20 import org.springframework.beans.factory.annotation.Autowired;
20 import org.springframework.http.HttpStatus; 21 import org.springframework.http.HttpStatus;
@@ -59,7 +60,11 @@ public class TenantController extends BaseController { @@ -59,7 +60,11 @@ public class TenantController extends BaseController {
59 checkParameter("tenantId", strTenantId); 60 checkParameter("tenantId", strTenantId);
60 try { 61 try {
61 TenantId tenantId = new TenantId(toUUID(strTenantId)); 62 TenantId tenantId = new TenantId(toUUID(strTenantId));
62 - return checkTenantId(tenantId, Operation.READ); 63 + Tenant tenant = checkTenantId(tenantId, Operation.READ);
  64 + if(!tenant.getAdditionalInfo().isNull()) {
  65 + processDashboardIdFromAdditionalInfo((ObjectNode) tenant.getAdditionalInfo(), HOME_DASHBOARD);
  66 + }
  67 + return tenant;
63 } catch (Exception e) { 68 } catch (Exception e) {
64 throw handleException(e); 69 throw handleException(e);
65 } 70 }
@@ -53,7 +53,6 @@ import org.thingsboard.server.service.security.model.token.JwtTokenFactory; @@ -53,7 +53,6 @@ import org.thingsboard.server.service.security.model.token.JwtTokenFactory;
53 import org.thingsboard.server.service.security.permission.Operation; 53 import org.thingsboard.server.service.security.permission.Operation;
54 import org.thingsboard.server.service.security.permission.Resource; 54 import org.thingsboard.server.service.security.permission.Resource;
55 import org.thingsboard.server.service.security.system.SystemSecurityService; 55 import org.thingsboard.server.service.security.system.SystemSecurityService;
56 -import org.thingsboard.server.utils.MiscUtils;  
57 56
58 import javax.servlet.http.HttpServletRequest; 57 import javax.servlet.http.HttpServletRequest;
59 58
@@ -90,12 +89,29 @@ public class UserController extends BaseController { @@ -90,12 +89,29 @@ public class UserController extends BaseController {
90 checkParameter(USER_ID, strUserId); 89 checkParameter(USER_ID, strUserId);
91 try { 90 try {
92 UserId userId = new UserId(toUUID(strUserId)); 91 UserId userId = new UserId(toUUID(strUserId));
93 - return checkUserId(userId, Operation.READ); 92 + User user = checkUserId(userId, Operation.READ);
  93 + if(!user.getAdditionalInfo().isNull()) {
  94 + processDashboardIdFromAdditionalInfo((ObjectNode) user.getAdditionalInfo(), DEFAULT_DASHBOARD);
  95 + processDashboardIdFromAdditionalInfo((ObjectNode) user.getAdditionalInfo(), HOME_DASHBOARD);
  96 + }
  97 + UserCredentials userCredentials = userService.findUserCredentialsByUserId(user.getTenantId(), user.getId());
  98 + if(userCredentials.isEnabled()) {
  99 + addUserCredentialsEnabled((ObjectNode) user.getAdditionalInfo());
  100 + }
  101 + return user;
94 } catch (Exception e) { 102 } catch (Exception e) {
95 throw handleException(e); 103 throw handleException(e);
96 } 104 }
97 } 105 }
98 106
  107 + private void addUserCredentialsEnabled(ObjectNode additionalInfo) {
  108 + if(!additionalInfo.isNull()) {
  109 + if(!additionalInfo.has("userCredentialsEnabled")) {
  110 + additionalInfo.put("userCredentialsEnabled", true);
  111 + }
  112 + }
  113 + }
  114 +
99 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") 115 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
100 @RequestMapping(value = "/user/tokenAccessEnabled", method = RequestMethod.GET) 116 @RequestMapping(value = "/user/tokenAccessEnabled", method = RequestMethod.GET)
101 @ResponseBody 117 @ResponseBody
@@ -189,13 +205,13 @@ public class UserController extends BaseController { @@ -189,13 +205,13 @@ public class UserController extends BaseController {
189 user.getId(), user); 205 user.getId(), user);
190 206
191 UserCredentials userCredentials = userService.findUserCredentialsByUserId(getCurrentUser().getTenantId(), user.getId()); 207 UserCredentials userCredentials = userService.findUserCredentialsByUserId(getCurrentUser().getTenantId(), user.getId());
192 - if (!userCredentials.isEnabled()) { 208 + if (!userCredentials.isEnabled() && userCredentials.getActivateToken() != null) {
193 String baseUrl = systemSecurityService.getBaseUrl(getTenantId(), getCurrentUser().getCustomerId(), request); 209 String baseUrl = systemSecurityService.getBaseUrl(getTenantId(), getCurrentUser().getCustomerId(), request);
194 String activateUrl = String.format(ACTIVATE_URL_PATTERN, baseUrl, 210 String activateUrl = String.format(ACTIVATE_URL_PATTERN, baseUrl,
195 userCredentials.getActivateToken()); 211 userCredentials.getActivateToken());
196 mailService.sendActivationEmail(activateUrl, email); 212 mailService.sendActivationEmail(activateUrl, email);
197 } else { 213 } else {
198 - throw new ThingsboardException("User is already active!", ThingsboardErrorCode.BAD_REQUEST_PARAMS); 214 + throw new ThingsboardException("User is already activated!", ThingsboardErrorCode.BAD_REQUEST_PARAMS);
199 } 215 }
200 } catch (Exception e) { 216 } catch (Exception e) {
201 throw handleException(e); 217 throw handleException(e);
@@ -214,13 +230,13 @@ public class UserController extends BaseController { @@ -214,13 +230,13 @@ public class UserController extends BaseController {
214 User user = checkUserId(userId, Operation.READ); 230 User user = checkUserId(userId, Operation.READ);
215 SecurityUser authUser = getCurrentUser(); 231 SecurityUser authUser = getCurrentUser();
216 UserCredentials userCredentials = userService.findUserCredentialsByUserId(authUser.getTenantId(), user.getId()); 232 UserCredentials userCredentials = userService.findUserCredentialsByUserId(authUser.getTenantId(), user.getId());
217 - if (!userCredentials.isEnabled()) { 233 + if (!userCredentials.isEnabled() && userCredentials.getActivateToken() != null) {
218 String baseUrl = systemSecurityService.getBaseUrl(getTenantId(), getCurrentUser().getCustomerId(), request); 234 String baseUrl = systemSecurityService.getBaseUrl(getTenantId(), getCurrentUser().getCustomerId(), request);
219 String activateUrl = String.format(ACTIVATE_URL_PATTERN, baseUrl, 235 String activateUrl = String.format(ACTIVATE_URL_PATTERN, baseUrl,
220 userCredentials.getActivateToken()); 236 userCredentials.getActivateToken());
221 return activateUrl; 237 return activateUrl;
222 } else { 238 } else {
223 - throw new ThingsboardException("User is already active!", ThingsboardErrorCode.BAD_REQUEST_PARAMS); 239 + throw new ThingsboardException("User is already activated!", ThingsboardErrorCode.BAD_REQUEST_PARAMS);
224 } 240 }
225 } catch (Exception e) { 241 } catch (Exception e) {
226 throw handleException(e); 242 throw handleException(e);
@@ -329,4 +345,5 @@ public class UserController extends BaseController { @@ -329,4 +345,5 @@ public class UserController extends BaseController {
329 throw handleException(e); 345 throw handleException(e);
330 } 346 }
331 } 347 }
  348 +
332 } 349 }
@@ -41,9 +41,13 @@ import org.thingsboard.server.service.telemetry.TelemetryWebSocketMsgEndpoint; @@ -41,9 +41,13 @@ import org.thingsboard.server.service.telemetry.TelemetryWebSocketMsgEndpoint;
41 import org.thingsboard.server.service.telemetry.TelemetryWebSocketService; 41 import org.thingsboard.server.service.telemetry.TelemetryWebSocketService;
42 import org.thingsboard.server.service.telemetry.TelemetryWebSocketSessionRef; 42 import org.thingsboard.server.service.telemetry.TelemetryWebSocketSessionRef;
43 43
44 -import javax.websocket.*; 44 +import javax.websocket.RemoteEndpoint;
  45 +import javax.websocket.SendHandler;
  46 +import javax.websocket.SendResult;
  47 +import javax.websocket.Session;
45 import java.io.IOException; 48 import java.io.IOException;
46 import java.net.URI; 49 import java.net.URI;
  50 +import java.nio.ByteBuffer;
47 import java.security.InvalidParameterException; 51 import java.security.InvalidParameterException;
48 import java.util.Queue; 52 import java.util.Queue;
49 import java.util.Set; 53 import java.util.Set;
@@ -79,6 +83,9 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr @@ -79,6 +83,9 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr
79 @Value("${server.ws.limits.max_updates_per_session:}") 83 @Value("${server.ws.limits.max_updates_per_session:}")
80 private String perSessionUpdatesConfiguration; 84 private String perSessionUpdatesConfiguration;
81 85
  86 + @Value("${server.ws.ping_timeout:30000}")
  87 + private long pingTimeout;
  88 +
82 private ConcurrentMap<String, TelemetryWebSocketSessionRef> blacklistedSessions = new ConcurrentHashMap<>(); 89 private ConcurrentMap<String, TelemetryWebSocketSessionRef> blacklistedSessions = new ConcurrentHashMap<>();
83 private ConcurrentMap<String, TbRateLimits> perSessionUpdateLimits = new ConcurrentHashMap<>(); 90 private ConcurrentMap<String, TbRateLimits> perSessionUpdateLimits = new ConcurrentHashMap<>();
84 91
@@ -120,6 +127,7 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr @@ -120,6 +127,7 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr
120 return; 127 return;
121 } 128 }
122 internalSessionMap.put(internalSessionId, new SessionMetaData(session, sessionRef, maxMsgQueuePerSession)); 129 internalSessionMap.put(internalSessionId, new SessionMetaData(session, sessionRef, maxMsgQueuePerSession));
  130 +
123 externalSessionMap.put(externalSessionId, internalSessionId); 131 externalSessionMap.put(externalSessionId, internalSessionId);
124 processInWebSocketService(sessionRef, SessionEvent.onEstablished()); 132 processInWebSocketService(sessionRef, SessionEvent.onEstablished());
125 log.info("[{}][{}][{}] Session is opened", sessionRef.getSecurityCtx().getTenantId(), externalSessionId, session.getId()); 133 log.info("[{}][{}][{}] Session is opened", sessionRef.getSecurityCtx().getTenantId(), externalSessionId, session.getId());
@@ -189,6 +197,8 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr @@ -189,6 +197,8 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr
189 private volatile boolean isSending = false; 197 private volatile boolean isSending = false;
190 private final Queue<String> msgQueue; 198 private final Queue<String> msgQueue;
191 199
  200 + private volatile long lastActivityTime;
  201 +
192 SessionMetaData(WebSocketSession session, TelemetryWebSocketSessionRef sessionRef, int maxMsgQueuePerSession) { 202 SessionMetaData(WebSocketSession session, TelemetryWebSocketSessionRef sessionRef, int maxMsgQueuePerSession) {
193 super(); 203 super();
194 this.session = session; 204 this.session = session;
@@ -196,6 +206,23 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr @@ -196,6 +206,23 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr
196 this.asyncRemote = nativeSession.getAsyncRemote(); 206 this.asyncRemote = nativeSession.getAsyncRemote();
197 this.sessionRef = sessionRef; 207 this.sessionRef = sessionRef;
198 this.msgQueue = new LinkedBlockingQueue<>(maxMsgQueuePerSession); 208 this.msgQueue = new LinkedBlockingQueue<>(maxMsgQueuePerSession);
  209 + this.lastActivityTime = System.currentTimeMillis();
  210 + }
  211 +
  212 + synchronized void sendPing(long currentTime) {
  213 + try {
  214 + if (currentTime - lastActivityTime >= pingTimeout) {
  215 + this.asyncRemote.sendPing(ByteBuffer.wrap(new byte[]{}));
  216 + lastActivityTime = currentTime;
  217 + }
  218 + } catch (Exception e) {
  219 + log.trace("[{}] Failed to send ping msg", session.getId(), e);
  220 + try {
  221 + close(this.sessionRef, CloseStatus.SESSION_NOT_RELIABLE);
  222 + } catch (IOException ioe) {
  223 + log.trace("[{}] Session transport error", session.getId(), ioe);
  224 + }
  225 + }
199 } 226 }
200 227
201 synchronized void sendMsg(String msg) { 228 synchronized void sendMsg(String msg) {
@@ -243,6 +270,7 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr @@ -243,6 +270,7 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr
243 log.trace("[{}] Session transport error", session.getId(), ioe); 270 log.trace("[{}] Session transport error", session.getId(), ioe);
244 } 271 }
245 } else { 272 } else {
  273 + lastActivityTime = System.currentTimeMillis();
246 String msg = msgQueue.poll(); 274 String msg = msgQueue.poll();
247 if (msg != null) { 275 if (msg != null) {
248 sendMsgInternal(msg); 276 sendMsgInternal(msg);
@@ -285,6 +313,22 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr @@ -285,6 +313,22 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr
285 } 313 }
286 314
287 @Override 315 @Override
  316 + public void sendPing(TelemetryWebSocketSessionRef sessionRef, long currentTime) throws IOException {
  317 + String externalId = sessionRef.getSessionId();
  318 + String internalId = externalSessionMap.get(externalId);
  319 + if (internalId != null) {
  320 + SessionMetaData sessionMd = internalSessionMap.get(internalId);
  321 + if (sessionMd != null) {
  322 + sessionMd.sendPing(currentTime);
  323 + } else {
  324 + log.warn("[{}][{}] Failed to find session by internal id", externalId, internalId);
  325 + }
  326 + } else {
  327 + log.warn("[{}] Failed to find session by external id", externalId);
  328 + }
  329 + }
  330 +
  331 + @Override
288 public void close(TelemetryWebSocketSessionRef sessionRef, CloseStatus reason) throws IOException { 332 public void close(TelemetryWebSocketSessionRef sessionRef, CloseStatus reason) throws IOException {
289 String externalId = sessionRef.getSessionId(); 333 String externalId = sessionRef.getSessionId();
290 log.debug("[{}] Processing close request", externalId); 334 log.debug("[{}] Processing close request", externalId);
@@ -185,9 +185,21 @@ public class ThingsboardInstallService { @@ -185,9 +185,21 @@ public class ThingsboardInstallService {
185 case "3.2.0": 185 case "3.2.0":
186 log.info("Upgrading ThingsBoard from version 3.2.0 to 3.2.1 ..."); 186 log.info("Upgrading ThingsBoard from version 3.2.0 to 3.2.1 ...");
187 databaseEntitiesUpgradeService.upgradeDatabase("3.2.0"); 187 databaseEntitiesUpgradeService.upgradeDatabase("3.2.0");
  188 + case "3.2.1":
  189 + log.info("Upgrading ThingsBoard from version 3.2.1 to 3.2.2 ...");
  190 + if (databaseTsUpgradeService != null) {
  191 + databaseTsUpgradeService.upgradeDatabase("3.2.1");
  192 + }
  193 +
188 log.info("Updating system data..."); 194 log.info("Updating system data...");
189 systemDataLoaderService.updateSystemWidgets(); 195 systemDataLoaderService.updateSystemWidgets();
190 break; 196 break;
  197 + case "3.2.2":
  198 + log.info("Upgrading ThingsBoard from version 3.2.2 to 3.3.0 ...");
  199 + databaseEntitiesUpgradeService.upgradeDatabase("3.2.2");
  200 +
  201 + log.info("Updating system data...");
  202 + break;
191 default: 203 default:
192 throw new RuntimeException("Unable to upgrade ThingsBoard, unsupported fromVersion: " + upgradeFromVersion); 204 throw new RuntimeException("Unable to upgrade ThingsBoard, unsupported fromVersion: " + upgradeFromVersion);
193 205
@@ -220,6 +232,7 @@ public class ThingsboardInstallService { @@ -220,6 +232,7 @@ public class ThingsboardInstallService {
220 systemDataLoaderService.createAdminSettings(); 232 systemDataLoaderService.createAdminSettings();
221 systemDataLoaderService.loadSystemWidgets(); 233 systemDataLoaderService.loadSystemWidgets();
222 systemDataLoaderService.createOAuth2Templates(); 234 systemDataLoaderService.createOAuth2Templates();
  235 + systemDataLoaderService.loadSystemLwm2mResources();
223 // systemDataLoaderService.loadSystemPlugins(); 236 // systemDataLoaderService.loadSystemPlugins();
224 // systemDataLoaderService.loadSystemRules(); 237 // systemDataLoaderService.loadSystemRules();
225 238
@@ -54,6 +54,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.UsageStatsKVProto; @@ -54,6 +54,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.UsageStatsKVProto;
54 import org.thingsboard.server.queue.common.TbProtoQueueMsg; 54 import org.thingsboard.server.queue.common.TbProtoQueueMsg;
55 import org.thingsboard.server.queue.discovery.PartitionChangeEvent; 55 import org.thingsboard.server.queue.discovery.PartitionChangeEvent;
56 import org.thingsboard.server.queue.discovery.PartitionService; 56 import org.thingsboard.server.queue.discovery.PartitionService;
  57 +import org.thingsboard.server.queue.discovery.TbApplicationEventListener;
57 import org.thingsboard.server.queue.scheduler.SchedulerComponent; 58 import org.thingsboard.server.queue.scheduler.SchedulerComponent;
58 import org.thingsboard.server.service.queue.TbClusterService; 59 import org.thingsboard.server.service.queue.TbClusterService;
59 import org.thingsboard.server.service.telemetry.InternalTelemetryService; 60 import org.thingsboard.server.service.telemetry.InternalTelemetryService;
@@ -78,7 +79,7 @@ import java.util.stream.Collectors; @@ -78,7 +79,7 @@ import java.util.stream.Collectors;
78 79
79 @Slf4j 80 @Slf4j
80 @Service 81 @Service
81 -public class DefaultTbApiUsageStateService implements TbApiUsageStateService { 82 +public class DefaultTbApiUsageStateService extends TbApplicationEventListener<PartitionChangeEvent> implements TbApiUsageStateService {
82 83
83 public static final String HOURLY = "Hourly"; 84 public static final String HOURLY = "Hourly";
84 public static final FutureCallback<Integer> VOID_CALLBACK = new FutureCallback<Integer>() { 85 public static final FutureCallback<Integer> VOID_CALLBACK = new FutureCallback<Integer>() {
@@ -188,7 +189,7 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService { @@ -188,7 +189,7 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService {
188 } 189 }
189 190
190 @Override 191 @Override
191 - public void onApplicationEvent(PartitionChangeEvent partitionChangeEvent) { 192 + protected void onTbApplicationEvent(PartitionChangeEvent partitionChangeEvent) {
192 if (partitionChangeEvent.getServiceType().equals(ServiceType.TB_CORE)) { 193 if (partitionChangeEvent.getServiceType().equals(ServiceType.TB_CORE)) {
193 myTenantStates.entrySet().removeIf(entry -> !partitionService.resolve(ServiceType.TB_CORE, entry.getKey(), entry.getKey()).isMyPartition()); 194 myTenantStates.entrySet().removeIf(entry -> !partitionService.resolve(ServiceType.TB_CORE, entry.getKey(), entry.getKey()).isMyPartition());
194 otherTenantStates.entrySet().removeIf(entry -> partitionService.resolve(ServiceType.TB_CORE, entry.getKey(), entry.getKey()).isMyPartition()); 195 otherTenantStates.entrySet().removeIf(entry -> partitionService.resolve(ServiceType.TB_CORE, entry.getKey(), entry.getKey()).isMyPartition());
@@ -24,6 +24,7 @@ import org.springframework.beans.factory.annotation.Value; @@ -24,6 +24,7 @@ import org.springframework.beans.factory.annotation.Value;
24 import org.springframework.beans.factory.config.BeanDefinition; 24 import org.springframework.beans.factory.config.BeanDefinition;
25 import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider; 25 import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
26 import org.springframework.core.env.Environment; 26 import org.springframework.core.env.Environment;
  27 +import org.springframework.core.env.Profiles;
27 import org.springframework.core.type.filter.AnnotationTypeFilter; 28 import org.springframework.core.type.filter.AnnotationTypeFilter;
28 import org.springframework.stereotype.Service; 29 import org.springframework.stereotype.Service;
29 import org.thingsboard.rule.engine.api.NodeConfiguration; 30 import org.thingsboard.rule.engine.api.NodeConfiguration;
@@ -69,7 +70,7 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe @@ -69,7 +70,7 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe
69 private ObjectMapper mapper = new ObjectMapper(); 70 private ObjectMapper mapper = new ObjectMapper();
70 71
71 private boolean isInstall() { 72 private boolean isInstall() {
72 - return environment.acceptsProfiles("install"); 73 + return environment.acceptsProfiles(Profiles.of("install"));
73 } 74 }
74 75
75 @PostConstruct 76 @PostConstruct
@@ -185,7 +186,7 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe @@ -185,7 +186,7 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe
185 nodeDefinition.setRelationTypes(getRelationTypesWithFailureRelation(nodeAnnotation)); 186 nodeDefinition.setRelationTypes(getRelationTypesWithFailureRelation(nodeAnnotation));
186 nodeDefinition.setCustomRelations(nodeAnnotation.customRelations()); 187 nodeDefinition.setCustomRelations(nodeAnnotation.customRelations());
187 Class<? extends NodeConfiguration> configClazz = nodeAnnotation.configClazz(); 188 Class<? extends NodeConfiguration> configClazz = nodeAnnotation.configClazz();
188 - NodeConfiguration config = configClazz.newInstance(); 189 + NodeConfiguration config = configClazz.getDeclaredConstructor().newInstance();
189 NodeConfiguration defaultConfiguration = config.defaultConfiguration(); 190 NodeConfiguration defaultConfiguration = config.defaultConfiguration();
190 nodeDefinition.setDefaultConfiguration(mapper.valueToTree(defaultConfiguration)); 191 nodeDefinition.setDefaultConfiguration(mapper.valueToTree(defaultConfiguration));
191 nodeDefinition.setUiResources(nodeAnnotation.uiResources()); 192 nodeDefinition.setUiResources(nodeAnnotation.uiResources());
@@ -20,7 +20,7 @@ import com.fasterxml.jackson.databind.JsonNode; @@ -20,7 +20,7 @@ import com.fasterxml.jackson.databind.JsonNode;
20 import com.fasterxml.jackson.databind.node.ObjectNode; 20 import com.fasterxml.jackson.databind.node.ObjectNode;
21 import com.google.common.util.concurrent.ListenableFuture; 21 import com.google.common.util.concurrent.ListenableFuture;
22 import lombok.extern.slf4j.Slf4j; 22 import lombok.extern.slf4j.Slf4j;
23 -import org.apache.commons.lang.RandomStringUtils; 23 +import org.apache.commons.lang3.RandomStringUtils;
24 import org.springframework.beans.factory.annotation.Autowired; 24 import org.springframework.beans.factory.annotation.Autowired;
25 import org.springframework.stereotype.Service; 25 import org.springframework.stereotype.Service;
26 import org.springframework.util.StringUtils; 26 import org.springframework.util.StringUtils;
@@ -50,7 +50,7 @@ import org.thingsboard.server.dao.device.provision.ProvisionFailedException; @@ -50,7 +50,7 @@ import org.thingsboard.server.dao.device.provision.ProvisionFailedException;
50 import org.thingsboard.server.dao.device.provision.ProvisionRequest; 50 import org.thingsboard.server.dao.device.provision.ProvisionRequest;
51 import org.thingsboard.server.dao.device.provision.ProvisionResponse; 51 import org.thingsboard.server.dao.device.provision.ProvisionResponse;
52 import org.thingsboard.server.dao.device.provision.ProvisionResponseStatus; 52 import org.thingsboard.server.dao.device.provision.ProvisionResponseStatus;
53 -import org.thingsboard.server.dao.util.mapping.JacksonUtil; 53 +import org.thingsboard.common.util.JacksonUtil;
54 import org.thingsboard.server.gen.transport.TransportProtos; 54 import org.thingsboard.server.gen.transport.TransportProtos;
55 import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; 55 import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg;
56 import org.thingsboard.server.queue.TbQueueCallback; 56 import org.thingsboard.server.queue.TbQueueCallback;
@@ -50,6 +50,7 @@ public class CassandraTsDatabaseUpgradeService extends AbstractCassandraDatabase @@ -50,6 +50,7 @@ public class CassandraTsDatabaseUpgradeService extends AbstractCassandraDatabase
50 break; 50 break;
51 case "2.5.0": 51 case "2.5.0":
52 case "3.1.1": 52 case "3.1.1":
  53 + case "3.2.1":
53 break; 54 break;
54 default: 55 default:
55 throw new RuntimeException("Unable to upgrade Cassandra database, unsupported fromVersion: " + fromVersion); 56 throw new RuntimeException("Unable to upgrade Cassandra database, unsupported fromVersion: " + fromVersion);
@@ -36,6 +36,9 @@ import org.thingsboard.server.common.data.TenantProfile; @@ -36,6 +36,9 @@ import org.thingsboard.server.common.data.TenantProfile;
36 import org.thingsboard.server.common.data.User; 36 import org.thingsboard.server.common.data.User;
37 import org.thingsboard.server.common.data.alarm.AlarmSeverity; 37 import org.thingsboard.server.common.data.alarm.AlarmSeverity;
38 import org.thingsboard.server.common.data.device.profile.AlarmCondition; 38 import org.thingsboard.server.common.data.device.profile.AlarmCondition;
  39 +import org.thingsboard.server.common.data.device.profile.AlarmConditionFilter;
  40 +import org.thingsboard.server.common.data.device.profile.AlarmConditionFilterKey;
  41 +import org.thingsboard.server.common.data.device.profile.AlarmConditionKeyType;
39 import org.thingsboard.server.common.data.device.profile.AlarmRule; 42 import org.thingsboard.server.common.data.device.profile.AlarmRule;
40 import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration; 43 import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration;
41 import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileTransportConfiguration; 44 import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileTransportConfiguration;
@@ -197,7 +200,7 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { @@ -197,7 +200,7 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
197 generalSettings.setKey("general"); 200 generalSettings.setKey("general");
198 ObjectNode node = objectMapper.createObjectNode(); 201 ObjectNode node = objectMapper.createObjectNode();
199 node.put("baseUrl", "http://localhost:8080"); 202 node.put("baseUrl", "http://localhost:8080");
200 - node.put("prohibitDifferentUrl", true); 203 + node.put("prohibitDifferentUrl", false);
201 generalSettings.setJsonValue(node); 204 generalSettings.setJsonValue(node);
202 adminSettingsService.saveAdminSettings(TenantId.SYS_TENANT_ID, generalSettings); 205 adminSettingsService.saveAdminSettings(TenantId.SYS_TENANT_ID, generalSettings);
203 206
@@ -290,16 +293,16 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { @@ -290,16 +293,16 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
290 AlarmCondition temperatureCondition = new AlarmCondition(); 293 AlarmCondition temperatureCondition = new AlarmCondition();
291 temperatureCondition.setSpec(new SimpleAlarmConditionSpec()); 294 temperatureCondition.setSpec(new SimpleAlarmConditionSpec());
292 295
293 - KeyFilter temperatureAlarmFlagAttributeFilter = new KeyFilter();  
294 - temperatureAlarmFlagAttributeFilter.setKey(new EntityKey(EntityKeyType.ATTRIBUTE, "temperatureAlarmFlag")); 296 + AlarmConditionFilter temperatureAlarmFlagAttributeFilter = new AlarmConditionFilter();
  297 + temperatureAlarmFlagAttributeFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.ATTRIBUTE, "temperatureAlarmFlag"));
295 temperatureAlarmFlagAttributeFilter.setValueType(EntityKeyValueType.BOOLEAN); 298 temperatureAlarmFlagAttributeFilter.setValueType(EntityKeyValueType.BOOLEAN);
296 BooleanFilterPredicate temperatureAlarmFlagAttributePredicate = new BooleanFilterPredicate(); 299 BooleanFilterPredicate temperatureAlarmFlagAttributePredicate = new BooleanFilterPredicate();
297 temperatureAlarmFlagAttributePredicate.setOperation(BooleanFilterPredicate.BooleanOperation.EQUAL); 300 temperatureAlarmFlagAttributePredicate.setOperation(BooleanFilterPredicate.BooleanOperation.EQUAL);
298 temperatureAlarmFlagAttributePredicate.setValue(new FilterPredicateValue<>(Boolean.TRUE)); 301 temperatureAlarmFlagAttributePredicate.setValue(new FilterPredicateValue<>(Boolean.TRUE));
299 temperatureAlarmFlagAttributeFilter.setPredicate(temperatureAlarmFlagAttributePredicate); 302 temperatureAlarmFlagAttributeFilter.setPredicate(temperatureAlarmFlagAttributePredicate);
300 303
301 - KeyFilter temperatureTimeseriesFilter = new KeyFilter();  
302 - temperatureTimeseriesFilter.setKey(new EntityKey(EntityKeyType.TIME_SERIES, "temperature")); 304 + AlarmConditionFilter temperatureTimeseriesFilter = new AlarmConditionFilter();
  305 + temperatureTimeseriesFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, "temperature"));
303 temperatureTimeseriesFilter.setValueType(EntityKeyValueType.NUMERIC); 306 temperatureTimeseriesFilter.setValueType(EntityKeyValueType.NUMERIC);
304 NumericFilterPredicate temperatureTimeseriesFilterPredicate = new NumericFilterPredicate(); 307 NumericFilterPredicate temperatureTimeseriesFilterPredicate = new NumericFilterPredicate();
305 temperatureTimeseriesFilterPredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER); 308 temperatureTimeseriesFilterPredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER);
@@ -317,8 +320,8 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { @@ -317,8 +320,8 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
317 AlarmCondition clearTemperatureCondition = new AlarmCondition(); 320 AlarmCondition clearTemperatureCondition = new AlarmCondition();
318 clearTemperatureCondition.setSpec(new SimpleAlarmConditionSpec()); 321 clearTemperatureCondition.setSpec(new SimpleAlarmConditionSpec());
319 322
320 - KeyFilter clearTemperatureTimeseriesFilter = new KeyFilter();  
321 - clearTemperatureTimeseriesFilter.setKey(new EntityKey(EntityKeyType.TIME_SERIES, "temperature")); 323 + AlarmConditionFilter clearTemperatureTimeseriesFilter = new AlarmConditionFilter();
  324 + clearTemperatureTimeseriesFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, "temperature"));
322 clearTemperatureTimeseriesFilter.setValueType(EntityKeyValueType.NUMERIC); 325 clearTemperatureTimeseriesFilter.setValueType(EntityKeyValueType.NUMERIC);
323 NumericFilterPredicate clearTemperatureTimeseriesFilterPredicate = new NumericFilterPredicate(); 326 NumericFilterPredicate clearTemperatureTimeseriesFilterPredicate = new NumericFilterPredicate();
324 clearTemperatureTimeseriesFilterPredicate.setOperation(NumericFilterPredicate.NumericOperation.LESS_OR_EQUAL); 327 clearTemperatureTimeseriesFilterPredicate.setOperation(NumericFilterPredicate.NumericOperation.LESS_OR_EQUAL);
@@ -340,16 +343,16 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { @@ -340,16 +343,16 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
340 AlarmCondition humidityCondition = new AlarmCondition(); 343 AlarmCondition humidityCondition = new AlarmCondition();
341 humidityCondition.setSpec(new SimpleAlarmConditionSpec()); 344 humidityCondition.setSpec(new SimpleAlarmConditionSpec());
342 345
343 - KeyFilter humidityAlarmFlagAttributeFilter = new KeyFilter();  
344 - humidityAlarmFlagAttributeFilter.setKey(new EntityKey(EntityKeyType.ATTRIBUTE, "humidityAlarmFlag")); 346 + AlarmConditionFilter humidityAlarmFlagAttributeFilter = new AlarmConditionFilter();
  347 + humidityAlarmFlagAttributeFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.ATTRIBUTE, "humidityAlarmFlag"));
345 humidityAlarmFlagAttributeFilter.setValueType(EntityKeyValueType.BOOLEAN); 348 humidityAlarmFlagAttributeFilter.setValueType(EntityKeyValueType.BOOLEAN);
346 BooleanFilterPredicate humidityAlarmFlagAttributePredicate = new BooleanFilterPredicate(); 349 BooleanFilterPredicate humidityAlarmFlagAttributePredicate = new BooleanFilterPredicate();
347 humidityAlarmFlagAttributePredicate.setOperation(BooleanFilterPredicate.BooleanOperation.EQUAL); 350 humidityAlarmFlagAttributePredicate.setOperation(BooleanFilterPredicate.BooleanOperation.EQUAL);
348 humidityAlarmFlagAttributePredicate.setValue(new FilterPredicateValue<>(Boolean.TRUE)); 351 humidityAlarmFlagAttributePredicate.setValue(new FilterPredicateValue<>(Boolean.TRUE));
349 humidityAlarmFlagAttributeFilter.setPredicate(humidityAlarmFlagAttributePredicate); 352 humidityAlarmFlagAttributeFilter.setPredicate(humidityAlarmFlagAttributePredicate);
350 353
351 - KeyFilter humidityTimeseriesFilter = new KeyFilter();  
352 - humidityTimeseriesFilter.setKey(new EntityKey(EntityKeyType.TIME_SERIES, "humidity")); 354 + AlarmConditionFilter humidityTimeseriesFilter = new AlarmConditionFilter();
  355 + humidityTimeseriesFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, "humidity"));
353 humidityTimeseriesFilter.setValueType(EntityKeyValueType.NUMERIC); 356 humidityTimeseriesFilter.setValueType(EntityKeyValueType.NUMERIC);
354 NumericFilterPredicate humidityTimeseriesFilterPredicate = new NumericFilterPredicate(); 357 NumericFilterPredicate humidityTimeseriesFilterPredicate = new NumericFilterPredicate();
355 humidityTimeseriesFilterPredicate.setOperation(NumericFilterPredicate.NumericOperation.LESS); 358 humidityTimeseriesFilterPredicate.setOperation(NumericFilterPredicate.NumericOperation.LESS);
@@ -368,8 +371,8 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { @@ -368,8 +371,8 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
368 AlarmCondition clearHumidityCondition = new AlarmCondition(); 371 AlarmCondition clearHumidityCondition = new AlarmCondition();
369 clearHumidityCondition.setSpec(new SimpleAlarmConditionSpec()); 372 clearHumidityCondition.setSpec(new SimpleAlarmConditionSpec());
370 373
371 - KeyFilter clearHumidityTimeseriesFilter = new KeyFilter();  
372 - clearHumidityTimeseriesFilter.setKey(new EntityKey(EntityKeyType.TIME_SERIES, "humidity")); 374 + AlarmConditionFilter clearHumidityTimeseriesFilter = new AlarmConditionFilter();
  375 + clearHumidityTimeseriesFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, "humidity"));
373 clearHumidityTimeseriesFilter.setValueType(EntityKeyValueType.NUMERIC); 376 clearHumidityTimeseriesFilter.setValueType(EntityKeyValueType.NUMERIC);
374 NumericFilterPredicate clearHumidityTimeseriesFilterPredicate = new NumericFilterPredicate(); 377 NumericFilterPredicate clearHumidityTimeseriesFilterPredicate = new NumericFilterPredicate();
375 clearHumidityTimeseriesFilterPredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER_OR_EQUAL); 378 clearHumidityTimeseriesFilterPredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER_OR_EQUAL);
@@ -438,9 +441,15 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { @@ -438,9 +441,15 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
438 this.deleteSystemWidgetBundle("input_widgets"); 441 this.deleteSystemWidgetBundle("input_widgets");
439 this.deleteSystemWidgetBundle("date"); 442 this.deleteSystemWidgetBundle("date");
440 this.deleteSystemWidgetBundle("entity_admin_widgets"); 443 this.deleteSystemWidgetBundle("entity_admin_widgets");
  444 + this.deleteSystemWidgetBundle("navigation_widgets");
441 installScripts.loadSystemWidgets(); 445 installScripts.loadSystemWidgets();
442 } 446 }
443 447
  448 + @Override
  449 + public void loadSystemLwm2mResources() throws Exception {
  450 + installScripts.loadSystemLwm2mResources();
  451 + }
  452 +
444 private User createUser(Authority authority, 453 private User createUser(Authority authority,
445 TenantId tenantId, 454 TenantId tenantId,
446 CustomerId customerId, 455 CustomerId customerId,
@@ -15,11 +15,20 @@ @@ -15,11 +15,20 @@
15 */ 15 */
16 package org.thingsboard.server.service.install; 16 package org.thingsboard.server.service.install;
17 17
  18 +import lombok.extern.slf4j.Slf4j;
18 import org.springframework.context.annotation.Profile; 19 import org.springframework.context.annotation.Profile;
19 import org.springframework.stereotype.Service; 20 import org.springframework.stereotype.Service;
20 import org.thingsboard.server.dao.util.HsqlDao; 21 import org.thingsboard.server.dao.util.HsqlDao;
21 22
  23 +import java.nio.charset.Charset;
  24 +import java.nio.file.Files;
  25 +import java.nio.file.Path;
  26 +import java.nio.file.Paths;
  27 +import java.sql.Connection;
  28 +import java.sql.DriverManager;
  29 +
22 @Service 30 @Service
  31 +@Slf4j
23 @HsqlDao 32 @HsqlDao
24 @Profile("install") 33 @Profile("install")
25 public class HsqlEntityDatabaseSchemaService extends SqlAbstractDatabaseSchemaService 34 public class HsqlEntityDatabaseSchemaService extends SqlAbstractDatabaseSchemaService
@@ -27,5 +36,21 @@ public class HsqlEntityDatabaseSchemaService extends SqlAbstractDatabaseSchemaSe @@ -27,5 +36,21 @@ public class HsqlEntityDatabaseSchemaService extends SqlAbstractDatabaseSchemaSe
27 protected HsqlEntityDatabaseSchemaService() { 36 protected HsqlEntityDatabaseSchemaService() {
28 super("schema-entities-hsql.sql", "schema-entities-idx.sql"); 37 super("schema-entities-hsql.sql", "schema-entities-idx.sql");
29 } 38 }
  39 +
  40 + private final String schemaTypesSql = "schema-types-hsql.sql";
  41 +
  42 + @Override
  43 + public void createDatabaseSchema(boolean createIndexes) throws Exception {
  44 +
  45 + log.info("Installing SQL DataBase types part: " + schemaTypesSql);
  46 +
  47 + Path schemaFile = Paths.get(installScripts.getDataDir(), SQL_DIR, schemaTypesSql);
  48 + try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) {
  49 + String sql = new String(Files.readAllBytes(schemaFile), Charset.forName("UTF-8"));
  50 + conn.createStatement().execute(sql); //NOSONAR, ignoring because method used to load initial thingsboard database schema
  51 + }
  52 +
  53 + super.createDatabaseSchema(createIndexes);
  54 + }
30 } 55 }
31 56
@@ -24,15 +24,17 @@ import org.springframework.util.StringUtils; @@ -24,15 +24,17 @@ import org.springframework.util.StringUtils;
24 import org.thingsboard.server.common.data.Dashboard; 24 import org.thingsboard.server.common.data.Dashboard;
25 import org.thingsboard.server.common.data.id.CustomerId; 25 import org.thingsboard.server.common.data.id.CustomerId;
26 import org.thingsboard.server.common.data.id.EntityId; 26 import org.thingsboard.server.common.data.id.EntityId;
27 -import org.thingsboard.server.common.data.id.RuleChainId;  
28 import org.thingsboard.server.common.data.id.TenantId; 27 import org.thingsboard.server.common.data.id.TenantId;
29 import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationTemplate; 28 import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationTemplate;
30 import org.thingsboard.server.common.data.rule.RuleChain; 29 import org.thingsboard.server.common.data.rule.RuleChain;
31 import org.thingsboard.server.common.data.rule.RuleChainMetaData; 30 import org.thingsboard.server.common.data.rule.RuleChainMetaData;
  31 +import org.thingsboard.server.common.data.transport.resource.Resource;
  32 +import org.thingsboard.server.common.data.transport.resource.ResourceType;
32 import org.thingsboard.server.common.data.widget.WidgetType; 33 import org.thingsboard.server.common.data.widget.WidgetType;
33 import org.thingsboard.server.common.data.widget.WidgetsBundle; 34 import org.thingsboard.server.common.data.widget.WidgetsBundle;
34 import org.thingsboard.server.dao.dashboard.DashboardService; 35 import org.thingsboard.server.dao.dashboard.DashboardService;
35 import org.thingsboard.server.dao.oauth2.OAuth2ConfigTemplateService; 36 import org.thingsboard.server.dao.oauth2.OAuth2ConfigTemplateService;
  37 +import org.thingsboard.server.dao.resource.ResourceService;
36 import org.thingsboard.server.dao.rule.RuleChainService; 38 import org.thingsboard.server.dao.rule.RuleChainService;
37 import org.thingsboard.server.dao.widget.WidgetTypeService; 39 import org.thingsboard.server.dao.widget.WidgetTypeService;
38 import org.thingsboard.server.dao.widget.WidgetsBundleService; 40 import org.thingsboard.server.dao.widget.WidgetsBundleService;
@@ -42,6 +44,7 @@ import java.nio.file.DirectoryStream; @@ -42,6 +44,7 @@ import java.nio.file.DirectoryStream;
42 import java.nio.file.Files; 44 import java.nio.file.Files;
43 import java.nio.file.Path; 45 import java.nio.file.Path;
44 import java.nio.file.Paths; 46 import java.nio.file.Paths;
  47 +import java.util.Base64;
45 import java.util.Optional; 48 import java.util.Optional;
46 49
47 import static org.thingsboard.server.service.install.DatabaseHelper.objectMapper; 50 import static org.thingsboard.server.service.install.DatabaseHelper.objectMapper;
@@ -66,8 +69,11 @@ public class InstallScripts { @@ -66,8 +69,11 @@ public class InstallScripts {
66 public static final String WIDGET_BUNDLES_DIR = "widget_bundles"; 69 public static final String WIDGET_BUNDLES_DIR = "widget_bundles";
67 public static final String OAUTH2_CONFIG_TEMPLATES_DIR = "oauth2_config_templates"; 70 public static final String OAUTH2_CONFIG_TEMPLATES_DIR = "oauth2_config_templates";
68 public static final String DASHBOARDS_DIR = "dashboards"; 71 public static final String DASHBOARDS_DIR = "dashboards";
  72 + public static final String MODELS_DIR = "models";
  73 + public static final String CREDENTIALS_DIR = "credentials";
69 74
70 public static final String JSON_EXT = ".json"; 75 public static final String JSON_EXT = ".json";
  76 + public static final String XML_EXT = ".xml";
71 77
72 @Value("${install.data_dir:}") 78 @Value("${install.data_dir:}")
73 private String dataDir; 79 private String dataDir;
@@ -87,6 +93,9 @@ public class InstallScripts { @@ -87,6 +93,9 @@ public class InstallScripts {
87 @Autowired 93 @Autowired
88 private OAuth2ConfigTemplateService oAuth2TemplateService; 94 private OAuth2ConfigTemplateService oAuth2TemplateService;
89 95
  96 + @Autowired
  97 + private ResourceService resourceService;
  98 +
90 public Path getTenantRuleChainsDir() { 99 public Path getTenantRuleChainsDir() {
91 return Paths.get(getDataDir(), JSON_DIR, TENANT_DIR, RULE_CHAINS_DIR); 100 return Paths.get(getDataDir(), JSON_DIR, TENANT_DIR, RULE_CHAINS_DIR);
92 } 101 }
@@ -186,6 +195,42 @@ public class InstallScripts { @@ -186,6 +195,42 @@ public class InstallScripts {
186 } 195 }
187 } 196 }
188 197
  198 + public void loadSystemLwm2mResources() throws Exception {
  199 + Path modelsDir = Paths.get(getDataDir(), MODELS_DIR);
  200 + if (Files.isDirectory(modelsDir)) {
  201 + try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(modelsDir, path -> path.toString().endsWith(XML_EXT))) {
  202 + dirStream.forEach(
  203 + path -> {
  204 + try {
  205 + Resource resource = new Resource();
  206 + resource.setTenantId(TenantId.SYS_TENANT_ID);
  207 + resource.setResourceType(ResourceType.LWM2M_MODEL);
  208 + resource.setResourceId(path.getFileName().toString());
  209 + resource.setValue(Base64.getEncoder().encodeToString(Files.readAllBytes(path)));
  210 + resourceService.saveResource(resource);
  211 + } catch (Exception e) {
  212 + log.error("Unable to load lwm2m model [{}]", path.toString());
  213 + throw new RuntimeException("Unable to load lwm2m model", e);
  214 + }
  215 + }
  216 + );
  217 + }
  218 + }
  219 +
  220 + Path jksPath = Paths.get(getDataDir(), CREDENTIALS_DIR, "serverKeyStore.jks");
  221 + try {
  222 + Resource resource = new Resource();
  223 + resource.setTenantId(TenantId.SYS_TENANT_ID);
  224 + resource.setResourceType(ResourceType.JKS);
  225 + resource.setResourceId(jksPath.getFileName().toString());
  226 + resource.setValue(Base64.getEncoder().encodeToString(Files.readAllBytes(jksPath)));
  227 + resourceService.saveResource(resource);
  228 + } catch (Exception e) {
  229 + log.error("Unable to load lwm2m serverKeyStore [{}]", jksPath.toString());
  230 + throw new RuntimeException("Unable to load l2m2m serverKeyStore", e);
  231 + }
  232 + }
  233 +
189 public void loadDashboards(TenantId tenantId, CustomerId customerId) throws Exception { 234 public void loadDashboards(TenantId tenantId, CustomerId customerId) throws Exception {
190 Path dashboardsDir = Paths.get(getDataDir(), JSON_DIR, DEMO_DIR, DASHBOARDS_DIR); 235 Path dashboardsDir = Paths.get(getDataDir(), JSON_DIR, DEMO_DIR, DASHBOARDS_DIR);
191 try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(dashboardsDir, path -> path.toString().endsWith(JSON_EXT))) { 236 try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(dashboardsDir, path -> path.toString().endsWith(JSON_EXT))) {
@@ -208,7 +253,6 @@ public class InstallScripts { @@ -208,7 +253,6 @@ public class InstallScripts {
208 } 253 }
209 } 254 }
210 255
211 -  
212 public void loadDemoRuleChains(TenantId tenantId) throws Exception { 256 public void loadDemoRuleChains(TenantId tenantId) throws Exception {
213 try { 257 try {
214 createDefaultRuleChains(tenantId); 258 createDefaultRuleChains(tenantId);
@@ -196,11 +196,17 @@ public class PsqlTsDatabaseUpgradeService extends AbstractSqlTsDatabaseUpgradeSe @@ -196,11 +196,17 @@ public class PsqlTsDatabaseUpgradeService extends AbstractSqlTsDatabaseUpgradeSe
196 } 196 }
197 break; 197 break;
198 case "3.1.1": 198 case "3.1.1":
  199 + case "3.2.1":
199 try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { 200 try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) {
200 log.info("Load TTL functions ..."); 201 log.info("Load TTL functions ...");
201 loadSql(conn, LOAD_TTL_FUNCTIONS_SQL); 202 loadSql(conn, LOAD_TTL_FUNCTIONS_SQL);
202 log.info("Load Drop Partitions functions ..."); 203 log.info("Load Drop Partitions functions ...");
203 loadSql(conn, LOAD_DROP_PARTITIONS_FUNCTIONS_SQL); 204 loadSql(conn, LOAD_DROP_PARTITIONS_FUNCTIONS_SQL);
  205 +
  206 + executeQuery(conn, "DROP PROCEDURE IF EXISTS cleanup_timeseries_by_ttl(character varying, bigint, bigint);");
  207 + executeQuery(conn, "DROP FUNCTION IF EXISTS delete_asset_records_from_ts_kv(character varying, character varying, bigint);");
  208 + executeQuery(conn, "DROP FUNCTION IF EXISTS delete_device_records_from_ts_kv(character varying, character varying, bigint);");
  209 + executeQuery(conn, "DROP FUNCTION IF EXISTS delete_customer_records_from_ts_kv(character varying, character varying, bigint);");
204 } 210 }
205 break; 211 break;
206 default: 212 default:
@@ -30,7 +30,7 @@ import java.sql.SQLException; @@ -30,7 +30,7 @@ import java.sql.SQLException;
30 @Slf4j 30 @Slf4j
31 public abstract class SqlAbstractDatabaseSchemaService implements DatabaseSchemaService { 31 public abstract class SqlAbstractDatabaseSchemaService implements DatabaseSchemaService {
32 32
33 - private static final String SQL_DIR = "sql"; 33 + protected static final String SQL_DIR = "sql";
34 34
35 @Value("${spring.datasource.url}") 35 @Value("${spring.datasource.url}")
36 protected String dbUrl; 36 protected String dbUrl;
@@ -42,7 +42,7 @@ public abstract class SqlAbstractDatabaseSchemaService implements DatabaseSchema @@ -42,7 +42,7 @@ public abstract class SqlAbstractDatabaseSchemaService implements DatabaseSchema
42 protected String dbPassword; 42 protected String dbPassword;
43 43
44 @Autowired 44 @Autowired
45 - private InstallScripts installScripts; 45 + protected InstallScripts installScripts;
46 46
47 private final String schemaSql; 47 private final String schemaSql;
48 private final String schemaIdxSql; 48 private final String schemaIdxSql;
@@ -434,6 +434,25 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService @@ -434,6 +434,25 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService
434 log.info("Schema updated."); 434 log.info("Schema updated.");
435 } 435 }
436 break; 436 break;
  437 + case "3.2.2":
  438 + try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) {
  439 + log.info("Updating schema ...");
  440 + try {
  441 + conn.createStatement().execute("CREATE TABLE IF NOT EXISTS resource (" +
  442 + " tenant_id uuid NOT NULL," +
  443 + " resource_type varchar(32) NOT NULL," +
  444 + " resource_id varchar(255) NOT NULL," +
  445 + " resource_value varchar," +
  446 + " CONSTRAINT resource_unq_key UNIQUE (tenant_id, resource_type, resource_id)" +
  447 + " );");
  448 +
  449 + conn.createStatement().execute("UPDATE tb_schema_settings SET schema_version = 3003000;");
  450 + } catch (Exception e) {
  451 + log.error("Failed updating schema!!!", e);
  452 + }
  453 + log.info("Schema updated.");
  454 + }
  455 + break;
437 default: 456 default:
438 throw new RuntimeException("Unable to upgrade SQL database, unsupported fromVersion: " + fromVersion); 457 throw new RuntimeException("Unable to upgrade SQL database, unsupported fromVersion: " + fromVersion);
439 } 458 }
@@ -33,4 +33,6 @@ public interface SystemDataLoaderService { @@ -33,4 +33,6 @@ public interface SystemDataLoaderService {
33 33
34 void deleteSystemWidgetBundle(String bundleAlias) throws Exception; 34 void deleteSystemWidgetBundle(String bundleAlias) throws Exception;
35 35
  36 + void loadSystemLwm2mResources() throws Exception;
  37 +
36 } 38 }
@@ -178,6 +178,7 @@ public class TimescaleTsDatabaseUpgradeService extends AbstractSqlTsDatabaseUpgr @@ -178,6 +178,7 @@ public class TimescaleTsDatabaseUpgradeService extends AbstractSqlTsDatabaseUpgr
178 } 178 }
179 break; 179 break;
180 case "3.1.1": 180 case "3.1.1":
  181 + case "3.2.1":
181 break; 182 break;
182 default: 183 default:
183 throw new RuntimeException("Unable to upgrade SQL database, unsupported fromVersion: " + fromVersion); 184 throw new RuntimeException("Unable to upgrade SQL database, unsupported fromVersion: " + fromVersion);
@@ -146,17 +146,17 @@ public class CassandraDbHelper { @@ -146,17 +146,17 @@ public class CassandraDbHelper {
146 if (row.isNull(index)) { 146 if (row.isNull(index)) {
147 return null; 147 return null;
148 } else if (type.getProtocolCode() == ProtocolConstants.DataType.DOUBLE) { 148 } else if (type.getProtocolCode() == ProtocolConstants.DataType.DOUBLE) {
149 - str = new Double(row.getDouble(index)).toString(); 149 + str = Double.valueOf(row.getDouble(index)).toString();
150 } else if (type.getProtocolCode() == ProtocolConstants.DataType.INT) { 150 } else if (type.getProtocolCode() == ProtocolConstants.DataType.INT) {
151 - str = new Integer(row.getInt(index)).toString(); 151 + str = Integer.valueOf(row.getInt(index)).toString();
152 } else if (type.getProtocolCode() == ProtocolConstants.DataType.BIGINT) { 152 } else if (type.getProtocolCode() == ProtocolConstants.DataType.BIGINT) {
153 - str = new Long(row.getLong(index)).toString(); 153 + str = Long.valueOf(row.getLong(index)).toString();
154 } else if (type.getProtocolCode() == ProtocolConstants.DataType.UUID) { 154 } else if (type.getProtocolCode() == ProtocolConstants.DataType.UUID) {
155 str = row.getUuid(index).toString(); 155 str = row.getUuid(index).toString();
156 } else if (type.getProtocolCode() == ProtocolConstants.DataType.TIMEUUID) { 156 } else if (type.getProtocolCode() == ProtocolConstants.DataType.TIMEUUID) {
157 str = row.getUuid(index).toString(); 157 str = row.getUuid(index).toString();
158 } else if (type.getProtocolCode() == ProtocolConstants.DataType.FLOAT) { 158 } else if (type.getProtocolCode() == ProtocolConstants.DataType.FLOAT) {
159 - str = new Float(row.getFloat(index)).toString(); 159 + str = Float.valueOf(row.getFloat(index)).toString();
160 } else if (type.getProtocolCode() == ProtocolConstants.DataType.TIMESTAMP) { 160 } else if (type.getProtocolCode() == ProtocolConstants.DataType.TIMESTAMP) {
161 str = ""+row.getInstant(index).toEpochMilli(); 161 str = ""+row.getInstant(index).toEpochMilli();
162 } else { 162 } else {
@@ -153,7 +153,8 @@ public class CassandraToSqlColumn { @@ -153,7 +153,8 @@ public class CassandraToSqlColumn {
153 sqlInsertStatement.setBoolean(this.sqlIndex, Boolean.parseBoolean(value)); 153 sqlInsertStatement.setBoolean(this.sqlIndex, Boolean.parseBoolean(value));
154 break; 154 break;
155 case ENUM_TO_INT: 155 case ENUM_TO_INT:
156 - Enum enumVal = Enum.valueOf(this.enumClass, value); 156 + @SuppressWarnings("unchecked")
  157 + Enum<?> enumVal = Enum.valueOf(this.enumClass, value);
157 int intValue = enumVal.ordinal(); 158 int intValue = enumVal.ordinal();
158 sqlInsertStatement.setInt(this.sqlIndex, intValue); 159 sqlInsertStatement.setInt(this.sqlIndex, intValue);
159 break; 160 break;
@@ -15,9 +15,7 @@ @@ -15,9 +15,7 @@
15 */ 15 */
16 package org.thingsboard.server.service.install.update; 16 package org.thingsboard.server.service.install.update;
17 17
18 -import com.fasterxml.jackson.databind.JsonNode;  
19 import com.fasterxml.jackson.databind.node.ObjectNode; 18 import com.fasterxml.jackson.databind.node.ObjectNode;
20 -import com.google.common.util.concurrent.FutureCallback;  
21 import com.google.common.util.concurrent.Futures; 19 import com.google.common.util.concurrent.Futures;
22 import com.google.common.util.concurrent.ListenableFuture; 20 import com.google.common.util.concurrent.ListenableFuture;
23 import com.google.common.util.concurrent.MoreExecutors; 21 import com.google.common.util.concurrent.MoreExecutors;
@@ -25,16 +23,12 @@ import lombok.extern.slf4j.Slf4j; @@ -25,16 +23,12 @@ import lombok.extern.slf4j.Slf4j;
25 import org.springframework.beans.factory.annotation.Autowired; 23 import org.springframework.beans.factory.annotation.Autowired;
26 import org.springframework.context.annotation.Profile; 24 import org.springframework.context.annotation.Profile;
27 import org.springframework.stereotype.Service; 25 import org.springframework.stereotype.Service;
28 -import org.springframework.util.StringUtils;  
29 import org.thingsboard.rule.engine.profile.TbDeviceProfileNode; 26 import org.thingsboard.rule.engine.profile.TbDeviceProfileNode;
30 import org.thingsboard.rule.engine.profile.TbDeviceProfileNodeConfiguration; 27 import org.thingsboard.rule.engine.profile.TbDeviceProfileNodeConfiguration;
31 import org.thingsboard.server.common.data.EntityView; 28 import org.thingsboard.server.common.data.EntityView;
32 -import org.thingsboard.server.common.data.SearchTextBased;  
33 import org.thingsboard.server.common.data.Tenant; 29 import org.thingsboard.server.common.data.Tenant;
34 -import org.thingsboard.server.common.data.id.EntityId;  
35 import org.thingsboard.server.common.data.id.EntityViewId; 30 import org.thingsboard.server.common.data.id.EntityViewId;
36 import org.thingsboard.server.common.data.id.TenantId; 31 import org.thingsboard.server.common.data.id.TenantId;
37 -import org.thingsboard.server.common.data.id.UUIDBased;  
38 import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery; 32 import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery;
39 import org.thingsboard.server.common.data.kv.ReadTsKvQuery; 33 import org.thingsboard.server.common.data.kv.ReadTsKvQuery;
40 import org.thingsboard.server.common.data.kv.TsKvEntry; 34 import org.thingsboard.server.common.data.kv.TsKvEntry;
@@ -47,18 +41,16 @@ import org.thingsboard.server.dao.entityview.EntityViewService; @@ -47,18 +41,16 @@ import org.thingsboard.server.dao.entityview.EntityViewService;
47 import org.thingsboard.server.dao.rule.RuleChainService; 41 import org.thingsboard.server.dao.rule.RuleChainService;
48 import org.thingsboard.server.dao.tenant.TenantService; 42 import org.thingsboard.server.dao.tenant.TenantService;
49 import org.thingsboard.server.dao.timeseries.TimeseriesService; 43 import org.thingsboard.server.dao.timeseries.TimeseriesService;
50 -import org.thingsboard.server.dao.util.mapping.JacksonUtil; 44 +import org.thingsboard.common.util.JacksonUtil;
51 import org.thingsboard.server.service.install.InstallScripts; 45 import org.thingsboard.server.service.install.InstallScripts;
52 46
53 -import javax.annotation.Nullable;  
54 import java.util.ArrayList; 47 import java.util.ArrayList;
55 import java.util.Collections; 48 import java.util.Collections;
56 import java.util.List; 49 import java.util.List;
57 import java.util.concurrent.ExecutionException; 50 import java.util.concurrent.ExecutionException;
58 import java.util.stream.Collectors; 51 import java.util.stream.Collectors;
59 52
60 -import static org.apache.commons.lang.StringUtils.isBlank;  
61 -import static org.thingsboard.server.service.install.DatabaseHelper.objectMapper; 53 +import static org.apache.commons.lang3.StringUtils.isBlank;
62 54
63 @Service 55 @Service
64 @Profile("install") 56 @Profile("install")
  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.lwm2m;
  17 +
  18 +
  19 +import lombok.extern.slf4j.Slf4j;
  20 +import org.eclipse.leshan.core.model.ObjectModel;
  21 +import org.eclipse.leshan.core.util.Hex;
  22 +import org.springframework.beans.factory.annotation.Autowired;
  23 +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
  24 +import org.springframework.data.domain.PageImpl;
  25 +import org.springframework.stereotype.Service;
  26 +import org.thingsboard.server.common.data.id.TenantId;
  27 +import org.thingsboard.server.common.data.lwm2m.LwM2mInstance;
  28 +import org.thingsboard.server.common.data.lwm2m.LwM2mObject;
  29 +import org.thingsboard.server.common.data.lwm2m.LwM2mResource;
  30 +import org.thingsboard.server.common.data.lwm2m.ServerSecurityConfig;
  31 +import org.thingsboard.server.common.data.page.PageData;
  32 +import org.thingsboard.server.common.data.page.PageLink;
  33 +import org.thingsboard.server.common.transport.lwm2m.LwM2MTransportConfigBootstrap;
  34 +import org.thingsboard.server.common.transport.lwm2m.LwM2MTransportConfigServer;
  35 +import org.thingsboard.server.dao.service.Validator;
  36 +import org.thingsboard.server.queue.util.TbLwM2mTransportComponent;
  37 +import org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode;
  38 +
  39 +import java.math.BigInteger;
  40 +import java.security.AlgorithmParameters;
  41 +import java.security.GeneralSecurityException;
  42 +import java.security.KeyFactory;
  43 +import java.security.KeyStoreException;
  44 +import java.security.PublicKey;
  45 +import java.security.cert.CertificateEncodingException;
  46 +import java.security.cert.X509Certificate;
  47 +import java.security.spec.ECGenParameterSpec;
  48 +import java.security.spec.ECParameterSpec;
  49 +import java.security.spec.ECPoint;
  50 +import java.security.spec.ECPublicKeySpec;
  51 +import java.security.spec.KeySpec;
  52 +import java.util.ArrayList;
  53 +import java.util.Comparator;
  54 +import java.util.List;
  55 +import java.util.concurrent.atomic.AtomicInteger;
  56 +import java.util.function.Predicate;
  57 +import java.util.stream.Collector;
  58 +import java.util.stream.Collectors;
  59 +import java.util.stream.IntStream;
  60 +
  61 +import static org.thingsboard.server.dao.service.Validator.validateId;
  62 +
  63 +@Slf4j
  64 +@Service
  65 +@ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true') || '${service.type:null}'=='monolith' || '${service.type:null}'=='tb-core'")
  66 +public class LwM2MModelsRepository {
  67 +
  68 + private static final String INCORRECT_TENANT_ID = "Incorrect tenantId ";
  69 +
  70 + @Autowired
  71 + LwM2MTransportConfigServer contextServer;
  72 +
  73 +
  74 + @Autowired
  75 + LwM2MTransportConfigBootstrap contextBootStrap;
  76 +
  77 + /**
  78 + * @param objectIds
  79 + * @param textSearch
  80 + * @return list of LwM2mObject
  81 + * Filter by Predicate (uses objectIds, if objectIds is null then it uses textSearch,
  82 + * if textSearch is null then it uses AllList from List<ObjectModel>)
  83 + */
  84 + public List<LwM2mObject> getLwm2mObjects(int[] objectIds, String textSearch, String sortProperty, String sortOrder) {
  85 + if (objectIds == null && textSearch != null && !textSearch.isEmpty()) {
  86 + objectIds = getObjectIdFromTextSearch(textSearch);
  87 + }
  88 + int[] finalObjectIds = objectIds;
  89 + return getLwm2mObjects((objectIds != null && objectIds.length > 0 && textSearch != null && !textSearch.isEmpty()) ?
  90 + (ObjectModel element) -> IntStream.of(finalObjectIds).anyMatch(x -> x == element.id) || element.name.toLowerCase().contains(textSearch.toLowerCase()) :
  91 + (objectIds != null && objectIds.length > 0) ?
  92 + (ObjectModel element) -> IntStream.of(finalObjectIds).anyMatch(x -> x == element.id) :
  93 + (textSearch != null && !textSearch.isEmpty()) ?
  94 + (ObjectModel element) -> element.name.contains(textSearch) :
  95 + null,
  96 + sortProperty, sortOrder);
  97 + }
  98 +
  99 + /**
  100 + * @param predicate
  101 + * @return list of LwM2mObject
  102 + */
  103 + private List<LwM2mObject> getLwm2mObjects(Predicate<? super ObjectModel> predicate, String sortProperty, String sortOrder) {
  104 + List<LwM2mObject> lwM2mObjects = new ArrayList<>();
  105 + List<ObjectModel> listObjects = (predicate == null) ? this.contextServer.getModelsValue() :
  106 + contextServer.getModelsValue().stream()
  107 + .filter(predicate)
  108 + .collect(Collectors.toList());
  109 +
  110 + listObjects.forEach(obj -> {
  111 + LwM2mObject lwM2mObject = new LwM2mObject();
  112 + lwM2mObject.setId(obj.id);
  113 + lwM2mObject.setName(obj.name);
  114 + lwM2mObject.setMultiple(obj.multiple);
  115 + lwM2mObject.setMandatory(obj.mandatory);
  116 + LwM2mInstance instance = new LwM2mInstance();
  117 + instance.setId(0);
  118 + List<LwM2mResource> resources = new ArrayList<>();
  119 + obj.resources.forEach((k, v) -> {
  120 + if (!v.operations.isExecutable()) {
  121 + LwM2mResource resource = new LwM2mResource(k, v.name, false, false, false);
  122 + resources.add(resource);
  123 + }
  124 + });
  125 + instance.setResources(resources.stream().toArray(LwM2mResource[]::new));
  126 + lwM2mObject.setInstances(new LwM2mInstance[]{instance});
  127 + lwM2mObjects.add(lwM2mObject);
  128 + });
  129 + return lwM2mObjects.size() > 1 ? this.sortList (lwM2mObjects, sortProperty, sortOrder) : lwM2mObjects;
  130 + }
  131 +
  132 + private List<LwM2mObject> sortList (List<LwM2mObject> lwM2mObjects, String sortProperty, String sortOrder) {
  133 + switch (sortProperty) {
  134 + case "name":
  135 + switch (sortOrder) {
  136 + case "ASC":
  137 + lwM2mObjects.sort((o1, o2) -> o1.getName().compareTo(o2.getName()));
  138 + break;
  139 + case "DESC":
  140 + lwM2mObjects.stream().sorted(Comparator.comparing(LwM2mObject::getName).reversed());
  141 + break;
  142 + }
  143 + case "id":
  144 + switch (sortOrder) {
  145 + case "ASC":
  146 + lwM2mObjects.sort((o1, o2) -> Long.compare(o1.getId(), o2.getId()));
  147 + break;
  148 + case "DESC":
  149 + lwM2mObjects.sort((o1, o2) -> Long.compare(o2.getId(), o1.getId()));
  150 + }
  151 + }
  152 + return lwM2mObjects;
  153 + }
  154 +
  155 + /**
  156 + * @param tenantId
  157 + * @param pageLink
  158 + * @return List of LwM2mObject in PageData format
  159 + */
  160 + public PageData<LwM2mObject> findDeviceLwm2mObjects(TenantId tenantId, PageLink pageLink) {
  161 + log.trace("Executing findDeviceProfileInfos tenantId [{}], pageLink [{}]", tenantId, pageLink);
  162 + validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
  163 + Validator.validatePageLink(pageLink);
  164 + return this.findLwm2mListObjects(pageLink);
  165 + }
  166 +
  167 + /**
  168 + * @param pageLink
  169 + * @return List of LwM2mObject in PageData format, filter == TextSearch
  170 + * PageNumber = 1, PageSize = List<LwM2mObject>.size()
  171 + */
  172 + public PageData<LwM2mObject> findLwm2mListObjects(PageLink pageLink) {
  173 + PageImpl<LwM2mObject> page = new PageImpl<>(getLwm2mObjects(getObjectIdFromTextSearch(pageLink.getTextSearch()),
  174 + pageLink.getTextSearch(),
  175 + pageLink.getSortOrder().getProperty(),
  176 + pageLink.getSortOrder().getDirection().name()));
  177 + PageData<LwM2mObject> pageData = new PageData<>(page.getContent(), page.getTotalPages(), page.getTotalElements(), page.hasNext());
  178 + return pageData;
  179 + }
  180 +
  181 + /**
  182 + * Filter for id Object
  183 + * @param textSearch -
  184 + * @return - return Object id only first chartAt in textSearch
  185 + */
  186 + private int[] getObjectIdFromTextSearch(String textSearch) {
  187 + String filtered = null;
  188 + if (textSearch !=null && !textSearch.isEmpty()) {
  189 + AtomicInteger a = new AtomicInteger();
  190 + filtered = textSearch.chars ()
  191 + .mapToObj(chr -> (char) chr)
  192 + .filter(i -> Character.isDigit(i) && textSearch.charAt(a.getAndIncrement()) == i)
  193 + .collect(Collector.of(StringBuilder::new, StringBuilder::append, StringBuilder::append, StringBuilder::toString));
  194 + }
  195 + return (filtered != null && !filtered.isEmpty()) ? new int[]{Integer.parseInt(filtered)} : new int[0];
  196 + }
  197 +
  198 + /**
  199 + * @param securityMode
  200 + * @param bootstrapServerIs
  201 + * @return ServerSecurityConfig more value is default: Important - port, host, publicKey
  202 + */
  203 + public ServerSecurityConfig getBootstrapSecurityInfo(String securityMode, boolean bootstrapServerIs) {
  204 + LwM2MSecurityMode lwM2MSecurityMode = LwM2MSecurityMode.fromSecurityMode(securityMode.toLowerCase());
  205 + return getBootstrapServer(bootstrapServerIs, lwM2MSecurityMode);
  206 + }
  207 +
  208 + /**
  209 + * @param bootstrapServerIs
  210 + * @param mode
  211 + * @return ServerSecurityConfig more value is default: Important - port, host, publicKey
  212 + */
  213 + private ServerSecurityConfig getBootstrapServer(boolean bootstrapServerIs, LwM2MSecurityMode mode) {
  214 + ServerSecurityConfig bsServ = new ServerSecurityConfig();
  215 + bsServ.setBootstrapServerIs(bootstrapServerIs);
  216 + if (bootstrapServerIs) {
  217 + bsServ.setServerId(contextBootStrap.getBootstrapServerId());
  218 + switch (mode) {
  219 + case NO_SEC:
  220 + bsServ.setHost(contextBootStrap.getBootstrapHost());
  221 + bsServ.setPort(contextBootStrap.getBootstrapPortNoSec());
  222 + bsServ.setServerPublicKey("");
  223 + break;
  224 + case PSK:
  225 + bsServ.setHost(contextBootStrap.getBootstrapHostSecurity());
  226 + bsServ.setPort(contextBootStrap.getBootstrapPortSecurity());
  227 + bsServ.setServerPublicKey("");
  228 + break;
  229 + case RPK:
  230 + case X509:
  231 + bsServ.setHost(contextBootStrap.getBootstrapHostSecurity());
  232 + bsServ.setPort(contextBootStrap.getBootstrapPortSecurity());
  233 + bsServ.setServerPublicKey(getPublicKey (contextBootStrap.getBootstrapAlias(), this.contextBootStrap.getBootstrapPublicX(), this.contextBootStrap.getBootstrapPublicY()));
  234 + break;
  235 + default:
  236 + break;
  237 + }
  238 + } else {
  239 + bsServ.setServerId(contextServer.getServerId());
  240 + switch (mode) {
  241 + case NO_SEC:
  242 + bsServ.setHost(contextServer.getServerHost());
  243 + bsServ.setPort(contextServer.getServerPortNoSec());
  244 + bsServ.setServerPublicKey("");
  245 + break;
  246 + case PSK:
  247 + bsServ.setHost(contextServer.getServerHostSecurity());
  248 + bsServ.setPort(contextServer.getServerPortSecurity());
  249 + bsServ.setServerPublicKey("");
  250 + break;
  251 + case RPK:
  252 + case X509:
  253 + bsServ.setHost(contextServer.getServerHostSecurity());
  254 + bsServ.setPort(contextServer.getServerPortSecurity());
  255 + bsServ.setServerPublicKey(getPublicKey (contextServer.getServerAlias(), this.contextServer.getServerPublicX(), this.contextServer.getServerPublicY()));
  256 + break;
  257 + default:
  258 + break;
  259 + }
  260 + }
  261 + return bsServ;
  262 + }
  263 +
  264 + private String getPublicKey (String alias, String publicServerX, String publicServerY) {
  265 + String publicKey = getServerPublicKeyX509(alias);
  266 + return publicKey != null ? publicKey : getRPKPublicKey(publicServerX, publicServerY);
  267 + }
  268 +
  269 + /**
  270 + * @param alias
  271 + * @return PublicKey format HexString or null
  272 + */
  273 + private String getServerPublicKeyX509(String alias) {
  274 + try {
  275 + X509Certificate serverCertificate = (X509Certificate) contextServer.getKeyStoreValue().getCertificate(alias);
  276 + return Hex.encodeHexString(serverCertificate.getEncoded());
  277 + } catch (CertificateEncodingException | KeyStoreException e) {
  278 + e.printStackTrace();
  279 + }
  280 + return null;
  281 + }
  282 +
  283 + /**
  284 + * @param publicServerX
  285 + * @param publicServerY
  286 + * @return PublicKey format HexString or null
  287 + */
  288 + private String getRPKPublicKey(String publicServerX, String publicServerY) {
  289 + try {
  290 + /** Get Elliptic Curve Parameter spec for secp256r1 */
  291 + AlgorithmParameters algoParameters = AlgorithmParameters.getInstance("EC");
  292 + algoParameters.init(new ECGenParameterSpec("secp256r1"));
  293 + ECParameterSpec parameterSpec = algoParameters.getParameterSpec(ECParameterSpec.class);
  294 + if (publicServerX != null && !publicServerX.isEmpty() && publicServerY != null && !publicServerY.isEmpty()) {
  295 + /** Get point values */
  296 + byte[] publicX = Hex.decodeHex(publicServerX.toCharArray());
  297 + byte[] publicY = Hex.decodeHex(publicServerY.toCharArray());
  298 + /** Create key specs */
  299 + KeySpec publicKeySpec = new ECPublicKeySpec(new ECPoint(new BigInteger(publicX), new BigInteger(publicY)),
  300 + parameterSpec);
  301 + /** Get keys */
  302 + PublicKey publicKey = KeyFactory.getInstance("EC").generatePublic(publicKeySpec);
  303 + if (publicKey != null && publicKey.getEncoded().length > 0) {
  304 + return Hex.encodeHexString(publicKey.getEncoded());
  305 + }
  306 + }
  307 + } catch (GeneralSecurityException | IllegalArgumentException e) {
  308 + log.error("[{}] Failed generate Server RPK for profile", e.getMessage());
  309 + throw new RuntimeException(e);
  310 + }
  311 + return null;
  312 + }
  313 +}
  314 +
@@ -47,7 +47,7 @@ import org.thingsboard.server.dao.attributes.AttributesService; @@ -47,7 +47,7 @@ import org.thingsboard.server.dao.attributes.AttributesService;
47 import org.thingsboard.server.dao.entity.EntityService; 47 import org.thingsboard.server.dao.entity.EntityService;
48 import org.thingsboard.server.dao.model.ModelConstants; 48 import org.thingsboard.server.dao.model.ModelConstants;
49 import org.thingsboard.server.dao.timeseries.TimeseriesService; 49 import org.thingsboard.server.dao.timeseries.TimeseriesService;
50 -import org.thingsboard.server.dao.util.mapping.JacksonUtil; 50 +import org.thingsboard.common.util.JacksonUtil;
51 import org.thingsboard.server.queue.util.TbCoreComponent; 51 import org.thingsboard.server.queue.util.TbCoreComponent;
52 import org.thingsboard.server.service.executors.DbCallbackExecutorService; 52 import org.thingsboard.server.service.executors.DbCallbackExecutorService;
53 import org.thingsboard.server.service.security.AccessValidator; 53 import org.thingsboard.server.service.security.AccessValidator;
@@ -206,7 +206,7 @@ public class DefaultEntityQueryService implements EntityQueryService { @@ -206,7 +206,7 @@ public class DefaultEntityQueryService implements EntityQueryService {
206 addItemsToArrayNode(json.putArray("entityTypes"), types); 206 addItemsToArrayNode(json.putArray("entityTypes"), types);
207 addItemsToArrayNode(json.putArray("timeseries"), timeseriesKeys); 207 addItemsToArrayNode(json.putArray("timeseries"), timeseriesKeys);
208 addItemsToArrayNode(json.putArray("attribute"), attributesKeys); 208 addItemsToArrayNode(json.putArray("attribute"), attributesKeys);
209 - response.setResult(new ResponseEntity(json, HttpStatus.OK)); 209 + response.setResult(new ResponseEntity<>(json, HttpStatus.OK));
210 } 210 }
211 211
212 private void replyWithEmptyResponse(DeferredResult<ResponseEntity> response) { 212 private void replyWithEmptyResponse(DeferredResult<ResponseEntity> response) {
@@ -34,6 +34,7 @@ import org.thingsboard.server.common.data.id.EntityId; @@ -34,6 +34,7 @@ import org.thingsboard.server.common.data.id.EntityId;
34 import org.thingsboard.server.common.data.id.RuleChainId; 34 import org.thingsboard.server.common.data.id.RuleChainId;
35 import org.thingsboard.server.common.data.id.TenantId; 35 import org.thingsboard.server.common.data.id.TenantId;
36 import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; 36 import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
  37 +import org.thingsboard.server.common.data.transport.resource.Resource;
37 import org.thingsboard.server.common.msg.TbMsg; 38 import org.thingsboard.server.common.msg.TbMsg;
38 import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; 39 import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
39 import org.thingsboard.server.common.msg.queue.ServiceType; 40 import org.thingsboard.server.common.msg.queue.ServiceType;
@@ -145,7 +146,7 @@ public class DefaultTbClusterService implements TbClusterService { @@ -145,7 +146,7 @@ public class DefaultTbClusterService implements TbClusterService {
145 tbMsg = transformMsg(tbMsg, deviceProfileCache.get(tenantId, new DeviceProfileId(entityId.getId()))); 146 tbMsg = transformMsg(tbMsg, deviceProfileCache.get(tenantId, new DeviceProfileId(entityId.getId())));
146 } 147 }
147 } 148 }
148 - TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_RULE_ENGINE, tenantId, entityId); 149 + TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_RULE_ENGINE, tbMsg.getQueueName(), tenantId, entityId);
149 log.trace("PUSHING msg: {} to:{}", tbMsg, tpi); 150 log.trace("PUSHING msg: {} to:{}", tbMsg, tpi);
150 ToRuleEngineMsg msg = ToRuleEngineMsg.newBuilder() 151 ToRuleEngineMsg msg = ToRuleEngineMsg.newBuilder()
151 .setTenantIdMSB(tenantId.getId().getMostSignificantBits()) 152 .setTenantIdMSB(tenantId.getId().getMostSignificantBits())
@@ -247,6 +248,34 @@ public class DefaultTbClusterService implements TbClusterService { @@ -247,6 +248,34 @@ public class DefaultTbClusterService implements TbClusterService {
247 onEntityDelete(entity.getTenantId(), entity.getId(), entity.getName(), callback); 248 onEntityDelete(entity.getTenantId(), entity.getId(), entity.getName(), callback);
248 } 249 }
249 250
  251 + @Override
  252 + public void onResourceChange(Resource resource, TbQueueCallback callback) {
  253 + TenantId tenantId = resource.getTenantId();
  254 + log.trace("[{}][{}][{}] Processing change resource", tenantId, resource.getResourceType(), resource.getResourceId());
  255 + TransportProtos.ResourceUpdateMsg resourceUpdateMsg = TransportProtos.ResourceUpdateMsg.newBuilder()
  256 + .setTenantIdMSB(tenantId.getId().getMostSignificantBits())
  257 + .setTenantIdLSB(tenantId.getId().getLeastSignificantBits())
  258 + .setResourceType(resource.getResourceType().name())
  259 + .setResourceId(resource.getResourceId())
  260 + .build();
  261 + ToTransportMsg transportMsg = ToTransportMsg.newBuilder().setResourceUpdateMsg(resourceUpdateMsg).build();
  262 + broadcast(transportMsg, callback);
  263 + }
  264 +
  265 + @Override
  266 + public void onResourceDeleted(Resource resource, TbQueueCallback callback) {
  267 + TenantId tenantId = resource.getTenantId();
  268 + log.trace("[{}][{}][{}] Processing delete resource", tenantId, resource.getResourceType(), resource.getResourceId());
  269 + TransportProtos.ResourceDeleteMsg resourceUpdateMsg = TransportProtos.ResourceDeleteMsg.newBuilder()
  270 + .setTenantIdMSB(tenantId.getId().getMostSignificantBits())
  271 + .setTenantIdLSB(tenantId.getId().getLeastSignificantBits())
  272 + .setResourceType(resource.getResourceType().name())
  273 + .setResourceId(resource.getResourceId())
  274 + .build();
  275 + ToTransportMsg transportMsg = ToTransportMsg.newBuilder().setResourceDeleteMsg(resourceUpdateMsg).build();
  276 + broadcast(transportMsg, callback);
  277 + }
  278 +
250 public <T> void onEntityChange(TenantId tenantId, EntityId entityid, T entity, TbQueueCallback callback) { 279 public <T> void onEntityChange(TenantId tenantId, EntityId entityid, T entity, TbQueueCallback callback) {
251 String entityName = (entity instanceof HasName) ? ((HasName) entity).getName() : entity.getClass().getName(); 280 String entityName = (entity instanceof HasName) ? ((HasName) entity).getName() : entity.getClass().getName();
252 log.trace("[{}][{}][{}] Processing [{}] change event", tenantId, entityid.getEntityType(), entityid.getId(), entityName); 281 log.trace("[{}][{}][{}] Processing [{}] change event", tenantId, entityid.getEntityType(), entityid.getId(), entityName);
@@ -35,7 +35,7 @@ import org.thingsboard.server.common.msg.queue.ServiceType; @@ -35,7 +35,7 @@ import org.thingsboard.server.common.msg.queue.ServiceType;
35 import org.thingsboard.server.common.msg.queue.TbCallback; 35 import org.thingsboard.server.common.msg.queue.TbCallback;
36 import org.thingsboard.server.common.stats.StatsFactory; 36 import org.thingsboard.server.common.stats.StatsFactory;
37 import org.thingsboard.server.common.transport.util.DataDecodingEncodingService; 37 import org.thingsboard.server.common.transport.util.DataDecodingEncodingService;
38 -import org.thingsboard.server.dao.util.mapping.JacksonUtil; 38 +import org.thingsboard.common.util.JacksonUtil;
39 import org.thingsboard.server.gen.transport.TransportProtos.DeviceStateServiceMsgProto; 39 import org.thingsboard.server.gen.transport.TransportProtos.DeviceStateServiceMsgProto;
40 import org.thingsboard.server.gen.transport.TransportProtos.FromDeviceRPCResponseProto; 40 import org.thingsboard.server.gen.transport.TransportProtos.FromDeviceRPCResponseProto;
41 import org.thingsboard.server.gen.transport.TransportProtos.LocalSubscriptionServiceMsgProto; 41 import org.thingsboard.server.gen.transport.TransportProtos.LocalSubscriptionServiceMsgProto;
@@ -151,12 +151,12 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService<ToCore @@ -151,12 +151,12 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService<ToCore
151 } 151 }
152 152
153 @Override 153 @Override
154 - public void onApplicationEvent(PartitionChangeEvent partitionChangeEvent) {  
155 - if (partitionChangeEvent.getServiceType().equals(getServiceType())) {  
156 - log.info("Subscribing to partitions: {}", partitionChangeEvent.getPartitions());  
157 - this.mainConsumer.subscribe(partitionChangeEvent.getPartitions()); 154 + protected void onTbApplicationEvent(PartitionChangeEvent event) {
  155 + if (event.getServiceType().equals(getServiceType())) {
  156 + log.info("Subscribing to partitions: {}", event.getPartitions());
  157 + this.mainConsumer.subscribe(event.getPartitions());
158 this.usageStatsConsumer.subscribe( 158 this.usageStatsConsumer.subscribe(
159 - partitionChangeEvent 159 + event
160 .getPartitions() 160 .getPartitions()
161 .stream() 161 .stream()
162 .map(tpi -> tpi.newByTopic(usageStatsConsumer.getTopic())) 162 .map(tpi -> tpi.newByTopic(usageStatsConsumer.getTopic()))
@@ -140,11 +140,11 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService< @@ -140,11 +140,11 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService<
140 } 140 }
141 141
142 @Override 142 @Override
143 - public void onApplicationEvent(PartitionChangeEvent partitionChangeEvent) {  
144 - if (partitionChangeEvent.getServiceType().equals(getServiceType())) {  
145 - ServiceQueue serviceQueue = partitionChangeEvent.getServiceQueueKey().getServiceQueue();  
146 - log.info("[{}] Subscribing to partitions: {}", serviceQueue.getQueue(), partitionChangeEvent.getPartitions());  
147 - consumers.get(serviceQueue.getQueue()).subscribe(partitionChangeEvent.getPartitions()); 143 + protected void onTbApplicationEvent(PartitionChangeEvent event) {
  144 + if (event.getServiceType().equals(getServiceType())) {
  145 + ServiceQueue serviceQueue = event.getServiceQueueKey().getServiceQueue();
  146 + log.info("[{}] Subscribing to partitions: {}", serviceQueue.getQueue(), event.getPartitions());
  147 + consumers.get(serviceQueue.getQueue()).subscribe(event.getPartitions());
148 } 148 }
149 } 149 }
150 150
@@ -181,7 +181,7 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService< @@ -181,7 +181,7 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService<
181 new TbMsgPackCallback(id, tenantId, ctx, stats.getTimer(tenantId, SUCCESSFUL_STATUS), stats.getTimer(tenantId, FAILED_STATUS)) : 181 new TbMsgPackCallback(id, tenantId, ctx, stats.getTimer(tenantId, SUCCESSFUL_STATUS), stats.getTimer(tenantId, FAILED_STATUS)) :
182 new TbMsgPackCallback(id, tenantId, ctx); 182 new TbMsgPackCallback(id, tenantId, ctx);
183 try { 183 try {
184 - if (toRuleEngineMsg.getTbMsg() != null && !toRuleEngineMsg.getTbMsg().isEmpty()) { 184 + if (!toRuleEngineMsg.getTbMsg().isEmpty()) {
185 forwardToRuleEngineActor(configuration.getName(), tenantId, toRuleEngineMsg, callback); 185 forwardToRuleEngineActor(configuration.getName(), tenantId, toRuleEngineMsg, callback);
186 } else { 186 } else {
187 callback.onSuccess(); 187 callback.onSuccess();
@@ -209,6 +209,9 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService< @@ -209,6 +209,9 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService<
209 if (statsEnabled) { 209 if (statsEnabled) {
210 stats.log(result, decision.isCommit()); 210 stats.log(result, decision.isCommit());
211 } 211 }
  212 +
  213 + ctx.cleanup();
  214 +
212 if (decision.isCommit()) { 215 if (decision.isCommit()) {
213 submitStrategy.stop(); 216 submitStrategy.stop();
214 break; 217 break;
@@ -24,6 +24,7 @@ import org.thingsboard.server.common.data.TenantProfile; @@ -24,6 +24,7 @@ import org.thingsboard.server.common.data.TenantProfile;
24 import org.thingsboard.server.common.data.id.EntityId; 24 import org.thingsboard.server.common.data.id.EntityId;
25 import org.thingsboard.server.common.data.id.TenantId; 25 import org.thingsboard.server.common.data.id.TenantId;
26 import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; 26 import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
  27 +import org.thingsboard.server.common.data.transport.resource.Resource;
27 import org.thingsboard.server.common.msg.TbMsg; 28 import org.thingsboard.server.common.msg.TbMsg;
28 import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; 29 import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
29 import org.thingsboard.server.gen.transport.TransportProtos; 30 import org.thingsboard.server.gen.transport.TransportProtos;
@@ -71,4 +72,8 @@ public interface TbClusterService { @@ -71,4 +72,8 @@ public interface TbClusterService {
71 void onDeviceChange(Device device, TbQueueCallback callback); 72 void onDeviceChange(Device device, TbQueueCallback callback);
72 73
73 void onDeviceDeleted(Device device, TbQueueCallback callback); 74 void onDeviceDeleted(Device device, TbQueueCallback callback);
  75 +
  76 + void onResourceChange(Resource resource, TbQueueCallback callback);
  77 +
  78 + void onResourceDeleted(Resource resource, TbQueueCallback callback);
74 } 79 }
@@ -55,31 +55,22 @@ public class TbCoreConsumerStats { @@ -55,31 +55,22 @@ public class TbCoreConsumerStats {
55 public TbCoreConsumerStats(StatsFactory statsFactory) { 55 public TbCoreConsumerStats(StatsFactory statsFactory) {
56 String statsKey = StatsType.CORE.getName(); 56 String statsKey = StatsType.CORE.getName();
57 57
58 - this.totalCounter = statsFactory.createStatsCounter(statsKey, TOTAL_MSGS);  
59 - this.sessionEventCounter = statsFactory.createStatsCounter(statsKey, SESSION_EVENTS);  
60 - this.getAttributesCounter = statsFactory.createStatsCounter(statsKey, GET_ATTRIBUTE);  
61 - this.subscribeToAttributesCounter = statsFactory.createStatsCounter(statsKey, ATTRIBUTE_SUBSCRIBES);  
62 - this.subscribeToRPCCounter = statsFactory.createStatsCounter(statsKey, RPC_SUBSCRIBES);  
63 - this.toDeviceRPCCallResponseCounter = statsFactory.createStatsCounter(statsKey, TO_DEVICE_RPC_CALL_RESPONSES);  
64 - this.subscriptionInfoCounter = statsFactory.createStatsCounter(statsKey, SUBSCRIPTION_INFO);  
65 - this.claimDeviceCounter = statsFactory.createStatsCounter(statsKey, DEVICE_CLAIMS);  
66 - this.deviceStateCounter = statsFactory.createStatsCounter(statsKey, DEVICE_STATES);  
67 - this.subscriptionMsgCounter = statsFactory.createStatsCounter(statsKey, SUBSCRIPTION_MSGS);  
68 - this.toCoreNotificationsCounter = statsFactory.createStatsCounter(statsKey, TO_CORE_NOTIFICATIONS);  
69 -  
70 -  
71 - counters.add(totalCounter);  
72 - counters.add(sessionEventCounter);  
73 - counters.add(getAttributesCounter);  
74 - counters.add(subscribeToAttributesCounter);  
75 - counters.add(subscribeToRPCCounter);  
76 - counters.add(toDeviceRPCCallResponseCounter);  
77 - counters.add(subscriptionInfoCounter);  
78 - counters.add(claimDeviceCounter); 58 + this.totalCounter = register(statsFactory.createStatsCounter(statsKey, TOTAL_MSGS));
  59 + this.sessionEventCounter = register(statsFactory.createStatsCounter(statsKey, SESSION_EVENTS));
  60 + this.getAttributesCounter = register(statsFactory.createStatsCounter(statsKey, GET_ATTRIBUTE));
  61 + this.subscribeToAttributesCounter = register(statsFactory.createStatsCounter(statsKey, ATTRIBUTE_SUBSCRIBES));
  62 + this.subscribeToRPCCounter = register(statsFactory.createStatsCounter(statsKey, RPC_SUBSCRIBES));
  63 + this.toDeviceRPCCallResponseCounter = register(statsFactory.createStatsCounter(statsKey, TO_DEVICE_RPC_CALL_RESPONSES));
  64 + this.subscriptionInfoCounter = register(statsFactory.createStatsCounter(statsKey, SUBSCRIPTION_INFO));
  65 + this.claimDeviceCounter = register(statsFactory.createStatsCounter(statsKey, DEVICE_CLAIMS));
  66 + this.deviceStateCounter = register(statsFactory.createStatsCounter(statsKey, DEVICE_STATES));
  67 + this.subscriptionMsgCounter = register(statsFactory.createStatsCounter(statsKey, SUBSCRIPTION_MSGS));
  68 + this.toCoreNotificationsCounter = register(statsFactory.createStatsCounter(statsKey, TO_CORE_NOTIFICATIONS));
  69 + }
79 70
80 - counters.add(deviceStateCounter);  
81 - counters.add(subscriptionMsgCounter);  
82 - counters.add(toCoreNotificationsCounter); 71 + private StatsCounter register(StatsCounter counter){
  72 + counters.add(counter);
  73 + return counter;
83 } 74 }
84 75
85 public void log(TransportProtos.TransportToDeviceActorMsg msg) { 76 public void log(TransportProtos.TransportToDeviceActorMsg msg) {
@@ -147,4 +147,10 @@ public class TbMsgPackProcessingContext { @@ -147,4 +147,10 @@ public class TbMsgPackProcessingContext {
147 .forEach(info -> log.info("[{}][{}] execution count: {}. {}", queueName, info.getRuleNodeId(), info.getExecutionCount(), info.getLabel())); 147 .forEach(info -> log.info("[{}][{}] execution count: {}. {}", queueName, info.getRuleNodeId(), info.getExecutionCount(), info.getLabel()));
148 } 148 }
149 } 149 }
  150 +
  151 + public void cleanup() {
  152 + pendingMap.clear();
  153 + successMap.clear();
  154 + failedMap.clear();
  155 + }
150 } 156 }
@@ -36,6 +36,7 @@ import org.thingsboard.server.queue.TbQueueConsumer; @@ -36,6 +36,7 @@ import org.thingsboard.server.queue.TbQueueConsumer;
36 import org.thingsboard.server.queue.common.TbProtoQueueMsg; 36 import org.thingsboard.server.queue.common.TbProtoQueueMsg;
37 import org.thingsboard.server.queue.discovery.PartitionChangeEvent; 37 import org.thingsboard.server.queue.discovery.PartitionChangeEvent;
38 import org.thingsboard.server.common.transport.util.DataDecodingEncodingService; 38 import org.thingsboard.server.common.transport.util.DataDecodingEncodingService;
  39 +import org.thingsboard.server.queue.discovery.TbApplicationEventListener;
39 import org.thingsboard.server.service.apiusage.TbApiUsageStateService; 40 import org.thingsboard.server.service.apiusage.TbApiUsageStateService;
40 import org.thingsboard.server.service.profile.TbDeviceProfileCache; 41 import org.thingsboard.server.service.profile.TbDeviceProfileCache;
41 import org.thingsboard.server.dao.tenant.TbTenantProfileCache; 42 import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
@@ -56,7 +57,7 @@ import java.util.function.Function; @@ -56,7 +57,7 @@ import java.util.function.Function;
56 import java.util.stream.Collectors; 57 import java.util.stream.Collectors;
57 58
58 @Slf4j 59 @Slf4j
59 -public abstract class AbstractConsumerService<N extends com.google.protobuf.GeneratedMessageV3> implements ApplicationListener<PartitionChangeEvent> { 60 +public abstract class AbstractConsumerService<N extends com.google.protobuf.GeneratedMessageV3> extends TbApplicationEventListener<PartitionChangeEvent> {
60 61
61 protected volatile ExecutorService consumersExecutor; 62 protected volatile ExecutorService consumersExecutor;
62 protected volatile ExecutorService notificationsConsumerExecutor; 63 protected volatile ExecutorService notificationsConsumerExecutor;
@@ -31,6 +31,8 @@ import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest; @@ -31,6 +31,8 @@ import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest;
31 @RequiredArgsConstructor 31 @RequiredArgsConstructor
32 public class ToDeviceRpcRequestActorMsg implements ToDeviceActorNotificationMsg { 32 public class ToDeviceRpcRequestActorMsg implements ToDeviceActorNotificationMsg {
33 33
  34 + private static final long serialVersionUID = -8592877558138716589L;
  35 +
34 @Getter 36 @Getter
35 private final String serviceId; 37 private final String serviceId;
36 @Getter 38 @Getter
@@ -21,7 +21,6 @@ import com.google.common.util.concurrent.ListenableFuture; @@ -21,7 +21,6 @@ import com.google.common.util.concurrent.ListenableFuture;
21 import com.google.common.util.concurrent.MoreExecutors; 21 import com.google.common.util.concurrent.MoreExecutors;
22 import delight.nashornsandbox.NashornSandbox; 22 import delight.nashornsandbox.NashornSandbox;
23 import delight.nashornsandbox.NashornSandboxes; 23 import delight.nashornsandbox.NashornSandboxes;
24 -import jdk.nashorn.api.scripting.NashornScriptEngineFactory;  
25 import lombok.Getter; 24 import lombok.Getter;
26 import lombok.extern.slf4j.Slf4j; 25 import lombok.extern.slf4j.Slf4j;
27 import org.springframework.beans.factory.annotation.Value; 26 import org.springframework.beans.factory.annotation.Value;
@@ -33,6 +32,7 @@ import javax.annotation.PostConstruct; @@ -33,6 +32,7 @@ import javax.annotation.PostConstruct;
33 import javax.annotation.PreDestroy; 32 import javax.annotation.PreDestroy;
34 import javax.script.Invocable; 33 import javax.script.Invocable;
35 import javax.script.ScriptEngine; 34 import javax.script.ScriptEngine;
  35 +import javax.script.ScriptEngineManager;
36 import javax.script.ScriptException; 36 import javax.script.ScriptException;
37 import java.util.UUID; 37 import java.util.UUID;
38 import java.util.concurrent.ExecutionException; 38 import java.util.concurrent.ExecutionException;
@@ -97,8 +97,8 @@ public abstract class AbstractNashornJsInvokeService extends AbstractJsInvokeSer @@ -97,8 +97,8 @@ public abstract class AbstractNashornJsInvokeService extends AbstractJsInvokeSer
97 sandbox.allowLoadFunctions(true); 97 sandbox.allowLoadFunctions(true);
98 sandbox.setMaxPreparedStatements(30); 98 sandbox.setMaxPreparedStatements(30);
99 } else { 99 } else {
100 - NashornScriptEngineFactory factory = new NashornScriptEngineFactory();  
101 - engine = factory.getScriptEngine(new String[]{"--no-java"}); 100 + ScriptEngineManager factory = new ScriptEngineManager();
  101 + engine = factory.getEngineByName("nashorn");
102 } 102 }
103 } 103 }
104 104
@@ -29,7 +29,7 @@ public class SkipPathRequestMatcher implements RequestMatcher { @@ -29,7 +29,7 @@ public class SkipPathRequestMatcher implements RequestMatcher {
29 private RequestMatcher processingMatcher; 29 private RequestMatcher processingMatcher;
30 30
31 public SkipPathRequestMatcher(List<String> pathsToSkip, String processingPath) { 31 public SkipPathRequestMatcher(List<String> pathsToSkip, String processingPath) {
32 - Assert.notNull(pathsToSkip); 32 + Assert.notNull(pathsToSkip, "List of paths to skip is required.");
33 List<RequestMatcher> m = pathsToSkip.stream().map(path -> new AntPathRequestMatcher(path)).collect(Collectors.toList()); 33 List<RequestMatcher> m = pathsToSkip.stream().map(path -> new AntPathRequestMatcher(path)).collect(Collectors.toList());
34 matchers = new OrRequestMatcher(m); 34 matchers = new OrRequestMatcher(m);
35 processingMatcher = new AntPathRequestMatcher(processingPath); 35 processingMatcher = new AntPathRequestMatcher(processingPath);
  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.security.auth.oauth2;
  17 +
  18 +import org.springframework.util.SerializationUtils;
  19 +import javax.servlet.http.Cookie;
  20 +import javax.servlet.http.HttpServletRequest;
  21 +import javax.servlet.http.HttpServletResponse;
  22 +import java.util.Base64;
  23 +import java.util.Optional;
  24 +
  25 +public class CookieUtils {
  26 +
  27 + public static Optional<Cookie> getCookie(HttpServletRequest request, String name) {
  28 + Cookie[] cookies = request.getCookies();
  29 +
  30 + if (cookies != null && cookies.length > 0) {
  31 + for (Cookie cookie : cookies) {
  32 + if (cookie.getName().equals(name)) {
  33 + return Optional.of(cookie);
  34 + }
  35 + }
  36 + }
  37 +
  38 + return Optional.empty();
  39 + }
  40 +
  41 + public static void addCookie(HttpServletResponse response, String name, String value, int maxAge) {
  42 + Cookie cookie = new Cookie(name, value);
  43 + cookie.setPath("/");
  44 + cookie.setHttpOnly(true);
  45 + cookie.setMaxAge(maxAge);
  46 + response.addCookie(cookie);
  47 + }
  48 +
  49 + public static void deleteCookie(HttpServletRequest request, HttpServletResponse response, String name) {
  50 + Cookie[] cookies = request.getCookies();
  51 + if (cookies != null && cookies.length > 0) {
  52 + for (Cookie cookie: cookies) {
  53 + if (cookie.getName().equals(name)) {
  54 + cookie.setValue("");
  55 + cookie.setPath("/");
  56 + cookie.setMaxAge(0);
  57 + response.addCookie(cookie);
  58 + }
  59 + }
  60 + }
  61 + }
  62 +
  63 + public static String serialize(Object object) {
  64 + return Base64.getUrlEncoder()
  65 + .encodeToString(SerializationUtils.serialize(object));
  66 + }
  67 +
  68 + public static <T> T deserialize(Cookie cookie, Class<T> cls) {
  69 + return cls.cast(SerializationUtils.deserialize(
  70 + Base64.getUrlDecoder().decode(cookie.getValue())));
  71 + }
  72 +}
  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.security.auth.oauth2;
  17 +
  18 +import org.springframework.security.oauth2.client.web.AuthorizationRequestRepository;
  19 +import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
  20 +import org.springframework.stereotype.Component;
  21 +
  22 +import javax.servlet.http.HttpServletRequest;
  23 +import javax.servlet.http.HttpServletResponse;
  24 +
  25 +@Component
  26 +public class HttpCookieOAuth2AuthorizationRequestRepository implements AuthorizationRequestRepository<OAuth2AuthorizationRequest> {
  27 + public static final String OAUTH2_AUTHORIZATION_REQUEST_COOKIE_NAME = "oauth2_auth_request";
  28 + private static final int cookieExpireSeconds = 180;
  29 +
  30 + @Override
  31 + public OAuth2AuthorizationRequest loadAuthorizationRequest(HttpServletRequest request) {
  32 + return CookieUtils.getCookie(request, OAUTH2_AUTHORIZATION_REQUEST_COOKIE_NAME)
  33 + .map(cookie -> CookieUtils.deserialize(cookie, OAuth2AuthorizationRequest.class))
  34 + .orElse(null);
  35 + }
  36 +
  37 + @Override
  38 + public void saveAuthorizationRequest(OAuth2AuthorizationRequest authorizationRequest, HttpServletRequest request, HttpServletResponse response) {
  39 + if (authorizationRequest == null) {
  40 + CookieUtils.deleteCookie(request, response, OAUTH2_AUTHORIZATION_REQUEST_COOKIE_NAME);
  41 + return;
  42 + }
  43 + CookieUtils.addCookie(response, OAUTH2_AUTHORIZATION_REQUEST_COOKIE_NAME, CookieUtils.serialize(authorizationRequest), cookieExpireSeconds);
  44 + }
  45 +
  46 + @SuppressWarnings("deprecation")
  47 + @Override
  48 + public OAuth2AuthorizationRequest removeAuthorizationRequest(HttpServletRequest request) {
  49 + return this.loadAuthorizationRequest(request);
  50 + }
  51 +
  52 + public void removeAuthorizationRequestCookies(HttpServletRequest request, HttpServletResponse response) {
  53 + CookieUtils.deleteCookie(request, response, OAUTH2_AUTHORIZATION_REQUEST_COOKIE_NAME);
  54 + }
  55 +}
@@ -37,10 +37,13 @@ import java.nio.charset.StandardCharsets; @@ -37,10 +37,13 @@ import java.nio.charset.StandardCharsets;
37 @ConditionalOnProperty(prefix = "security.oauth2", value = "enabled", havingValue = "true") 37 @ConditionalOnProperty(prefix = "security.oauth2", value = "enabled", havingValue = "true")
38 public class Oauth2AuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler { 38 public class Oauth2AuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
39 39
  40 + private final HttpCookieOAuth2AuthorizationRequestRepository httpCookieOAuth2AuthorizationRequestRepository;
40 private final SystemSecurityService systemSecurityService; 41 private final SystemSecurityService systemSecurityService;
41 42
42 @Autowired 43 @Autowired
43 - public Oauth2AuthenticationFailureHandler(final SystemSecurityService systemSecurityService) { 44 + public Oauth2AuthenticationFailureHandler(final HttpCookieOAuth2AuthorizationRequestRepository httpCookieOAuth2AuthorizationRequestRepository,
  45 + final SystemSecurityService systemSecurityService) {
  46 + this.httpCookieOAuth2AuthorizationRequestRepository = httpCookieOAuth2AuthorizationRequestRepository;
44 this.systemSecurityService = systemSecurityService; 47 this.systemSecurityService = systemSecurityService;
45 } 48 }
46 49
@@ -49,6 +52,7 @@ public class Oauth2AuthenticationFailureHandler extends SimpleUrlAuthenticationF @@ -49,6 +52,7 @@ public class Oauth2AuthenticationFailureHandler extends SimpleUrlAuthenticationF
49 HttpServletResponse response, AuthenticationException exception) 52 HttpServletResponse response, AuthenticationException exception)
50 throws IOException, ServletException { 53 throws IOException, ServletException {
51 String baseUrl = this.systemSecurityService.getBaseUrl(TenantId.SYS_TENANT_ID, new CustomerId(EntityId.NULL_UUID), request); 54 String baseUrl = this.systemSecurityService.getBaseUrl(TenantId.SYS_TENANT_ID, new CustomerId(EntityId.NULL_UUID), request);
  55 + httpCookieOAuth2AuthorizationRequestRepository.removeAuthorizationRequestCookies(request, response);
52 getRedirectStrategy().sendRedirect(request, response, baseUrl + "/login?loginError=" + 56 getRedirectStrategy().sendRedirect(request, response, baseUrl + "/login?loginError=" +
53 URLEncoder.encode(exception.getMessage(), StandardCharsets.UTF_8.toString())); 57 URLEncoder.encode(exception.getMessage(), StandardCharsets.UTF_8.toString()));
54 } 58 }
@@ -49,6 +49,7 @@ public class Oauth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationS @@ -49,6 +49,7 @@ public class Oauth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationS
49 private final OAuth2ClientMapperProvider oauth2ClientMapperProvider; 49 private final OAuth2ClientMapperProvider oauth2ClientMapperProvider;
50 private final OAuth2Service oAuth2Service; 50 private final OAuth2Service oAuth2Service;
51 private final OAuth2AuthorizedClientService oAuth2AuthorizedClientService; 51 private final OAuth2AuthorizedClientService oAuth2AuthorizedClientService;
  52 + private final HttpCookieOAuth2AuthorizationRequestRepository httpCookieOAuth2AuthorizationRequestRepository;
52 private final SystemSecurityService systemSecurityService; 53 private final SystemSecurityService systemSecurityService;
53 54
54 @Autowired 55 @Autowired
@@ -56,12 +57,15 @@ public class Oauth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationS @@ -56,12 +57,15 @@ public class Oauth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationS
56 final RefreshTokenRepository refreshTokenRepository, 57 final RefreshTokenRepository refreshTokenRepository,
57 final OAuth2ClientMapperProvider oauth2ClientMapperProvider, 58 final OAuth2ClientMapperProvider oauth2ClientMapperProvider,
58 final OAuth2Service oAuth2Service, 59 final OAuth2Service oAuth2Service,
59 - final OAuth2AuthorizedClientService oAuth2AuthorizedClientService, final SystemSecurityService systemSecurityService) { 60 + final OAuth2AuthorizedClientService oAuth2AuthorizedClientService,
  61 + final HttpCookieOAuth2AuthorizationRequestRepository httpCookieOAuth2AuthorizationRequestRepository,
  62 + final SystemSecurityService systemSecurityService) {
60 this.tokenFactory = tokenFactory; 63 this.tokenFactory = tokenFactory;
61 this.refreshTokenRepository = refreshTokenRepository; 64 this.refreshTokenRepository = refreshTokenRepository;
62 this.oauth2ClientMapperProvider = oauth2ClientMapperProvider; 65 this.oauth2ClientMapperProvider = oauth2ClientMapperProvider;
63 this.oAuth2Service = oAuth2Service; 66 this.oAuth2Service = oAuth2Service;
64 this.oAuth2AuthorizedClientService = oAuth2AuthorizedClientService; 67 this.oAuth2AuthorizedClientService = oAuth2AuthorizedClientService;
  68 + this.httpCookieOAuth2AuthorizationRequestRepository = httpCookieOAuth2AuthorizationRequestRepository;
65 this.systemSecurityService = systemSecurityService; 69 this.systemSecurityService = systemSecurityService;
66 } 70 }
67 71
@@ -84,10 +88,17 @@ public class Oauth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationS @@ -84,10 +88,17 @@ public class Oauth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationS
84 JwtToken accessToken = tokenFactory.createAccessJwtToken(securityUser); 88 JwtToken accessToken = tokenFactory.createAccessJwtToken(securityUser);
85 JwtToken refreshToken = refreshTokenRepository.requestRefreshToken(securityUser); 89 JwtToken refreshToken = refreshTokenRepository.requestRefreshToken(securityUser);
86 90
  91 + clearAuthenticationAttributes(request, response);
87 getRedirectStrategy().sendRedirect(request, response, baseUrl + "/?accessToken=" + accessToken.getToken() + "&refreshToken=" + refreshToken.getToken()); 92 getRedirectStrategy().sendRedirect(request, response, baseUrl + "/?accessToken=" + accessToken.getToken() + "&refreshToken=" + refreshToken.getToken());
88 } catch (Exception e) { 93 } catch (Exception e) {
  94 + clearAuthenticationAttributes(request, response);
89 getRedirectStrategy().sendRedirect(request, response, baseUrl + "/login?loginError=" + 95 getRedirectStrategy().sendRedirect(request, response, baseUrl + "/login?loginError=" +
90 URLEncoder.encode(e.getMessage(), StandardCharsets.UTF_8.toString())); 96 URLEncoder.encode(e.getMessage(), StandardCharsets.UTF_8.toString()));
91 } 97 }
92 } 98 }
  99 +
  100 + protected void clearAuthenticationAttributes(HttpServletRequest request, HttpServletResponse response) {
  101 + super.clearAuthenticationAttributes(request);
  102 + httpCookieOAuth2AuthorizationRequestRepository.removeAuthorizationRequestCookies(request, response);
  103 + }
93 } 104 }
@@ -53,6 +53,8 @@ public class DefaultDeviceAuthService implements DeviceAuthService { @@ -53,6 +53,8 @@ public class DefaultDeviceAuthService implements DeviceAuthService {
53 return DeviceAuthResult.of(credentials.getDeviceId()); 53 return DeviceAuthResult.of(credentials.getDeviceId());
54 case X509_CERTIFICATE: 54 case X509_CERTIFICATE:
55 return DeviceAuthResult.of(credentials.getDeviceId()); 55 return DeviceAuthResult.of(credentials.getDeviceId());
  56 + case LWM2M_CREDENTIALS:
  57 + return DeviceAuthResult.of(credentials.getDeviceId());
56 default: 58 default:
57 return DeviceAuthResult.of("Credentials Type is not supported yet!"); 59 return DeviceAuthResult.of("Credentials Type is not supported yet!");
58 } 60 }
@@ -65,4 +67,4 @@ public class DefaultDeviceAuthService implements DeviceAuthService { @@ -65,4 +67,4 @@ public class DefaultDeviceAuthService implements DeviceAuthService {
65 } 67 }
66 } 68 }
67 69
68 -}  
  70 +}
@@ -100,6 +100,7 @@ public class JwtTokenFactory { @@ -100,6 +100,7 @@ public class JwtTokenFactory {
100 Jws<Claims> jwsClaims = rawAccessToken.parseClaims(settings.getTokenSigningKey()); 100 Jws<Claims> jwsClaims = rawAccessToken.parseClaims(settings.getTokenSigningKey());
101 Claims claims = jwsClaims.getBody(); 101 Claims claims = jwsClaims.getBody();
102 String subject = claims.getSubject(); 102 String subject = claims.getSubject();
  103 + @SuppressWarnings("unchecked")
103 List<String> scopes = claims.get(SCOPES, List.class); 104 List<String> scopes = claims.get(SCOPES, List.class);
104 if (scopes == null || scopes.isEmpty()) { 105 if (scopes == null || scopes.isEmpty()) {
105 throw new IllegalArgumentException("JWT Token doesn't have any scopes"); 106 throw new IllegalArgumentException("JWT Token doesn't have any scopes");
@@ -155,6 +156,7 @@ public class JwtTokenFactory { @@ -155,6 +156,7 @@ public class JwtTokenFactory {
155 Jws<Claims> jwsClaims = rawAccessToken.parseClaims(settings.getTokenSigningKey()); 156 Jws<Claims> jwsClaims = rawAccessToken.parseClaims(settings.getTokenSigningKey());
156 Claims claims = jwsClaims.getBody(); 157 Claims claims = jwsClaims.getBody();
157 String subject = claims.getSubject(); 158 String subject = claims.getSubject();
  159 + @SuppressWarnings("unchecked")
158 List<String> scopes = claims.get(SCOPES, List.class); 160 List<String> scopes = claims.get(SCOPES, List.class);
159 if (scopes == null || scopes.isEmpty()) { 161 if (scopes == null || scopes.isEmpty()) {
160 throw new IllegalArgumentException("Refresh Token doesn't have any scopes"); 162 throw new IllegalArgumentException("Refresh Token doesn't have any scopes");
@@ -47,6 +47,7 @@ public class CustomerUserPermissions extends AbstractPermissions { @@ -47,6 +47,7 @@ public class CustomerUserPermissions extends AbstractPermissions {
47 Operation.READ_ATTRIBUTES, Operation.READ_TELEMETRY, Operation.RPC_CALL, Operation.CLAIM_DEVICES) { 47 Operation.READ_ATTRIBUTES, Operation.READ_TELEMETRY, Operation.RPC_CALL, Operation.CLAIM_DEVICES) {
48 48
49 @Override 49 @Override
  50 + @SuppressWarnings("unchecked")
50 public boolean hasPermission(SecurityUser user, Operation operation, EntityId entityId, HasTenantId entity) { 51 public boolean hasPermission(SecurityUser user, Operation operation, EntityId entityId, HasTenantId entity) {
51 52
52 if (!super.hasPermission(user, operation, entityId, entity)) { 53 if (!super.hasPermission(user, operation, entityId, entity)) {
@@ -69,6 +70,7 @@ public class CustomerUserPermissions extends AbstractPermissions { @@ -69,6 +70,7 @@ public class CustomerUserPermissions extends AbstractPermissions {
69 new PermissionChecker.GenericPermissionChecker(Operation.READ, Operation.READ_ATTRIBUTES, Operation.READ_TELEMETRY) { 70 new PermissionChecker.GenericPermissionChecker(Operation.READ, Operation.READ_ATTRIBUTES, Operation.READ_TELEMETRY) {
70 71
71 @Override 72 @Override
  73 + @SuppressWarnings("unchecked")
72 public boolean hasPermission(SecurityUser user, Operation operation, EntityId entityId, HasTenantId entity) { 74 public boolean hasPermission(SecurityUser user, Operation operation, EntityId entityId, HasTenantId entity) {
73 if (!super.hasPermission(user, operation, entityId, entity)) { 75 if (!super.hasPermission(user, operation, entityId, entity)) {
74 return false; 76 return false;
@@ -119,6 +121,7 @@ public class CustomerUserPermissions extends AbstractPermissions { @@ -119,6 +121,7 @@ public class CustomerUserPermissions extends AbstractPermissions {
119 private static final PermissionChecker widgetsPermissionChecker = new PermissionChecker.GenericPermissionChecker(Operation.READ) { 121 private static final PermissionChecker widgetsPermissionChecker = new PermissionChecker.GenericPermissionChecker(Operation.READ) {
120 122
121 @Override 123 @Override
  124 + @SuppressWarnings("unchecked")
122 public boolean hasPermission(SecurityUser user, Operation operation, EntityId entityId, HasTenantId entity) { 125 public boolean hasPermission(SecurityUser user, Operation operation, EntityId entityId, HasTenantId entity) {
123 if (!super.hasPermission(user, operation, entityId, entity)) { 126 if (!super.hasPermission(user, operation, entityId, entity)) {
124 return false; 127 return false;
@@ -56,6 +56,7 @@ public class DefaultAccessControlService implements AccessControlService { @@ -56,6 +56,7 @@ public class DefaultAccessControlService implements AccessControlService {
56 } 56 }
57 57
58 @Override 58 @Override
  59 + @SuppressWarnings("unchecked")
59 public <I extends EntityId, T extends HasTenantId> void checkPermission(SecurityUser user, Resource resource, 60 public <I extends EntityId, T extends HasTenantId> void checkPermission(SecurityUser user, Resource resource,
60 Operation operation, I entityId, T entity) throws ThingsboardException { 61 Operation operation, I entityId, T entity) throws ThingsboardException {
61 PermissionChecker permissionChecker = getPermissionChecker(user.getAuthority(), resource); 62 PermissionChecker permissionChecker = getPermissionChecker(user.getAuthority(), resource);
@@ -59,6 +59,7 @@ public class TenantAdminPermissions extends AbstractPermissions { @@ -59,6 +59,7 @@ public class TenantAdminPermissions extends AbstractPermissions {
59 new PermissionChecker.GenericPermissionChecker(Operation.READ, Operation.READ_ATTRIBUTES, Operation.READ_TELEMETRY) { 59 new PermissionChecker.GenericPermissionChecker(Operation.READ, Operation.READ_ATTRIBUTES, Operation.READ_TELEMETRY) {
60 60
61 @Override 61 @Override
  62 + @SuppressWarnings("unchecked")
62 public boolean hasPermission(SecurityUser user, Operation operation, EntityId entityId, HasTenantId entity) { 63 public boolean hasPermission(SecurityUser user, Operation operation, EntityId entityId, HasTenantId entity) {
63 if (!super.hasPermission(user, operation, entityId, entity)) { 64 if (!super.hasPermission(user, operation, entityId, entity)) {
64 return false; 65 return false;
@@ -15,8 +15,8 @@ @@ -15,8 +15,8 @@
15 */ 15 */
16 package org.thingsboard.server.service.security.system; 16 package org.thingsboard.server.service.security.system;
17 17
  18 +import com.fasterxml.jackson.core.type.TypeReference;
18 import com.fasterxml.jackson.databind.JsonNode; 19 import com.fasterxml.jackson.databind.JsonNode;
19 -import com.fasterxml.jackson.databind.ObjectMapper;  
20 import com.fasterxml.jackson.databind.node.ObjectNode; 20 import com.fasterxml.jackson.databind.node.ObjectNode;
21 import lombok.extern.slf4j.Slf4j; 21 import lombok.extern.slf4j.Slf4j;
22 import org.apache.commons.lang3.StringUtils; 22 import org.apache.commons.lang3.StringUtils;
@@ -49,6 +49,7 @@ import org.thingsboard.server.dao.exception.DataValidationException; @@ -49,6 +49,7 @@ import org.thingsboard.server.dao.exception.DataValidationException;
49 import org.thingsboard.server.dao.settings.AdminSettingsService; 49 import org.thingsboard.server.dao.settings.AdminSettingsService;
50 import org.thingsboard.server.dao.user.UserService; 50 import org.thingsboard.server.dao.user.UserService;
51 import org.thingsboard.server.dao.user.UserServiceImpl; 51 import org.thingsboard.server.dao.user.UserServiceImpl;
  52 +import org.thingsboard.common.util.JacksonUtil;
52 import org.thingsboard.server.service.security.exception.UserPasswordExpiredException; 53 import org.thingsboard.server.service.security.exception.UserPasswordExpiredException;
53 import org.thingsboard.server.utils.MiscUtils; 54 import org.thingsboard.server.utils.MiscUtils;
54 55
@@ -65,8 +66,6 @@ import static org.thingsboard.server.common.data.CacheConstants.SECURITY_SETTING @@ -65,8 +66,6 @@ import static org.thingsboard.server.common.data.CacheConstants.SECURITY_SETTING
65 @Slf4j 66 @Slf4j
66 public class DefaultSystemSecurityService implements SystemSecurityService { 67 public class DefaultSystemSecurityService implements SystemSecurityService {
67 68
68 - private static final ObjectMapper objectMapper = new ObjectMapper();  
69 -  
70 @Autowired 69 @Autowired
71 private AdminSettingsService adminSettingsService; 70 private AdminSettingsService adminSettingsService;
72 71
@@ -89,7 +88,7 @@ public class DefaultSystemSecurityService implements SystemSecurityService { @@ -89,7 +88,7 @@ public class DefaultSystemSecurityService implements SystemSecurityService {
89 AdminSettings adminSettings = adminSettingsService.findAdminSettingsByKey(tenantId, "securitySettings"); 88 AdminSettings adminSettings = adminSettingsService.findAdminSettingsByKey(tenantId, "securitySettings");
90 if (adminSettings != null) { 89 if (adminSettings != null) {
91 try { 90 try {
92 - securitySettings = objectMapper.treeToValue(adminSettings.getJsonValue(), SecuritySettings.class); 91 + securitySettings = JacksonUtil.convertValue(adminSettings.getJsonValue(), SecuritySettings.class);
93 } catch (Exception e) { 92 } catch (Exception e) {
94 throw new RuntimeException("Failed to load security settings!", e); 93 throw new RuntimeException("Failed to load security settings!", e);
95 } 94 }
@@ -109,10 +108,10 @@ public class DefaultSystemSecurityService implements SystemSecurityService { @@ -109,10 +108,10 @@ public class DefaultSystemSecurityService implements SystemSecurityService {
109 adminSettings = new AdminSettings(); 108 adminSettings = new AdminSettings();
110 adminSettings.setKey("securitySettings"); 109 adminSettings.setKey("securitySettings");
111 } 110 }
112 - adminSettings.setJsonValue(objectMapper.valueToTree(securitySettings)); 111 + adminSettings.setJsonValue(JacksonUtil.valueToTree(securitySettings));
113 AdminSettings savedAdminSettings = adminSettingsService.saveAdminSettings(tenantId, adminSettings); 112 AdminSettings savedAdminSettings = adminSettingsService.saveAdminSettings(tenantId, adminSettings);
114 try { 113 try {
115 - return objectMapper.treeToValue(savedAdminSettings.getJsonValue(), SecuritySettings.class); 114 + return JacksonUtil.convertValue(savedAdminSettings.getJsonValue(), SecuritySettings.class);
116 } catch (Exception e) { 115 } catch (Exception e) {
117 throw new RuntimeException("Failed to load security settings!", e); 116 throw new RuntimeException("Failed to load security settings!", e);
118 } 117 }
@@ -189,7 +188,7 @@ public class DefaultSystemSecurityService implements SystemSecurityService { @@ -189,7 +188,7 @@ public class DefaultSystemSecurityService implements SystemSecurityService {
189 JsonNode additionalInfo = user.getAdditionalInfo(); 188 JsonNode additionalInfo = user.getAdditionalInfo();
190 if (additionalInfo instanceof ObjectNode && additionalInfo.has(UserServiceImpl.USER_PASSWORD_HISTORY)) { 189 if (additionalInfo instanceof ObjectNode && additionalInfo.has(UserServiceImpl.USER_PASSWORD_HISTORY)) {
191 JsonNode userPasswordHistoryJson = additionalInfo.get(UserServiceImpl.USER_PASSWORD_HISTORY); 190 JsonNode userPasswordHistoryJson = additionalInfo.get(UserServiceImpl.USER_PASSWORD_HISTORY);
192 - Map<String, String> userPasswordHistoryMap = objectMapper.convertValue(userPasswordHistoryJson, Map.class); 191 + Map<String, String> userPasswordHistoryMap = JacksonUtil.convertValue(userPasswordHistoryJson, new TypeReference<>() {});
193 for (Map.Entry<String, String> entry : userPasswordHistoryMap.entrySet()) { 192 for (Map.Entry<String, String> entry : userPasswordHistoryMap.entrySet()) {
194 if (encoder.matches(password, entry.getValue()) && Long.parseLong(entry.getKey()) > passwordReuseFrequencyTs) { 193 if (encoder.matches(password, entry.getValue()) && Long.parseLong(entry.getKey()) > passwordReuseFrequencyTs) {
195 throw new DataValidationException("Password was already used for the last " + passwordPolicy.getPasswordReuseFrequencyDays() + " days"); 194 throw new DataValidationException("Password was already used for the last " + passwordPolicy.getPasswordReuseFrequencyDays() + " days");
@@ -24,7 +24,7 @@ import java.util.regex.Pattern; @@ -24,7 +24,7 @@ import java.util.regex.Pattern;
24 @Slf4j 24 @Slf4j
25 public abstract class AbstractSmsSender implements SmsSender { 25 public abstract class AbstractSmsSender implements SmsSender {
26 26
27 - private static final Pattern E_164_PHONE_NUMBER_PATTERN = Pattern.compile("^\\+[1-9]\\d{1,14}$"); 27 + protected static final Pattern E_164_PHONE_NUMBER_PATTERN = Pattern.compile("^\\+[1-9]\\d{1,14}$");
28 28
29 private static final int MAX_SMS_MESSAGE_LENGTH = 1600; 29 private static final int MAX_SMS_MESSAGE_LENGTH = 1600;
30 private static final int MAX_SMS_SEGMENT_LENGTH = 70; 30 private static final int MAX_SMS_SEGMENT_LENGTH = 70;
@@ -31,7 +31,7 @@ import org.thingsboard.server.common.data.exception.ThingsboardException; @@ -31,7 +31,7 @@ import org.thingsboard.server.common.data.exception.ThingsboardException;
31 import org.thingsboard.server.common.data.id.EntityId; 31 import org.thingsboard.server.common.data.id.EntityId;
32 import org.thingsboard.server.common.data.id.TenantId; 32 import org.thingsboard.server.common.data.id.TenantId;
33 import org.thingsboard.server.dao.settings.AdminSettingsService; 33 import org.thingsboard.server.dao.settings.AdminSettingsService;
34 -import org.thingsboard.server.dao.util.mapping.JacksonUtil; 34 +import org.thingsboard.common.util.JacksonUtil;
35 import org.thingsboard.server.queue.usagestats.TbApiUsageClient; 35 import org.thingsboard.server.queue.usagestats.TbApiUsageClient;
36 import org.thingsboard.server.service.apiusage.TbApiUsageStateService; 36 import org.thingsboard.server.service.apiusage.TbApiUsageStateService;
37 37
@@ -19,21 +19,34 @@ import com.twilio.http.TwilioRestClient; @@ -19,21 +19,34 @@ import com.twilio.http.TwilioRestClient;
19 import com.twilio.rest.api.v2010.account.Message; 19 import com.twilio.rest.api.v2010.account.Message;
20 import com.twilio.type.PhoneNumber; 20 import com.twilio.type.PhoneNumber;
21 import org.apache.commons.lang3.StringUtils; 21 import org.apache.commons.lang3.StringUtils;
  22 +import org.thingsboard.rule.engine.api.sms.exception.SmsParseException;
22 import org.thingsboard.server.common.data.sms.config.TwilioSmsProviderConfiguration; 23 import org.thingsboard.server.common.data.sms.config.TwilioSmsProviderConfiguration;
23 import org.thingsboard.rule.engine.api.sms.exception.SmsException; 24 import org.thingsboard.rule.engine.api.sms.exception.SmsException;
24 import org.thingsboard.rule.engine.api.sms.exception.SmsSendException; 25 import org.thingsboard.rule.engine.api.sms.exception.SmsSendException;
25 import org.thingsboard.server.service.sms.AbstractSmsSender; 26 import org.thingsboard.server.service.sms.AbstractSmsSender;
26 27
  28 +import java.util.regex.Pattern;
  29 +
27 public class TwilioSmsSender extends AbstractSmsSender { 30 public class TwilioSmsSender extends AbstractSmsSender {
28 31
  32 + private static final Pattern PHONE_NUMBERS_SID_MESSAGE_SERVICE_SID = Pattern.compile("^(PN|MG).*$");
  33 +
29 private TwilioRestClient twilioRestClient; 34 private TwilioRestClient twilioRestClient;
30 private String numberFrom; 35 private String numberFrom;
31 36
  37 + private String validatePhoneTwilioNumber(String phoneNumber) throws SmsParseException {
  38 + phoneNumber = phoneNumber.trim();
  39 + if (!E_164_PHONE_NUMBER_PATTERN.matcher(phoneNumber).matches() && !PHONE_NUMBERS_SID_MESSAGE_SERVICE_SID.matcher(phoneNumber).matches()) {
  40 + throw new SmsParseException("Invalid phone number format. Phone number must be in E.164 format/Phone Number's SID/Messaging Service SID.");
  41 + }
  42 + return phoneNumber;
  43 + }
  44 +
32 public TwilioSmsSender(TwilioSmsProviderConfiguration config) { 45 public TwilioSmsSender(TwilioSmsProviderConfiguration config) {
33 if (StringUtils.isEmpty(config.getAccountSid()) || StringUtils.isEmpty(config.getAccountToken()) || StringUtils.isEmpty(config.getNumberFrom())) { 46 if (StringUtils.isEmpty(config.getAccountSid()) || StringUtils.isEmpty(config.getAccountToken()) || StringUtils.isEmpty(config.getNumberFrom())) {
34 throw new IllegalArgumentException("Invalid twilio sms provider configuration: accountSid, accountToken and numberFrom should be specified!"); 47 throw new IllegalArgumentException("Invalid twilio sms provider configuration: accountSid, accountToken and numberFrom should be specified!");
35 } 48 }
36 - this.numberFrom = this.validatePhoneNumber(config.getNumberFrom()); 49 + this.numberFrom = this.validatePhoneTwilioNumber(config.getNumberFrom());
37 this.twilioRestClient = new TwilioRestClient.Builder(config.getAccountSid(), config.getAccountToken()).build(); 50 this.twilioRestClient = new TwilioRestClient.Builder(config.getAccountSid(), config.getAccountToken()).build();
38 } 51 }
39 52
@@ -52,13 +52,15 @@ import org.thingsboard.server.dao.attributes.AttributesService; @@ -52,13 +52,15 @@ import org.thingsboard.server.dao.attributes.AttributesService;
52 import org.thingsboard.server.dao.device.DeviceService; 52 import org.thingsboard.server.dao.device.DeviceService;
53 import org.thingsboard.server.dao.tenant.TenantService; 53 import org.thingsboard.server.dao.tenant.TenantService;
54 import org.thingsboard.server.dao.timeseries.TimeseriesService; 54 import org.thingsboard.server.dao.timeseries.TimeseriesService;
55 -import org.thingsboard.server.dao.util.mapping.JacksonUtil; 55 +import org.thingsboard.common.util.JacksonUtil;
56 import org.thingsboard.server.gen.transport.TransportProtos; 56 import org.thingsboard.server.gen.transport.TransportProtos;
57 import org.thingsboard.server.queue.discovery.PartitionChangeEvent; 57 import org.thingsboard.server.queue.discovery.PartitionChangeEvent;
58 import org.thingsboard.server.queue.discovery.PartitionService; 58 import org.thingsboard.server.queue.discovery.PartitionService;
  59 +import org.thingsboard.server.queue.discovery.TbApplicationEventListener;
59 import org.thingsboard.server.queue.util.TbCoreComponent; 60 import org.thingsboard.server.queue.util.TbCoreComponent;
60 import org.thingsboard.server.service.queue.TbClusterService; 61 import org.thingsboard.server.service.queue.TbClusterService;
61 import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService; 62 import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService;
  63 +import org.thingsboard.server.utils.EventDeduplicationExecutor;
62 64
63 import javax.annotation.Nullable; 65 import javax.annotation.Nullable;
64 import javax.annotation.PostConstruct; 66 import javax.annotation.PostConstruct;
@@ -89,7 +91,7 @@ import static org.thingsboard.server.common.data.DataConstants.SERVER_SCOPE; @@ -89,7 +91,7 @@ import static org.thingsboard.server.common.data.DataConstants.SERVER_SCOPE;
89 @Service 91 @Service
90 @TbCoreComponent 92 @TbCoreComponent
91 @Slf4j 93 @Slf4j
92 -public class DefaultDeviceStateService implements DeviceStateService { 94 +public class DefaultDeviceStateService extends TbApplicationEventListener<PartitionChangeEvent> implements DeviceStateService {
93 95
94 public static final String ACTIVITY_STATE = "active"; 96 public static final String ACTIVITY_STATE = "active";
95 public static final String LAST_CONNECT_TIME = "lastConnectTime"; 97 public static final String LAST_CONNECT_TIME = "lastConnectTime";
@@ -126,13 +128,13 @@ public class DefaultDeviceStateService implements DeviceStateService { @@ -126,13 +128,13 @@ public class DefaultDeviceStateService implements DeviceStateService {
126 @Getter 128 @Getter
127 private int initFetchPackSize; 129 private int initFetchPackSize;
128 130
129 - private volatile boolean clusterUpdatePending = false;  
130 -  
131 private ListeningScheduledExecutorService queueExecutor; 131 private ListeningScheduledExecutorService queueExecutor;
132 private final ConcurrentMap<TopicPartitionInfo, Set<DeviceId>> partitionedDevices = new ConcurrentHashMap<>(); 132 private final ConcurrentMap<TopicPartitionInfo, Set<DeviceId>> partitionedDevices = new ConcurrentHashMap<>();
133 private final ConcurrentMap<DeviceId, DeviceStateData> deviceStates = new ConcurrentHashMap<>(); 133 private final ConcurrentMap<DeviceId, DeviceStateData> deviceStates = new ConcurrentHashMap<>();
134 private final ConcurrentMap<DeviceId, Long> deviceLastReportedActivity = new ConcurrentHashMap<>(); 134 private final ConcurrentMap<DeviceId, Long> deviceLastReportedActivity = new ConcurrentHashMap<>();
135 private final ConcurrentMap<DeviceId, Long> deviceLastSavedActivity = new ConcurrentHashMap<>(); 135 private final ConcurrentMap<DeviceId, Long> deviceLastSavedActivity = new ConcurrentHashMap<>();
  136 + private volatile EventDeduplicationExecutor<Set<TopicPartitionInfo>> deduplicationExecutor;
  137 +
136 138
137 public DefaultDeviceStateService(TenantService tenantService, DeviceService deviceService, 139 public DefaultDeviceStateService(TenantService tenantService, DeviceService deviceService,
138 AttributesService attributesService, TimeseriesService tsService, 140 AttributesService attributesService, TimeseriesService tsService,
@@ -155,6 +157,7 @@ public class DefaultDeviceStateService implements DeviceStateService { @@ -155,6 +157,7 @@ public class DefaultDeviceStateService implements DeviceStateService {
155 // Should be always single threaded due to absence of locks. 157 // Should be always single threaded due to absence of locks.
156 queueExecutor = MoreExecutors.listeningDecorator(Executors.newSingleThreadScheduledExecutor(ThingsBoardThreadFactory.forName("device-state"))); 158 queueExecutor = MoreExecutors.listeningDecorator(Executors.newSingleThreadScheduledExecutor(ThingsBoardThreadFactory.forName("device-state")));
157 queueExecutor.scheduleAtFixedRate(this::updateState, new Random().nextInt(defaultStateCheckIntervalInSec), defaultStateCheckIntervalInSec, TimeUnit.SECONDS); 159 queueExecutor.scheduleAtFixedRate(this::updateState, new Random().nextInt(defaultStateCheckIntervalInSec), defaultStateCheckIntervalInSec, TimeUnit.SECONDS);
  160 + deduplicationExecutor = new EventDeduplicationExecutor<>(DefaultDeviceStateService.class.getSimpleName(), queueExecutor, this::initStateFromDB);
158 } 161 }
159 162
160 @PreDestroy 163 @PreDestroy
@@ -204,7 +207,6 @@ public class DefaultDeviceStateService implements DeviceStateService { @@ -204,7 +207,6 @@ public class DefaultDeviceStateService implements DeviceStateService {
204 if (!state.isActive()) { 207 if (!state.isActive()) {
205 state.setActive(true); 208 state.setActive(true);
206 save(deviceId, ACTIVITY_STATE, state.isActive()); 209 save(deviceId, ACTIVITY_STATE, state.isActive());
207 - stateData.getMetaData().putValue("scope", SERVER_SCOPE);  
208 pushRuleEngineMessage(stateData, ACTIVITY_EVENT); 210 pushRuleEngineMessage(stateData, ACTIVITY_EVENT);
209 } 211 }
210 } 212 }
@@ -292,25 +294,14 @@ public class DefaultDeviceStateService implements DeviceStateService { @@ -292,25 +294,14 @@ public class DefaultDeviceStateService implements DeviceStateService {
292 } 294 }
293 } 295 }
294 296
295 - volatile Set<TopicPartitionInfo> pendingPartitions;  
296 -  
297 @Override 297 @Override
298 - public void onApplicationEvent(PartitionChangeEvent partitionChangeEvent) { 298 + protected void onTbApplicationEvent(PartitionChangeEvent partitionChangeEvent) {
299 if (ServiceType.TB_CORE.equals(partitionChangeEvent.getServiceType())) { 299 if (ServiceType.TB_CORE.equals(partitionChangeEvent.getServiceType())) {
300 - synchronized (this) {  
301 - pendingPartitions = partitionChangeEvent.getPartitions();  
302 - if (!clusterUpdatePending) {  
303 - clusterUpdatePending = true;  
304 - queueExecutor.submit(() -> {  
305 - clusterUpdatePending = false;  
306 - initStateFromDB();  
307 - });  
308 - }  
309 - } 300 + deduplicationExecutor.submit(partitionChangeEvent.getPartitions());
310 } 301 }
311 } 302 }
312 303
313 - private void initStateFromDB() { 304 + private void initStateFromDB(Set<TopicPartitionInfo> pendingPartitions) {
314 try { 305 try {
315 log.info("CURRENT PARTITIONS: {}", partitionedDevices.keySet()); 306 log.info("CURRENT PARTITIONS: {}", partitionedDevices.keySet());
316 log.info("NEW PARTITIONS: {}", pendingPartitions); 307 log.info("NEW PARTITIONS: {}", pendingPartitions);
@@ -456,7 +447,7 @@ public class DefaultDeviceStateService implements DeviceStateService { @@ -456,7 +447,7 @@ public class DefaultDeviceStateService implements DeviceStateService {
456 } 447 }
457 448
458 private <T extends KvEntry> Function<List<T>, DeviceStateData> extractDeviceStateData(Device device) { 449 private <T extends KvEntry> Function<List<T>, DeviceStateData> extractDeviceStateData(Device device) {
459 - return new Function<List<T>, DeviceStateData>() { 450 + return new Function<>() {
460 @Nullable 451 @Nullable
461 @Override 452 @Override
462 public DeviceStateData apply(@Nullable List<T> data) { 453 public DeviceStateData apply(@Nullable List<T> data) {
@@ -512,7 +503,11 @@ public class DefaultDeviceStateService implements DeviceStateService { @@ -512,7 +503,11 @@ public class DefaultDeviceStateService implements DeviceStateService {
512 } else { 503 } else {
513 data = JacksonUtil.toString(state); 504 data = JacksonUtil.toString(state);
514 } 505 }
515 - TbMsg tbMsg = TbMsg.newMsg(msgType, stateData.getDeviceId(), stateData.getMetaData().copy(), TbMsgDataType.JSON, data); 506 + TbMsgMetaData md = stateData.getMetaData().copy();
  507 + if(!persistToTelemetry){
  508 + md.putValue(DataConstants.SCOPE, SERVER_SCOPE);
  509 + }
  510 + TbMsg tbMsg = TbMsg.newMsg(msgType, stateData.getDeviceId(), md, TbMsgDataType.JSON, data);
516 clusterService.pushMsgToRuleEngine(stateData.getTenantId(), stateData.getDeviceId(), tbMsg, null); 511 clusterService.pushMsgToRuleEngine(stateData.getTenantId(), stateData.getDeviceId(), tbMsg, null);
517 } catch (Exception e) { 512 } catch (Exception e) {
518 log.warn("[{}] Failed to push inactivity alarm: {}", stateData.getDeviceId(), state, e); 513 log.warn("[{}] Failed to push inactivity alarm: {}", stateData.getDeviceId(), state, e);
@@ -39,7 +39,7 @@ import org.thingsboard.server.common.msg.queue.TbCallback; @@ -39,7 +39,7 @@ import org.thingsboard.server.common.msg.queue.TbCallback;
39 import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; 39 import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
40 import org.thingsboard.server.dao.attributes.AttributesService; 40 import org.thingsboard.server.dao.attributes.AttributesService;
41 import org.thingsboard.server.dao.timeseries.TimeseriesService; 41 import org.thingsboard.server.dao.timeseries.TimeseriesService;
42 -import org.thingsboard.server.dao.util.mapping.JacksonUtil; 42 +import org.thingsboard.common.util.JacksonUtil;
43 import org.thingsboard.server.gen.transport.TransportProtos.*; 43 import org.thingsboard.server.gen.transport.TransportProtos.*;
44 import org.thingsboard.server.gen.transport.TransportProtos.LocalSubscriptionServiceMsgProto; 44 import org.thingsboard.server.gen.transport.TransportProtos.LocalSubscriptionServiceMsgProto;
45 import org.thingsboard.server.gen.transport.TransportProtos.TbSubscriptionUpdateProto; 45 import org.thingsboard.server.gen.transport.TransportProtos.TbSubscriptionUpdateProto;
@@ -48,6 +48,7 @@ import org.thingsboard.server.queue.TbQueueProducer; @@ -48,6 +48,7 @@ import org.thingsboard.server.queue.TbQueueProducer;
48 import org.thingsboard.server.queue.common.TbProtoQueueMsg; 48 import org.thingsboard.server.queue.common.TbProtoQueueMsg;
49 import org.thingsboard.server.queue.discovery.PartitionChangeEvent; 49 import org.thingsboard.server.queue.discovery.PartitionChangeEvent;
50 import org.thingsboard.server.queue.discovery.PartitionService; 50 import org.thingsboard.server.queue.discovery.PartitionService;
  51 +import org.thingsboard.server.queue.discovery.TbApplicationEventListener;
51 import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; 52 import org.thingsboard.server.queue.discovery.TbServiceInfoProvider;
52 import org.thingsboard.server.queue.provider.TbQueueProducerProvider; 53 import org.thingsboard.server.queue.provider.TbQueueProducerProvider;
53 import org.thingsboard.server.queue.util.TbCoreComponent; 54 import org.thingsboard.server.queue.util.TbCoreComponent;
@@ -76,7 +77,7 @@ import java.util.function.Predicate; @@ -76,7 +77,7 @@ import java.util.function.Predicate;
76 @Slf4j 77 @Slf4j
77 @TbCoreComponent 78 @TbCoreComponent
78 @Service 79 @Service
79 -public class DefaultSubscriptionManagerService implements SubscriptionManagerService { 80 +public class DefaultSubscriptionManagerService extends TbApplicationEventListener<PartitionChangeEvent> implements SubscriptionManagerService {
80 81
81 @Autowired 82 @Autowired
82 private AttributesService attrService; 83 private AttributesService attrService;
@@ -178,7 +179,7 @@ public class DefaultSubscriptionManagerService implements SubscriptionManagerSer @@ -178,7 +179,7 @@ public class DefaultSubscriptionManagerService implements SubscriptionManagerSer
178 } 179 }
179 180
180 @Override 181 @Override
181 - public void onApplicationEvent(PartitionChangeEvent partitionChangeEvent) { 182 + protected void onTbApplicationEvent(PartitionChangeEvent partitionChangeEvent) {
182 if (ServiceType.TB_CORE.equals(partitionChangeEvent.getServiceType())) { 183 if (ServiceType.TB_CORE.equals(partitionChangeEvent.getServiceType())) {
183 Set<TopicPartitionInfo> removedPartitions = new HashSet<>(currentPartitions); 184 Set<TopicPartitionInfo> removedPartitions = new HashSet<>(currentPartitions);
184 removedPartitions.removeAll(partitionChangeEvent.getPartitions()); 185 removedPartitions.removeAll(partitionChangeEvent.getPartitions());
@@ -54,6 +54,7 @@ import org.thingsboard.server.service.telemetry.TelemetryWebSocketService; @@ -54,6 +54,7 @@ import org.thingsboard.server.service.telemetry.TelemetryWebSocketService;
54 import org.thingsboard.server.service.telemetry.TelemetryWebSocketSessionRef; 54 import org.thingsboard.server.service.telemetry.TelemetryWebSocketSessionRef;
55 import org.thingsboard.server.service.telemetry.cmd.v2.AlarmDataCmd; 55 import org.thingsboard.server.service.telemetry.cmd.v2.AlarmDataCmd;
56 import org.thingsboard.server.service.telemetry.cmd.v2.AlarmDataUpdate; 56 import org.thingsboard.server.service.telemetry.cmd.v2.AlarmDataUpdate;
  57 +import org.thingsboard.server.service.telemetry.cmd.v2.EntityCountCmd;
57 import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataCmd; 58 import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataCmd;
58 import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUpdate; 59 import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUpdate;
59 import org.thingsboard.server.service.telemetry.cmd.v2.EntityHistoryCmd; 60 import org.thingsboard.server.service.telemetry.cmd.v2.EntityHistoryCmd;
@@ -92,7 +93,7 @@ import java.util.stream.Collectors; @@ -92,7 +93,7 @@ import java.util.stream.Collectors;
92 public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubscriptionService { 93 public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubscriptionService {
93 94
94 private static final int DEFAULT_LIMIT = 100; 95 private static final int DEFAULT_LIMIT = 100;
95 - private final Map<String, Map<Integer, TbAbstractDataSubCtx>> subscriptionsBySessionId = new ConcurrentHashMap<>(); 96 + private final Map<String, Map<Integer, TbAbstractSubCtx>> subscriptionsBySessionId = new ConcurrentHashMap<>();
96 97
97 @Autowired 98 @Autowired
98 private TelemetryWebSocketService wsService; 99 private TelemetryWebSocketService wsService;
@@ -202,7 +203,7 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc @@ -202,7 +203,7 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc
202 //TODO: validate number of dynamic page links against rate limits. Ignore dynamic flag if limit is reached. 203 //TODO: validate number of dynamic page links against rate limits. Ignore dynamic flag if limit is reached.
203 TbEntityDataSubCtx finalCtx = ctx; 204 TbEntityDataSubCtx finalCtx = ctx;
204 ScheduledFuture<?> task = scheduler.scheduleWithFixedDelay( 205 ScheduledFuture<?> task = scheduler.scheduleWithFixedDelay(
205 - () -> refreshDynamicQuery(tenantId, customerId, finalCtx), 206 + () -> refreshDynamicQuery(finalCtx),
206 dynamicPageLinkRefreshInterval, dynamicPageLinkRefreshInterval, TimeUnit.SECONDS); 207 dynamicPageLinkRefreshInterval, dynamicPageLinkRefreshInterval, TimeUnit.SECONDS);
207 finalCtx.setRefreshTask(task); 208 finalCtx.setRefreshTask(task);
208 } 209 }
@@ -236,6 +237,26 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc @@ -236,6 +237,26 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc
236 } 237 }
237 238
238 @Override 239 @Override
  240 + public void handleCmd(TelemetryWebSocketSessionRef session, EntityCountCmd cmd) {
  241 + TbEntityCountSubCtx ctx = getSubCtx(session.getSessionId(), cmd.getCmdId());
  242 + if (ctx == null) {
  243 + ctx = createSubCtx(session, cmd);
  244 + long start = System.currentTimeMillis();
  245 + ctx.fetchData();
  246 + long end = System.currentTimeMillis();
  247 + stats.getRegularQueryInvocationCnt().incrementAndGet();
  248 + stats.getRegularQueryTimeSpent().addAndGet(end - start);
  249 + TbEntityCountSubCtx finalCtx = ctx;
  250 + ScheduledFuture<?> task = scheduler.scheduleWithFixedDelay(
  251 + () -> refreshDynamicQuery(finalCtx),
  252 + dynamicPageLinkRefreshInterval, dynamicPageLinkRefreshInterval, TimeUnit.SECONDS);
  253 + finalCtx.setRefreshTask(task);
  254 + } else {
  255 + log.debug("[{}][{}] Received duplicate command: {}", session.getSessionId(), cmd.getCmdId(), cmd);
  256 + }
  257 + }
  258 +
  259 + @Override
239 public void handleCmd(TelemetryWebSocketSessionRef session, AlarmDataCmd cmd) { 260 public void handleCmd(TelemetryWebSocketSessionRef session, AlarmDataCmd cmd) {
240 TbAlarmDataSubCtx ctx = getSubCtx(session.getSessionId(), cmd.getCmdId()); 261 TbAlarmDataSubCtx ctx = getSubCtx(session.getSessionId(), cmd.getCmdId());
241 if (ctx == null) { 262 if (ctx == null) {
@@ -267,7 +288,7 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc @@ -267,7 +288,7 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc
267 } 288 }
268 } 289 }
269 290
270 - private void refreshDynamicQuery(TenantId tenantId, CustomerId customerId, TbEntityDataSubCtx finalCtx) { 291 + private void refreshDynamicQuery(TbAbstractSubCtx finalCtx) {
271 try { 292 try {
272 long start = System.currentTimeMillis(); 293 long start = System.currentTimeMillis();
273 finalCtx.update(); 294 finalCtx.update();
@@ -299,16 +320,30 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc @@ -299,16 +320,30 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc
299 } 320 }
300 321
301 private TbEntityDataSubCtx createSubCtx(TelemetryWebSocketSessionRef sessionRef, EntityDataCmd cmd) { 322 private TbEntityDataSubCtx createSubCtx(TelemetryWebSocketSessionRef sessionRef, EntityDataCmd cmd) {
302 - Map<Integer, TbAbstractDataSubCtx> sessionSubs = subscriptionsBySessionId.computeIfAbsent(sessionRef.getSessionId(), k -> new HashMap<>()); 323 + Map<Integer, TbAbstractSubCtx> sessionSubs = subscriptionsBySessionId.computeIfAbsent(sessionRef.getSessionId(), k -> new HashMap<>());
303 TbEntityDataSubCtx ctx = new TbEntityDataSubCtx(serviceId, wsService, entityService, localSubscriptionService, 324 TbEntityDataSubCtx ctx = new TbEntityDataSubCtx(serviceId, wsService, entityService, localSubscriptionService,
304 attributesService, stats, sessionRef, cmd.getCmdId(), maxEntitiesPerDataSubscription); 325 attributesService, stats, sessionRef, cmd.getCmdId(), maxEntitiesPerDataSubscription);
305 - ctx.setAndResolveQuery(cmd.getQuery()); 326 + if (cmd.getQuery() != null) {
  327 + ctx.setAndResolveQuery(cmd.getQuery());
  328 + }
  329 + sessionSubs.put(cmd.getCmdId(), ctx);
  330 + return ctx;
  331 + }
  332 +
  333 + private TbEntityCountSubCtx createSubCtx(TelemetryWebSocketSessionRef sessionRef, EntityCountCmd cmd) {
  334 + Map<Integer, TbAbstractSubCtx> sessionSubs = subscriptionsBySessionId.computeIfAbsent(sessionRef.getSessionId(), k -> new HashMap<>());
  335 + TbEntityCountSubCtx ctx = new TbEntityCountSubCtx(serviceId, wsService, entityService, localSubscriptionService,
  336 + attributesService, stats, sessionRef, cmd.getCmdId());
  337 + if (cmd.getQuery() != null) {
  338 + ctx.setAndResolveQuery(cmd.getQuery());
  339 + }
306 sessionSubs.put(cmd.getCmdId(), ctx); 340 sessionSubs.put(cmd.getCmdId(), ctx);
307 return ctx; 341 return ctx;
308 } 342 }
309 343
  344 +
310 private TbAlarmDataSubCtx createSubCtx(TelemetryWebSocketSessionRef sessionRef, AlarmDataCmd cmd) { 345 private TbAlarmDataSubCtx createSubCtx(TelemetryWebSocketSessionRef sessionRef, AlarmDataCmd cmd) {
311 - Map<Integer, TbAbstractDataSubCtx> sessionSubs = subscriptionsBySessionId.computeIfAbsent(sessionRef.getSessionId(), k -> new HashMap<>()); 346 + Map<Integer, TbAbstractSubCtx> sessionSubs = subscriptionsBySessionId.computeIfAbsent(sessionRef.getSessionId(), k -> new HashMap<>());
312 TbAlarmDataSubCtx ctx = new TbAlarmDataSubCtx(serviceId, wsService, entityService, localSubscriptionService, 347 TbAlarmDataSubCtx ctx = new TbAlarmDataSubCtx(serviceId, wsService, entityService, localSubscriptionService,
313 attributesService, stats, alarmService, sessionRef, cmd.getCmdId(), maxEntitiesPerAlarmSubscription); 348 attributesService, stats, alarmService, sessionRef, cmd.getCmdId(), maxEntitiesPerAlarmSubscription);
314 ctx.setAndResolveQuery(cmd.getQuery()); 349 ctx.setAndResolveQuery(cmd.getQuery());
@@ -316,8 +351,9 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc @@ -316,8 +351,9 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc
316 return ctx; 351 return ctx;
317 } 352 }
318 353
319 - private <T extends TbAbstractDataSubCtx> T getSubCtx(String sessionId, int cmdId) {  
320 - Map<Integer, TbAbstractDataSubCtx> sessionSubs = subscriptionsBySessionId.get(sessionId); 354 + @SuppressWarnings("unchecked")
  355 + private <T extends TbAbstractSubCtx> T getSubCtx(String sessionId, int cmdId) {
  356 + Map<Integer, TbAbstractSubCtx> sessionSubs = subscriptionsBySessionId.get(sessionId);
321 if (sessionSubs != null) { 357 if (sessionSubs != null) {
322 return (T) sessionSubs.get(cmdId); 358 return (T) sessionSubs.get(cmdId);
323 } else { 359 } else {
@@ -461,19 +497,18 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc @@ -461,19 +497,18 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc
461 cleanupAndCancel(getSubCtx(sessionId, cmd.getCmdId())); 497 cleanupAndCancel(getSubCtx(sessionId, cmd.getCmdId()));
462 } 498 }
463 499
464 - private void cleanupAndCancel(TbAbstractDataSubCtx ctx) { 500 + private void cleanupAndCancel(TbAbstractSubCtx ctx) {
465 if (ctx != null) { 501 if (ctx != null) {
466 ctx.cancelTasks(); 502 ctx.cancelTasks();
467 - ctx.clearEntitySubscriptions();  
468 - ctx.clearDynamicValueSubscriptions(); 503 + ctx.clearSubscriptions();
469 } 504 }
470 } 505 }
471 506
472 @Override 507 @Override
473 public void cancelAllSessionSubscriptions(String sessionId) { 508 public void cancelAllSessionSubscriptions(String sessionId) {
474 - Map<Integer, TbAbstractDataSubCtx> sessionSubs = subscriptionsBySessionId.remove(sessionId); 509 + Map<Integer, TbAbstractSubCtx> sessionSubs = subscriptionsBySessionId.remove(sessionId);
475 if (sessionSubs != null) { 510 if (sessionSubs != null) {
476 - sessionSubs.values().stream().filter(sub -> sub instanceof TbEntityDataSubCtx).map(sub -> (TbEntityDataSubCtx) sub).forEach(this::cleanupAndCancel); 511 + sessionSubs.values().forEach(this::cleanupAndCancel);
477 } 512 }
478 } 513 }
479 514
@@ -28,6 +28,7 @@ import org.thingsboard.server.queue.discovery.PartitionService; @@ -28,6 +28,7 @@ import org.thingsboard.server.queue.discovery.PartitionService;
28 import org.thingsboard.server.common.msg.queue.ServiceType; 28 import org.thingsboard.server.common.msg.queue.ServiceType;
29 import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; 29 import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
30 import org.thingsboard.server.common.msg.queue.TbCallback; 30 import org.thingsboard.server.common.msg.queue.TbCallback;
  31 +import org.thingsboard.server.queue.discovery.TbApplicationEventListener;
31 import org.thingsboard.server.queue.util.TbCoreComponent; 32 import org.thingsboard.server.queue.util.TbCoreComponent;
32 import org.thingsboard.server.service.queue.TbClusterService; 33 import org.thingsboard.server.service.queue.TbClusterService;
33 import org.thingsboard.server.service.telemetry.sub.AlarmSubscriptionUpdate; 34 import org.thingsboard.server.service.telemetry.sub.AlarmSubscriptionUpdate;
@@ -62,6 +63,34 @@ public class DefaultTbLocalSubscriptionService implements TbLocalSubscriptionSer @@ -62,6 +63,34 @@ public class DefaultTbLocalSubscriptionService implements TbLocalSubscriptionSer
62 private SubscriptionManagerService subscriptionManagerService; 63 private SubscriptionManagerService subscriptionManagerService;
63 64
64 private ExecutorService subscriptionUpdateExecutor; 65 private ExecutorService subscriptionUpdateExecutor;
  66 +
  67 + private TbApplicationEventListener<PartitionChangeEvent> partitionChangeListener = new TbApplicationEventListener<>() {
  68 + @Override
  69 + protected void onTbApplicationEvent(PartitionChangeEvent event) {
  70 + if (ServiceType.TB_CORE.equals(event.getServiceType())) {
  71 + currentPartitions.clear();
  72 + currentPartitions.addAll(event.getPartitions());
  73 + }
  74 + }
  75 + };
  76 +
  77 + private TbApplicationEventListener<ClusterTopologyChangeEvent> clusterTopologyChangeListener = new TbApplicationEventListener<>() {
  78 + @Override
  79 + protected void onTbApplicationEvent(ClusterTopologyChangeEvent event) {
  80 + if (event.getServiceQueueKeys().stream().anyMatch(key -> ServiceType.TB_CORE.equals(key.getServiceType()))) {
  81 + /*
  82 + * If the cluster topology has changed, we need to push all current subscriptions to SubscriptionManagerService again.
  83 + * Otherwise, the SubscriptionManagerService may "forget" those subscriptions in case of restart.
  84 + * Although this is resource consuming operation, it is cheaper than sending ping/pong commands periodically
  85 + * It is also cheaper then caching the subscriptions by entity id and then lookup of those caches every time we have new telemetry in SubscriptionManagerService.
  86 + * Even if we cache locally the list of active subscriptions by entity id, it is still time consuming operation to get them from cache
  87 + * Since number of subscriptions is usually much less then number of devices that are pushing data.
  88 + */
  89 + subscriptionsBySessionId.values().forEach(map -> map.values()
  90 + .forEach(sub -> pushSubscriptionToManagerService(sub, true)));
  91 + }
  92 + }
  93 + };
65 94
66 @PostConstruct 95 @PostConstruct
67 public void initExecutor() { 96 public void initExecutor() {
@@ -77,28 +106,14 @@ public class DefaultTbLocalSubscriptionService implements TbLocalSubscriptionSer @@ -77,28 +106,14 @@ public class DefaultTbLocalSubscriptionService implements TbLocalSubscriptionSer
77 106
78 @Override 107 @Override
79 @EventListener(PartitionChangeEvent.class) 108 @EventListener(PartitionChangeEvent.class)
80 - public void onApplicationEvent(PartitionChangeEvent partitionChangeEvent) {  
81 - if (ServiceType.TB_CORE.equals(partitionChangeEvent.getServiceType())) {  
82 - currentPartitions.clear();  
83 - currentPartitions.addAll(partitionChangeEvent.getPartitions());  
84 - } 109 + public void onApplicationEvent(PartitionChangeEvent event) {
  110 + partitionChangeListener.onApplicationEvent(event);
85 } 111 }
86 112
87 @Override 113 @Override
88 @EventListener(ClusterTopologyChangeEvent.class) 114 @EventListener(ClusterTopologyChangeEvent.class)
89 public void onApplicationEvent(ClusterTopologyChangeEvent event) { 115 public void onApplicationEvent(ClusterTopologyChangeEvent event) {
90 - if (event.getServiceQueueKeys().stream().anyMatch(key -> ServiceType.TB_CORE.equals(key.getServiceType()))) {  
91 - /*  
92 - * If the cluster topology has changed, we need to push all current subscriptions to SubscriptionManagerService again.  
93 - * Otherwise, the SubscriptionManagerService may "forget" those subscriptions in case of restart.  
94 - * Although this is resource consuming operation, it is cheaper than sending ping/pong commands periodically  
95 - * It is also cheaper then caching the subscriptions by entity id and then lookup of those caches every time we have new telemetry in SubscriptionManagerService.  
96 - * Even if we cache locally the list of active subscriptions by entity id, it is still time consuming operation to get them from cache  
97 - * Since number of subscriptions is usually much less then number of devices that are pushing data.  
98 - */  
99 - subscriptionsBySessionId.values().forEach(map -> map.values()  
100 - .forEach(sub -> pushSubscriptionToManagerService(sub, true)));  
101 - } 116 + clusterTopologyChangeListener.onApplicationEvent(event);
102 } 117 }
103 118
104 //TODO 3.1: replace null callbacks with callbacks from websocket service. 119 //TODO 3.1: replace null callbacks with callbacks from websocket service.
@@ -123,6 +138,7 @@ public class DefaultTbLocalSubscriptionService implements TbLocalSubscriptionSer @@ -123,6 +138,7 @@ public class DefaultTbLocalSubscriptionService implements TbLocalSubscriptionSer
123 } 138 }
124 139
125 @Override 140 @Override
  141 + @SuppressWarnings("unchecked")
126 public void onSubscriptionUpdate(String sessionId, TelemetrySubscriptionUpdate update, TbCallback callback) { 142 public void onSubscriptionUpdate(String sessionId, TelemetrySubscriptionUpdate update, TbCallback callback) {
127 TbSubscription subscription = subscriptionsBySessionId 143 TbSubscription subscription = subscriptionsBySessionId
128 .getOrDefault(sessionId, Collections.emptyMap()).get(update.getSubscriptionId()); 144 .getOrDefault(sessionId, Collections.emptyMap()).get(update.getSubscriptionId());
@@ -143,6 +159,7 @@ public class DefaultTbLocalSubscriptionService implements TbLocalSubscriptionSer @@ -143,6 +159,7 @@ public class DefaultTbLocalSubscriptionService implements TbLocalSubscriptionSer
143 } 159 }
144 160
145 @Override 161 @Override
  162 + @SuppressWarnings("unchecked")
146 public void onSubscriptionUpdate(String sessionId, AlarmSubscriptionUpdate update, TbCallback callback) { 163 public void onSubscriptionUpdate(String sessionId, AlarmSubscriptionUpdate update, TbCallback callback) {
147 TbSubscription subscription = subscriptionsBySessionId 164 TbSubscription subscription = subscriptionsBySessionId
148 .getOrDefault(sessionId, Collections.emptyMap()).get(update.getSubscriptionId()); 165 .getOrDefault(sessionId, Collections.emptyMap()).get(update.getSubscriptionId());
@@ -15,32 +15,16 @@ @@ -15,32 +15,16 @@
15 */ 15 */
16 package org.thingsboard.server.service.subscription; 16 package org.thingsboard.server.service.subscription;
17 17
18 -import com.google.common.util.concurrent.Futures;  
19 -import com.google.common.util.concurrent.ListenableFuture;  
20 -import com.google.common.util.concurrent.MoreExecutors;  
21 -import lombok.Data;  
22 import lombok.Getter; 18 import lombok.Getter;
23 -import lombok.Setter;  
24 import lombok.extern.slf4j.Slf4j; 19 import lombok.extern.slf4j.Slf4j;
25 -import org.thingsboard.server.common.data.id.CustomerId;  
26 import org.thingsboard.server.common.data.id.EntityId; 20 import org.thingsboard.server.common.data.id.EntityId;
27 -import org.thingsboard.server.common.data.id.TenantId;  
28 -import org.thingsboard.server.common.data.id.UserId;  
29 -import org.thingsboard.server.common.data.kv.AttributeKvEntry;  
30 import org.thingsboard.server.common.data.page.PageData; 21 import org.thingsboard.server.common.data.page.PageData;
31 import org.thingsboard.server.common.data.query.AbstractDataQuery; 22 import org.thingsboard.server.common.data.query.AbstractDataQuery;
32 -import org.thingsboard.server.common.data.query.ComplexFilterPredicate;  
33 -import org.thingsboard.server.common.data.query.DynamicValue;  
34 -import org.thingsboard.server.common.data.query.DynamicValueSourceType;  
35 import org.thingsboard.server.common.data.query.EntityData; 23 import org.thingsboard.server.common.data.query.EntityData;
36 import org.thingsboard.server.common.data.query.EntityDataPageLink; 24 import org.thingsboard.server.common.data.query.EntityDataPageLink;
37 import org.thingsboard.server.common.data.query.EntityDataQuery; 25 import org.thingsboard.server.common.data.query.EntityDataQuery;
38 import org.thingsboard.server.common.data.query.EntityKey; 26 import org.thingsboard.server.common.data.query.EntityKey;
39 import org.thingsboard.server.common.data.query.EntityKeyType; 27 import org.thingsboard.server.common.data.query.EntityKeyType;
40 -import org.thingsboard.server.common.data.query.FilterPredicateType;  
41 -import org.thingsboard.server.common.data.query.KeyFilter;  
42 -import org.thingsboard.server.common.data.query.KeyFilterPredicate;  
43 -import org.thingsboard.server.common.data.query.SimpleKeyFilterPredicate;  
44 import org.thingsboard.server.common.data.query.TsValue; 28 import org.thingsboard.server.common.data.query.TsValue;
45 import org.thingsboard.server.dao.attributes.AttributesService; 29 import org.thingsboard.server.dao.attributes.AttributesService;
46 import org.thingsboard.server.dao.entity.EntityService; 30 import org.thingsboard.server.dao.entity.EntityService;
@@ -52,140 +36,25 @@ import java.util.ArrayList; @@ -52,140 +36,25 @@ import java.util.ArrayList;
52 import java.util.Arrays; 36 import java.util.Arrays;
53 import java.util.Collections; 37 import java.util.Collections;
54 import java.util.HashMap; 38 import java.util.HashMap;
55 -import java.util.HashSet;  
56 import java.util.List; 39 import java.util.List;
57 import java.util.Map; 40 import java.util.Map;
58 -import java.util.Optional;  
59 -import java.util.Set;  
60 import java.util.concurrent.ConcurrentHashMap; 41 import java.util.concurrent.ConcurrentHashMap;
61 -import java.util.concurrent.ExecutionException;  
62 -import java.util.concurrent.ScheduledFuture;  
63 import java.util.function.Function; 42 import java.util.function.Function;
64 import java.util.stream.Collectors; 43 import java.util.stream.Collectors;
65 44
66 @Slf4j 45 @Slf4j
67 -@Data  
68 -public abstract class TbAbstractDataSubCtx<T extends AbstractDataQuery<? extends EntityDataPageLink>> { 46 +public abstract class TbAbstractDataSubCtx<T extends AbstractDataQuery<? extends EntityDataPageLink>> extends TbAbstractSubCtx<T> {
69 47
70 - protected final String serviceId;  
71 - protected final SubscriptionServiceStatistics stats;  
72 - protected final TelemetryWebSocketService wsService;  
73 - protected final EntityService entityService;  
74 - protected final TbLocalSubscriptionService localSubscriptionService;  
75 - protected final AttributesService attributesService;  
76 - protected final TelemetryWebSocketSessionRef sessionRef;  
77 - protected final int cmdId;  
78 protected final Map<Integer, EntityId> subToEntityIdMap; 48 protected final Map<Integer, EntityId> subToEntityIdMap;
79 - protected final Set<Integer> subToDynamicValueKeySet;  
80 - @Getter  
81 - protected final Map<DynamicValueKey, List<DynamicValue>> dynamicValues;  
82 @Getter 49 @Getter
83 protected PageData<EntityData> data; 50 protected PageData<EntityData> data;
84 - @Getter  
85 - @Setter  
86 - protected T query;  
87 - @Setter  
88 - protected volatile ScheduledFuture<?> refreshTask;  
89 51
90 public TbAbstractDataSubCtx(String serviceId, TelemetryWebSocketService wsService, 52 public TbAbstractDataSubCtx(String serviceId, TelemetryWebSocketService wsService,
91 EntityService entityService, TbLocalSubscriptionService localSubscriptionService, 53 EntityService entityService, TbLocalSubscriptionService localSubscriptionService,
92 AttributesService attributesService, SubscriptionServiceStatistics stats, 54 AttributesService attributesService, SubscriptionServiceStatistics stats,
93 TelemetryWebSocketSessionRef sessionRef, int cmdId) { 55 TelemetryWebSocketSessionRef sessionRef, int cmdId) {
94 - this.serviceId = serviceId;  
95 - this.wsService = wsService;  
96 - this.entityService = entityService;  
97 - this.localSubscriptionService = localSubscriptionService;  
98 - this.attributesService = attributesService;  
99 - this.stats = stats;  
100 - this.sessionRef = sessionRef;  
101 - this.cmdId = cmdId; 56 + super(serviceId, wsService, entityService, localSubscriptionService, attributesService, stats, sessionRef, cmdId);
102 this.subToEntityIdMap = new ConcurrentHashMap<>(); 57 this.subToEntityIdMap = new ConcurrentHashMap<>();
103 - this.subToDynamicValueKeySet = ConcurrentHashMap.newKeySet();  
104 - this.dynamicValues = new ConcurrentHashMap<>();  
105 - }  
106 -  
107 - public void setAndResolveQuery(T query) {  
108 - dynamicValues.clear();  
109 - this.query = query;  
110 - if (query.getKeyFilters() != null) {  
111 - for (KeyFilter filter : query.getKeyFilters()) {  
112 - registerDynamicValues(filter.getPredicate());  
113 - }  
114 - }  
115 - resolve(getTenantId(), getCustomerId(), getUserId());  
116 - }  
117 -  
118 - public void resolve(TenantId tenantId, CustomerId customerId, UserId userId) {  
119 - List<ListenableFuture<DynamicValueKeySub>> futures = new ArrayList<>();  
120 - for (DynamicValueKey key : dynamicValues.keySet()) {  
121 - switch (key.getSourceType()) {  
122 - case CURRENT_TENANT:  
123 - futures.add(resolveEntityValue(tenantId, tenantId, key));  
124 - break;  
125 - case CURRENT_CUSTOMER:  
126 - if (customerId != null && !customerId.isNullUid()) {  
127 - futures.add(resolveEntityValue(tenantId, customerId, key));  
128 - }  
129 - break;  
130 - case CURRENT_USER:  
131 - if (userId != null && !userId.isNullUid()) {  
132 - futures.add(resolveEntityValue(tenantId, userId, key));  
133 - }  
134 - break;  
135 - }  
136 - }  
137 - try {  
138 - Map<EntityId, Map<String, DynamicValueKeySub>> tmpSubMap = new HashMap<>();  
139 - for (DynamicValueKeySub sub : Futures.successfulAsList(futures).get()) {  
140 - tmpSubMap.computeIfAbsent(sub.getEntityId(), tmp -> new HashMap<>()).put(sub.getKey().getSourceAttribute(), sub);  
141 - }  
142 - for (EntityId entityId : tmpSubMap.keySet()) {  
143 - Map<String, Long> keyStates = new HashMap<>();  
144 - Map<String, DynamicValueKeySub> dynamicValueKeySubMap = tmpSubMap.get(entityId);  
145 - dynamicValueKeySubMap.forEach((k, v) -> keyStates.put(k, v.getLastUpdateTs()));  
146 - int subIdx = sessionRef.getSessionSubIdSeq().incrementAndGet();  
147 - TbAttributeSubscription sub = TbAttributeSubscription.builder()  
148 - .serviceId(serviceId)  
149 - .sessionId(sessionRef.getSessionId())  
150 - .subscriptionId(subIdx)  
151 - .tenantId(sessionRef.getSecurityCtx().getTenantId())  
152 - .entityId(entityId)  
153 - .updateConsumer((s, subscriptionUpdate) -> dynamicValueSubUpdate(s, subscriptionUpdate, dynamicValueKeySubMap))  
154 - .allKeys(false)  
155 - .keyStates(keyStates)  
156 - .scope(TbAttributeSubscriptionScope.SERVER_SCOPE)  
157 - .build();  
158 - subToDynamicValueKeySet.add(subIdx);  
159 - localSubscriptionService.addSubscription(sub);  
160 - }  
161 - } catch (InterruptedException | ExecutionException e) {  
162 - log.info("[{}][{}][{}] Failed to resolve dynamic values: {}", tenantId, customerId, userId, dynamicValues.keySet());  
163 - }  
164 -  
165 - }  
166 -  
167 - private void dynamicValueSubUpdate(String sessionId, TelemetrySubscriptionUpdate subscriptionUpdate,  
168 - Map<String, DynamicValueKeySub> dynamicValueKeySubMap) {  
169 - Map<String, TsValue> latestUpdate = new HashMap<>();  
170 - subscriptionUpdate.getData().forEach((k, v) -> {  
171 - Object[] data = (Object[]) v.get(0);  
172 - latestUpdate.put(k, new TsValue((Long) data[0], (String) data[1]));  
173 - });  
174 -  
175 - boolean invalidateFilter = false;  
176 - for (Map.Entry<String, TsValue> entry : latestUpdate.entrySet()) {  
177 - String k = entry.getKey();  
178 - TsValue tsValue = entry.getValue();  
179 - DynamicValueKeySub sub = dynamicValueKeySubMap.get(k);  
180 - if (sub.updateValue(tsValue)) {  
181 - invalidateFilter = true;  
182 - updateDynamicValuesByKey(sub, tsValue);  
183 - }  
184 - }  
185 -  
186 - if (invalidateFilter) {  
187 - update();  
188 - }  
189 } 58 }
190 59
191 public void fetchData() { 60 public void fetchData() {
@@ -231,102 +100,10 @@ public abstract class TbAbstractDataSubCtx<T extends AbstractDataQuery<? extends @@ -231,102 +100,10 @@ public abstract class TbAbstractDataSubCtx<T extends AbstractDataQuery<? extends
231 return data.getData(); 100 return data.getData();
232 } 101 }
233 102
234 - @Data  
235 - private static class DynamicValueKeySub {  
236 - private final DynamicValueKey key;  
237 - private final EntityId entityId;  
238 - private long lastUpdateTs;  
239 - private String lastUpdateValue;  
240 -  
241 - boolean updateValue(TsValue value) {  
242 - if (value.getTs() > lastUpdateTs && (lastUpdateValue == null || !lastUpdateValue.equals(value.getValue()))) {  
243 - this.lastUpdateTs = value.getTs();  
244 - this.lastUpdateValue = value.getValue();  
245 - return true;  
246 - } else {  
247 - return false;  
248 - }  
249 - }  
250 - }  
251 -  
252 - private ListenableFuture<DynamicValueKeySub> resolveEntityValue(TenantId tenantId, EntityId entityId, DynamicValueKey key) {  
253 - ListenableFuture<Optional<AttributeKvEntry>> entry = attributesService.find(tenantId, entityId,  
254 - TbAttributeSubscriptionScope.SERVER_SCOPE.name(), key.getSourceAttribute());  
255 - return Futures.transform(entry, attributeOpt -> {  
256 - DynamicValueKeySub sub = new DynamicValueKeySub(key, entityId);  
257 - if (attributeOpt.isPresent()) {  
258 - AttributeKvEntry attribute = attributeOpt.get();  
259 - sub.setLastUpdateTs(attribute.getLastUpdateTs());  
260 - sub.setLastUpdateValue(attribute.getValueAsString());  
261 - updateDynamicValuesByKey(sub, new TsValue(attribute.getLastUpdateTs(), attribute.getValueAsString()));  
262 - }  
263 - return sub;  
264 - }, MoreExecutors.directExecutor());  
265 - }  
266 -  
267 - private void updateDynamicValuesByKey(DynamicValueKeySub sub, TsValue tsValue) {  
268 - DynamicValueKey dvk = sub.getKey();  
269 - switch (dvk.getPredicateType()) {  
270 - case STRING:  
271 - dynamicValues.get(dvk).forEach(dynamicValue -> dynamicValue.setResolvedValue(tsValue.getValue()));  
272 - break;  
273 - case NUMERIC:  
274 - try {  
275 - Double dValue = Double.parseDouble(tsValue.getValue());  
276 - dynamicValues.get(dvk).forEach(dynamicValue -> dynamicValue.setResolvedValue(dValue));  
277 - } catch (NumberFormatException e) {  
278 - dynamicValues.get(dvk).forEach(dynamicValue -> dynamicValue.setResolvedValue(null));  
279 - }  
280 - break;  
281 - case BOOLEAN:  
282 - Boolean bValue = Boolean.parseBoolean(tsValue.getValue());  
283 - dynamicValues.get(dvk).forEach(dynamicValue -> dynamicValue.setResolvedValue(bValue));  
284 - break;  
285 - }  
286 - }  
287 -  
288 - private void registerDynamicValues(KeyFilterPredicate predicate) {  
289 - switch (predicate.getType()) {  
290 - case STRING:  
291 - case NUMERIC:  
292 - case BOOLEAN:  
293 - Optional<DynamicValue> value = getDynamicValueFromSimplePredicate((SimpleKeyFilterPredicate) predicate);  
294 - if (value.isPresent()) {  
295 - DynamicValue dynamicValue = value.get();  
296 - DynamicValueKey key = new DynamicValueKey(  
297 - predicate.getType(),  
298 - dynamicValue.getSourceType(),  
299 - dynamicValue.getSourceAttribute());  
300 - dynamicValues.computeIfAbsent(key, tmp -> new ArrayList<>()).add(dynamicValue);  
301 - }  
302 - break;  
303 - case COMPLEX:  
304 - ((ComplexFilterPredicate) predicate).getPredicates().forEach(this::registerDynamicValues);  
305 - }  
306 - }  
307 -  
308 - private Optional<DynamicValue<T>> getDynamicValueFromSimplePredicate(SimpleKeyFilterPredicate<T> predicate) {  
309 - if (predicate.getValue().getUserValue() == null) {  
310 - return Optional.ofNullable(predicate.getValue().getDynamicValue());  
311 - } else {  
312 - return Optional.empty();  
313 - }  
314 - }  
315 -  
316 - public String getSessionId() {  
317 - return sessionRef.getSessionId();  
318 - }  
319 -  
320 - public TenantId getTenantId() {  
321 - return sessionRef.getSecurityCtx().getTenantId();  
322 - }  
323 -  
324 - public CustomerId getCustomerId() {  
325 - return sessionRef.getSecurityCtx().getCustomerId();  
326 - }  
327 -  
328 - public UserId getUserId() {  
329 - return sessionRef.getSecurityCtx().getId(); 103 + @Override
  104 + public void clearSubscriptions() {
  105 + clearEntitySubscriptions();
  106 + super.clearSubscriptions();
330 } 107 }
331 108
332 public void clearEntitySubscriptions() { 109 public void clearEntitySubscriptions() {
@@ -338,26 +115,6 @@ public abstract class TbAbstractDataSubCtx<T extends AbstractDataQuery<? extends @@ -338,26 +115,6 @@ public abstract class TbAbstractDataSubCtx<T extends AbstractDataQuery<? extends
338 } 115 }
339 } 116 }
340 117
341 - public void clearDynamicValueSubscriptions() {  
342 - if (subToDynamicValueKeySet != null) {  
343 - for (Integer subId : subToDynamicValueKeySet) {  
344 - localSubscriptionService.cancelSubscription(sessionRef.getSessionId(), subId);  
345 - }  
346 - subToDynamicValueKeySet.clear();  
347 - }  
348 - }  
349 -  
350 - public void setRefreshTask(ScheduledFuture<?> task) {  
351 - this.refreshTask = task;  
352 - }  
353 -  
354 - public void cancelTasks() {  
355 - if (this.refreshTask != null) {  
356 - log.trace("[{}][{}] Canceling old refresh task", sessionRef.getSessionId(), cmdId);  
357 - this.refreshTask.cancel(true);  
358 - }  
359 - }  
360 -  
361 public void createSubscriptions(List<EntityKey> keys, boolean resultToLatestValues) { 118 public void createSubscriptions(List<EntityKey> keys, boolean resultToLatestValues) {
362 Map<EntityKeyType, List<EntityKey>> keysByType = getEntityKeyByTypeMap(keys); 119 Map<EntityKeyType, List<EntityKey>> keysByType = getEntityKeyByTypeMap(keys);
363 for (EntityData entityData : data.getData()) { 120 for (EntityData entityData : data.getData()) {
@@ -457,14 +214,4 @@ public abstract class TbAbstractDataSubCtx<T extends AbstractDataQuery<? extends @@ -457,14 +214,4 @@ public abstract class TbAbstractDataSubCtx<T extends AbstractDataQuery<? extends
457 214
458 abstract void sendWsMsg(String sessionId, TelemetrySubscriptionUpdate subscriptionUpdate, EntityKeyType keyType, boolean resultToLatestValues); 215 abstract void sendWsMsg(String sessionId, TelemetrySubscriptionUpdate subscriptionUpdate, EntityKeyType keyType, boolean resultToLatestValues);
459 216
460 - @Data  
461 - private static class DynamicValueKey {  
462 - @Getter  
463 - private final FilterPredicateType predicateType;  
464 - @Getter  
465 - private final DynamicValueSourceType sourceType;  
466 - @Getter  
467 - private final String sourceAttribute;  
468 - }  
469 -  
470 } 217 }
  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.subscription;
  17 +
  18 +import com.google.common.util.concurrent.Futures;
  19 +import com.google.common.util.concurrent.ListenableFuture;
  20 +import com.google.common.util.concurrent.MoreExecutors;
  21 +import lombok.Data;
  22 +import lombok.Getter;
  23 +import lombok.Setter;
  24 +import lombok.extern.slf4j.Slf4j;
  25 +import org.thingsboard.server.common.data.id.CustomerId;
  26 +import org.thingsboard.server.common.data.id.EntityId;
  27 +import org.thingsboard.server.common.data.id.TenantId;
  28 +import org.thingsboard.server.common.data.id.UserId;
  29 +import org.thingsboard.server.common.data.kv.AttributeKvEntry;
  30 +import org.thingsboard.server.common.data.query.ComplexFilterPredicate;
  31 +import org.thingsboard.server.common.data.query.DynamicValue;
  32 +import org.thingsboard.server.common.data.query.DynamicValueSourceType;
  33 +import org.thingsboard.server.common.data.query.EntityCountQuery;
  34 +import org.thingsboard.server.common.data.query.EntityKeyType;
  35 +import org.thingsboard.server.common.data.query.FilterPredicateType;
  36 +import org.thingsboard.server.common.data.query.KeyFilter;
  37 +import org.thingsboard.server.common.data.query.KeyFilterPredicate;
  38 +import org.thingsboard.server.common.data.query.SimpleKeyFilterPredicate;
  39 +import org.thingsboard.server.common.data.query.TsValue;
  40 +import org.thingsboard.server.dao.attributes.AttributesService;
  41 +import org.thingsboard.server.dao.entity.EntityService;
  42 +import org.thingsboard.server.service.telemetry.TelemetryWebSocketService;
  43 +import org.thingsboard.server.service.telemetry.TelemetryWebSocketSessionRef;
  44 +import org.thingsboard.server.service.telemetry.sub.TelemetrySubscriptionUpdate;
  45 +
  46 +import java.util.ArrayList;
  47 +import java.util.HashMap;
  48 +import java.util.List;
  49 +import java.util.Map;
  50 +import java.util.Optional;
  51 +import java.util.Set;
  52 +import java.util.concurrent.ConcurrentHashMap;
  53 +import java.util.concurrent.ExecutionException;
  54 +import java.util.concurrent.ScheduledFuture;
  55 +
  56 +@Slf4j
  57 +@Data
  58 +public abstract class TbAbstractSubCtx<T extends EntityCountQuery> {
  59 +
  60 + protected final String serviceId;
  61 + protected final SubscriptionServiceStatistics stats;
  62 + protected final TelemetryWebSocketService wsService;
  63 + protected final EntityService entityService;
  64 + protected final TbLocalSubscriptionService localSubscriptionService;
  65 + protected final AttributesService attributesService;
  66 + protected final TelemetryWebSocketSessionRef sessionRef;
  67 + protected final int cmdId;
  68 + protected final Set<Integer> subToDynamicValueKeySet;
  69 + @Getter
  70 + protected final Map<DynamicValueKey, List<DynamicValue>> dynamicValues;
  71 + @Getter
  72 + @Setter
  73 + protected T query;
  74 + @Setter
  75 + protected volatile ScheduledFuture<?> refreshTask;
  76 +
  77 + public TbAbstractSubCtx(String serviceId, TelemetryWebSocketService wsService,
  78 + EntityService entityService, TbLocalSubscriptionService localSubscriptionService,
  79 + AttributesService attributesService, SubscriptionServiceStatistics stats,
  80 + TelemetryWebSocketSessionRef sessionRef, int cmdId) {
  81 + this.serviceId = serviceId;
  82 + this.wsService = wsService;
  83 + this.entityService = entityService;
  84 + this.localSubscriptionService = localSubscriptionService;
  85 + this.attributesService = attributesService;
  86 + this.stats = stats;
  87 + this.sessionRef = sessionRef;
  88 + this.cmdId = cmdId;
  89 + this.subToDynamicValueKeySet = ConcurrentHashMap.newKeySet();
  90 + this.dynamicValues = new ConcurrentHashMap<>();
  91 + }
  92 +
  93 + public void setAndResolveQuery(T query) {
  94 + dynamicValues.clear();
  95 + this.query = query;
  96 + if (query != null && query.getKeyFilters() != null) {
  97 + for (KeyFilter filter : query.getKeyFilters()) {
  98 + registerDynamicValues(filter.getPredicate());
  99 + }
  100 + }
  101 + resolve(getTenantId(), getCustomerId(), getUserId());
  102 + }
  103 +
  104 + public void resolve(TenantId tenantId, CustomerId customerId, UserId userId) {
  105 + List<ListenableFuture<DynamicValueKeySub>> futures = new ArrayList<>();
  106 + for (DynamicValueKey key : dynamicValues.keySet()) {
  107 + switch (key.getSourceType()) {
  108 + case CURRENT_TENANT:
  109 + futures.add(resolveEntityValue(tenantId, tenantId, key));
  110 + break;
  111 + case CURRENT_CUSTOMER:
  112 + if (customerId != null && !customerId.isNullUid()) {
  113 + futures.add(resolveEntityValue(tenantId, customerId, key));
  114 + }
  115 + break;
  116 + case CURRENT_USER:
  117 + if (userId != null && !userId.isNullUid()) {
  118 + futures.add(resolveEntityValue(tenantId, userId, key));
  119 + }
  120 + break;
  121 + }
  122 + }
  123 + try {
  124 + Map<EntityId, Map<String, DynamicValueKeySub>> tmpSubMap = new HashMap<>();
  125 + for (DynamicValueKeySub sub : Futures.successfulAsList(futures).get()) {
  126 + tmpSubMap.computeIfAbsent(sub.getEntityId(), tmp -> new HashMap<>()).put(sub.getKey().getSourceAttribute(), sub);
  127 + }
  128 + for (EntityId entityId : tmpSubMap.keySet()) {
  129 + Map<String, Long> keyStates = new HashMap<>();
  130 + Map<String, DynamicValueKeySub> dynamicValueKeySubMap = tmpSubMap.get(entityId);
  131 + dynamicValueKeySubMap.forEach((k, v) -> keyStates.put(k, v.getLastUpdateTs()));
  132 + int subIdx = sessionRef.getSessionSubIdSeq().incrementAndGet();
  133 + TbAttributeSubscription sub = TbAttributeSubscription.builder()
  134 + .serviceId(serviceId)
  135 + .sessionId(sessionRef.getSessionId())
  136 + .subscriptionId(subIdx)
  137 + .tenantId(sessionRef.getSecurityCtx().getTenantId())
  138 + .entityId(entityId)
  139 + .updateConsumer((s, subscriptionUpdate) -> dynamicValueSubUpdate(s, subscriptionUpdate, dynamicValueKeySubMap))
  140 + .allKeys(false)
  141 + .keyStates(keyStates)
  142 + .scope(TbAttributeSubscriptionScope.SERVER_SCOPE)
  143 + .build();
  144 + subToDynamicValueKeySet.add(subIdx);
  145 + localSubscriptionService.addSubscription(sub);
  146 + }
  147 + } catch (InterruptedException | ExecutionException e) {
  148 + log.info("[{}][{}][{}] Failed to resolve dynamic values: {}", tenantId, customerId, userId, dynamicValues.keySet());
  149 + }
  150 +
  151 + }
  152 +
  153 + private void dynamicValueSubUpdate(String sessionId, TelemetrySubscriptionUpdate subscriptionUpdate,
  154 + Map<String, DynamicValueKeySub> dynamicValueKeySubMap) {
  155 + Map<String, TsValue> latestUpdate = new HashMap<>();
  156 + subscriptionUpdate.getData().forEach((k, v) -> {
  157 + Object[] data = (Object[]) v.get(0);
  158 + latestUpdate.put(k, new TsValue((Long) data[0], (String) data[1]));
  159 + });
  160 +
  161 + boolean invalidateFilter = false;
  162 + for (Map.Entry<String, TsValue> entry : latestUpdate.entrySet()) {
  163 + String k = entry.getKey();
  164 + TsValue tsValue = entry.getValue();
  165 + DynamicValueKeySub sub = dynamicValueKeySubMap.get(k);
  166 + if (sub.updateValue(tsValue)) {
  167 + invalidateFilter = true;
  168 + updateDynamicValuesByKey(sub, tsValue);
  169 + }
  170 + }
  171 +
  172 + if (invalidateFilter) {
  173 + update();
  174 + }
  175 + }
  176 +
  177 + public abstract void fetchData();
  178 +
  179 + protected abstract void update();
  180 +
  181 + public void clearSubscriptions() {
  182 + clearDynamicValueSubscriptions();
  183 + }
  184 +
  185 + @Data
  186 + private static class DynamicValueKeySub {
  187 + private final DynamicValueKey key;
  188 + private final EntityId entityId;
  189 + private long lastUpdateTs;
  190 + private String lastUpdateValue;
  191 +
  192 + boolean updateValue(TsValue value) {
  193 + if (value.getTs() > lastUpdateTs && (lastUpdateValue == null || !lastUpdateValue.equals(value.getValue()))) {
  194 + this.lastUpdateTs = value.getTs();
  195 + this.lastUpdateValue = value.getValue();
  196 + return true;
  197 + } else {
  198 + return false;
  199 + }
  200 + }
  201 + }
  202 +
  203 + private ListenableFuture<DynamicValueKeySub> resolveEntityValue(TenantId tenantId, EntityId entityId, DynamicValueKey key) {
  204 + ListenableFuture<Optional<AttributeKvEntry>> entry = attributesService.find(tenantId, entityId,
  205 + TbAttributeSubscriptionScope.SERVER_SCOPE.name(), key.getSourceAttribute());
  206 + return Futures.transform(entry, attributeOpt -> {
  207 + DynamicValueKeySub sub = new DynamicValueKeySub(key, entityId);
  208 + if (attributeOpt.isPresent()) {
  209 + AttributeKvEntry attribute = attributeOpt.get();
  210 + sub.setLastUpdateTs(attribute.getLastUpdateTs());
  211 + sub.setLastUpdateValue(attribute.getValueAsString());
  212 + updateDynamicValuesByKey(sub, new TsValue(attribute.getLastUpdateTs(), attribute.getValueAsString()));
  213 + }
  214 + return sub;
  215 + }, MoreExecutors.directExecutor());
  216 + }
  217 +
  218 + @SuppressWarnings("unchecked")
  219 + protected void updateDynamicValuesByKey(DynamicValueKeySub sub, TsValue tsValue) {
  220 + DynamicValueKey dvk = sub.getKey();
  221 + switch (dvk.getPredicateType()) {
  222 + case STRING:
  223 + dynamicValues.get(dvk).forEach(dynamicValue -> dynamicValue.setResolvedValue(tsValue.getValue()));
  224 + break;
  225 + case NUMERIC:
  226 + try {
  227 + Double dValue = Double.parseDouble(tsValue.getValue());
  228 + dynamicValues.get(dvk).forEach(dynamicValue -> dynamicValue.setResolvedValue(dValue));
  229 + } catch (NumberFormatException e) {
  230 + dynamicValues.get(dvk).forEach(dynamicValue -> dynamicValue.setResolvedValue(null));
  231 + }
  232 + break;
  233 + case BOOLEAN:
  234 + Boolean bValue = Boolean.parseBoolean(tsValue.getValue());
  235 + dynamicValues.get(dvk).forEach(dynamicValue -> dynamicValue.setResolvedValue(bValue));
  236 + break;
  237 + }
  238 + }
  239 +
  240 + @SuppressWarnings("unchecked")
  241 + private void registerDynamicValues(KeyFilterPredicate predicate) {
  242 + switch (predicate.getType()) {
  243 + case STRING:
  244 + case NUMERIC:
  245 + case BOOLEAN:
  246 + Optional<DynamicValue> value = getDynamicValueFromSimplePredicate((SimpleKeyFilterPredicate) predicate);
  247 + if (value.isPresent()) {
  248 + DynamicValue dynamicValue = value.get();
  249 + DynamicValueKey key = new DynamicValueKey(
  250 + predicate.getType(),
  251 + dynamicValue.getSourceType(),
  252 + dynamicValue.getSourceAttribute());
  253 + dynamicValues.computeIfAbsent(key, tmp -> new ArrayList<>()).add(dynamicValue);
  254 + }
  255 + break;
  256 + case COMPLEX:
  257 + ((ComplexFilterPredicate) predicate).getPredicates().forEach(this::registerDynamicValues);
  258 + }
  259 + }
  260 +
  261 + private Optional<DynamicValue<T>> getDynamicValueFromSimplePredicate(SimpleKeyFilterPredicate<T> predicate) {
  262 + if (predicate.getValue().getUserValue() == null) {
  263 + return Optional.ofNullable(predicate.getValue().getDynamicValue());
  264 + } else {
  265 + return Optional.empty();
  266 + }
  267 + }
  268 +
  269 + public String getSessionId() {
  270 + return sessionRef.getSessionId();
  271 + }
  272 +
  273 + public TenantId getTenantId() {
  274 + return sessionRef.getSecurityCtx().getTenantId();
  275 + }
  276 +
  277 + public CustomerId getCustomerId() {
  278 + return sessionRef.getSecurityCtx().getCustomerId();
  279 + }
  280 +
  281 + public UserId getUserId() {
  282 + return sessionRef.getSecurityCtx().getId();
  283 + }
  284 +
  285 + protected void clearDynamicValueSubscriptions() {
  286 + if (subToDynamicValueKeySet != null) {
  287 + for (Integer subId : subToDynamicValueKeySet) {
  288 + localSubscriptionService.cancelSubscription(sessionRef.getSessionId(), subId);
  289 + }
  290 + subToDynamicValueKeySet.clear();
  291 + }
  292 + }
  293 +
  294 + public void setRefreshTask(ScheduledFuture<?> task) {
  295 + this.refreshTask = task;
  296 + }
  297 +
  298 + public void cancelTasks() {
  299 + if (this.refreshTask != null) {
  300 + log.trace("[{}][{}] Canceling old refresh task", sessionRef.getSessionId(), cmdId);
  301 + this.refreshTask.cancel(true);
  302 + }
  303 + }
  304 +
  305 + @Data
  306 + public static class DynamicValueKey {
  307 + @Getter
  308 + private final FilterPredicateType predicateType;
  309 + @Getter
  310 + private final DynamicValueSourceType sourceType;
  311 + @Getter
  312 + private final String sourceAttribute;
  313 + }
  314 +
  315 +}
@@ -90,8 +90,7 @@ public class TbAlarmDataSubCtx extends TbAbstractDataSubCtx<AlarmDataQuery> { @@ -90,8 +90,7 @@ public class TbAlarmDataSubCtx extends TbAbstractDataSubCtx<AlarmDataQuery> {
90 AlarmDataUpdate update; 90 AlarmDataUpdate update;
91 if (!entitiesMap.isEmpty()) { 91 if (!entitiesMap.isEmpty()) {
92 long start = System.currentTimeMillis(); 92 long start = System.currentTimeMillis();
93 - PageData<AlarmData> alarms = alarmService.findAlarmDataByQueryForEntities(getTenantId(), getCustomerId(),  
94 - query, getOrderedEntityIds()); 93 + PageData<AlarmData> alarms = alarmService.findAlarmDataByQueryForEntities(getTenantId(), getCustomerId(), query, getOrderedEntityIds());
95 long end = System.currentTimeMillis(); 94 long end = System.currentTimeMillis();
96 stats.getAlarmQueryInvocationCnt().incrementAndGet(); 95 stats.getAlarmQueryInvocationCnt().incrementAndGet();
97 stats.getAlarmQueryTimeSpent().addAndGet(end - start); 96 stats.getAlarmQueryTimeSpent().addAndGet(end - start);
  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.subscription;
  17 +
  18 +import lombok.extern.slf4j.Slf4j;
  19 +import org.thingsboard.server.common.data.query.EntityCountQuery;
  20 +import org.thingsboard.server.common.data.query.EntityKeyType;
  21 +import org.thingsboard.server.dao.attributes.AttributesService;
  22 +import org.thingsboard.server.dao.entity.EntityService;
  23 +import org.thingsboard.server.service.telemetry.TelemetryWebSocketService;
  24 +import org.thingsboard.server.service.telemetry.TelemetryWebSocketSessionRef;
  25 +import org.thingsboard.server.service.telemetry.cmd.v2.EntityCountUpdate;
  26 +import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUpdate;
  27 +import org.thingsboard.server.service.telemetry.sub.TelemetrySubscriptionUpdate;
  28 +
  29 +@Slf4j
  30 +public class TbEntityCountSubCtx extends TbAbstractSubCtx<EntityCountQuery> {
  31 +
  32 + private volatile int result;
  33 +
  34 + public TbEntityCountSubCtx(String serviceId, TelemetryWebSocketService wsService, EntityService entityService,
  35 + TbLocalSubscriptionService localSubscriptionService, AttributesService attributesService,
  36 + SubscriptionServiceStatistics stats, TelemetryWebSocketSessionRef sessionRef, int cmdId) {
  37 + super(serviceId, wsService, entityService, localSubscriptionService, attributesService, stats, sessionRef, cmdId);
  38 + }
  39 +
  40 + @Override
  41 + public void fetchData() {
  42 + result = (int) entityService.countEntitiesByQuery(getTenantId(), getCustomerId(), query);
  43 + wsService.sendWsMsg(sessionRef.getSessionId(), new EntityCountUpdate(cmdId, result));
  44 + }
  45 +
  46 + @Override
  47 + protected void update() {
  48 + int newCount = (int) entityService.countEntitiesByQuery(getTenantId(), getCustomerId(), query);
  49 + if (newCount != result) {
  50 + result = newCount;
  51 + wsService.sendWsMsg(sessionRef.getSessionId(), new EntityCountUpdate(cmdId, result));
  52 + }
  53 + }
  54 +
  55 +}
@@ -17,6 +17,7 @@ package org.thingsboard.server.service.subscription; @@ -17,6 +17,7 @@ package org.thingsboard.server.service.subscription;
17 17
18 import org.thingsboard.server.service.telemetry.TelemetryWebSocketSessionRef; 18 import org.thingsboard.server.service.telemetry.TelemetryWebSocketSessionRef;
19 import org.thingsboard.server.service.telemetry.cmd.v2.AlarmDataCmd; 19 import org.thingsboard.server.service.telemetry.cmd.v2.AlarmDataCmd;
  20 +import org.thingsboard.server.service.telemetry.cmd.v2.EntityCountCmd;
20 import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataCmd; 21 import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataCmd;
21 import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUnsubscribeCmd; 22 import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUnsubscribeCmd;
22 import org.thingsboard.server.service.telemetry.cmd.v2.UnsubscribeCmd; 23 import org.thingsboard.server.service.telemetry.cmd.v2.UnsubscribeCmd;
@@ -25,6 +26,8 @@ public interface TbEntityDataSubscriptionService { @@ -25,6 +26,8 @@ public interface TbEntityDataSubscriptionService {
25 26
26 void handleCmd(TelemetryWebSocketSessionRef sessionId, EntityDataCmd cmd); 27 void handleCmd(TelemetryWebSocketSessionRef sessionId, EntityDataCmd cmd);
27 28
  29 + void handleCmd(TelemetryWebSocketSessionRef sessionId, EntityCountCmd cmd);
  30 +
28 void handleCmd(TelemetryWebSocketSessionRef sessionId, AlarmDataCmd cmd); 31 void handleCmd(TelemetryWebSocketSessionRef sessionId, AlarmDataCmd cmd);
29 32
30 void cancelSubscription(String sessionId, UnsubscribeCmd subscriptionId); 33 void cancelSubscription(String sessionId, UnsubscribeCmd subscriptionId);
@@ -30,7 +30,7 @@ import org.thingsboard.server.common.data.kv.KvEntry; @@ -30,7 +30,7 @@ import org.thingsboard.server.common.data.kv.KvEntry;
30 import org.thingsboard.server.common.data.kv.LongDataEntry; 30 import org.thingsboard.server.common.data.kv.LongDataEntry;
31 import org.thingsboard.server.common.data.kv.StringDataEntry; 31 import org.thingsboard.server.common.data.kv.StringDataEntry;
32 import org.thingsboard.server.common.data.kv.TsKvEntry; 32 import org.thingsboard.server.common.data.kv.TsKvEntry;
33 -import org.thingsboard.server.dao.util.mapping.JacksonUtil; 33 +import org.thingsboard.common.util.JacksonUtil;
34 import org.thingsboard.server.gen.transport.TransportProtos; 34 import org.thingsboard.server.gen.transport.TransportProtos;
35 import org.thingsboard.server.gen.transport.TransportProtos.KeyValueProto; 35 import org.thingsboard.server.gen.transport.TransportProtos.KeyValueProto;
36 import org.thingsboard.server.gen.transport.TransportProtos.KeyValueType; 36 import org.thingsboard.server.gen.transport.TransportProtos.KeyValueType;
@@ -41,6 +41,7 @@ import org.thingsboard.server.dao.timeseries.TimeseriesService; @@ -41,6 +41,7 @@ import org.thingsboard.server.dao.timeseries.TimeseriesService;
41 import org.thingsboard.server.gen.transport.TransportProtos; 41 import org.thingsboard.server.gen.transport.TransportProtos;
42 import org.thingsboard.server.queue.discovery.PartitionChangeEvent; 42 import org.thingsboard.server.queue.discovery.PartitionChangeEvent;
43 import org.thingsboard.server.queue.discovery.PartitionService; 43 import org.thingsboard.server.queue.discovery.PartitionService;
  44 +import org.thingsboard.server.queue.discovery.TbApplicationEventListener;
44 import org.thingsboard.server.service.queue.TbClusterService; 45 import org.thingsboard.server.service.queue.TbClusterService;
45 import org.thingsboard.server.service.subscription.SubscriptionManagerService; 46 import org.thingsboard.server.service.subscription.SubscriptionManagerService;
46 import org.thingsboard.server.service.subscription.TbSubscriptionUtils; 47 import org.thingsboard.server.service.subscription.TbSubscriptionUtils;
@@ -61,7 +62,7 @@ import java.util.function.Consumer; @@ -61,7 +62,7 @@ import java.util.function.Consumer;
61 * Created by ashvayka on 27.03.18. 62 * Created by ashvayka on 27.03.18.
62 */ 63 */
63 @Slf4j 64 @Slf4j
64 -public abstract class AbstractSubscriptionService implements ApplicationListener<PartitionChangeEvent> { 65 +public abstract class AbstractSubscriptionService extends TbApplicationEventListener<PartitionChangeEvent>{
65 66
66 protected final Set<TopicPartitionInfo> currentPartitions = ConcurrentHashMap.newKeySet(); 67 protected final Set<TopicPartitionInfo> currentPartitions = ConcurrentHashMap.newKeySet();
67 68
@@ -97,8 +98,7 @@ public abstract class AbstractSubscriptionService implements ApplicationListener @@ -97,8 +98,7 @@ public abstract class AbstractSubscriptionService implements ApplicationListener
97 } 98 }
98 99
99 @Override 100 @Override
100 - @EventListener(PartitionChangeEvent.class)  
101 - public void onApplicationEvent(PartitionChangeEvent partitionChangeEvent) { 101 + protected void onTbApplicationEvent(PartitionChangeEvent partitionChangeEvent) {
102 if (ServiceType.TB_CORE.equals(partitionChangeEvent.getServiceType())) { 102 if (ServiceType.TB_CORE.equals(partitionChangeEvent.getServiceType())) {
103 currentPartitions.clear(); 103 currentPartitions.clear();
104 currentPartitions.addAll(partitionChangeEvent.getPartitions()); 104 currentPartitions.addAll(partitionChangeEvent.getPartitions());
@@ -201,6 +201,7 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer @@ -201,6 +201,7 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer
201 } 201 }
202 202
203 @Override 203 @Override
  204 +
204 public void saveAndNotify(TenantId tenantId, EntityId entityId, String scope, List<AttributeKvEntry> attributes, FutureCallback<Void> callback) { 205 public void saveAndNotify(TenantId tenantId, EntityId entityId, String scope, List<AttributeKvEntry> attributes, FutureCallback<Void> callback) {
205 saveAndNotify(tenantId, entityId, scope, attributes, true, callback); 206 saveAndNotify(tenantId, entityId, scope, attributes, true, callback);
206 } 207 }
@@ -51,22 +51,21 @@ import org.thingsboard.server.service.security.ValidationResult; @@ -51,22 +51,21 @@ import org.thingsboard.server.service.security.ValidationResult;
51 import org.thingsboard.server.service.security.ValidationResultCode; 51 import org.thingsboard.server.service.security.ValidationResultCode;
52 import org.thingsboard.server.service.security.model.UserPrincipal; 52 import org.thingsboard.server.service.security.model.UserPrincipal;
53 import org.thingsboard.server.service.security.permission.Operation; 53 import org.thingsboard.server.service.security.permission.Operation;
  54 +import org.thingsboard.server.service.subscription.TbAttributeSubscription;
  55 +import org.thingsboard.server.service.subscription.TbAttributeSubscriptionScope;
54 import org.thingsboard.server.service.subscription.TbEntityDataSubscriptionService; 56 import org.thingsboard.server.service.subscription.TbEntityDataSubscriptionService;
55 import org.thingsboard.server.service.subscription.TbLocalSubscriptionService; 57 import org.thingsboard.server.service.subscription.TbLocalSubscriptionService;
56 -import org.thingsboard.server.service.subscription.TbAttributeSubscriptionScope;  
57 -import org.thingsboard.server.service.subscription.TbAttributeSubscription;  
58 import org.thingsboard.server.service.subscription.TbTimeseriesSubscription; 58 import org.thingsboard.server.service.subscription.TbTimeseriesSubscription;
  59 +import org.thingsboard.server.service.telemetry.cmd.TelemetryPluginCmdsWrapper;
59 import org.thingsboard.server.service.telemetry.cmd.v1.AttributesSubscriptionCmd; 60 import org.thingsboard.server.service.telemetry.cmd.v1.AttributesSubscriptionCmd;
60 import org.thingsboard.server.service.telemetry.cmd.v1.GetHistoryCmd; 61 import org.thingsboard.server.service.telemetry.cmd.v1.GetHistoryCmd;
61 import org.thingsboard.server.service.telemetry.cmd.v1.SubscriptionCmd; 62 import org.thingsboard.server.service.telemetry.cmd.v1.SubscriptionCmd;
62 import org.thingsboard.server.service.telemetry.cmd.v1.TelemetryPluginCmd; 63 import org.thingsboard.server.service.telemetry.cmd.v1.TelemetryPluginCmd;
63 -import org.thingsboard.server.service.telemetry.cmd.TelemetryPluginCmdsWrapper;  
64 import org.thingsboard.server.service.telemetry.cmd.v1.TimeseriesSubscriptionCmd; 64 import org.thingsboard.server.service.telemetry.cmd.v1.TimeseriesSubscriptionCmd;
65 import org.thingsboard.server.service.telemetry.cmd.v2.AlarmDataCmd; 65 import org.thingsboard.server.service.telemetry.cmd.v2.AlarmDataCmd;
66 -import org.thingsboard.server.service.telemetry.cmd.v2.AlarmDataUnsubscribeCmd;  
67 -import org.thingsboard.server.service.telemetry.cmd.v2.DataUpdate; 66 +import org.thingsboard.server.service.telemetry.cmd.v2.CmdUpdate;
  67 +import org.thingsboard.server.service.telemetry.cmd.v2.EntityCountCmd;
68 import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataCmd; 68 import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataCmd;
69 -import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUnsubscribeCmd;  
70 import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUpdate; 69 import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUpdate;
71 import org.thingsboard.server.service.telemetry.cmd.v2.UnsubscribeCmd; 70 import org.thingsboard.server.service.telemetry.cmd.v2.UnsubscribeCmd;
72 import org.thingsboard.server.service.telemetry.exception.UnauthorizedException; 71 import org.thingsboard.server.service.telemetry.exception.UnauthorizedException;
@@ -89,6 +88,8 @@ import java.util.concurrent.ConcurrentHashMap; @@ -89,6 +88,8 @@ import java.util.concurrent.ConcurrentHashMap;
89 import java.util.concurrent.ConcurrentMap; 88 import java.util.concurrent.ConcurrentMap;
90 import java.util.concurrent.ExecutorService; 89 import java.util.concurrent.ExecutorService;
91 import java.util.concurrent.Executors; 90 import java.util.concurrent.Executors;
  91 +import java.util.concurrent.ScheduledExecutorService;
  92 +import java.util.concurrent.TimeUnit;
92 import java.util.function.Consumer; 93 import java.util.function.Consumer;
93 import java.util.stream.Collectors; 94 import java.util.stream.Collectors;
94 95
@@ -151,14 +152,23 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi @@ -151,14 +152,23 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
151 private ExecutorService executor; 152 private ExecutorService executor;
152 private String serviceId; 153 private String serviceId;
153 154
  155 + private ScheduledExecutorService pingExecutor;
  156 +
154 @PostConstruct 157 @PostConstruct
155 public void initExecutor() { 158 public void initExecutor() {
156 serviceId = serviceInfoProvider.getServiceId(); 159 serviceId = serviceInfoProvider.getServiceId();
157 executor = Executors.newWorkStealingPool(50); 160 executor = Executors.newWorkStealingPool(50);
  161 +
  162 + pingExecutor = Executors.newSingleThreadScheduledExecutor();
  163 + pingExecutor.scheduleWithFixedDelay(this::sendPing, 10000, 10000, TimeUnit.MILLISECONDS);
158 } 164 }
159 165
160 @PreDestroy 166 @PreDestroy
161 public void shutdownExecutor() { 167 public void shutdownExecutor() {
  168 + if (pingExecutor != null) {
  169 + pingExecutor.shutdownNow();
  170 + }
  171 +
162 if (executor != null) { 172 if (executor != null) {
163 executor.shutdownNow(); 173 executor.shutdownNow();
164 } 174 }
@@ -216,12 +226,18 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi @@ -216,12 +226,18 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
216 if (cmdsWrapper.getAlarmDataCmds() != null) { 226 if (cmdsWrapper.getAlarmDataCmds() != null) {
217 cmdsWrapper.getAlarmDataCmds().forEach(cmd -> handleWsAlarmDataCmd(sessionRef, cmd)); 227 cmdsWrapper.getAlarmDataCmds().forEach(cmd -> handleWsAlarmDataCmd(sessionRef, cmd));
218 } 228 }
  229 + if (cmdsWrapper.getEntityCountCmds() != null) {
  230 + cmdsWrapper.getEntityCountCmds().forEach(cmd -> handleWsEntityCountCmd(sessionRef, cmd));
  231 + }
219 if (cmdsWrapper.getEntityDataUnsubscribeCmds() != null) { 232 if (cmdsWrapper.getEntityDataUnsubscribeCmds() != null) {
220 cmdsWrapper.getEntityDataUnsubscribeCmds().forEach(cmd -> handleWsDataUnsubscribeCmd(sessionRef, cmd)); 233 cmdsWrapper.getEntityDataUnsubscribeCmds().forEach(cmd -> handleWsDataUnsubscribeCmd(sessionRef, cmd));
221 } 234 }
222 if (cmdsWrapper.getAlarmDataUnsubscribeCmds() != null) { 235 if (cmdsWrapper.getAlarmDataUnsubscribeCmds() != null) {
223 cmdsWrapper.getAlarmDataUnsubscribeCmds().forEach(cmd -> handleWsDataUnsubscribeCmd(sessionRef, cmd)); 236 cmdsWrapper.getAlarmDataUnsubscribeCmds().forEach(cmd -> handleWsDataUnsubscribeCmd(sessionRef, cmd));
224 } 237 }
  238 + if (cmdsWrapper.getEntityCountUnsubscribeCmds() != null) {
  239 + cmdsWrapper.getEntityCountUnsubscribeCmds().forEach(cmd -> handleWsDataUnsubscribeCmd(sessionRef, cmd));
  240 + }
225 } 241 }
226 } catch (IOException e) { 242 } catch (IOException e) {
227 log.warn("Failed to decode subscription cmd: {}", e.getMessage(), e); 243 log.warn("Failed to decode subscription cmd: {}", e.getMessage(), e);
@@ -239,6 +255,16 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi @@ -239,6 +255,16 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
239 } 255 }
240 } 256 }
241 257
  258 + private void handleWsEntityCountCmd(TelemetryWebSocketSessionRef sessionRef, EntityCountCmd cmd) {
  259 + String sessionId = sessionRef.getSessionId();
  260 + log.debug("[{}] Processing: {}", sessionId, cmd);
  261 +
  262 + if (validateSessionMetadata(sessionRef, cmd.getCmdId(), sessionId)
  263 + && validateSubscriptionCmd(sessionRef, cmd)) {
  264 + entityDataSubService.handleCmd(sessionRef, cmd);
  265 + }
  266 + }
  267 +
242 private void handleWsAlarmDataCmd(TelemetryWebSocketSessionRef sessionRef, AlarmDataCmd cmd) { 268 private void handleWsAlarmDataCmd(TelemetryWebSocketSessionRef sessionRef, AlarmDataCmd cmd) {
243 String sessionId = sessionRef.getSessionId(); 269 String sessionId = sessionRef.getSessionId();
244 log.debug("[{}] Processing: {}", sessionId, cmd); 270 log.debug("[{}] Processing: {}", sessionId, cmd);
@@ -264,7 +290,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi @@ -264,7 +290,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
264 } 290 }
265 291
266 @Override 292 @Override
267 - public void sendWsMsg(String sessionId, DataUpdate update) { 293 + public void sendWsMsg(String sessionId, CmdUpdate update) {
268 sendWsMsg(sessionId, update.getCmdId(), update); 294 sendWsMsg(sessionId, update.getCmdId(), update);
269 } 295 }
270 296
@@ -679,6 +705,20 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi @@ -679,6 +705,20 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
679 return true; 705 return true;
680 } 706 }
681 707
  708 + private boolean validateSubscriptionCmd(TelemetryWebSocketSessionRef sessionRef, EntityCountCmd cmd) {
  709 + if (cmd.getCmdId() < 0) {
  710 + TelemetrySubscriptionUpdate update = new TelemetrySubscriptionUpdate(cmd.getCmdId(), SubscriptionErrorCode.BAD_REQUEST,
  711 + "Cmd id is negative value!");
  712 + sendWsMsg(sessionRef, update);
  713 + return false;
  714 + } else if (cmd.getQuery() == null) {
  715 + TelemetrySubscriptionUpdate update = new TelemetrySubscriptionUpdate(cmd.getCmdId(), SubscriptionErrorCode.BAD_REQUEST, "Query is empty!");
  716 + sendWsMsg(sessionRef, update);
  717 + return false;
  718 + }
  719 + return true;
  720 + }
  721 +
682 private boolean validateSubscriptionCmd(TelemetryWebSocketSessionRef sessionRef, AlarmDataCmd cmd) { 722 private boolean validateSubscriptionCmd(TelemetryWebSocketSessionRef sessionRef, AlarmDataCmd cmd) {
683 if (cmd.getCmdId() < 0) { 723 if (cmd.getCmdId() < 0) {
684 TelemetrySubscriptionUpdate update = new TelemetrySubscriptionUpdate(cmd.getCmdId(), SubscriptionErrorCode.BAD_REQUEST, 724 TelemetrySubscriptionUpdate update = new TelemetrySubscriptionUpdate(cmd.getCmdId(), SubscriptionErrorCode.BAD_REQUEST,
@@ -744,6 +784,17 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi @@ -744,6 +784,17 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
744 } 784 }
745 } 785 }
746 786
  787 + private void sendPing() {
  788 + long currentTime = System.currentTimeMillis();
  789 + wsSessionsMap.values().forEach(md ->
  790 + executor.submit(() -> {
  791 + try {
  792 + msgEndpoint.sendPing(md.getSessionRef(), currentTime);
  793 + } catch (IOException e) {
  794 + log.warn("[{}] Failed to send ping: {}", md.getSessionRef().getSessionId(), e);
  795 + }
  796 + }));
  797 + }
747 798
748 private static Optional<Set<String>> getKeys(TelemetryPluginCmd cmd) { 799 private static Optional<Set<String>> getKeys(TelemetryPluginCmd cmd) {
749 if (!StringUtils.isEmpty(cmd.getKeys())) { 800 if (!StringUtils.isEmpty(cmd.getKeys())) {
@@ -26,5 +26,7 @@ public interface TelemetryWebSocketMsgEndpoint { @@ -26,5 +26,7 @@ public interface TelemetryWebSocketMsgEndpoint {
26 26
27 void send(TelemetryWebSocketSessionRef sessionRef, int subscriptionId, String msg) throws IOException; 27 void send(TelemetryWebSocketSessionRef sessionRef, int subscriptionId, String msg) throws IOException;
28 28
  29 + void sendPing(TelemetryWebSocketSessionRef sessionRef, long currentTime) throws IOException;
  30 +
29 void close(TelemetryWebSocketSessionRef sessionRef, CloseStatus withReason) throws IOException; 31 void close(TelemetryWebSocketSessionRef sessionRef, CloseStatus withReason) throws IOException;
30 } 32 }
@@ -15,6 +15,7 @@ @@ -15,6 +15,7 @@
15 */ 15 */
16 package org.thingsboard.server.service.telemetry; 16 package org.thingsboard.server.service.telemetry;
17 17
  18 +import org.thingsboard.server.service.telemetry.cmd.v2.CmdUpdate;
18 import org.thingsboard.server.service.telemetry.cmd.v2.DataUpdate; 19 import org.thingsboard.server.service.telemetry.cmd.v2.DataUpdate;
19 import org.thingsboard.server.service.telemetry.sub.TelemetrySubscriptionUpdate; 20 import org.thingsboard.server.service.telemetry.sub.TelemetrySubscriptionUpdate;
20 21
@@ -29,6 +30,6 @@ public interface TelemetryWebSocketService { @@ -29,6 +30,6 @@ public interface TelemetryWebSocketService {
29 30
30 void sendWsMsg(String sessionId, TelemetrySubscriptionUpdate update); 31 void sendWsMsg(String sessionId, TelemetrySubscriptionUpdate update);
31 32
32 - void sendWsMsg(String sessionId, DataUpdate update); 33 + void sendWsMsg(String sessionId, CmdUpdate update);
33 34
34 } 35 }
@@ -21,6 +21,8 @@ import org.thingsboard.server.service.telemetry.cmd.v1.GetHistoryCmd; @@ -21,6 +21,8 @@ import org.thingsboard.server.service.telemetry.cmd.v1.GetHistoryCmd;
21 import org.thingsboard.server.service.telemetry.cmd.v1.TimeseriesSubscriptionCmd; 21 import org.thingsboard.server.service.telemetry.cmd.v1.TimeseriesSubscriptionCmd;
22 import org.thingsboard.server.service.telemetry.cmd.v2.AlarmDataCmd; 22 import org.thingsboard.server.service.telemetry.cmd.v2.AlarmDataCmd;
23 import org.thingsboard.server.service.telemetry.cmd.v2.AlarmDataUnsubscribeCmd; 23 import org.thingsboard.server.service.telemetry.cmd.v2.AlarmDataUnsubscribeCmd;
  24 +import org.thingsboard.server.service.telemetry.cmd.v2.EntityCountCmd;
  25 +import org.thingsboard.server.service.telemetry.cmd.v2.EntityCountUnsubscribeCmd;
24 import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataCmd; 26 import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataCmd;
25 import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUnsubscribeCmd; 27 import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUnsubscribeCmd;
26 28
@@ -46,4 +48,8 @@ public class TelemetryPluginCmdsWrapper { @@ -46,4 +48,8 @@ public class TelemetryPluginCmdsWrapper {
46 48
47 private List<AlarmDataUnsubscribeCmd> alarmDataUnsubscribeCmds; 49 private List<AlarmDataUnsubscribeCmd> alarmDataUnsubscribeCmds;
48 50
  51 + private List<EntityCountCmd> entityCountCmds;
  52 +
  53 + private List<EntityCountUnsubscribeCmd> entityCountUnsubscribeCmds;
  54 +
49 } 55 }
@@ -18,14 +18,14 @@ package org.thingsboard.server.service.telemetry.cmd.v2; @@ -18,14 +18,14 @@ package org.thingsboard.server.service.telemetry.cmd.v2;
18 import com.fasterxml.jackson.annotation.JsonCreator; 18 import com.fasterxml.jackson.annotation.JsonCreator;
19 import com.fasterxml.jackson.annotation.JsonProperty; 19 import com.fasterxml.jackson.annotation.JsonProperty;
20 import lombok.Getter; 20 import lombok.Getter;
21 -import lombok.NoArgsConstructor; 21 +import lombok.ToString;
22 import org.thingsboard.server.common.data.page.PageData; 22 import org.thingsboard.server.common.data.page.PageData;
23 import org.thingsboard.server.common.data.query.AlarmData; 23 import org.thingsboard.server.common.data.query.AlarmData;
24 -import org.thingsboard.server.common.data.query.EntityData;  
25 import org.thingsboard.server.service.telemetry.sub.SubscriptionErrorCode; 24 import org.thingsboard.server.service.telemetry.sub.SubscriptionErrorCode;
26 25
27 import java.util.List; 26 import java.util.List;
28 27
  28 +@ToString
29 public class AlarmDataUpdate extends DataUpdate<AlarmData> { 29 public class AlarmDataUpdate extends DataUpdate<AlarmData> {
30 30
31 @Getter 31 @Getter
@@ -44,8 +44,8 @@ public class AlarmDataUpdate extends DataUpdate<AlarmData> { @@ -44,8 +44,8 @@ public class AlarmDataUpdate extends DataUpdate<AlarmData> {
44 } 44 }
45 45
46 @Override 46 @Override
47 - public DataUpdateType getDataUpdateType() {  
48 - return DataUpdateType.ALARM_DATA; 47 + public CmdUpdateType getCmdUpdateType() {
  48 + return CmdUpdateType.ALARM_DATA;
49 } 49 }
50 50
51 @JsonCreator 51 @JsonCreator
  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.telemetry.cmd.v2;
  17 +
  18 +import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
  19 +import lombok.AllArgsConstructor;
  20 +import lombok.Data;
  21 +
  22 +@Data
  23 +@AllArgsConstructor
  24 +@JsonIgnoreProperties(ignoreUnknown = true)
  25 +public abstract class CmdUpdate {
  26 +
  27 + private final int cmdId;
  28 + private final int errorCode;
  29 + private final String errorMsg;
  30 +
  31 + public abstract CmdUpdateType getCmdUpdateType();
  32 +
  33 +}
application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/CmdUpdateType.java renamed from application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/DataUpdateType.java
@@ -15,7 +15,8 @@ @@ -15,7 +15,8 @@
15 */ 15 */
16 package org.thingsboard.server.service.telemetry.cmd.v2; 16 package org.thingsboard.server.service.telemetry.cmd.v2;
17 17
18 -public enum DataUpdateType { 18 +public enum CmdUpdateType {
19 ENTITY_DATA, 19 ENTITY_DATA,
20 - ALARM_DATA 20 + ALARM_DATA,
  21 + COUNT_DATA
21 } 22 }
@@ -15,24 +15,24 @@ @@ -15,24 +15,24 @@
15 */ 15 */
16 package org.thingsboard.server.service.telemetry.cmd.v2; 16 package org.thingsboard.server.service.telemetry.cmd.v2;
17 17
18 -import com.fasterxml.jackson.annotation.JsonIgnoreProperties;  
19 -import lombok.AllArgsConstructor;  
20 -import lombok.Data; 18 +import lombok.Getter;
21 import org.thingsboard.server.common.data.page.PageData; 19 import org.thingsboard.server.common.data.page.PageData;
22 import org.thingsboard.server.service.telemetry.sub.SubscriptionErrorCode; 20 import org.thingsboard.server.service.telemetry.sub.SubscriptionErrorCode;
23 21
24 import java.util.List; 22 import java.util.List;
25 23
26 -@Data  
27 -@AllArgsConstructor  
28 -@JsonIgnoreProperties(ignoreUnknown = true)  
29 -public abstract class DataUpdate<T> { 24 +public abstract class DataUpdate<T> extends CmdUpdate {
30 25
31 - private final int cmdId; 26 + @Getter
32 private final PageData<T> data; 27 private final PageData<T> data;
  28 + @Getter
33 private final List<T> update; 29 private final List<T> update;
34 - private final int errorCode;  
35 - private final String errorMsg; 30 +
  31 + public DataUpdate(int cmdId, PageData<T> data, List<T> update, int errorCode, String errorMsg) {
  32 + super(cmdId, errorCode, errorMsg);
  33 + this.data = data;
  34 + this.update = update;
  35 + }
36 36
37 public DataUpdate(int cmdId, PageData<T> data, List<T> update) { 37 public DataUpdate(int cmdId, PageData<T> data, List<T> update) {
38 this(cmdId, data, update, SubscriptionErrorCode.NO_ERROR.getCode(), null); 38 this(cmdId, data, update, SubscriptionErrorCode.NO_ERROR.getCode(), null);
@@ -42,5 +42,4 @@ public abstract class DataUpdate<T> { @@ -42,5 +42,4 @@ public abstract class DataUpdate<T> {
42 this(cmdId, null, null, errorCode, errorMsg); 42 this(cmdId, null, null, errorCode, errorMsg);
43 } 43 }
44 44
45 - public abstract DataUpdateType getDataUpdateType();  
46 } 45 }
  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.telemetry.cmd.v2;
  17 +
  18 +import com.fasterxml.jackson.annotation.JsonCreator;
  19 +import com.fasterxml.jackson.annotation.JsonProperty;
  20 +import lombok.Getter;
  21 +import org.thingsboard.server.common.data.query.EntityCountQuery;
  22 +import org.thingsboard.server.common.data.query.EntityDataQuery;
  23 +
  24 +public class EntityCountCmd extends DataCmd {
  25 +
  26 + @Getter
  27 + private final EntityCountQuery query;
  28 +
  29 + @JsonCreator
  30 + public EntityCountCmd(@JsonProperty("cmdId") int cmdId,
  31 + @JsonProperty("query") EntityCountQuery query) {
  32 + super(cmdId);
  33 + this.query = query;
  34 + }
  35 +}
  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.telemetry.cmd.v2;
  17 +
  18 +import lombok.Data;
  19 +
  20 +@Data
  21 +public class EntityCountUnsubscribeCmd implements UnsubscribeCmd {
  22 +
  23 + private final int cmdId;
  24 +
  25 +}