Commit 712a14ec22f75ac4623626f5b1ed3a4471f337c7

Authored by Artem Babak
2 parents 8936b868 10d3dd01

Merge remote-tracking branch 'origin/develop/3.3-edge' into develop/3.3-edge

Showing 100 changed files with 1170 additions and 391 deletions

Too many changes to show.

To preserve performance only 100 of 376 files are displayed.

@@ -283,7 +283,7 @@ @@ -283,7 +283,7 @@
283 </dependency> 283 </dependency>
284 <dependency> 284 <dependency>
285 <groupId>org.mockito</groupId> 285 <groupId>org.mockito</groupId>
286 - <artifactId>mockito-all</artifactId> 286 + <artifactId>mockito-core</artifactId>
287 <scope>test</scope> 287 <scope>test</scope>
288 </dependency> 288 </dependency>
289 <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
@@ -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 +}
@@ -141,12 +141,12 @@ public class AppActor extends ContextAwareActor { @@ -141,12 +141,12 @@ public class AppActor extends ContextAwareActor {
141 141
142 private void onQueueToRuleEngineMsg(QueueToRuleEngineMsg msg) { 142 private void onQueueToRuleEngineMsg(QueueToRuleEngineMsg msg) {
143 if (TenantId.SYS_TENANT_ID.equals(msg.getTenantId())) { 143 if (TenantId.SYS_TENANT_ID.equals(msg.getTenantId())) {
144 - msg.getTbMsg().getCallback().onFailure(new RuleEngineException("Message has system tenant id!")); 144 + msg.getMsg().getCallback().onFailure(new RuleEngineException("Message has system tenant id!"));
145 } else { 145 } else {
146 if (!deletedTenants.contains(msg.getTenantId())) { 146 if (!deletedTenants.contains(msg.getTenantId())) {
147 getOrCreateTenantActor(msg.getTenantId()).tell(msg); 147 getOrCreateTenantActor(msg.getTenantId()).tell(msg);
148 } else { 148 } else {
149 - msg.getTbMsg().getCallback().onSuccess(); 149 + msg.getMsg().getCallback().onSuccess();
150 } 150 }
151 } 151 }
152 } 152 }
@@ -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;
@@ -92,10 +91,12 @@ class DefaultTbContext implements TbContext { @@ -92,10 +91,12 @@ class DefaultTbContext implements TbContext {
92 public final static ObjectMapper mapper = new ObjectMapper(); 91 public final static ObjectMapper mapper = new ObjectMapper();
93 92
94 private final ActorSystemContext mainCtx; 93 private final ActorSystemContext mainCtx;
  94 + private final String ruleChainName;
95 private final RuleNodeCtx nodeCtx; 95 private final RuleNodeCtx nodeCtx;
96 96
97 - public DefaultTbContext(ActorSystemContext mainCtx, RuleNodeCtx nodeCtx) { 97 + public DefaultTbContext(ActorSystemContext mainCtx, String ruleChainName, RuleNodeCtx nodeCtx) {
98 this.mainCtx = mainCtx; 98 this.mainCtx = mainCtx;
  99 + this.ruleChainName = ruleChainName;
99 this.nodeCtx = nodeCtx; 100 this.nodeCtx = nodeCtx;
100 } 101 }
101 102
@@ -119,13 +120,13 @@ class DefaultTbContext implements TbContext { @@ -119,13 +120,13 @@ class DefaultTbContext implements TbContext {
119 relationTypes.forEach(relationType -> mainCtx.persistDebugOutput(nodeCtx.getTenantId(), nodeCtx.getSelf().getId(), msg, relationType, th)); 120 relationTypes.forEach(relationType -> mainCtx.persistDebugOutput(nodeCtx.getTenantId(), nodeCtx.getSelf().getId(), msg, relationType, th));
120 } 121 }
121 msg.getCallback().onProcessingEnd(nodeCtx.getSelf().getId()); 122 msg.getCallback().onProcessingEnd(nodeCtx.getSelf().getId());
122 - nodeCtx.getChainActor().tell(new RuleNodeToRuleChainTellNextMsg(nodeCtx.getSelf().getId(), relationTypes, msg, th != null ? th.getMessage() : null)); 123 + nodeCtx.getChainActor().tell(new RuleNodeToRuleChainTellNextMsg(nodeCtx.getSelf().getRuleChainId(), nodeCtx.getSelf().getId(), relationTypes, msg, th != null ? th.getMessage() : null));
123 } 124 }
124 125
125 @Override 126 @Override
126 public void tellSelf(TbMsg msg, long delayMs) { 127 public void tellSelf(TbMsg msg, long delayMs) {
127 //TODO: add persistence layer 128 //TODO: add persistence layer
128 - scheduleMsgWithDelay(new RuleNodeToSelfMsg(msg), delayMs, nodeCtx.getSelfActor()); 129 + scheduleMsgWithDelay(new RuleNodeToSelfMsg(this, msg), delayMs, nodeCtx.getSelfActor());
129 } 130 }
130 131
131 @Override 132 @Override
@@ -256,7 +257,8 @@ class DefaultTbContext implements TbContext { @@ -256,7 +257,8 @@ class DefaultTbContext implements TbContext {
256 } else { 257 } else {
257 failureMessage = null; 258 failureMessage = null;
258 } 259 }
259 - nodeCtx.getChainActor().tell(new RuleNodeToRuleChainTellNextMsg(nodeCtx.getSelf().getId(), Collections.singleton(TbRelationTypes.FAILURE), 260 + nodeCtx.getChainActor().tell(new RuleNodeToRuleChainTellNextMsg(nodeCtx.getSelf().getRuleChainId(),
  261 + nodeCtx.getSelf().getId(), Collections.singleton(TbRelationTypes.FAILURE),
260 msg, failureMessage)); 262 msg, failureMessage));
261 } 263 }
262 264
@@ -309,6 +311,16 @@ class DefaultTbContext implements TbContext { @@ -309,6 +311,16 @@ class DefaultTbContext implements TbContext {
309 } 311 }
310 312
311 @Override 313 @Override
  314 + public RuleNode getSelf() {
  315 + return nodeCtx.getSelf();
  316 + }
  317 +
  318 + @Override
  319 + public String getRuleChainName() {
  320 + return ruleChainName;
  321 + }
  322 +
  323 + @Override
