Commit f673b0944bd08c144b0ffb8d09ce6c29ac25a082
Committed by
GitHub
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; |
application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeActorMessageProcessor.java
@@ -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() { |
application/src/main/java/org/thingsboard/server/actors/ruleChain/TbToRuleNodeActorMsg.java
0 → 100644
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 |
application/src/main/java/org/thingsboard/server/service/apiusage/DefaultTbApiUsageStateService.java
@@ -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); |
application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java
@@ -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") |
application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MModelsRepository.java
0 → 100644
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); |
application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/CookieUtils.java
0 → 100644
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 | } |
application/src/main/java/org/thingsboard/server/service/subscription/TbAbstractSubCtx.java
0 → 100644
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); |
application/src/main/java/org/thingsboard/server/service/subscription/TbEntityCountSubCtx.java
0 → 100644
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 | } |
application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/EntityCountCmd.java
0 → 100644
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 | +} |