Browse Source

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

pull/3811/head
Artem Babak 6 years ago
parent
commit
712a14ec22
  1. 2
      application/pom.xml
  2. 9
      application/src/main/conf/thingsboard.conf
  3. 52
      application/src/main/data/json/system/widget_bundles/charts.json
  4. 41
      application/src/main/data/json/system/widget_bundles/navigation_widgets.json
  5. 4
      application/src/main/java/org/thingsboard/server/actors/app/AppActor.java
  6. 27
      application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java
  7. 16
      application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java
  8. 30
      application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainToRuleChainMsg.java
  9. 18
      application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainToRuleNodeMsg.java
  10. 9
      application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeActor.java
  11. 13
      application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeActorMessageProcessor.java
  12. 34
      application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeToRuleChainTellNextMsg.java
  13. 17
      application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeToSelfMsg.java
  14. 42
      application/src/main/java/org/thingsboard/server/actors/ruleChain/TbToRuleNodeActorMsg.java
  15. 4
      application/src/main/java/org/thingsboard/server/actors/service/ComponentActor.java
  16. 9
      application/src/main/java/org/thingsboard/server/actors/service/DefaultActorService.java
  17. 8
      application/src/main/java/org/thingsboard/server/actors/stats/StatsPersistMsg.java
  18. 2
      application/src/main/java/org/thingsboard/server/actors/stats/StatsPersistTick.java
  19. 4
      application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java
  20. 1
      application/src/main/java/org/thingsboard/server/config/CustomOAuth2AuthorizationRequestResolver.java
  21. 2
      application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java
  22. 22
      application/src/main/java/org/thingsboard/server/controller/BaseController.java
  23. 6
      application/src/main/java/org/thingsboard/server/controller/CustomerController.java
  24. 107
      application/src/main/java/org/thingsboard/server/controller/DashboardController.java
  25. 2
      application/src/main/java/org/thingsboard/server/controller/EntityViewController.java
  26. 7
      application/src/main/java/org/thingsboard/server/controller/TenantController.java
  27. 8
      application/src/main/java/org/thingsboard/server/controller/UserController.java
  28. 5
      application/src/main/java/org/thingsboard/server/service/apiusage/DefaultTbApiUsageStateService.java
  29. 5
      application/src/main/java/org/thingsboard/server/service/component/AnnotationComponentDiscoveryService.java
  30. 2
      application/src/main/java/org/thingsboard/server/service/device/DeviceProvisionServiceImpl.java
  31. 7
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/DeviceProcessor.java
  32. 23
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/TelemetryProcessor.java
  33. 3
      application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java
  34. 25
      application/src/main/java/org/thingsboard/server/service/install/HsqlEntityDatabaseSchemaService.java
  35. 4
      application/src/main/java/org/thingsboard/server/service/install/SqlAbstractDatabaseSchemaService.java
  36. 8
      application/src/main/java/org/thingsboard/server/service/install/cql/CassandraDbHelper.java
  37. 3
      application/src/main/java/org/thingsboard/server/service/install/migrate/CassandraToSqlColumn.java
  38. 3
      application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java
  39. 41
      application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MModelsRepository.java
  40. 2
      application/src/main/java/org/thingsboard/server/service/query/DefaultEntityQueryService.java
  41. 10
      application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java
  42. 15
      application/src/main/java/org/thingsboard/server/service/queue/DefaultTbRuleEngineConsumerService.java
  43. 6
      application/src/main/java/org/thingsboard/server/service/queue/TbMsgPackProcessingContext.java
  44. 3
      application/src/main/java/org/thingsboard/server/service/queue/processing/AbstractConsumerService.java
  45. 2
      application/src/main/java/org/thingsboard/server/service/rpc/ToDeviceRpcRequestActorMsg.java
  46. 6
      application/src/main/java/org/thingsboard/server/service/script/AbstractNashornJsInvokeService.java
  47. 2
      application/src/main/java/org/thingsboard/server/service/security/auth/jwt/SkipPathRequestMatcher.java
  48. 2
      application/src/main/java/org/thingsboard/server/service/security/model/token/JwtTokenFactory.java
  49. 3
      application/src/main/java/org/thingsboard/server/service/security/permission/CustomerUserPermissions.java
  50. 1
      application/src/main/java/org/thingsboard/server/service/security/permission/DefaultAccessControlService.java
  51. 1
      application/src/main/java/org/thingsboard/server/service/security/permission/TenantAdminPermissions.java
  52. 13
      application/src/main/java/org/thingsboard/server/service/security/system/DefaultSystemSecurityService.java
  53. 35
      application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java
  54. 5
      application/src/main/java/org/thingsboard/server/service/subscription/DefaultSubscriptionManagerService.java
  55. 7
      application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbEntityDataSubscriptionService.java
  56. 51
      application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbLocalSubscriptionService.java
  57. 4
      application/src/main/java/org/thingsboard/server/service/subscription/TbAbstractDataSubCtx.java
  58. 6
      application/src/main/java/org/thingsboard/server/service/telemetry/AbstractSubscriptionService.java
  59. 2
      application/src/main/java/org/thingsboard/server/service/transport/msg/TransportToDeviceActorMsgWrapper.java
  60. 85
      application/src/main/java/org/thingsboard/server/utils/EventDeduplicationExecutor.java
  61. 1
      application/src/main/java/org/thingsboard/server/utils/MiscUtils.java
  62. 5
      application/src/main/resources/logback.xml
  63. 73
      application/src/main/resources/thingsboard.yml
  64. 4
      application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java
  65. 20
      application/src/test/java/org/thingsboard/server/controller/BaseEntityViewControllerTest.java
  66. 6
      application/src/test/java/org/thingsboard/server/edge/BaseEdgeTest.java
  67. 14
      application/src/test/java/org/thingsboard/server/mqtt/telemetry/attributes/AbstractMqttAttributesIntegrationTest.java
  68. 30
      application/src/test/java/org/thingsboard/server/mqtt/telemetry/timeseries/AbstractMqttTimeseriesIntegrationTest.java
  69. 3
      application/src/test/java/org/thingsboard/server/service/cluster/routing/HashPartitionServiceTest.java
  70. 2
      application/src/test/java/org/thingsboard/server/service/queue/TbMsgPackProcessingContextTest.java
  71. 155
      application/src/test/java/org/thingsboard/server/util/EventDeduplicationExecutorTest.java
  72. 2
      common/actor/pom.xml
  73. 2
      common/actor/src/main/java/org/thingsboard/server/actors/TbActor.java
  74. 2
      common/actor/src/main/java/org/thingsboard/server/actors/TbActorException.java
  75. 38
      common/actor/src/main/java/org/thingsboard/server/actors/TbActorMailbox.java
  76. 26
      common/actor/src/main/java/org/thingsboard/server/actors/TbRuleNodeUpdateException.java
  77. 2
      common/actor/src/test/java/org/thingsboard/server/actors/ActorSystemTest.java
  78. 6
      common/dao-api/pom.xml
  79. 3
      common/dao-api/src/main/java/org/thingsboard/server/dao/cassandra/AbstractCassandraCluster.java
  80. 31
      common/dao-api/src/main/java/org/thingsboard/server/dao/cassandra/guava/GuavaSessionBuilder.java
  81. 14
      common/dao-api/src/main/java/org/thingsboard/server/dao/util/mapping/JacksonUtil.java
  82. 2
      common/data/pom.xml
  83. 1
      common/data/src/main/java/org/thingsboard/server/common/data/DataConstants.java
  84. 19
      common/data/src/main/java/org/thingsboard/server/common/data/HomeDashboard.java
  85. 27
      common/data/src/main/java/org/thingsboard/server/common/data/HomeDashboardInfo.java
  86. 5
      common/data/src/main/java/org/thingsboard/server/common/data/lwm2m/ServerSecurityConfig.java
  87. 12
      common/data/src/main/java/org/thingsboard/server/common/data/query/DynamicValue.java
  88. 2
      common/data/src/test/java/org/thingsboard/server/common/data/UUIDConverterTest.java
  89. 2
      common/message/pom.xml
  90. 10
      common/message/src/main/java/org/thingsboard/server/common/msg/MsgType.java
  91. 8
      common/message/src/main/java/org/thingsboard/server/common/msg/TbActorMsg.java
  92. 22
      common/message/src/main/java/org/thingsboard/server/common/msg/TbActorStopReason.java
  93. 30
      common/message/src/main/java/org/thingsboard/server/common/msg/TbRuleEngineActorMsg.java
  94. 40
      common/message/src/main/java/org/thingsboard/server/common/msg/plugin/RuleNodeUpdatedMsg.java
  95. 38
      common/message/src/main/java/org/thingsboard/server/common/msg/queue/QueueToRuleEngineMsg.java
  96. 4
      common/message/src/main/java/org/thingsboard/server/common/msg/queue/RuleNodeException.java
  97. 2
      common/queue/pom.xml
  98. 1
      common/queue/src/main/java/org/thingsboard/server/queue/azure/servicebus/TbServiceBusConsumerTemplate.java
  99. 4
      common/queue/src/main/java/org/thingsboard/server/queue/discovery/ClusterTopologyChangeEvent.java
  100. 4
      common/queue/src/main/java/org/thingsboard/server/queue/discovery/HashPartitionService.java

2
application/pom.xml

@ -283,7 +283,7 @@
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.mockito</groupId> <groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId> <artifactId>mockito-core</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>

9
application/src/main/conf/thingsboard.conf

@ -15,11 +15,10 @@
# #
export JAVA_OPTS="$JAVA_OPTS -Dplatform=@pkg.platform@ -Dinstall.data_dir=@pkg.installFolder@/data" export JAVA_OPTS="$JAVA_OPTS -Dplatform=@pkg.platform@ -Dinstall.data_dir=@pkg.installFolder@/data"
export JAVA_OPTS="$JAVA_OPTS -Xloggc:@pkg.logFolder@/gc.log -XX:+IgnoreUnrecognizedVMOptions -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDetails -XX:+PrintGCDateStamps" export JAVA_OPTS="$JAVA_OPTS -Xlog:gc*,heap*,age*,safepoint=debug:file=@pkg.logFolder@/gc.log:time,uptime,level,tags:filecount=10,filesize=10M"
export JAVA_OPTS="$JAVA_OPTS -XX:+PrintHeapAtGC -XX:+PrintTenuringDistribution -XX:+PrintGCApplicationStoppedTime -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10" export JAVA_OPTS="$JAVA_OPTS -XX:+IgnoreUnrecognizedVMOptions -XX:+HeapDumpOnOutOfMemoryError"
export JAVA_OPTS="$JAVA_OPTS -XX:GCLogFileSize=10M -XX:-UseBiasedLocking -XX:+UseTLAB -XX:+ResizeTLAB -XX:+PerfDisableSharedMem -XX:+UseCondCardMark" export JAVA_OPTS="$JAVA_OPTS -XX:-UseBiasedLocking -XX:+UseTLAB -XX:+ResizeTLAB -XX:+PerfDisableSharedMem -XX:+UseCondCardMark"
export JAVA_OPTS="$JAVA_OPTS -XX:CMSWaitDuration=10000 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+CMSParallelInitialMarkEnabled" export JAVA_OPTS="$JAVA_OPTS -XX:+UseG1GC -XX:MaxGCPauseMillis=500 -XX:+UseStringDeduplication -XX:+ParallelRefProcEnabled -XX:MaxTenuringThreshold=10"
export JAVA_OPTS="$JAVA_OPTS -XX:+CMSEdenChunksRecordAlways -XX:CMSInitiatingOccupancyFraction=75 -XX:+UseCMSInitiatingOccupancyOnly"
export LOG_FILENAME=${pkg.name}.out export LOG_FILENAME=${pkg.name}.out
export LOADER_PATH=${pkg.installFolder}/conf,${pkg.installFolder}/extensions export LOADER_PATH=${pkg.installFolder}/conf,${pkg.installFolder}/extensions
export SQL_DATA_FOLDER=${pkg.installFolder}/data/sql export SQL_DATA_FOLDER=${pkg.installFolder}/data/sql

52
application/src/main/data/json/system/widget_bundles/charts.json

@ -25,22 +25,6 @@
"defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.545701115289893,\"funcBody\":\"var value = (prevValue-20) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+20;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Third\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.2592906835158064,\"funcBody\":\"var value = (prevValue-40) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+40;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Fourth\",\"color\":\"#ffc107\",\"settings\":{},\"_hash\":0.12880275585455747,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Bars - Chart.js\"}" "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.545701115289893,\"funcBody\":\"var value = (prevValue-20) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+20;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Third\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.2592906835158064,\"funcBody\":\"var value = (prevValue-40) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+40;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Fourth\",\"color\":\"#ffc107\",\"settings\":{},\"_hash\":0.12880275585455747,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Bars - Chart.js\"}"
} }
}, },
{
"alias": "basic_timeseries",
"name": "Timeseries - Flot",
"descriptor": {
"type": "timeseries",
"sizeX": 8,
"sizeY": 5,
"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('graph');\n}\n\nself.getDataKeySettingsSchema = function() {\n return TbFlot.datakeySettingsSchema(true, 'graph');\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}"
}
},
{ {
"alias": "doughnut_chart_js", "alias": "doughnut_chart_js",
"name": "Doughnut - Chart.js", "name": "Doughnut - Chart.js",
@ -71,7 +55,7 @@
"resources": [], "resources": [],
"templateHtml": "", "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.pie-label {\n font-size: 12px;\n font-family: 'Roboto';\n font-weight: bold;\n text-align: center;\n padding: 2px;\n color: white;\n}\n", "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.pie-label {\n font-size: 12px;\n font-family: 'Roboto';\n font-weight: bold;\n text-align: center;\n padding: 2px;\n color: white;\n}\n",
"controllerScript": "self.onInit = function() {\n self.ctx.flot = new TbFlot(self.ctx, 'pie'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.flot.update();\n}\n\nself.onResize = function() {\n self.ctx.flot.resize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.flot.checkMouseEvents();\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.flot.checkMouseEvents();\n}\n\nself.getSettingsSchema = function() {\n return TbFlot.pieSettingsSchema();\n}\n\nself.getDataKeySettingsSchema = function() {\n return TbFlot.pieDatakeySettingsSchema();\n}\n\nself.onDestroy = function() {\n self.ctx.flot.destroy();\n}\nself.actionSources = function() {\n return {\n 'sliceClick': {\n name: 'widget-action.pie-slice-click',\n multiple: false\n }\n };\n}\n", "controllerScript": "self.onInit = function() {\n self.ctx.flot = new TbFlot(self.ctx, 'pie'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.flot.update();\n}\n\nself.onResize = function() {\n self.ctx.flot.resize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.flot.checkMouseEvents();\n}\n\nself.getSettingsSchema = function() {\n return TbFlot.pieSettingsSchema();\n}\n\nself.getDataKeySettingsSchema = function() {\n return TbFlot.pieDatakeySettingsSchema();\n}\n\nself.onDestroy = function() {\n self.ctx.flot.destroy();\n}\nself.actionSources = function() {\n return {\n 'sliceClick': {\n name: 'widget-action.pie-slice-click',\n multiple: false\n }\n };\n}\n",
"settingsSchema": "{}\n", "settingsSchema": "{}\n",
"dataKeySettingsSchema": "{}\n", "dataKeySettingsSchema": "{}\n",
"defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.6114638304362894,\"funcBody\":\"var value = (prevValue-20) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+20;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Third\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.9955906536344441,\"funcBody\":\"var value = (prevValue-40) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+40;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Fourth\",\"color\":\"#ffc107\",\"settings\":{},\"_hash\":0.9430835931647599,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"radius\":1,\"fontColor\":\"#545454\",\"fontSize\":10,\"decimals\":1,\"legend\":{\"show\":true,\"position\":\"nw\",\"labelBoxBorderColor\":\"#CCCCCC\",\"backgroundColor\":\"#F0F0F0\",\"backgroundOpacity\":0.85},\"innerRadius\":0,\"showLabels\":true,\"showPercentages\":true,\"stroke\":{\"width\":5},\"tilt\":1,\"animatedPie\":false},\"title\":\"Pie - Flot\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}" "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.6114638304362894,\"funcBody\":\"var value = (prevValue-20) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+20;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Third\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.9955906536344441,\"funcBody\":\"var value = (prevValue-40) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+40;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Fourth\",\"color\":\"#ffc107\",\"settings\":{},\"_hash\":0.9430835931647599,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"radius\":1,\"fontColor\":\"#545454\",\"fontSize\":10,\"decimals\":1,\"legend\":{\"show\":true,\"position\":\"nw\",\"labelBoxBorderColor\":\"#CCCCCC\",\"backgroundColor\":\"#F0F0F0\",\"backgroundOpacity\":0.85},\"innerRadius\":0,\"showLabels\":true,\"showPercentages\":true,\"stroke\":{\"width\":5},\"tilt\":1,\"animatedPie\":false},\"title\":\"Pie - Flot\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}"
@ -138,8 +122,8 @@
} }
}, },
{ {
"alias": "timeseries_bars_flot", "alias": "state_chart",
"name": "Timeseries Bars - Flot", "name": "State Chart",
"descriptor": { "descriptor": {
"type": "timeseries", "type": "timeseries",
"sizeX": 8, "sizeX": 8,
@ -147,15 +131,15 @@
"resources": [], "resources": [],
"templateHtml": "", "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", "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('bar');\n}\n\nself.getDataKeySettingsSchema = function() {\n return TbFlot.datakeySettingsSchema(false, 'bar');\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.getSettingsSchema = function() {\n return TbFlot.settingsSchema('graph');\n}\n\nself.getDataKeySettingsSchema = function() {\n return TbFlot.datakeySettingsSchema(true, 'graph');\n}\n\nself.onDestroy = function() {\n self.ctx.flot.destroy();\n}\n",
"settingsSchema": "{}", "settingsSchema": "{}",
"dataKeySettingsSchema": "{}", "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},\"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\":{}}" "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Switch 1\",\"color\":\"#2196f3\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false,\"axisPosition\":\"left\",\"showSeparateAxis\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"return Math.random() > 0.5 ? 1 : 0;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Switch 2\",\"color\":\"#ffc107\",\"settings\":{\"showLines\":true,\"fillLines\":false,\"showPoints\":false,\"axisPosition\":\"left\"},\"_hash\":0.12775350966079668,\"funcBody\":\"return Math.random() <= 0.5 ? 1 : 0;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"shadowSize\":4,\"fontColor\":\"#545454\",\"fontSize\":10,\"xaxis\":{\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"showLabels\":true,\"color\":\"#545454\",\"ticksFormatter\":\"if (value > 0 && value <= 1) {\\n return 'On';\\n} else if (value === 0) {\\n return 'Off';\\n} else {\\n return '';\\n}\"},\"grid\":{\"color\":\"#545454\",\"tickColor\":\"#DDDDDD\",\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1},\"stack\":false,\"tooltipIndividual\":false,\"tooltipValueFormatter\":\"if (value > 0 && value <= 1) {\\n return 'On';\\n} else if (value === 0) {\\n return 'Off';\\n} else {\\n return '';\\n}\",\"smoothLines\":false},\"title\":\"State Chart\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"widgetStyle\":{},\"useDashboardTimewindow\":true,\"showLegend\":true,\"actions\":{},\"legendConfig\":{\"direction\":\"column\",\",position\":\"bottom\",\"showMin\":false,\"showMax\":false,\"showAvg\":false,\"showTotal\":false}}"
} }
}, },
{ {
"alias": "state_chart", "alias": "basic_timeseries",
"name": "State Chart", "name": "Timeseries - Flot",
"descriptor": { "descriptor": {
"type": "timeseries", "type": "timeseries",
"sizeX": 8, "sizeX": 8,
@ -163,11 +147,27 @@
"resources": [], "resources": [],
"templateHtml": "", "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", "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('graph');\n}\n\nself.getDataKeySettingsSchema = function() {\n return TbFlot.datakeySettingsSchema(true, 'graph');\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.getSettingsSchema = function() {\n return TbFlot.settingsSchema('graph');\n}\n\nself.getDataKeySettingsSchema = function() {\n return TbFlot.datakeySettingsSchema(true, 'graph');\n}\n\nself.onDestroy = function() {\n self.ctx.flot.destroy();\n}\n",
"settingsSchema": "{}", "settingsSchema": "{}",
"dataKeySettingsSchema": "{}", "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\":{\"direction\":\"column\",\",position\":\"bottom\",\"showMin\":false,\"showMax\":false,\"showAvg\":false,\"showTotal\":false}}" "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}"
}
},
{
"alias": "timeseries_bars_flot",
"name": "Timeseries Bars - Flot",
"descriptor": {
"type": "timeseries",
"sizeX": 8,
"sizeY": 5,
"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.getSettingsSchema = function() {\n return TbFlot.settingsSchema('bar');\n}\n\nself.getDataKeySettingsSchema = function() {\n return TbFlot.datakeySettingsSchema(false, 'bar');\n}\n\nself.onDestroy = function() {\n self.ctx.flot.destroy();\n}\n",
"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},\"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\":{}}"
} }
} }
] ]
} }

41
application/src/main/data/json/system/widget_bundles/navigation_widgets.json

@ -0,0 +1,41 @@
{
"widgetsBundle": {
"alias": "navigation_widgets",
"title": "Navigation widgets",
"image": null
},
"widgetTypes": [
{
"alias": "navigation_cards",
"name": "Navigation cards",
"descriptor": {
"type": "static",
"sizeX": 7,
"sizeY": 6,
"resources": [],
"templateHtml": "<tb-navigation-cards-widget [ctx]=\"ctx\"></tb-navigation-cards-widget>",
"templateCss": "/*#widget-container {\n overflow-y: auto;\n box-sizing: content-box !important;\n cursor: auto;\n}*/\n\n#widget-container #container {\n overflow-y: auto;\n box-sizing: content-box;\n cursor: auto;\n}",
"controllerScript": "self.onInit = function() {\n self.ctx.$scope.navigationCardsWidget.resize();\n}\n\nself.onResize = function() {\n self.ctx.$scope.navigationCardsWidget.resize();\n}\n\nself.onDestroy = function() {\n}\n",
"settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"filterType\": {\n \"title\": \"Filter type\",\n \"type\": \"string\",\n \"default\": \"all\"\n },\n \"filter\": {\n \"title\": \"Items\",\n \"type\": \"array\"\n }\n },\n \"required\": []\n },\n \"form\": [\n {\n \"key\": \"filterType\",\n \"type\": \"radios\",\n \"direction\": \"row\",\n \"titleMap\": [\n {\n \"value\": \"all\",\n \"name\": \"All items\"\n },\n {\n \"value\": \"include\",\n \"name\": \"Include items\"\n },\n {\n \"value\": \"exclude\",\n \"name\": \"Exclude items\"\n }\n ]\n },\n {\n \"key\": \"filter\",\n \"type\": \"rc-select\",\n \"condition\": \"model.filterType !== 'all'\",\n \"tags\": true,\n \"placeholder\": \"Enter urls to filter\",\n \"items\": [{\"value\": \"/devices\", \"label\": \"/devices\"}, {\"value\": \"/assets\", \"label\": \"/assets\"}, {\"value\": \"/deviceProfies\", \"label\": \"/deviceProfies\"}]\n }\n ]\n}\n",
"dataKeySettingsSchema": "{}\n",
"defaultConfig": "{\"datasources\":[{\"type\":\"static\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(255,255,255,0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"filterType\":\"all\"},\"title\":\"Navigation cards\",\"dropShadow\":false,\"showTitleIcon\":false,\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"showLegend\":false}"
}
},
{
"alias": "navigation_card",
"name": "Navigation card",
"descriptor": {
"type": "static",
"sizeX": 2.5,
"sizeY": 2,
"resources": [],
"templateHtml": "<tb-navigation-card-widget [ctx]=\"ctx\"></tb-navigation-card-widget>",
"templateCss": "",
"controllerScript": "self.onInit = function() {\n\n}\n\n\nself.onDestroy = function() {\n}\n",
"settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"name\": {\n \"title\": \"Title\",\n \"type\": \"string\",\n \"default\": \"{i18n:device.devices}\"\n },\n \"icon\": {\n \"title\": \"icon\",\n \"type\": \"string\",\n \"default\": \"devices_other\"\n },\n \"path\": {\n \"title\": \"Navigation path\",\n \"type\": \"string\",\n \"default\": \"/devices\"\n }\n },\n \"required\": [\"name\", \"icon\", \"path\"]\n },\n \"form\": [\n \"name\",\n {\n \"key\": \"icon\",\n \"type\": \"icon\"\n },\n \"path\"\n ]\n}\n",
"dataKeySettingsSchema": "{}\n",
"defaultConfig": "{\"datasources\":[{\"type\":\"static\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(255,255,255,0)\",\"color\":\"rgba(255,255,255,0.87)\",\"padding\":\"8px\",\"settings\":{\"name\":\"{i18n:device.devices}\",\"icon\":\"devices_other\",\"path\":\"/devices\"},\"title\":\"Navigation card\",\"dropShadow\":false,\"showTitleIcon\":false,\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"showLegend\":false}"
}
}
]
}

4
application/src/main/java/org/thingsboard/server/actors/app/AppActor.java

