Commit b58c831343209a653473ede69cfa3cc35d31a005

Authored by Volodymyr Babak
2 parents a8ecf68b 67aba9e4

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

Showing 100 changed files with 1626 additions and 1035 deletions

Too many changes to show.

To preserve performance only 100 of 309 files are displayed.

... ... @@ -283,7 +283,7 @@
283 283 </dependency>
284 284 <dependency>
285 285 <groupId>org.mockito</groupId>
286   - <artifactId>mockito-all</artifactId>
  286 + <artifactId>mockito-core</artifactId>
287 287 <scope>test</scope>
288 288 </dependency>
289 289 <dependency>
... ...
... ... @@ -15,11 +15,10 @@
15 15 #
16 16
17 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 22 export LOG_FILENAME=${pkg.name}.out
24 23 export LOADER_PATH=${pkg.installFolder}/conf,${pkg.installFolder}/extensions
25 24 export SQL_DATA_FOLDER=${pkg.installFolder}/data/sql
... ...
... ... @@ -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 29 "alias": "doughnut_chart_js",
46 30 "name": "Doughnut - Chart.js",
47 31 "descriptor": {
... ... @@ -71,7 +55,7 @@
71 55 "resources": [],
72 56 "templateHtml": "",
73 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 59 "settingsSchema": "{}\n",
76 60 "dataKeySettingsSchema": "{}\n",
77 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 122 }
139 123 },
140 124 {
141   - "alias": "timeseries_bars_flot",
142   - "name": "Timeseries Bars - Flot",
  125 + "alias": "state_chart",
  126 + "name": "State Chart",
143 127 "descriptor": {
144 128 "type": "timeseries",
145 129 "sizeX": 8,
... ... @@ -147,15 +131,15 @@
147 131 "resources": [],
148 132 "templateHtml": "",
149 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 135 "settingsSchema": "{}",
152 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 143 "descriptor": {
160 144 "type": "timeseries",
161 145 "sizeX": 8,
... ... @@ -163,11 +147,27 @@
163 147 "resources": [],
164 148 "templateHtml": "",
165 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 151 "settingsSchema": "{}",
168 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 +}
\ No newline at end of file
... ...
  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 +}
\ No newline at end of file
... ...
... ... @@ -141,12 +141,12 @@ public class AppActor extends ContextAwareActor {
141 141
142 142 private void onQueueToRuleEngineMsg(QueueToRuleEngineMsg msg) {
143 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 145 } else {
146 146 if (!deletedTenants.contains(msg.getTenantId())) {
147 147 getOrCreateTenantActor(msg.getTenantId()).tell(msg);
148 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 19 import com.fasterxml.jackson.databind.ObjectMapper;
20 20 import io.netty.channel.EventLoopGroup;
21 21 import lombok.extern.slf4j.Slf4j;
22   -import org.springframework.data.redis.core.RedisTemplate;
23 22 import org.springframework.util.StringUtils;
24 23 import org.thingsboard.common.util.ListeningExecutor;
25 24 import org.thingsboard.rule.engine.api.MailService;
... ... @@ -92,10 +91,12 @@ class DefaultTbContext implements TbContext {
92 91 public final static ObjectMapper mapper = new ObjectMapper();
93 92
94 93 private final ActorSystemContext mainCtx;
  94 + private final String ruleChainName;
95 95 private final RuleNodeCtx nodeCtx;
96 96
97   - public DefaultTbContext(ActorSystemContext mainCtx, RuleNodeCtx nodeCtx) {
  97 + public DefaultTbContext(ActorSystemContext mainCtx, String ruleChainName, RuleNodeCtx nodeCtx) {
98 98 this.mainCtx = mainCtx;
  99 + this.ruleChainName = ruleChainName;
99 100 this.nodeCtx = nodeCtx;
100 101 }
101 102
... ... @@ -119,13 +120,13 @@ class DefaultTbContext implements TbContext {
119 120 relationTypes.forEach(relationType -> mainCtx.persistDebugOutput(nodeCtx.getTenantId(), nodeCtx.getSelf().getId(), msg, relationType, th));
120 121 }
121 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 126 @Override
126 127 public void tellSelf(TbMsg msg, long delayMs) {
127 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 132 @Override
... ... @@ -256,7 +257,8 @@ class DefaultTbContext implements TbContext {
256 257 } else {
257 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 262 msg, failureMessage));
261 263 }
262 264
... ... @@ -309,6 +311,16 @@ class DefaultTbContext implements TbContext {
309 311 }
310 312
311 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 324 public TenantId getTenantId() {
313 325 return nodeCtx.getTenantId();
314 326 }
... ... @@ -493,11 +505,6 @@ class DefaultTbContext implements TbContext {
493 505 }
494 506
495 507 @Override
496   - public RedisTemplate<String, Object> getRedisTemplate() {
497   - return mainCtx.getRedisTemplate();
498   - }
499   -
500   - @Override
501 508 public PageData<RuleNodeState> findRuleNodeStates(PageLink pageLink) {
502 509 if (log.isDebugEnabled()) {
503 510 log.debug("[{}][{}] Fetch Rule Node States.", getTenantId(), getSelfId());
... ...
... ... @@ -23,7 +23,6 @@ import org.thingsboard.server.actors.TbActorRef;
23 23 import org.thingsboard.server.actors.TbEntityActorId;
24 24 import org.thingsboard.server.actors.service.DefaultActorService;
25 25 import org.thingsboard.server.actors.shared.ComponentMsgProcessor;
26   -import org.thingsboard.server.common.data.ApiUsageRecordKey;
27 26 import org.thingsboard.server.common.data.EntityType;
28 27 import org.thingsboard.server.common.data.id.EntityId;
29 28 import org.thingsboard.server.common.data.id.RuleChainId;
... ... @@ -198,11 +197,11 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
198 197 }
199 198
200 199 void onQueueToRuleEngineMsg(QueueToRuleEngineMsg envelope) {
201   - TbMsg msg = envelope.getTbMsg();
  200 + TbMsg msg = envelope.getMsg();
202 201 log.trace("[{}][{}] Processing message [{}]: {}", entityId, firstId, msg.getId(), msg);
203 202 if (envelope.getRelationTypes() == null || envelope.getRelationTypes().isEmpty()) {
204 203 try {
205   - checkActive(envelope.getTbMsg());
  204 + checkActive(envelope.getMsg());
206 205 RuleNodeId targetId = msg.getRuleNodeId();
207 206 RuleNodeCtx targetCtx;
208 207 if (targetId == null) {
... ... @@ -219,12 +218,12 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
219 218 msg.getCallback().onSuccess();
220 219 }
221 220 } catch (RuleNodeException rne) {
222   - envelope.getTbMsg().getCallback().onFailure(rne);
  221 + envelope.getMsg().getCallback().onFailure(rne);
223 222 } catch (Exception e) {
224   - envelope.getTbMsg().getCallback().onFailure(new RuleEngineException(e.getMessage()));
  223 + envelope.getMsg().getCallback().onFailure(new RuleEngineException(e.getMessage()));
225 224 }
226 225 } else {
227   - onTellNext(envelope.getTbMsg(), envelope.getTbMsg().getRuleNodeId(), envelope.getRelationTypes(), envelope.getFailureMessage());
  226 + onTellNext(envelope.getMsg(), envelope.getMsg().getRuleNodeId(), envelope.getRelationTypes(), envelope.getFailureMessage());
228 227 }
229 228 }
230 229
... ... @@ -336,7 +335,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
336 335
337 336 private void pushMsgToNode(RuleNodeCtx nodeCtx, TbMsg msg, String fromRelationType) {
338 337 if (nodeCtx != null) {
339   - nodeCtx.getSelfActor().tell(new RuleChainToRuleNodeMsg(new DefaultTbContext(systemContext, nodeCtx), msg, fromRelationType));
  338 + nodeCtx.getSelfActor().tell(new RuleChainToRuleNodeMsg(new DefaultTbContext(systemContext, ruleChainName, nodeCtx), msg, fromRelationType));
340 339 } else {
341 340 log.error("[{}][{}] RuleNodeCtx is empty", entityId, ruleChainName);
342 341 msg.getCallback().onFailure(new RuleEngineException("Rule Node CTX is empty"));
... ...
... ... @@ -15,24 +15,44 @@
15 15 */
16 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 21 import org.thingsboard.server.common.data.id.RuleChainId;
20 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 24 import org.thingsboard.server.common.msg.TbMsg;
  25 +import org.thingsboard.server.common.msg.TbRuleEngineActorMsg;
23 26 import org.thingsboard.server.common.msg.aware.RuleChainAwareMsg;
  27 +import org.thingsboard.server.common.msg.queue.RuleEngineException;
24 28
25 29 /**
26 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 37 private final RuleChainId target;
  38 + @Getter
32 39 private final RuleChainId source;
33   - private final TbMsg msg;
  40 + @Getter
34 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 56 @Override
37 57 public RuleChainId getRuleChainId() {
38 58 return target;
... ...
... ... @@ -15,22 +15,28 @@
15 15 */
16 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 21 import org.thingsboard.rule.engine.api.TbContext;
20 22 import org.thingsboard.server.common.msg.MsgType;
21   -import org.thingsboard.server.common.msg.TbActorMsg;
22 23 import org.thingsboard.server.common.msg.TbMsg;
23 24
24 25 /**
25 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 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 40 @Override
35 41 public MsgType getMsgType() {
36 42 return MsgType.RULE_CHAIN_TO_RULE_MSG;
... ...
... ... @@ -59,9 +59,6 @@ public class RuleNodeActor extends ComponentActor<RuleNodeId, RuleNodeActorMessa
59 59 case RULE_CHAIN_TO_RULE_MSG:
60 60 onRuleChainToRuleNodeMsg((RuleChainToRuleNodeMsg) msg);
61 61 break;
62   - case RULE_TO_SELF_ERROR_MSG:
63   - onRuleNodeToSelfErrorMsg((RuleNodeToSelfErrorMsg) msg);
64   - break;
65 62 case RULE_TO_SELF_MSG:
66 63 onRuleNodeToSelfMsg((RuleNodeToSelfMsg) msg);
67 64 break;
... ... @@ -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 101 public static class ActorCreator extends ContextBasedCreator {
109 102
110 103 private final TenantId tenantId;
... ...
... ... @@ -54,7 +54,7 @@ public class RuleNodeActorMessageProcessor extends ComponentMsgProcessor<RuleNod
54 54 this.ruleChainName = ruleChainName;
55 55 this.self = self;
56 56 this.ruleNode = systemContext.getRuleChainService().findRuleNodeById(tenantId, entityId);
57   - this.defaultCtx = new DefaultTbContext(systemContext, new RuleNodeCtx(tenantId, parent, self, ruleNode));
  57 + this.defaultCtx = new DefaultTbContext(systemContext, ruleChainName, new RuleNodeCtx(tenantId, parent, self, ruleNode));
58 58 this.info = new RuleNodeInfo(ruleNodeId, ruleChainName, ruleNode != null ? ruleNode.getName() : "Unknown");
59 59 }
60 60
... ... @@ -147,7 +147,7 @@ public class RuleNodeActorMessageProcessor extends ComponentMsgProcessor<RuleNod
147 147 TbNode tbNode = null;
148 148 if (ruleNode != null) {
149 149 Class<?> componentClazz = Class.forName(ruleNode.getType());
150   - tbNode = (TbNode) (componentClazz.newInstance());
  150 + tbNode = (TbNode) (componentClazz.getDeclaredConstructor().newInstance());
151 151 tbNode.init(defaultCtx, new TbNodeConfiguration(ruleNode.getConfiguration()));
152 152 }
153 153 return tbNode;
... ...
... ... @@ -15,11 +15,16 @@
15 15 */
16 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 22 import org.thingsboard.server.common.data.id.RuleNodeId;
20 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 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 29 import java.io.Serializable;
25 30 import java.util.Set;
... ... @@ -27,15 +32,34 @@ import java.util.Set;
27 32 /**
28 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 39 private static final long serialVersionUID = 4577026446412871820L;
  40 + @Getter
  41 + private final RuleChainId ruleChainId;
  42 + @Getter
34 43 private final RuleNodeId originator;
  44 + @Getter
35 45 private final Set<String> relationTypes;
36   - private final TbMsg msg;
  46 + @Getter
37 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 63 @Override
40 64 public MsgType getMsgType() {
41 65 return MsgType.RULE_TO_RULE_CHAIN_TELL_NEXT_MSG;
... ...
... ... @@ -15,18 +15,25 @@
15 15 */
16 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 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 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 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 38 @Override
32 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 +}
... ...
... ... @@ -28,10 +28,10 @@ import org.thingsboard.server.common.msg.TbActorMsg;
28 28 @ToString
29 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 36 @Override
37 37 public MsgType getMsgType() {
... ...
... ... @@ -18,7 +18,7 @@ package org.thingsboard.server.actors.stats;
18 18 import org.thingsboard.server.common.msg.MsgType;
19 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 22 @Override
23 23 public MsgType getMsgType() {
24 24 return MsgType.STATS_PERSIST_TICK_MSG;
... ...
... ... @@ -125,7 +125,7 @@ public class TenantActor extends RuleChainManagerActor {
125 125 log.info("[{}] Processing missing Tenant msg: {}", tenantId, msg);
126 126 if (msg.getMsgType().equals(MsgType.QUEUE_TO_RULE_ENGINE_MSG)) {
127 127 QueueToRuleEngineMsg queueMsg = (QueueToRuleEngineMsg) msg;
128   - queueMsg.getTbMsg().getCallback().onSuccess();
  128 + queueMsg.getMsg().getCallback().onSuccess();
129 129 } else if (msg.getMsgType().equals(MsgType.TRANSPORT_TO_DEVICE_ACTOR_MSG)) {
130 130 TransportToDeviceActorMsgWrapper transportMsg = (TransportToDeviceActorMsgWrapper) msg;
131 131 transportMsg.getCallback().onSuccess();
... ... @@ -188,7 +188,7 @@ public class TenantActor extends RuleChainManagerActor {
188 188 log.warn("RECEIVED INVALID MESSAGE: {}", msg);
189 189 return;
190 190 }
191   - TbMsg tbMsg = msg.getTbMsg();
  191 + TbMsg tbMsg = msg.getMsg();
192 192 if (apiUsageState.isReExecEnabled()) {
193 193 if (tbMsg.getRuleChainId() == null) {
194 194 if (getRootChainActor() != null) {
... ...
... ... @@ -91,6 +91,7 @@ public class CustomOAuth2AuthorizationRequestResolver implements OAuth2Authoriza
91 91 return action;
92 92 }
93 93
  94 + @SuppressWarnings("deprecation")
94 95 private OAuth2AuthorizationRequest resolve(HttpServletRequest request, String registrationId, String redirectUriAction) {
95 96 if (registrationId == null) {
96 97 return null;
... ...
... ... @@ -127,7 +127,7 @@ public class ThingsboardSecurityConfiguration extends WebSecurityConfigurerAdapt
127 127 }
128 128
129 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 131 pathsToSkip.addAll(Arrays.asList(WS_TOKEN_BASED_AUTH_ENTRY_POINT, TOKEN_REFRESH_ENTRY_POINT, FORM_BASED_LOGIN_ENTRY_POINT,
132 132 PUBLIC_LOGIN_ENTRY_POINT, DEVICE_API_ENTRY_POINT, WEBJARS_ENTRY_POINT));
133 133 SkipPathRequestMatcher matcher = new SkipPathRequestMatcher(pathsToSkip, TOKEN_BASED_AUTH_ENTRY_POINT);
... ...
... ... @@ -719,6 +719,7 @@ public abstract class BaseController {
719 719 return ruleNode;
720 720 }
721 721
  722 + @SuppressWarnings("unchecked")
722 723 protected <I extends EntityId> I emptyId(EntityType entityType) {
723 724 return (I) EntityIdFactory.getByTypeAndUuid(entityType, ModelConstants.NULL_UUID);
724 725 }
... ... @@ -849,6 +850,7 @@ public abstract class BaseController {
849 850 entityNode = json.createObjectNode();
850 851 if (actionType == ActionType.ATTRIBUTES_UPDATED) {
851 852 String scope = extractParameter(String.class, 0, additionalInfo);
  853 + @SuppressWarnings("unchecked")
852 854 List<AttributeKvEntry> attributes = extractParameter(List.class, 1, additionalInfo);
853 855 metaData.putValue("scope", scope);
854 856 if (attributes != null) {
... ... @@ -858,6 +860,7 @@ public abstract class BaseController {
858 860 }
859 861 } else if (actionType == ActionType.ATTRIBUTES_DELETED) {
860 862 String scope = extractParameter(String.class, 0, additionalInfo);
  863 + @SuppressWarnings("unchecked")
861 864 List<String> keys = extractParameter(List.class, 1, additionalInfo);
862 865 metaData.putValue("scope", scope);
863 866 ArrayNode attrsArrayNode = entityNode.putArray("attributes");
... ... @@ -865,9 +868,11 @@ public abstract class BaseController {
865 868 keys.forEach(attrsArrayNode::add);
866 869 }
867 870 } else if (actionType == ActionType.TIMESERIES_UPDATED) {
  871 + @SuppressWarnings("unchecked")
868 872 List<TsKvEntry> timeseries = extractParameter(List.class, 0, additionalInfo);
869 873 addTimeseries(entityNode, timeseries);
870 874 } else if (actionType == ActionType.TIMESERIES_DELETED) {
  875 + @SuppressWarnings("unchecked")
871 876 List<String> keys = extractParameter(List.class, 0, additionalInfo);
872 877 if (keys != null) {
873 878 ArrayNode timeseriesArrayNode = entityNode.putArray("timeseries");
... ...
... ... @@ -15,6 +15,8 @@
15 15 */
16 16 package org.thingsboard.server.controller;
17 17
  18 +import com.fasterxml.jackson.databind.JsonNode;
  19 +import com.fasterxml.jackson.databind.node.ObjectNode;
18 20 import org.springframework.beans.factory.annotation.Value;
19 21 import org.springframework.http.HttpStatus;
20 22 import org.springframework.security.access.prepost.PreAuthorize;
... ... @@ -30,7 +32,11 @@ import org.thingsboard.server.common.data.Customer;
30 32 import org.thingsboard.server.common.data.Dashboard;
31 33 import org.thingsboard.server.common.data.DashboardInfo;
32 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 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 40 import org.thingsboard.server.common.data.audit.ActionType;
35 41 import org.thingsboard.server.common.data.edge.Edge;
36 42 import org.thingsboard.server.common.data.edge.EdgeEventActionType;
... ... @@ -42,7 +48,9 @@ import org.thingsboard.server.common.data.id.TenantId;
42 48 import org.thingsboard.server.common.data.page.PageData;
43 49 import org.thingsboard.server.common.data.page.PageLink;
44 50 import org.thingsboard.server.common.data.page.TimePageLink;
  51 +import org.thingsboard.server.dao.util.mapping.JacksonUtil;
45 52 import org.thingsboard.server.queue.util.TbCoreComponent;
  53 +import org.thingsboard.server.service.security.model.SecurityUser;
46 54 import org.thingsboard.server.service.security.permission.Operation;
47 55 import org.thingsboard.server.service.security.permission.Resource;
48 56
... ... @@ -58,6 +66,9 @@ public class DashboardController extends BaseController {
58 66
59 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 72 @Value("${dashboard.max_datapoints_limit}")
62 73 private long maxDatapointsLimit;
63 74
... ... @@ -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 601 @PreAuthorize("hasAuthority('TENANT_ADMIN')")
495 602 @RequestMapping(value = "/edge/{edgeId}/dashboard/{dashboardId}", method = RequestMethod.POST)
496 603 @ResponseBody
... ...
... ... @@ -73,7 +73,7 @@ import java.util.List;
73 73 import java.util.concurrent.ExecutionException;
74 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 77 import static org.thingsboard.server.controller.CustomerController.CUSTOMER_ID;
78 78 import static org.thingsboard.server.controller.EdgeController.EDGE_ID;
79 79
... ...
... ... @@ -24,6 +24,7 @@ import org.springframework.beans.factory.annotation.Value;
24 24 import org.springframework.beans.factory.config.BeanDefinition;
25 25 import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
26 26 import org.springframework.core.env.Environment;
  27 +import org.springframework.core.env.Profiles;
27 28 import org.springframework.core.type.filter.AnnotationTypeFilter;
28 29 import org.springframework.stereotype.Service;
29 30 import org.thingsboard.rule.engine.api.NodeConfiguration;
... ... @@ -71,7 +72,7 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe
71 72 private ObjectMapper mapper = new ObjectMapper();
72 73
73 74 private boolean isInstall() {
74   - return environment.acceptsProfiles("install");
  75 + return environment.acceptsProfiles(Profiles.of("install"));
75 76 }
76 77
77 78 @PostConstruct
... ... @@ -200,7 +201,7 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe
200 201 nodeDefinition.setRelationTypes(getRelationTypesWithFailureRelation(nodeAnnotation));
201 202 nodeDefinition.setCustomRelations(nodeAnnotation.customRelations());
202 203 Class<? extends NodeConfiguration> configClazz = nodeAnnotation.configClazz();
203   - NodeConfiguration config = configClazz.newInstance();
  204 + NodeConfiguration config = configClazz.getDeclaredConstructor().newInstance();
204 205 NodeConfiguration defaultConfiguration = config.defaultConfiguration();
205 206 nodeDefinition.setDefaultConfiguration(mapper.valueToTree(defaultConfiguration));
206 207 nodeDefinition.setUiResources(nodeAnnotation.uiResources());
... ...
... ... @@ -20,7 +20,7 @@ import com.fasterxml.jackson.databind.JsonNode;
20 20 import com.fasterxml.jackson.databind.node.ObjectNode;
21 21 import com.google.common.util.concurrent.ListenableFuture;
22 22 import lombok.extern.slf4j.Slf4j;
23   -import org.apache.commons.lang.RandomStringUtils;
  23 +import org.apache.commons.lang3.RandomStringUtils;
24 24 import org.springframework.beans.factory.annotation.Autowired;
25 25 import org.springframework.stereotype.Service;
26 26 import org.springframework.util.StringUtils;
... ...
... ... @@ -198,7 +198,7 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
198 198 generalSettings.setKey("general");
199 199 ObjectNode node = objectMapper.createObjectNode();
200 200 node.put("baseUrl", "http://localhost:8080");
201   - node.put("prohibitDifferentUrl", true);
  201 + node.put("prohibitDifferentUrl", false);
202 202 generalSettings.setJsonValue(node);
203 203 adminSettingsService.saveAdminSettings(TenantId.SYS_TENANT_ID, generalSettings);
204 204
... ... @@ -439,6 +439,7 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
439 439 this.deleteSystemWidgetBundle("input_widgets");
440 440 this.deleteSystemWidgetBundle("date");
441 441 this.deleteSystemWidgetBundle("entity_admin_widgets");
  442 + this.deleteSystemWidgetBundle("navigation_widgets");
442 443 this.deleteSystemWidgetBundle("edge_widgets");
443 444 installScripts.loadSystemWidgets();
444 445 }
... ...
... ... @@ -15,11 +15,20 @@
15 15 */
16 16 package org.thingsboard.server.service.install;
17 17
  18 +import lombok.extern.slf4j.Slf4j;
18 19 import org.springframework.context.annotation.Profile;
19 20 import org.springframework.stereotype.Service;
20 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 30 @Service
  31 +@Slf4j
23 32 @HsqlDao
24 33 @Profile("install")
25 34 public class HsqlEntityDatabaseSchemaService extends SqlAbstractDatabaseSchemaService
... ... @@ -27,5 +36,21 @@ public class HsqlEntityDatabaseSchemaService extends SqlAbstractDatabaseSchemaSe
27 36 protected HsqlEntityDatabaseSchemaService() {
28 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 30 @Slf4j
31 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 35 @Value("${spring.datasource.url}")
36 36 protected String dbUrl;
... ... @@ -42,7 +42,7 @@ public abstract class SqlAbstractDatabaseSchemaService implements DatabaseSchema
42 42 protected String dbPassword;
43 43
44 44 @Autowired
45   - private InstallScripts installScripts;
  45 + protected InstallScripts installScripts;
46 46
47 47 private final String schemaSql;
48 48 private final String schemaIdxSql;
... ...
... ... @@ -145,17 +145,17 @@ public class CassandraDbHelper {
145 145 if (row.isNull(index)) {
146 146 return null;
147 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 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 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 153 } else if (type.getProtocolCode() == ProtocolConstants.DataType.UUID) {
154 154 str = row.getUuid(index).toString();
155 155 } else if (type.getProtocolCode() == ProtocolConstants.DataType.TIMEUUID) {
156 156 str = row.getUuid(index).toString();
157 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 159 } else if (type.getProtocolCode() == ProtocolConstants.DataType.TIMESTAMP) {
160 160 str = ""+row.getInstant(index).toEpochMilli();
161 161 } else if (type.getProtocolCode() == ProtocolConstants.DataType.BOOLEAN) {
... ...
... ... @@ -153,7 +153,8 @@ public class CassandraToSqlColumn {
153 153 sqlInsertStatement.setBoolean(this.sqlIndex, Boolean.parseBoolean(value));
154 154 break;
155 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 158 int intValue = enumVal.ordinal();
158 159 sqlInsertStatement.setInt(this.sqlIndex, intValue);
159 160 break;
... ...
... ... @@ -50,7 +50,8 @@ import java.util.List;
50 50 import java.util.concurrent.ExecutionException;
51 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 56 @Service
56 57 @Profile("install")
... ...
... ... @@ -169,11 +169,11 @@ public class LwM2MModelsRepository {
169 169 * PageNumber = 1, PageSize = List<LwM2mObject>.size()
170 170 */
171 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 173 pageLink.getTextSearch(),
174 174 pageLink.getSortOrder().getProperty(),
175 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 177 return pageData;
178 178 }
179 179
... ...
... ... @@ -206,7 +206,7 @@ public class DefaultEntityQueryService implements EntityQueryService {
206 206 addItemsToArrayNode(json.putArray("entityTypes"), types);
207 207 addItemsToArrayNode(json.putArray("timeseries"), timeseriesKeys);
208 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 212 private void replyWithEmptyResponse(DeferredResult<ResponseEntity> response) {
... ...
... ... @@ -181,7 +181,7 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService<
181 181 new TbMsgPackCallback(id, tenantId, ctx, stats.getTimer(tenantId, SUCCESSFUL_STATUS), stats.getTimer(tenantId, FAILED_STATUS)) :
182 182 new TbMsgPackCallback(id, tenantId, ctx);
183 183 try {
184   - if (toRuleEngineMsg.getTbMsg() != null && !toRuleEngineMsg.getTbMsg().isEmpty()) {
  184 + if (!toRuleEngineMsg.getTbMsg().isEmpty()) {
185 185 forwardToRuleEngineActor(configuration.getName(), tenantId, toRuleEngineMsg, callback);
186 186 } else {
187 187 callback.onSuccess();
... ... @@ -209,6 +209,9 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService<
209 209 if (statsEnabled) {
210 210 stats.log(result, decision.isCommit());
211 211 }
  212 +
  213 + ctx.cleanup();
  214 +
212 215 if (decision.isCommit()) {
213 216 submitStrategy.stop();
214 217 break;
... ...
... ... @@ -147,4 +147,10 @@ public class TbMsgPackProcessingContext {
147 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 }
... ...
... ... @@ -31,6 +31,8 @@ import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest;
31 31 @RequiredArgsConstructor
32 32 public class ToDeviceRpcRequestActorMsg implements ToDeviceActorNotificationMsg {
33 33
  34 + private static final long serialVersionUID = -8592877558138716589L;
  35 +
34 36 @Getter
35 37 private final String serviceId;
36 38 @Getter
... ...
... ... @@ -21,7 +21,6 @@ import com.google.common.util.concurrent.ListenableFuture;
21 21 import com.google.common.util.concurrent.MoreExecutors;
22 22 import delight.nashornsandbox.NashornSandbox;
23 23 import delight.nashornsandbox.NashornSandboxes;
24   -import jdk.nashorn.api.scripting.NashornScriptEngineFactory;
25 24 import lombok.Getter;
26 25 import lombok.extern.slf4j.Slf4j;
27 26 import org.springframework.beans.factory.annotation.Value;
... ... @@ -33,6 +32,7 @@ import javax.annotation.PostConstruct;
33 32 import javax.annotation.PreDestroy;
34 33 import javax.script.Invocable;
35 34 import javax.script.ScriptEngine;
  35 +import javax.script.ScriptEngineManager;
36 36 import javax.script.ScriptException;
37 37 import java.util.UUID;
38 38 import java.util.concurrent.ExecutionException;
... ... @@ -97,8 +97,8 @@ public abstract class AbstractNashornJsInvokeService extends AbstractJsInvokeSer
97 97 sandbox.allowLoadFunctions(true);
98 98 sandbox.setMaxPreparedStatements(30);
99 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 29 private RequestMatcher processingMatcher;
30 30
31 31 public SkipPathRequestMatcher(List<String> pathsToSkip, String processingPath) {
32   - Assert.notNull(pathsToSkip);
  32 + Assert.notNull(pathsToSkip, "List of paths to skip is required.");
33 33 List<RequestMatcher> m = pathsToSkip.stream().map(path -> new AntPathRequestMatcher(path)).collect(Collectors.toList());
34 34 matchers = new OrRequestMatcher(m);
35 35 processingMatcher = new AntPathRequestMatcher(processingPath);
... ...
... ... @@ -100,6 +100,7 @@ public class JwtTokenFactory {
100 100 Jws<Claims> jwsClaims = rawAccessToken.parseClaims(settings.getTokenSigningKey());
101 101 Claims claims = jwsClaims.getBody();
102 102 String subject = claims.getSubject();
  103 + @SuppressWarnings("unchecked")
103 104 List<String> scopes = claims.get(SCOPES, List.class);
104 105 if (scopes == null || scopes.isEmpty()) {
105 106 throw new IllegalArgumentException("JWT Token doesn't have any scopes");
... ... @@ -155,6 +156,7 @@ public class JwtTokenFactory {
155 156 Jws<Claims> jwsClaims = rawAccessToken.parseClaims(settings.getTokenSigningKey());
156 157 Claims claims = jwsClaims.getBody();
157 158 String subject = claims.getSubject();
  159 + @SuppressWarnings("unchecked")
158 160 List<String> scopes = claims.get(SCOPES, List.class);
159 161 if (scopes == null || scopes.isEmpty()) {
160 162 throw new IllegalArgumentException("Refresh Token doesn't have any scopes");
... ...
... ... @@ -48,6 +48,7 @@ public class CustomerUserPermissions extends AbstractPermissions {
48 48 Operation.READ_ATTRIBUTES, Operation.READ_TELEMETRY, Operation.RPC_CALL, Operation.CLAIM_DEVICES) {
49 49
50 50 @Override
  51 + @SuppressWarnings("unchecked")
51 52 public boolean hasPermission(SecurityUser user, Operation operation, EntityId entityId, HasTenantId entity) {
52 53
53 54 if (!super.hasPermission(user, operation, entityId, entity)) {
... ... @@ -70,6 +71,7 @@ public class CustomerUserPermissions extends AbstractPermissions {
70 71 new PermissionChecker.GenericPermissionChecker(Operation.READ, Operation.READ_ATTRIBUTES, Operation.READ_TELEMETRY) {
71 72
72 73 @Override
  74 + @SuppressWarnings("unchecked")
73 75 public boolean hasPermission(SecurityUser user, Operation operation, EntityId entityId, HasTenantId entity) {
74 76 if (!super.hasPermission(user, operation, entityId, entity)) {
75 77 return false;
... ... @@ -120,6 +122,7 @@ public class CustomerUserPermissions extends AbstractPermissions {
120 122 private static final PermissionChecker widgetsPermissionChecker = new PermissionChecker.GenericPermissionChecker(Operation.READ) {
121 123
122 124 @Override
  125 + @SuppressWarnings("unchecked")
123 126 public boolean hasPermission(SecurityUser user, Operation operation, EntityId entityId, HasTenantId entity) {
124 127 if (!super.hasPermission(user, operation, entityId, entity)) {
125 128 return false;
... ...
... ... @@ -56,6 +56,7 @@ public class DefaultAccessControlService implements AccessControlService {
56 56 }
57 57
58 58 @Override
  59 + @SuppressWarnings("unchecked")
59 60 public <I extends EntityId, T extends HasTenantId> void checkPermission(SecurityUser user, Resource resource,
60 61 Operation operation, I entityId, T entity) throws ThingsboardException {
61 62 PermissionChecker permissionChecker = getPermissionChecker(user.getAuthority(), resource);
... ...
... ... @@ -60,6 +60,7 @@ public class TenantAdminPermissions extends AbstractPermissions {
60 60 new PermissionChecker.GenericPermissionChecker(Operation.READ, Operation.READ_ATTRIBUTES, Operation.READ_TELEMETRY) {
61 61
62 62 @Override
  63 + @SuppressWarnings("unchecked")
63 64 public boolean hasPermission(SecurityUser user, Operation operation, EntityId entityId, HasTenantId entity) {
64 65 if (!super.hasPermission(user, operation, entityId, entity)) {
65 66 return false;
... ...
... ... @@ -15,8 +15,8 @@
15 15 */
16 16 package org.thingsboard.server.service.security.system;
17 17
  18 +import com.fasterxml.jackson.core.type.TypeReference;
18 19 import com.fasterxml.jackson.databind.JsonNode;
19   -import com.fasterxml.jackson.databind.ObjectMapper;
20 20 import com.fasterxml.jackson.databind.node.ObjectNode;
21 21 import lombok.extern.slf4j.Slf4j;
22 22 import org.apache.commons.lang3.StringUtils;
... ... @@ -49,6 +49,7 @@ import org.thingsboard.server.dao.exception.DataValidationException;
49 49 import org.thingsboard.server.dao.settings.AdminSettingsService;
50 50 import org.thingsboard.server.dao.user.UserService;
51 51 import org.thingsboard.server.dao.user.UserServiceImpl;
  52 +import org.thingsboard.server.dao.util.mapping.JacksonUtil;
52 53 import org.thingsboard.server.service.security.exception.UserPasswordExpiredException;
53 54 import org.thingsboard.server.utils.MiscUtils;
54 55
... ... @@ -65,8 +66,6 @@ import static org.thingsboard.server.common.data.CacheConstants.SECURITY_SETTING
65 66 @Slf4j
66 67 public class DefaultSystemSecurityService implements SystemSecurityService {
67 68
68   - private static final ObjectMapper objectMapper = new ObjectMapper();
69   -
70 69 @Autowired
71 70 private AdminSettingsService adminSettingsService;
72 71
... ... @@ -89,7 +88,7 @@ public class DefaultSystemSecurityService implements SystemSecurityService {
89 88 AdminSettings adminSettings = adminSettingsService.findAdminSettingsByKey(tenantId, "securitySettings");
90 89 if (adminSettings != null) {
91 90 try {
92   - securitySettings = objectMapper.treeToValue(adminSettings.getJsonValue(), SecuritySettings.class);
  91 + securitySettings = JacksonUtil.convertValue(adminSettings.getJsonValue(), SecuritySettings.class);
93 92 } catch (Exception e) {
94 93 throw new RuntimeException("Failed to load security settings!", e);
95 94 }
... ... @@ -109,10 +108,10 @@ public class DefaultSystemSecurityService implements SystemSecurityService {
109 108 adminSettings = new AdminSettings();
110 109 adminSettings.setKey("securitySettings");
111 110 }
112   - adminSettings.setJsonValue(objectMapper.valueToTree(securitySettings));
  111 + adminSettings.setJsonValue(JacksonUtil.valueToTree(securitySettings));
113 112 AdminSettings savedAdminSettings = adminSettingsService.saveAdminSettings(tenantId, adminSettings);
114 113 try {
115   - return objectMapper.treeToValue(savedAdminSettings.getJsonValue(), SecuritySettings.class);
  114 + return JacksonUtil.convertValue(savedAdminSettings.getJsonValue(), SecuritySettings.class);
116 115 } catch (Exception e) {
117 116 throw new RuntimeException("Failed to load security settings!", e);
118 117 }
... ... @@ -189,7 +188,7 @@ public class DefaultSystemSecurityService implements SystemSecurityService {
189 188 JsonNode additionalInfo = user.getAdditionalInfo();
190 189 if (additionalInfo instanceof ObjectNode && additionalInfo.has(UserServiceImpl.USER_PASSWORD_HISTORY)) {
191 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 192 for (Map.Entry<String, String> entry : userPasswordHistoryMap.entrySet()) {
194 193 if (encoder.matches(password, entry.getValue()) && Long.parseLong(entry.getKey()) > passwordReuseFrequencyTs) {
195 194 throw new DataValidationException("Password was already used for the last " + passwordPolicy.getPasswordReuseFrequencyDays() + " days");
... ...
... ... @@ -59,6 +59,7 @@ import org.thingsboard.server.queue.discovery.PartitionService;
59 59 import org.thingsboard.server.queue.util.TbCoreComponent;
60 60 import org.thingsboard.server.service.queue.TbClusterService;
61 61 import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService;
  62 +import org.thingsboard.server.utils.EventDeduplicationExecutor;
62 63
63 64 import javax.annotation.Nullable;
64 65 import javax.annotation.PostConstruct;
... ... @@ -126,13 +127,13 @@ public class DefaultDeviceStateService implements DeviceStateService {
126 127 @Getter
127 128 private int initFetchPackSize;
128 129
129   - private volatile boolean clusterUpdatePending = false;
130   -
131 130 private ListeningScheduledExecutorService queueExecutor;
132 131 private final ConcurrentMap<TopicPartitionInfo, Set<DeviceId>> partitionedDevices = new ConcurrentHashMap<>();
133 132 private final ConcurrentMap<DeviceId, DeviceStateData> deviceStates = new ConcurrentHashMap<>();
134 133 private final ConcurrentMap<DeviceId, Long> deviceLastReportedActivity = new ConcurrentHashMap<>();
135 134 private final ConcurrentMap<DeviceId, Long> deviceLastSavedActivity = new ConcurrentHashMap<>();
  135 + private volatile EventDeduplicationExecutor<Set<TopicPartitionInfo>> deduplicationExecutor;
  136 +
136 137
137 138 public DefaultDeviceStateService(TenantService tenantService, DeviceService deviceService,
138 139 AttributesService attributesService, TimeseriesService tsService,
... ... @@ -155,6 +156,7 @@ public class DefaultDeviceStateService implements DeviceStateService {
155 156 // Should be always single threaded due to absence of locks.
156 157 queueExecutor = MoreExecutors.listeningDecorator(Executors.newSingleThreadScheduledExecutor(ThingsBoardThreadFactory.forName("device-state")));
157 158 queueExecutor.scheduleAtFixedRate(this::updateState, new Random().nextInt(defaultStateCheckIntervalInSec), defaultStateCheckIntervalInSec, TimeUnit.SECONDS);
  159 + deduplicationExecutor = new EventDeduplicationExecutor<>(DefaultDeviceStateService.class.getSimpleName(), queueExecutor, this::initStateFromDB);
158 160 }
159 161
160 162 @PreDestroy
... ... @@ -292,25 +294,14 @@ public class DefaultDeviceStateService implements DeviceStateService {
292 294 }
293 295 }
294 296
295   - volatile Set<TopicPartitionInfo> pendingPartitions;
296   -
297 297 @Override
298 298 public void onApplicationEvent(PartitionChangeEvent partitionChangeEvent) {
299 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 305 try {
315 306 log.info("CURRENT PARTITIONS: {}", partitionedDevices.keySet());
316 307 log.info("NEW PARTITIONS: {}", pendingPartitions);
... ...
... ... @@ -302,7 +302,9 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc
302 302 Map<Integer, TbAbstractDataSubCtx> sessionSubs = subscriptionsBySessionId.computeIfAbsent(sessionRef.getSessionId(), k -> new HashMap<>());
303 303 TbEntityDataSubCtx ctx = new TbEntityDataSubCtx(serviceId, wsService, entityService, localSubscriptionService,
304 304 attributesService, stats, sessionRef, cmd.getCmdId(), maxEntitiesPerDataSubscription);
305   - ctx.setAndResolveQuery(cmd.getQuery());
  305 + if (cmd.getQuery() != null) {
  306 + ctx.setAndResolveQuery(cmd.getQuery());
  307 + }
306 308 sessionSubs.put(cmd.getCmdId(), ctx);
307 309 return ctx;
308 310 }
... ... @@ -316,6 +318,7 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc
316 318 return ctx;
317 319 }
318 320
  321 + @SuppressWarnings("unchecked")
319 322 private <T extends TbAbstractDataSubCtx> T getSubCtx(String sessionId, int cmdId) {
320 323 Map<Integer, TbAbstractDataSubCtx> sessionSubs = subscriptionsBySessionId.get(sessionId);
321 324 if (sessionSubs != null) {
... ...
... ... @@ -123,6 +123,7 @@ public class DefaultTbLocalSubscriptionService implements TbLocalSubscriptionSer
123 123 }
124 124
125 125 @Override
  126 + @SuppressWarnings("unchecked")
126 127 public void onSubscriptionUpdate(String sessionId, TelemetrySubscriptionUpdate update, TbCallback callback) {
127 128 TbSubscription subscription = subscriptionsBySessionId
128 129 .getOrDefault(sessionId, Collections.emptyMap()).get(update.getSubscriptionId());
... ... @@ -143,6 +144,7 @@ public class DefaultTbLocalSubscriptionService implements TbLocalSubscriptionSer
143 144 }
144 145
145 146 @Override
  147 + @SuppressWarnings("unchecked")
146 148 public void onSubscriptionUpdate(String sessionId, AlarmSubscriptionUpdate update, TbCallback callback) {
147 149 TbSubscription subscription = subscriptionsBySessionId
148 150 .getOrDefault(sessionId, Collections.emptyMap()).get(update.getSubscriptionId());
... ...
... ... @@ -107,7 +107,7 @@ public abstract class TbAbstractDataSubCtx<T extends AbstractDataQuery<? extends
107 107 public void setAndResolveQuery(T query) {
108 108 dynamicValues.clear();
109 109 this.query = query;
110   - if (query.getKeyFilters() != null) {
  110 + if (query != null && query.getKeyFilters() != null) {
111 111 for (KeyFilter filter : query.getKeyFilters()) {
112 112 registerDynamicValues(filter.getPredicate());
113 113 }
... ... @@ -264,6 +264,7 @@ public abstract class TbAbstractDataSubCtx<T extends AbstractDataQuery<? extends
264 264 }, MoreExecutors.directExecutor());
265 265 }
266 266
  267 + @SuppressWarnings("unchecked")
267 268 private void updateDynamicValuesByKey(DynamicValueKeySub sub, TsValue tsValue) {
268 269 DynamicValueKey dvk = sub.getKey();
269 270 switch (dvk.getPredicateType()) {
... ... @@ -285,6 +286,7 @@ public abstract class TbAbstractDataSubCtx<T extends AbstractDataQuery<? extends
285 286 }
286 287 }
287 288
  289 + @SuppressWarnings("unchecked")
288 290 private void registerDynamicValues(KeyFilterPredicate predicate) {
289 291 switch (predicate.getType()) {
290 292 case STRING:
... ...
... ... @@ -34,6 +34,8 @@ import java.util.UUID;
34 34 @Data
35 35 public class TransportToDeviceActorMsgWrapper implements TbActorMsg, DeviceAwareMsg, TenantAwareMsg, Serializable {
36 36
  37 + private static final long serialVersionUID = 7191333353202935941L;
  38 +
37 39 private final TenantId tenantId;
38 40 private final DeviceId deviceId;
39 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 33 return "The " + propertyName + " property need to be set!";
34 34 }
35 35
  36 + @SuppressWarnings("deprecation")
36 37 public static HashFunction forName(String name) {
37 38 switch (name) {
38 39 case "murmur3_32":
... ...
... ... @@ -377,6 +377,10 @@ public abstract class AbstractWebTest {
377 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 384 protected ResultActions doGetAsync(String urlTemplate, Object... urlVariables) throws Exception {
381 385 MockHttpServletRequestBuilder getRequest;
382 386 getRequest = get(urlTemplate, urlVariables);
... ...
... ... @@ -352,8 +352,8 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
352 352
353 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 358 assertEquals("value1", getValue(values, "caKey1"));
359 359 assertEquals(true, getValue(values, "caKey2"));
... ... @@ -369,8 +369,8 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
369 369 Set<String> expectedActualAttributesSet = new HashSet<>(Arrays.asList("caKey1", "caKey2", "caKey3", "caKey4"));
370 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 375 EntityView view = new EntityView();
376 376 view.setEntityId(testDevice.getId());
... ... @@ -384,8 +384,8 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
384 384
385 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 389 assertEquals(0, values.size());
390 390 }
391 391
... ... @@ -454,12 +454,12 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
454 454 }
455 455
456 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 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 465 private Set<String> getAttributesByKeys(String stringKV) throws Exception {
... ... @@ -484,7 +484,7 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
484 484 client.publish("v1/devices/me/attributes", message);
485 485 Thread.sleep(1000);
486 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 490 private Object getValue(List<Map<String, Object>> values, String stringValue) {
... ...
... ... @@ -16,6 +16,7 @@
16 16 package org.thingsboard.server.mqtt.telemetry.attributes;
17 17
18 18 import com.fasterxml.jackson.core.JsonProcessingException;
  19 +import com.fasterxml.jackson.core.type.TypeReference;
19 20 import lombok.extern.slf4j.Slf4j;
20 21 import org.eclipse.paho.client.mqttv3.MqttAsyncClient;
21 22 import org.junit.After;
... ... @@ -80,7 +81,7 @@ public abstract class AbstractMqttAttributesIntegrationTest extends AbstractMqtt
80 81
81 82 List<String> actualKeys = null;
82 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 85 if (actualKeys.size() == expectedKeys.size()) {
85 86 break;
86 87 }
... ... @@ -96,7 +97,7 @@ public abstract class AbstractMqttAttributesIntegrationTest extends AbstractMqtt
96 97 assertEquals(expectedKeySet, actualKeySet);
97 98
98 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 101 assertAttributesValues(values, expectedKeySet);
101 102 String deleteAttributesUrl = "/api/plugins/telemetry/DEVICE/" + deviceId + "/CLIENT_SCOPE?keys=" + String.join(",", actualKeySet);
102 103 doDelete(deleteAttributesUrl);
... ... @@ -121,10 +122,10 @@ public abstract class AbstractMqttAttributesIntegrationTest extends AbstractMqtt
121 122
122 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 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 129 Set<String> secondDeviceActualKeySet = new HashSet<>(secondDeviceActualKeys);
129 130
130 131 Set<String> expectedKeySet = new HashSet<>(expectedKeys);
... ... @@ -135,14 +136,15 @@ public abstract class AbstractMqttAttributesIntegrationTest extends AbstractMqtt
135 136 String getAttributesValuesUrlFirstDevice = getAttributesValuesUrl(firstDevice.getId(), firstDeviceActualKeySet);
136 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 142 assertAttributesValues(firstDeviceValues, expectedKeySet);
142 143 assertAttributesValues(secondDeviceValues, expectedKeySet);
143 144
144 145 }
145 146
  147 + @SuppressWarnings("unchecked")
146 148 protected void assertAttributesValues(List<Map<String, Object>> deviceValues, Set<String> expectedKeySet) throws JsonProcessingException {
147 149 for (Map<String, Object> map : deviceValues) {
148 150 String key = (String) map.get("key");
... ...
... ... @@ -15,6 +15,7 @@
15 15 */
16 16 package org.thingsboard.server.mqtt.telemetry.timeseries;
17 17
  18 +import com.fasterxml.jackson.core.type.TypeReference;
18 19 import io.netty.handler.codec.mqtt.MqttQoS;
19 20 import lombok.extern.slf4j.Slf4j;
20 21 import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
... ... @@ -25,6 +26,7 @@ import org.eclipse.paho.client.mqttv3.MqttMessage;
25 26 import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
26 27 import org.junit.After;
27 28 import org.junit.Before;
  29 +import org.junit.Ignore;
28 30 import org.junit.Test;
29 31 import org.thingsboard.server.common.data.Device;
30 32 import org.thingsboard.server.common.data.device.profile.MqttTopics;
... ... @@ -107,7 +109,7 @@ public abstract class AbstractMqttTimeseriesIntegrationTest extends AbstractMqtt
107 109
108 110 List<String> actualKeys = null;
109 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 113 if (actualKeys.size() == expectedKeys.size()) {
112 114 break;
113 115 }
... ... @@ -129,13 +131,13 @@ public abstract class AbstractMqttTimeseriesIntegrationTest extends AbstractMqtt
129 131 }
130 132 start = System.currentTimeMillis();
131 133 end = System.currentTimeMillis() + 5000;
132   - Map<String, List<Map<String, String>>> values = null;
  134 + Map<String, List<Map<String, Object>>> values = null;
133 135 while (start <= end) {
134   - values = doGetAsync(getTelemetryValuesUrl, Map.class);
  136 + values = doGetAsyncTyped(getTelemetryValuesUrl, new TypeReference<>() {});
135 137 boolean valid = values.size() == expectedKeys.size();
136 138 if (valid) {
137 139 for (String key : expectedKeys) {
138   - List<Map<String, String>> tsValues = values.get(key);
  140 + List<Map<String, Object>> tsValues = values.get(key);
139 141 if (tsValues != null && tsValues.size() > 0) {
140 142 Object ts = tsValues.get(0).get("ts");
141 143 if (ts == null) {
... ... @@ -181,10 +183,10 @@ public abstract class AbstractMqttTimeseriesIntegrationTest extends AbstractMqtt
181 183
182 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 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 190 Set<String> secondDeviceActualKeySet = new HashSet<>(secondDeviceActualKeys);
189 191
190 192 Set<String> expectedKeySet = new HashSet<>(expectedKeys);
... ... @@ -195,8 +197,8 @@ public abstract class AbstractMqttTimeseriesIntegrationTest extends AbstractMqtt
195 197 String getTelemetryValuesUrlFirstDevice = getTelemetryValuesUrl(firstDevice.getId(), firstDeviceActualKeySet);
196 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 203 assertGatewayDeviceData(firstDeviceValues, expectedKeys);
202 204 assertGatewayDeviceData(secondDeviceValues, expectedKeys);
... ... @@ -212,7 +214,7 @@ public abstract class AbstractMqttTimeseriesIntegrationTest extends AbstractMqtt
212 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 219 assertEquals(2, deviceValues.get(expectedKeys.get(0)).size());
218 220 assertEquals(2, deviceValues.get(expectedKeys.get(1)).size());
... ... @@ -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 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 238 switch (key) {
237 239 case "key1":
238 240 assertEquals("value1", value);
... ... @@ -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 259 assertEquals(ts, deviceValues.get(expectedKeys.get(0)).get(arrayIndex).get("ts"));
258 260 assertEquals(ts, deviceValues.get(expectedKeys.get(1)).get(arrayIndex).get("ts"));
259 261 assertEquals(ts, deviceValues.get(expectedKeys.get(2)).get(arrayIndex).get("ts"));
... ...
... ... @@ -21,12 +21,11 @@ import org.junit.Assert;
21 21 import org.junit.Before;
22 22 import org.junit.Test;
23 23 import org.junit.runner.RunWith;
24   -import org.mockito.runners.MockitoJUnitRunner;
  24 +import org.mockito.junit.MockitoJUnitRunner;
25 25 import org.springframework.context.ApplicationEventPublisher;
26 26 import org.springframework.test.util.ReflectionTestUtils;
27 27 import org.thingsboard.server.common.data.id.DeviceId;
28 28 import org.thingsboard.server.common.data.id.TenantId;
29   -import org.thingsboard.server.common.msg.queue.ServiceQueue;
30 29 import org.thingsboard.server.queue.discovery.HashPartitionService;
31 30 import org.thingsboard.server.common.msg.queue.ServiceType;
32 31 import org.thingsboard.server.queue.discovery.TbServiceInfoProvider;
... ...
... ... @@ -20,7 +20,7 @@ import org.junit.Assert;
20 20 import org.junit.Test;
21 21 import org.junit.runner.RunWith;
22 22 import org.mockito.Mockito;
23   -import org.mockito.runners.MockitoJUnitRunner;
  23 +import org.mockito.junit.MockitoJUnitRunner;
24 24 import org.thingsboard.server.gen.transport.TransportProtos;
25 25 import org.thingsboard.server.queue.common.TbProtoQueueMsg;
26 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 67 </dependency>
68 68 <dependency>
69 69 <groupId>org.mockito</groupId>
70   - <artifactId>mockito-all</artifactId>
  70 + <artifactId>mockito-core</artifactId>
71 71 <scope>test</scope>
72 72 </dependency>
73 73 </dependencies>
... ...
... ... @@ -30,7 +30,7 @@ public interface TbActor {
30 30 }
31 31
32 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 36 default ProcessFailureStrategy onProcessFailure(Throwable t) {
... ...
... ... @@ -17,6 +17,8 @@ package org.thingsboard.server.actors;
17 17
18 18 public class TbActorException extends Exception {
19 19
  20 + private static final long serialVersionUID = 8209771144711980882L;
  21 +
20 22 public TbActorException(String message, Throwable cause) {
21 23 super(message, cause);
22 24 }
... ...
... ... @@ -18,6 +18,7 @@ package org.thingsboard.server.actors;
18 18 import lombok.Data;
19 19 import lombok.extern.slf4j.Slf4j;
20 20 import org.thingsboard.server.common.msg.TbActorMsg;
  21 +import org.thingsboard.server.common.msg.TbActorStopReason;
21 22
22 23 import java.util.List;
23 24 import java.util.concurrent.ConcurrentLinkedQueue;
... ... @@ -49,6 +50,7 @@ public final class TbActorMailbox implements TbActorCtx {
49 50 private final AtomicBoolean busy = new AtomicBoolean(FREE);
50 51 private final AtomicBoolean ready = new AtomicBoolean(NOT_READY);
51 52 private final AtomicBoolean destroyInProgress = new AtomicBoolean();
  53 + private volatile TbActorStopReason stopReason;
52 54
53 55 public void initActor() {
54 56 dispatcher.getExecutor().execute(() -> tryInit(1));
... ... @@ -70,6 +72,7 @@ public final class TbActorMailbox implements TbActorCtx {
70 72 InitFailureStrategy strategy = actor.onInitFailure(attempt, t);
71 73 if (strategy.isStop() || (settings.getMaxActorInitAttempts() > 0 && attemptIdx > settings.getMaxActorInitAttempts())) {
72 74 log.info("[{}] Failed to init actor, attempt {}, going to stop attempts.", selfId, attempt, t);
  75 + stopReason = TbActorStopReason.INIT_FAILED;
73 76 system.stop(selfId);
74 77 } else if (strategy.getRetryDelay() > 0) {
75 78 log.info("[{}] Failed to init actor, attempt {}, going to retry in attempts in {}ms", selfId, attempt, strategy.getRetryDelay());
... ... @@ -84,12 +87,16 @@ public final class TbActorMailbox implements TbActorCtx {
84 87 }
85 88
86 89 private void enqueue(TbActorMsg msg, boolean highPriority) {
87   - if (highPriority) {
88   - highPriorityMsgs.add(msg);
  90 + if (!destroyInProgress.get()) {
  91 + if (highPriority) {
  92 + highPriorityMsgs.add(msg);
  93 + } else {
  94 + normalPriorityMsgs.add(msg);
  95 + }
  96 + tryProcessQueue(true);
89 97 } else {
90   - normalPriorityMsgs.add(msg);
  98 + msg.onTbActorStopped(stopReason);
91 99 }
92   - tryProcessQueue(true);
93 100 }
94 101
95 102 private void tryProcessQueue(boolean newMsg) {
... ... @@ -180,11 +187,16 @@ public final class TbActorMailbox implements TbActorCtx {
180 187 }
181 188
182 189 public void destroy() {
  190 + if (stopReason == null) {
  191 + stopReason = TbActorStopReason.STOPPED;
  192 + }
183 193 destroyInProgress.set(true);
184 194 dispatcher.getExecutor().execute(() -> {
185 195 try {
186 196 ready.set(NOT_READY);
187 197 actor.destroy();
  198 + highPriorityMsgs.forEach(msg -> msg.onTbActorStopped(stopReason));
  199 + normalPriorityMsgs.forEach(msg -> msg.onTbActorStopped(stopReason));
188 200 } catch (Throwable t) {
189 201 log.warn("[{}] Failed to destroy actor: {}", selfId, t);
190 202 }
... ...
... ... @@ -21,7 +21,7 @@ import org.junit.Assert;
21 21 import org.junit.Before;
22 22 import org.junit.Test;
23 23 import org.junit.runner.RunWith;
24   -import org.mockito.runners.MockitoJUnitRunner;
  24 +import org.mockito.junit.MockitoJUnitRunner;
25 25 import org.thingsboard.server.common.data.id.DeviceId;
26 26
27 27 import java.util.ArrayList;
... ...
... ... @@ -49,6 +49,10 @@
49 49 <artifactId>guava</artifactId>
50 50 </dependency>
51 51 <dependency>
  52 + <groupId>javax.annotation</groupId>
  53 + <artifactId>javax.annotation-api</artifactId>
  54 + </dependency>
  55 + <dependency>
52 56 <groupId>com.github.fge</groupId>
53 57 <artifactId>json-schema-validator</artifactId>
54 58 </dependency>
... ... @@ -99,7 +103,7 @@
99 103 </dependency>
100 104 <dependency>
101 105 <groupId>org.mockito</groupId>
102   - <artifactId>mockito-all</artifactId>
  106 + <artifactId>mockito-core</artifactId>
103 107 <scope>test</scope>
104 108 </dependency>
105 109 </dependencies>
... ...
... ... @@ -23,6 +23,7 @@ import lombok.extern.slf4j.Slf4j;
23 23 import org.springframework.beans.factory.annotation.Autowired;
24 24 import org.springframework.beans.factory.annotation.Value;
25 25 import org.springframework.core.env.Environment;
  26 +import org.springframework.core.env.Profiles;
26 27 import org.thingsboard.server.dao.cassandra.guava.GuavaSession;
27 28 import org.thingsboard.server.dao.cassandra.guava.GuavaSessionBuilder;
28 29 import org.thingsboard.server.dao.cassandra.guava.GuavaSessionUtils;
... ... @@ -77,7 +78,7 @@ public abstract class AbstractCassandraCluster {
77 78 }
78 79
79 80 private boolean isInstall() {
80   - return environment.acceptsProfiles("install");
  81 + return environment.acceptsProfiles(Profiles.of("install"));
81 82 }
82 83
83 84 private void initSession() {
... ...
... ... @@ -18,38 +18,25 @@ package org.thingsboard.server.dao.cassandra.guava;
18 18 import com.datastax.oss.driver.api.core.CqlSession;
19 19 import com.datastax.oss.driver.api.core.config.DriverConfigLoader;
20 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 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 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 25 public class GuavaSessionBuilder extends SessionBuilder<GuavaSessionBuilder, GuavaSession> {
33 26
34 27 @Override
35 28 protected DriverContext buildContext(
36 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 31 return new GuavaDriverContext(
45 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 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 16 package org.thingsboard.server.dao.util.mapping;
17 17
18 18 import com.fasterxml.jackson.core.JsonProcessingException;
  19 +import com.fasterxml.jackson.core.type.TypeReference;
19 20 import com.fasterxml.jackson.databind.JsonNode;
20 21 import com.fasterxml.jackson.databind.ObjectMapper;
21 22 import com.fasterxml.jackson.databind.node.ObjectNode;
... ... @@ -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 51 public static <T> T fromString(String string, Class<T> clazz) {
42 52 try {
43 53 return string != null ? OBJECT_MAPPER.readValue(string, clazz) : null;
... ... @@ -72,7 +82,9 @@ public class JacksonUtil {
72 82 }
73 83
74 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 90 public static <T> JsonNode valueToTree(T value) {
... ...
... ... @@ -63,7 +63,7 @@
63 63 </dependency>
64 64 <dependency>
65 65 <groupId>org.mockito</groupId>
66   - <artifactId>mockito-all</artifactId>
  66 + <artifactId>mockito-core</artifactId>
67 67 <scope>test</scope>
68 68 </dependency>
69 69 <dependency>
... ...
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 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
16   -package org.thingsboard.server.actors.ruleChain;
  16 +package org.thingsboard.server.common.data;
17 17
18 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 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 15 */
16 16 package org.thingsboard.server.common.data.lwm2m;
17 17
18   -import lombok.Builder;
19 18 import lombok.Data;
20 19
21 20 @Data
... ... @@ -23,12 +22,8 @@ public class ServerSecurityConfig {
23 22 String host;
24 23 Integer port;
25 24 String serverPublicKey;
26   - @Builder.Default
27 25 boolean bootstrapServerIs = true;
28   - @Builder.Default
29 26 Integer clientHoldOffTime = 1;
30   - @Builder.Default
31 27 Integer serverId = 111;
32   - @Builder.Default
33 28 Integer bootstrapServerAccountTimeout = 0;
34 29 }
... ...
... ... @@ -19,7 +19,7 @@ import com.datastax.oss.driver.api.core.uuid.Uuids;
19 19 import org.junit.Assert;
20 20 import org.junit.Test;
21 21 import org.junit.runner.RunWith;
22   -import org.mockito.runners.MockitoJUnitRunner;
  22 +import org.mockito.junit.MockitoJUnitRunner;
23 23
24 24 import java.util.ArrayList;
25 25 import java.util.Arrays;
... ...
... ... @@ -76,7 +76,7 @@
76 76 </dependency>
77 77 <dependency>
78 78 <groupId>org.mockito</groupId>
79   - <artifactId>mockito-all</artifactId>
  79 + <artifactId>mockito-core</artifactId>
80 80 <scope>test</scope>
81 81 </dependency>
82 82 </dependencies>
... ...
... ... @@ -67,11 +67,6 @@ public enum MsgType {
67 67 REMOTE_TO_RULE_CHAIN_TELL_NEXT_MSG,
68 68
69 69 /**
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 70 * Message that is sent by RuleActor implementation to RuleActor itself to process the message.
76 71 */
77 72 RULE_TO_SELF_MSG,
... ...
... ... @@ -22,4 +22,12 @@ public interface TbActorMsg {
22 22
23 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 +}
... ...
... ... @@ -15,32 +15,58 @@
15 15 */
16 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 21 import org.thingsboard.server.common.data.id.TenantId;
20 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 24 import org.thingsboard.server.common.msg.TbMsg;
  25 +import org.thingsboard.server.common.msg.TbRuleEngineActorMsg;
23 26
24   -import java.io.Serializable;
25 27 import java.util.Set;
26 28
27 29 /**
28 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 37 private final TenantId tenantId;
34   - private final TbMsg tbMsg;
  38 + @Getter
35 39 private final Set<String> relationTypes;
  40 + @Getter
36 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 50 @Override
39 51 public MsgType getMsgType() {
40 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 68 public boolean isTellNext() {
44 69 return relationTypes != null && !relationTypes.isEmpty();
45 70 }
  71 +
46 72 }
... ...
... ... @@ -24,6 +24,9 @@ import org.thingsboard.server.common.data.rule.RuleNode;
24 24
25 25 @Slf4j
26 26 public class RuleNodeException extends RuleEngineException {
  27 +
  28 + private static final long serialVersionUID = -1776681087370749776L;
  29 +
27 30 @Getter
28 31 private final String ruleChainName;
29 32 @Getter
... ... @@ -33,6 +36,7 @@ public class RuleNodeException extends RuleEngineException {
33 36 @Getter
34 37 private final RuleNodeId ruleNodeId;
35 38
  39 +
36 40 public RuleNodeException(String message, String ruleChainName, RuleNode ruleNode) {
37 41 super(message);
38 42 this.ruleChainName = ruleChainName;
... ...
... ... @@ -124,7 +124,7 @@
124 124 </dependency>
125 125 <dependency>
126 126 <groupId>org.mockito</groupId>
127   - <artifactId>mockito-all</artifactId>
  127 + <artifactId>mockito-core</artifactId>
128 128 <scope>test</scope>
129 129 </dependency>
130 130 </dependencies>
... ...
... ... @@ -154,6 +154,7 @@ public class TbServiceBusConsumerTemplate<T extends TbQueueMsg> extends Abstract
154 154 }
155 155
156 156 private <V> CompletableFuture<List<V>> fromList(List<CompletableFuture<V>> futures) {
  157 + @SuppressWarnings("unchecked")
157 158 CompletableFuture<Collection<V>>[] arrayFuture = new CompletableFuture[futures.size()];
158 159 futures.toArray(arrayFuture);
159 160
... ...
... ... @@ -22,6 +22,10 @@ import org.apache.kafka.clients.CommonClientConfigs;
22 22 import org.apache.kafka.clients.admin.AdminClientConfig;
23 23 import org.apache.kafka.clients.consumer.ConsumerConfig;
24 24 import org.apache.kafka.clients.producer.ProducerConfig;
  25 +import org.apache.kafka.common.serialization.ByteArrayDeserializer;
  26 +import org.apache.kafka.common.serialization.ByteArraySerializer;
  27 +import org.apache.kafka.common.serialization.StringDeserializer;
  28 +import org.apache.kafka.common.serialization.StringSerializer;
25 29 import org.springframework.beans.factory.annotation.Value;
26 30 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
27 31 import org.springframework.boot.context.properties.ConfigurationProperties;
... ... @@ -107,8 +111,8 @@ public class TbKafkaSettings {
107 111 props.put(ConsumerConfig.FETCH_MAX_BYTES_CONFIG, fetchMaxBytes);
108 112 props.put(ConsumerConfig.MAX_POLL_INTERVAL_MS_CONFIG, maxPollIntervalMs);
109 113
110   - props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringDeserializer");
111   - props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.ByteArrayDeserializer");
  114 + props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
  115 + props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, ByteArrayDeserializer.class);
112 116 return props;
113 117 }
114 118
... ... @@ -120,8 +124,8 @@ public class TbKafkaSettings {
120 124 props.put(ProducerConfig.BATCH_SIZE_CONFIG, batchSize);
121 125 props.put(ProducerConfig.LINGER_MS_CONFIG, lingerMs);
122 126 props.put(ProducerConfig.BUFFER_MEMORY_CONFIG, bufferMemory);
123   - props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringSerializer");
124   - props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.ByteArraySerializer");
  127 + props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
  128 + props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, ByteArraySerializer.class);
125 129 return props;
126 130 }
127 131
... ...
... ... @@ -60,6 +60,7 @@ public final class InMemoryStorage {
60 60 public <T extends TbQueueMsg> List<T> get(String topic) throws InterruptedException {
61 61 if (storage.containsKey(topic)) {
62 62 List<T> entities;
  63 + @SuppressWarnings("unchecked")
63 64 T first = (T) storage.get(topic).poll();
64 65 if (first != null) {
65 66 entities = new ArrayList<>();
... ... @@ -67,7 +68,9 @@ public final class InMemoryStorage {
67 68 List<TbQueueMsg> otherList = new ArrayList<>();
68 69 storage.get(topic).drainTo(otherList, 999);
69 70 for (TbQueueMsg other : otherList) {
70   - entities.add((T) other);
  71 + @SuppressWarnings("unchecked")
  72 + T entity = (T) other;
  73 + entities.add(entity);
71 74 }
72 75 } else {
73 76 entities = Collections.emptyList();
... ...
... ... @@ -64,6 +64,7 @@ public class InMemoryTbQueueConsumer<T extends TbQueueMsg> implements TbQueueCon
64 64 @Override
65 65 public List<T> poll(long durationInMillis) {
66 66 if (subscribed) {
  67 + @SuppressWarnings("unchecked")
67 68 List<T> messages = partitions
68 69 .stream()
69 70 .map(tpi -> {
... ...
... ... @@ -47,6 +47,7 @@ public class DefaultTbApiUsageClient implements TbApiUsageClient {
47 47 @Value("${usage.stats.report.interval:10}")
48 48 private int interval;
49 49
  50 + @SuppressWarnings("unchecked")
50 51 private final ConcurrentMap<TenantId, AtomicLong>[] values = new ConcurrentMap[ApiUsageRecordKey.values().length];
51 52 private final PartitionService partitionService;
52 53 private final SchedulerComponent scheduler;
... ...
... ... @@ -79,7 +79,7 @@
79 79 </dependency>
80 80 <dependency>
81 81 <groupId>org.mockito</groupId>
82   - <artifactId>mockito-all</artifactId>
  82 + <artifactId>mockito-core</artifactId>
83 83 <scope>test</scope>
84 84 </dependency>
85 85 </dependencies>
... ... @@ -89,4 +89,4 @@
89 89 </plugins>
90 90 </build>
91 91
92   -</project>
\ No newline at end of file
  92 +</project>
... ...
... ... @@ -80,7 +80,7 @@
80 80 </dependency>
81 81 <dependency>
82 82 <groupId>org.mockito</groupId>
83   - <artifactId>mockito-all</artifactId>
  83 + <artifactId>mockito-core</artifactId>
84 84 <scope>test</scope>
85 85 </dependency>
86 86 </dependencies>
... ...
... ... @@ -25,6 +25,7 @@ import org.eclipse.californium.core.network.Endpoint;
25 25 import org.eclipse.californium.core.network.Exchange;
26 26 import org.eclipse.californium.core.server.resources.CoapExchange;
27 27 import org.eclipse.californium.core.server.resources.Resource;
  28 +import org.eclipse.californium.elements.EndpointContext;
28 29 import org.thingsboard.server.common.data.DataConstants;
29 30 import org.thingsboard.server.common.data.DeviceTransportType;
30 31 import org.thingsboard.server.common.data.security.DeviceTokenCredentials;
... ... @@ -264,12 +265,15 @@ public class CoapTransportResource extends CoapResource {
264 265 }
265 266
266 267 private TransportProtos.SessionInfoProto lookupAsyncSessionInfo(Request request) {
267   - String token = request.getSource().getHostAddress() + ":" + request.getSourcePort() + ":" + request.getTokenString();
  268 + EndpointContext sourceContext = request.getSourceContext();
  269 + String token = sourceContext.getPeerAddress().getAddress().getHostAddress() + ":" + sourceContext.getPeerAddress().getPort() + ":" + request.getTokenString();
268 270 return tokenToSessionIdMap.remove(token);
  271 +
269 272 }
270 273
271 274 private String registerAsyncCoapSession(CoapExchange exchange, Request request, TransportProtos.SessionInfoProto sessionInfo, UUID sessionId) {
272   - String token = request.getSource().getHostAddress() + ":" + request.getSourcePort() + ":" + request.getTokenString();
  275 + EndpointContext sourceContext = request.getSourceContext();
  276 + String token = sourceContext.getPeerAddress().getAddress().getHostAddress() + ":" + sourceContext.getPeerAddress().getPort() + ":" + request.getTokenString();
273 277 tokenToSessionIdMap.putIfAbsent(token, sessionInfo);
274 278 CoapSessionListener attrListener = new CoapSessionListener(sessionId, exchange);
275 279 transportService.registerAsyncSession(sessionInfo, attrListener);
... ...
... ... @@ -73,7 +73,7 @@
73 73 </dependency>
74 74 <dependency>
75 75 <groupId>org.mockito</groupId>
76   - <artifactId>mockito-all</artifactId>
  76 + <artifactId>mockito-core</artifactId>
77 77 <scope>test</scope>
78 78 </dependency>
79 79 </dependencies>
... ...
... ... @@ -107,7 +107,7 @@
107 107 </dependency>
108 108 <dependency>
109 109 <groupId>org.mockito</groupId>
110   - <artifactId>mockito-all</artifactId>
  110 + <artifactId>mockito-core</artifactId>
111 111 <scope>test</scope>
112 112 </dependency>
113 113 <dependency>
... ...
... ... @@ -15,7 +15,6 @@
15 15 */
16 16 package org.thingsboard.server.transport.lwm2m.bootstrap.secure;
17 17
18   -import lombok.Builder;
19 18 import lombok.Data;
20 19 import org.eclipse.leshan.core.SecurityMode;
21 20 import org.eclipse.leshan.core.request.BindingMode;
... ... @@ -40,7 +39,6 @@ public class LwM2MBootstrapConfig {
40 39 * notifIfDisabled: boolean,
41 40 * binding: string
42 41 * */
43   - @Builder.Default
44 42 LwM2MBootstrapServers servers;
45 43
46 44 /** -bootstrapServer, lwm2mServer
... ...
... ... @@ -166,13 +166,13 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
166 166 if (this.getValidatedSecurityMode(lwM2MBootstrapConfig.bootstrapServer, profileServerBootstrap, lwM2MBootstrapConfig.lwm2mServer, profileLwm2mServer)) {
167 167 lwM2MBootstrapConfig.bootstrapServer = new LwM2MServerBootstrap(lwM2MBootstrapConfig.bootstrapServer, profileServerBootstrap);
168 168 lwM2MBootstrapConfig.lwm2mServer = new LwM2MServerBootstrap(lwM2MBootstrapConfig.lwm2mServer, profileLwm2mServer);
169   - String logMsg = String.format(LOG_LW2M_INFO + ": getParametersBootstrap: %s Access connect client with bootstrap server.", store.getEndPoint());
  169 + String logMsg = String.format("%s: getParametersBootstrap: %s Access connect client with bootstrap server.", LOG_LW2M_INFO, store.getEndPoint());
170 170 context.sentParametersOnThingsboard(context.getTelemetryMsgObject(logMsg), LwM2MTransportHandler.DEVICE_TELEMETRY_TOPIC, sessionInfo);
171 171 return lwM2MBootstrapConfig;
172 172 } else {
173 173 log.error(" [{}] Different values SecurityMode between of client and profile.", store.getEndPoint());
174   - log.error(LOG_LW2M_ERROR + " getParametersBootstrap: [{}] Different values SecurityMode between of client and profile.", store.getEndPoint());
175   - String logMsg = String.format(LOG_LW2M_ERROR + ": getParametersBootstrap: %s Different values SecurityMode between of client and profile.", store.getEndPoint());
  174 + log.error("{} getParametersBootstrap: [{}] Different values SecurityMode between of client and profile.", LOG_LW2M_ERROR, store.getEndPoint());
  175 + String logMsg = String.format("%s: getParametersBootstrap: %s Different values SecurityMode between of client and profile.", LOG_LW2M_ERROR, store.getEndPoint());
176 176 context.sentParametersOnThingsboard(context.getTelemetryMsgObject(logMsg), LwM2MTransportHandler.DEVICE_TELEMETRY_TOPIC, sessionInfo);
177 177 return null;
178 178 }
... ... @@ -188,6 +188,7 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
188 188 /**
189 189 * Bootstrap security have to sync between (bootstrapServer in credential and bootstrapServer in profile)
190 190 * and (lwm2mServer in credential and lwm2mServer in profile
  191 + *
191 192 * @param bootstrapFromCredential - Bootstrap -> Security of bootstrapServer in credential
192 193 * @param profileServerBootstrap - Bootstrap -> Security of bootstrapServer in profile
193 194 * @param lwm2mFromCredential - Bootstrap -> Security of lwm2mServer in credential
... ...
... ... @@ -15,19 +15,13 @@
15 15 */
16 16 package org.thingsboard.server.transport.lwm2m.bootstrap.secure;
17 17
18   -import lombok.Builder;
19 18 import lombok.Data;
20 19
21 20 @Data
22 21 public class LwM2MBootstrapServers {
23   - @Builder.Default
24 22 private Integer shortId = 123;
25   - @Builder.Default
26 23 private Integer lifetime = 300;
27   - @Builder.Default
28 24 private Integer defaultMinPeriod = 1;
29   - @Builder.Default
30 25 private boolean notifIfDisabled = true;
31   - @Builder.Default
32 26 private String binding = "U";
33 27 }
... ...
... ... @@ -15,7 +15,6 @@
15 15 */
16 16 package org.thingsboard.server.transport.lwm2m.bootstrap.secure;
17 17
18   -import lombok.Builder;
19 18 import lombok.Data;
20 19 import lombok.extern.slf4j.Slf4j;
21 20 import org.eclipse.leshan.core.SecurityMode;
... ... @@ -24,28 +23,18 @@ import org.eclipse.leshan.core.SecurityMode;
24 23 @Data
25 24 public class LwM2MServerBootstrap {
26 25
27   - @Builder.Default
28 26 String clientPublicKeyOrId = "";
29   - @Builder.Default
30 27 String clientSecretKey = "";
31   - @Builder.Default
32 28 String serverPublicKey = "";
33   - @Builder.Default
34 29 Integer clientHoldOffTime = 1;
35   - @Builder.Default
36 30 Integer bootstrapServerAccountTimeout = 0;
37 31
38   - @Builder.Default
39 32 String host = "0.0.0.0";
40   - @Builder.Default
41 33 Integer port = 0;
42 34
43   - @Builder.Default
44 35 String securityMode = SecurityMode.NO_SEC.name();
45 36
46   - @Builder.Default
47 37 Integer serverId = 123;
48   - @Builder.Default
49 38 boolean bootstrapServerIs = false;
50 39
51 40 public LwM2MServerBootstrap(){};
... ...
... ... @@ -25,6 +25,7 @@ import org.eclipse.leshan.server.security.SecurityChecker;
25 25 import org.eclipse.leshan.server.security.SecurityInfo;
26 26
27 27 import java.util.Arrays;
  28 +import java.util.Collections;
28 29 import java.util.List;
29 30
30 31 @Slf4j
... ... @@ -49,10 +50,11 @@ public class LwM2mDefaultBootstrapSessionManager extends DefaultBootstrapSession
49 50 this.securityChecker = securityChecker;
50 51 }
51 52
  53 + @SuppressWarnings("deprecation")
52 54 public BootstrapSession begin(String endpoint, Identity clientIdentity) {
53 55 boolean authorized;
54 56 if (bsSecurityStore != null) {
55   - List<SecurityInfo> securityInfos = (clientIdentity.getPskIdentity() != null && !clientIdentity.getPskIdentity().isEmpty()) ? Arrays.asList(bsSecurityStore.getByIdentity(clientIdentity.getPskIdentity())) : bsSecurityStore.getAllByEndpoint(endpoint);
  57 + List<SecurityInfo> securityInfos = (clientIdentity.getPskIdentity() != null && !clientIdentity.getPskIdentity().isEmpty()) ? Collections.singletonList(bsSecurityStore.getByIdentity(clientIdentity.getPskIdentity())) : bsSecurityStore.getAllByEndpoint(endpoint);
56 58 log.info("Bootstrap session started securityInfos: [{}]", securityInfos);
57 59 authorized = securityChecker.checkSecurityInfos(endpoint, clientIdentity, securityInfos);
58 60 } else {
... ...
... ... @@ -16,7 +16,6 @@
16 16 package org.thingsboard.server.transport.lwm2m.secure;
17 17
18 18 import com.google.gson.JsonObject;
19   -import lombok.Builder;
20 19 import lombok.Data;
21 20 import org.eclipse.leshan.server.bootstrap.BootstrapConfig;
22 21 import org.eclipse.leshan.server.security.SecurityInfo;
... ... @@ -29,7 +28,6 @@ import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.DE
29 28 public class ReadResultSecurityStore {
30 29 private ValidateDeviceCredentialsResponseMsg msg;
31 30 private SecurityInfo securityInfo;
32   - @Builder.Default
33 31 private int securityMode = DEFAULT_MODE.code;
34 32
35 33 /** bootstrap */
... ...
... ... @@ -36,7 +36,7 @@ import org.nustaq.serialization.FSTConfiguration;
36 36 import org.thingsboard.server.common.data.DeviceProfile;
37 37 import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration;
38 38 import org.thingsboard.server.common.transport.TransportServiceCallback;
39   -import org.thingsboard.server.transport.lwm2m.server.client.AttrTelemetryObserveValue;
  39 +import org.thingsboard.server.transport.lwm2m.server.client.LwM2MClientProfile;
40 40 import org.thingsboard.server.transport.lwm2m.server.client.LwM2MClient;
41 41
42 42 import java.io.File;
... ... @@ -70,7 +70,8 @@ public class LwM2MTransportHandler {
70 70
71 71 public static final long DEFAULT_TIMEOUT = 2 * 60 * 1000L; // 2min in ms
72 72 public static final String OBSERVE_ATTRIBUTE_TELEMETRY = "observeAttr";
73   - public static final String KEYNAME = "keyName";
  73 + public static final String CLIENT_LWM2M_SETTINGS = "clientLwM2mSettings";
  74 + public static final String KEY_NAME = "keyName";
74 75 public static final String OBSERVE = "observe";
75 76 public static final String BOOTSTRAP = "bootstrap";
76 77 public static final String SERVERS = "servers";
... ... @@ -187,18 +188,22 @@ public class LwM2MTransportHandler {
187 188 return null;
188 189 }
189 190
190   - public static AttrTelemetryObserveValue getNewProfileParameters(JsonObject profilesConfigData) {
191   - AttrTelemetryObserveValue attrTelemetryObserveValue = new AttrTelemetryObserveValue();
192   - attrTelemetryObserveValue.setPostKeyNameProfile(profilesConfigData.get(KEYNAME).getAsJsonObject());
193   - attrTelemetryObserveValue.setPostAttributeProfile(profilesConfigData.get(ATTRIBUTE).getAsJsonArray());
194   - attrTelemetryObserveValue.setPostTelemetryProfile(profilesConfigData.get(TELEMETRY).getAsJsonArray());
195   - attrTelemetryObserveValue.setPostObserveProfile(profilesConfigData.get(OBSERVE).getAsJsonArray());
196   - return attrTelemetryObserveValue;
  191 + public static LwM2MClientProfile getNewProfileParameters(JsonObject profilesConfigData) {
  192 + LwM2MClientProfile lwM2MClientProfile = new LwM2MClientProfile();
  193 + lwM2MClientProfile.setPostClientLwM2mSettings(profilesConfigData.get(CLIENT_LWM2M_SETTINGS).getAsJsonObject());
  194 + lwM2MClientProfile.setPostKeyNameProfile(profilesConfigData.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(KEY_NAME).getAsJsonObject());
  195 + lwM2MClientProfile.setPostAttributeProfile(profilesConfigData.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(ATTRIBUTE).getAsJsonArray());
  196 + lwM2MClientProfile.setPostTelemetryProfile(profilesConfigData.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(TELEMETRY).getAsJsonArray());
  197 + lwM2MClientProfile.setPostObserveProfile(profilesConfigData.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(OBSERVE).getAsJsonArray());
  198 + return lwM2MClientProfile;
197 199 }
198 200
199 201 /**
200 202 * @return deviceProfileBody with Observe&Attribute&Telemetry From Thingsboard
201   - * Example: with pathResource (use only pathResource)
  203 + * Example:
  204 + * property: {"clientLwM2mSettings": {
  205 + * clientUpdateValueAfterConnect: false;
  206 + * }
202 207 * property: "observeAttr"
203 208 * {"keyName": {
204 209 * "/3/0/1": "modelNumber",
... ... @@ -209,15 +214,14 @@ public class LwM2MTransportHandler {
209 214 * "telemetry":["/1/0/1","/2/0/1","/6/0/1"],
210 215 * "observe":["/2/0","/2/0/0","/4/0/2"]}
211 216 */
212   - public static JsonObject getObserveAttrTelemetryFromThingsboard(DeviceProfile deviceProfile) {
  217 + public static LwM2MClientProfile getLwM2MClientProfileFromThingsboard(DeviceProfile deviceProfile) {
213 218 if (deviceProfile != null && ((Lwm2mDeviceProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration()).getProperties().size() > 0) {
214   -// Lwm2mDeviceProfileTransportConfiguration lwm2mDeviceProfileTransportConfiguration = (Lwm2mDeviceProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration();
215   - Object observeAttr = ((Lwm2mDeviceProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration()).getProperties();
  219 + Object profile = ((Lwm2mDeviceProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration()).getProperties();
216 220 try {
217 221 ObjectMapper mapper = new ObjectMapper();
218   - String observeAttrStr = mapper.writeValueAsString(observeAttr);
219   - JsonObject objectMsg = (observeAttrStr != null) ? validateJson(observeAttrStr) : null;
220   - return (getValidateCredentialsBodyFromThingsboard(objectMsg)) ? objectMsg.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject() : null;
  222 + String profileStr = mapper.writeValueAsString(profile);
  223 + JsonObject profileJson = (profileStr != null) ? validateJson(profileStr) : null;
  224 + return (getValidateCredentialsBodyFromThingsboard(profileJson)) ? LwM2MTransportHandler.getNewProfileParameters(profileJson) : null;
221 225 } catch (IOException e) {
222 226 log.error("", e);
223 227 }
... ... @@ -240,15 +244,28 @@ public class LwM2MTransportHandler {
240 244 return null;
241 245 }
242 246
  247 + public static boolean getClientUpdateValueAfterConnect (LwM2MClientProfile profile) {
  248 + return profile.getPostClientLwM2mSettings().getAsJsonObject().has("clientUpdateValueAfterConnect") &&
  249 + profile.getPostClientLwM2mSettings().getAsJsonObject().get("clientUpdateValueAfterConnect").getAsBoolean();
  250 + }
  251 +
  252 + public static boolean getClientOnlyObserveAfterConnect (LwM2MClientProfile profile) {
  253 + return profile.getPostClientLwM2mSettings().getAsJsonObject().has("clientOnlyObserveAfterConnect") &&
  254 + profile.getPostClientLwM2mSettings().getAsJsonObject().get("clientOnlyObserveAfterConnect").getAsBoolean();
  255 + }
  256 +
243 257 private static boolean getValidateCredentialsBodyFromThingsboard(JsonObject objectMsg) {
244 258 return (objectMsg != null &&
245 259 !objectMsg.isJsonNull() &&
  260 + objectMsg.has(CLIENT_LWM2M_SETTINGS) &&
  261 + !objectMsg.get(CLIENT_LWM2M_SETTINGS).isJsonNull() &&
  262 + objectMsg.get(CLIENT_LWM2M_SETTINGS).isJsonObject() &&
246 263 objectMsg.has(OBSERVE_ATTRIBUTE_TELEMETRY) &&
247 264 !objectMsg.get(OBSERVE_ATTRIBUTE_TELEMETRY).isJsonNull() &&
248 265 objectMsg.get(OBSERVE_ATTRIBUTE_TELEMETRY).isJsonObject() &&
249   - objectMsg.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().has(KEYNAME) &&
250   - !objectMsg.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(KEYNAME).isJsonNull() &&
251   - objectMsg.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(KEYNAME).isJsonObject() &&
  266 + objectMsg.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().has(KEY_NAME) &&
  267 + !objectMsg.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(KEY_NAME).isJsonNull() &&
  268 + objectMsg.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(KEY_NAME).isJsonObject() &&
252 269 objectMsg.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().has(ATTRIBUTE) &&
253 270 !objectMsg.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(ATTRIBUTE).isJsonNull() &&
254 271 objectMsg.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(ATTRIBUTE).isJsonArray() &&
... ... @@ -295,6 +312,7 @@ public class LwM2MTransportHandler {
295 312 return object;
296 313 }
297 314
  315 + @SuppressWarnings("unchecked")
298 316 public static <T> Optional<T> decode(byte[] byteArray) {
299 317 try {
300 318 FSTConfiguration config = FSTConfiguration.createDefaultConfiguration();
... ...
... ... @@ -39,7 +39,6 @@ import org.eclipse.leshan.core.response.DeleteResponse;
39 39 import org.eclipse.leshan.core.response.DiscoverResponse;
40 40 import org.eclipse.leshan.core.response.ExecuteResponse;
41 41 import org.eclipse.leshan.core.response.LwM2mResponse;
42   -import org.eclipse.leshan.core.response.ObserveResponse;
43 42 import org.eclipse.leshan.core.response.ReadResponse;
44 43 import org.eclipse.leshan.core.response.ResponseCallback;
45 44 import org.eclipse.leshan.core.response.WriteAttributesResponse;
... ... @@ -96,7 +95,7 @@ public class LwM2MTransportRequest {
96 95 this.converter = LwM2mValueConverterImpl.getInstance();
97 96 executorResponse = Executors.newFixedThreadPool(this.context.getCtxServer().getRequestPoolSize(),
98 97 new NamedThreadFactory(String.format("LwM2M %s channel response", RESPONSE_CHANNEL)));
99   - executorResponseError = Executors.newFixedThreadPool(this.context.getCtxServer().getRequestErrorPoolSize(),
  98 + executorResponseError = Executors.newFixedThreadPool(this.context.getCtxServer().getRequestErrorPoolSize(),
100 99 new NamedThreadFactory(String.format("LwM2M %s channel response Error", RESPONSE_CHANNEL)));
101 100 }
102 101
... ... @@ -112,16 +111,15 @@ public class LwM2MTransportRequest {
112 111 /**
113 112 * Device management and service enablement, including Read, Write, Execute, Discover, Create, Delete and Write-Attributes
114 113 *
115   - * @param lwServer
116   - * @param registration
117   - * @param target
118   - * @param typeOper
119   - * @param contentFormatParam
120   - * @param lwM2MClient
121   - * @param observation
  114 + * @param lwServer -
  115 + * @param registration -
  116 + * @param target -
  117 + * @param typeOper -
  118 + * @param contentFormatParam -
  119 + * @param observation -
122 120 */
123   - public void sendAllRequest(LeshanServer lwServer, Registration registration, String target, String typeOper, String contentFormatParam,
124   - LwM2MClient lwM2MClient, Observation observation, Object params, long timeoutInMs, boolean isDelayedUpdate) {
  121 + public void sendAllRequest(LeshanServer lwServer, Registration registration, String target, String typeOper,
  122 + String contentFormatParam, Observation observation, Object params, long timeoutInMs) {
125 123 LwM2mPath resultIds = new LwM2mPath(target);
126 124 if (registration != null && resultIds.getObjectId() >= 0) {
127 125 DownlinkRequest request = null;
... ... @@ -221,13 +219,7 @@ public class LwM2MTransportRequest {
221 219 }
222 220
223 221 if (request != null) {
224   - this.sendRequest(lwServer, registration, request, lwM2MClient, timeoutInMs, isDelayedUpdate);
225   - } else if (isDelayedUpdate) {
226   - String msg = String.format(LOG_LW2M_ERROR + ": sendRequest: Resource path - %s msg No SendRequest to Client", target);
227   - service.sentLogsToThingsboard(msg, registration);
228   - log.error("[{}] - [{}] No SendRequest", target);
229   - this.handleResponseError(registration, target, lwM2MClient, isDelayedUpdate);
230   -
  222 + this.sendRequest(lwServer, registration, request, timeoutInMs);
231 223 }
232 224 }
233 225 }
... ... @@ -237,45 +229,42 @@ public class LwM2MTransportRequest {
237 229 * @param lwServer -
238 230 * @param registration -
239 231 * @param request -
240   - * @param lwM2MClient -
241 232 * @param timeoutInMs -
242 233 */
243 234
244   - private void sendRequest(LeshanServer lwServer, Registration registration, DownlinkRequest request, LwM2MClient lwM2MClient, long timeoutInMs, boolean isDelayedUpdate) {
  235 + @SuppressWarnings("unchecked")
  236 + private void sendRequest(LeshanServer lwServer, Registration registration, DownlinkRequest request, long timeoutInMs) {
  237 + LwM2MClient lwM2MClient = this.service.lwM2mInMemorySecurityStore.getLwM2MClientWithReg(registration, null);
245 238 lwServer.send(registration, request, timeoutInMs, (ResponseCallback<?>) response -> {
  239 + if (!lwM2MClient.isInit()) {
  240 + lwM2MClient.initValue(this.service, request.getPath().toString());
  241 + }
246 242 if (isSuccess(((Response) response.getCoapResponse()).getCode())) {
247   - this.handleResponse(registration, request.getPath().toString(), response, request, lwM2MClient, isDelayedUpdate);
  243 + this.handleResponse(registration, request.getPath().toString(), response, request);
248 244 if (request instanceof WriteRequest && ((WriteRequest) request).isReplaceRequest()) {
249   - String delayedUpdateStr = "";
250   - if (isDelayedUpdate) {
251   - delayedUpdateStr = " (delayedUpdate) ";
252   - }
253   - String msg = String.format(LOG_LW2M_INFO + ": sendRequest Replace%s: CoapCde - %s Lwm2m code - %d name - %s Resource path - %s value - %s SendRequest to Client",
254   - delayedUpdateStr, ((Response) response.getCoapResponse()).getCode(), response.getCode().getCode(), response.getCode().getName(), request.getPath().toString(),
  245 + String msg = String.format("%s: sendRequest Replace: CoapCde - %s Lwm2m code - %d name - %s Resource path - %s value - %s SendRequest to Client",
  246 + LOG_LW2M_INFO, ((Response) response.getCoapResponse()).getCode(), response.getCode().getCode(), response.getCode().getName(), request.getPath().toString(),
255 247 ((LwM2mSingleResource) ((WriteRequest) request).getNode()).getValue().toString());
256 248 service.sentLogsToThingsboard(msg, registration);
257   - log.info("[{}] - [{}] [{}] [{}] Update SendRequest[{}]", ((Response) response.getCoapResponse()).getCode(), response.getCode(), request.getPath().toString(),
258   - ((LwM2mSingleResource) ((WriteRequest) request).getNode()).getValue(), delayedUpdateStr);
  249 + log.info("[{}] [{}] - [{}] [{}] Update SendRequest[{}]", registration.getEndpoint(), ((Response) response.getCoapResponse()).getCode(), response.getCode(), request.getPath().toString(),
  250 + ((LwM2mSingleResource) ((WriteRequest) request).getNode()).getValue());
259 251 }
260 252 } else {
261   - String msg = String.format(LOG_LW2M_ERROR + ": sendRequest: CoapCode - %s Lwm2m code - %d name - %s Resource path - %s SendRequest to Client",
  253 + String msg = String.format("%s: sendRequest: CoapCode - %s Lwm2m code - %d name - %s Resource path - %s SendRequest to Client", LOG_LW2M_ERROR,
262 254 ((Response) response.getCoapResponse()).getCode(), response.getCode().getCode(), response.getCode().getName(), request.getPath().toString());
263 255 service.sentLogsToThingsboard(msg, registration);
264 256 log.error("[{}] - [{}] [{}] error SendRequest", ((Response) response.getCoapResponse()).getCode(), response.getCode(), request.getPath().toString());
265   - if (request instanceof WriteRequest && ((WriteRequest) request).isReplaceRequest() && isDelayedUpdate) {
266   - this.handleResponseError(registration, request.getPath().toString(), lwM2MClient, isDelayedUpdate);
267   - }
268 257 }
269   -
270 258 }, e -> {
271   - String msg = String.format(LOG_LW2M_ERROR + ": sendRequest: Resource path - %s msg error - %s SendRequest to Client",
272   - request.getPath().toString(), e.toString());
  259 + if (!lwM2MClient.isInit()) {
  260 + lwM2MClient.initValue(this.service, request.getPath().toString());
  261 + }
  262 + String msg = String.format("%s: sendRequest: Resource path - %s msg error - %s SendRequest to Client",
  263 + LOG_LW2M_ERROR, request.getPath().toString(), e.toString());
273 264 service.sentLogsToThingsboard(msg, registration);
274 265 log.error("[{}] - [{}] error SendRequest", request.getPath().toString(), e.toString());
275   - if (request instanceof WriteRequest && ((WriteRequest) request).isReplaceRequest() && isDelayedUpdate) {
276   - this.handleResponseError(registration, request.getPath().toString(), lwM2MClient, isDelayedUpdate);
277   - }
278 266 });
  267 +
279 268 }
280 269
281 270 private WriteRequest getWriteRequestSingleResource(ContentFormat contentFormat, Integer objectId, Integer instanceId, Integer resourceId, Object value, ResourceModel.Type type, Registration registration) {
... ... @@ -308,54 +297,27 @@ public class LwM2MTransportRequest {
308 297 }
309 298 }
310 299
311   - private void handleResponse(Registration registration, final String path, LwM2mResponse response, DownlinkRequest request, LwM2MClient lwM2MClient, boolean isDelayedUpdate) {
  300 + private void handleResponse(Registration registration, final String path, LwM2mResponse response, DownlinkRequest request) {
312 301 executorResponse.submit(() -> {
313 302 try {
314   - sendResponse(registration, path, response, request, lwM2MClient, isDelayedUpdate);
  303 + sendResponse(registration, path, response, request);
315 304 } catch (Exception e) {
316 305 log.error("[{}] endpoint [{}] path [{}] Exception Unable to after send response.", registration.getEndpoint(), path, e);
317 306 }
318 307 });
319 308 }
320 309
321   - private void handleResponseError(Registration registration, final String path, LwM2MClient lwM2MClient, boolean isDelayedUpdate) {
322   - executorResponseError.submit(() -> {
323   - try {
324   - if (isDelayedUpdate) lwM2MClient.onSuccessOrErrorDelayedRequests(path);
325   - } catch (RuntimeException t) {
326   - log.error("[{}] endpoint [{}] path [{}] RuntimeException Unable to after send response.", registration.getEndpoint(), path, t);
327   - }
328   - });
329   - }
330   -
331 310 /**
332 311 * processing a response from a client
333 312 * @param registration -
334 313 * @param path -
335 314 * @param response -
336   - * @param lwM2MClient -
337 315 */
338   - private void sendResponse(Registration registration, String path, LwM2mResponse response, DownlinkRequest request, LwM2MClient lwM2MClient, boolean isDelayedUpdate) {
339   - if (response instanceof ObserveResponse) {
  316 + private void sendResponse(Registration registration, String path, LwM2mResponse response, DownlinkRequest request) {
  317 + if (response instanceof ReadResponse) {
340 318 service.onObservationResponse(registration, path, (ReadResponse) response);
341 319 } else if (response instanceof CancelObservationResponse) {
342 320 log.info("[{}] Path [{}] CancelObservationResponse 3_Send", path, response);
343   - } else if (response instanceof ReadResponse) {
344   - /**
345   - * Use only at the first start after registration
346   - * Fill with data -> Model client
347   - */
348   - if (lwM2MClient != null) {
349   - if (lwM2MClient.getPendingRequests().size() > 0) {
350   - lwM2MClient.onSuccessHandler(path, response);
351   - }
352   - }
353   - /**
354   - * Use after registration on request
355   - */
356   - else {
357   - service.onObservationResponse(registration, path, (ReadResponse) response);
358   - }
359 321 } else if (response instanceof DeleteResponse) {
360 322 log.info("[{}] Path [{}] DeleteResponse 5_Send", path, response);
361 323 } else if (response instanceof DiscoverResponse) {
... ... @@ -366,7 +328,7 @@ public class LwM2MTransportRequest {
366 328 log.info("[{}] Path [{}] WriteAttributesResponse 8_Send", path, response);
367 329 } else if (response instanceof WriteResponse) {
368 330 log.info("[{}] Path [{}] WriteAttributesResponse 9_Send", path, response);
369   - service.onAttributeUpdateOk(registration, path, (WriteRequest) request, isDelayedUpdate);
  331 + service.onWriteResponseOk(registration, path, (WriteRequest) request);
370 332 }
371 333 }
372 334 }
... ...
... ... @@ -15,18 +15,19 @@
15 15 */
16 16 package org.thingsboard.server.transport.lwm2m.server;
17 17
  18 +import com.fasterxml.jackson.core.type.TypeReference;
  19 +import com.fasterxml.jackson.databind.ObjectMapper;
18 20 import com.google.gson.Gson;
19 21 import com.google.gson.JsonArray;
20 22 import com.google.gson.JsonElement;
21 23 import com.google.gson.JsonObject;
  24 +import com.google.gson.reflect.TypeToken;
22 25 import lombok.extern.slf4j.Slf4j;
23   -import org.eclipse.leshan.core.Link;
24 26 import org.eclipse.leshan.core.model.ResourceModel;
25   -import org.eclipse.leshan.core.node.LwM2mMultipleResource;
26 27 import org.eclipse.leshan.core.node.LwM2mObject;
27 28 import org.eclipse.leshan.core.node.LwM2mObjectInstance;
28 29 import org.eclipse.leshan.core.node.LwM2mPath;
29   -import org.eclipse.leshan.core.node.LwM2mSingleResource;
  30 +import org.eclipse.leshan.core.node.LwM2mResource;
30 31 import org.eclipse.leshan.core.observation.Observation;
31 32 import org.eclipse.leshan.core.request.ContentFormat;
32 33 import org.eclipse.leshan.core.request.WriteRequest;
... ... @@ -44,18 +45,18 @@ import org.thingsboard.server.common.transport.adaptor.AdaptorException;
44 45 import org.thingsboard.server.common.transport.adaptor.JsonConverter;
45 46 import org.thingsboard.server.common.transport.service.DefaultTransportService;
46 47 import org.thingsboard.server.gen.transport.TransportProtos;
  48 +import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg;
47 49 import org.thingsboard.server.gen.transport.TransportProtos.SessionEvent;
48 50 import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto;
49   -import org.thingsboard.server.gen.transport.TransportProtos.ToTransportUpdateCredentialsProto;
50   -import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceCredentialsResponseMsg;
51   -import org.thingsboard.server.transport.lwm2m.server.client.AttrTelemetryObserveValue;
52 51 import org.thingsboard.server.transport.lwm2m.server.client.LwM2MClient;
  52 +import org.thingsboard.server.transport.lwm2m.server.client.LwM2MClientProfile;
53 53 import org.thingsboard.server.transport.lwm2m.server.client.ResourceValue;
54 54 import org.thingsboard.server.transport.lwm2m.server.client.ResultsAnalyzerParameters;
55 55 import org.thingsboard.server.transport.lwm2m.server.secure.LwM2mInMemorySecurityStore;
56 56 import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl;
57 57
58 58 import javax.annotation.PostConstruct;
  59 +import java.io.IOException;
59 60 import java.util.ArrayList;
60 61 import java.util.Arrays;
61 62 import java.util.Collection;
... ... @@ -72,7 +73,6 @@ import java.util.concurrent.ConcurrentMap;
72 73 import java.util.concurrent.ExecutorService;
73 74 import java.util.concurrent.Executors;
74 75 import java.util.concurrent.TimeUnit;
75   -import java.util.concurrent.atomic.AtomicBoolean;
76 76 import java.util.concurrent.locks.Lock;
77 77 import java.util.concurrent.locks.ReadWriteLock;
78 78 import java.util.concurrent.locks.ReentrantReadWriteLock;
... ... @@ -149,12 +149,10 @@ public class LwM2MTransportServiceImpl implements LwM2MTransportService {
149 149 executorRegistered.submit(() -> {
150 150 try {
151 151 log.warn("[{}] [{{}] Client: create after Registration", registration.getEndpoint(), registration.getId());
152   - LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.updateInSessionsLwM2MClient(lwServer, registration);
  152 + LwM2MClient lwM2MClient = this.lwM2mInMemorySecurityStore.updateInSessionsLwM2MClient(lwServer, registration);
153 153 if (lwM2MClient != null) {
154 154 lwM2MClient.setLwM2MTransportServiceImpl(this);
155   - lwM2MClient.setSessionUuid(UUID.randomUUID());
156 155 this.sentLogsToThingsboard(LOG_LW2M_INFO + ": Client Registered", registration);
157   - this.setLwM2mFromClientValue(lwServer, registration, lwM2MClient);
158 156 SessionInfoProto sessionInfo = this.getValidateSessionInfo(registration);
159 157 if (sessionInfo != null) {
160 158 lwM2MClient.setDeviceUuid(new UUID(sessionInfo.getDeviceIdMSB(), sessionInfo.getDeviceIdLSB()));
... ... @@ -165,7 +163,8 @@ public class LwM2MTransportServiceImpl implements LwM2MTransportService {
165 163 transportService.process(sessionInfo, DefaultTransportService.getSessionEventMsg(SessionEvent.OPEN), null);
166 164 transportService.process(sessionInfo, TransportProtos.SubscribeToAttributeUpdatesMsg.newBuilder().build(), null);
167 165 this.sentLogsToThingsboard(LOG_LW2M_INFO + ": Client create after Registration", registration);
168   - this.putDelayedUpdateResourcesThingsboard(lwM2MClient);
  166 + this.initLwM2mFromClientValue(lwServer, registration, lwM2MClient);
  167 +
169 168 } else {
170 169 log.error("Client: [{}] onRegistered [{}] name [{}] sessionInfo ", registration.getId(), registration.getEndpoint(), null);
171 170 }
... ... @@ -237,6 +236,151 @@ public class LwM2MTransportServiceImpl implements LwM2MTransportService {
237 236 //TODO: associate endpointId with device information.
238 237 }
239 238
  239 + @Override
  240 + public void setCancelObservations(LeshanServer lwServer, Registration registration) {
  241 + if (registration != null) {
  242 + Set<Observation> observations = lwServer.getObservationService().getObservations(registration);
  243 + observations.forEach(observation -> this.setCancelObservationRecourse(lwServer, registration, observation.getPath().toString()));
  244 + }
  245 + }
  246 +
  247 + /**
  248 + * lwM2MTransportRequest.sendAllRequest(lwServer, registration, path, POST_TYPE_OPER_OBSERVE_CANCEL, null, null, null, null, context.getTimeout());
  249 + * At server side this will not remove the observation from the observation store, to do it you need to use
  250 + * {@code ObservationService#cancelObservation()}
  251 + */
  252 + @Override
  253 + public void setCancelObservationRecourse(LeshanServer lwServer, Registration registration, String path) {
  254 + lwServer.getObservationService().cancelObservations(registration, path);
  255 + }
  256 +
  257 + /**
  258 + * Sending observe value to thingsboard from ObservationListener.onResponse: object, instance, SingleResource or MultipleResource
  259 + *
  260 + * @param registration - Registration LwM2M Client
  261 + * @param path - observe
  262 + * @param response - observe
  263 + */
  264 + @Override
  265 + public void onObservationResponse(Registration registration, String path, ReadResponse response) {
  266 + if (response.getContent() != null) {
  267 + if (response.getContent() instanceof LwM2mObject) {
  268 + LwM2mObject lwM2mObject = (LwM2mObject) response.getContent();
  269 + this.updateObjectResourceValue(registration, lwM2mObject, path);
  270 + } else if (response.getContent() instanceof LwM2mObjectInstance) {
  271 + LwM2mObjectInstance lwM2mObjectInstance = (LwM2mObjectInstance) response.getContent();
  272 + this.updateObjectInstanceResourceValue(registration, lwM2mObjectInstance, path);
  273 + } else if (response.getContent() instanceof LwM2mResource) {
  274 + LwM2mResource lwM2mResource = (LwM2mResource) response.getContent();
  275 + this.updateResourcesValue(registration, lwM2mResource, path);
  276 + }
  277 + }
  278 + }
  279 +
  280 + /**
  281 + * Update - sent request in change value resources in Client
  282 + * Path to resources from profile equal keyName or from ModelObject equal name
  283 + * Only for resources: isWritable && isPresent as attribute in profile -> LwM2MClientProfile (format: CamelCase)
  284 + * Delete - nothing *
  285 + *
  286 + * @param msg -
  287 + */
  288 + @Override
  289 + public void onAttributeUpdate(AttributeUpdateNotificationMsg msg, TransportProtos.SessionInfoProto sessionInfo) {
  290 + if (msg.getSharedUpdatedCount() > 0) {
  291 + JsonElement el = JsonConverter.toJson(msg);
  292 + el.getAsJsonObject().entrySet().forEach(de -> {
  293 + String path = this.getPathAttributeUpdate(sessionInfo, de.getKey());
  294 + String value = de.getValue().getAsString();
  295 + LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getSession(new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB())).entrySet().iterator().next().getValue();
  296 + LwM2MClientProfile profile = lwM2mInMemorySecurityStore.getProfile(new UUID(sessionInfo.getDeviceProfileIdMSB(), sessionInfo.getDeviceProfileIdLSB()));
  297 + ResourceModel resourceModel = context.getCtxServer().getResourceModel(lwM2MClient.getRegistration(), new LwM2mPath(path));
  298 + if (!path.isEmpty() && (this.validatePathInAttrProfile(profile, path) || this.validatePathInTelemetryProfile(profile, path))) {
  299 + if (resourceModel != null && resourceModel.operations.isWritable()) {
  300 + lwM2MTransportRequest.sendAllRequest(lwM2MClient.getLwServer(), lwM2MClient.getRegistration(), path, POST_TYPE_OPER_WRITE_REPLACE,
  301 + ContentFormat.TLV.getName(), null, value, this.context.getCtxServer().getTimeout());
  302 + } else {
  303 + log.error("Resource path - [{}] value - [{}] is not Writable and cannot be updated", path, value);
  304 + String logMsg = String.format("%s: attributeUpdate: Resource path - %s value - %s is not Writable and cannot be updated",
  305 + LOG_LW2M_ERROR, path, value);
  306 + this.sentLogsToThingsboard(logMsg, lwM2MClient.getRegistration());
  307 + }
  308 + } else {
  309 + log.error("Attribute name - [{}] value - [{}] is not present as attribute in profile and cannot be updated", de.getKey(), value);
  310 + String logMsg = String.format("%s: attributeUpdate: attribute name - %s value - %s is not present as attribute in profile and cannot be updated",
  311 + LOG_LW2M_ERROR, de.getKey(), value);
  312 + this.sentLogsToThingsboard(logMsg, lwM2MClient.getRegistration());
  313 + }
  314 + });
  315 + } else if (msg.getSharedDeletedCount() > 0) {
  316 + log.info("[{}] delete [{}] onAttributeUpdate", msg.getSharedDeletedList(), sessionInfo);
  317 + }
  318 + }
  319 +
  320 + /**
  321 + * @param sessionInfo -
  322 + * @param deviceProfile -
  323 + */
  324 + @Override
  325 + public void onDeviceProfileUpdate(SessionInfoProto sessionInfo, DeviceProfile deviceProfile) {
  326 + Set<String> registrationIds = lwM2mInMemorySecurityStore.getSessions().entrySet()
  327 + .stream()
  328 + .filter(e -> e.getValue().getProfileUuid().equals(deviceProfile.getUuidId()))
  329 + .map(Map.Entry::getKey).sorted().collect(Collectors.toCollection(LinkedHashSet::new));
  330 + if (registrationIds.size() > 0) {
  331 + this.onDeviceUpdateChangeProfile(registrationIds, deviceProfile);
  332 + }
  333 + }
  334 +
  335 + /**
  336 + * @param sessionInfo -
  337 + * @param device -
  338 + * @param deviceProfileOpt -
  339 + */
  340 + @Override
  341 + public void onDeviceUpdate(SessionInfoProto sessionInfo, Device device, Optional<DeviceProfile> deviceProfileOpt) {
  342 + Optional<String> registrationIdOpt = lwM2mInMemorySecurityStore.getSessions().entrySet().stream()
  343 + .filter(e -> device.getUuidId().equals(e.getValue().getDeviceUuid()))
  344 + .map(Map.Entry::getKey)
  345 + .findFirst();
  346 + registrationIdOpt.ifPresent(registrationId -> this.onDeviceUpdateLwM2MClient(registrationId, device, deviceProfileOpt));
  347 + }
  348 +
  349 + /**
  350 + * Trigger Server path = "/1/0/8"
  351 + *
  352 + * Trigger bootStrap path = "/1/0/9" - have to implemented on client
  353 + */
  354 + @Override
  355 + public void doTrigger(LeshanServer lwServer, Registration registration, String path) {
  356 + lwM2MTransportRequest.sendAllRequest(lwServer, registration, path, POST_TYPE_OPER_EXECUTE,
  357 + ContentFormat.TLV.getName(), null, null, this.context.getCtxServer().getTimeout());
  358 + }
  359 +
  360 + /**
  361 + * Deregister session in transport
  362 + *
  363 + * @param sessionInfo - lwm2m client
  364 + */
  365 + @Override
  366 + public void doDisconnect(SessionInfoProto sessionInfo) {
  367 + transportService.process(sessionInfo, DefaultTransportService.getSessionEventMsg(SessionEvent.CLOSED), null);
  368 + transportService.deregisterSession(sessionInfo);
  369 + }
  370 +
  371 + /**
  372 + * Session device in thingsboard is closed
  373 + *
  374 + * @param sessionInfo - lwm2m client
  375 + */
  376 + private void doCloseSession(SessionInfoProto sessionInfo) {
  377 + TransportProtos.SessionEvent event = SessionEvent.CLOSED;
  378 + TransportProtos.SessionEventMsg msg = TransportProtos.SessionEventMsg.newBuilder()
  379 + .setSessionType(TransportProtos.SessionType.ASYNC)
  380 + .setEvent(event).build();
  381 + transportService.process(sessionInfo, msg, null);
  382 + }
  383 +
240 384 /**
241 385 * Those methods are called by the protocol stage thread pool, this means that execution MUST be done in a short delay,
242 386 * * if you need to do long time processing use a dedicated thread pool.
... ... @@ -253,7 +397,7 @@ public class LwM2MTransportServiceImpl implements LwM2MTransportService {
253 397 * Removes a profile if not used in sessions
254 398 */
255 399 private void syncSessionsAndProfiles() {
256   - Map<UUID, AttrTelemetryObserveValue> profilesClone = lwM2mInMemorySecurityStore.getProfiles().entrySet()
  400 + Map<UUID, LwM2MClientProfile> profilesClone = lwM2mInMemorySecurityStore.getProfiles().entrySet()
257 401 .stream()
258 402 .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
259 403 profilesClone.forEach((k, v) -> {
... ... @@ -270,257 +414,261 @@ public class LwM2MTransportServiceImpl implements LwM2MTransportService {
270 414 }
271 415
272 416 /**
273   - * #0 Add new ObjectModel to context
274   - * Create new LwM2MClient for current session -> setModelClient...
275   - * #1 Add all ObjectLinks (instance) to control the process of executing requests to the client
276   - * to get the client model with current values
277   - * #2 Get the client model with current values. Analyze the response in -> lwM2MTransportRequest.sendResponse
  417 + * @param msg - text msg
  418 + * @param registration - Id of Registration LwM2M Client
  419 + */
  420 + public void sentLogsToThingsboard(String msg, Registration registration) {
  421 + if (msg != null) {
  422 + JsonObject telemetries = new JsonObject();
  423 + telemetries.addProperty(LOG_LW2M_TELEMETRY, msg);
  424 + this.updateParametersOnThingsboard(telemetries, DEVICE_TELEMETRY_TOPIC, registration);
  425 + }
  426 + }
  427 +
  428 +
  429 + /**
  430 + * // !!! Ok
  431 + * Prepare Sent to Thigsboard callback - Attribute or Telemetry
  432 + *
  433 + * @param msg - JsonArray: [{name: value}]
  434 + * @param topicName - Api Attribute or Telemetry
  435 + * @param registration - Id of Registration LwM2M Client
  436 + */
  437 + public void updateParametersOnThingsboard(JsonElement msg, String topicName, Registration registration) {
  438 + SessionInfoProto sessionInfo = this.getValidateSessionInfo(registration);
  439 + if (sessionInfo != null) {
  440 + context.sentParametersOnThingsboard(msg, topicName, sessionInfo);
  441 + } else {
  442 + log.error("Client: [{}] updateParametersOnThingsboard [{}] sessionInfo ", registration, null);
  443 + }
  444 + }
  445 +
  446 + /**
  447 + * #1 сlientOnlyObserveAfterConnect == true
  448 + * - Only Observe Request to the client marked as observe from the profile configuration.
  449 + * #2. сlientOnlyObserveAfterConnect == false & clientUpdateValueAfterConnect == false
  450 + * - Request to the client after registration to read the values of the resources marked as attribute or telemetry from the profile configuration.
  451 + * - then Observe Request to the client marked as observe from the profile configuration.
  452 + * #3. сlientOnlyObserveAfterConnect == false & clientUpdateValueAfterConnect == true
  453 + * После регистрации отправляю запрос на read всех ресурсов, котрые послк регистрации, а затем запрос на observe (edited)
  454 + * - Request to the client after registration to read all resource values for all objects
  455 + * - then Observe Request to the client marked as observe from the profile configuration.
278 456 *
279 457 * @param lwServer - LeshanServer
280 458 * @param registration - Registration LwM2M Client
281 459 * @param lwM2MClient - object with All parameters off client
282 460 */
283   - private void setLwM2mFromClientValue(LeshanServer lwServer, Registration registration, LwM2MClient lwM2MClient) {
284   - Arrays.stream(registration.getObjectLinks()).forEach(url -> {
285   - LwM2mPath pathIds = new LwM2mPath(url.getUrl());
286   - if (pathIds.isObjectInstance() && !pathIds.isResource()) {
287   - // #1
288   - lwM2MClient.getPendingRequests().add(url.getUrl());
289   - // #2
290   - lwM2MTransportRequest.sendAllRequest(lwServer, registration, url.getUrl(), GET_TYPE_OPER_READ, ContentFormat.TLV.getName(),
291   - lwM2MClient, null, null, this.context.getCtxServer().getTimeout(), false);
292   - }
293   - });
  461 + private void initLwM2mFromClientValue(LeshanServer lwServer, Registration registration, LwM2MClient lwM2MClient) {
  462 + LwM2MClientProfile lwM2MClientProfile = lwM2mInMemorySecurityStore.getProfile(registration.getId());
  463 + Set<String> clientObjects = this.getAllOjectsInClient(registration);
  464 + if (clientObjects != null && !LwM2MTransportHandler.getClientOnlyObserveAfterConnect(lwM2MClientProfile)) {
  465 + // #2
  466 + if (!LwM2MTransportHandler.getClientUpdateValueAfterConnect(lwM2MClientProfile)) {
  467 + this.initReadAttrTelemetryObserveToClient(lwServer, registration, lwM2MClient, GET_TYPE_OPER_READ);
294 468
295   - // #1
296   - for (Link url : registration.getObjectLinks()) {
297   - LwM2mPath pathIds = new LwM2mPath(url.getUrl());
298   - if (pathIds.isObjectInstance() && !pathIds.isResource()) {
299   - lwM2MClient.getPendingRequests().add(url.getUrl());
300 469 }
301   - }
302   - // #2
303   - for (Link url : registration.getObjectLinks()) {
304   - LwM2mPath pathIds = new LwM2mPath(url.getUrl());
305   - if (pathIds.isObjectInstance() && !pathIds.isResource()) {
306   - lwM2MTransportRequest.sendAllRequest(lwServer, registration, url.getUrl(), GET_TYPE_OPER_READ, ContentFormat.TLV.getName(),
307   - lwM2MClient, null, null, this.context.getCtxServer().getTimeout(), false);
  470 + // #3
  471 + else {
  472 + lwM2MClient.getPendingRequests().addAll(clientObjects);
  473 + clientObjects.forEach(path -> {
  474 + lwM2MTransportRequest.sendAllRequest(lwServer, registration, path, GET_TYPE_OPER_READ, ContentFormat.TLV.getName(),
  475 + null, null, this.context.getCtxServer().getTimeout());
  476 + });
308 477 }
309 478 }
310   -
311 479 // #1
312   - Arrays.stream(registration.getObjectLinks()).forEach(url -> {
313   - LwM2mPath pathIds = new LwM2mPath(url.getUrl());
314   - if (pathIds.isObjectInstance() && !pathIds.isResource()) {
315   - lwM2MClient.getPendingRequests().add(url.getUrl());
316   - }
  480 + this.initReadAttrTelemetryObserveToClient(lwServer, registration, lwM2MClient, GET_TYPE_OPER_OBSERVE);
  481 + }
  482 +
  483 + /**
  484 + * @param registration -
  485 + * @param lwM2mObject -
  486 + * @param path -
  487 + */
  488 + private void updateObjectResourceValue(Registration registration, LwM2mObject lwM2mObject, String path) {
  489 + LwM2mPath pathIds = new LwM2mPath(path);
  490 + lwM2mObject.getInstances().forEach((instanceId, instance) -> {
  491 + String pathInstance = pathIds.toString() + "/" + instanceId;
  492 + this.updateObjectInstanceResourceValue(registration, instance, pathInstance);
317 493 });
  494 + }
318 495
319   - // #2
320   - Arrays.stream(registration.getObjectLinks()).forEach(url -> {
321   - LwM2mPath pathIds = new LwM2mPath(url.getUrl());
322   - if (pathIds.isObjectInstance() && !pathIds.isResource()) {
323   - lwM2MTransportRequest.sendAllRequest(lwServer, registration, url.getUrl(), GET_TYPE_OPER_READ, ContentFormat.TLV.getName(),
324   - lwM2MClient, null, null, this.context.getCtxServer().getTimeout(), false);
325   - }
  496 + /**
  497 + * @param registration -
  498 + * @param lwM2mObjectInstance -
  499 + * @param path -
  500 + */
  501 + private void updateObjectInstanceResourceValue(Registration registration, LwM2mObjectInstance lwM2mObjectInstance, String path) {
  502 + LwM2mPath pathIds = new LwM2mPath(path);
  503 + lwM2mObjectInstance.getResources().forEach((resourceId, resource) -> {
  504 + String pathRez = pathIds.toString() + "/" + resourceId;
  505 + this.updateResourcesValue(registration, resource, pathRez);
326 506 });
327 507 }
328 508
329 509 /**
  510 + * Sending observe value of resources to thingsboard
  511 + * #1 Return old Value Resource from LwM2MClient
  512 + * #2 Update new Resources (replace old Resource Value on new Resource Value)
  513 + *
330 514 * @param registration - Registration LwM2M Client
331   - * @return - sessionInfo after access connect client
  515 + * @param - LwM2mSingleResource response.getContent()
  516 + * @param - LwM2mSingleResource response.getContent()
  517 + * @param path - resource
332 518 */
333   - private SessionInfoProto getValidateSessionInfo(Registration registration) {
  519 + private void updateResourcesValue(Registration registration, LwM2mResource lwM2mResource, String path) {
334 520 LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClientWithReg(registration, null);
335   - return getNewSessionInfoProto(lwM2MClient);
336   -
  521 + lwM2MClient.updateResourceValue(path, lwM2mResource);
  522 + Set<String> paths = new HashSet<>();
  523 + paths.add(path);
  524 + this.updateAttrTelemetry(registration, false, paths);
337 525 }
338 526
339 527 /**
340   - * @param registrationId -
341   - * @return -
  528 + * Sent Attribute and Telemetry to Thingsboard
  529 + * #1 - get AttrName/TelemetryName with value:
  530 + * #1.1 from Client
  531 + * #1.2 from LwM2MClient:
  532 + * -- resourceId == path from LwM2MClientProfile.postAttributeProfile/postTelemetryProfile/postObserveProfile
  533 + * -- AttrName/TelemetryName == resourceName from ModelObject.objectModel, value from ModelObject.instance.resource(resourceId)
  534 + * #2 - set Attribute/Telemetry
  535 + *
  536 + * @param registration - Registration LwM2M Client
342 537 */
343   - private SessionInfoProto getValidateSessionInfo(String registrationId) {
344   - LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClientWithReg(null, registrationId);
345   - return getNewSessionInfoProto(lwM2MClient);
346   - }
347   -
348   - private SessionInfoProto getNewSessionInfoProto(LwM2MClient lwM2MClient) {
349   - if (lwM2MClient != null) {
350   - ValidateDeviceCredentialsResponseMsg msg = lwM2MClient.getCredentialsResponse();
351   - if (msg == null || msg.getDeviceInfo() == null) {
352   - log.error("[{}] [{}]", lwM2MClient.getEndPoint(), CLIENT_NOT_AUTHORIZED);
353   - this.closeClientSession(lwM2MClient.getRegistration());
354   - return null;
355   - } else {
356   - return SessionInfoProto.newBuilder()
357   - .setNodeId(this.context.getNodeId())
358   - .setSessionIdMSB(lwM2MClient.getSessionUuid().getMostSignificantBits())
359   - .setSessionIdLSB(lwM2MClient.getSessionUuid().getLeastSignificantBits())
360   - .setDeviceIdMSB(msg.getDeviceInfo().getDeviceIdMSB())
361   - .setDeviceIdLSB(msg.getDeviceInfo().getDeviceIdLSB())
362   - .setTenantIdMSB(msg.getDeviceInfo().getTenantIdMSB())
363   - .setTenantIdLSB(msg.getDeviceInfo().getTenantIdLSB())
364   - .setDeviceName(msg.getDeviceInfo().getDeviceName())
365   - .setDeviceType(msg.getDeviceInfo().getDeviceType())
366   - .setDeviceProfileIdLSB(msg.getDeviceInfo().getDeviceProfileIdLSB())
367   - .setDeviceProfileIdMSB(msg.getDeviceInfo().getDeviceProfileIdMSB())
368   - .build();
  538 + private void updateAttrTelemetry(Registration registration, boolean start, Set<String> paths) {
  539 + JsonObject attributes = new JsonObject();
  540 + JsonObject telemetries = new JsonObject();
  541 + if (start) {
  542 + // #1.1
  543 + JsonObject attributeClient = this.getAttributeClient(registration);
  544 + if (attributeClient != null) {
  545 + attributeClient.entrySet().forEach(p -> attributes.add(p.getKey(), p.getValue()));
369 546 }
370 547 }
371   - return null;
  548 + // #1.2
  549 + try {
  550 + writeLock.lock();
  551 + this.getParametersFromProfile(attributes, telemetries, registration, paths);
  552 + } catch (Exception e) {
  553 + log.error("UpdateAttrTelemetry", e);
  554 + } finally {
  555 + writeLock.unlock();
  556 + }
  557 + if (attributes.getAsJsonObject().entrySet().size() > 0)
  558 + this.updateParametersOnThingsboard(attributes, DEVICE_ATTRIBUTES_TOPIC, registration);
  559 + if (telemetries.getAsJsonObject().entrySet().size() > 0)
  560 + this.updateParametersOnThingsboard(telemetries, DEVICE_TELEMETRY_TOPIC, registration);
372 561 }
373 562
374 563 /**
375   - * Add attribute/telemetry information from Client and credentials/Profile to client model and start observe
376   - * !!! if the resource has an observation, but no telemetry or attribute - the observation will not use
377   - * #1 Sending Attribute Telemetry with value to thingsboard only once at the start of the connection
378   - * #2 Start observe
379   - *
380   - * @param lwM2MClient - LwM2M Client
  564 + * @param profile -
  565 + * @param path -
  566 + * @return true if path isPresent in postAttributeProfile
381 567 */
382   -
383   - public void updatesAndSentModelParameter(LwM2MClient lwM2MClient) {
384   - // #1
385   - this.updateAttrTelemetry(lwM2MClient.getRegistration(), true, null);
386   - // #2
387   - this.onSentObserveToClient(lwM2MClient.getLwServer(), lwM2MClient.getRegistration());
388   -
  568 + private boolean validatePathInAttrProfile(LwM2MClientProfile profile, String path) {
  569 + Set<String> attributesSet = new Gson().fromJson(profile.getPostAttributeProfile(), new TypeToken<>(){}.getType());
  570 + return attributesSet.stream().filter(p -> p.equals(path)).findFirst().isPresent();
389 571 }
390 572
391 573 /**
392   - * If there is a difference in values between the current resource values and the shared attribute values
393   - * when the client connects to the server
394   - * #1 get attributes name from profile include name resources in ModelObject if resource isWritable
395   - * #2.1 #1 size > 0 => send Request getAttributes to thingsboard
396   - * #2.2 #1 size == 0 => continue normal process
397   - *
398   - * @param lwM2MClient - LwM2M Client
  574 + * @param profile -
  575 + * @param path -
  576 + * @return true if path isPresent in postAttributeProfile
399 577 */
400   - public void putDelayedUpdateResourcesThingsboard(LwM2MClient lwM2MClient) {
401   - SessionInfoProto sessionInfo = this.getValidateSessionInfo(lwM2MClient.getRegistration());
402   - if (sessionInfo != null) {
403   - //#1.1 + #1.2
404   - List<String> attrSharedNames = this.getNamesAttrFromProfileIsWritable(lwM2MClient);
405   - if (attrSharedNames.size() > 0) {
406   - //#2.1
407   - try {
408   - TransportProtos.GetAttributeRequestMsg getAttributeMsg = context.getAdaptor().convertToGetAttributes(null, attrSharedNames);
409   - lwM2MClient.getDelayedRequestsId().add(getAttributeMsg.getRequestId());
410   - transportService.process(sessionInfo, getAttributeMsg, getAckCallback(lwM2MClient, getAttributeMsg.getRequestId(), DEVICE_ATTRIBUTES_REQUEST));
411   - } catch (AdaptorException e) {
412   - log.warn("Failed to decode get attributes request", e);
413   - }
414   - }
415   - // #2.2
416   - else {
417   - lwM2MClient.onSuccessOrErrorDelayedRequests(null);
418   - }
419   - }
  578 + private boolean validatePathInTelemetryProfile(LwM2MClientProfile profile, String path) {
  579 + Set<String> telemetriesSet = new Gson().fromJson(profile.getPostTelemetryProfile(), new TypeToken<>(){}.getType());
  580 + return telemetriesSet.stream().filter(p -> p.equals(path)).findFirst().isPresent();
420 581 }
421 582
422 583 /**
423   - * Update resource value on client: if there is a difference in values between the current resource values and the shared attribute values
424   - * #1 Get path resource by result attributesResponse
425   - * #1.1 If two names have equal path => last time attribute
426   - * #2.1 if there is a difference in values between the current resource values and the shared attribute values
427   - * => sent to client Request Update of value (new value from shared attribute)
428   - * and LwM2MClient.delayedRequests.add(path)
429   - * #2.1 if there is not a difference in values between the current resource values and the shared attribute values
  584 + * Start observe/read: Attr/Telemetry
  585 + * #1 - Analyze:
  586 + * #1.1 path in resource profile == client resource
430 587 *
431   - * @param attributesResponse -
432   - * @param sessionInfo -
  588 + * @param lwServer -
  589 + * @param registration -
433 590 */
434   - public void onGetAttributesResponse(TransportProtos.GetAttributeResponseMsg attributesResponse, TransportProtos.SessionInfoProto sessionInfo) {
435   - LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClient(sessionInfo);
436   - if (lwM2MClient.getDelayedRequestsId().contains(attributesResponse.getRequestId())) {
437   - attributesResponse.getSharedAttributeListList().forEach(attr -> {
438   - String path = this.getPathAttributeUpdate(sessionInfo, attr.getKv().getKey());
  591 + private void initReadAttrTelemetryObserveToClient(LeshanServer lwServer, Registration registration, LwM2MClient lwM2MClient, String typeOper) {
  592 + try {
  593 + LwM2MClientProfile lwM2MClientProfile = lwM2mInMemorySecurityStore.getProfile(registration.getId());
  594 + Set<String> clientInstances = this.getAllInstancesInClient(registration);
  595 + Set<String> result;
  596 + if (GET_TYPE_OPER_READ.equals(typeOper)) {
  597 + result = new ObjectMapper().readValue(lwM2MClientProfile.getPostAttributeProfile().getAsJsonArray().toString().getBytes(), new TypeReference<>() {});
  598 + result.addAll(new ObjectMapper().readValue(lwM2MClientProfile.getPostTelemetryProfile().getAsJsonArray().toString().getBytes(), new TypeReference<>() {}));
  599 + } else {
  600 + result = new ObjectMapper().readValue(lwM2MClientProfile.getPostObserveProfile().getAsJsonArray().toString().getBytes(), new TypeReference<>() {});
  601 + }
  602 + Set<String> pathSent = ConcurrentHashMap.newKeySet();
  603 + result.forEach(p -> {
439 604 // #1.1
440   - if (lwM2MClient.getDelayedRequests().containsKey(path) && attr.getTs() > lwM2MClient.getDelayedRequests().get(path).getTs()) {
441   - lwM2MClient.getDelayedRequests().put(path, attr);
442   - } else {
443   - lwM2MClient.getDelayedRequests().put(path, attr);
  605 + String target = p;
  606 + String[] resPath = target.split("/");
  607 + String instance = "/" + resPath[1] + "/" + resPath[2];
  608 + if (clientInstances.contains(instance)) {
  609 + pathSent.add(target);
444 610 }
445 611 });
446   - // #2.1
447   - lwM2MClient.getDelayedRequests().forEach((k, v) -> {
448   - ArrayList<TransportProtos.KeyValueProto> listV = new ArrayList<>();
449   - listV.add(v.getKv());
450   - this.putDelayedUpdateResourcesClient(lwM2MClient, this.getResourceValueToString(lwM2MClient, k), getJsonObject(listV).get(v.getKv().getKey()), k);
  612 + lwM2MClient.getPendingRequests().addAll(pathSent);
  613 + pathSent.forEach(target -> {
  614 + lwM2MTransportRequest.sendAllRequest(lwServer, registration, target, typeOper, ContentFormat.TLV.getName(),
  615 + null, null, this.context.getCtxServer().getTimeout());
451 616 });
452   - lwM2MClient.getDelayedRequestsId().remove(attributesResponse.getRequestId());
453   - if (lwM2MClient.getDelayedRequests().size() == 0) {
454   - lwM2MClient.onSuccessOrErrorDelayedRequests(null);
  617 + if (GET_TYPE_OPER_OBSERVE.equals(typeOper)) {
  618 + lwM2MClient.initValue(this, null);
455 619 }
456   - }
457   - }
458   -
459   - private void putDelayedUpdateResourcesClient(LwM2MClient lwM2MClient, Object valueOld, Object valueNew, String path) {
460   - if (valueNew != null && !valueNew.toString().equals(valueOld.toString())) {
461   - lwM2MTransportRequest.sendAllRequest(lwM2MClient.getLwServer(), lwM2MClient.getRegistration(), path, POST_TYPE_OPER_WRITE_REPLACE,
462   - ContentFormat.TLV.getName(), lwM2MClient, null, valueNew, this.context.getCtxServer().getTimeout(),
463   - true);
  620 + } catch (IOException e) {
  621 + e.printStackTrace();
464 622 }
465 623 }
466 624
467 625 /**
468   - * Get names and keyNames from profile shared!!!! attr resources IsWritable
  626 + * Update parameters device in LwM2MClient
  627 + * If new deviceProfile != old deviceProfile => update deviceProfile
469 628 *
470   - * @param lwM2MClient -
471   - * @return ArrayList keyNames from profile attr resources shared!!!! && IsWritable
  629 + * @param registrationId -
  630 + * @param device -
472 631 */
473   - private List<String> getNamesAttrFromProfileIsWritable(LwM2MClient lwM2MClient) {
474   - AttrTelemetryObserveValue profile = lwM2mInMemorySecurityStore.getProfile(lwM2MClient.getProfileUuid());
475   - Set attrSet = new Gson().fromJson(profile.getPostAttributeProfile(), Set.class);
476   - ConcurrentMap<String, String> keyNamesMap = new Gson().fromJson(profile.getPostKeyNameProfile().toString(), ConcurrentHashMap.class);
477   -
478   - ConcurrentMap<String, String> keyNamesIsWritable = keyNamesMap.entrySet()
479   - .stream()
480   - .filter(e -> (attrSet.contains(e.getKey()) && context.getCtxServer().getResourceModel(lwM2MClient.getRegistration(), new LwM2mPath(e.getKey())) != null &&
481   - context.getCtxServer().getResourceModel(lwM2MClient.getRegistration(), new LwM2mPath(e.getKey())).operations.isWritable()))
482   - .collect(Collectors.toConcurrentMap(Map.Entry::getKey, Map.Entry::getValue));
  632 + private void onDeviceUpdateLwM2MClient(String registrationId, Device device, Optional<DeviceProfile> deviceProfileOpt) {
  633 + LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getSessions().get(registrationId);
  634 + lwM2MClient.setDeviceName(device.getName());
  635 + if (!lwM2MClient.getProfileUuid().equals(device.getDeviceProfileId().getId())) {
  636 + Set<String> registrationIds = new HashSet<>();
  637 + registrationIds.add(registrationId);
  638 + deviceProfileOpt.ifPresent(deviceProfile -> this.onDeviceUpdateChangeProfile(registrationIds, deviceProfile));
  639 + }
483 640
484   - Set<String> namesIsWritable = ConcurrentHashMap.newKeySet();
485   - namesIsWritable.addAll(new HashSet<>(keyNamesIsWritable.values()));
486   - return new ArrayList<>(namesIsWritable);
  641 + lwM2MClient.setProfileUuid(device.getDeviceProfileId().getId());
487 642 }
488 643
  644 + /**
  645 + * @param registration -
  646 + * @return - all object in client
  647 + */
  648 + private Set<String> getAllOjectsInClient(Registration registration) {
  649 + Set<String> clientObjects = ConcurrentHashMap.newKeySet();
  650 + Arrays.stream(registration.getObjectLinks()).forEach(url -> {
  651 + LwM2mPath pathIds = new LwM2mPath(url.getUrl());
  652 + if (pathIds.isObjectInstance()) {
  653 + clientObjects.add("/" + pathIds.getObjectId());
  654 + }
  655 + });
  656 + return (clientObjects.size() > 0) ? clientObjects : null;
  657 + }
489 658
490 659 /**
491   - * Sent Attribute and Telemetry to Thingsboard
492   - * #1 - get AttrName/TelemetryName with value:
493   - * #1.1 from Client
494   - * #1.2 from LwM2MClient:
495   - * -- resourceId == path from AttrTelemetryObserveValue.postAttributeProfile/postTelemetryProfile/postObserveProfile
496   - * -- AttrName/TelemetryName == resourceName from ModelObject.objectModel, value from ModelObject.instance.resource(resourceId)
497   - * #2 - set Attribute/Telemetry
498   - *
499   - * @param registration - Registration LwM2M Client
  660 + * @param registration -
  661 + * @return all instances in client
500 662 */
501   - private void updateAttrTelemetry(Registration registration, boolean start, Set<String> paths) {
502   - JsonObject attributes = new JsonObject();
503   - JsonObject telemetries = new JsonObject();
504   - if (start) {
505   - // #1.1
506   - JsonObject attributeClient = this.getAttributeClient(registration);
507   - if (attributeClient != null) {
508   - attributeClient.entrySet().forEach(p -> attributes.add(p.getKey(), p.getValue()));
  663 + private Set<String> getAllInstancesInClient(Registration registration) {
  664 + Set<String> clientInstances = ConcurrentHashMap.newKeySet();
  665 + Arrays.stream(registration.getObjectLinks()).forEach(url -> {
  666 + LwM2mPath pathIds = new LwM2mPath(url.getUrl());
  667 + if (pathIds.isObjectInstance()) {
  668 + clientInstances.add(url.getUrl());
509 669 }
510   - }
511   - // #1.2
512   - try {
513   - writeLock.lock();
514   - this.getParametersFromProfile(attributes, telemetries, registration, paths);
515   - } catch (Exception e) {
516   - log.error("UpdateAttrTelemetry", e);
517   - } finally {
518   - writeLock.unlock();
519   - }
520   - if (attributes.getAsJsonObject().entrySet().size() > 0)
521   - this.updateParametersOnThingsboard(attributes, DEVICE_ATTRIBUTES_TOPIC, registration);
522   - if (telemetries.getAsJsonObject().entrySet().size() > 0)
523   - this.updateParametersOnThingsboard(telemetries, DEVICE_TELEMETRY_TOPIC, registration);
  670 + });
  671 + return (clientInstances.size() > 0) ? clientInstances : null;
524 672 }
525 673
526 674 /**
... ... @@ -542,13 +690,11 @@ public class LwM2MTransportServiceImpl implements LwM2MTransportService {
542 690 * @param attributes - new JsonObject
543 691 * @param telemetry - new JsonObject
544 692 * @param registration - Registration LwM2M Client
545   - * result: add to JsonObject those resources to which the user is subscribed and they have a value
546   - * if path==null add All resources else only one
547   - * (attributes/telemetry): new {name(Attr/Telemetry):value}
  693 + * @param path
548 694 */
549 695 private void getParametersFromProfile(JsonObject attributes, JsonObject telemetry, Registration registration, Set<String> path) {
550   - AttrTelemetryObserveValue attrTelemetryObserveValue = lwM2mInMemorySecurityStore.getProfiles().get(lwM2mInMemorySecurityStore.getSessions().get(registration.getId()).getProfileUuid());
551   - attrTelemetryObserveValue.getPostAttributeProfile().forEach(p -> {
  696 + LwM2MClientProfile lwM2MClientProfile = lwM2mInMemorySecurityStore.getProfile(registration.getId());
  697 + lwM2MClientProfile.getPostAttributeProfile().forEach(p -> {
552 698 LwM2mPath pathIds = new LwM2mPath(p.getAsString().toString());
553 699 if (pathIds.isResource()) {
554 700 if (path == null || path.contains(p.getAsString())) {
... ... @@ -556,7 +702,7 @@ public class LwM2MTransportServiceImpl implements LwM2MTransportService {
556 702 }
557 703 }
558 704 });
559   - attrTelemetryObserveValue.getPostTelemetryProfile().forEach(p -> {
  705 + lwM2MClientProfile.getPostTelemetryProfile().forEach(p -> {
560 706 LwM2mPath pathIds = new LwM2mPath(p.getAsString().toString());
561 707 if (pathIds.isResource()) {
562 708 if (path == null || path.contains(p.getAsString())) {
... ... @@ -567,338 +713,59 @@ public class LwM2MTransportServiceImpl implements LwM2MTransportService {
567 713 }
568 714
569 715 /**
570   - * @param parameters - JsonObject attributes/telemetry
571   - * @param registration - Registration LwM2M Client
572   - */
573   - private void addParameters(String path, JsonObject parameters, Registration registration) {
574   - LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getSessions().get(registration.getId());
575   - JsonObject names = lwM2mInMemorySecurityStore.getProfiles().get(lwM2MClient.getProfileUuid()).getPostKeyNameProfile();
576   - String resName = String.valueOf(names.get(path));
577   - if (resName != null && !resName.isEmpty()) {
578   - try {
579   - String resValue = this.getResourceValueToString(lwM2MClient, path);
580   - if (resValue != null) {
581   - parameters.addProperty(resName, resValue);
582   - }
583   - } catch (Exception e) {
584   - log.error(e.getStackTrace().toString());
585   - }
586   - }
587   - }
588   -
589   - /**
590   - * Prepare Sent to Thigsboard callback - Attribute or Telemetry
591   - *
592   - * @param msg - JsonArray: [{name: value}]
593   - * @param topicName - Api Attribute or Telemetry
594   - * @param registration - Id of Registration LwM2M Client
595   - */
596   - public void updateParametersOnThingsboard(JsonElement msg, String topicName, Registration registration) {
597   - SessionInfoProto sessionInfo = this.getValidateSessionInfo(registration);
598   - if (sessionInfo != null) {
599   - context.sentParametersOnThingsboard(msg, topicName, sessionInfo);
600   - } else {
601   - log.error("Client: [{}] updateParametersOnThingsboard [{}] sessionInfo ", registration, null);
602   - }
603   - }
604   -
605   - /**
606   - * Start observe
607   - * #1 - Analyze:
608   - * #1.1 path in observe == (attribute or telemetry)
609   - * #2 Analyze after sent request (response):
610   - * #2.1 First: lwM2MTransportRequest.sendResponse -> ObservationListener.newObservation
611   - * #2.2 Next: ObservationListener.onResponse *
612   - *
613   - * @param lwServer - LeshanServer
614   - * @param registration - Registration LwM2M Client
615   - */
616   - private void onSentObserveToClient(LeshanServer lwServer, Registration registration) {
617   - if (lwServer.getObservationService().getObservations(registration).size() > 0) {
618   - this.setCancelObservations(lwServer, registration);
619   - }
620   - UUID profileUUid = lwM2mInMemorySecurityStore.getSessions().get(registration.getId()).getProfileUuid();
621   - AttrTelemetryObserveValue attrTelemetryObserveValue = lwM2mInMemorySecurityStore.getProfiles().get(profileUUid);
622   - attrTelemetryObserveValue.getPostObserveProfile().forEach(p -> {
623   - // #1.1
624   - String target = (getValidateObserve(attrTelemetryObserveValue.getPostAttributeProfile(), p.getAsString().toString())) ?
625   - p.getAsString().toString() : (getValidateObserve(attrTelemetryObserveValue.getPostTelemetryProfile(), p.getAsString().toString())) ?
626   - p.getAsString().toString() : null;
627   - if (target != null) {
628   - // #2
629   - if (this.getResourceValueToString(lwM2mInMemorySecurityStore.getSessions().get(registration.getId()), target) != null) {
630   - lwM2MTransportRequest.sendAllRequest(lwServer, registration, target, GET_TYPE_OPER_OBSERVE,
631   - null, null, null, null, this.context.getCtxServer().getTimeout(),
632   - false);
633   - }
634   - }
635   - });
636   - }
637   -
638   - public void setCancelObservations(LeshanServer lwServer, Registration registration) {
639   - if (registration != null) {
640   - Set<Observation> observations = lwServer.getObservationService().getObservations(registration);
641   - observations.forEach(observation -> this.setCancelObservationRecourse(lwServer, registration, observation.getPath().toString()));
642   - }
643   - }
644   -
645   - /**
646   - * lwM2MTransportRequest.sendAllRequest(lwServer, registration, path, POST_TYPE_OPER_OBSERVE_CANCEL, null, null, null, null, context.getTimeout());
647   - * At server side this will not remove the observation from the observation store, to do it you need to use
648   - * {@code ObservationService#cancelObservation()}
649   - */
650   - public void setCancelObservationRecourse(LeshanServer lwServer, Registration registration, String path) {
651   - lwServer.getObservationService().cancelObservations(registration, path);
652   - }
653   -
654   - /**
655   - * @param parameters - JsonArray postAttributeProfile/postTelemetryProfile
656   - * @param path - recourse from postObserveProfile
657   - * @return rez - true if path observe is in attribute/telemetry
658   - */
659   - private boolean getValidateObserve(JsonElement parameters, String path) {
660   - AtomicBoolean rez = new AtomicBoolean(false);
661   - if (parameters.isJsonArray()) {
662   - parameters.getAsJsonArray().forEach(p -> {
663   - if (p.getAsString().toString().equals(path)) rez.set(true);
664   - }
665   - );
666   - } else if (parameters.isJsonObject()) {
667   - rez.set((parameters.getAsJsonObject().entrySet()).stream().map(json -> json.toString())
668   - .filter(path::equals).findAny().orElse(null) != null);
669   - }
670   - return rez.get();
671   - }
672   -
673   - /**
674   - * Sending observe value to thingsboard from ObservationListener.onResponse: object, instance, SingleResource or MultipleResource
675   - *
676   - * @param registration - Registration LwM2M Client
677   - * @param path - observe
678   - * @param response - observe
679   - */
680   -
681   - public void onObservationResponse(Registration registration, String path, ReadResponse response) {
682   - if (response.getContent() != null) {
683   - if (response.getContent() instanceof LwM2mObject) {
684   -// LwM2mObject content = (LwM2mObject) response.getContent();
685   - } else if (response.getContent() instanceof LwM2mObjectInstance) {
686   -// LwM2mObjectInstance content = (LwM2mObjectInstance) response.getContent();
687   - } else if (response.getContent() instanceof LwM2mSingleResource) {
688   - LwM2mSingleResource content = (LwM2mSingleResource) response.getContent();
689   - this.onObservationSetResourcesValue(registration, content.getValue(), null, path);
690   - } else if (response.getContent() instanceof LwM2mMultipleResource) {
691   - LwM2mMultipleResource content = (LwM2mMultipleResource) response.getContent();
692   - this.onObservationSetResourcesValue(registration, null, content.getValues(), path);
693   - }
694   - }
695   - }
696   -
697   - /**
698   - * Sending observe value of resources to thingsboard
699   - * #1 Return old Value Resource from LwM2MClient
700   - * #2 Update new Resources (replace old Resource Value on new Resource Value)
701   - *
702   - * @param registration - Registration LwM2M Client
703   - * @param value - LwM2mSingleResource response.getContent()
704   - * @param values - LwM2mSingleResource response.getContent()
705   - * @param path - resource
706   - */
707   - private void onObservationSetResourcesValue(Registration registration, Object value, Map<Integer, ?> values, String path) {
708   - boolean isChange = false;
709   - try {
710   - writeLock.lock();
711   - // #1
712   - LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClientWithReg(registration, null);
713   - LwM2mPath pathIds = new LwM2mPath(path);
714   -// log.warn("#0 nameDevice: [{}] resultIds: [{}] value: [{}], values: [{}] ", lwM2MClient.getDeviceName(), pathIds, value, values);
715   - ResourceModel.Type resModelType = context.getCtxServer().getResourceModelType(registration, pathIds);
716   - ResourceValue resValueOld = lwM2MClient.getResources().get(path);
717   - // #2
718   - if (resValueOld.isMultiInstances() && !values.toString().equals(resValueOld.getResourceValue().toString())) {
719   - lwM2MClient.getResources().get(path).setValues(values);
720   -// ResourceValue resourceValue = new ResourceValue(values, null, true);
721   -// lwM2MClient.getResources().put(path, resourceValue);
722   - isChange = true;
723   - } else if (!LwM2MTransportHandler.equalsResourceValue(resValueOld.getValue(), value, resModelType, pathIds)) {
724   - lwM2MClient.getResources().get(path).setValue(value);
725   -// ResourceValue resourceValueOld = lwM2MClient.getResources().get(path);
726   -// lwM2MClient.getResources().remove(resourceValueOld);
727   -// ResourceValue resourceValue = new ResourceValue(null, value, false);
728   -// lwM2MClient.getResources().put(path, resourceValue);
729   - log.warn("upDateResize: [{}] [{}] [{}] [{}]", lwM2MClient.getEndPoint(), lwM2MClient.getResources().size(), value, path);
730   - isChange = true;
731   - }
732   - } catch (Exception e) {
733   - log.error("#1_1 Update ResourcesValue after Observation is unsuccessfully path: [{}] value: [{}] [{}]", path, value, e.toString());
734   - } finally {
735   - writeLock.unlock();
736   - }
737   -
738   - if (isChange) {
739   - Set<String> paths = new HashSet<>();
740   - paths.add(path);
741   - this.updateAttrTelemetry(registration, false, paths);
742   - }
743   - }
744   -
745   - /**
746   - * @param updateCredentials - Credentials include config only security Client (without config attr/telemetry...)
747   - * config attr/telemetry... in profile
748   - */
749   - public void onToTransportUpdateCredentials(ToTransportUpdateCredentialsProto updateCredentials) {
750   - log.info("[{}] idList [{}] valueList updateCredentials", updateCredentials.getCredentialsIdList(), updateCredentials.getCredentialsValueList());
751   - }
752   -
753   - /**
754   - * Update - sent request in change value resources in Client
755   - * Path to resources from profile equal keyName or from ModelObject equal name
756   - * Only for resources: isWritable && isPresent as attribute in profile -> AttrTelemetryObserveValue (format: CamelCase)
757   - * Delete - nothing *
758   - *
759   - * @param msg -
760   - */
761   - public void onAttributeUpdate(TransportProtos.AttributeUpdateNotificationMsg msg, TransportProtos.SessionInfoProto sessionInfo) {
762   - if (msg.getSharedUpdatedCount() > 0) {
763   - JsonElement el = JsonConverter.toJson(msg);
764   - el.getAsJsonObject().entrySet().forEach(de -> {
765   - String path = this.getPathAttributeUpdate(sessionInfo, de.getKey());
766   - String value = de.getValue().getAsString();
767   - LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getSession(new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB())).entrySet().iterator().next().getValue();
768   - AttrTelemetryObserveValue profile = lwM2mInMemorySecurityStore.getProfile(new UUID(sessionInfo.getDeviceProfileIdMSB(), sessionInfo.getDeviceProfileIdLSB()));
769   - ResourceModel resourceModel = context.getCtxServer().getResourceModel(lwM2MClient.getRegistration(), new LwM2mPath(path));
770   - if (!path.isEmpty() && (this.validatePathInAttrProfile(profile, path) || this.validatePathInTelemetryProfile(profile, path))) {
771   - if (resourceModel != null && resourceModel.operations.isWritable()) {
772   - lwM2MTransportRequest.sendAllRequest(lwM2MClient.getLwServer(), lwM2MClient.getRegistration(), path, POST_TYPE_OPER_WRITE_REPLACE,
773   - ContentFormat.TLV.getName(), lwM2MClient, null, value, this.context.getCtxServer().getTimeout(),
774   - false);
775   - } else {
776   - log.error("Resource path - [{}] value - [{}] is not Writable and cannot be updated", path, value);
777   - String logMsg = String.format(LOG_LW2M_ERROR + ": attributeUpdate: Resource path - %s value - %s is not Writable and cannot be updated", path, value);
778   - this.sentLogsToThingsboard(logMsg, lwM2MClient.getRegistration());
779   - }
780   - } else {
781   - log.error("Attribute name - [{}] value - [{}] is not present as attribute in profile and cannot be updated", de.getKey(), value);
782   - String logMsg = String.format(LOG_LW2M_ERROR + ": attributeUpdate: attribute name - %s value - %s is not present as attribute in profile and cannot be updated", de.getKey(), value);
783   - this.sentLogsToThingsboard(logMsg, lwM2MClient.getRegistration());
784   - }
785   - });
786   - } else if (msg.getSharedDeletedCount() > 0) {
787   - log.info("[{}] delete [{}] onAttributeUpdate", msg.getSharedDeletedList(), sessionInfo);
788   - }
789   - }
790   -
791   - /**
792   - * Get path to resource from profile equal keyName or from ModelObject equal name
793   - * Only for resource: isWritable && isPresent as attribute in profile -> AttrTelemetryObserveValue (format: CamelCase)
794   - *
795   - * @param sessionInfo -
796   - * @param name -
797   - * @return path if path isPresent in postProfile
798   - */
799   - private String getPathAttributeUpdate(TransportProtos.SessionInfoProto sessionInfo, String name) {
800   - String profilePath = this.getPathAttributeUpdateProfile(sessionInfo, name);
801   -// return !profilePath.isEmpty() ? profilePath : this.getPathAttributeUpdateModelObject(name);
802   - return !profilePath.isEmpty() ? profilePath : null;
803   - }
804   -
805   - /**
806   - * @param profile -
807   - * @param path -
808   - * @return true if path isPresent in postAttributeProfile
809   - */
810   - private boolean validatePathInAttrProfile(AttrTelemetryObserveValue profile, String path) {
811   - Set<String> attributesSet = new Gson().fromJson(profile.getPostAttributeProfile(), Set.class);
812   - return attributesSet.stream().filter(p -> p.equals(path)).findFirst().isPresent();
813   - }
814   -
815   - /**
816   - * @param profile -
817   - * @param path -
818   - * @return true if path isPresent in postAttributeProfile
819   - */
820   - private boolean validatePathInTelemetryProfile(AttrTelemetryObserveValue profile, String path) {
821   - Set<String> telemetriesSet = new Gson().fromJson(profile.getPostTelemetryProfile(), Set.class);
822   - return telemetriesSet.stream().filter(p -> p.equals(path)).findFirst().isPresent();
823   - }
824   -
825   -
826   - /**
827   - * Get path to resource from profile equal keyName
828   - *
829   - * @param sessionInfo -
830   - * @param name -
831   - * @return -
832   - */
833   - private String getPathAttributeUpdateProfile(TransportProtos.SessionInfoProto sessionInfo, String name) {
834   - AttrTelemetryObserveValue profile = lwM2mInMemorySecurityStore.getProfile(new UUID(sessionInfo.getDeviceProfileIdMSB(), sessionInfo.getDeviceProfileIdLSB()));
835   - return profile.getPostKeyNameProfile().getAsJsonObject().entrySet().stream()
836   - .filter(e -> e.getValue().getAsString().equals(name)).findFirst().map(Map.Entry::getKey)
837   - .orElse("");
838   - }
839   -
840   - /**
841   - * Update resource (attribute) value on thingsboard after update value in client
842   - *
843   - * @param registration -
844   - * @param path -
845   - * @param request -
846   - */
847   - public void onAttributeUpdateOk(Registration registration, String path, WriteRequest request, boolean isDelayedUpdate) {
848   - ResourceModel resource = context.getCtxServer().getResourceModel(registration, new LwM2mPath(path));
849   - if (resource.multiple) {
850   - this.onObservationSetResourcesValue(registration, null, ((LwM2mSingleResource) request.getNode()).getValues(), path);
851   - } else {
852   - this.onObservationSetResourcesValue(registration, ((LwM2mSingleResource) request.getNode()).getValue(), null, path);
853   - }
854   - if (isDelayedUpdate) lwM2mInMemorySecurityStore.getLwM2MClientWithReg(registration, null)
855   - .onSuccessOrErrorDelayedRequests(request.getPath().toString());
856   - }
857   -
858   - /**
859   - * @param sessionInfo -
860   - * @param deviceProfile -
  716 + * @param parameters - JsonObject attributes/telemetry
  717 + * @param registration - Registration LwM2M Client
861 718 */
862   - public void onDeviceProfileUpdate(TransportProtos.SessionInfoProto sessionInfo, DeviceProfile deviceProfile) {
863   - Set<String> registrationIds = lwM2mInMemorySecurityStore.getSessions().entrySet()
864   - .stream()
865   - .filter(e -> e.getValue().getProfileUuid().equals(deviceProfile.getUuidId()))
866   - .map(Map.Entry::getKey).sorted().collect(Collectors.toCollection(LinkedHashSet::new));
867   - if (registrationIds.size() > 0) {
868   - this.onDeviceUpdateChangeProfile(registrationIds, deviceProfile);
  719 + private void addParameters(String path, JsonObject parameters, Registration registration) {
  720 + LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getSessions().get(registration.getId());
  721 + JsonObject names = lwM2mInMemorySecurityStore.getProfiles().get(lwM2MClient.getProfileUuid()).getPostKeyNameProfile();
  722 + String resName = String.valueOf(names.get(path));
  723 + if (resName != null && !resName.isEmpty()) {
  724 + try {
  725 + String resValue = this.getResourceValueToString(lwM2MClient, path);
  726 + if (resValue != null) {
  727 + parameters.addProperty(resName, resValue);
  728 + }
  729 + } catch (Exception e) {
  730 + log.error(e.getStackTrace().toString());
  731 + }
869 732 }
870 733 }
871 734
872 735 /**
873   - * @param sessionInfo -
874   - * @param device -
875   - * @param deviceProfileOpt -
  736 + * @param path - path resource
  737 + * @return - value of Resource or null
876 738 */
877   - public void onDeviceUpdate(TransportProtos.SessionInfoProto sessionInfo, Device device, Optional<DeviceProfile> deviceProfileOpt) {
878   - Optional<String> registrationIdOpt = lwM2mInMemorySecurityStore.getSessions().entrySet().stream()
879   - .filter(e -> device.getUuidId().equals(e.getValue().getDeviceUuid()))
880   - .map(Map.Entry::getKey)
881   - .findFirst();
882   - registrationIdOpt.ifPresent(registrationId -> this.onDeviceUpdateLwM2MClient(registrationId, device, deviceProfileOpt));
  739 + private String getResourceValueToString(LwM2MClient lwM2MClient, String path) {
  740 + LwM2mPath pathIds = new LwM2mPath(path);
  741 + ResourceValue resourceValue = this.returnResourceValueFromLwM2MClient(lwM2MClient, pathIds);
  742 + return (resourceValue == null) ? null :
  743 + (String) this.converter.convertValue(resourceValue.getResourceValue(), this.context.getCtxServer().getResourceModelType(lwM2MClient.getRegistration(), pathIds), ResourceModel.Type.STRING, pathIds);
883 744 }
884 745
885 746 /**
886   - * Update parameters device in LwM2MClient
887   - * If new deviceProfile != old deviceProfile => update deviceProfile
888 747 *
889   - * @param registrationId -
890   - * @param device -
  748 + * @param lwM2MClient -
  749 + * @param pathIds -
  750 + * @return - return value of Resource by idPath
891 751 */
892   - private void onDeviceUpdateLwM2MClient(String registrationId, Device device, Optional<DeviceProfile> deviceProfileOpt) {
893   - LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getSessions().get(registrationId);
894   - lwM2MClient.setDeviceName(device.getName());
895   - if (!lwM2MClient.getProfileUuid().equals(device.getDeviceProfileId().getId())) {
896   - Set<String> registrationIds = new HashSet<>();
897   - registrationIds.add(registrationId);
898   - deviceProfileOpt.ifPresent(deviceProfile -> this.onDeviceUpdateChangeProfile(registrationIds, deviceProfile));
  752 + private ResourceValue returnResourceValueFromLwM2MClient(LwM2MClient lwM2MClient, LwM2mPath pathIds) {
  753 + ResourceValue resourceValue = null;
  754 + if (pathIds.isResource()) {
  755 + resourceValue = lwM2MClient.getResources().get(pathIds.toString());
899 756 }
  757 + return resourceValue;
  758 + }
900 759
901   - lwM2MClient.setProfileUuid(device.getDeviceProfileId().getId());
  760 + /**
  761 + * Update resource (attribute) value on thingsboard after update value in client
  762 + *
  763 + * @param registration -
  764 + * @param path -
  765 + * @param request -
  766 + */
  767 + public void onWriteResponseOk(Registration registration, String path, WriteRequest request) {
  768 + this.updateResourcesValue(registration, ((LwM2mResource) request.getNode()), path);
902 769 }
903 770
904 771 /**
... ... @@ -924,43 +791,43 @@ public class LwM2MTransportServiceImpl implements LwM2MTransportService {
924 791 */
925 792 private void onDeviceUpdateChangeProfile(Set<String> registrationIds, DeviceProfile deviceProfile) {
926 793
927   - AttrTelemetryObserveValue attrTelemetryObserveValueOld = lwM2mInMemorySecurityStore.getProfiles().get(deviceProfile.getUuidId());
  794 + LwM2MClientProfile lwM2MClientProfileOld = lwM2mInMemorySecurityStore.getProfiles().get(deviceProfile.getUuidId());
928 795 if (lwM2mInMemorySecurityStore.addUpdateProfileParameters(deviceProfile)) {
929 796
930 797 // #1
931   - JsonArray attributeOld = attrTelemetryObserveValueOld.getPostAttributeProfile();
932   - Set attributeSetOld = new Gson().fromJson(attributeOld, Set.class);
933   - JsonArray telemetryOld = attrTelemetryObserveValueOld.getPostTelemetryProfile();
934   - Set telemetrySetOld = new Gson().fromJson(telemetryOld, Set.class);
935   - JsonArray observeOld = attrTelemetryObserveValueOld.getPostObserveProfile();
936   - JsonObject keyNameOld = attrTelemetryObserveValueOld.getPostKeyNameProfile();
937   -
938   - AttrTelemetryObserveValue attrTelemetryObserveValueNew = lwM2mInMemorySecurityStore.getProfiles().get(deviceProfile.getUuidId());
939   - JsonArray attributeNew = attrTelemetryObserveValueNew.getPostAttributeProfile();
940   - Set attributeSetNew = new Gson().fromJson(attributeNew, Set.class);
941   - JsonArray telemetryNew = attrTelemetryObserveValueNew.getPostTelemetryProfile();
942   - Set telemetrySetNew = new Gson().fromJson(telemetryNew, Set.class);
943   - JsonArray observeNew = attrTelemetryObserveValueNew.getPostObserveProfile();
944   - JsonObject keyNameNew = attrTelemetryObserveValueNew.getPostKeyNameProfile();
  798 + JsonArray attributeOld = lwM2MClientProfileOld.getPostAttributeProfile();
  799 + Set<String> attributeSetOld = new Gson().fromJson(attributeOld, new TypeToken<>(){}.getType());
  800 + JsonArray telemetryOld = lwM2MClientProfileOld.getPostTelemetryProfile();
  801 + Set<String> telemetrySetOld = new Gson().fromJson(telemetryOld, new TypeToken<>(){}.getType());
  802 + JsonArray observeOld = lwM2MClientProfileOld.getPostObserveProfile();
  803 + JsonObject keyNameOld = lwM2MClientProfileOld.getPostKeyNameProfile();
  804 +
  805 + LwM2MClientProfile lwM2MClientProfileNew = lwM2mInMemorySecurityStore.getProfiles().get(deviceProfile.getUuidId());
  806 + JsonArray attributeNew = lwM2MClientProfileNew.getPostAttributeProfile();
  807 + Set<String> attributeSetNew = new Gson().fromJson(attributeNew, new TypeToken<>(){}.getType());
  808 + JsonArray telemetryNew = lwM2MClientProfileNew.getPostTelemetryProfile();
  809 + Set<String> telemetrySetNew = new Gson().fromJson(telemetryNew, new TypeToken<>(){}.getType());
  810 + JsonArray observeNew = lwM2MClientProfileNew.getPostObserveProfile();
  811 + JsonObject keyNameNew = lwM2MClientProfileNew.getPostKeyNameProfile();
945 812
946 813 // #3
947 814 ResultsAnalyzerParameters sentAttrToThingsboard = new ResultsAnalyzerParameters();
948 815 // #3.1
949 816 if (!attributeOld.equals(attributeNew)) {
950   - ResultsAnalyzerParameters postAttributeAnalyzer = this.getAnalyzerParameters(new Gson().fromJson(attributeOld, Set.class), attributeSetNew);
  817 + ResultsAnalyzerParameters postAttributeAnalyzer = this.getAnalyzerParameters(new Gson().fromJson(attributeOld, new TypeToken<Set<String>>(){}.getType()), attributeSetNew);
951 818 sentAttrToThingsboard.getPathPostParametersAdd().addAll(postAttributeAnalyzer.getPathPostParametersAdd());
952 819 sentAttrToThingsboard.getPathPostParametersDel().addAll(postAttributeAnalyzer.getPathPostParametersDel());
953 820 }
954 821 // #3.2
955 822 if (!attributeOld.equals(attributeNew)) {
956   - ResultsAnalyzerParameters postTelemetryAnalyzer = this.getAnalyzerParameters(new Gson().fromJson(telemetryOld, Set.class), telemetrySetNew);
  823 + ResultsAnalyzerParameters postTelemetryAnalyzer = this.getAnalyzerParameters(new Gson().fromJson(telemetryOld, new TypeToken<Set<String>>(){}.getType()), telemetrySetNew);
957 824 sentAttrToThingsboard.getPathPostParametersAdd().addAll(postTelemetryAnalyzer.getPathPostParametersAdd());
958 825 sentAttrToThingsboard.getPathPostParametersDel().addAll(postTelemetryAnalyzer.getPathPostParametersDel());
959 826 }
960 827 // #3.3
961 828 if (!keyNameOld.equals(keyNameNew)) {
962   - ResultsAnalyzerParameters keyNameChange = this.getAnalyzerKeyName(new Gson().fromJson(keyNameOld.toString(), ConcurrentHashMap.class),
963   - new Gson().fromJson(keyNameNew.toString(), ConcurrentHashMap.class));
  829 + ResultsAnalyzerParameters keyNameChange = this.getAnalyzerKeyName(new Gson().fromJson(keyNameOld.toString(), new TypeToken<ConcurrentHashMap<String, String>>(){}.getType()),
  830 + new Gson().fromJson(keyNameNew.toString(), new TypeToken<ConcurrentHashMap<String, String>>(){}.getType()));
964 831 sentAttrToThingsboard.getPathPostParametersAdd().addAll(keyNameChange.getPathPostParametersAdd());
965 832 }
966 833
... ... @@ -971,8 +838,7 @@ public class LwM2MTransportServiceImpl implements LwM2MTransportService {
971 838 LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClientWithReg(null, registrationId);
972 839 LeshanServer lwServer = lwM2MClient.getLwServer();
973 840 Registration registration = lwM2mInMemorySecurityStore.getByRegistration(registrationId);
974   - log.warn("[{}] # 4.1", registration.getEndpoint());
975   - this.updateResourceValueObserve(lwServer, registration, sentAttrToThingsboard.getPathPostParametersAdd(), GET_TYPE_OPER_READ);
  841 + this.readResourceValueObserve(lwServer, registration, sentAttrToThingsboard.getPathPostParametersAdd(), GET_TYPE_OPER_READ);
976 842 // sent attr/telemetry to tingsboard for new path
977 843 this.updateAttrTelemetry(registration, false, sentAttrToThingsboard.getPathPostParametersAdd());
978 844 });
... ... @@ -985,8 +851,8 @@ public class LwM2MTransportServiceImpl implements LwM2MTransportService {
985 851
986 852 // #5.1
987 853 if (!observeOld.equals(observeNew)) {
988   - Set observeSetOld = new Gson().fromJson(observeOld, Set.class);
989   - Set observeSetNew = new Gson().fromJson(observeNew, Set.class);
  854 + Set<String> observeSetOld = new Gson().fromJson(observeOld, new TypeToken<>(){}.getType());
  855 + Set<String> observeSetNew = new Gson().fromJson(observeNew, new TypeToken<>(){}.getType());
990 856 //#5.2 add
991 857 // path Attr/Telemetry includes newObserve
992 858 attributeSetOld.addAll(telemetrySetOld);
... ... @@ -1000,8 +866,7 @@ public class LwM2MTransportServiceImpl implements LwM2MTransportService {
1000 866 LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClient(null, registrationId);
1001 867 LeshanServer lwServer = lwM2MClient.getLwServer();
1002 868 Registration registration = lwM2mInMemorySecurityStore.getByRegistration(registrationId);
1003   - log.warn("[{}] # 5.1", registration.getEndpoint());
1004   - this.updateResourceValueObserve(lwServer, registration, postObserveAnalyzer.getPathPostParametersAdd(), GET_TYPE_OPER_OBSERVE);
  869 + this.readResourceValueObserve(lwServer, registration, postObserveAnalyzer.getPathPostParametersAdd(), GET_TYPE_OPER_OBSERVE);
1005 870 // 5.3 del
1006 871 // sent Request cancel observe to Client
1007 872 this.cancelObserveIsValue(lwServer, registration, postObserveAnalyzer.getPathPostParametersDel());
... ... @@ -1029,16 +894,6 @@ public class LwM2MTransportServiceImpl implements LwM2MTransportService {
1029 894 return analyzerParameters;
1030 895 }
1031 896
1032   - private ResultsAnalyzerParameters getAnalyzerKeyName(ConcurrentMap<String, String> keyNameOld, ConcurrentMap<String, String> keyNameNew) {
1033   - ResultsAnalyzerParameters analyzerParameters = new ResultsAnalyzerParameters();
1034   - Set<String> paths = keyNameNew.entrySet()
1035   - .stream()
1036   - .filter(e -> !e.getValue().equals(keyNameOld.get(e.getKey())))
1037   - .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)).keySet();
1038   - analyzerParameters.setPathPostParametersAdd(paths);
1039   - return analyzerParameters;
1040   - }
1041   -
1042 897 private ResultsAnalyzerParameters getAnalyzerParametersIn(Set<String> parametersObserve, Set<String> parameters) {
1043 898 ResultsAnalyzerParameters analyzerParameters = new ResultsAnalyzerParameters();
1044 899 analyzerParameters.setPathPostParametersAdd(parametersObserve
... ... @@ -1054,77 +909,167 @@ public class LwM2MTransportServiceImpl implements LwM2MTransportService {
1054 909 * @param registration - Registration LwM2M Client
1055 910 * @param targets - path Resources == [ "/2/0/0", "/2/0/1"]
1056 911 */
1057   - private void updateResourceValueObserve(LeshanServer lwServer, Registration registration, Set<String> targets, String typeOper) {
  912 + private void readResourceValueObserve(LeshanServer lwServer, Registration registration, Set<String> targets, String typeOper) {
1058 913 targets.forEach(target -> {
1059 914 LwM2mPath pathIds = new LwM2mPath(target);
1060 915 if (pathIds.isResource()) {
1061 916 if (GET_TYPE_OPER_READ.equals(typeOper)) {
1062 917 lwM2MTransportRequest.sendAllRequest(lwServer, registration, target, typeOper,
1063   - ContentFormat.TLV.getName(), null, null, null, this.context.getCtxServer().getTimeout(),
1064   - false);
  918 + ContentFormat.TLV.getName(), null, null, this.context.getCtxServer().getTimeout());
1065 919 } else if (GET_TYPE_OPER_OBSERVE.equals(typeOper)) {
1066 920 lwM2MTransportRequest.sendAllRequest(lwServer, registration, target, typeOper,
1067   - null, null, null, null, this.context.getCtxServer().getTimeout(),
1068   - false);
  921 + null, null, null, this.context.getCtxServer().getTimeout());
1069 922 }
1070 923 }
1071 924 });
1072 925 }
1073 926
  927 + private ResultsAnalyzerParameters getAnalyzerKeyName(ConcurrentMap<String, String> keyNameOld, ConcurrentMap<String, String> keyNameNew) {
  928 + ResultsAnalyzerParameters analyzerParameters = new ResultsAnalyzerParameters();
  929 + Set<String> paths = keyNameNew.entrySet()
  930 + .stream()
  931 + .filter(e -> !e.getValue().equals(keyNameOld.get(e.getKey())))
  932 + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)).keySet();
  933 + analyzerParameters.setPathPostParametersAdd(paths);
  934 + return analyzerParameters;
  935 + }
  936 +
1074 937 private void cancelObserveIsValue(LeshanServer lwServer, Registration registration, Set<String> paramAnallyzer) {
1075 938 LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClientWithReg(registration, null);
1076 939 paramAnallyzer.forEach(p -> {
1077   - if (this.getResourceValue(lwM2MClient, new LwM2mPath(p)) != null) {
  940 + if (this.returnResourceValueFromLwM2MClient(lwM2MClient, new LwM2mPath(p)) != null) {
1078 941 this.setCancelObservationRecourse(lwServer, registration, p);
1079 942 }
1080 943 }
1081 944 );
1082 945 }
1083 946
1084   - private ResourceValue getResourceValue(LwM2MClient lwM2MClient, LwM2mPath pathIds) {
1085   - ResourceValue resourceValue = null;
1086   - if (pathIds.isResource()) {
1087   - resourceValue = lwM2MClient.getResources().get(pathIds.toString());
  947 + private void putDelayedUpdateResourcesClient(LwM2MClient lwM2MClient, Object valueOld, Object valueNew, String path) {
  948 + if (valueNew != null && (valueOld == null || !valueNew.toString().equals(valueOld.toString()))) {
  949 + lwM2MTransportRequest.sendAllRequest(lwM2MClient.getLwServer(), lwM2MClient.getRegistration(), path, POST_TYPE_OPER_WRITE_REPLACE,
  950 + ContentFormat.TLV.getName(), null, valueNew, this.context.getCtxServer().getTimeout());
  951 + } else {
  952 + log.error("05 delayError");
1088 953 }
1089   - return resourceValue;
1090 954 }
1091 955
1092 956 /**
1093   - * Trigger Server path = "/1/0/8"
  957 + * @param updateCredentials - Credentials include config only security Client (without config attr/telemetry...)
  958 + * config attr/telemetry... in profile
  959 + */
  960 + public void onToTransportUpdateCredentials(TransportProtos.ToTransportUpdateCredentialsProto updateCredentials) {
  961 + log.info("[{}] idList [{}] valueList updateCredentials", updateCredentials.getCredentialsIdList(), updateCredentials.getCredentialsValueList());
  962 + }
  963 +
  964 + /**
  965 + * Get path to resource from profile equal keyName or from ModelObject equal name
  966 + * Only for resource: isWritable && isPresent as attribute in profile -> LwM2MClientProfile (format: CamelCase)
1094 967 *
1095   - * Trigger bootStrap path = "/1/0/9" - have to implemented on client
  968 + * @param sessionInfo -
  969 + * @param name -
  970 + * @return path if path isPresent in postProfile
1096 971 */
1097   - public void doTrigger(LeshanServer lwServer, Registration registration, String path) {
1098   - lwM2MTransportRequest.sendAllRequest(lwServer, registration, path, POST_TYPE_OPER_EXECUTE,
1099   - ContentFormat.TLV.getName(), null, null, null, this.context.getCtxServer().getTimeout(),
1100   - false);
  972 + private String getPathAttributeUpdate(TransportProtos.SessionInfoProto sessionInfo, String name) {
  973 + String profilePath = this.getPathAttributeUpdateProfile(sessionInfo, name);
  974 + return !profilePath.isEmpty() ? profilePath : null;
1101 975 }
1102 976
1103 977 /**
1104   - * Session device in thingsboard is closed
  978 + * Get path to resource from profile equal keyName
1105 979 *
1106   - * @param sessionInfo - lwm2m client
  980 + * @param sessionInfo -
  981 + * @param name -
  982 + * @return -
1107 983 */
1108   - private void doCloseSession(SessionInfoProto sessionInfo) {
1109   - TransportProtos.SessionEvent event = SessionEvent.CLOSED;
1110   - TransportProtos.SessionEventMsg msg = TransportProtos.SessionEventMsg.newBuilder()
1111   - .setSessionType(TransportProtos.SessionType.ASYNC)
1112   - .setEvent(event).build();
1113   - transportService.process(sessionInfo, msg, null);
  984 + private String getPathAttributeUpdateProfile(TransportProtos.SessionInfoProto sessionInfo, String name) {
  985 + LwM2MClientProfile profile = lwM2mInMemorySecurityStore.getProfile(new UUID(sessionInfo.getDeviceProfileIdMSB(), sessionInfo.getDeviceProfileIdLSB()));
  986 + return profile.getPostKeyNameProfile().getAsJsonObject().entrySet().stream()
  987 + .filter(e -> e.getValue().getAsString().equals(name)).findFirst().map(Map.Entry::getKey)
  988 + .orElse("");
1114 989 }
1115 990
1116 991 /**
1117   - * Deregister session in transport
  992 + * Update resource value on client: if there is a difference in values between the current resource values and the shared attribute values
  993 + * #1 Get path resource by result attributesResponse
  994 + * #1.1 If two names have equal path => last time attribute
  995 + * #2.1 if there is a difference in values between the current resource values and the shared attribute values
  996 + * => sent to client Request Update of value (new value from shared attribute)
  997 + * and LwM2MClient.delayedRequests.add(path)
  998 + * #2.1 if there is not a difference in values between the current resource values and the shared attribute values
1118 999 *
1119   - * @param sessionInfo - lwm2m client
  1000 + * @param attributesResponse -
  1001 + * @param sessionInfo -
1120 1002 */
1121   - public void doDisconnect(SessionInfoProto sessionInfo) {
1122   - transportService.process(sessionInfo, DefaultTransportService.getSessionEventMsg(SessionEvent.CLOSED), null);
1123   - transportService.deregisterSession(sessionInfo);
  1003 + public void onGetAttributesResponse(TransportProtos.GetAttributeResponseMsg attributesResponse, TransportProtos.SessionInfoProto sessionInfo) {
  1004 + try {
  1005 + LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClient(sessionInfo);
  1006 + attributesResponse.getSharedAttributeListList().forEach(attr -> {
  1007 + String path = this.getPathAttributeUpdate(sessionInfo, attr.getKv().getKey());
  1008 + // #1.1
  1009 + if (lwM2MClient.getDelayedRequests().containsKey(path) && attr.getTs() > lwM2MClient.getDelayedRequests().get(path).getTs()) {
  1010 + lwM2MClient.getDelayedRequests().put(path, attr);
  1011 + } else {
  1012 + lwM2MClient.getDelayedRequests().put(path, attr);
  1013 + }
  1014 + });
  1015 + // #2.1
  1016 + lwM2MClient.getDelayedRequests().forEach((k, v) -> {
  1017 + ArrayList<TransportProtos.KeyValueProto> listV = new ArrayList<>();
  1018 + listV.add(v.getKv());
  1019 + this.putDelayedUpdateResourcesClient(lwM2MClient, this.getResourceValueToString(lwM2MClient, k), getJsonObject(listV).get(v.getKv().getKey()), k);
  1020 + });
  1021 + } catch (Exception e) {
  1022 + log.error(String.valueOf(e));
  1023 + }
1124 1024 }
1125 1025
1126   - private void checkInactivityAndReportActivity() {
1127   - lwM2mInMemorySecurityStore.getSessions().forEach((key, value) -> this.checkInactivity(this.getValidateSessionInfo(key)));
  1026 + /**
  1027 + * @param lwM2MClient -
  1028 + * @return
  1029 + */
  1030 + private SessionInfoProto getNewSessionInfoProto(LwM2MClient lwM2MClient) {
  1031 + if (lwM2MClient != null) {
  1032 + TransportProtos.ValidateDeviceCredentialsResponseMsg msg = lwM2MClient.getCredentialsResponse();
  1033 + if (msg == null || msg.getDeviceInfo() == null) {
  1034 + log.error("[{}] [{}]", lwM2MClient.getEndPoint(), CLIENT_NOT_AUTHORIZED);
  1035 + this.closeClientSession(lwM2MClient.getRegistration());
  1036 + return null;
  1037 + } else {
  1038 + return SessionInfoProto.newBuilder()
  1039 + .setNodeId(this.context.getNodeId())
  1040 + .setSessionIdMSB(lwM2MClient.getSessionUuid().getMostSignificantBits())
  1041 + .setSessionIdLSB(lwM2MClient.getSessionUuid().getLeastSignificantBits())
  1042 + .setDeviceIdMSB(msg.getDeviceInfo().getDeviceIdMSB())
  1043 + .setDeviceIdLSB(msg.getDeviceInfo().getDeviceIdLSB())
  1044 + .setTenantIdMSB(msg.getDeviceInfo().getTenantIdMSB())
  1045 + .setTenantIdLSB(msg.getDeviceInfo().getTenantIdLSB())
  1046 + .setDeviceName(msg.getDeviceInfo().getDeviceName())
  1047 + .setDeviceType(msg.getDeviceInfo().getDeviceType())
  1048 + .setDeviceProfileIdLSB(msg.getDeviceInfo().getDeviceProfileIdLSB())
  1049 + .setDeviceProfileIdMSB(msg.getDeviceInfo().getDeviceProfileIdMSB())
  1050 + .build();
  1051 + }
  1052 + }
  1053 + return null;
  1054 + }
  1055 +
  1056 +
  1057 + /**
  1058 + * @param registration - Registration LwM2M Client
  1059 + * @return - sessionInfo after access connect client
  1060 + */
  1061 + private SessionInfoProto getValidateSessionInfo(Registration registration) {
  1062 + LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClientWithReg(registration, null);
  1063 + return getNewSessionInfoProto(lwM2MClient);
  1064 + }
  1065 +
  1066 + /**
  1067 + * @param registrationId -
  1068 + * @return -
  1069 + */
  1070 + private SessionInfoProto getValidateSessionInfo(String registrationId) {
  1071 + LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClientWithReg(null, registrationId);
  1072 + return getNewSessionInfoProto(lwM2MClient);
1128 1073 }
1129 1074
1130 1075 /**
... ... @@ -1138,22 +1083,55 @@ public class LwM2MTransportServiceImpl implements LwM2MTransportService {
1138 1083 }
1139 1084 }
1140 1085
1141   - public void sentLogsToThingsboard(String msg, Registration registration) {
1142   - if (msg != null) {
1143   - JsonObject telemetries = new JsonObject();
1144   - telemetries.addProperty(LOG_LW2M_TELEMETRY, msg);
1145   - this.updateParametersOnThingsboard(telemetries, LwM2MTransportHandler.DEVICE_TELEMETRY_TOPIC, registration);
  1086 + private void checkInactivityAndReportActivity() {
  1087 + lwM2mInMemorySecurityStore.getSessions().forEach((key, value) -> this.checkInactivity(this.getValidateSessionInfo(key)));
  1088 + }
  1089 +
  1090 + /**
  1091 + * If there is a difference in values between the current resource values and the shared attribute values
  1092 + * when the client connects to the server
  1093 + * #1 get attributes name from profile include name resources in ModelObject if resource isWritable
  1094 + * #2.1 #1 size > 0 => send Request getAttributes to thingsboard
  1095 + *
  1096 + * @param lwM2MClient - LwM2M Client
  1097 + */
  1098 + public void putDelayedUpdateResourcesThingsboard(LwM2MClient lwM2MClient) {
  1099 + SessionInfoProto sessionInfo = this.getValidateSessionInfo(lwM2MClient.getRegistration());
  1100 + if (sessionInfo != null) {
  1101 + //#1.1 + #1.2
  1102 + List<String> attrSharedNames = this.getNamesAttrFromProfileIsWritable(lwM2MClient);
  1103 + if (attrSharedNames.size() > 0) {
  1104 + //#2.1
  1105 + try {
  1106 + TransportProtos.GetAttributeRequestMsg getAttributeMsg = context.getAdaptor().convertToGetAttributes(null, attrSharedNames);
  1107 + transportService.process(sessionInfo, getAttributeMsg, getAckCallback(lwM2MClient, getAttributeMsg.getRequestId(), DEVICE_ATTRIBUTES_REQUEST));
  1108 + } catch (AdaptorException e) {
  1109 + log.warn("Failed to decode get attributes request", e);
  1110 + }
  1111 + }
1146 1112 }
1147 1113 }
1148 1114
  1115 +
1149 1116 /**
1150   - * @param path - path resource
1151   - * @return - value of Resource or null
  1117 + * Get names and keyNames from profile shared!!!! attr resources IsWritable
  1118 + *
  1119 + * @param lwM2MClient -
  1120 + * @return ArrayList keyNames from profile attr resources shared!!!! && IsWritable
1152 1121 */
1153   - private String getResourceValueToString(LwM2MClient lwM2MClient, String path) {
1154   - LwM2mPath pathIds = new LwM2mPath(path);
1155   - ResourceValue resourceValue = this.getResourceValue(lwM2MClient, pathIds);
1156   - return (resourceValue == null) ? null :
1157   - (String) this.converter.convertValue(resourceValue.getResourceValue(), this.context.getCtxServer().getResourceModelType(lwM2MClient.getRegistration(), pathIds), ResourceModel.Type.STRING, pathIds);
  1122 + private List<String> getNamesAttrFromProfileIsWritable(LwM2MClient lwM2MClient) {
  1123 + LwM2MClientProfile profile = lwM2mInMemorySecurityStore.getProfile(lwM2MClient.getProfileUuid());
  1124 + Set attrSet = new Gson().fromJson(profile.getPostAttributeProfile(), Set.class);
  1125 + ConcurrentMap<String, String> keyNamesMap = new Gson().fromJson(profile.getPostKeyNameProfile().toString(), new TypeToken<ConcurrentHashMap<String, String>>(){}.getType());
  1126 +
  1127 + ConcurrentMap<String, String> keyNamesIsWritable = keyNamesMap.entrySet()
  1128 + .stream()
  1129 + .filter(e -> (attrSet.contains(e.getKey()) && context.getCtxServer().getResourceModel(lwM2MClient.getRegistration(), new LwM2mPath(e.getKey())) != null &&
  1130 + context.getCtxServer().getResourceModel(lwM2MClient.getRegistration(), new LwM2mPath(e.getKey())).operations.isWritable()))
  1131 + .collect(Collectors.toConcurrentMap(Map.Entry::getKey, Map.Entry::getValue));
  1132 +
  1133 + Set<String> namesIsWritable = ConcurrentHashMap.newKeySet();
  1134 + namesIsWritable.addAll(new HashSet<>(keyNamesIsWritable.values()));
  1135 + return new ArrayList<>(namesIsWritable);
1158 1136 }
1159 1137 }
... ...
... ... @@ -108,7 +108,7 @@ public class LwM2mServerListener {
108 108
109 109 @Override
110 110 public void newObservation(Observation observation, Registration registration) {
111   - log.info("Received newObservation from [{}] endpoint [{}] ", observation.getPath(), registration.getEndpoint());
  111 +// log.info("Received newObservation from [{}] endpoint [{}] ", observation.getPath(), registration.getEndpoint());
112 112 }
113 113 };
114 114
... ...
... ... @@ -17,12 +17,9 @@ package org.thingsboard.server.transport.lwm2m.server.client;
17 17
18 18 import lombok.Data;
19 19 import lombok.extern.slf4j.Slf4j;
20   -import org.eclipse.leshan.core.model.ObjectModel;
21 20 import org.eclipse.leshan.core.node.LwM2mMultipleResource;
22   -import org.eclipse.leshan.core.node.LwM2mObjectInstance;
23   -import org.eclipse.leshan.core.node.LwM2mPath;
24   -import org.eclipse.leshan.core.response.LwM2mResponse;
25   -import org.eclipse.leshan.core.response.ReadResponse;
  21 +import org.eclipse.leshan.core.node.LwM2mResource;
  22 +import org.eclipse.leshan.core.node.LwM2mSingleResource;
26 23 import org.eclipse.leshan.server.californium.LeshanServer;
27 24 import org.eclipse.leshan.server.registration.Registration;
28 25 import org.eclipse.leshan.server.security.SecurityInfo;
... ... @@ -31,11 +28,11 @@ import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceCreden
31 28 import org.thingsboard.server.transport.lwm2m.server.LwM2MTransportServiceImpl;
32 29 import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl;
33 30
  31 +import java.util.List;
34 32 import java.util.Map;
35   -import java.util.Set;
36 33 import java.util.UUID;
37 34 import java.util.concurrent.ConcurrentHashMap;
38   -import java.util.stream.Collectors;
  35 +import java.util.concurrent.CopyOnWriteArrayList;
39 36
40 37 @Slf4j
41 38 @Data
... ... @@ -44,7 +41,7 @@ public class LwM2MClient implements Cloneable {
44 41 private String deviceProfileName;
45 42 private String endPoint;
46 43 private String identity;
47   - private SecurityInfo info;
  44 + private SecurityInfo securityInfo;
48 45 private UUID deviceUuid;
49 46 private UUID sessionUuid;
50 47 private UUID profileUuid;
... ... @@ -52,82 +49,52 @@ public class LwM2MClient implements Cloneable {
52 49 private LwM2MTransportServiceImpl lwM2MTransportServiceImpl;
53 50 private Registration registration;
54 51 private ValidateDeviceCredentialsResponseMsg credentialsResponse;
55   - private Map<String, String> attributes;
56   - private Map<String, ResourceValue> resources;
57   - private Set<String> pendingRequests;
58   - private Map<String, TransportProtos.TsKvProto> delayedRequests;
59   - private Set<Integer> delayedRequestsId;
60   - private Map<String, LwM2mResponse> responses;
  52 + private final Map<String, String> attributes;
  53 + private final Map<String, ResourceValue> resources;
  54 + private final Map<String, TransportProtos.TsKvProto> delayedRequests;
  55 + private final List<String> pendingRequests;
  56 + private boolean init;
61 57 private final LwM2mValueConverterImpl converter;
62 58
63 59 public Object clone() throws CloneNotSupportedException {
64 60 return super.clone();
65 61 }
66 62
67   - public LwM2MClient(String endPoint, String identity, SecurityInfo info, ValidateDeviceCredentialsResponseMsg credentialsResponse, UUID profileUuid) {
  63 + public LwM2MClient(String endPoint, String identity, SecurityInfo securityInfo, ValidateDeviceCredentialsResponseMsg credentialsResponse, UUID profileUuid, UUID sessionUuid) {
68 64 this.endPoint = endPoint;
69 65 this.identity = identity;
70   - this.info = info;
  66 + this.securityInfo = securityInfo;
71 67 this.credentialsResponse = credentialsResponse;
72 68 this.attributes = new ConcurrentHashMap<>();
73   - this.pendingRequests = ConcurrentHashMap.newKeySet();
74 69 this.delayedRequests = new ConcurrentHashMap<>();
  70 + this.pendingRequests = new CopyOnWriteArrayList<>();
75 71 this.resources = new ConcurrentHashMap<>();
76   - this.delayedRequestsId = ConcurrentHashMap.newKeySet();
77 72 this.profileUuid = profileUuid;
78   - /**
79   - * Key <objectId>, response<Value -> instance -> resources: value...>
80   - */
81   - this.responses = new ConcurrentHashMap<>();
  73 + this.sessionUuid = sessionUuid;
82 74 this.converter = LwM2mValueConverterImpl.getInstance();
  75 + this.init = false;
83 76 }
84 77
85   - /**
86   - * Fill with data -> Model client
87   - *
88   - * @param path -
89   - * @param response -
90   - */
91   - public void onSuccessHandler(String path, LwM2mResponse response) {
92   - this.responses.put(path, response);
93   - this.pendingRequests.remove(path);
94   - if (this.pendingRequests.size() == 0) {
95   - this.initValue();
96   - this.lwM2MTransportServiceImpl.putDelayedUpdateResourcesThingsboard(this);
  78 + public void updateResourceValue(String pathRez, LwM2mResource rez) {
  79 + if (rez instanceof LwM2mMultipleResource) {
  80 + this.resources.put(pathRez, new ResourceValue(rez.getValues(), null, true));
  81 + } else if (rez instanceof LwM2mSingleResource) {
  82 + this.resources.put(pathRez, new ResourceValue(null, rez.getValue(), false));
97 83 }
98 84 }
99 85
100   - private void initValue() {
101   - this.responses.forEach((key, resp) -> {
102   - LwM2mPath pathIds = new LwM2mPath(key);
103   - if (pathIds.isObject() || pathIds.isObjectInstance() || pathIds.isResource()) {
104   - ObjectModel objectModel = this.lwServer.getModelProvider().getObjectModel(registration).getObjectModels().stream().filter(v -> v.id == pathIds.getObjectId()).collect(Collectors.toList()).get(0);
105   - if (objectModel != null) {
106   - ((LwM2mObjectInstance)((ReadResponse)resp).getContent()).getResources().forEach((k, v) -> {
107   - String rez = pathIds.toString() + "/" + k;
108   - if (((LwM2mObjectInstance) ((ReadResponse) resp).getContent()).getResource(k) instanceof LwM2mMultipleResource){
109   - this.resources.put(rez, new ResourceValue(v.getValues(), null, true));
110   - }
111   - else {
112   - this.resources.put(rez, new ResourceValue(null, v.getValue(), false));
113   - }
114   - });
115   - }
116   - }
117   - });
118   - if (this.responses.size() == 0) this.responses = new ConcurrentHashMap<>();
119   - }
120   -
121   - /**
122   - * if path != null
123   - * @param path
124   - */
125   - public void onSuccessOrErrorDelayedRequests(String path) {
126   - if (path != null) this.delayedRequests.remove(path);
127   - if (this.delayedRequests.size() == 0 && this.getDelayedRequestsId().size() == 0) {
128   - this.lwM2MTransportServiceImpl.updatesAndSentModelParameter(this);
  86 + public void initValue(LwM2MTransportServiceImpl lwM2MTransportService, String path) {
  87 + if (path != null) {
  88 + this.pendingRequests.remove(path);
  89 + }
  90 + if (this.pendingRequests.size() == 0) {
  91 + this.init = true;
  92 + lwM2MTransportService.putDelayedUpdateResourcesThingsboard(this);
129 93 }
130 94 }
131 95
  96 + public LwM2MClient copy() {
  97 + return new LwM2MClient(this.endPoint, this.identity, this.securityInfo, this.credentialsResponse, this.profileUuid, this.sessionUuid);
  98 + }
132 99 }
133 100
... ...
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2MClientProfile.java renamed from common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/AttrTelemetryObserveValue.java
... ... @@ -20,7 +20,14 @@ import com.google.gson.JsonObject;
20 20 import lombok.Data;
21 21
22 22 @Data
23   -public class AttrTelemetryObserveValue {
  23 +public class LwM2MClientProfile {
  24 + /**
  25 + * {"clientLwM2mSettings": {
  26 + * clientUpdateValueAfterConnect: false;
  27 + * }
  28 + **/
  29 + JsonObject postClientLwM2mSettings;
  30 +
24 31 /**
25 32 * {"keyName": {
26 33 * "/3/0/1": "modelNumber",
... ...