Commit b58c831343209a653473ede69cfa3cc35d31a005
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.
... | ... | @@ -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; | ... | ... |
application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeActorMessageProcessor.java
... | ... | @@ -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() { | ... | ... |
application/src/main/java/org/thingsboard/server/actors/ruleChain/TbToRuleNodeActorMsg.java
0 → 100644
1 | +/** | |
2 | + * Copyright © 2016-2021 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.server.actors.ruleChain; | |
17 | + | |
18 | +import lombok.EqualsAndHashCode; | |
19 | +import lombok.Getter; | |
20 | +import org.thingsboard.rule.engine.api.TbContext; | |
21 | +import org.thingsboard.server.common.msg.TbActorStopReason; | |
22 | +import org.thingsboard.server.common.msg.TbMsg; | |
23 | +import org.thingsboard.server.common.msg.TbRuleEngineActorMsg; | |
24 | +import org.thingsboard.server.common.msg.queue.RuleNodeException; | |
25 | + | |
26 | +@EqualsAndHashCode(callSuper = true) | |
27 | +public abstract class TbToRuleNodeActorMsg extends TbRuleEngineActorMsg { | |
28 | + | |
29 | + @Getter | |
30 | + private final TbContext ctx; | |
31 | + | |
32 | + public TbToRuleNodeActorMsg(TbContext ctx, TbMsg tbMsg) { | |
33 | + super(tbMsg); | |
34 | + this.ctx = ctx; | |
35 | + } | |
36 | + | |
37 | + @Override | |
38 | + public void onTbActorStopped(TbActorStopReason reason) { | |
39 | + String message = reason == TbActorStopReason.STOPPED ? "Rule node stopped" : "Failed to initialize rule node!"; | |
40 | + msg.getCallback().onFailure(new RuleNodeException(message, ctx.getRuleChainName(), ctx.getSelf())); | |
41 | + } | |
42 | +} | ... | ... |
... | ... | @@ -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; | ... | ... |
application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java
... | ... | @@ -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 | +} | ... | ... |
... | ... | @@ -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; | ... | ... |
application/src/test/java/org/thingsboard/server/util/EventDeduplicationExecutorTest.java
0 → 100644
1 | +/** | |
2 | + * Copyright © 2016-2021 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.server.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 | +} | ... | ... |
... | ... | @@ -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) { | ... | ... |
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; | ... | ... |
... | ... | @@ -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; | ... | ... |
... | ... | @@ -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> | ... | ... |
common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java
... | ... | @@ -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); | ... | ... |
... | ... | @@ -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", | ... | ... |