312 public TenantId getTenantId() { 324 public TenantId getTenantId() {
313 return nodeCtx.getTenantId(); 325 return nodeCtx.getTenantId();
314 } 326 }
@@ -493,11 +505,6 @@ class DefaultTbContext implements TbContext { @@ -493,11 +505,6 @@ class DefaultTbContext implements TbContext {
493 } 505 }
494 506
495 @Override 507 @Override
496 - public RedisTemplate<String, Object> getRedisTemplate() {  
497 - return mainCtx.getRedisTemplate();  
498 - }  
499 -  
500 - @Override  
501 public PageData<RuleNodeState> findRuleNodeStates(PageLink pageLink) { 508 public PageData<RuleNodeState> findRuleNodeStates(PageLink pageLink) {
502 if (log.isDebugEnabled()) { 509 if (log.isDebugEnabled()) {
503 log.debug("[{}][{}] Fetch Rule Node States.", getTenantId(), getSelfId()); 510 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;
@@ -37,6 +36,7 @@ import org.thingsboard.server.common.data.rule.RuleChainType; @@ -37,6 +36,7 @@ import org.thingsboard.server.common.data.rule.RuleChainType;
37 import org.thingsboard.server.common.data.rule.RuleNode; 36 import org.thingsboard.server.common.data.rule.RuleNode;
38 import org.thingsboard.server.common.msg.TbMsg; 37 import org.thingsboard.server.common.msg.TbMsg;
39 import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; 38 import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
  39 +import org.thingsboard.server.common.msg.plugin.RuleNodeUpdatedMsg;
40 import org.thingsboard.server.common.msg.queue.PartitionChangeMsg; 40 import org.thingsboard.server.common.msg.queue.PartitionChangeMsg;
41 import org.thingsboard.server.common.msg.queue.QueueToRuleEngineMsg; 41 import org.thingsboard.server.common.msg.queue.QueueToRuleEngineMsg;
42 import org.thingsboard.server.common.msg.queue.RuleEngineException; 42 import org.thingsboard.server.common.msg.queue.RuleEngineException;
@@ -133,7 +133,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh @@ -133,7 +133,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
133 } else { 133 } else {
134 log.trace("[{}][{}] Updating rule node [{}]: {}", entityId, ruleNode.getId(), ruleNode.getName(), ruleNode); 134 log.trace("[{}][{}] Updating rule node [{}]: {}", entityId, ruleNode.getId(), ruleNode.getName(), ruleNode);
135 existing.setSelf(ruleNode); 135 existing.setSelf(ruleNode);
136 - existing.getSelfActor().tellWithHighPriority(new ComponentLifecycleMsg(tenantId, existing.getSelf().getId(), ComponentLifecycleEvent.UPDATED)); 136 + existing.getSelfActor().tellWithHighPriority(new RuleNodeUpdatedMsg(tenantId, existing.getSelf().getId()));
137 } 137 }
138 } 138 }
139 139
@@ -198,11 +198,11 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh @@ -198,11 +198,11 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
198 } 198 }
199 199
200 void onQueueToRuleEngineMsg(QueueToRuleEngineMsg envelope) { 200 void onQueueToRuleEngineMsg(QueueToRuleEngineMsg envelope) {
201 - TbMsg msg = envelope.getTbMsg(); 201 + TbMsg msg = envelope.getMsg();
202 log.trace("[{}][{}] Processing message [{}]: {}", entityId, firstId, msg.getId(), msg); 202 log.trace("[{}][{}] Processing message [{}]: {}", entityId, firstId, msg.getId(), msg);
203 if (envelope.getRelationTypes() == null || envelope.getRelationTypes().isEmpty()) { 203 if (envelope.getRelationTypes() == null || envelope.getRelationTypes().isEmpty()) {
204 try { 204 try {
205 - checkActive(envelope.getTbMsg()); 205 + checkActive(envelope.getMsg());
206 RuleNodeId targetId = msg.getRuleNodeId(); 206 RuleNodeId targetId = msg.getRuleNodeId();
207 RuleNodeCtx targetCtx; 207 RuleNodeCtx targetCtx;
208 if (targetId == null) { 208 if (targetId == null) {
@@ -219,12 +219,12 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh @@ -219,12 +219,12 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
219 msg.getCallback().onSuccess(); 219 msg.getCallback().onSuccess();
220 } 220 }
221 } catch (RuleNodeException rne) { 221 } catch (RuleNodeException rne) {
222 - envelope.getTbMsg().getCallback().onFailure(rne); 222 + envelope.getMsg().getCallback().onFailure(rne);
223 } catch (Exception e) { 223 } catch (Exception e) {
224 - envelope.getTbMsg().getCallback().onFailure(new RuleEngineException(e.getMessage())); 224 + envelope.getMsg().getCallback().onFailure(new RuleEngineException(e.getMessage()));
225 } 225 }
226 } else { 226 } else {
227 - onTellNext(envelope.getTbMsg(), envelope.getTbMsg().getRuleNodeId(), envelope.getRelationTypes(), envelope.getFailureMessage()); 227 + onTellNext(envelope.getMsg(), envelope.getMsg().getRuleNodeId(), envelope.getRelationTypes(), envelope.getFailureMessage());
228 } 228 }
229 } 229 }
230 230
@@ -336,7 +336,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh @@ -336,7 +336,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
336 336
337 private void pushMsgToNode(RuleNodeCtx nodeCtx, TbMsg msg, String fromRelationType) { 337 private void pushMsgToNode(RuleNodeCtx nodeCtx, TbMsg msg, String fromRelationType) {
338 if (nodeCtx != null) { 338 if (nodeCtx != null) {
339 - nodeCtx.getSelfActor().tell(new RuleChainToRuleNodeMsg(new DefaultTbContext(systemContext, nodeCtx), msg, fromRelationType)); 339 + nodeCtx.getSelfActor().tell(new RuleChainToRuleNodeMsg(new DefaultTbContext(systemContext, ruleChainName, nodeCtx), msg, fromRelationType));
340 } else { 340 } else {
341 log.error("[{}][{}] RuleNodeCtx is empty", entityId, ruleChainName); 341 log.error("[{}][{}] RuleNodeCtx is empty", entityId, ruleChainName);
342 msg.getCallback().onFailure(new RuleEngineException("Rule Node CTX is empty")); 342 msg.getCallback().onFailure(new RuleEngineException("Rule Node CTX is empty"));
@@ -15,24 +15,44 @@ @@ -15,24 +15,44 @@
15 */ 15 */
16 package org.thingsboard.server.actors.ruleChain; 16 package org.thingsboard.server.actors.ruleChain;
17 17
18 -import lombok.Data; 18 +import lombok.EqualsAndHashCode;
  19 +import lombok.Getter;
  20 +import lombok.ToString;
19 import org.thingsboard.server.common.data.id.RuleChainId; 21 import org.thingsboard.server.common.data.id.RuleChainId;
20 import org.thingsboard.server.common.msg.MsgType; 22 import org.thingsboard.server.common.msg.MsgType;
21 -import org.thingsboard.server.common.msg.TbActorMsg; 23 +import org.thingsboard.server.common.msg.TbActorStopReason;
22 import org.thingsboard.server.common.msg.TbMsg; 24 import org.thingsboard.server.common.msg.TbMsg;
  25 +import org.thingsboard.server.common.msg.TbRuleEngineActorMsg;
23 import org.thingsboard.server.common.msg.aware.RuleChainAwareMsg; 26 import org.thingsboard.server.common.msg.aware.RuleChainAwareMsg;
  27 +import org.thingsboard.server.common.msg.queue.RuleEngineException;
24 28
25 /** 29 /**
26 * Created by ashvayka on 19.03.18. 30 * Created by ashvayka on 19.03.18.
27 */ 31 */
28 -@Data  
29 -public final class RuleChainToRuleChainMsg implements TbActorMsg, RuleChainAwareMsg { 32 +@EqualsAndHashCode(callSuper = true)
  33 +@ToString
  34 +public final class RuleChainToRuleChainMsg extends TbRuleEngineActorMsg implements RuleChainAwareMsg {
30 35
  36 + @Getter
31 private final RuleChainId target; 37 private final RuleChainId target;
  38 + @Getter
32 private final RuleChainId source; 39 private final RuleChainId source;
33 - private final TbMsg msg; 40 + @Getter
34 private final String fromRelationType; 41 private final String fromRelationType;
35 42
  43 + public RuleChainToRuleChainMsg(RuleChainId target, RuleChainId source, TbMsg tbMsg, String fromRelationType) {
  44 + super(tbMsg);
  45 + this.target = target;
  46 + this.source = source;
  47 + this.fromRelationType = fromRelationType;
  48 + }
  49 +
  50 + @Override
  51 + public void onTbActorStopped(TbActorStopReason reason) {
  52 + String message = reason == TbActorStopReason.STOPPED ? String.format("Rule chain [%s] stopped", target.getId()) : String.format("Failed to initialize rule chain [%s]!", target.getId());
  53 + msg.getCallback().onFailure(new RuleEngineException(message));
  54 + }
  55 +
36 @Override 56 @Override
37 public RuleChainId getRuleChainId() { 57 public RuleChainId getRuleChainId() {
38 return target; 58 return target;
@@ -15,22 +15,28 @@ @@ -15,22 +15,28 @@
15 */ 15 */
16 package org.thingsboard.server.actors.ruleChain; 16 package org.thingsboard.server.actors.ruleChain;
17 17
18 -import lombok.Data; 18 +import lombok.EqualsAndHashCode;
  19 +import lombok.Getter;
  20 +import lombok.ToString;
19 import org.thingsboard.rule.engine.api.TbContext; 21 import org.thingsboard.rule.engine.api.TbContext;
20 import org.thingsboard.server.common.msg.MsgType; 22 import org.thingsboard.server.common.msg.MsgType;
21 -import org.thingsboard.server.common.msg.TbActorMsg;  
22 import org.thingsboard.server.common.msg.TbMsg; 23 import org.thingsboard.server.common.msg.TbMsg;
23 24
24 /** 25 /**
25 * Created by ashvayka on 19.03.18. 26 * Created by ashvayka on 19.03.18.
26 */ 27 */
27 -@Data  
28 -final class RuleChainToRuleNodeMsg implements TbActorMsg { 28 +@EqualsAndHashCode(callSuper = true)
  29 +@ToString
  30 +final class RuleChainToRuleNodeMsg extends TbToRuleNodeActorMsg {
29 31
30 - private final TbContext ctx;  
31 - private final TbMsg msg; 32 + @Getter
32 private final String fromRelationType; 33 private final String fromRelationType;
33 34
  35 + public RuleChainToRuleNodeMsg(TbContext ctx, TbMsg tbMsg, String fromRelationType) {
  36 + super(ctx, tbMsg);
  37 + this.fromRelationType = fromRelationType;
  38 + }
  39 +
34 @Override 40 @Override
35 public MsgType getMsgType() { 41 public MsgType getMsgType() {
36 return MsgType.RULE_CHAIN_TO_RULE_MSG; 42 return MsgType.RULE_CHAIN_TO_RULE_MSG;
@@ -26,7 +26,6 @@ import org.thingsboard.server.actors.service.ContextBasedCreator; @@ -26,7 +26,6 @@ import org.thingsboard.server.actors.service.ContextBasedCreator;
26 import org.thingsboard.server.common.data.id.RuleChainId; 26 import org.thingsboard.server.common.data.id.RuleChainId;
27 import org.thingsboard.server.common.data.id.RuleNodeId; 27 import org.thingsboard.server.common.data.id.RuleNodeId;
28 import org.thingsboard.server.common.data.id.TenantId; 28 import org.thingsboard.server.common.data.id.TenantId;
29 -import org.thingsboard.server.common.data.rule.RuleChain;  
30 import org.thingsboard.server.common.msg.TbActorMsg; 29 import org.thingsboard.server.common.msg.TbActorMsg;
31 import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; 30 import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
32 import org.thingsboard.server.common.msg.queue.PartitionChangeMsg; 31 import org.thingsboard.server.common.msg.queue.PartitionChangeMsg;
@@ -54,14 +53,12 @@ public class RuleNodeActor extends ComponentActor<RuleNodeId, RuleNodeActorMessa @@ -54,14 +53,12 @@ public class RuleNodeActor extends ComponentActor<RuleNodeId, RuleNodeActorMessa
54 protected boolean doProcess(TbActorMsg msg) { 53 protected boolean doProcess(TbActorMsg msg) {
55 switch (msg.getMsgType()) { 54 switch (msg.getMsgType()) {
56 case COMPONENT_LIFE_CYCLE_MSG: 55 case COMPONENT_LIFE_CYCLE_MSG:
  56 + case RULE_NODE_UPDATED_MSG:
57 onComponentLifecycleMsg((ComponentLifecycleMsg) msg); 57 onComponentLifecycleMsg((ComponentLifecycleMsg) msg);
58 break; 58 break;
59 case RULE_CHAIN_TO_RULE_MSG: 59 case RULE_CHAIN_TO_RULE_MSG:
60 onRuleChainToRuleNodeMsg((RuleChainToRuleNodeMsg) msg); 60 onRuleChainToRuleNodeMsg((RuleChainToRuleNodeMsg) msg);
61 break; 61 break;
62 - case RULE_TO_SELF_ERROR_MSG:  
63 - onRuleNodeToSelfErrorMsg((RuleNodeToSelfErrorMsg) msg);  
64 - break;  
65 case RULE_TO_SELF_MSG: 62 case RULE_TO_SELF_MSG:
66 onRuleNodeToSelfMsg((RuleNodeToSelfMsg) msg); 63 onRuleNodeToSelfMsg((RuleNodeToSelfMsg) msg);
67 break; 64 break;
@@ -101,10 +98,6 @@ public class RuleNodeActor extends ComponentActor<RuleNodeId, RuleNodeActorMessa @@ -101,10 +98,6 @@ public class RuleNodeActor extends ComponentActor<RuleNodeId, RuleNodeActorMessa
101 } 98 }
102 } 99 }
103 100
104 - private void onRuleNodeToSelfErrorMsg(RuleNodeToSelfErrorMsg msg) {  
105 - logAndPersist("onRuleMsg", ActorSystemContext.toException(msg.getError()));  
106 - }  
107 -  
108 public static class ActorCreator extends ContextBasedCreator { 101 public static class ActorCreator extends ContextBasedCreator {
109 102
110 private final TenantId tenantId; 103 private final TenantId tenantId;
@@ -20,14 +20,13 @@ import org.thingsboard.rule.engine.api.TbNodeConfiguration; @@ -20,14 +20,13 @@ import org.thingsboard.rule.engine.api.TbNodeConfiguration;
20 import org.thingsboard.server.actors.ActorSystemContext; 20 import org.thingsboard.server.actors.ActorSystemContext;
21 import org.thingsboard.server.actors.TbActorCtx; 21 import org.thingsboard.server.actors.TbActorCtx;
22 import org.thingsboard.server.actors.TbActorRef; 22 import org.thingsboard.server.actors.TbActorRef;
  23 +import org.thingsboard.server.actors.TbRuleNodeUpdateException;
23 import org.thingsboard.server.actors.shared.ComponentMsgProcessor; 24 import org.thingsboard.server.actors.shared.ComponentMsgProcessor;
24 import org.thingsboard.server.common.data.ApiUsageRecordKey; 25 import org.thingsboard.server.common.data.ApiUsageRecordKey;
25 -import org.thingsboard.server.common.data.TenantProfile;  
26 import org.thingsboard.server.common.data.id.RuleNodeId; 26 import org.thingsboard.server.common.data.id.RuleNodeId;
27 import org.thingsboard.server.common.data.id.TenantId; 27 import org.thingsboard.server.common.data.id.TenantId;
28 import org.thingsboard.server.common.data.plugin.ComponentLifecycleState; 28 import org.thingsboard.server.common.data.plugin.ComponentLifecycleState;
29 import org.thingsboard.server.common.data.rule.RuleNode; 29 import org.thingsboard.server.common.data.rule.RuleNode;
30 -import org.thingsboard.server.common.data.tenant.profile.TenantProfileConfiguration;  
31 import org.thingsboard.server.common.msg.TbMsg; 30 import org.thingsboard.server.common.msg.TbMsg;
32 import org.thingsboard.server.common.msg.queue.PartitionChangeMsg; 31 import org.thingsboard.server.common.msg.queue.PartitionChangeMsg;
33 import org.thingsboard.server.common.msg.queue.RuleNodeException; 32 import org.thingsboard.server.common.msg.queue.RuleNodeException;
@@ -54,7 +53,7 @@ public class RuleNodeActorMessageProcessor extends ComponentMsgProcessor<RuleNod @@ -54,7 +53,7 @@ public class RuleNodeActorMessageProcessor extends ComponentMsgProcessor<RuleNod
54 this.ruleChainName = ruleChainName; 53 this.ruleChainName = ruleChainName;
55 this.self = self; 54 this.self = self;
56 this.ruleNode = systemContext.getRuleChainService().findRuleNodeById(tenantId, entityId); 55 this.ruleNode = systemContext.getRuleChainService().findRuleNodeById(tenantId, entityId);
57 - this.defaultCtx = new DefaultTbContext(systemContext, new RuleNodeCtx(tenantId, parent, self, ruleNode)); 56 + this.defaultCtx = new DefaultTbContext(systemContext, ruleChainName, new RuleNodeCtx(tenantId, parent, self, ruleNode));
58 this.info = new RuleNodeInfo(ruleNodeId, ruleChainName, ruleNode != null ? ruleNode.getName() : "Unknown"); 57 this.info = new RuleNodeInfo(ruleNodeId, ruleChainName, ruleNode != null ? ruleNode.getName() : "Unknown");
59 } 58 }
60 59
@@ -78,7 +77,11 @@ public class RuleNodeActorMessageProcessor extends ComponentMsgProcessor<RuleNod @@ -78,7 +77,11 @@ public class RuleNodeActorMessageProcessor extends ComponentMsgProcessor<RuleNod
78 if (tbNode != null) { 77 if (tbNode != null) {
79 tbNode.destroy(); 78 tbNode.destroy();
80 } 79 }
81 - start(context); 80 + try {
  81 + start(context);
  82 + } catch (Exception e) {
  83 + throw new TbRuleNodeUpdateException("Failed to update rule node", e);
  84 + }
82 } 85 }
83 } 86 }
84 87
@@ -147,7 +150,7 @@ public class RuleNodeActorMessageProcessor extends ComponentMsgProcessor<RuleNod @@ -147,7 +150,7 @@ public class RuleNodeActorMessageProcessor extends ComponentMsgProcessor<RuleNod
147 TbNode tbNode = null; 150 TbNode tbNode = null;
148 if (ruleNode != null) { 151 if (ruleNode != null) {
149 Class<?> componentClazz = Class.forName(ruleNode.getType()); 152 Class<?> componentClazz = Class.forName(ruleNode.getType());
150 - tbNode = (TbNode) (componentClazz.newInstance()); 153 + tbNode = (TbNode) (componentClazz.getDeclaredConstructor().newInstance());
151 tbNode.init(defaultCtx, new TbNodeConfiguration(ruleNode.getConfiguration())); 154 tbNode.init(defaultCtx, new TbNodeConfiguration(ruleNode.getConfiguration()));
152 } 155 }
153 return tbNode; 156 return tbNode;
@@ -15,11 +15,16 @@ @@ -15,11 +15,16 @@
15 */ 15 */
16 package org.thingsboard.server.actors.ruleChain; 16 package org.thingsboard.server.actors.ruleChain;
17 17
18 -import lombok.Data; 18 +import lombok.EqualsAndHashCode;
  19 +import lombok.Getter;
  20 +import lombok.ToString;
  21 +import org.thingsboard.server.common.data.id.RuleChainId;
19 import org.thingsboard.server.common.data.id.RuleNodeId; 22 import org.thingsboard.server.common.data.id.RuleNodeId;
20 import org.thingsboard.server.common.msg.MsgType; 23 import org.thingsboard.server.common.msg.MsgType;
21 -import org.thingsboard.server.common.msg.TbActorMsg; 24 +import org.thingsboard.server.common.msg.TbActorStopReason;
22 import org.thingsboard.server.common.msg.TbMsg; 25 import org.thingsboard.server.common.msg.TbMsg;
  26 +import org.thingsboard.server.common.msg.TbRuleEngineActorMsg;
  27 +import org.thingsboard.server.common.msg.queue.RuleEngineException;
23 28
24 import java.io.Serializable; 29 import java.io.Serializable;
25 import java.util.Set; 30 import java.util.Set;
@@ -27,15 +32,34 @@ import java.util.Set; @@ -27,15 +32,34 @@ import java.util.Set;
27 /** 32 /**
28 * Created by ashvayka on 19.03.18. 33 * Created by ashvayka on 19.03.18.
29 */ 34 */
30 -@Data  
31 -class RuleNodeToRuleChainTellNextMsg implements TbActorMsg, Serializable { 35 +@EqualsAndHashCode(callSuper = true)
  36 +@ToString
  37 +class RuleNodeToRuleChainTellNextMsg extends TbRuleEngineActorMsg implements Serializable {
32 38
33 private static final long serialVersionUID = 4577026446412871820L; 39 private static final long serialVersionUID = 4577026446412871820L;
  40 + @Getter
  41 + private final RuleChainId ruleChainId;
  42 + @Getter
34 private final RuleNodeId originator; 43 private final RuleNodeId originator;
  44 + @Getter
35 private final Set<String> relationTypes; 45 private final Set<String> relationTypes;
36 - private final TbMsg msg; 46 + @Getter
37 private final String failureMessage; 47 private final String failureMessage;
38 48
  49 + public RuleNodeToRuleChainTellNextMsg(RuleChainId ruleChainId, RuleNodeId originator, Set<String> relationTypes, TbMsg tbMsg, String failureMessage) {
  50 + super(tbMsg);
  51 + this.ruleChainId = ruleChainId;
  52 + this.originator = originator;
  53 + this.relationTypes = relationTypes;
  54 + this.failureMessage = failureMessage;
  55 + }
  56 +
  57 + @Override
  58 + public void onTbActorStopped(TbActorStopReason reason) {
  59 + String message = reason == TbActorStopReason.STOPPED ? String.format("Rule chain [%s] stopped", ruleChainId.getId()) : String.format("Failed to initialize rule chain [%s]!", ruleChainId.getId());
  60 + msg.getCallback().onFailure(new RuleEngineException(message));
  61 + }
  62 +
39 @Override 63 @Override
40 public MsgType getMsgType() { 64 public MsgType getMsgType() {
41 return MsgType.RULE_TO_RULE_CHAIN_TELL_NEXT_MSG; 65 return MsgType.RULE_TO_RULE_CHAIN_TELL_NEXT_MSG;
@@ -15,18 +15,25 @@ @@ -15,18 +15,25 @@
15 */ 15 */
16 package org.thingsboard.server.actors.ruleChain; 16 package org.thingsboard.server.actors.ruleChain;
17 17
18 -import lombok.Data; 18 +import lombok.EqualsAndHashCode;
  19 +import lombok.ToString;
  20 +import org.thingsboard.rule.engine.api.TbContext;
19 import org.thingsboard.server.common.msg.MsgType; 21 import org.thingsboard.server.common.msg.MsgType;
20 -import org.thingsboard.server.common.msg.TbActorMsg; 22 +import org.thingsboard.server.common.msg.TbActorStopReason;
21 import org.thingsboard.server.common.msg.TbMsg; 23 import org.thingsboard.server.common.msg.TbMsg;
  24 +import org.thingsboard.server.common.msg.TbRuleEngineActorMsg;
  25 +import org.thingsboard.server.common.msg.queue.RuleNodeException;
22 26
23 /** 27 /**
24 * Created by ashvayka on 19.03.18. 28 * Created by ashvayka on 19.03.18.
25 */ 29 */
26 -@Data  
27 -final class RuleNodeToSelfMsg implements TbActorMsg { 30 +@EqualsAndHashCode(callSuper = true)
  31 +@ToString
  32 +final class RuleNodeToSelfMsg extends TbToRuleNodeActorMsg {
28 33
29 - private final TbMsg msg; 34 + public RuleNodeToSelfMsg(TbContext ctx, TbMsg tbMsg) {
  35 + super(ctx, tbMsg);
  36 + }
30 37
31 @Override 38 @Override
32 public MsgType getMsgType() { 39 public MsgType getMsgType() {
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.actors.ruleChain;
  17 +
  18 +import lombok.EqualsAndHashCode;
  19 +import lombok.Getter;
  20 +import org.thingsboard.rule.engine.api.TbContext;
  21 +import org.thingsboard.server.common.msg.TbActorStopReason;
  22 +import org.thingsboard.server.common.msg.TbMsg;
  23 +import org.thingsboard.server.common.msg.TbRuleEngineActorMsg;
  24 +import org.thingsboard.server.common.msg.queue.RuleNodeException;
  25 +
  26 +@EqualsAndHashCode(callSuper = true)
  27 +public abstract class TbToRuleNodeActorMsg extends TbRuleEngineActorMsg {
  28 +
  29 + @Getter
  30 + private final TbContext ctx;
  31 +
  32 + public TbToRuleNodeActorMsg(TbContext ctx, TbMsg tbMsg) {
  33 + super(tbMsg);
  34 + this.ctx = ctx;
  35 + }
  36 +
  37 + @Override
  38 + public void onTbActorStopped(TbActorStopReason reason) {
  39 + String message = reason == TbActorStopReason.STOPPED ? "Rule node stopped" : "Failed to initialize rule node!";
  40 + msg.getCallback().onFailure(new RuleNodeException(message, ctx.getRuleChainName(), ctx.getSelf()));
  41 + }
  42 +}
@@ -20,6 +20,7 @@ import org.thingsboard.server.actors.ActorSystemContext; @@ -20,6 +20,7 @@ import org.thingsboard.server.actors.ActorSystemContext;
20 import org.thingsboard.server.actors.TbActor; 20 import org.thingsboard.server.actors.TbActor;
21 import org.thingsboard.server.actors.TbActorCtx; 21 import org.thingsboard.server.actors.TbActorCtx;
22 import org.thingsboard.server.actors.TbActorException; 22 import org.thingsboard.server.actors.TbActorException;
  23 +import org.thingsboard.server.actors.TbRuleNodeUpdateException;
23 import org.thingsboard.server.actors.shared.ComponentMsgProcessor; 24 import org.thingsboard.server.actors.shared.ComponentMsgProcessor;
24 import org.thingsboard.server.actors.stats.StatsPersistMsg; 25 import org.thingsboard.server.actors.stats.StatsPersistMsg;
25 import org.thingsboard.server.common.data.id.EntityId; 26 import org.thingsboard.server.common.data.id.EntityId;
@@ -123,6 +124,9 @@ public abstract class ComponentActor<T extends EntityId, P extends ComponentMsgP @@ -123,6 +124,9 @@ public abstract class ComponentActor<T extends EntityId, P extends ComponentMsgP
123 } catch (Exception e) { 124 } catch (Exception e) {
124 logAndPersist("onLifecycleMsg", e, true); 125 logAndPersist("onLifecycleMsg", e, true);
125 logLifecycleEvent(msg.getEvent(), e); 126 logLifecycleEvent(msg.getEvent(), e);
  127 + if (e instanceof TbRuleNodeUpdateException) {
  128 + throw (TbRuleNodeUpdateException) e;
  129 + }
126 } 130 }
127 } 131 }
128 132
@@ -34,6 +34,7 @@ import org.thingsboard.server.actors.app.AppInitMsg; @@ -34,6 +34,7 @@ import org.thingsboard.server.actors.app.AppInitMsg;
34 import org.thingsboard.server.actors.stats.StatsActor; 34 import org.thingsboard.server.actors.stats.StatsActor;
35 import org.thingsboard.server.common.msg.queue.PartitionChangeMsg; 35 import org.thingsboard.server.common.msg.queue.PartitionChangeMsg;
36 import org.thingsboard.server.queue.discovery.PartitionChangeEvent; 36 import org.thingsboard.server.queue.discovery.PartitionChangeEvent;
  37 +import org.thingsboard.server.queue.discovery.TbApplicationEventListener;
37 38
38 import javax.annotation.PostConstruct; 39 import javax.annotation.PostConstruct;
39 import javax.annotation.PreDestroy; 40 import javax.annotation.PreDestroy;
@@ -43,7 +44,7 @@ import java.util.concurrent.ScheduledExecutorService; @@ -43,7 +44,7 @@ import java.util.concurrent.ScheduledExecutorService;
43 44
44 @Service 45 @Service
45 @Slf4j 46 @Slf4j
46 -public class DefaultActorService implements ActorService { 47 +public class DefaultActorService extends TbApplicationEventListener<PartitionChangeEvent> implements ActorService {
47 48
48 public static final String APP_DISPATCHER_NAME = "app-dispatcher"; 49 public static final String APP_DISPATCHER_NAME = "app-dispatcher";
49 public static final String TENANT_DISPATCHER_NAME = "tenant-dispatcher"; 50 public static final String TENANT_DISPATCHER_NAME = "tenant-dispatcher";
@@ -120,10 +121,10 @@ public class DefaultActorService implements ActorService { @@ -120,10 +121,10 @@ public class DefaultActorService implements ActorService {
120 appActor.tellWithHighPriority(new AppInitMsg()); 121 appActor.tellWithHighPriority(new AppInitMsg());
121 } 122 }
122 123
123 - @EventListener(PartitionChangeEvent.class)  
124 - public void onApplicationEvent(PartitionChangeEvent partitionChangeEvent) { 124 + @Override
  125 + protected void onTbApplicationEvent(PartitionChangeEvent event) {
125 log.info("Received partition change event."); 126 log.info("Received partition change event.");
126 - this.appActor.tellWithHighPriority(new PartitionChangeMsg(partitionChangeEvent.getServiceQueueKey(), partitionChangeEvent.getPartitions())); 127 + this.appActor.tellWithHighPriority(new PartitionChangeMsg(event.getServiceQueueKey(), event.getPartitions()));
127 } 128 }
128 129
129 @PreDestroy 130 @PreDestroy
@@ -28,10 +28,10 @@ import org.thingsboard.server.common.msg.TbActorMsg; @@ -28,10 +28,10 @@ import org.thingsboard.server.common.msg.TbActorMsg;
28 @ToString 28 @ToString
29 public final class StatsPersistMsg implements TbActorMsg { 29 public final class StatsPersistMsg implements TbActorMsg {
30 30
31 - private long messagesProcessed;  
32 - private long errorsOccurred;  
33 - private TenantId tenantId;  
34 - private EntityId entityId; 31 + private final long messagesProcessed;
  32 + private final long errorsOccurred;
  33 + private final TenantId tenantId;
  34 + private final EntityId entityId;
35 35
36 @Override 36 @Override
37 public MsgType getMsgType() { 37 public MsgType getMsgType() {
@@ -18,7 +18,7 @@ package org.thingsboard.server.actors.stats; @@ -18,7 +18,7 @@ package org.thingsboard.server.actors.stats;
18 import org.thingsboard.server.common.msg.MsgType; 18 import org.thingsboard.server.common.msg.MsgType;
19 import org.thingsboard.server.common.msg.TbActorMsg; 19 import org.thingsboard.server.common.msg.TbActorMsg;
20 20
21 -public final class StatsPersistTick implements TbActorMsg{ 21 +public final class StatsPersistTick implements TbActorMsg {
22 @Override 22 @Override
23 public MsgType getMsgType() { 23 public MsgType getMsgType() {
24 return MsgType.STATS_PERSIST_TICK_MSG; 24 return MsgType.STATS_PERSIST_TICK_MSG;
@@ -125,7 +125,7 @@ public class TenantActor extends RuleChainManagerActor { @@ -125,7 +125,7 @@ public class TenantActor extends RuleChainManagerActor {
125 log.info("[{}] Processing missing Tenant msg: {}", tenantId, msg); 125 log.info("[{}] Processing missing Tenant msg: {}", tenantId, msg);
126 if (msg.getMsgType().equals(MsgType.QUEUE_TO_RULE_ENGINE_MSG)) { 126 if (msg.getMsgType().equals(MsgType.QUEUE_TO_RULE_ENGINE_MSG)) {
127 QueueToRuleEngineMsg queueMsg = (QueueToRuleEngineMsg) msg; 127 QueueToRuleEngineMsg queueMsg = (QueueToRuleEngineMsg) msg;
128 - queueMsg.getTbMsg().getCallback().onSuccess(); 128 + queueMsg.getMsg().getCallback().onSuccess();
129 } else if (msg.getMsgType().equals(MsgType.TRANSPORT_TO_DEVICE_ACTOR_MSG)) { 129 } else if (msg.getMsgType().equals(MsgType.TRANSPORT_TO_DEVICE_ACTOR_MSG)) {
130 TransportToDeviceActorMsgWrapper transportMsg = (TransportToDeviceActorMsgWrapper) msg; 130 TransportToDeviceActorMsgWrapper transportMsg = (TransportToDeviceActorMsgWrapper) msg;
131 transportMsg.getCallback().onSuccess(); 131 transportMsg.getCallback().onSuccess();
@@ -188,7 +188,7 @@ public class TenantActor extends RuleChainManagerActor { @@ -188,7 +188,7 @@ public class TenantActor extends RuleChainManagerActor {
188 log.warn("RECEIVED INVALID MESSAGE: {}", msg); 188 log.warn("RECEIVED INVALID MESSAGE: {}", msg);
189 return; 189 return;
190 } 190 }
191 - TbMsg tbMsg = msg.getTbMsg(); 191 + TbMsg tbMsg = msg.getMsg();
192 if (apiUsageState.isReExecEnabled()) { 192 if (apiUsageState.isReExecEnabled()) {
193 if (tbMsg.getRuleChainId() == null) { 193 if (tbMsg.getRuleChainId() == null) {
194 if (getRootChainActor() != null) { 194 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;
@@ -127,7 +127,7 @@ public class ThingsboardSecurityConfiguration extends WebSecurityConfigurerAdapt @@ -127,7 +127,7 @@ public class ThingsboardSecurityConfiguration extends WebSecurityConfigurerAdapt
127 } 127 }
128 128
129 protected JwtTokenAuthenticationProcessingFilter buildJwtTokenAuthenticationProcessingFilter() throws Exception { 129 protected JwtTokenAuthenticationProcessingFilter buildJwtTokenAuthenticationProcessingFilter() throws Exception {
130 - List<String> pathsToSkip = new ArrayList(Arrays.asList(NON_TOKEN_BASED_AUTH_ENTRY_POINTS)); 130 + 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, 131 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)); 132 PUBLIC_LOGIN_ENTRY_POINT, DEVICE_API_ENTRY_POINT, WEBJARS_ENTRY_POINT));
133 SkipPathRequestMatcher matcher = new SkipPathRequestMatcher(pathsToSkip, TOKEN_BASED_AUTH_ENTRY_POINT); 133 SkipPathRequestMatcher matcher = new SkipPathRequestMatcher(pathsToSkip, TOKEN_BASED_AUTH_ENTRY_POINT);
@@ -152,6 +152,9 @@ public abstract class BaseController { @@ -152,6 +152,9 @@ public abstract class BaseController {
152 public static final String INCORRECT_TENANT_ID = "Incorrect tenantId "; 152 public static final String INCORRECT_TENANT_ID = "Incorrect tenantId ";
153 public static final String YOU_DON_T_HAVE_PERMISSION_TO_PERFORM_THIS_OPERATION = "You don't have permission to perform this operation!"; 153 public static final String YOU_DON_T_HAVE_PERMISSION_TO_PERFORM_THIS_OPERATION = "You don't have permission to perform this operation!";
154 154
  155 + protected static final String DEFAULT_DASHBOARD = "defaultDashboardId";
  156 + protected static final String HOME_DASHBOARD = "homeDashboardId";
  157 +
155 private static final ObjectMapper json = new ObjectMapper(); 158 private static final ObjectMapper json = new ObjectMapper();
156 159
157 @Autowired 160 @Autowired
@@ -719,6 +722,7 @@ public abstract class BaseController { @@ -719,6 +722,7 @@ public abstract class BaseController {
719 return ruleNode; 722 return ruleNode;
720 } 723 }
721 724
  725 + @SuppressWarnings("unchecked")
722 protected <I extends EntityId> I emptyId(EntityType entityType) { 726 protected <I extends EntityId> I emptyId(EntityType entityType) {
723 return (I) EntityIdFactory.getByTypeAndUuid(entityType, ModelConstants.NULL_UUID); 727 return (I) EntityIdFactory.getByTypeAndUuid(entityType, ModelConstants.NULL_UUID);
724 } 728 }
@@ -849,8 +853,9 @@ public abstract class BaseController { @@ -849,8 +853,9 @@ public abstract class BaseController {
849 entityNode = json.createObjectNode(); 853 entityNode = json.createObjectNode();
850 if (actionType == ActionType.ATTRIBUTES_UPDATED) { 854 if (actionType == ActionType.ATTRIBUTES_UPDATED) {
851 String scope = extractParameter(String.class, 0, additionalInfo); 855 String scope = extractParameter(String.class, 0, additionalInfo);
  856 + @SuppressWarnings("unchecked")
852 List<AttributeKvEntry> attributes = extractParameter(List.class, 1, additionalInfo); 857 List<AttributeKvEntry> attributes = extractParameter(List.class, 1, additionalInfo);
853 - metaData.putValue("scope", scope); 858 + metaData.putValue(DataConstants.SCOPE, scope);
854 if (attributes != null) { 859 if (attributes != null) {
855 for (AttributeKvEntry attr : attributes) { 860 for (AttributeKvEntry attr : attributes) {
856 addKvEntry(entityNode, attr); 861 addKvEntry(entityNode, attr);
@@ -858,16 +863,19 @@ public abstract class BaseController { @@ -858,16 +863,19 @@ public abstract class BaseController {
858 } 863 }
859 } else if (actionType == ActionType.ATTRIBUTES_DELETED) { 864 } else if (actionType == ActionType.ATTRIBUTES_DELETED) {
860 String scope = extractParameter(String.class, 0, additionalInfo); 865 String scope = extractParameter(String.class, 0, additionalInfo);
  866 + @SuppressWarnings("unchecked")
861 List<String> keys = extractParameter(List.class, 1, additionalInfo); 867 List<String> keys = extractParameter(List.class, 1, additionalInfo);
862 - metaData.putValue("scope", scope); 868 + metaData.putValue(DataConstants.SCOPE, scope);
863 ArrayNode attrsArrayNode = entityNode.putArray("attributes"); 869 ArrayNode attrsArrayNode = entityNode.putArray("attributes");
864 if (keys != null) { 870 if (keys != null) {
865 keys.forEach(attrsArrayNode::add); 871 keys.forEach(attrsArrayNode::add);
866 } 872 }
867 } else if (actionType == ActionType.TIMESERIES_UPDATED) { 873 } else if (actionType == ActionType.TIMESERIES_UPDATED) {
  874 + @SuppressWarnings("unchecked")
868 List<TsKvEntry> timeseries = extractParameter(List.class, 0, additionalInfo); 875 List<TsKvEntry> timeseries = extractParameter(List.class, 0, additionalInfo);
869 addTimeseries(entityNode, timeseries); 876 addTimeseries(entityNode, timeseries);
870 } else if (actionType == ActionType.TIMESERIES_DELETED) { 877 } else if (actionType == ActionType.TIMESERIES_DELETED) {
  878 + @SuppressWarnings("unchecked")
871 List<String> keys = extractParameter(List.class, 0, additionalInfo); 879 List<String> keys = extractParameter(List.class, 0, additionalInfo);
872 if (keys != null) { 880 if (keys != null) {
873 ArrayNode timeseriesArrayNode = entityNode.putArray("timeseries"); 881 ArrayNode timeseriesArrayNode = entityNode.putArray("timeseries");
@@ -1030,4 +1038,14 @@ public abstract class BaseController { @@ -1030,4 +1038,14 @@ public abstract class BaseController {
1030 } 1038 }
1031 } 1039 }
1032 } 1040 }
  1041 +
  1042 + protected void processDashboardIdFromAdditionalInfo(ObjectNode additionalInfo, String requiredFields) throws ThingsboardException {
  1043 + String dashboardId = additionalInfo.has(requiredFields) ? additionalInfo.get(requiredFields).asText() : null;
  1044 + if(dashboardId != null && !dashboardId.equals("null")) {
  1045 + if(dashboardService.findDashboardById(getTenantId(), new DashboardId(UUID.fromString(dashboardId))) == null) {
  1046 + additionalInfo.remove(requiredFields);
  1047 + }
  1048 + }
  1049 + }
  1050 +
1033 } 1051 }
@@ -59,7 +59,11 @@ public class CustomerController extends BaseController { @@ -59,7 +59,11 @@ public class CustomerController extends BaseController {
59 checkParameter(CUSTOMER_ID, strCustomerId); 59 checkParameter(CUSTOMER_ID, strCustomerId);
60 try { 60 try {
61 CustomerId customerId = new CustomerId(toUUID(strCustomerId)); 61 CustomerId customerId = new CustomerId(toUUID(strCustomerId));
62 - return checkCustomerId(customerId, Operation.READ); 62 + Customer customer = checkCustomerId(customerId, Operation.READ);
  63 + if(!customer.getAdditionalInfo().isNull()) {
  64 + processDashboardIdFromAdditionalInfo((ObjectNode) customer.getAdditionalInfo(), HOME_DASHBOARD);
  65 + }
  66 + return customer;
63 } catch (Exception e) { 67 } catch (Exception e) {
64 throw handleException(e); 68 throw handleException(e);
65 } 69 }
@@ -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.edge.Edge; 41 import org.thingsboard.server.common.data.edge.Edge;
36 import org.thingsboard.server.common.data.edge.EdgeEventActionType; 42 import org.thingsboard.server.common.data.edge.EdgeEventActionType;
@@ -42,7 +48,9 @@ import org.thingsboard.server.common.data.id.TenantId; @@ -42,7 +48,9 @@ import org.thingsboard.server.common.data.id.TenantId;
42 import org.thingsboard.server.common.data.page.PageData; 48 import org.thingsboard.server.common.data.page.PageData;
43 import org.thingsboard.server.common.data.page.PageLink; 49 import org.thingsboard.server.common.data.page.PageLink;
44 import org.thingsboard.server.common.data.page.TimePageLink; 50 import org.thingsboard.server.common.data.page.TimePageLink;
  51 +import org.thingsboard.server.dao.util.mapping.JacksonUtil;
45 import org.thingsboard.server.queue.util.TbCoreComponent; 52 import org.thingsboard.server.queue.util.TbCoreComponent;
  53 +import org.thingsboard.server.service.security.model.SecurityUser;
46 import org.thingsboard.server.service.security.permission.Operation; 54 import org.thingsboard.server.service.security.permission.Operation;
47 import org.thingsboard.server.service.security.permission.Resource; 55 import org.thingsboard.server.service.security.permission.Resource;
48 56
@@ -58,6 +66,9 @@ public class DashboardController extends BaseController { @@ -58,6 +66,9 @@ public class DashboardController extends BaseController {
58 66
59 public static final String DASHBOARD_ID = "dashboardId"; 67 public static final String DASHBOARD_ID = "dashboardId";
60 68
  69 + private static final String HOME_DASHBOARD_ID = "homeDashboardId";
  70 + private static final String HOME_DASHBOARD_HIDE_TOOLBAR = "homeDashboardHideToolbar";
  71 +
61 @Value("${dashboard.max_datapoints_limit}") 72 @Value("${dashboard.max_datapoints_limit}")
62 private long maxDatapointsLimit; 73 private long maxDatapointsLimit;
63 74
@@ -491,6 +502,102 @@ public class DashboardController extends BaseController { @@ -491,6 +502,102 @@ public class DashboardController extends BaseController {
491 } 502 }
492 } 503 }
493 504
  505 + @PreAuthorize("isAuthenticated()")
  506 + @RequestMapping(value = "/dashboard/home", method = RequestMethod.GET)
  507 + @ResponseBody
  508 + public HomeDashboard getHomeDashboard() throws ThingsboardException {
  509 + try {
  510 + SecurityUser securityUser = getCurrentUser();
  511 + if (securityUser.isSystemAdmin()) {
  512 + return null;
  513 + }
  514 + User user = userService.findUserById(securityUser.getTenantId(), securityUser.getId());
  515 + JsonNode additionalInfo = user.getAdditionalInfo();
  516 + HomeDashboard homeDashboard;
  517 + homeDashboard = extractHomeDashboardFromAdditionalInfo(additionalInfo);
  518 + if (homeDashboard == null) {
  519 + if (securityUser.isCustomerUser()) {
  520 + Customer customer = customerService.findCustomerById(securityUser.getTenantId(), securityUser.getCustomerId());
  521 + additionalInfo = customer.getAdditionalInfo();
  522 + homeDashboard = extractHomeDashboardFromAdditionalInfo(additionalInfo);
  523 + }
  524 + if (homeDashboard == null) {
  525 + Tenant tenant = tenantService.findTenantById(securityUser.getTenantId());
  526 + additionalInfo = tenant.getAdditionalInfo();
  527 + homeDashboard = extractHomeDashboardFromAdditionalInfo(additionalInfo);
  528 + }
  529 + }
  530 + return homeDashboard;
  531 + } catch (Exception e) {
  532 + throw handleException(e);
  533 + }
  534 + }
  535 +
  536 + @PreAuthorize("hasAuthority('TENANT_ADMIN')")
  537 + @RequestMapping(value = "/tenant/dashboard/home/info", method = RequestMethod.GET)
  538 + @ResponseBody
  539 + public HomeDashboardInfo getTenantHomeDashboardInfo() throws ThingsboardException {
  540 + try {
  541 + Tenant tenant = tenantService.findTenantById(getTenantId());
  542 + JsonNode additionalInfo = tenant.getAdditionalInfo();
  543 + DashboardId dashboardId = null;
  544 + boolean hideDashboardToolbar = true;
  545 + if (additionalInfo != null && additionalInfo.has(HOME_DASHBOARD_ID) && !additionalInfo.get(HOME_DASHBOARD_ID).isNull()) {
  546 + String strDashboardId = additionalInfo.get(HOME_DASHBOARD_ID).asText();
  547 + dashboardId = new DashboardId(toUUID(strDashboardId));
  548 + if (additionalInfo.has(HOME_DASHBOARD_HIDE_TOOLBAR)) {
  549 + hideDashboardToolbar = additionalInfo.get(HOME_DASHBOARD_HIDE_TOOLBAR).asBoolean();
  550 + }
  551 + }
  552 + return new HomeDashboardInfo(dashboardId, hideDashboardToolbar);
  553 + } catch (Exception e) {
  554 + throw handleException(e);
  555 + }
  556 + }
  557 +
  558 + @PreAuthorize("hasAuthority('TENANT_ADMIN')")
  559 + @RequestMapping(value = "/tenant/dashboard/home/info", method = RequestMethod.POST)
  560 + @ResponseStatus(value = HttpStatus.OK)
  561 + public void setTenantHomeDashboardInfo(@RequestBody HomeDashboardInfo homeDashboardInfo) throws ThingsboardException {
  562 + try {
  563 + if (homeDashboardInfo.getDashboardId() != null) {
  564 + checkDashboardId(homeDashboardInfo.getDashboardId(), Operation.READ);
  565 + }
  566 + Tenant tenant = tenantService.findTenantById(getTenantId());
  567 + JsonNode additionalInfo = tenant.getAdditionalInfo();
  568 + if (additionalInfo == null || !(additionalInfo instanceof ObjectNode)) {
  569 + additionalInfo = JacksonUtil.OBJECT_MAPPER.createObjectNode();
  570 + }
  571 + if (homeDashboardInfo.getDashboardId() != null) {
  572 + ((ObjectNode) additionalInfo).put(HOME_DASHBOARD_ID, homeDashboardInfo.getDashboardId().getId().toString());
  573 + ((ObjectNode) additionalInfo).put(HOME_DASHBOARD_HIDE_TOOLBAR, homeDashboardInfo.isHideDashboardToolbar());
  574 + } else {
  575 + ((ObjectNode) additionalInfo).remove(HOME_DASHBOARD_ID);
  576 + ((ObjectNode) additionalInfo).remove(HOME_DASHBOARD_HIDE_TOOLBAR);
  577 + }
  578 + tenant.setAdditionalInfo(additionalInfo);
  579 + tenantService.saveTenant(tenant);
  580 + } catch (Exception e) {
  581 + throw handleException(e);
  582 + }
  583 + }
  584 +
  585 + private HomeDashboard extractHomeDashboardFromAdditionalInfo(JsonNode additionalInfo) {
  586 + try {
  587 + if (additionalInfo != null && additionalInfo.has(HOME_DASHBOARD_ID) && !additionalInfo.get(HOME_DASHBOARD_ID).isNull()) {
  588 + String strDashboardId = additionalInfo.get(HOME_DASHBOARD_ID).asText();
  589 + DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
  590 + Dashboard dashboard = checkDashboardId(dashboardId, Operation.READ);
  591 + boolean hideDashboardToolbar = true;
  592 + if (additionalInfo.has(HOME_DASHBOARD_HIDE_TOOLBAR)) {
  593 + hideDashboardToolbar = additionalInfo.get(HOME_DASHBOARD_HIDE_TOOLBAR).asBoolean();
  594 + }
  595 + return new HomeDashboard(dashboard, hideDashboardToolbar);
  596 + }
  597 + } catch (Exception e) {}
  598 + return null;
  599 + }
  600 +
494 @PreAuthorize("hasAuthority('TENANT_ADMIN')") 601 @PreAuthorize("hasAuthority('TENANT_ADMIN')")
495 @RequestMapping(value = "/edge/{edgeId}/dashboard/{dashboardId}", method = RequestMethod.POST) 602 @RequestMapping(value = "/edge/{edgeId}/dashboard/{dashboardId}", method = RequestMethod.POST)
496 @ResponseBody 603 @ResponseBody
@@ -73,7 +73,7 @@ import java.util.List; @@ -73,7 +73,7 @@ import java.util.List;
73 import java.util.concurrent.ExecutionException; 73 import java.util.concurrent.ExecutionException;
74 import java.util.stream.Collectors; 74 import java.util.stream.Collectors;
75 75
76 -import static org.apache.commons.lang.StringUtils.isBlank; 76 +import static org.apache.commons.lang3.StringUtils.isBlank;
77 import static org.thingsboard.server.controller.CustomerController.CUSTOMER_ID; 77 import static org.thingsboard.server.controller.CustomerController.CUSTOMER_ID;
78 import static org.thingsboard.server.controller.EdgeController.EDGE_ID; 78 import static org.thingsboard.server.controller.EdgeController.EDGE_ID;
79 79
@@ -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 }
@@ -92,7 +92,12 @@ public class UserController extends BaseController { @@ -92,7 +92,12 @@ public class UserController extends BaseController {
92 checkParameter(USER_ID, strUserId); 92 checkParameter(USER_ID, strUserId);
93 try { 93 try {
94 UserId userId = new UserId(toUUID(strUserId)); 94 UserId userId = new UserId(toUUID(strUserId));
95 - return checkUserId(userId, Operation.READ); 95 + User user = checkUserId(userId, Operation.READ);
  96 + if(!user.getAdditionalInfo().isNull()) {
  97 + processDashboardIdFromAdditionalInfo((ObjectNode) user.getAdditionalInfo(), DEFAULT_DASHBOARD);
  98 + processDashboardIdFromAdditionalInfo((ObjectNode) user.getAdditionalInfo(), HOME_DASHBOARD);
  99 + }
  100 + return user;
96 } catch (Exception e) { 101 } catch (Exception e) {
97 throw handleException(e); 102 throw handleException(e);
98 } 103 }
@@ -339,4 +344,5 @@ public class UserController extends BaseController { @@ -339,4 +344,5 @@ public class UserController extends BaseController {
339 throw handleException(e); 344 throw handleException(e);
340 } 345 }
341 } 346 }
  347 +
342 } 348 }
@@ -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;
@@ -71,7 +72,7 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe @@ -71,7 +72,7 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe
71 private ObjectMapper mapper = new ObjectMapper(); 72 private ObjectMapper mapper = new ObjectMapper();
72 73
73 private boolean isInstall() { 74 private boolean isInstall() {
74 - return environment.acceptsProfiles("install"); 75 + return environment.acceptsProfiles(Profiles.of("install"));
75 } 76 }
76 77
77 @PostConstruct 78 @PostConstruct
@@ -200,7 +201,7 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe @@ -200,7 +201,7 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe
200 nodeDefinition.setRelationTypes(getRelationTypesWithFailureRelation(nodeAnnotation)); 201 nodeDefinition.setRelationTypes(getRelationTypesWithFailureRelation(nodeAnnotation));
201 nodeDefinition.setCustomRelations(nodeAnnotation.customRelations()); 202 nodeDefinition.setCustomRelations(nodeAnnotation.customRelations());
202 Class<? extends NodeConfiguration> configClazz = nodeAnnotation.configClazz(); 203 Class<? extends NodeConfiguration> configClazz = nodeAnnotation.configClazz();
203 - NodeConfiguration config = configClazz.newInstance(); 204 + NodeConfiguration config = configClazz.getDeclaredConstructor().newInstance();
204 NodeConfiguration defaultConfiguration = config.defaultConfiguration(); 205 NodeConfiguration defaultConfiguration = config.defaultConfiguration();
205 nodeDefinition.setDefaultConfiguration(mapper.valueToTree(defaultConfiguration)); 206 nodeDefinition.setDefaultConfiguration(mapper.valueToTree(defaultConfiguration));
206 nodeDefinition.setUiResources(nodeAnnotation.uiResources()); 207 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;
@@ -22,8 +22,8 @@ import com.google.common.util.concurrent.Futures; @@ -22,8 +22,8 @@ import com.google.common.util.concurrent.Futures;
22 import com.google.common.util.concurrent.ListenableFuture; 22 import com.google.common.util.concurrent.ListenableFuture;
23 import com.google.common.util.concurrent.SettableFuture; 23 import com.google.common.util.concurrent.SettableFuture;
24 import lombok.extern.slf4j.Slf4j; 24 import lombok.extern.slf4j.Slf4j;
25 -import org.apache.commons.lang.RandomStringUtils;  
26 -import org.apache.commons.lang.StringUtils; 25 +import org.apache.commons.lang3.RandomStringUtils;
  26 +import org.apache.commons.lang3.StringUtils;
27 import org.checkerframework.checker.nullness.qual.Nullable; 27 import org.checkerframework.checker.nullness.qual.Nullable;
28 import org.springframework.stereotype.Component; 28 import org.springframework.stereotype.Component;
29 import org.thingsboard.rule.engine.api.RpcError; 29 import org.thingsboard.rule.engine.api.RpcError;
@@ -99,7 +99,6 @@ public class DeviceProcessor extends BaseProcessor { @@ -99,7 +99,6 @@ public class DeviceProcessor extends BaseProcessor {
99 ObjectNode body = mapper.createObjectNode(); 99 ObjectNode body = mapper.createObjectNode();
100 body.put("conflictName", deviceName); 100 body.put("conflictName", deviceName);
101 saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE, EdgeEventActionType.ENTITY_MERGE_REQUEST, device.getId(), body); 101 saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE, EdgeEventActionType.ENTITY_MERGE_REQUEST, device.getId(), body);
102 - deviceService.assignDeviceToEdge(edge.getTenantId(), device.getId(), edge.getId());  
103 } 102 }
104 futureToSet.set(null); 103 futureToSet.set(null);
105 } 104 }
@@ -115,7 +114,6 @@ public class DeviceProcessor extends BaseProcessor { @@ -115,7 +114,6 @@ public class DeviceProcessor extends BaseProcessor {
115 log.info("[{}] Creating new device and replacing device entity on the edge [{}]", tenantId, deviceUpdateMsg); 114 log.info("[{}] Creating new device and replacing device entity on the edge [{}]", tenantId, deviceUpdateMsg);
116 device = createDevice(tenantId, edge, deviceUpdateMsg, deviceUpdateMsg.getName()); 115 device = createDevice(tenantId, edge, deviceUpdateMsg, deviceUpdateMsg.getName());
117 saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE, EdgeEventActionType.ENTITY_MERGE_REQUEST, device.getId(), null); 116 saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE, EdgeEventActionType.ENTITY_MERGE_REQUEST, device.getId(), null);
118 - deviceService.assignDeviceToEdge(edge.getTenantId(), device.getId(), edge.getId());  
119 } 117 }
120 break; 118 break;
121 case ENTITY_UPDATED_RPC_MESSAGE: 119 case ENTITY_UPDATED_RPC_MESSAGE:
@@ -201,6 +199,7 @@ public class DeviceProcessor extends BaseProcessor { @@ -201,6 +199,7 @@ public class DeviceProcessor extends BaseProcessor {
201 createRelationFromEdge(tenantId, edge.getId(), device.getId()); 199 createRelationFromEdge(tenantId, edge.getId(), device.getId());
202 deviceStateService.onDeviceAdded(device); 200 deviceStateService.onDeviceAdded(device);
203 pushDeviceCreatedEventToRuleEngine(tenantId, edge, device); 201 pushDeviceCreatedEventToRuleEngine(tenantId, edge, device);
  202 + deviceService.assignDeviceToEdge(edge.getTenantId(), device.getId(), edge.getId());
204 } finally { 203 } finally {
205 deviceCreationLock.unlock(); 204 deviceCreationLock.unlock();
206 } 205 }
@@ -21,10 +21,9 @@ import com.google.common.util.concurrent.ListenableFuture; @@ -21,10 +21,9 @@ import com.google.common.util.concurrent.ListenableFuture;
21 import com.google.common.util.concurrent.SettableFuture; 21 import com.google.common.util.concurrent.SettableFuture;
22 import com.google.gson.Gson; 22 import com.google.gson.Gson;
23 import com.google.gson.JsonObject; 23 import com.google.gson.JsonObject;
24 -import groovy.lang.Tuple;  
25 import lombok.extern.slf4j.Slf4j; 24 import lombok.extern.slf4j.Slf4j;
26 -import org.javatuples.Pair;  
27 -import org.passay.Rule; 25 +import org.apache.commons.lang3.tuple.ImmutablePair;
  26 +import org.apache.commons.lang3.tuple.Pair;
28 import org.springframework.stereotype.Component; 27 import org.springframework.stereotype.Component;
29 import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg; 28 import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg;
30 import org.thingsboard.server.common.data.DataConstants; 29 import org.thingsboard.server.common.data.DataConstants;
@@ -37,7 +36,6 @@ import org.thingsboard.server.common.data.id.AssetId; @@ -37,7 +36,6 @@ import org.thingsboard.server.common.data.id.AssetId;
37 import org.thingsboard.server.common.data.id.CustomerId; 36 import org.thingsboard.server.common.data.id.CustomerId;
38 import org.thingsboard.server.common.data.id.DashboardId; 37 import org.thingsboard.server.common.data.id.DashboardId;
39 import org.thingsboard.server.common.data.id.DeviceId; 38 import org.thingsboard.server.common.data.id.DeviceId;
40 -import org.thingsboard.server.common.data.id.DeviceProfileId;  
41 import org.thingsboard.server.common.data.id.EntityId; 39 import org.thingsboard.server.common.data.id.EntityId;
42 import org.thingsboard.server.common.data.id.EntityViewId; 40 import org.thingsboard.server.common.data.id.EntityViewId;
43 import org.thingsboard.server.common.data.id.RuleChainId; 41 import org.thingsboard.server.common.data.id.RuleChainId;
@@ -45,7 +43,6 @@ import org.thingsboard.server.common.data.id.TenantId; @@ -45,7 +43,6 @@ import org.thingsboard.server.common.data.id.TenantId;
45 import org.thingsboard.server.common.data.id.UserId; 43 import org.thingsboard.server.common.data.id.UserId;
46 import org.thingsboard.server.common.data.kv.AttributeKey; 44 import org.thingsboard.server.common.data.kv.AttributeKey;
47 import org.thingsboard.server.common.data.kv.AttributeKvEntry; 45 import org.thingsboard.server.common.data.kv.AttributeKvEntry;
48 -import org.thingsboard.server.common.data.rule.RuleChain;  
49 import org.thingsboard.server.common.msg.TbMsg; 46 import org.thingsboard.server.common.msg.TbMsg;
50 import org.thingsboard.server.common.msg.TbMsgMetaData; 47 import org.thingsboard.server.common.msg.TbMsgMetaData;
51 import org.thingsboard.server.common.msg.queue.ServiceQueue; 48 import org.thingsboard.server.common.msg.queue.ServiceQueue;
@@ -145,9 +142,9 @@ public class TelemetryProcessor extends BaseProcessor { @@ -145,9 +142,9 @@ public class TelemetryProcessor extends BaseProcessor {
145 String defaultQueueName = deviceProfile.getDefaultQueueName(); 142 String defaultQueueName = deviceProfile.getDefaultQueueName();
146 queueName = defaultQueueName != null ? defaultQueueName : ServiceQueue.MAIN; 143 queueName = defaultQueueName != null ? defaultQueueName : ServiceQueue.MAIN;
147 } 144 }
148 - return new Pair<>(queueName, ruleChainId); 145 + return new ImmutablePair<>(queueName, ruleChainId);
149 } else { 146 } else {
150 - return new Pair<>(ServiceQueue.MAIN, null); 147 + return new ImmutablePair<>(ServiceQueue.MAIN, null);
151 } 148 }
152 } 149 }
153 150
@@ -157,8 +154,8 @@ public class TelemetryProcessor extends BaseProcessor { @@ -157,8 +154,8 @@ public class TelemetryProcessor extends BaseProcessor {
157 JsonObject json = JsonUtils.getJsonObject(tsKv.getKvList()); 154 JsonObject json = JsonUtils.getJsonObject(tsKv.getKvList());
158 metaData.putValue("ts", tsKv.getTs() + ""); 155 metaData.putValue("ts", tsKv.getTs() + "");
159 Pair<String, RuleChainId> defaultQueueAndRuleChain = getDefaultQueueNameAndRuleChainId(tenantId, entityId); 156 Pair<String, RuleChainId> defaultQueueAndRuleChain = getDefaultQueueNameAndRuleChainId(tenantId, entityId);
160 - String queueName = defaultQueueAndRuleChain.getValue0();  
161 - RuleChainId ruleChainId = defaultQueueAndRuleChain.getValue1(); 157 + String queueName = defaultQueueAndRuleChain.getKey();
  158 + RuleChainId ruleChainId = defaultQueueAndRuleChain.getValue();
162 TbMsg tbMsg = TbMsg.newMsg(queueName, SessionMsgType.POST_TELEMETRY_REQUEST.name(), entityId, metaData, gson.toJson(json), ruleChainId, null); 159 TbMsg tbMsg = TbMsg.newMsg(queueName, SessionMsgType.POST_TELEMETRY_REQUEST.name(), entityId, metaData, gson.toJson(json), ruleChainId, null);
163 tbClusterService.pushMsgToRuleEngine(tenantId, tbMsg.getOriginator(), tbMsg, new TbQueueCallback() { 160 tbClusterService.pushMsgToRuleEngine(tenantId, tbMsg.getOriginator(), tbMsg, new TbQueueCallback() {
164 @Override 161 @Override
@@ -180,8 +177,8 @@ public class TelemetryProcessor extends BaseProcessor { @@ -180,8 +177,8 @@ public class TelemetryProcessor extends BaseProcessor {
180 SettableFuture<Void> futureToSet = SettableFuture.create(); 177 SettableFuture<Void> futureToSet = SettableFuture.create();
181 JsonObject json = JsonUtils.getJsonObject(msg.getKvList()); 178 JsonObject json = JsonUtils.getJsonObject(msg.getKvList());
182 Pair<String, RuleChainId> defaultQueueAndRuleChain = getDefaultQueueNameAndRuleChainId(tenantId, entityId); 179 Pair<String, RuleChainId> defaultQueueAndRuleChain = getDefaultQueueNameAndRuleChainId(tenantId, entityId);
183 - String queueName = defaultQueueAndRuleChain.getValue0();  
184 - RuleChainId ruleChainId = defaultQueueAndRuleChain.getValue1(); 180 + String queueName = defaultQueueAndRuleChain.getKey();
  181 + RuleChainId ruleChainId = defaultQueueAndRuleChain.getValue();
185 TbMsg tbMsg = TbMsg.newMsg(queueName, SessionMsgType.POST_ATTRIBUTES_REQUEST.name(), entityId, metaData, gson.toJson(json), ruleChainId, null); 182 TbMsg tbMsg = TbMsg.newMsg(queueName, SessionMsgType.POST_ATTRIBUTES_REQUEST.name(), entityId, metaData, gson.toJson(json), ruleChainId, null);
186 tbClusterService.pushMsgToRuleEngine(tenantId, tbMsg.getOriginator(), tbMsg, new TbQueueCallback() { 183 tbClusterService.pushMsgToRuleEngine(tenantId, tbMsg.getOriginator(), tbMsg, new TbQueueCallback() {
187 @Override 184 @Override
@@ -207,8 +204,8 @@ public class TelemetryProcessor extends BaseProcessor { @@ -207,8 +204,8 @@ public class TelemetryProcessor extends BaseProcessor {
207 @Override 204 @Override
208 public void onSuccess(@Nullable List<Void> voids) { 205 public void onSuccess(@Nullable List<Void> voids) {
209 Pair<String, RuleChainId> defaultQueueAndRuleChain = getDefaultQueueNameAndRuleChainId(tenantId, entityId); 206 Pair<String, RuleChainId> defaultQueueAndRuleChain = getDefaultQueueNameAndRuleChainId(tenantId, entityId);
210 - String queueName = defaultQueueAndRuleChain.getValue0();  
211 - RuleChainId ruleChainId = defaultQueueAndRuleChain.getValue1(); 207 + String queueName = defaultQueueAndRuleChain.getKey();
  208 + RuleChainId ruleChainId = defaultQueueAndRuleChain.getValue();
212 TbMsg tbMsg = TbMsg.newMsg(queueName, DataConstants.ATTRIBUTES_UPDATED, entityId, metaData, gson.toJson(json), ruleChainId, null); 209 TbMsg tbMsg = TbMsg.newMsg(queueName, DataConstants.ATTRIBUTES_UPDATED, entityId, metaData, gson.toJson(json), ruleChainId, null);
213 tbClusterService.pushMsgToRuleEngine(tenantId, tbMsg.getOriginator(), tbMsg, new TbQueueCallback() { 210 tbClusterService.pushMsgToRuleEngine(tenantId, tbMsg.getOriginator(), tbMsg, new TbQueueCallback() {
214 @Override 211 @Override
@@ -198,7 +198,7 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { @@ -198,7 +198,7 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
198 generalSettings.setKey("general"); 198 generalSettings.setKey("general");
199 ObjectNode node = objectMapper.createObjectNode(); 199 ObjectNode node = objectMapper.createObjectNode();
200 node.put("baseUrl", "http://localhost:8080"); 200 node.put("baseUrl", "http://localhost:8080");
201 - node.put("prohibitDifferentUrl", true); 201 + node.put("prohibitDifferentUrl", false);
202 generalSettings.setJsonValue(node); 202 generalSettings.setJsonValue(node);
203 adminSettingsService.saveAdminSettings(TenantId.SYS_TENANT_ID, generalSettings); 203 adminSettingsService.saveAdminSettings(TenantId.SYS_TENANT_ID, generalSettings);
204 204
@@ -439,6 +439,7 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { @@ -439,6 +439,7 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
439 this.deleteSystemWidgetBundle("input_widgets"); 439 this.deleteSystemWidgetBundle("input_widgets");
440 this.deleteSystemWidgetBundle("date"); 440 this.deleteSystemWidgetBundle("date");
441 this.deleteSystemWidgetBundle("entity_admin_widgets"); 441 this.deleteSystemWidgetBundle("entity_admin_widgets");
  442 + this.deleteSystemWidgetBundle("navigation_widgets");
442 this.deleteSystemWidgetBundle("edge_widgets"); 443 this.deleteSystemWidgetBundle("edge_widgets");
443 installScripts.loadSystemWidgets(); 444 installScripts.loadSystemWidgets();
444 } 445 }
@@ -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
@@ -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;
@@ -145,17 +145,17 @@ public class CassandraDbHelper { @@ -145,17 +145,17 @@ public class CassandraDbHelper {
145 if (row.isNull(index)) { 145 if (row.isNull(index)) {
146 return null; 146 return null;
147 } else if (type.getProtocolCode() == ProtocolConstants.DataType.DOUBLE) { 147 } else if (type.getProtocolCode() == ProtocolConstants.DataType.DOUBLE) {
148 - str = new Double(row.getDouble(index)).toString(); 148 + str = Double.valueOf(row.getDouble(index)).toString();
149 } else if (type.getProtocolCode() == ProtocolConstants.DataType.INT) { 149 } else if (type.getProtocolCode() == ProtocolConstants.DataType.INT) {
150 - str = new Integer(row.getInt(index)).toString(); 150 + str = Integer.valueOf(row.getInt(index)).toString();
151 } else if (type.getProtocolCode() == ProtocolConstants.DataType.BIGINT) { 151 } else if (type.getProtocolCode() == ProtocolConstants.DataType.BIGINT) {
152 - str = new Long(row.getLong(index)).toString(); 152 + str = Long.valueOf(row.getLong(index)).toString();
153 } else if (type.getProtocolCode() == ProtocolConstants.DataType.UUID) { 153 } else if (type.getProtocolCode() == ProtocolConstants.DataType.UUID) {
154 str = row.getUuid(index).toString(); 154 str = row.getUuid(index).toString();
155 } else if (type.getProtocolCode() == ProtocolConstants.DataType.TIMEUUID) { 155 } else if (type.getProtocolCode() == ProtocolConstants.DataType.TIMEUUID) {
156 str = row.getUuid(index).toString(); 156 str = row.getUuid(index).toString();
157 } else if (type.getProtocolCode() == ProtocolConstants.DataType.FLOAT) { 157 } else if (type.getProtocolCode() == ProtocolConstants.DataType.FLOAT) {
158 - str = new Float(row.getFloat(index)).toString(); 158 + str = Float.valueOf(row.getFloat(index)).toString();
159 } else if (type.getProtocolCode() == ProtocolConstants.DataType.TIMESTAMP) { 159 } else if (type.getProtocolCode() == ProtocolConstants.DataType.TIMESTAMP) {
160 str = ""+row.getInstant(index).toEpochMilli(); 160 str = ""+row.getInstant(index).toEpochMilli();
161 } else if (type.getProtocolCode() == ProtocolConstants.DataType.BOOLEAN) { 161 } else if (type.getProtocolCode() == ProtocolConstants.DataType.BOOLEAN) {
@@ -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;
@@ -50,7 +50,8 @@ import java.util.List; @@ -50,7 +50,8 @@ import java.util.List;
50 import java.util.concurrent.ExecutionException; 50 import java.util.concurrent.ExecutionException;
51 import java.util.stream.Collectors; 51 import java.util.stream.Collectors;
52 52
53 -import static org.apache.commons.lang.StringUtils.isBlank; 53 +import static org.apache.commons.lang3.StringUtils.isBlank;
  54 +import static org.thingsboard.server.service.install.DatabaseHelper.objectMapper;
54 55
55 @Service 56 @Service
56 @Profile("install") 57 @Profile("install")
@@ -169,11 +169,11 @@ public class LwM2MModelsRepository { @@ -169,11 +169,11 @@ public class LwM2MModelsRepository {
169 * PageNumber = 1, PageSize = List<LwM2mObject>.size() 169 * PageNumber = 1, PageSize = List<LwM2mObject>.size()
170 */ 170 */
171 public PageData<LwM2mObject> findLwm2mListObjects(PageLink pageLink) { 171 public PageData<LwM2mObject> findLwm2mListObjects(PageLink pageLink) {
172 - PageImpl page = new PageImpl(getLwm2mObjects(getObjectIdFromTextSearch(pageLink.getTextSearch()), 172 + PageImpl<LwM2mObject> page = new PageImpl<>(getLwm2mObjects(getObjectIdFromTextSearch(pageLink.getTextSearch()),
173 pageLink.getTextSearch(), 173 pageLink.getTextSearch(),
174 pageLink.getSortOrder().getProperty(), 174 pageLink.getSortOrder().getProperty(),
175 pageLink.getSortOrder().getDirection().name())); 175 pageLink.getSortOrder().getDirection().name()));
176 - PageData pageData = new PageData(page.getContent(), page.getTotalPages(), page.getTotalElements(), page.hasNext()); 176 + PageData<LwM2mObject> pageData = new PageData<>(page.getContent(), page.getTotalPages(), page.getTotalElements(), page.hasNext());
177 return pageData; 177 return pageData;
178 } 178 }
179 179
@@ -217,23 +217,19 @@ public class LwM2MModelsRepository { @@ -217,23 +217,19 @@ public class LwM2MModelsRepository {
217 switch (mode) { 217 switch (mode) {
218 case NO_SEC: 218 case NO_SEC:
219 bsServ.setHost(contextBootStrap.getBootstrapHost()); 219 bsServ.setHost(contextBootStrap.getBootstrapHost());
220 - bsServ.setPort(contextBootStrap.getBootstrapPortNoSecPsk()); 220 + bsServ.setPort(contextBootStrap.getBootstrapPortNoSec());
221 bsServ.setServerPublicKey(""); 221 bsServ.setServerPublicKey("");
222 break; 222 break;
223 case PSK: 223 case PSK:
224 - bsServ.setHost(contextBootStrap.getBootstrapSecureHost());  
225 - bsServ.setPort(contextBootStrap.getBootstrapSecurePortPsk()); 224 + bsServ.setHost(contextBootStrap.getBootstrapHostSecurity());
  225 + bsServ.setPort(contextBootStrap.getBootstrapPortSecurity());
226 bsServ.setServerPublicKey(""); 226 bsServ.setServerPublicKey("");
227 break; 227 break;
228 case RPK: 228 case RPK:
229 - bsServ.setHost(contextBootStrap.getBootstrapSecureHost());  
230 - bsServ.setPort(contextBootStrap.getBootstrapSecurePortRpk());  
231 - bsServ.setServerPublicKey(getRPKPublicKey(this.contextBootStrap.getBootstrapPublicX(), this.contextBootStrap.getBootstrapPublicY()));  
232 - break;  
233 case X509: 229 case X509:
234 - bsServ.setHost(contextBootStrap.getBootstrapSecureHost());  
235 - bsServ.setPort(contextBootStrap.getBootstrapSecurePortX509());  
236 - bsServ.setServerPublicKey(getServerPublicKeyX509(contextBootStrap.getBootstrapAlias())); 230 + bsServ.setHost(contextBootStrap.getBootstrapHostSecurity());
  231 + bsServ.setPort(contextBootStrap.getBootstrapPortSecurity());
  232 + bsServ.setServerPublicKey(getPublicKey (contextBootStrap.getBootstrapAlias(), this.contextBootStrap.getBootstrapPublicX(), this.contextBootStrap.getBootstrapPublicY()));
237 break; 233 break;
238 default: 234 default:
239 break; 235 break;
@@ -243,23 +239,19 @@ public class LwM2MModelsRepository { @@ -243,23 +239,19 @@ public class LwM2MModelsRepository {
243 switch (mode) { 239 switch (mode) {
244 case NO_SEC: 240 case NO_SEC:
245 bsServ.setHost(contextServer.getServerHost()); 241 bsServ.setHost(contextServer.getServerHost());
246 - bsServ.setPort(contextServer.getServerPortNoSecPsk()); 242 + bsServ.setPort(contextServer.getServerPortNoSec());
247 bsServ.setServerPublicKey(""); 243 bsServ.setServerPublicKey("");
248 break; 244 break;
249 case PSK: 245 case PSK:
250 - bsServ.setHost(contextServer.getServerSecureHost());  
251 - bsServ.setPort(contextServer.getServerPortPsk()); 246 + bsServ.setHost(contextServer.getServerHostSecurity());
  247 + bsServ.setPort(contextServer.getServerPortSecurity());
252 bsServ.setServerPublicKey(""); 248 bsServ.setServerPublicKey("");
253 break; 249 break;
254 case RPK: 250 case RPK:
255 - bsServ.setHost(contextServer.getServerSecureHost());  
256 - bsServ.setPort(contextServer.getServerPortRpk());  
257 - bsServ.setServerPublicKey(getRPKPublicKey(this.contextServer.getServerPublicX(), this.contextServer.getServerPublicY()));  
258 - break;  
259 case X509: 251 case X509:
260 - bsServ.setHost(contextServer.getServerSecureHost());  
261 - bsServ.setPort(contextServer.getServerPortX509());  
262 - bsServ.setServerPublicKey(getServerPublicKeyX509(contextServer.getServerAlias())); 252 + bsServ.setHost(contextServer.getServerHostSecurity());
  253 + bsServ.setPort(contextServer.getServerPortSecurity());
  254 + bsServ.setServerPublicKey(getPublicKey (contextServer.getServerAlias(), this.contextServer.getServerPublicX(), this.contextServer.getServerPublicY()));
263 break; 255 break;
264 default: 256 default:
265 break; 257 break;
@@ -268,6 +260,11 @@ public class LwM2MModelsRepository { @@ -268,6 +260,11 @@ public class LwM2MModelsRepository {
268 return bsServ; 260 return bsServ;
269 } 261 }
270 262
  263 + private String getPublicKey (String alias, String publicServerX, String publicServerY) {
  264 + String publicKey = getServerPublicKeyX509(alias);
  265 + return publicKey != null ? publicKey : getRPKPublicKey(publicServerX, publicServerY);
  266 + }
  267 +
271 /** 268 /**
272 * @param alias 269 * @param alias
273 * @return PublicKey format HexString or null 270 * @return PublicKey format HexString or null
@@ -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) {
@@ -156,12 +156,12 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService<ToCore @@ -156,12 +156,12 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService<ToCore
156 } 156 }
157 157
158 @Override 158 @Override
159 - public void onApplicationEvent(PartitionChangeEvent partitionChangeEvent) {  
160 - if (partitionChangeEvent.getServiceType().equals(getServiceType())) {  
161 - log.info("Subscribing to partitions: {}", partitionChangeEvent.getPartitions());  
162 - this.mainConsumer.subscribe(partitionChangeEvent.getPartitions()); 159 + protected void onTbApplicationEvent(PartitionChangeEvent event) {
  160 + if (event.getServiceType().equals(getServiceType())) {
  161 + log.info("Subscribing to partitions: {}", event.getPartitions());
  162 + this.mainConsumer.subscribe(event.getPartitions());
163 this.usageStatsConsumer.subscribe( 163 this.usageStatsConsumer.subscribe(
164 - partitionChangeEvent 164 + event
165 .getPartitions() 165 .getPartitions()
166 .stream() 166 .stream()
167 .map(tpi -> tpi.newByTopic(usageStatsConsumer.getTopic())) 167 .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;
@@ -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);
@@ -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");
@@ -48,6 +48,7 @@ public class CustomerUserPermissions extends AbstractPermissions { @@ -48,6 +48,7 @@ public class CustomerUserPermissions extends AbstractPermissions {
48 Operation.READ_ATTRIBUTES, Operation.READ_TELEMETRY, Operation.RPC_CALL, Operation.CLAIM_DEVICES) { 48 Operation.READ_ATTRIBUTES, Operation.READ_TELEMETRY, Operation.RPC_CALL, Operation.CLAIM_DEVICES) {
49 49
50 @Override 50 @Override
  51 + @SuppressWarnings("unchecked")
51 public boolean hasPermission(SecurityUser user, Operation operation, EntityId entityId, HasTenantId entity) { 52 public boolean hasPermission(SecurityUser user, Operation operation, EntityId entityId, HasTenantId entity) {
52 53
53 if (!super.hasPermission(user, operation, entityId, entity)) { 54 if (!super.hasPermission(user, operation, entityId, entity)) {
@@ -70,6 +71,7 @@ public class CustomerUserPermissions extends AbstractPermissions { @@ -70,6 +71,7 @@ public class CustomerUserPermissions extends AbstractPermissions {
70 new PermissionChecker.GenericPermissionChecker(Operation.READ, Operation.READ_ATTRIBUTES, Operation.READ_TELEMETRY) { 71 new PermissionChecker.GenericPermissionChecker(Operation.READ, Operation.READ_ATTRIBUTES, Operation.READ_TELEMETRY) {
71 72
72 @Override 73 @Override
  74 + @SuppressWarnings("unchecked")
73 public boolean hasPermission(SecurityUser user, Operation operation, EntityId entityId, HasTenantId entity) { 75 public boolean hasPermission(SecurityUser user, Operation operation, EntityId entityId, HasTenantId entity) {
74 if (!super.hasPermission(user, operation, entityId, entity)) { 76 if (!super.hasPermission(user, operation, entityId, entity)) {
75 return false; 77 return false;
@@ -120,6 +122,7 @@ public class CustomerUserPermissions extends AbstractPermissions { @@ -120,6 +122,7 @@ public class CustomerUserPermissions extends AbstractPermissions {
120 private static final PermissionChecker widgetsPermissionChecker = new PermissionChecker.GenericPermissionChecker(Operation.READ) { 122 private static final PermissionChecker widgetsPermissionChecker = new PermissionChecker.GenericPermissionChecker(Operation.READ) {
121 123
122 @Override 124 @Override
  125 + @SuppressWarnings("unchecked")
123 public boolean hasPermission(SecurityUser user, Operation operation, EntityId entityId, HasTenantId entity) { 126 public boolean hasPermission(SecurityUser user, Operation operation, EntityId entityId, HasTenantId entity) {
124 if (!super.hasPermission(user, operation, entityId, entity)) { 127 if (!super.hasPermission(user, operation, entityId, entity)) {
125 return false; 128 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);
@@ -60,6 +60,7 @@ public class TenantAdminPermissions extends AbstractPermissions { @@ -60,6 +60,7 @@ public class TenantAdminPermissions extends AbstractPermissions {
60 new PermissionChecker.GenericPermissionChecker(Operation.READ, Operation.READ_ATTRIBUTES, Operation.READ_TELEMETRY) { 60 new PermissionChecker.GenericPermissionChecker(Operation.READ, Operation.READ_ATTRIBUTES, Operation.READ_TELEMETRY) {
61 61
62 @Override 62 @Override
  63 + @SuppressWarnings("unchecked")
63 public boolean hasPermission(SecurityUser user, Operation operation, EntityId entityId, HasTenantId entity) { 64 public boolean hasPermission(SecurityUser user, Operation operation, EntityId entityId, HasTenantId entity) {
64 if (!super.hasPermission(user, operation, entityId, entity)) { 65 if (!super.hasPermission(user, operation, entityId, entity)) {
65 return false; 66 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.server.dao.util.mapping.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");
@@ -56,9 +56,11 @@ import org.thingsboard.server.dao.util.mapping.JacksonUtil; @@ -56,9 +56,11 @@ import org.thingsboard.server.dao.util.mapping.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);
@@ -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());
@@ -302,7 +302,9 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc @@ -302,7 +302,9 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc
302 Map<Integer, TbAbstractDataSubCtx> sessionSubs = subscriptionsBySessionId.computeIfAbsent(sessionRef.getSessionId(), k -> new HashMap<>()); 302 Map<Integer, TbAbstractDataSubCtx> sessionSubs = subscriptionsBySessionId.computeIfAbsent(sessionRef.getSessionId(), k -> new HashMap<>());
303 TbEntityDataSubCtx ctx = new TbEntityDataSubCtx(serviceId, wsService, entityService, localSubscriptionService, 303 TbEntityDataSubCtx ctx = new TbEntityDataSubCtx(serviceId, wsService, entityService, localSubscriptionService,
304 attributesService, stats, sessionRef, cmd.getCmdId(), maxEntitiesPerDataSubscription); 304 attributesService, stats, sessionRef, cmd.getCmdId(), maxEntitiesPerDataSubscription);
305 - ctx.setAndResolveQuery(cmd.getQuery()); 305 + if (cmd.getQuery() != null) {
  306 + ctx.setAndResolveQuery(cmd.getQuery());
  307 + }
306 sessionSubs.put(cmd.getCmdId(), ctx); 308 sessionSubs.put(cmd.getCmdId(), ctx);
307 return ctx; 309 return ctx;
308 } 310 }
@@ -316,6 +318,7 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc @@ -316,6 +318,7 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc
316 return ctx; 318 return ctx;
317 } 319 }
318 320
  321 + @SuppressWarnings("unchecked")
319 private <T extends TbAbstractDataSubCtx> T getSubCtx(String sessionId, int cmdId) { 322 private <T extends TbAbstractDataSubCtx> T getSubCtx(String sessionId, int cmdId) {
320 Map<Integer, TbAbstractDataSubCtx> sessionSubs = subscriptionsBySessionId.get(sessionId); 323 Map<Integer, TbAbstractDataSubCtx> sessionSubs = subscriptionsBySessionId.get(sessionId);
321 if (sessionSubs != null) { 324 if (sessionSubs != null) {
@@ -473,7 +476,7 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc @@ -473,7 +476,7 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc
473 public void cancelAllSessionSubscriptions(String sessionId) { 476 public void cancelAllSessionSubscriptions(String sessionId) {
474 Map<Integer, TbAbstractDataSubCtx> sessionSubs = subscriptionsBySessionId.remove(sessionId); 477 Map<Integer, TbAbstractDataSubCtx> sessionSubs = subscriptionsBySessionId.remove(sessionId);
475 if (sessionSubs != null) { 478 if (sessionSubs != null) {
476 - sessionSubs.values().stream().filter(sub -> sub instanceof TbEntityDataSubCtx).map(sub -> (TbEntityDataSubCtx) sub).forEach(this::cleanupAndCancel); 479 + sessionSubs.values().forEach(this::cleanupAndCancel);
477 } 480 }
478 } 481 }
479 482
@@ -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());
@@ -107,7 +107,7 @@ public abstract class TbAbstractDataSubCtx<T extends AbstractDataQuery<? extends @@ -107,7 +107,7 @@ public abstract class TbAbstractDataSubCtx<T extends AbstractDataQuery<? extends
107 public void setAndResolveQuery(T query) { 107 public void setAndResolveQuery(T query) {
108 dynamicValues.clear(); 108 dynamicValues.clear();
109 this.query = query; 109 this.query = query;
110 - if (query.getKeyFilters() != null) { 110 + if (query != null && query.getKeyFilters() != null) {
111 for (KeyFilter filter : query.getKeyFilters()) { 111 for (KeyFilter filter : query.getKeyFilters()) {
112 registerDynamicValues(filter.getPredicate()); 112 registerDynamicValues(filter.getPredicate());
113 } 113 }
@@ -264,6 +264,7 @@ public abstract class TbAbstractDataSubCtx<T extends AbstractDataQuery<? extends @@ -264,6 +264,7 @@ public abstract class TbAbstractDataSubCtx<T extends AbstractDataQuery<? extends
264 }, MoreExecutors.directExecutor()); 264 }, MoreExecutors.directExecutor());
265 } 265 }
266 266
  267 + @SuppressWarnings("unchecked")
267 private void updateDynamicValuesByKey(DynamicValueKeySub sub, TsValue tsValue) { 268 private void updateDynamicValuesByKey(DynamicValueKeySub sub, TsValue tsValue) {
268 DynamicValueKey dvk = sub.getKey(); 269 DynamicValueKey dvk = sub.getKey();
269 switch (dvk.getPredicateType()) { 270 switch (dvk.getPredicateType()) {
@@ -285,6 +286,7 @@ public abstract class TbAbstractDataSubCtx<T extends AbstractDataQuery<? extends @@ -285,6 +286,7 @@ public abstract class TbAbstractDataSubCtx<T extends AbstractDataQuery<? extends
285 } 286 }
286 } 287 }
287 288
  289 + @SuppressWarnings("unchecked")
288 private void registerDynamicValues(KeyFilterPredicate predicate) { 290 private void registerDynamicValues(KeyFilterPredicate predicate) {
289 switch (predicate.getType()) { 291 switch (predicate.getType()) {
290 case STRING: 292 case STRING:
@@ -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());
@@ -34,6 +34,8 @@ import java.util.UUID; @@ -34,6 +34,8 @@ import java.util.UUID;
34 @Data 34 @Data
35 public class TransportToDeviceActorMsgWrapper implements TbActorMsg, DeviceAwareMsg, TenantAwareMsg, Serializable { 35 public class TransportToDeviceActorMsgWrapper implements TbActorMsg, DeviceAwareMsg, TenantAwareMsg, Serializable {
36 36
  37 + private static final long serialVersionUID = 7191333353202935941L;
  38 +
37 private final TenantId tenantId; 39 private final TenantId tenantId;
38 private final DeviceId deviceId; 40 private final DeviceId deviceId;
39 private final TransportToDeviceActorMsg msg; 41 private final TransportToDeviceActorMsg msg;
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.utils;
  17 +
  18 +import lombok.extern.slf4j.Slf4j;
  19 +
  20 +import java.util.concurrent.Executor;
  21 +import java.util.concurrent.ExecutorService;
  22 +import java.util.function.Consumer;
  23 +
  24 +/**
  25 + * This class deduplicate executions of the specified function.
  26 + * Useful in cluster mode, when you get event about partition change multiple times.
  27 + * Assuming that the function execution is expensive, we should execute it immediately when first time event occurs and
  28 + * later, once the processing of first event is done, process last pending task.
  29 + *
  30 + * @param <P> parameters of the function
  31 + */
  32 +@Slf4j
  33 +public class EventDeduplicationExecutor<P> {
  34 + private final String name;
  35 + private final ExecutorService executor;
  36 + private final Consumer<P> function;
  37 + private P pendingTask;
  38 + private boolean busy;
  39 +
  40 + public EventDeduplicationExecutor(String name, ExecutorService executor, Consumer<P> function) {
  41 + this.name = name;
  42 + this.executor = executor;
  43 + this.function = function;
  44 + }
  45 +
  46 + public void submit(P params) {
  47 + log.info("[{}] Going to submit: {}", name, params);
  48 + synchronized (EventDeduplicationExecutor.this) {
  49 + if (!busy) {
  50 + busy = true;
  51 + pendingTask = null;
  52 + try {
  53 + log.info("[{}] Submitting task: {}", name, params);
  54 + executor.submit(() -> {
  55 + try {
  56 + log.info("[{}] Executing task: {}", name, params);
  57 + function.accept(params);
  58 + } catch (Throwable e) {
  59 + log.warn("[{}] Failed to process task with parameters: {}", name, params, e);
  60 + throw e;
  61 + } finally {
  62 + unlockAndProcessIfAny();
  63 + }
  64 + });
  65 + } catch (Throwable e) {
  66 + log.warn("[{}] Failed to submit task with parameters: {}", name, params, e);
  67 + unlockAndProcessIfAny();
  68 + throw e;
  69 + }
  70 + } else {
  71 + log.info("[{}] Task is already in progress. {} pending task: {}", name, pendingTask == null ? "adding" : "updating", params);
  72 + pendingTask = params;
  73 + }
  74 + }
  75 + }
  76 +
  77 + private void unlockAndProcessIfAny() {
  78 + synchronized (EventDeduplicationExecutor.this) {
  79 + busy = false;
  80 + if (pendingTask != null) {
  81 + submit(pendingTask);
  82 + }
  83 + }
  84 + }
  85 +}
@@ -33,6 +33,7 @@ public class MiscUtils { @@ -33,6 +33,7 @@ public class MiscUtils {
33 return "The " + propertyName + " property need to be set!"; 33 return "The " + propertyName + " property need to be set!";
34 } 34 }
35 35
  36 + @SuppressWarnings("deprecation")
36 public static HashFunction forName(String name) { 37 public static HashFunction forName(String name) {
37 switch (name) { 38 switch (name) {
38 case "murmur3_32": 39 case "murmur3_32":
@@ -34,6 +34,9 @@ @@ -34,6 +34,9 @@
34 34
35 <!-- <logger name="org.thingsboard.server.service.subscription" level="TRACE"/>--> 35 <!-- <logger name="org.thingsboard.server.service.subscription" level="TRACE"/>-->
36 <!-- <logger name="org.thingsboard.server.service.telemetry" level="TRACE"/>--> 36 <!-- <logger name="org.thingsboard.server.service.telemetry" level="TRACE"/>-->
  37 +<!-- <logger name="org.eclipse.californium.scandium.DTLSConnector" level="TRACE" />-->
  38 +<!-- <logger name="org.eclipse.californium.scandium.dtls.Handshaker" level="DEBUG" />-->
  39 +
37 <logger name="com.microsoft.azure.servicebus.primitives.CoreMessageReceiver" level="OFF" /> 40 <logger name="com.microsoft.azure.servicebus.primitives.CoreMessageReceiver" level="OFF" />
38 41
39 <logger name="org.thingsboard.server.service.edge" level="INFO" /> 42 <logger name="org.thingsboard.server.service.edge" level="INFO" />
@@ -43,4 +46,4 @@ @@ -43,4 +46,4 @@
43 </root> 46 </root>
44 47
45 48
46 -</configuration>  
  49 +</configuration>
@@ -118,6 +118,15 @@ security: @@ -118,6 +118,15 @@ security:
118 githubMapper: 118 githubMapper:
119 emailUrl: "${SECURITY_OAUTH2_GITHUB_MAPPER_EMAIL_URL_KEY:https://api.github.com/user/emails}" 119 emailUrl: "${SECURITY_OAUTH2_GITHUB_MAPPER_EMAIL_URL_KEY:https://api.github.com/user/emails}"
120 120
  121 +# Usage statistics parameters
  122 +usage:
  123 + stats:
  124 + report:
  125 + enabled: "${USAGE_STATS_REPORT_ENABLED:true}"
  126 + interval: "${USAGE_STATS_REPORT_INTERVAL:10}"
  127 + check:
  128 + cycle: "${USAGE_STATS_CHECK_CYCLE:60000}"
  129 +
121 # Dashboard parameters 130 # Dashboard parameters
122 dashboard: 131 dashboard:
123 # Maximum allowed datapoints fetched by widgets 132 # Maximum allowed datapoints fetched by widgets
@@ -585,14 +594,14 @@ transport: @@ -585,14 +594,14 @@ transport:
585 # model_path_file: "${LWM2M_MODEL_PATH_FILE:./common/transport/lwm2m/src/main/resources/models/}" 594 # model_path_file: "${LWM2M_MODEL_PATH_FILE:./common/transport/lwm2m/src/main/resources/models/}"
586 model_path_file: "${LWM2M_MODEL_PATH_FILE:}" 595 model_path_file: "${LWM2M_MODEL_PATH_FILE:}"
587 recommended_ciphers: "${LWM2M_RECOMMENDED_CIPHERS:false}" 596 recommended_ciphers: "${LWM2M_RECOMMENDED_CIPHERS:false}"
588 - recommended_supported_groups: "${LWM2M_RECOMMENDED_SUPPORTED_GROUPS:false}" 597 + recommended_supported_groups: "${LWM2M_RECOMMENDED_SUPPORTED_GROUPS:true}"
589 request_pool_size: "${LWM2M_REQUEST_POOL_SIZE:100}" 598 request_pool_size: "${LWM2M_REQUEST_POOL_SIZE:100}"
590 request_error_pool_size: "${LWM2M_REQUEST_ERROR_POOL_SIZE:10}" 599 request_error_pool_size: "${LWM2M_REQUEST_ERROR_POOL_SIZE:10}"
591 registered_pool_size: "${LWM2M_REGISTERED_POOL_SIZE:10}" 600 registered_pool_size: "${LWM2M_REGISTERED_POOL_SIZE:10}"
592 update_registered_pool_size: "${LWM2M_UPDATE_REGISTERED_POOL_SIZE:10}" 601 update_registered_pool_size: "${LWM2M_UPDATE_REGISTERED_POOL_SIZE:10}"
593 un_registered_pool_size: "${LWM2M_UN_REGISTERED_POOL_SIZE:10}" 602 un_registered_pool_size: "${LWM2M_UN_REGISTERED_POOL_SIZE:10}"
594 secure: 603 secure:
595 - # Only Certificate_x509: 604 + # Certificate_x509:
596 # To get helps about files format and how to generate it, see: https://github.com/eclipse/leshan/wiki/Credential-files-format 605 # To get helps about files format and how to generate it, see: https://github.com/eclipse/leshan/wiki/Credential-files-format
597 # Create new X509 Certificates: common/transport/lwm2m/src/main/resources/credentials/shell/lwM2M_credentials.sh 606 # Create new X509 Certificates: common/transport/lwm2m/src/main/resources/credentials/shell/lwM2M_credentials.sh
598 key_store_type: "${LWM2M_KEYSTORE_TYPE:JKS}" 607 key_store_type: "${LWM2M_KEYSTORE_TYPE:JKS}"
@@ -601,48 +610,38 @@ transport: @@ -601,48 +610,38 @@ transport:
601 key_store_path_file: "${KEY_STORE_PATH_FILE:}" 610 key_store_path_file: "${KEY_STORE_PATH_FILE:}"
602 key_store_password: "${LWM2M_KEYSTORE_PASSWORD_SERVER:server_ks_password}" 611 key_store_password: "${LWM2M_KEYSTORE_PASSWORD_SERVER:server_ks_password}"
603 root_alias: "${LWM2M_SERVER_ROOT_CA:rootca}" 612 root_alias: "${LWM2M_SERVER_ROOT_CA:rootca}"
604 - enable_gen_psk_rpk: "${ENABLE_GEN_PSK_RPK:true}" 613 + enable_gen_new_key_psk_rpk: "${ENABLE_GEN_NEW_KEY_PSK_RPK:false}"
605 server: 614 server:
606 id: "${LWM2M_SERVER_ID:123}" 615 id: "${LWM2M_SERVER_ID:123}"
607 bind_address: "${LWM2M_BIND_ADDRESS:0.0.0.0}" 616 bind_address: "${LWM2M_BIND_ADDRESS:0.0.0.0}"
608 - bind_port_no_sec_psk: "${LWM2M_BIND_PORT_NO_SEC_PSK:5685}"  
609 - bind_port_no_sec_rpk: "${LWM2M_BIND_PORT_NO_SEC_RPK:5687}"  
610 - bind_port_no_sec_x509: "${LWM2M_BIND_PORT_NO_SEC_X509:5689}" 617 + bind_port_no_sec: "${LWM2M_BIND_PORT_NO_SEC:5685}"
611 secure: 618 secure:
612 - bind_address: "${LWM2M_BIND_ADDRESS:0.0.0.0}"  
613 - start_psk: "${START_SERVER_PSK:true}"  
614 - start_rpk: "${START_SERVER_RPK:true}"  
615 - start_x509: "${START_SERVER_X509:true}"  
616 - bind_port_psk: "${LWM2M_BIND_PORT_SEC_PSK:5686}"  
617 - bind_port_rpk: "${LWM2M_BIND_PORT_SEC_RPK:5688}"  
618 - bind_port_x509: "${LWM2M_BIND_PORT_SEC_X509:5690}"  
619 - # Only RPK: Public & Private Key  
620 -# create_rpk: "${CREATE_RPK:}"  
621 - public_x: "${LWM2M_SERVER_PUBLIC_X:405354ea8893471d9296afbc8b020a5c6201b0bb25812a53b849d4480fa5f069}"  
622 - public_y: "${LWM2M_SERVER_PUBLIC_Y:30c9237e946a3a1692c1cafaa01a238a077f632c99371348337512363f28212b}"  
623 - private_s: "${LWM2M_SERVER_PRIVATE_S:274671fe40ce937b8a6352cf0a418e8a39e4bf0bb9bf74c910db953c20c73802}"  
624 - # Only Certificate_x509: 619 + bind_address_security: "${LWM2M_BIND_ADDRESS_SECURITY:0.0.0.0}"
  620 + bind_port_security: "${LWM2M_BIND_PORT_SECURITY:5686}"
  621 + # create_rpk: "${CREATE_RPK:}"
  622 + # Only for RPK: Public & Private Key. If the keystore file is missing or not working
  623 + # - Public Key (Hex): [3059301306072a8648ce3d020106082a8648ce3d0301070342000405064b9e6762dd8d8b8a52355d7b4d8b9a3d64e6d2ee277d76c248861353f3585eeb1838e4f9e37b31fa347aef5ce3431eb54e0a2506910c5e0298817445721b]
  624 + # - Private Key (Hex): [308193020100301306072a8648ce3d020106082a8648ce3d030107047930770201010420dc774b309e547ceb48fee547e104ce201a9c48c449dc5414cd04e7f5cf05f67ba00a06082a8648ce3d030107a1440342000405064b9e6762dd8d8b8a52355d7b4d8b9a3d64e6d2ee277d76c248861353f3585eeb1838e4f9e37b31fa347aef5ce3431eb54e0a2506910c5e0298817445721b],
  625 + # - Elliptic Curve parameters : [secp256r1 [NIST P-256, X9.62 prime256v1] (1.2.840.10045.3.1.7)]
  626 + public_x: "${LWM2M_SERVER_PUBLIC_X:05064b9e6762dd8d8b8a52355d7b4d8b9a3d64e6d2ee277d76c248861353f358}"
  627 + public_y: "${LWM2M_SERVER_PUBLIC_Y:5eeb1838e4f9e37b31fa347aef5ce3431eb54e0a2506910c5e0298817445721b}"
  628 + private_encoded: "${LWM2M_SERVER_PRIVATE_ENCODED:308193020100301306072a8648ce3d020106082a8648ce3d030107047930770201010420dc774b309e547ceb48fee547e104ce201a9c48c449dc5414cd04e7f5cf05f67ba00a06082a8648ce3d030107a1440342000405064b9e6762dd8d8b8a52355d7b4d8b9a3d64e6d2ee277d76c248861353f3585eeb1838e4f9e37b31fa347aef5ce3431eb54e0a2506910c5e0298817445721b}" # Only Certificate_x509:
625 alias: "${LWM2M_KEYSTORE_ALIAS_SERVER:server}" 629 alias: "${LWM2M_KEYSTORE_ALIAS_SERVER:server}"
626 bootstrap: 630 bootstrap:
627 - enable: "${BOOTSTRAP:true}" 631 + enable: "${LWM2M_BOOTSTRAP_ENABLED:true}"
628 id: "${LWM2M_SERVER_ID:111}" 632 id: "${LWM2M_SERVER_ID:111}"
629 bind_address: "${LWM2M_BIND_ADDRESS_BS:0.0.0.0}" 633 bind_address: "${LWM2M_BIND_ADDRESS_BS:0.0.0.0}"
630 - bind_port_no_sec_psk: "${LWM2M_BIND_PORT_NO_SEC_BS:5691}"  
631 - bind_port_no_sec_rpk: "${LWM2M_BIND_PORT_NO_SEC_BS:5693}"  
632 - bind_port_no_sec_x509: "${LWM2M_BIND_PORT_NO_SEC_BS:5695}" 634 + bind_port_no_sec: "${LWM2M_BIND_PORT_NO_SEC_BS:5687}"
633 secure: 635 secure:
634 - bind_address: "${LWM2M_BIND_ADDRESS_BS:0.0.0.0}"  
635 - start_psk: "${START_SERVER_PSK_BS:true}"  
636 - start_rpk: "${START_SERVER_RPK_BS:true}"  
637 - start_x509: "${START_SERVER_X509_BS:true}"  
638 - bind_port_psk: "${LWM2M_BIND_PORT_SEC_PSK_BS:5692}"  
639 - bind_port_rpk: "${LWM2M_BIND_PORT_SER_RPK_BS:5694}"  
640 - bind_port_x509: "${LWM2M_BIND_PORT_SEC_X509_BS:5696}"  
641 - # Only RPK: Public & Private Key  
642 - public_x: "${LWM2M_SERVER_PUBLIC_X_BS:993ef2b698c6a9c0c1d8be78b13a9383c0854c7c7c7a504d289b403794648183}"  
643 - public_y: "${LWM2M_SERVER_PUBLIC_Y_BS:267412d5fc4e5ceb2257cb7fd7f76ebdac2fa9aa100afb162e990074cc0bfaa2}"  
644 - private_s: "${LWM2M_SERVER_PRIVATE_S_BS:9dbdbb073fc63570693a9aaf1013414e261c571f27e27fc6a8c1c2ad9347875a}"  
645 - # Only Certificate_x509: 636 + bind_address_security: "${LWM2M_BIND_ADDRESS_BS:0.0.0.0}"
  637 + bind_port_security: "${LWM2M_BIND_PORT_SEC_BS:5688}"
  638 + # Only for RPK: Public & Private Key. If the keystore file is missing or not working
  639 + # - Elliptic Curve parameters : [secp256r1 [NIST P-256, X9.62 prime256v1] (1.2.840.10045.3.1.7)]
  640 + # - Public Key (Hex): [3059301306072a8648ce3d020106082a8648ce3d030107034200045017c87a1c1768264656b3b355434b0def6edb8b9bf166a4762d9930cd730f913fc4e61bcd8901ec27c424114c3e887ed372497f0c2cf85839b8443e76988b34]
  641 + # - Private Key (Hex): [308193020100301306072a8648ce3d020106082a8648ce3d0301070479307702010104205ecafd90caa7be45c42e1f3f32571632b8409e6e6249d7124f4ba56fab3c8083a00a06082a8648ce3d030107a144034200045017c87a1c1768264656b3b355434b0def6edb8b9bf166a4762d9930cd730f913fc4e61bcd8901ec27c424114c3e887ed372497f0c2cf85839b8443e76988b34],
  642 + public_x: "${LWM2M_SERVER_PUBLIC_X_BS:5017c87a1c1768264656b3b355434b0def6edb8b9bf166a4762d9930cd730f91}"
  643 + public_y: "${LWM2M_SERVER_PUBLIC_Y_BS:3fc4e61bcd8901ec27c424114c3e887ed372497f0c2cf85839b8443e76988b34}"
  644 + private_encoded: "${LWM2M_SERVER_PRIVATE_ENCODED_BS:308193020100301306072a8648ce3d020106082a8648ce3d0301070479307702010104205ecafd90caa7be45c42e1f3f32571632b8409e6e6249d7124f4ba56fab3c8083a00a06082a8648ce3d030107a144034200045017c87a1c1768264656b3b355434b0def6edb8b9bf166a4762d9930cd730f913fc4e61bcd8901ec27c424114c3e887ed372497f0c2cf85839b8443e76988b34}" # Only Certificate_x509:
646 alias: "${LWM2M_KEYSTORE_ALIAS_BOOTSTRAP:bootstrap}" 645 alias: "${LWM2M_KEYSTORE_ALIAS_BOOTSTRAP:bootstrap}"
647 # Redis 646 # Redis
648 redis_url: "${LWM2M_REDIS_URL:''}" 647 redis_url: "${LWM2M_REDIS_URL:''}"
@@ -713,6 +712,10 @@ queue: @@ -713,6 +712,10 @@ queue:
713 transport-api: "${TB_QUEUE_KAFKA_TA_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1}" 712 transport-api: "${TB_QUEUE_KAFKA_TA_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1}"
714 notifications: "${TB_QUEUE_KAFKA_NOTIFICATIONS_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1}" 713 notifications: "${TB_QUEUE_KAFKA_NOTIFICATIONS_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1}"
715 js-executor: "${TB_QUEUE_KAFKA_JE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:104857600;partitions:100}" 714 js-executor: "${TB_QUEUE_KAFKA_JE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:104857600;partitions:100}"
  715 + consumer-stats:
  716 + enabled: "${TB_QUEUE_KAFKA_CONSUMER_STATS_ENABLED:true}"
  717 + print-interval-ms: "${TB_QUEUE_KAFKA_CONSUMER_STATS_MIN_PRINT_INTERVAL_MS:60000}"
  718 + kafka-response-timeout-ms: "${TB_QUEUE_KAFKA_CONSUMER_STATS_RESPONSE_TIMEOUT_MS:1000}"
716 aws_sqs: 719 aws_sqs:
717 use_default_credential_provider_chain: "${TB_QUEUE_AWS_SQS_USE_DEFAULT_CREDENTIAL_PROVIDER_CHAIN:false}" 720 use_default_credential_provider_chain: "${TB_QUEUE_AWS_SQS_USE_DEFAULT_CREDENTIAL_PROVIDER_CHAIN:false}"
718 access_key_id: "${TB_QUEUE_AWS_SQS_ACCESS_KEY_ID:YOUR_KEY}" 721 access_key_id: "${TB_QUEUE_AWS_SQS_ACCESS_KEY_ID:YOUR_KEY}"
@@ -377,6 +377,10 @@ public abstract class AbstractWebTest { @@ -377,6 +377,10 @@ public abstract class AbstractWebTest {
377 return readResponse(doGetAsync(urlTemplate, urlVariables).andExpect(status().isOk()), responseClass); 377 return readResponse(doGetAsync(urlTemplate, urlVariables).andExpect(status().isOk()), responseClass);
378 } 378 }
379 379
  380 + protected <T> T doGetAsyncTyped(String urlTemplate, TypeReference<T> responseType, Object... urlVariables) throws Exception {
  381 + return readResponse(doGetAsync(urlTemplate, urlVariables).andExpect(status().isOk()), responseType);
  382 + }
  383 +
380 protected ResultActions doGetAsync(String urlTemplate, Object... urlVariables) throws Exception { 384 protected ResultActions doGetAsync(String urlTemplate, Object... urlVariables) throws Exception {
381 MockHttpServletRequestBuilder getRequest; 385 MockHttpServletRequestBuilder getRequest;
382 getRequest = get(urlTemplate, urlVariables); 386 getRequest = get(urlTemplate, urlVariables);
@@ -352,8 +352,8 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes @@ -352,8 +352,8 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
352 352
353 Thread.sleep(1000); 353 Thread.sleep(1000);
354 354
355 - List<Map<String, Object>> values = doGetAsync("/api/plugins/telemetry/ENTITY_VIEW/" + savedView.getId().getId().toString() +  
356 - "/values/attributes?keys=" + String.join(",", actualAttributesSet), List.class); 355 + List<Map<String, Object>> values = doGetAsyncTyped("/api/plugins/telemetry/ENTITY_VIEW/" + savedView.getId().getId().toString() +
  356 + "/values/attributes?keys=" + String.join(",", actualAttributesSet), new TypeReference<>() {});
357 357
358 assertEquals("value1", getValue(values, "caKey1")); 358 assertEquals("value1", getValue(values, "caKey1"));
359 assertEquals(true, getValue(values, "caKey2")); 359 assertEquals(true, getValue(values, "caKey2"));
@@ -369,8 +369,8 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes @@ -369,8 +369,8 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
369 Set<String> expectedActualAttributesSet = new HashSet<>(Arrays.asList("caKey1", "caKey2", "caKey3", "caKey4")); 369 Set<String> expectedActualAttributesSet = new HashSet<>(Arrays.asList("caKey1", "caKey2", "caKey3", "caKey4"));
370 assertTrue(actualAttributesSet.containsAll(expectedActualAttributesSet)); 370 assertTrue(actualAttributesSet.containsAll(expectedActualAttributesSet));
371 371
372 - List<Map<String, Object>> valueTelemetryOfDevices = doGetAsync("/api/plugins/telemetry/DEVICE/" + testDevice.getId().getId().toString() +  
373 - "/values/attributes?keys=" + String.join(",", actualAttributesSet), List.class); 372 + List<Map<String, Object>> valueTelemetryOfDevices = doGetAsyncTyped("/api/plugins/telemetry/DEVICE/" + testDevice.getId().getId().toString() +
  373 + "/values/attributes?keys=" + String.join(",", actualAttributesSet), new TypeReference<>() {});
374 374
375 EntityView view = new EntityView(); 375 EntityView view = new EntityView();
376 view.setEntityId(testDevice.getId()); 376 view.setEntityId(testDevice.getId());
@@ -384,8 +384,8 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes @@ -384,8 +384,8 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
384 384
385 Thread.sleep(1000); 385 Thread.sleep(1000);
386 386
387 - List<Map<String, Object>> values = doGetAsync("/api/plugins/telemetry/ENTITY_VIEW/" + savedView.getId().getId().toString() +  
388 - "/values/attributes?keys=" + String.join(",", actualAttributesSet), List.class); 387 + List<Map<String, Object>> values = doGetAsyncTyped("/api/plugins/telemetry/ENTITY_VIEW/" + savedView.getId().getId().toString() +
  388 + "/values/attributes?keys=" + String.join(",", actualAttributesSet), new TypeReference<>() {});
389 assertEquals(0, values.size()); 389 assertEquals(0, values.size());
390 } 390 }
391 391
@@ -454,12 +454,12 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes @@ -454,12 +454,12 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
454 } 454 }
455 455
456 private Set<String> getTelemetryKeys(String type, String id) throws Exception { 456 private Set<String> getTelemetryKeys(String type, String id) throws Exception {
457 - return new HashSet<>(doGetAsync("/api/plugins/telemetry/" + type + "/" + id + "/keys/timeseries", List.class)); 457 + return new HashSet<>(doGetAsyncTyped("/api/plugins/telemetry/" + type + "/" + id + "/keys/timeseries", new TypeReference<>() {}));
458 } 458 }
459 459
460 private Map<String, List<Map<String, String>>> getTelemetryValues(String type, String id, Set<String> keys, Long startTs, Long endTs) throws Exception { 460 private Map<String, List<Map<String, String>>> getTelemetryValues(String type, String id, Set<String> keys, Long startTs, Long endTs) throws Exception {
461 - return doGetAsync("/api/plugins/telemetry/" + type + "/" + id +  
462 - "/values/timeseries?keys=" + String.join(",", keys) + "&startTs=" + startTs + "&endTs=" + endTs, Map.class); 461 + return doGetAsyncTyped("/api/plugins/telemetry/" + type + "/" + id +
  462 + "/values/timeseries?keys=" + String.join(",", keys) + "&startTs=" + startTs + "&endTs=" + endTs, new TypeReference<>() {});
463 } 463 }
464 464
465 private Set<String> getAttributesByKeys(String stringKV) throws Exception { 465 private Set<String> getAttributesByKeys(String stringKV) throws Exception {
@@ -484,7 +484,7 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes @@ -484,7 +484,7 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
484 client.publish("v1/devices/me/attributes", message); 484 client.publish("v1/devices/me/attributes", message);
485 Thread.sleep(1000); 485 Thread.sleep(1000);
486 client.disconnect(); 486 client.disconnect();
487 - return new HashSet<>(doGetAsync("/api/plugins/telemetry/DEVICE/" + viewDeviceId + "/keys/attributes", List.class)); 487 + return new HashSet<>(doGetAsyncTyped("/api/plugins/telemetry/DEVICE/" + viewDeviceId + "/keys/attributes", new TypeReference<>() {}));
488 } 488 }
489 489
490 private Object getValue(List<Map<String, Object>> values, String stringValue) { 490 private Object getValue(List<Map<String, Object>> values, String stringValue) {
@@ -26,7 +26,7 @@ import com.google.protobuf.AbstractMessage; @@ -26,7 +26,7 @@ import com.google.protobuf.AbstractMessage;
26 import com.google.protobuf.InvalidProtocolBufferException; 26 import com.google.protobuf.InvalidProtocolBufferException;
27 import com.google.protobuf.MessageLite; 27 import com.google.protobuf.MessageLite;
28 import lombok.extern.slf4j.Slf4j; 28 import lombok.extern.slf4j.Slf4j;
29 -import org.apache.commons.lang.RandomStringUtils; 29 +import org.apache.commons.lang3.RandomStringUtils;
30 import org.junit.After; 30 import org.junit.After;
31 import org.junit.Assert; 31 import org.junit.Assert;
32 import org.junit.Before; 32 import org.junit.Before;
@@ -1216,12 +1216,12 @@ abstract public class BaseEdgeTest extends AbstractControllerTest { @@ -1216,12 +1216,12 @@ abstract public class BaseEdgeTest extends AbstractControllerTest {
1216 1216
1217 // Wait before device attributes saved to database before requesting them from controller 1217 // Wait before device attributes saved to database before requesting them from controller
1218 Thread.sleep(1000); 1218 Thread.sleep(1000);
1219 - Map<String, List<Map<String, String>>> timeseries = doGetAsync("/api/plugins/telemetry/DEVICE/" + device.getUuidId() + "/values/timeseries?keys=" + timeseriesKey, Map.class); 1219 + Map<String, List<Map<String, String>>> timeseries = doGetAsyncTyped("/api/plugins/telemetry/DEVICE/" + device.getUuidId() + "/values/timeseries?keys=" + timeseriesKey, new TypeReference<>() {});
1220 Assert.assertTrue(timeseries.containsKey(timeseriesKey)); 1220 Assert.assertTrue(timeseries.containsKey(timeseriesKey));
1221 Assert.assertEquals(1, timeseries.get(timeseriesKey).size()); 1221 Assert.assertEquals(1, timeseries.get(timeseriesKey).size());
1222 Assert.assertEquals(timeseriesValue, timeseries.get(timeseriesKey).get(0).get("value")); 1222 Assert.assertEquals(timeseriesValue, timeseries.get(timeseriesKey).get(0).get("value"));
1223 1223
1224 - List<Map<String, String>> attributes = doGetAsync("/api/plugins/telemetry/DEVICE/" + device.getId() + "/values/attributes/" + DataConstants.SERVER_SCOPE, List.class); 1224 + List<Map<String, String>> attributes = doGetAsyncTyped("/api/plugins/telemetry/DEVICE/" + device.getId() + "/values/attributes/" + DataConstants.SERVER_SCOPE, new TypeReference<>() {});
1225 Assert.assertEquals(1, attributes.size()); 1225 Assert.assertEquals(1, attributes.size());
1226 Assert.assertEquals(attributes.get(0).get("key"), attributesKey); 1226 Assert.assertEquals(attributes.get(0).get("key"), attributesKey);
1227 Assert.assertEquals(attributes.get(0).get("value"), attributesValue); 1227 Assert.assertEquals(attributes.get(0).get("value"), attributesValue);
@@ -16,6 +16,7 @@ @@ -16,6 +16,7 @@
16 package org.thingsboard.server.mqtt.telemetry.attributes; 16 package org.thingsboard.server.mqtt.telemetry.attributes;
17 17
18 import com.fasterxml.jackson.core.JsonProcessingException; 18 import com.fasterxml.jackson.core.JsonProcessingException;
  19 +import com.fasterxml.jackson.core.type.TypeReference;
19 import lombok.extern.slf4j.Slf4j; 20 import lombok.extern.slf4j.Slf4j;
20 import org.eclipse.paho.client.mqttv3.MqttAsyncClient; 21 import org.eclipse.paho.client.mqttv3.MqttAsyncClient;
21 import org.junit.After; 22 import org.junit.After;
@@ -80,7 +81,7 @@ public abstract class AbstractMqttAttributesIntegrationTest extends AbstractMqtt @@ -80,7 +81,7 @@ public abstract class AbstractMqttAttributesIntegrationTest extends AbstractMqtt
80 81
81 List<String> actualKeys = null; 82 List<String> actualKeys = null;
82 while (start <= end) { 83 while (start <= end) {
83 - actualKeys = doGetAsync("/api/plugins/telemetry/DEVICE/" + deviceId + "/keys/attributes/CLIENT_SCOPE", List.class); 84 + actualKeys = doGetAsyncTyped("/api/plugins/telemetry/DEVICE/" + deviceId + "/keys/attributes/CLIENT_SCOPE", new TypeReference<>() {});
84 if (actualKeys.size() == expectedKeys.size()) { 85 if (actualKeys.size() == expectedKeys.size()) {
85 break; 86 break;
86 } 87 }
@@ -96,7 +97,7 @@ public abstract class AbstractMqttAttributesIntegrationTest extends AbstractMqtt @@ -96,7 +97,7 @@ public abstract class AbstractMqttAttributesIntegrationTest extends AbstractMqtt
96 assertEquals(expectedKeySet, actualKeySet); 97 assertEquals(expectedKeySet, actualKeySet);
97 98
98 String getAttributesValuesUrl = getAttributesValuesUrl(deviceId, actualKeySet); 99 String getAttributesValuesUrl = getAttributesValuesUrl(deviceId, actualKeySet);
99 - List<Map<String, Object>> values = doGetAsync(getAttributesValuesUrl, List.class); 100 + List<Map<String, Object>> values = doGetAsyncTyped(getAttributesValuesUrl, new TypeReference<>() {});
100 assertAttributesValues(values, expectedKeySet); 101 assertAttributesValues(values, expectedKeySet);
101 String deleteAttributesUrl = "/api/plugins/telemetry/DEVICE/" + deviceId + "/CLIENT_SCOPE?keys=" + String.join(",", actualKeySet); 102 String deleteAttributesUrl = "/api/plugins/telemetry/DEVICE/" + deviceId + "/CLIENT_SCOPE?keys=" + String.join(",", actualKeySet);
102 doDelete(deleteAttributesUrl); 103 doDelete(deleteAttributesUrl);
@@ -121,10 +122,10 @@ public abstract class AbstractMqttAttributesIntegrationTest extends AbstractMqtt @@ -121,10 +122,10 @@ public abstract class AbstractMqttAttributesIntegrationTest extends AbstractMqtt
121 122
122 Thread.sleep(2000); 123 Thread.sleep(2000);
123 124
124 - List<String> firstDeviceActualKeys = doGetAsync("/api/plugins/telemetry/DEVICE/" + firstDevice.getId() + "/keys/attributes/CLIENT_SCOPE", List.class); 125 + List<String> firstDeviceActualKeys = doGetAsyncTyped("/api/plugins/telemetry/DEVICE/" + firstDevice.getId() + "/keys/attributes/CLIENT_SCOPE", new TypeReference<>() {});
125 Set<String> firstDeviceActualKeySet = new HashSet<>(firstDeviceActualKeys); 126 Set<String> firstDeviceActualKeySet = new HashSet<>(firstDeviceActualKeys);
126 127
127 - List<String> secondDeviceActualKeys = doGetAsync("/api/plugins/telemetry/DEVICE/" + secondDevice.getId() + "/keys/attributes/CLIENT_SCOPE", List.class); 128 + List<String> secondDeviceActualKeys = doGetAsyncTyped("/api/plugins/telemetry/DEVICE/" + secondDevice.getId() + "/keys/attributes/CLIENT_SCOPE", new TypeReference<>() {});
128 Set<String> secondDeviceActualKeySet = new HashSet<>(secondDeviceActualKeys); 129 Set<String> secondDeviceActualKeySet = new HashSet<>(secondDeviceActualKeys);
129 130
130 Set<String> expectedKeySet = new HashSet<>(expectedKeys); 131 Set<String> expectedKeySet = new HashSet<>(expectedKeys);
@@ -135,14 +136,15 @@ public abstract class AbstractMqttAttributesIntegrationTest extends AbstractMqtt @@ -135,14 +136,15 @@ public abstract class AbstractMqttAttributesIntegrationTest extends AbstractMqtt
135 String getAttributesValuesUrlFirstDevice = getAttributesValuesUrl(firstDevice.getId(), firstDeviceActualKeySet); 136 String getAttributesValuesUrlFirstDevice = getAttributesValuesUrl(firstDevice.getId(), firstDeviceActualKeySet);
136 String getAttributesValuesUrlSecondDevice = getAttributesValuesUrl(firstDevice.getId(), secondDeviceActualKeySet); 137 String getAttributesValuesUrlSecondDevice = getAttributesValuesUrl(firstDevice.getId(), secondDeviceActualKeySet);
137 138
138 - List<Map<String, Object>> firstDeviceValues = doGetAsync(getAttributesValuesUrlFirstDevice, List.class);  
139 - List<Map<String, Object>> secondDeviceValues = doGetAsync(getAttributesValuesUrlSecondDevice, List.class); 139 + List<Map<String, Object>> firstDeviceValues = doGetAsyncTyped(getAttributesValuesUrlFirstDevice, new TypeReference<>() {});
  140 + List<Map<String, Object>> secondDeviceValues = doGetAsyncTyped(getAttributesValuesUrlSecondDevice, new TypeReference<>() {});
140 141
141 assertAttributesValues(firstDeviceValues, expectedKeySet); 142 assertAttributesValues(firstDeviceValues, expectedKeySet);
142 assertAttributesValues(secondDeviceValues, expectedKeySet); 143 assertAttributesValues(secondDeviceValues, expectedKeySet);
143 144
144 } 145 }
145 146
  147 + @SuppressWarnings("unchecked")
146 protected void assertAttributesValues(List<Map<String, Object>> deviceValues, Set<String> expectedKeySet) throws JsonProcessingException { 148 protected void assertAttributesValues(List<Map<String, Object>> deviceValues, Set<String> expectedKeySet) throws JsonProcessingException {
147 for (Map<String, Object> map : deviceValues) { 149 for (Map<String, Object> map : deviceValues) {
148 String key = (String) map.get("key"); 150 String key = (String) map.get("key");
@@ -15,6 +15,7 @@ @@ -15,6 +15,7 @@
15 */ 15 */
16 package org.thingsboard.server.mqtt.telemetry.timeseries; 16 package org.thingsboard.server.mqtt.telemetry.timeseries;
17 17
  18 +import com.fasterxml.jackson.core.type.TypeReference;
18 import io.netty.handler.codec.mqtt.MqttQoS; 19 import io.netty.handler.codec.mqtt.MqttQoS;
19 import lombok.extern.slf4j.Slf4j; 20 import lombok.extern.slf4j.Slf4j;
20 import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken; 21 import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
@@ -25,6 +26,7 @@ import org.eclipse.paho.client.mqttv3.MqttMessage; @@ -25,6 +26,7 @@ import org.eclipse.paho.client.mqttv3.MqttMessage;
25 import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence; 26 import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
26 import org.junit.After; 27 import org.junit.After;
27 import org.junit.Before; 28 import org.junit.Before;
  29 +import org.junit.Ignore;
28 import org.junit.Test; 30 import org.junit.Test;
29 import org.thingsboard.server.common.data.Device; 31 import org.thingsboard.server.common.data.Device;
30 import org.thingsboard.server.common.data.device.profile.MqttTopics; 32 import org.thingsboard.server.common.data.device.profile.MqttTopics;
@@ -107,7 +109,7 @@ public abstract class AbstractMqttTimeseriesIntegrationTest extends AbstractMqtt @@ -107,7 +109,7 @@ public abstract class AbstractMqttTimeseriesIntegrationTest extends AbstractMqtt
107 109
108 List<String> actualKeys = null; 110 List<String> actualKeys = null;
109 while (start <= end) { 111 while (start <= end) {
110 - actualKeys = doGetAsync("/api/plugins/telemetry/DEVICE/" + deviceId + "/keys/timeseries", List.class); 112 + actualKeys = doGetAsyncTyped("/api/plugins/telemetry/DEVICE/" + deviceId + "/keys/timeseries", new TypeReference<>() {});
111 if (actualKeys.size() == expectedKeys.size()) { 113 if (actualKeys.size() == expectedKeys.size()) {
112 break; 114 break;
113 } 115 }
@@ -129,13 +131,13 @@ public abstract class AbstractMqttTimeseriesIntegrationTest extends AbstractMqtt @@ -129,13 +131,13 @@ public abstract class AbstractMqttTimeseriesIntegrationTest extends AbstractMqtt
129 } 131 }
130 start = System.currentTimeMillis(); 132 start = System.currentTimeMillis();
131 end = System.currentTimeMillis() + 5000; 133 end = System.currentTimeMillis() + 5000;
132 - Map<String, List<Map<String, String>>> values = null; 134 + Map<String, List<Map<String, Object>>> values = null;
133 while (start <= end) { 135 while (start <= end) {
134 - values = doGetAsync(getTelemetryValuesUrl, Map.class); 136 + values = doGetAsyncTyped(getTelemetryValuesUrl, new TypeReference<>() {});
135 boolean valid = values.size() == expectedKeys.size(); 137 boolean valid = values.size() == expectedKeys.size();
136 if (valid) { 138 if (valid) {
137 for (String key : expectedKeys) { 139 for (String key : expectedKeys) {
138 - List<Map<String, String>> tsValues = values.get(key); 140 + List<Map<String, Object>> tsValues = values.get(key);
139 if (tsValues != null && tsValues.size() > 0) { 141 if (tsValues != null && tsValues.size() > 0) {
140 Object ts = tsValues.get(0).get("ts"); 142 Object ts = tsValues.get(0).get("ts");
141 if (ts == null) { 143 if (ts == null) {
@@ -181,10 +183,10 @@ public abstract class AbstractMqttTimeseriesIntegrationTest extends AbstractMqtt @@ -181,10 +183,10 @@ public abstract class AbstractMqttTimeseriesIntegrationTest extends AbstractMqtt
181 183
182 Thread.sleep(2000); 184 Thread.sleep(2000);
183 185
184 - List<String> firstDeviceActualKeys = doGetAsync("/api/plugins/telemetry/DEVICE/" + firstDevice.getId() + "/keys/timeseries", List.class); 186 + List<String> firstDeviceActualKeys = doGetAsyncTyped("/api/plugins/telemetry/DEVICE/" + firstDevice.getId() + "/keys/timeseries", new TypeReference<>() {});
185 Set<String> firstDeviceActualKeySet = new HashSet<>(firstDeviceActualKeys); 187 Set<String> firstDeviceActualKeySet = new HashSet<>(firstDeviceActualKeys);
186 188
187 - List<String> secondDeviceActualKeys = doGetAsync("/api/plugins/telemetry/DEVICE/" + secondDevice.getId() + "/keys/timeseries", List.class); 189 + List<String> secondDeviceActualKeys = doGetAsyncTyped("/api/plugins/telemetry/DEVICE/" + secondDevice.getId() + "/keys/timeseries", new TypeReference<>() {});
188 Set<String> secondDeviceActualKeySet = new HashSet<>(secondDeviceActualKeys); 190 Set<String> secondDeviceActualKeySet = new HashSet<>(secondDeviceActualKeys);
189 191
190 Set<String> expectedKeySet = new HashSet<>(expectedKeys); 192 Set<String> expectedKeySet = new HashSet<>(expectedKeys);
@@ -195,8 +197,8 @@ public abstract class AbstractMqttTimeseriesIntegrationTest extends AbstractMqtt @@ -195,8 +197,8 @@ public abstract class AbstractMqttTimeseriesIntegrationTest extends AbstractMqtt
195 String getTelemetryValuesUrlFirstDevice = getTelemetryValuesUrl(firstDevice.getId(), firstDeviceActualKeySet); 197 String getTelemetryValuesUrlFirstDevice = getTelemetryValuesUrl(firstDevice.getId(), firstDeviceActualKeySet);
196 String getTelemetryValuesUrlSecondDevice = getTelemetryValuesUrl(firstDevice.getId(), secondDeviceActualKeySet); 198 String getTelemetryValuesUrlSecondDevice = getTelemetryValuesUrl(firstDevice.getId(), secondDeviceActualKeySet);
197 199
198 - Map<String, List<Map<String, String>>> firstDeviceValues = doGetAsync(getTelemetryValuesUrlFirstDevice, Map.class);  
199 - Map<String, List<Map<String, String>>> secondDeviceValues = doGetAsync(getTelemetryValuesUrlSecondDevice, Map.class); 200 + Map<String, List<Map<String, Object>>> firstDeviceValues = doGetAsyncTyped(getTelemetryValuesUrlFirstDevice, new TypeReference<>() {});
  201 + Map<String, List<Map<String, Object>>> secondDeviceValues = doGetAsyncTyped(getTelemetryValuesUrlSecondDevice, new TypeReference<>() {});
200 202
201 assertGatewayDeviceData(firstDeviceValues, expectedKeys); 203 assertGatewayDeviceData(firstDeviceValues, expectedKeys);
202 assertGatewayDeviceData(secondDeviceValues, expectedKeys); 204 assertGatewayDeviceData(secondDeviceValues, expectedKeys);
@@ -212,7 +214,7 @@ public abstract class AbstractMqttTimeseriesIntegrationTest extends AbstractMqtt @@ -212,7 +214,7 @@ public abstract class AbstractMqttTimeseriesIntegrationTest extends AbstractMqtt
212 return "/api/plugins/telemetry/DEVICE/" + deviceId + "/values/timeseries?startTs=0&endTs=25000&keys=" + String.join(",", actualKeySet); 214 return "/api/plugins/telemetry/DEVICE/" + deviceId + "/values/timeseries?startTs=0&endTs=25000&keys=" + String.join(",", actualKeySet);
213 } 215 }
214 216
215 - private void assertGatewayDeviceData(Map<String, List<Map<String, String>>> deviceValues, List<String> expectedKeys) { 217 + private void assertGatewayDeviceData(Map<String, List<Map<String, Object>>> deviceValues, List<String> expectedKeys) {
216 218
217 assertEquals(2, deviceValues.get(expectedKeys.get(0)).size()); 219 assertEquals(2, deviceValues.get(expectedKeys.get(0)).size());
218 assertEquals(2, deviceValues.get(expectedKeys.get(1)).size()); 220 assertEquals(2, deviceValues.get(expectedKeys.get(1)).size());
@@ -228,11 +230,11 @@ public abstract class AbstractMqttTimeseriesIntegrationTest extends AbstractMqtt @@ -228,11 +230,11 @@ public abstract class AbstractMqttTimeseriesIntegrationTest extends AbstractMqtt
228 230
229 } 231 }
230 232
231 - private void assertValues(Map<String, List<Map<String, String>>> deviceValues, int arrayIndex) {  
232 - for (Map.Entry<String, List<Map<String, String>>> entry : deviceValues.entrySet()) { 233 + private void assertValues(Map<String, List<Map<String, Object>>> deviceValues, int arrayIndex) {
  234 + for (Map.Entry<String, List<Map<String, Object>>> entry : deviceValues.entrySet()) {
233 String key = entry.getKey(); 235 String key = entry.getKey();
234 - List<Map<String, String>> tsKv = entry.getValue();  
235 - String value = tsKv.get(arrayIndex).get("value"); 236 + List<Map<String, Object>> tsKv = entry.getValue();
  237 + String value = (String) tsKv.get(arrayIndex).get("value");
236 switch (key) { 238 switch (key) {
237 case "key1": 239 case "key1":
238 assertEquals("value1", value); 240 assertEquals("value1", value);
@@ -253,7 +255,7 @@ public abstract class AbstractMqttTimeseriesIntegrationTest extends AbstractMqtt @@ -253,7 +255,7 @@ public abstract class AbstractMqttTimeseriesIntegrationTest extends AbstractMqtt
253 } 255 }
254 } 256 }
255 257
256 - private void assertTs(Map<String, List<Map<String, String>>> deviceValues, List<String> expectedKeys, int ts, int arrayIndex) { 258 + private void assertTs(Map<String, List<Map<String, Object>>> deviceValues, List<String> expectedKeys, int ts, int arrayIndex) {
257 assertEquals(ts, deviceValues.get(expectedKeys.get(0)).get(arrayIndex).get("ts")); 259 assertEquals(ts, deviceValues.get(expectedKeys.get(0)).get(arrayIndex).get("ts"));
258 assertEquals(ts, deviceValues.get(expectedKeys.get(1)).get(arrayIndex).get("ts")); 260 assertEquals(ts, deviceValues.get(expectedKeys.get(1)).get(arrayIndex).get("ts"));
259 assertEquals(ts, deviceValues.get(expectedKeys.get(2)).get(arrayIndex).get("ts")); 261 assertEquals(ts, deviceValues.get(expectedKeys.get(2)).get(arrayIndex).get("ts"));
@@ -21,12 +21,11 @@ import org.junit.Assert; @@ -21,12 +21,11 @@ import org.junit.Assert;
21 import org.junit.Before; 21 import org.junit.Before;
22 import org.junit.Test; 22 import org.junit.Test;
23 import org.junit.runner.RunWith; 23 import org.junit.runner.RunWith;
24 -import org.mockito.runners.MockitoJUnitRunner; 24 +import org.mockito.junit.MockitoJUnitRunner;
25 import org.springframework.context.ApplicationEventPublisher; 25 import org.springframework.context.ApplicationEventPublisher;
26 import org.springframework.test.util.ReflectionTestUtils; 26 import org.springframework.test.util.ReflectionTestUtils;
27 import org.thingsboard.server.common.data.id.DeviceId; 27 import org.thingsboard.server.common.data.id.DeviceId;
28 import org.thingsboard.server.common.data.id.TenantId; 28 import org.thingsboard.server.common.data.id.TenantId;
29 -import org.thingsboard.server.common.msg.queue.ServiceQueue;  
30 import org.thingsboard.server.queue.discovery.HashPartitionService; 29 import org.thingsboard.server.queue.discovery.HashPartitionService;
31 import org.thingsboard.server.common.msg.queue.ServiceType; 30 import org.thingsboard.server.common.msg.queue.ServiceType;
32 import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; 31 import org.thingsboard.server.queue.discovery.TbServiceInfoProvider;
@@ -20,7 +20,7 @@ import org.junit.Assert; @@ -20,7 +20,7 @@ import org.junit.Assert;
20 import org.junit.Test; 20 import org.junit.Test;
21 import org.junit.runner.RunWith; 21 import org.junit.runner.RunWith;
22 import org.mockito.Mockito; 22 import org.mockito.Mockito;
23 -import org.mockito.runners.MockitoJUnitRunner; 23 +import org.mockito.junit.MockitoJUnitRunner;
24 import org.thingsboard.server.gen.transport.TransportProtos; 24 import org.thingsboard.server.gen.transport.TransportProtos;
25 import org.thingsboard.server.queue.common.TbProtoQueueMsg; 25 import org.thingsboard.server.queue.common.TbProtoQueueMsg;
26 import org.thingsboard.server.service.queue.processing.TbRuleEngineSubmitStrategy; 26 import org.thingsboard.server.service.queue.processing.TbRuleEngineSubmitStrategy;
  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.util;
  17 +
  18 +import com.google.common.util.concurrent.MoreExecutors;
  19 +import lombok.extern.slf4j.Slf4j;
  20 +import org.junit.Test;
  21 +import org.junit.runner.RunWith;
  22 +import org.mockito.Mockito;
  23 +import org.mockito.junit.MockitoJUnitRunner;
  24 +import org.thingsboard.server.utils.EventDeduplicationExecutor;
  25 +
  26 +import java.util.concurrent.ExecutorService;
  27 +import java.util.concurrent.Executors;
  28 +import java.util.function.Consumer;
  29 +
  30 +@Slf4j
  31 +@RunWith(MockitoJUnitRunner.class)
  32 +public class EventDeduplicationExecutorTest {
  33 +
  34 + @Test
  35 + public void testSimpleFlowSameThread() throws InterruptedException {
  36 + simpleFlow(MoreExecutors.newDirectExecutorService());
  37 + }
  38 +
  39 + @Test
  40 + public void testPeriodicFlowSameThread() throws InterruptedException {
  41 + periodicFlow(MoreExecutors.newDirectExecutorService());
  42 + }
  43 +
  44 + @Test
  45 + public void testExceptionFlowSameThread() throws InterruptedException {
  46 + exceptionFlow(MoreExecutors.newDirectExecutorService());
  47 + }
  48 +
  49 + @Test
  50 + public void testSimpleFlowSingleThread() throws InterruptedException {
  51 + simpleFlow(Executors.newSingleThreadExecutor());
  52 + }
  53 +
  54 + @Test
  55 + public void testPeriodicFlowSingleThread() throws InterruptedException {
  56 + periodicFlow(Executors.newSingleThreadExecutor());
  57 + }
  58 +
  59 + @Test
  60 + public void testExceptionFlowSingleThread() throws InterruptedException {
  61 + exceptionFlow(Executors.newSingleThreadExecutor());
  62 + }
  63 +
  64 + @Test
  65 + public void testSimpleFlowMultiThread() throws InterruptedException {
  66 + simpleFlow(Executors.newFixedThreadPool(3));
  67 + }
  68 +
  69 + @Test
  70 + public void testPeriodicFlowMultiThread() throws InterruptedException {
  71 + periodicFlow(Executors.newFixedThreadPool(3));
  72 + }
  73 +
  74 + @Test
  75 + public void testExceptionFlowMultiThread() throws InterruptedException {
  76 + exceptionFlow(Executors.newFixedThreadPool(3));
  77 + }
  78 +
  79 + private void simpleFlow(ExecutorService executorService) throws InterruptedException {
  80 + try {
  81 + Consumer<String> function = Mockito.spy(StringConsumer.class);
  82 + EventDeduplicationExecutor<String> executor = new EventDeduplicationExecutor<>(EventDeduplicationExecutorTest.class.getSimpleName(), executorService, function);
  83 +
  84 + String params1 = "params1";
  85 + String params2 = "params2";
  86 + String params3 = "params3";
  87 +
  88 + executor.submit(params1);
  89 + executor.submit(params2);
  90 + executor.submit(params3);
  91 + Thread.sleep(500);
  92 + Mockito.verify(function).accept(params1);
  93 + Mockito.verify(function).accept(params3);
  94 + } finally {
  95 + executorService.shutdownNow();
  96 + }
  97 + }
  98 +
  99 + private void periodicFlow(ExecutorService executorService) throws InterruptedException {
  100 + try {
  101 + Consumer<String> function = Mockito.spy(StringConsumer.class);
  102 + EventDeduplicationExecutor<String> executor = new EventDeduplicationExecutor<>(EventDeduplicationExecutorTest.class.getSimpleName(), executorService, function);
  103 +
  104 + String params1 = "params1";
  105 + String params2 = "params2";
  106 + String params3 = "params3";
  107 +
  108 + executor.submit(params1);
  109 + Thread.sleep(500);
  110 + executor.submit(params2);
  111 + Thread.sleep(500);
  112 + executor.submit(params3);
  113 + Thread.sleep(500);
  114 + Mockito.verify(function).accept(params1);
  115 + Mockito.verify(function).accept(params2);
  116 + Mockito.verify(function).accept(params3);
  117 + } finally {
  118 + executorService.shutdownNow();
  119 + }
  120 + }
  121 +
  122 + private void exceptionFlow(ExecutorService executorService) throws InterruptedException {
  123 + try {
  124 + Consumer<String> function = Mockito.spy(StringConsumer.class);
  125 + EventDeduplicationExecutor<String> executor = new EventDeduplicationExecutor<>(EventDeduplicationExecutorTest.class.getSimpleName(), executorService, function);
  126 +
  127 + String params1 = "params1";
  128 + String params2 = "params2";
  129 + String params3 = "params3";
  130 +
  131 + Mockito.doThrow(new RuntimeException()).when(function).accept("params1");
  132 + executor.submit(params1);
  133 + executor.submit(params2);
  134 + Thread.sleep(500);
  135 + executor.submit(params3);
  136 + Thread.sleep(500);
  137 + Mockito.verify(function).accept(params2);
  138 + Mockito.verify(function).accept(params3);
  139 + } finally {
  140 + executorService.shutdownNow();
  141 + }
  142 + }
  143 +
  144 + public static class StringConsumer implements Consumer<String> {
  145 + @Override
  146 + public void accept(String s) {
  147 + try {
  148 + Thread.sleep(100);
  149 + } catch (InterruptedException e) {
  150 + throw new RuntimeException(e);
  151 + }
  152 + }
  153 + }
  154 +
  155 +}
@@ -67,7 +67,7 @@ @@ -67,7 +67,7 @@
67 </dependency> 67 </dependency>
68 <dependency> 68 <dependency>
69 <groupId>org.mockito</groupId> 69 <groupId>org.mockito</groupId>
70 - <artifactId>mockito-all</artifactId> 70 + <artifactId>mockito-core</artifactId>
71 <scope>test</scope> 71 <scope>test</scope>
72 </dependency> 72 </dependency>
73 </dependencies> 73 </dependencies>
@@ -30,7 +30,7 @@ public interface TbActor { @@ -30,7 +30,7 @@ public interface TbActor {
30 } 30 }
31 31
32 default InitFailureStrategy onInitFailure(int attempt, Throwable t) { 32 default InitFailureStrategy onInitFailure(int attempt, Throwable t) {
33 - return InitFailureStrategy.retryWithDelay(5000 * attempt); 33 + return InitFailureStrategy.retryWithDelay(5000L * attempt);
34 } 34 }
35 35
36 default ProcessFailureStrategy onProcessFailure(Throwable t) { 36 default ProcessFailureStrategy onProcessFailure(Throwable t) {
@@ -17,6 +17,8 @@ package org.thingsboard.server.actors; @@ -17,6 +17,8 @@ package org.thingsboard.server.actors;
17 17
18 public class TbActorException extends Exception { 18 public class TbActorException extends Exception {
19 19
  20 + private static final long serialVersionUID = 8209771144711980882L;
  21 +
20 public TbActorException(String message, Throwable cause) { 22 public TbActorException(String message, Throwable cause) {
21 super(message, cause); 23 super(message, cause);
22 } 24 }
@@ -17,7 +17,9 @@ package org.thingsboard.server.actors; @@ -17,7 +17,9 @@ package org.thingsboard.server.actors;
17 17
18 import lombok.Data; 18 import lombok.Data;
19 import lombok.extern.slf4j.Slf4j; 19 import lombok.extern.slf4j.Slf4j;
  20 +import org.thingsboard.server.common.msg.MsgType;
20 import org.thingsboard.server.common.msg.TbActorMsg; 21 import org.thingsboard.server.common.msg.TbActorMsg;
  22 +import org.thingsboard.server.common.msg.TbActorStopReason;
21 23
22 import java.util.List; 24 import java.util.List;
23 import java.util.concurrent.ConcurrentLinkedQueue; 25 import java.util.concurrent.ConcurrentLinkedQueue;
@@ -49,6 +51,7 @@ public final class TbActorMailbox implements TbActorCtx { @@ -49,6 +51,7 @@ public final class TbActorMailbox implements TbActorCtx {
49 private final AtomicBoolean busy = new AtomicBoolean(FREE); 51 private final AtomicBoolean busy = new AtomicBoolean(FREE);
50 private final AtomicBoolean ready = new AtomicBoolean(NOT_READY); 52 private final AtomicBoolean ready = new AtomicBoolean(NOT_READY);
51 private final AtomicBoolean destroyInProgress = new AtomicBoolean(); 53 private final AtomicBoolean destroyInProgress = new AtomicBoolean();
  54 + private volatile TbActorStopReason stopReason;
52 55
53 public void initActor() { 56 public void initActor() {
54 dispatcher.getExecutor().execute(() -> tryInit(1)); 57 dispatcher.getExecutor().execute(() -> tryInit(1));
@@ -70,7 +73,8 @@ public final class TbActorMailbox implements TbActorCtx { @@ -70,7 +73,8 @@ public final class TbActorMailbox implements TbActorCtx {
70 InitFailureStrategy strategy = actor.onInitFailure(attempt, t); 73 InitFailureStrategy strategy = actor.onInitFailure(attempt, t);
71 if (strategy.isStop() || (settings.getMaxActorInitAttempts() > 0 && attemptIdx > settings.getMaxActorInitAttempts())) { 74 if (strategy.isStop() || (settings.getMaxActorInitAttempts() > 0 && attemptIdx > settings.getMaxActorInitAttempts())) {
72 log.info("[{}] Failed to init actor, attempt {}, going to stop attempts.", selfId, attempt, t); 75 log.info("[{}] Failed to init actor, attempt {}, going to stop attempts.", selfId, attempt, t);
73 - system.stop(selfId); 76 + stopReason = TbActorStopReason.INIT_FAILED;
  77 + destroy();
74 } else if (strategy.getRetryDelay() > 0) { 78 } else if (strategy.getRetryDelay() > 0) {
75 log.info("[{}] Failed to init actor, attempt {}, going to retry in attempts in {}ms", selfId, attempt, strategy.getRetryDelay()); 79 log.info("[{}] Failed to init actor, attempt {}, going to retry in attempts in {}ms", selfId, attempt, strategy.getRetryDelay());
76 log.debug("[{}] Error", selfId, t); 80 log.debug("[{}] Error", selfId, t);
@@ -84,12 +88,28 @@ public final class TbActorMailbox implements TbActorCtx { @@ -84,12 +88,28 @@ public final class TbActorMailbox implements TbActorCtx {
84 } 88 }
85 89
86 private void enqueue(TbActorMsg msg, boolean highPriority) { 90 private void enqueue(TbActorMsg msg, boolean highPriority) {
87 - if (highPriority) {  
88 - highPriorityMsgs.add(msg); 91 + if (!destroyInProgress.get()) {
  92 + if (highPriority) {
  93 + highPriorityMsgs.add(msg);
  94 + } else {
  95 + normalPriorityMsgs.add(msg);
  96 + }
  97 + tryProcessQueue(true);
89 } else { 98 } else {
90 - normalPriorityMsgs.add(msg); 99 + if (highPriority && msg.getMsgType().equals(MsgType.RULE_NODE_UPDATED_MSG)) {
  100 + synchronized (this) {
  101 + if (stopReason == TbActorStopReason.INIT_FAILED) {
  102 + destroyInProgress.set(false);
  103 + stopReason = null;
  104 + initActor();
  105 + } else {
  106 + msg.onTbActorStopped(stopReason);
  107 + }
  108 + }
  109 + } else {
  110 + msg.onTbActorStopped(stopReason);
  111 + }
91 } 112 }
92 - tryProcessQueue(true);  
93 } 113 }
94 114
95 private void tryProcessQueue(boolean newMsg) { 115 private void tryProcessQueue(boolean newMsg) {
@@ -119,6 +139,9 @@ public final class TbActorMailbox implements TbActorCtx { @@ -119,6 +139,9 @@ public final class TbActorMailbox implements TbActorCtx {
119 try { 139 try {
120 log.debug("[{}] Going to process message: {}", selfId, msg); 140 log.debug("[{}] Going to process message: {}", selfId, msg);
121 actor.process(msg); 141 actor.process(msg);
  142 + } catch (TbRuleNodeUpdateException updateException){
  143 + stopReason = TbActorStopReason.INIT_FAILED;
  144 + destroy();
122 } catch (Throwable t) { 145 } catch (Throwable t) {
123 log.debug("[{}] Failed to process message: {}", selfId, msg, t); 146 log.debug("[{}] Failed to process message: {}", selfId, msg, t);
124 ProcessFailureStrategy strategy = actor.onProcessFailure(t); 147 ProcessFailureStrategy strategy = actor.onProcessFailure(t);
@@ -180,11 +203,16 @@ public final class TbActorMailbox implements TbActorCtx { @@ -180,11 +203,16 @@ public final class TbActorMailbox implements TbActorCtx {
180 } 203 }
181 204
182 public void destroy() { 205 public void destroy() {
  206 + if (stopReason == null) {
  207 + stopReason = TbActorStopReason.STOPPED;
  208 + }
183 destroyInProgress.set(true); 209 destroyInProgress.set(true);
184 dispatcher.getExecutor().execute(() -> { 210 dispatcher.getExecutor().execute(() -> {
185 try { 211 try {
186 ready.set(NOT_READY); 212 ready.set(NOT_READY);
187 actor.destroy(); 213 actor.destroy();
  214 + highPriorityMsgs.forEach(msg -> msg.onTbActorStopped(stopReason));
  215 + normalPriorityMsgs.forEach(msg -> msg.onTbActorStopped(stopReason));
188 } catch (Throwable t) { 216 } catch (Throwable t) {
189 log.warn("[{}] Failed to destroy actor: {}", selfId, t); 217 log.warn("[{}] Failed to destroy actor: {}", selfId, t);
190 } 218 }
common/actor/src/main/java/org/thingsboard/server/actors/TbRuleNodeUpdateException.java renamed from ui/src/app/edge/downlinks/edge-downlinks-header.directive.js
1 -/*  
2 - * Copyright © 2016-2020 The Thingsboard Authors 1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
3 * 3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License. 5 * you may not use this file except in compliance with the License.
@@ -13,27 +13,14 @@ @@ -13,27 +13,14 @@
13 * See the License for the specific language governing permissions and 13 * See the License for the specific language governing permissions and
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 -/* eslint-disable import/no-unresolved, import/default */ 16 +package org.thingsboard.server.actors;
17 17
18 -import edgeDownlinksHeaderTemplate from './edge-downlinks-header.tpl.html' 18 +public class TbRuleNodeUpdateException extends RuntimeException {
19 19
20 -/* eslint-enable import/no-unresolved, import/default */ 20 + private static final long serialVersionUID = 8209771144711980882L;
21 21
22 -/*@ngInject*/  
23 -export default function EdgeDownlinksHeaderDirective($compile, $templateCache) {  
24 -  
25 - var linker = function (scope, element) {  
26 -  
27 - var template = edgeDownlinksHeaderTemplate;  
28 -  
29 - element.html($templateCache.get(template));  
30 - $compile(element.contents())(scope); 22 + public TbRuleNodeUpdateException(String message, Throwable cause) {
  23 + super(message, cause);
31 } 24 }
32 -  
33 - return {  
34 - restrict: "A",  
35 - replace: false,  
36 - link: linker,  
37 - scope: false  
38 - };  
39 } 25 }
  26 +
@@ -21,7 +21,7 @@ import org.junit.Assert; @@ -21,7 +21,7 @@ import org.junit.Assert;
21 import org.junit.Before; 21 import org.junit.Before;
22 import org.junit.Test; 22 import org.junit.Test;
23 import org.junit.runner.RunWith; 23 import org.junit.runner.RunWith;
24 -import org.mockito.runners.MockitoJUnitRunner; 24 +import org.mockito.junit.MockitoJUnitRunner;
25 import org.thingsboard.server.common.data.id.DeviceId; 25 import org.thingsboard.server.common.data.id.DeviceId;
26 26
27 import java.util.ArrayList; 27 import java.util.ArrayList;
@@ -49,6 +49,10 @@ @@ -49,6 +49,10 @@
49 <artifactId>guava</artifactId> 49 <artifactId>guava</artifactId>
50 </dependency> 50 </dependency>
51 <dependency> 51 <dependency>
  52 + <groupId>javax.annotation</groupId>
  53 + <artifactId>javax.annotation-api</artifactId>
  54 + </dependency>
  55 + <dependency>
52 <groupId>com.github.fge</groupId> 56 <groupId>com.github.fge</groupId>
53 <artifactId>json-schema-validator</artifactId> 57 <artifactId>json-schema-validator</artifactId>
54 </dependency> 58 </dependency>
@@ -99,7 +103,7 @@ @@ -99,7 +103,7 @@
99 </dependency> 103 </dependency>
100 <dependency> 104 <dependency>
101 <groupId>org.mockito</groupId> 105 <groupId>org.mockito</groupId>
102 - <artifactId>mockito-all</artifactId> 106 + <artifactId>mockito-core</artifactId>
103 <scope>test</scope> 107 <scope>test</scope>
104 </dependency> 108 </dependency>
105 </dependencies> 109 </dependencies>
@@ -23,6 +23,7 @@ import lombok.extern.slf4j.Slf4j; @@ -23,6 +23,7 @@ import lombok.extern.slf4j.Slf4j;
23 import org.springframework.beans.factory.annotation.Autowired; 23 import org.springframework.beans.factory.annotation.Autowired;
24 import org.springframework.beans.factory.annotation.Value; 24 import org.springframework.beans.factory.annotation.Value;
25 import org.springframework.core.env.Environment; 25 import org.springframework.core.env.Environment;
  26 +import org.springframework.core.env.Profiles;
26 import org.thingsboard.server.dao.cassandra.guava.GuavaSession; 27 import org.thingsboard.server.dao.cassandra.guava.GuavaSession;
27 import org.thingsboard.server.dao.cassandra.guava.GuavaSessionBuilder; 28 import org.thingsboard.server.dao.cassandra.guava.GuavaSessionBuilder;
28 import org.thingsboard.server.dao.cassandra.guava.GuavaSessionUtils; 29 import org.thingsboard.server.dao.cassandra.guava.GuavaSessionUtils;
@@ -77,7 +78,7 @@ public abstract class AbstractCassandraCluster { @@ -77,7 +78,7 @@ public abstract class AbstractCassandraCluster {
77 } 78 }
78 79
79 private boolean isInstall() { 80 private boolean isInstall() {
80 - return environment.acceptsProfiles("install"); 81 + return environment.acceptsProfiles(Profiles.of("install"));
81 } 82 }
82 83
83 private void initSession() { 84 private void initSession() {
@@ -18,38 +18,25 @@ package org.thingsboard.server.dao.cassandra.guava; @@ -18,38 +18,25 @@ package org.thingsboard.server.dao.cassandra.guava;
18 import com.datastax.oss.driver.api.core.CqlSession; 18 import com.datastax.oss.driver.api.core.CqlSession;
19 import com.datastax.oss.driver.api.core.config.DriverConfigLoader; 19 import com.datastax.oss.driver.api.core.config.DriverConfigLoader;
20 import com.datastax.oss.driver.api.core.context.DriverContext; 20 import com.datastax.oss.driver.api.core.context.DriverContext;
21 -import com.datastax.oss.driver.api.core.metadata.Node;  
22 -import com.datastax.oss.driver.api.core.metadata.NodeStateListener;  
23 -import com.datastax.oss.driver.api.core.metadata.schema.SchemaChangeListener; 21 +import com.datastax.oss.driver.api.core.session.ProgrammaticArguments;
24 import com.datastax.oss.driver.api.core.session.SessionBuilder; 22 import com.datastax.oss.driver.api.core.session.SessionBuilder;
25 -import com.datastax.oss.driver.api.core.tracker.RequestTracker;  
26 -import com.datastax.oss.driver.api.core.type.codec.TypeCodec;  
27 import edu.umd.cs.findbugs.annotations.NonNull; 23 import edu.umd.cs.findbugs.annotations.NonNull;
28 -import java.util.List;  
29 -import java.util.Map;  
30 -import java.util.function.Predicate;  
31 24
32 public class GuavaSessionBuilder extends SessionBuilder<GuavaSessionBuilder, GuavaSession> { 25 public class GuavaSessionBuilder extends SessionBuilder<GuavaSessionBuilder, GuavaSession> {
33 26
34 @Override 27 @Override
35 protected DriverContext buildContext( 28 protected DriverContext buildContext(
36 DriverConfigLoader configLoader, 29 DriverConfigLoader configLoader,
37 - List<TypeCodec<?>> typeCodecs,  
38 - NodeStateListener nodeStateListener,  
39 - SchemaChangeListener schemaChangeListener,  
40 - RequestTracker requestTracker,  
41 - Map<String, String> localDatacenters,  
42 - Map<String, Predicate<Node>> nodeFilters,  
43 - ClassLoader classLoader) { 30 + ProgrammaticArguments programmaticArguments) {
44 return new GuavaDriverContext( 31 return new GuavaDriverContext(
45 configLoader, 32 configLoader,
46 - typeCodecs,  
47 - nodeStateListener,  
48 - schemaChangeListener,  
49 - requestTracker,  
50 - localDatacenters,  
51 - nodeFilters,  
52 - classLoader); 33 + programmaticArguments.getTypeCodecs(),
  34 + programmaticArguments.getNodeStateListener(),
  35 + programmaticArguments.getSchemaChangeListener(),
  36 + programmaticArguments.getRequestTracker(),
  37 + programmaticArguments.getLocalDatacenters(),
  38 + programmaticArguments.getNodeFilters(),
  39 + programmaticArguments.getClassLoader());
53 } 40 }
54 41
55 @Override 42 @Override
common/dao-api/src/main/java/org/thingsboard/server/dao/util/mapping/JacksonUtil.java renamed from dao/src/main/java/org/thingsboard/server/dao/util/mapping/JacksonUtil.java
@@ -16,6 +16,7 @@ @@ -16,6 +16,7 @@
16 package org.thingsboard.server.dao.util.mapping; 16 package org.thingsboard.server.dao.util.mapping;
17 17
18 import com.fasterxml.jackson.core.JsonProcessingException; 18 import com.fasterxml.jackson.core.JsonProcessingException;
  19 +import com.fasterxml.jackson.core.type.TypeReference;
19 import com.fasterxml.jackson.databind.JsonNode; 20 import com.fasterxml.jackson.databind.JsonNode;
20 import com.fasterxml.jackson.databind.ObjectMapper; 21 import com.fasterxml.jackson.databind.ObjectMapper;
21 import com.fasterxml.jackson.databind.node.ObjectNode; 22 import com.fasterxml.jackson.databind.node.ObjectNode;
@@ -38,6 +39,15 @@ public class JacksonUtil { @@ -38,6 +39,15 @@ public class JacksonUtil {
38 } 39 }
39 } 40 }
40 41
  42 + public static <T> T convertValue(Object fromValue, TypeReference<T> toValueTypeRef) {
  43 + try {
  44 + return fromValue != null ? OBJECT_MAPPER.convertValue(fromValue, toValueTypeRef) : null;
  45 + } catch (IllegalArgumentException e) {
  46 + throw new IllegalArgumentException("The given object value: "
  47 + + fromValue + " cannot be converted to " + toValueTypeRef, e);
  48 + }
  49 + }
  50 +
41 public static <T> T fromString(String string, Class<T> clazz) { 51 public static <T> T fromString(String string, Class<T> clazz) {
42 try { 52 try {
43 return string != null ? OBJECT_MAPPER.readValue(string, clazz) : null; 53 return string != null ? OBJECT_MAPPER.readValue(string, clazz) : null;
@@ -72,7 +82,9 @@ public class JacksonUtil { @@ -72,7 +82,9 @@ public class JacksonUtil {
72 } 82 }
73 83
74 public static <T> T clone(T value) { 84 public static <T> T clone(T value) {
75 - return fromString(toString(value), (Class<T>) value.getClass()); 85 + @SuppressWarnings("unchecked")
  86 + Class<T> valueClass = (Class<T>) value.getClass();
  87 + return fromString(toString(value), valueClass);
76 } 88 }
77 89
78 public static <T> JsonNode valueToTree(T value) { 90 public static <T> JsonNode valueToTree(T value) {
@@ -63,7 +63,7 @@ @@ -63,7 +63,7 @@
63 </dependency> 63 </dependency>
64 <dependency> 64 <dependency>
65 <groupId>org.mockito</groupId> 65 <groupId>org.mockito</groupId>
66 - <artifactId>mockito-all</artifactId> 66 + <artifactId>mockito-core</artifactId>
67 <scope>test</scope> 67 <scope>test</scope>
68 </dependency> 68 </dependency>
69 <dependency> 69 <dependency>
@@ -24,6 +24,7 @@ public class DataConstants { @@ -24,6 +24,7 @@ public class DataConstants {
24 public static final String CUSTOMER = "CUSTOMER"; 24 public static final String CUSTOMER = "CUSTOMER";
25 public static final String DEVICE = "DEVICE"; 25 public static final String DEVICE = "DEVICE";
26 26
  27 + public static final String SCOPE = "scope";
27 public static final String CLIENT_SCOPE = "CLIENT_SCOPE"; 28 public static final String CLIENT_SCOPE = "CLIENT_SCOPE";
28 public static final String SERVER_SCOPE = "SERVER_SCOPE"; 29 public static final String SERVER_SCOPE = "SERVER_SCOPE";
29 public static final String SHARED_SCOPE = "SHARED_SCOPE"; 30 public static final String SHARED_SCOPE = "SHARED_SCOPE";
common/data/src/main/java/org/thingsboard/server/common/data/HomeDashboard.java renamed from application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeToSelfErrorMsg.java
@@ -13,25 +13,18 @@ @@ -13,25 +13,18 @@
13 * See the License for the specific language governing permissions and 13 * See the License for the specific language governing permissions and
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 -package org.thingsboard.server.actors.ruleChain; 16 +package org.thingsboard.server.common.data;
17 17
18 import lombok.Data; 18 import lombok.Data;
19 -import org.thingsboard.server.common.msg.MsgType;  
20 -import org.thingsboard.server.common.msg.TbActorMsg;  
21 -import org.thingsboard.server.common.msg.TbMsg;  
22 19
23 -/**  
24 - * Created by ashvayka on 19.03.18.  
25 - */  
26 @Data 20 @Data
27 -final class RuleNodeToSelfErrorMsg implements TbActorMsg { 21 +public class HomeDashboard extends Dashboard {
28 22
29 - private final TbMsg msg;  
30 - private final Throwable error; 23 + private boolean hideDashboardToolbar;
31 24
32 - @Override  
33 - public MsgType getMsgType() {  
34 - return MsgType.RULE_TO_SELF_ERROR_MSG; 25 + public HomeDashboard(Dashboard dashboard, boolean hideDashboardToolbar) {
  26 + super(dashboard);
  27 + this.hideDashboardToolbar = hideDashboardToolbar;
35 } 28 }
36 29
37 } 30 }
  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.common.data;
  17 +
  18 +import lombok.AllArgsConstructor;
  19 +import lombok.Data;
  20 +import org.thingsboard.server.common.data.id.DashboardId;
  21 +
  22 +@Data
  23 +@AllArgsConstructor
  24 +public class HomeDashboardInfo {
  25 + private DashboardId dashboardId;
  26 + private boolean hideDashboardToolbar;
  27 +}
@@ -15,7 +15,6 @@ @@ -15,7 +15,6 @@
15 */ 15 */
16 package org.thingsboard.server.common.data.lwm2m; 16 package org.thingsboard.server.common.data.lwm2m;
17 17
18 -import lombok.Builder;  
19 import lombok.Data; 18 import lombok.Data;
20 19
21 @Data 20 @Data
@@ -23,12 +22,8 @@ public class ServerSecurityConfig { @@ -23,12 +22,8 @@ public class ServerSecurityConfig {
23 String host; 22 String host;
24 Integer port; 23 Integer port;
25 String serverPublicKey; 24 String serverPublicKey;
26 - @Builder.Default  
27 boolean bootstrapServerIs = true; 25 boolean bootstrapServerIs = true;
28 - @Builder.Default  
29 Integer clientHoldOffTime = 1; 26 Integer clientHoldOffTime = 1;
30 - @Builder.Default  
31 Integer serverId = 111; 27 Integer serverId = 111;
32 - @Builder.Default  
33 Integer bootstrapServerAccountTimeout = 0; 28 Integer bootstrapServerAccountTimeout = 0;
34 } 29 }
@@ -17,19 +17,25 @@ package org.thingsboard.server.common.data.query; @@ -17,19 +17,25 @@ package org.thingsboard.server.common.data.query;
17 17
18 import com.fasterxml.jackson.annotation.JsonIgnore; 18 import com.fasterxml.jackson.annotation.JsonIgnore;
19 import lombok.Data; 19 import lombok.Data;
20 -import lombok.Getter; 20 +import lombok.RequiredArgsConstructor;
21 21
22 import java.io.Serializable; 22 import java.io.Serializable;
23 23
24 @Data 24 @Data
  25 +@RequiredArgsConstructor
25 public class DynamicValue<T> implements Serializable { 26 public class DynamicValue<T> implements Serializable {
26 27
27 @JsonIgnore 28 @JsonIgnore
28 private T resolvedValue; 29 private T resolvedValue;
29 30
30 - @Getter  
31 private final DynamicValueSourceType sourceType; 31 private final DynamicValueSourceType sourceType;
32 - @Getter  
33 private final String sourceAttribute; 32 private final String sourceAttribute;
  33 + private final boolean inherit;
  34 +
  35 + public DynamicValue(DynamicValueSourceType sourceType, String sourceAttribute) {
  36 + this.sourceAttribute = sourceAttribute;
  37 + this.sourceType = sourceType;
  38 + this.inherit = false;
  39 + }
34 40
35 } 41 }
@@ -19,7 +19,7 @@ import com.datastax.oss.driver.api.core.uuid.Uuids; @@ -19,7 +19,7 @@ import com.datastax.oss.driver.api.core.uuid.Uuids;
19 import org.junit.Assert; 19 import org.junit.Assert;
20 import org.junit.Test; 20 import org.junit.Test;
21 import org.junit.runner.RunWith; 21 import org.junit.runner.RunWith;
22 -import org.mockito.runners.MockitoJUnitRunner; 22 +import org.mockito.junit.MockitoJUnitRunner;
23 23
24 import java.util.ArrayList; 24 import java.util.ArrayList;
25 import java.util.Arrays; 25 import java.util.Arrays;
@@ -76,7 +76,7 @@ @@ -76,7 +76,7 @@
76 </dependency> 76 </dependency>
77 <dependency> 77 <dependency>
78 <groupId>org.mockito</groupId> 78 <groupId>org.mockito</groupId>
79 - <artifactId>mockito-all</artifactId> 79 + <artifactId>mockito-core</artifactId>
80 <scope>test</scope> 80 <scope>test</scope>
81 </dependency> 81 </dependency>
82 </dependencies> 82 </dependencies>
@@ -40,6 +40,11 @@ public enum MsgType { @@ -40,6 +40,11 @@ public enum MsgType {
40 COMPONENT_LIFE_CYCLE_MSG, 40 COMPONENT_LIFE_CYCLE_MSG,
41 41
42 /** 42 /**
  43 + * Special message to indicate rule node update request
  44 + */
  45 + RULE_NODE_UPDATED_MSG,
  46 +
  47 + /**
43 * Misc messages consumed from the Queue and forwarded to Rule Engine Actor. 48 * Misc messages consumed from the Queue and forwarded to Rule Engine Actor.
44 * 49 *
45 * See {@link QueueToRuleEngineMsg} 50 * See {@link QueueToRuleEngineMsg}
@@ -67,11 +72,6 @@ public enum MsgType { @@ -67,11 +72,6 @@ public enum MsgType {
67 REMOTE_TO_RULE_CHAIN_TELL_NEXT_MSG, 72 REMOTE_TO_RULE_CHAIN_TELL_NEXT_MSG,
68 73
69 /** 74 /**
70 - * Message that is sent by RuleActor implementation to RuleActor itself to log the error.  
71 - */  
72 - RULE_TO_SELF_ERROR_MSG,  
73 -  
74 - /**  
75 * Message that is sent by RuleActor implementation to RuleActor itself to process the message. 75 * Message that is sent by RuleActor implementation to RuleActor itself to process the message.
76 */ 76 */
77 RULE_TO_SELF_MSG, 77 RULE_TO_SELF_MSG,
@@ -22,4 +22,12 @@ public interface TbActorMsg { @@ -22,4 +22,12 @@ public interface TbActorMsg {
22 22
23 MsgType getMsgType(); 23 MsgType getMsgType();
24 24
  25 + /**
  26 + * Executed when the target TbActor is stopped or destroyed.
  27 + * For example, rule node failed to initialize or removed from rule chain.
  28 + * Implementation should cleanup the resources.
  29 + */
  30 + default void onTbActorStopped(TbActorStopReason reason) {
  31 + }
  32 +
25 } 33 }
  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.common.msg;
  17 +
  18 +public enum TbActorStopReason {
  19 +
  20 + INIT_FAILED, STOPPED
  21 +
  22 +}
  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.common.msg;
  17 +
  18 +import lombok.EqualsAndHashCode;
  19 +import lombok.Getter;
  20 +
  21 +@EqualsAndHashCode
  22 +public abstract class TbRuleEngineActorMsg implements TbActorMsg {
  23 +
  24 + @Getter
  25 + protected final TbMsg msg;
  26 +
  27 + public TbRuleEngineActorMsg(TbMsg msg) {
  28 + this.msg = msg;
  29 + }
  30 +}
  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.common.msg.plugin;
  17 +
  18 +import lombok.ToString;
  19 +import org.thingsboard.server.common.data.id.EntityId;
  20 +import org.thingsboard.server.common.data.id.TenantId;
  21 +import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
  22 +import org.thingsboard.server.common.msg.MsgType;
  23 +
  24 +import java.util.Optional;
  25 +
  26 +/**
  27 + * @author Andrew Shvayka
  28 + */
  29 +@ToString
  30 +public class RuleNodeUpdatedMsg extends ComponentLifecycleMsg {
  31 +
  32 + public RuleNodeUpdatedMsg(TenantId tenantId, EntityId entityId) {
  33 + super(tenantId, entityId, ComponentLifecycleEvent.UPDATED);
  34 + }
  35 +
  36 + @Override
  37 + public MsgType getMsgType() {
  38 + return MsgType.RULE_NODE_UPDATED_MSG;
  39 + }
  40 +}
@@ -15,32 +15,58 @@ @@ -15,32 +15,58 @@
15 */ 15 */
16 package org.thingsboard.server.common.msg.queue; 16 package org.thingsboard.server.common.msg.queue;
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.TenantId; 21 import org.thingsboard.server.common.data.id.TenantId;
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 26
24 -import java.io.Serializable;  
25 import java.util.Set; 27 import java.util.Set;
26 28
27 /** 29 /**
28 * Created by ashvayka on 15.03.18. 30 * Created by ashvayka on 15.03.18.
29 */ 31 */
30 -@Data  
31 -public final class QueueToRuleEngineMsg implements TbActorMsg { 32 +@ToString
  33 +@EqualsAndHashCode(callSuper = true)
  34 +public final class QueueToRuleEngineMsg extends TbRuleEngineActorMsg {
32 35
  36 + @Getter
33 private final TenantId tenantId; 37 private final TenantId tenantId;
34 - private final TbMsg tbMsg; 38 + @Getter
35 private final Set<String> relationTypes; 39 private final Set<String> relationTypes;
  40 + @Getter
36 private final String failureMessage; 41 private final String failureMessage;
37 42
  43 + public QueueToRuleEngineMsg(TenantId tenantId, TbMsg tbMsg, Set<String> relationTypes, String failureMessage) {
  44 + super(tbMsg);
  45 + this.tenantId = tenantId;
  46 + this.relationTypes = relationTypes;
  47 + this.failureMessage = failureMessage;
  48 + }
  49 +
38 @Override 50 @Override
39 public MsgType getMsgType() { 51 public MsgType getMsgType() {
40 return MsgType.QUEUE_TO_RULE_ENGINE_MSG; 52 return MsgType.QUEUE_TO_RULE_ENGINE_MSG;
41 } 53 }
42 54
  55 + @Override
  56 + public void onTbActorStopped(TbActorStopReason reason) {
  57 + String message;
  58 + if (msg.getRuleChainId() != null) {
  59 + message = reason == TbActorStopReason.STOPPED ?
  60 + String.format("Rule chain [%s] stopped", msg.getRuleChainId().getId()) :
  61 + String.format("Failed to initialize rule chain [%s]!", msg.getRuleChainId().getId());
  62 + } else {
  63 + message = reason == TbActorStopReason.STOPPED ? "Rule chain stopped" : "Failed to initialize rule chain!";
  64 + }
  65 + msg.getCallback().onFailure(new RuleEngineException(message));
  66 + }
  67 +
43 public boolean isTellNext() { 68 public boolean isTellNext() {
44 return relationTypes != null && !relationTypes.isEmpty(); 69 return relationTypes != null && !relationTypes.isEmpty();
45 } 70 }
  71 +
46 } 72 }
@@ -24,6 +24,9 @@ import org.thingsboard.server.common.data.rule.RuleNode; @@ -24,6 +24,9 @@ import org.thingsboard.server.common.data.rule.RuleNode;
24 24
25 @Slf4j 25 @Slf4j
26 public class RuleNodeException extends RuleEngineException { 26 public class RuleNodeException extends RuleEngineException {
  27 +
  28 + private static final long serialVersionUID = -1776681087370749776L;
  29 +
27 @Getter 30 @Getter
28 private final String ruleChainName; 31 private final String ruleChainName;
29 @Getter 32 @Getter
@@ -33,6 +36,7 @@ public class RuleNodeException extends RuleEngineException { @@ -33,6 +36,7 @@ public class RuleNodeException extends RuleEngineException {
33 @Getter 36 @Getter
34 private final RuleNodeId ruleNodeId; 37 private final RuleNodeId ruleNodeId;
35 38
  39 +
36 public RuleNodeException(String message, String ruleChainName, RuleNode ruleNode) { 40 public RuleNodeException(String message, String ruleChainName, RuleNode ruleNode) {
37 super(message); 41 super(message);
38 this.ruleChainName = ruleChainName; 42 this.ruleChainName = ruleChainName;
@@ -124,7 +124,7 @@ @@ -124,7 +124,7 @@
124 </dependency> 124 </dependency>
125 <dependency> 125 <dependency>
126 <groupId>org.mockito</groupId> 126 <groupId>org.mockito</groupId>
127 - <artifactId>mockito-all</artifactId> 127 + <artifactId>mockito-core</artifactId>
128 <scope>test</scope> 128 <scope>test</scope>
129 </dependency> 129 </dependency>
130 </dependencies> 130 </dependencies>
@@ -154,6 +154,7 @@ public class TbServiceBusConsumerTemplate<T extends TbQueueMsg> extends Abstract @@ -154,6 +154,7 @@ public class TbServiceBusConsumerTemplate<T extends TbQueueMsg> extends Abstract
154 } 154 }
155 155
156 private <V> CompletableFuture<List<V>> fromList(List<CompletableFuture<V>> futures) { 156 private <V> CompletableFuture<List<V>> fromList(List<CompletableFuture<V>> futures) {
  157 + @SuppressWarnings("unchecked")
157 CompletableFuture<Collection<V>>[] arrayFuture = new CompletableFuture[futures.size()]; 158 CompletableFuture<Collection<V>>[] arrayFuture = new CompletableFuture[futures.size()];
158 futures.toArray(arrayFuture); 159 futures.toArray(arrayFuture);
159 160
@@ -22,7 +22,9 @@ import org.thingsboard.server.common.msg.queue.ServiceQueueKey; @@ -22,7 +22,9 @@ import org.thingsboard.server.common.msg.queue.ServiceQueueKey;
22 import java.util.Set; 22 import java.util.Set;
23 23
24 24
25 -public class ClusterTopologyChangeEvent extends ApplicationEvent { 25 +public class ClusterTopologyChangeEvent extends TbApplicationEvent {
  26 +
  27 + private static final long serialVersionUID = -2441739930040282254L;
26 28
27 @Getter 29 @Getter
28 private final Set<ServiceQueueKey> serviceQueueKeys; 30 private final Set<ServiceQueueKey> serviceQueueKeys;
@@ -126,7 +126,7 @@ public class HashPartitionService implements PartitionService { @@ -126,7 +126,7 @@ public class HashPartitionService implements PartitionService {
126 } 126 }
127 127
128 @Override 128 @Override
129 - public void recalculatePartitions(ServiceInfo currentService, List<ServiceInfo> otherServices) { 129 + public synchronized void recalculatePartitions(ServiceInfo currentService, List<ServiceInfo> otherServices) {
130 logServiceInfo(currentService); 130 logServiceInfo(currentService);
131 otherServices.forEach(this::logServiceInfo); 131 otherServices.forEach(this::logServiceInfo);
132 Map<ServiceQueueKey, List<ServiceInfo>> queueServicesMap = new HashMap<>(); 132 Map<ServiceQueueKey, List<ServiceInfo>> queueServicesMap = new HashMap<>();
@@ -134,7 +134,7 @@ public class HashPartitionService implements PartitionService { @@ -134,7 +134,7 @@ public class HashPartitionService implements PartitionService {
134 for (ServiceInfo other : otherServices) { 134 for (ServiceInfo other : otherServices) {
135 addNode(queueServicesMap, other); 135 addNode(queueServicesMap, other);
136 } 136 }
137 - queueServicesMap.values().forEach(list -> list.sort((a, b) -> a.getServiceId().compareTo(b.getServiceId()))); 137 + queueServicesMap.values().forEach(list -> list.sort(Comparator.comparing(ServiceInfo::getServiceId)));
138 138
139 ConcurrentMap<ServiceQueueKey, List<Integer>> oldPartitions = myPartitions; 139 ConcurrentMap<ServiceQueueKey, List<Integer>> oldPartitions = myPartitions;
140 TenantId myIsolatedOrSystemTenantId = getSystemOrIsolatedTenantId(currentService); 140 TenantId myIsolatedOrSystemTenantId = getSystemOrIsolatedTenantId(currentService);