org.xolstice.maven.plugins
protobuf-maven-plugin
diff --git a/application/src/main/data/json/system/widget_bundles/cards.json b/application/src/main/data/json/system/widget_bundles/cards.json
index 803f1d3953..6550f58da4 100644
--- a/application/src/main/data/json/system/widget_bundles/cards.json
+++ b/application/src/main/data/json/system/widget_bundles/cards.json
@@ -15,7 +15,7 @@
"resources": [],
"templateHtml": "",
"templateCss": "#container {\n overflow: auto;\n}\n\n.tbDatasource-container {\n margin: 5px;\n padding: 8px;\n}\n\n.tbDatasource-title {\n font-size: 1.200rem;\n font-weight: 500;\n padding-bottom: 10px;\n}\n\n.tbDatasource-table {\n width: 100%;\n box-shadow: 0 0 10px #ccc;\n border-collapse: collapse;\n white-space: nowrap;\n font-size: 1.000rem;\n color: #757575;\n}\n\n.tbDatasource-table td {\n position: relative;\n border-top: 1px solid rgba(0, 0, 0, 0.12);\n border-bottom: 1px solid rgba(0, 0, 0, 0.12);\n padding: 0px 18px;\n box-sizing: border-box;\n}",
- "controllerScript": "self.onInit = function() {\n \n self.ctx.datasourceTitleCells = [];\n self.ctx.valueCells = [];\n self.ctx.labelCells = [];\n \n for (var i=0; i < self.ctx.datasources.length; i++) {\n var tbDatasource = self.ctx.datasources[i];\n\n var datasourceId = 'tbDatasource' + i;\n self.ctx.$container.append(\n \"\"\n );\n\n var datasourceContainer = $('#' + datasourceId,\n self.ctx.$container);\n\n datasourceContainer.append(\n \"\" +\n tbDatasource.name + \"
\"\n );\n \n var datasourceTitleCell = $('.tbDatasource-title', datasourceContainer);\n self.ctx.datasourceTitleCells.push(datasourceTitleCell);\n \n var tableId = 'table' + i;\n datasourceContainer.append(\n \"\"\n );\n var table = $('#' + tableId, self.ctx.$container);\n\n for (var a = 0; a < tbDatasource.dataKeys.length; a++) {\n var dataKey = tbDatasource.dataKeys[a];\n var labelCellId = 'labelCell' + a;\n var cellId = 'cell' + a;\n table.append(\"| \" + dataKey.label +\n \" | |
\");\n var labelCell = $('#' + labelCellId, table);\n self.ctx.labelCells.push(labelCell);\n var valueCell = $('#' + cellId, table);\n self.ctx.valueCells.push(valueCell);\n }\n } \n \n self.onResize();\n}\n\nself.onDataUpdated = function() {\n for (var i = 0; i < self.ctx.valueCells.length; i++) {\n var cellData = self.ctx.data[i];\n if (cellData && cellData.data && cellData.data.length > 0) {\n var tvPair = cellData.data[cellData.data.length -\n 1];\n var value = tvPair[1];\n self.ctx.valueCells[i].html(value);\n }\n } \n}\n\nself.onResize = function() {\n var datasoirceTitleFontSize = self.ctx.height/8;\n if (self.ctx.width/self.ctx.height <= 1.5) {\n datasoirceTitleFontSize = self.ctx.width/12;\n }\n datasoirceTitleFontSize = Math.min(datasoirceTitleFontSize, 20);\n for (var i = 0; i < self.ctx.datasourceTitleCells.length; i++) {\n self.ctx.datasourceTitleCells[i].css('font-size', datasoirceTitleFontSize+'px');\n }\n var valueFontSize = self.ctx.height/9;\n var labelFontSize = self.ctx.height/9;\n if (self.ctx.width/self.ctx.height <= 1.5) {\n valueFontSize = self.ctx.width/15;\n labelFontSize = self.ctx.width/15;\n }\n valueFontSize = Math.min(valueFontSize, 18);\n labelFontSize = Math.min(labelFontSize, 18);\n\n for (i = 0; i < self.ctx.valueCells; i++) {\n self.ctx.valueCells[i].css('font-size', valueFontSize+'px');\n self.ctx.valueCells[i].css('height', valueFontSize*2.5+'px');\n self.ctx.valueCells[i].css('padding', '0px ' + valueFontSize + 'px');\n self.ctx.labelCells[i].css('font-size', labelFontSize+'px');\n self.ctx.labelCells[i].css('height', labelFontSize*2.5+'px');\n self.ctx.labelCells[i].css('padding', '0px ' + labelFontSize + 'px');\n } \n}\n\nself.onDestroy = function() {\n}\n",
+ "controllerScript": "self.onInit = function() {\n \n self.ctx.datasourceTitleCells = [];\n self.ctx.valueCells = [];\n self.ctx.labelCells = [];\n \n for (var i=0; i < self.ctx.datasources.length; i++) {\n var tbDatasource = self.ctx.datasources[i];\n\n var datasourceId = 'tbDatasource' + i;\n self.ctx.$container.append(\n \"\"\n );\n\n var datasourceContainer = $('#' + datasourceId,\n self.ctx.$container);\n\n datasourceContainer.append(\n \"\" +\n tbDatasource.name + \"
\"\n );\n \n var datasourceTitleCell = $('.tbDatasource-title', datasourceContainer);\n self.ctx.datasourceTitleCells.push(datasourceTitleCell);\n \n var tableId = 'table' + i;\n datasourceContainer.append(\n \"\"\n );\n var table = $('#' + tableId, self.ctx.$container);\n\n for (var a = 0; a < tbDatasource.dataKeys.length; a++) {\n var dataKey = tbDatasource.dataKeys[a];\n var labelCellId = 'labelCell' + a;\n var cellId = 'cell' + a;\n table.append(\"| \" + dataKey.label +\n \" | |
\");\n var labelCell = $('#' + labelCellId, table);\n self.ctx.labelCells.push(labelCell);\n var valueCell = $('#' + cellId, table);\n self.ctx.valueCells.push(valueCell);\n }\n } \n \n self.onResize();\n}\n\nself.onDataUpdated = function() {\n for (var i = 0; i < self.ctx.valueCells.length; i++) {\n var cellData = self.ctx.data[i];\n if (cellData && cellData.data && cellData.data.length > 0) {\n var tvPair = cellData.data[cellData.data.length -\n 1];\n var value = tvPair[1];\n var textValue;\n //toDo -> + IsNumber\n \n if (isNumber(value)) {\n var decimals = self.ctx.decimals;\n var units = self.ctx.units;\n if (cellData.dataKey.decimals || cellData.dataKey.decimals === 0) {\n decimals = cellData.dataKey.decimals;\n }\n if (cellData.dataKey.units) {\n units = cellData.dataKey.units;\n }\n txtValue = self.ctx.utils.formatValue(value, decimals, units, true);\n } else {\n txtValue = value;\n }\n self.ctx.valueCells[i].html(txtValue);\n }\n }\n \n function isNumber(n) {\n return !isNaN(parseFloat(n)) && isFinite(n);\n }\n}\n\nself.onResize = function() {\n var datasoirceTitleFontSize = self.ctx.height/8;\n if (self.ctx.width/self.ctx.height <= 1.5) {\n datasoirceTitleFontSize = self.ctx.width/12;\n }\n datasoirceTitleFontSize = Math.min(datasoirceTitleFontSize, 20);\n for (var i = 0; i < self.ctx.datasourceTitleCells.length; i++) {\n self.ctx.datasourceTitleCells[i].css('font-size', datasoirceTitleFontSize+'px');\n }\n var valueFontSize = self.ctx.height/9;\n var labelFontSize = self.ctx.height/9;\n if (self.ctx.width/self.ctx.height <= 1.5) {\n valueFontSize = self.ctx.width/15;\n labelFontSize = self.ctx.width/15;\n }\n valueFontSize = Math.min(valueFontSize, 18);\n labelFontSize = Math.min(labelFontSize, 18);\n\n for (i = 0; i < self.ctx.valueCells; i++) {\n self.ctx.valueCells[i].css('font-size', valueFontSize+'px');\n self.ctx.valueCells[i].css('height', valueFontSize*2.5+'px');\n self.ctx.valueCells[i].css('padding', '0px ' + valueFontSize + 'px');\n self.ctx.labelCells[i].css('font-size', labelFontSize+'px');\n self.ctx.labelCells[i].css('height', labelFontSize*2.5+'px');\n self.ctx.labelCells[i].css('padding', '0px ' + labelFontSize + 'px');\n } \n}\n\nself.onDestroy = function() {\n}\n",
"settingsSchema": "{}",
"dataKeySettingsSchema": "{}\n",
"defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"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\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Attributes card\"}"
@@ -27,7 +27,7 @@
"descriptor": {
"type": "latest",
"sizeX": 7.5,
- "sizeY": 4.5,
+ "sizeY": 6.5,
"resources": [],
"templateHtml": "\n",
"templateCss": "",
@@ -95,7 +95,7 @@
"resources": [],
"templateHtml": "",
"templateCss": "#container {\n overflow: auto;\n}\n\n.tbDatasource-container {\n width: 100%;\n height: 100%;\n overflow: hidden;\n}\n\n.tbDatasource-table {\n width: 100%;\n height: 100%;\n border-collapse: collapse;\n white-space: nowrap;\n font-weight: 100;\n text-align: right;\n}\n\n.tbDatasource-table td {\n padding: 12px;\n position: relative;\n box-sizing: border-box;\n}\n\n.tbDatasource-data-key {\n opacity: 0.7;\n font-weight: 400;\n font-size: 3.500rem;\n}\n\n.tbDatasource-value {\n font-size: 5.000rem;\n}",
- "controllerScript": "self.onInit = function() {\n\n self.ctx.labelPosition = self.ctx.settings.labelPosition || 'left';\n \n if (self.ctx.datasources.length > 0) {\n var tbDatasource = self.ctx.datasources[0];\n var datasourceId = 'tbDatasource' + 0;\n self.ctx.$container.append(\n \"\"\n );\n \n self.ctx.datasourceContainer = $('#' + datasourceId,\n self.ctx.$container);\n \n var tableId = 'table' + 0;\n self.ctx.datasourceContainer.append(\n \"\"\n );\n var table = $('#' + tableId, self.ctx.$container);\n if (self.ctx.labelPosition === 'top') {\n table.css('text-align', 'left');\n }\n \n if (tbDatasource.dataKeys.length > 0) {\n var dataKey = tbDatasource.dataKeys[0];\n var labelCellId = 'labelCell' + 0;\n var cellId = 'cell' + 0;\n if (self.ctx.labelPosition === 'left') {\n table.append(\n \"| \" +\n dataKey.label +\n \" | |
\");\n } else {\n table.append(\n \"| \" +\n dataKey.label +\n \" |
|
\");\n }\n self.ctx.labelCell = $('#' + labelCellId, table);\n self.ctx.valueCell = $('#' + cellId, table);\n self.ctx.valueCell.html(0 + ' ' + self.ctx.units);\n }\n }\n \n $.fn.textWidth = function(){\n var html_org = $(this).html();\n var html_calc = '' + html_org + '';\n $(this).html(html_calc);\n var width = $(this).find('span:first').width();\n $(this).html(html_org);\n return width;\n }; \n \n self.onResize();\n};\n\nself.onDataUpdated = function() {\n \n function isNumber(n) {\n return !isNaN(parseFloat(n)) && isFinite(n);\n }\n\n if (self.ctx.valueCell && self.ctx.data.length > 0) {\n var cellData = self.ctx.data[0];\n if (cellData.data.length > 0) {\n var tvPair = cellData.data[cellData.data.length -\n 1];\n var value = tvPair[1];\n var txtValue;\n if (isNumber(value)) {\n txtValue = self.ctx.utils.formatValue(value, self.ctx.decimals, self.ctx.units);\n } else {\n txtValue = value;\n }\n self.ctx.valueCell.html(txtValue);\n var targetWidth;\n var minDelta;\n if (self.ctx.labelPosition === 'left') {\n targetWidth = self.ctx.datasourceContainer.width() - self.ctx.labelCell.width();\n minDelta = self.ctx.width/16 + self.ctx.padding;\n } else {\n targetWidth = self.ctx.datasourceContainer.width();\n minDelta = self.ctx.padding;\n }\n var delta = targetWidth - self.ctx.valueCell.textWidth();\n var fontSize = self.ctx.valueFontSize;\n if (targetWidth > minDelta) {\n while (delta < minDelta && fontSize > 6) {\n fontSize--;\n self.ctx.valueCell.css('font-size', fontSize+'px');\n delta = targetWidth - self.ctx.valueCell.textWidth();\n }\n }\n }\n } \n \n};\n\nself.onResize = function() {\n var labelFontSize;\n if (self.ctx.labelPosition === 'top') {\n self.ctx.padding = self.ctx.height/20;\n labelFontSize = self.ctx.height/4;\n self.ctx.valueFontSize = self.ctx.height/2;\n } else {\n self.ctx.padding = self.ctx.width/50;\n labelFontSize = self.ctx.height/2.5;\n self.ctx.valueFontSize = self.ctx.height/2;\n if (self.ctx.width/self.ctx.height <= 2.7) {\n labelFontSize = self.ctx.width/7;\n self.ctx.valueFontSize = self.ctx.width/6;\n }\n }\n self.ctx.padding = Math.min(12, self.ctx.padding);\n \n if (self.ctx.labelCell) {\n self.ctx.labelCell.css('font-size', labelFontSize+'px');\n self.ctx.labelCell.css('padding', self.ctx.padding+'px');\n }\n if (self.ctx.valueCell) {\n self.ctx.valueCell.css('font-size', self.ctx.valueFontSize+'px');\n self.ctx.valueCell.css('padding', self.ctx.padding+'px');\n } \n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1\n };\n};\n\n\nself.onDestroy = function() {\n};\n",
+ "controllerScript": "self.onInit = function() {\n\n self.ctx.labelPosition = self.ctx.settings.labelPosition || 'left';\n \n if (self.ctx.datasources.length > 0) {\n var tbDatasource = self.ctx.datasources[0];\n var datasourceId = 'tbDatasource' + 0;\n self.ctx.$container.append(\n \"\"\n );\n \n self.ctx.datasourceContainer = $('#' + datasourceId,\n self.ctx.$container);\n \n var tableId = 'table' + 0;\n self.ctx.datasourceContainer.append(\n \"\"\n );\n var table = $('#' + tableId, self.ctx.$container);\n if (self.ctx.labelPosition === 'top') {\n table.css('text-align', 'left');\n }\n \n if (tbDatasource.dataKeys.length > 0) {\n var dataKey = tbDatasource.dataKeys[0];\n var labelCellId = 'labelCell' + 0;\n var cellId = 'cell' + 0;\n if (self.ctx.labelPosition === 'left') {\n table.append(\n \"| \" +\n dataKey.label +\n \" | |
\");\n } else {\n table.append(\n \"| \" +\n dataKey.label +\n \" |
|
\");\n }\n self.ctx.labelCell = $('#' + labelCellId, table);\n self.ctx.valueCell = $('#' + cellId, table);\n self.ctx.valueCell.html(0 + ' ' + self.ctx.units);\n }\n }\n \n $.fn.textWidth = function(){\n var html_org = $(this).html();\n var html_calc = '' + html_org + '';\n $(this).html(html_calc);\n var width = $(this).find('span:first').width();\n $(this).html(html_org);\n return width;\n }; \n \n self.onResize();\n};\n\nself.onDataUpdated = function() {\n \n function isNumber(n) {\n return !isNaN(parseFloat(n)) && isFinite(n);\n }\n\n if (self.ctx.valueCell && self.ctx.data.length > 0) {\n var cellData = self.ctx.data[0];\n if (cellData.data.length > 0) {\n var tvPair = cellData.data[cellData.data.length -\n 1];\n var value = tvPair[1];\n var txtValue;\n if (isNumber(value)) {\n var decimals = self.ctx.decimals;\n var units = self.ctx.units;\n if (self.ctx.datasources.length > 0 && self.ctx.datasources[0].dataKeys.length > 0) {\n dataKey = self.ctx.datasources[0].dataKeys[0];\n if (dataKey.decimals || dataKey.decimals === 0) {\n decimals = dataKey.decimals;\n }\n if (dataKey.units) {\n units = dataKey.units;\n }\n }\n txtValue = self.ctx.utils.formatValue(value, decimals, units, true);\n } else {\n txtValue = value;\n }\n self.ctx.valueCell.html(txtValue);\n var targetWidth;\n var minDelta;\n if (self.ctx.labelPosition === 'left') {\n targetWidth = self.ctx.datasourceContainer.width() - self.ctx.labelCell.width();\n minDelta = self.ctx.width/16 + self.ctx.padding;\n } else {\n targetWidth = self.ctx.datasourceContainer.width();\n minDelta = self.ctx.padding;\n }\n var delta = targetWidth - self.ctx.valueCell.textWidth();\n var fontSize = self.ctx.valueFontSize;\n if (targetWidth > minDelta) {\n while (delta < minDelta && fontSize > 6) {\n fontSize--;\n self.ctx.valueCell.css('font-size', fontSize+'px');\n delta = targetWidth - self.ctx.valueCell.textWidth();\n }\n }\n }\n } \n \n};\n\nself.onResize = function() {\n var labelFontSize;\n if (self.ctx.labelPosition === 'top') {\n self.ctx.padding = self.ctx.height/20;\n labelFontSize = self.ctx.height/4;\n self.ctx.valueFontSize = self.ctx.height/2;\n } else {\n self.ctx.padding = self.ctx.width/50;\n labelFontSize = self.ctx.height/2.5;\n self.ctx.valueFontSize = self.ctx.height/2;\n if (self.ctx.width/self.ctx.height <= 2.7) {\n labelFontSize = self.ctx.width/7;\n self.ctx.valueFontSize = self.ctx.width/6;\n }\n }\n self.ctx.padding = Math.min(12, self.ctx.padding);\n \n if (self.ctx.labelCell) {\n self.ctx.labelCell.css('font-size', labelFontSize+'px');\n self.ctx.labelCell.css('padding', self.ctx.padding+'px');\n }\n if (self.ctx.valueCell) {\n self.ctx.valueCell.css('font-size', self.ctx.valueFontSize+'px');\n self.ctx.valueCell.css('padding', self.ctx.padding+'px');\n } \n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1\n };\n};\n\n\nself.onDestroy = function() {\n};\n",
"settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"labelPosition\": {\n \"title\": \"Label position\",\n \"type\": \"string\",\n \"default\": \"left\"\n }\n },\n \"required\": []\n },\n \"form\": [\n {\n \"key\": \"labelPosition\",\n \"type\": \"rc-select\",\n \"multiple\": false,\n \"items\": [\n {\n \"value\": \"left\",\n \"label\": \"Left\"\n },\n {\n \"value\": \"top\",\n \"label\": \"Top\"\n }\n ]\n }\n ]\n}",
"dataKeySettingsSchema": "{}\n",
"defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temp\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#ff5722\",\"color\":\"rgba(255, 255, 255, 0.87)\",\"padding\":\"16px\",\"settings\":{\"labelPosition\":\"top\"},\"title\":\"Simple card\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"°C\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{}}"
diff --git a/application/src/main/data/json/system/widget_bundles/charts.json b/application/src/main/data/json/system/widget_bundles/charts.json
index a264ba6515..71e9fa7ace 100644
--- a/application/src/main/data/json/system/widget_bundles/charts.json
+++ b/application/src/main/data/json/system/widget_bundles/charts.json
@@ -35,7 +35,7 @@
"resources": [],
"templateHtml": "",
"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",
- "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;\n}\n\nself.getDataKeySettingsSchema = function() {\n return TbFlot.datakeySettingsSchema(true);\n}\n\nself.onDestroy = function() {\n self.ctx.flot.destroy();\n}\n",
+ "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);\n}\n\nself.onDestroy = function() {\n self.ctx.flot.destroy();\n}\n",
"settingsSchema": "{}",
"dataKeySettingsSchema": "{}",
"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}"
@@ -147,10 +147,10 @@
"resources": [],
"templateHtml": "",
"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",
- "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;\n}\n\nself.getDataKeySettingsSchema = function() {\n return TbFlot.datakeySettingsSchema(false);\n}\n\nself.onDestroy = function() {\n self.ctx.flot.destroy();\n}\n",
+ "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);\n}\n\nself.onDestroy = function() {\n self.ctx.flot.destroy();\n}\n",
"settingsSchema": "{}",
"dataKeySettingsSchema": "{}",
- "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},\"legend\":{\"show\":true,\"position\":\"nw\",\"backgroundColor\":\"#f0f0f0\",\"backgroundOpacity\":0.85,\"labelBoxBorderColor\":\"rgba(1, 1, 1, 0.45)\"},\"decimals\":1,\"stack\":true,\"tooltipIndividual\":false},\"title\":\"Timeseries Bars - Flot\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null}"
+ "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\":{}}"
}
},
{
@@ -163,7 +163,7 @@
"resources": [],
"templateHtml": "",
"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",
- "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;\n}\n\nself.getDataKeySettingsSchema = function() {\n return TbFlot.datakeySettingsSchema(true);\n}\n\nself.onDestroy = function() {\n self.ctx.flot.destroy();\n}\n",
+ "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);\n}\n\nself.onDestroy = function() {\n self.ctx.flot.destroy();\n}\n",
"settingsSchema": "{}",
"dataKeySettingsSchema": "{}",
"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\":{\"position\":\"bottom\",\"showMin\":false,\"showMax\":false,\"showAvg\":false,\"showTotal\":false}}"
diff --git a/application/src/main/data/upgrade/2.1.1/schema_update.cql b/application/src/main/data/upgrade/2.1.1/schema_update.cql
new file mode 100644
index 0000000000..36ac8e4d38
--- /dev/null
+++ b/application/src/main/data/upgrade/2.1.1/schema_update.cql
@@ -0,0 +1,81 @@
+--
+-- Copyright © 2016-2018 The Thingsboard Authors
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+
+DROP MATERIALIZED VIEW IF EXISTS thingsboard.entity_view_by_tenant_and_name;
+DROP MATERIALIZED VIEW IF EXISTS thingsboard.entity_view_by_tenant_and_search_text;
+DROP MATERIALIZED VIEW IF EXISTS thingsboard.entity_view_by_tenant_and_customer;
+DROP MATERIALIZED VIEW IF EXISTS thingsboard.entity_view_by_tenant_and_entity_id;
+
+DROP TABLE IF EXISTS thingsboard.entity_views;
+
+CREATE TABLE IF NOT EXISTS thingsboard.entity_views (
+ id timeuuid,
+ entity_id timeuuid,
+ entity_type text,
+ tenant_id timeuuid,
+ customer_id timeuuid,
+ name text,
+ keys text,
+ start_ts bigint,
+ end_ts bigint,
+ search_text text,
+ additional_info text,
+ PRIMARY KEY (id, entity_id, tenant_id, customer_id)
+);
+
+CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_view_by_tenant_and_name AS
+ SELECT *
+ from thingsboard.entity_views
+ WHERE tenant_id IS NOT NULL
+ AND entity_id IS NOT NULL
+ AND customer_id IS NOT NULL
+ AND name IS NOT NULL
+ AND id IS NOT NULL
+ PRIMARY KEY (tenant_id, name, id, customer_id, entity_id)
+ WITH CLUSTERING ORDER BY (name ASC, id DESC, customer_id DESC);
+
+CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_view_by_tenant_and_search_text AS
+ SELECT *
+ from thingsboard.entity_views
+ WHERE tenant_id IS NOT NULL
+ AND entity_id IS NOT NULL
+ AND customer_id IS NOT NULL
+ AND search_text IS NOT NULL
+ AND id IS NOT NULL
+ PRIMARY KEY (tenant_id, search_text, id, customer_id, entity_id)
+ WITH CLUSTERING ORDER BY (search_text ASC, id DESC, customer_id DESC);
+
+CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_view_by_tenant_and_customer AS
+ SELECT *
+ from thingsboard.entity_views
+ WHERE tenant_id IS NOT NULL
+ AND customer_id IS NOT NULL
+ AND entity_id IS NOT NULL
+ AND search_text IS NOT NULL
+ AND id IS NOT NULL
+ PRIMARY KEY (tenant_id, customer_id, search_text, id, entity_id)
+ WITH CLUSTERING ORDER BY (customer_id DESC, search_text ASC, id DESC);
+
+CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_view_by_tenant_and_entity_id AS
+ SELECT *
+ from thingsboard.entity_views
+ WHERE tenant_id IS NOT NULL
+ AND customer_id IS NOT NULL
+ AND entity_id IS NOT NULL
+ AND search_text IS NOT NULL
+ AND id IS NOT NULL
+ PRIMARY KEY (tenant_id, entity_id, customer_id, search_text, id)
+ WITH CLUSTERING ORDER BY (entity_id DESC, customer_id DESC, search_text ASC, id DESC);
\ No newline at end of file
diff --git a/application/src/main/data/upgrade/2.1.1/schema_update.sql b/application/src/main/data/upgrade/2.1.1/schema_update.sql
new file mode 100644
index 0000000000..bd2c341e7f
--- /dev/null
+++ b/application/src/main/data/upgrade/2.1.1/schema_update.sql
@@ -0,0 +1,31 @@
+--
+-- Copyright © 2016-2018 The Thingsboard Authors
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+
+DROP TABLE IF EXISTS entity_views;
+
+CREATE TABLE IF NOT EXISTS entity_views (
+ id varchar(31) NOT NULL CONSTRAINT entity_view_pkey PRIMARY KEY,
+ entity_id varchar(31),
+ entity_type varchar(255),
+ tenant_id varchar(31),
+ customer_id varchar(31),
+ name varchar(255),
+ keys varchar(255),
+ start_ts bigint,
+ end_ts bigint,
+ search_text varchar(255),
+ additional_info varchar
+);
diff --git a/application/src/main/data/upgrade/2.1.2/schema_update.cql b/application/src/main/data/upgrade/2.1.2/schema_update.cql
new file mode 100644
index 0000000000..c02571ada4
--- /dev/null
+++ b/application/src/main/data/upgrade/2.1.2/schema_update.cql
@@ -0,0 +1,110 @@
+--
+-- Copyright © 2016-2018 The Thingsboard Authors
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+
+DROP MATERIALIZED VIEW IF EXISTS thingsboard.entity_view_by_tenant_and_name;
+DROP MATERIALIZED VIEW IF EXISTS thingsboard.entity_view_by_tenant_and_search_text;
+DROP MATERIALIZED VIEW IF EXISTS thingsboard.entity_view_by_tenant_and_customer;
+DROP MATERIALIZED VIEW IF EXISTS thingsboard.entity_view_by_tenant_and_entity_id;
+
+DROP TABLE IF EXISTS thingsboard.entity_views;
+
+CREATE TABLE IF NOT EXISTS thingsboard.entity_view (
+ id timeuuid,
+ entity_id timeuuid,
+ entity_type text,
+ tenant_id timeuuid,
+ customer_id timeuuid,
+ name text,
+ type text,
+ keys text,
+ start_ts bigint,
+ end_ts bigint,
+ search_text text,
+ additional_info text,
+ PRIMARY KEY (id, entity_id, tenant_id, customer_id, type)
+);
+
+CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_view_by_tenant_and_name AS
+ SELECT *
+ from thingsboard.entity_view
+ WHERE tenant_id IS NOT NULL
+ AND entity_id IS NOT NULL
+ AND customer_id IS NOT NULL
+ AND type IS NOT NULL
+ AND name IS NOT NULL
+ AND id IS NOT NULL
+ PRIMARY KEY (tenant_id, name, id, customer_id, entity_id, type)
+ WITH CLUSTERING ORDER BY (name ASC, id DESC, customer_id DESC);
+
+CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_view_by_tenant_and_search_text AS
+ SELECT *
+ from thingsboard.entity_view
+ WHERE tenant_id IS NOT NULL
+ AND entity_id IS NOT NULL
+ AND customer_id IS NOT NULL
+ AND type IS NOT NULL
+ AND search_text IS NOT NULL
+ AND id IS NOT NULL
+ PRIMARY KEY (tenant_id, search_text, id, customer_id, entity_id, type)
+ WITH CLUSTERING ORDER BY (search_text ASC, id DESC, customer_id DESC);
+
+CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_view_by_tenant_by_type_and_search_text AS
+ SELECT *
+ from thingsboard.entity_view
+ WHERE tenant_id IS NOT NULL
+ AND entity_id IS NOT NULL
+ AND customer_id IS NOT NULL
+ AND type IS NOT NULL
+ AND search_text IS NOT NULL
+ AND id IS NOT NULL
+ PRIMARY KEY (tenant_id, type, search_text, id, customer_id, entity_id)
+ WITH CLUSTERING ORDER BY (type ASC, search_text ASC, id DESC, customer_id DESC);
+
+CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_view_by_tenant_and_customer AS
+ SELECT *
+ from thingsboard.entity_view
+ WHERE tenant_id IS NOT NULL
+ AND customer_id IS NOT NULL
+ AND entity_id IS NOT NULL
+ AND type IS NOT NULL
+ AND search_text IS NOT NULL
+ AND id IS NOT NULL
+ PRIMARY KEY (tenant_id, customer_id, search_text, id, entity_id, type)
+ WITH CLUSTERING ORDER BY (customer_id DESC, search_text ASC, id DESC);
+
+CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_view_by_tenant_and_customer_and_type AS
+ SELECT *
+ from thingsboard.entity_view
+ WHERE tenant_id IS NOT NULL
+ AND customer_id IS NOT NULL
+ AND entity_id IS NOT NULL
+ AND type IS NOT NULL
+ AND search_text IS NOT NULL
+ AND id IS NOT NULL
+ PRIMARY KEY (tenant_id, type, customer_id, search_text, id, entity_id)
+ WITH CLUSTERING ORDER BY (type ASC, customer_id DESC, search_text ASC, id DESC);
+
+CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_view_by_tenant_and_entity_id AS
+ SELECT *
+ from thingsboard.entity_view
+ WHERE tenant_id IS NOT NULL
+ AND customer_id IS NOT NULL
+ AND entity_id IS NOT NULL
+ AND type IS NOT NULL
+ AND search_text IS NOT NULL
+ AND id IS NOT NULL
+ PRIMARY KEY (tenant_id, entity_id, customer_id, search_text, id, type)
+ WITH CLUSTERING ORDER BY (entity_id DESC, customer_id DESC, search_text ASC, id DESC);
\ No newline at end of file
diff --git a/application/src/main/data/upgrade/2.1.2/schema_update.sql b/application/src/main/data/upgrade/2.1.2/schema_update.sql
new file mode 100644
index 0000000000..14b717c07c
--- /dev/null
+++ b/application/src/main/data/upgrade/2.1.2/schema_update.sql
@@ -0,0 +1,32 @@
+--
+-- Copyright © 2016-2018 The Thingsboard Authors
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+
+DROP TABLE IF EXISTS entity_views;
+
+CREATE TABLE IF NOT EXISTS entity_view (
+ id varchar(31) NOT NULL CONSTRAINT entity_view_pkey PRIMARY KEY,
+ entity_id varchar(31),
+ entity_type varchar(255),
+ tenant_id varchar(31),
+ customer_id varchar(31),
+ type varchar(255),
+ name varchar(255),
+ keys varchar(255),
+ start_ts bigint,
+ end_ts bigint,
+ search_text varchar(255),
+ additional_info varchar
+);
diff --git a/application/src/main/data/upgrade/2.2.0/schema_update.sql b/application/src/main/data/upgrade/2.2.0/schema_update.sql
new file mode 100644
index 0000000000..1832b7901b
--- /dev/null
+++ b/application/src/main/data/upgrade/2.2.0/schema_update.sql
@@ -0,0 +1,17 @@
+--
+-- Copyright © 2016-2018 The Thingsboard Authors
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+
+ALTER TABLE component_descriptor ADD UNIQUE (clazz);
diff --git a/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java b/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java
index d33734e14c..091b504ea7 100644
--- a/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java
+++ b/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java
@@ -31,6 +31,7 @@ import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import org.thingsboard.rule.engine.api.MailService;
import org.thingsboard.server.actors.service.ActorService;
@@ -48,6 +49,7 @@ import org.thingsboard.server.dao.attributes.AttributesService;
import org.thingsboard.server.dao.audit.AuditLogService;
import org.thingsboard.server.dao.customer.CustomerService;
import org.thingsboard.server.dao.device.DeviceService;
+import org.thingsboard.server.dao.entityview.EntityViewService;
import org.thingsboard.server.dao.event.EventService;
import org.thingsboard.server.dao.relation.RelationService;
import org.thingsboard.server.dao.rule.RuleChainService;
@@ -62,12 +64,13 @@ import org.thingsboard.server.service.encoding.DataDecodingEncodingService;
import org.thingsboard.server.service.executors.DbCallbackExecutorService;
import org.thingsboard.server.service.executors.ExternalCallExecutorService;
import org.thingsboard.server.service.mail.MailExecutorService;
-import org.thingsboard.server.service.queue.MsgQueueService;
import org.thingsboard.server.service.rpc.DeviceRpcService;
import org.thingsboard.server.service.script.JsExecutorService;
-import org.thingsboard.server.service.script.JsSandboxService;
+import org.thingsboard.server.service.script.JsInvokeService;
+import org.thingsboard.server.service.session.DeviceSessionCacheService;
import org.thingsboard.server.service.state.DeviceStateService;
import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService;
+import org.thingsboard.server.service.transport.RuleEngineTransportService;
import javax.annotation.Nullable;
import java.io.IOException;
@@ -159,6 +162,10 @@ public class ActorSystemContext {
@Getter
private AuditLogService auditLogService;
+ @Autowired
+ @Getter
+ private EntityViewService entityViewService;
+
@Autowired
@Getter
private TelemetrySubscriptionService tsSubService;
@@ -169,7 +176,7 @@ public class ActorSystemContext {
@Autowired
@Getter
- private JsSandboxService jsSandbox;
+ private JsInvokeService jsSandbox;
@Autowired
@Getter
@@ -193,11 +200,16 @@ public class ActorSystemContext {
@Autowired
@Getter
- private MsgQueueService msgQueueService;
+ private DeviceStateService deviceStateService;
+
+ @Autowired
+ @Getter
+ private DeviceSessionCacheService deviceSessionCacheService;
+ @Lazy
@Autowired
@Getter
- private DeviceStateService deviceStateService;
+ private RuleEngineTransportService ruleEngineTransportService;
@Value("${cluster.partition_id}")
@Getter
@@ -247,17 +259,21 @@ public class ActorSystemContext {
@Getter
private boolean allowSystemMailService;
+ @Value("${transport.sessions.inactivity_timeout}")
@Getter
- @Setter
- private ActorSystem actorSystem;
+ private long sessionInactivityTimeout;
+
+ @Value("${transport.sessions.report_timeout}")
+ @Getter
+ private long sessionReportTimeout;
@Getter
@Setter
- private ActorRef appActor;
+ private ActorSystem actorSystem;
@Getter
@Setter
- private ActorRef sessionManagerActor;
+ private ActorRef appActor;
@Getter
@Setter
diff --git a/application/src/main/java/org/thingsboard/server/actors/app/AppActor.java b/application/src/main/java/org/thingsboard/server/actors/app/AppActor.java
index 6a78f78d1b..714ab577af 100644
--- a/application/src/main/java/org/thingsboard/server/actors/app/AppActor.java
+++ b/application/src/main/java/org/thingsboard/server/actors/app/AppActor.java
@@ -25,21 +25,24 @@ import akka.actor.Terminated;
import akka.event.Logging;
import akka.event.LoggingAdapter;
import akka.japi.Function;
+import com.google.common.collect.BiMap;
+import com.google.common.collect.HashBiMap;
+import lombok.extern.slf4j.Slf4j;
import org.thingsboard.server.actors.ActorSystemContext;
import org.thingsboard.server.actors.ruleChain.RuleChainManagerActor;
import org.thingsboard.server.actors.service.ContextBasedCreator;
import org.thingsboard.server.actors.service.DefaultActorService;
import org.thingsboard.server.actors.shared.rulechain.SystemRuleChainManager;
import org.thingsboard.server.actors.tenant.TenantActor;
+import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageDataIterable;
+import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
import org.thingsboard.server.common.msg.TbActorMsg;
import org.thingsboard.server.common.msg.aware.TenantAwareMsg;
import org.thingsboard.server.common.msg.cluster.SendToClusterMsg;
import org.thingsboard.server.common.msg.cluster.ServerAddress;
-import org.thingsboard.server.common.msg.core.BasicActorSystemToDeviceSessionActorMsg;
-import org.thingsboard.server.common.msg.device.DeviceToDeviceActorMsg;
import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg;
import org.thingsboard.server.dao.model.ModelConstants;
@@ -52,16 +55,14 @@ import java.util.Optional;
public class AppActor extends RuleChainManagerActor {
- private final LoggingAdapter logger = Logging.getLogger(getContext().system(), this);
-
- public static final TenantId SYSTEM_TENANT = new TenantId(ModelConstants.NULL_UUID);
+ private static final TenantId SYSTEM_TENANT = new TenantId(ModelConstants.NULL_UUID);
private final TenantService tenantService;
- private final Map tenantActors;
+ private final BiMap tenantActors;
private AppActor(ActorSystemContext systemContext) {
super(systemContext, new SystemRuleChainManager(systemContext));
this.tenantService = systemContext.getTenantService();
- this.tenantActors = new HashMap<>();
+ this.tenantActors = HashBiMap.create();
}
@Override
@@ -71,22 +72,20 @@ public class AppActor extends RuleChainManagerActor {
@Override
public void preStart() {
- logger.info("Starting main system actor.");
+ log.info("Starting main system actor.");
try {
initRuleChains();
-
if (systemContext.isTenantComponentsInitEnabled()) {
PageDataIterable tenantIterator = new PageDataIterable<>(tenantService::findTenants, ENTITY_PACK_LIMIT);
for (Tenant tenant : tenantIterator) {
- logger.debug("[{}] Creating tenant actor", tenant.getId());
+ log.debug("[{}] Creating tenant actor", tenant.getId());
getOrCreateTenantActor(tenant.getId());
- logger.debug("Tenant actor created.");
+ log.debug("Tenant actor created.");
}
}
-
- logger.info("Main system actor started.");
+ log.info("Main system actor started.");
} catch (Exception e) {
- logger.error(e, "Unknown failure");
+ log.warn("Unknown failure", e);
}
}
@@ -105,7 +104,7 @@ public class AppActor extends RuleChainManagerActor {
case SERVICE_TO_RULE_ENGINE_MSG:
onServiceToRuleEngineMsg((ServiceToRuleEngineMsg) msg);
break;
- case DEVICE_SESSION_TO_DEVICE_ACTOR_MSG:
+ case TRANSPORT_TO_DEVICE_ACTOR_MSG:
case DEVICE_ATTRIBUTES_UPDATE_TO_DEVICE_ACTOR_MSG:
case DEVICE_CREDENTIALS_UPDATE_TO_DEVICE_ACTOR_MSG:
case DEVICE_NAME_OR_TYPE_UPDATE_TO_DEVICE_ACTOR_MSG:
@@ -114,19 +113,12 @@ public class AppActor extends RuleChainManagerActor {
case REMOTE_TO_RULE_CHAIN_TELL_NEXT_MSG:
onToDeviceActorMsg((TenantAwareMsg) msg);
break;
- case ACTOR_SYSTEM_TO_DEVICE_SESSION_ACTOR_MSG:
- onToDeviceSessionMsg((BasicActorSystemToDeviceSessionActorMsg) msg);
- break;
default:
return false;
}
return true;
}
- private void onToDeviceSessionMsg(BasicActorSystemToDeviceSessionActorMsg msg) {
- systemContext.getSessionManagerActor().tell(msg, self());
- }
-
private void onPossibleClusterMsg(SendToClusterMsg msg) {
Optional address = systemContext.getRoutingService().resolveById(msg.getEntityId());
if (address.isPresent()) {
@@ -139,7 +131,8 @@ public class AppActor extends RuleChainManagerActor {
private void onServiceToRuleEngineMsg(ServiceToRuleEngineMsg msg) {
if (SYSTEM_TENANT.equals(msg.getTenantId())) {
- //TODO: ashvayka handle this.
+// this may be a notification about system entities created.
+// log.warn("[{}] Invalid service to rule engine msg called. System messages are not supported yet: {}", SYSTEM_TENANT, msg);
} else {
getOrCreateTenantActor(msg.getTenantId()).tell(msg, self());
}
@@ -152,16 +145,26 @@ public class AppActor extends RuleChainManagerActor {
}
private void onComponentLifecycleMsg(ComponentLifecycleMsg msg) {
- ActorRef target;
+ ActorRef target = null;
if (SYSTEM_TENANT.equals(msg.getTenantId())) {
target = getEntityActorRef(msg.getEntityId());
} else {
- target = getOrCreateTenantActor(msg.getTenantId());
+ if (msg.getEntityId().getEntityType() == EntityType.TENANT
+ && msg.getEvent() == ComponentLifecycleEvent.DELETED) {
+ log.debug("[{}] Handling tenant deleted notification: {}", msg.getTenantId(), msg);
+ ActorRef tenantActor = tenantActors.remove(new TenantId(msg.getEntityId().getId()));
+ if (tenantActor != null) {
+ log.debug("[{}] Deleting tenant actor: {}", msg.getTenantId(), tenantActor);
+ context().stop(tenantActor);
+ }
+ } else {
+ target = getOrCreateTenantActor(msg.getTenantId());
+ }
}
if (target != null) {
target.tell(msg, ActorRef.noSender());
} else {
- logger.debug("Invalid component lifecycle msg: {}", msg);
+ log.debug("[{}] Invalid component lifecycle msg: {}", msg.getTenantId(), msg);
}
}
@@ -169,25 +172,25 @@ public class AppActor extends RuleChainManagerActor {
getOrCreateTenantActor(msg.getTenantId()).tell(msg, ActorRef.noSender());
}
- private void processDeviceMsg(DeviceToDeviceActorMsg deviceToDeviceActorMsg) {
- TenantId tenantId = deviceToDeviceActorMsg.getTenantId();
- ActorRef tenantActor = getOrCreateTenantActor(tenantId);
- if (deviceToDeviceActorMsg.getPayload().getMsgType().requiresRulesProcessing()) {
-// tenantActor.tell(new RuleChainDeviceMsg(deviceToDeviceActorMsg, ruleManager.getRuleChain(this.context())), context().self());
- } else {
- tenantActor.tell(deviceToDeviceActorMsg, context().self());
- }
- }
-
private ActorRef getOrCreateTenantActor(TenantId tenantId) {
- return tenantActors.computeIfAbsent(tenantId, k -> context().actorOf(Props.create(new TenantActor.ActorCreator(systemContext, tenantId))
- .withDispatcher(DefaultActorService.CORE_DISPATCHER_NAME), tenantId.toString()));
+ return tenantActors.computeIfAbsent(tenantId, k -> {
+ log.debug("[{}] Creating tenant actor.", tenantId);
+ ActorRef tenantActor = context().actorOf(Props.create(new TenantActor.ActorCreator(systemContext, tenantId))
+ .withDispatcher(DefaultActorService.CORE_DISPATCHER_NAME), tenantId.toString());
+ context().watch(tenantActor);
+ log.debug("[{}] Created tenant actor: {}.", tenantId, tenantActor);
+ return tenantActor;
+ });
}
- private void processTermination(Terminated message) {
+ @Override
+ protected void processTermination(Terminated message) {
ActorRef terminated = message.actor();
if (terminated instanceof LocalActorRef) {
- logger.debug("Removed actor: {}", terminated);
+ boolean removed = tenantActors.inverse().remove(terminated) != null;
+ if (removed) {
+ log.debug("[{}] Removed actor:", terminated);
+ }
} else {
throw new IllegalStateException("Remote actors are not supported!");
}
@@ -201,20 +204,17 @@ public class AppActor extends RuleChainManagerActor {
}
@Override
- public AppActor create() throws Exception {
+ public AppActor create() {
return new AppActor(context);
}
}
- private final SupervisorStrategy strategy = new OneForOneStrategy(3, Duration.create("1 minute"), new Function() {
- @Override
- public Directive apply(Throwable t) {
- logger.error(t, "Unknown failure");
- if (t instanceof RuntimeException) {
- return SupervisorStrategy.restart();
- } else {
- return SupervisorStrategy.stop();
- }
+ private final SupervisorStrategy strategy = new OneForOneStrategy(3, Duration.create("1 minute"), t -> {
+ log.warn("Unknown failure", t);
+ if (t instanceof RuntimeException) {
+ return SupervisorStrategy.restart();
+ } else {
+ return SupervisorStrategy.stop();
}
});
}
diff --git a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActor.java b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActor.java
index 99d004574e..f53a410d5f 100644
--- a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActor.java
+++ b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActor.java
@@ -15,43 +15,44 @@
*/
package org.thingsboard.server.actors.device;
-import akka.event.Logging;
-import akka.event.LoggingAdapter;
import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg;
import org.thingsboard.rule.engine.api.msg.DeviceNameOrTypeUpdateMsg;
import org.thingsboard.server.actors.ActorSystemContext;
import org.thingsboard.server.actors.service.ContextAwareActor;
-import org.thingsboard.server.actors.service.ContextBasedCreator;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.msg.TbActorMsg;
-import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;
-import org.thingsboard.server.common.msg.device.DeviceToDeviceActorMsg;
import org.thingsboard.server.common.msg.timeout.DeviceActorClientSideRpcTimeoutMsg;
-import org.thingsboard.server.common.msg.timeout.DeviceActorQueueTimeoutMsg;
import org.thingsboard.server.common.msg.timeout.DeviceActorServerSideRpcTimeoutMsg;
import org.thingsboard.server.service.rpc.ToDeviceRpcRequestActorMsg;
import org.thingsboard.server.service.rpc.ToServerRpcResponseActorMsg;
+import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper;
public class DeviceActor extends ContextAwareActor {
- private final LoggingAdapter logger = Logging.getLogger(getContext().system(), this);
-
private final DeviceActorMessageProcessor processor;
- private DeviceActor(ActorSystemContext systemContext, TenantId tenantId, DeviceId deviceId) {
+ DeviceActor(ActorSystemContext systemContext, TenantId tenantId, DeviceId deviceId) {
super(systemContext);
- this.processor = new DeviceActorMessageProcessor(systemContext, logger, tenantId, deviceId);
+ this.processor = new DeviceActorMessageProcessor(systemContext, tenantId, deviceId);
+ }
+
+ @Override
+ public void preStart() {
+ log.debug("[{}][{}] Starting device actor.", processor.tenantId, processor.deviceId);
+ try {
+ processor.initSessionTimeout(context());
+ log.debug("[{}][{}] Device actor started.", processor.tenantId, processor.deviceId);
+ } catch (Exception e) {
+ log.warn("[{}][{}] Unknown failure", processor.tenantId, processor.deviceId, e);
+ }
}
@Override
protected boolean process(TbActorMsg msg) {
switch (msg.getMsgType()) {
- case CLUSTER_EVENT_MSG:
- processor.processClusterEventMsg((ClusterEventMsg) msg);
- break;
- case DEVICE_SESSION_TO_DEVICE_ACTOR_MSG:
- processor.process(context(), (DeviceToDeviceActorMsg) msg);
+ case TRANSPORT_TO_DEVICE_ACTOR_MSG:
+ processor.process(context(), (TransportToDeviceActorMsgWrapper) msg);
break;
case DEVICE_ATTRIBUTES_UPDATE_TO_DEVICE_ACTOR_MSG:
processor.processAttributesUpdate(context(), (DeviceAttributesEventNotificationMsg) msg);
@@ -74,11 +75,8 @@ public class DeviceActor extends ContextAwareActor {
case DEVICE_ACTOR_CLIENT_SIDE_RPC_TIMEOUT_MSG:
processor.processClientSideRpcTimeout(context(), (DeviceActorClientSideRpcTimeoutMsg) msg);
break;
- case DEVICE_ACTOR_QUEUE_TIMEOUT_MSG:
- processor.processQueueTimeout(context(), (DeviceActorQueueTimeoutMsg) msg);
- break;
- case RULE_ENGINE_QUEUE_PUT_ACK_MSG:
- processor.processQueueAck(context(), (RuleEngineQueuePutAckMsg) msg);
+ case SESSION_TIMEOUT_MSG:
+ processor.checkSessionsTimeout();
break;
default:
return false;
@@ -86,22 +84,4 @@ public class DeviceActor extends ContextAwareActor {
return true;
}
- public static class ActorCreator extends ContextBasedCreator {
- private static final long serialVersionUID = 1L;
-
- private final TenantId tenantId;
- private final DeviceId deviceId;
-
- public ActorCreator(ActorSystemContext context, TenantId tenantId, DeviceId deviceId) {
- super(context);
- this.tenantId = tenantId;
- this.deviceId = deviceId;
- }
-
- @Override
- public DeviceActor create() throws Exception {
- return new DeviceActor(context, tenantId, deviceId);
- }
- }
-
}
diff --git a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorCreator.java b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorCreator.java
new file mode 100644
index 0000000000..18aa9268bd
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorCreator.java
@@ -0,0 +1,39 @@
+/**
+ * Copyright © 2016-2018 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.actors.device;
+
+import org.thingsboard.server.actors.ActorSystemContext;
+import org.thingsboard.server.actors.service.ContextBasedCreator;
+import org.thingsboard.server.common.data.id.DeviceId;
+import org.thingsboard.server.common.data.id.TenantId;
+
+public class DeviceActorCreator extends ContextBasedCreator {
+ private static final long serialVersionUID = 1L;
+
+ private final TenantId tenantId;
+ private final DeviceId deviceId;
+
+ public DeviceActorCreator(ActorSystemContext context, TenantId tenantId, DeviceId deviceId) {
+ super(context);
+ this.tenantId = tenantId;
+ this.deviceId = deviceId;
+ }
+
+ @Override
+ public DeviceActor create() {
+ return new DeviceActor(context, tenantId, deviceId);
+ }
+}
diff --git a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java
index a2ea0487cf..41d1ea08c6 100644
--- a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java
+++ b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java
@@ -16,7 +16,6 @@
package org.thingsboard.server.actors.device;
import akka.actor.ActorContext;
-import akka.actor.ActorRef;
import akka.event.LoggingAdapter;
import com.datastax.driver.core.utils.UUIDs;
import com.google.common.util.concurrent.FutureCallback;
@@ -25,6 +24,7 @@ import com.google.common.util.concurrent.ListenableFuture;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
+import lombok.extern.slf4j.Slf4j;
import org.thingsboard.rule.engine.api.RpcError;
import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg;
import org.thingsboard.rule.engine.api.msg.DeviceNameOrTypeUpdateMsg;
@@ -33,7 +33,6 @@ import org.thingsboard.server.actors.shared.AbstractContextAwareMsgProcessor;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.id.DeviceId;
-import org.thingsboard.server.common.data.id.SessionId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.kv.AttributeKey;
import org.thingsboard.server.common.data.kv.AttributeKvEntry;
@@ -43,37 +42,34 @@ import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbMsgDataType;
import org.thingsboard.server.common.msg.TbMsgMetaData;
import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;
-import org.thingsboard.server.common.msg.cluster.ServerAddress;
-import org.thingsboard.server.common.msg.core.ActorSystemToDeviceSessionActorMsg;
-import org.thingsboard.server.common.msg.core.AttributesUpdateNotification;
-import org.thingsboard.server.common.msg.core.AttributesUpdateRequest;
-import org.thingsboard.server.common.msg.core.BasicActorSystemToDeviceSessionActorMsg;
-import org.thingsboard.server.common.msg.core.BasicCommandAckResponse;
-import org.thingsboard.server.common.msg.core.BasicGetAttributesResponse;
-import org.thingsboard.server.common.msg.core.BasicStatusCodeResponse;
-import org.thingsboard.server.common.msg.core.GetAttributesRequest;
-import org.thingsboard.server.common.msg.core.RuleEngineError;
-import org.thingsboard.server.common.msg.core.RuleEngineErrorMsg;
-import org.thingsboard.server.common.msg.core.SessionCloseMsg;
-import org.thingsboard.server.common.msg.core.SessionCloseNotification;
-import org.thingsboard.server.common.msg.core.SessionOpenMsg;
-import org.thingsboard.server.common.msg.core.TelemetryUploadRequest;
-import org.thingsboard.server.common.msg.core.ToDeviceRpcRequestMsg;
-import org.thingsboard.server.common.msg.core.ToDeviceRpcResponseMsg;
-import org.thingsboard.server.common.msg.core.ToServerRpcRequestMsg;
-import org.thingsboard.server.common.msg.device.DeviceToDeviceActorMsg;
-import org.thingsboard.server.common.msg.kv.BasicAttributeKVMsg;
import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest;
-import org.thingsboard.server.common.msg.session.FromDeviceMsg;
import org.thingsboard.server.common.msg.session.SessionMsgType;
-import org.thingsboard.server.common.msg.session.SessionType;
-import org.thingsboard.server.common.msg.session.ToDeviceMsg;
import org.thingsboard.server.common.msg.timeout.DeviceActorClientSideRpcTimeoutMsg;
-import org.thingsboard.server.common.msg.timeout.DeviceActorQueueTimeoutMsg;
import org.thingsboard.server.common.msg.timeout.DeviceActorServerSideRpcTimeoutMsg;
+import org.thingsboard.server.gen.transport.TransportProtos;
+import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.DeviceActorToTransportMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeRequestMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeResponseMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.KeyValueProto;
+import org.thingsboard.server.gen.transport.TransportProtos.KeyValueType;
+import org.thingsboard.server.gen.transport.TransportProtos.PostAttributeMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.PostTelemetryMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.SessionCloseNotificationProto;
+import org.thingsboard.server.gen.transport.TransportProtos.SessionEvent;
+import org.thingsboard.server.gen.transport.TransportProtos.SessionEventMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto;
+import org.thingsboard.server.gen.transport.TransportProtos.SubscribeToAttributeUpdatesMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.SubscribeToRPCMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcRequestMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcResponseMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.TransportToDeviceActorMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.TsKvListProto;
+import org.thingsboard.server.gen.transport.TransportProtos.TsKvProto;
import org.thingsboard.server.service.rpc.FromDeviceRpcResponse;
import org.thingsboard.server.service.rpc.ToDeviceRpcRequestActorMsg;
import org.thingsboard.server.service.rpc.ToServerRpcResponseActorMsg;
+import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper;
import javax.annotation.Nullable;
import java.util.ArrayList;
@@ -87,24 +83,22 @@ import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
-import java.util.concurrent.TimeoutException;
import java.util.function.Consumer;
-import java.util.function.Predicate;
import java.util.stream.Collectors;
/**
* @author Andrew Shvayka
*/
-public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
-
- private final TenantId tenantId;
- private final DeviceId deviceId;
- private final Map sessions;
- private final Map attributeSubscriptions;
- private final Map rpcSubscriptions;
+@Slf4j
+class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
+
+ final TenantId tenantId;
+ final DeviceId deviceId;
+ private final Map sessions;
+ private final Map attributeSubscriptions;
+ private final Map rpcSubscriptions;
private final Map toDeviceRpcPendingMap;
private final Map toServerRpcPendingMap;
- private final Map pendingMsgs;
private final Gson gson = new Gson();
private final JsonParser jsonParser = new JsonParser();
@@ -114,8 +108,8 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
private String deviceType;
private TbMsgMetaData defaultMetaData;
- DeviceActorMessageProcessor(ActorSystemContext systemContext, LoggingAdapter logger, TenantId tenantId, DeviceId deviceId) {
- super(systemContext, logger);
+ DeviceActorMessageProcessor(ActorSystemContext systemContext, TenantId tenantId, DeviceId deviceId) {
+ super(systemContext);
this.tenantId = tenantId;
this.deviceId = deviceId;
this.sessions = new LinkedHashMap<>();
@@ -123,8 +117,8 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
this.rpcSubscriptions = new HashMap<>();
this.toDeviceRpcPendingMap = new HashMap<>();
this.toServerRpcPendingMap = new HashMap<>();
- this.pendingMsgs = new HashMap<>();
initAttributes();
+ restoreSessions();
}
private void initAttributes() {
@@ -139,41 +133,36 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
void processRpcRequest(ActorContext context, ToDeviceRpcRequestActorMsg msg) {
ToDeviceRpcRequest request = msg.getMsg();
ToDeviceRpcRequestBody body = request.getBody();
- ToDeviceRpcRequestMsg rpcRequest = new ToDeviceRpcRequestMsg(
- rpcSeq++,
- body.getMethod(),
- body.getParams()
- );
+ ToDeviceRpcRequestMsg rpcRequest = ToDeviceRpcRequestMsg.newBuilder().setRequestId(
+ rpcSeq++).setMethodName(body.getMethod()).setParams(body.getParams()).build();
long timeout = request.getExpirationTime() - System.currentTimeMillis();
if (timeout <= 0) {
- logger.debug("[{}][{}] Ignoring message due to exp time reached", deviceId, request.getId(), request.getExpirationTime());
+ log.debug("[{}][{}] Ignoring message due to exp time reached, {}", deviceId, request.getId(), request.getExpirationTime());
return;
}
boolean sent = rpcSubscriptions.size() > 0;
- Set syncSessionSet = new HashSet<>();
- rpcSubscriptions.entrySet().forEach(sub -> {
- ActorSystemToDeviceSessionActorMsg response = new BasicActorSystemToDeviceSessionActorMsg(rpcRequest, sub.getKey());
- sendMsgToSessionActor(response, sub.getValue().getServer());
- if (SessionType.SYNC == sub.getValue().getType()) {
- syncSessionSet.add(sub.getKey());
+ Set syncSessionSet = new HashSet<>();
+ rpcSubscriptions.forEach((key, value) -> {
+ sendToTransport(rpcRequest, key, value.getNodeId());
+ if (TransportProtos.SessionType.SYNC == value.getType()) {
+ syncSessionSet.add(key);
}
});
syncSessionSet.forEach(rpcSubscriptions::remove);
if (request.isOneway() && sent) {
- logger.debug("[{}] Rpc command response sent [{}]!", deviceId, request.getId());
- systemContext.getDeviceRpcService().processRpcResponseFromDevice(new FromDeviceRpcResponse(msg.getMsg().getId(), msg.getServerAddress(), null, null));
+ log.debug("[{}] Rpc command response sent [{}]!", deviceId, request.getId());
+ systemContext.getDeviceRpcService().processResponseToServerSideRPCRequestFromDeviceActor(new FromDeviceRpcResponse(msg.getMsg().getId(), null, null));
} else {
registerPendingRpcRequest(context, msg, sent, rpcRequest, timeout);
}
if (sent) {
- logger.debug("[{}] RPC request {} is sent!", deviceId, request.getId());
+ log.debug("[{}] RPC request {} is sent!", deviceId, request.getId());
} else {
- logger.debug("[{}] RPC request {} is NOT sent!", deviceId, request.getId());
+ log.debug("[{}] RPC request {} is NOT sent!", deviceId, request.getId());
}
-
}
private void registerPendingRpcRequest(ActorContext context, ToDeviceRpcRequestActorMsg msg, boolean sent, ToDeviceRpcRequestMsg rpcRequest, long timeout) {
@@ -185,101 +174,82 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
void processServerSideRpcTimeout(ActorContext context, DeviceActorServerSideRpcTimeoutMsg msg) {
ToDeviceRpcRequestMetadata requestMd = toDeviceRpcPendingMap.remove(msg.getId());
if (requestMd != null) {
- logger.debug("[{}] RPC request [{}] timeout detected!", deviceId, msg.getId());
- systemContext.getDeviceRpcService().processRpcResponseFromDevice(new FromDeviceRpcResponse(requestMd.getMsg().getMsg().getId(),
- requestMd.getMsg().getServerAddress(), null, requestMd.isSent() ? RpcError.TIMEOUT : RpcError.NO_ACTIVE_CONNECTION));
- }
- }
-
- void processQueueTimeout(ActorContext context, DeviceActorQueueTimeoutMsg msg) {
- PendingSessionMsgData data = pendingMsgs.remove(msg.getId());
- if (data != null) {
- logger.debug("[{}] Queue put [{}] timeout detected!", deviceId, msg.getId());
- ToDeviceMsg toDeviceMsg = new RuleEngineErrorMsg(data.getSessionMsgType(), RuleEngineError.QUEUE_PUT_TIMEOUT);
- sendMsgToSessionActor(new BasicActorSystemToDeviceSessionActorMsg(toDeviceMsg, data.getSessionId()), data.getServerAddress());
- }
- }
-
- void processQueueAck(ActorContext context, RuleEngineQueuePutAckMsg msg) {
- PendingSessionMsgData data = pendingMsgs.remove(msg.getId());
- if (data != null && data.isReplyOnQueueAck()) {
- int remainingAcks = data.getAckMsgCount() - 1;
- data.setAckMsgCount(remainingAcks);
- logger.debug("[{}] Queue put [{}] ack detected. Remaining acks: {}!", deviceId, msg.getId(), remainingAcks);
- if (remainingAcks == 0) {
- ToDeviceMsg toDeviceMsg = BasicStatusCodeResponse.onSuccess(data.getSessionMsgType(), data.getRequestId());
- sendMsgToSessionActor(new BasicActorSystemToDeviceSessionActorMsg(toDeviceMsg, data.getSessionId()), data.getServerAddress());
- }
+ log.debug("[{}] RPC request [{}] timeout detected!", deviceId, msg.getId());
+ systemContext.getDeviceRpcService().processResponseToServerSideRPCRequestFromDeviceActor(new FromDeviceRpcResponse(requestMd.getMsg().getMsg().getId(),
+ null, requestMd.isSent() ? RpcError.TIMEOUT : RpcError.NO_ACTIVE_CONNECTION));
}
}
- private void sendPendingRequests(ActorContext context, SessionId sessionId, SessionType type, Optional server) {
+ private void sendPendingRequests(ActorContext context, UUID sessionId, SessionInfoProto sessionInfo) {
+ TransportProtos.SessionType sessionType = getSessionType(sessionId);
if (!toDeviceRpcPendingMap.isEmpty()) {
- logger.debug("[{}] Pushing {} pending RPC messages to new async session [{}]", deviceId, toDeviceRpcPendingMap.size(), sessionId);
- if (type == SessionType.SYNC) {
- logger.debug("[{}] Cleanup sync rpc session [{}]", deviceId, sessionId);
+ log.debug("[{}] Pushing {} pending RPC messages to new async session [{}]", deviceId, toDeviceRpcPendingMap.size(), sessionId);
+ if (sessionType == TransportProtos.SessionType.SYNC) {
+ log.debug("[{}] Cleanup sync rpc session [{}]", deviceId, sessionId);
rpcSubscriptions.remove(sessionId);
}
} else {
- logger.debug("[{}] No pending RPC messages for new async session [{}]", deviceId, sessionId);
+ log.debug("[{}] No pending RPC messages for new async session [{}]", deviceId, sessionId);
}
Set sentOneWayIds = new HashSet<>();
- if (type == SessionType.ASYNC) {
- toDeviceRpcPendingMap.entrySet().forEach(processPendingRpc(context, sessionId, server, sentOneWayIds));
+ if (sessionType == TransportProtos.SessionType.ASYNC) {
+ toDeviceRpcPendingMap.entrySet().forEach(processPendingRpc(context, sessionId, sessionInfo.getNodeId(), sentOneWayIds));
} else {
- toDeviceRpcPendingMap.entrySet().stream().findFirst().ifPresent(processPendingRpc(context, sessionId, server, sentOneWayIds));
+ toDeviceRpcPendingMap.entrySet().stream().findFirst().ifPresent(processPendingRpc(context, sessionId, sessionInfo.getNodeId(), sentOneWayIds));
}
sentOneWayIds.forEach(toDeviceRpcPendingMap::remove);
}
- private Consumer> processPendingRpc(ActorContext context, SessionId sessionId, Optional server, Set sentOneWayIds) {
+ private Consumer> processPendingRpc(ActorContext context, UUID sessionId, String nodeId, Set sentOneWayIds) {
return entry -> {
- ToDeviceRpcRequestActorMsg requestActorMsg = entry.getValue().getMsg();
ToDeviceRpcRequest request = entry.getValue().getMsg().getMsg();
ToDeviceRpcRequestBody body = request.getBody();
if (request.isOneway()) {
sentOneWayIds.add(entry.getKey());
- systemContext.getDeviceRpcService().processRpcResponseFromDevice(new FromDeviceRpcResponse(request.getId(), requestActorMsg.getServerAddress(), null, null));
+ systemContext.getDeviceRpcService().processResponseToServerSideRPCRequestFromDeviceActor(new FromDeviceRpcResponse(request.getId(), null, null));
}
- ToDeviceRpcRequestMsg rpcRequest = new ToDeviceRpcRequestMsg(
- entry.getKey(),
- body.getMethod(),
- body.getParams()
- );
- ActorSystemToDeviceSessionActorMsg response = new BasicActorSystemToDeviceSessionActorMsg(rpcRequest, sessionId);
- sendMsgToSessionActor(response, server);
+ ToDeviceRpcRequestMsg rpcRequest = ToDeviceRpcRequestMsg.newBuilder().setRequestId(
+ entry.getKey()).setMethodName(body.getMethod()).setParams(body.getParams()).build();
+ sendToTransport(rpcRequest, sessionId, nodeId);
};
}
- void process(ActorContext context, DeviceToDeviceActorMsg msg) {
- processSubscriptionCommands(context, msg);
- processRpcResponses(context, msg);
- processSessionStateMsgs(msg);
-
- SessionMsgType sessionMsgType = msg.getPayload().getMsgType();
- if (sessionMsgType.requiresRulesProcessing()) {
- switch (sessionMsgType) {
- case GET_ATTRIBUTES_REQUEST:
- handleGetAttributesRequest(msg);
- break;
- case POST_ATTRIBUTES_REQUEST:
- handlePostAttributesRequest(context, msg);
- reportActivity();
- break;
- case POST_TELEMETRY_REQUEST:
- handlePostTelemetryRequest(context, msg);
- reportActivity();
- break;
- case TO_SERVER_RPC_REQUEST:
- handleClientSideRPCRequest(context, msg);
- reportActivity();
- break;
- }
+ void process(ActorContext context, TransportToDeviceActorMsgWrapper wrapper) {
+ TransportToDeviceActorMsg msg = wrapper.getMsg();
+ if (msg.hasSessionEvent()) {
+ processSessionStateMsgs(msg.getSessionInfo(), msg.getSessionEvent());
+ }
+ if (msg.hasSubscribeToAttributes()) {
+ processSubscriptionCommands(context, msg.getSessionInfo(), msg.getSubscribeToAttributes());
+ }
+ if (msg.hasSubscribeToRPC()) {
+ processSubscriptionCommands(context, msg.getSessionInfo(), msg.getSubscribeToRPC());
+ }
+ if (msg.hasPostAttributes()) {
+ handlePostAttributesRequest(context, msg.getSessionInfo(), msg.getPostAttributes());
+ reportLogicalDeviceActivity();
+ }
+ if (msg.hasPostTelemetry()) {
+ handlePostTelemetryRequest(context, msg.getSessionInfo(), msg.getPostTelemetry());
+ reportLogicalDeviceActivity();
+ }
+ if (msg.hasGetAttributes()) {
+ handleGetAttributesRequest(context, msg.getSessionInfo(), msg.getGetAttributes());
+ }
+ if (msg.hasToDeviceRPCCallResponse()) {
+ processRpcResponses(context, msg.getSessionInfo(), msg.getToDeviceRPCCallResponse());
+ }
+ if (msg.hasToServerRPCCallRequest()) {
+ handleClientSideRPCRequest(context, msg.getSessionInfo(), msg.getToServerRPCCallRequest());
+ reportLogicalDeviceActivity();
+ }
+ if (msg.hasSubscriptionInfo()) {
+ handleSessionActivity(context, msg.getSessionInfo(), msg.getSubscriptionInfo());
}
}
- private void reportActivity() {
+ private void reportLogicalDeviceActivity() {
systemContext.getDeviceStateService().onDeviceActivity(deviceId);
}
@@ -291,27 +261,27 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
systemContext.getDeviceStateService().onDeviceDisconnect(deviceId);
}
- private void handleGetAttributesRequest(DeviceToDeviceActorMsg src) {
- GetAttributesRequest request = (GetAttributesRequest) src.getPayload();
- ListenableFuture> clientAttributesFuture = getAttributeKvEntries(deviceId, DataConstants.CLIENT_SCOPE, request.getClientAttributeNames());
- ListenableFuture> sharedAttributesFuture = getAttributeKvEntries(deviceId, DataConstants.SHARED_SCOPE, request.getSharedAttributeNames());
-
+ private void handleGetAttributesRequest(ActorContext context, SessionInfoProto sessionInfo, GetAttributeRequestMsg request) {
+ ListenableFuture> clientAttributesFuture = getAttributeKvEntries(deviceId, DataConstants.CLIENT_SCOPE, toOptionalSet(request.getClientAttributeNamesList()));
+ ListenableFuture> sharedAttributesFuture = getAttributeKvEntries(deviceId, DataConstants.SHARED_SCOPE, toOptionalSet(request.getSharedAttributeNamesList()));
+ int requestId = request.getRequestId();
Futures.addCallback(Futures.allAsList(Arrays.asList(clientAttributesFuture, sharedAttributesFuture)), new FutureCallback>>() {
@Override
public void onSuccess(@Nullable List> result) {
- BasicGetAttributesResponse response = BasicGetAttributesResponse.onSuccess(request.getMsgType(),
- request.getRequestId(), BasicAttributeKVMsg.from(result.get(0), result.get(1)));
- sendMsgToSessionActor(new BasicActorSystemToDeviceSessionActorMsg(response, src.getSessionId()), src.getServerAddress());
+ GetAttributeResponseMsg responseMsg = GetAttributeResponseMsg.newBuilder()
+ .setRequestId(requestId)
+ .addAllClientAttributeList(toTsKvProtos(result.get(0)))
+ .addAllSharedAttributeList(toTsKvProtos(result.get(1)))
+ .build();
+ sendToTransport(responseMsg, sessionInfo);
}
@Override
public void onFailure(Throwable t) {
- if (t instanceof Exception) {
- ToDeviceMsg toDeviceMsg = BasicStatusCodeResponse.onError(SessionMsgType.GET_ATTRIBUTES_REQUEST, request.getRequestId(), (Exception) t);
- sendMsgToSessionActor(new BasicActorSystemToDeviceSessionActorMsg(toDeviceMsg, src.getSessionId()), src.getServerAddress());
- } else {
- logger.error("[{}] Failed to process attributes request", deviceId, t);
- }
+ GetAttributeResponseMsg responseMsg = GetAttributeResponseMsg.newBuilder()
+ .setError(t.getMessage())
+ .build();
+ sendToTransport(responseMsg, sessionInfo);
}
});
}
@@ -328,220 +298,221 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
}
}
- private void handlePostAttributesRequest(ActorContext context, DeviceToDeviceActorMsg src) {
- AttributesUpdateRequest request = (AttributesUpdateRequest) src.getPayload();
-
- JsonObject json = new JsonObject();
- for (AttributeKvEntry kv : request.getAttributes()) {
- kv.getBooleanValue().ifPresent(v -> json.addProperty(kv.getKey(), v));
- kv.getLongValue().ifPresent(v -> json.addProperty(kv.getKey(), v));
- kv.getDoubleValue().ifPresent(v -> json.addProperty(kv.getKey(), v));
- kv.getStrValue().ifPresent(v -> json.addProperty(kv.getKey(), v));
- }
-
- TbMsg tbMsg = new TbMsg(UUIDs.timeBased(), SessionMsgType.POST_ATTRIBUTES_REQUEST.name(), deviceId, defaultMetaData.copy(), TbMsgDataType.JSON, gson.toJson(json), null, null, 0L);
- PendingSessionMsgData msgData = new PendingSessionMsgData(src.getSessionId(), src.getServerAddress(),
- SessionMsgType.POST_ATTRIBUTES_REQUEST, request.getRequestId(), true, 1);
- pushToRuleEngineWithTimeout(context, tbMsg, msgData);
+ private void handlePostAttributesRequest(ActorContext context, SessionInfoProto sessionInfo, PostAttributeMsg postAttributes) {
+ JsonObject json = getJsonObject(postAttributes.getKvList());
+ TbMsg tbMsg = new TbMsg(UUIDs.timeBased(), SessionMsgType.POST_ATTRIBUTES_REQUEST.name(), deviceId, defaultMetaData.copy(),
+ TbMsgDataType.JSON, gson.toJson(json), null, null, 0L);
+ pushToRuleEngine(context, tbMsg);
}
- private void handlePostTelemetryRequest(ActorContext context, DeviceToDeviceActorMsg src) {
- TelemetryUploadRequest request = (TelemetryUploadRequest) src.getPayload();
-
- Map> tsData = request.getData();
-
- PendingSessionMsgData msgData = new PendingSessionMsgData(src.getSessionId(), src.getServerAddress(),
- SessionMsgType.POST_TELEMETRY_REQUEST, request.getRequestId(), true, tsData.size());
-
- for (Map.Entry> entry : tsData.entrySet()) {
- JsonObject json = new JsonObject();
- for (KvEntry kv : entry.getValue()) {
- kv.getBooleanValue().ifPresent(v -> json.addProperty(kv.getKey(), v));
- kv.getLongValue().ifPresent(v -> json.addProperty(kv.getKey(), v));
- kv.getDoubleValue().ifPresent(v -> json.addProperty(kv.getKey(), v));
- kv.getStrValue().ifPresent(v -> json.addProperty(kv.getKey(), v));
- }
+ private void handlePostTelemetryRequest(ActorContext context, SessionInfoProto sessionInfo, PostTelemetryMsg postTelemetry) {
+ for (TsKvListProto tsKv : postTelemetry.getTsKvListList()) {
+ JsonObject json = getJsonObject(tsKv.getKvList());
TbMsgMetaData metaData = defaultMetaData.copy();
- metaData.putValue("ts", entry.getKey() + "");
+ metaData.putValue("ts", tsKv.getTs() + "");
TbMsg tbMsg = new TbMsg(UUIDs.timeBased(), SessionMsgType.POST_TELEMETRY_REQUEST.name(), deviceId, metaData, TbMsgDataType.JSON, gson.toJson(json), null, null, 0L);
- pushToRuleEngineWithTimeout(context, tbMsg, msgData);
+ pushToRuleEngine(context, tbMsg);
}
}
- private void handleClientSideRPCRequest(ActorContext context, DeviceToDeviceActorMsg src) {
- ToServerRpcRequestMsg request = (ToServerRpcRequestMsg) src.getPayload();
-
+ private void handleClientSideRPCRequest(ActorContext context, SessionInfoProto sessionInfo, TransportProtos.ToServerRpcRequestMsg request) {
+ UUID sessionId = getSessionId(sessionInfo);
JsonObject json = new JsonObject();
- json.addProperty("method", request.getMethod());
+ json.addProperty("method", request.getMethodName());
json.add("params", jsonParser.parse(request.getParams()));
TbMsgMetaData requestMetaData = defaultMetaData.copy();
requestMetaData.putValue("requestId", Integer.toString(request.getRequestId()));
TbMsg tbMsg = new TbMsg(UUIDs.timeBased(), SessionMsgType.TO_SERVER_RPC_REQUEST.name(), deviceId, requestMetaData, TbMsgDataType.JSON, gson.toJson(json), null, null, 0L);
- PendingSessionMsgData msgData = new PendingSessionMsgData(src.getSessionId(), src.getServerAddress(), SessionMsgType.TO_SERVER_RPC_REQUEST, request.getRequestId(), false, 1);
- pushToRuleEngineWithTimeout(context, tbMsg, msgData);
+ context.parent().tell(new DeviceActorToRuleEngineMsg(context.self(), tbMsg), context.self());
scheduleMsgWithDelay(context, new DeviceActorClientSideRpcTimeoutMsg(request.getRequestId(), systemContext.getClientSideRpcTimeout()), systemContext.getClientSideRpcTimeout());
- toServerRpcPendingMap.put(request.getRequestId(), new ToServerRpcRequestMetadata(src.getSessionId(), src.getSessionType(), src.getServerAddress()));
+ toServerRpcPendingMap.put(request.getRequestId(), new ToServerRpcRequestMetadata(sessionId, getSessionType(sessionId), sessionInfo.getNodeId()));
+ }
+
+ private TransportProtos.SessionType getSessionType(UUID sessionId) {
+ return sessions.containsKey(sessionId) ? TransportProtos.SessionType.ASYNC : TransportProtos.SessionType.SYNC;
}
- public void processClientSideRpcTimeout(ActorContext context, DeviceActorClientSideRpcTimeoutMsg msg) {
+ void processClientSideRpcTimeout(ActorContext context, DeviceActorClientSideRpcTimeoutMsg msg) {
ToServerRpcRequestMetadata data = toServerRpcPendingMap.remove(msg.getId());
if (data != null) {
- logger.debug("[{}] Client side RPC request [{}] timeout detected!", deviceId, msg.getId());
- ToDeviceMsg toDeviceMsg = new RuleEngineErrorMsg(SessionMsgType.TO_SERVER_RPC_REQUEST, RuleEngineError.TIMEOUT);
- sendMsgToSessionActor(new BasicActorSystemToDeviceSessionActorMsg(toDeviceMsg, data.getSessionId()), data.getServer());
+ log.debug("[{}] Client side RPC request [{}] timeout detected!", deviceId, msg.getId());
+ sendToTransport(TransportProtos.ToServerRpcResponseMsg.newBuilder()
+ .setRequestId(msg.getId()).setError("timeout").build()
+ , data.getSessionId(), data.getNodeId());
}
}
void processToServerRPCResponse(ActorContext context, ToServerRpcResponseActorMsg msg) {
- ToServerRpcRequestMetadata data = toServerRpcPendingMap.remove(msg.getMsg().getRequestId());
+ int requestId = msg.getMsg().getRequestId();
+ ToServerRpcRequestMetadata data = toServerRpcPendingMap.remove(requestId);
if (data != null) {
- sendMsgToSessionActor(new BasicActorSystemToDeviceSessionActorMsg(msg.getMsg(), data.getSessionId()), data.getServer());
+ log.debug("[{}] Pushing reply to [{}][{}]!", deviceId, data.getNodeId(), data.getSessionId());
+ sendToTransport(TransportProtos.ToServerRpcResponseMsg.newBuilder()
+ .setRequestId(requestId).setPayload(msg.getMsg().getData()).build()
+ , data.getSessionId(), data.getNodeId());
+ } else {
+ log.debug("[{}][{}] Pending RPC request to server not found!", deviceId, requestId);
}
}
- private void pushToRuleEngineWithTimeout(ActorContext context, TbMsg tbMsg, PendingSessionMsgData pendingMsgData) {
- SessionMsgType sessionMsgType = pendingMsgData.getSessionMsgType();
- int requestId = pendingMsgData.getRequestId();
- if (systemContext.isQueuePersistenceEnabled()) {
- pendingMsgs.put(tbMsg.getId(), pendingMsgData);
- scheduleMsgWithDelay(context, new DeviceActorQueueTimeoutMsg(tbMsg.getId(), systemContext.getQueuePersistenceTimeout()), systemContext.getQueuePersistenceTimeout());
- } else {
- ActorSystemToDeviceSessionActorMsg response = new BasicActorSystemToDeviceSessionActorMsg(BasicStatusCodeResponse.onSuccess(sessionMsgType, requestId), pendingMsgData.getSessionId());
- sendMsgToSessionActor(response, pendingMsgData.getServerAddress());
- }
+ private void pushToRuleEngine(ActorContext context, TbMsg tbMsg) {
context.parent().tell(new DeviceActorToRuleEngineMsg(context.self(), tbMsg), context.self());
}
void processAttributesUpdate(ActorContext context, DeviceAttributesEventNotificationMsg msg) {
if (attributeSubscriptions.size() > 0) {
- ToDeviceMsg notification = null;
+ boolean hasNotificationData = false;
+ AttributeUpdateNotificationMsg.Builder notification = AttributeUpdateNotificationMsg.newBuilder();
if (msg.isDeleted()) {
- List sharedKeys = msg.getDeletedKeys().stream()
+ List sharedKeys = msg.getDeletedKeys().stream()
.filter(key -> DataConstants.SHARED_SCOPE.equals(key.getScope()))
+ .map(AttributeKey::getAttributeKey)
.collect(Collectors.toList());
- notification = new AttributesUpdateNotification(BasicAttributeKVMsg.fromDeleted(sharedKeys));
+ if (!sharedKeys.isEmpty()) {
+ notification.addAllSharedDeleted(sharedKeys);
+ hasNotificationData = true;
+ }
} else {
if (DataConstants.SHARED_SCOPE.equals(msg.getScope())) {
List attributes = new ArrayList<>(msg.getValues());
if (attributes.size() > 0) {
- notification = new AttributesUpdateNotification(BasicAttributeKVMsg.fromShared(attributes));
+ List sharedUpdated = msg.getValues().stream().map(this::toTsKvProto)
+ .collect(Collectors.toList());
+ if (!sharedUpdated.isEmpty()) {
+ notification.addAllSharedUpdated(sharedUpdated);
+ hasNotificationData = true;
+ }
} else {
- logger.debug("[{}] No public server side attributes changed!", deviceId);
+ log.debug("[{}] No public server side attributes changed!", deviceId);
}
}
}
- if (notification != null) {
- ToDeviceMsg finalNotification = notification;
+ if (hasNotificationData) {
+ AttributeUpdateNotificationMsg finalNotification = notification.build();
attributeSubscriptions.entrySet().forEach(sub -> {
- ActorSystemToDeviceSessionActorMsg response = new BasicActorSystemToDeviceSessionActorMsg(finalNotification, sub.getKey());
- sendMsgToSessionActor(response, sub.getValue().getServer());
+ sendToTransport(finalNotification, sub.getKey(), sub.getValue().getNodeId());
});
}
} else {
- logger.debug("[{}] No registered attributes subscriptions to process!", deviceId);
+ log.debug("[{}] No registered attributes subscriptions to process!", deviceId);
}
}
- private void processRpcResponses(ActorContext context, DeviceToDeviceActorMsg msg) {
- SessionId sessionId = msg.getSessionId();
- FromDeviceMsg inMsg = msg.getPayload();
- if (inMsg.getMsgType() == SessionMsgType.TO_DEVICE_RPC_RESPONSE) {
- logger.debug("[{}] Processing rpc command response [{}]", deviceId, sessionId);
- ToDeviceRpcResponseMsg responseMsg = (ToDeviceRpcResponseMsg) inMsg;
- ToDeviceRpcRequestMetadata requestMd = toDeviceRpcPendingMap.remove(responseMsg.getRequestId());
- boolean success = requestMd != null;
- if (success) {
- systemContext.getDeviceRpcService().processRpcResponseFromDevice(new FromDeviceRpcResponse(requestMd.getMsg().getMsg().getId(),
- requestMd.getMsg().getServerAddress(), responseMsg.getData(), null));
- } else {
- logger.debug("[{}] Rpc command response [{}] is stale!", deviceId, responseMsg.getRequestId());
- }
- if (msg.getSessionType() == SessionType.SYNC) {
- BasicCommandAckResponse response = success
- ? BasicCommandAckResponse.onSuccess(SessionMsgType.TO_DEVICE_RPC_REQUEST, responseMsg.getRequestId())
- : BasicCommandAckResponse.onError(SessionMsgType.TO_DEVICE_RPC_REQUEST, responseMsg.getRequestId(), new TimeoutException());
- sendMsgToSessionActor(new BasicActorSystemToDeviceSessionActorMsg(response, msg.getSessionId()), msg.getServerAddress());
- }
+ private void processRpcResponses(ActorContext context, SessionInfoProto sessionInfo, ToDeviceRpcResponseMsg responseMsg) {
+ UUID sessionId = getSessionId(sessionInfo);
+ log.debug("[{}] Processing rpc command response [{}]", deviceId, sessionId);
+ ToDeviceRpcRequestMetadata requestMd = toDeviceRpcPendingMap.remove(responseMsg.getRequestId());
+ boolean success = requestMd != null;
+ if (success) {
+ systemContext.getDeviceRpcService().processResponseToServerSideRPCRequestFromDeviceActor(new FromDeviceRpcResponse(requestMd.getMsg().getMsg().getId(),
+ responseMsg.getPayload(), null));
+ } else {
+ log.debug("[{}] Rpc command response [{}] is stale!", deviceId, responseMsg.getRequestId());
}
}
- void processClusterEventMsg(ClusterEventMsg msg) {
- if (!msg.isAdded()) {
- logger.debug("[{}] Clearing attributes/rpc subscription for server [{}]", deviceId, msg.getServerAddress());
- Predicate> filter = e -> e.getValue().getServer()
- .map(serverAddress -> serverAddress.equals(msg.getServerAddress())).orElse(false);
- attributeSubscriptions.entrySet().removeIf(filter);
- rpcSubscriptions.entrySet().removeIf(filter);
+ private void processSubscriptionCommands(ActorContext context, SessionInfoProto sessionInfo, SubscribeToAttributeUpdatesMsg subscribeCmd) {
+ UUID sessionId = getSessionId(sessionInfo);
+ if (subscribeCmd.getUnsubscribe()) {
+ log.debug("[{}] Canceling attributes subscription for session [{}]", deviceId, sessionId);
+ attributeSubscriptions.remove(sessionId);
+ } else {
+ SessionInfoMetaData sessionMD = sessions.get(sessionId);
+ if (sessionMD == null) {
+ sessionMD = new SessionInfoMetaData(new SessionInfo(TransportProtos.SessionType.SYNC, sessionInfo.getNodeId()));
+ }
+ sessionMD.setSubscribedToAttributes(true);
+ log.debug("[{}] Registering attributes subscription for session [{}]", deviceId, sessionId);
+ attributeSubscriptions.put(sessionId, sessionMD.getSessionInfo());
+ dumpSessions();
}
}
- private void processSubscriptionCommands(ActorContext context, DeviceToDeviceActorMsg msg) {
- SessionId sessionId = msg.getSessionId();
- SessionType sessionType = msg.getSessionType();
- FromDeviceMsg inMsg = msg.getPayload();
- if (inMsg.getMsgType() == SessionMsgType.SUBSCRIBE_ATTRIBUTES_REQUEST) {
- logger.debug("[{}] Registering attributes subscription for session [{}]", deviceId, sessionId);
- attributeSubscriptions.put(sessionId, new SessionInfo(sessionType, msg.getServerAddress()));
- } else if (inMsg.getMsgType() == SessionMsgType.UNSUBSCRIBE_ATTRIBUTES_REQUEST) {
- logger.debug("[{}] Canceling attributes subscription for session [{}]", deviceId, sessionId);
- attributeSubscriptions.remove(sessionId);
- } else if (inMsg.getMsgType() == SessionMsgType.SUBSCRIBE_RPC_COMMANDS_REQUEST) {
- logger.debug("[{}] Registering rpc subscription for session [{}][{}]", deviceId, sessionId, sessionType);
- rpcSubscriptions.put(sessionId, new SessionInfo(sessionType, msg.getServerAddress()));
- sendPendingRequests(context, sessionId, sessionType, msg.getServerAddress());
- } else if (inMsg.getMsgType() == SessionMsgType.UNSUBSCRIBE_RPC_COMMANDS_REQUEST) {
- logger.debug("[{}] Canceling rpc subscription for session [{}][{}]", deviceId, sessionId, sessionType);
+ private UUID getSessionId(SessionInfoProto sessionInfo) {
+ return new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB());
+ }
+
+ private void processSubscriptionCommands(ActorContext context, SessionInfoProto sessionInfo, SubscribeToRPCMsg subscribeCmd) {
+ UUID sessionId = getSessionId(sessionInfo);
+ if (subscribeCmd.getUnsubscribe()) {
+ log.debug("[{}] Canceling rpc subscription for session [{}]", deviceId, sessionId);
rpcSubscriptions.remove(sessionId);
+ } else {
+ SessionInfoMetaData sessionMD = sessions.get(sessionId);
+ if (sessionMD == null) {
+ sessionMD = new SessionInfoMetaData(new SessionInfo(TransportProtos.SessionType.SYNC, sessionInfo.getNodeId()));
+ }
+ sessionMD.setSubscribedToRPC(true);
+ log.debug("[{}] Registering rpc subscription for session [{}]", deviceId, sessionId);
+ rpcSubscriptions.put(sessionId, sessionMD.getSessionInfo());
+ sendPendingRequests(context, sessionId, sessionInfo);
+ dumpSessions();
}
}
- private void processSessionStateMsgs(DeviceToDeviceActorMsg msg) {
- SessionId sessionId = msg.getSessionId();
- FromDeviceMsg inMsg = msg.getPayload();
- if (inMsg instanceof SessionOpenMsg) {
- logger.debug("[{}] Processing new session [{}]", deviceId, sessionId);
+ private void processSessionStateMsgs(SessionInfoProto sessionInfo, SessionEventMsg msg) {
+ UUID sessionId = getSessionId(sessionInfo);
+ if (msg.getEvent() == SessionEvent.OPEN) {
+ if (sessions.containsKey(sessionId)) {
+ log.debug("[{}] Received duplicate session open event [{}]", deviceId, sessionId);
+ return;
+ }
+ log.debug("[{}] Processing new session [{}]", deviceId, sessionId);
if (sessions.size() >= systemContext.getMaxConcurrentSessionsPerDevice()) {
- SessionId sessionIdToRemove = sessions.keySet().stream().findFirst().orElse(null);
+ UUID sessionIdToRemove = sessions.keySet().stream().findFirst().orElse(null);
if (sessionIdToRemove != null) {
- closeSession(sessionIdToRemove, sessions.remove(sessionIdToRemove));
+ notifyTransportAboutClosedSession(sessionIdToRemove, sessions.remove(sessionIdToRemove));
}
}
- sessions.put(sessionId, new SessionInfo(SessionType.ASYNC, msg.getServerAddress()));
+ sessions.put(sessionId, new SessionInfoMetaData(new SessionInfo(TransportProtos.SessionType.ASYNC, sessionInfo.getNodeId())));
if (sessions.size() == 1) {
reportSessionOpen();
}
- } else if (inMsg instanceof SessionCloseMsg) {
- logger.debug("[{}] Canceling subscriptions for closed session [{}]", deviceId, sessionId);
+ dumpSessions();
+ } else if (msg.getEvent() == SessionEvent.CLOSED) {
+ log.debug("[{}] Canceling subscriptions for closed session [{}]", deviceId, sessionId);
sessions.remove(sessionId);
attributeSubscriptions.remove(sessionId);
rpcSubscriptions.remove(sessionId);
if (sessions.isEmpty()) {
reportSessionClose();
}
+ dumpSessions();
}
}
- private void sendMsgToSessionActor(ActorSystemToDeviceSessionActorMsg response, Optional sessionAddress) {
- if (sessionAddress.isPresent()) {
- ServerAddress address = sessionAddress.get();
- logger.debug("{} Forwarding msg: {}", address, response);
- systemContext.getRpcService().tell(systemContext.getEncodingService()
- .convertToProtoDataMessage(sessionAddress.get(), response));
- } else {
- systemContext.getSessionManagerActor().tell(response, ActorRef.noSender());
+ private void handleSessionActivity(ActorContext context, SessionInfoProto sessionInfo, TransportProtos.SubscriptionInfoProto subscriptionInfo) {
+ UUID sessionId = getSessionId(sessionInfo);
+ SessionInfoMetaData sessionMD = sessions.get(sessionId);
+ if (sessionMD != null) {
+ sessionMD.setLastActivityTime(subscriptionInfo.getLastActivityTime());
+ sessionMD.setSubscribedToAttributes(subscriptionInfo.getAttributeSubscription());
+ sessionMD.setSubscribedToRPC(subscriptionInfo.getRpcSubscription());
+ if (subscriptionInfo.getAttributeSubscription()) {
+ attributeSubscriptions.putIfAbsent(sessionId, sessionMD.getSessionInfo());
+ }
+ if (subscriptionInfo.getRpcSubscription()) {
+ rpcSubscriptions.putIfAbsent(sessionId, sessionMD.getSessionInfo());
+ }
}
+ dumpSessions();
}
void processCredentialsUpdate() {
- sessions.forEach(this::closeSession);
+ sessions.forEach(this::notifyTransportAboutClosedSession);
attributeSubscriptions.clear();
rpcSubscriptions.clear();
+ dumpSessions();
}
- private void closeSession(SessionId sessionId, SessionInfo sessionInfo) {
- sendMsgToSessionActor(new BasicActorSystemToDeviceSessionActorMsg(new SessionCloseNotification(), sessionId), sessionInfo.getServer());
+ private void notifyTransportAboutClosedSession(UUID sessionId, SessionInfoMetaData sessionMd) {
+ DeviceActorToTransportMsg msg = DeviceActorToTransportMsg.newBuilder()
+ .setSessionIdMSB(sessionId.getMostSignificantBits())
+ .setSessionIdLSB(sessionId.getLeastSignificantBits())
+ .setSessionCloseNotification(SessionCloseNotificationProto.getDefaultInstance()).build();
+ systemContext.getRuleEngineTransportService().process(sessionMd.getSessionInfo().getNodeId(), msg);
}
void processNameOrTypeUpdate(DeviceNameOrTypeUpdateMsg msg) {
@@ -552,4 +523,178 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
this.defaultMetaData.putValue("deviceType", deviceType);
}
+ private JsonObject getJsonObject(List tsKv) {
+ JsonObject json = new JsonObject();
+ for (KeyValueProto kv : tsKv) {
+ switch (kv.getType()) {
+ case BOOLEAN_V:
+ json.addProperty(kv.getKey(), kv.getBoolV());
+ break;
+ case LONG_V:
+ json.addProperty(kv.getKey(), kv.getLongV());
+ break;
+ case DOUBLE_V:
+ json.addProperty(kv.getKey(), kv.getDoubleV());
+ break;
+ case STRING_V:
+ json.addProperty(kv.getKey(), kv.getStringV());
+ break;
+ }
+ }
+ return json;
+ }
+
+ private Optional> toOptionalSet(List strings) {
+ if (strings == null || strings.isEmpty()) {
+ return Optional.empty();
+ } else {
+ return Optional.of(new HashSet<>(strings));
+ }
+ }
+
+ private void sendToTransport(GetAttributeResponseMsg responseMsg, SessionInfoProto sessionInfo) {
+ DeviceActorToTransportMsg msg = DeviceActorToTransportMsg.newBuilder()
+ .setSessionIdMSB(sessionInfo.getSessionIdMSB())
+ .setSessionIdLSB(sessionInfo.getSessionIdLSB())
+ .setGetAttributesResponse(responseMsg).build();
+ systemContext.getRuleEngineTransportService().process(sessionInfo.getNodeId(), msg);
+ }
+
+ private void sendToTransport(AttributeUpdateNotificationMsg notificationMsg, UUID sessionId, String nodeId) {
+ DeviceActorToTransportMsg msg = DeviceActorToTransportMsg.newBuilder()
+ .setSessionIdMSB(sessionId.getMostSignificantBits())
+ .setSessionIdLSB(sessionId.getLeastSignificantBits())
+ .setAttributeUpdateNotification(notificationMsg).build();
+ systemContext.getRuleEngineTransportService().process(nodeId, msg);
+ }
+
+ private void sendToTransport(ToDeviceRpcRequestMsg rpcMsg, UUID sessionId, String nodeId) {
+ DeviceActorToTransportMsg msg = DeviceActorToTransportMsg.newBuilder()
+ .setSessionIdMSB(sessionId.getMostSignificantBits())
+ .setSessionIdLSB(sessionId.getLeastSignificantBits())
+ .setToDeviceRequest(rpcMsg).build();
+ systemContext.getRuleEngineTransportService().process(nodeId, msg);
+ }
+
+ private void sendToTransport(TransportProtos.ToServerRpcResponseMsg rpcMsg, UUID sessionId, String nodeId) {
+ DeviceActorToTransportMsg msg = DeviceActorToTransportMsg.newBuilder()
+ .setSessionIdMSB(sessionId.getMostSignificantBits())
+ .setSessionIdLSB(sessionId.getLeastSignificantBits())
+ .setToServerResponse(rpcMsg).build();
+ systemContext.getRuleEngineTransportService().process(nodeId, msg);
+ }
+
+
+ private List toTsKvProtos(@Nullable List result) {
+ List clientAttributes;
+ if (result == null || result.isEmpty()) {
+ clientAttributes = Collections.emptyList();
+ } else {
+ clientAttributes = new ArrayList<>(result.size());
+ for (AttributeKvEntry attrEntry : result) {
+ clientAttributes.add(toTsKvProto(attrEntry));
+ }
+ }
+ return clientAttributes;
+ }
+
+ private TsKvProto toTsKvProto(AttributeKvEntry attrEntry) {
+ return TsKvProto.newBuilder().setTs(attrEntry.getLastUpdateTs())
+ .setKv(toKeyValueProto(attrEntry)).build();
+ }
+
+ private KeyValueProto toKeyValueProto(KvEntry kvEntry) {
+ KeyValueProto.Builder builder = KeyValueProto.newBuilder();
+ builder.setKey(kvEntry.getKey());
+ switch (kvEntry.getDataType()) {
+ case BOOLEAN:
+ builder.setType(KeyValueType.BOOLEAN_V);
+ builder.setBoolV(kvEntry.getBooleanValue().get());
+ break;
+ case DOUBLE:
+ builder.setType(KeyValueType.DOUBLE_V);
+ builder.setDoubleV(kvEntry.getDoubleValue().get());
+ break;
+ case LONG:
+ builder.setType(KeyValueType.LONG_V);
+ builder.setLongV(kvEntry.getLongValue().get());
+ break;
+ case STRING:
+ builder.setType(KeyValueType.STRING_V);
+ builder.setStringV(kvEntry.getStrValue().get());
+ break;
+ }
+ return builder.build();
+ }
+
+ private void restoreSessions() {
+ log.debug("[{}] Restoring sessions from cache", deviceId);
+ TransportProtos.DeviceSessionsCacheEntry sessionsDump = systemContext.getDeviceSessionCacheService().get(deviceId);
+ if (sessionsDump.getSerializedSize() == 0) {
+ log.debug("[{}] No session information found", deviceId);
+ return;
+ }
+ for (TransportProtos.SessionSubscriptionInfoProto sessionSubscriptionInfoProto : sessionsDump.getSessionsList()) {
+ SessionInfoProto sessionInfoProto = sessionSubscriptionInfoProto.getSessionInfo();
+ UUID sessionId = getSessionId(sessionInfoProto);
+ SessionInfo sessionInfo = new SessionInfo(TransportProtos.SessionType.ASYNC, sessionInfoProto.getNodeId());
+ TransportProtos.SubscriptionInfoProto subInfo = sessionSubscriptionInfoProto.getSubscriptionInfo();
+ SessionInfoMetaData sessionMD = new SessionInfoMetaData(sessionInfo, subInfo.getLastActivityTime());
+ sessions.put(sessionId, sessionMD);
+ if (subInfo.getAttributeSubscription()) {
+ attributeSubscriptions.put(sessionId, sessionInfo);
+ sessionMD.setSubscribedToAttributes(true);
+ }
+ if (subInfo.getRpcSubscription()) {
+ rpcSubscriptions.put(sessionId, sessionInfo);
+ sessionMD.setSubscribedToRPC(true);
+ }
+ log.debug("[{}] Restored session: {}", deviceId, sessionMD);
+ }
+ log.debug("[{}] Restored sessions: {}, rpc subscriptions: {}, attribute subscriptions: {}", deviceId, sessions.size(), rpcSubscriptions.size(), attributeSubscriptions.size());
+ }
+
+ private void dumpSessions() {
+ log.debug("[{}] Dumping sessions: {}, rpc subscriptions: {}, attribute subscriptions: {} to cache", deviceId, sessions.size(), rpcSubscriptions.size(), attributeSubscriptions.size());
+ List sessionsList = new ArrayList<>(sessions.size());
+ sessions.forEach((uuid, sessionMD) -> {
+ if (sessionMD.getSessionInfo().getType() == TransportProtos.SessionType.SYNC) {
+ return;
+ }
+ SessionInfo sessionInfo = sessionMD.getSessionInfo();
+ TransportProtos.SubscriptionInfoProto subscriptionInfoProto = TransportProtos.SubscriptionInfoProto.newBuilder()
+ .setLastActivityTime(sessionMD.getLastActivityTime())
+ .setAttributeSubscription(sessionMD.isSubscribedToAttributes())
+ .setRpcSubscription(sessionMD.isSubscribedToRPC()).build();
+ TransportProtos.SessionInfoProto sessionInfoProto = TransportProtos.SessionInfoProto.newBuilder()
+ .setSessionIdMSB(uuid.getMostSignificantBits())
+ .setSessionIdLSB(uuid.getLeastSignificantBits())
+ .setNodeId(sessionInfo.getNodeId()).build();
+ sessionsList.add(TransportProtos.SessionSubscriptionInfoProto.newBuilder()
+ .setSessionInfo(sessionInfoProto)
+ .setSubscriptionInfo(subscriptionInfoProto).build());
+ log.debug("[{}] Dumping session: {}", deviceId, sessionMD);
+ });
+ systemContext.getDeviceSessionCacheService()
+ .put(deviceId, TransportProtos.DeviceSessionsCacheEntry.newBuilder()
+ .addAllSessions(sessionsList).build());
+ }
+
+ void initSessionTimeout(ActorContext context) {
+ schedulePeriodicMsgWithDelay(context, SessionTimeoutCheckMsg.instance(), systemContext.getSessionInactivityTimeout(), systemContext.getSessionInactivityTimeout());
+ }
+
+ void checkSessionsTimeout() {
+ long expTime = System.currentTimeMillis() - systemContext.getSessionInactivityTimeout();
+ Map sessionsToRemove = sessions.entrySet().stream().filter(kv -> kv.getValue().getLastActivityTime() < expTime).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
+ sessionsToRemove.forEach((sessionId, sessionMD) -> {
+ sessions.remove(sessionId);
+ rpcSubscriptions.remove(sessionId);
+ attributeSubscriptions.remove(sessionId);
+ notifyTransportAboutClosedSession(sessionId, sessionMD);
+ });
+ if (!sessionsToRemove.isEmpty()) {
+ dumpSessions();
+ }
+ }
}
diff --git a/application/src/main/java/org/thingsboard/server/actors/device/PendingSessionMsgData.java b/application/src/main/java/org/thingsboard/server/actors/device/PendingSessionMsgData.java
deleted file mode 100644
index 23ad966049..0000000000
--- a/application/src/main/java/org/thingsboard/server/actors/device/PendingSessionMsgData.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/**
- * Copyright © 2016-2018 The Thingsboard Authors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.thingsboard.server.actors.device;
-
-import lombok.AllArgsConstructor;
-import lombok.Data;
-import org.thingsboard.server.common.data.id.SessionId;
-import org.thingsboard.server.common.msg.cluster.ServerAddress;
-import org.thingsboard.server.common.msg.session.SessionMsgType;
-
-import java.util.Optional;
-
-/**
- * Created by ashvayka on 17.04.18.
- */
-@Data
-@AllArgsConstructor
-public final class PendingSessionMsgData {
-
- private final SessionId sessionId;
- private final Optional serverAddress;
- private final SessionMsgType sessionMsgType;
- private final int requestId;
- private final boolean replyOnQueueAck;
- private int ackMsgCount;
-
-}
diff --git a/application/src/main/java/org/thingsboard/server/actors/device/SessionInfo.java b/application/src/main/java/org/thingsboard/server/actors/device/SessionInfo.java
index 04c457c92e..fe09077af1 100644
--- a/application/src/main/java/org/thingsboard/server/actors/device/SessionInfo.java
+++ b/application/src/main/java/org/thingsboard/server/actors/device/SessionInfo.java
@@ -16,10 +16,7 @@
package org.thingsboard.server.actors.device;
import lombok.Data;
-import org.thingsboard.server.common.msg.cluster.ServerAddress;
-import org.thingsboard.server.common.msg.session.SessionType;
-
-import java.util.Optional;
+import org.thingsboard.server.gen.transport.TransportProtos.SessionType;
/**
* @author Andrew Shvayka
@@ -27,5 +24,6 @@ import java.util.Optional;
@Data
public class SessionInfo {
private final SessionType type;
- private final Optional server;
+ private final String nodeId;
+ private long lastActivityTime;
}
diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/ToDeviceRpcRequestMsg.java b/application/src/main/java/org/thingsboard/server/actors/device/SessionInfoMetaData.java
similarity index 54%
rename from common/message/src/main/java/org/thingsboard/server/common/msg/core/ToDeviceRpcRequestMsg.java
rename to application/src/main/java/org/thingsboard/server/actors/device/SessionInfoMetaData.java
index d1a1f96451..dd0839486f 100644
--- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/ToDeviceRpcRequestMsg.java
+++ b/application/src/main/java/org/thingsboard/server/actors/device/SessionInfoMetaData.java
@@ -13,29 +13,27 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.thingsboard.server.common.msg.core;
+package org.thingsboard.server.actors.device;
import lombok.Data;
-import org.thingsboard.server.common.msg.session.SessionMsgType;
-import org.thingsboard.server.common.msg.session.SessionMsgType;
-import org.thingsboard.server.common.msg.session.ToDeviceMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.SessionType;
/**
* @author Andrew Shvayka
*/
@Data
-public class ToDeviceRpcRequestMsg implements ToDeviceMsg {
+class SessionInfoMetaData {
+ private final SessionInfo sessionInfo;
+ private long lastActivityTime;
+ private boolean subscribedToAttributes;
+ private boolean subscribedToRPC;
- private final int requestId;
- private final String method;
- private final String params;
-
- public SessionMsgType getSessionMsgType() {
- return SessionMsgType.TO_DEVICE_RPC_REQUEST;
+ SessionInfoMetaData(SessionInfo sessionInfo) {
+ this(sessionInfo, System.currentTimeMillis());
}
- @Override
- public boolean isSuccess() {
- return true;
+ SessionInfoMetaData(SessionInfo sessionInfo, long lastActivityTime) {
+ this.sessionInfo = sessionInfo;
+ this.lastActivityTime = lastActivityTime;
}
}
diff --git a/application/src/main/java/org/thingsboard/server/actors/device/RuleEngineQueuePutAckMsg.java b/application/src/main/java/org/thingsboard/server/actors/device/SessionTimeoutCheckMsg.java
similarity index 69%
rename from application/src/main/java/org/thingsboard/server/actors/device/RuleEngineQueuePutAckMsg.java
rename to application/src/main/java/org/thingsboard/server/actors/device/SessionTimeoutCheckMsg.java
index 0b6fe54c4a..d9172ae825 100644
--- a/application/src/main/java/org/thingsboard/server/actors/device/RuleEngineQueuePutAckMsg.java
+++ b/application/src/main/java/org/thingsboard/server/actors/device/SessionTimeoutCheckMsg.java
@@ -15,22 +15,25 @@
*/
package org.thingsboard.server.actors.device;
-import lombok.Data;
import org.thingsboard.server.common.msg.MsgType;
import org.thingsboard.server.common.msg.TbActorMsg;
-import java.util.UUID;
-
/**
- * Created by ashvayka on 15.03.18.
+ * Created by ashvayka on 29.10.18.
*/
-@Data
-public final class RuleEngineQueuePutAckMsg implements TbActorMsg {
+public class SessionTimeoutCheckMsg implements TbActorMsg {
+
+ private static final SessionTimeoutCheckMsg INSTANCE = new SessionTimeoutCheckMsg();
- private final UUID id;
+ private SessionTimeoutCheckMsg() {
+ }
+
+ public static SessionTimeoutCheckMsg instance() {
+ return INSTANCE;
+ }
@Override
public MsgType getMsgType() {
- return MsgType.RULE_ENGINE_QUEUE_PUT_ACK_MSG;
+ return MsgType.SESSION_TIMEOUT_MSG;
}
}
diff --git a/application/src/main/java/org/thingsboard/server/actors/device/ToServerRpcRequestMetadata.java b/application/src/main/java/org/thingsboard/server/actors/device/ToServerRpcRequestMetadata.java
index f82a8c2481..669d94b17d 100644
--- a/application/src/main/java/org/thingsboard/server/actors/device/ToServerRpcRequestMetadata.java
+++ b/application/src/main/java/org/thingsboard/server/actors/device/ToServerRpcRequestMetadata.java
@@ -16,18 +16,16 @@
package org.thingsboard.server.actors.device;
import lombok.Data;
-import org.thingsboard.server.common.data.id.SessionId;
-import org.thingsboard.server.common.msg.cluster.ServerAddress;
-import org.thingsboard.server.common.msg.session.SessionType;
+import org.thingsboard.server.gen.transport.TransportProtos;
-import java.util.Optional;
+import java.util.UUID;
/**
* @author Andrew Shvayka
*/
@Data
public class ToServerRpcRequestMetadata {
- private final SessionId sessionId;
- private final SessionType type;
- private final Optional server;
+ private final UUID sessionId;
+ private final TransportProtos.SessionType type;
+ private final String nodeId;
}
diff --git a/application/src/main/java/org/thingsboard/server/actors/rpc/BasicRpcSessionListener.java b/application/src/main/java/org/thingsboard/server/actors/rpc/BasicRpcSessionListener.java
index 14bb636830..760d4a6d96 100644
--- a/application/src/main/java/org/thingsboard/server/actors/rpc/BasicRpcSessionListener.java
+++ b/application/src/main/java/org/thingsboard/server/actors/rpc/BasicRpcSessionListener.java
@@ -32,7 +32,7 @@ public class BasicRpcSessionListener implements GrpcSessionListener {
private final ActorRef manager;
private final ActorRef self;
- public BasicRpcSessionListener(ActorService service, ActorRef manager, ActorRef self) {
+ BasicRpcSessionListener(ActorService service, ActorRef manager, ActorRef self) {
this.service = service;
this.manager = manager;
this.self = self;
@@ -40,7 +40,7 @@ public class BasicRpcSessionListener implements GrpcSessionListener {
@Override
public void onConnected(GrpcSession session) {
- log.info("{} session started -> {}", getType(session), session.getRemoteServer());
+ log.info("[{}][{}] session started", session.getRemoteServer(), getType(session));
if (!session.isClient()) {
manager.tell(new RpcSessionConnectedMsg(session.getRemoteServer(), session.getSessionId()), self);
}
@@ -48,21 +48,19 @@ public class BasicRpcSessionListener implements GrpcSessionListener {
@Override
public void onDisconnected(GrpcSession session) {
- log.info("{} session closed -> {}", getType(session), session.getRemoteServer());
+ log.info("[{}][{}] session closed", session.getRemoteServer(), getType(session));
manager.tell(new RpcSessionDisconnectedMsg(session.isClient(), session.getRemoteServer()), self);
}
@Override
public void onReceiveClusterGrpcMsg(GrpcSession session, ClusterAPIProtos.ClusterMessage clusterMessage) {
- log.trace("{} Service [{}] received session actor msg {}", getType(session),
- session.getRemoteServer(),
- clusterMessage);
+ log.trace("Received session actor msg from [{}][{}]: {}", session.getRemoteServer(), getType(session), clusterMessage);
service.onReceivedMsg(session.getRemoteServer(), clusterMessage);
}
@Override
public void onError(GrpcSession session, Throwable t) {
- log.warn("{} session got error -> {}", getType(session), session.getRemoteServer(), t);
+ log.warn("[{}][{}] session got error -> {}", session.getRemoteServer(), getType(session), t);
manager.tell(new RpcSessionClosedMsg(session.isClient(), session.getRemoteServer()), self);
session.close();
}
diff --git a/application/src/main/java/org/thingsboard/server/actors/rpc/RpcManagerActor.java b/application/src/main/java/org/thingsboard/server/actors/rpc/RpcManagerActor.java
index 9e38c17aff..9fa087e630 100644
--- a/application/src/main/java/org/thingsboard/server/actors/rpc/RpcManagerActor.java
+++ b/application/src/main/java/org/thingsboard/server/actors/rpc/RpcManagerActor.java
@@ -19,6 +19,7 @@ import akka.actor.ActorRef;
import akka.actor.Props;
import akka.event.Logging;
import akka.event.LoggingAdapter;
+import lombok.extern.slf4j.Slf4j;
import org.thingsboard.server.actors.ActorSystemContext;
import org.thingsboard.server.actors.service.ContextAwareActor;
import org.thingsboard.server.actors.service.ContextBasedCreator;
@@ -26,6 +27,7 @@ import org.thingsboard.server.actors.service.DefaultActorService;
import org.thingsboard.server.common.msg.TbActorMsg;
import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;
import org.thingsboard.server.common.msg.cluster.ServerAddress;
+import org.thingsboard.server.common.msg.cluster.ServerType;
import org.thingsboard.server.gen.cluster.ClusterAPIProtos;
import org.thingsboard.server.service.cluster.discovery.ServerInstance;
@@ -36,15 +38,13 @@ import java.util.*;
*/
public class RpcManagerActor extends ContextAwareActor {
- private final LoggingAdapter log = Logging.getLogger(getContext().system(), this);
-
private final Map sessionActors;
private final Map> pendingMsgs;
private final ServerAddress instance;
- public RpcManagerActor(ActorSystemContext systemContext) {
+ private RpcManagerActor(ActorSystemContext systemContext) {
super(systemContext);
this.sessionActors = new HashMap<>();
this.pendingMsgs = new HashMap<>();
@@ -54,7 +54,6 @@ public class RpcManagerActor extends ContextAwareActor {
.filter(otherServer -> otherServer.getServerAddress().compareTo(instance) > 0)
.forEach(otherServer -> onCreateSessionRequest(
new RpcSessionCreateRequestMsg(UUID.randomUUID(), otherServer.getServerAddress(), null)));
-
}
@Override
@@ -100,24 +99,23 @@ public class RpcManagerActor extends ContextAwareActor {
private void onMsg(ClusterAPIProtos.ClusterMessage msg) {
if (msg.hasServerAddress()) {
- ServerAddress address = new ServerAddress(msg.getServerAddress().getHost(),
- msg.getServerAddress().getPort());
+ ServerAddress address = new ServerAddress(msg.getServerAddress().getHost(), msg.getServerAddress().getPort(), ServerType.CORE);
SessionActorInfo session = sessionActors.get(address);
if (session != null) {
- log.debug("{} Forwarding msg to session actor", address);
+ log.debug("{} Forwarding msg to session actor: {}", address, msg);
session.getActor().tell(msg, ActorRef.noSender());
} else {
- log.debug("{} Storing msg to pending queue", address);
+ log.debug("{} Storing msg to pending queue: {}", address, msg);
Queue queue = pendingMsgs.get(address);
if (queue == null) {
queue = new LinkedList<>();
pendingMsgs.put(new ServerAddress(
- msg.getServerAddress().getHost(), msg.getServerAddress().getPort()), queue);
+ msg.getServerAddress().getHost(), msg.getServerAddress().getPort(), ServerType.CORE), queue);
}
queue.add(msg);
}
} else {
- logger.warning("Cluster msg doesn't have set Server Address [{}]", msg);
+ log.warn("Cluster msg doesn't have server address [{}]", msg);
}
}
@@ -162,9 +160,9 @@ public class RpcManagerActor extends ContextAwareActor {
}
private void onSessionClose(boolean reconnect, ServerAddress remoteAddress) {
- log.debug("[{}] session closed. Should reconnect: {}", remoteAddress, reconnect);
+ log.info("[{}] session closed. Should reconnect: {}", remoteAddress, reconnect);
SessionActorInfo sessionRef = sessionActors.get(remoteAddress);
- if (context().sender() != null && context().sender().equals(sessionRef.actor)) {
+ if (sessionRef != null && context().sender() != null && context().sender().equals(sessionRef.actor)) {
sessionActors.remove(remoteAddress);
pendingMsgs.remove(remoteAddress);
if (reconnect) {
@@ -182,18 +180,18 @@ public class RpcManagerActor extends ContextAwareActor {
private void register(ServerAddress remoteAddress, UUID uuid, ActorRef sender) {
sessionActors.put(remoteAddress, new SessionActorInfo(uuid, sender));
- log.debug("[{}][{}] Registering session actor.", remoteAddress, uuid);
+ log.info("[{}][{}] Registering session actor.", remoteAddress, uuid);
Queue data = pendingMsgs.remove(remoteAddress);
if (data != null) {
- log.debug("[{}][{}] Forwarding {} pending messages.", remoteAddress, uuid, data.size());
+ log.info("[{}][{}] Forwarding {} pending messages.", remoteAddress, uuid, data.size());
data.forEach(msg -> sender.tell(new RpcSessionTellMsg(msg), ActorRef.noSender()));
} else {
- log.debug("[{}][{}] No pending messages to forward.", remoteAddress, uuid);
+ log.info("[{}][{}] No pending messages to forward.", remoteAddress, uuid);
}
}
private ActorRef createSessionActor(RpcSessionCreateRequestMsg msg) {
- log.debug("[{}] Creating session actor.", msg.getMsgUid());
+ log.info("[{}] Creating session actor.", msg.getMsgUid());
ActorRef actor = context().actorOf(
Props.create(new RpcSessionActor.ActorCreator(systemContext, msg.getMsgUid())).withDispatcher(DefaultActorService.RPC_DISPATCHER_NAME));
actor.tell(msg, context().self());
@@ -208,7 +206,7 @@ public class RpcManagerActor extends ContextAwareActor {
}
@Override
- public RpcManagerActor create() throws Exception {
+ public RpcManagerActor create() {
return new RpcManagerActor(context);
}
}
diff --git a/application/src/main/java/org/thingsboard/server/actors/rpc/RpcSessionActor.java b/application/src/main/java/org/thingsboard/server/actors/rpc/RpcSessionActor.java
index c9cf8696e5..86509ca08e 100644
--- a/application/src/main/java/org/thingsboard/server/actors/rpc/RpcSessionActor.java
+++ b/application/src/main/java/org/thingsboard/server/actors/rpc/RpcSessionActor.java
@@ -18,6 +18,7 @@ package org.thingsboard.server.actors.rpc;
import akka.event.Logging;
import akka.event.LoggingAdapter;
import io.grpc.Channel;
+import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.grpc.stub.StreamObserver;
import org.thingsboard.server.actors.ActorSystemContext;
@@ -88,8 +89,8 @@ public class RpcSessionActor extends ContextAwareActor {
systemContext.getRpcService().onSessionCreated(msg.getMsgUid(), session.getInputStream());
} else {
// Client session
- Channel channel = ManagedChannelBuilder.forAddress(remoteServer.getHost(), remoteServer.getPort()).usePlaintext(true).build();
- session = new GrpcSession(remoteServer, listener);
+ ManagedChannel channel = ManagedChannelBuilder.forAddress(remoteServer.getHost(), remoteServer.getPort()).usePlaintext().build();
+ session = new GrpcSession(remoteServer, listener, channel);
session.initInputStream();
ClusterRpcServiceGrpc.ClusterRpcServiceStub stub = ClusterRpcServiceGrpc.newStub(channel);
diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java
index 7279347b1c..0baeceabc3 100644
--- a/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java
+++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java
@@ -17,6 +17,7 @@ package org.thingsboard.server.actors.ruleChain;
import akka.actor.ActorRef;
import com.datastax.driver.core.utils.UUIDs;
+import org.springframework.util.StringUtils;
import org.thingsboard.rule.engine.api.ListeningExecutor;
import org.thingsboard.rule.engine.api.MailService;
import org.thingsboard.rule.engine.api.RuleEngineDeviceRpcRequest;
@@ -35,12 +36,15 @@ import org.thingsboard.server.common.data.rpc.ToDeviceRpcRequestBody;
import org.thingsboard.server.common.data.rule.RuleNode;
import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbMsgMetaData;
+import org.thingsboard.server.common.msg.cluster.ServerAddress;
+import org.thingsboard.server.common.msg.cluster.ServerType;
import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest;
import org.thingsboard.server.dao.alarm.AlarmService;
import org.thingsboard.server.dao.asset.AssetService;
import org.thingsboard.server.dao.attributes.AttributesService;
import org.thingsboard.server.dao.customer.CustomerService;
import org.thingsboard.server.dao.device.DeviceService;
+import org.thingsboard.server.dao.entityview.EntityViewService;
import org.thingsboard.server.dao.relation.RelationService;
import org.thingsboard.server.dao.rule.RuleChainService;
import org.thingsboard.server.dao.tenant.TenantService;
@@ -154,7 +158,7 @@ class DefaultTbContext implements TbContext {
@Override
public ScriptEngine createJsScriptEngine(String script, String... argNames) {
- return new RuleNodeJsScriptEngine(mainCtx.getJsSandbox(), script, argNames);
+ return new RuleNodeJsScriptEngine(mainCtx.getJsSandbox(), nodeCtx.getSelf().getId(), script, argNames);
}
@Override
@@ -212,6 +216,11 @@ class DefaultTbContext implements TbContext {
return mainCtx.getRelationService();
}
+ @Override
+ public EntityViewService getEntityViewService() {
+ return mainCtx.getEntityViewService();
+ }
+
@Override
public MailService getMailService() {
if (mainCtx.isAllowSystemMailService()) {
@@ -226,16 +235,22 @@ class DefaultTbContext implements TbContext {
return new RuleEngineRpcService() {
@Override
public void sendRpcReply(DeviceId deviceId, int requestId, String body) {
- mainCtx.getDeviceRpcService().sendRpcReplyToDevice(nodeCtx.getTenantId(), deviceId, requestId, body);
+ mainCtx.getDeviceRpcService().sendReplyToRpcCallFromDevice(nodeCtx.getTenantId(), deviceId, requestId, body);
}
@Override
public void sendRpcRequest(RuleEngineDeviceRpcRequest src, Consumer consumer) {
ToDeviceRpcRequest request = new ToDeviceRpcRequest(src.getRequestUUID(), nodeCtx.getTenantId(), src.getDeviceId(),
src.isOneway(), src.getExpirationTime(), new ToDeviceRpcRequestBody(src.getMethod(), src.getBody()));
- mainCtx.getDeviceRpcService().processRpcRequestToDevice(request, response -> {
+ mainCtx.getDeviceRpcService().forwardServerSideRPCRequestToDeviceActor(request, response -> {
if (src.isRestApiCall()) {
- mainCtx.getDeviceRpcService().processRestAPIRpcResponseFromRuleEngine(response);
+ ServerAddress requestOriginAddress;
+ if (!StringUtils.isEmpty(src.getOriginHost())) {
+ requestOriginAddress = new ServerAddress(src.getOriginHost(), src.getOriginPort(), ServerType.CORE);
+ } else {
+ requestOriginAddress = mainCtx.getRoutingService().getCurrentServer();
+ }
+ mainCtx.getDeviceRpcService().processResponseToServerSideRPCRequestFromRuleEngine(requestOriginAddress, response);
}
consumer.accept(RuleEngineDeviceRpcResponse.builder()
.deviceId(src.getDeviceId())
diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActor.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActor.java
index dbad7c0b66..be393204ed 100644
--- a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActor.java
+++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActor.java
@@ -15,6 +15,7 @@
*/
package org.thingsboard.server.actors.ruleChain;
+import akka.actor.ActorInitializationException;
import akka.actor.OneForOneStrategy;
import akka.actor.SupervisorStrategy;
import org.thingsboard.server.actors.ActorSystemContext;
@@ -33,7 +34,7 @@ public class RuleChainActor extends ComponentActor {
private static final long DEFAULT_CLUSTER_PARTITION = 0L;
@@ -68,59 +69,58 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor();
this.nodeRoutes = new HashMap<>();
this.service = systemContext.getRuleChainService();
+ this.ruleChainName = ruleChainId.toString();
}
@Override
- public void start(ActorContext context) throws Exception {
+ public String getComponentName() {
+ return null;
+ }
+
+ @Override
+ public void start(ActorContext context) {
if (!started) {
RuleChain ruleChain = service.findRuleChainById(entityId);
+ ruleChainName = ruleChain.getName();
List ruleNodeList = service.getRuleChainNodes(entityId);
+ log.trace("[{}][{}] Starting rule chain with {} nodes", tenantId, entityId, ruleNodeList.size());
// Creating and starting the actors;
for (RuleNode ruleNode : ruleNodeList) {
+ log.trace("[{}][{}] Creating rule node [{}]: {}", entityId, ruleNode.getId(), ruleNode.getName(), ruleNode);
ActorRef ruleNodeActor = createRuleNodeActor(context, ruleNode);
nodeActors.put(ruleNode.getId(), new RuleNodeCtx(tenantId, self, ruleNodeActor, ruleNode));
}
initRoutes(ruleChain, ruleNodeList);
- reprocess(ruleNodeList);
started = true;
} else {
onUpdate(context);
}
}
- private void reprocess(List ruleNodeList) {
- for (RuleNode ruleNode : ruleNodeList) {
- for (TbMsg tbMsg : queue.findUnprocessed(tenantId, ruleNode.getId().getId(), systemContext.getQueuePartitionId())) {
- pushMsgToNode(nodeActors.get(ruleNode.getId()), tbMsg, "");
- }
- }
- if (firstNode != null) {
- for (TbMsg tbMsg : queue.findUnprocessed(tenantId, entityId.getId(), systemContext.getQueuePartitionId())) {
- pushMsgToNode(firstNode, tbMsg, "");
- }
- }
- }
-
@Override
- public void onUpdate(ActorContext context) throws Exception {
+ public void onUpdate(ActorContext context) {
RuleChain ruleChain = service.findRuleChainById(entityId);
+ ruleChainName = ruleChain.getName();
List ruleNodeList = service.getRuleChainNodes(entityId);
-
+ log.trace("[{}][{}] Updating rule chain with {} nodes", tenantId, entityId, ruleNodeList.size());
for (RuleNode ruleNode : ruleNodeList) {
RuleNodeCtx existing = nodeActors.get(ruleNode.getId());
if (existing == null) {
+ log.trace("[{}][{}] Creating rule node [{}]: {}", entityId, ruleNode.getId(), ruleNode.getName(), ruleNode);
ActorRef ruleNodeActor = createRuleNodeActor(context, ruleNode);
nodeActors.put(ruleNode.getId(), new RuleNodeCtx(tenantId, self, ruleNodeActor, ruleNode));
} else {
+ log.trace("[{}][{}] Updating rule node [{}]: {}", entityId, ruleNode.getId(), ruleNode.getName(), ruleNode);
existing.setSelf(ruleNode);
existing.getSelfActor().tell(new ComponentLifecycleMsg(tenantId, existing.getSelf().getId(), ComponentLifecycleEvent.UPDATED), self);
}
@@ -129,16 +129,17 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor existingNodes = ruleNodeList.stream().map(RuleNode::getId).collect(Collectors.toSet());
List removedRules = nodeActors.keySet().stream().filter(node -> !existingNodes.contains(node)).collect(Collectors.toList());
removedRules.forEach(ruleNodeId -> {
+ log.trace("[{}][{}] Removing rule node [{}]", tenantId, entityId, ruleNodeId);
RuleNodeCtx removed = nodeActors.remove(ruleNodeId);
removed.getSelfActor().tell(new ComponentLifecycleMsg(tenantId, removed.getSelf().getId(), ComponentLifecycleEvent.DELETED), self);
});
initRoutes(ruleChain, ruleNodeList);
- reprocess(ruleNodeList);
}
@Override
- public void stop(ActorContext context) throws Exception {
+ public void stop(ActorContext context) {
+ log.trace("[{}][{}] Stopping rule chain with {} nodes", tenantId, entityId, nodeActors.size());
nodeActors.values().stream().map(RuleNodeCtx::getSelfActor).forEach(context::stop);
nodeActors.clear();
nodeRoutes.clear();
@@ -147,7 +148,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor relations = service.getRuleNodeRelations(ruleNode.getId());
+ log.trace("[{}][{}][{}] Processing rule node relations [{}]", tenantId, entityId, ruleNode.getId(), relations.size());
if (relations.size() == 0) {
nodeRoutes.put(ruleNode.getId(), Collections.emptyList());
} else {
for (EntityRelation relation : relations) {
+ log.trace("[{}][{}][{}] Processing rule node relation [{}]", tenantId, entityId, ruleNode.getId(), relation.getTo());
if (relation.getTo().getEntityType() == EntityType.RULE_NODE) {
RuleNodeCtx ruleNodeCtx = nodeActors.get(new RuleNodeId(relation.getTo().getId()));
if (ruleNodeCtx == null) {
@@ -181,24 +184,23 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor pushMsgToNode(firstNode, msg, ""));
+ log.trace("[{}][{}] Pushing message to first rule node", entityId, firstId);
+ pushMsgToNode(firstNode, enrichWithRuleChainId(envelope.getTbMsg()), "");
}
}
void onDeviceActorToRuleEngineMsg(DeviceActorToRuleEngineMsg envelope) {
checkActive();
if (firstNode != null) {
- putToQueue(enrichWithRuleChainId(envelope.getTbMsg()), msg -> {
- pushMsgToNode(firstNode, msg, "");
- envelope.getCallbackRef().tell(new RuleEngineQueuePutAckMsg(msg.getId()), self);
- });
+ pushMsgToNode(firstNode, enrichWithRuleChainId(envelope.getTbMsg()), "");
}
}
@@ -206,15 +208,16 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor pushMsgToNode(firstNode, msg, envelope.getFromRelationType()));
+ pushMsgToNode(firstNode, enrichWithRuleChainId(envelope.getMsg()), envelope.getFromRelationType());
}
} else {
if (firstNode != null) {
pushMsgToNode(firstNode, envelope.getMsg(), envelope.getFromRelationType());
} else {
- TbMsg msg = envelope.getMsg();
- EntityId ackId = msg.getRuleNodeId() != null ? msg.getRuleNodeId() : msg.getRuleChainId();
- queue.ack(tenantId, envelope.getMsg(), ackId.getId(), msg.getClusterPartition());
+// TODO: Ack this message in Kafka
+// TbMsg msg = envelope.getMsg();
+// EntityId ackId = msg.getRuleNodeId() != null ? msg.getRuleNodeId() : msg.getRuleChainId();
+// queue.ack(tenantId, envelope.getMsg(), ackId.getId(), msg.getClusterPartition());
}
}
}
@@ -234,7 +237,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor pushMsgToNode(targetNodeCtx, queuedMsg, fromRelationType));
+ pushMsgToNode(targetNodeCtx, copy, fromRelationType);
}
private void pushToTarget(TbMsg msg, EntityId target, String fromRelationType) {
diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeActor.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeActor.java
index 273a569cdc..f5521a03f8 100644
--- a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeActor.java
+++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeActor.java
@@ -32,7 +32,7 @@ public class RuleNodeActor extends ComponentActor componentClazz = Class.forName(ruleNode.getType());
TbNode tbNode = (TbNode) (componentClazz.newInstance());
diff --git a/application/src/main/java/org/thingsboard/server/actors/service/ActorService.java b/application/src/main/java/org/thingsboard/server/actors/service/ActorService.java
index f7e80e4baa..08d1dd8a11 100644
--- a/application/src/main/java/org/thingsboard/server/actors/service/ActorService.java
+++ b/application/src/main/java/org/thingsboard/server/actors/service/ActorService.java
@@ -35,5 +35,4 @@ public interface ActorService extends SessionMsgProcessor, RpcMsgListener, Disco
void onDeviceNameOrTypeUpdate(TenantId tenantId, DeviceId deviceId, String deviceName, String deviceType);
- void onMsg(ServiceToRuleEngineMsg serviceToRuleEngineMsg);
}
diff --git a/application/src/main/java/org/thingsboard/server/actors/service/ComponentActor.java b/application/src/main/java/org/thingsboard/server/actors/service/ComponentActor.java
index ed5905179f..1f084e1bb7 100644
--- a/application/src/main/java/org/thingsboard/server/actors/service/ComponentActor.java
+++ b/application/src/main/java/org/thingsboard/server/actors/service/ComponentActor.java
@@ -18,6 +18,7 @@ package org.thingsboard.server.actors.service;
import akka.actor.ActorRef;
import akka.event.Logging;
import akka.event.LoggingAdapter;
+import lombok.extern.slf4j.Slf4j;
import org.thingsboard.server.actors.ActorSystemContext;
import org.thingsboard.server.actors.shared.ComponentMsgProcessor;
import org.thingsboard.server.actors.stats.StatsPersistMsg;
@@ -32,8 +33,6 @@ import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
*/
public abstract class ComponentActor> extends ContextAwareActor {
- protected final LoggingAdapter logger = Logging.getLogger(getContext().system(), this);
-
private long lastPersistedErrorTs = 0L;
protected final TenantId tenantId;
protected final T id;
@@ -54,13 +53,14 @@ public abstract class ComponentActor getErrorPersistFrequency()) {
diff --git a/application/src/main/java/org/thingsboard/server/actors/service/ContextAwareActor.java b/application/src/main/java/org/thingsboard/server/actors/service/ContextAwareActor.java
index 3624127194..1c7e2d2664 100644
--- a/application/src/main/java/org/thingsboard/server/actors/service/ContextAwareActor.java
+++ b/application/src/main/java/org/thingsboard/server/actors/service/ContextAwareActor.java
@@ -15,14 +15,17 @@
*/
package org.thingsboard.server.actors.service;
+import akka.actor.Terminated;
import akka.actor.UntypedActor;
-import akka.event.Logging;
-import akka.event.LoggingAdapter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.thingsboard.server.actors.ActorSystemContext;
import org.thingsboard.server.common.msg.TbActorMsg;
+
public abstract class ContextAwareActor extends UntypedActor {
- protected final LoggingAdapter logger = Logging.getLogger(getContext().system(), this);
+
+ protected final Logger log = LoggerFactory.getLogger(getClass());
public static final int ENTITY_PACK_LIMIT = 1024;
@@ -35,21 +38,26 @@ public abstract class ContextAwareActor extends UntypedActor {
@Override
public void onReceive(Object msg) throws Exception {
- if (logger.isDebugEnabled()) {
- logger.debug("Processing msg: {}", msg);
+ if (log.isDebugEnabled()) {
+ log.debug("Processing msg: {}", msg);
}
if (msg instanceof TbActorMsg) {
try {
if (!process((TbActorMsg) msg)) {
- logger.warning("Unknown message: {}!", msg);
+ log.warn("Unknown message: {}!", msg);
}
} catch (Exception e) {
throw e;
}
+ } else if (msg instanceof Terminated) {
+ processTermination((Terminated) msg);
} else {
- logger.warning("Unknown message: {}!", msg);
+ log.warn("Unknown message: {}!", msg);
}
}
+ protected void processTermination(Terminated msg) {
+ }
+
protected abstract boolean process(TbActorMsg msg);
}
diff --git a/application/src/main/java/org/thingsboard/server/actors/service/DefaultActorService.java b/application/src/main/java/org/thingsboard/server/actors/service/DefaultActorService.java
index fac5d97175..85b8943569 100644
--- a/application/src/main/java/org/thingsboard/server/actors/service/DefaultActorService.java
+++ b/application/src/main/java/org/thingsboard/server/actors/service/DefaultActorService.java
@@ -30,7 +30,6 @@ import org.thingsboard.server.actors.app.AppActor;
import org.thingsboard.server.actors.rpc.RpcBroadcastMsg;
import org.thingsboard.server.actors.rpc.RpcManagerActor;
import org.thingsboard.server.actors.rpc.RpcSessionCreateRequestMsg;
-import org.thingsboard.server.actors.session.SessionManagerActor;
import org.thingsboard.server.actors.stats.StatsActor;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.id.DeviceId;
@@ -38,13 +37,11 @@ import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
import org.thingsboard.server.common.msg.TbActorMsg;
-import org.thingsboard.server.common.msg.aware.SessionAwareMsg;
import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;
import org.thingsboard.server.common.msg.cluster.SendToClusterMsg;
import org.thingsboard.server.common.msg.cluster.ServerAddress;
import org.thingsboard.server.common.msg.cluster.ToAllNodesMsg;
import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
-import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg;
import org.thingsboard.server.gen.cluster.ClusterAPIProtos;
import org.thingsboard.server.service.cluster.discovery.DiscoveryService;
import org.thingsboard.server.service.cluster.discovery.ServerInstance;
@@ -68,10 +65,7 @@ public class DefaultActorService implements ActorService {
public static final String APP_DISPATCHER_NAME = "app-dispatcher";
public static final String CORE_DISPATCHER_NAME = "core-dispatcher";
public static final String SYSTEM_RULE_DISPATCHER_NAME = "system-rule-dispatcher";
- public static final String SYSTEM_PLUGIN_DISPATCHER_NAME = "system-plugin-dispatcher";
public static final String TENANT_RULE_DISPATCHER_NAME = "rule-dispatcher";
- public static final String TENANT_PLUGIN_DISPATCHER_NAME = "plugin-dispatcher";
- public static final String SESSION_DISPATCHER_NAME = "session-dispatcher";
public static final String RPC_DISPATCHER_NAME = "rpc-dispatcher";
@Autowired
@@ -90,8 +84,6 @@ public class DefaultActorService implements ActorService {
private ActorRef appActor;
- private ActorRef sessionManagerActor;
-
private ActorRef rpcManagerActor;
@PostConstruct
@@ -104,10 +96,6 @@ public class DefaultActorService implements ActorService {
appActor = system.actorOf(Props.create(new AppActor.ActorCreator(actorContext)).withDispatcher(APP_DISPATCHER_NAME), "appActor");
actorContext.setAppActor(appActor);
- sessionManagerActor = system.actorOf(Props.create(new SessionManagerActor.ActorCreator(actorContext)).withDispatcher(CORE_DISPATCHER_NAME),
- "sessionManagerActor");
- actorContext.setSessionManagerActor(sessionManagerActor);
-
rpcManagerActor = system.actorOf(Props.create(new RpcManagerActor.ActorCreator(actorContext)).withDispatcher(CORE_DISPATCHER_NAME),
"rpcManagerActor");
@@ -115,8 +103,6 @@ public class DefaultActorService implements ActorService {
actorContext.setStatsActor(statsActor);
rpcService.init(this);
-
- discoveryService.addListener(this);
log.info("Actor system initialized.");
}
@@ -136,12 +122,6 @@ public class DefaultActorService implements ActorService {
appActor.tell(msg, ActorRef.noSender());
}
- @Override
- public void process(SessionAwareMsg msg) {
- log.debug("Processing session aware msg: {}", msg);
- sessionManagerActor.tell(msg, ActorRef.noSender());
- }
-
@Override
public void onServerAdded(ServerInstance server) {
log.trace("Processing onServerAdded msg: {}", server);
@@ -178,11 +158,6 @@ public class DefaultActorService implements ActorService {
appActor.tell(new SendToClusterMsg(deviceId, msg), ActorRef.noSender());
}
- @Override
- public void onMsg(ServiceToRuleEngineMsg msg) {
- appActor.tell(msg, ActorRef.noSender());
- }
-
public void broadcast(ToAllNodesMsg msg) {
actorContext.getEncodingService().encode(msg);
rpcService.broadcast(new RpcBroadcastMsg(ClusterAPIProtos.ClusterMessage
@@ -196,16 +171,15 @@ public class DefaultActorService implements ActorService {
private void broadcast(ClusterEventMsg msg) {
this.appActor.tell(msg, ActorRef.noSender());
- this.sessionManagerActor.tell(msg, ActorRef.noSender());
this.rpcManagerActor.tell(msg, ActorRef.noSender());
}
@Override
public void onReceivedMsg(ServerAddress source, ClusterAPIProtos.ClusterMessage msg) {
- ServerAddress serverAddress = new ServerAddress(source.getHost(), source.getPort());
- log.info("Received msg [{}] from [{}]", msg.getMessageType().name(), serverAddress);
+ ServerAddress serverAddress = new ServerAddress(source.getHost(), source.getPort(), source.getServerType());
if (log.isDebugEnabled()) {
- log.info("MSG: ", msg);
+ log.info("Received msg [{}] from [{}]", msg.getMessageType().name(), serverAddress);
+ log.info("MSG: {}", msg);
}
switch (msg.getMessageType()) {
case CLUSTER_ACTOR_MESSAGE:
@@ -239,7 +213,7 @@ public class DefaultActorService implements ActorService {
actorContext.getTsSubService().onRemoteTsUpdate(serverAddress, msg.getPayload().toByteArray());
break;
case CLUSTER_RPC_FROM_DEVICE_RESPONSE_MESSAGE:
- actorContext.getDeviceRpcService().processRemoteResponseFromDevice(serverAddress, msg.getPayload().toByteArray());
+ actorContext.getDeviceRpcService().processResponseToServerSideRPCRequestFromRemoteServer(serverAddress, msg.getPayload().toByteArray());
break;
case CLUSTER_DEVICE_STATE_SERVICE_MESSAGE:
actorContext.getDeviceStateService().onRemoteMsg(serverAddress, msg.getPayload().toByteArray());
diff --git a/application/src/main/java/org/thingsboard/server/actors/session/ASyncMsgProcessor.java b/application/src/main/java/org/thingsboard/server/actors/session/ASyncMsgProcessor.java
deleted file mode 100644
index a8f14fedf5..0000000000
--- a/application/src/main/java/org/thingsboard/server/actors/session/ASyncMsgProcessor.java
+++ /dev/null
@@ -1,156 +0,0 @@
-/**
- * Copyright © 2016-2018 The Thingsboard Authors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.thingsboard.server.actors.session;
-
-import akka.actor.ActorContext;
-import akka.event.LoggingAdapter;
-import org.thingsboard.server.actors.ActorSystemContext;
-import org.thingsboard.server.actors.shared.SessionTimeoutMsg;
-import org.thingsboard.server.common.data.id.SessionId;
-import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;
-import org.thingsboard.server.common.msg.cluster.ServerAddress;
-import org.thingsboard.server.common.msg.core.AttributesSubscribeMsg;
-import org.thingsboard.server.common.msg.core.ResponseMsg;
-import org.thingsboard.server.common.msg.core.RpcSubscribeMsg;
-import org.thingsboard.server.common.msg.core.SessionCloseMsg;
-import org.thingsboard.server.common.msg.core.SessionOpenMsg;
-import org.thingsboard.server.common.msg.device.DeviceToDeviceActorMsg;
-import org.thingsboard.server.common.msg.session.BasicSessionActorToAdaptorMsg;
-import org.thingsboard.server.common.msg.session.FromDeviceMsg;
-import org.thingsboard.server.common.msg.session.FromDeviceRequestMsg;
-import org.thingsboard.server.common.msg.session.SessionMsgType;
-import org.thingsboard.server.common.msg.session.SessionType;
-import org.thingsboard.server.common.msg.session.ToDeviceMsg;
-import org.thingsboard.server.common.msg.session.TransportToDeviceSessionActorMsg;
-import org.thingsboard.server.common.msg.session.ex.SessionException;
-
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Optional;
-
-class ASyncMsgProcessor extends AbstractSessionActorMsgProcessor {
-
- private boolean firstMsg = true;
- private Map pendingMap = new HashMap<>();
- private Optional currentTargetServer;
- private boolean subscribedToAttributeUpdates;
- private boolean subscribedToRpcCommands;
-
- public ASyncMsgProcessor(ActorSystemContext ctx, LoggingAdapter logger, SessionId sessionId) {
- super(ctx, logger, sessionId);
- }
-
- @Override
- protected void processToDeviceActorMsg(ActorContext ctx, TransportToDeviceSessionActorMsg msg) {
- updateSessionCtx(msg, SessionType.ASYNC);
- DeviceToDeviceActorMsg pendingMsg = toDeviceMsg(msg);
- FromDeviceMsg fromDeviceMsg = pendingMsg.getPayload();
- if (firstMsg) {
- if (fromDeviceMsg.getMsgType() != SessionMsgType.SESSION_OPEN) {
- toDeviceMsg(new SessionOpenMsg()).ifPresent(m -> forwardToAppActor(ctx, m));
- }
- firstMsg = false;
- }
- switch (fromDeviceMsg.getMsgType()) {
- case POST_TELEMETRY_REQUEST:
- case POST_ATTRIBUTES_REQUEST:
- FromDeviceRequestMsg requestMsg = (FromDeviceRequestMsg) fromDeviceMsg;
- if (requestMsg.getRequestId() >= 0) {
- logger.debug("[{}] Pending request {} registered", requestMsg.getRequestId(), requestMsg.getMsgType());
- //TODO: handle duplicates.
- pendingMap.put(requestMsg.getRequestId(), pendingMsg);
- }
- break;
- case SUBSCRIBE_ATTRIBUTES_REQUEST:
- subscribedToAttributeUpdates = true;
- break;
- case UNSUBSCRIBE_ATTRIBUTES_REQUEST:
- subscribedToAttributeUpdates = false;
- break;
- case SUBSCRIBE_RPC_COMMANDS_REQUEST:
- subscribedToRpcCommands = true;
- break;
- case UNSUBSCRIBE_RPC_COMMANDS_REQUEST:
- subscribedToRpcCommands = false;
- break;
- default:
- break;
- }
- currentTargetServer = forwardToAppActor(ctx, pendingMsg);
- }
-
- @Override
- public void processToDeviceMsg(ActorContext context, ToDeviceMsg msg) {
- try {
- if (msg.getSessionMsgType() != SessionMsgType.SESSION_CLOSE) {
- switch (msg.getSessionMsgType()) {
- case STATUS_CODE_RESPONSE:
- case GET_ATTRIBUTES_RESPONSE:
- ResponseMsg responseMsg = (ResponseMsg) msg;
- if (responseMsg.getRequestId() >= 0) {
- logger.debug("[{}] Pending request processed: {}", responseMsg.getRequestId(), responseMsg);
- pendingMap.remove(responseMsg.getRequestId());
- }
- break;
- default:
- break;
- }
- sessionCtx.onMsg(new BasicSessionActorToAdaptorMsg(this.sessionCtx, msg));
- } else {
- sessionCtx.onMsg(org.thingsboard.server.common.msg.session.ctrl.SessionCloseMsg.onCredentialsRevoked(sessionCtx.getSessionId()));
- }
- } catch (SessionException e) {
- logger.warning("Failed to push session response msg", e);
- }
- }
-
- @Override
- public void processTimeoutMsg(ActorContext context, SessionTimeoutMsg msg) {
- // TODO Auto-generated method stub
- }
-
- @Override
- protected void cleanupSession(ActorContext ctx) {
- toDeviceMsg(new SessionCloseMsg()).ifPresent(m -> forwardToAppActor(ctx, m));
- }
-
- @Override
- public void processClusterEvent(ActorContext context, ClusterEventMsg msg) {
- if (pendingMap.size() > 0 || subscribedToAttributeUpdates || subscribedToRpcCommands) {
- Optional newTargetServer = systemContext.getRoutingService().resolveById(getDeviceId());
- if (!newTargetServer.equals(currentTargetServer)) {
- firstMsg = true;
- currentTargetServer = newTargetServer;
- pendingMap.values().forEach(v -> {
- forwardToAppActor(context, v, currentTargetServer);
- if (currentTargetServer.isPresent()) {
- logger.debug("[{}] Forwarded msg to new server: {}", sessionId, currentTargetServer.get());
- } else {
- logger.debug("[{}] Forwarded msg to local server.", sessionId);
- }
- });
- if (subscribedToAttributeUpdates) {
- toDeviceMsg(new AttributesSubscribeMsg()).ifPresent(m -> forwardToAppActor(context, m, currentTargetServer));
- logger.debug("[{}] Forwarded attributes subscription.", sessionId);
- }
- if (subscribedToRpcCommands) {
- toDeviceMsg(new RpcSubscribeMsg()).ifPresent(m -> forwardToAppActor(context, m, currentTargetServer));
- logger.debug("[{}] Forwarded rpc commands subscription.", sessionId);
- }
- }
- }
- }
-}
diff --git a/application/src/main/java/org/thingsboard/server/actors/session/AbstractSessionActorMsgProcessor.java b/application/src/main/java/org/thingsboard/server/actors/session/AbstractSessionActorMsgProcessor.java
deleted file mode 100644
index 469cda9c5f..0000000000
--- a/application/src/main/java/org/thingsboard/server/actors/session/AbstractSessionActorMsgProcessor.java
+++ /dev/null
@@ -1,122 +0,0 @@
-/**
- * Copyright © 2016-2018 The Thingsboard Authors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.thingsboard.server.actors.session;
-
-import akka.actor.ActorContext;
-import akka.actor.ActorRef;
-import akka.event.LoggingAdapter;
-import org.thingsboard.server.actors.ActorSystemContext;
-import org.thingsboard.server.actors.shared.AbstractContextAwareMsgProcessor;
-import org.thingsboard.server.actors.shared.SessionTimeoutMsg;
-import org.thingsboard.server.common.data.id.DeviceId;
-import org.thingsboard.server.common.data.id.SessionId;
-import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;
-import org.thingsboard.server.common.msg.cluster.SendToClusterMsg;
-import org.thingsboard.server.common.msg.cluster.ServerAddress;
-import org.thingsboard.server.common.msg.device.BasicDeviceToDeviceActorMsg;
-import org.thingsboard.server.common.msg.device.DeviceToDeviceActorMsg;
-import org.thingsboard.server.common.msg.session.AdaptorToSessionActorMsg;
-import org.thingsboard.server.common.msg.session.FromDeviceMsg;
-import org.thingsboard.server.common.msg.session.SessionContext;
-import org.thingsboard.server.common.msg.session.SessionCtrlMsg;
-import org.thingsboard.server.common.msg.session.SessionType;
-import org.thingsboard.server.common.msg.session.ToDeviceMsg;
-import org.thingsboard.server.common.msg.session.TransportToDeviceSessionActorMsg;
-import org.thingsboard.server.common.msg.session.ctrl.SessionCloseMsg;
-
-import java.util.Optional;
-
-abstract class AbstractSessionActorMsgProcessor extends AbstractContextAwareMsgProcessor {
-
- protected final SessionId sessionId;
- protected SessionContext sessionCtx;
- protected DeviceToDeviceActorMsg deviceToDeviceActorMsgPrototype;
-
- protected AbstractSessionActorMsgProcessor(ActorSystemContext ctx, LoggingAdapter logger, SessionId sessionId) {
- super(ctx, logger);
- this.sessionId = sessionId;
- }
-
- protected abstract void processToDeviceActorMsg(ActorContext ctx, TransportToDeviceSessionActorMsg msg);
-
- protected abstract void processTimeoutMsg(ActorContext context, SessionTimeoutMsg msg);
-
- protected abstract void processToDeviceMsg(ActorContext context, ToDeviceMsg msg);
-
- public abstract void processClusterEvent(ActorContext context, ClusterEventMsg msg);
-
- protected void processSessionCtrlMsg(ActorContext ctx, SessionCtrlMsg msg) {
- if (msg instanceof SessionCloseMsg) {
- cleanupSession(ctx);
- terminateSession(ctx, sessionId);
- }
- }
-
- protected void cleanupSession(ActorContext ctx) {
- }
-
- protected void updateSessionCtx(TransportToDeviceSessionActorMsg msg, SessionType type) {
- sessionCtx = msg.getSessionMsg().getSessionContext();
- deviceToDeviceActorMsgPrototype = new BasicDeviceToDeviceActorMsg(msg, type);
- }
-
- protected DeviceToDeviceActorMsg toDeviceMsg(TransportToDeviceSessionActorMsg msg) {
- AdaptorToSessionActorMsg adaptorMsg = msg.getSessionMsg();
- return new BasicDeviceToDeviceActorMsg(deviceToDeviceActorMsgPrototype, adaptorMsg.getMsg());
- }
-
- protected Optional toDeviceMsg(FromDeviceMsg msg) {
- if (deviceToDeviceActorMsgPrototype != null) {
- return Optional.of(new BasicDeviceToDeviceActorMsg(deviceToDeviceActorMsgPrototype, msg));
- } else {
- return Optional.empty();
- }
- }
-
- protected Optional forwardToAppActor(ActorContext ctx, DeviceToDeviceActorMsg toForward) {
- Optional address = systemContext.getRoutingService().resolveById(toForward.getDeviceId());
- forwardToAppActor(ctx, toForward, address);
- return address;
- }
-
- protected Optional forwardToAppActorIfAddressChanged(ActorContext ctx, DeviceToDeviceActorMsg toForward, Optional oldAddress) {
-
- Optional newAddress = systemContext.getRoutingService().resolveById(toForward.getDeviceId());
- if (!newAddress.equals(oldAddress)) {
- getAppActor().tell(new SendToClusterMsg(toForward.getDeviceId(), toForward
- .toOtherAddress(systemContext.getRoutingService().getCurrentServer())), ctx.self());
- }
- return newAddress;
- }
-
- protected void forwardToAppActor(ActorContext ctx, DeviceToDeviceActorMsg toForward, Optional address) {
- if (address.isPresent()) {
- systemContext.getRpcService().tell(systemContext.getEncodingService().convertToProtoDataMessage(address.get(),
- toForward.toOtherAddress(systemContext.getRoutingService().getCurrentServer())));
- } else {
- getAppActor().tell(toForward, ctx.self());
- }
- }
-
- public static void terminateSession(ActorContext ctx, SessionId sessionId) {
- ctx.parent().tell(new SessionTerminationMsg(sessionId), ActorRef.noSender());
- ctx.stop(ctx.self());
- }
-
- public DeviceId getDeviceId() {
- return deviceToDeviceActorMsgPrototype.getDeviceId();
- }
-}
diff --git a/application/src/main/java/org/thingsboard/server/actors/session/SessionActor.java b/application/src/main/java/org/thingsboard/server/actors/session/SessionActor.java
deleted file mode 100644
index 05926c1825..0000000000
--- a/application/src/main/java/org/thingsboard/server/actors/session/SessionActor.java
+++ /dev/null
@@ -1,143 +0,0 @@
-/**
- * Copyright © 2016-2018 The Thingsboard Authors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.thingsboard.server.actors.session;
-
-import akka.actor.OneForOneStrategy;
-import akka.actor.SupervisorStrategy;
-import akka.event.Logging;
-import akka.event.LoggingAdapter;
-import org.thingsboard.server.actors.ActorSystemContext;
-import org.thingsboard.server.actors.service.ContextAwareActor;
-import org.thingsboard.server.actors.service.ContextBasedCreator;
-import org.thingsboard.server.actors.shared.SessionTimeoutMsg;
-import org.thingsboard.server.common.data.id.SessionId;
-import org.thingsboard.server.common.msg.TbActorMsg;
-import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;
-import org.thingsboard.server.common.msg.core.ActorSystemToDeviceSessionActorMsg;
-import org.thingsboard.server.common.msg.session.SessionCtrlMsg;
-import org.thingsboard.server.common.msg.session.SessionMsg;
-import org.thingsboard.server.common.msg.session.SessionType;
-import org.thingsboard.server.common.msg.session.TransportToDeviceSessionActorMsg;
-import org.thingsboard.server.common.msg.session.ctrl.SessionCloseMsg;
-import scala.concurrent.duration.Duration;
-
-public class SessionActor extends ContextAwareActor {
-
- private final LoggingAdapter logger = Logging.getLogger(getContext().system(), this);
-
- private final SessionId sessionId;
- private AbstractSessionActorMsgProcessor processor;
-
- private SessionActor(ActorSystemContext systemContext, SessionId sessionId) {
- super(systemContext);
- this.sessionId = sessionId;
- }
-
- @Override
- public SupervisorStrategy supervisorStrategy() {
- return new OneForOneStrategy(-1, Duration.Inf(),
- throwable -> {
- logger.error(throwable, "Unknown session error");
- if (throwable instanceof Error) {
- return OneForOneStrategy.escalate();
- } else {
- return OneForOneStrategy.resume();
- }
- });
- }
-
- @Override
- protected boolean process(TbActorMsg msg) {
- switch (msg.getMsgType()) {
- case TRANSPORT_TO_DEVICE_SESSION_ACTOR_MSG:
- processTransportToSessionMsg((TransportToDeviceSessionActorMsg) msg);
- break;
- case ACTOR_SYSTEM_TO_DEVICE_SESSION_ACTOR_MSG:
- processActorsToSessionMsg((ActorSystemToDeviceSessionActorMsg) msg);
- break;
- case SESSION_TIMEOUT_MSG:
- processTimeoutMsg((SessionTimeoutMsg) msg);
- break;
- case SESSION_CTRL_MSG:
- processSessionCloseMsg((SessionCtrlMsg) msg);
- break;
- case CLUSTER_EVENT_MSG:
- processClusterEvent((ClusterEventMsg) msg);
- break;
- default: return false;
- }
- return true;
- }
-
- private void processClusterEvent(ClusterEventMsg msg) {
- processor.processClusterEvent(context(), msg);
- }
-
- private void processTransportToSessionMsg(TransportToDeviceSessionActorMsg msg) {
- initProcessor(msg);
- processor.processToDeviceActorMsg(context(), msg);
- }
-
- private void processActorsToSessionMsg(ActorSystemToDeviceSessionActorMsg msg) {
- processor.processToDeviceMsg(context(), msg.getMsg());
- }
-
- private void processTimeoutMsg(SessionTimeoutMsg msg) {
- if (processor != null) {
- processor.processTimeoutMsg(context(), msg);
- } else {
- logger.warning("[{}] Can't process timeout msg: {} without processor", sessionId, msg);
- }
- }
-
- private void processSessionCloseMsg(SessionCtrlMsg msg) {
- if (processor != null) {
- processor.processSessionCtrlMsg(context(), msg);
- } else if (msg instanceof SessionCloseMsg) {
- AbstractSessionActorMsgProcessor.terminateSession(context(), sessionId);
- } else {
- logger.warning("[{}] Can't process session ctrl msg: {} without processor", sessionId, msg);
- }
- }
-
- private void initProcessor(TransportToDeviceSessionActorMsg msg) {
- if (processor == null) {
- SessionMsg sessionMsg = (SessionMsg) msg.getSessionMsg();
- if (sessionMsg.getSessionContext().getSessionType() == SessionType.SYNC) {
- processor = new SyncMsgProcessor(systemContext, logger, sessionId);
- } else {
- processor = new ASyncMsgProcessor(systemContext, logger, sessionId);
- }
- }
- }
-
- public static class ActorCreator extends ContextBasedCreator {
- private static final long serialVersionUID = 1L;
-
- private final SessionId sessionId;
-
- public ActorCreator(ActorSystemContext context, SessionId sessionId) {
- super(context);
- this.sessionId = sessionId;
- }
-
- @Override
- public SessionActor create() throws Exception {
- return new SessionActor(context, sessionId);
- }
- }
-
-}
diff --git a/application/src/main/java/org/thingsboard/server/actors/session/SessionManagerActor.java b/application/src/main/java/org/thingsboard/server/actors/session/SessionManagerActor.java
deleted file mode 100644
index 9039d302e3..0000000000
--- a/application/src/main/java/org/thingsboard/server/actors/session/SessionManagerActor.java
+++ /dev/null
@@ -1,180 +0,0 @@
-/**
- * Copyright © 2016-2018 The Thingsboard Authors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.thingsboard.server.actors.session;
-
-import akka.actor.ActorInitializationException;
-import akka.actor.ActorRef;
-import akka.actor.InvalidActorNameException;
-import akka.actor.LocalActorRef;
-import akka.actor.OneForOneStrategy;
-import akka.actor.Props;
-import akka.actor.SupervisorStrategy;
-import akka.actor.Terminated;
-import akka.event.Logging;
-import akka.event.LoggingAdapter;
-import akka.japi.Function;
-import org.thingsboard.server.actors.ActorSystemContext;
-import org.thingsboard.server.actors.service.ContextAwareActor;
-import org.thingsboard.server.actors.service.ContextBasedCreator;
-import org.thingsboard.server.actors.service.DefaultActorService;
-import org.thingsboard.server.actors.shared.SessionTimeoutMsg;
-import org.thingsboard.server.common.data.id.SessionId;
-import org.thingsboard.server.common.msg.TbActorMsg;
-import org.thingsboard.server.common.msg.aware.SessionAwareMsg;
-import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;
-import org.thingsboard.server.common.msg.core.ActorSystemToDeviceSessionActorMsg;
-import org.thingsboard.server.common.msg.core.SessionCloseMsg;
-import org.thingsboard.server.common.msg.session.SessionCtrlMsg;
-import scala.concurrent.duration.Duration;
-
-import java.util.HashMap;
-import java.util.Map;
-
-public class SessionManagerActor extends ContextAwareActor {
-
- private static final int INITIAL_SESSION_MAP_SIZE = 1024;
-
- private final LoggingAdapter log = Logging.getLogger(getContext().system(), this);
-
- private final Map sessionActors;
-
- SessionManagerActor(ActorSystemContext systemContext) {
- super(systemContext);
- this.sessionActors = new HashMap<>(INITIAL_SESSION_MAP_SIZE);
- }
-
- @Override
- public SupervisorStrategy supervisorStrategy() {
- return strategy;
- }
-
- @Override
- protected boolean process(TbActorMsg msg) {
- //TODO Move everything here, to work with TbActorMsg
- return false;
- }
-
- @Override
- public void onReceive(Object msg) throws Exception {
- if (msg instanceof SessionCtrlMsg) {
- onSessionCtrlMsg((SessionCtrlMsg) msg);
- } else if (msg instanceof SessionAwareMsg) {
- forwardToSessionActor((SessionAwareMsg) msg);
- } else if (msg instanceof SessionTerminationMsg) {
- onSessionTermination((SessionTerminationMsg) msg);
- } else if (msg instanceof Terminated) {
- onTermination((Terminated) msg);
- } else if (msg instanceof SessionTimeoutMsg) {
- onSessionTimeout((SessionTimeoutMsg) msg);
- } else if (msg instanceof ClusterEventMsg) {
- broadcast(msg);
- }
- }
-
- private void broadcast(Object msg) {
- sessionActors.values().forEach(actorRef -> actorRef.tell(msg, ActorRef.noSender()));
- }
-
- private void onSessionTimeout(SessionTimeoutMsg msg) {
- String sessionIdStr = msg.getSessionId().toUidStr();
- ActorRef sessionActor = sessionActors.get(sessionIdStr);
- if (sessionActor != null) {
- sessionActor.tell(msg, ActorRef.noSender());
- }
- }
-
- private void onSessionCtrlMsg(SessionCtrlMsg msg) {
- String sessionIdStr = msg.getSessionId().toUidStr();
- ActorRef sessionActor = sessionActors.get(sessionIdStr);
- if (sessionActor != null) {
- sessionActor.tell(msg, ActorRef.noSender());
- }
- }
-
- private void onSessionTermination(SessionTerminationMsg msg) {
- String sessionIdStr = msg.getId().toUidStr();
- ActorRef sessionActor = sessionActors.remove(sessionIdStr);
- if (sessionActor != null) {
- log.debug("[{}] Removed session actor.", sessionIdStr);
- //TODO: onSubscriptionUpdate device actor about session close;
- } else {
- log.debug("[{}] Session actor was already removed.", sessionIdStr);
- }
- }
-
- private void forwardToSessionActor(SessionAwareMsg msg) {
- if (msg instanceof ActorSystemToDeviceSessionActorMsg || msg instanceof SessionCloseMsg) {
- String sessionIdStr = msg.getSessionId().toUidStr();
- ActorRef sessionActor = sessionActors.get(sessionIdStr);
- if (sessionActor != null) {
- sessionActor.tell(msg, ActorRef.noSender());
- } else {
- log.debug("[{}] Session actor was already removed.", sessionIdStr);
- }
- } else {
- try {
- getOrCreateSessionActor(msg.getSessionId()).tell(msg, self());
- } catch (InvalidActorNameException e) {
- log.info("Invalid msg : {}", msg);
- }
- }
- }
-
- private ActorRef getOrCreateSessionActor(SessionId sessionId) {
- String sessionIdStr = sessionId.toUidStr();
- ActorRef sessionActor = sessionActors.get(sessionIdStr);
- if (sessionActor == null) {
- log.debug("[{}] Creating session actor.", sessionIdStr);
- sessionActor = context().actorOf(
- Props.create(new SessionActor.ActorCreator(systemContext, sessionId)).withDispatcher(DefaultActorService.SESSION_DISPATCHER_NAME),
- sessionIdStr);
- sessionActors.put(sessionIdStr, sessionActor);
- log.debug("[{}] Created session actor.", sessionIdStr);
- }
- return sessionActor;
- }
-
- private void onTermination(Terminated message) {
- ActorRef terminated = message.actor();
- if (terminated instanceof LocalActorRef) {
- log.info("Removed actor: {}.", terminated);
- //TODO: cleanup session actors map
- } else {
- throw new IllegalStateException("Remote actors are not supported!");
- }
- }
-
- public static class ActorCreator extends ContextBasedCreator {
- private static final long serialVersionUID = 1L;
-
- public ActorCreator(ActorSystemContext context) {
- super(context);
- }
-
- @Override
- public SessionManagerActor create() throws Exception {
- return new SessionManagerActor(context);
- }
- }
-
- private final SupervisorStrategy strategy = new OneForOneStrategy(3, Duration.create("1 minute"), new Function() {
- @Override
- public SupervisorStrategy.Directive apply(Throwable t) {
- logger.error(t, "Unknown failure");
- return SupervisorStrategy.stop();
- }
- });
-}
diff --git a/application/src/main/java/org/thingsboard/server/actors/session/SyncMsgProcessor.java b/application/src/main/java/org/thingsboard/server/actors/session/SyncMsgProcessor.java
deleted file mode 100644
index cf8df1385f..0000000000
--- a/application/src/main/java/org/thingsboard/server/actors/session/SyncMsgProcessor.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/**
- * Copyright © 2016-2018 The Thingsboard Authors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.thingsboard.server.actors.session;
-
-import akka.actor.ActorContext;
-import akka.event.LoggingAdapter;
-import org.thingsboard.server.actors.ActorSystemContext;
-import org.thingsboard.server.actors.shared.SessionTimeoutMsg;
-import org.thingsboard.server.common.data.id.SessionId;
-import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;
-import org.thingsboard.server.common.msg.cluster.ServerAddress;
-import org.thingsboard.server.common.msg.device.DeviceToDeviceActorMsg;
-import org.thingsboard.server.common.msg.session.BasicSessionActorToAdaptorMsg;
-import org.thingsboard.server.common.msg.session.SessionContext;
-import org.thingsboard.server.common.msg.session.SessionType;
-import org.thingsboard.server.common.msg.session.ToDeviceMsg;
-import org.thingsboard.server.common.msg.session.TransportToDeviceSessionActorMsg;
-import org.thingsboard.server.common.msg.session.ctrl.SessionCloseMsg;
-import org.thingsboard.server.common.msg.session.ex.SessionException;
-
-import java.util.Optional;
-
-class SyncMsgProcessor extends AbstractSessionActorMsgProcessor {
- private DeviceToDeviceActorMsg pendingMsg;
- private Optional currentTargetServer;
- private boolean pendingResponse;
-
- public SyncMsgProcessor(ActorSystemContext ctx, LoggingAdapter logger, SessionId sessionId) {
- super(ctx, logger, sessionId);
- }
-
- @Override
- protected void processToDeviceActorMsg(ActorContext ctx, TransportToDeviceSessionActorMsg msg) {
- updateSessionCtx(msg, SessionType.SYNC);
- pendingMsg = toDeviceMsg(msg);
- pendingResponse = true;
- currentTargetServer = forwardToAppActor(ctx, pendingMsg);
- scheduleMsgWithDelay(ctx, new SessionTimeoutMsg(sessionId), getTimeout(systemContext, msg.getSessionMsg().getSessionContext()), ctx.parent());
- }
-
- public void processTimeoutMsg(ActorContext context, SessionTimeoutMsg msg) {
- if (pendingResponse) {
- try {
- sessionCtx.onMsg(SessionCloseMsg.onTimeout(sessionId));
- } catch (SessionException e) {
- logger.warning("Failed to push session close msg", e);
- }
- terminateSession(context, this.sessionId);
- }
- }
-
- public void processToDeviceMsg(ActorContext context, ToDeviceMsg msg) {
- try {
- sessionCtx.onMsg(new BasicSessionActorToAdaptorMsg(this.sessionCtx, msg));
- pendingResponse = false;
- } catch (SessionException e) {
- logger.warning("Failed to push session response msg", e);
- }
- terminateSession(context, this.sessionId);
- }
-
- @Override
- public void processClusterEvent(ActorContext context, ClusterEventMsg msg) {
- if (pendingResponse) {
- Optional newTargetServer = forwardToAppActorIfAddressChanged(context, pendingMsg, currentTargetServer);
- if (logger.isDebugEnabled()) {
- if (!newTargetServer.equals(currentTargetServer)) {
- if (newTargetServer.isPresent()) {
- logger.debug("[{}] Forwarded msg to new server: {}", sessionId, newTargetServer.get());
- } else {
- logger.debug("[{}] Forwarded msg to local server.", sessionId);
- }
- }
- }
- currentTargetServer = newTargetServer;
- }
- }
-
- private long getTimeout(ActorSystemContext ctx, SessionContext sessionCtx) {
- return sessionCtx.getTimeout() > 0 ? sessionCtx.getTimeout() : ctx.getSyncSessionTimeout();
- }
-}
diff --git a/application/src/main/java/org/thingsboard/server/actors/shared/AbstractContextAwareMsgProcessor.java b/application/src/main/java/org/thingsboard/server/actors/shared/AbstractContextAwareMsgProcessor.java
index 8864486150..b707baf14e 100644
--- a/application/src/main/java/org/thingsboard/server/actors/shared/AbstractContextAwareMsgProcessor.java
+++ b/application/src/main/java/org/thingsboard/server/actors/shared/AbstractContextAwareMsgProcessor.java
@@ -22,61 +22,49 @@ import akka.event.LoggingAdapter;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.AllArgsConstructor;
import lombok.Data;
+import lombok.extern.slf4j.Slf4j;
import org.thingsboard.server.actors.ActorSystemContext;
import scala.concurrent.ExecutionContextExecutor;
import scala.concurrent.duration.Duration;
import java.util.concurrent.TimeUnit;
+@Slf4j
public abstract class AbstractContextAwareMsgProcessor {
protected final ActorSystemContext systemContext;
- protected final LoggingAdapter logger;
protected final ObjectMapper mapper = new ObjectMapper();
- protected AbstractContextAwareMsgProcessor(ActorSystemContext systemContext, LoggingAdapter logger) {
+ protected AbstractContextAwareMsgProcessor(ActorSystemContext systemContext) {
super();
this.systemContext = systemContext;
- this.logger = logger;
}
- protected ActorRef getAppActor() {
- return systemContext.getAppActor();
- }
-
- protected Scheduler getScheduler() {
+ private Scheduler getScheduler() {
return systemContext.getScheduler();
}
- protected ExecutionContextExecutor getSystemDispatcher() {
+ private ExecutionContextExecutor getSystemDispatcher() {
return systemContext.getActorSystem().dispatcher();
}
protected void schedulePeriodicMsgWithDelay(ActorContext ctx, Object msg, long delayInMs, long periodInMs) {
- schedulePeriodicMsgWithDelay(ctx, msg, delayInMs, periodInMs, ctx.self());
+ schedulePeriodicMsgWithDelay(msg, delayInMs, periodInMs, ctx.self());
}
- protected void schedulePeriodicMsgWithDelay(ActorContext ctx, Object msg, long delayInMs, long periodInMs, ActorRef target) {
- logger.debug("Scheduling periodic msg {} every {} ms with delay {} ms", msg, periodInMs, delayInMs);
+ private void schedulePeriodicMsgWithDelay(Object msg, long delayInMs, long periodInMs, ActorRef target) {
+ log.debug("Scheduling periodic msg {} every {} ms with delay {} ms", msg, periodInMs, delayInMs);
getScheduler().schedule(Duration.create(delayInMs, TimeUnit.MILLISECONDS), Duration.create(periodInMs, TimeUnit.MILLISECONDS), target, msg, getSystemDispatcher(), null);
}
-
protected void scheduleMsgWithDelay(ActorContext ctx, Object msg, long delayInMs) {
- scheduleMsgWithDelay(ctx, msg, delayInMs, ctx.self());
+ scheduleMsgWithDelay(msg, delayInMs, ctx.self());
}
- protected void scheduleMsgWithDelay(ActorContext ctx, Object msg, long delayInMs, ActorRef target) {
- logger.debug("Scheduling msg {} with delay {} ms", msg, delayInMs);
+ private void scheduleMsgWithDelay(Object msg, long delayInMs, ActorRef target) {
+ log.debug("Scheduling msg {} with delay {} ms", msg, delayInMs);
getScheduler().scheduleOnce(Duration.create(delayInMs, TimeUnit.MILLISECONDS), target, msg, getSystemDispatcher(), null);
}
- @Data
- @AllArgsConstructor
- private static class ComponentConfiguration {
- private final String clazz;
- private final String name;
- private final String configuration;
- }
}
diff --git a/application/src/main/java/org/thingsboard/server/actors/shared/ComponentMsgProcessor.java b/application/src/main/java/org/thingsboard/server/actors/shared/ComponentMsgProcessor.java
index 1cf83397fc..46a76e6d43 100644
--- a/application/src/main/java/org/thingsboard/server/actors/shared/ComponentMsgProcessor.java
+++ b/application/src/main/java/org/thingsboard/server/actors/shared/ComponentMsgProcessor.java
@@ -19,6 +19,7 @@ import akka.actor.ActorContext;
import akka.event.LoggingAdapter;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
+import lombok.extern.slf4j.Slf4j;
import org.thingsboard.server.actors.ActorSystemContext;
import org.thingsboard.server.actors.stats.StatsPersistTick;
import org.thingsboard.server.common.data.id.EntityId;
@@ -26,25 +27,25 @@ import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.plugin.ComponentLifecycleState;
import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;
-import org.thingsboard.server.service.queue.MsgQueueService;
import javax.annotation.Nullable;
import java.util.function.Consumer;
+@Slf4j
public abstract class ComponentMsgProcessor extends AbstractContextAwareMsgProcessor {
protected final TenantId tenantId;
protected final T entityId;
- protected final MsgQueueService queue;
protected ComponentLifecycleState state;
- protected ComponentMsgProcessor(ActorSystemContext systemContext, LoggingAdapter logger, TenantId tenantId, T id) {
- super(systemContext, logger);
+ protected ComponentMsgProcessor(ActorSystemContext systemContext, TenantId tenantId, T id) {
+ super(systemContext);
this.tenantId = tenantId;
this.entityId = id;
- this.queue = systemContext.getMsgQueueService();
}
+ public abstract String getComponentName();
+
public abstract void start(ActorContext context) throws Exception;
public abstract void stop(ActorContext context) throws Exception;
@@ -82,22 +83,9 @@ public abstract class ComponentMsgProcessor extends Abstract
protected void checkActive() {
if (state != ComponentLifecycleState.ACTIVE) {
- throw new IllegalStateException("Rule chain is not active!");
+ log.warn("Rule chain is not active. Current state [{}] for processor [{}] tenant [{}]", state, tenantId, entityId);
+ throw new IllegalStateException("Rule chain is not active! " + entityId + " - " + tenantId);
}
}
- protected void putToQueue(final TbMsg tbMsg, final Consumer onSuccess) {
- EntityId entityId = tbMsg.getRuleNodeId() != null ? tbMsg.getRuleNodeId() : tbMsg.getRuleChainId();
- Futures.addCallback(queue.put(this.tenantId, tbMsg, entityId.getId(), tbMsg.getClusterPartition()), new FutureCallback() {
- @Override
- public void onSuccess(@Nullable Void result) {
- onSuccess.accept(tbMsg);
- }
-
- @Override
- public void onFailure(Throwable t) {
- logger.debug("Failed to push message [{}] to queue due to [{}]", tbMsg, t);
- }
- });
- }
}
diff --git a/application/src/main/java/org/thingsboard/server/actors/shared/EntityActorsManager.java b/application/src/main/java/org/thingsboard/server/actors/shared/EntityActorsManager.java
index 1b9e6a8074..dd03d69a62 100644
--- a/application/src/main/java/org/thingsboard/server/actors/shared/EntityActorsManager.java
+++ b/application/src/main/java/org/thingsboard/server/actors/shared/EntityActorsManager.java
@@ -20,6 +20,8 @@ import akka.actor.ActorRef;
import akka.actor.Props;
import akka.actor.UntypedActor;
import akka.japi.Creator;
+import com.google.common.collect.BiMap;
+import com.google.common.collect.HashBiMap;
import lombok.extern.slf4j.Slf4j;
import org.thingsboard.server.actors.ActorSystemContext;
import org.thingsboard.server.actors.service.ContextAwareActor;
@@ -39,11 +41,11 @@ import java.util.Map;
public abstract class EntityActorsManager> {
protected final ActorSystemContext systemContext;
- protected final Map actors;
+ protected final BiMap actors;
public EntityActorsManager(ActorSystemContext systemContext) {
this.systemContext = systemContext;
- this.actors = new HashMap<>();
+ this.actors = HashBiMap.create();
}
protected abstract TenantId getTenantId();
@@ -65,7 +67,8 @@ public abstract class EntityActorsManager
diff --git a/application/src/main/java/org/thingsboard/server/actors/shared/rulechain/RuleChainManager.java b/application/src/main/java/org/thingsboard/server/actors/shared/rulechain/RuleChainManager.java
index 11ed5a3363..d349fec71d 100644
--- a/application/src/main/java/org/thingsboard/server/actors/shared/rulechain/RuleChainManager.java
+++ b/application/src/main/java/org/thingsboard/server/actors/shared/rulechain/RuleChainManager.java
@@ -50,7 +50,7 @@ public abstract class RuleChainManager extends EntityActorsManager deviceActors;
+ private final BiMap deviceActors;
private TenantActor(ActorSystemContext systemContext, TenantId tenantId) {
super(systemContext, new TenantRuleChainManager(systemContext, tenantId));
this.tenantId = tenantId;
- this.deviceActors = new HashMap<>();
+ this.deviceActors = HashBiMap.create();
}
-
@Override
public SupervisorStrategy supervisorStrategy() {
return strategy;
@@ -63,15 +67,20 @@ public class TenantActor extends RuleChainManagerActor {
@Override
public void preStart() {
- logger.info("[{}] Starting tenant actor.", tenantId);
+ log.info("[{}] Starting tenant actor.", tenantId);
try {
initRuleChains();
- logger.info("[{}] Tenant actor started.", tenantId);
+ log.info("[{}] Tenant actor started.", tenantId);
} catch (Exception e) {
- logger.error(e, "[{}] Unknown failure", tenantId);
+ log.warn("[{}] Unknown failure", tenantId, e);
}
}
+ @Override
+ public void postStop() {
+ log.info("[{}] Stopping tenant actor.", tenantId);
+ }
+
@Override
protected boolean process(TbActorMsg msg) {
switch (msg.getMsgType()) {
@@ -87,7 +96,7 @@ public class TenantActor extends RuleChainManagerActor {
case DEVICE_ACTOR_TO_RULE_ENGINE_MSG:
onDeviceActorToRuleEngineMsg((DeviceActorToRuleEngineMsg) msg);
break;
- case DEVICE_SESSION_TO_DEVICE_ACTOR_MSG:
+ case TRANSPORT_TO_DEVICE_ACTOR_MSG:
case DEVICE_ATTRIBUTES_UPDATE_TO_DEVICE_ACTOR_MSG:
case DEVICE_CREDENTIALS_UPDATE_TO_DEVICE_ACTOR_MSG:
case DEVICE_NAME_OR_TYPE_UPDATE_TO_DEVICE_ACTOR_MSG:
@@ -105,29 +114,26 @@ public class TenantActor extends RuleChainManagerActor {
return true;
}
- @Override
- protected void broadcast(Object msg) {
- super.broadcast(msg);
- deviceActors.values().forEach(actorRef -> actorRef.tell(msg, ActorRef.noSender()));
- }
-
private void onServiceToRuleEngineMsg(ServiceToRuleEngineMsg msg) {
- if (ruleChainManager.getRootChainActor()!=null)
- ruleChainManager.getRootChainActor().tell(msg, self());
- else logger.info("[{}] No Root Chain", msg);
+ if (ruleChainManager.getRootChainActor() != null) {
+ ruleChainManager.getRootChainActor().tell(msg, self());
+ } else {
+ log.info("[{}] No Root Chain: {}", tenantId, msg);
+ }
}
private void onDeviceActorToRuleEngineMsg(DeviceActorToRuleEngineMsg msg) {
- if (ruleChainManager.getRootChainActor()!=null)
- ruleChainManager.getRootChainActor().tell(msg, self());
- else logger.info("[{}] No Root Chain", msg);
+ if (ruleChainManager.getRootChainActor() != null) {
+ ruleChainManager.getRootChainActor().tell(msg, self());
+ } else {
+ log.info("[{}] No Root Chain: {}", tenantId, msg);
+ }
}
private void onRuleChainMsg(RuleChainAwareMsg msg) {
ruleChainManager.getOrCreateActor(context(), msg.getRuleChainId()).tell(msg, self());
}
-
private void onToDeviceActorMsg(DeviceAwareMsg msg) {
getOrCreateDeviceActor(msg.getDeviceId()).tell(msg, ActorRef.noSender());
}
@@ -142,13 +148,35 @@ public class TenantActor extends RuleChainManagerActor {
}
target.tell(msg, ActorRef.noSender());
} else {
- logger.debug("Invalid component lifecycle msg: {}", msg);
+ log.debug("[{}] Invalid component lifecycle msg: {}", tenantId, msg);
}
}
private ActorRef getOrCreateDeviceActor(DeviceId deviceId) {
- return deviceActors.computeIfAbsent(deviceId, k -> context().actorOf(Props.create(new DeviceActor.ActorCreator(systemContext, tenantId, deviceId))
- .withDispatcher(DefaultActorService.CORE_DISPATCHER_NAME), deviceId.toString()));
+ return deviceActors.computeIfAbsent(deviceId, k -> {
+ log.debug("[{}][{}] Creating device actor.", tenantId, deviceId);
+ ActorRef deviceActor = context().actorOf(Props.create(new DeviceActorCreator(systemContext, tenantId, deviceId))
+ .withDispatcher(DefaultActorService.CORE_DISPATCHER_NAME)
+ , deviceId.toString());
+ context().watch(deviceActor);
+ log.debug("[{}][{}] Created device actor: {}.", tenantId, deviceId, deviceActor);
+ return deviceActor;
+ });
+ }
+
+ @Override
+ protected void processTermination(Terminated message) {
+ ActorRef terminated = message.actor();
+ if (terminated instanceof LocalActorRef) {
+ boolean removed = deviceActors.inverse().remove(terminated) != null;
+ if (removed) {
+ log.debug("[{}] Removed actor:", terminated);
+ } else {
+ log.warn("[{}] Removed actor was not found in the device map!");
+ }
+ } else {
+ throw new IllegalStateException("Remote actors are not supported!");
+ }
}
public static class ActorCreator extends ContextBasedCreator {
@@ -162,7 +190,7 @@ public class TenantActor extends RuleChainManagerActor {
}
@Override
- public TenantActor create() throws Exception {
+ public TenantActor create() {
return new TenantActor(context, tenantId);
}
}
@@ -170,8 +198,8 @@ public class TenantActor extends RuleChainManagerActor {
private final SupervisorStrategy strategy = new OneForOneStrategy(3, Duration.create("1 minute"), new Function() {
@Override
public SupervisorStrategy.Directive apply(Throwable t) {
- logger.error(t, "Unknown failure");
- if(t instanceof ActorInitializationException){
+ log.warn("[{}] Unknown failure", tenantId, t);
+ if (t instanceof ActorInitializationException) {
return SupervisorStrategy.stop();
} else {
return SupervisorStrategy.resume();
diff --git a/application/src/main/java/org/thingsboard/server/controller/AlarmController.java b/application/src/main/java/org/thingsboard/server/controller/AlarmController.java
index 586e8c35ef..00885e34b0 100644
--- a/application/src/main/java/org/thingsboard/server/controller/AlarmController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/AlarmController.java
@@ -83,7 +83,7 @@ public class AlarmController extends BaseController {
Alarm savedAlarm = checkNotNull(alarmService.createOrUpdateAlarm(alarm));
logEntityAction(savedAlarm.getId(), savedAlarm,
getCurrentUser().getCustomerId(),
- savedAlarm.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null);
+ alarm.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null);
return savedAlarm;
} catch (Exception e) {
logEntityAction(emptyId(EntityType.ALARM), alarm,
diff --git a/application/src/main/java/org/thingsboard/server/controller/BaseController.java b/application/src/main/java/org/thingsboard/server/controller/BaseController.java
index de73fe0796..825929f863 100644
--- a/application/src/main/java/org/thingsboard/server/controller/BaseController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/BaseController.java
@@ -19,9 +19,11 @@ import com.datastax.driver.core.utils.UUIDs;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
+import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.ExceptionHandler;
@@ -48,14 +50,17 @@ import org.thingsboard.server.common.data.widget.WidgetsBundle;
import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbMsgDataType;
import org.thingsboard.server.common.msg.TbMsgMetaData;
+import org.thingsboard.server.common.msg.cluster.SendToClusterMsg;
import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg;
import org.thingsboard.server.dao.alarm.AlarmService;
import org.thingsboard.server.dao.asset.AssetService;
+import org.thingsboard.server.dao.attributes.AttributesService;
import org.thingsboard.server.dao.audit.AuditLogService;
import org.thingsboard.server.dao.customer.CustomerService;
import org.thingsboard.server.dao.dashboard.DashboardService;
import org.thingsboard.server.dao.device.DeviceCredentialsService;
import org.thingsboard.server.dao.device.DeviceService;
+import org.thingsboard.server.dao.entityview.EntityViewService;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.exception.IncorrectParameterException;
import org.thingsboard.server.dao.model.ModelConstants;
@@ -69,6 +74,7 @@ import org.thingsboard.server.exception.ThingsboardErrorResponseHandler;
import org.thingsboard.server.service.component.ComponentDiscoveryService;
import org.thingsboard.server.service.security.model.SecurityUser;
import org.thingsboard.server.service.state.DeviceStateService;
+import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService;
import javax.mail.MessagingException;
import javax.servlet.http.HttpServletRequest;
@@ -139,6 +145,20 @@ public abstract class BaseController {
@Autowired
protected DeviceStateService deviceStateService;
+ @Autowired
+ protected EntityViewService entityViewService;
+
+ @Autowired
+ protected TelemetrySubscriptionService tsSubService;
+
+ @Autowired
+ protected AttributesService attributesService;
+
+ @Value("${server.log_controller_error_stack_trace}")
+ @Getter
+ private boolean logControllerErrorStackTrace;
+
+
@ExceptionHandler(ThingsboardException.class)
public void handleThingsboardException(ThingsboardException ex, HttpServletResponse response) {
errorResponseHandler.handle(ex, response);
@@ -149,7 +169,7 @@ public abstract class BaseController {
}
private ThingsboardException handleException(Exception exception, boolean logException) {
- if (logException) {
+ if (logException && logControllerErrorStackTrace) {
log.error("Error [{}]", exception.getMessage(), exception);
}
@@ -247,7 +267,6 @@ public abstract class BaseController {
Customer checkCustomerId(CustomerId customerId) throws ThingsboardException {
try {
- validateId(customerId, "Incorrect customerId " + customerId);
SecurityUser authUser = getCurrentUser();
if (authUser.getAuthority() == Authority.SYS_ADMIN ||
(authUser.getAuthority() != Authority.TENANT_ADMIN &&
@@ -255,9 +274,13 @@ public abstract class BaseController {
throw new ThingsboardException(YOU_DON_T_HAVE_PERMISSION_TO_PERFORM_THIS_OPERATION,
ThingsboardErrorCode.PERMISSION_DENIED);
}
- Customer customer = customerService.findCustomerById(customerId);
- checkCustomer(customer);
- return customer;
+ if (customerId != null && !customerId.isNullUid()) {
+ Customer customer = customerService.findCustomerById(customerId);
+ checkCustomer(customer);
+ return customer;
+ } else {
+ return null;
+ }
} catch (Exception e) {
throw handleException(e, false);
}
@@ -313,6 +336,9 @@ public abstract class BaseController {
case USER:
checkUserId(new UserId(entityId.getId()));
return;
+ case ENTITY_VIEW:
+ checkEntityViewId(new EntityViewId(entityId.getId()));
+ return;
default:
throw new IllegalArgumentException("Unsupported entity type: " + entityId.getEntityType());
}
@@ -335,11 +361,26 @@ public abstract class BaseController {
protected void checkDevice(Device device) throws ThingsboardException {
checkNotNull(device);
checkTenantId(device.getTenantId());
- if (device.getCustomerId() != null && !device.getCustomerId().getId().equals(ModelConstants.NULL_UUID)) {
- checkCustomerId(device.getCustomerId());
+ checkCustomerId(device.getCustomerId());
+ }
+
+ protected EntityView checkEntityViewId(EntityViewId entityViewId) throws ThingsboardException {
+ try {
+ validateId(entityViewId, "Incorrect entityViewId " + entityViewId);
+ EntityView entityView = entityViewService.findEntityViewById(entityViewId);
+ checkEntityView(entityView);
+ return entityView;
+ } catch (Exception e) {
+ throw handleException(e, false);
}
}
+ protected void checkEntityView(EntityView entityView) throws ThingsboardException {
+ checkNotNull(entityView);
+ checkTenantId(entityView.getTenantId());
+ checkCustomerId(entityView.getCustomerId());
+ }
+
Asset checkAssetId(AssetId assetId) throws ThingsboardException {
try {
validateId(assetId, "Incorrect assetId " + assetId);
@@ -354,9 +395,7 @@ public abstract class BaseController {
protected void checkAsset(Asset asset) throws ThingsboardException {
checkNotNull(asset);
checkTenantId(asset.getTenantId());
- if (asset.getCustomerId() != null && !asset.getCustomerId().getId().equals(ModelConstants.NULL_UUID)) {
- checkCustomerId(asset.getCustomerId());
- }
+ checkCustomerId(asset.getCustomerId());
}
Alarm checkAlarmId(AlarmId alarmId) throws ThingsboardException {
@@ -465,8 +504,7 @@ public abstract class BaseController {
ComponentDescriptor checkComponentDescriptorByClazz(String clazz) throws ThingsboardException {
try {
log.debug("[{}] Lookup component descriptor", clazz);
- ComponentDescriptor componentDescriptor = checkNotNull(componentDescriptorService.getComponent(clazz));
- return componentDescriptor;
+ return checkNotNull(componentDescriptorService.getComponent(clazz));
} catch (Exception e) {
throw handleException(e, false);
}
@@ -530,16 +568,16 @@ public abstract class BaseController {
}
protected I emptyId(EntityType entityType) {
- return (I)EntityIdFactory.getByTypeAndUuid(entityType, ModelConstants.NULL_UUID);
+ return (I) EntityIdFactory.getByTypeAndUuid(entityType, ModelConstants.NULL_UUID);
}
protected void logEntityAction(I entityId, E entity, CustomerId customerId,
- ActionType actionType, Exception e, Object... additionalInfo) throws ThingsboardException {
+ ActionType actionType, Exception e, Object... additionalInfo) throws ThingsboardException {
logEntityAction(getCurrentUser(), entityId, entity, customerId, actionType, e, additionalInfo);
}
protected void logEntityAction(User user, I entityId, E entity, CustomerId customerId,
- ActionType actionType, Exception e, Object... additionalInfo) throws ThingsboardException {
+ ActionType actionType, Exception e, Object... additionalInfo) throws ThingsboardException {
if (customerId == null || customerId.isNullUid()) {
customerId = user.getCustomerId();
}
@@ -555,7 +593,7 @@ public abstract class BaseController {
}
private void pushEntityActionToRuleEngine(I entityId, E entity, User user, CustomerId customerId,
- ActionType actionType, Object... additionalInfo) {
+ ActionType actionType, Object... additionalInfo) {
String msgType = null;
switch (actionType) {
case ADDED:
@@ -579,6 +617,12 @@ public abstract class BaseController {
case ATTRIBUTES_DELETED:
msgType = DataConstants.ATTRIBUTES_DELETED;
break;
+ case ALARM_ACK:
+ msgType = DataConstants.ALARM_ACK;
+ break;
+ case ALARM_CLEAR:
+ msgType = DataConstants.ALARM_CLEAR;
+ break;
}
if (!StringUtils.isEmpty(msgType)) {
try {
@@ -628,7 +672,7 @@ public abstract class BaseController {
String scope = extractParameter(String.class, 0, additionalInfo);
List keys = extractParameter(List.class, 1, additionalInfo);
metaData.putValue("scope", scope);
- ArrayNode attrsArrayNode = entityNode.putArray("attributes");
+ ArrayNode attrsArrayNode = entityNode.putArray("attributes");
if (keys != null) {
keys.forEach(attrsArrayNode::add);
}
@@ -637,7 +681,7 @@ public abstract class BaseController {
TbMsg tbMsg = new TbMsg(UUIDs.timeBased(), msgType, entityId, metaData, TbMsgDataType.JSON
, json.writeValueAsString(entityNode)
, null, null, 0L);
- actorService.onMsg(new ServiceToRuleEngineMsg(user.getTenantId(), tbMsg));
+ actorService.onMsg(new SendToClusterMsg(entityId, new ServiceToRuleEngineMsg(user.getTenantId(), tbMsg)));
} catch (Exception e) {
log.warn("[{}] Failed to push entity action to rule engine: {}", entityId, actionType, e);
}
diff --git a/application/src/main/java/org/thingsboard/server/controller/DashboardController.java b/application/src/main/java/org/thingsboard/server/controller/DashboardController.java
index 85227e76a9..0bb6a6683d 100644
--- a/application/src/main/java/org/thingsboard/server/controller/DashboardController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/DashboardController.java
@@ -15,6 +15,8 @@
*/
package org.thingsboard.server.controller;
+import lombok.Getter;
+import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PathVariable;
@@ -49,6 +51,10 @@ public class DashboardController extends BaseController {
public static final String DASHBOARD_ID = "dashboardId";
+ @Value("${dashboard.max_datapoints_limit}")
+ private long maxDatapointsLimit;
+
+
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/dashboard/serverTime", method = RequestMethod.GET)
@ResponseBody
@@ -56,6 +62,13 @@ public class DashboardController extends BaseController {
return System.currentTimeMillis();
}
+ @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
+ @RequestMapping(value = "/dashboard/maxDatapointsLimit", method = RequestMethod.GET)
+ @ResponseBody
+ public long getMaxDatapointsLimit() throws ThingsboardException {
+ return maxDatapointsLimit;
+ }
+
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/dashboard/info/{dashboardId}", method = RequestMethod.GET)
@ResponseBody
diff --git a/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java b/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java
new file mode 100644
index 0000000000..2a9c073d34
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java
@@ -0,0 +1,336 @@
+/**
+ * Copyright © 2016-2018 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.controller;
+
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.http.HttpStatus;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestController;
+import org.thingsboard.server.common.data.Customer;
+import org.thingsboard.server.common.data.DataConstants;
+import org.thingsboard.server.common.data.EntitySubtype;
+import org.thingsboard.server.common.data.EntityType;
+import org.thingsboard.server.common.data.EntityView;
+import org.thingsboard.server.common.data.audit.ActionType;
+import org.thingsboard.server.common.data.entityview.EntityViewSearchQuery;
+import org.thingsboard.server.common.data.exception.ThingsboardException;
+import org.thingsboard.server.common.data.id.CustomerId;
+import org.thingsboard.server.common.data.id.EntityId;
+import org.thingsboard.server.common.data.id.EntityViewId;
+import org.thingsboard.server.common.data.id.TenantId;
+import org.thingsboard.server.common.data.id.UUIDBased;
+import org.thingsboard.server.common.data.kv.AttributeKvEntry;
+import org.thingsboard.server.common.data.page.TextPageData;
+import org.thingsboard.server.common.data.page.TextPageLink;
+import org.thingsboard.server.dao.exception.IncorrectParameterException;
+import org.thingsboard.server.dao.model.ModelConstants;
+import org.thingsboard.server.service.security.model.SecurityUser;
+
+import javax.annotation.Nullable;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.stream.Collectors;
+
+import static org.thingsboard.server.controller.CustomerController.CUSTOMER_ID;
+
+/**
+ * Created by Victor Basanets on 8/28/2017.
+ */
+@RestController
+@RequestMapping("/api")
+@Slf4j
+public class EntityViewController extends BaseController {
+
+ public static final String ENTITY_VIEW_ID = "entityViewId";
+
+ @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
+ @RequestMapping(value = "/entityView/{entityViewId}", method = RequestMethod.GET)
+ @ResponseBody
+ public EntityView getEntityViewById(@PathVariable(ENTITY_VIEW_ID) String strEntityViewId) throws ThingsboardException {
+ checkParameter(ENTITY_VIEW_ID, strEntityViewId);
+ try {
+ return checkEntityViewId(new EntityViewId(toUUID(strEntityViewId)));
+ } catch (Exception e) {
+ throw handleException(e);
+ }
+ }
+
+ @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
+ @RequestMapping(value = "/entityView", method = RequestMethod.POST)
+ @ResponseBody
+ public EntityView saveEntityView(@RequestBody EntityView entityView) throws ThingsboardException {
+ try {
+ entityView.setTenantId(getCurrentUser().getTenantId());
+ EntityView savedEntityView = checkNotNull(entityViewService.saveEntityView(entityView));
+ List>> futures = new ArrayList<>();
+ if (savedEntityView.getKeys() != null && savedEntityView.getKeys().getAttributes() != null) {
+ futures.add(copyAttributesFromEntityToEntityView(savedEntityView, DataConstants.CLIENT_SCOPE, savedEntityView.getKeys().getAttributes().getCs(), getCurrentUser()));
+ futures.add(copyAttributesFromEntityToEntityView(savedEntityView, DataConstants.SERVER_SCOPE, savedEntityView.getKeys().getAttributes().getSs(), getCurrentUser()));
+ futures.add(copyAttributesFromEntityToEntityView(savedEntityView, DataConstants.SHARED_SCOPE, savedEntityView.getKeys().getAttributes().getSh(), getCurrentUser()));
+ }
+ for (ListenableFuture> future : futures) {
+ try {
+ future.get();
+ } catch (InterruptedException | ExecutionException e) {
+ throw new RuntimeException("Failed to copy attributes to entity view", e);
+ }
+ }
+
+ logEntityAction(savedEntityView.getId(), savedEntityView, null,
+ entityView.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null);
+ return savedEntityView;
+ } catch (Exception e) {
+ logEntityAction(emptyId(EntityType.ENTITY_VIEW), entityView, null,
+ entityView.getId() == null ? ActionType.ADDED : ActionType.UPDATED, e);
+ throw handleException(e);
+ }
+ }
+
+ private ListenableFuture> copyAttributesFromEntityToEntityView(EntityView entityView, String scope, Collection keys, SecurityUser user) throws ThingsboardException {
+ EntityViewId entityId = entityView.getId();
+ if (keys != null && !keys.isEmpty()) {
+ ListenableFuture> getAttrFuture = attributesService.find(entityView.getEntityId(), scope, keys);
+ return Futures.transform(getAttrFuture, attributeKvEntries -> {
+ List attributes;
+ if (attributeKvEntries != null && !attributeKvEntries.isEmpty()) {
+ attributes =
+ attributeKvEntries.stream()
+ .filter(attributeKvEntry -> {
+ long startTime = entityView.getStartTimeMs();
+ long endTime = entityView.getEndTimeMs();
+ long lastUpdateTs = attributeKvEntry.getLastUpdateTs();
+ return startTime == 0 && endTime == 0 ||
+ (endTime == 0 && startTime < lastUpdateTs) ||
+ (startTime == 0 && endTime > lastUpdateTs)
+ ? true : startTime < lastUpdateTs && endTime > lastUpdateTs;
+ }).collect(Collectors.toList());
+ tsSubService.saveAndNotify(entityId, scope, attributes, new FutureCallback() {
+ @Override
+ public void onSuccess(@Nullable Void tmp) {
+ try {
+ logAttributesUpdated(user, entityId, scope, attributes, null);
+ } catch (ThingsboardException e) {
+ log.error("Failed to log attribute updates", e);
+ }
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ try {
+ logAttributesUpdated(user, entityId, scope, attributes, t);
+ } catch (ThingsboardException e) {
+ log.error("Failed to log attribute updates", e);
+ }
+ }
+ });
+ }
+ return null;
+ });
+ } else {
+ return Futures.immediateFuture(null);
+ }
+ }
+
+ private void logAttributesUpdated(SecurityUser user, EntityId entityId, String scope, List attributes, Throwable e) throws ThingsboardException {
+ logEntityAction(user, (UUIDBased & EntityId) entityId, null, null, ActionType.ATTRIBUTES_UPDATED, toException(e),
+ scope, attributes);
+ }
+
+ @PreAuthorize("hasAuthority('TENANT_ADMIN')")
+ @RequestMapping(value = "/entityView/{entityViewId}", method = RequestMethod.DELETE)
+ @ResponseStatus(value = HttpStatus.OK)
+ public void deleteEntityView(@PathVariable(ENTITY_VIEW_ID) String strEntityViewId) throws ThingsboardException {
+ checkParameter(ENTITY_VIEW_ID, strEntityViewId);
+ try {
+ EntityViewId entityViewId = new EntityViewId(toUUID(strEntityViewId));
+ EntityView entityView = checkEntityViewId(entityViewId);
+ entityViewService.deleteEntityView(entityViewId);
+ logEntityAction(entityViewId, entityView, entityView.getCustomerId(),
+ ActionType.DELETED, null, strEntityViewId);
+ } catch (Exception e) {
+ logEntityAction(emptyId(EntityType.ENTITY_VIEW),
+ null,
+ null,
+ ActionType.DELETED, e, strEntityViewId);
+ throw handleException(e);
+ }
+ }
+
+ @PreAuthorize("hasAuthority('TENANT_ADMIN')")
+ @RequestMapping(value = "/tenant/entityViews", params = {"entityViewName"}, method = RequestMethod.GET)
+ @ResponseBody
+ public EntityView getTenantEntityView(
+ @RequestParam String entityViewName) throws ThingsboardException {
+ try {
+ TenantId tenantId = getCurrentUser().getTenantId();
+ return checkNotNull(entityViewService.findEntityViewByTenantIdAndName(tenantId, entityViewName));
+ } catch (Exception e) {
+ throw handleException(e);
+ }
+ }
+
+ @PreAuthorize("hasAuthority('TENANT_ADMIN')")
+ @RequestMapping(value = "/customer/{customerId}/entityView/{entityViewId}", method = RequestMethod.POST)
+ @ResponseBody
+ public EntityView assignEntityViewToCustomer(@PathVariable(CUSTOMER_ID) String strCustomerId,
+ @PathVariable(ENTITY_VIEW_ID) String strEntityViewId) throws ThingsboardException {
+ checkParameter(CUSTOMER_ID, strCustomerId);
+ checkParameter(ENTITY_VIEW_ID, strEntityViewId);
+ try {
+ CustomerId customerId = new CustomerId(toUUID(strCustomerId));
+ Customer customer = checkCustomerId(customerId);
+
+ EntityViewId entityViewId = new EntityViewId(toUUID(strEntityViewId));
+ checkEntityViewId(entityViewId);
+
+ EntityView savedEntityView = checkNotNull(entityViewService.assignEntityViewToCustomer(entityViewId, customerId));
+ logEntityAction(entityViewId, savedEntityView,
+ savedEntityView.getCustomerId(),
+ ActionType.ASSIGNED_TO_CUSTOMER, null, strEntityViewId, strCustomerId, customer.getName());
+ return savedEntityView;
+ } catch (Exception e) {
+ logEntityAction(emptyId(EntityType.ENTITY_VIEW), null,
+ null,
+ ActionType.ASSIGNED_TO_CUSTOMER, e, strEntityViewId, strCustomerId);
+ throw handleException(e);
+ }
+ }
+
+ @PreAuthorize("hasAuthority('TENANT_ADMIN')")
+ @RequestMapping(value = "/customer/entityView/{entityViewId}", method = RequestMethod.DELETE)
+ @ResponseBody
+ public EntityView unassignEntityViewFromCustomer(@PathVariable(ENTITY_VIEW_ID) String strEntityViewId) throws ThingsboardException {
+ checkParameter(ENTITY_VIEW_ID, strEntityViewId);
+ try {
+ EntityViewId entityViewId = new EntityViewId(toUUID(strEntityViewId));
+ EntityView entityView = checkEntityViewId(entityViewId);
+ if (entityView.getCustomerId() == null || entityView.getCustomerId().getId().equals(ModelConstants.NULL_UUID)) {
+ throw new IncorrectParameterException("Entity View isn't assigned to any customer!");
+ }
+ Customer customer = checkCustomerId(entityView.getCustomerId());
+ EntityView savedEntityView = checkNotNull(entityViewService.unassignEntityViewFromCustomer(entityViewId));
+ logEntityAction(entityViewId, entityView,
+ entityView.getCustomerId(),
+ ActionType.UNASSIGNED_FROM_CUSTOMER, null, strEntityViewId, customer.getId().toString(), customer.getName());
+
+ return savedEntityView;
+ } catch (Exception e) {
+ logEntityAction(emptyId(EntityType.ENTITY_VIEW), null,
+ null,
+ ActionType.UNASSIGNED_FROM_CUSTOMER, e, strEntityViewId);
+ throw handleException(e);
+ }
+ }
+
+ @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
+ @RequestMapping(value = "/customer/{customerId}/entityViews", params = {"limit"}, method = RequestMethod.GET)
+ @ResponseBody
+ public TextPageData getCustomerEntityViews(
+ @PathVariable("customerId") String strCustomerId,
+ @RequestParam int limit,
+ @RequestParam(required = false) String type,
+ @RequestParam(required = false) String textSearch,
+ @RequestParam(required = false) String idOffset,
+ @RequestParam(required = false) String textOffset) throws ThingsboardException {
+ checkParameter("customerId", strCustomerId);
+ try {
+ TenantId tenantId = getCurrentUser().getTenantId();
+ CustomerId customerId = new CustomerId(toUUID(strCustomerId));
+ checkCustomerId(customerId);
+ TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset);
+ if (type != null && type.trim().length() > 0) {
+ return checkNotNull(entityViewService.findEntityViewsByTenantIdAndCustomerIdAndType(tenantId, customerId, pageLink, type));
+ } else {
+ return checkNotNull(entityViewService.findEntityViewsByTenantIdAndCustomerId(tenantId, customerId, pageLink));
+ }
+ } catch (Exception e) {
+ throw handleException(e);
+ }
+ }
+
+ @PreAuthorize("hasAuthority('TENANT_ADMIN')")
+ @RequestMapping(value = "/tenant/entityViews", params = {"limit"}, method = RequestMethod.GET)
+ @ResponseBody
+ public TextPageData getTenantEntityViews(
+ @RequestParam int limit,
+ @RequestParam(required = false) String type,
+ @RequestParam(required = false) String textSearch,
+ @RequestParam(required = false) String idOffset,
+ @RequestParam(required = false) String textOffset) throws ThingsboardException {
+ try {
+ TenantId tenantId = getCurrentUser().getTenantId();
+ TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset);
+
+ if (type != null && type.trim().length() > 0) {
+ return checkNotNull(entityViewService.findEntityViewByTenantIdAndType(tenantId, pageLink, type));
+ } else {
+ return checkNotNull(entityViewService.findEntityViewByTenantId(tenantId, pageLink));
+ }
+ } catch (Exception e) {
+ throw handleException(e);
+ }
+ }
+
+ @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
+ @RequestMapping(value = "/entityViews", method = RequestMethod.POST)
+ @ResponseBody
+ public List findByQuery(@RequestBody EntityViewSearchQuery query) throws ThingsboardException {
+ checkNotNull(query);
+ checkNotNull(query.getParameters());
+ checkNotNull(query.getEntityViewTypes());
+ checkEntityId(query.getParameters().getEntityId());
+ try {
+ List entityViews = checkNotNull(entityViewService.findEntityViewsByQuery(query).get());
+ entityViews = entityViews.stream().filter(entityView -> {
+ try {
+ checkEntityView(entityView);
+ return true;
+ } catch (ThingsboardException e) {
+ return false;
+ }
+ }).collect(Collectors.toList());
+ return entityViews;
+ } catch (Exception e) {
+ throw handleException(e);
+ }
+ }
+
+ @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
+ @RequestMapping(value = "/entityView/types", method = RequestMethod.GET)
+ @ResponseBody
+ public List getEntityViewTypes() throws ThingsboardException {
+ try {
+ SecurityUser user = getCurrentUser();
+ TenantId tenantId = user.getTenantId();
+ ListenableFuture> entityViewTypes = entityViewService.findEntityViewTypesByTenantId(tenantId);
+ return checkNotNull(entityViewTypes.get());
+ } catch (Exception e) {
+ throw handleException(e);
+ }
+ }
+}
diff --git a/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java b/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java
index 86b8fdacd7..fbd5faf75b 100644
--- a/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java
@@ -50,7 +50,7 @@ import org.thingsboard.server.common.data.rule.RuleChainMetaData;
import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbMsgMetaData;
import org.thingsboard.server.dao.event.EventService;
-import org.thingsboard.server.service.script.JsSandboxService;
+import org.thingsboard.server.service.script.JsInvokeService;
import org.thingsboard.server.service.script.RuleNodeJsScriptEngine;
import java.util.List;
@@ -71,7 +71,7 @@ public class RuleChainController extends BaseController {
private EventService eventService;
@Autowired
- private JsSandboxService jsSandboxService;
+ private JsInvokeService jsInvokeService;
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/ruleChain/{ruleChainId}", method = RequestMethod.GET)
@@ -276,7 +276,7 @@ public class RuleChainController extends BaseController {
String errorText = "";
ScriptEngine engine = null;
try {
- engine = new RuleNodeJsScriptEngine(jsSandboxService, script, argNames);
+ engine = new RuleNodeJsScriptEngine(jsInvokeService, getCurrentUser().getId(), script, argNames);
TbMsg inMsg = new TbMsg(UUIDs.timeBased(), msgType, null, new TbMsgMetaData(metadata), data, null, null, 0L);
switch (scriptType) {
case "update":
diff --git a/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java b/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java
index 7062ca97f9..ef09a7a62c 100644
--- a/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java
@@ -49,9 +49,11 @@ import org.thingsboard.server.common.data.kv.Aggregation;
import org.thingsboard.server.common.data.kv.AttributeKey;
import org.thingsboard.server.common.data.kv.AttributeKvEntry;
import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry;
+import org.thingsboard.server.common.data.kv.BaseDeleteTsKvQuery;
import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery;
import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
import org.thingsboard.server.common.data.kv.BooleanDataEntry;
+import org.thingsboard.server.common.data.kv.DeleteTsKvQuery;
import org.thingsboard.server.common.data.kv.DoubleDataEntry;
import org.thingsboard.server.common.data.kv.KvEntry;
import org.thingsboard.server.common.data.kv.LongDataEntry;
@@ -59,14 +61,11 @@ import org.thingsboard.server.common.data.kv.ReadTsKvQuery;
import org.thingsboard.server.common.data.kv.StringDataEntry;
import org.thingsboard.server.common.data.kv.TsKvEntry;
import org.thingsboard.server.common.msg.cluster.SendToClusterMsg;
-import org.thingsboard.server.common.msg.core.TelemetryUploadRequest;
import org.thingsboard.server.common.transport.adaptor.JsonConverter;
-import org.thingsboard.server.dao.attributes.AttributesService;
import org.thingsboard.server.dao.timeseries.TimeseriesService;
import org.thingsboard.server.service.security.AccessValidator;
import org.thingsboard.server.service.security.model.SecurityUser;
import org.thingsboard.server.service.telemetry.AttributeData;
-import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService;
import org.thingsboard.server.service.telemetry.TsData;
import org.thingsboard.server.service.telemetry.exception.InvalidParametersException;
import org.thingsboard.server.service.telemetry.exception.UncheckedApiException;
@@ -93,15 +92,9 @@ import java.util.stream.Collectors;
@Slf4j
public class TelemetryController extends BaseController {
- @Autowired
- private AttributesService attributesService;
-
@Autowired
private TimeseriesService tsService;
- @Autowired
- private TelemetrySubscriptionService tsSubService;
-
@Autowired
private AccessValidator accessValidator;
@@ -256,6 +249,60 @@ public class TelemetryController extends BaseController {
return saveTelemetry(entityId, requestBody, ttl);
}
+ @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
+ @RequestMapping(value = "/{entityType}/{entityId}/timeseries/delete", method = RequestMethod.DELETE)
+ @ResponseBody
+ public DeferredResult deleteEntityTimeseries(@PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr,
+ @RequestParam(name = "keys") String keysStr,
+ @RequestParam(name = "deleteAllDataForKeys", defaultValue = "false") boolean deleteAllDataForKeys,
+ @RequestParam(name = "startTs", required = false) Long startTs,
+ @RequestParam(name = "endTs", required = false) Long endTs,
+ @RequestParam(name = "rewriteLatestIfDeleted", defaultValue = "false") boolean rewriteLatestIfDeleted) throws ThingsboardException {
+ EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr);
+ return deleteTimeseries(entityId, keysStr, deleteAllDataForKeys, startTs, endTs, rewriteLatestIfDeleted);
+ }
+
+ private DeferredResult deleteTimeseries(EntityId entityIdStr, String keysStr, boolean deleteAllDataForKeys,
+ Long startTs, Long endTs, boolean rewriteLatestIfDeleted) throws ThingsboardException {
+ List keys = toKeysList(keysStr);
+ if (keys.isEmpty()) {
+ return getImmediateDeferredResult("Empty keys: " + keysStr, HttpStatus.BAD_REQUEST);
+ }
+ SecurityUser user = getCurrentUser();
+
+ long deleteFromTs;
+ long deleteToTs;
+ if (deleteAllDataForKeys) {
+ deleteFromTs = 0L;
+ deleteToTs = System.currentTimeMillis();
+ } else {
+ deleteFromTs = startTs;
+ deleteToTs = endTs;
+ }
+
+ return accessValidator.validateEntityAndCallback(user, entityIdStr, (result, entityId) -> {
+ List deleteTsKvQueries = new ArrayList<>();
+ for (String key : keys) {
+ deleteTsKvQueries.add(new BaseDeleteTsKvQuery(key, deleteFromTs, deleteToTs, rewriteLatestIfDeleted));
+ }
+
+ ListenableFuture> future = tsService.remove(entityId, deleteTsKvQueries);
+ Futures.addCallback(future, new FutureCallback>() {
+ @Override
+ public void onSuccess(@Nullable List tmp) {
+ logTimeseriesDeleted(user, entityId, keys, null);
+ result.setResult(new ResponseEntity<>(HttpStatus.OK));
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ logTimeseriesDeleted(user, entityId, keys, t);
+ result.setResult(new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR));
+ }
+ }, executor);
+ });
+ }
+
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/{deviceId}/{scope}", method = RequestMethod.DELETE)
@ResponseBody
@@ -352,7 +399,7 @@ public class TelemetryController extends BaseController {
}
private DeferredResult saveTelemetry(EntityId entityIdSrc, String requestBody, long ttl) throws ThingsboardException {
- TelemetryUploadRequest telemetryRequest;
+ Map> telemetryRequest;
JsonElement telemetryJson;
try {
telemetryJson = new JsonParser().parse(requestBody);
@@ -360,12 +407,12 @@ public class TelemetryController extends BaseController {
return getImmediateDeferredResult("Unable to parse timeseries payload: Invalid JSON body!", HttpStatus.BAD_REQUEST);
}
try {
- telemetryRequest = JsonConverter.convertToTelemetry(telemetryJson);
+ telemetryRequest = JsonConverter.convertToTelemetry(telemetryJson, System.currentTimeMillis());
} catch (Exception e) {
return getImmediateDeferredResult("Unable to parse timeseries payload. Invalid JSON body: " + e.getMessage(), HttpStatus.BAD_REQUEST);
}
List entries = new ArrayList<>();
- for (Map.Entry> entry : telemetryRequest.getData().entrySet()) {
+ for (Map.Entry> entry : telemetryRequest.entrySet()) {
for (KvEntry kv : entry.getValue()) {
entries.add(new BasicTsKvEntry(entry.getKey(), kv));
}
@@ -513,6 +560,15 @@ public class TelemetryController extends BaseController {
};
}
+ private void logTimeseriesDeleted(SecurityUser user, EntityId entityId, List keys, Throwable e) {
+ try {
+ logEntityAction(user, (UUIDBased & EntityId) entityId, null, null, ActionType.TIMESERIES_DELETED, toException(e),
+ keys);
+ } catch (ThingsboardException te) {
+ log.warn("Failed to log timeseries delete", te);
+ }
+ }
+
private void logAttributesDeleted(SecurityUser user, EntityId entityId, String scope, List keys, Throwable e) {
try {
logEntityAction(user, (UUIDBased & EntityId) entityId, null, null, ActionType.ATTRIBUTES_DELETED, toException(e),
diff --git a/application/src/main/java/org/thingsboard/server/controller/TenantController.java b/application/src/main/java/org/thingsboard/server/controller/TenantController.java
index 1a7c116560..155f0a1095 100644
--- a/application/src/main/java/org/thingsboard/server/controller/TenantController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/TenantController.java
@@ -32,6 +32,7 @@ import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
+import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
import org.thingsboard.server.dao.tenant.TenantService;
import org.thingsboard.server.service.install.InstallScripts;
@@ -84,6 +85,8 @@ public class TenantController extends BaseController {
try {
TenantId tenantId = new TenantId(toUUID(strTenantId));
tenantService.deleteTenant(tenantId);
+
+ actorService.onEntityStateChange(tenantId, tenantId, ComponentLifecycleEvent.DELETED);
} catch (Exception e) {
throw handleException(e);
}
diff --git a/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java b/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java
index f863d0bb2a..2486cd8d41 100644
--- a/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java
+++ b/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java
@@ -24,9 +24,10 @@ import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;
import org.thingsboard.server.service.component.ComponentDiscoveryService;
import org.thingsboard.server.service.install.DataUpdateService;
-import org.thingsboard.server.service.install.DatabaseSchemaService;
import org.thingsboard.server.service.install.DatabaseUpgradeService;
+import org.thingsboard.server.service.install.EntityDatabaseSchemaService;
import org.thingsboard.server.service.install.SystemDataLoaderService;
+import org.thingsboard.server.service.install.TsDatabaseSchemaService;
@Service
@Profile("install")
@@ -43,7 +44,10 @@ public class ThingsboardInstallService {
private Boolean loadDemo;
@Autowired
- private DatabaseSchemaService databaseSchemaService;
+ private EntityDatabaseSchemaService entityDatabaseSchemaService;
+
+ @Autowired
+ private TsDatabaseSchemaService tsDatabaseSchemaService;
@Autowired
private DatabaseUpgradeService databaseUpgradeService;
@@ -88,6 +92,20 @@ public class ThingsboardInstallService {
dataUpdateService.updateData("1.4.0");
+ case "2.0.0":
+ log.info("Upgrading ThingsBoard from version 2.0.0 to 2.1.1 ...");
+
+ databaseUpgradeService.upgradeDatabase("2.0.0");
+
+ case "2.1.1":
+ log.info("Upgrading ThingsBoard from version 2.1.1 to 2.1.2 ...");
+
+ databaseUpgradeService.upgradeDatabase("2.1.1");
+ case "2.1.3":
+ log.info("Upgrading ThingsBoard from version 2.1.3 to 2.2.0 ...");
+
+ databaseUpgradeService.upgradeDatabase("2.1.3");
+
log.info("Updating system data...");
systemDataLoaderService.deleteSystemWidgetBundle("charts");
@@ -114,9 +132,13 @@ public class ThingsboardInstallService {
log.info("Starting ThingsBoard Installation...");
- log.info("Installing DataBase schema...");
+ log.info("Installing DataBase schema for entities...");
+
+ entityDatabaseSchemaService.createDatabaseSchema();
+
+ log.info("Installing DataBase schema for timeseries...");
- databaseSchemaService.createDatabaseSchema();
+ tsDatabaseSchemaService.createDatabaseSchema();
log.info("Loading system data...");
diff --git a/application/src/main/java/org/thingsboard/server/service/cluster/discovery/CurrentServerInstanceService.java b/application/src/main/java/org/thingsboard/server/service/cluster/discovery/CurrentServerInstanceService.java
index 2232ef898c..3ae7b16af1 100644
--- a/application/src/main/java/org/thingsboard/server/service/cluster/discovery/CurrentServerInstanceService.java
+++ b/application/src/main/java/org/thingsboard/server/service/cluster/discovery/CurrentServerInstanceService.java
@@ -19,7 +19,8 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
-import org.thingsboard.server.gen.discovery.ServerInstanceProtos;
+import org.thingsboard.server.common.msg.cluster.ServerAddress;
+import org.thingsboard.server.common.msg.cluster.ServerType;
import javax.annotation.PostConstruct;
@@ -43,8 +44,7 @@ public class CurrentServerInstanceService implements ServerInstanceService {
public void init() {
Assert.hasLength(rpcHost, missingProperty("rpc.bind_host"));
Assert.notNull(rpcPort, missingProperty("rpc.bind_port"));
-
- self = new ServerInstance(ServerInstanceProtos.ServerInfo.newBuilder().setHost(rpcHost).setPort(rpcPort).setTs(System.currentTimeMillis()).build());
+ self = new ServerInstance(new ServerAddress(rpcHost, rpcPort, ServerType.CORE));
log.info("Current server instance: [{};{}]", self.getHost(), self.getPort());
}
diff --git a/application/src/main/java/org/thingsboard/server/service/cluster/discovery/DiscoveryService.java b/application/src/main/java/org/thingsboard/server/service/cluster/discovery/DiscoveryService.java
index 516fca9484..f9caafa412 100644
--- a/application/src/main/java/org/thingsboard/server/service/cluster/discovery/DiscoveryService.java
+++ b/application/src/main/java/org/thingsboard/server/service/cluster/discovery/DiscoveryService.java
@@ -30,8 +30,4 @@ public interface DiscoveryService {
List getOtherServers();
- boolean addListener(DiscoveryServiceListener listener);
-
- boolean removeListener(DiscoveryServiceListener listener);
-
}
diff --git a/application/src/main/java/org/thingsboard/server/service/cluster/discovery/DummyDiscoveryService.java b/application/src/main/java/org/thingsboard/server/service/cluster/discovery/DummyDiscoveryService.java
index c21f1aa84c..358c8477d8 100644
--- a/application/src/main/java/org/thingsboard/server/service/cluster/discovery/DummyDiscoveryService.java
+++ b/application/src/main/java/org/thingsboard/server/service/cluster/discovery/DummyDiscoveryService.java
@@ -16,6 +16,7 @@
package org.thingsboard.server.service.cluster.discovery;
import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.DependsOn;
@@ -62,13 +63,5 @@ public class DummyDiscoveryService implements DiscoveryService {
return Collections.emptyList();
}
- @Override
- public boolean addListener(DiscoveryServiceListener listener) {
- return false;
- }
- @Override
- public boolean removeListener(DiscoveryServiceListener listener) {
- return false;
- }
}
diff --git a/application/src/main/java/org/thingsboard/server/service/cluster/discovery/ServerInstance.java b/application/src/main/java/org/thingsboard/server/service/cluster/discovery/ServerInstance.java
index 6eee5f3061..8f4525c20d 100644
--- a/application/src/main/java/org/thingsboard/server/service/cluster/discovery/ServerInstance.java
+++ b/application/src/main/java/org/thingsboard/server/service/cluster/discovery/ServerInstance.java
@@ -19,7 +19,6 @@ import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
import org.thingsboard.server.common.msg.cluster.ServerAddress;
-import org.thingsboard.server.gen.discovery.ServerInstanceProtos;
/**
* @author Andrew Shvayka
@@ -41,12 +40,6 @@ public final class ServerInstance implements Comparable {
this.port = serverAddress.getPort();
}
- public ServerInstance(ServerInstanceProtos.ServerInfo serverInfo) {
- this.host = serverInfo.getHost();
- this.port = serverInfo.getPort();
- this.serverAddress = new ServerAddress(host, port);
- }
-
@Override
public int compareTo(ServerInstance o) {
return this.serverAddress.compareTo(o.serverAddress);
diff --git a/application/src/main/java/org/thingsboard/server/service/cluster/discovery/ZkDiscoveryService.java b/application/src/main/java/org/thingsboard/server/service/cluster/discovery/ZkDiscoveryService.java
index c3ffbabbea..f5321e5806 100644
--- a/application/src/main/java/org/thingsboard/server/service/cluster/discovery/ZkDiscoveryService.java
+++ b/application/src/main/java/org/thingsboard/server/service/cluster/discovery/ZkDiscoveryService.java
@@ -16,17 +16,22 @@
package org.thingsboard.server.service.cluster.discovery;
import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.SerializationException;
import org.apache.commons.lang3.SerializationUtils;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
+import org.apache.curator.framework.imps.CuratorFrameworkState;
import org.apache.curator.framework.recipes.cache.ChildData;
import org.apache.curator.framework.recipes.cache.PathChildrenCache;
import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent;
import org.apache.curator.framework.recipes.cache.PathChildrenCacheListener;
+import org.apache.curator.framework.state.ConnectionState;
+import org.apache.curator.framework.state.ConnectionStateListener;
import org.apache.curator.retry.RetryForever;
import org.apache.curator.utils.CloseableUtils;
import org.apache.zookeeper.CreateMode;
+import org.apache.zookeeper.KeeperException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@@ -35,7 +40,9 @@ import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
+import org.thingsboard.server.actors.service.ActorService;
import org.thingsboard.server.common.msg.cluster.ServerAddress;
+import org.thingsboard.server.service.cluster.routing.ClusterRoutingService;
import org.thingsboard.server.service.state.DeviceStateService;
import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService;
import org.thingsboard.server.utils.MiscUtils;
@@ -44,9 +51,10 @@ import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.List;
import java.util.NoSuchElementException;
-import java.util.concurrent.CopyOnWriteArrayList;
import java.util.stream.Collectors;
+import static org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent.Type.CHILD_REMOVED;
+
/**
* @author Andrew Shvayka
*/
@@ -79,12 +87,19 @@ public class ZkDiscoveryService implements DiscoveryService, PathChildrenCacheLi
@Lazy
private DeviceStateService deviceStateService;
- private final List listeners = new CopyOnWriteArrayList<>();
+ @Autowired
+ @Lazy
+ private ActorService actorService;
+
+ @Autowired
+ @Lazy
+ private ClusterRoutingService routingService;
private CuratorFramework client;
private PathChildrenCache cache;
private String nodePath;
+ private volatile boolean stopped = false;
@PostConstruct
public void init() {
@@ -106,6 +121,7 @@ public class ZkDiscoveryService implements DiscoveryService, PathChildrenCacheLi
cache.start();
} catch (Exception e) {
log.error("Failed to connect to ZK: {}", e.getMessage(), e);
+ CloseableUtils.closeQuietly(cache);
CloseableUtils.closeQuietly(client);
throw new RuntimeException(e);
}
@@ -113,23 +129,74 @@ public class ZkDiscoveryService implements DiscoveryService, PathChildrenCacheLi
@PreDestroy
public void destroy() {
+ stopped = true;
unpublishCurrentServer();
+ CloseableUtils.closeQuietly(cache);
CloseableUtils.closeQuietly(client);
log.info("Stopped discovery service");
}
@Override
- public void publishCurrentServer() {
+ public synchronized void publishCurrentServer() {
+ ServerInstance self = this.serverInstance.getSelf();
+ if (currentServerExists()) {
+ log.info("[{}:{}] ZK node for current instance already exists, NOT created new one: {}", self.getHost(), self.getPort(), nodePath);
+ } else {
+ try {
+ log.info("[{}:{}] Creating ZK node for current instance", self.getHost(), self.getPort());
+ nodePath = client.create()
+ .creatingParentsIfNeeded()
+ .withMode(CreateMode.EPHEMERAL_SEQUENTIAL).forPath(zkNodesDir + "/", SerializationUtils.serialize(self.getServerAddress()));
+ log.info("[{}:{}] Created ZK node for current instance: {}", self.getHost(), self.getPort(), nodePath);
+ client.getConnectionStateListenable().addListener(checkReconnect(self));
+ } catch (Exception e) {
+ log.error("Failed to create ZK node", e);
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ private boolean currentServerExists() {
+ if (nodePath == null) {
+ return false;
+ }
try {
ServerInstance self = this.serverInstance.getSelf();
- log.info("[{}:{}] Creating ZK node for current instance", self.getHost(), self.getPort());
- nodePath = client.create()
- .creatingParentsIfNeeded()
- .withMode(CreateMode.EPHEMERAL_SEQUENTIAL).forPath(zkNodesDir + "/", SerializationUtils.serialize(self.getServerAddress()));
- log.info("[{}:{}] Created ZK node for current instance: {}", self.getHost(), self.getPort(), nodePath);
+ ServerAddress registeredServerAdress = null;
+ registeredServerAdress = SerializationUtils.deserialize(client.getData().forPath(nodePath));
+ if (self.getServerAddress() != null && self.getServerAddress().equals(registeredServerAdress)) {
+ return true;
+ }
+ } catch (KeeperException.NoNodeException e) {
+ log.info("ZK node does not exist: {}", nodePath);
} catch (Exception e) {
- log.error("Failed to create ZK node", e);
- throw new RuntimeException(e);
+ log.error("Couldn't check if ZK node exists", e);
+ }
+ return false;
+ }
+
+ private ConnectionStateListener checkReconnect(ServerInstance self) {
+ return (client, newState) -> {
+ log.info("[{}:{}] ZK state changed: {}", self.getHost(), self.getPort(), newState);
+ if (newState == ConnectionState.LOST) {
+ reconnect();
+ }
+ };
+ }
+
+ private boolean reconnectInProgress = false;
+
+ private synchronized void reconnect() {
+ if (!reconnectInProgress) {
+ reconnectInProgress = true;
+ try {
+ client.blockUntilConnected();
+ publishCurrentServer();
+ } catch (InterruptedException e) {
+ log.error("Failed to reconnect to ZK: {}", e.getMessage(), e);
+ } finally {
+ reconnectInProgress = false;
+ }
}
}
@@ -156,7 +223,7 @@ public class ZkDiscoveryService implements DiscoveryService, PathChildrenCacheLi
.filter(cd -> !cd.getPath().equals(nodePath))
.map(cd -> {
try {
- return new ServerInstance( (ServerAddress) SerializationUtils.deserialize(cd.getData()));
+ return new ServerInstance((ServerAddress) SerializationUtils.deserialize(cd.getData()));
} catch (NoSuchElementException e) {
log.error("Failed to decode ZK node", e);
throw new RuntimeException(e);
@@ -165,18 +232,16 @@ public class ZkDiscoveryService implements DiscoveryService, PathChildrenCacheLi
.collect(Collectors.toList());
}
- @Override
- public boolean addListener(DiscoveryServiceListener listener) {
- return listeners.add(listener);
- }
-
- @Override
- public boolean removeListener(DiscoveryServiceListener listener) {
- return listeners.remove(listener);
- }
-
@Override
public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) {
+ if (stopped) {
+ log.debug("Ignoring application ready event. Service is stopped.");
+ return;
+ }
+ if (client.getState() != CuratorFrameworkState.STARTED) {
+ log.debug("Ignoring application ready event, ZK client is not started, ZK client state [{}]", client.getState());
+ return;
+ }
publishCurrentServer();
getOtherServers().forEach(
server -> log.info("Found active server: [{}:{}]", server.getHost(), server.getPort())
@@ -185,6 +250,14 @@ public class ZkDiscoveryService implements DiscoveryService, PathChildrenCacheLi
@Override
public void childEvent(CuratorFramework curatorFramework, PathChildrenCacheEvent pathChildrenCacheEvent) throws Exception {
+ if (stopped) {
+ log.debug("Ignoring {}. Service is stopped.", pathChildrenCacheEvent);
+ return;
+ }
+ if (client.getState() != CuratorFrameworkState.STARTED) {
+ log.debug("Ignoring {}, ZK client is not started, ZK client state [{}]", pathChildrenCacheEvent, client.getState());
+ return;
+ }
ChildData data = pathChildrenCacheEvent.getData();
if (data == null) {
log.debug("Ignoring {} due to empty child data", pathChildrenCacheEvent);
@@ -193,12 +266,16 @@ public class ZkDiscoveryService implements DiscoveryService, PathChildrenCacheLi
log.debug("Ignoring {} due to empty child's data", pathChildrenCacheEvent);
return;
} else if (nodePath != null && nodePath.equals(data.getPath())) {
+ if (pathChildrenCacheEvent.getType() == CHILD_REMOVED) {
+ log.info("ZK node for current instance is somehow deleted.");
+ publishCurrentServer();
+ }
log.debug("Ignoring event about current server {}", pathChildrenCacheEvent);
return;
}
ServerInstance instance;
try {
- ServerAddress serverAddress = SerializationUtils.deserialize(data.getData());
+ ServerAddress serverAddress = SerializationUtils.deserialize(data.getData());
instance = new ServerInstance(serverAddress);
} catch (SerializationException e) {
log.error("Failed to decode server instance for node {}", data.getPath(), e);
@@ -207,17 +284,20 @@ public class ZkDiscoveryService implements DiscoveryService, PathChildrenCacheLi
log.info("Processing [{}] event for [{}:{}]", pathChildrenCacheEvent.getType(), instance.getHost(), instance.getPort());
switch (pathChildrenCacheEvent.getType()) {
case CHILD_ADDED:
+ routingService.onServerAdded(instance);
tsSubService.onClusterUpdate();
deviceStateService.onClusterUpdate();
- listeners.forEach(listener -> listener.onServerAdded(instance));
+ actorService.onServerAdded(instance);
break;
case CHILD_UPDATED:
- listeners.forEach(listener -> listener.onServerUpdated(instance));
+ routingService.onServerUpdated(instance);
+ actorService.onServerUpdated(instance);
break;
case CHILD_REMOVED:
+ routingService.onServerRemoved(instance);
tsSubService.onClusterUpdate();
deviceStateService.onClusterUpdate();
- listeners.forEach(listener -> listener.onServerRemoved(instance));
+ actorService.onServerRemoved(instance);
break;
default:
break;
diff --git a/application/src/main/java/org/thingsboard/server/service/cluster/routing/ClusterRoutingService.java b/application/src/main/java/org/thingsboard/server/service/cluster/routing/ClusterRoutingService.java
index 272073dd23..bb76c74e28 100644
--- a/application/src/main/java/org/thingsboard/server/service/cluster/routing/ClusterRoutingService.java
+++ b/application/src/main/java/org/thingsboard/server/service/cluster/routing/ClusterRoutingService.java
@@ -17,6 +17,8 @@ package org.thingsboard.server.service.cluster.routing;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.msg.cluster.ServerAddress;
+import org.thingsboard.server.common.msg.cluster.ServerType;
+import org.thingsboard.server.service.cluster.discovery.DiscoveryServiceListener;
import java.util.Optional;
import java.util.UUID;
@@ -24,7 +26,7 @@ import java.util.UUID;
/**
* @author Andrew Shvayka
*/
-public interface ClusterRoutingService {
+public interface ClusterRoutingService extends DiscoveryServiceListener {
ServerAddress getCurrentServer();
@@ -32,4 +34,9 @@ public interface ClusterRoutingService {
Optional resolveById(EntityId entityId);
+ Optional resolveByUuid(ServerType server, UUID uuid);
+
+ Optional resolveById(ServerType server, EntityId entityId);
+
+
}
diff --git a/application/src/main/java/org/thingsboard/server/service/cluster/routing/ConsistentClusterRoutingService.java b/application/src/main/java/org/thingsboard/server/service/cluster/routing/ConsistentClusterRoutingService.java
index 5087b5ce46..f47f6d2e8e 100644
--- a/application/src/main/java/org/thingsboard/server/service/cluster/routing/ConsistentClusterRoutingService.java
+++ b/application/src/main/java/org/thingsboard/server/service/cluster/routing/ConsistentClusterRoutingService.java
@@ -24,12 +24,14 @@ import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.msg.cluster.ServerAddress;
+import org.thingsboard.server.common.msg.cluster.ServerType;
import org.thingsboard.server.service.cluster.discovery.DiscoveryService;
import org.thingsboard.server.service.cluster.discovery.DiscoveryServiceListener;
import org.thingsboard.server.service.cluster.discovery.ServerInstance;
import org.thingsboard.server.utils.MiscUtils;
import javax.annotation.PostConstruct;
+import java.util.Arrays;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.ConcurrentNavigableMap;
@@ -41,7 +43,7 @@ import java.util.concurrent.ConcurrentSkipListMap;
@Service
@Slf4j
-public class ConsistentClusterRoutingService implements ClusterRoutingService, DiscoveryServiceListener {
+public class ConsistentClusterRoutingService implements ClusterRoutingService {
@Autowired
private DiscoveryService discoveryService;
@@ -55,15 +57,19 @@ public class ConsistentClusterRoutingService implements ClusterRoutingService, D
private HashFunction hashFunction;
- private final ConcurrentNavigableMap circle =
- new ConcurrentSkipListMap<>();
+ private ConsistentHashCircle[] circles;
+ private ConsistentHashCircle rootCircle;
@PostConstruct
public void init() {
log.info("Initializing Cluster routing service!");
- hashFunction = MiscUtils.forName(hashFunctionName);
- discoveryService.addListener(this);
+ this.hashFunction = MiscUtils.forName(hashFunctionName);
this.currentServer = discoveryService.getCurrentServer();
+ this.circles = new ConsistentHashCircle[ServerType.values().length];
+ for (ServerType serverType : ServerType.values()) {
+ circles[serverType.ordinal()] = new ConsistentHashCircle();
+ }
+ rootCircle = circles[ServerType.CORE.ordinal()];
addNode(discoveryService.getCurrentServer());
for (ServerInstance instance : discoveryService.getOtherServers()) {
addNode(instance);
@@ -79,11 +85,25 @@ public class ConsistentClusterRoutingService implements ClusterRoutingService, D
@Override
public Optional resolveById(EntityId entityId) {
- return resolveByUuid(entityId.getId());
+ return resolveByUuid(rootCircle, entityId.getId());
}
@Override
public Optional resolveByUuid(UUID uuid) {
+ return resolveByUuid(rootCircle, uuid);
+ }
+
+ @Override
+ public Optional resolveByUuid(ServerType server, UUID uuid) {
+ return resolveByUuid(circles[server.ordinal()], uuid);
+ }
+
+ @Override
+ public Optional resolveById(ServerType server, EntityId entityId) {
+ return resolveByUuid(circles[server.ordinal()], entityId.getId());
+ }
+
+ private Optional resolveByUuid(ConsistentHashCircle circle, UUID uuid) {
Assert.notNull(uuid);
if (circle.isEmpty()) {
return Optional.empty();
@@ -125,13 +145,13 @@ public class ConsistentClusterRoutingService implements ClusterRoutingService, D
private void addNode(ServerInstance instance) {
for (int i = 0; i < virtualNodesSize; i++) {
- circle.put(hash(instance, i).asLong(), instance);
+ circles[instance.getServerAddress().getServerType().ordinal()].put(hash(instance, i).asLong(), instance);
}
}
private void removeNode(ServerInstance instance) {
for (int i = 0; i < virtualNodesSize; i++) {
- circle.remove(hash(instance, i).asLong());
+ circles[instance.getServerAddress().getServerType().ordinal()].remove(hash(instance, i).asLong());
}
}
@@ -141,7 +161,7 @@ public class ConsistentClusterRoutingService implements ClusterRoutingService, D
private void logCircle() {
log.trace("Consistent Hash Circle Start");
- circle.entrySet().forEach((e) -> log.debug("{} -> {}", e.getKey(), e.getValue().getServerAddress()));
+ Arrays.asList(circles).forEach(ConsistentHashCircle::log);
log.trace("Consistent Hash Circle End");
}
diff --git a/application/src/main/java/org/thingsboard/server/service/cluster/routing/ConsistentHashCircle.java b/application/src/main/java/org/thingsboard/server/service/cluster/routing/ConsistentHashCircle.java
new file mode 100644
index 0000000000..a8c8f37a02
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/service/cluster/routing/ConsistentHashCircle.java
@@ -0,0 +1,63 @@
+/**
+ * Copyright © 2016-2018 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.service.cluster.routing;
+
+import lombok.extern.slf4j.Slf4j;
+import org.thingsboard.server.service.cluster.discovery.ServerInstance;
+
+import java.util.concurrent.ConcurrentNavigableMap;
+import java.util.concurrent.ConcurrentSkipListMap;
+
+/**
+ * Created by ashvayka on 23.09.18.
+ */
+@Slf4j
+public class ConsistentHashCircle {
+ private final ConcurrentNavigableMap circle =
+ new ConcurrentSkipListMap<>();
+
+ public void put(long hash, ServerInstance instance) {
+ circle.put(hash, instance);
+ }
+
+ public void remove(long hash) {
+ circle.remove(hash);
+ }
+
+ public boolean isEmpty() {
+ return circle.isEmpty();
+ }
+
+ public boolean containsKey(Long hash) {
+ return circle.containsKey(hash);
+ }
+
+ public ConcurrentNavigableMap tailMap(Long hash) {
+ return circle.tailMap(hash);
+ }
+
+ public Long firstKey() {
+ return circle.firstKey();
+ }
+
+ public ServerInstance get(Long hash) {
+ return circle.get(hash);
+ }
+
+ public void log() {
+ circle.entrySet().forEach((e) -> log.debug("{} -> {}", e.getKey(), e.getValue().getServerAddress()));
+ }
+}
diff --git a/application/src/main/java/org/thingsboard/server/service/cluster/rpc/GrpcSession.java b/application/src/main/java/org/thingsboard/server/service/cluster/rpc/GrpcSession.java
index 7216c4319f..dd0a995c84 100644
--- a/application/src/main/java/org/thingsboard/server/service/cluster/rpc/GrpcSession.java
+++ b/application/src/main/java/org/thingsboard/server/service/cluster/rpc/GrpcSession.java
@@ -15,10 +15,13 @@
*/
package org.thingsboard.server.service.cluster.rpc;
+import io.grpc.Channel;
+import io.grpc.ManagedChannel;
import io.grpc.stub.StreamObserver;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.thingsboard.server.common.msg.cluster.ServerAddress;
+import org.thingsboard.server.common.msg.cluster.ServerType;
import org.thingsboard.server.gen.cluster.ClusterAPIProtos;
import java.io.Closeable;
@@ -33,6 +36,7 @@ public final class GrpcSession implements Closeable {
private final UUID sessionId;
private final boolean client;
private final GrpcSessionListener listener;
+ private final ManagedChannel channel;
private StreamObserver inputStream;
private StreamObserver outputStream;
@@ -40,10 +44,10 @@ public final class GrpcSession implements Closeable {
private ServerAddress remoteServer;
public GrpcSession(GrpcSessionListener listener) {
- this(null, listener);
+ this(null, listener, null);
}
- public GrpcSession(ServerAddress remoteServer, GrpcSessionListener listener) {
+ public GrpcSession(ServerAddress remoteServer, GrpcSessionListener listener, ManagedChannel channel) {
this.sessionId = UUID.randomUUID();
this.listener = listener;
if (remoteServer != null) {
@@ -53,6 +57,7 @@ public final class GrpcSession implements Closeable {
} else {
this.client = false;
}
+ this.channel = channel;
}
public void initInputStream() {
@@ -61,8 +66,8 @@ public final class GrpcSession implements Closeable {
public void onNext(ClusterAPIProtos.ClusterMessage clusterMessage) {
if (!connected && clusterMessage.getMessageType() == ClusterAPIProtos.MessageType.CONNECT_RPC_MESSAGE) {
connected = true;
- ServerAddress rpcAddress = new ServerAddress(clusterMessage.getServerAddress().getHost(), clusterMessage.getServerAddress().getPort());
- remoteServer = new ServerAddress(rpcAddress.getHost(), rpcAddress.getPort());
+ ServerAddress rpcAddress = new ServerAddress(clusterMessage.getServerAddress().getHost(), clusterMessage.getServerAddress().getPort(), ServerType.CORE);
+ remoteServer = new ServerAddress(rpcAddress.getHost(), rpcAddress.getPort(), ServerType.CORE);
listener.onConnected(GrpcSession.this);
}
if (connected) {
@@ -104,5 +109,8 @@ public final class GrpcSession implements Closeable {
} catch (IllegalStateException e) {
log.debug("[{}] Failed to close output stream: {}", sessionId, e.getMessage());
}
+ if (channel != null) {
+ channel.shutdownNow();
+ }
}
}
diff --git a/application/src/main/java/org/thingsboard/server/service/component/AnnotationComponentDiscoveryService.java b/application/src/main/java/org/thingsboard/server/service/component/AnnotationComponentDiscoveryService.java
index f3ceed2069..545baf8e11 100644
--- a/application/src/main/java/org/thingsboard/server/service/component/AnnotationComponentDiscoveryService.java
+++ b/application/src/main/java/org/thingsboard/server/service/component/AnnotationComponentDiscoveryService.java
@@ -30,7 +30,6 @@ import org.thingsboard.rule.engine.api.NodeConfiguration;
import org.thingsboard.rule.engine.api.NodeDefinition;
import org.thingsboard.rule.engine.api.RuleNode;
import org.thingsboard.rule.engine.api.TbRelationTypes;
-import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.plugin.ComponentDescriptor;
import org.thingsboard.server.common.data.plugin.ComponentType;
import org.thingsboard.server.dao.component.ComponentDescriptorService;
@@ -52,6 +51,7 @@ import java.util.Set;
@Slf4j
public class AnnotationComponentDiscoveryService implements ComponentDiscoveryService {
+ public static final int MAX_OPTIMISITC_RETRIES = 3;
@Value("${plugins.scan_packages}")
private String[] scanPackages;
@@ -81,17 +81,32 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe
private void registerRuleNodeComponents() {
Set ruleNodeBeanDefinitions = getBeanDefinitions(RuleNode.class);
for (BeanDefinition def : ruleNodeBeanDefinitions) {
- try {
- String clazzName = def.getBeanClassName();
- Class> clazz = Class.forName(clazzName);
- RuleNode ruleNodeAnnotation = clazz.getAnnotation(RuleNode.class);
- ComponentType type = ruleNodeAnnotation.type();
- ComponentDescriptor component = scanAndPersistComponent(def, type);
- components.put(component.getClazz(), component);
- componentsMap.computeIfAbsent(type, k -> new ArrayList<>()).add(component);
- } catch (Exception e) {
- log.error("Can't initialize component {}, due to {}", def.getBeanClassName(), e.getMessage(), e);
- throw new RuntimeException(e);
+ int retryCount = 0;
+ Exception cause = null;
+ while (retryCount < MAX_OPTIMISITC_RETRIES) {
+ try {
+ String clazzName = def.getBeanClassName();
+ Class> clazz = Class.forName(clazzName);
+ RuleNode ruleNodeAnnotation = clazz.getAnnotation(RuleNode.class);
+ ComponentType type = ruleNodeAnnotation.type();
+ ComponentDescriptor component = scanAndPersistComponent(def, type);
+ components.put(component.getClazz(), component);
+ componentsMap.computeIfAbsent(type, k -> new ArrayList<>()).add(component);
+ break;
+ } catch (Exception e) {
+ log.trace("Can't initialize component {}, due to {}", def.getBeanClassName(), e.getMessage(), e);
+ cause = e;
+ retryCount++;
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e1) {
+ throw new RuntimeException(e1);
+ }
+ }
+ }
+ if (cause != null && retryCount == MAX_OPTIMISITC_RETRIES) {
+ log.error("Can't initialize component {}, due to {}", def.getBeanClassName(), cause.getMessage(), cause);
+ throw new RuntimeException(cause);
}
}
}
diff --git a/application/src/main/java/org/thingsboard/server/service/install/CassandraDatabaseSchemaService.java b/application/src/main/java/org/thingsboard/server/service/install/CassandraAbstractDatabaseSchemaService.java
similarity index 78%
rename from application/src/main/java/org/thingsboard/server/service/install/CassandraDatabaseSchemaService.java
rename to application/src/main/java/org/thingsboard/server/service/install/CassandraAbstractDatabaseSchemaService.java
index dd76b21844..10559ba2d2 100644
--- a/application/src/main/java/org/thingsboard/server/service/install/CassandraDatabaseSchemaService.java
+++ b/application/src/main/java/org/thingsboard/server/service/install/CassandraAbstractDatabaseSchemaService.java
@@ -17,24 +17,17 @@ package org.thingsboard.server.service.install;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.context.annotation.Profile;
-import org.springframework.stereotype.Service;
import org.thingsboard.server.dao.cassandra.CassandraInstallCluster;
-import org.thingsboard.server.dao.util.NoSqlDao;
import org.thingsboard.server.service.install.cql.CQLStatementsParser;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
-@Service
-@NoSqlDao
-@Profile("install")
@Slf4j
-public class CassandraDatabaseSchemaService implements DatabaseSchemaService {
+public abstract class CassandraAbstractDatabaseSchemaService implements DatabaseSchemaService {
private static final String CASSANDRA_DIR = "cassandra";
- private static final String SCHEMA_CQL = "schema.cql";
@Autowired
private CassandraInstallCluster cluster;
@@ -42,10 +35,16 @@ public class CassandraDatabaseSchemaService implements DatabaseSchemaService {
@Autowired
private InstallScripts installScripts;
+ private final String schemaCql;
+
+ protected CassandraAbstractDatabaseSchemaService(String schemaCql) {
+ this.schemaCql = schemaCql;
+ }
+
@Override
public void createDatabaseSchema() throws Exception {
- log.info("Installing Cassandra DataBase schema...");
- Path schemaFile = Paths.get(installScripts.getDataDir(), CASSANDRA_DIR, SCHEMA_CQL);
+ log.info("Installing Cassandra DataBase schema part: " + schemaCql);
+ Path schemaFile = Paths.get(installScripts.getDataDir(), CASSANDRA_DIR, schemaCql);
loadCql(schemaFile);
}
diff --git a/application/src/main/java/org/thingsboard/server/service/install/CassandraDatabaseUpgradeService.java b/application/src/main/java/org/thingsboard/server/service/install/CassandraDatabaseUpgradeService.java
index f4ff92cf23..a2aeefaecf 100644
--- a/application/src/main/java/org/thingsboard/server/service/install/CassandraDatabaseUpgradeService.java
+++ b/application/src/main/java/org/thingsboard/server/service/install/CassandraDatabaseUpgradeService.java
@@ -39,10 +39,19 @@ import static org.thingsboard.server.service.install.DatabaseHelper.CONFIGURATIO
import static org.thingsboard.server.service.install.DatabaseHelper.CUSTOMER_ID;
import static org.thingsboard.server.service.install.DatabaseHelper.DASHBOARD;
import static org.thingsboard.server.service.install.DatabaseHelper.DEVICE;
+import static org.thingsboard.server.service.install.DatabaseHelper.END_TS;
+import static org.thingsboard.server.service.install.DatabaseHelper.ENTITY_ID;
+import static org.thingsboard.server.service.install.DatabaseHelper.ENTITY_TYPE;
+import static org.thingsboard.server.service.install.DatabaseHelper.ENTITY_VIEW;
+import static org.thingsboard.server.service.install.DatabaseHelper.ENTITY_VIEWS;
import static org.thingsboard.server.service.install.DatabaseHelper.ID;
+import static org.thingsboard.server.service.install.DatabaseHelper.KEYS;
+import static org.thingsboard.server.service.install.DatabaseHelper.NAME;
import static org.thingsboard.server.service.install.DatabaseHelper.SEARCH_TEXT;
+import static org.thingsboard.server.service.install.DatabaseHelper.START_TS;
import static org.thingsboard.server.service.install.DatabaseHelper.TENANT_ID;
import static org.thingsboard.server.service.install.DatabaseHelper.TITLE;
+import static org.thingsboard.server.service.install.DatabaseHelper.TYPE;
@Service
@NoSqlDao
@@ -203,6 +212,47 @@ public class CassandraDatabaseUpgradeService implements DatabaseUpgradeService {
log.info("Schema updated.");
break;
+
+ case "2.0.0":
+
+ log.info("Updating schema ...");
+ schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "2.1.1", SCHEMA_UPDATE_CQL);
+ loadCql(schemaUpdateFile);
+ log.info("Schema updated.");
+
+ break;
+
+ case "2.1.1":
+
+ log.info("Upgrading Cassandra DataBase from version {} to 2.1.2 ...", fromVersion);
+
+ cluster.getSession();
+
+ ks = cluster.getCluster().getMetadata().getKeyspace(cluster.getKeyspaceName());
+
+ log.info("Dumping entity views ...");
+ Path entityViewsDump = CassandraDbHelper.dumpCfIfExists(ks, cluster.getSession(), ENTITY_VIEWS,
+ new String[]{ID, ENTITY_ID, ENTITY_TYPE, TENANT_ID, CUSTOMER_ID, NAME, TYPE, KEYS, START_TS, END_TS, SEARCH_TEXT, ADDITIONAL_INFO},
+ new String[]{"", "", "", "", "", "", "default", "", "0", "0", "", ""},
+ "tb-entity-views");
+ log.info("Entity views dumped.");
+
+ log.info("Updating schema ...");
+ schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "2.1.2", SCHEMA_UPDATE_CQL);
+ loadCql(schemaUpdateFile);
+ log.info("Schema updated.");
+
+ log.info("Restoring entity views ...");
+ if (entityViewsDump != null) {
+ CassandraDbHelper.loadCf(ks, cluster.getSession(), ENTITY_VIEW,
+ new String[]{ID, ENTITY_ID, ENTITY_TYPE, TENANT_ID, CUSTOMER_ID, NAME, TYPE, KEYS, START_TS, END_TS, SEARCH_TEXT, ADDITIONAL_INFO}, entityViewsDump);
+ Files.deleteIfExists(entityViewsDump);
+ }
+ log.info("Entity views restored.");
+
+ break;
+ case "2.1.3":
+ break;
default:
throw new RuntimeException("Unable to upgrade Cassandra database, unsupported fromVersion: " + fromVersion);
}
diff --git a/application/src/main/java/org/thingsboard/server/service/install/CassandraEntityDatabaseSchemaService.java b/application/src/main/java/org/thingsboard/server/service/install/CassandraEntityDatabaseSchemaService.java
new file mode 100644
index 0000000000..b492a51cbc
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/service/install/CassandraEntityDatabaseSchemaService.java
@@ -0,0 +1,30 @@
+/**
+ * Copyright © 2016-2018 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.service.install;
+
+import org.springframework.context.annotation.Profile;
+import org.springframework.stereotype.Service;
+import org.thingsboard.server.dao.util.NoSqlDao;
+
+@Service
+@NoSqlDao
+@Profile("install")
+public class CassandraEntityDatabaseSchemaService extends CassandraAbstractDatabaseSchemaService
+ implements EntityDatabaseSchemaService {
+ public CassandraEntityDatabaseSchemaService() {
+ super("schema-entities.cql");
+ }
+}
diff --git a/application/src/main/java/org/thingsboard/server/service/install/CassandraTsDatabaseSchemaService.java b/application/src/main/java/org/thingsboard/server/service/install/CassandraTsDatabaseSchemaService.java
new file mode 100644
index 0000000000..1a3a2e429b
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/service/install/CassandraTsDatabaseSchemaService.java
@@ -0,0 +1,30 @@
+/**
+ * Copyright © 2016-2018 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.service.install;
+
+import org.springframework.context.annotation.Profile;
+import org.springframework.stereotype.Service;
+import org.thingsboard.server.dao.util.NoSqlTsDao;
+
+@Service
+@NoSqlTsDao
+@Profile("install")
+public class CassandraTsDatabaseSchemaService extends CassandraAbstractDatabaseSchemaService
+ implements TsDatabaseSchemaService {
+ public CassandraTsDatabaseSchemaService() {
+ super("schema-ts.cql");
+ }
+}
diff --git a/application/src/main/java/org/thingsboard/server/service/install/DatabaseHelper.java b/application/src/main/java/org/thingsboard/server/service/install/DatabaseHelper.java
index 2f9b4a5ed3..699eaf1846 100644
--- a/application/src/main/java/org/thingsboard/server/service/install/DatabaseHelper.java
+++ b/application/src/main/java/org/thingsboard/server/service/install/DatabaseHelper.java
@@ -45,14 +45,23 @@ public class DatabaseHelper {
public static final CSVFormat CSV_DUMP_FORMAT = CSVFormat.DEFAULT.withNullString("\\N");
public static final String DEVICE = "device";
+ public static final String ENTITY_ID = "entity_id";
public static final String TENANT_ID = "tenant_id";
+ public static final String ENTITY_TYPE = "entity_type";
public static final String CUSTOMER_ID = "customer_id";
public static final String SEARCH_TEXT = "search_text";
public static final String ADDITIONAL_INFO = "additional_info";
public static final String ASSET = "asset";
public static final String DASHBOARD = "dashboard";
+ public static final String ENTITY_VIEWS = "entity_views";
+ public static final String ENTITY_VIEW = "entity_view";
public static final String ID = "id";
public static final String TITLE = "title";
+ public static final String TYPE = "type";
+ public static final String NAME = "name";
+ public static final String KEYS = "keys";
+ public static final String START_TS = "start_ts";
+ public static final String END_TS = "end_ts";
public static final String ASSIGNED_CUSTOMERS = "assigned_customers";
public static final String CONFIGURATION = "configuration";
diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionActorToAdaptorMsg.java b/application/src/main/java/org/thingsboard/server/service/install/EntityDatabaseSchemaService.java
similarity index 81%
rename from common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionActorToAdaptorMsg.java
rename to application/src/main/java/org/thingsboard/server/service/install/EntityDatabaseSchemaService.java
index fa1f6d5e8f..c215ea0d8f 100644
--- a/common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionActorToAdaptorMsg.java
+++ b/application/src/main/java/org/thingsboard/server/service/install/EntityDatabaseSchemaService.java
@@ -13,10 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.thingsboard.server.common.msg.session;
-
-public interface SessionActorToAdaptorMsg extends SessionMsg {
-
- ToDeviceMsg getMsg();
+package org.thingsboard.server.service.install;
+public interface EntityDatabaseSchemaService extends DatabaseSchemaService {
}
diff --git a/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseSchemaService.java b/application/src/main/java/org/thingsboard/server/service/install/SqlAbstractDatabaseSchemaService.java
similarity index 82%
rename from application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseSchemaService.java
rename to application/src/main/java/org/thingsboard/server/service/install/SqlAbstractDatabaseSchemaService.java
index 1daf66086a..4a39b8f60b 100644
--- a/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseSchemaService.java
+++ b/application/src/main/java/org/thingsboard/server/service/install/SqlAbstractDatabaseSchemaService.java
@@ -18,9 +18,6 @@ package org.thingsboard.server.service.install;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
-import org.springframework.context.annotation.Profile;
-import org.springframework.stereotype.Service;
-import org.thingsboard.server.dao.util.SqlDao;
import java.nio.charset.Charset;
import java.nio.file.Files;
@@ -29,14 +26,10 @@ import java.nio.file.Paths;
import java.sql.Connection;
import java.sql.DriverManager;
-@Service
-@Profile("install")
@Slf4j
-@SqlDao
-public class SqlDatabaseSchemaService implements DatabaseSchemaService {
+public abstract class SqlAbstractDatabaseSchemaService implements DatabaseSchemaService {
private static final String SQL_DIR = "sql";
- private static final String SCHEMA_SQL = "schema.sql";
@Value("${spring.datasource.url}")
private String dbUrl;
@@ -50,12 +43,18 @@ public class SqlDatabaseSchemaService implements DatabaseSchemaService {
@Autowired
private InstallScripts installScripts;
+ private final String schemaSql;
+
+ protected SqlAbstractDatabaseSchemaService(String schemaSql) {
+ this.schemaSql = schemaSql;
+ }
+
@Override
public void createDatabaseSchema() throws Exception {
- log.info("Installing SQL DataBase schema...");
+ log.info("Installing SQL DataBase schema part: " + schemaSql);
- Path schemaFile = Paths.get(installScripts.getDataDir(), SQL_DIR, SCHEMA_SQL);
+ Path schemaFile = Paths.get(installScripts.getDataDir(), SQL_DIR, schemaSql);
try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) {
String sql = new String(Files.readAllBytes(schemaFile), Charset.forName("UTF-8"));
conn.createStatement().execute(sql); //NOSONAR, ignoring because method used to load initial thingsboard database schema
diff --git a/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java b/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java
index 3a4a837217..b4a725d941 100644
--- a/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java
+++ b/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java
@@ -31,14 +31,24 @@ import java.nio.file.Paths;
import java.sql.Connection;
import java.sql.DriverManager;
+import static org.thingsboard.server.service.install.DatabaseHelper.ADDITIONAL_INFO;
import static org.thingsboard.server.service.install.DatabaseHelper.ASSIGNED_CUSTOMERS;
import static org.thingsboard.server.service.install.DatabaseHelper.CONFIGURATION;
import static org.thingsboard.server.service.install.DatabaseHelper.CUSTOMER_ID;
import static org.thingsboard.server.service.install.DatabaseHelper.DASHBOARD;
+import static org.thingsboard.server.service.install.DatabaseHelper.END_TS;
+import static org.thingsboard.server.service.install.DatabaseHelper.ENTITY_ID;
+import static org.thingsboard.server.service.install.DatabaseHelper.ENTITY_TYPE;
+import static org.thingsboard.server.service.install.DatabaseHelper.ENTITY_VIEW;
+import static org.thingsboard.server.service.install.DatabaseHelper.ENTITY_VIEWS;
import static org.thingsboard.server.service.install.DatabaseHelper.ID;
+import static org.thingsboard.server.service.install.DatabaseHelper.KEYS;
+import static org.thingsboard.server.service.install.DatabaseHelper.NAME;
import static org.thingsboard.server.service.install.DatabaseHelper.SEARCH_TEXT;
+import static org.thingsboard.server.service.install.DatabaseHelper.START_TS;
import static org.thingsboard.server.service.install.DatabaseHelper.TENANT_ID;
import static org.thingsboard.server.service.install.DatabaseHelper.TITLE;
+import static org.thingsboard.server.service.install.DatabaseHelper.TYPE;
@Service
@Profile("install")
@@ -107,6 +117,46 @@ public class SqlDatabaseUpgradeService implements DatabaseUpgradeService {
log.info("Schema updated.");
}
break;
+ case "2.0.0":
+ try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) {
+ log.info("Updating schema ...");
+ schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "2.1.1", SCHEMA_UPDATE_SQL);
+ loadSql(schemaUpdateFile, conn);
+ log.info("Schema updated.");
+ }
+ break;
+ case "2.1.1":
+ try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) {
+
+ log.info("Dumping entity views ...");
+ Path entityViewsDump = SqlDbHelper.dumpTableIfExists(conn, ENTITY_VIEWS,
+ new String[]{ID, ENTITY_ID, ENTITY_TYPE, TENANT_ID, CUSTOMER_ID, TYPE, NAME, KEYS, START_TS, END_TS, SEARCH_TEXT, ADDITIONAL_INFO},
+ new String[]{"", "", "", "", "", "default", "", "", "0", "0", "", ""},
+ "tb-entity-views", true);
+ log.info("Entity views dumped.");
+
+ log.info("Updating schema ...");
+ schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "2.1.2", SCHEMA_UPDATE_SQL);
+ loadSql(schemaUpdateFile, conn);
+ log.info("Schema updated.");
+
+ log.info("Restoring entity views ...");
+ if (entityViewsDump != null) {
+ SqlDbHelper.loadTable(conn, ENTITY_VIEW,
+ new String[]{ID, ENTITY_ID, ENTITY_TYPE, TENANT_ID, CUSTOMER_ID, TYPE, NAME, KEYS, START_TS, END_TS, SEARCH_TEXT, ADDITIONAL_INFO}, entityViewsDump, true);
+ Files.deleteIfExists(entityViewsDump);
+ }
+ log.info("Entity views restored.");
+ }
+ break;
+ case "2.1.3":
+ try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) {
+ log.info("Updating schema ...");
+ schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "2.2.0", SCHEMA_UPDATE_SQL);
+ loadSql(schemaUpdateFile, conn);
+ log.info("Schema updated.");
+ }
+ break;
default:
throw new RuntimeException("Unable to upgrade SQL database, unsupported fromVersion: " + fromVersion);
}
diff --git a/application/src/main/java/org/thingsboard/server/service/install/SqlEntityDatabaseSchemaService.java b/application/src/main/java/org/thingsboard/server/service/install/SqlEntityDatabaseSchemaService.java
new file mode 100644
index 0000000000..bfd371b28a
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/service/install/SqlEntityDatabaseSchemaService.java
@@ -0,0 +1,30 @@
+/**
+ * Copyright © 2016-2018 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.service.install;
+
+import org.springframework.context.annotation.Profile;
+import org.springframework.stereotype.Service;
+import org.thingsboard.server.dao.util.SqlDao;
+
+@Service
+@SqlDao
+@Profile("install")
+public class SqlEntityDatabaseSchemaService extends SqlAbstractDatabaseSchemaService
+ implements EntityDatabaseSchemaService {
+ public SqlEntityDatabaseSchemaService() {
+ super("schema-entities.sql");
+ }
+}
diff --git a/application/src/main/java/org/thingsboard/server/actors/session/SessionTerminationMsg.java b/application/src/main/java/org/thingsboard/server/service/install/SqlTsDatabaseSchemaService.java
similarity index 58%
rename from application/src/main/java/org/thingsboard/server/actors/session/SessionTerminationMsg.java
rename to application/src/main/java/org/thingsboard/server/service/install/SqlTsDatabaseSchemaService.java
index 38cf878fa7..03a35178bf 100644
--- a/application/src/main/java/org/thingsboard/server/actors/session/SessionTerminationMsg.java
+++ b/application/src/main/java/org/thingsboard/server/service/install/SqlTsDatabaseSchemaService.java
@@ -13,14 +13,18 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.thingsboard.server.actors.session;
+package org.thingsboard.server.service.install;
-import org.thingsboard.server.actors.shared.ActorTerminationMsg;
-import org.thingsboard.server.common.data.id.SessionId;
+import org.springframework.context.annotation.Profile;
+import org.springframework.stereotype.Service;
+import org.thingsboard.server.dao.util.SqlTsDao;
-public class SessionTerminationMsg extends ActorTerminationMsg {
-
- public SessionTerminationMsg(SessionId id) {
- super(id);
+@Service
+@SqlTsDao
+@Profile("install")
+public class SqlTsDatabaseSchemaService extends SqlAbstractDatabaseSchemaService
+ implements TsDatabaseSchemaService {
+ public SqlTsDatabaseSchemaService() {
+ super("schema-ts.sql");
}
-}
+}
\ No newline at end of file
diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/StatusCodeResponse.java b/application/src/main/java/org/thingsboard/server/service/install/TsDatabaseSchemaService.java
similarity index 83%
rename from common/message/src/main/java/org/thingsboard/server/common/msg/core/StatusCodeResponse.java
rename to application/src/main/java/org/thingsboard/server/service/install/TsDatabaseSchemaService.java
index b2d8611d1f..0a2ba7586c 100644
--- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/StatusCodeResponse.java
+++ b/application/src/main/java/org/thingsboard/server/service/install/TsDatabaseSchemaService.java
@@ -13,8 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.thingsboard.server.common.msg.core;
-
-public interface StatusCodeResponse extends ResponseMsg{
+package org.thingsboard.server.service.install;
+public interface TsDatabaseSchemaService extends DatabaseSchemaService {
}
diff --git a/application/src/main/java/org/thingsboard/server/service/install/cql/CassandraDbHelper.java b/application/src/main/java/org/thingsboard/server/service/install/cql/CassandraDbHelper.java
index bdf980fea2..7c03a719c9 100644
--- a/application/src/main/java/org/thingsboard/server/service/install/cql/CassandraDbHelper.java
+++ b/application/src/main/java/org/thingsboard/server/service/install/cql/CassandraDbHelper.java
@@ -147,6 +147,8 @@ public class CassandraDbHelper {
str = new Double(row.getDouble(index)).toString();
} else if (type == DataType.cint()) {
str = new Integer(row.getInt(index)).toString();
+ } else if (type == DataType.bigint()) {
+ str = new Long(row.getLong(index)).toString();
} else if (type == DataType.uuid()) {
str = row.getUUID(index).toString();
} else if (type == DataType.timeuuid()) {
@@ -193,6 +195,8 @@ public class CassandraDbHelper {
boundStatement.setDouble(column, Double.valueOf(value));
} else if (type == DataType.cint()) {
boundStatement.setInt(column, Integer.valueOf(value));
+ } else if (type == DataType.bigint()) {
+ boundStatement.setLong(column, Long.valueOf(value));
} else if (type == DataType.uuid()) {
boundStatement.setUUID(column, UUID.fromString(value));
} else if (type == DataType.timeuuid()) {
diff --git a/application/src/main/java/org/thingsboard/server/service/queue/DefaultMsgQueueService.java b/application/src/main/java/org/thingsboard/server/service/queue/DefaultMsgQueueService.java
deleted file mode 100644
index 927584789d..0000000000
--- a/application/src/main/java/org/thingsboard/server/service/queue/DefaultMsgQueueService.java
+++ /dev/null
@@ -1,111 +0,0 @@
-/**
- * Copyright © 2016-2018 The Thingsboard Authors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.thingsboard.server.service.queue;
-
-import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.stereotype.Service;
-import org.thingsboard.server.common.data.id.TenantId;
-import org.thingsboard.server.common.msg.TbMsg;
-import org.thingsboard.server.common.transport.quota.tenant.TenantQuotaService;
-import org.thingsboard.server.dao.queue.MsgQueue;
-
-import javax.annotation.PostConstruct;
-import javax.annotation.PreDestroy;
-import java.util.Map;
-import java.util.UUID;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicLong;
-
-@Service
-@Slf4j
-public class DefaultMsgQueueService implements MsgQueueService {
-
- @Value("${actors.rule.queue.max_size}")
- private long queueMaxSize;
-
- @Value("${actors.rule.queue.cleanup_period}")
- private long queueCleanUpPeriod;
-
- @Autowired
- private MsgQueue msgQueue;
-
- @Autowired
- private TenantQuotaService quotaService;
-
- private ScheduledExecutorService cleanupExecutor;
-
- private Map pendingCountPerTenant = new ConcurrentHashMap<>();
-
- @PostConstruct
- public void init() {
- if (queueCleanUpPeriod > 0) {
- cleanupExecutor = Executors.newSingleThreadScheduledExecutor();
- cleanupExecutor.scheduleAtFixedRate(() -> cleanup(),
- queueCleanUpPeriod, queueCleanUpPeriod, TimeUnit.SECONDS);
- }
- }
-
- @PreDestroy
- public void stop() {
- if (cleanupExecutor != null) {
- cleanupExecutor.shutdownNow();
- }
- }
-
- @Override
- public ListenableFuture put(TenantId tenantId, TbMsg msg, UUID nodeId, long clusterPartition) {
- if(quotaService.isQuotaExceeded(tenantId.getId().toString())) {
- log.warn("Tenant TbMsg Quota exceeded for [{}:{}] . Reject", tenantId.getId());
- return Futures.immediateFailedFuture(new RuntimeException("Tenant TbMsg Quota exceeded"));
- }
-
- AtomicLong pendingMsgCount = pendingCountPerTenant.computeIfAbsent(tenantId, key -> new AtomicLong());
- if (pendingMsgCount.incrementAndGet() < queueMaxSize) {
- return msgQueue.put(tenantId, msg, nodeId, clusterPartition);
- } else {
- pendingMsgCount.decrementAndGet();
- return Futures.immediateFailedFuture(new RuntimeException("Message queue is full!"));
- }
- }
-
- @Override
- public ListenableFuture ack(TenantId tenantId, TbMsg msg, UUID nodeId, long clusterPartition) {
- ListenableFuture result = msgQueue.ack(tenantId, msg, nodeId, clusterPartition);
- AtomicLong pendingMsgCount = pendingCountPerTenant.computeIfAbsent(tenantId, key -> new AtomicLong());
- pendingMsgCount.decrementAndGet();
- return result;
- }
-
- @Override
- public Iterable findUnprocessed(TenantId tenantId, UUID nodeId, long clusterPartition) {
- return msgQueue.findUnprocessed(tenantId, nodeId, clusterPartition);
- }
-
- private void cleanup() {
- pendingCountPerTenant.forEach((tenantId, pendingMsgCount) -> {
- pendingMsgCount.set(0);
- msgQueue.cleanUp(tenantId);
- });
- }
-
-}
diff --git a/application/src/main/java/org/thingsboard/server/service/queue/MsgQueueService.java b/application/src/main/java/org/thingsboard/server/service/queue/MsgQueueService.java
deleted file mode 100644
index 2cf001ba73..0000000000
--- a/application/src/main/java/org/thingsboard/server/service/queue/MsgQueueService.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/**
- * Copyright © 2016-2018 The Thingsboard Authors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.thingsboard.server.service.queue;
-
-import com.google.common.util.concurrent.ListenableFuture;
-import org.thingsboard.server.common.data.id.TenantId;
-import org.thingsboard.server.common.msg.TbMsg;
-
-import java.util.UUID;
-
-public interface MsgQueueService {
-
- ListenableFuture put(TenantId tenantId, TbMsg msg, UUID nodeId, long clusterPartition);
-
- ListenableFuture ack(TenantId tenantId, TbMsg msg, UUID nodeId, long clusterPartition);
-
- Iterable findUnprocessed(TenantId tenantId, UUID nodeId, long clusterPartition);
-
-}
diff --git a/application/src/main/java/org/thingsboard/server/service/rpc/DefaultDeviceRpcService.java b/application/src/main/java/org/thingsboard/server/service/rpc/DefaultDeviceRpcService.java
index 64f5213d65..a65c1a5771 100644
--- a/application/src/main/java/org/thingsboard/server/service/rpc/DefaultDeviceRpcService.java
+++ b/application/src/main/java/org/thingsboard/server/service/rpc/DefaultDeviceRpcService.java
@@ -90,7 +90,7 @@ public class DefaultDeviceRpcService implements DeviceRpcService {
@Override
public void processRestAPIRpcRequestToRuleEngine(ToDeviceRpcRequest request, Consumer responseConsumer) {
- log.trace("[{}] Processing local rpc call to rule engine [{}]", request.getTenantId(), request.getDeviceId());
+ log.trace("[{}][{}] Processing REST API call to rule engine [{}]", request.getTenantId(), request.getId(), request.getDeviceId());
UUID requestId = request.getId();
localToRuleEngineRpcRequests.put(requestId, responseConsumer);
sendRpcRequestToRuleEngine(request);
@@ -98,31 +98,11 @@ public class DefaultDeviceRpcService implements DeviceRpcService {
}
@Override
- public void processRestAPIRpcResponseFromRuleEngine(FromDeviceRpcResponse response) {
- UUID requestId = response.getId();
- Consumer consumer = localToRuleEngineRpcRequests.remove(requestId);
- if (consumer != null) {
- consumer.accept(response);
- } else {
- log.trace("[{}] Unknown or stale rpc response received [{}]", requestId, response);
- }
- }
-
- @Override
- public void processRpcRequestToDevice(ToDeviceRpcRequest request, Consumer responseConsumer) {
- log.trace("[{}] Processing local rpc call to device [{}]", request.getTenantId(), request.getDeviceId());
- UUID requestId = request.getId();
- localToDeviceRpcRequests.put(requestId, responseConsumer);
- sendRpcRequestToDevice(request);
- scheduleTimeout(request, requestId, localToDeviceRpcRequests);
- }
-
- @Override
- public void processRpcResponseFromDevice(FromDeviceRpcResponse response) {
- log.trace("[{}] response to request: [{}]", this.hashCode(), response.getId());
- if (routingService.getCurrentServer().equals(response.getServerAddress())) {
+ public void processResponseToServerSideRPCRequestFromRuleEngine(ServerAddress requestOriginAddress, FromDeviceRpcResponse response) {
+ log.trace("[{}] Received response to server-side RPC request from rule engine: [{}]", response.getId(), requestOriginAddress);
+ if (routingService.getCurrentServer().equals(requestOriginAddress)) {
UUID requestId = response.getId();
- Consumer consumer = localToDeviceRpcRequests.remove(requestId);
+ Consumer consumer = localToRuleEngineRpcRequests.remove(requestId);
if (consumer != null) {
consumer.accept(response);
} else {
@@ -138,12 +118,33 @@ public class DefaultDeviceRpcService implements DeviceRpcService {
} else {
builder.setError(-1);
}
- rpcService.tell(response.getServerAddress(), ClusterAPIProtos.MessageType.CLUSTER_RPC_FROM_DEVICE_RESPONSE_MESSAGE, builder.build().toByteArray());
+ rpcService.tell(requestOriginAddress, ClusterAPIProtos.MessageType.CLUSTER_RPC_FROM_DEVICE_RESPONSE_MESSAGE, builder.build().toByteArray());
+ }
+ }
+
+ @Override
+ public void forwardServerSideRPCRequestToDeviceActor(ToDeviceRpcRequest request, Consumer responseConsumer) {
+ log.trace("[{}][{}] Processing local rpc call to device actor [{}]", request.getTenantId(), request.getId(), request.getDeviceId());
+ UUID requestId = request.getId();
+ localToDeviceRpcRequests.put(requestId, responseConsumer);
+ sendRpcRequestToDevice(request);
+ scheduleTimeout(request, requestId, localToDeviceRpcRequests);
+ }
+
+ @Override
+ public void processResponseToServerSideRPCRequestFromDeviceActor(FromDeviceRpcResponse response) {
+ log.trace("[{}] Received response to server-side RPC request from device actor.", response.getId());
+ UUID requestId = response.getId();
+ Consumer consumer = localToDeviceRpcRequests.remove(requestId);
+ if (consumer != null) {
+ consumer.accept(response);
+ } else {
+ log.trace("[{}] Unknown or stale rpc response received [{}]", requestId, response);
}
}
@Override
- public void processRemoteResponseFromDevice(ServerAddress serverAddress, byte[] data) {
+ public void processResponseToServerSideRPCRequestFromRemoteServer(ServerAddress serverAddress, byte[] data) {
ClusterAPIProtos.FromDeviceRPCResponseProto proto;
try {
proto = ClusterAPIProtos.FromDeviceRPCResponseProto.parseFrom(data);
@@ -151,13 +152,12 @@ public class DefaultDeviceRpcService implements DeviceRpcService {
throw new RuntimeException(e);
}
RpcError error = proto.getError() > 0 ? RpcError.values()[proto.getError()] : null;
- FromDeviceRpcResponse response = new FromDeviceRpcResponse(new UUID(proto.getRequestIdMSB(), proto.getRequestIdLSB()), routingService.getCurrentServer(),
- proto.getResponse(), error);
- processRpcResponseFromDevice(response);
+ FromDeviceRpcResponse response = new FromDeviceRpcResponse(new UUID(proto.getRequestIdMSB(), proto.getRequestIdLSB()), proto.getResponse(), error);
+ processResponseToServerSideRPCRequestFromRuleEngine(routingService.getCurrentServer(), response);
}
@Override
- public void sendRpcReplyToDevice(TenantId tenantId, DeviceId deviceId, int requestId, String body) {
+ public void sendReplyToRpcCallFromDevice(TenantId tenantId, DeviceId deviceId, int requestId, String body) {
ToServerRpcResponseActorMsg rpcMsg = new ToServerRpcResponseActorMsg(tenantId, deviceId, new ToServerRpcResponseMsg(requestId, body));
forward(deviceId, rpcMsg);
}
@@ -166,6 +166,8 @@ public class DefaultDeviceRpcService implements DeviceRpcService {
ObjectNode entityNode = json.createObjectNode();
TbMsgMetaData metaData = new TbMsgMetaData();
metaData.putValue("requestUUID", msg.getId().toString());
+ metaData.putValue("originHost", routingService.getCurrentServer().getHost());
+ metaData.putValue("originPort", Integer.toString(routingService.getCurrentServer().getPort()));
metaData.putValue("expirationTime", Long.toString(msg.getExpirationTime()));
metaData.putValue("oneway", Boolean.toString(msg.isOneway()));
@@ -176,7 +178,7 @@ public class DefaultDeviceRpcService implements DeviceRpcService {
TbMsg tbMsg = new TbMsg(UUIDs.timeBased(), DataConstants.RPC_CALL_FROM_SERVER_TO_DEVICE, msg.getDeviceId(), metaData, TbMsgDataType.JSON
, json.writeValueAsString(entityNode)
, null, null, 0L);
- actorService.onMsg(new ServiceToRuleEngineMsg(msg.getTenantId(), tbMsg));
+ actorService.onMsg(new SendToClusterMsg(msg.getDeviceId(), new ServiceToRuleEngineMsg(msg.getTenantId(), tbMsg)));
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
@@ -199,7 +201,7 @@ public class DefaultDeviceRpcService implements DeviceRpcService {
log.trace("[{}] timeout the request: [{}]", this.hashCode(), requestId);
Consumer consumer = requestsMap.remove(requestId);
if (consumer != null) {
- consumer.accept(new FromDeviceRpcResponse(requestId, null, null, RpcError.TIMEOUT));
+ consumer.accept(new FromDeviceRpcResponse(requestId, null, RpcError.TIMEOUT));
}
}, timeout, TimeUnit.MILLISECONDS);
}
diff --git a/application/src/main/java/org/thingsboard/server/service/rpc/DeviceRpcService.java b/application/src/main/java/org/thingsboard/server/service/rpc/DeviceRpcService.java
index 4cee96ee71..bf12ec664c 100644
--- a/application/src/main/java/org/thingsboard/server/service/rpc/DeviceRpcService.java
+++ b/application/src/main/java/org/thingsboard/server/service/rpc/DeviceRpcService.java
@@ -29,13 +29,13 @@ public interface DeviceRpcService {
void processRestAPIRpcRequestToRuleEngine(ToDeviceRpcRequest request, Consumer responseConsumer);
- void processRestAPIRpcResponseFromRuleEngine(FromDeviceRpcResponse response);
+ void processResponseToServerSideRPCRequestFromRuleEngine(ServerAddress requestOriginAddress, FromDeviceRpcResponse response);
- void processRpcRequestToDevice(ToDeviceRpcRequest request, Consumer responseConsumer);
+ void forwardServerSideRPCRequestToDeviceActor(ToDeviceRpcRequest request, Consumer responseConsumer);
- void processRpcResponseFromDevice(FromDeviceRpcResponse response);
+ void processResponseToServerSideRPCRequestFromDeviceActor(FromDeviceRpcResponse response);
- void sendRpcReplyToDevice(TenantId tenantId, DeviceId deviceId, int requestId, String body);
+ void processResponseToServerSideRPCRequestFromRemoteServer(ServerAddress serverAddress, byte[] data);
- void processRemoteResponseFromDevice(ServerAddress serverAddress, byte[] bytes);
+ void sendReplyToRpcCallFromDevice(TenantId tenantId, DeviceId deviceId, int requestId, String body);
}
diff --git a/application/src/main/java/org/thingsboard/server/service/rpc/FromDeviceRpcResponse.java b/application/src/main/java/org/thingsboard/server/service/rpc/FromDeviceRpcResponse.java
index 9c3ce9a834..75506dfacc 100644
--- a/application/src/main/java/org/thingsboard/server/service/rpc/FromDeviceRpcResponse.java
+++ b/application/src/main/java/org/thingsboard/server/service/rpc/FromDeviceRpcResponse.java
@@ -32,8 +32,6 @@ import java.util.UUID;
public class FromDeviceRpcResponse {
@Getter
private final UUID id;
- @Getter
- private final ServerAddress serverAddress;
private final String response;
private final RpcError error;
diff --git a/application/src/main/java/org/thingsboard/server/service/rpc/ToServerRpcResponseActorMsg.java b/application/src/main/java/org/thingsboard/server/service/rpc/ToServerRpcResponseActorMsg.java
index 201f65613a..d33a3380f4 100644
--- a/application/src/main/java/org/thingsboard/server/service/rpc/ToServerRpcResponseActorMsg.java
+++ b/application/src/main/java/org/thingsboard/server/service/rpc/ToServerRpcResponseActorMsg.java
@@ -22,11 +22,8 @@ import org.thingsboard.rule.engine.api.msg.ToDeviceActorNotificationMsg;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.msg.MsgType;
-import org.thingsboard.server.common.msg.cluster.ServerAddress;
import org.thingsboard.server.common.msg.core.ToServerRpcResponseMsg;
-import java.util.Optional;
-
/**
* Created by ashvayka on 16.04.18.
*/
diff --git a/application/src/main/java/org/thingsboard/server/service/script/AbstractJsInvokeService.java b/application/src/main/java/org/thingsboard/server/service/script/AbstractJsInvokeService.java
new file mode 100644
index 0000000000..461b89447f
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/service/script/AbstractJsInvokeService.java
@@ -0,0 +1,102 @@
+/**
+ * Copyright © 2016-2018 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.service.script;
+
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * Created by ashvayka on 26.09.18.
+ */
+@Slf4j
+public abstract class AbstractJsInvokeService implements JsInvokeService {
+
+ protected Map scriptIdToNameMap = new ConcurrentHashMap<>();
+ protected Map blackListedFunctions = new ConcurrentHashMap<>();
+
+ @Override
+ public ListenableFuture eval(JsScriptType scriptType, String scriptBody, String... argNames) {
+ UUID scriptId = UUID.randomUUID();
+ String functionName = "invokeInternal_" + scriptId.toString().replace('-', '_');
+ String jsScript = generateJsScript(scriptType, functionName, scriptBody, argNames);
+ return doEval(scriptId, functionName, jsScript);
+ }
+
+ @Override
+ public ListenableFuture