@ -141,12 +141,12 @@ public class AppActor extends ContextAwareActor {
private void onQueueToRuleEngineMsg(QueueToRuleEngineMsg msg) { private void onQueueToRuleEngineMsg(QueueToRuleEngineMsg msg) {
if (TenantId.SYS_TENANT_ID.equals(msg.getTenantId())) { if (TenantId.SYS_TENANT_ID.equals(msg.getTenantId())) {
msg.getTbMsg().getCallback().onFailure(new RuleEngineException("Message has system tenant id!")); msg.getMsg().getCallback().onFailure(new RuleEngineException("Message has system tenant id!"));
} else { } else {
if (!deletedTenants.contains(msg.getTenantId())) { if (!deletedTenants.contains(msg.getTenantId())) {
getOrCreateTenantActor(msg.getTenantId()).tell(msg); getOrCreateTenantActor(msg.getTenantId()).tell(msg);
} else { } else {
msg.getTbMsg().getCallback().onSuccess(); msg.getMsg().getCallback().onSuccess();
} }
} }
} }

27
application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java

@ -19,7 +19,6 @@ import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import io.netty.channel.EventLoopGroup; import io.netty.channel.EventLoopGroup;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import org.thingsboard.common.util.ListeningExecutor; import org.thingsboard.common.util.ListeningExecutor;
import org.thingsboard.rule.engine.api.MailService; import org.thingsboard.rule.engine.api.MailService;
@ -92,10 +91,12 @@ class DefaultTbContext implements TbContext {
public final static ObjectMapper mapper = new ObjectMapper(); public final static ObjectMapper mapper = new ObjectMapper();
private final ActorSystemContext mainCtx; private final ActorSystemContext mainCtx;
private final String ruleChainName;
private final RuleNodeCtx nodeCtx; private final RuleNodeCtx nodeCtx;
public DefaultTbContext(ActorSystemContext mainCtx, RuleNodeCtx nodeCtx) { public DefaultTbContext(ActorSystemContext mainCtx, String ruleChainName, RuleNodeCtx nodeCtx) {
this.mainCtx = mainCtx; this.mainCtx = mainCtx;
this.ruleChainName = ruleChainName;
this.nodeCtx = nodeCtx; this.nodeCtx = nodeCtx;
} }
@ -119,13 +120,13 @@ class DefaultTbContext implements TbContext {
relationTypes.forEach(relationType -> mainCtx.persistDebugOutput(nodeCtx.getTenantId(), nodeCtx.getSelf().getId(), msg, relationType, th)); relationTypes.forEach(relationType -> mainCtx.persistDebugOutput(nodeCtx.getTenantId(), nodeCtx.getSelf().getId(), msg, relationType, th));
} }
msg.getCallback().onProcessingEnd(nodeCtx.getSelf().getId()); msg.getCallback().onProcessingEnd(nodeCtx.getSelf().getId());
nodeCtx.getChainActor().tell(new RuleNodeToRuleChainTellNextMsg(nodeCtx.getSelf().getId(), relationTypes, msg, th != null ? th.getMessage() : null)); nodeCtx.getChainActor().tell(new RuleNodeToRuleChainTellNextMsg(nodeCtx.getSelf().getRuleChainId(), nodeCtx.getSelf().getId(), relationTypes, msg, th != null ? th.getMessage() : null));
} }
@Override @Override
public void tellSelf(TbMsg msg, long delayMs) { public void tellSelf(TbMsg msg, long delayMs) {
//TODO: add persistence layer //TODO: add persistence layer
scheduleMsgWithDelay(new RuleNodeToSelfMsg(msg), delayMs, nodeCtx.getSelfActor()); scheduleMsgWithDelay(new RuleNodeToSelfMsg(this, msg), delayMs, nodeCtx.getSelfActor());
} }
@Override @Override
@ -256,7 +257,8 @@ class DefaultTbContext implements TbContext {
} else { } else {
failureMessage = null; failureMessage = null;
} }
nodeCtx.getChainActor().tell(new RuleNodeToRuleChainTellNextMsg(nodeCtx.getSelf().getId(), Collections.singleton(TbRelationTypes.FAILURE), nodeCtx.getChainActor().tell(new RuleNodeToRuleChainTellNextMsg(nodeCtx.getSelf().getRuleChainId(),
nodeCtx.getSelf().getId(), Collections.singleton(TbRelationTypes.FAILURE),
msg, failureMessage)); msg, failureMessage));
} }
@ -308,6 +310,16 @@ class DefaultTbContext implements TbContext {
return nodeCtx.getSelf().getId(); return nodeCtx.getSelf().getId();
} }
@Override
public RuleNode getSelf() {
return nodeCtx.getSelf();
}
@Override
public String getRuleChainName() {
return ruleChainName;
}
@Override @Override
public TenantId getTenantId() { public TenantId getTenantId() {
return nodeCtx.getTenantId(); return nodeCtx.getTenantId();
@ -492,11 +504,6 @@ class DefaultTbContext implements TbContext {
return mainCtx.getCassandraBufferedRateExecutor().submit(task); return mainCtx.getCassandraBufferedRateExecutor().submit(task);
} }
@Override
public RedisTemplate<String, Object> getRedisTemplate() {
return mainCtx.getRedisTemplate();
}
@Override @Override
public PageData<RuleNodeState> findRuleNodeStates(PageLink pageLink) { public PageData<RuleNodeState> findRuleNodeStates(PageLink pageLink) {
if (log.isDebugEnabled()) { if (log.isDebugEnabled()) {

16
application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java

@ -23,7 +23,6 @@ import org.thingsboard.server.actors.TbActorRef;
import org.thingsboard.server.actors.TbEntityActorId; import org.thingsboard.server.actors.TbEntityActorId;
import org.thingsboard.server.actors.service.DefaultActorService; import org.thingsboard.server.actors.service.DefaultActorService;
import org.thingsboard.server.actors.shared.ComponentMsgProcessor; import org.thingsboard.server.actors.shared.ComponentMsgProcessor;
import org.thingsboard.server.common.data.ApiUsageRecordKey;
import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.RuleChainId;
@ -37,6 +36,7 @@ import org.thingsboard.server.common.data.rule.RuleChainType;
import org.thingsboard.server.common.data.rule.RuleNode; import org.thingsboard.server.common.data.rule.RuleNode;
import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
import org.thingsboard.server.common.msg.plugin.RuleNodeUpdatedMsg;
import org.thingsboard.server.common.msg.queue.PartitionChangeMsg; import org.thingsboard.server.common.msg.queue.PartitionChangeMsg;
import org.thingsboard.server.common.msg.queue.QueueToRuleEngineMsg; import org.thingsboard.server.common.msg.queue.QueueToRuleEngineMsg;
import org.thingsboard.server.common.msg.queue.RuleEngineException; import org.thingsboard.server.common.msg.queue.RuleEngineException;
@ -133,7 +133,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
} else { } else {
log.trace("[{}][{}] Updating rule node [{}]: {}", entityId, ruleNode.getId(), ruleNode.getName(), ruleNode); log.trace("[{}][{}] Updating rule node [{}]: {}", entityId, ruleNode.getId(), ruleNode.getName(), ruleNode);
existing.setSelf(ruleNode); existing.setSelf(ruleNode);
existing.getSelfActor().tellWithHighPriority(new ComponentLifecycleMsg(tenantId, existing.getSelf().getId(), ComponentLifecycleEvent.UPDATED)); existing.getSelfActor().tellWithHighPriority(new RuleNodeUpdatedMsg(tenantId, existing.getSelf().getId()));
} }
} }
@ -198,11 +198,11 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
} }
void onQueueToRuleEngineMsg(QueueToRuleEngineMsg envelope) { void onQueueToRuleEngineMsg(QueueToRuleEngineMsg envelope) {
TbMsg msg = envelope.getTbMsg(); TbMsg msg = envelope.getMsg();
log.trace("[{}][{}] Processing message [{}]: {}", entityId, firstId, msg.getId(), msg); log.trace("[{}][{}] Processing message [{}]: {}", entityId, firstId, msg.getId(), msg);
if (envelope.getRelationTypes() == null || envelope.getRelationTypes().isEmpty()) { if (envelope.getRelationTypes() == null || envelope.getRelationTypes().isEmpty()) {
try { try {
checkActive(envelope.getTbMsg()); checkActive(envelope.getMsg());
RuleNodeId targetId = msg.getRuleNodeId(); RuleNodeId targetId = msg.getRuleNodeId();
RuleNodeCtx targetCtx; RuleNodeCtx targetCtx;
if (targetId == null) { if (targetId == null) {
@ -219,12 +219,12 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
msg.getCallback().onSuccess(); msg.getCallback().onSuccess();
} }
} catch (RuleNodeException rne) { } catch (RuleNodeException rne) {
envelope.getTbMsg().getCallback().onFailure(rne); envelope.getMsg().getCallback().onFailure(rne);
} catch (Exception e) { } catch (Exception e) {
envelope.getTbMsg().getCallback().onFailure(new RuleEngineException(e.getMessage())); envelope.getMsg().getCallback().onFailure(new RuleEngineException(e.getMessage()));
} }
} else { } else {
onTellNext(envelope.getTbMsg(), envelope.getTbMsg().getRuleNodeId(), envelope.getRelationTypes(), envelope.getFailureMessage()); onTellNext(envelope.getMsg(), envelope.getMsg().getRuleNodeId(), envelope.getRelationTypes(), envelope.getFailureMessage());
} }
} }
@ -336,7 +336,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
private void pushMsgToNode(RuleNodeCtx nodeCtx, TbMsg msg, String fromRelationType) { private void pushMsgToNode(RuleNodeCtx nodeCtx, TbMsg msg, String fromRelationType) {
if (nodeCtx != null) { if (nodeCtx != null) {
nodeCtx.getSelfActor().tell(new RuleChainToRuleNodeMsg(new DefaultTbContext(systemContext, nodeCtx), msg, fromRelationType)); nodeCtx.getSelfActor().tell(new RuleChainToRuleNodeMsg(new DefaultTbContext(systemContext, ruleChainName, nodeCtx), msg, fromRelationType));
} else { } else {
log.error("[{}][{}] RuleNodeCtx is empty", entityId, ruleChainName); log.error("[{}][{}] RuleNodeCtx is empty", entityId, ruleChainName);
msg.getCallback().onFailure(new RuleEngineException("Rule Node CTX is empty")); msg.getCallback().onFailure(new RuleEngineException("Rule Node CTX is empty"));

30
application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainToRuleChainMsg.java

@ -15,24 +15,44 @@
*/ */
package org.thingsboard.server.actors.ruleChain; package org.thingsboard.server.actors.ruleChain;
import lombok.Data; import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.msg.MsgType; import org.thingsboard.server.common.msg.MsgType;
import org.thingsboard.server.common.msg.TbActorMsg; import org.thingsboard.server.common.msg.TbActorStopReason;
import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbRuleEngineActorMsg;
import org.thingsboard.server.common.msg.aware.RuleChainAwareMsg; import org.thingsboard.server.common.msg.aware.RuleChainAwareMsg;
import org.thingsboard.server.common.msg.queue.RuleEngineException;
/** /**
* Created by ashvayka on 19.03.18. * Created by ashvayka on 19.03.18.
*/ */
@Data @EqualsAndHashCode(callSuper = true)
public final class RuleChainToRuleChainMsg implements TbActorMsg, RuleChainAwareMsg { @ToString
public final class RuleChainToRuleChainMsg extends TbRuleEngineActorMsg implements RuleChainAwareMsg {
@Getter
private final RuleChainId target; private final RuleChainId target;
@Getter
private final RuleChainId source; private final RuleChainId source;
private final TbMsg msg; @Getter
private final String fromRelationType; private final String fromRelationType;
public RuleChainToRuleChainMsg(RuleChainId target, RuleChainId source, TbMsg tbMsg, String fromRelationType) {
super(tbMsg);
this.target = target;
this.source = source;
this.fromRelationType = fromRelationType;
}
@Override
public void onTbActorStopped(TbActorStopReason reason) {
String message = reason == TbActorStopReason.STOPPED ? String.format("Rule chain [%s] stopped", target.getId()) : String.format("Failed to initialize rule chain [%s]!", target.getId());
msg.getCallback().onFailure(new RuleEngineException(message));
}
@Override @Override
public RuleChainId getRuleChainId() { public RuleChainId getRuleChainId() {
return target; return target;

18
application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainToRuleNodeMsg.java

@ -15,22 +15,28 @@
*/ */
package org.thingsboard.server.actors.ruleChain; package org.thingsboard.server.actors.ruleChain;
import lombok.Data; import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.TbContext;
import org.thingsboard.server.common.msg.MsgType; import org.thingsboard.server.common.msg.MsgType;
import org.thingsboard.server.common.msg.TbActorMsg;
import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsg;
/** /**
* Created by ashvayka on 19.03.18. * Created by ashvayka on 19.03.18.
*/ */
@Data @EqualsAndHashCode(callSuper = true)
final class RuleChainToRuleNodeMsg implements TbActorMsg { @ToString
final class RuleChainToRuleNodeMsg extends TbToRuleNodeActorMsg {
private final TbContext ctx; @Getter
private final TbMsg msg;
private final String fromRelationType; private final String fromRelationType;
public RuleChainToRuleNodeMsg(TbContext ctx, TbMsg tbMsg, String fromRelationType) {
super(ctx, tbMsg);
this.fromRelationType = fromRelationType;
}
@Override @Override
public MsgType getMsgType() { public MsgType getMsgType() {
return MsgType.RULE_CHAIN_TO_RULE_MSG; return MsgType.RULE_CHAIN_TO_RULE_MSG;

9
application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeActor.java

@ -26,7 +26,6 @@ import org.thingsboard.server.actors.service.ContextBasedCreator;
import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.RuleNodeId; import org.thingsboard.server.common.data.id.RuleNodeId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.msg.TbActorMsg; import org.thingsboard.server.common.msg.TbActorMsg;
import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
import org.thingsboard.server.common.msg.queue.PartitionChangeMsg; import org.thingsboard.server.common.msg.queue.PartitionChangeMsg;
@ -54,14 +53,12 @@ public class RuleNodeActor extends ComponentActor<RuleNodeId, RuleNodeActorMessa
protected boolean doProcess(TbActorMsg msg) { protected boolean doProcess(TbActorMsg msg) {
switch (msg.getMsgType()) { switch (msg.getMsgType()) {
case COMPONENT_LIFE_CYCLE_MSG: case COMPONENT_LIFE_CYCLE_MSG:
case RULE_NODE_UPDATED_MSG:
onComponentLifecycleMsg((ComponentLifecycleMsg) msg); onComponentLifecycleMsg((ComponentLifecycleMsg) msg);
break; break;
case RULE_CHAIN_TO_RULE_MSG: case RULE_CHAIN_TO_RULE_MSG:
onRuleChainToRuleNodeMsg((RuleChainToRuleNodeMsg) msg); onRuleChainToRuleNodeMsg((RuleChainToRuleNodeMsg) msg);
break; break;
case RULE_TO_SELF_ERROR_MSG:
onRuleNodeToSelfErrorMsg((RuleNodeToSelfErrorMsg) msg);
break;
case RULE_TO_SELF_MSG: case RULE_TO_SELF_MSG:
onRuleNodeToSelfMsg((RuleNodeToSelfMsg) msg); onRuleNodeToSelfMsg((RuleNodeToSelfMsg) msg);
break; break;
@ -101,10 +98,6 @@ public class RuleNodeActor extends ComponentActor<RuleNodeId, RuleNodeActorMessa
} }
} }
private void onRuleNodeToSelfErrorMsg(RuleNodeToSelfErrorMsg msg) {
logAndPersist("onRuleMsg", ActorSystemContext.toException(msg.getError()));
}
public static class ActorCreator extends ContextBasedCreator { public static class ActorCreator extends ContextBasedCreator {
private final TenantId tenantId; private final TenantId tenantId;

13
application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeActorMessageProcessor.java

@ -20,14 +20,13 @@ import org.thingsboard.rule.engine.api.TbNodeConfiguration;
import org.thingsboard.server.actors.ActorSystemContext; import org.thingsboard.server.actors.ActorSystemContext;
import org.thingsboard.server.actors.TbActorCtx; import org.thingsboard.server.actors.TbActorCtx;
import org.thingsboard.server.actors.TbActorRef; import org.thingsboard.server.actors.TbActorRef;
import org.thingsboard.server.actors.TbRuleNodeUpdateException;
import org.thingsboard.server.actors.shared.ComponentMsgProcessor; import org.thingsboard.server.actors.shared.ComponentMsgProcessor;
import org.thingsboard.server.common.data.ApiUsageRecordKey; import org.thingsboard.server.common.data.ApiUsageRecordKey;
import org.thingsboard.server.common.data.TenantProfile;
import org.thingsboard.server.common.data.id.RuleNodeId; import org.thingsboard.server.common.data.id.RuleNodeId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.plugin.ComponentLifecycleState; import org.thingsboard.server.common.data.plugin.ComponentLifecycleState;
import org.thingsboard.server.common.data.rule.RuleNode; import org.thingsboard.server.common.data.rule.RuleNode;
import org.thingsboard.server.common.data.tenant.profile.TenantProfileConfiguration;
import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.queue.PartitionChangeMsg; import org.thingsboard.server.common.msg.queue.PartitionChangeMsg;
import org.thingsboard.server.common.msg.queue.RuleNodeException; import org.thingsboard.server.common.msg.queue.RuleNodeException;
@ -54,7 +53,7 @@ public class RuleNodeActorMessageProcessor extends ComponentMsgProcessor<RuleNod
this.ruleChainName = ruleChainName; this.ruleChainName = ruleChainName;
this.self = self; this.self = self;
this.ruleNode = systemContext.getRuleChainService().findRuleNodeById(tenantId, entityId); this.ruleNode = systemContext.getRuleChainService().findRuleNodeById(tenantId, entityId);
this.defaultCtx = new DefaultTbContext(systemContext, new RuleNodeCtx(tenantId, parent, self, ruleNode)); this.defaultCtx = new DefaultTbContext(systemContext, ruleChainName, new RuleNodeCtx(tenantId, parent, self, ruleNode));
this.info = new RuleNodeInfo(ruleNodeId, ruleChainName, ruleNode != null ? ruleNode.getName() : "Unknown"); this.info = new RuleNodeInfo(ruleNodeId, ruleChainName, ruleNode != null ? ruleNode.getName() : "Unknown");
} }
@ -78,7 +77,11 @@ public class RuleNodeActorMessageProcessor extends ComponentMsgProcessor<RuleNod
if (tbNode != null) { if (tbNode != null) {
tbNode.destroy(); tbNode.destroy();
} }
start(context); try {
start(context);
} catch (Exception e) {
throw new TbRuleNodeUpdateException("Failed to update rule node", e);
}
} }
} }
@ -147,7 +150,7 @@ public class RuleNodeActorMessageProcessor extends ComponentMsgProcessor<RuleNod
TbNode tbNode = null; TbNode tbNode = null;
if (ruleNode != null) { if (ruleNode != null) {
Class<?> componentClazz = Class.forName(ruleNode.getType()); Class<?> componentClazz = Class.forName(ruleNode.getType());
tbNode = (TbNode) (componentClazz.newInstance()); tbNode = (TbNode) (componentClazz.getDeclaredConstructor().newInstance());
tbNode.init(defaultCtx, new TbNodeConfiguration(ruleNode.getConfiguration())); tbNode.init(defaultCtx, new TbNodeConfiguration(ruleNode.getConfiguration()));
} }
return tbNode; return tbNode;

34
application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeToRuleChainTellNextMsg.java

@ -15,11 +15,16 @@
*/ */
package org.thingsboard.server.actors.ruleChain; package org.thingsboard.server.actors.ruleChain;
import lombok.Data; import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.RuleNodeId; import org.thingsboard.server.common.data.id.RuleNodeId;
import org.thingsboard.server.common.msg.MsgType; import org.thingsboard.server.common.msg.MsgType;
import org.thingsboard.server.common.msg.TbActorMsg; import org.thingsboard.server.common.msg.TbActorStopReason;
import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbRuleEngineActorMsg;
import org.thingsboard.server.common.msg.queue.RuleEngineException;
import java.io.Serializable; import java.io.Serializable;
import java.util.Set; import java.util.Set;
@ -27,15 +32,34 @@ import java.util.Set;
/** /**
* Created by ashvayka on 19.03.18. * Created by ashvayka on 19.03.18.
*/ */
@Data @EqualsAndHashCode(callSuper = true)
class RuleNodeToRuleChainTellNextMsg implements TbActorMsg, Serializable { @ToString
class RuleNodeToRuleChainTellNextMsg extends TbRuleEngineActorMsg implements Serializable {
private static final long serialVersionUID = 4577026446412871820L; private static final long serialVersionUID = 4577026446412871820L;
@Getter
private final RuleChainId ruleChainId;
@Getter
private final RuleNodeId originator; private final RuleNodeId originator;
@Getter
private final Set<String> relationTypes; private final Set<String> relationTypes;
private final TbMsg msg; @Getter
private final String failureMessage; private final String failureMessage;
public RuleNodeToRuleChainTellNextMsg(RuleChainId ruleChainId, RuleNodeId originator, Set<String> relationTypes, TbMsg tbMsg, String failureMessage) {
super(tbMsg);
this.ruleChainId = ruleChainId;
this.originator = originator;
this.relationTypes = relationTypes;
this.failureMessage = failureMessage;
}
@Override
public void onTbActorStopped(TbActorStopReason reason) {
String message = reason == TbActorStopReason.STOPPED ? String.format("Rule chain [%s] stopped", ruleChainId.getId()) : String.format("Failed to initialize rule chain [%s]!", ruleChainId.getId());
msg.getCallback().onFailure(new RuleEngineException(message));
}
@Override @Override
public MsgType getMsgType() { public MsgType getMsgType() {
return MsgType.RULE_TO_RULE_CHAIN_TELL_NEXT_MSG; return MsgType.RULE_TO_RULE_CHAIN_TELL_NEXT_MSG;

17
application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeToSelfMsg.java

@ -15,18 +15,25 @@
*/ */
package org.thingsboard.server.actors.ruleChain; package org.thingsboard.server.actors.ruleChain;
import lombok.Data; import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.thingsboard.rule.engine.api.TbContext;
import org.thingsboard.server.common.msg.MsgType; import org.thingsboard.server.common.msg.MsgType;
import org.thingsboard.server.common.msg.TbActorMsg; import org.thingsboard.server.common.msg.TbActorStopReason;
import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbRuleEngineActorMsg;
import org.thingsboard.server.common.msg.queue.RuleNodeException;
/** /**
* Created by ashvayka on 19.03.18. * Created by ashvayka on 19.03.18.
*/ */
@Data @EqualsAndHashCode(callSuper = true)
final class RuleNodeToSelfMsg implements TbActorMsg { @ToString
final class RuleNodeToSelfMsg extends TbToRuleNodeActorMsg {
private final TbMsg msg; public RuleNodeToSelfMsg(TbContext ctx, TbMsg tbMsg) {
super(ctx, tbMsg);
}
@Override @Override
public MsgType getMsgType() { public MsgType getMsgType() {

42
application/src/main/java/org/thingsboard/server/actors/ruleChain/TbToRuleNodeActorMsg.java

@ -0,0 +1,42 @@
/**
* Copyright © 2016-2021 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.ruleChain;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import org.thingsboard.rule.engine.api.TbContext;
import org.thingsboard.server.common.msg.TbActorStopReason;
import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbRuleEngineActorMsg;
import org.thingsboard.server.common.msg.queue.RuleNodeException;
@EqualsAndHashCode(callSuper = true)
public abstract class TbToRuleNodeActorMsg extends TbRuleEngineActorMsg {
@Getter
private final TbContext ctx;
public TbToRuleNodeActorMsg(TbContext ctx, TbMsg tbMsg) {
super(tbMsg);
this.ctx = ctx;
}
@Override
public void onTbActorStopped(TbActorStopReason reason) {
String message = reason == TbActorStopReason.STOPPED ? "Rule node stopped" : "Failed to initialize rule node!";
msg.getCallback().onFailure(new RuleNodeException(message, ctx.getRuleChainName(), ctx.getSelf()));
}
}

4
application/src/main/java/org/thingsboard/server/actors/service/ComponentActor.java

@ -20,6 +20,7 @@ import org.thingsboard.server.actors.ActorSystemContext;
import org.thingsboard.server.actors.TbActor; import org.thingsboard.server.actors.TbActor;
import org.thingsboard.server.actors.TbActorCtx; import org.thingsboard.server.actors.TbActorCtx;
import org.thingsboard.server.actors.TbActorException; import org.thingsboard.server.actors.TbActorException;
import org.thingsboard.server.actors.TbRuleNodeUpdateException;
import org.thingsboard.server.actors.shared.ComponentMsgProcessor; import org.thingsboard.server.actors.shared.ComponentMsgProcessor;
import org.thingsboard.server.actors.stats.StatsPersistMsg; import org.thingsboard.server.actors.stats.StatsPersistMsg;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
@ -123,6 +124,9 @@ public abstract class ComponentActor<T extends EntityId, P extends ComponentMsgP
} catch (Exception e) { } catch (Exception e) {
logAndPersist("onLifecycleMsg", e, true); logAndPersist("onLifecycleMsg", e, true);
logLifecycleEvent(msg.getEvent(), e); logLifecycleEvent(msg.getEvent(), e);
if (e instanceof TbRuleNodeUpdateException) {
throw (TbRuleNodeUpdateException) e;
}
} }
} }

9
application/src/main/java/org/thingsboard/server/actors/service/DefaultActorService.java

@ -34,6 +34,7 @@ import org.thingsboard.server.actors.app.AppInitMsg;
import org.thingsboard.server.actors.stats.StatsActor; import org.thingsboard.server.actors.stats.StatsActor;
import org.thingsboard.server.common.msg.queue.PartitionChangeMsg; import org.thingsboard.server.common.msg.queue.PartitionChangeMsg;
import org.thingsboard.server.queue.discovery.PartitionChangeEvent; import org.thingsboard.server.queue.discovery.PartitionChangeEvent;
import org.thingsboard.server.queue.discovery.TbApplicationEventListener;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy; import javax.annotation.PreDestroy;
@ -43,7 +44,7 @@ import java.util.concurrent.ScheduledExecutorService;
@Service @Service
@Slf4j @Slf4j
public class DefaultActorService implements ActorService { public class DefaultActorService extends TbApplicationEventListener<PartitionChangeEvent> implements ActorService {
public static final String APP_DISPATCHER_NAME = "app-dispatcher"; public static final String APP_DISPATCHER_NAME = "app-dispatcher";
public static final String TENANT_DISPATCHER_NAME = "tenant-dispatcher"; public static final String TENANT_DISPATCHER_NAME = "tenant-dispatcher";
@ -120,10 +121,10 @@ public class DefaultActorService implements ActorService {
appActor.tellWithHighPriority(new AppInitMsg()); appActor.tellWithHighPriority(new AppInitMsg());
} }
@EventListener(PartitionChangeEvent.class) @Override
public void onApplicationEvent(PartitionChangeEvent partitionChangeEvent) { protected void onTbApplicationEvent(PartitionChangeEvent event) {
log.info("Received partition change event."); log.info("Received partition change event.");
this.appActor.tellWithHighPriority(new PartitionChangeMsg(partitionChangeEvent.getServiceQueueKey(), partitionChangeEvent.getPartitions())); this.appActor.tellWithHighPriority(new PartitionChangeMsg(event.getServiceQueueKey(), event.getPartitions()));
} }
@PreDestroy @PreDestroy

8
application/src/main/java/org/thingsboard/server/actors/stats/StatsPersistMsg.java

@ -28,10 +28,10 @@ import org.thingsboard.server.common.msg.TbActorMsg;
@ToString @ToString
public final class StatsPersistMsg implements TbActorMsg { public final class StatsPersistMsg implements TbActorMsg {
private long messagesProcessed; private final long messagesProcessed;
private long errorsOccurred; private final long errorsOccurred;
private TenantId tenantId; private final TenantId tenantId;
private EntityId entityId; private final EntityId entityId;
@Override @Override
public MsgType getMsgType() { public MsgType getMsgType() {

2
application/src/main/java/org/thingsboard/server/actors/stats/StatsPersistTick.java

@ -18,7 +18,7 @@ package org.thingsboard.server.actors.stats;
import org.thingsboard.server.common.msg.MsgType; import org.thingsboard.server.common.msg.MsgType;
import org.thingsboard.server.common.msg.TbActorMsg; import org.thingsboard.server.common.msg.TbActorMsg;
public final class StatsPersistTick implements TbActorMsg{ public final class StatsPersistTick implements TbActorMsg {
@Override @Override
public MsgType getMsgType() { public MsgType getMsgType() {
return MsgType.STATS_PERSIST_TICK_MSG; return MsgType.STATS_PERSIST_TICK_MSG;

4
application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java

@ -125,7 +125,7 @@ public class TenantActor extends RuleChainManagerActor {
log.info("[{}] Processing missing Tenant msg: {}", tenantId, msg); log.info("[{}] Processing missing Tenant msg: {}", tenantId, msg);
if (msg.getMsgType().equals(MsgType.QUEUE_TO_RULE_ENGINE_MSG)) { if (msg.getMsgType().equals(MsgType.QUEUE_TO_RULE_ENGINE_MSG)) {
QueueToRuleEngineMsg queueMsg = (QueueToRuleEngineMsg) msg; QueueToRuleEngineMsg queueMsg = (QueueToRuleEngineMsg) msg;
queueMsg.getTbMsg().getCallback().onSuccess(); queueMsg.getMsg().getCallback().onSuccess();
} else if (msg.getMsgType().equals(MsgType.TRANSPORT_TO_DEVICE_ACTOR_MSG)) { } else if (msg.getMsgType().equals(MsgType.TRANSPORT_TO_DEVICE_ACTOR_MSG)) {
TransportToDeviceActorMsgWrapper transportMsg = (TransportToDeviceActorMsgWrapper) msg; TransportToDeviceActorMsgWrapper transportMsg = (TransportToDeviceActorMsgWrapper) msg;
transportMsg.getCallback().onSuccess(); transportMsg.getCallback().onSuccess();
@ -188,7 +188,7 @@ public class TenantActor extends RuleChainManagerActor {
log.warn("RECEIVED INVALID MESSAGE: {}", msg); log.warn("RECEIVED INVALID MESSAGE: {}", msg);
return; return;
} }
TbMsg tbMsg = msg.getTbMsg(); TbMsg tbMsg = msg.getMsg();
if (apiUsageState.isReExecEnabled()) { if (apiUsageState.isReExecEnabled()) {
if (tbMsg.getRuleChainId() == null) { if (tbMsg.getRuleChainId() == null) {
if (getRootChainActor() != null) { if (getRootChainActor() != null) {

1
application/src/main/java/org/thingsboard/server/config/CustomOAuth2AuthorizationRequestResolver.java

@ -91,6 +91,7 @@ public class CustomOAuth2AuthorizationRequestResolver implements OAuth2Authoriza
return action; return action;
} }
@SuppressWarnings("deprecation")
private OAuth2AuthorizationRequest resolve(HttpServletRequest request, String registrationId, String redirectUriAction) { private OAuth2AuthorizationRequest resolve(HttpServletRequest request, String registrationId, String redirectUriAction) {
if (registrationId == null) { if (registrationId == null) {
return null; return null;

2
application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java

@ -127,7 +127,7 @@ public class ThingsboardSecurityConfiguration extends WebSecurityConfigurerAdapt
} }
protected JwtTokenAuthenticationProcessingFilter buildJwtTokenAuthenticationProcessingFilter() throws Exception { protected JwtTokenAuthenticationProcessingFilter buildJwtTokenAuthenticationProcessingFilter() throws Exception {
List<String> pathsToSkip = new ArrayList(Arrays.asList(NON_TOKEN_BASED_AUTH_ENTRY_POINTS)); List<String> pathsToSkip = new ArrayList<>(Arrays.asList(NON_TOKEN_BASED_AUTH_ENTRY_POINTS));
pathsToSkip.addAll(Arrays.asList(WS_TOKEN_BASED_AUTH_ENTRY_POINT, TOKEN_REFRESH_ENTRY_POINT, FORM_BASED_LOGIN_ENTRY_POINT, pathsToSkip.addAll(Arrays.asList(WS_TOKEN_BASED_AUTH_ENTRY_POINT, TOKEN_REFRESH_ENTRY_POINT, FORM_BASED_LOGIN_ENTRY_POINT,
PUBLIC_LOGIN_ENTRY_POINT, DEVICE_API_ENTRY_POINT, WEBJARS_ENTRY_POINT)); PUBLIC_LOGIN_ENTRY_POINT, DEVICE_API_ENTRY_POINT, WEBJARS_ENTRY_POINT));
SkipPathRequestMatcher matcher = new SkipPathRequestMatcher(pathsToSkip, TOKEN_BASED_AUTH_ENTRY_POINT); SkipPathRequestMatcher matcher = new SkipPathRequestMatcher(pathsToSkip, TOKEN_BASED_AUTH_ENTRY_POINT);

22
application/src/main/java/org/thingsboard/server/controller/BaseController.java

@ -152,6 +152,9 @@ public abstract class BaseController {
public static final String INCORRECT_TENANT_ID = "Incorrect tenantId "; public static final String INCORRECT_TENANT_ID = "Incorrect tenantId ";
public static final String YOU_DON_T_HAVE_PERMISSION_TO_PERFORM_THIS_OPERATION = "You don't have permission to perform this operation!"; public static final String YOU_DON_T_HAVE_PERMISSION_TO_PERFORM_THIS_OPERATION = "You don't have permission to perform this operation!";
protected static final String DEFAULT_DASHBOARD = "defaultDashboardId";
protected static final String HOME_DASHBOARD = "homeDashboardId";
private static final ObjectMapper json = new ObjectMapper(); private static final ObjectMapper json = new ObjectMapper();
@Autowired @Autowired
@ -719,6 +722,7 @@ public abstract class BaseController {
return ruleNode; return ruleNode;
} }
@SuppressWarnings("unchecked")
protected <I extends EntityId> I emptyId(EntityType entityType) { protected <I extends EntityId> I emptyId(EntityType entityType) {
return (I) EntityIdFactory.getByTypeAndUuid(entityType, ModelConstants.NULL_UUID); return (I) EntityIdFactory.getByTypeAndUuid(entityType, ModelConstants.NULL_UUID);
} }
@ -849,8 +853,9 @@ public abstract class BaseController {
entityNode = json.createObjectNode(); entityNode = json.createObjectNode();
if (actionType == ActionType.ATTRIBUTES_UPDATED) { if (actionType == ActionType.ATTRIBUTES_UPDATED) {
String scope = extractParameter(String.class, 0, additionalInfo); String scope = extractParameter(String.class, 0, additionalInfo);
@SuppressWarnings("unchecked")
List<AttributeKvEntry> attributes = extractParameter(List.class, 1, additionalInfo); List<AttributeKvEntry> attributes = extractParameter(List.class, 1, additionalInfo);
metaData.putValue("scope", scope); metaData.putValue(DataConstants.SCOPE, scope);
if (attributes != null) { if (attributes != null) {
for (AttributeKvEntry attr : attributes) { for (AttributeKvEntry attr : attributes) {
addKvEntry(entityNode, attr); addKvEntry(entityNode, attr);
@ -858,16 +863,19 @@ public abstract class BaseController {
} }
} else if (actionType == ActionType.ATTRIBUTES_DELETED) { } else if (actionType == ActionType.ATTRIBUTES_DELETED) {
String scope = extractParameter(String.class, 0, additionalInfo); String scope = extractParameter(String.class, 0, additionalInfo);
@SuppressWarnings("unchecked")
List<String> keys = extractParameter(List.class, 1, additionalInfo); List<String> keys = extractParameter(List.class, 1, additionalInfo);
metaData.putValue("scope", scope); metaData.putValue(DataConstants.SCOPE, scope);
ArrayNode attrsArrayNode = entityNode.putArray("attributes"); ArrayNode attrsArrayNode = entityNode.putArray("attributes");
if (keys != null) { if (keys != null) {
keys.forEach(attrsArrayNode::add); keys.forEach(attrsArrayNode::add);
} }
} else if (actionType == ActionType.TIMESERIES_UPDATED) { } else if (actionType == ActionType.TIMESERIES_UPDATED) {
@SuppressWarnings("unchecked")
List<TsKvEntry> timeseries = extractParameter(List.class, 0, additionalInfo); List<TsKvEntry> timeseries = extractParameter(List.class, 0, additionalInfo);
addTimeseries(entityNode, timeseries); addTimeseries(entityNode, timeseries);
} else if (actionType == ActionType.TIMESERIES_DELETED) { } else if (actionType == ActionType.TIMESERIES_DELETED) {
@SuppressWarnings("unchecked")
List<String> keys = extractParameter(List.class, 0, additionalInfo); List<String> keys = extractParameter(List.class, 0, additionalInfo);
if (keys != null) { if (keys != null) {
ArrayNode timeseriesArrayNode = entityNode.putArray("timeseries"); ArrayNode timeseriesArrayNode = entityNode.putArray("timeseries");
@ -1030,4 +1038,14 @@ public abstract class BaseController {
} }
} }
} }
protected void processDashboardIdFromAdditionalInfo(ObjectNode additionalInfo, String requiredFields) throws ThingsboardException {
String dashboardId = additionalInfo.has(requiredFields) ? additionalInfo.get(requiredFields).asText() : null;
if(dashboardId != null && !dashboardId.equals("null")) {
if(dashboardService.findDashboardById(getTenantId(), new DashboardId(UUID.fromString(dashboardId))) == null) {
additionalInfo.remove(requiredFields);
}
}
}
} }

6
application/src/main/java/org/thingsboard/server/controller/CustomerController.java

@ -59,7 +59,11 @@ public class CustomerController extends BaseController {
checkParameter(CUSTOMER_ID, strCustomerId); checkParameter(CUSTOMER_ID, strCustomerId);
try { try {
CustomerId customerId = new CustomerId(toUUID(strCustomerId)); CustomerId customerId = new CustomerId(toUUID(strCustomerId));
return checkCustomerId(customerId, Operation.READ); Customer customer = checkCustomerId(customerId, Operation.READ);
if(!customer.getAdditionalInfo().isNull()) {
processDashboardIdFromAdditionalInfo((ObjectNode) customer.getAdditionalInfo(), HOME_DASHBOARD);
}
return customer;
} catch (Exception e) { } catch (Exception e) {
throw handleException(e); throw handleException(e);
} }

107
application/src/main/java/org/thingsboard/server/controller/DashboardController.java

@ -15,6 +15,8 @@
*/ */
package org.thingsboard.server.controller; package org.thingsboard.server.controller;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.access.prepost.PreAuthorize;
@ -30,7 +32,11 @@ import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.Dashboard; import org.thingsboard.server.common.data.Dashboard;
import org.thingsboard.server.common.data.DashboardInfo; import org.thingsboard.server.common.data.DashboardInfo;
import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.HomeDashboard;
import org.thingsboard.server.common.data.HomeDashboardInfo;
import org.thingsboard.server.common.data.ShortCustomerInfo; import org.thingsboard.server.common.data.ShortCustomerInfo;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.edge.EdgeEventActionType; import org.thingsboard.server.common.data.edge.EdgeEventActionType;
@ -42,7 +48,9 @@ import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.page.TimePageLink; import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.dao.util.mapping.JacksonUtil;
import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.security.model.SecurityUser;
import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Operation;
import org.thingsboard.server.service.security.permission.Resource; import org.thingsboard.server.service.security.permission.Resource;
@ -58,6 +66,9 @@ public class DashboardController extends BaseController {
public static final String DASHBOARD_ID = "dashboardId"; public static final String DASHBOARD_ID = "dashboardId";
private static final String HOME_DASHBOARD_ID = "homeDashboardId";
private static final String HOME_DASHBOARD_HIDE_TOOLBAR = "homeDashboardHideToolbar";
@Value("${dashboard.max_datapoints_limit}") @Value("${dashboard.max_datapoints_limit}")
private long maxDatapointsLimit; private long maxDatapointsLimit;
@ -491,6 +502,102 @@ public class DashboardController extends BaseController {
} }
} }
@PreAuthorize("isAuthenticated()")
@RequestMapping(value = "/dashboard/home", method = RequestMethod.GET)
@ResponseBody
public HomeDashboard getHomeDashboard() throws ThingsboardException {
try {
SecurityUser securityUser = getCurrentUser();
if (securityUser.isSystemAdmin()) {
return null;
}
User user = userService.findUserById(securityUser.getTenantId(), securityUser.getId());
JsonNode additionalInfo = user.getAdditionalInfo();
HomeDashboard homeDashboard;
homeDashboard = extractHomeDashboardFromAdditionalInfo(additionalInfo);
if (homeDashboard == null) {
if (securityUser.isCustomerUser()) {
Customer customer = customerService.findCustomerById(securityUser.getTenantId(), securityUser.getCustomerId());
additionalInfo = customer.getAdditionalInfo();
homeDashboard = extractHomeDashboardFromAdditionalInfo(additionalInfo);
}
if (homeDashboard == null) {
Tenant tenant = tenantService.findTenantById(securityUser.getTenantId());
additionalInfo = tenant.getAdditionalInfo();
homeDashboard = extractHomeDashboardFromAdditionalInfo(additionalInfo);
}
}
return homeDashboard;
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/tenant/dashboard/home/info", method = RequestMethod.GET)
@ResponseBody
public HomeDashboardInfo getTenantHomeDashboardInfo() throws ThingsboardException {
try {
Tenant tenant = tenantService.findTenantById(getTenantId());
JsonNode additionalInfo = tenant.getAdditionalInfo();
DashboardId dashboardId = null;
boolean hideDashboardToolbar = true;
if (additionalInfo != null && additionalInfo.has(HOME_DASHBOARD_ID) && !additionalInfo.get(HOME_DASHBOARD_ID).isNull()) {
String strDashboardId = additionalInfo.get(HOME_DASHBOARD_ID).asText();
dashboardId = new DashboardId(toUUID(strDashboardId));
if (additionalInfo.has(HOME_DASHBOARD_HIDE_TOOLBAR)) {
hideDashboardToolbar = additionalInfo.get(HOME_DASHBOARD_HIDE_TOOLBAR).asBoolean();
}
}
return new HomeDashboardInfo(dashboardId, hideDashboardToolbar);
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/tenant/dashboard/home/info", method = RequestMethod.POST)
@ResponseStatus(value = HttpStatus.OK)
public void setTenantHomeDashboardInfo(@RequestBody HomeDashboardInfo homeDashboardInfo) throws ThingsboardException {
try {
if (homeDashboardInfo.getDashboardId() != null) {
checkDashboardId(homeDashboardInfo.getDashboardId(), Operation.READ);
}
Tenant tenant = tenantService.findTenantById(getTenantId());
JsonNode additionalInfo = tenant.getAdditionalInfo();
if (additionalInfo == null || !(additionalInfo instanceof ObjectNode)) {
additionalInfo = JacksonUtil.OBJECT_MAPPER.createObjectNode();
}
if (homeDashboardInfo.getDashboardId() != null) {
((ObjectNode) additionalInfo).put(HOME_DASHBOARD_ID, homeDashboardInfo.getDashboardId().getId().toString());
((ObjectNode) additionalInfo).put(HOME_DASHBOARD_HIDE_TOOLBAR, homeDashboardInfo.isHideDashboardToolbar());
} else {
((ObjectNode) additionalInfo).remove(HOME_DASHBOARD_ID);
((ObjectNode) additionalInfo).remove(HOME_DASHBOARD_HIDE_TOOLBAR);
}
tenant.setAdditionalInfo(additionalInfo);
tenantService.saveTenant(tenant);
} catch (Exception e) {
throw handleException(e);
}
}
private HomeDashboard extractHomeDashboardFromAdditionalInfo(JsonNode additionalInfo) {
try {
if (additionalInfo != null && additionalInfo.has(HOME_DASHBOARD_ID) && !additionalInfo.get(HOME_DASHBOARD_ID).isNull()) {
String strDashboardId = additionalInfo.get(HOME_DASHBOARD_ID).asText();
DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
Dashboard dashboard = checkDashboardId(dashboardId, Operation.READ);
boolean hideDashboardToolbar = true;
if (additionalInfo.has(HOME_DASHBOARD_HIDE_TOOLBAR)) {
hideDashboardToolbar = additionalInfo.get(HOME_DASHBOARD_HIDE_TOOLBAR).asBoolean();
}
return new HomeDashboard(dashboard, hideDashboardToolbar);
}
} catch (Exception e) {}
return null;
}
@PreAuthorize("hasAuthority('TENANT_ADMIN')") @PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/edge/{edgeId}/dashboard/{dashboardId}", method = RequestMethod.POST) @RequestMapping(value = "/edge/{edgeId}/dashboard/{dashboardId}", method = RequestMethod.POST)
@ResponseBody @ResponseBody

2
application/src/main/java/org/thingsboard/server/controller/EntityViewController.java

@ -73,7 +73,7 @@ import java.util.List;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static org.apache.commons.lang.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.thingsboard.server.controller.CustomerController.CUSTOMER_ID; import static org.thingsboard.server.controller.CustomerController.CUSTOMER_ID;
import static org.thingsboard.server.controller.EdgeController.EDGE_ID; import static org.thingsboard.server.controller.EdgeController.EDGE_ID;

7
application/src/main/java/org/thingsboard/server/controller/TenantController.java

@ -15,6 +15,7 @@
*/ */
package org.thingsboard.server.controller; package org.thingsboard.server.controller;
import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
@ -59,7 +60,11 @@ public class TenantController extends BaseController {
checkParameter("tenantId", strTenantId); checkParameter("tenantId", strTenantId);
try { try {
TenantId tenantId = new TenantId(toUUID(strTenantId)); TenantId tenantId = new TenantId(toUUID(strTenantId));
return checkTenantId(tenantId, Operation.READ); Tenant tenant = checkTenantId(tenantId, Operation.READ);
if(!tenant.getAdditionalInfo().isNull()) {
processDashboardIdFromAdditionalInfo((ObjectNode) tenant.getAdditionalInfo(), HOME_DASHBOARD);
}
return tenant;
} catch (Exception e) { } catch (Exception e) {
throw handleException(e); throw handleException(e);
} }

8
application/src/main/java/org/thingsboard/server/controller/UserController.java

@ -92,7 +92,12 @@ public class UserController extends BaseController {
checkParameter(USER_ID, strUserId); checkParameter(USER_ID, strUserId);
try { try {
UserId userId = new UserId(toUUID(strUserId)); UserId userId = new UserId(toUUID(strUserId));
return checkUserId(userId, Operation.READ); User user = checkUserId(userId, Operation.READ);
if(!user.getAdditionalInfo().isNull()) {
processDashboardIdFromAdditionalInfo((ObjectNode) user.getAdditionalInfo(), DEFAULT_DASHBOARD);
processDashboardIdFromAdditionalInfo((ObjectNode) user.getAdditionalInfo(), HOME_DASHBOARD);
}
return user;
} catch (Exception e) { } catch (Exception e) {
throw handleException(e); throw handleException(e);
} }
@ -339,4 +344,5 @@ public class UserController extends BaseController {
throw handleException(e); throw handleException(e);
} }
} }
} }

5
application/src/main/java/org/thingsboard/server/service/apiusage/DefaultTbApiUsageStateService.java

@ -54,6 +54,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.UsageStatsKVProto;
import org.thingsboard.server.queue.common.TbProtoQueueMsg; import org.thingsboard.server.queue.common.TbProtoQueueMsg;
import org.thingsboard.server.queue.discovery.PartitionChangeEvent; import org.thingsboard.server.queue.discovery.PartitionChangeEvent;
import org.thingsboard.server.queue.discovery.PartitionService; import org.thingsboard.server.queue.discovery.PartitionService;
import org.thingsboard.server.queue.discovery.TbApplicationEventListener;
import org.thingsboard.server.queue.scheduler.SchedulerComponent; import org.thingsboard.server.queue.scheduler.SchedulerComponent;
import org.thingsboard.server.service.queue.TbClusterService; import org.thingsboard.server.service.queue.TbClusterService;
import org.thingsboard.server.service.telemetry.InternalTelemetryService; import org.thingsboard.server.service.telemetry.InternalTelemetryService;
@ -78,7 +79,7 @@ import java.util.stream.Collectors;
@Slf4j @Slf4j
@Service @Service
public class DefaultTbApiUsageStateService implements TbApiUsageStateService { public class DefaultTbApiUsageStateService extends TbApplicationEventListener<PartitionChangeEvent> implements TbApiUsageStateService {
public static final String HOURLY = "Hourly"; public static final String HOURLY = "Hourly";
public static final FutureCallback<Integer> VOID_CALLBACK = new FutureCallback<Integer>() { public static final FutureCallback<Integer> VOID_CALLBACK = new FutureCallback<Integer>() {
@ -188,7 +189,7 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService {
} }
@Override @Override
public void onApplicationEvent(PartitionChangeEvent partitionChangeEvent) { protected void onTbApplicationEvent(PartitionChangeEvent partitionChangeEvent) {
if (partitionChangeEvent.getServiceType().equals(ServiceType.TB_CORE)) { if (partitionChangeEvent.getServiceType().equals(ServiceType.TB_CORE)) {
myTenantStates.entrySet().removeIf(entry -> !partitionService.resolve(ServiceType.TB_CORE, entry.getKey(), entry.getKey()).isMyPartition()); myTenantStates.entrySet().removeIf(entry -> !partitionService.resolve(ServiceType.TB_CORE, entry.getKey(), entry.getKey()).isMyPartition());
otherTenantStates.entrySet().removeIf(entry -> partitionService.resolve(ServiceType.TB_CORE, entry.getKey(), entry.getKey()).isMyPartition()); otherTenantStates.entrySet().removeIf(entry -> partitionService.resolve(ServiceType.TB_CORE, entry.getKey(), entry.getKey()).isMyPartition());

5
application/src/main/java/org/thingsboard/server/service/component/AnnotationComponentDiscoveryService.java

@ -24,6 +24,7 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider; import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
import org.springframework.core.env.Profiles;
import org.springframework.core.type.filter.AnnotationTypeFilter; import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.thingsboard.rule.engine.api.NodeConfiguration; import org.thingsboard.rule.engine.api.NodeConfiguration;
@ -71,7 +72,7 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe
private ObjectMapper mapper = new ObjectMapper(); private ObjectMapper mapper = new ObjectMapper();
private boolean isInstall() { private boolean isInstall() {
return environment.acceptsProfiles("install"); return environment.acceptsProfiles(Profiles.of("install"));
} }
@PostConstruct @PostConstruct
@ -200,7 +201,7 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe
nodeDefinition.setRelationTypes(getRelationTypesWithFailureRelation(nodeAnnotation)); nodeDefinition.setRelationTypes(getRelationTypesWithFailureRelation(nodeAnnotation));
nodeDefinition.setCustomRelations(nodeAnnotation.customRelations()); nodeDefinition.setCustomRelations(nodeAnnotation.customRelations());
Class<? extends NodeConfiguration> configClazz = nodeAnnotation.configClazz(); Class<? extends NodeConfiguration> configClazz = nodeAnnotation.configClazz();
NodeConfiguration config = configClazz.newInstance(); NodeConfiguration config = configClazz.getDeclaredConstructor().newInstance();
NodeConfiguration defaultConfiguration = config.defaultConfiguration(); NodeConfiguration defaultConfiguration = config.defaultConfiguration();
nodeDefinition.setDefaultConfiguration(mapper.valueToTree(defaultConfiguration)); nodeDefinition.setDefaultConfiguration(mapper.valueToTree(defaultConfiguration));
nodeDefinition.setUiResources(nodeAnnotation.uiResources()); nodeDefinition.setUiResources(nodeAnnotation.uiResources());

2
application/src/main/java/org/thingsboard/server/service/device/DeviceProvisionServiceImpl.java

@ -20,7 +20,7 @@ import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.RandomStringUtils; import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;

7
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/DeviceProcessor.java

@ -22,8 +22,8 @@ import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.SettableFuture; import com.google.common.util.concurrent.SettableFuture;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.RandomStringUtils; import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.thingsboard.rule.engine.api.RpcError; import org.thingsboard.rule.engine.api.RpcError;
@ -99,7 +99,6 @@ public class DeviceProcessor extends BaseProcessor {
ObjectNode body = mapper.createObjectNode(); ObjectNode body = mapper.createObjectNode();
body.put("conflictName", deviceName); body.put("conflictName", deviceName);
saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE, EdgeEventActionType.ENTITY_MERGE_REQUEST, device.getId(), body); saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE, EdgeEventActionType.ENTITY_MERGE_REQUEST, device.getId(), body);
deviceService.assignDeviceToEdge(edge.getTenantId(), device.getId(), edge.getId());
} }
futureToSet.set(null); futureToSet.set(null);
} }
@ -115,7 +114,6 @@ public class DeviceProcessor extends BaseProcessor {
log.info("[{}] Creating new device and replacing device entity on the edge [{}]", tenantId, deviceUpdateMsg); log.info("[{}] Creating new device and replacing device entity on the edge [{}]", tenantId, deviceUpdateMsg);
device = createDevice(tenantId, edge, deviceUpdateMsg, deviceUpdateMsg.getName()); device = createDevice(tenantId, edge, deviceUpdateMsg, deviceUpdateMsg.getName());
saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE, EdgeEventActionType.ENTITY_MERGE_REQUEST, device.getId(), null); saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE, EdgeEventActionType.ENTITY_MERGE_REQUEST, device.getId(), null);
deviceService.assignDeviceToEdge(edge.getTenantId(), device.getId(), edge.getId());
} }
break; break;
case ENTITY_UPDATED_RPC_MESSAGE: case ENTITY_UPDATED_RPC_MESSAGE:
@ -201,6 +199,7 @@ public class DeviceProcessor extends BaseProcessor {
createRelationFromEdge(tenantId, edge.getId(), device.getId()); createRelationFromEdge(tenantId, edge.getId(), device.getId());
deviceStateService.onDeviceAdded(device); deviceStateService.onDeviceAdded(device);
pushDeviceCreatedEventToRuleEngine(tenantId, edge, device); pushDeviceCreatedEventToRuleEngine(tenantId, edge, device);
deviceService.assignDeviceToEdge(edge.getTenantId(), device.getId(), edge.getId());
} finally { } finally {
deviceCreationLock.unlock(); deviceCreationLock.unlock();
} }

23
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/TelemetryProcessor.java

@ -21,10 +21,9 @@ import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.SettableFuture; import com.google.common.util.concurrent.SettableFuture;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import groovy.lang.Tuple;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.javatuples.Pair; import org.apache.commons.lang3.tuple.ImmutablePair;
import org.passay.Rule; import org.apache.commons.lang3.tuple.Pair;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg; import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg;
import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.DataConstants;
@ -37,7 +36,6 @@ import org.thingsboard.server.common.data.id.AssetId;
import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.common.data.id.DashboardId;
import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.EntityViewId; import org.thingsboard.server.common.data.id.EntityViewId;
import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.RuleChainId;
@ -45,7 +43,6 @@ import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.kv.AttributeKey; import org.thingsboard.server.common.data.kv.AttributeKey;
import org.thingsboard.server.common.data.kv.AttributeKvEntry; import org.thingsboard.server.common.data.kv.AttributeKvEntry;
import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbMsgMetaData; import org.thingsboard.server.common.msg.TbMsgMetaData;
import org.thingsboard.server.common.msg.queue.ServiceQueue; import org.thingsboard.server.common.msg.queue.ServiceQueue;
@ -145,9 +142,9 @@ public class TelemetryProcessor extends BaseProcessor {
String defaultQueueName = deviceProfile.getDefaultQueueName(); String defaultQueueName = deviceProfile.getDefaultQueueName();
queueName = defaultQueueName != null ? defaultQueueName : ServiceQueue.MAIN; queueName = defaultQueueName != null ? defaultQueueName : ServiceQueue.MAIN;
} }
return new Pair<>(queueName, ruleChainId); return new ImmutablePair<>(queueName, ruleChainId);
} else { } else {
return new Pair<>(ServiceQueue.MAIN, null); return new ImmutablePair<>(ServiceQueue.MAIN, null);
} }
} }
@ -157,8 +154,8 @@ public class TelemetryProcessor extends BaseProcessor {
JsonObject json = JsonUtils.getJsonObject(tsKv.getKvList()); JsonObject json = JsonUtils.getJsonObject(tsKv.getKvList());
metaData.putValue("ts", tsKv.getTs() + ""); metaData.putValue("ts", tsKv.getTs() + "");
Pair<String, RuleChainId> defaultQueueAndRuleChain = getDefaultQueueNameAndRuleChainId(tenantId, entityId); Pair<String, RuleChainId> defaultQueueAndRuleChain = getDefaultQueueNameAndRuleChainId(tenantId, entityId);
String queueName = defaultQueueAndRuleChain.getValue0(); String queueName = defaultQueueAndRuleChain.getKey();
RuleChainId ruleChainId = defaultQueueAndRuleChain.getValue1(); RuleChainId ruleChainId = defaultQueueAndRuleChain.getValue();
TbMsg tbMsg = TbMsg.newMsg(queueName, SessionMsgType.POST_TELEMETRY_REQUEST.name(), entityId, metaData, gson.toJson(json), ruleChainId, null); TbMsg tbMsg = TbMsg.newMsg(queueName, SessionMsgType.POST_TELEMETRY_REQUEST.name(), entityId, metaData, gson.toJson(json), ruleChainId, null);
tbClusterService.pushMsgToRuleEngine(tenantId, tbMsg.getOriginator(), tbMsg, new TbQueueCallback() { tbClusterService.pushMsgToRuleEngine(tenantId, tbMsg.getOriginator(), tbMsg, new TbQueueCallback() {
@Override @Override
@ -180,8 +177,8 @@ public class TelemetryProcessor extends BaseProcessor {
SettableFuture<Void> futureToSet = SettableFuture.create(); SettableFuture<Void> futureToSet = SettableFuture.create();
JsonObject json = JsonUtils.getJsonObject(msg.getKvList()); JsonObject json = JsonUtils.getJsonObject(msg.getKvList());
Pair<String, RuleChainId> defaultQueueAndRuleChain = getDefaultQueueNameAndRuleChainId(tenantId, entityId); Pair<String, RuleChainId> defaultQueueAndRuleChain = getDefaultQueueNameAndRuleChainId(tenantId, entityId);
String queueName = defaultQueueAndRuleChain.getValue0(); String queueName = defaultQueueAndRuleChain.getKey();
RuleChainId ruleChainId = defaultQueueAndRuleChain.getValue1(); RuleChainId ruleChainId = defaultQueueAndRuleChain.getValue();
TbMsg tbMsg = TbMsg.newMsg(queueName, SessionMsgType.POST_ATTRIBUTES_REQUEST.name(), entityId, metaData, gson.toJson(json), ruleChainId, null); TbMsg tbMsg = TbMsg.newMsg(queueName, SessionMsgType.POST_ATTRIBUTES_REQUEST.name(), entityId, metaData, gson.toJson(json), ruleChainId, null);
tbClusterService.pushMsgToRuleEngine(tenantId, tbMsg.getOriginator(), tbMsg, new TbQueueCallback() { tbClusterService.pushMsgToRuleEngine(tenantId, tbMsg.getOriginator(), tbMsg, new TbQueueCallback() {
@Override @Override
@ -207,8 +204,8 @@ public class TelemetryProcessor extends BaseProcessor {
@Override @Override
public void onSuccess(@Nullable List<Void> voids) { public void onSuccess(@Nullable List<Void> voids) {
Pair<String, RuleChainId> defaultQueueAndRuleChain = getDefaultQueueNameAndRuleChainId(tenantId, entityId); Pair<String, RuleChainId> defaultQueueAndRuleChain = getDefaultQueueNameAndRuleChainId(tenantId, entityId);
String queueName = defaultQueueAndRuleChain.getValue0(); String queueName = defaultQueueAndRuleChain.getKey();
RuleChainId ruleChainId = defaultQueueAndRuleChain.getValue1(); RuleChainId ruleChainId = defaultQueueAndRuleChain.getValue();
TbMsg tbMsg = TbMsg.newMsg(queueName, DataConstants.ATTRIBUTES_UPDATED, entityId, metaData, gson.toJson(json), ruleChainId, null); TbMsg tbMsg = TbMsg.newMsg(queueName, DataConstants.ATTRIBUTES_UPDATED, entityId, metaData, gson.toJson(json), ruleChainId, null);
tbClusterService.pushMsgToRuleEngine(tenantId, tbMsg.getOriginator(), tbMsg, new TbQueueCallback() { tbClusterService.pushMsgToRuleEngine(tenantId, tbMsg.getOriginator(), tbMsg, new TbQueueCallback() {
@Override @Override

3
application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java

@ -198,7 +198,7 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
generalSettings.setKey("general"); generalSettings.setKey("general");
ObjectNode node = objectMapper.createObjectNode(); ObjectNode node = objectMapper.createObjectNode();
node.put("baseUrl", "http://localhost:8080"); node.put("baseUrl", "http://localhost:8080");
node.put("prohibitDifferentUrl", true); node.put("prohibitDifferentUrl", false);
generalSettings.setJsonValue(node); generalSettings.setJsonValue(node);
adminSettingsService.saveAdminSettings(TenantId.SYS_TENANT_ID, generalSettings); adminSettingsService.saveAdminSettings(TenantId.SYS_TENANT_ID, generalSettings);
@ -439,6 +439,7 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
this.deleteSystemWidgetBundle("input_widgets"); this.deleteSystemWidgetBundle("input_widgets");
this.deleteSystemWidgetBundle("date"); this.deleteSystemWidgetBundle("date");
this.deleteSystemWidgetBundle("entity_admin_widgets"); this.deleteSystemWidgetBundle("entity_admin_widgets");
this.deleteSystemWidgetBundle("navigation_widgets");
this.deleteSystemWidgetBundle("edge_widgets"); this.deleteSystemWidgetBundle("edge_widgets");
installScripts.loadSystemWidgets(); installScripts.loadSystemWidgets();
} }

25
application/src/main/java/org/thingsboard/server/service/install/HsqlEntityDatabaseSchemaService.java

@ -15,11 +15,20 @@
*/ */
package org.thingsboard.server.service.install; package org.thingsboard.server.service.install;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Profile; import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.thingsboard.server.dao.util.HsqlDao; import org.thingsboard.server.dao.util.HsqlDao;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.sql.Connection;
import java.sql.DriverManager;
@Service @Service
@Slf4j
@HsqlDao @HsqlDao
@Profile("install") @Profile("install")
public class HsqlEntityDatabaseSchemaService extends SqlAbstractDatabaseSchemaService public class HsqlEntityDatabaseSchemaService extends SqlAbstractDatabaseSchemaService
@ -27,5 +36,21 @@ public class HsqlEntityDatabaseSchemaService extends SqlAbstractDatabaseSchemaSe
protected HsqlEntityDatabaseSchemaService() { protected HsqlEntityDatabaseSchemaService() {
super("schema-entities-hsql.sql", "schema-entities-idx.sql"); super("schema-entities-hsql.sql", "schema-entities-idx.sql");
} }
private final String schemaTypesSql = "schema-types-hsql.sql";
@Override
public void createDatabaseSchema(boolean createIndexes) throws Exception {
log.info("Installing SQL DataBase types part: " + schemaTypesSql);
Path schemaFile = Paths.get(installScripts.getDataDir(), SQL_DIR, schemaTypesSql);
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
}
super.createDatabaseSchema(createIndexes);
}
} }

4
application/src/main/java/org/thingsboard/server/service/install/SqlAbstractDatabaseSchemaService.java

@ -30,7 +30,7 @@ import java.sql.SQLException;
@Slf4j @Slf4j
public abstract class SqlAbstractDatabaseSchemaService implements DatabaseSchemaService { public abstract class SqlAbstractDatabaseSchemaService implements DatabaseSchemaService {
private static final String SQL_DIR = "sql"; protected static final String SQL_DIR = "sql";
@Value("${spring.datasource.url}") @Value("${spring.datasource.url}")
protected String dbUrl; protected String dbUrl;
@ -42,7 +42,7 @@ public abstract class SqlAbstractDatabaseSchemaService implements DatabaseSchema
protected String dbPassword; protected String dbPassword;
@Autowired @Autowired
private InstallScripts installScripts; protected InstallScripts installScripts;
private final String schemaSql; private final String schemaSql;
private final String schemaIdxSql; private final String schemaIdxSql;

8
application/src/main/java/org/thingsboard/server/service/install/cql/CassandraDbHelper.java

@ -145,17 +145,17 @@ public class CassandraDbHelper {
if (row.isNull(index)) { if (row.isNull(index)) {
return null; return null;
} else if (type.getProtocolCode() == ProtocolConstants.DataType.DOUBLE) { } else if (type.getProtocolCode() == ProtocolConstants.DataType.DOUBLE) {
str = new Double(row.getDouble(index)).toString(); str = Double.valueOf(row.getDouble(index)).toString();
} else if (type.getProtocolCode() == ProtocolConstants.DataType.INT) { } else if (type.getProtocolCode() == ProtocolConstants.DataType.INT) {
str = new Integer(row.getInt(index)).toString(); str = Integer.valueOf(row.getInt(index)).toString();
} else if (type.getProtocolCode() == ProtocolConstants.DataType.BIGINT) { } else if (type.getProtocolCode() == ProtocolConstants.DataType.BIGINT) {
str = new Long(row.getLong(index)).toString(); str = Long.valueOf(row.getLong(index)).toString();
} else if (type.getProtocolCode() == ProtocolConstants.DataType.UUID) { } else if (type.getProtocolCode() == ProtocolConstants.DataType.UUID) {
str = row.getUuid(index).toString(); str = row.getUuid(index).toString();
} else if (type.getProtocolCode() == ProtocolConstants.DataType.TIMEUUID) { } else if (type.getProtocolCode() == ProtocolConstants.DataType.TIMEUUID) {
str = row.getUuid(index).toString(); str = row.getUuid(index).toString();
} else if (type.getProtocolCode() == ProtocolConstants.DataType.FLOAT) { } else if (type.getProtocolCode() == ProtocolConstants.DataType.FLOAT) {
str = new Float(row.getFloat(index)).toString(); str = Float.valueOf(row.getFloat(index)).toString();
} else if (type.getProtocolCode() == ProtocolConstants.DataType.TIMESTAMP) { } else if (type.getProtocolCode() == ProtocolConstants.DataType.TIMESTAMP) {
str = ""+row.getInstant(index).toEpochMilli(); str = ""+row.getInstant(index).toEpochMilli();
} else if (type.getProtocolCode() == ProtocolConstants.DataType.BOOLEAN) { } else if (type.getProtocolCode() == ProtocolConstants.DataType.BOOLEAN) {

3
application/src/main/java/org/thingsboard/server/service/install/migrate/CassandraToSqlColumn.java

@ -153,7 +153,8 @@ public class CassandraToSqlColumn {
sqlInsertStatement.setBoolean(this.sqlIndex, Boolean.parseBoolean(value)); sqlInsertStatement.setBoolean(this.sqlIndex, Boolean.parseBoolean(value));
break; break;
case ENUM_TO_INT: case ENUM_TO_INT:
Enum enumVal = Enum.valueOf(this.enumClass, value); @SuppressWarnings("unchecked")
Enum<?> enumVal = Enum.valueOf(this.enumClass, value);
int intValue = enumVal.ordinal(); int intValue = enumVal.ordinal();
sqlInsertStatement.setInt(this.sqlIndex, intValue); sqlInsertStatement.setInt(this.sqlIndex, intValue);
break; break;

3
application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java

@ -50,7 +50,8 @@ import java.util.List;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static org.apache.commons.lang.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.thingsboard.server.service.install.DatabaseHelper.objectMapper;
@Service @Service
@Profile("install") @Profile("install")

41
application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MModelsRepository.java

@ -169,11 +169,11 @@ public class LwM2MModelsRepository {
* PageNumber = 1, PageSize = List<LwM2mObject>.size() * PageNumber = 1, PageSize = List<LwM2mObject>.size()
*/ */
public PageData<LwM2mObject> findLwm2mListObjects(PageLink pageLink) { public PageData<LwM2mObject> findLwm2mListObjects(PageLink pageLink) {
PageImpl page = new PageImpl(getLwm2mObjects(getObjectIdFromTextSearch(pageLink.getTextSearch()), PageImpl<LwM2mObject> page = new PageImpl<>(getLwm2mObjects(getObjectIdFromTextSearch(pageLink.getTextSearch()),
pageLink.getTextSearch(), pageLink.getTextSearch(),
pageLink.getSortOrder().getProperty(), pageLink.getSortOrder().getProperty(),
pageLink.getSortOrder().getDirection().name())); pageLink.getSortOrder().getDirection().name()));
PageData pageData = new PageData(page.getContent(), page.getTotalPages(), page.getTotalElements(), page.hasNext()); PageData<LwM2mObject> pageData = new PageData<>(page.getContent(), page.getTotalPages(), page.getTotalElements(), page.hasNext());
return pageData; return pageData;
} }
@ -217,23 +217,19 @@ public class LwM2MModelsRepository {
switch (mode) { switch (mode) {
case NO_SEC: case NO_SEC:
bsServ.setHost(contextBootStrap.getBootstrapHost()); bsServ.setHost(contextBootStrap.getBootstrapHost());
bsServ.setPort(contextBootStrap.getBootstrapPortNoSecPsk()); bsServ.setPort(contextBootStrap.getBootstrapPortNoSec());
bsServ.setServerPublicKey(""); bsServ.setServerPublicKey("");
break; break;
case PSK: case PSK:
bsServ.setHost(contextBootStrap.getBootstrapSecureHost()); bsServ.setHost(contextBootStrap.getBootstrapHostSecurity());
bsServ.setPort(contextBootStrap.getBootstrapSecurePortPsk()); bsServ.setPort(contextBootStrap.getBootstrapPortSecurity());
bsServ.setServerPublicKey(""); bsServ.setServerPublicKey("");
break; break;
case RPK: case RPK:
bsServ.setHost(contextBootStrap.getBootstrapSecureHost());
bsServ.setPort(contextBootStrap.getBootstrapSecurePortRpk());
bsServ.setServerPublicKey(getRPKPublicKey(this.contextBootStrap.getBootstrapPublicX(), this.contextBootStrap.getBootstrapPublicY()));
break;
case X509: case X509:
bsServ.setHost(contextBootStrap.getBootstrapSecureHost()); bsServ.setHost(contextBootStrap.getBootstrapHostSecurity());
bsServ.setPort(contextBootStrap.getBootstrapSecurePortX509()); bsServ.setPort(contextBootStrap.getBootstrapPortSecurity());
bsServ.setServerPublicKey(getServerPublicKeyX509(contextBootStrap.getBootstrapAlias())); bsServ.setServerPublicKey(getPublicKey (contextBootStrap.getBootstrapAlias(), this.contextBootStrap.getBootstrapPublicX(), this.contextBootStrap.getBootstrapPublicY()));
break; break;
default: default:
break; break;
@ -243,23 +239,19 @@ public class LwM2MModelsRepository {
switch (mode) { switch (mode) {
case NO_SEC: case NO_SEC:
bsServ.setHost(contextServer.getServerHost()); bsServ.setHost(contextServer.getServerHost());
bsServ.setPort(contextServer.getServerPortNoSecPsk()); bsServ.setPort(contextServer.getServerPortNoSec());
bsServ.setServerPublicKey(""); bsServ.setServerPublicKey("");
break; break;
case PSK: case PSK:
bsServ.setHost(contextServer.getServerSecureHost()); bsServ.setHost(contextServer.getServerHostSecurity());
bsServ.setPort(contextServer.getServerPortPsk()); bsServ.setPort(contextServer.getServerPortSecurity());
bsServ.setServerPublicKey(""); bsServ.setServerPublicKey("");
break; break;
case RPK: case RPK:
bsServ.setHost(contextServer.getServerSecureHost());
bsServ.setPort(contextServer.getServerPortRpk());
bsServ.setServerPublicKey(getRPKPublicKey(this.contextServer.getServerPublicX(), this.contextServer.getServerPublicY()));
break;
case X509: case X509:
bsServ.setHost(contextServer.getServerSecureHost()); bsServ.setHost(contextServer.getServerHostSecurity());
bsServ.setPort(contextServer.getServerPortX509()); bsServ.setPort(contextServer.getServerPortSecurity());
bsServ.setServerPublicKey(getServerPublicKeyX509(contextServer.getServerAlias())); bsServ.setServerPublicKey(getPublicKey (contextServer.getServerAlias(), this.contextServer.getServerPublicX(), this.contextServer.getServerPublicY()));
break; break;
default: default:
break; break;
@ -268,6 +260,11 @@ public class LwM2MModelsRepository {
return bsServ; return bsServ;
} }
private String getPublicKey (String alias, String publicServerX, String publicServerY) {
String publicKey = getServerPublicKeyX509(alias);
return publicKey != null ? publicKey : getRPKPublicKey(publicServerX, publicServerY);
}
/** /**
* @param alias * @param alias
* @return PublicKey format HexString or null * @return PublicKey format HexString or null

2
application/src/main/java/org/thingsboard/server/service/query/DefaultEntityQueryService.java

@ -206,7 +206,7 @@ public class DefaultEntityQueryService implements EntityQueryService {
addItemsToArrayNode(json.putArray("entityTypes"), types); addItemsToArrayNode(json.putArray("entityTypes"), types);
addItemsToArrayNode(json.putArray("timeseries"), timeseriesKeys); addItemsToArrayNode(json.putArray("timeseries"), timeseriesKeys);
addItemsToArrayNode(json.putArray("attribute"), attributesKeys); addItemsToArrayNode(json.putArray("attribute"), attributesKeys);
response.setResult(new ResponseEntity(json, HttpStatus.OK)); response.setResult(new ResponseEntity<>(json, HttpStatus.OK));
} }
private void replyWithEmptyResponse(DeferredResult<ResponseEntity> response) { private void replyWithEmptyResponse(DeferredResult<ResponseEntity> response) {

10
application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java

@ -156,12 +156,12 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService<ToCore
} }
@Override @Override
public void onApplicationEvent(PartitionChangeEvent partitionChangeEvent) { protected void onTbApplicationEvent(PartitionChangeEvent event) {
if (partitionChangeEvent.getServiceType().equals(getServiceType())) { if (event.getServiceType().equals(getServiceType())) {
log.info("Subscribing to partitions: {}", partitionChangeEvent.getPartitions()); log.info("Subscribing to partitions: {}", event.getPartitions());
this.mainConsumer.subscribe(partitionChangeEvent.getPartitions()); this.mainConsumer.subscribe(event.getPartitions());
this.usageStatsConsumer.subscribe( this.usageStatsConsumer.subscribe(
partitionChangeEvent event
.getPartitions() .getPartitions()
.stream() .stream()
.map(tpi -> tpi.newByTopic(usageStatsConsumer.getTopic())) .map(tpi -> tpi.newByTopic(usageStatsConsumer.getTopic()))

15
application/src/main/java/org/thingsboard/server/service/queue/DefaultTbRuleEngineConsumerService.java

@ -140,11 +140,11 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService<
} }
@Override @Override
public void onApplicationEvent(PartitionChangeEvent partitionChangeEvent) { protected void onTbApplicationEvent(PartitionChangeEvent event) {
if (partitionChangeEvent.getServiceType().equals(getServiceType())) { if (event.getServiceType().equals(getServiceType())) {
ServiceQueue serviceQueue = partitionChangeEvent.getServiceQueueKey().getServiceQueue(); ServiceQueue serviceQueue = event.getServiceQueueKey().getServiceQueue();
log.info("[{}] Subscribing to partitions: {}", serviceQueue.getQueue(), partitionChangeEvent.getPartitions()); log.info("[{}] Subscribing to partitions: {}", serviceQueue.getQueue(), event.getPartitions());
consumers.get(serviceQueue.getQueue()).subscribe(partitionChangeEvent.getPartitions()); consumers.get(serviceQueue.getQueue()).subscribe(event.getPartitions());
} }
} }
@ -181,7 +181,7 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService<
new TbMsgPackCallback(id, tenantId, ctx, stats.getTimer(tenantId, SUCCESSFUL_STATUS), stats.getTimer(tenantId, FAILED_STATUS)) : new TbMsgPackCallback(id, tenantId, ctx, stats.getTimer(tenantId, SUCCESSFUL_STATUS), stats.getTimer(tenantId, FAILED_STATUS)) :
new TbMsgPackCallback(id, tenantId, ctx); new TbMsgPackCallback(id, tenantId, ctx);
try { try {
if (toRuleEngineMsg.getTbMsg() != null && !toRuleEngineMsg.getTbMsg().isEmpty()) { if (!toRuleEngineMsg.getTbMsg().isEmpty()) {
forwardToRuleEngineActor(configuration.getName(), tenantId, toRuleEngineMsg, callback); forwardToRuleEngineActor(configuration.getName(), tenantId, toRuleEngineMsg, callback);
} else { } else {
callback.onSuccess(); callback.onSuccess();
@ -209,6 +209,9 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService<
if (statsEnabled) { if (statsEnabled) {
stats.log(result, decision.isCommit()); stats.log(result, decision.isCommit());
} }
ctx.cleanup();
if (decision.isCommit()) { if (decision.isCommit()) {
submitStrategy.stop(); submitStrategy.stop();
break; break;

6
application/src/main/java/org/thingsboard/server/service/queue/TbMsgPackProcessingContext.java

@ -147,4 +147,10 @@ public class TbMsgPackProcessingContext {
.forEach(info -> log.info("[{}][{}] execution count: {}. {}", queueName, info.getRuleNodeId(), info.getExecutionCount(), info.getLabel())); .forEach(info -> log.info("[{}][{}] execution count: {}. {}", queueName, info.getRuleNodeId(), info.getExecutionCount(), info.getLabel()));
} }
} }
public void cleanup() {
pendingMap.clear();
successMap.clear();
failedMap.clear();
}
} }

3
application/src/main/java/org/thingsboard/server/service/queue/processing/AbstractConsumerService.java

@ -36,6 +36,7 @@ import org.thingsboard.server.queue.TbQueueConsumer;
import org.thingsboard.server.queue.common.TbProtoQueueMsg; import org.thingsboard.server.queue.common.TbProtoQueueMsg;
import org.thingsboard.server.queue.discovery.PartitionChangeEvent; import org.thingsboard.server.queue.discovery.PartitionChangeEvent;
import org.thingsboard.server.common.transport.util.DataDecodingEncodingService; import org.thingsboard.server.common.transport.util.DataDecodingEncodingService;
import org.thingsboard.server.queue.discovery.TbApplicationEventListener;
import org.thingsboard.server.service.apiusage.TbApiUsageStateService; import org.thingsboard.server.service.apiusage.TbApiUsageStateService;
import org.thingsboard.server.service.profile.TbDeviceProfileCache; import org.thingsboard.server.service.profile.TbDeviceProfileCache;
import org.thingsboard.server.dao.tenant.TbTenantProfileCache; import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
@ -56,7 +57,7 @@ import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@Slf4j @Slf4j
public abstract class AbstractConsumerService<N extends com.google.protobuf.GeneratedMessageV3> implements ApplicationListener<PartitionChangeEvent> { public abstract class AbstractConsumerService<N extends com.google.protobuf.GeneratedMessageV3> extends TbApplicationEventListener<PartitionChangeEvent> {
protected volatile ExecutorService consumersExecutor; protected volatile ExecutorService consumersExecutor;
protected volatile ExecutorService notificationsConsumerExecutor; protected volatile ExecutorService notificationsConsumerExecutor;

2
application/src/main/java/org/thingsboard/server/service/rpc/ToDeviceRpcRequestActorMsg.java

@ -31,6 +31,8 @@ import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest;
@RequiredArgsConstructor @RequiredArgsConstructor
public class ToDeviceRpcRequestActorMsg implements ToDeviceActorNotificationMsg { public class ToDeviceRpcRequestActorMsg implements ToDeviceActorNotificationMsg {
private static final long serialVersionUID = -8592877558138716589L;
@Getter @Getter
private final String serviceId; private final String serviceId;
@Getter @Getter

6
application/src/main/java/org/thingsboard/server/service/script/AbstractNashornJsInvokeService.java

@ -21,7 +21,6 @@ import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors; import com.google.common.util.concurrent.MoreExecutors;
import delight.nashornsandbox.NashornSandbox; import delight.nashornsandbox.NashornSandbox;
import delight.nashornsandbox.NashornSandboxes; import delight.nashornsandbox.NashornSandboxes;
import jdk.nashorn.api.scripting.NashornScriptEngineFactory;
import lombok.Getter; import lombok.Getter;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
@ -33,6 +32,7 @@ import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy; import javax.annotation.PreDestroy;
import javax.script.Invocable; import javax.script.Invocable;
import javax.script.ScriptEngine; import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException; import javax.script.ScriptException;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
@ -97,8 +97,8 @@ public abstract class AbstractNashornJsInvokeService extends AbstractJsInvokeSer
sandbox.allowLoadFunctions(true); sandbox.allowLoadFunctions(true);
sandbox.setMaxPreparedStatements(30); sandbox.setMaxPreparedStatements(30);
} else { } else {
NashornScriptEngineFactory factory = new NashornScriptEngineFactory(); ScriptEngineManager factory = new ScriptEngineManager();
engine = factory.getScriptEngine(new String[]{"--no-java"}); engine = factory.getEngineByName("nashorn");
} }
} }

2
application/src/main/java/org/thingsboard/server/service/security/auth/jwt/SkipPathRequestMatcher.java

@ -29,7 +29,7 @@ public class SkipPathRequestMatcher implements RequestMatcher {
private RequestMatcher processingMatcher; private RequestMatcher processingMatcher;
public SkipPathRequestMatcher(List<String> pathsToSkip, String processingPath) { public SkipPathRequestMatcher(List<String> pathsToSkip, String processingPath) {
Assert.notNull(pathsToSkip); Assert.notNull(pathsToSkip, "List of paths to skip is required.");
List<RequestMatcher> m = pathsToSkip.stream().map(path -> new AntPathRequestMatcher(path)).collect(Collectors.toList()); List<RequestMatcher> m = pathsToSkip.stream().map(path -> new AntPathRequestMatcher(path)).collect(Collectors.toList());
matchers = new OrRequestMatcher(m); matchers = new OrRequestMatcher(m);
processingMatcher = new AntPathRequestMatcher(processingPath); processingMatcher = new AntPathRequestMatcher(processingPath);

2
application/src/main/java/org/thingsboard/server/service/security/model/token/JwtTokenFactory.java

@ -100,6 +100,7 @@ public class JwtTokenFactory {
Jws<Claims> jwsClaims = rawAccessToken.parseClaims(settings.getTokenSigningKey()); Jws<Claims> jwsClaims = rawAccessToken.parseClaims(settings.getTokenSigningKey());
Claims claims = jwsClaims.getBody(); Claims claims = jwsClaims.getBody();
String subject = claims.getSubject(); String subject = claims.getSubject();
@SuppressWarnings("unchecked")
List<String> scopes = claims.get(SCOPES, List.class); List<String> scopes = claims.get(SCOPES, List.class);
if (scopes == null || scopes.isEmpty()) { if (scopes == null || scopes.isEmpty()) {
throw new IllegalArgumentException("JWT Token doesn't have any scopes"); throw new IllegalArgumentException("JWT Token doesn't have any scopes");
@ -155,6 +156,7 @@ public class JwtTokenFactory {
Jws<Claims> jwsClaims = rawAccessToken.parseClaims(settings.getTokenSigningKey()); Jws<Claims> jwsClaims = rawAccessToken.parseClaims(settings.getTokenSigningKey());
Claims claims = jwsClaims.getBody(); Claims claims = jwsClaims.getBody();
String subject = claims.getSubject(); String subject = claims.getSubject();
@SuppressWarnings("unchecked")
List<String> scopes = claims.get(SCOPES, List.class); List<String> scopes = claims.get(SCOPES, List.class);
if (scopes == null || scopes.isEmpty()) { if (scopes == null || scopes.isEmpty()) {
throw new IllegalArgumentException("Refresh Token doesn't have any scopes"); throw new IllegalArgumentException("Refresh Token doesn't have any scopes");

3
application/src/main/java/org/thingsboard/server/service/security/permission/CustomerUserPermissions.java

@ -48,6 +48,7 @@ public class CustomerUserPermissions extends AbstractPermissions {
Operation.READ_ATTRIBUTES, Operation.READ_TELEMETRY, Operation.RPC_CALL, Operation.CLAIM_DEVICES) { Operation.READ_ATTRIBUTES, Operation.READ_TELEMETRY, Operation.RPC_CALL, Operation.CLAIM_DEVICES) {
@Override @Override
@SuppressWarnings("unchecked")
public boolean hasPermission(SecurityUser user, Operation operation, EntityId entityId, HasTenantId entity) { public boolean hasPermission(SecurityUser user, Operation operation, EntityId entityId, HasTenantId entity) {
if (!super.hasPermission(user, operation, entityId, entity)) { if (!super.hasPermission(user, operation, entityId, entity)) {
@ -70,6 +71,7 @@ public class CustomerUserPermissions extends AbstractPermissions {
new PermissionChecker.GenericPermissionChecker(Operation.READ, Operation.READ_ATTRIBUTES, Operation.READ_TELEMETRY) { new PermissionChecker.GenericPermissionChecker(Operation.READ, Operation.READ_ATTRIBUTES, Operation.READ_TELEMETRY) {
@Override @Override
@SuppressWarnings("unchecked")
public boolean hasPermission(SecurityUser user, Operation operation, EntityId entityId, HasTenantId entity) { public boolean hasPermission(SecurityUser user, Operation operation, EntityId entityId, HasTenantId entity) {
if (!super.hasPermission(user, operation, entityId, entity)) { if (!super.hasPermission(user, operation, entityId, entity)) {
return false; return false;
@ -120,6 +122,7 @@ public class CustomerUserPermissions extends AbstractPermissions {
private static final PermissionChecker widgetsPermissionChecker = new PermissionChecker.GenericPermissionChecker(Operation.READ) { private static final PermissionChecker widgetsPermissionChecker = new PermissionChecker.GenericPermissionChecker(Operation.READ) {
@Override @Override
@SuppressWarnings("unchecked")
public boolean hasPermission(SecurityUser user, Operation operation, EntityId entityId, HasTenantId entity) { public boolean hasPermission(SecurityUser user, Operation operation, EntityId entityId, HasTenantId entity) {
if (!super.hasPermission(user, operation, entityId, entity)) { if (!super.hasPermission(user, operation, entityId, entity)) {
return false; return false;

1
application/src/main/java/org/thingsboard/server/service/security/permission/DefaultAccessControlService.java

@ -56,6 +56,7 @@ public class DefaultAccessControlService implements AccessControlService {
} }
@Override @Override
@SuppressWarnings("unchecked")
public <I extends EntityId, T extends HasTenantId> void checkPermission(SecurityUser user, Resource resource, public <I extends EntityId, T extends HasTenantId> void checkPermission(SecurityUser user, Resource resource,
Operation operation, I entityId, T entity) throws ThingsboardException { Operation operation, I entityId, T entity) throws ThingsboardException {
PermissionChecker permissionChecker = getPermissionChecker(user.getAuthority(), resource); PermissionChecker permissionChecker = getPermissionChecker(user.getAuthority(), resource);

1
application/src/main/java/org/thingsboard/server/service/security/permission/TenantAdminPermissions.java

@ -60,6 +60,7 @@ public class TenantAdminPermissions extends AbstractPermissions {
new PermissionChecker.GenericPermissionChecker(Operation.READ, Operation.READ_ATTRIBUTES, Operation.READ_TELEMETRY) { new PermissionChecker.GenericPermissionChecker(Operation.READ, Operation.READ_ATTRIBUTES, Operation.READ_TELEMETRY) {
@Override @Override
@SuppressWarnings("unchecked")
public boolean hasPermission(SecurityUser user, Operation operation, EntityId entityId, HasTenantId entity) { public boolean hasPermission(SecurityUser user, Operation operation, EntityId entityId, HasTenantId entity) {
if (!super.hasPermission(user, operation, entityId, entity)) { if (!super.hasPermission(user, operation, entityId, entity)) {
return false; return false;

13
application/src/main/java/org/thingsboard/server/service/security/system/DefaultSystemSecurityService.java

@ -15,8 +15,8 @@
*/ */
package org.thingsboard.server.service.security.system; package org.thingsboard.server.service.security.system;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
@ -49,6 +49,7 @@ import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.settings.AdminSettingsService; import org.thingsboard.server.dao.settings.AdminSettingsService;
import org.thingsboard.server.dao.user.UserService; import org.thingsboard.server.dao.user.UserService;
import org.thingsboard.server.dao.user.UserServiceImpl; import org.thingsboard.server.dao.user.UserServiceImpl;
import org.thingsboard.server.dao.util.mapping.JacksonUtil;
import org.thingsboard.server.service.security.exception.UserPasswordExpiredException; import org.thingsboard.server.service.security.exception.UserPasswordExpiredException;
import org.thingsboard.server.utils.MiscUtils; import org.thingsboard.server.utils.MiscUtils;
@ -65,8 +66,6 @@ import static org.thingsboard.server.common.data.CacheConstants.SECURITY_SETTING
@Slf4j @Slf4j
public class DefaultSystemSecurityService implements SystemSecurityService { public class DefaultSystemSecurityService implements SystemSecurityService {
private static final ObjectMapper objectMapper = new ObjectMapper();
@Autowired @Autowired
private AdminSettingsService adminSettingsService; private AdminSettingsService adminSettingsService;
@ -89,7 +88,7 @@ public class DefaultSystemSecurityService implements SystemSecurityService {
AdminSettings adminSettings = adminSettingsService.findAdminSettingsByKey(tenantId, "securitySettings"); AdminSettings adminSettings = adminSettingsService.findAdminSettingsByKey(tenantId, "securitySettings");
if (adminSettings != null) { if (adminSettings != null) {
try { try {
securitySettings = objectMapper.treeToValue(adminSettings.getJsonValue(), SecuritySettings.class); securitySettings = JacksonUtil.convertValue(adminSettings.getJsonValue(), SecuritySettings.class);
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException("Failed to load security settings!", e); throw new RuntimeException("Failed to load security settings!", e);
} }
@ -109,10 +108,10 @@ public class DefaultSystemSecurityService implements SystemSecurityService {
adminSettings = new AdminSettings(); adminSettings = new AdminSettings();
adminSettings.setKey("securitySettings"); adminSettings.setKey("securitySettings");
} }
adminSettings.setJsonValue(objectMapper.valueToTree(securitySettings)); adminSettings.setJsonValue(JacksonUtil.valueToTree(securitySettings));
AdminSettings savedAdminSettings = adminSettingsService.saveAdminSettings(tenantId, adminSettings); AdminSettings savedAdminSettings = adminSettingsService.saveAdminSettings(tenantId, adminSettings);
try { try {
return objectMapper.treeToValue(savedAdminSettings.getJsonValue(), SecuritySettings.class); return JacksonUtil.convertValue(savedAdminSettings.getJsonValue(), SecuritySettings.class);
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException("Failed to load security settings!", e); throw new RuntimeException("Failed to load security settings!", e);
} }
@ -189,7 +188,7 @@ public class DefaultSystemSecurityService implements SystemSecurityService {
JsonNode additionalInfo = user.getAdditionalInfo(); JsonNode additionalInfo = user.getAdditionalInfo();
if (additionalInfo instanceof ObjectNode && additionalInfo.has(UserServiceImpl.USER_PASSWORD_HISTORY)) { if (additionalInfo instanceof ObjectNode && additionalInfo.has(UserServiceImpl.USER_PASSWORD_HISTORY)) {
JsonNode userPasswordHistoryJson = additionalInfo.get(UserServiceImpl.USER_PASSWORD_HISTORY); JsonNode userPasswordHistoryJson = additionalInfo.get(UserServiceImpl.USER_PASSWORD_HISTORY);
Map<String, String> userPasswordHistoryMap = objectMapper.convertValue(userPasswordHistoryJson, Map.class); Map<String, String> userPasswordHistoryMap = JacksonUtil.convertValue(userPasswordHistoryJson, new TypeReference<>() {});
for (Map.Entry<String, String> entry : userPasswordHistoryMap.entrySet()) { for (Map.Entry<String, String> entry : userPasswordHistoryMap.entrySet()) {
if (encoder.matches(password, entry.getValue()) && Long.parseLong(entry.getKey()) > passwordReuseFrequencyTs) { if (encoder.matches(password, entry.getValue()) && Long.parseLong(entry.getKey()) > passwordReuseFrequencyTs) {
throw new DataValidationException("Password was already used for the last " + passwordPolicy.getPasswordReuseFrequencyDays() + " days"); throw new DataValidationException("Password was already used for the last " + passwordPolicy.getPasswordReuseFrequencyDays() + " days");

35
application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java

@ -56,9 +56,11 @@ import org.thingsboard.server.dao.util.mapping.JacksonUtil;
import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.queue.discovery.PartitionChangeEvent; import org.thingsboard.server.queue.discovery.PartitionChangeEvent;
import org.thingsboard.server.queue.discovery.PartitionService; import org.thingsboard.server.queue.discovery.PartitionService;
import org.thingsboard.server.queue.discovery.TbApplicationEventListener;
import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.queue.TbClusterService; import org.thingsboard.server.service.queue.TbClusterService;
import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService; import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService;
import org.thingsboard.server.utils.EventDeduplicationExecutor;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
@ -89,7 +91,7 @@ import static org.thingsboard.server.common.data.DataConstants.SERVER_SCOPE;
@Service @Service
@TbCoreComponent @TbCoreComponent
@Slf4j @Slf4j
public class DefaultDeviceStateService implements DeviceStateService { public class DefaultDeviceStateService extends TbApplicationEventListener<PartitionChangeEvent> implements DeviceStateService {
public static final String ACTIVITY_STATE = "active"; public static final String ACTIVITY_STATE = "active";
public static final String LAST_CONNECT_TIME = "lastConnectTime"; public static final String LAST_CONNECT_TIME = "lastConnectTime";
@ -126,13 +128,13 @@ public class DefaultDeviceStateService implements DeviceStateService {
@Getter @Getter
private int initFetchPackSize; private int initFetchPackSize;
private volatile boolean clusterUpdatePending = false;
private ListeningScheduledExecutorService queueExecutor; private ListeningScheduledExecutorService queueExecutor;
private final ConcurrentMap<TopicPartitionInfo, Set<DeviceId>> partitionedDevices = new ConcurrentHashMap<>(); private final ConcurrentMap<TopicPartitionInfo, Set<DeviceId>> partitionedDevices = new ConcurrentHashMap<>();
private final ConcurrentMap<DeviceId, DeviceStateData> deviceStates = new ConcurrentHashMap<>(); private final ConcurrentMap<DeviceId, DeviceStateData> deviceStates = new ConcurrentHashMap<>();
private final ConcurrentMap<DeviceId, Long> deviceLastReportedActivity = new ConcurrentHashMap<>(); private final ConcurrentMap<DeviceId, Long> deviceLastReportedActivity = new ConcurrentHashMap<>();
private final ConcurrentMap<DeviceId, Long> deviceLastSavedActivity = new ConcurrentHashMap<>(); private final ConcurrentMap<DeviceId, Long> deviceLastSavedActivity = new ConcurrentHashMap<>();
private volatile EventDeduplicationExecutor<Set<TopicPartitionInfo>> deduplicationExecutor;
public DefaultDeviceStateService(TenantService tenantService, DeviceService deviceService, public DefaultDeviceStateService(TenantService tenantService, DeviceService deviceService,
AttributesService attributesService, TimeseriesService tsService, AttributesService attributesService, TimeseriesService tsService,
@ -155,6 +157,7 @@ public class DefaultDeviceStateService implements DeviceStateService {
// Should be always single threaded due to absence of locks. // Should be always single threaded due to absence of locks.
queueExecutor = MoreExecutors.listeningDecorator(Executors.newSingleThreadScheduledExecutor(ThingsBoardThreadFactory.forName("device-state"))); queueExecutor = MoreExecutors.listeningDecorator(Executors.newSingleThreadScheduledExecutor(ThingsBoardThreadFactory.forName("device-state")));
queueExecutor.scheduleAtFixedRate(this::updateState, new Random().nextInt(defaultStateCheckIntervalInSec), defaultStateCheckIntervalInSec, TimeUnit.SECONDS); queueExecutor.scheduleAtFixedRate(this::updateState, new Random().nextInt(defaultStateCheckIntervalInSec), defaultStateCheckIntervalInSec, TimeUnit.SECONDS);
deduplicationExecutor = new EventDeduplicationExecutor<>(DefaultDeviceStateService.class.getSimpleName(), queueExecutor, this::initStateFromDB);
} }
@PreDestroy @PreDestroy
@ -204,7 +207,6 @@ public class DefaultDeviceStateService implements DeviceStateService {
if (!state.isActive()) { if (!state.isActive()) {
state.setActive(true); state.setActive(true);
save(deviceId, ACTIVITY_STATE, state.isActive()); save(deviceId, ACTIVITY_STATE, state.isActive());
stateData.getMetaData().putValue("scope", SERVER_SCOPE);
pushRuleEngineMessage(stateData, ACTIVITY_EVENT); pushRuleEngineMessage(stateData, ACTIVITY_EVENT);
} }
} }
@ -292,25 +294,14 @@ public class DefaultDeviceStateService implements DeviceStateService {
} }
} }
volatile Set<TopicPartitionInfo> pendingPartitions;
@Override @Override
public void onApplicationEvent(PartitionChangeEvent partitionChangeEvent) { protected void onTbApplicationEvent(PartitionChangeEvent partitionChangeEvent) {
if (ServiceType.TB_CORE.equals(partitionChangeEvent.getServiceType())) { if (ServiceType.TB_CORE.equals(partitionChangeEvent.getServiceType())) {
synchronized (this) { deduplicationExecutor.submit(partitionChangeEvent.getPartitions());
pendingPartitions = partitionChangeEvent.getPartitions();
if (!clusterUpdatePending) {
clusterUpdatePending = true;
queueExecutor.submit(() -> {
clusterUpdatePending = false;
initStateFromDB();
});
}
}
} }
} }
private void initStateFromDB() { private void initStateFromDB(Set<TopicPartitionInfo> pendingPartitions) {
try { try {
log.info("CURRENT PARTITIONS: {}", partitionedDevices.keySet()); log.info("CURRENT PARTITIONS: {}", partitionedDevices.keySet());
log.info("NEW PARTITIONS: {}", pendingPartitions); log.info("NEW PARTITIONS: {}", pendingPartitions);
@ -456,7 +447,7 @@ public class DefaultDeviceStateService implements DeviceStateService {
} }
private <T extends KvEntry> Function<List<T>, DeviceStateData> extractDeviceStateData(Device device) { private <T extends KvEntry> Function<List<T>, DeviceStateData> extractDeviceStateData(Device device) {
return new Function<List<T>, DeviceStateData>() { return new Function<>() {
@Nullable @Nullable
@Override @Override
public DeviceStateData apply(@Nullable List<T> data) { public DeviceStateData apply(@Nullable List<T> data) {
@ -512,7 +503,11 @@ public class DefaultDeviceStateService implements DeviceStateService {
} else { } else {
data = JacksonUtil.toString(state); data = JacksonUtil.toString(state);
} }
TbMsg tbMsg = TbMsg.newMsg(msgType, stateData.getDeviceId(), stateData.getMetaData().copy(), TbMsgDataType.JSON, data); TbMsgMetaData md = stateData.getMetaData().copy();
if(!persistToTelemetry){
md.putValue(DataConstants.SCOPE, SERVER_SCOPE);
}
TbMsg tbMsg = TbMsg.newMsg(msgType, stateData.getDeviceId(), md, TbMsgDataType.JSON, data);
clusterService.pushMsgToRuleEngine(stateData.getTenantId(), stateData.getDeviceId(), tbMsg, null); clusterService.pushMsgToRuleEngine(stateData.getTenantId(), stateData.getDeviceId(), tbMsg, null);
} catch (Exception e) { } catch (Exception e) {
log.warn("[{}] Failed to push inactivity alarm: {}", stateData.getDeviceId(), state, e); log.warn("[{}] Failed to push inactivity alarm: {}", stateData.getDeviceId(), state, e);

5
application/src/main/java/org/thingsboard/server/service/subscription/DefaultSubscriptionManagerService.java

@ -48,6 +48,7 @@ import org.thingsboard.server.queue.TbQueueProducer;
import org.thingsboard.server.queue.common.TbProtoQueueMsg; import org.thingsboard.server.queue.common.TbProtoQueueMsg;
import org.thingsboard.server.queue.discovery.PartitionChangeEvent; import org.thingsboard.server.queue.discovery.PartitionChangeEvent;
import org.thingsboard.server.queue.discovery.PartitionService; import org.thingsboard.server.queue.discovery.PartitionService;
import org.thingsboard.server.queue.discovery.TbApplicationEventListener;
import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; import org.thingsboard.server.queue.discovery.TbServiceInfoProvider;
import org.thingsboard.server.queue.provider.TbQueueProducerProvider; import org.thingsboard.server.queue.provider.TbQueueProducerProvider;
import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.queue.util.TbCoreComponent;
@ -76,7 +77,7 @@ import java.util.function.Predicate;
@Slf4j @Slf4j
@TbCoreComponent @TbCoreComponent
@Service @Service
public class DefaultSubscriptionManagerService implements SubscriptionManagerService { public class DefaultSubscriptionManagerService extends TbApplicationEventListener<PartitionChangeEvent> implements SubscriptionManagerService {
@Autowired @Autowired
private AttributesService attrService; private AttributesService attrService;
@ -178,7 +179,7 @@ public class DefaultSubscriptionManagerService implements SubscriptionManagerSer
} }
@Override @Override
public void onApplicationEvent(PartitionChangeEvent partitionChangeEvent) { protected void onTbApplicationEvent(PartitionChangeEvent partitionChangeEvent) {
if (ServiceType.TB_CORE.equals(partitionChangeEvent.getServiceType())) { if (ServiceType.TB_CORE.equals(partitionChangeEvent.getServiceType())) {
Set<TopicPartitionInfo> removedPartitions = new HashSet<>(currentPartitions); Set<TopicPartitionInfo> removedPartitions = new HashSet<>(currentPartitions);
removedPartitions.removeAll(partitionChangeEvent.getPartitions()); removedPartitions.removeAll(partitionChangeEvent.getPartitions());

7
application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbEntityDataSubscriptionService.java

@ -302,7 +302,9 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc
Map<Integer, TbAbstractDataSubCtx> sessionSubs = subscriptionsBySessionId.computeIfAbsent(sessionRef.getSessionId(), k -> new HashMap<>()); Map<Integer, TbAbstractDataSubCtx> sessionSubs = subscriptionsBySessionId.computeIfAbsent(sessionRef.getSessionId(), k -> new HashMap<>());
TbEntityDataSubCtx ctx = new TbEntityDataSubCtx(serviceId, wsService, entityService, localSubscriptionService, TbEntityDataSubCtx ctx = new TbEntityDataSubCtx(serviceId, wsService, entityService, localSubscriptionService,
attributesService, stats, sessionRef, cmd.getCmdId(), maxEntitiesPerDataSubscription); attributesService, stats, sessionRef, cmd.getCmdId(), maxEntitiesPerDataSubscription);
ctx.setAndResolveQuery(cmd.getQuery()); if (cmd.getQuery() != null) {
ctx.setAndResolveQuery(cmd.getQuery());
}
sessionSubs.put(cmd.getCmdId(), ctx); sessionSubs.put(cmd.getCmdId(), ctx);
return ctx; return ctx;
} }
@ -316,6 +318,7 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc
return ctx; return ctx;
} }
@SuppressWarnings("unchecked")
private <T extends TbAbstractDataSubCtx> T getSubCtx(String sessionId, int cmdId) { private <T extends TbAbstractDataSubCtx> T getSubCtx(String sessionId, int cmdId) {
Map<Integer, TbAbstractDataSubCtx> sessionSubs = subscriptionsBySessionId.get(sessionId); Map<Integer, TbAbstractDataSubCtx> sessionSubs = subscriptionsBySessionId.get(sessionId);
if (sessionSubs != null) { if (sessionSubs != null) {
@ -473,7 +476,7 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc
public void cancelAllSessionSubscriptions(String sessionId) { public void cancelAllSessionSubscriptions(String sessionId) {
Map<Integer, TbAbstractDataSubCtx> sessionSubs = subscriptionsBySessionId.remove(sessionId); Map<Integer, TbAbstractDataSubCtx> sessionSubs = subscriptionsBySessionId.remove(sessionId);
if (sessionSubs != null) { if (sessionSubs != null) {
sessionSubs.values().stream().filter(sub -> sub instanceof TbEntityDataSubCtx).map(sub -> (TbEntityDataSubCtx) sub).forEach(this::cleanupAndCancel); sessionSubs.values().forEach(this::cleanupAndCancel);
} }
} }

51
application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbLocalSubscriptionService.java

@ -28,6 +28,7 @@ import org.thingsboard.server.queue.discovery.PartitionService;
import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.common.msg.queue.ServiceType;
import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
import org.thingsboard.server.common.msg.queue.TbCallback; import org.thingsboard.server.common.msg.queue.TbCallback;
import org.thingsboard.server.queue.discovery.TbApplicationEventListener;
import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.queue.TbClusterService; import org.thingsboard.server.service.queue.TbClusterService;
import org.thingsboard.server.service.telemetry.sub.AlarmSubscriptionUpdate; import org.thingsboard.server.service.telemetry.sub.AlarmSubscriptionUpdate;
@ -62,6 +63,34 @@ public class DefaultTbLocalSubscriptionService implements TbLocalSubscriptionSer
private SubscriptionManagerService subscriptionManagerService; private SubscriptionManagerService subscriptionManagerService;
private ExecutorService subscriptionUpdateExecutor; private ExecutorService subscriptionUpdateExecutor;
private TbApplicationEventListener<PartitionChangeEvent> partitionChangeListener = new TbApplicationEventListener<>() {
@Override
protected void onTbApplicationEvent(PartitionChangeEvent event) {
if (ServiceType.TB_CORE.equals(event.getServiceType())) {
currentPartitions.clear();
currentPartitions.addAll(event.getPartitions());
}
}
};
private TbApplicationEventListener<ClusterTopologyChangeEvent> clusterTopologyChangeListener = new TbApplicationEventListener<>() {
@Override
protected void onTbApplicationEvent(ClusterTopologyChangeEvent event) {
if (event.getServiceQueueKeys().stream().anyMatch(key -> ServiceType.TB_CORE.equals(key.getServiceType()))) {
/*
* If the cluster topology has changed, we need to push all current subscriptions to SubscriptionManagerService again.
* Otherwise, the SubscriptionManagerService may "forget" those subscriptions in case of restart.
* Although this is resource consuming operation, it is cheaper than sending ping/pong commands periodically
* It is also cheaper then caching the subscriptions by entity id and then lookup of those caches every time we have new telemetry in SubscriptionManagerService.
* Even if we cache locally the list of active subscriptions by entity id, it is still time consuming operation to get them from cache
* Since number of subscriptions is usually much less then number of devices that are pushing data.
*/
subscriptionsBySessionId.values().forEach(map -> map.values()
.forEach(sub -> pushSubscriptionToManagerService(sub, true)));
}
}
};
@PostConstruct @PostConstruct
public void initExecutor() { public void initExecutor() {
@ -77,28 +106,14 @@ public class DefaultTbLocalSubscriptionService implements TbLocalSubscriptionSer
@Override @Override
@EventListener(PartitionChangeEvent.class) @EventListener(PartitionChangeEvent.class)
public void onApplicationEvent(PartitionChangeEvent partitionChangeEvent) { public void onApplicationEvent(PartitionChangeEvent event) {
if (ServiceType.TB_CORE.equals(partitionChangeEvent.getServiceType())) { partitionChangeListener.onApplicationEvent(event);
currentPartitions.clear();
currentPartitions.addAll(partitionChangeEvent.getPartitions());
}
} }
@Override @Override
@EventListener(ClusterTopologyChangeEvent.class) @EventListener(ClusterTopologyChangeEvent.class)
public void onApplicationEvent(ClusterTopologyChangeEvent event) { public void onApplicationEvent(ClusterTopologyChangeEvent event) {
if (event.getServiceQueueKeys().stream().anyMatch(key -> ServiceType.TB_CORE.equals(key.getServiceType()))) { clusterTopologyChangeListener.onApplicationEvent(event);
/*
* If the cluster topology has changed, we need to push all current subscriptions to SubscriptionManagerService again.
* Otherwise, the SubscriptionManagerService may "forget" those subscriptions in case of restart.
* Although this is resource consuming operation, it is cheaper than sending ping/pong commands periodically
* It is also cheaper then caching the subscriptions by entity id and then lookup of those caches every time we have new telemetry in SubscriptionManagerService.
* Even if we cache locally the list of active subscriptions by entity id, it is still time consuming operation to get them from cache
* Since number of subscriptions is usually much less then number of devices that are pushing data.
*/
subscriptionsBySessionId.values().forEach(map -> map.values()
.forEach(sub -> pushSubscriptionToManagerService(sub, true)));
}
} }
//TODO 3.1: replace null callbacks with callbacks from websocket service. //TODO 3.1: replace null callbacks with callbacks from websocket service.
@ -123,6 +138,7 @@ public class DefaultTbLocalSubscriptionService implements TbLocalSubscriptionSer
} }
@Override @Override
@SuppressWarnings("unchecked")
public void onSubscriptionUpdate(String sessionId, TelemetrySubscriptionUpdate update, TbCallback callback) { public void onSubscriptionUpdate(String sessionId, TelemetrySubscriptionUpdate update, TbCallback callback) {
TbSubscription subscription = subscriptionsBySessionId TbSubscription subscription = subscriptionsBySessionId
.getOrDefault(sessionId, Collections.emptyMap()).get(update.getSubscriptionId()); .getOrDefault(sessionId, Collections.emptyMap()).get(update.getSubscriptionId());
@ -143,6 +159,7 @@ public class DefaultTbLocalSubscriptionService implements TbLocalSubscriptionSer
} }
@Override @Override
@SuppressWarnings("unchecked")
public void onSubscriptionUpdate(String sessionId, AlarmSubscriptionUpdate update, TbCallback callback) { public void onSubscriptionUpdate(String sessionId, AlarmSubscriptionUpdate update, TbCallback callback) {
TbSubscription subscription = subscriptionsBySessionId TbSubscription subscription = subscriptionsBySessionId
.getOrDefault(sessionId, Collections.emptyMap()).get(update.getSubscriptionId()); .getOrDefault(sessionId, Collections.emptyMap()).get(update.getSubscriptionId());

4
application/src/main/java/org/thingsboard/server/service/subscription/TbAbstractDataSubCtx.java

@ -107,7 +107,7 @@ public abstract class TbAbstractDataSubCtx<T extends AbstractDataQuery<? extends
public void setAndResolveQuery(T query) { public void setAndResolveQuery(T query) {
dynamicValues.clear(); dynamicValues.clear();
this.query = query; this.query = query;
if (query.getKeyFilters() != null) { if (query != null && query.getKeyFilters() != null) {
for (KeyFilter filter : query.getKeyFilters()) { for (KeyFilter filter : query.getKeyFilters()) {
registerDynamicValues(filter.getPredicate()); registerDynamicValues(filter.getPredicate());
} }
@ -264,6 +264,7 @@ public abstract class TbAbstractDataSubCtx<T extends AbstractDataQuery<? extends
}, MoreExecutors.directExecutor()); }, MoreExecutors.directExecutor());
} }
@SuppressWarnings("unchecked")
private void updateDynamicValuesByKey(DynamicValueKeySub sub, TsValue tsValue) { private void updateDynamicValuesByKey(DynamicValueKeySub sub, TsValue tsValue) {
DynamicValueKey dvk = sub.getKey(); DynamicValueKey dvk = sub.getKey();
switch (dvk.getPredicateType()) { switch (dvk.getPredicateType()) {
@ -285,6 +286,7 @@ public abstract class TbAbstractDataSubCtx<T extends AbstractDataQuery<? extends
} }
} }
@SuppressWarnings("unchecked")
private void registerDynamicValues(KeyFilterPredicate predicate) { private void registerDynamicValues(KeyFilterPredicate predicate) {
switch (predicate.getType()) { switch (predicate.getType()) {
case STRING: case STRING:

6
application/src/main/java/org/thingsboard/server/service/telemetry/AbstractSubscriptionService.java

@ -41,6 +41,7 @@ import org.thingsboard.server.dao.timeseries.TimeseriesService;
import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.queue.discovery.PartitionChangeEvent; import org.thingsboard.server.queue.discovery.PartitionChangeEvent;
import org.thingsboard.server.queue.discovery.PartitionService; import org.thingsboard.server.queue.discovery.PartitionService;
import org.thingsboard.server.queue.discovery.TbApplicationEventListener;
import org.thingsboard.server.service.queue.TbClusterService; import org.thingsboard.server.service.queue.TbClusterService;
import org.thingsboard.server.service.subscription.SubscriptionManagerService; import org.thingsboard.server.service.subscription.SubscriptionManagerService;
import org.thingsboard.server.service.subscription.TbSubscriptionUtils; import org.thingsboard.server.service.subscription.TbSubscriptionUtils;
@ -61,7 +62,7 @@ import java.util.function.Consumer;
* Created by ashvayka on 27.03.18. * Created by ashvayka on 27.03.18.
*/ */
@Slf4j @Slf4j
public abstract class AbstractSubscriptionService implements ApplicationListener<PartitionChangeEvent> { public abstract class AbstractSubscriptionService extends TbApplicationEventListener<PartitionChangeEvent>{
protected final Set<TopicPartitionInfo> currentPartitions = ConcurrentHashMap.newKeySet(); protected final Set<TopicPartitionInfo> currentPartitions = ConcurrentHashMap.newKeySet();
@ -97,8 +98,7 @@ public abstract class AbstractSubscriptionService implements ApplicationListener
} }
@Override @Override
@EventListener(PartitionChangeEvent.class) protected void onTbApplicationEvent(PartitionChangeEvent partitionChangeEvent) {
public void onApplicationEvent(PartitionChangeEvent partitionChangeEvent) {
if (ServiceType.TB_CORE.equals(partitionChangeEvent.getServiceType())) { if (ServiceType.TB_CORE.equals(partitionChangeEvent.getServiceType())) {
currentPartitions.clear(); currentPartitions.clear();
currentPartitions.addAll(partitionChangeEvent.getPartitions()); currentPartitions.addAll(partitionChangeEvent.getPartitions());

2
application/src/main/java/org/thingsboard/server/service/transport/msg/TransportToDeviceActorMsgWrapper.java

@ -34,6 +34,8 @@ import java.util.UUID;
@Data @Data
public class TransportToDeviceActorMsgWrapper implements TbActorMsg, DeviceAwareMsg, TenantAwareMsg, Serializable { public class TransportToDeviceActorMsgWrapper implements TbActorMsg, DeviceAwareMsg, TenantAwareMsg, Serializable {
private static final long serialVersionUID = 7191333353202935941L;
private final TenantId tenantId; private final TenantId tenantId;
private final DeviceId deviceId; private final DeviceId deviceId;
private final TransportToDeviceActorMsg msg; private final TransportToDeviceActorMsg msg;

85
application/src/main/java/org/thingsboard/server/utils/EventDeduplicationExecutor.java

@ -0,0 +1,85 @@
/**
* Copyright © 2016-2021 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.utils;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.function.Consumer;
/**
* This class deduplicate executions of the specified function.
* Useful in cluster mode, when you get event about partition change multiple times.
* Assuming that the function execution is expensive, we should execute it immediately when first time event occurs and
* later, once the processing of first event is done, process last pending task.
*
* @param <P> parameters of the function
*/
@Slf4j
public class EventDeduplicationExecutor<P> {
private final String name;
private final ExecutorService executor;
private final Consumer<P> function;
private P pendingTask;
private boolean busy;
public EventDeduplicationExecutor(String name, ExecutorService executor, Consumer<P> function) {
this.name = name;
this.executor = executor;
this.function = function;
}
public void submit(P params) {
log.info("[{}] Going to submit: {}", name, params);
synchronized (EventDeduplicationExecutor.this) {
if (!busy) {
busy = true;
pendingTask = null;
try {
log.info("[{}] Submitting task: {}", name, params);
executor.submit(() -> {
try {
log.info("[{}] Executing task: {}", name, params);
function.accept(params);
} catch (Throwable e) {
log.warn("[{}] Failed to process task with parameters: {}", name, params, e);
throw e;
} finally {
unlockAndProcessIfAny();
}
});
} catch (Throwable e) {
log.warn("[{}] Failed to submit task with parameters: {}", name, params, e);
unlockAndProcessIfAny();
throw e;
}
} else {
log.info("[{}] Task is already in progress. {} pending task: {}", name, pendingTask == null ? "adding" : "updating", params);
pendingTask = params;
}
}
}
private void unlockAndProcessIfAny() {
synchronized (EventDeduplicationExecutor.this) {
busy = false;
if (pendingTask != null) {
submit(pendingTask);
}
}
}
}

1
application/src/main/java/org/thingsboard/server/utils/MiscUtils.java

@ -33,6 +33,7 @@ public class MiscUtils {
return "The " + propertyName + " property need to be set!"; return "The " + propertyName + " property need to be set!";
} }
@SuppressWarnings("deprecation")
public static HashFunction forName(String name) { public static HashFunction forName(String name) {
switch (name) { switch (name) {
case "murmur3_32": case "murmur3_32":

5
application/src/main/resources/logback.xml

@ -34,6 +34,9 @@
<!-- <logger name="org.thingsboard.server.service.subscription" level="TRACE"/>--> <!-- <logger name="org.thingsboard.server.service.subscription" level="TRACE"/>-->
<!-- <logger name="org.thingsboard.server.service.telemetry" level="TRACE"/>--> <!-- <logger name="org.thingsboard.server.service.telemetry" level="TRACE"/>-->
<!-- <logger name="org.eclipse.californium.scandium.DTLSConnector" level="TRACE" />-->
<!-- <logger name="org.eclipse.californium.scandium.dtls.Handshaker" level="DEBUG" />-->
<logger name="com.microsoft.azure.servicebus.primitives.CoreMessageReceiver" level="OFF" /> <logger name="com.microsoft.azure.servicebus.primitives.CoreMessageReceiver" level="OFF" />
<logger name="org.thingsboard.server.service.edge" level="INFO" /> <logger name="org.thingsboard.server.service.edge" level="INFO" />
@ -43,4 +46,4 @@
</root> </root>
</configuration> </configuration>

73
application/src/main/resources/thingsboard.yml

@ -118,6 +118,15 @@ security:
githubMapper: githubMapper:
emailUrl: "${SECURITY_OAUTH2_GITHUB_MAPPER_EMAIL_URL_KEY:https://api.github.com/user/emails}" emailUrl: "${SECURITY_OAUTH2_GITHUB_MAPPER_EMAIL_URL_KEY:https://api.github.com/user/emails}"
# Usage statistics parameters
usage:
stats:
report:
enabled: "${USAGE_STATS_REPORT_ENABLED:true}"
interval: "${USAGE_STATS_REPORT_INTERVAL:10}"
check:
cycle: "${USAGE_STATS_CHECK_CYCLE:60000}"
# Dashboard parameters # Dashboard parameters
dashboard: dashboard:
# Maximum allowed datapoints fetched by widgets # Maximum allowed datapoints fetched by widgets
@ -585,14 +594,14 @@ transport:
# model_path_file: "${LWM2M_MODEL_PATH_FILE:./common/transport/lwm2m/src/main/resources/models/}" # model_path_file: "${LWM2M_MODEL_PATH_FILE:./common/transport/lwm2m/src/main/resources/models/}"
model_path_file: "${LWM2M_MODEL_PATH_FILE:}" model_path_file: "${LWM2M_MODEL_PATH_FILE:}"
recommended_ciphers: "${LWM2M_RECOMMENDED_CIPHERS:false}" recommended_ciphers: "${LWM2M_RECOMMENDED_CIPHERS:false}"
recommended_supported_groups: "${LWM2M_RECOMMENDED_SUPPORTED_GROUPS:false}" recommended_supported_groups: "${LWM2M_RECOMMENDED_SUPPORTED_GROUPS:true}"
request_pool_size: "${LWM2M_REQUEST_POOL_SIZE:100}" request_pool_size: "${LWM2M_REQUEST_POOL_SIZE:100}"
request_error_pool_size: "${LWM2M_REQUEST_ERROR_POOL_SIZE:10}" request_error_pool_size: "${LWM2M_REQUEST_ERROR_POOL_SIZE:10}"
registered_pool_size: "${LWM2M_REGISTERED_POOL_SIZE:10}" registered_pool_size: "${LWM2M_REGISTERED_POOL_SIZE:10}"
update_registered_pool_size: "${LWM2M_UPDATE_REGISTERED_POOL_SIZE:10}" update_registered_pool_size: "${LWM2M_UPDATE_REGISTERED_POOL_SIZE:10}"
un_registered_pool_size: "${LWM2M_UN_REGISTERED_POOL_SIZE:10}" un_registered_pool_size: "${LWM2M_UN_REGISTERED_POOL_SIZE:10}"
secure: secure:
# Only Certificate_x509: # Certificate_x509:
# To get helps about files format and how to generate it, see: https://github.com/eclipse/leshan/wiki/Credential-files-format # To get helps about files format and how to generate it, see: https://github.com/eclipse/leshan/wiki/Credential-files-format
# Create new X509 Certificates: common/transport/lwm2m/src/main/resources/credentials/shell/lwM2M_credentials.sh # Create new X509 Certificates: common/transport/lwm2m/src/main/resources/credentials/shell/lwM2M_credentials.sh
key_store_type: "${LWM2M_KEYSTORE_TYPE:JKS}" key_store_type: "${LWM2M_KEYSTORE_TYPE:JKS}"
@ -601,48 +610,38 @@ transport:
key_store_path_file: "${KEY_STORE_PATH_FILE:}" key_store_path_file: "${KEY_STORE_PATH_FILE:}"
key_store_password: "${LWM2M_KEYSTORE_PASSWORD_SERVER:server_ks_password}" key_store_password: "${LWM2M_KEYSTORE_PASSWORD_SERVER:server_ks_password}"
root_alias: "${LWM2M_SERVER_ROOT_CA:rootca}" root_alias: "${LWM2M_SERVER_ROOT_CA:rootca}"
enable_gen_psk_rpk: "${ENABLE_GEN_PSK_RPK:true}" enable_gen_new_key_psk_rpk: "${ENABLE_GEN_NEW_KEY_PSK_RPK:false}"
server: server:
id: "${LWM2M_SERVER_ID:123}" id: "${LWM2M_SERVER_ID:123}"
bind_address: "${LWM2M_BIND_ADDRESS:0.0.0.0}" bind_address: "${LWM2M_BIND_ADDRESS:0.0.0.0}"
bind_port_no_sec_psk: "${LWM2M_BIND_PORT_NO_SEC_PSK:5685}" bind_port_no_sec: "${LWM2M_BIND_PORT_NO_SEC:5685}"
bind_port_no_sec_rpk: "${LWM2M_BIND_PORT_NO_SEC_RPK:5687}"
bind_port_no_sec_x509: "${LWM2M_BIND_PORT_NO_SEC_X509:5689}"
secure: secure:
bind_address: "${LWM2M_BIND_ADDRESS:0.0.0.0}" bind_address_security: "${LWM2M_BIND_ADDRESS_SECURITY:0.0.0.0}"
start_psk: "${START_SERVER_PSK:true}" bind_port_security: "${LWM2M_BIND_PORT_SECURITY:5686}"
start_rpk: "${START_SERVER_RPK:true}" # create_rpk: "${CREATE_RPK:}"
start_x509: "${START_SERVER_X509:true}" # Only for RPK: Public & Private Key. If the keystore file is missing or not working
bind_port_psk: "${LWM2M_BIND_PORT_SEC_PSK:5686}" # - Public Key (Hex): [3059301306072a8648ce3d020106082a8648ce3d0301070342000405064b9e6762dd8d8b8a52355d7b4d8b9a3d64e6d2ee277d76c248861353f3585eeb1838e4f9e37b31fa347aef5ce3431eb54e0a2506910c5e0298817445721b]
bind_port_rpk: "${LWM2M_BIND_PORT_SEC_RPK:5688}" # - Private Key (Hex): [308193020100301306072a8648ce3d020106082a8648ce3d030107047930770201010420dc774b309e547ceb48fee547e104ce201a9c48c449dc5414cd04e7f5cf05f67ba00a06082a8648ce3d030107a1440342000405064b9e6762dd8d8b8a52355d7b4d8b9a3d64e6d2ee277d76c248861353f3585eeb1838e4f9e37b31fa347aef5ce3431eb54e0a2506910c5e0298817445721b],
bind_port_x509: "${LWM2M_BIND_PORT_SEC_X509:5690}" # - Elliptic Curve parameters : [secp256r1 [NIST P-256, X9.62 prime256v1] (1.2.840.10045.3.1.7)]
# Only RPK: Public & Private Key public_x: "${LWM2M_SERVER_PUBLIC_X:05064b9e6762dd8d8b8a52355d7b4d8b9a3d64e6d2ee277d76c248861353f358}"
# create_rpk: "${CREATE_RPK:}" public_y: "${LWM2M_SERVER_PUBLIC_Y:5eeb1838e4f9e37b31fa347aef5ce3431eb54e0a2506910c5e0298817445721b}"
public_x: "${LWM2M_SERVER_PUBLIC_X:405354ea8893471d9296afbc8b020a5c6201b0bb25812a53b849d4480fa5f069}" private_encoded: "${LWM2M_SERVER_PRIVATE_ENCODED:308193020100301306072a8648ce3d020106082a8648ce3d030107047930770201010420dc774b309e547ceb48fee547e104ce201a9c48c449dc5414cd04e7f5cf05f67ba00a06082a8648ce3d030107a1440342000405064b9e6762dd8d8b8a52355d7b4d8b9a3d64e6d2ee277d76c248861353f3585eeb1838e4f9e37b31fa347aef5ce3431eb54e0a2506910c5e0298817445721b}" # Only Certificate_x509:
public_y: "${LWM2M_SERVER_PUBLIC_Y:30c9237e946a3a1692c1cafaa01a238a077f632c99371348337512363f28212b}"
private_s: "${LWM2M_SERVER_PRIVATE_S:274671fe40ce937b8a6352cf0a418e8a39e4bf0bb9bf74c910db953c20c73802}"
# Only Certificate_x509:
alias: "${LWM2M_KEYSTORE_ALIAS_SERVER:server}" alias: "${LWM2M_KEYSTORE_ALIAS_SERVER:server}"
bootstrap: bootstrap:
enable: "${BOOTSTRAP:true}" enable: "${LWM2M_BOOTSTRAP_ENABLED:true}"
id: "${LWM2M_SERVER_ID:111}" id: "${LWM2M_SERVER_ID:111}"
bind_address: "${LWM2M_BIND_ADDRESS_BS:0.0.0.0}" bind_address: "${LWM2M_BIND_ADDRESS_BS:0.0.0.0}"
bind_port_no_sec_psk: "${LWM2M_BIND_PORT_NO_SEC_BS:5691}" bind_port_no_sec: "${LWM2M_BIND_PORT_NO_SEC_BS:5687}"
bind_port_no_sec_rpk: "${LWM2M_BIND_PORT_NO_SEC_BS:5693}"
bind_port_no_sec_x509: "${LWM2M_BIND_PORT_NO_SEC_BS:5695}"
secure: secure:
bind_address: "${LWM2M_BIND_ADDRESS_BS:0.0.0.0}" bind_address_security: "${LWM2M_BIND_ADDRESS_BS:0.0.0.0}"
start_psk: "${START_SERVER_PSK_BS:true}" bind_port_security: "${LWM2M_BIND_PORT_SEC_BS:5688}"
start_rpk: "${START_SERVER_RPK_BS:true}" # Only for RPK: Public & Private Key. If the keystore file is missing or not working
start_x509: "${START_SERVER_X509_BS:true}" # - Elliptic Curve parameters : [secp256r1 [NIST P-256, X9.62 prime256v1] (1.2.840.10045.3.1.7)]
bind_port_psk: "${LWM2M_BIND_PORT_SEC_PSK_BS:5692}" # - Public Key (Hex): [3059301306072a8648ce3d020106082a8648ce3d030107034200045017c87a1c1768264656b3b355434b0def6edb8b9bf166a4762d9930cd730f913fc4e61bcd8901ec27c424114c3e887ed372497f0c2cf85839b8443e76988b34]
bind_port_rpk: "${LWM2M_BIND_PORT_SER_RPK_BS:5694}" # - Private Key (Hex): [308193020100301306072a8648ce3d020106082a8648ce3d0301070479307702010104205ecafd90caa7be45c42e1f3f32571632b8409e6e6249d7124f4ba56fab3c8083a00a06082a8648ce3d030107a144034200045017c87a1c1768264656b3b355434b0def6edb8b9bf166a4762d9930cd730f913fc4e61bcd8901ec27c424114c3e887ed372497f0c2cf85839b8443e76988b34],
bind_port_x509: "${LWM2M_BIND_PORT_SEC_X509_BS:5696}" public_x: "${LWM2M_SERVER_PUBLIC_X_BS:5017c87a1c1768264656b3b355434b0def6edb8b9bf166a4762d9930cd730f91}"
# Only RPK: Public & Private Key public_y: "${LWM2M_SERVER_PUBLIC_Y_BS:3fc4e61bcd8901ec27c424114c3e887ed372497f0c2cf85839b8443e76988b34}"
public_x: "${LWM2M_SERVER_PUBLIC_X_BS:993ef2b698c6a9c0c1d8be78b13a9383c0854c7c7c7a504d289b403794648183}" private_encoded: "${LWM2M_SERVER_PRIVATE_ENCODED_BS:308193020100301306072a8648ce3d020106082a8648ce3d0301070479307702010104205ecafd90caa7be45c42e1f3f32571632b8409e6e6249d7124f4ba56fab3c8083a00a06082a8648ce3d030107a144034200045017c87a1c1768264656b3b355434b0def6edb8b9bf166a4762d9930cd730f913fc4e61bcd8901ec27c424114c3e887ed372497f0c2cf85839b8443e76988b34}" # Only Certificate_x509:
public_y: "${LWM2M_SERVER_PUBLIC_Y_BS:267412d5fc4e5ceb2257cb7fd7f76ebdac2fa9aa100afb162e990074cc0bfaa2}"
private_s: "${LWM2M_SERVER_PRIVATE_S_BS:9dbdbb073fc63570693a9aaf1013414e261c571f27e27fc6a8c1c2ad9347875a}"
# Only Certificate_x509:
alias: "${LWM2M_KEYSTORE_ALIAS_BOOTSTRAP:bootstrap}" alias: "${LWM2M_KEYSTORE_ALIAS_BOOTSTRAP:bootstrap}"
# Redis # Redis
redis_url: "${LWM2M_REDIS_URL:''}" redis_url: "${LWM2M_REDIS_URL:''}"
@ -713,6 +712,10 @@ queue:
transport-api: "${TB_QUEUE_KAFKA_TA_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1}" transport-api: "${TB_QUEUE_KAFKA_TA_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1}"
notifications: "${TB_QUEUE_KAFKA_NOTIFICATIONS_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1}" notifications: "${TB_QUEUE_KAFKA_NOTIFICATIONS_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1}"
js-executor: "${TB_QUEUE_KAFKA_JE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:104857600;partitions:100}" js-executor: "${TB_QUEUE_KAFKA_JE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:104857600;partitions:100}"
consumer-stats:
enabled: "${TB_QUEUE_KAFKA_CONSUMER_STATS_ENABLED:true}"
print-interval-ms: "${TB_QUEUE_KAFKA_CONSUMER_STATS_MIN_PRINT_INTERVAL_MS:60000}"
kafka-response-timeout-ms: "${TB_QUEUE_KAFKA_CONSUMER_STATS_RESPONSE_TIMEOUT_MS:1000}"
aws_sqs: aws_sqs:
use_default_credential_provider_chain: "${TB_QUEUE_AWS_SQS_USE_DEFAULT_CREDENTIAL_PROVIDER_CHAIN:false}" use_default_credential_provider_chain: "${TB_QUEUE_AWS_SQS_USE_DEFAULT_CREDENTIAL_PROVIDER_CHAIN:false}"
access_key_id: "${TB_QUEUE_AWS_SQS_ACCESS_KEY_ID:YOUR_KEY}" access_key_id: "${TB_QUEUE_AWS_SQS_ACCESS_KEY_ID:YOUR_KEY}"

4
application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java

@ -377,6 +377,10 @@ public abstract class AbstractWebTest {
return readResponse(doGetAsync(urlTemplate, urlVariables).andExpect(status().isOk()), responseClass); return readResponse(doGetAsync(urlTemplate, urlVariables).andExpect(status().isOk()), responseClass);
} }
protected <T> T doGetAsyncTyped(String urlTemplate, TypeReference<T> responseType, Object... urlVariables) throws Exception {
return readResponse(doGetAsync(urlTemplate, urlVariables).andExpect(status().isOk()), responseType);
}
protected ResultActions doGetAsync(String urlTemplate, Object... urlVariables) throws Exception { protected ResultActions doGetAsync(String urlTemplate, Object... urlVariables) throws Exception {
MockHttpServletRequestBuilder getRequest; MockHttpServletRequestBuilder getRequest;
getRequest = get(urlTemplate, urlVariables); getRequest = get(urlTemplate, urlVariables);

20
application/src/test/java/org/thingsboard/server/controller/BaseEntityViewControllerTest.java

@ -352,8 +352,8 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
Thread.sleep(1000); Thread.sleep(1000);
List<Map<String, Object>> values = doGetAsync("/api/plugins/telemetry/ENTITY_VIEW/" + savedView.getId().getId().toString() + List<Map<String, Object>> values = doGetAsyncTyped("/api/plugins/telemetry/ENTITY_VIEW/" + savedView.getId().getId().toString() +
"/values/attributes?keys=" + String.join(",", actualAttributesSet), List.class); "/values/attributes?keys=" + String.join(",", actualAttributesSet), new TypeReference<>() {});
assertEquals("value1", getValue(values, "caKey1")); assertEquals("value1", getValue(values, "caKey1"));
assertEquals(true, getValue(values, "caKey2")); assertEquals(true, getValue(values, "caKey2"));
@ -369,8 +369,8 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
Set<String> expectedActualAttributesSet = new HashSet<>(Arrays.asList("caKey1", "caKey2", "caKey3", "caKey4")); Set<String> expectedActualAttributesSet = new HashSet<>(Arrays.asList("caKey1", "caKey2", "caKey3", "caKey4"));
assertTrue(actualAttributesSet.containsAll(expectedActualAttributesSet)); assertTrue(actualAttributesSet.containsAll(expectedActualAttributesSet));
List<Map<String, Object>> valueTelemetryOfDevices = doGetAsync("/api/plugins/telemetry/DEVICE/" + testDevice.getId().getId().toString() + List<Map<String, Object>> valueTelemetryOfDevices = doGetAsyncTyped("/api/plugins/telemetry/DEVICE/" + testDevice.getId().getId().toString() +
"/values/attributes?keys=" + String.join(",", actualAttributesSet), List.class); "/values/attributes?keys=" + String.join(",", actualAttributesSet), new TypeReference<>() {});
EntityView view = new EntityView(); EntityView view = new EntityView();
view.setEntityId(testDevice.getId()); view.setEntityId(testDevice.getId());
@ -384,8 +384,8 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
Thread.sleep(1000); Thread.sleep(1000);
List<Map<String, Object>> values = doGetAsync("/api/plugins/telemetry/ENTITY_VIEW/" + savedView.getId().getId().toString() + List<Map<String, Object>> values = doGetAsyncTyped("/api/plugins/telemetry/ENTITY_VIEW/" + savedView.getId().getId().toString() +
"/values/attributes?keys=" + String.join(",", actualAttributesSet), List.class); "/values/attributes?keys=" + String.join(",", actualAttributesSet), new TypeReference<>() {});
assertEquals(0, values.size()); assertEquals(0, values.size());
} }
@ -454,12 +454,12 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
} }
private Set<String> getTelemetryKeys(String type, String id) throws Exception { private Set<String> getTelemetryKeys(String type, String id) throws Exception {
return new HashSet<>(doGetAsync("/api/plugins/telemetry/" + type + "/" + id + "/keys/timeseries", List.class)); return new HashSet<>(doGetAsyncTyped("/api/plugins/telemetry/" + type + "/" + id + "/keys/timeseries", new TypeReference<>() {}));
} }
private Map<String, List<Map<String, String>>> getTelemetryValues(String type, String id, Set<String> keys, Long startTs, Long endTs) throws Exception { private Map<String, List<Map<String, String>>> getTelemetryValues(String type, String id, Set<String> keys, Long startTs, Long endTs) throws Exception {
return doGetAsync("/api/plugins/telemetry/" + type + "/" + id + return doGetAsyncTyped("/api/plugins/telemetry/" + type + "/" + id +
"/values/timeseries?keys=" + String.join(",", keys) + "&startTs=" + startTs + "&endTs=" + endTs, Map.class); "/values/timeseries?keys=" + String.join(",", keys) + "&startTs=" + startTs + "&endTs=" + endTs, new TypeReference<>() {});
} }
private Set<String> getAttributesByKeys(String stringKV) throws Exception { private Set<String> getAttributesByKeys(String stringKV) throws Exception {
@ -484,7 +484,7 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
client.publish("v1/devices/me/attributes", message); client.publish("v1/devices/me/attributes", message);
Thread.sleep(1000); Thread.sleep(1000);
client.disconnect(); client.disconnect();
return new HashSet<>(doGetAsync("/api/plugins/telemetry/DEVICE/" + viewDeviceId + "/keys/attributes", List.class)); return new HashSet<>(doGetAsyncTyped("/api/plugins/telemetry/DEVICE/" + viewDeviceId + "/keys/attributes", new TypeReference<>() {}));
} }
private Object getValue(List<Map<String, Object>> values, String stringValue) { private Object getValue(List<Map<String, Object>> values, String stringValue) {

6
application/src/test/java/org/thingsboard/server/edge/BaseEdgeTest.java

@ -26,7 +26,7 @@ import com.google.protobuf.AbstractMessage;
import com.google.protobuf.InvalidProtocolBufferException; import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.MessageLite; import com.google.protobuf.MessageLite;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.RandomStringUtils; import org.apache.commons.lang3.RandomStringUtils;
import org.junit.After; import org.junit.After;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
@ -1216,12 +1216,12 @@ abstract public class BaseEdgeTest extends AbstractControllerTest {
// Wait before device attributes saved to database before requesting them from controller // Wait before device attributes saved to database before requesting them from controller
Thread.sleep(1000); Thread.sleep(1000);
Map<String, List<Map<String, String>>> timeseries = doGetAsync("/api/plugins/telemetry/DEVICE/" + device.getUuidId() + "/values/timeseries?keys=" + timeseriesKey, Map.class); Map<String, List<Map<String, String>>> timeseries = doGetAsyncTyped("/api/plugins/telemetry/DEVICE/" + device.getUuidId() + "/values/timeseries?keys=" + timeseriesKey, new TypeReference<>() {});
Assert.assertTrue(timeseries.containsKey(timeseriesKey)); Assert.assertTrue(timeseries.containsKey(timeseriesKey));
Assert.assertEquals(1, timeseries.get(timeseriesKey).size()); Assert.assertEquals(1, timeseries.get(timeseriesKey).size());
Assert.assertEquals(timeseriesValue, timeseries.get(timeseriesKey).get(0).get("value")); Assert.assertEquals(timeseriesValue, timeseries.get(timeseriesKey).get(0).get("value"));
List<Map<String, String>> attributes = doGetAsync("/api/plugins/telemetry/DEVICE/" + device.getId() + "/values/attributes/" + DataConstants.SERVER_SCOPE, List.class); List<Map<String, String>> attributes = doGetAsyncTyped("/api/plugins/telemetry/DEVICE/" + device.getId() + "/values/attributes/" + DataConstants.SERVER_SCOPE, new TypeReference<>() {});
Assert.assertEquals(1, attributes.size()); Assert.assertEquals(1, attributes.size());
Assert.assertEquals(attributes.get(0).get("key"), attributesKey); Assert.assertEquals(attributes.get(0).get("key"), attributesKey);
Assert.assertEquals(attributes.get(0).get("value"), attributesValue); Assert.assertEquals(attributes.get(0).get("value"), attributesValue);

14
application/src/test/java/org/thingsboard/server/mqtt/telemetry/attributes/AbstractMqttAttributesIntegrationTest.java

@ -16,6 +16,7 @@
package org.thingsboard.server.mqtt.telemetry.attributes; package org.thingsboard.server.mqtt.telemetry.attributes;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.eclipse.paho.client.mqttv3.MqttAsyncClient; import org.eclipse.paho.client.mqttv3.MqttAsyncClient;
import org.junit.After; import org.junit.After;
@ -80,7 +81,7 @@ public abstract class AbstractMqttAttributesIntegrationTest extends AbstractMqtt
List<String> actualKeys = null; List<String> actualKeys = null;
while (start <= end) { while (start <= end) {
actualKeys = doGetAsync("/api/plugins/telemetry/DEVICE/" + deviceId + "/keys/attributes/CLIENT_SCOPE", List.class); actualKeys = doGetAsyncTyped("/api/plugins/telemetry/DEVICE/" + deviceId + "/keys/attributes/CLIENT_SCOPE", new TypeReference<>() {});
if (actualKeys.size() == expectedKeys.size()) { if (actualKeys.size() == expectedKeys.size()) {
break; break;
} }
@ -96,7 +97,7 @@ public abstract class AbstractMqttAttributesIntegrationTest extends AbstractMqtt
assertEquals(expectedKeySet, actualKeySet); assertEquals(expectedKeySet, actualKeySet);
String getAttributesValuesUrl = getAttributesValuesUrl(deviceId, actualKeySet); String getAttributesValuesUrl = getAttributesValuesUrl(deviceId, actualKeySet);
List<Map<String, Object>> values = doGetAsync(getAttributesValuesUrl, List.class); List<Map<String, Object>> values = doGetAsyncTyped(getAttributesValuesUrl, new TypeReference<>() {});
assertAttributesValues(values, expectedKeySet); assertAttributesValues(values, expectedKeySet);
String deleteAttributesUrl = "/api/plugins/telemetry/DEVICE/" + deviceId + "/CLIENT_SCOPE?keys=" + String.join(",", actualKeySet); String deleteAttributesUrl = "/api/plugins/telemetry/DEVICE/" + deviceId + "/CLIENT_SCOPE?keys=" + String.join(",", actualKeySet);
doDelete(deleteAttributesUrl); doDelete(deleteAttributesUrl);
@ -121,10 +122,10 @@ public abstract class AbstractMqttAttributesIntegrationTest extends AbstractMqtt
Thread.sleep(2000); Thread.sleep(2000);
List<String> firstDeviceActualKeys = doGetAsync("/api/plugins/telemetry/DEVICE/" + firstDevice.getId() + "/keys/attributes/CLIENT_SCOPE", List.class); List<String> firstDeviceActualKeys = doGetAsyncTyped("/api/plugins/telemetry/DEVICE/" + firstDevice.getId() + "/keys/attributes/CLIENT_SCOPE", new TypeReference<>() {});
Set<String> firstDeviceActualKeySet = new HashSet<>(firstDeviceActualKeys); Set<String> firstDeviceActualKeySet = new HashSet<>(firstDeviceActualKeys);
List<String> secondDeviceActualKeys = doGetAsync("/api/plugins/telemetry/DEVICE/" + secondDevice.getId() + "/keys/attributes/CLIENT_SCOPE", List.class); List<String> secondDeviceActualKeys = doGetAsyncTyped("/api/plugins/telemetry/DEVICE/" + secondDevice.getId() + "/keys/attributes/CLIENT_SCOPE", new TypeReference<>() {});
Set<String> secondDeviceActualKeySet = new HashSet<>(secondDeviceActualKeys); Set<String> secondDeviceActualKeySet = new HashSet<>(secondDeviceActualKeys);
Set<String> expectedKeySet = new HashSet<>(expectedKeys); Set<String> expectedKeySet = new HashSet<>(expectedKeys);
@ -135,14 +136,15 @@ public abstract class AbstractMqttAttributesIntegrationTest extends AbstractMqtt
String getAttributesValuesUrlFirstDevice = getAttributesValuesUrl(firstDevice.getId(), firstDeviceActualKeySet); String getAttributesValuesUrlFirstDevice = getAttributesValuesUrl(firstDevice.getId(), firstDeviceActualKeySet);
String getAttributesValuesUrlSecondDevice = getAttributesValuesUrl(firstDevice.getId(), secondDeviceActualKeySet); String getAttributesValuesUrlSecondDevice = getAttributesValuesUrl(firstDevice.getId(), secondDeviceActualKeySet);
List<Map<String, Object>> firstDeviceValues = doGetAsync(getAttributesValuesUrlFirstDevice, List.class); List<Map<String, Object>> firstDeviceValues = doGetAsyncTyped(getAttributesValuesUrlFirstDevice, new TypeReference<>() {});
List<Map<String, Object>> secondDeviceValues = doGetAsync(getAttributesValuesUrlSecondDevice, List.class); List<Map<String, Object>> secondDeviceValues = doGetAsyncTyped(getAttributesValuesUrlSecondDevice, new TypeReference<>() {});
assertAttributesValues(firstDeviceValues, expectedKeySet); assertAttributesValues(firstDeviceValues, expectedKeySet);
assertAttributesValues(secondDeviceValues, expectedKeySet); assertAttributesValues(secondDeviceValues, expectedKeySet);
} }
@SuppressWarnings("unchecked")
protected void assertAttributesValues(List<Map<String, Object>> deviceValues, Set<String> expectedKeySet) throws JsonProcessingException { protected void assertAttributesValues(List<Map<String, Object>> deviceValues, Set<String> expectedKeySet) throws JsonProcessingException {
for (Map<String, Object> map : deviceValues) { for (Map<String, Object> map : deviceValues) {
String key = (String) map.get("key"); String key = (String) map.get("key");

30
application/src/test/java/org/thingsboard/server/mqtt/telemetry/timeseries/AbstractMqttTimeseriesIntegrationTest.java

@ -15,6 +15,7 @@
*/ */
package org.thingsboard.server.mqtt.telemetry.timeseries; package org.thingsboard.server.mqtt.telemetry.timeseries;
import com.fasterxml.jackson.core.type.TypeReference;
import io.netty.handler.codec.mqtt.MqttQoS; import io.netty.handler.codec.mqtt.MqttQoS;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken; import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
@ -25,6 +26,7 @@ import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence; import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.device.profile.MqttTopics; import org.thingsboard.server.common.data.device.profile.MqttTopics;
@ -107,7 +109,7 @@ public abstract class AbstractMqttTimeseriesIntegrationTest extends AbstractMqtt
List<String> actualKeys = null; List<String> actualKeys = null;
while (start <= end) { while (start <= end) {
actualKeys = doGetAsync("/api/plugins/telemetry/DEVICE/" + deviceId + "/keys/timeseries", List.class); actualKeys = doGetAsyncTyped("/api/plugins/telemetry/DEVICE/" + deviceId + "/keys/timeseries", new TypeReference<>() {});
if (actualKeys.size() == expectedKeys.size()) { if (actualKeys.size() == expectedKeys.size()) {
break; break;
} }
@ -129,13 +131,13 @@ public abstract class AbstractMqttTimeseriesIntegrationTest extends AbstractMqtt
} }
start = System.currentTimeMillis(); start = System.currentTimeMillis();
end = System.currentTimeMillis() + 5000; end = System.currentTimeMillis() + 5000;
Map<String, List<Map<String, String>>> values = null; Map<String, List<Map<String, Object>>> values = null;
while (start <= end) { while (start <= end) {
values = doGetAsync(getTelemetryValuesUrl, Map.class); values = doGetAsyncTyped(getTelemetryValuesUrl, new TypeReference<>() {});
boolean valid = values.size() == expectedKeys.size(); boolean valid = values.size() == expectedKeys.size();
if (valid) { if (valid) {
for (String key : expectedKeys) { for (String key : expectedKeys) {
List<Map<String, String>> tsValues = values.get(key); List<Map<String, Object>> tsValues = values.get(key);
if (tsValues != null && tsValues.size() > 0) { if (tsValues != null && tsValues.size() > 0) {
Object ts = tsValues.get(0).get("ts"); Object ts = tsValues.get(0).get("ts");
if (ts == null) { if (ts == null) {
@ -181,10 +183,10 @@ public abstract class AbstractMqttTimeseriesIntegrationTest extends AbstractMqtt
Thread.sleep(2000); Thread.sleep(2000);
List<String> firstDeviceActualKeys = doGetAsync("/api/plugins/telemetry/DEVICE/" + firstDevice.getId() + "/keys/timeseries", List.class); List<String> firstDeviceActualKeys = doGetAsyncTyped("/api/plugins/telemetry/DEVICE/" + firstDevice.getId() + "/keys/timeseries", new TypeReference<>() {});
Set<String> firstDeviceActualKeySet = new HashSet<>(firstDeviceActualKeys); Set<String> firstDeviceActualKeySet = new HashSet<>(firstDeviceActualKeys);
List<String> secondDeviceActualKeys = doGetAsync("/api/plugins/telemetry/DEVICE/" + secondDevice.getId() + "/keys/timeseries", List.class); List<String> secondDeviceActualKeys = doGetAsyncTyped("/api/plugins/telemetry/DEVICE/" + secondDevice.getId() + "/keys/timeseries", new TypeReference<>() {});
Set<String> secondDeviceActualKeySet = new HashSet<>(secondDeviceActualKeys); Set<String> secondDeviceActualKeySet = new HashSet<>(secondDeviceActualKeys);
Set<String> expectedKeySet = new HashSet<>(expectedKeys); Set<String> expectedKeySet = new HashSet<>(expectedKeys);
@ -195,8 +197,8 @@ public abstract class AbstractMqttTimeseriesIntegrationTest extends AbstractMqtt
String getTelemetryValuesUrlFirstDevice = getTelemetryValuesUrl(firstDevice.getId(), firstDeviceActualKeySet); String getTelemetryValuesUrlFirstDevice = getTelemetryValuesUrl(firstDevice.getId(), firstDeviceActualKeySet);
String getTelemetryValuesUrlSecondDevice = getTelemetryValuesUrl(firstDevice.getId(), secondDeviceActualKeySet); String getTelemetryValuesUrlSecondDevice = getTelemetryValuesUrl(firstDevice.getId(), secondDeviceActualKeySet);
Map<String, List<Map<String, String>>> firstDeviceValues = doGetAsync(getTelemetryValuesUrlFirstDevice, Map.class); Map<String, List<Map<String, Object>>> firstDeviceValues = doGetAsyncTyped(getTelemetryValuesUrlFirstDevice, new TypeReference<>() {});
Map<String, List<Map<String, String>>> secondDeviceValues = doGetAsync(getTelemetryValuesUrlSecondDevice, Map.class); Map<String, List<Map<String, Object>>> secondDeviceValues = doGetAsyncTyped(getTelemetryValuesUrlSecondDevice, new TypeReference<>() {});
assertGatewayDeviceData(firstDeviceValues, expectedKeys); assertGatewayDeviceData(firstDeviceValues, expectedKeys);
assertGatewayDeviceData(secondDeviceValues, expectedKeys); assertGatewayDeviceData(secondDeviceValues, expectedKeys);
@ -212,7 +214,7 @@ public abstract class AbstractMqttTimeseriesIntegrationTest extends AbstractMqtt
return "/api/plugins/telemetry/DEVICE/" + deviceId + "/values/timeseries?startTs=0&endTs=25000&keys=" + String.join(",", actualKeySet); return "/api/plugins/telemetry/DEVICE/" + deviceId + "/values/timeseries?startTs=0&endTs=25000&keys=" + String.join(",", actualKeySet);
} }
private void assertGatewayDeviceData(Map<String, List<Map<String, String>>> deviceValues, List<String> expectedKeys) { private void assertGatewayDeviceData(Map<String, List<Map<String, Object>>> deviceValues, List<String> expectedKeys) {
assertEquals(2, deviceValues.get(expectedKeys.get(0)).size()); assertEquals(2, deviceValues.get(expectedKeys.get(0)).size());
assertEquals(2, deviceValues.get(expectedKeys.get(1)).size()); assertEquals(2, deviceValues.get(expectedKeys.get(1)).size());
@ -228,11 +230,11 @@ public abstract class AbstractMqttTimeseriesIntegrationTest extends AbstractMqtt
} }
private void assertValues(Map<String, List<Map<String, String>>> deviceValues, int arrayIndex) { private void assertValues(Map<String, List<Map<String, Object>>> deviceValues, int arrayIndex) {
for (Map.Entry<String, List<Map<String, String>>> entry : deviceValues.entrySet()) { for (Map.Entry<String, List<Map<String, Object>>> entry : deviceValues.entrySet()) {
String key = entry.getKey(); String key = entry.getKey();
List<Map<String, String>> tsKv = entry.getValue(); List<Map<String, Object>> tsKv = entry.getValue();
String value = tsKv.get(arrayIndex).get("value"); String value = (String) tsKv.get(arrayIndex).get("value");
switch (key) { switch (key) {
case "key1": case "key1":
assertEquals("value1", value); assertEquals("value1", value);
@ -253,7 +255,7 @@ public abstract class AbstractMqttTimeseriesIntegrationTest extends AbstractMqtt
} }
} }
private void assertTs(Map<String, List<Map<String, String>>> deviceValues, List<String> expectedKeys, int ts, int arrayIndex) { private void assertTs(Map<String, List<Map<String, Object>>> deviceValues, List<String> expectedKeys, int ts, int arrayIndex) {
assertEquals(ts, deviceValues.get(expectedKeys.get(0)).get(arrayIndex).get("ts")); assertEquals(ts, deviceValues.get(expectedKeys.get(0)).get(arrayIndex).get("ts"));
assertEquals(ts, deviceValues.get(expectedKeys.get(1)).get(arrayIndex).get("ts")); assertEquals(ts, deviceValues.get(expectedKeys.get(1)).get(arrayIndex).get("ts"));
assertEquals(ts, deviceValues.get(expectedKeys.get(2)).get(arrayIndex).get("ts")); assertEquals(ts, deviceValues.get(expectedKeys.get(2)).get(arrayIndex).get("ts"));

3
application/src/test/java/org/thingsboard/server/service/cluster/routing/HashPartitionServiceTest.java

@ -21,12 +21,11 @@ import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.runners.MockitoJUnitRunner; import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisher;
import org.springframework.test.util.ReflectionTestUtils; import org.springframework.test.util.ReflectionTestUtils;
import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.msg.queue.ServiceQueue;
import org.thingsboard.server.queue.discovery.HashPartitionService; import org.thingsboard.server.queue.discovery.HashPartitionService;
import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.common.msg.queue.ServiceType;
import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; import org.thingsboard.server.queue.discovery.TbServiceInfoProvider;

2
application/src/test/java/org/thingsboard/server/service/queue/TbMsgPackProcessingContextTest.java

@ -20,7 +20,7 @@ import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.Mockito; import org.mockito.Mockito;
import org.mockito.runners.MockitoJUnitRunner; import org.mockito.junit.MockitoJUnitRunner;
import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.queue.common.TbProtoQueueMsg; import org.thingsboard.server.queue.common.TbProtoQueueMsg;
import org.thingsboard.server.service.queue.processing.TbRuleEngineSubmitStrategy; import org.thingsboard.server.service.queue.processing.TbRuleEngineSubmitStrategy;

155
application/src/test/java/org/thingsboard/server/util/EventDeduplicationExecutorTest.java

@ -0,0 +1,155 @@
/**
* Copyright © 2016-2021 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.util;
import com.google.common.util.concurrent.MoreExecutors;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import org.thingsboard.server.utils.EventDeduplicationExecutor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.function.Consumer;
@Slf4j
@RunWith(MockitoJUnitRunner.class)
public class EventDeduplicationExecutorTest {
@Test
public void testSimpleFlowSameThread() throws InterruptedException {
simpleFlow(MoreExecutors.newDirectExecutorService());
}
@Test
public void testPeriodicFlowSameThread() throws InterruptedException {
periodicFlow(MoreExecutors.newDirectExecutorService());
}
@Test
public void testExceptionFlowSameThread() throws InterruptedException {
exceptionFlow(MoreExecutors.newDirectExecutorService());
}
@Test
public void testSimpleFlowSingleThread() throws InterruptedException {
simpleFlow(Executors.newSingleThreadExecutor());
}
@Test
public void testPeriodicFlowSingleThread() throws InterruptedException {
periodicFlow(Executors.newSingleThreadExecutor());
}
@Test
public void testExceptionFlowSingleThread() throws InterruptedException {
exceptionFlow(Executors.newSingleThreadExecutor());
}
@Test
public void testSimpleFlowMultiThread() throws InterruptedException {
simpleFlow(Executors.newFixedThreadPool(3));
}
@Test
public void testPeriodicFlowMultiThread() throws InterruptedException {
periodicFlow(Executors.newFixedThreadPool(3));
}
@Test
public void testExceptionFlowMultiThread() throws InterruptedException {
exceptionFlow(Executors.newFixedThreadPool(3));
}
private void simpleFlow(ExecutorService executorService) throws InterruptedException {
try {
Consumer<String> function = Mockito.spy(StringConsumer.class);
EventDeduplicationExecutor<String> executor = new EventDeduplicationExecutor<>(EventDeduplicationExecutorTest.class.getSimpleName(), executorService, function);
String params1 = "params1";
String params2 = "params2";
String params3 = "params3";
executor.submit(params1);
executor.submit(params2);
executor.submit(params3);
Thread.sleep(500);
Mockito.verify(function).accept(params1);
Mockito.verify(function).accept(params3);
} finally {
executorService.shutdownNow();
}
}
private void periodicFlow(ExecutorService executorService) throws InterruptedException {
try {
Consumer<String> function = Mockito.spy(StringConsumer.class);
EventDeduplicationExecutor<String> executor = new EventDeduplicationExecutor<>(EventDeduplicationExecutorTest.class.getSimpleName(), executorService, function);
String params1 = "params1";
String params2 = "params2";
String params3 = "params3";
executor.submit(params1);
Thread.sleep(500);
executor.submit(params2);
Thread.sleep(500);
executor.submit(params3);
Thread.sleep(500);
Mockito.verify(function).accept(params1);
Mockito.verify(function).accept(params2);
Mockito.verify(function).accept(params3);
} finally {
executorService.shutdownNow();
}
}
private void exceptionFlow(ExecutorService executorService) throws InterruptedException {
try {
Consumer<String> function = Mockito.spy(StringConsumer.class);
EventDeduplicationExecutor<String> executor = new EventDeduplicationExecutor<>(EventDeduplicationExecutorTest.class.getSimpleName(), executorService, function);
String params1 = "params1";
String params2 = "params2";
String params3 = "params3";
Mockito.doThrow(new RuntimeException()).when(function).accept("params1");
executor.submit(params1);
executor.submit(params2);
Thread.sleep(500);
executor.submit(params3);
Thread.sleep(500);
Mockito.verify(function).accept(params2);
Mockito.verify(function).accept(params3);
} finally {
executorService.shutdownNow();
}
}
public static class StringConsumer implements Consumer<String> {
@Override
public void accept(String s) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}

2
common/actor/pom.xml

@ -67,7 +67,7 @@
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.mockito</groupId> <groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId> <artifactId>mockito-core</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
</dependencies> </dependencies>

2
common/actor/src/main/java/org/thingsboard/server/actors/TbActor.java

@ -30,7 +30,7 @@ public interface TbActor {
} }
default InitFailureStrategy onInitFailure(int attempt, Throwable t) { default InitFailureStrategy onInitFailure(int attempt, Throwable t) {
return InitFailureStrategy.retryWithDelay(5000 * attempt); return InitFailureStrategy.retryWithDelay(5000L * attempt);
} }
default ProcessFailureStrategy onProcessFailure(Throwable t) { default ProcessFailureStrategy onProcessFailure(Throwable t) {

2
common/actor/src/main/java/org/thingsboard/server/actors/TbActorException.java

@ -17,6 +17,8 @@ package org.thingsboard.server.actors;
public class TbActorException extends Exception { public class TbActorException extends Exception {
private static final long serialVersionUID = 8209771144711980882L;
public TbActorException(String message, Throwable cause) { public TbActorException(String message, Throwable cause) {
super(message, cause); super(message, cause);
} }

38
common/actor/src/main/java/org/thingsboard/server/actors/TbActorMailbox.java

@ -17,7 +17,9 @@ package org.thingsboard.server.actors;
import lombok.Data; import lombok.Data;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.thingsboard.server.common.msg.MsgType;
import org.thingsboard.server.common.msg.TbActorMsg; import org.thingsboard.server.common.msg.TbActorMsg;
import org.thingsboard.server.common.msg.TbActorStopReason;
import java.util.List; import java.util.List;
import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentLinkedQueue;
@ -49,6 +51,7 @@ public final class TbActorMailbox implements TbActorCtx {
private final AtomicBoolean busy = new AtomicBoolean(FREE); private final AtomicBoolean busy = new AtomicBoolean(FREE);
private final AtomicBoolean ready = new AtomicBoolean(NOT_READY); private final AtomicBoolean ready = new AtomicBoolean(NOT_READY);
private final AtomicBoolean destroyInProgress = new AtomicBoolean(); private final AtomicBoolean destroyInProgress = new AtomicBoolean();
private volatile TbActorStopReason stopReason;
public void initActor() { public void initActor() {
dispatcher.getExecutor().execute(() -> tryInit(1)); dispatcher.getExecutor().execute(() -> tryInit(1));
@ -70,7 +73,8 @@ public final class TbActorMailbox implements TbActorCtx {
InitFailureStrategy strategy = actor.onInitFailure(attempt, t); InitFailureStrategy strategy = actor.onInitFailure(attempt, t);
if (strategy.isStop() || (settings.getMaxActorInitAttempts() > 0 && attemptIdx > settings.getMaxActorInitAttempts())) { if (strategy.isStop() || (settings.getMaxActorInitAttempts() > 0 && attemptIdx > settings.getMaxActorInitAttempts())) {
log.info("[{}] Failed to init actor, attempt {}, going to stop attempts.", selfId, attempt, t); log.info("[{}] Failed to init actor, attempt {}, going to stop attempts.", selfId, attempt, t);
system.stop(selfId); stopReason = TbActorStopReason.INIT_FAILED;
destroy();
} else if (strategy.getRetryDelay() > 0) { } else if (strategy.getRetryDelay() > 0) {
log.info("[{}] Failed to init actor, attempt {}, going to retry in attempts in {}ms", selfId, attempt, strategy.getRetryDelay()); log.info("[{}] Failed to init actor, attempt {}, going to retry in attempts in {}ms", selfId, attempt, strategy.getRetryDelay());
log.debug("[{}] Error", selfId, t); log.debug("[{}] Error", selfId, t);
@ -84,12 +88,28 @@ public final class TbActorMailbox implements TbActorCtx {
} }
private void enqueue(TbActorMsg msg, boolean highPriority) { private void enqueue(TbActorMsg msg, boolean highPriority) {
if (highPriority) { if (!destroyInProgress.get()) {
highPriorityMsgs.add(msg); if (highPriority) {
highPriorityMsgs.add(msg);
} else {
normalPriorityMsgs.add(msg);
}
tryProcessQueue(true);
} else { } else {
normalPriorityMsgs.add(msg); if (highPriority && msg.getMsgType().equals(MsgType.RULE_NODE_UPDATED_MSG)) {
synchronized (this) {
if (stopReason == TbActorStopReason.INIT_FAILED) {
destroyInProgress.set(false);
stopReason = null;
initActor();
} else {
msg.onTbActorStopped(stopReason);
}
}
} else {
msg.onTbActorStopped(stopReason);
}
} }
tryProcessQueue(true);
} }
private void tryProcessQueue(boolean newMsg) { private void tryProcessQueue(boolean newMsg) {
@ -119,6 +139,9 @@ public final class TbActorMailbox implements TbActorCtx {
try { try {
log.debug("[{}] Going to process message: {}", selfId, msg); log.debug("[{}] Going to process message: {}", selfId, msg);
actor.process(msg); actor.process(msg);
} catch (TbRuleNodeUpdateException updateException){
stopReason = TbActorStopReason.INIT_FAILED;
destroy();
} catch (Throwable t) { } catch (Throwable t) {
log.debug("[{}] Failed to process message: {}", selfId, msg, t); log.debug("[{}] Failed to process message: {}", selfId, msg, t);
ProcessFailureStrategy strategy = actor.onProcessFailure(t); ProcessFailureStrategy strategy = actor.onProcessFailure(t);
@ -180,11 +203,16 @@ public final class TbActorMailbox implements TbActorCtx {
} }
public void destroy() { public void destroy() {
if (stopReason == null) {
stopReason = TbActorStopReason.STOPPED;
}
destroyInProgress.set(true); destroyInProgress.set(true);
dispatcher.getExecutor().execute(() -> { dispatcher.getExecutor().execute(() -> {
try { try {
ready.set(NOT_READY); ready.set(NOT_READY);
actor.destroy(); actor.destroy();
highPriorityMsgs.forEach(msg -> msg.onTbActorStopped(stopReason));
normalPriorityMsgs.forEach(msg -> msg.onTbActorStopped(stopReason));
} catch (Throwable t) { } catch (Throwable t) {
log.warn("[{}] Failed to destroy actor: {}", selfId, t); log.warn("[{}] Failed to destroy actor: {}", selfId, t);
} }

26
common/actor/src/main/java/org/thingsboard/server/actors/TbRuleNodeUpdateException.java

@ -0,0 +1,26 @@
/**
* Copyright © 2016-2021 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;
public class TbRuleNodeUpdateException extends RuntimeException {
private static final long serialVersionUID = 8209771144711980882L;
public TbRuleNodeUpdateException(String message, Throwable cause) {
super(message, cause);
}
}

2
common/actor/src/test/java/org/thingsboard/server/actors/ActorSystemTest.java

@ -21,7 +21,7 @@ import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.runners.MockitoJUnitRunner; import org.mockito.junit.MockitoJUnitRunner;
import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceId;
import java.util.ArrayList; import java.util.ArrayList;

6
common/dao-api/pom.xml

@ -48,6 +48,10 @@
<groupId>com.google.guava</groupId> <groupId>com.google.guava</groupId>
<artifactId>guava</artifactId> <artifactId>guava</artifactId>
</dependency> </dependency>
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
</dependency>
<dependency> <dependency>
<groupId>com.github.fge</groupId> <groupId>com.github.fge</groupId>
<artifactId>json-schema-validator</artifactId> <artifactId>json-schema-validator</artifactId>
@ -99,7 +103,7 @@
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.mockito</groupId> <groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId> <artifactId>mockito-core</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
</dependencies> </dependencies>

3
common/dao-api/src/main/java/org/thingsboard/server/dao/cassandra/AbstractCassandraCluster.java

@ -23,6 +23,7 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
import org.springframework.core.env.Profiles;
import org.thingsboard.server.dao.cassandra.guava.GuavaSession; import org.thingsboard.server.dao.cassandra.guava.GuavaSession;
import org.thingsboard.server.dao.cassandra.guava.GuavaSessionBuilder; import org.thingsboard.server.dao.cassandra.guava.GuavaSessionBuilder;
import org.thingsboard.server.dao.cassandra.guava.GuavaSessionUtils; import org.thingsboard.server.dao.cassandra.guava.GuavaSessionUtils;
@ -77,7 +78,7 @@ public abstract class AbstractCassandraCluster {
} }
private boolean isInstall() { private boolean isInstall() {
return environment.acceptsProfiles("install"); return environment.acceptsProfiles(Profiles.of("install"));
} }
private void initSession() { private void initSession() {

31
common/dao-api/src/main/java/org/thingsboard/server/dao/cassandra/guava/GuavaSessionBuilder.java

@ -18,38 +18,25 @@ package org.thingsboard.server.dao.cassandra.guava;
import com.datastax.oss.driver.api.core.CqlSession; import com.datastax.oss.driver.api.core.CqlSession;
import com.datastax.oss.driver.api.core.config.DriverConfigLoader; import com.datastax.oss.driver.api.core.config.DriverConfigLoader;
import com.datastax.oss.driver.api.core.context.DriverContext; import com.datastax.oss.driver.api.core.context.DriverContext;
import com.datastax.oss.driver.api.core.metadata.Node; import com.datastax.oss.driver.api.core.session.ProgrammaticArguments;
import com.datastax.oss.driver.api.core.metadata.NodeStateListener;
import com.datastax.oss.driver.api.core.metadata.schema.SchemaChangeListener;
import com.datastax.oss.driver.api.core.session.SessionBuilder; import com.datastax.oss.driver.api.core.session.SessionBuilder;
import com.datastax.oss.driver.api.core.tracker.RequestTracker;
import com.datastax.oss.driver.api.core.type.codec.TypeCodec;
import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.NonNull;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
public class GuavaSessionBuilder extends SessionBuilder<GuavaSessionBuilder, GuavaSession> { public class GuavaSessionBuilder extends SessionBuilder<GuavaSessionBuilder, GuavaSession> {
@Override @Override
protected DriverContext buildContext( protected DriverContext buildContext(
DriverConfigLoader configLoader, DriverConfigLoader configLoader,
List<TypeCodec<?>> typeCodecs, ProgrammaticArguments programmaticArguments) {
NodeStateListener nodeStateListener,
SchemaChangeListener schemaChangeListener,
RequestTracker requestTracker,
Map<String, String> localDatacenters,
Map<String, Predicate<Node>> nodeFilters,
ClassLoader classLoader) {
return new GuavaDriverContext( return new GuavaDriverContext(
configLoader, configLoader,
typeCodecs, programmaticArguments.getTypeCodecs(),
nodeStateListener, programmaticArguments.getNodeStateListener(),
schemaChangeListener, programmaticArguments.getSchemaChangeListener(),
requestTracker, programmaticArguments.getRequestTracker(),
localDatacenters, programmaticArguments.getLocalDatacenters(),
nodeFilters, programmaticArguments.getNodeFilters(),
classLoader); programmaticArguments.getClassLoader());
} }
@Override @Override

14
dao/src/main/java/org/thingsboard/server/dao/util/mapping/JacksonUtil.java → common/dao-api/src/main/java/org/thingsboard/server/dao/util/mapping/JacksonUtil.java

@ -16,6 +16,7 @@
package org.thingsboard.server.dao.util.mapping; package org.thingsboard.server.dao.util.mapping;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.node.ObjectNode;
@ -38,6 +39,15 @@ public class JacksonUtil {
} }
} }
public static <T> T convertValue(Object fromValue, TypeReference<T> toValueTypeRef) {
try {
return fromValue != null ? OBJECT_MAPPER.convertValue(fromValue, toValueTypeRef) : null;
} catch (IllegalArgumentException e) {
throw new IllegalArgumentException("The given object value: "
+ fromValue + " cannot be converted to " + toValueTypeRef, e);
}
}
public static <T> T fromString(String string, Class<T> clazz) { public static <T> T fromString(String string, Class<T> clazz) {
try { try {
return string != null ? OBJECT_MAPPER.readValue(string, clazz) : null; return string != null ? OBJECT_MAPPER.readValue(string, clazz) : null;
@ -72,7 +82,9 @@ public class JacksonUtil {
} }
public static <T> T clone(T value) { public static <T> T clone(T value) {
return fromString(toString(value), (Class<T>) value.getClass()); @SuppressWarnings("unchecked")
Class<T> valueClass = (Class<T>) value.getClass();
return fromString(toString(value), valueClass);
} }
public static <T> JsonNode valueToTree(T value) { public static <T> JsonNode valueToTree(T value) {

2
common/data/pom.xml

@ -63,7 +63,7 @@
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.mockito</groupId> <groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId> <artifactId>mockito-core</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>

1
common/data/src/main/java/org/thingsboard/server/common/data/DataConstants.java

@ -24,6 +24,7 @@ public class DataConstants {
public static final String CUSTOMER = "CUSTOMER"; public static final String CUSTOMER = "CUSTOMER";
public static final String DEVICE = "DEVICE"; public static final String DEVICE = "DEVICE";
public static final String SCOPE = "scope";
public static final String CLIENT_SCOPE = "CLIENT_SCOPE"; public static final String CLIENT_SCOPE = "CLIENT_SCOPE";
public static final String SERVER_SCOPE = "SERVER_SCOPE"; public static final String SERVER_SCOPE = "SERVER_SCOPE";
public static final String SHARED_SCOPE = "SHARED_SCOPE"; public static final String SHARED_SCOPE = "SHARED_SCOPE";

19
application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeToSelfErrorMsg.java → common/data/src/main/java/org/thingsboard/server/common/data/HomeDashboard.java

@ -13,25 +13,18 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.thingsboard.server.actors.ruleChain; package org.thingsboard.server.common.data;
import lombok.Data; import lombok.Data;
import org.thingsboard.server.common.msg.MsgType;
import org.thingsboard.server.common.msg.TbActorMsg;
import org.thingsboard.server.common.msg.TbMsg;
/**
* Created by ashvayka on 19.03.18.
*/
@Data @Data
final class RuleNodeToSelfErrorMsg implements TbActorMsg { public class HomeDashboard extends Dashboard {
private final TbMsg msg; private boolean hideDashboardToolbar;
private final Throwable error;
@Override public HomeDashboard(Dashboard dashboard, boolean hideDashboardToolbar) {
public MsgType getMsgType() { super(dashboard);
return MsgType.RULE_TO_SELF_ERROR_MSG; this.hideDashboardToolbar = hideDashboardToolbar;
} }
} }

27
common/data/src/main/java/org/thingsboard/server/common/data/HomeDashboardInfo.java

@ -0,0 +1,27 @@
/**
* Copyright © 2016-2021 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.common.data;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.thingsboard.server.common.data.id.DashboardId;
@Data
@AllArgsConstructor
public class HomeDashboardInfo {
private DashboardId dashboardId;
private boolean hideDashboardToolbar;
}

5
common/data/src/main/java/org/thingsboard/server/common/data/lwm2m/ServerSecurityConfig.java

@ -15,7 +15,6 @@
*/ */
package org.thingsboard.server.common.data.lwm2m; package org.thingsboard.server.common.data.lwm2m;
import lombok.Builder;
import lombok.Data; import lombok.Data;
@Data @Data
@ -23,12 +22,8 @@ public class ServerSecurityConfig {
String host; String host;
Integer port; Integer port;
String serverPublicKey; String serverPublicKey;
@Builder.Default
boolean bootstrapServerIs = true; boolean bootstrapServerIs = true;
@Builder.Default
Integer clientHoldOffTime = 1; Integer clientHoldOffTime = 1;
@Builder.Default
Integer serverId = 111; Integer serverId = 111;
@Builder.Default
Integer bootstrapServerAccountTimeout = 0; Integer bootstrapServerAccountTimeout = 0;
} }

12
common/data/src/main/java/org/thingsboard/server/common/data/query/DynamicValue.java

@ -17,19 +17,25 @@ package org.thingsboard.server.common.data.query;
import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data; import lombok.Data;
import lombok.Getter; import lombok.RequiredArgsConstructor;
import java.io.Serializable; import java.io.Serializable;
@Data @Data
@RequiredArgsConstructor
public class DynamicValue<T> implements Serializable { public class DynamicValue<T> implements Serializable {
@JsonIgnore @JsonIgnore
private T resolvedValue; private T resolvedValue;
@Getter
private final DynamicValueSourceType sourceType; private final DynamicValueSourceType sourceType;
@Getter
private final String sourceAttribute; private final String sourceAttribute;
private final boolean inherit;
public DynamicValue(DynamicValueSourceType sourceType, String sourceAttribute) {
this.sourceAttribute = sourceAttribute;
this.sourceType = sourceType;
this.inherit = false;
}
} }

2
common/data/src/test/java/org/thingsboard/server/common/data/UUIDConverterTest.java

@ -19,7 +19,7 @@ import com.datastax.oss.driver.api.core.uuid.Uuids;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.runners.MockitoJUnitRunner; import org.mockito.junit.MockitoJUnitRunner;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;

2
common/message/pom.xml

@ -76,7 +76,7 @@
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.mockito</groupId> <groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId> <artifactId>mockito-core</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
</dependencies> </dependencies>

10
common/message/src/main/java/org/thingsboard/server/common/msg/MsgType.java

@ -39,6 +39,11 @@ public enum MsgType {
*/ */
COMPONENT_LIFE_CYCLE_MSG, COMPONENT_LIFE_CYCLE_MSG,
/**
* Special message to indicate rule node update request
*/
RULE_NODE_UPDATED_MSG,
/** /**
* Misc messages consumed from the Queue and forwarded to Rule Engine Actor. * Misc messages consumed from the Queue and forwarded to Rule Engine Actor.
* *
@ -66,11 +71,6 @@ public enum MsgType {
*/ */
REMOTE_TO_RULE_CHAIN_TELL_NEXT_MSG, REMOTE_TO_RULE_CHAIN_TELL_NEXT_MSG,
/**
* Message that is sent by RuleActor implementation to RuleActor itself to log the error.
*/
RULE_TO_SELF_ERROR_MSG,
/** /**
* Message that is sent by RuleActor implementation to RuleActor itself to process the message. * Message that is sent by RuleActor implementation to RuleActor itself to process the message.
*/ */

8
common/message/src/main/java/org/thingsboard/server/common/msg/TbActorMsg.java

@ -22,4 +22,12 @@ public interface TbActorMsg {
MsgType getMsgType(); MsgType getMsgType();
/**
* Executed when the target TbActor is stopped or destroyed.
* For example, rule node failed to initialize or removed from rule chain.
* Implementation should cleanup the resources.
*/
default void onTbActorStopped(TbActorStopReason reason) {
}
} }

22
common/message/src/main/java/org/thingsboard/server/common/msg/TbActorStopReason.java

@ -0,0 +1,22 @@
/**
* Copyright © 2016-2021 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.common.msg;
public enum TbActorStopReason {
INIT_FAILED, STOPPED
}

30
common/message/src/main/java/org/thingsboard/server/common/msg/TbRuleEngineActorMsg.java

@ -0,0 +1,30 @@
/**
* Copyright © 2016-2021 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.common.msg;
import lombok.EqualsAndHashCode;
import lombok.Getter;
@EqualsAndHashCode
public abstract class TbRuleEngineActorMsg implements TbActorMsg {
@Getter
protected final TbMsg msg;
public TbRuleEngineActorMsg(TbMsg msg) {
this.msg = msg;
}
}

40
common/message/src/main/java/org/thingsboard/server/common/msg/plugin/RuleNodeUpdatedMsg.java

@ -0,0 +1,40 @@
/**
* Copyright © 2016-2021 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.common.msg.plugin;
import lombok.ToString;
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.MsgType;
import java.util.Optional;
/**
* @author Andrew Shvayka
*/
@ToString
public class RuleNodeUpdatedMsg extends ComponentLifecycleMsg {
public RuleNodeUpdatedMsg(TenantId tenantId, EntityId entityId) {
super(tenantId, entityId, ComponentLifecycleEvent.UPDATED);
}
@Override
public MsgType getMsgType() {
return MsgType.RULE_NODE_UPDATED_MSG;
}
}

38
common/message/src/main/java/org/thingsboard/server/common/msg/queue/QueueToRuleEngineMsg.java

@ -15,32 +15,58 @@
*/ */
package org.thingsboard.server.common.msg.queue; package org.thingsboard.server.common.msg.queue;
import lombok.Data; import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.msg.MsgType; import org.thingsboard.server.common.msg.MsgType;
import org.thingsboard.server.common.msg.TbActorMsg; import org.thingsboard.server.common.msg.TbActorStopReason;
import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbRuleEngineActorMsg;
import java.io.Serializable;
import java.util.Set; import java.util.Set;
/** /**
* Created by ashvayka on 15.03.18. * Created by ashvayka on 15.03.18.
*/ */
@Data @ToString
public final class QueueToRuleEngineMsg implements TbActorMsg { @EqualsAndHashCode(callSuper = true)
public final class QueueToRuleEngineMsg extends TbRuleEngineActorMsg {
@Getter
private final TenantId tenantId; private final TenantId tenantId;
private final TbMsg tbMsg; @Getter
private final Set<String> relationTypes; private final Set<String> relationTypes;
@Getter
private final String failureMessage; private final String failureMessage;
public QueueToRuleEngineMsg(TenantId tenantId, TbMsg tbMsg, Set<String> relationTypes, String failureMessage) {
super(tbMsg);
this.tenantId = tenantId;
this.relationTypes = relationTypes;
this.failureMessage = failureMessage;
}
@Override @Override
public MsgType getMsgType() { public MsgType getMsgType() {
return MsgType.QUEUE_TO_RULE_ENGINE_MSG; return MsgType.QUEUE_TO_RULE_ENGINE_MSG;
} }
@Override
public void onTbActorStopped(TbActorStopReason reason) {
String message;
if (msg.getRuleChainId() != null) {
message = reason == TbActorStopReason.STOPPED ?
String.format("Rule chain [%s] stopped", msg.getRuleChainId().getId()) :
String.format("Failed to initialize rule chain [%s]!", msg.getRuleChainId().getId());
} else {
message = reason == TbActorStopReason.STOPPED ? "Rule chain stopped" : "Failed to initialize rule chain!";
}
msg.getCallback().onFailure(new RuleEngineException(message));
}
public boolean isTellNext() { public boolean isTellNext() {
return relationTypes != null && !relationTypes.isEmpty(); return relationTypes != null && !relationTypes.isEmpty();
} }
} }

4
common/message/src/main/java/org/thingsboard/server/common/msg/queue/RuleNodeException.java

@ -24,6 +24,9 @@ import org.thingsboard.server.common.data.rule.RuleNode;
@Slf4j @Slf4j
public class RuleNodeException extends RuleEngineException { public class RuleNodeException extends RuleEngineException {
private static final long serialVersionUID = -1776681087370749776L;
@Getter @Getter
private final String ruleChainName; private final String ruleChainName;
@Getter @Getter
@ -33,6 +36,7 @@ public class RuleNodeException extends RuleEngineException {
@Getter @Getter
private final RuleNodeId ruleNodeId; private final RuleNodeId ruleNodeId;
public RuleNodeException(String message, String ruleChainName, RuleNode ruleNode) { public RuleNodeException(String message, String ruleChainName, RuleNode ruleNode) {
super(message); super(message);
this.ruleChainName = ruleChainName; this.ruleChainName = ruleChainName;

2
common/queue/pom.xml

@ -124,7 +124,7 @@
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.mockito</groupId> <groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId> <artifactId>mockito-core</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
</dependencies> </dependencies>

1
common/queue/src/main/java/org/thingsboard/server/queue/azure/servicebus/TbServiceBusConsumerTemplate.java

@ -154,6 +154,7 @@ public class TbServiceBusConsumerTemplate<T extends TbQueueMsg> extends Abstract
} }
private <V> CompletableFuture<List<V>> fromList(List<CompletableFuture<V>> futures) { private <V> CompletableFuture<List<V>> fromList(List<CompletableFuture<V>> futures) {
@SuppressWarnings("unchecked")
CompletableFuture<Collection<V>>[] arrayFuture = new CompletableFuture[futures.size()]; CompletableFuture<Collection<V>>[] arrayFuture = new CompletableFuture[futures.size()];
futures.toArray(arrayFuture); futures.toArray(arrayFuture);

4
common/queue/src/main/java/org/thingsboard/server/queue/discovery/ClusterTopologyChangeEvent.java

@ -22,7 +22,9 @@ import org.thingsboard.server.common.msg.queue.ServiceQueueKey;
import java.util.Set; import java.util.Set;
public class ClusterTopologyChangeEvent extends ApplicationEvent { public class ClusterTopologyChangeEvent extends TbApplicationEvent {
private static final long serialVersionUID = -2441739930040282254L;
@Getter @Getter
private final Set<ServiceQueueKey> serviceQueueKeys; private final Set<ServiceQueueKey> serviceQueueKeys;

4
common/queue/src/main/java/org/thingsboard/server/queue/discovery/HashPartitionService.java

@ -126,7 +126,7 @@ public class HashPartitionService implements PartitionService {
} }
@Override @Override
public void recalculatePartitions(ServiceInfo currentService, List<ServiceInfo> otherServices) { public synchronized void recalculatePartitions(ServiceInfo currentService, List<ServiceInfo> otherServices) {
logServiceInfo(currentService); logServiceInfo(currentService);
otherServices.forEach(this::logServiceInfo); otherServices.forEach(this::logServiceInfo);
Map<ServiceQueueKey, List<ServiceInfo>> queueServicesMap = new HashMap<>(); Map<ServiceQueueKey, List<ServiceInfo>> queueServicesMap = new HashMap<>();
@ -134,7 +134,7 @@ public class HashPartitionService implements PartitionService {
for (ServiceInfo other : otherServices) { for (ServiceInfo other : otherServices) {
addNode(queueServicesMap, other); addNode(queueServicesMap, other);
} }
queueServicesMap.values().forEach(list -> list.sort((a, b) -> a.getServiceId().compareTo(b.getServiceId()))); queueServicesMap.values().forEach(list -> list.sort(Comparator.comparing(ServiceInfo::getServiceId)));
ConcurrentMap<ServiceQueueKey, List<Integer>> oldPartitions = myPartitions; ConcurrentMap<ServiceQueueKey, List<Integer>> oldPartitions = myPartitions;
TenantId myIsolatedOrSystemTenantId = getSystemOrIsolatedTenantId(currentService); TenantId myIsolatedOrSystemTenantId = getSystemOrIsolatedTenantId(currentService);

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save