Browse Source

Merge branch 'develop/3.5.2' into fix/snmp

# Conflicts:
#	common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java
pull/8757/head
ViacheslavKlimov 3 years ago
parent
commit
2109d36f7e
  1. 13
      application/src/main/data/json/system/widget_bundles/alarm_widgets.json
  2. 29
      application/src/main/data/json/system/widget_bundles/analogue_gauges.json
  3. 128
      application/src/main/data/json/system/widget_bundles/cards.json
  4. 57
      application/src/main/data/json/system/widget_bundles/charts.json
  5. 56
      application/src/main/data/json/system/widget_bundles/control_widgets.json
  6. 9
      application/src/main/data/json/system/widget_bundles/date.json
  7. 69
      application/src/main/data/json/system/widget_bundles/digital_gauges.json
  8. 9
      application/src/main/data/json/system/widget_bundles/edge_widgets.json
  9. 14
      application/src/main/data/json/system/widget_bundles/entity_admin_widgets.json
  10. 19
      application/src/main/data/json/system/widget_bundles/gateway_widgets.json
  11. 24
      application/src/main/data/json/system/widget_bundles/gpio_widgets.json
  12. 25
      application/src/main/data/json/system/widget_bundles/home_page_widgets.json
  13. 136
      application/src/main/data/json/system/widget_bundles/input_widgets.json
  14. 51
      application/src/main/data/json/system/widget_bundles/maps.json
  15. 14
      application/src/main/data/json/system/widget_bundles/navigation_widgets.json
  16. 82
      application/src/main/data/upgrade/3.5.1/schema_update.sql
  17. 8
      application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java
  18. 57
      application/src/main/java/org/thingsboard/server/actors/app/AppActor.java
  19. 159
      application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java
  20. 11
      application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java
  21. 12
      application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainManagerActor.java
  22. 2
      application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleEngineComponentActor.java
  23. 78
      application/src/main/java/org/thingsboard/server/actors/shared/RuleChainErrorActor.java
  24. 26
      application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java
  25. 4
      application/src/main/java/org/thingsboard/server/config/RateLimitProcessingFilter.java
  26. 35
      application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java
  27. 9
      application/src/main/java/org/thingsboard/server/controller/AuthController.java
  28. 22
      application/src/main/java/org/thingsboard/server/controller/BaseController.java
  29. 255
      application/src/main/java/org/thingsboard/server/controller/ControllerConstants.java
  30. 107
      application/src/main/java/org/thingsboard/server/controller/DeviceConnectivityController.java
  31. 59
      application/src/main/java/org/thingsboard/server/controller/DeviceController.java
  32. 22
      application/src/main/java/org/thingsboard/server/controller/EdgeController.java
  33. 2
      application/src/main/java/org/thingsboard/server/controller/EdgeEventController.java
  34. 6
      application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java
  35. 49
      application/src/main/java/org/thingsboard/server/controller/NotificationController.java
  36. 1
      application/src/main/java/org/thingsboard/server/controller/QueueController.java
  37. 4
      application/src/main/java/org/thingsboard/server/controller/RpcV2Controller.java
  38. 22
      application/src/main/java/org/thingsboard/server/controller/TelemetryController.java
  39. 58
      application/src/main/java/org/thingsboard/server/controller/WidgetTypeController.java
  40. 88
      application/src/main/java/org/thingsboard/server/controller/plugin/TbWebSocketHandler.java
  41. 1
      application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponseHandler.java
  42. 98
      application/src/main/java/org/thingsboard/server/service/action/EntityActionService.java
  43. 25
      application/src/main/java/org/thingsboard/server/service/component/AnnotationComponentDiscoveryService.java
  44. 19
      application/src/main/java/org/thingsboard/server/service/device/DeviceProvisionServiceImpl.java
  45. 80
      application/src/main/java/org/thingsboard/server/service/edge/DefaultEdgeNotificationService.java
  46. 16
      application/src/main/java/org/thingsboard/server/service/edge/EdgeContextComponent.java
  47. 158
      application/src/main/java/org/thingsboard/server/service/edge/EdgeEventSourcingListener.java
  48. 20
      application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcService.java
  49. 275
      application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java
  50. 6
      application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeSyncCursor.java
  51. 18
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AlarmMsgConstructor.java
  52. 67
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/TenantMsgConstructor.java
  53. 48
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/TenantProfileMsgConstructor.java
  54. 4
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/WidgetTypeMsgConstructor.java
  55. 35
      application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/GeneralEdgeEventFetcher.java
  56. 51
      application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/TenantEdgeEventFetcher.java
  57. 148
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessor.java
  58. 49
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/AlarmEdgeProcessor.java
  59. 12
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/BaseAlarmProcessor.java
  60. 99
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetEdgeProcessor.java
  61. 77
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetProfileEdgeProcessor.java
  62. 77
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/BaseAssetProcessor.java
  63. 69
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/BaseAssetProfileProcessor.java
  64. 77
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/dashboard/BaseDashboardProcessor.java
  65. 88
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/dashboard/DashboardEdgeProcessor.java
  66. 20
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/BaseDeviceProcessor.java
  67. 102
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/BaseDeviceProfileProcessor.java
  68. 47
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceEdgeProcessor.java
  69. 77
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceProfileEdgeProcessor.java
  70. 76
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/entityview/BaseEntityViewProcessor.java
  71. 96
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/entityview/EntityViewEdgeProcessor.java
  72. 7
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/ota/OtaPackageEdgeProcessor.java
  73. 6
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/queue/QueueEdgeProcessor.java
  74. 16
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/relation/BaseRelationProcessor.java
  75. 7
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/rule/RuleChainEdgeProcessor.java
  76. 8
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/BaseTelemetryProcessor.java
  77. 57
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/tenant/TenantEdgeProcessor.java
  78. 53
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/tenant/TenantProfileEdgeProcessor.java
  79. 7
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/user/UserEdgeProcessor.java
  80. 7
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/widget/WidgetBundleEdgeProcessor.java
  81. 6
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/widget/WidgetTypeEdgeProcessor.java
  82. 2
      application/src/main/java/org/thingsboard/server/service/edge/rpc/sync/DefaultEdgeRequestsService.java
  83. 143
      application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbNotificationEntityService.java
  84. 50
      application/src/main/java/org/thingsboard/server/service/entitiy/TbNotificationEntityService.java
  85. 5
      application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmCommentService.java
  86. 71
      application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmService.java
  87. 3
      application/src/main/java/org/thingsboard/server/service/entitiy/alarm/TbAlarmService.java
  88. 36
      application/src/main/java/org/thingsboard/server/service/entitiy/asset/DefaultTbAssetService.java
  89. 11
      application/src/main/java/org/thingsboard/server/service/entitiy/asset/profile/DefaultTbAssetProfileService.java
  90. 15
      application/src/main/java/org/thingsboard/server/service/entitiy/customer/DefaultTbCustomerService.java
  91. 49
      application/src/main/java/org/thingsboard/server/service/entitiy/dashboard/DefaultTbDashboardService.java
  92. 32
      application/src/main/java/org/thingsboard/server/service/entitiy/device/DefaultTbDeviceService.java
  93. 11
      application/src/main/java/org/thingsboard/server/service/entitiy/device/profile/DefaultTbDeviceProfileService.java
  94. 8
      application/src/main/java/org/thingsboard/server/service/entitiy/edge/DefaultTbEdgeService.java
  95. 25
      application/src/main/java/org/thingsboard/server/service/entitiy/entity/relation/DefaultTbEntityRelationService.java
  96. 2
      application/src/main/java/org/thingsboard/server/service/entitiy/entity/relation/TbEntityRelationService.java
  97. 67
      application/src/main/java/org/thingsboard/server/service/entitiy/entityview/DefaultTbEntityViewService.java
  98. 19
      application/src/main/java/org/thingsboard/server/service/entitiy/ota/DefaultTbOtaPackageService.java
  99. 48
      application/src/main/java/org/thingsboard/server/service/entitiy/queue/DefaultTbQueueService.java
  100. 8
      application/src/main/java/org/thingsboard/server/service/entitiy/tenant/profile/DefaultTbTenantProfileService.java

13
application/src/main/data/json/system/widget_bundles/alarm_widgets.json

File diff suppressed because one or more lines are too long

29
application/src/main/data/json/system/widget_bundles/analogue_gauges.json

File diff suppressed because one or more lines are too long

128
application/src/main/data/json/system/widget_bundles/cards.json

File diff suppressed because one or more lines are too long

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

File diff suppressed because one or more lines are too long

56
application/src/main/data/json/system/widget_bundles/control_widgets.json

File diff suppressed because one or more lines are too long

9
application/src/main/data/json/system/widget_bundles/date.json

@ -3,11 +3,12 @@
"alias": "date", "alias": "date",
"title": "Date", "title": "Date",
"image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAAAAABslHx1AAAAAmJLR0QA/4ePzL8AAAh0SURBVHja7d3/UxNnHsBx/7L7oXc/3NzM3dycvdajnavtUYXOVM/zsJ0eharIN5EgGLIEEWj40ihEEMSAhCU0CCoB0qAYhBKIfI+JBBJCstn3/UC9KtjT3g2apc/nh0x299nJvvLs83ye2X1mdx/xwB6IGPviARXNhxqI79sLDlAD+wLsiQjsC+4NSFBABERABERABERAdkKCblgf31bI3Xc79D//wuPJNwIZT5vFZ9xWqHRgoGB666vlZx9W0PvMQvXKa4MU5Ss+I7MGyTdxl6sriUagdJmlEial8qnxry6EFiTD98BY/YVog9TCiMVQpSiNerMTubw+BjS2ljqYqyh3B9pZ6pzqx1Ha1IxLMiwMf1GZGLtQ4aPB5NhtSH1Hh8/I2fVQcag8lt01fXkLQjb2jWUdZg+6YKQwHo72mxK+B+gXbU3UPRhopq3LV4ld3gjHc33xk9wORHMoiHeMulqD+cq0jpsJTw3SUvzM5mouuTO7XiP1ify7xujxiop8dKMOqfP+FiSRjdP4TT5mj3q8ouKUx9Td381qdd0pr83BtdHmMQa6+nIqSq+1mWZyFU4zbjRncsOti7taH5gJ6bBX1kpIS75aKIrkKrsPYfZzo3oqQYS2M2t1xfEtiMPMKTWUj9nD6RgRoL+bNifGLYjNjr3L1YSyCeQqnOZMNJbNY109rtbFcvy6WC6zEtLS6jkSObwWCC1GBosvdjBdxJgBoLS0pDbK1yVSJrcKgiNFVS1bkIlThpMum4Nro+FCY/5N9WJlmfcppPWsPiNB0UNcrZj0eh1lZeUFXNfFOvW6gdcAeRqJ+M510R8+ftymPD2e6GLM4oTN7aW3YibmMf24ezyexAkxUi+ZEz+5tdNYGXjdeUQMUQREQAREQAREQAREQATklwNZ7Otf3Vkq5gYIyRqC3OqeuhAgsUG0NIYSA2KJDRI+iBBoZENlAzUW1QDkDg9uzEm1dteXY1O1VRPQfqm2M1wTu9RkCjQOW6LVlqv+rGuagDxqjjmtBvQYuzq+hnYvZeEat4PJQG65OlzZd8rfiCYg8h2HfdWAHsOkdwXavZSGa1wOfAGdcXGw3ev1f6MFSHHtZfX+eZMOyTVhNLuh/WKNHK6JVV5uCjSuX1iTmts0AdmKuAoJBVUB2r1bVz2eXvuIa6PX2hnj2kg0IrMLiIAIyC8DEo8mO2SjswXYbMlTAJbzPq1UmMj8uxXAnZWVleUHwFr2Q/nJrKPN4PlXxh24dizTA6sWexJAFlIz84AvLj5SAY7cUk63JA7MRFJnANvpUCiUeA6ipExF0kaU97wrKWt3jkc87zKSfqwuCSAK/Xkwnrm0dR+5CS5XK9OQ6QRsBQAzx9MvYj169KNbQPgGnB149CnkON0PUN5RFRrrkqKN9OdBy4HM1OatxcihBbDmnEwAtkN1dQ4OTaifuaxHlcW3t4ZdCx9H730GJd2ARYLdhQxnZ2Vl5S29IqTKSPhPvtRUD0qmDZiTP5oDbEdl+d7ab7KyPrhiLYNPZgHC6Q+4fwJKZHB+Gt9tCLdPZGTNvmqNNNfAfgWgqAE2hsDQ/vTU2vzj3NzcmrUMPlwCYse/hcVDkO3i4eEguw5h8EWOn4CsvO9uOQFQfcLjmVRSBh+mfv+fNvJl7bQ0Y31ntONvKvBVicfj42P7SEps4YDd43my6xDCr9ZGvDIwcb46DPCNJEkm5g3FLoDJXoBN8zmbOtFbdWEZSEiSJLXhl8q8eCRJkjwwOiwyu4AIiIAIyM+E3NRQiFNLQAREQAREQAREQLQGmfwOAN/2cnVPNAYZ7gs+GgrOnZnC50oEpr9/SHAl4nykRciw9F1ZsGhlvGnw6lD5bGXMstQ0fn5Vi5AB9OhpqDTphwa5M1rFSFPBrHYhbZMEhgaJFfego0F7kMnvHt7nGrI9Ym4cnxiH6yGsNQ3e7nXR/QqIgAiIgAjILxyy7NEgxNCNsn8Z18ln1tkLNQixFeH63XVMDTDzBFj3KtgLiS5pDbLyARWmbE6MKRk56V0MHM5Pj9oLN470JMsBr/h8Pp8v9PI2khJKC6cqbys3Slh/n5QABqs9LyN5ZizPZWdkZJSEXw7Jv/4PznUcw/Dn1NSU6FupqR802X/9h3mSSfICx07Ijb9e5tuDNVy5CAr7wyjYMwfSkmiSrM8YeZXud+5Xs4TfGmHtw0vGHCxHrxyZsBdSUaa9PDIMuOMQGXApMG1fJuBFcSqag4ghioAIiIBoCSImDIhTS0AEREAEREAERAOQxIuemK1qD2KrqnUysG3l+rfag5xNIDsyJzba2mLD3Y3LoN6sn12/6zGZHGpPU1A7kBFdT5RyGv0um+VeUIL52o0evxkapvpHgtWagair6kgV5RSZvnZY5tGDKlcP+c3cvklDlemqdmqkbMGrpzTW6PF5LYPeKpi94T/vNy+WLAf7uwNj2oGEerpC3J2M9coxS491HRi+vrQxPm21DqpDnU+0A3kmLPMkc4jMLiACIiACkoyQR0OugHYhzzwS3XisLC0Z77qtvApE/sT/I+QKic9lFPnKPD1rzI4kh8P/ifxySN/hXp6F0JvDyaruA+Fz7RRbX/9BP5FlWZbdz690pPW8DNJ3+Fms8QqMfMbCnPPjcfc/1b+E38DfL2dkZBRsH5/ukOyYwpGuZxuk4xwFha0H76kpt798IyeSvNMB+vSV/14jzvT25yGPD44lfq8oH41RdaD3zTSJFzyosDPt9svayHMS4/7Uw72gez/9oMzMbzeTpdfa4XhRr3X3iH/HjpEE4M5LFof/yN1XySM/MXu85T1v0uSR9f9niBIMk8QhBo0C8kYg88l1Y31e1IiACIiACIiA7Bpkz7wgeM+8sjm2N16irewjHgisajwCAYV/A1HeMsl2Bkj/AAAAAElFTkSuQmCC", "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAAAAABslHx1AAAAAmJLR0QA/4ePzL8AAAh0SURBVHja7d3/UxNnHsBx/7L7oXc/3NzM3dycvdajnavtUYXOVM/zsJ0eharIN5EgGLIEEWj40ihEEMSAhCU0CCoB0qAYhBKIfI+JBBJCstn3/UC9KtjT3g2apc/nh0x299nJvvLs83ye2X1mdx/xwB6IGPviARXNhxqI79sLDlAD+wLsiQjsC+4NSFBABERABERABERAdkKCblgf31bI3Xc79D//wuPJNwIZT5vFZ9xWqHRgoGB666vlZx9W0PvMQvXKa4MU5Ss+I7MGyTdxl6sriUagdJmlEial8qnxry6EFiTD98BY/YVog9TCiMVQpSiNerMTubw+BjS2ljqYqyh3B9pZ6pzqx1Ha1IxLMiwMf1GZGLtQ4aPB5NhtSH1Hh8/I2fVQcag8lt01fXkLQjb2jWUdZg+6YKQwHo72mxK+B+gXbU3UPRhopq3LV4ld3gjHc33xk9wORHMoiHeMulqD+cq0jpsJTw3SUvzM5mouuTO7XiP1ify7xujxiop8dKMOqfP+FiSRjdP4TT5mj3q8ouKUx9Td381qdd0pr83BtdHmMQa6+nIqSq+1mWZyFU4zbjRncsOti7taH5gJ6bBX1kpIS75aKIrkKrsPYfZzo3oqQYS2M2t1xfEtiMPMKTWUj9nD6RgRoL+bNifGLYjNjr3L1YSyCeQqnOZMNJbNY109rtbFcvy6WC6zEtLS6jkSObwWCC1GBosvdjBdxJgBoLS0pDbK1yVSJrcKgiNFVS1bkIlThpMum4Nro+FCY/5N9WJlmfcppPWsPiNB0UNcrZj0eh1lZeUFXNfFOvW6gdcAeRqJ+M510R8+ftymPD2e6GLM4oTN7aW3YibmMf24ezyexAkxUi+ZEz+5tdNYGXjdeUQMUQREQAREQAREQAREQATklwNZ7Otf3Vkq5gYIyRqC3OqeuhAgsUG0NIYSA2KJDRI+iBBoZENlAzUW1QDkDg9uzEm1dteXY1O1VRPQfqm2M1wTu9RkCjQOW6LVlqv+rGuagDxqjjmtBvQYuzq+hnYvZeEat4PJQG65OlzZd8rfiCYg8h2HfdWAHsOkdwXavZSGa1wOfAGdcXGw3ev1f6MFSHHtZfX+eZMOyTVhNLuh/WKNHK6JVV5uCjSuX1iTmts0AdmKuAoJBVUB2r1bVz2eXvuIa6PX2hnj2kg0IrMLiIAIyC8DEo8mO2SjswXYbMlTAJbzPq1UmMj8uxXAnZWVleUHwFr2Q/nJrKPN4PlXxh24dizTA6sWexJAFlIz84AvLj5SAY7cUk63JA7MRFJnANvpUCiUeA6ipExF0kaU97wrKWt3jkc87zKSfqwuCSAK/Xkwnrm0dR+5CS5XK9OQ6QRsBQAzx9MvYj169KNbQPgGnB149CnkON0PUN5RFRrrkqKN9OdBy4HM1OatxcihBbDmnEwAtkN1dQ4OTaifuaxHlcW3t4ZdCx9H730GJd2ARYLdhQxnZ2Vl5S29IqTKSPhPvtRUD0qmDZiTP5oDbEdl+d7ab7KyPrhiLYNPZgHC6Q+4fwJKZHB+Gt9tCLdPZGTNvmqNNNfAfgWgqAE2hsDQ/vTU2vzj3NzcmrUMPlwCYse/hcVDkO3i4eEguw5h8EWOn4CsvO9uOQFQfcLjmVRSBh+mfv+fNvJl7bQ0Y31ntONvKvBVicfj42P7SEps4YDd43my6xDCr9ZGvDIwcb46DPCNJEkm5g3FLoDJXoBN8zmbOtFbdWEZSEiSJLXhl8q8eCRJkjwwOiwyu4AIiIAIyM+E3NRQiFNLQAREQAREQAREQLQGmfwOAN/2cnVPNAYZ7gs+GgrOnZnC50oEpr9/SHAl4nykRciw9F1ZsGhlvGnw6lD5bGXMstQ0fn5Vi5AB9OhpqDTphwa5M1rFSFPBrHYhbZMEhgaJFfego0F7kMnvHt7nGrI9Ym4cnxiH6yGsNQ3e7nXR/QqIgAiIgAjILxyy7NEgxNCNsn8Z18ln1tkLNQixFeH63XVMDTDzBFj3KtgLiS5pDbLyARWmbE6MKRk56V0MHM5Pj9oLN470JMsBr/h8Pp8v9PI2khJKC6cqbys3Slh/n5QABqs9LyN5ZizPZWdkZJSEXw7Jv/4PznUcw/Dn1NSU6FupqR802X/9h3mSSfICx07Ijb9e5tuDNVy5CAr7wyjYMwfSkmiSrM8YeZXud+5Xs4TfGmHtw0vGHCxHrxyZsBdSUaa9PDIMuOMQGXApMG1fJuBFcSqag4ghioAIiIBoCSImDIhTS0AEREAEREAERAOQxIuemK1qD2KrqnUysG3l+rfag5xNIDsyJzba2mLD3Y3LoN6sn12/6zGZHGpPU1A7kBFdT5RyGv0um+VeUIL52o0evxkapvpHgtWagair6kgV5RSZvnZY5tGDKlcP+c3cvklDlemqdmqkbMGrpzTW6PF5LYPeKpi94T/vNy+WLAf7uwNj2oGEerpC3J2M9coxS491HRi+vrQxPm21DqpDnU+0A3kmLPMkc4jMLiACIiACkoyQR0OugHYhzzwS3XisLC0Z77qtvApE/sT/I+QKic9lFPnKPD1rzI4kh8P/ifxySN/hXp6F0JvDyaruA+Fz7RRbX/9BP5FlWZbdz690pPW8DNJ3+Fms8QqMfMbCnPPjcfc/1b+E38DfL2dkZBRsH5/ukOyYwpGuZxuk4xwFha0H76kpt798IyeSvNMB+vSV/14jzvT25yGPD44lfq8oH41RdaD3zTSJFzyosDPt9svayHMS4/7Uw72gez/9oMzMbzeTpdfa4XhRr3X3iH/HjpEE4M5LFof/yN1XySM/MXu85T1v0uSR9f9niBIMk8QhBo0C8kYg88l1Y31e1IiACIiACIiA7Bpkz7wgeM+8sjm2N16irewjHgisajwCAYV/A1HeMsl2Bkj/AAAAAElFTkSuQmCC",
"description": "Contains widgets to change the data range for other widgets on the dashboard." "description": "Contains widgets to change the data range for other widgets on the dashboard.",
"externalId": null,
"name": "Date"
}, },
"widgetTypes": [ "widgetTypes": [
{ {
"alias": "date_range_navigator",
"name": "Date-range-navigator", "name": "Date-range-navigator",
"image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAAAAABslHx1AAAAAmJLR0QA/4ePzL8AAAfbSURBVHja7d3tUxNJHsBx/jLcs+64rWVx9di9xYdSuUWL3boVo0b3BDwJCuFZkVWEEy8adYVEEZWFE42gPAgXNAQisCIPMUAYCI95fvjeC/R8AC2oYg2D3W8mM91JzSfdv+meVE86Av/Y0IDM09CYjwi/dTqAzFNg2uqPGJtmDaTpsYihwFqABIYiBlgTaUBABERABERABERA1jbEJ0n+95Qen9+0+mQB6U4vz2uYf+mvfDvrxrww1ykPiJZgjoSp3EjVwRb6ytsAf0Nd5Rz3QsH68n5yneP3Ga5oCNmbrwVWNYTbzdaKiXzrSPq0vWD8Yge49/e1/MyxYM2tseyZ3OEsuzNrtOq+RdUTWt2Q6qZQwxW1yavm/kl9iR7c2aDiWLDAxZw/N6WNDrX+QqlFt8qbFvnDzZX+6yavmvoqSZoDdzahoxwLFk4y6s5tzvSYL0rS9CqHZFYX3saUV3u4JZTSNZvZcLkP3HvvXNZzLPik+H6BP9fZfs6Xc+96++qGOC29M8Bg98Q4tn5c5lHAnT3UHeJ5CHunh4EAfT5fp5U5u+w6RHf2GunZQ24xRBEQAREQARGQ3wXSObVqIV35JgBLZsr8/ouULUkd0Htg8+EXABxJSEhIuPWydHzrq/dNHN/6fSPMZW7d2w3PDm5W9gK9p5rCBNmeuKEJaNpYOZ8X2FLpvfuFy7OxwVueOF/ips1mm10A2VPqMUaNkpPlrN/kd/3lrvdabIgfvvv61zBBrCQ0QfBb46s79Z+APw9O6WBsfQhgeyMAwwXp9RBfmZdpAQjsd8H2NjYMwLaOwWPgiZxmiP2/hi1GEppgOOb0LtX4qyPmbwLgcZw6wmvIZGyVcetD4uMf6j8fe1luJHo2sM4JSXcBHn4HrBjkN4PBYDAYhpYJaV93U8rfy31DEzC71QhoY7/onodERUdv40o6VKcQ3wypV1/+ApNYiyvSBQdqAOmb3pWEeIuVSqXynH+ZkMZdMLvOU1J8ETw/auczHn3le10jeV/GxcWlEN8KxacACB4pAP+6WUgywFz8bVYSgrdYqSzzL7dp9W0K4vzMCxBILgLGqiC0fuw1pCz3/8GePg8tOBoENj2DzWa8e17qVy5GvMWawLJjJJR43lGkBAhlH7DZbFNTMQ1z17e9EexD0c1Tuiri9w22fT4I8O+EQZttgpNpkzWxgUBKms1mm1lRCN7A8oI9rxNwpO9UTwL4FQqFQlFB18HtaTYAsjoB6FBuz5wk50ZSogGAVIVCoSjBfeJvh/qZVCgUCkU1UNwmhigCIiACIiCfEOSaLJJoWgIiIAIiIAIiIAIiIAKyEhD/zPuL+x/JCDJUOr819Sws7syRF8TQUT5oyz9lD97T27nXaqqDulC/3hCUGyTtYb/KU1nru24YVgcz7kyW2CfOeM5JVxrlBlGBijtNpOv0qVKGH1N1XTumK6dvyhVyfFiS/Bl+AieL/LYzbqM8IS1nHY1nH2pCGX64qkVS3VGXywrifkEf9BE0zzLc6aI/BNNTIJmnbMEBGUFEzy4gAiIgAiIgAvKBNNIvM0j5JRhJ9EFm81vHtWqZQQyJULnOSCDqhbwhjvVufjpwhqexYMnI6gT/pSMaL1o1UwVWOcXItnZfTE88FWk8jW2o3/CCfx43Z6ajVTt3nQ/nuS57vlaOpk1JnHSkEtVlKNSMRPlw/sGvVf29KKxf+rLna/1HWaQnvzq2n8SNcXFxpcb1cXFxcZL2sz/WEHbJcuZr2b/aOUzj/pgQyZUAAzEBAG2S5cvBcEuWN1/r2x3g+lMKPPhrj3TaFEw8M/UkM6RVo98R5sdIljlf64IeKLwL3Nmz86wPR9aOfSbqfoHCajFEERABERABEZAPQ8R8LdG0BERABERABERABERAPmmI3b74fx35puUFOX9Jn+exNi/MsLXICuI9CvVPSjJ+o63iGcbHOgtAz9UHIbtZ0uv1A7To+mVRI2XFPaFAs8bV+ouUO6W5YTtuB6dq9HJnl84z0JMy3qiXsmdlESPPNXluSwVlGn3WE80AdY0QLLzcS5cO/mXi7EX9/BPtqxwy2gj6dksFZU8kyaMZ4HYruB2jxW1dOup1UNwlSV4ZQHy5t+szJqxq67O8xnNOTckDlRNmsut/NnXpRvcbGvq7CxrPueXQtIL9HU7olXCYp9F0dcwAzJmHmbPPWSyWMcbNM/LrEDWrswddPsQbFEMUAREQAREQAfk0IaUJuw9Xh2QJcdS+uacqsj36IWO1nHKtY+kQR3LS+JsQDThjOglUphUMc+kxXGsLm2M8KdmxVIgjOek570A4epG81KearVxIIRD9/Hc/4WCTwWAwGO4vuD8YVPxDWhpk4h3HPOREEf1O10Tk9EiUy7jzY7T6VKVSeei/CzMWlSwCcR1OfOf7VmmAVC2P4/elRk7yY90JLR9HspgDnicedi2lRmp2lwYXQKajLWwxEoicpCr565GPEg7Wo8ZFG9353TeXFiM1u0vekqjyegzxebDtkvVU5AizUd9/pMB2LeooW8TxnqtW7dsSrUKRdi8EPYoEXUovKPVhvPoGy3bfWno/Un3I8f6Pmot1hBHiOFS9nJ79A4sO1G05E9YO0blSYy2bWQwaBURA1iakLfyzs9pEjQiIgAiIgAiIgHxikDWzQPDaWLJ5aizCtzYW0Q5ErI1lzQP8DwJX9hDY3Q8ZAAAAAElFTkSuQmCC", "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAAAAABslHx1AAAAAmJLR0QA/4ePzL8AAAfbSURBVHja7d3tUxNJHsBx/jLcs+64rWVx9di9xYdSuUWL3boVo0b3BDwJCuFZkVWEEy8adYVEEZWFE42gPAgXNAQisCIPMUAYCI95fvjeC/R8AC2oYg2D3W8mM91JzSfdv+meVE86Av/Y0IDM09CYjwi/dTqAzFNg2uqPGJtmDaTpsYihwFqABIYiBlgTaUBABERABERABERA1jbEJ0n+95Qen9+0+mQB6U4vz2uYf+mvfDvrxrww1ykPiJZgjoSp3EjVwRb6ytsAf0Nd5Rz3QsH68n5yneP3Ga5oCNmbrwVWNYTbzdaKiXzrSPq0vWD8Yge49/e1/MyxYM2tseyZ3OEsuzNrtOq+RdUTWt2Q6qZQwxW1yavm/kl9iR7c2aDiWLDAxZw/N6WNDrX+QqlFt8qbFvnDzZX+6yavmvoqSZoDdzahoxwLFk4y6s5tzvSYL0rS9CqHZFYX3saUV3u4JZTSNZvZcLkP3HvvXNZzLPik+H6BP9fZfs6Xc+96++qGOC29M8Bg98Q4tn5c5lHAnT3UHeJ5CHunh4EAfT5fp5U5u+w6RHf2GunZQ24xRBEQAREQARGQ3wXSObVqIV35JgBLZsr8/ouULUkd0Htg8+EXABxJSEhIuPWydHzrq/dNHN/6fSPMZW7d2w3PDm5W9gK9p5rCBNmeuKEJaNpYOZ8X2FLpvfuFy7OxwVueOF/ips1mm10A2VPqMUaNkpPlrN/kd/3lrvdabIgfvvv61zBBrCQ0QfBb46s79Z+APw9O6WBsfQhgeyMAwwXp9RBfmZdpAQjsd8H2NjYMwLaOwWPgiZxmiP2/hi1GEppgOOb0LtX4qyPmbwLgcZw6wmvIZGyVcetD4uMf6j8fe1luJHo2sM4JSXcBHn4HrBjkN4PBYDAYhpYJaV93U8rfy31DEzC71QhoY7/onodERUdv40o6VKcQ3wypV1/+ApNYiyvSBQdqAOmb3pWEeIuVSqXynH+ZkMZdMLvOU1J8ETw/auczHn3le10jeV/GxcWlEN8KxacACB4pAP+6WUgywFz8bVYSgrdYqSzzL7dp9W0K4vzMCxBILgLGqiC0fuw1pCz3/8GePg8tOBoENj2DzWa8e17qVy5GvMWawLJjJJR43lGkBAhlH7DZbFNTMQ1z17e9EexD0c1Tuiri9w22fT4I8O+EQZttgpNpkzWxgUBKms1mm1lRCN7A8oI9rxNwpO9UTwL4FQqFQlFB18HtaTYAsjoB6FBuz5wk50ZSogGAVIVCoSjBfeJvh/qZVCgUCkU1UNwmhigCIiACIiCfEOSaLJJoWgIiIAIiIAIiIAIiIAKyEhD/zPuL+x/JCDJUOr819Sws7syRF8TQUT5oyz9lD97T27nXaqqDulC/3hCUGyTtYb/KU1nru24YVgcz7kyW2CfOeM5JVxrlBlGBijtNpOv0qVKGH1N1XTumK6dvyhVyfFiS/Bl+AieL/LYzbqM8IS1nHY1nH2pCGX64qkVS3VGXywrifkEf9BE0zzLc6aI/BNNTIJmnbMEBGUFEzy4gAiIgAiIgAvKBNNIvM0j5JRhJ9EFm81vHtWqZQQyJULnOSCDqhbwhjvVufjpwhqexYMnI6gT/pSMaL1o1UwVWOcXItnZfTE88FWk8jW2o3/CCfx43Z6ajVTt3nQ/nuS57vlaOpk1JnHSkEtVlKNSMRPlw/sGvVf29KKxf+rLna/1HWaQnvzq2n8SNcXFxpcb1cXFxcZL2sz/WEHbJcuZr2b/aOUzj/pgQyZUAAzEBAG2S5cvBcEuWN1/r2x3g+lMKPPhrj3TaFEw8M/UkM6RVo98R5sdIljlf64IeKLwL3Nmz86wPR9aOfSbqfoHCajFEERABERABEZAPQ8R8LdG0BERABERABERABERAPmmI3b74fx35puUFOX9Jn+exNi/MsLXICuI9CvVPSjJ+o63iGcbHOgtAz9UHIbtZ0uv1A7To+mVRI2XFPaFAs8bV+ouUO6W5YTtuB6dq9HJnl84z0JMy3qiXsmdlESPPNXluSwVlGn3WE80AdY0QLLzcS5cO/mXi7EX9/BPtqxwy2gj6dksFZU8kyaMZ4HYruB2jxW1dOup1UNwlSV4ZQHy5t+szJqxq67O8xnNOTckDlRNmsut/NnXpRvcbGvq7CxrPueXQtIL9HU7olXCYp9F0dcwAzJmHmbPPWSyWMcbNM/LrEDWrswddPsQbFEMUAREQAREQAfk0IaUJuw9Xh2QJcdS+uacqsj36IWO1nHKtY+kQR3LS+JsQDThjOglUphUMc+kxXGsLm2M8KdmxVIgjOek570A4epG81KearVxIIRD9/Hc/4WCTwWAwGO4vuD8YVPxDWhpk4h3HPOREEf1O10Tk9EiUy7jzY7T6VKVSeei/CzMWlSwCcR1OfOf7VmmAVC2P4/elRk7yY90JLR9HspgDnicedi2lRmp2lwYXQKajLWwxEoicpCr565GPEg7Wo8ZFG9353TeXFiM1u0vekqjyegzxebDtkvVU5AizUd9/pMB2LeooW8TxnqtW7dsSrUKRdi8EPYoEXUovKPVhvPoGy3bfWno/Un3I8f6Pmot1hBHiOFS9nJ79A4sO1G05E9YO0blSYy2bWQwaBURA1iakLfyzs9pEjQiIgAiIgAiIgHxikDWzQPDaWLJ5aizCtzYW0Q5ErI1lzQP8DwJX9hDY3Q8ZAAAAAElFTkSuQmCC",
"description": "Allows to change the data range for other widgets on the dashboard.", "description": "Allows to change the data range for other widgets on the dashboard.",
@ -23,7 +24,9 @@
"dataKeySettingsSchema": "{}\n", "dataKeySettingsSchema": "{}\n",
"settingsDirective": "tb-date-range-navigator-widget-settings", "settingsDirective": "tb-date-range-navigator-widget-settings",
"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\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"defaultInterval\":\"week\",\"stepSize\":\"day\"},\"title\":\"Date-range-navigator\",\"dropShadow\":true,\"enableFullscreen\":true,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" "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\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"defaultInterval\":\"week\",\"stepSize\":\"day\"},\"title\":\"Date-range-navigator\",\"dropShadow\":true,\"enableFullscreen\":true,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
} },
"fqn": "date.date_range_navigator",
"deprecated": false
} }
] ]
} }

69
application/src/main/data/json/system/widget_bundles/digital_gauges.json

File diff suppressed because one or more lines are too long

9
application/src/main/data/json/system/widget_bundles/edge_widgets.json

File diff suppressed because one or more lines are too long

14
application/src/main/data/json/system/widget_bundles/entity_admin_widgets.json

File diff suppressed because one or more lines are too long

19
application/src/main/data/json/system/widget_bundles/gateway_widgets.json

File diff suppressed because one or more lines are too long

24
application/src/main/data/json/system/widget_bundles/gpio_widgets.json

File diff suppressed because one or more lines are too long

25
application/src/main/data/json/system/widget_bundles/home_page_widgets.json

File diff suppressed because one or more lines are too long

136
application/src/main/data/json/system/widget_bundles/input_widgets.json

File diff suppressed because one or more lines are too long

51
application/src/main/data/json/system/widget_bundles/maps.json

File diff suppressed because one or more lines are too long

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

File diff suppressed because one or more lines are too long

82
application/src/main/data/upgrade/3.5.1/schema_update.sql

@ -53,9 +53,91 @@ $$;
-- NOTIFICATION CONFIGS VERSION CONTROL END -- NOTIFICATION CONFIGS VERSION CONTROL END
-- EDGE EVENTS MIGRATION START
DO
$$
DECLARE table_partition RECORD;
BEGIN
-- in case of running the upgrade script a second time:
IF NOT (SELECT exists(SELECT FROM pg_tables WHERE tablename = 'old_edge_event')) THEN
ALTER TABLE edge_event RENAME TO old_edge_event;
CREATE INDEX IF NOT EXISTS idx_old_edge_event_created_time_tmp ON old_edge_event(created_time);
ALTER INDEX IF EXISTS idx_edge_event_tenant_id_and_created_time RENAME TO idx_old_edge_event_tenant_id_and_created_time;
FOR table_partition IN SELECT tablename AS name, split_part(tablename, '_', 3) AS partition_ts
FROM pg_tables WHERE tablename LIKE 'edge_event_%'
LOOP
EXECUTE format('ALTER TABLE %s RENAME TO old_edge_event_%s', table_partition.name, table_partition.partition_ts);
END LOOP;
ELSE
RAISE NOTICE 'Table old_edge_event already exists, leaving as is';
END IF;
END;
$$;
CREATE TABLE IF NOT EXISTS edge_event (
seq_id INT GENERATED ALWAYS AS IDENTITY,
id uuid NOT NULL,
created_time bigint NOT NULL,
edge_id uuid,
edge_event_type varchar(255),
edge_event_uid varchar(255),
entity_id uuid,
edge_event_action varchar(255),
body varchar(10000000),
tenant_id uuid,
ts bigint NOT NULL
) PARTITION BY RANGE (created_time);
CREATE INDEX IF NOT EXISTS idx_edge_event_tenant_id_and_created_time ON edge_event(tenant_id, created_time DESC);
CREATE INDEX IF NOT EXISTS idx_edge_event_id ON edge_event(id);
ALTER TABLE IF EXISTS edge_event ALTER COLUMN seq_id SET CYCLE;
CREATE OR REPLACE PROCEDURE migrate_edge_event(IN start_time_ms BIGINT, IN end_time_ms BIGINT, IN partition_size_ms BIGINT)
LANGUAGE plpgsql AS
$$
DECLARE
p RECORD;
partition_end_ts BIGINT;
BEGIN
IF (SELECT exists(SELECT FROM pg_tables WHERE tablename = 'old_edge_event')) THEN
FOR p IN SELECT DISTINCT (created_time - created_time % partition_size_ms) AS partition_ts FROM old_edge_event
WHERE created_time >= start_time_ms AND created_time < end_time_ms
LOOP
partition_end_ts = p.partition_ts + partition_size_ms;
RAISE NOTICE '[edge_event] Partition to create : [%-%]', p.partition_ts, partition_end_ts;
EXECUTE format('CREATE TABLE IF NOT EXISTS edge_event_%s PARTITION OF edge_event ' ||
'FOR VALUES FROM ( %s ) TO ( %s )', p.partition_ts, p.partition_ts, partition_end_ts);
END LOOP;
INSERT INTO edge_event (id, created_time, edge_id, edge_event_type, edge_event_uid, entity_id, edge_event_action, body, tenant_id, ts)
SELECT id, created_time, edge_id, edge_event_type, edge_event_uid, entity_id, edge_event_action, body, tenant_id, ts
FROM old_edge_event
WHERE created_time >= start_time_ms AND created_time < end_time_ms;
ELSE
RAISE NOTICE 'Table old_edge_event does not exists, skipping migration';
END IF;
END;
$$;
-- EDGE EVENTS MIGRATION END
ALTER TABLE resource ALTER TABLE resource
ADD COLUMN IF NOT EXISTS etag varchar; ADD COLUMN IF NOT EXISTS etag varchar;
UPDATE resource UPDATE resource
SET etag = encode(sha256(decode(resource.data, 'base64')),'hex') WHERE resource.data is not null; SET etag = encode(sha256(decode(resource.data, 'base64')),'hex') WHERE resource.data is not null;
ALTER TABLE notification_request ALTER COLUMN info SET DATA TYPE varchar(1000000);
ALTER TABLE widget_type
ADD COLUMN IF NOT EXISTS fqn varchar(512);
ALTER TABLE widget_type
ADD COLUMN IF NOT EXISTS deprecated boolean NOT NULL DEFAULT false;
DO
$$
BEGIN
IF NOT EXISTS(SELECT 1 FROM pg_constraint WHERE conname = 'uq_widget_type_fqn') THEN
UPDATE widget_type SET fqn = concat(widget_type.bundle_alias, '.', widget_type.alias);
ALTER TABLE widget_type ADD CONSTRAINT uq_widget_type_fqn UNIQUE (tenant_id, fqn);
ALTER TABLE widget_type DROP COLUMN IF EXISTS alias;
END IF;
END;
$$;

8
application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java

@ -48,6 +48,7 @@ import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
import org.thingsboard.server.common.msg.TbActorMsg; import org.thingsboard.server.common.msg.TbActorMsg;
import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.notification.NotificationRuleProcessor;
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.tools.TbRateLimits; import org.thingsboard.server.common.msg.tools.TbRateLimits;
@ -90,7 +91,6 @@ import org.thingsboard.server.dao.widget.WidgetsBundleService;
import org.thingsboard.server.queue.discovery.DiscoveryService; import org.thingsboard.server.queue.discovery.DiscoveryService;
import org.thingsboard.server.queue.discovery.PartitionService; import org.thingsboard.server.queue.discovery.PartitionService;
import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; import org.thingsboard.server.queue.discovery.TbServiceInfoProvider;
import org.thingsboard.server.common.msg.notification.NotificationRuleProcessor;
import org.thingsboard.server.queue.util.DataDecodingEncodingService; import org.thingsboard.server.queue.util.DataDecodingEncodingService;
import org.thingsboard.server.service.apiusage.TbApiUsageStateService; import org.thingsboard.server.service.apiusage.TbApiUsageStateService;
import org.thingsboard.server.service.component.ComponentDiscoveryService; import org.thingsboard.server.service.component.ComponentDiscoveryService;
@ -115,7 +115,6 @@ import org.thingsboard.server.service.transport.TbCoreToTransportService;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import java.io.IOException;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.io.StringWriter; import java.io.StringWriter;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
@ -247,6 +246,7 @@ public class ActorSystemContext {
private RuleNodeStateService ruleNodeStateService; private RuleNodeStateService ruleNodeStateService;
@Autowired @Autowired
@Getter
private PartitionService partitionService; private PartitionService partitionService;
@Autowired @Autowired
@ -536,6 +536,10 @@ public class ActorSystemContext {
@Getter @Getter
private int maxRpcRetries; private int maxRpcRetries;
@Value("${actors.rule.external.force_ack:false}")
@Getter
private boolean externalNodeForceAck;
@Getter @Getter
@Setter @Setter
private TbActorSystem actorSystem; private TbActorSystem actorSystem;

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

@ -46,6 +46,7 @@ import org.thingsboard.server.dao.tenant.TenantService;
import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper; import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper;
import java.util.HashSet; import java.util.HashSet;
import java.util.Optional;
import java.util.Set; import java.util.Set;
@Slf4j @Slf4j
@ -73,10 +74,14 @@ public class AppActor extends ContextAwareActor {
@Override @Override
protected boolean doProcess(TbActorMsg msg) { protected boolean doProcess(TbActorMsg msg) {
if (!ruleChainsInitialized) { if (!ruleChainsInitialized) {
initTenantActors(); if (MsgType.APP_INIT_MSG.equals(msg.getMsgType())) {
ruleChainsInitialized = true; initTenantActors();
if (msg.getMsgType() != MsgType.APP_INIT_MSG && msg.getMsgType() != MsgType.PARTITION_CHANGE_MSG) { ruleChainsInitialized = true;
log.warn("Rule Chains initialized by unexpected message: {}", msg); } else {
if (!msg.getMsgType().isIgnoreOnStart()) {
log.warn("Attempt to initialize Rule Chains by unexpected message: {}", msg);
}
return true;
} }
} }
switch (msg.getMsgType()) { switch (msg.getMsgType()) {
@ -125,8 +130,11 @@ public class AppActor extends ContextAwareActor {
PageDataIterable<Tenant> tenantIterator = new PageDataIterable<>(tenantService::findTenants, ENTITY_PACK_LIMIT); PageDataIterable<Tenant> tenantIterator = new PageDataIterable<>(tenantService::findTenants, ENTITY_PACK_LIMIT);
for (Tenant tenant : tenantIterator) { for (Tenant tenant : tenantIterator) {
log.debug("[{}] Creating tenant actor", tenant.getId()); log.debug("[{}] Creating tenant actor", tenant.getId());
getOrCreateTenantActor(tenant.getId()); getOrCreateTenantActor(tenant.getId()).ifPresentOrElse(tenantActor -> {
log.debug("[{}] Tenant actor created.", tenant.getId()); log.debug("[{}] Tenant actor created.", tenant.getId());
}, () -> {
log.debug("[{}] Skipped actor creation", tenant.getId());
});
} }
} }
log.info("Main system actor started."); log.info("Main system actor started.");
@ -139,11 +147,9 @@ public class AppActor extends ContextAwareActor {
if (TenantId.SYS_TENANT_ID.equals(msg.getTenantId())) { if (TenantId.SYS_TENANT_ID.equals(msg.getTenantId())) {
msg.getMsg().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())) { getOrCreateTenantActor(msg.getTenantId()).ifPresentOrElse(actor -> {
getOrCreateTenantActor(msg.getTenantId()).tell(msg); actor.tell(msg);
} else { }, () -> msg.getMsg().getCallback().onSuccess());
msg.getMsg().getCallback().onSuccess();
}
} }
} }
@ -160,12 +166,13 @@ public class AppActor extends ContextAwareActor {
log.info("[{}] Handling tenant deleted notification: {}", msg.getTenantId(), msg); log.info("[{}] Handling tenant deleted notification: {}", msg.getTenantId(), msg);
deletedTenants.add(tenantId); deletedTenants.add(tenantId);
ctx.stop(new TbEntityActorId(tenantId)); ctx.stop(new TbEntityActorId(tenantId));
} else { return;
target = getOrCreateTenantActor(msg.getTenantId());
} }
} else {
target = getOrCreateTenantActor(msg.getTenantId());
} }
target = getOrCreateTenantActor(msg.getTenantId()).orElseGet(() -> {
log.debug("Ignoring component lifecycle msg for tenant {} because it is not managed by this service", msg.getTenantId());
return null;
});
} }
if (target != null) { if (target != null) {
target.tellWithHighPriority(msg); target.tellWithHighPriority(msg);
@ -175,24 +182,28 @@ public class AppActor extends ContextAwareActor {
} }
private void onToDeviceActorMsg(TenantAwareMsg msg, boolean priority) { private void onToDeviceActorMsg(TenantAwareMsg msg, boolean priority) {
if (!deletedTenants.contains(msg.getTenantId())) { getOrCreateTenantActor(msg.getTenantId()).ifPresentOrElse(tenantActor -> {
TbActorRef tenantActor = getOrCreateTenantActor(msg.getTenantId());
if (priority) { if (priority) {
tenantActor.tellWithHighPriority(msg); tenantActor.tellWithHighPriority(msg);
} else { } else {
tenantActor.tell(msg); tenantActor.tell(msg);
} }
} else { }, () -> {
if (msg instanceof TransportToDeviceActorMsgWrapper) { if (msg instanceof TransportToDeviceActorMsgWrapper) {
((TransportToDeviceActorMsgWrapper) msg).getCallback().onSuccess(); ((TransportToDeviceActorMsgWrapper) msg).getCallback().onSuccess();
} }
} });
} }
private TbActorRef getOrCreateTenantActor(TenantId tenantId) { private Optional<TbActorRef> getOrCreateTenantActor(TenantId tenantId) {
return ctx.getOrCreateChildActor(new TbEntityActorId(tenantId), if (deletedTenants.contains(tenantId)) {
return Optional.empty();
}
return Optional.ofNullable(ctx.getOrCreateChildActor(new TbEntityActorId(tenantId),
() -> DefaultActorService.TENANT_DISPATCHER_NAME, () -> DefaultActorService.TENANT_DISPATCHER_NAME,
() -> new TenantActor.ActorCreator(systemContext, tenantId)); () -> new TenantActor.ActorCreator(systemContext, tenantId),
() -> systemContext.getServiceInfoProvider().isService(ServiceType.TB_CORE) ||
systemContext.getPartitionService().isManagedByCurrentService(tenantId)));
} }
private void onToEdgeSessionMsg(EdgeSessionMsg msg) { private void onToEdgeSessionMsg(EdgeSessionMsg msg) {
@ -200,7 +211,7 @@ public class AppActor extends ContextAwareActor {
if (ModelConstants.SYSTEM_TENANT.equals(msg.getTenantId())) { if (ModelConstants.SYSTEM_TENANT.equals(msg.getTenantId())) {
log.warn("Message has system tenant id: {}", msg); log.warn("Message has system tenant id: {}", msg);
} else { } else {
target = getOrCreateTenantActor(msg.getTenantId()); target = getOrCreateTenantActor(msg.getTenantId()).orElse(null);
} }
if (target != null) { if (target != null) {
target.tellWithHighPriority(msg); target.tellWithHighPriority(msg);

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

@ -15,7 +15,6 @@
*/ */
package org.thingsboard.server.actors.ruleChain; package org.thingsboard.server.actors.ruleChain;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.node.ObjectNode;
import io.netty.channel.EventLoopGroup; import io.netty.channel.EventLoopGroup;
@ -34,7 +33,7 @@ import org.thingsboard.rule.engine.api.RuleEngineTelemetryService;
import org.thingsboard.rule.engine.api.ScriptEngine; import org.thingsboard.rule.engine.api.ScriptEngine;
import org.thingsboard.rule.engine.api.SmsService; import org.thingsboard.rule.engine.api.SmsService;
import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.TbContext;
import org.thingsboard.rule.engine.api.TbRelationTypes; import org.thingsboard.rule.engine.api.TbNodeException;
import org.thingsboard.rule.engine.api.slack.SlackService; import org.thingsboard.rule.engine.api.slack.SlackService;
import org.thingsboard.rule.engine.api.sms.SmsSenderFactory; import org.thingsboard.rule.engine.api.sms.SmsSenderFactory;
import org.thingsboard.rule.engine.util.TenantIdLoader; import org.thingsboard.rule.engine.util.TenantIdLoader;
@ -42,7 +41,6 @@ import org.thingsboard.server.actors.ActorSystemContext;
import org.thingsboard.server.actors.TbActorRef; import org.thingsboard.server.actors.TbActorRef;
import org.thingsboard.server.cluster.TbClusterService; import org.thingsboard.server.cluster.TbClusterService;
import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityType;
@ -61,6 +59,8 @@ 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.kv.AttributeKvEntry; import org.thingsboard.server.common.data.kv.AttributeKvEntry;
import org.thingsboard.server.common.data.msg.TbMsgType;
import org.thingsboard.server.common.data.msg.TbNodeConnectionType;
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.rule.RuleNode; import org.thingsboard.server.common.data.rule.RuleNode;
@ -114,6 +114,10 @@ import java.util.concurrent.TimeUnit;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
import java.util.function.Consumer; import java.util.function.Consumer;
import static org.thingsboard.server.common.data.msg.TbMsgType.ATTRIBUTES_DELETED;
import static org.thingsboard.server.common.data.msg.TbMsgType.ATTRIBUTES_UPDATED;
import static org.thingsboard.server.common.data.msg.TbMsgType.ENTITY_CREATED;
/** /**
* Created by ashvayka on 19.03.18. * Created by ashvayka on 19.03.18.
*/ */
@ -132,7 +136,7 @@ class DefaultTbContext implements TbContext {
@Override @Override
public void tellSuccess(TbMsg msg) { public void tellSuccess(TbMsg msg) {
tellNext(msg, Collections.singleton(TbRelationTypes.SUCCESS), null); tellNext(msg, Collections.singleton(TbNodeConnectionType.SUCCESS), null);
} }
@Override @Override
@ -211,7 +215,13 @@ class DefaultTbContext implements TbContext {
@Override @Override
public void enqueueForTellFailure(TbMsg tbMsg, String failureMessage) { public void enqueueForTellFailure(TbMsg tbMsg, String failureMessage) {
TopicPartitionInfo tpi = resolvePartition(tbMsg); TopicPartitionInfo tpi = resolvePartition(tbMsg);
enqueueForTellNext(tpi, tbMsg, Collections.singleton(TbRelationTypes.FAILURE), failureMessage, null, null); enqueueForTellNext(tpi, tbMsg, Collections.singleton(TbNodeConnectionType.FAILURE), failureMessage, null, null);
}
@Override
public void enqueueForTellFailure(TbMsg tbMsg, Throwable th) {
TopicPartitionInfo tpi = resolvePartition(tbMsg);
enqueueForTellNext(tpi, tbMsg, Collections.singleton(TbNodeConnectionType.FAILURE), getFailureMessage(th), null, null);
} }
@Override @Override
@ -309,20 +319,11 @@ class DefaultTbContext implements TbContext {
@Override @Override
public void tellFailure(TbMsg msg, Throwable th) { public void tellFailure(TbMsg msg, Throwable th) {
if (nodeCtx.getSelf().isDebugMode()) { if (nodeCtx.getSelf().isDebugMode()) {
mainCtx.persistDebugOutput(nodeCtx.getTenantId(), nodeCtx.getSelf().getId(), msg, TbRelationTypes.FAILURE, th); mainCtx.persistDebugOutput(nodeCtx.getTenantId(), nodeCtx.getSelf().getId(), msg, TbNodeConnectionType.FAILURE, th);
}
String failureMessage;
if (th != null) {
if (!StringUtils.isEmpty(th.getMessage())) {
failureMessage = th.getMessage();
} else {
failureMessage = th.getClass().getSimpleName();
}
} else {
failureMessage = null;
} }
String failureMessage = getFailureMessage(th);
nodeCtx.getChainActor().tell(new RuleNodeToRuleChainTellNextMsg(nodeCtx.getSelf().getRuleChainId(), nodeCtx.getChainActor().tell(new RuleNodeToRuleChainTellNextMsg(nodeCtx.getSelf().getRuleChainId(),
nodeCtx.getSelf().getId(), Collections.singleton(TbRelationTypes.FAILURE), nodeCtx.getSelf().getId(), Collections.singleton(TbNodeConnectionType.FAILURE),
msg, failureMessage)); msg, failureMessage));
} }
@ -345,67 +346,104 @@ class DefaultTbContext implements TbContext {
return TbMsg.transformMsg(origMsg, type, originator, metaData, data); return TbMsg.transformMsg(origMsg, type, originator, metaData, data);
} }
@Override
public TbMsg newMsg(String queueName, TbMsgType type, EntityId originator, TbMsgMetaData metaData, String data) {
return newMsg(queueName, type, originator, null, metaData, data);
}
@Override
public TbMsg newMsg(String queueName, TbMsgType type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, String data) {
return TbMsg.newMsg(queueName, type, originator, customerId, metaData, data, nodeCtx.getSelf().getRuleChainId(), nodeCtx.getSelf().getId());
}
@Override
public TbMsg transformMsg(TbMsg origMsg, TbMsgType type, EntityId originator, TbMsgMetaData metaData, String data) {
return TbMsg.transformMsg(origMsg, type, originator, metaData, data);
}
@Override
public TbMsg transformMsg(TbMsg origMsg, TbMsgMetaData metaData, String data) {
return TbMsg.transformMsg(origMsg, metaData, data);
}
@Override
public TbMsg transformMsgOriginator(TbMsg origMsg, EntityId originator) {
return TbMsg.transformMsgOriginator(origMsg, originator);
}
@Override
public TbMsg customerCreatedMsg(Customer customer, RuleNodeId ruleNodeId) { public TbMsg customerCreatedMsg(Customer customer, RuleNodeId ruleNodeId) {
return entityActionMsg(customer, customer.getId(), ruleNodeId, DataConstants.ENTITY_CREATED); return entityActionMsg(customer, customer.getId(), ruleNodeId, ENTITY_CREATED);
} }
@Override
public TbMsg deviceCreatedMsg(Device device, RuleNodeId ruleNodeId) { public TbMsg deviceCreatedMsg(Device device, RuleNodeId ruleNodeId) {
DeviceProfile deviceProfile = null; DeviceProfile deviceProfile = null;
if (device.getDeviceProfileId() != null) { if (device.getDeviceProfileId() != null) {
deviceProfile = mainCtx.getDeviceProfileCache().find(device.getDeviceProfileId()); deviceProfile = mainCtx.getDeviceProfileCache().find(device.getDeviceProfileId());
} }
return entityActionMsg(device, device.getId(), ruleNodeId, DataConstants.ENTITY_CREATED, deviceProfile); return entityActionMsg(device, device.getId(), ruleNodeId, ENTITY_CREATED, deviceProfile);
} }
@Override
public TbMsg assetCreatedMsg(Asset asset, RuleNodeId ruleNodeId) { public TbMsg assetCreatedMsg(Asset asset, RuleNodeId ruleNodeId) {
AssetProfile assetProfile = null; AssetProfile assetProfile = null;
if (asset.getAssetProfileId() != null) { if (asset.getAssetProfileId() != null) {
assetProfile = mainCtx.getAssetProfileCache().find(asset.getAssetProfileId()); assetProfile = mainCtx.getAssetProfileCache().find(asset.getAssetProfileId());
} }
return entityActionMsg(asset, asset.getId(), ruleNodeId, DataConstants.ENTITY_CREATED, assetProfile); return entityActionMsg(asset, asset.getId(), ruleNodeId, ENTITY_CREATED, assetProfile);
} }
@Override
public TbMsg alarmActionMsg(Alarm alarm, RuleNodeId ruleNodeId, String action) { public TbMsg alarmActionMsg(Alarm alarm, RuleNodeId ruleNodeId, String action) {
EntityId originator = alarm.getOriginator();
HasRuleEngineProfile profile = getRuleEngineProfile(originator);
return entityActionMsg(alarm, originator, ruleNodeId, action, profile);
}
@Override
public TbMsg alarmActionMsg(Alarm alarm, RuleNodeId ruleNodeId, TbMsgType actionMsgType) {
EntityId originator = alarm.getOriginator();
HasRuleEngineProfile profile = getRuleEngineProfile(originator);
return entityActionMsg(alarm, originator, ruleNodeId, actionMsgType, profile);
}
private HasRuleEngineProfile getRuleEngineProfile(EntityId originator) {
HasRuleEngineProfile profile = null; HasRuleEngineProfile profile = null;
if (EntityType.DEVICE.equals(alarm.getOriginator().getEntityType())) { if (EntityType.DEVICE.equals(originator.getEntityType())) {
DeviceId deviceId = new DeviceId(alarm.getOriginator().getId()); DeviceId deviceId = new DeviceId(originator.getId());
profile = mainCtx.getDeviceProfileCache().get(getTenantId(), deviceId); profile = mainCtx.getDeviceProfileCache().get(getTenantId(), deviceId);
} else if (EntityType.ASSET.equals(alarm.getOriginator().getEntityType())) { } else if (EntityType.ASSET.equals(originator.getEntityType())) {
AssetId assetId = new AssetId(alarm.getOriginator().getId()); AssetId assetId = new AssetId(originator.getId());
profile = mainCtx.getAssetProfileCache().get(getTenantId(), assetId); profile = mainCtx.getAssetProfileCache().get(getTenantId(), assetId);
} }
return entityActionMsg(alarm, alarm.getOriginator(), ruleNodeId, action, profile); return profile;
} }
@Override
public TbMsg attributesUpdatedActionMsg(EntityId originator, RuleNodeId ruleNodeId, String scope, List<AttributeKvEntry> attributes) { public TbMsg attributesUpdatedActionMsg(EntityId originator, RuleNodeId ruleNodeId, String scope, List<AttributeKvEntry> attributes) {
ObjectNode entityNode = JacksonUtil.newObjectNode(); ObjectNode entityNode = JacksonUtil.newObjectNode();
if (attributes != null) { if (attributes != null) {
attributes.forEach(attributeKvEntry -> JacksonUtil.addKvEntry(entityNode, attributeKvEntry)); attributes.forEach(attributeKvEntry -> JacksonUtil.addKvEntry(entityNode, attributeKvEntry));
} }
return attributesActionMsg(originator, ruleNodeId, scope, DataConstants.ATTRIBUTES_UPDATED, JacksonUtil.toString(entityNode)); return attributesActionMsg(originator, ruleNodeId, scope, ATTRIBUTES_UPDATED, JacksonUtil.toString(entityNode));
} }
@Override
public TbMsg attributesDeletedActionMsg(EntityId originator, RuleNodeId ruleNodeId, String scope, List<String> keys) { public TbMsg attributesDeletedActionMsg(EntityId originator, RuleNodeId ruleNodeId, String scope, List<String> keys) {
ObjectNode entityNode = JacksonUtil.newObjectNode(); ObjectNode entityNode = JacksonUtil.newObjectNode();
ArrayNode attrsArrayNode = entityNode.putArray("attributes"); ArrayNode attrsArrayNode = entityNode.putArray("attributes");
if (keys != null) { if (keys != null) {
keys.forEach(attrsArrayNode::add); keys.forEach(attrsArrayNode::add);
} }
return attributesActionMsg(originator, ruleNodeId, scope, DataConstants.ATTRIBUTES_DELETED, JacksonUtil.toString(entityNode)); return attributesActionMsg(originator, ruleNodeId, scope, ATTRIBUTES_DELETED, JacksonUtil.toString(entityNode));
} }
private TbMsg attributesActionMsg(EntityId originator, RuleNodeId ruleNodeId, String scope, String action, String msgData) { private TbMsg attributesActionMsg(EntityId originator, RuleNodeId ruleNodeId, String scope, TbMsgType actionMsgType, String msgData) {
TbMsgMetaData tbMsgMetaData = getActionMetaData(ruleNodeId); TbMsgMetaData tbMsgMetaData = getActionMetaData(ruleNodeId);
tbMsgMetaData.putValue("scope", scope); tbMsgMetaData.putValue("scope", scope);
HasRuleEngineProfile profile = null; HasRuleEngineProfile profile = getRuleEngineProfile(originator);
if (EntityType.DEVICE.equals(originator.getEntityType())) { return entityActionMsg(originator, tbMsgMetaData, msgData, actionMsgType, profile);
DeviceId deviceId = new DeviceId(originator.getId());
profile = mainCtx.getDeviceProfileCache().get(getTenantId(), deviceId);
} else if (EntityType.ASSET.equals(originator.getEntityType())) {
AssetId assetId = new AssetId(originator.getId());
profile = mainCtx.getAssetProfileCache().get(getTenantId(), assetId);
}
return entityActionMsg(originator, tbMsgMetaData, msgData, action, profile);
} }
@Override @Override
@ -413,10 +451,11 @@ class DefaultTbContext implements TbContext {
mainCtx.getClusterService().onEdgeEventUpdate(tenantId, edgeId); mainCtx.getClusterService().onEdgeEventUpdate(tenantId, edgeId);
} }
public <E, I extends EntityId> TbMsg entityActionMsg(E entity, I id, RuleNodeId ruleNodeId, String action) { public <E, I extends EntityId> TbMsg entityActionMsg(E entity, I id, RuleNodeId ruleNodeId, TbMsgType actionMsgType) {
return entityActionMsg(entity, id, ruleNodeId, action, null); return entityActionMsg(entity, id, ruleNodeId, actionMsgType, null);
} }
@Deprecated(since = "3.5.2", forRemoval = true)
public <E, I extends EntityId, K extends HasRuleEngineProfile> TbMsg entityActionMsg(E entity, I id, RuleNodeId ruleNodeId, String action, K profile) { public <E, I extends EntityId, K extends HasRuleEngineProfile> TbMsg entityActionMsg(E entity, I id, RuleNodeId ruleNodeId, String action, K profile) {
try { try {
return entityActionMsg(id, getActionMetaData(ruleNodeId), JacksonUtil.toString(JacksonUtil.valueToTree(entity)), action, profile); return entityActionMsg(id, getActionMetaData(ruleNodeId), JacksonUtil.toString(JacksonUtil.valueToTree(entity)), action, profile);
@ -425,6 +464,7 @@ class DefaultTbContext implements TbContext {
} }
} }
@Deprecated(since = "3.5.2", forRemoval = true)
private <I extends EntityId, K extends HasRuleEngineProfile> TbMsg entityActionMsg(I id, TbMsgMetaData msgMetaData, String msgData, String action, K profile) { private <I extends EntityId, K extends HasRuleEngineProfile> TbMsg entityActionMsg(I id, TbMsgMetaData msgMetaData, String msgData, String action, K profile) {
String defaultQueueName = null; String defaultQueueName = null;
RuleChainId defaultRuleChainId = null; RuleChainId defaultRuleChainId = null;
@ -435,6 +475,24 @@ class DefaultTbContext implements TbContext {
return TbMsg.newMsg(defaultQueueName, action, id, msgMetaData, msgData, defaultRuleChainId, null); return TbMsg.newMsg(defaultQueueName, action, id, msgMetaData, msgData, defaultRuleChainId, null);
} }
public <E, I extends EntityId, K extends HasRuleEngineProfile> TbMsg entityActionMsg(E entity, I id, RuleNodeId ruleNodeId, TbMsgType actionMsgType, K profile) {
try {
return entityActionMsg(id, getActionMetaData(ruleNodeId), JacksonUtil.toString(JacksonUtil.valueToTree(entity)), actionMsgType, profile);
} catch (IllegalArgumentException e) {
throw new RuntimeException("Failed to process " + id.getEntityType().name().toLowerCase() + " " + actionMsgType.name() + " msg: " + e);
}
}
private <I extends EntityId, K extends HasRuleEngineProfile> TbMsg entityActionMsg(I id, TbMsgMetaData msgMetaData, String msgData, TbMsgType actionMsgType, K profile) {
String defaultQueueName = null;
RuleChainId defaultRuleChainId = null;
if (profile != null) {
defaultQueueName = profile.getDefaultQueueName();
defaultRuleChainId = profile.getDefaultRuleChainId();
}
return TbMsg.newMsg(defaultQueueName, actionMsgType, id, msgMetaData, msgData, defaultRuleChainId, null);
}
@Override @Override
public RuleNodeId getSelfId() { public RuleNodeId getSelfId() {
return nodeCtx.getSelf().getId(); return nodeCtx.getSelf().getId();
@ -724,6 +782,11 @@ class DefaultTbContext implements TbContext {
return mainCtx.getSlackService(); return mainCtx.getSlackService();
} }
@Override
public boolean isExternalNodeForceAck() {
return mainCtx.isExternalNodeForceAck();
}
@Override @Override
public RuleEngineRpcService getRpcService() { public RuleEngineRpcService getRpcService() {
return mainCtx.getTbRuleEngineDeviceRpcService(); return mainCtx.getTbRuleEngineDeviceRpcService();
@ -840,10 +903,24 @@ class DefaultTbContext implements TbContext {
} }
@Override @Override
public void checkTenantEntity(EntityId entityId) { public void checkTenantEntity(EntityId entityId) throws TbNodeException {
if (!this.getTenantId().equals(TenantIdLoader.findTenantId(this, entityId))) { if (!this.getTenantId().equals(TenantIdLoader.findTenantId(this, entityId))) {
throw new RuntimeException("Entity with id: '" + entityId + "' specified in the configuration doesn't belong to the current tenant."); throw new TbNodeException("Entity with id: '" + entityId + "' specified in the configuration doesn't belong to the current tenant.", true);
}
}
private static String getFailureMessage(Throwable th) {
String failureMessage;
if (th != null) {
if (!StringUtils.isEmpty(th.getMessage())) {
failureMessage = th.getMessage();
} else {
failureMessage = th.getClass().getSimpleName();
}
} else {
failureMessage = null;
} }
return failureMessage;
} }
private class SimpleTbQueueCallback implements TbQueueCallback { private class SimpleTbQueueCallback implements TbQueueCallback {

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

@ -16,7 +16,7 @@
package org.thingsboard.server.actors.ruleChain; package org.thingsboard.server.actors.ruleChain;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.thingsboard.rule.engine.api.TbRelationTypes; import org.thingsboard.server.common.data.msg.TbNodeConnectionType;
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;
@ -167,7 +167,8 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
private TbActorRef createRuleNodeActor(TbActorCtx ctx, RuleNode ruleNode) { private TbActorRef createRuleNodeActor(TbActorCtx ctx, RuleNode ruleNode) {
return ctx.getOrCreateChildActor(new TbEntityActorId(ruleNode.getId()), return ctx.getOrCreateChildActor(new TbEntityActorId(ruleNode.getId()),
() -> DefaultActorService.RULE_DISPATCHER_NAME, () -> DefaultActorService.RULE_DISPATCHER_NAME,
() -> new RuleNodeActor.ActorCreator(systemContext, tenantId, entityId, ruleChainName, ruleNode.getId())); () -> new RuleNodeActor.ActorCreator(systemContext, tenantId, entityId, ruleChainName, ruleNode.getId()),
() -> true);
} }
private void initRoutes(RuleChain ruleChain, List<RuleNode> ruleNodeList) { private void initRoutes(RuleChain ruleChain, List<RuleNode> ruleNodeList) {
@ -232,7 +233,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
} catch (RuleNodeException rne) { } catch (RuleNodeException rne) {
msg.getCallback().onFailure(rne); msg.getCallback().onFailure(rne);
} catch (Exception e) { } catch (Exception e) {
msg.getCallback().onFailure(new RuleEngineException(e.getMessage())); msg.getCallback().onFailure(new RuleEngineException(e.getMessage(), e));
} }
} }
@ -307,7 +308,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
int relationsCount = relationsByTypes.size(); int relationsCount = relationsByTypes.size();
if (relationsCount == 0) { if (relationsCount == 0) {
log.trace("[{}][{}][{}] No outbound relations to process", tenantId, entityId, msg.getId()); log.trace("[{}][{}][{}] No outbound relations to process", tenantId, entityId, msg.getId());
if (relationTypes.contains(TbRelationTypes.FAILURE)) { if (relationTypes.contains(TbNodeConnectionType.FAILURE)) {
RuleNodeCtx ruleNodeCtx = nodeActors.get(originatorNodeId); RuleNodeCtx ruleNodeCtx = nodeActors.get(originatorNodeId);
if (ruleNodeCtx != null) { if (ruleNodeCtx != null) {
msg.getCallback().onFailure(new RuleNodeException(failureMessage, ruleChainName, ruleNodeCtx.getSelf())); msg.getCallback().onFailure(new RuleNodeException(failureMessage, ruleChainName, ruleNodeCtx.getSelf()));
@ -335,7 +336,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
msg.getCallback().onFailure(rne); msg.getCallback().onFailure(rne);
} catch (Exception e) { } catch (Exception e) {
log.warn("[" + tenantId + "]" + "[" + entityId + "]" + "[" + msg.getId() + "]" + " onTellNext failure", e); log.warn("[" + tenantId + "]" + "[" + entityId + "]" + "[" + msg.getId() + "]" + " onTellNext failure", e);
msg.getCallback().onFailure(new RuleEngineException("onTellNext - " + e.getMessage())); msg.getCallback().onFailure(new RuleEngineException("onTellNext - " + e.getMessage(), e));
} }
} }

12
application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainManagerActor.java

@ -23,6 +23,7 @@ import org.thingsboard.server.actors.TbEntityActorId;
import org.thingsboard.server.actors.TbEntityTypeActorIdPredicate; import org.thingsboard.server.actors.TbEntityTypeActorIdPredicate;
import org.thingsboard.server.actors.service.ContextAwareActor; import org.thingsboard.server.actors.service.ContextAwareActor;
import org.thingsboard.server.actors.service.DefaultActorService; import org.thingsboard.server.actors.service.DefaultActorService;
import org.thingsboard.server.actors.shared.RuleChainErrorActor;
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;
@ -31,6 +32,7 @@ import org.thingsboard.server.common.data.page.PageDataIterable;
import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.common.data.rule.RuleChainType;
import org.thingsboard.server.common.msg.TbActorMsg; import org.thingsboard.server.common.msg.TbActorMsg;
import org.thingsboard.server.common.msg.queue.RuleEngineException;
import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.rule.RuleChainService;
import java.util.function.Function; import java.util.function.Function;
@ -86,8 +88,14 @@ public abstract class RuleChainManagerActor extends ContextAwareActor {
() -> DefaultActorService.RULE_DISPATCHER_NAME, () -> DefaultActorService.RULE_DISPATCHER_NAME,
() -> { () -> {
RuleChain ruleChain = provider.apply(ruleChainId); RuleChain ruleChain = provider.apply(ruleChainId);
return new RuleChainActor.ActorCreator(systemContext, tenantId, ruleChain); if (ruleChain == null) {
}); return new RuleChainErrorActor.ActorCreator(systemContext, tenantId,
new RuleEngineException("Rule Chain with id: " + ruleChainId + " not found!"));
} else {
return new RuleChainActor.ActorCreator(systemContext, tenantId, ruleChain);
}
},
() -> true);
} }
protected TbActorRef getEntityActorRef(EntityId entityId) { protected TbActorRef getEntityActorRef(EntityId entityId) {

2
application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleEngineComponentActor.java

@ -22,9 +22,9 @@ import org.thingsboard.server.actors.shared.ComponentMsgProcessor;
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;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.notification.rule.trigger.RuleEngineComponentLifecycleEventTrigger;
import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
import org.thingsboard.server.common.msg.TbActorStopReason; import org.thingsboard.server.common.msg.TbActorStopReason;
import org.thingsboard.server.common.data.notification.rule.trigger.RuleEngineComponentLifecycleEventTrigger;
public abstract class RuleEngineComponentActor<T extends EntityId, P extends ComponentMsgProcessor<T>> extends ComponentActor<T, P> { public abstract class RuleEngineComponentActor<T extends EntityId, P extends ComponentMsgProcessor<T>> extends ComponentActor<T, P> {

78
application/src/main/java/org/thingsboard/server/actors/shared/RuleChainErrorActor.java

@ -0,0 +1,78 @@
/**
* Copyright © 2016-2023 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.shared;
import lombok.extern.slf4j.Slf4j;
import org.thingsboard.server.actors.ActorSystemContext;
import org.thingsboard.server.actors.TbActor;
import org.thingsboard.server.actors.TbActorId;
import org.thingsboard.server.actors.TbStringActorId;
import org.thingsboard.server.actors.service.ContextAwareActor;
import org.thingsboard.server.actors.service.ContextBasedCreator;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.msg.TbActorMsg;
import org.thingsboard.server.common.msg.aware.RuleChainAwareMsg;
import org.thingsboard.server.common.msg.queue.RuleEngineException;
import java.util.UUID;
@Slf4j
public class RuleChainErrorActor extends ContextAwareActor {
private final TenantId tenantId;
private final RuleEngineException error;
private RuleChainErrorActor(ActorSystemContext systemContext, TenantId tenantId, RuleEngineException error) {
super(systemContext);
this.tenantId = tenantId;
this.error = error;
}
@Override
protected boolean doProcess(TbActorMsg msg) {
if (msg instanceof RuleChainAwareMsg) {
log.debug("[{}] Reply with {} for message {}", tenantId, error.getMessage(), msg);
var rcMsg = (RuleChainAwareMsg) msg;
rcMsg.getMsg().getCallback().onFailure(error);
return true;
} else {
return false;
}
}
public static class ActorCreator extends ContextBasedCreator {
private final TenantId tenantId;
private final RuleEngineException error;
public ActorCreator(ActorSystemContext context, TenantId tenantId, RuleEngineException error) {
super(context);
this.tenantId = tenantId;
this.error = error;
}
@Override
public TbActorId createActorId() {
return new TbStringActorId(UUID.randomUUID().toString());
}
@Override
public TbActor createActor() {
return new RuleChainErrorActor(context, tenantId, error);
}
}
}

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

@ -32,7 +32,6 @@ import org.thingsboard.server.actors.service.DefaultActorService;
import org.thingsboard.server.common.data.ApiUsageState; import org.thingsboard.server.common.data.ApiUsageState;
import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.TenantProfile;
import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.EdgeId;
@ -82,21 +81,21 @@ public class TenantActor extends RuleChainManagerActor {
cantFindTenant = true; cantFindTenant = true;
log.info("[{}] Started tenant actor for missing tenant.", tenantId); log.info("[{}] Started tenant actor for missing tenant.", tenantId);
} else { } else {
TenantProfile tenantProfile = systemContext.getTenantProfileCache().get(tenant.getTenantProfileId());
isCore = systemContext.getServiceInfoProvider().isService(ServiceType.TB_CORE); isCore = systemContext.getServiceInfoProvider().isService(ServiceType.TB_CORE);
isRuleEngine = systemContext.getServiceInfoProvider().isService(ServiceType.TB_RULE_ENGINE); isRuleEngine = systemContext.getServiceInfoProvider().isService(ServiceType.TB_RULE_ENGINE);
if (isRuleEngine) { if (isRuleEngine) {
try { if (systemContext.getPartitionService().isManagedByCurrentService(tenantId)) {
if (getApiUsageState().isReExecEnabled()) { try {
log.debug("[{}] Going to init rule chains", tenantId); if (getApiUsageState().isReExecEnabled()) {
initRuleChains(); log.debug("[{}] Going to init rule chains", tenantId);
} else { initRuleChains();
log.info("[{}] Skip init of the rule chains due to API limits", tenantId); } else {
log.info("[{}] Skip init of the rule chains due to API limits", tenantId);
}
} catch (Exception e) {
log.info("Failed to check ApiUsage \"ReExecEnabled\"!!!", e);
cantFindTenant = true;
} }
} catch (Exception e) {
log.info("Failed to check ApiUsage \"ReExecEnabled\"!!!", e);
cantFindTenant = true;
} }
} }
log.debug("[{}] Tenant actor started.", tenantId); log.debug("[{}] Tenant actor started.", tenantId);
@ -270,7 +269,8 @@ public class TenantActor extends RuleChainManagerActor {
private TbActorRef getOrCreateDeviceActor(DeviceId deviceId) { private TbActorRef getOrCreateDeviceActor(DeviceId deviceId) {
return ctx.getOrCreateChildActor(new TbEntityActorId(deviceId), return ctx.getOrCreateChildActor(new TbEntityActorId(deviceId),
() -> DefaultActorService.DEVICE_DISPATCHER_NAME, () -> DefaultActorService.DEVICE_DISPATCHER_NAME,
() -> new DeviceActorCreator(systemContext, tenantId, deviceId)); () -> new DeviceActorCreator(systemContext, tenantId, deviceId),
() -> true);
} }
private void onToEdgeSessionMsg(EdgeSessionMsg msg) { private void onToEdgeSessionMsg(EdgeSessionMsg msg) {

4
application/src/main/java/org/thingsboard/server/config/RateLimitProcessingFilter.java

@ -24,10 +24,10 @@ import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter; import org.springframework.web.filter.OncePerRequestFilter;
import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.exception.TenantProfileNotFoundException; import org.thingsboard.server.common.data.exception.TenantProfileNotFoundException;
import org.thingsboard.server.common.msg.tools.TbRateLimitsException;
import org.thingsboard.server.exception.ThingsboardErrorResponseHandler;
import org.thingsboard.server.common.data.limit.LimitedApi; import org.thingsboard.server.common.data.limit.LimitedApi;
import org.thingsboard.server.common.msg.tools.TbRateLimitsException;
import org.thingsboard.server.dao.util.limits.RateLimitService; import org.thingsboard.server.dao.util.limits.RateLimitService;
import org.thingsboard.server.exception.ThingsboardErrorResponseHandler;
import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.model.SecurityUser;
import javax.servlet.FilterChain; import javax.servlet.FilterChain;

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

@ -19,9 +19,11 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.security.SecurityProperties; import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order; import org.springframework.core.annotation.Order;
import org.springframework.http.HttpHeaders;
import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.DefaultAuthenticationEventPublisher; import org.springframework.security.authentication.DefaultAuthenticationEventPublisher;
import org.springframework.security.config.annotation.ObjectPostProcessor; import org.springframework.security.config.annotation.ObjectPostProcessor;
@ -29,7 +31,6 @@ import org.springframework.security.config.annotation.authentication.builders.Au
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestResolver; import org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestResolver;
@ -37,9 +38,11 @@ import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.AuthenticationFailureHandler; import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler; import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.header.writers.StaticHeadersWriter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter; import org.springframework.web.filter.CorsFilter;
import org.springframework.web.filter.ShallowEtagHeaderFilter;
import org.thingsboard.server.dao.oauth2.OAuth2Configuration; import org.thingsboard.server.dao.oauth2.OAuth2Configuration;
import org.thingsboard.server.exception.ThingsboardErrorResponseHandler; import org.thingsboard.server.exception.ThingsboardErrorResponseHandler;
import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.queue.util.TbCoreComponent;
@ -78,6 +81,7 @@ public class ThingsboardSecurityConfiguration {
public static final String TOKEN_BASED_AUTH_ENTRY_POINT = "/api/**"; public static final String TOKEN_BASED_AUTH_ENTRY_POINT = "/api/**";
public static final String WS_TOKEN_BASED_AUTH_ENTRY_POINT = "/api/ws/**"; public static final String WS_TOKEN_BASED_AUTH_ENTRY_POINT = "/api/ws/**";
public static final String MAIL_OAUTH2_PROCESSING_ENTRY_POINT = "/api/admin/mail/oauth2/code"; public static final String MAIL_OAUTH2_PROCESSING_ENTRY_POINT = "/api/admin/mail/oauth2/code";
public static final String DEVICE_CONNECTIVITY_CERTIFICATE_DOWNLOAD_ENTRY_POINT = "/api/device-connectivity/mqtts/certificate/download";
@Autowired private ThingsboardErrorResponseHandler restAccessDeniedHandler; @Autowired private ThingsboardErrorResponseHandler restAccessDeniedHandler;
@ -119,6 +123,17 @@ public class ThingsboardSecurityConfiguration {
@Autowired private RateLimitProcessingFilter rateLimitProcessingFilter; @Autowired private RateLimitProcessingFilter rateLimitProcessingFilter;
@Bean
protected FilterRegistrationBean<ShallowEtagHeaderFilter> buildEtagFilter() throws Exception {
ShallowEtagHeaderFilter etagFilter = new ShallowEtagHeaderFilter();
etagFilter.setWriteWeakETag(true);
FilterRegistrationBean<ShallowEtagHeaderFilter> filterRegistrationBean
= new FilterRegistrationBean<>( etagFilter);
filterRegistrationBean.addUrlPatterns("*.js","*.css","*.ico","/assets/*","/static/*");
filterRegistrationBean.setName("etagFilter");
return filterRegistrationBean;
}
@Bean @Bean
protected RestLoginProcessingFilter buildRestLoginProcessingFilter() throws Exception { protected RestLoginProcessingFilter buildRestLoginProcessingFilter() throws Exception {
RestLoginProcessingFilter filter = new RestLoginProcessingFilter(FORM_BASED_LOGIN_ENTRY_POINT, successHandler, failureHandler); RestLoginProcessingFilter filter = new RestLoginProcessingFilter(FORM_BASED_LOGIN_ENTRY_POINT, successHandler, failureHandler);
@ -136,7 +151,8 @@ public class ThingsboardSecurityConfiguration {
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, MAIL_OAUTH2_PROCESSING_ENTRY_POINT)); PUBLIC_LOGIN_ENTRY_POINT, DEVICE_API_ENTRY_POINT, WEBJARS_ENTRY_POINT, MAIL_OAUTH2_PROCESSING_ENTRY_POINT,
DEVICE_CONNECTIVITY_CERTIFICATE_DOWNLOAD_ENTRY_POINT));
SkipPathRequestMatcher matcher = new SkipPathRequestMatcher(pathsToSkip, TOKEN_BASED_AUTH_ENTRY_POINT); SkipPathRequestMatcher matcher = new SkipPathRequestMatcher(pathsToSkip, TOKEN_BASED_AUTH_ENTRY_POINT);
JwtTokenAuthenticationProcessingFilter filter JwtTokenAuthenticationProcessingFilter filter
= new JwtTokenAuthenticationProcessingFilter(failureHandler, jwtHeaderTokenExtractor, matcher); = new JwtTokenAuthenticationProcessingFilter(failureHandler, jwtHeaderTokenExtractor, matcher);
@ -181,8 +197,18 @@ public class ThingsboardSecurityConfiguration {
private OAuth2AuthorizationRequestResolver oAuth2AuthorizationRequestResolver; private OAuth2AuthorizationRequestResolver oAuth2AuthorizationRequestResolver;
@Bean @Bean
public WebSecurityCustomizer webSecurityCustomizer() { @Order(0)
return (web) -> web.ignoring().antMatchers("/*.js","/*.css","/*.ico","/assets/**","/static/**"); SecurityFilterChain resources(HttpSecurity http) throws Exception {
http
.requestMatchers((matchers) -> matchers.antMatchers("/*.js","/*.css","/*.ico","/assets/**","/static/**"))
.headers().defaultsDisabled()
.addHeaderWriter(new StaticHeadersWriter(HttpHeaders.CACHE_CONTROL, "max-age=0, public"))
.and()
.authorizeHttpRequests((authorize) -> authorize.anyRequest().permitAll())
.requestCache().disable()
.securityContext().disable()
.sessionManagement().disable();
return http.build();
} }
@Bean @Bean
@ -204,6 +230,7 @@ public class ThingsboardSecurityConfiguration {
.antMatchers(PUBLIC_LOGIN_ENTRY_POINT).permitAll() // Public login end-point .antMatchers(PUBLIC_LOGIN_ENTRY_POINT).permitAll() // Public login end-point
.antMatchers(TOKEN_REFRESH_ENTRY_POINT).permitAll() // Token refresh end-point .antMatchers(TOKEN_REFRESH_ENTRY_POINT).permitAll() // Token refresh end-point
.antMatchers(MAIL_OAUTH2_PROCESSING_ENTRY_POINT).permitAll() // Mail oauth2 code processing url .antMatchers(MAIL_OAUTH2_PROCESSING_ENTRY_POINT).permitAll() // Mail oauth2 code processing url
.antMatchers(DEVICE_CONNECTIVITY_CERTIFICATE_DOWNLOAD_ENTRY_POINT).permitAll() // Mail oauth2 code processing url
.antMatchers(NON_TOKEN_BASED_AUTH_ENTRY_POINTS).permitAll() // static resources, user activation and password reset end-points .antMatchers(NON_TOKEN_BASED_AUTH_ENTRY_POINTS).permitAll() // static resources, user activation and password reset end-points
.and() .and()
.authorizeRequests() .authorizeRequests()

9
application/src/main/java/org/thingsboard/server/controller/AuthController.java

@ -38,19 +38,18 @@ import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.rule.engine.api.MailService; import org.thingsboard.rule.engine.api.MailService;
import org.thingsboard.server.common.data.User; 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.EdgeEventActionType;
import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.limit.LimitedApi;
import org.thingsboard.server.common.data.security.UserCredentials; import org.thingsboard.server.common.data.security.UserCredentials;
import org.thingsboard.server.common.data.security.event.UserCredentialsInvalidationEvent; import org.thingsboard.server.common.data.security.event.UserCredentialsInvalidationEvent;
import org.thingsboard.server.common.data.security.event.UserSessionInvalidationEvent; import org.thingsboard.server.common.data.security.event.UserSessionInvalidationEvent;
import org.thingsboard.server.common.data.security.model.JwtPair; import org.thingsboard.server.common.data.security.model.JwtPair;
import org.thingsboard.server.common.data.security.model.SecuritySettings; import org.thingsboard.server.common.data.security.model.SecuritySettings;
import org.thingsboard.server.common.data.security.model.UserPasswordPolicy; import org.thingsboard.server.common.data.security.model.UserPasswordPolicy;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.common.data.limit.LimitedApi;
import org.thingsboard.server.dao.util.limits.RateLimitService; import org.thingsboard.server.dao.util.limits.RateLimitService;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.security.auth.rest.RestAuthenticationDetails; import org.thingsboard.server.service.security.auth.rest.RestAuthenticationDetails;
import org.thingsboard.server.service.security.model.ActivateUserRequest; import org.thingsboard.server.service.security.model.ActivateUserRequest;
import org.thingsboard.server.service.security.model.ChangePasswordRequest; import org.thingsboard.server.service.security.model.ChangePasswordRequest;
@ -123,8 +122,6 @@ public class AuthController extends BaseController {
userCredentials.setPassword(passwordEncoder.encode(newPassword)); userCredentials.setPassword(passwordEncoder.encode(newPassword));
userService.replaceUserCredentials(securityUser.getTenantId(), userCredentials); userService.replaceUserCredentials(securityUser.getTenantId(), userCredentials);
sendEntityNotificationMsg(getTenantId(), userCredentials.getUserId(), EdgeEventActionType.CREDENTIALS_UPDATED);
eventPublisher.publishEvent(new UserCredentialsInvalidationEvent(securityUser.getId())); eventPublisher.publishEvent(new UserCredentialsInvalidationEvent(securityUser.getId()));
ObjectNode response = JacksonUtil.newObjectNode(); ObjectNode response = JacksonUtil.newObjectNode();
response.put("token", tokenFactory.createAccessJwtToken(securityUser).getToken()); response.put("token", tokenFactory.createAccessJwtToken(securityUser).getToken());
@ -259,8 +256,6 @@ public class AuthController extends BaseController {
} }
} }
sendEntityNotificationMsg(user.getTenantId(), user.getId(), EdgeEventActionType.CREDENTIALS_UPDATED);
return tokenFactory.createTokenPair(securityUser); return tokenFactory.createTokenPair(securityUser);
} }

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

@ -60,8 +60,6 @@ import org.thingsboard.server.common.data.asset.AssetInfo;
import org.thingsboard.server.common.data.asset.AssetProfile; import org.thingsboard.server.common.data.asset.AssetProfile;
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.EdgeEventType;
import org.thingsboard.server.common.data.edge.EdgeInfo; import org.thingsboard.server.common.data.edge.EdgeInfo;
import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.exception.ThingsboardException;
@ -142,8 +140,6 @@ import org.thingsboard.server.queue.provider.TbQueueProducerProvider;
import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.action.EntityActionService; import org.thingsboard.server.service.action.EntityActionService;
import org.thingsboard.server.service.component.ComponentDiscoveryService; import org.thingsboard.server.service.component.ComponentDiscoveryService;
import org.thingsboard.server.service.edge.instructions.EdgeInstallService;
import org.thingsboard.server.service.edge.rpc.EdgeRpcService;
import org.thingsboard.server.service.entitiy.TbNotificationEntityService; import org.thingsboard.server.service.entitiy.TbNotificationEntityService;
import org.thingsboard.server.service.entitiy.user.TbUserSettingsService; import org.thingsboard.server.service.entitiy.user.TbUserSettingsService;
import org.thingsboard.server.service.ota.OtaPackageStateService; import org.thingsboard.server.service.ota.OtaPackageStateService;
@ -301,12 +297,6 @@ public abstract class BaseController {
@Autowired(required = false) @Autowired(required = false)
protected EdgeService edgeService; protected EdgeService edgeService;
@Autowired(required = false)
protected EdgeRpcService edgeRpcService;
@Autowired(required = false)
protected EdgeInstallService edgeInstallService;
@Autowired @Autowired
protected TbNotificationEntityService notificationEntityService; protected TbNotificationEntityService notificationEntityService;
@ -824,18 +814,6 @@ public abstract class BaseController {
} }
} }
protected void sendEntityNotificationMsg(TenantId tenantId, EntityId entityId, EdgeEventActionType action) {
sendNotificationMsgToEdge(tenantId, null, entityId, null, null, action);
}
protected void sendEntityAssignToEdgeNotificationMsg(TenantId tenantId, EdgeId edgeId, EntityId entityId, EdgeEventActionType action) {
sendNotificationMsgToEdge(tenantId, edgeId, entityId, null, null, action);
}
private void sendNotificationMsgToEdge(TenantId tenantId, EdgeId edgeId, EntityId entityId, String body, EdgeEventType type, EdgeEventActionType action) {
tbClusterService.sendNotificationMsgToEdge(tenantId, edgeId, entityId, body, type, action);
}
protected void processDashboardIdFromAdditionalInfo(ObjectNode additionalInfo, String requiredFields) throws ThingsboardException { protected void processDashboardIdFromAdditionalInfo(ObjectNode additionalInfo, String requiredFields) throws ThingsboardException {
String dashboardId = additionalInfo.has(requiredFields) ? additionalInfo.get(requiredFields).asText() : null; String dashboardId = additionalInfo.has(requiredFields) ? additionalInfo.get(requiredFields).asText() : null;
if (dashboardId != null && !dashboardId.equals("null")) { if (dashboardId != null && !dashboardId.equals("null")) {

255
application/src/main/java/org/thingsboard/server/controller/ControllerConstants.java

@ -24,6 +24,7 @@ public class ControllerConstants {
protected static final String CUSTOMER_ID = "customerId"; protected static final String CUSTOMER_ID = "customerId";
protected static final String TENANT_ID = "tenantId"; protected static final String TENANT_ID = "tenantId";
protected static final String DEVICE_ID = "deviceId"; protected static final String DEVICE_ID = "deviceId";
protected static final String PROTOCOL = "protocol";
protected static final String EDGE_ID = "edgeId"; protected static final String EDGE_ID = "edgeId";
protected static final String RPC_ID = "rpcId"; protected static final String RPC_ID = "rpcId";
protected static final String ENTITY_ID = "entityId"; protected static final String ENTITY_ID = "entityId";
@ -34,6 +35,7 @@ public class ControllerConstants {
protected static final String DASHBOARD_ID_PARAM_DESCRIPTION = "A string value representing the dashboard id. For example, '784f394c-42b6-435a-983c-b7beff2784f9'"; protected static final String DASHBOARD_ID_PARAM_DESCRIPTION = "A string value representing the dashboard id. For example, '784f394c-42b6-435a-983c-b7beff2784f9'";
protected static final String RPC_ID_PARAM_DESCRIPTION = "A string value representing the rpc id. For example, '784f394c-42b6-435a-983c-b7beff2784f9'"; protected static final String RPC_ID_PARAM_DESCRIPTION = "A string value representing the rpc id. For example, '784f394c-42b6-435a-983c-b7beff2784f9'";
protected static final String DEVICE_ID_PARAM_DESCRIPTION = "A string value representing the device id. For example, '784f394c-42b6-435a-983c-b7beff2784f9'"; protected static final String DEVICE_ID_PARAM_DESCRIPTION = "A string value representing the device id. For example, '784f394c-42b6-435a-983c-b7beff2784f9'";
protected static final String PROTOCOL_PARAM_DESCRIPTION = "A string value representing the device connectivity protocol. Possible values: 'mqtt', 'mqtts', 'http', 'https', 'coap', 'coaps'";
protected static final String ENTITY_VIEW_ID_PARAM_DESCRIPTION = "A string value representing the entity view id. For example, '784f394c-42b6-435a-983c-b7beff2784f9'"; protected static final String ENTITY_VIEW_ID_PARAM_DESCRIPTION = "A string value representing the entity view id. For example, '784f394c-42b6-435a-983c-b7beff2784f9'";
protected static final String DEVICE_PROFILE_ID_PARAM_DESCRIPTION = "A string value representing the device profile id. For example, '784f394c-42b6-435a-983c-b7beff2784f9'"; protected static final String DEVICE_PROFILE_ID_PARAM_DESCRIPTION = "A string value representing the device profile id. For example, '784f394c-42b6-435a-983c-b7beff2784f9'";
@ -207,42 +209,222 @@ public class ControllerConstants {
protected static final String IS_BOOTSTRAP_SERVER_PARAM_DESCRIPTION = "A Boolean value representing the Server SecurityInfo for future Bootstrap client mode settings. Values: 'true' for Bootstrap Server; 'false' for Lwm2m Server. "; protected static final String IS_BOOTSTRAP_SERVER_PARAM_DESCRIPTION = "A Boolean value representing the Server SecurityInfo for future Bootstrap client mode settings. Values: 'true' for Bootstrap Server; 'false' for Lwm2m Server. ";
protected static final String DEVICE_WITH_DEVICE_CREDENTIALS_PARAM_DESCRIPTION = protected static final String DEVICE_WITH_DEVICE_CREDENTIALS_ACCESS_TOKEN_PARAM_DESCRIPTION =
"{\n" + "{\n" +
" \"device\": {\n" + " \"device\": {\n" +
" \"name\": \"LwRpk00000000\",\n" + " \"name\":\"Name_DeviceWithCredantial_AccessToken\",\n" +
" \"type\": \"lwm2mProfileRpk\"\n" + " \"label\":\"Label_DeviceWithCredantial_AccessToken\",\n" +
" },\n" + " \"deviceProfileId\":{\n" +
" \"id\":\"9d9588c0-06c9-11ee-b618-19be30fdeb60\",\n" +
" \"entityType\":\"DEVICE_PROFILE\"\n" +
" }\n" +
" },\n" +
" \"credentials\": {\n" + " \"credentials\": {\n" +
" \"id\": \"null\",\n" + " \"credentialsType\": \"ACCESS_TOKEN\",\n" +
" \"createdTime\": 0,\n" + " \"credentialsId\": \"6hmxew8pmmzng4e3une2\"\n" +
" \"deviceId\": \"null\",\n" + " }\n" +
" \"credentialsType\": \"LWM2M_CREDENTIALS\",\n" +
" \"credentialsId\": \"LwRpk00000000\",\n" +
" \"credentialsValue\": {\n" +
" \"client\": {\n" +
" \"endpoint\": \"LwRpk00000000\",\n" +
" \"securityConfigClientMode\": \"RPK\",\n" +
" \"key\": \"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEUEBxNl/RcYJNm8mk91CyVXoIJiROYDlXcSSqK6e5bDHwOW4ZiN2lNnXalyF0Jxw8MbAytnDMERXyAja5VEMeVQ==\"\n" +
" },\n" +
" \"bootstrap\": {\n" +
" \"bootstrapServer\": {\n" +
" \"securityMode\": \"RPK\",\n" +
" \"clientPublicKeyOrId\": \"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEUEBxNl/RcYJNm8mk91CyVXoIJiROYDlXcSSqK6e5bDHwOW4ZiN2lNnXalyF0Jxw8MbAytnDMERXyAja5VEMeVQ==\",\n" +
" \"clientSecretKey\": \"MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgd9GAx7yZW37autew5KZykn4IgRpge/tZSjnudnZJnMahRANCAARQQHE2X9Fxgk2byaT3ULJVeggmJE5gOVdxJKorp7lsMfA5bhmI3aU2ddqXIXQnHDwxsDK2cMwRFfICNrlUQx5V\"\n" +
" },\n" +
" \"lwm2mServer\": {\n" +
" \"securityMode\": \"RPK\",\n" +
" \"clientPublicKeyOrId\": \"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEUEBxNl/RcYJNm8mk91CyVXoIJiROYDlXcSSqK6e5bDHwOW4ZiN2lNnXalyF0Jxw8MbAytnDMERXyAja5VEMeVQ==\",\n" +
" \"clientSecretKey\": \"MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgd9GAx7yZW37autew5KZykn4IgRpge/tZSjnudnZJnMahRANCAARQQHE2X9Fxgk2byaT3ULJVeggmJE5gOVdxJKorp7lsMfA5bhmI3aU2ddqXIXQnHDwxsDK2cMwRFfICNrlUQx5V\"\n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
"}"; "}";
protected static final String DEVICE_WITH_DEVICE_CREDENTIALS_PARAM_DESCRIPTION_MARKDOWN = protected static final String DEVICE_UPDATE_CREDENTIALS_ACCESS_TOKEN_PARAM_DESCRIPTION =
MARKDOWN_CODE_BLOCK_START + DEVICE_WITH_DEVICE_CREDENTIALS_PARAM_DESCRIPTION + MARKDOWN_CODE_BLOCK_END; "{\n" +
" \"id\": {\n" +
" \"id\":\"c886a090-168d-11ee-87c9-6f157dbc816a\"\n" +
" },\n" +
" \"deviceId\": {\n" +
" \"id\":\"c5fb3ac0-168d-11ee-87c9-6f157dbc816a\",\n" +
" \"entityType\":\"DEVICE\"\n" +
" },\n" +
" \"credentialsType\": \"ACCESS_TOKEN\",\n" +
" \"credentialsId\": \"6hmxew8pmmzng4e3une4\"\n" +
"}";
protected static final String DEVICE_WITH_DEVICE_CREDENTIALS_ACCESS_TOKEN_DEFAULT_PARAM_DESCRIPTION =
"{\n" +
" \"device\": {\n" +
" \"name\":\"Name_DeviceWithCredantial_AccessToken_Default\",\n" +
" \"label\":\"Label_DeviceWithCredantial_AccessToken_Default\",\n" +
" \"type\": \"default\"\n" +
" },\n" +
" \"credentials\": {\n" +
" \"credentialsType\": \"ACCESS_TOKEN\",\n" +
" \"credentialsId\": \"6hmxew8pmmzng4e3une3\"\n" +
" }\n" +
"}";
protected static final String certificateValue = "\"-----BEGIN CERTIFICATE----- " +
"MIICMTCCAdegAwIBAgIUI9dBuwN6pTtK6uZ03rkiCwV4wEYwCgYIKoZIzj0EAwIwbjELMAkGA1UEBhMCVVMxETAPBgNVBAgMCE5ldyBZb3JrMRowGAYDVQQKDBFUaGluZ3NCb2FyZCwgSW5jLjEwMC4GA1UEAwwnZGV2aWNlQ2VydGlmaWNhdGVAWDUwOVByb3Zpc2lvblN0cmF0ZWd5MB4XDTIzMDMyOTE0NTYxN1oXDTI0MDMyODE0NTYxN1owbjELMAkGA1UEBhMCVVMxETAPBgNVBAgMCE5ldyBZb3JrMRowGAYDVQQKDBFUaGluZ3NCb2FyZCwgSW5jLjEwMC4GA1UEAwwnZGV2aWNlQ2VydGlmaWNhdGVAWDUwOVByb3Zpc2lvblN0cmF0ZWd5MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE9Zo791qKQiGNBm11r4ZGxh+w+ossZL3xc46ufq5QckQHP7zkD2XDAcmP5GvdkM1sBFN9AWaCkQfNnWmfERsOOKNTMFEwHQYDVR0OBBYEFFFc5uyCyglQoZiKhzXzMcQ3BKORMB8GA1UdIwQYMBaAFFFc5uyCyglQoZiKhzXzMcQ3BKORMA8GA1UdEwEB/wQFMAMBAf8wCgYIKoZIzj0EAwIDSAAwRQIhANbA9CuhoOifZMMmqkpuld+65CR+ItKdXeRAhLMZuccuAiB0FSQB34zMutXrZj1g8Gl5OkE7YryFHbei1z0SveHR8g== " +
"-----END CERTIFICATE-----\"";
protected static final String certificateId = "\"84f5911765abba1f96bf4165604e9e90338fc6214081a8e623b6ff9669aedb27\"";
protected static final String certificateValueUpdate = "\"-----BEGIN CERTIFICATE----- " +
"MIICMTCCAdegAwIBAgIUUEKxS9hTz4l+oLUMF0LV6TC/gCIwCgYIKoZIzj0EAwIwbjELMAkGA1UEBhMCVVMxETAPBgNVBAgMCE5ldyBZb3JrMRowGAYDVQQKDBFUaGluZ3NCb2FyZCwgSW5jLjEwMC4GA1UEAwwnZGV2aWNlUHJvZmlsZUNlcnRAWDUwOVByb3Zpc2lvblN0cmF0ZWd5MB4XDTIzMDMyOTE0NTczNloXDTI0MDMyODE0NTczNlowbjELMAkGA1UEBhMCVVMxETAPBgNVBAgMCE5ldyBZb3JrMRowGAYDVQQKDBFUaGluZ3NCb2FyZCwgSW5jLjEwMC4GA1UEAwwnZGV2aWNlUHJvZmlsZUNlcnRAWDUwOVByb3Zpc2lvblN0cmF0ZWd5MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAECMlWO72krDoUL9FQjUmSCetkhaEGJUfQkdSfkLSNa0GyAEIMbfmzI4zITeapunu4rGet3EMyLydQzuQanBicp6NTMFEwHQYDVR0OBBYEFHpZ78tPnztNii4Da/yCw6mhEIL3MB8GA1UdIwQYMBaAFHpZ78tPnztNii4Da/yCw6mhEIL3MA8GA1UdEwEB/wQFMAMBAf8wCgYIKoZIzj0EAwIDSAAwRQIgJ7qyMFqNcwSYkH6o+UlQXzLWfwZbNjVk+aR7foAZNGsCIQDsd7v3WQIGHiArfZeDs1DLEDuV/2h6L+ZNoGNhEKL+1A== " +
"-----END CERTIFICATE-----\"";
protected static final String certificateIdUpdate = "\"6b8adb49015500e51a527acd332b51684ab9b49b4ade03a9582a44c455e2e9b6\"";
protected static final String DEVICE_WITH_DEVICE_CREDENTIALS_X509_CERTIFICATE_PARAM_DESCRIPTION =
"{\n" +
" \"device\": {\n" +
" \"name\":\"Name_DeviceWithCredantial_X509_Certificate\",\n" +
" \"label\":\"Label_DeviceWithCredantial_X509_Certificate\",\n" +
" \"deviceProfileId\":{\n" +
" \"id\":\"9d9588c0-06c9-11ee-b618-19be30fdeb60\",\n" +
" \"entityType\":\"DEVICE_PROFILE\"\n" +
" }\n" +
" },\n" +
" \"credentials\": {\n" +
" \"credentialsType\": \"X509_CERTIFICATE\",\n" +
" \"credentialsId\": " + certificateId + ",\n" +
" \"credentialsValue\": " + certificateValue + "\n" +
" }\n" +
"}";
protected static final String DEVICE_UPDATE_CREDENTIALS_X509_CERTIFICATE_PARAM_DESCRIPTION =
"{\n" +
" \"id\": {\n" +
" \"id\":\"309bd9c0-14f4-11ee-9fc9-d9b7463abb63\"\n" +
" },\n" +
" \"deviceId\": {\n" +
" \"id\":\"3092b200-14f4-11ee-9fc9-d9b7463abb63\",\n" +
" \"entityType\":\"DEVICE\"\n" +
" },\n" +
" \"credentialsType\": \"X509_CERTIFICATE\",\n" +
" \"credentialsId\": " + certificateIdUpdate + ",\n" +
" \"credentialsValue\": " + certificateValueUpdate + "\n" +
"}";
protected static final String MQTT_BASIC_VALUE = "\"{\\\"clientId\\\":\\\"5euh5nzm34bjjh1efmlt\\\",\\\"userName\\\":\\\"onasd1lgwasmjl7v2v7h\\\",\\\"password\\\":\\\"b9xtm4ny8kt9zewaga5o\\\"}\"";
protected static final String MQTT_BASIC_VALUE_UPDATE = "\"{\\\"clientId\\\":\\\"juy03yv4owqxcmqhqtvk\\\",\\\"userName\\\":\\\"ov19fxca0cyjn7lm7w7u\\\",\\\"password\\\":\\\"twy94he114dfi9usyk1o\\\"}\"";
protected static final String DEVICE_WITH_DEVICE_CREDENTIALS_MQTT_BASIC_PARAM_DESCRIPTION =
"{\n" +
" \"device\": {\n" +
" \"name\":\"Name_DeviceWithCredantial_MQTT_Basic\",\n" +
" \"label\":\"Label_DeviceWithCredantial_MQTT_Basic\",\n" +
" \"deviceProfileId\":{\n" +
" \"id\":\"9d9588c0-06c9-11ee-b618-19be30fdeb60\",\n" +
" \"entityType\":\"DEVICE_PROFILE\"\n" +
" }\n" +
" },\n" +
" \"credentials\": {\n" +
" \"credentialsType\": \"MQTT_BASIC\",\n" +
" \"credentialsValue\": " + MQTT_BASIC_VALUE + "\n" +
" }\n" +
"}";
protected static final String DEVICE_UPDATE_CREDENTIALS_MQTT_BASIC_PARAM_DESCRIPTION =
"{\n" +
" \"id\": {\n" +
" \"id\":\"d877ffb0-14f5-11ee-9fc9-d9b7463abb63\"\n" +
" },\n" +
" \"deviceId\": {\n" +
" \"id\":\"d875dcd0-14f5-11ee-9fc9-d9b7463abb63\",\n" +
" \"entityType\":\"DEVICE\"\n" +
" },\n" +
" \"credentialsType\": \"MQTT_BASIC\",\n" +
" \"credentialsValue\": " + MQTT_BASIC_VALUE_UPDATE + "\n" +
"}";
protected static final String CREDENTIALS_VALUE_LVM2M_RPK_DESCRIPTION =
" \"{" +
"\\\"client\\\":{ " +
"\\\"endpoint\\\":\\\"LwRpk00000000\\\", " +
"\\\"securityConfigClientMode\\\":\\\"RPK\\\", " +
"\\\"key\\\":\\\"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEUEBxNl/RcYJNm8mk91CyVXoIJiROYDlXcSSqK6e5bDHwOW4ZiN2lNnXalyF0Jxw8MbAytnDMERXyAja5VEMeVQ==\\\"" +
" }, " +
"\\\"bootstrap\\\":{ " +
"\\\"bootstrapServer\\\":{ " +
"\\\"securityMode\\\":\\\"RPK\\\", " +
"\\\"clientPublicKeyOrId\\\":\\\"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEUEBxNl/RcYJNm8mk91CyVXoIJiROYDlXcSSqK6e5bDHwOW4ZiN2lNnXalyF0Jxw8MbAytnDMERXyAja5VEMeVQ==\\\", " +
"\\\"clientSecretKey\\\":\\\"MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgd9GAx7yZW37autew5KZykn4IgRpge/tZSjnudnZJnMahRANCAARQQHE2X9Fxgk2byaT3ULJVeggmJE5gOVdxJKorp7lsMfA5bhmI3aU2ddqXIXQnHDwxsDK2cMwRFfICNrlUQx5V\\\"" +
"}, " +
"\\\"lwm2mServer\\\":{ \\\"securityMode\\\":\\\"RPK\\\", " +
"\\\"clientPublicKeyOrId\\\":\\\"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEUEBxNl/RcYJNm8mk91CyVXoIJiROYDlXcSSqK6e5bDHwOW4ZiN2lNnXalyF0Jxw8MbAytnDMERXyAja5VEMeVQ==\\\", " +
"\\\"clientSecretKey\\\":\\\"MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgd9GAx7yZW37autew5KZykn4IgRpge/tZSjnudnZJnMahRANCAARQQHE2X9Fxgk2byaT3ULJVeggmJE5gOVdxJKorp7lsMfA5bhmI3aU2ddqXIXQnHDwxsDK2cMwRFfICNrlUQx5V\\\"" +
"}" +
"} " +
"}\"";
protected static final String CREDENTIALS_VALUE_UPDATE_LVM2M_RPK_DESCRIPTION =
" \"{" +
"\\\"client\\\":{ " +
"\\\"endpoint\\\":\\\"LwRpk00000000\\\", " +
"\\\"securityConfigClientMode\\\":\\\"RPK\\\", " +
"\\\"key\\\":\\\"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEdvBZZ2vQRK9wgDhctj6B1c7bxR3Z0wYg1+YdoYFnVUKWb+rIfTTyYK9tmQJx5Vlb5fxdLnVv1RJOPiwsLIQbAA==\\\"" +
" }, " +
"\\\"bootstrap\\\":{ " +
"\\\"bootstrapServer\\\":{ " +
"\\\"securityMode\\\":\\\"RPK\\\", " +
"\\\"clientPublicKeyOrId\\\":\\\"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEUEBxNl/RcYJNm8mk91CyVXoIJiROYDlXcSSqK6e5bDHwOW4ZiN2lNnXalyF0Jxw8MbAytnDMERXyAja5VEMeVQ==\\\", " +
"\\\"clientSecretKey\\\":\\\"MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgd9GAx7yZW37autew5KZykn4IgRpge/tZSjnudnZJnMahRANCAARQQHE2X9Fxgk2byaT3ULJVeggmJE5gOVdxJKorp7lsMfA5bhmI3aU2ddqXIXQnHDwxsDK2cMwRFfICNrlUQx5V\\\"" +
"}, " +
"\\\"lwm2mServer\\\":{ \\\"securityMode\\\":\\\"RPK\\\", " +
"\\\"clientPublicKeyOrId\\\":\\\"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEUEBxNl/RcYJNm8mk91CyVXoIJiROYDlXcSSqK6e5bDHwOW4ZiN2lNnXalyF0Jxw8MbAytnDMERXyAja5VEMeVQ==\\\", " +
"\\\"clientSecretKey\\\":\\\"MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgd9GAx7yZW37autew5KZykn4IgRpge/tZSjnudnZJnMahRANCAARQQHE2X9Fxgk2byaT3ULJVeggmJE5gOVdxJKorp7lsMfA5bhmI3aU2ddqXIXQnHDwxsDK2cMwRFfICNrlUQx5V\\\"" +
"}" +
"} " +
"}\"";
protected static final String DEVICE_WITH_DEVICE_CREDENTIALS_PARAM_LVM2M_RPK_DESCRIPTION =
"{\n" +
" \"device\": {\n" +
" \"name\":\"Name_LwRpk00000000\",\n" +
" \"label\":\"Label_LwRpk00000000\",\n" +
" \"deviceProfileId\":{\n" +
" \"id\":\"a660bd50-10ef-11ee-8737-b5634e73c779\",\n" +
" \"entityType\":\"DEVICE_PROFILE\"\n" +
" }\n" +
" },\n" +
" \"credentials\": {\n" +
" \"credentialsType\": \"LWM2M_CREDENTIALS\",\n" +
" \"credentialsId\": \"LwRpk00000000\",\n" +
" \"credentialsValue\":\n" + CREDENTIALS_VALUE_LVM2M_RPK_DESCRIPTION + "\n" +
" }\n" +
"}";
protected static final String DEVICE_UPDATE_CREDENTIALS_PARAM_LVM2M_RPK_DESCRIPTION =
"{\n" +
" \"id\": {\n" +
" \"id\":\"e238d4d0-1689-11ee-98c6-1713c1be5a8e\"\n" +
" },\n" +
" \"deviceId\": {\n" +
" \"id\":\"e232e160-1689-11ee-98c6-1713c1be5a8e\",\n" +
" \"entityType\":\"DEVICE\"\n" +
" },\n" +
" \"credentialsType\": \"LWM2M_CREDENTIALS\",\n" +
" \"credentialsId\": \"LwRpk00000000\",\n" +
" \"credentialsValue\":\n" + CREDENTIALS_VALUE_UPDATE_LVM2M_RPK_DESCRIPTION + "\n" +
"}";
protected static final String DEVICE_WITH_DEVICE_CREDENTIALS_PARAM_ACCESS_TOKEN_DESCRIPTION_MARKDOWN =
MARKDOWN_CODE_BLOCK_START + DEVICE_WITH_DEVICE_CREDENTIALS_ACCESS_TOKEN_PARAM_DESCRIPTION + MARKDOWN_CODE_BLOCK_END;
protected static final String DEVICE_WITH_DEVICE_CREDENTIALS_PARAM_ACCESS_TOKEN_DEFAULT_DESCRIPTION_MARKDOWN =
MARKDOWN_CODE_BLOCK_START + DEVICE_WITH_DEVICE_CREDENTIALS_ACCESS_TOKEN_DEFAULT_PARAM_DESCRIPTION + MARKDOWN_CODE_BLOCK_END;
protected static final String DEVICE_WITH_DEVICE_CREDENTIALS_PARAM_X509_CERTIFICATE_DESCRIPTION_MARKDOWN =
MARKDOWN_CODE_BLOCK_START + DEVICE_WITH_DEVICE_CREDENTIALS_X509_CERTIFICATE_PARAM_DESCRIPTION + MARKDOWN_CODE_BLOCK_END;
protected static final String DEVICE_WITH_DEVICE_CREDENTIALS_PARAM_MQTT_BASIC_DESCRIPTION_MARKDOWN =
MARKDOWN_CODE_BLOCK_START + DEVICE_WITH_DEVICE_CREDENTIALS_MQTT_BASIC_PARAM_DESCRIPTION + MARKDOWN_CODE_BLOCK_END;
protected static final String DEVICE_WITH_DEVICE_CREDENTIALS_PARAM_LVM2M_RPK_DESCRIPTION_MARKDOWN =
MARKDOWN_CODE_BLOCK_START + DEVICE_WITH_DEVICE_CREDENTIALS_PARAM_LVM2M_RPK_DESCRIPTION + MARKDOWN_CODE_BLOCK_END;
protected static final String DEVICE_UPDATE_CREDENTIALS_PARAM_ACCESS_TOKEN_DESCRIPTION_MARKDOWN =
MARKDOWN_CODE_BLOCK_START + DEVICE_UPDATE_CREDENTIALS_ACCESS_TOKEN_PARAM_DESCRIPTION + MARKDOWN_CODE_BLOCK_END;
protected static final String DEVICE_UPDATE_CREDENTIALS_PARAM_X509_CERTIFICATE_DESCRIPTION_MARKDOWN =
MARKDOWN_CODE_BLOCK_START + DEVICE_UPDATE_CREDENTIALS_X509_CERTIFICATE_PARAM_DESCRIPTION + MARKDOWN_CODE_BLOCK_END;
protected static final String DEVICE_UPDATE_CREDENTIALS_PARAM_MQTT_BASIC_DESCRIPTION_MARKDOWN =
MARKDOWN_CODE_BLOCK_START + DEVICE_UPDATE_CREDENTIALS_MQTT_BASIC_PARAM_DESCRIPTION + MARKDOWN_CODE_BLOCK_END;
protected static final String DEVICE_UPDATE_CREDENTIALS_PARAM_LVM2M_RPK_DESCRIPTION_MARKDOWN =
MARKDOWN_CODE_BLOCK_START + DEVICE_UPDATE_CREDENTIALS_PARAM_LVM2M_RPK_DESCRIPTION + MARKDOWN_CODE_BLOCK_END;
protected static final String FILTER_VALUE_TYPE = NEW_LINE + "## Value Type and Operations" + NEW_LINE + protected static final String FILTER_VALUE_TYPE = NEW_LINE + "## Value Type and Operations" + NEW_LINE +
@ -254,7 +436,7 @@ public class ControllerConstants {
" * 'BOOLEAN' - used for boolean values. Operations: EQUAL, NOT_EQUAL;\n" + " * 'BOOLEAN' - used for boolean values. Operations: EQUAL, NOT_EQUAL;\n" +
" * 'DATE_TIME' - similar to numeric, transforms value to milliseconds since epoch. Operations: EQUAL, NOT_EQUAL, GREATER, LESS, GREATER_OR_EQUAL, LESS_OR_EQUAL; \n"; " * 'DATE_TIME' - similar to numeric, transforms value to milliseconds since epoch. Operations: EQUAL, NOT_EQUAL, GREATER, LESS, GREATER_OR_EQUAL, LESS_OR_EQUAL; \n";
protected static final String DEVICE_PROFILE_ALARM_SCHEDULE_SPECIFIC_TIME_EXAMPLE = MARKDOWN_CODE_BLOCK_START + protected static final String DEVICE_PROFILE_ALARM_SCHEDULE_SPECIFIC_TIME_EXAMPLE = MARKDOWN_CODE_BLOCK_START +
"{\n" + "{\n" +
" \"schedule\":{\n" + " \"schedule\":{\n" +
" \"type\":\"SPECIFIC_TIME\",\n" + " \"type\":\"SPECIFIC_TIME\",\n" +
@ -269,7 +451,7 @@ public class ControllerConstants {
" }\n" + " }\n" +
"}" + "}" +
MARKDOWN_CODE_BLOCK_END; MARKDOWN_CODE_BLOCK_END;
protected static final String DEVICE_PROFILE_ALARM_SCHEDULE_CUSTOM_EXAMPLE = MARKDOWN_CODE_BLOCK_START + protected static final String DEVICE_PROFILE_ALARM_SCHEDULE_CUSTOM_EXAMPLE = MARKDOWN_CODE_BLOCK_START +
"{\n" + "{\n" +
" \"schedule\":{\n" + " \"schedule\":{\n" +
" \"type\":\"CUSTOM\",\n" + " \"type\":\"CUSTOM\",\n" +
@ -321,9 +503,9 @@ public class ControllerConstants {
" }\n" + " }\n" +
"}" + "}" +
MARKDOWN_CODE_BLOCK_END; MARKDOWN_CODE_BLOCK_END;
protected static final String DEVICE_PROFILE_ALARM_SCHEDULE_ALWAYS_EXAMPLE = MARKDOWN_CODE_BLOCK_START + "\"schedule\": null" + MARKDOWN_CODE_BLOCK_END; protected static final String DEVICE_PROFILE_ALARM_SCHEDULE_ALWAYS_EXAMPLE = MARKDOWN_CODE_BLOCK_START + "\"schedule\": null" + MARKDOWN_CODE_BLOCK_END;
protected static final String DEVICE_PROFILE_ALARM_CONDITION_REPEATING_EXAMPLE = MARKDOWN_CODE_BLOCK_START + protected static final String DEVICE_PROFILE_ALARM_CONDITION_REPEATING_EXAMPLE = MARKDOWN_CODE_BLOCK_START +
"{\n" + "{\n" +
" \"spec\":{\n" + " \"spec\":{\n" +
" \"type\":\"REPEATING\",\n" + " \"type\":\"REPEATING\",\n" +
@ -339,7 +521,8 @@ public class ControllerConstants {
" }\n" + " }\n" +
"}" + "}" +
MARKDOWN_CODE_BLOCK_END; MARKDOWN_CODE_BLOCK_END;
protected static final String DEVICE_PROFILE_ALARM_CONDITION_DURATION_EXAMPLE = MARKDOWN_CODE_BLOCK_START +
protected static final String DEVICE_PROFILE_ALARM_CONDITION_DURATION_EXAMPLE = MARKDOWN_CODE_BLOCK_START +
"{\n" + "{\n" +
" \"spec\":{\n" + " \"spec\":{\n" +
" \"type\":\"DURATION\",\n" + " \"type\":\"DURATION\",\n" +

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

@ -0,0 +1,107 @@
/**
* Copyright © 2016-2023 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.controller;
import com.fasterxml.jackson.databind.JsonNode;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.dao.device.DeviceConnectivityService;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.security.permission.Operation;
import org.thingsboard.server.service.security.system.SystemSecurityService;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.net.URISyntaxException;
import static org.thingsboard.server.controller.ControllerConstants.DEVICE_ID;
import static org.thingsboard.server.controller.ControllerConstants.DEVICE_ID_PARAM_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.PROTOCOL;
import static org.thingsboard.server.controller.ControllerConstants.PROTOCOL_PARAM_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH;
import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.PEM_CERT_FILE_NAME;
@RestController
@TbCoreComponent
@RequestMapping("/api")
@RequiredArgsConstructor
@Slf4j
public class DeviceConnectivityController extends BaseController {
private final DeviceConnectivityService deviceConnectivityService;
private final SystemSecurityService systemSecurityService;
@ApiOperation(value = "Get commands to publish device telemetry (getDevicePublishTelemetryCommands)",
notes = "Fetch the list of commands to publish device telemetry based on device profile " +
"If the user has the authority of 'Tenant Administrator', the server checks that the device is owned by the same tenant. " +
"If the user has the authority of 'Customer User', the server checks that the device is assigned to the same customer. " +
TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH)
@ApiResponses(value = {
@ApiResponse(code = 200, message = "OK",
examples = @io.swagger.annotations.Example(
value = {
@io.swagger.annotations.ExampleProperty(
mediaType = "application/json",
value = "{\"http\":\"curl -v -X POST http://localhost:8080/api/v1/0ySs4FTOn5WU15XLmal8/telemetry --header Content-Type:application/json --data {temperature:25}\"," +
"\"mqtt\":\"mosquitto_pub -d -q 1 -h localhost -t v1/devices/me/telemetry -i myClient1 -u myUsername1 -P myPassword -m {temperature:25}\"," +
"\"coap\":\"coap-client -m POST coap://localhost:5683/api/v1/0ySs4FTOn5WU15XLmal8/telemetry -t json -e {temperature:25}\"}")}))})
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/device-connectivity/{deviceId}", method = RequestMethod.GET)
@ResponseBody
public JsonNode getDevicePublishTelemetryCommands(@ApiParam(value = DEVICE_ID_PARAM_DESCRIPTION)
@PathVariable(DEVICE_ID) String strDeviceId, HttpServletRequest request) throws ThingsboardException, URISyntaxException {
checkParameter(DEVICE_ID, strDeviceId);
DeviceId deviceId = new DeviceId(toUUID(strDeviceId));
Device device = checkDeviceId(deviceId, Operation.READ_CREDENTIALS);
String baseUrl = systemSecurityService.getBaseUrl(getTenantId(), getCurrentUser().getCustomerId(), request);
return deviceConnectivityService.findDevicePublishTelemetryCommands(baseUrl, device);
}
@ApiOperation(value = "Download server certificate using file path defined in device.connectivity properties (downloadServerCertificate)", notes = "Download server certificate.")
@RequestMapping(value = "/device-connectivity/{protocol}/certificate/download", method = RequestMethod.GET)
@ResponseBody
public ResponseEntity<org.springframework.core.io.Resource> downloadServerCertificate(@ApiParam(value = PROTOCOL_PARAM_DESCRIPTION)
@PathVariable(PROTOCOL) String protocol) throws ThingsboardException, IOException {
checkParameter(PROTOCOL, protocol);
var pemCert =
checkNotNull(deviceConnectivityService.getPemCertFile(protocol), protocol + " pem cert file is not found!");
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + PEM_CERT_FILE_NAME)
.header("x-filename", PEM_CERT_FILE_NAME)
.contentLength(pemCert.contentLength())
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.body(pemCert);
}
}

59
application/src/main/java/org/thingsboard/server/controller/DeviceController.java

@ -75,6 +75,7 @@ import org.thingsboard.server.service.security.permission.Operation;
import org.thingsboard.server.service.security.permission.Resource; import org.thingsboard.server.service.security.permission.Resource;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.validation.Valid;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
@ -93,7 +94,15 @@ import static org.thingsboard.server.controller.ControllerConstants.DEVICE_PROFI
import static org.thingsboard.server.controller.ControllerConstants.DEVICE_SORT_PROPERTY_ALLOWABLE_VALUES; import static org.thingsboard.server.controller.ControllerConstants.DEVICE_SORT_PROPERTY_ALLOWABLE_VALUES;
import static org.thingsboard.server.controller.ControllerConstants.DEVICE_TEXT_SEARCH_DESCRIPTION; import static org.thingsboard.server.controller.ControllerConstants.DEVICE_TEXT_SEARCH_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.DEVICE_TYPE_DESCRIPTION; import static org.thingsboard.server.controller.ControllerConstants.DEVICE_TYPE_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.DEVICE_WITH_DEVICE_CREDENTIALS_PARAM_DESCRIPTION_MARKDOWN; import static org.thingsboard.server.controller.ControllerConstants.DEVICE_UPDATE_CREDENTIALS_PARAM_ACCESS_TOKEN_DESCRIPTION_MARKDOWN;
import static org.thingsboard.server.controller.ControllerConstants.DEVICE_UPDATE_CREDENTIALS_PARAM_LVM2M_RPK_DESCRIPTION_MARKDOWN;
import static org.thingsboard.server.controller.ControllerConstants.DEVICE_UPDATE_CREDENTIALS_PARAM_MQTT_BASIC_DESCRIPTION_MARKDOWN;
import static org.thingsboard.server.controller.ControllerConstants.DEVICE_UPDATE_CREDENTIALS_PARAM_X509_CERTIFICATE_DESCRIPTION_MARKDOWN;
import static org.thingsboard.server.controller.ControllerConstants.DEVICE_WITH_DEVICE_CREDENTIALS_PARAM_ACCESS_TOKEN_DEFAULT_DESCRIPTION_MARKDOWN;
import static org.thingsboard.server.controller.ControllerConstants.DEVICE_WITH_DEVICE_CREDENTIALS_PARAM_ACCESS_TOKEN_DESCRIPTION_MARKDOWN;
import static org.thingsboard.server.controller.ControllerConstants.DEVICE_WITH_DEVICE_CREDENTIALS_PARAM_LVM2M_RPK_DESCRIPTION_MARKDOWN;
import static org.thingsboard.server.controller.ControllerConstants.DEVICE_WITH_DEVICE_CREDENTIALS_PARAM_MQTT_BASIC_DESCRIPTION_MARKDOWN;
import static org.thingsboard.server.controller.ControllerConstants.DEVICE_WITH_DEVICE_CREDENTIALS_PARAM_X509_CERTIFICATE_DESCRIPTION_MARKDOWN;
import static org.thingsboard.server.controller.ControllerConstants.EDGE_ASSIGN_ASYNC_FIRST_STEP_DESCRIPTION; import static org.thingsboard.server.controller.ControllerConstants.EDGE_ASSIGN_ASYNC_FIRST_STEP_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.EDGE_ASSIGN_RECEIVE_STEP_DESCRIPTION; import static org.thingsboard.server.controller.ControllerConstants.EDGE_ASSIGN_RECEIVE_STEP_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.EDGE_ID_PARAM_DESCRIPTION; import static org.thingsboard.server.controller.ControllerConstants.EDGE_ID_PARAM_DESCRIPTION;
@ -182,18 +191,29 @@ public class DeviceController extends BaseController {
@ApiOperation(value = "Create Device (saveDevice) with credentials ", @ApiOperation(value = "Create Device (saveDevice) with credentials ",
notes = "Create or update the Device. When creating device, platform generates Device Id as " + UUID_WIKI_LINK + notes = "Create or update the Device. When creating device, platform generates Device Id as " + UUID_WIKI_LINK +
"Requires to provide the Device Credentials object as well. Useful to create device and credentials in one request. " + "Requires to provide the Device Credentials object as well as an existing device profile ID or use \"default\".\n" +
"You may find the example of LwM2M device and RPK credentials below: \n\n" + "You may find the example of device with different type of credentials below: \n\n" +
DEVICE_WITH_DEVICE_CREDENTIALS_PARAM_DESCRIPTION_MARKDOWN + "- Credentials type: <b>\"Access token\"</b> with <b>device profile ID</b> below: \n\n" +
DEVICE_WITH_DEVICE_CREDENTIALS_PARAM_ACCESS_TOKEN_DESCRIPTION_MARKDOWN + "\n\n" +
"- Credentials type: <b>\"Access token\"</b> with <b>device profile default</b> below: \n\n" +
DEVICE_WITH_DEVICE_CREDENTIALS_PARAM_ACCESS_TOKEN_DEFAULT_DESCRIPTION_MARKDOWN + "\n\n" +
"- Credentials type: <b>\"X509\"</b> with <b>device profile ID</b> below: \n\n" +
"Note: <b>credentialsId</b> - format <b>Sha3Hash</b>, <b>certificateValue</b> - format <b>PEM</b> (with \"--BEGIN CERTIFICATE----\" and -\"----END CERTIFICATE-\").\n\n" +
DEVICE_WITH_DEVICE_CREDENTIALS_PARAM_X509_CERTIFICATE_DESCRIPTION_MARKDOWN + "\n\n" +
"- Credentials type: <b>\"MQTT_BASIC\"</b> with <b>device profile ID</b> below: \n\n" +
DEVICE_WITH_DEVICE_CREDENTIALS_PARAM_MQTT_BASIC_DESCRIPTION_MARKDOWN + "\n\n" +
"- You may find the example of <b>LwM2M</b> device and <b>RPK</b> credentials below: \n\n" +
"Note: LwM2M device - only existing device profile ID (Transport configuration -> Transport type: \"LWM2M\".\n\n" +
DEVICE_WITH_DEVICE_CREDENTIALS_PARAM_LVM2M_RPK_DESCRIPTION_MARKDOWN + "\n\n" +
"Remove 'id', 'tenantId' and optionally 'customerId' from the request body example (below) to create new Device entity. " + "Remove 'id', 'tenantId' and optionally 'customerId' from the request body example (below) to create new Device entity. " +
TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/device-with-credentials", method = RequestMethod.POST) @RequestMapping(value = "/device-with-credentials", method = RequestMethod.POST)
@ResponseBody @ResponseBody
public Device saveDeviceWithCredentials(@ApiParam(value = "The JSON object with device and credentials. See method description above for example.") public Device saveDeviceWithCredentials(@ApiParam(value = "The JSON object with device and credentials. See method description above for example.")
@RequestBody SaveDeviceWithCredentialsRequest deviceAndCredentials) throws ThingsboardException { @Valid @RequestBody SaveDeviceWithCredentialsRequest deviceAndCredentials) throws ThingsboardException {
Device device = checkNotNull(deviceAndCredentials.getDevice()); Device device = deviceAndCredentials.getDevice();
DeviceCredentials credentials = checkNotNull(deviceAndCredentials.getCredentials()); DeviceCredentials credentials = deviceAndCredentials.getCredentials();
device.setTenantId(getCurrentUser().getTenantId()); device.setTenantId(getCurrentUser().getTenantId());
checkEntity(device.getId(), device, Resource.DEVICE); checkEntity(device.getId(), device, Resource.DEVICE);
return tbDeviceService.saveDeviceWithCredentials(device, credentials, getCurrentUser()); return tbDeviceService.saveDeviceWithCredentials(device, credentials, getCurrentUser());
@ -277,10 +297,27 @@ public class DeviceController extends BaseController {
return tbDeviceService.getDeviceCredentialsByDeviceId(device, getCurrentUser()); return tbDeviceService.getDeviceCredentialsByDeviceId(device, getCurrentUser());
} }
@ApiOperation(value = "Update device credentials (updateDeviceCredentials)", notes = "During device creation, platform generates random 'ACCESS_TOKEN' credentials. " + @ApiOperation(value = "Update device credentials (updateDeviceCredentials)",
"Use this method to update the device credentials. First use 'getDeviceCredentialsByDeviceId' to get the credentials id and value. " + notes = "During device creation, platform generates random 'ACCESS_TOKEN' credentials. \" +\n" +
"Then use current method to update the credentials type and value. It is not possible to create multiple device credentials for the same device. " + "Use this method to update the device credentials. First use 'getDeviceCredentialsByDeviceId' to get the credentials id and value.\n" +
"The structure of device credentials id and value is simple for the 'ACCESS_TOKEN' but is much more complex for the 'MQTT_BASIC' or 'LWM2M_CREDENTIALS'." + TENANT_AUTHORITY_PARAGRAPH) "Then use current method to update the credentials type and value. It is not possible to create multiple device credentials for the same device.\n" +
"The structure of device credentials id and value is simple for the 'ACCESS_TOKEN' but is much more complex for the 'MQTT_BASIC' or 'LWM2M_CREDENTIALS'.\n" +
"You may find the example of device with different type of credentials below: \n\n" +
"- Credentials type: <b>\"Access token\"</b> with <b>device ID</b> and with <b>device ID</b> below: \n\n" +
DEVICE_UPDATE_CREDENTIALS_PARAM_ACCESS_TOKEN_DESCRIPTION_MARKDOWN + "\n\n" +
"- Credentials type: <b>\"X509\"</b> with <b>device profile ID</b> below: \n\n" +
"Note: <b>credentialsId</b> - format <b>Sha3Hash</b>, <b>certificateValue</b> - format <b>PEM</b> (with \"--BEGIN CERTIFICATE----\" and -\"----END CERTIFICATE-\").\n\n" +
DEVICE_UPDATE_CREDENTIALS_PARAM_X509_CERTIFICATE_DESCRIPTION_MARKDOWN + "\n\n" +
"- Credentials type: <b>\"MQTT_BASIC\"</b> with <b>device profile ID</b> below: \n\n" +
DEVICE_UPDATE_CREDENTIALS_PARAM_MQTT_BASIC_DESCRIPTION_MARKDOWN + "\n\n" +
"- You may find the example of <b>LwM2M</b> device and <b>RPK</b> credentials below: \n\n" +
"Note: LwM2M device - only existing device profile ID (Transport configuration -> Transport type: \"LWM2M\".\n\n" +
DEVICE_UPDATE_CREDENTIALS_PARAM_LVM2M_RPK_DESCRIPTION_MARKDOWN + "\n\n" +
"Update to real value:\n" +
" - 'id' (this is id of Device Credentials -> \"Get Device Credentials (getDeviceCredentialsByDeviceId)\",\n" +
" - 'deviceId.id' (this is id of Device).\n" +
"Remove 'tenantId' and optionally 'customerId' from the request body example (below) to create new Device entity." +
TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAuthority('TENANT_ADMIN')") @PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/device/credentials", method = RequestMethod.POST) @RequestMapping(value = "/device/credentials", method = RequestMethod.POST)
@ResponseBody @ResponseBody

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

@ -59,6 +59,8 @@ import org.thingsboard.server.dao.exception.IncorrectParameterException;
import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.edge.EdgeBulkImportService; import org.thingsboard.server.service.edge.EdgeBulkImportService;
import org.thingsboard.server.service.edge.instructions.EdgeInstallService;
import org.thingsboard.server.service.edge.rpc.EdgeRpcService;
import org.thingsboard.server.service.entitiy.edge.TbEdgeService; import org.thingsboard.server.service.entitiy.edge.TbEdgeService;
import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.model.SecurityUser;
import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Operation;
@ -67,6 +69,7 @@ import org.thingsboard.server.service.security.permission.Resource;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Optional;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -94,8 +97,11 @@ import static org.thingsboard.server.controller.ControllerConstants.UUID_WIKI_LI
@RequestMapping("/api") @RequestMapping("/api")
@RequiredArgsConstructor @RequiredArgsConstructor
public class EdgeController extends BaseController { public class EdgeController extends BaseController {
private final EdgeBulkImportService edgeBulkImportService; private final EdgeBulkImportService edgeBulkImportService;
private final TbEdgeService tbEdgeService; private final TbEdgeService tbEdgeService;
private final Optional<EdgeRpcService> edgeRpcServiceOpt;
private final Optional<EdgeInstallService> edgeInstallServiceOpt;
public static final String EDGE_ID = "edgeId"; public static final String EDGE_ID = "edgeId";
public static final String EDGE_SECURITY_CHECK = "If the user has the authority of 'Tenant Administrator', the server checks that the edge is owned by the same tenant. " + public static final String EDGE_SECURITY_CHECK = "If the user has the authority of 'Tenant Administrator', the server checks that the edge is owned by the same tenant. " +
@ -497,13 +503,13 @@ public class EdgeController extends BaseController {
@PathVariable("edgeId") String strEdgeId) throws ThingsboardException { @PathVariable("edgeId") String strEdgeId) throws ThingsboardException {
checkParameter("edgeId", strEdgeId); checkParameter("edgeId", strEdgeId);
final DeferredResult<ResponseEntity> response = new DeferredResult<>(); final DeferredResult<ResponseEntity> response = new DeferredResult<>();
if (isEdgesEnabled()) { if (isEdgesEnabled() && edgeRpcServiceOpt.isPresent()) {
EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
edgeId = checkNotNull(edgeId); edgeId = checkNotNull(edgeId);
SecurityUser user = getCurrentUser(); SecurityUser user = getCurrentUser();
TenantId tenantId = user.getTenantId(); TenantId tenantId = user.getTenantId();
ToEdgeSyncRequest request = new ToEdgeSyncRequest(UUID.randomUUID(), tenantId, edgeId); ToEdgeSyncRequest request = new ToEdgeSyncRequest(UUID.randomUUID(), tenantId, edgeId);
edgeRpcService.processSyncRequest(request, fromEdgeSyncResponse -> reply(response, fromEdgeSyncResponse)); edgeRpcServiceOpt.get().processSyncRequest(request, fromEdgeSyncResponse -> reply(response, fromEdgeSyncResponse));
} else { } else {
throw new ThingsboardException("Edges support disabled", ThingsboardErrorCode.GENERAL); throw new ThingsboardException("Edges support disabled", ThingsboardErrorCode.GENERAL);
} }
@ -557,9 +563,13 @@ public class EdgeController extends BaseController {
@ApiParam(value = EDGE_ID_PARAM_DESCRIPTION, required = true) @ApiParam(value = EDGE_ID_PARAM_DESCRIPTION, required = true)
@PathVariable("edgeId") String strEdgeId, @PathVariable("edgeId") String strEdgeId,
HttpServletRequest request) throws ThingsboardException { HttpServletRequest request) throws ThingsboardException {
EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); if (isEdgesEnabled() && edgeInstallServiceOpt.isPresent()) {
edgeId = checkNotNull(edgeId); EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
Edge edge = checkEdgeId(edgeId, Operation.READ); edgeId = checkNotNull(edgeId);
return checkNotNull(edgeInstallService.getDockerInstallInstructions(getTenantId(), edge, request)); Edge edge = checkEdgeId(edgeId, Operation.READ);
return checkNotNull(edgeInstallServiceOpt.get().getDockerInstallInstructions(getTenantId(), edge, request));
} else {
throw new ThingsboardException("Edges support disabled", ThingsboardErrorCode.GENERAL);
}
} }
} }

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

@ -85,6 +85,6 @@ public class EdgeEventController extends BaseController {
EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
checkEdgeId(edgeId, Operation.READ); checkEdgeId(edgeId, Operation.READ);
TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime); TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime);
return checkNotNull(edgeEventService.findEdgeEvents(tenantId, edgeId, pageLink, false)); return checkNotNull(edgeEventService.findEdgeEvents(tenantId, edgeId, 0L, null, pageLink));
} }
} }

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

@ -117,8 +117,8 @@ public class EntityRelationController extends BaseController {
tbEntityRelationService.delete(getTenantId(), getCurrentUser().getCustomerId(), relation, getCurrentUser()); tbEntityRelationService.delete(getTenantId(), getCurrentUser().getCustomerId(), relation, getCurrentUser());
} }
@ApiOperation(value = "Delete Relations (deleteRelations)", @ApiOperation(value = "Delete common relations (deleteCommonRelations)",
notes = "Deletes all the relation (both 'from' and 'to' direction) for the specified entity. " + notes = "Deletes all the relations ('from' and 'to' direction) for the specified entity and relation type group: 'COMMON'. " +
SECURITY_CHECKS_ENTITY_DESCRIPTION) SECURITY_CHECKS_ENTITY_DESCRIPTION)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN','TENANT_ADMIN', 'CUSTOMER_USER')") @PreAuthorize("hasAnyAuthority('SYS_ADMIN','TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/relations", method = RequestMethod.DELETE, params = {"entityId", "entityType"}) @RequestMapping(value = "/relations", method = RequestMethod.DELETE, params = {"entityId", "entityType"})
@ -129,7 +129,7 @@ public class EntityRelationController extends BaseController {
checkParameter("entityType", strType); checkParameter("entityType", strType);
EntityId entityId = EntityIdFactory.getByTypeAndId(strType, strId); EntityId entityId = EntityIdFactory.getByTypeAndId(strType, strId);
checkEntityId(entityId, Operation.WRITE); checkEntityId(entityId, Operation.WRITE);
tbEntityRelationService.deleteRelations(getTenantId(), getCurrentUser().getCustomerId(), entityId, getCurrentUser()); tbEntityRelationService.deleteCommonRelations(getTenantId(), getCurrentUser().getCustomerId(), entityId, getCurrentUser());
} }
@ApiOperation(value = "Get Relation (getRelation)", @ApiOperation(value = "Get Relation (getRelation)",

49
application/src/main/java/org/thingsboard/server/controller/NotificationController.java

@ -44,6 +44,8 @@ import org.thingsboard.server.common.data.notification.NotificationRequest;
import org.thingsboard.server.common.data.notification.NotificationRequestInfo; import org.thingsboard.server.common.data.notification.NotificationRequestInfo;
import org.thingsboard.server.common.data.notification.NotificationRequestPreview; import org.thingsboard.server.common.data.notification.NotificationRequestPreview;
import org.thingsboard.server.common.data.notification.settings.NotificationSettings; import org.thingsboard.server.common.data.notification.settings.NotificationSettings;
import org.thingsboard.server.common.data.notification.settings.UserNotificationSettings;
import org.thingsboard.server.common.data.notification.targets.MicrosoftTeamsNotificationTargetConfig;
import org.thingsboard.server.common.data.notification.targets.NotificationRecipient; import org.thingsboard.server.common.data.notification.targets.NotificationRecipient;
import org.thingsboard.server.common.data.notification.targets.NotificationTarget; import org.thingsboard.server.common.data.notification.targets.NotificationTarget;
import org.thingsboard.server.common.data.notification.targets.NotificationTargetType; import org.thingsboard.server.common.data.notification.targets.NotificationTargetType;
@ -294,15 +296,27 @@ public class NotificationController extends BaseController {
int recipientsCount; int recipientsCount;
List<NotificationRecipient> recipientsPart; List<NotificationRecipient> recipientsPart;
NotificationTargetType targetType = target.getConfiguration().getType(); NotificationTargetType targetType = target.getConfiguration().getType();
if (targetType == NotificationTargetType.PLATFORM_USERS) { switch (targetType) {
PageData<User> recipients = notificationTargetService.findRecipientsForNotificationTargetConfig(user.getTenantId(), case PLATFORM_USERS: {
(PlatformUsersNotificationTargetConfig) target.getConfiguration(), new PageLink(recipientsPreviewSize, 0, null, PageData<User> recipients = notificationTargetService.findRecipientsForNotificationTargetConfig(user.getTenantId(),
new SortOrder("createdTime", SortOrder.Direction.DESC))); (PlatformUsersNotificationTargetConfig) target.getConfiguration(), new PageLink(recipientsPreviewSize, 0, null,
recipientsCount = (int) recipients.getTotalElements(); SortOrder.BY_CREATED_TIME_DESC));
recipientsPart = recipients.getData().stream().map(r -> (NotificationRecipient) r).collect(Collectors.toList()); recipientsCount = (int) recipients.getTotalElements();
} else { recipientsPart = recipients.getData().stream().map(r -> (NotificationRecipient) r).collect(Collectors.toList());
recipientsCount = 1; break;
recipientsPart = List.of(((SlackNotificationTargetConfig) target.getConfiguration()).getConversation()); }
case SLACK: {
recipientsCount = 1;
recipientsPart = List.of(((SlackNotificationTargetConfig) target.getConfiguration()).getConversation());
break;
}
case MICROSOFT_TEAMS: {
recipientsCount = 1;
recipientsPart = List.of(((MicrosoftTeamsNotificationTargetConfig) target.getConfiguration()));
break;
}
default:
throw new IllegalArgumentException("Target type " + targetType + " not supported");
} }
firstRecipient.putIfAbsent(targetType, !recipientsPart.isEmpty() ? recipientsPart.get(0) : null); firstRecipient.putIfAbsent(targetType, !recipientsPart.isEmpty() ? recipientsPart.get(0) : null);
for (NotificationRecipient recipient : recipientsPart) { for (NotificationRecipient recipient : recipientsPart) {
@ -431,10 +445,23 @@ public class NotificationController extends BaseController {
notes = "Returns the list of delivery methods that are properly configured and are allowed to be used for sending notifications." + notes = "Returns the list of delivery methods that are properly configured and are allowed to be used for sending notifications." +
SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@GetMapping("/notification/deliveryMethods") @GetMapping("/notification/deliveryMethods")
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
public Set<NotificationDeliveryMethod> getAvailableDeliveryMethods(@AuthenticationPrincipal SecurityUser user) throws ThingsboardException { public Set<NotificationDeliveryMethod> getAvailableDeliveryMethods(@AuthenticationPrincipal SecurityUser user) throws ThingsboardException {
accessControlService.checkPermission(user, Resource.ADMIN_SETTINGS, Operation.READ);
return notificationCenter.getAvailableDeliveryMethods(user.getTenantId()); return notificationCenter.getAvailableDeliveryMethods(user.getTenantId());
} }
@PostMapping("/notification/settings/user")
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
public UserNotificationSettings saveUserNotificationSettings(@RequestBody @Valid UserNotificationSettings settings,
@AuthenticationPrincipal SecurityUser user) {
return notificationSettingsService.saveUserNotificationSettings(user.getTenantId(), user.getId(), settings);
}
@GetMapping("/notification/settings/user")
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
public UserNotificationSettings getUserNotificationSettings(@AuthenticationPrincipal SecurityUser user) {
return notificationSettingsService.getUserNotificationSettings(user.getTenantId(), user.getId(), true);
}
} }

1
application/src/main/java/org/thingsboard/server/controller/QueueController.java

@ -126,7 +126,6 @@ public class QueueController extends BaseController {
@PreAuthorize("hasAnyAuthority('SYS_ADMIN')") @PreAuthorize("hasAnyAuthority('SYS_ADMIN')")
@RequestMapping(value = "/queues", params = {"serviceType"}, method = RequestMethod.POST) @RequestMapping(value = "/queues", params = {"serviceType"}, method = RequestMethod.POST)
@ResponseBody @ResponseBody
public Queue saveQueue(@ApiParam(value = "A JSON value representing the queue.") public Queue saveQueue(@ApiParam(value = "A JSON value representing the queue.")
@RequestBody Queue queue, @RequestBody Queue queue,
@ApiParam(value = QUEUE_SERVICE_TYPE_DESCRIPTION, allowableValues = QUEUE_SERVICE_TYPE_ALLOWABLE_VALUES, required = true) @ApiParam(value = QUEUE_SERVICE_TYPE_DESCRIPTION, allowableValues = QUEUE_SERVICE_TYPE_ALLOWABLE_VALUES, required = true)

4
application/src/main/java/org/thingsboard/server/controller/RpcV2Controller.java

@ -38,6 +38,7 @@ import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.RpcId; import org.thingsboard.server.common.data.id.RpcId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.msg.TbMsgType;
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.rpc.Rpc; import org.thingsboard.server.common.data.rpc.Rpc;
@ -52,7 +53,6 @@ import org.thingsboard.server.service.security.permission.Operation;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.util.UUID; import java.util.UUID;
import static org.thingsboard.server.common.data.DataConstants.RPC_DELETED;
import static org.thingsboard.server.controller.ControllerConstants.DEVICE_ID; import static org.thingsboard.server.controller.ControllerConstants.DEVICE_ID;
import static org.thingsboard.server.controller.ControllerConstants.DEVICE_ID_PARAM_DESCRIPTION; import static org.thingsboard.server.controller.ControllerConstants.DEVICE_ID_PARAM_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.MARKDOWN_CODE_BLOCK_END; import static org.thingsboard.server.controller.ControllerConstants.MARKDOWN_CODE_BLOCK_END;
@ -239,7 +239,7 @@ public class RpcV2Controller extends AbstractRpcController {
rpcService.deleteRpc(getTenantId(), rpcId); rpcService.deleteRpc(getTenantId(), rpcId);
rpc.setStatus(RpcStatus.DELETED); rpc.setStatus(RpcStatus.DELETED);
TbMsg msg = TbMsg.newMsg(RPC_DELETED, rpc.getDeviceId(), TbMsgMetaData.EMPTY, JacksonUtil.toString(rpc)); TbMsg msg = TbMsg.newMsg(TbMsgType.RPC_DELETED, rpc.getDeviceId(), TbMsgMetaData.EMPTY, JacksonUtil.toString(rpc));
tbClusterService.pushMsgToRuleEngine(getTenantId(), rpc.getDeviceId(), msg, null); tbClusterService.pushMsgToRuleEngine(getTenantId(), rpc.getDeviceId(), msg, null);
} }
} }

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

@ -201,7 +201,7 @@ public class TelemetryController extends BaseController {
@ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr, @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr,
@ApiParam(value = ATTRIBUTES_SCOPE_DESCRIPTION, required = true, allowableValues = ATTRIBUTES_SCOPE_ALLOWED_VALUES) @PathVariable("scope") String scope) throws ThingsboardException { @ApiParam(value = ATTRIBUTES_SCOPE_DESCRIPTION, required = true, allowableValues = ATTRIBUTES_SCOPE_ALLOWED_VALUES) @PathVariable("scope") String scope) throws ThingsboardException {
return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_ATTRIBUTES, entityType, entityIdStr, return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_ATTRIBUTES, entityType, entityIdStr,
(result, tenantId, entityId) -> getAttributeKeysCallback(result, tenantId, entityId, scope)); (result, tenantId, entityId) -> getAttributeKeysCallback(result, tenantId, entityId, scope));
} }
@ApiOperation(value = "Get attributes (getAttributes)", @ApiOperation(value = "Get attributes (getAttributes)",
@ -219,9 +219,9 @@ public class TelemetryController extends BaseController {
@ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, defaultValue = "DEVICE") @PathVariable("entityType") String entityType, @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, defaultValue = "DEVICE") @PathVariable("entityType") String entityType,
@ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr, @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr,
@ApiParam(value = ATTRIBUTES_KEYS_DESCRIPTION) @RequestParam(name = "keys", required = false) String keysStr) throws ThingsboardException { @ApiParam(value = ATTRIBUTES_KEYS_DESCRIPTION) @RequestParam(name = "keys", required = false) String keysStr) throws ThingsboardException {
SecurityUser user = getCurrentUser(); SecurityUser user = getCurrentUser();
return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_ATTRIBUTES, entityType, entityIdStr, return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_ATTRIBUTES, entityType, entityIdStr,
(result, tenantId, entityId) -> getAttributeValuesCallback(result, user, entityId, null, keysStr)); (result, tenantId, entityId) -> getAttributeValuesCallback(result, user, entityId, null, keysStr));
} }
@ -245,7 +245,7 @@ public class TelemetryController extends BaseController {
@ApiParam(value = ATTRIBUTES_KEYS_DESCRIPTION) @RequestParam(name = "keys", required = false) String keysStr) throws ThingsboardException { @ApiParam(value = ATTRIBUTES_KEYS_DESCRIPTION) @RequestParam(name = "keys", required = false) String keysStr) throws ThingsboardException {
SecurityUser user = getCurrentUser(); SecurityUser user = getCurrentUser();
return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_ATTRIBUTES, entityType, entityIdStr, return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_ATTRIBUTES, entityType, entityIdStr,
(result, tenantId, entityId) -> getAttributeValuesCallback(result, user, entityId, scope, keysStr)); (result, tenantId, entityId) -> getAttributeValuesCallback(result, user, entityId, scope, keysStr));
} }
@ApiOperation(value = "Get time-series keys (getTimeseriesKeys)", @ApiOperation(value = "Get time-series keys (getTimeseriesKeys)",
@ -259,7 +259,7 @@ public class TelemetryController extends BaseController {
@ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, defaultValue = "DEVICE") @PathVariable("entityType") String entityType, @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, defaultValue = "DEVICE") @PathVariable("entityType") String entityType,
@ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr) throws ThingsboardException { @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr) throws ThingsboardException {
return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_TELEMETRY, entityType, entityIdStr, return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_TELEMETRY, entityType, entityIdStr,
(result, tenantId, entityId) -> Futures.addCallback(tsService.findAllLatest(tenantId, entityId), getTsKeysToResponseCallback(result), MoreExecutors.directExecutor())); (result, tenantId, entityId) -> Futures.addCallback(tsService.findAllLatest(tenantId, entityId), getTsKeysToResponseCallback(result), MoreExecutors.directExecutor()));
} }
@ApiOperation(value = "Get latest time-series value (getLatestTimeseries)", @ApiOperation(value = "Get latest time-series value (getLatestTimeseries)",
@ -462,7 +462,9 @@ public class TelemetryController extends BaseController {
notes = "Delete time-series for selected entity based on entity id, entity type and keys." + notes = "Delete time-series for selected entity based on entity id, entity type and keys." +
" Use 'deleteAllDataForKeys' to delete all time-series data." + " Use 'deleteAllDataForKeys' to delete all time-series data." +
" Use 'startTs' and 'endTs' to specify time-range instead. " + " Use 'startTs' and 'endTs' to specify time-range instead. " +
" Use 'rewriteLatestIfDeleted' to rewrite latest value (stored in separate table for performance) after deletion of the time range. " + " Use 'deleteLatest' to delete latest value (stored in separate table for performance) if the value's timestamp matches the time-range. " +
" Use 'rewriteLatestIfDeleted' to rewrite latest value (stored in separate table for performance) if the value's timestamp matches the time-range and 'deleteLatest' param is true." +
" The replacement value will be fetched from the 'time-series' table, and its timestamp will be the most recent one before the defined time-range. " +
TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH,
produces = MediaType.APPLICATION_JSON_VALUE) produces = MediaType.APPLICATION_JSON_VALUE)
@ApiResponses(value = { @ApiResponses(value = {
@ -486,14 +488,16 @@ public class TelemetryController extends BaseController {
@RequestParam(name = "startTs", required = false) Long startTs, @RequestParam(name = "startTs", required = false) Long startTs,
@ApiParam(value = "A long value representing the end timestamp of removal time range in milliseconds.") @ApiParam(value = "A long value representing the end timestamp of removal time range in milliseconds.")
@RequestParam(name = "endTs", required = false) Long endTs, @RequestParam(name = "endTs", required = false) Long endTs,
@ApiParam(value = "If the parameter is set to true, the latest telemetry can be removed, otherwise, in case that parameter is set to false the latest value will not removed.")
@RequestParam(name = "deleteLatest", required = false, defaultValue = "true") boolean deleteLatest,
@ApiParam(value = "If the parameter is set to true, the latest telemetry will be rewritten in case that current latest value was removed, otherwise, in case that parameter is set to false the new latest value will not set.") @ApiParam(value = "If the parameter is set to true, the latest telemetry will be rewritten in case that current latest value was removed, otherwise, in case that parameter is set to false the new latest value will not set.")
@RequestParam(name = "rewriteLatestIfDeleted", defaultValue = "false") boolean rewriteLatestIfDeleted) throws ThingsboardException { @RequestParam(name = "rewriteLatestIfDeleted", defaultValue = "false") boolean rewriteLatestIfDeleted) throws ThingsboardException {
EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr);
return deleteTimeseries(entityId, keysStr, deleteAllDataForKeys, startTs, endTs, rewriteLatestIfDeleted); return deleteTimeseries(entityId, keysStr, deleteAllDataForKeys, startTs, endTs, rewriteLatestIfDeleted, deleteLatest);
} }
private DeferredResult<ResponseEntity> deleteTimeseries(EntityId entityIdStr, String keysStr, boolean deleteAllDataForKeys, private DeferredResult<ResponseEntity> deleteTimeseries(EntityId entityIdStr, String keysStr, boolean deleteAllDataForKeys,
Long startTs, Long endTs, boolean rewriteLatestIfDeleted) throws ThingsboardException { Long startTs, Long endTs, boolean rewriteLatestIfDeleted, boolean deleteLatest) throws ThingsboardException {
List<String> keys = toKeysList(keysStr); List<String> keys = toKeysList(keysStr);
if (keys.isEmpty()) { if (keys.isEmpty()) {
return getImmediateDeferredResult("Empty keys: " + keysStr, HttpStatus.BAD_REQUEST); return getImmediateDeferredResult("Empty keys: " + keysStr, HttpStatus.BAD_REQUEST);
@ -517,7 +521,7 @@ public class TelemetryController extends BaseController {
return accessValidator.validateEntityAndCallback(user, Operation.WRITE_TELEMETRY, entityIdStr, (result, tenantId, entityId) -> { return accessValidator.validateEntityAndCallback(user, Operation.WRITE_TELEMETRY, entityIdStr, (result, tenantId, entityId) -> {
List<DeleteTsKvQuery> deleteTsKvQueries = new ArrayList<>(); List<DeleteTsKvQuery> deleteTsKvQueries = new ArrayList<>();
for (String key : keys) { for (String key : keys) {
deleteTsKvQueries.add(new BaseDeleteTsKvQuery(key, deleteFromTs, deleteToTs, rewriteLatestIfDeleted)); deleteTsKvQueries.add(new BaseDeleteTsKvQuery(key, deleteFromTs, deleteToTs, rewriteLatestIfDeleted, deleteLatest));
} }
tsSubService.deleteTimeseriesAndNotify(tenantId, entityId, keys, deleteTsKvQueries, new FutureCallback<>() { tsSubService.deleteTimeseriesAndNotify(tenantId, entityId, keys, deleteTsKvQueries, new FutureCallback<>() {
@Override @Override

58
application/src/main/java/org/thingsboard/server/controller/WidgetTypeController.java

@ -28,7 +28,7 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import org.thingsboard.server.common.data.edge.EdgeEventActionType; import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.WidgetTypeId; import org.thingsboard.server.common.data.id.WidgetTypeId;
@ -107,9 +107,6 @@ public class WidgetTypeController extends AutoCommitController {
} }
} }
sendEntityNotificationMsg(getTenantId(), savedWidgetTypeDetails.getId(),
widgetTypeDetails.getId() == null ? EdgeEventActionType.ADDED : EdgeEventActionType.UPDATED);
return checkNotNull(savedWidgetTypeDetails); return checkNotNull(savedWidgetTypeDetails);
} }
@ -133,8 +130,6 @@ public class WidgetTypeController extends AutoCommitController {
autoCommit(currentUser, widgetsBundle.getId()); autoCommit(currentUser, widgetsBundle.getId());
} }
} }
sendEntityNotificationMsg(getTenantId(), widgetTypeId, EdgeEventActionType.DELETED);
} }
@ApiOperation(value = "Get all Widget types for specified Bundle (getBundleWidgetTypes)", @ApiOperation(value = "Get all Widget types for specified Bundle (getBundleWidgetTypes)",
@ -212,10 +207,59 @@ public class WidgetTypeController extends AutoCommitController {
} else { } else {
tenantId = getCurrentUser().getTenantId(); tenantId = getCurrentUser().getTenantId();
} }
WidgetType widgetType = widgetTypeService.findWidgetTypeByTenantIdBundleAliasAndAlias(tenantId, bundleAlias, alias); WidgetType widgetType = widgetTypeService.findWidgetTypeByTenantIdAndFqn(tenantId, bundleAlias + "." + alias);
checkNotNull(widgetType);
accessControlService.checkPermission(getCurrentUser(), Resource.WIDGET_TYPE, Operation.READ, widgetType.getId(), widgetType);
return widgetType;
}
@ApiOperation(value = "Get Widget Type by fqn (getWidgetTypeByFqn)",
notes = "Get the Widget Type by FQN. " + WIDGET_TYPE_DESCRIPTION + AVAILABLE_FOR_ANY_AUTHORIZED_USER)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/widgetType", params = {"fqn"}, method = RequestMethod.GET)
@ResponseBody
public WidgetType getWidgetTypeByFqn(
@ApiParam(value = "Widget Type fqn", required = true)
@RequestParam String fqn) throws ThingsboardException {
String[] parts = fqn.split("\\.");
String scopeQualifier = parts.length > 0 ? parts[0] : null;
if (parts.length < 2 || (!scopeQualifier.equals("system") && !scopeQualifier.equals("tenant"))) {
throw new ThingsboardException("Invalid fqn!", ThingsboardErrorCode.BAD_REQUEST_PARAMS);
}
TenantId tenantId;
if ("system".equals(scopeQualifier)) {
tenantId = TenantId.fromUUID(ModelConstants.NULL_UUID);
} else {
tenantId = getCurrentUser().getTenantId();
}
String typeFqn = fqn.substring(scopeQualifier.length() + 1);
WidgetType widgetType = widgetTypeService.findWidgetTypeByTenantIdAndFqn(tenantId, typeFqn);
checkNotNull(widgetType); checkNotNull(widgetType);
accessControlService.checkPermission(getCurrentUser(), Resource.WIDGET_TYPE, Operation.READ, widgetType.getId(), widgetType); accessControlService.checkPermission(getCurrentUser(), Resource.WIDGET_TYPE, Operation.READ, widgetType.getId(), widgetType);
return widgetType; return widgetType;
} }
@ApiOperation(value = "Set widget type deprecated (setWidgetTypeDeprecated)",
notes = "Set Widget Type deprecated flag. Referencing non-existing Widget Type Id will cause an error." + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@RequestMapping(value = "/widgetType/{widgetTypeId}/deprecate/{deprecated}", method = RequestMethod.POST)
@ResponseStatus(value = HttpStatus.OK)
public void setWidgetTypeDeprecated(
@ApiParam(value = WIDGET_TYPE_ID_PARAM_DESCRIPTION, required = true)
@PathVariable("widgetTypeId") String strWidgetTypeId,
@PathVariable("deprecated") boolean deprecated) throws Exception {
checkParameter("widgetTypeId", strWidgetTypeId);
var currentUser = getCurrentUser();
WidgetTypeId widgetTypeId = new WidgetTypeId(toUUID(strWidgetTypeId));
WidgetTypeDetails wtd = checkWidgetTypeId(widgetTypeId, Operation.WRITE);
widgetTypeService.setWidgetTypeDeprecated(currentUser.getTenantId(), widgetTypeId, deprecated);
if (wtd != null && !Authority.SYS_ADMIN.equals(currentUser.getAuthority())) {
WidgetsBundle widgetsBundle = widgetsBundleService.findWidgetsBundleByTenantIdAndAlias(wtd.getTenantId(), wtd.getBundleAlias());
if (widgetsBundle != null) {
autoCommit(currentUser, widgetsBundle.getId());
}
}
}
} }

88
application/src/main/java/org/thingsboard/server/controller/plugin/TbWebSocketHandler.java

@ -33,10 +33,10 @@ import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.TenantId; 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.limit.LimitedApi;
import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration;
import org.thingsboard.server.config.WebSocketConfiguration; import org.thingsboard.server.config.WebSocketConfiguration;
import org.thingsboard.server.dao.tenant.TbTenantProfileCache; import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
import org.thingsboard.server.common.data.limit.LimitedApi;
import org.thingsboard.server.dao.util.limits.RateLimitService; import org.thingsboard.server.dao.util.limits.RateLimitService;
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.model.SecurityUser;
@ -221,12 +221,12 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements WebSocke
.build(); .build();
} }
private class SessionMetaData implements SendHandler { class SessionMetaData implements SendHandler {
private final WebSocketSession session; private final WebSocketSession session;
private final RemoteEndpoint.Async asyncRemote; private final RemoteEndpoint.Async asyncRemote;
private final WebSocketSessionRef sessionRef; private final WebSocketSessionRef sessionRef;
private final AtomicBoolean isSending = new AtomicBoolean(false); final AtomicBoolean isSending = new AtomicBoolean(false);
private final Queue<TbWebSocketMsg<?>> msgQueue; private final Queue<TbWebSocketMsg<?>> msgQueue;
private volatile long lastActivityTime; private volatile long lastActivityTime;
@ -241,7 +241,7 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements WebSocke
this.lastActivityTime = System.currentTimeMillis(); this.lastActivityTime = System.currentTimeMillis();
} }
synchronized void sendPing(long currentTime) { void sendPing(long currentTime) {
try { try {
long timeSinceLastActivity = currentTime - lastActivityTime; long timeSinceLastActivity = currentTime - lastActivityTime;
if (timeSinceLastActivity >= pingTimeout) { if (timeSinceLastActivity >= pingTimeout) {
@ -256,37 +256,38 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements WebSocke
} }
} }
private void closeSession(CloseStatus reason) { void closeSession(CloseStatus reason) {
try { try {
close(this.sessionRef, reason); close(this.sessionRef, reason);
} catch (IOException ioe) { } catch (IOException ioe) {
log.trace("[{}] Session transport error", session.getId(), ioe); log.trace("[{}] Session transport error", session.getId(), ioe);
} finally {
msgQueue.clear();
} }
} }
synchronized void processPongMessage(long currentTime) { void processPongMessage(long currentTime) {
lastActivityTime = currentTime; lastActivityTime = currentTime;
} }
synchronized void sendMsg(String msg) { void sendMsg(String msg) {
sendMsg(new TbWebSocketTextMsg(msg)); sendMsg(new TbWebSocketTextMsg(msg));
} }
synchronized void sendMsg(TbWebSocketMsg<?> msg) { void sendMsg(TbWebSocketMsg<?> msg) {
if (isSending.compareAndSet(false, true)) { try {
sendMsgInternal(msg); msgQueue.add(msg);
} else { } catch (RuntimeException e) {
try { if (log.isTraceEnabled()) {
msgQueue.add(msg); log.trace("[{}][{}] Session closed due to queue error", sessionRef.getSecurityCtx().getTenantId(), session.getId(), e);
} catch (RuntimeException e) { } else {
if (log.isTraceEnabled()) { log.info("[{}][{}] Session closed due to queue error", sessionRef.getSecurityCtx().getTenantId(), session.getId());
log.trace("[{}][{}] Session closed due to queue error", sessionRef.getSecurityCtx().getTenantId(), session.getId(), e);
} else {
log.info("[{}][{}] Session closed due to queue error", sessionRef.getSecurityCtx().getTenantId(), session.getId());
}
closeSession(CloseStatus.POLICY_VIOLATION.withReason("Max pending updates limit reached!"));
} }
closeSession(CloseStatus.POLICY_VIOLATION.withReason("Max pending updates limit reached!"));
return;
} }
processNextMsg();
} }
private void sendMsgInternal(TbWebSocketMsg<?> msg) { private void sendMsgInternal(TbWebSocketMsg<?> msg) {
@ -294,9 +295,11 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements WebSocke
if (TbWebSocketMsgType.TEXT.equals(msg.getType())) { if (TbWebSocketMsgType.TEXT.equals(msg.getType())) {
TbWebSocketTextMsg textMsg = (TbWebSocketTextMsg) msg; TbWebSocketTextMsg textMsg = (TbWebSocketTextMsg) msg;
this.asyncRemote.sendText(textMsg.getMsg(), this); this.asyncRemote.sendText(textMsg.getMsg(), this);
// isSending status will be reset in the onResult method by call back
} else { } else {
TbWebSocketPingMsg pingMsg = (TbWebSocketPingMsg) msg; TbWebSocketPingMsg pingMsg = (TbWebSocketPingMsg) msg;
this.asyncRemote.sendPing(pingMsg.getMsg()); this.asyncRemote.sendPing(pingMsg.getMsg()); // blocking call
isSending.set(false);
processNextMsg(); processNextMsg();
} }
} catch (Exception e) { } catch (Exception e) {
@ -310,12 +313,17 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements WebSocke
if (!result.isOK()) { if (!result.isOK()) {
log.trace("[{}] Failed to send msg", session.getId(), result.getException()); log.trace("[{}] Failed to send msg", session.getId(), result.getException());
closeSession(CloseStatus.SESSION_NOT_RELIABLE); closeSession(CloseStatus.SESSION_NOT_RELIABLE);
} else { return;
processNextMsg();
} }
isSending.set(false);
processNextMsg();
} }
private void processNextMsg() { private void processNextMsg() {
if (msgQueue.isEmpty() || !isSending.compareAndSet(false, true)) {
return;
}
TbWebSocketMsg<?> msg = msgQueue.poll(); TbWebSocketMsg<?> msg = msgQueue.poll();
if (msg != null) { if (msg != null) {
sendMsgInternal(msg); sendMsgInternal(msg);
@ -392,19 +400,21 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements WebSocke
if (tenantProfileConfiguration == null) { if (tenantProfileConfiguration == null) {
return true; return true;
} }
boolean limitAllowed;
String sessionId = session.getId(); String sessionId = session.getId();
if (tenantProfileConfiguration.getMaxWsSessionsPerTenant() > 0) { if (tenantProfileConfiguration.getMaxWsSessionsPerTenant() > 0) {
Set<String> tenantSessions = tenantSessionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getTenantId(), id -> ConcurrentHashMap.newKeySet()); Set<String> tenantSessions = tenantSessionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getTenantId(), id -> ConcurrentHashMap.newKeySet());
synchronized (tenantSessions) { synchronized (tenantSessions) {
if (tenantSessions.size() < tenantProfileConfiguration.getMaxWsSessionsPerTenant()) { limitAllowed = tenantSessions.size() < tenantProfileConfiguration.getMaxWsSessionsPerTenant();
if (limitAllowed) {
tenantSessions.add(sessionId); tenantSessions.add(sessionId);
} else { }
}
if (!limitAllowed) {
log.info("[{}][{}][{}] Failed to start session. Max tenant sessions limit reached" log.info("[{}][{}][{}] Failed to start session. Max tenant sessions limit reached"
, sessionRef.getSecurityCtx().getTenantId(), sessionRef.getSecurityCtx().getId(), sessionId); , sessionRef.getSecurityCtx().getTenantId(), sessionRef.getSecurityCtx().getId(), sessionId);
session.close(CloseStatus.POLICY_VIOLATION.withReason("Max tenant sessions limit reached!")); session.close(CloseStatus.POLICY_VIOLATION.withReason("Max tenant sessions limit reached!"));
return false; return false;
}
} }
} }
@ -412,42 +422,48 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements WebSocke
if (tenantProfileConfiguration.getMaxWsSessionsPerCustomer() > 0) { if (tenantProfileConfiguration.getMaxWsSessionsPerCustomer() > 0) {
Set<String> customerSessions = customerSessionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getCustomerId(), id -> ConcurrentHashMap.newKeySet()); Set<String> customerSessions = customerSessionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getCustomerId(), id -> ConcurrentHashMap.newKeySet());
synchronized (customerSessions) { synchronized (customerSessions) {
if (customerSessions.size() < tenantProfileConfiguration.getMaxWsSessionsPerCustomer()) { limitAllowed = customerSessions.size() < tenantProfileConfiguration.getMaxWsSessionsPerCustomer();
if (limitAllowed) {
customerSessions.add(sessionId); customerSessions.add(sessionId);
} else { }
}
if (!limitAllowed) {
log.info("[{}][{}][{}] Failed to start session. Max customer sessions limit reached" log.info("[{}][{}][{}] Failed to start session. Max customer sessions limit reached"
, sessionRef.getSecurityCtx().getTenantId(), sessionRef.getSecurityCtx().getId(), sessionId); , sessionRef.getSecurityCtx().getTenantId(), sessionRef.getSecurityCtx().getId(), sessionId);
session.close(CloseStatus.POLICY_VIOLATION.withReason("Max customer sessions limit reached")); session.close(CloseStatus.POLICY_VIOLATION.withReason("Max customer sessions limit reached"));
return false; return false;
}
} }
} }
if (tenantProfileConfiguration.getMaxWsSessionsPerRegularUser() > 0 if (tenantProfileConfiguration.getMaxWsSessionsPerRegularUser() > 0
&& UserPrincipal.Type.USER_NAME.equals(sessionRef.getSecurityCtx().getUserPrincipal().getType())) { && UserPrincipal.Type.USER_NAME.equals(sessionRef.getSecurityCtx().getUserPrincipal().getType())) {
Set<String> regularUserSessions = regularUserSessionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getId(), id -> ConcurrentHashMap.newKeySet()); Set<String> regularUserSessions = regularUserSessionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getId(), id -> ConcurrentHashMap.newKeySet());
synchronized (regularUserSessions) { synchronized (regularUserSessions) {
if (regularUserSessions.size() < tenantProfileConfiguration.getMaxWsSessionsPerRegularUser()) { limitAllowed = regularUserSessions.size() < tenantProfileConfiguration.getMaxWsSessionsPerRegularUser();
if (limitAllowed) {
regularUserSessions.add(sessionId); regularUserSessions.add(sessionId);
} else { }
}
if (!limitAllowed) {
log.info("[{}][{}][{}] Failed to start session. Max regular user sessions limit reached" log.info("[{}][{}][{}] Failed to start session. Max regular user sessions limit reached"
, sessionRef.getSecurityCtx().getTenantId(), sessionRef.getSecurityCtx().getId(), sessionId); , sessionRef.getSecurityCtx().getTenantId(), sessionRef.getSecurityCtx().getId(), sessionId);
session.close(CloseStatus.POLICY_VIOLATION.withReason("Max regular user sessions limit reached")); session.close(CloseStatus.POLICY_VIOLATION.withReason("Max regular user sessions limit reached"));
return false; return false;
}
} }
} }
if (tenantProfileConfiguration.getMaxWsSessionsPerPublicUser() > 0 if (tenantProfileConfiguration.getMaxWsSessionsPerPublicUser() > 0
&& UserPrincipal.Type.PUBLIC_ID.equals(sessionRef.getSecurityCtx().getUserPrincipal().getType())) { && UserPrincipal.Type.PUBLIC_ID.equals(sessionRef.getSecurityCtx().getUserPrincipal().getType())) {
Set<String> publicUserSessions = publicUserSessionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getId(), id -> ConcurrentHashMap.newKeySet()); Set<String> publicUserSessions = publicUserSessionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getId(), id -> ConcurrentHashMap.newKeySet());
synchronized (publicUserSessions) { synchronized (publicUserSessions) {
if (publicUserSessions.size() < tenantProfileConfiguration.getMaxWsSessionsPerPublicUser()) { limitAllowed = publicUserSessions.size() < tenantProfileConfiguration.getMaxWsSessionsPerPublicUser();
if (limitAllowed) {
publicUserSessions.add(sessionId); publicUserSessions.add(sessionId);
} else { }
}
if (!limitAllowed) {
log.info("[{}][{}][{}] Failed to start session. Max public user sessions limit reached" log.info("[{}][{}][{}] Failed to start session. Max public user sessions limit reached"
, sessionRef.getSecurityCtx().getTenantId(), sessionRef.getSecurityCtx().getId(), sessionId); , sessionRef.getSecurityCtx().getTenantId(), sessionRef.getSecurityCtx().getId(), sessionId);
session.close(CloseStatus.POLICY_VIOLATION.withReason("Max public user sessions limit reached")); session.close(CloseStatus.POLICY_VIOLATION.withReason("Max public user sessions limit reached"));
return false; return false;
}
} }
} }
} }

1
application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponseHandler.java

@ -16,7 +16,6 @@
package org.thingsboard.server.exception; package org.thingsboard.server.exception;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;

98
application/src/main/java/org/thingsboard/server/service/action/EntityActionService.java

@ -26,7 +26,6 @@ import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.HasName; import org.thingsboard.server.common.data.HasName;
import org.thingsboard.server.common.data.HasTenantId; import org.thingsboard.server.common.data.HasTenantId;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.Alarm;
import org.thingsboard.server.common.data.alarm.AlarmComment; import org.thingsboard.server.common.data.alarm.AlarmComment;
@ -38,19 +37,21 @@ import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.kv.AttributeKvEntry; import org.thingsboard.server.common.data.kv.AttributeKvEntry;
import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.common.data.kv.TsKvEntry;
import org.thingsboard.server.common.data.msg.TbMsgType;
import org.thingsboard.server.common.data.notification.rule.trigger.AlarmAssignmentTrigger;
import org.thingsboard.server.common.data.notification.rule.trigger.AlarmCommentTrigger;
import org.thingsboard.server.common.data.notification.rule.trigger.EntitiesLimitTrigger;
import org.thingsboard.server.common.data.notification.rule.trigger.EntityActionTrigger;
import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbMsgDataType; import org.thingsboard.server.common.msg.TbMsgDataType;
import org.thingsboard.server.common.msg.TbMsgMetaData; import org.thingsboard.server.common.msg.TbMsgMetaData;
import org.thingsboard.server.common.msg.notification.NotificationRuleProcessor; import org.thingsboard.server.common.msg.notification.NotificationRuleProcessor;
import org.thingsboard.server.common.data.notification.rule.trigger.AlarmAssignmentTrigger;
import org.thingsboard.server.common.data.notification.rule.trigger.AlarmCommentTrigger;
import org.thingsboard.server.common.data.notification.rule.trigger.EntitiesLimitTrigger;
import org.thingsboard.server.common.data.notification.rule.trigger.EntityActionTrigger;
import org.thingsboard.server.dao.audit.AuditLogService; import org.thingsboard.server.dao.audit.AuditLogService;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@Service @Service
@ -63,85 +64,8 @@ public class EntityActionService {
public void pushEntityActionToRuleEngine(EntityId entityId, HasName entity, TenantId tenantId, CustomerId customerId, public void pushEntityActionToRuleEngine(EntityId entityId, HasName entity, TenantId tenantId, CustomerId customerId,
ActionType actionType, User user, Object... additionalInfo) { ActionType actionType, User user, Object... additionalInfo) {
String msgType = null; Optional<TbMsgType> msgType = actionType.getRuleEngineMsgType();
switch (actionType) { if (msgType.isPresent()) {
case ADDED:
msgType = DataConstants.ENTITY_CREATED;
break;
case DELETED:
msgType = DataConstants.ENTITY_DELETED;
break;
case UPDATED:
msgType = DataConstants.ENTITY_UPDATED;
break;
case ASSIGNED_TO_CUSTOMER:
msgType = DataConstants.ENTITY_ASSIGNED;
break;
case UNASSIGNED_FROM_CUSTOMER:
msgType = DataConstants.ENTITY_UNASSIGNED;
break;
case ATTRIBUTES_UPDATED:
msgType = DataConstants.ATTRIBUTES_UPDATED;
break;
case ATTRIBUTES_DELETED:
msgType = DataConstants.ATTRIBUTES_DELETED;
break;
case ALARM_ACK:
msgType = DataConstants.ALARM_ACK;
break;
case ALARM_CLEAR:
msgType = DataConstants.ALARM_CLEAR;
break;
case ALARM_ASSIGNED:
msgType = DataConstants.ALARM_ASSIGNED;
break;
case ALARM_UNASSIGNED:
msgType = DataConstants.ALARM_UNASSIGNED;
break;
case ALARM_DELETE:
msgType = DataConstants.ALARM_DELETE;
break;
case ADDED_COMMENT:
msgType = DataConstants.COMMENT_CREATED;
break;
case UPDATED_COMMENT:
msgType = DataConstants.COMMENT_UPDATED;
break;
case ASSIGNED_FROM_TENANT:
msgType = DataConstants.ENTITY_ASSIGNED_FROM_TENANT;
break;
case ASSIGNED_TO_TENANT:
msgType = DataConstants.ENTITY_ASSIGNED_TO_TENANT;
break;
case PROVISION_SUCCESS:
msgType = DataConstants.PROVISION_SUCCESS;
break;
case PROVISION_FAILURE:
msgType = DataConstants.PROVISION_FAILURE;
break;
case TIMESERIES_UPDATED:
msgType = DataConstants.TIMESERIES_UPDATED;
break;
case TIMESERIES_DELETED:
msgType = DataConstants.TIMESERIES_DELETED;
break;
case ASSIGNED_TO_EDGE:
msgType = DataConstants.ENTITY_ASSIGNED_TO_EDGE;
break;
case UNASSIGNED_FROM_EDGE:
msgType = DataConstants.ENTITY_UNASSIGNED_FROM_EDGE;
break;
case RELATION_ADD_OR_UPDATE:
msgType = DataConstants.RELATION_ADD_OR_UPDATE;
break;
case RELATION_DELETED:
msgType = DataConstants.RELATION_DELETED;
break;
case RELATIONS_DELETED:
msgType = DataConstants.RELATIONS_DELETED;
break;
}
if (!StringUtils.isEmpty(msgType)) {
try { try {
TbMsgMetaData metaData = new TbMsgMetaData(); TbMsgMetaData metaData = new TbMsgMetaData();
if (user != null) { if (user != null) {
@ -247,7 +171,7 @@ public class EntityActionService {
if (tenantId != null && !tenantId.isSysTenantId()) { if (tenantId != null && !tenantId.isSysTenantId()) {
processNotificationRules(tenantId, entityId, entity, actionType, user, additionalInfo); processNotificationRules(tenantId, entityId, entity, actionType, user, additionalInfo);
} }
TbMsg tbMsg = TbMsg.newMsg(msgType, entityId, customerId, metaData, TbMsgDataType.JSON, JacksonUtil.toString(entityNode)); TbMsg tbMsg = TbMsg.newMsg(msgType.get(), entityId, customerId, metaData, TbMsgDataType.JSON, JacksonUtil.toString(entityNode));
tbClusterService.pushMsgToRuleEngine(tenantId, entityId, tbMsg, null); tbClusterService.pushMsgToRuleEngine(tenantId, entityId, tbMsg, null);
} catch (Exception e) { } catch (Exception e) {
log.warn("[{}] Failed to push entity action to rule engine: {}", entityId, actionType, e); log.warn("[{}] Failed to push entity action to rule engine: {}", entityId, actionType, e);
@ -313,10 +237,6 @@ public class EntityActionService {
auditLogService.logEntityAction(user.getTenantId(), customerId, user.getId(), user.getName(), entityId, entity, actionType, e, additionalInfo); auditLogService.logEntityAction(user.getTenantId(), customerId, user.getId(), user.getName(), entityId, entity, actionType, e, additionalInfo);
} }
public void sendEntityNotificationMsgToEdge(TenantId tenantId, EntityId entityId, EdgeEventActionType action) {
tbClusterService.sendNotificationMsgToEdge(tenantId, null, entityId, null, null, action);
}
private <T> T extractParameter(Class<T> clazz, int index, Object... additionalInfo) { private <T> T extractParameter(Class<T> clazz, int index, Object... additionalInfo) {
T result = null; T result = null;
if (additionalInfo != null && additionalInfo.length > index) { if (additionalInfo != null && additionalInfo.length > index) {

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

@ -30,8 +30,12 @@ import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.rule.engine.api.NodeConfiguration; import org.thingsboard.rule.engine.api.NodeConfiguration;
import org.thingsboard.rule.engine.api.NodeDefinition; import org.thingsboard.rule.engine.api.NodeDefinition;
import org.thingsboard.rule.engine.api.RuleNode; import org.thingsboard.rule.engine.api.RuleNode;
import org.thingsboard.rule.engine.api.TbRelationTypes; import org.thingsboard.server.common.data.msg.TbMsgType;
import org.thingsboard.server.common.data.msg.TbNodeConnectionType;
import org.thingsboard.rule.engine.api.TbVersionedNode; import org.thingsboard.rule.engine.api.TbVersionedNode;
import org.thingsboard.rule.engine.filter.TbMsgTypeSwitchNode;
import org.thingsboard.rule.engine.filter.TbOriginatorTypeSwitchNode;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.plugin.ComponentDescriptor; import org.thingsboard.server.common.data.plugin.ComponentDescriptor;
import org.thingsboard.server.common.data.plugin.ComponentType; import org.thingsboard.server.common.data.plugin.ComponentType;
@ -194,7 +198,7 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe
scannedComponent.setName(ruleNodeAnnotation.name()); scannedComponent.setName(ruleNodeAnnotation.name());
scannedComponent.setScope(ruleNodeAnnotation.scope()); scannedComponent.setScope(ruleNodeAnnotation.scope());
scannedComponent.setClusteringMode(ruleNodeAnnotation.clusteringMode()); scannedComponent.setClusteringMode(ruleNodeAnnotation.clusteringMode());
NodeDefinition nodeDefinition = prepareNodeDefinition(ruleNodeAnnotation); NodeDefinition nodeDefinition = prepareNodeDefinition(clazz, ruleNodeAnnotation);
ObjectNode configurationDescriptor = JacksonUtil.newObjectNode(); ObjectNode configurationDescriptor = JacksonUtil.newObjectNode();
JsonNode node = JacksonUtil.valueToTree(nodeDefinition); JsonNode node = JacksonUtil.valueToTree(nodeDefinition);
configurationDescriptor.set("nodeDefinition", node); configurationDescriptor.set("nodeDefinition", node);
@ -221,13 +225,13 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe
return scannedComponent; return scannedComponent;
} }
private NodeDefinition prepareNodeDefinition(RuleNode nodeAnnotation) throws Exception { private NodeDefinition prepareNodeDefinition(Class<?> clazz, RuleNode nodeAnnotation) throws Exception {
NodeDefinition nodeDefinition = new NodeDefinition(); NodeDefinition nodeDefinition = new NodeDefinition();
nodeDefinition.setDetails(nodeAnnotation.nodeDetails()); nodeDefinition.setDetails(nodeAnnotation.nodeDetails());
nodeDefinition.setDescription(nodeAnnotation.nodeDescription()); nodeDefinition.setDescription(nodeAnnotation.nodeDescription());
nodeDefinition.setInEnabled(nodeAnnotation.inEnabled()); nodeDefinition.setInEnabled(nodeAnnotation.inEnabled());
nodeDefinition.setOutEnabled(nodeAnnotation.outEnabled()); nodeDefinition.setOutEnabled(nodeAnnotation.outEnabled());
nodeDefinition.setRelationTypes(getRelationTypesWithFailureRelation(nodeAnnotation)); nodeDefinition.setRelationTypes(getRelationTypesWithFailureRelation(clazz, nodeAnnotation));
nodeDefinition.setCustomRelations(nodeAnnotation.customRelations()); nodeDefinition.setCustomRelations(nodeAnnotation.customRelations());
nodeDefinition.setRuleChainNode(nodeAnnotation.ruleChainNode()); nodeDefinition.setRuleChainNode(nodeAnnotation.ruleChainNode());
Class<? extends NodeConfiguration> configClazz = nodeAnnotation.configClazz(); Class<? extends NodeConfiguration> configClazz = nodeAnnotation.configClazz();
@ -242,10 +246,17 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe
return nodeDefinition; return nodeDefinition;
} }
private String[] getRelationTypesWithFailureRelation(RuleNode nodeAnnotation) { private String[] getRelationTypesWithFailureRelation(Class<?> clazz, RuleNode nodeAnnotation) {
List<String> relationTypes = new ArrayList<>(Arrays.asList(nodeAnnotation.relationTypes())); List<String> relationTypes = new ArrayList<>(Arrays.asList(nodeAnnotation.relationTypes()));
if (!relationTypes.contains(TbRelationTypes.FAILURE)) { if (TbOriginatorTypeSwitchNode.class.equals(clazz)) {
relationTypes.add(TbRelationTypes.FAILURE); relationTypes.addAll(EntityType.NORMAL_NAMES);
}
if (TbMsgTypeSwitchNode.class.equals(clazz)) {
relationTypes.addAll(TbMsgType.NODE_CONNECTIONS);
relationTypes.add(TbNodeConnectionType.OTHER);
}
if (!relationTypes.contains(TbNodeConnectionType.FAILURE)) {
relationTypes.add(TbNodeConnectionType.FAILURE);
} }
return relationTypes.toArray(new String[relationTypes.size()]); return relationTypes.toArray(new String[relationTypes.size()]);
} }

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

@ -36,6 +36,7 @@ import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.kv.AttributeKvEntry; import org.thingsboard.server.common.data.kv.AttributeKvEntry;
import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry;
import org.thingsboard.server.common.data.kv.StringDataEntry; import org.thingsboard.server.common.data.kv.StringDataEntry;
import org.thingsboard.server.common.data.msg.TbMsgType;
import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.common.data.security.DeviceCredentialsType; import org.thingsboard.server.common.data.security.DeviceCredentialsType;
import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsg;
@ -162,7 +163,7 @@ public class DeviceProvisionServiceImpl implements DeviceProvisionService {
if (targetProfile.getProfileData().getProvisionConfiguration().getProvisionDeviceSecret().equals(provisionRequestSecret)) { if (targetProfile.getProfileData().getProvisionConfiguration().getProvisionDeviceSecret().equals(provisionRequestSecret)) {
if (targetDevice != null) { if (targetDevice != null) {
log.warn("[{}] The device is present and could not be provisioned once more!", targetDevice.getName()); log.warn("[{}] The device is present and could not be provisioned once more!", targetDevice.getName());
notify(targetDevice, provisionRequest, DataConstants.PROVISION_FAILURE, false); notify(targetDevice, provisionRequest, TbMsgType.PROVISION_FAILURE, false);
throw new ProvisionFailedException(ProvisionResponseStatus.FAILURE.name()); throw new ProvisionFailedException(ProvisionResponseStatus.FAILURE.name());
} else { } else {
return createDevice(provisionRequest, targetProfile); return createDevice(provisionRequest, targetProfile);
@ -190,11 +191,11 @@ public class DeviceProvisionServiceImpl implements DeviceProvisionService {
Optional<AttributeKvEntry> provisionState = attributesService.find(device.getTenantId(), device.getId(), Optional<AttributeKvEntry> provisionState = attributesService.find(device.getTenantId(), device.getId(),
DataConstants.SERVER_SCOPE, DEVICE_PROVISION_STATE).get(); DataConstants.SERVER_SCOPE, DEVICE_PROVISION_STATE).get();
if (provisionState != null && provisionState.isPresent() && !provisionState.get().getValueAsString().equals(PROVISIONED_STATE)) { if (provisionState != null && provisionState.isPresent() && !provisionState.get().getValueAsString().equals(PROVISIONED_STATE)) {
notify(device, provisionRequest, DataConstants.PROVISION_FAILURE, false); notify(device, provisionRequest, TbMsgType.PROVISION_FAILURE, false);
throw new ProvisionFailedException(ProvisionResponseStatus.FAILURE.name()); throw new ProvisionFailedException(ProvisionResponseStatus.FAILURE.name());
} else { } else {
saveProvisionStateAttribute(device).get(); saveProvisionStateAttribute(device).get();
notify(device, provisionRequest, DataConstants.PROVISION_SUCCESS, true); notify(device, provisionRequest, TbMsgType.PROVISION_SUCCESS, true);
} }
} catch (InterruptedException | ExecutionException e) { } catch (InterruptedException | ExecutionException e) {
throw new ProvisionFailedException(ProvisionResponseStatus.FAILURE.name()); throw new ProvisionFailedException(ProvisionResponseStatus.FAILURE.name());
@ -206,7 +207,7 @@ public class DeviceProvisionServiceImpl implements DeviceProvisionService {
return processCreateDevice(provisionRequest, profile); return processCreateDevice(provisionRequest, profile);
} }
private void notify(Device device, ProvisionRequest provisionRequest, String type, boolean success) { private void notify(Device device, ProvisionRequest provisionRequest, TbMsgType type, boolean success) {
pushProvisionEventToRuleEngine(provisionRequest, device, type); pushProvisionEventToRuleEngine(provisionRequest, device, type);
logAction(device.getTenantId(), device.getCustomerId(), device, success, provisionRequest); logAction(device.getTenantId(), device.getCustomerId(), device, success, provisionRequest);
} }
@ -222,14 +223,14 @@ public class DeviceProvisionServiceImpl implements DeviceProvisionService {
clusterService.onDeviceUpdated(savedDevice, null); clusterService.onDeviceUpdated(savedDevice, null);
saveProvisionStateAttribute(savedDevice).get(); saveProvisionStateAttribute(savedDevice).get();
pushDeviceCreatedEventToRuleEngine(savedDevice); pushDeviceCreatedEventToRuleEngine(savedDevice);
notify(savedDevice, provisionRequest, DataConstants.PROVISION_SUCCESS, true); notify(savedDevice, provisionRequest, TbMsgType.PROVISION_SUCCESS, true);
return new ProvisionResponse(getDeviceCredentials(savedDevice), ProvisionResponseStatus.SUCCESS); return new ProvisionResponse(getDeviceCredentials(savedDevice), ProvisionResponseStatus.SUCCESS);
} catch (Exception e) { } catch (Exception e) {
log.warn("[{}] Error during device creation from provision request: [{}]", provisionRequest.getDeviceName(), provisionRequest, e); log.warn("[{}] Error during device creation from provision request: [{}]", provisionRequest.getDeviceName(), provisionRequest, e);
Device device = deviceService.findDeviceByTenantIdAndName(profile.getTenantId(), provisionRequest.getDeviceName()); Device device = deviceService.findDeviceByTenantIdAndName(profile.getTenantId(), provisionRequest.getDeviceName());
if (device != null) { if (device != null) {
notify(device, provisionRequest, DataConstants.PROVISION_FAILURE, false); notify(device, provisionRequest, TbMsgType.PROVISION_FAILURE, false);
} }
throw new ProvisionFailedException(ProvisionResponseStatus.FAILURE.name()); throw new ProvisionFailedException(ProvisionResponseStatus.FAILURE.name());
} }
@ -253,7 +254,7 @@ public class DeviceProvisionServiceImpl implements DeviceProvisionService {
return deviceCredentialsService.findDeviceCredentialsByDeviceId(device.getTenantId(), device.getId()); return deviceCredentialsService.findDeviceCredentialsByDeviceId(device.getTenantId(), device.getId());
} }
private void pushProvisionEventToRuleEngine(ProvisionRequest request, Device device, String type) { private void pushProvisionEventToRuleEngine(ProvisionRequest request, Device device, TbMsgType type) {
try { try {
JsonNode entityNode = JacksonUtil.valueToTree(request); JsonNode entityNode = JacksonUtil.valueToTree(request);
TbMsg msg = TbMsg.newMsg(type, device.getId(), device.getCustomerId(), createTbMsgMetaData(device), JacksonUtil.toString(entityNode)); TbMsg msg = TbMsg.newMsg(type, device.getId(), device.getCustomerId(), createTbMsgMetaData(device), JacksonUtil.toString(entityNode));
@ -266,10 +267,10 @@ public class DeviceProvisionServiceImpl implements DeviceProvisionService {
private void pushDeviceCreatedEventToRuleEngine(Device device) { private void pushDeviceCreatedEventToRuleEngine(Device device) {
try { try {
ObjectNode entityNode = JacksonUtil.OBJECT_MAPPER.valueToTree(device); ObjectNode entityNode = JacksonUtil.OBJECT_MAPPER.valueToTree(device);
TbMsg msg = TbMsg.newMsg(DataConstants.ENTITY_CREATED, device.getId(), device.getCustomerId(), createTbMsgMetaData(device), JacksonUtil.OBJECT_MAPPER.writeValueAsString(entityNode)); TbMsg msg = TbMsg.newMsg(TbMsgType.ENTITY_CREATED, device.getId(), device.getCustomerId(), createTbMsgMetaData(device), JacksonUtil.OBJECT_MAPPER.writeValueAsString(entityNode));
sendToRuleEngine(device.getTenantId(), msg, null); sendToRuleEngine(device.getTenantId(), msg, null);
} catch (JsonProcessingException | IllegalArgumentException e) { } catch (JsonProcessingException | IllegalArgumentException e) {
log.warn("[{}] Failed to push device action to rule engine: {}", device.getId(), DataConstants.ENTITY_CREATED, e); log.warn("[{}] Failed to push device action to rule engine: {}", device.getId(), TbMsgType.ENTITY_CREATED.name(), e);
} }
} }

80
application/src/main/java/org/thingsboard/server/service/edge/DefaultEdgeNotificationService.java

@ -15,7 +15,6 @@
*/ */
package org.thingsboard.server.service.edge; package org.thingsboard.server.service.edge;
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.FutureCallback; import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.Futures;
@ -23,22 +22,18 @@ import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.common.util.ThingsBoardThreadFactory; import org.thingsboard.common.util.ThingsBoardThreadFactory;
import org.thingsboard.server.cluster.TbClusterService; import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.EdgeUtils;
import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.edge.EdgeEvent;
import org.thingsboard.server.common.data.edge.EdgeEventActionType;
import org.thingsboard.server.common.data.edge.EdgeEventType; import org.thingsboard.server.common.data.edge.EdgeEventType;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.msg.queue.TbCallback; import org.thingsboard.server.common.msg.queue.TbCallback;
import org.thingsboard.server.dao.edge.EdgeEventService;
import org.thingsboard.server.dao.edge.EdgeService; import org.thingsboard.server.dao.edge.EdgeService;
import org.thingsboard.server.dao.eventsourcing.ActionEntityEvent;
import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.edge.rpc.processor.alarm.AlarmEdgeProcessor; import org.thingsboard.server.service.edge.rpc.processor.alarm.AlarmEdgeProcessor;
@ -54,6 +49,8 @@ import org.thingsboard.server.service.edge.rpc.processor.ota.OtaPackageEdgeProce
import org.thingsboard.server.service.edge.rpc.processor.queue.QueueEdgeProcessor; import org.thingsboard.server.service.edge.rpc.processor.queue.QueueEdgeProcessor;
import org.thingsboard.server.service.edge.rpc.processor.relation.RelationEdgeProcessor; import org.thingsboard.server.service.edge.rpc.processor.relation.RelationEdgeProcessor;
import org.thingsboard.server.service.edge.rpc.processor.rule.RuleChainEdgeProcessor; import org.thingsboard.server.service.edge.rpc.processor.rule.RuleChainEdgeProcessor;
import org.thingsboard.server.service.edge.rpc.processor.tenant.TenantEdgeProcessor;
import org.thingsboard.server.service.edge.rpc.processor.tenant.TenantProfileEdgeProcessor;
import org.thingsboard.server.service.edge.rpc.processor.user.UserEdgeProcessor; import org.thingsboard.server.service.edge.rpc.processor.user.UserEdgeProcessor;
import org.thingsboard.server.service.edge.rpc.processor.widget.WidgetBundleEdgeProcessor; import org.thingsboard.server.service.edge.rpc.processor.widget.WidgetBundleEdgeProcessor;
import org.thingsboard.server.service.edge.rpc.processor.widget.WidgetTypeEdgeProcessor; import org.thingsboard.server.service.edge.rpc.processor.widget.WidgetTypeEdgeProcessor;
@ -74,12 +71,6 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService {
@Autowired @Autowired
private EdgeService edgeService; private EdgeService edgeService;
@Autowired
private EdgeEventService edgeEventService;
@Autowired
private TbClusterService clusterService;
@Autowired @Autowired
private EdgeProcessor edgeProcessor; private EdgeProcessor edgeProcessor;
@ -122,12 +113,21 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService {
@Autowired @Autowired
private QueueEdgeProcessor queueProcessor; private QueueEdgeProcessor queueProcessor;
@Autowired
private TenantEdgeProcessor tenantEdgeProcessor;
@Autowired
private TenantProfileEdgeProcessor tenantProfileEdgeProcessor;
@Autowired @Autowired
private AlarmEdgeProcessor alarmProcessor; private AlarmEdgeProcessor alarmProcessor;
@Autowired @Autowired
private RelationEdgeProcessor relationProcessor; private RelationEdgeProcessor relationProcessor;
@Autowired
protected ApplicationEventPublisher eventPublisher;
private ExecutorService dbCallBackExecutor; private ExecutorService dbCallBackExecutor;
@PostConstruct @PostConstruct
@ -143,32 +143,16 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService {
} }
@Override @Override
public Edge setEdgeRootRuleChain(TenantId tenantId, Edge edge, RuleChainId ruleChainId) throws Exception { public Edge setEdgeRootRuleChain(TenantId tenantId, Edge edge, RuleChainId ruleChainId) {
edge.setRootRuleChainId(ruleChainId); edge.setRootRuleChainId(ruleChainId);
Edge savedEdge = edgeService.saveEdge(edge); Edge savedEdge = edgeService.saveEdge(edge);
ObjectNode isRootBody = JacksonUtil.newObjectNode(); ObjectNode isRootBody = JacksonUtil.newObjectNode();
isRootBody.put(EDGE_IS_ROOT_BODY_KEY, Boolean.TRUE); isRootBody.put(EDGE_IS_ROOT_BODY_KEY, Boolean.TRUE);
saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.RULE_CHAIN, EdgeEventActionType.UPDATED, ruleChainId, isRootBody).get(); eventPublisher.publishEvent(ActionEntityEvent.builder().tenantId(tenantId).edgeId(edge.getId()).entityId(ruleChainId)
.body(JacksonUtil.toString(isRootBody)).actionType(ActionType.UPDATED).build());
return savedEdge; return savedEdge;
} }
private ListenableFuture<Void> saveEdgeEvent(TenantId tenantId,
EdgeId edgeId,
EdgeEventType type,
EdgeEventActionType action,
EntityId entityId,
JsonNode body) {
log.debug("Pushing edge event to edge queue. tenantId [{}], edgeId [{}], type [{}], action[{}], entityId [{}], body [{}]",
tenantId, edgeId, type, action, entityId, body);
EdgeEvent edgeEvent = EdgeUtils.constructEdgeEvent(tenantId, edgeId, type, action, entityId, body);
return Futures.transform(edgeEventService.saveAsync(edgeEvent), unused -> {
clusterService.onEdgeEventUpdate(tenantId, edgeId);
return null;
}, dbCallBackExecutor);
}
@Override @Override
public void pushNotificationToEdge(TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg, TbCallback callback) { public void pushNotificationToEdge(TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg, TbCallback callback) {
log.debug("Pushing notification to edge {}", edgeNotificationMsg); log.debug("Pushing notification to edge {}", edgeNotificationMsg);
@ -181,43 +165,43 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService {
future = edgeProcessor.processEdgeNotification(tenantId, edgeNotificationMsg); future = edgeProcessor.processEdgeNotification(tenantId, edgeNotificationMsg);
break; break;
case ASSET: case ASSET:
future = assetProcessor.processAssetNotification(tenantId, edgeNotificationMsg); future = assetProcessor.processEntityNotification(tenantId, edgeNotificationMsg);
break; break;
case DEVICE: case DEVICE:
future = deviceProcessor.processDeviceNotification(tenantId, edgeNotificationMsg); future = deviceProcessor.processEntityNotification(tenantId, edgeNotificationMsg);
break; break;
case ENTITY_VIEW: case ENTITY_VIEW:
future = entityViewProcessor.processEntityViewNotification(tenantId, edgeNotificationMsg); future = entityViewProcessor.processEntityNotification(tenantId, edgeNotificationMsg);
break; break;
case DASHBOARD: case DASHBOARD:
future = dashboardProcessor.processDashboardNotification(tenantId, edgeNotificationMsg); future = dashboardProcessor.processEntityNotification(tenantId, edgeNotificationMsg);
break; break;
case RULE_CHAIN: case RULE_CHAIN:
future = ruleChainProcessor.processRuleChainNotification(tenantId, edgeNotificationMsg); future = ruleChainProcessor.processEntityNotification(tenantId, edgeNotificationMsg);
break; break;
case USER: case USER:
future = userProcessor.processUserNotification(tenantId, edgeNotificationMsg); future = userProcessor.processEntityNotification(tenantId, edgeNotificationMsg);
break; break;
case CUSTOMER: case CUSTOMER:
future = customerProcessor.processCustomerNotification(tenantId, edgeNotificationMsg); future = customerProcessor.processCustomerNotification(tenantId, edgeNotificationMsg);
break; break;
case DEVICE_PROFILE: case DEVICE_PROFILE:
future = deviceProfileProcessor.processDeviceProfileNotification(tenantId, edgeNotificationMsg); future = deviceProfileProcessor.processEntityNotification(tenantId, edgeNotificationMsg);
break; break;
case ASSET_PROFILE: case ASSET_PROFILE:
future = assetProfileProcessor.processAssetProfileNotification(tenantId, edgeNotificationMsg); future = assetProfileProcessor.processEntityNotification(tenantId, edgeNotificationMsg);
break; break;
case OTA_PACKAGE: case OTA_PACKAGE:
future = otaPackageProcessor.processOtaPackageNotification(tenantId, edgeNotificationMsg); future = otaPackageProcessor.processEntityNotification(tenantId, edgeNotificationMsg);
break; break;
case WIDGETS_BUNDLE: case WIDGETS_BUNDLE:
future = widgetBundleProcessor.processWidgetsBundleNotification(tenantId, edgeNotificationMsg); future = widgetBundleProcessor.processEntityNotification(tenantId, edgeNotificationMsg);
break; break;
case WIDGET_TYPE: case WIDGET_TYPE:
future = widgetTypeProcessor.processWidgetTypeNotification(tenantId, edgeNotificationMsg); future = widgetTypeProcessor.processEntityNotification(tenantId, edgeNotificationMsg);
break; break;
case QUEUE: case QUEUE:
future = queueProcessor.processQueueNotification(tenantId, edgeNotificationMsg); future = queueProcessor.processEntityNotification(tenantId, edgeNotificationMsg);
break; break;
case ALARM: case ALARM:
future = alarmProcessor.processAlarmNotification(tenantId, edgeNotificationMsg); future = alarmProcessor.processAlarmNotification(tenantId, edgeNotificationMsg);
@ -225,6 +209,12 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService {
case RELATION: case RELATION:
future = relationProcessor.processRelationNotification(tenantId, edgeNotificationMsg); future = relationProcessor.processRelationNotification(tenantId, edgeNotificationMsg);
break; break;
case TENANT:
future = tenantEdgeProcessor.processEntityNotification(tenantId, edgeNotificationMsg);
break;
case TENANT_PROFILE:
future = tenantProfileEdgeProcessor.processEntityNotification(tenantId, edgeNotificationMsg);
break;
default: default:
log.warn("Edge event type [{}] is not designed to be pushed to edge", type); log.warn("Edge event type [{}] is not designed to be pushed to edge", type);
future = Futures.immediateFuture(null); future = Futures.immediateFuture(null);

16
application/src/main/java/org/thingsboard/server/service/edge/EdgeContextComponent.java

@ -35,6 +35,8 @@ import org.thingsboard.server.dao.ota.OtaPackageService;
import org.thingsboard.server.dao.queue.QueueService; import org.thingsboard.server.dao.queue.QueueService;
import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.rule.RuleChainService;
import org.thingsboard.server.dao.settings.AdminSettingsService; import org.thingsboard.server.dao.settings.AdminSettingsService;
import org.thingsboard.server.dao.tenant.TenantProfileService;
import org.thingsboard.server.dao.tenant.TenantService;
import org.thingsboard.server.dao.user.UserService; import org.thingsboard.server.dao.user.UserService;
import org.thingsboard.server.dao.widget.WidgetsBundleService; import org.thingsboard.server.dao.widget.WidgetsBundleService;
import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.queue.util.TbCoreComponent;
@ -55,6 +57,8 @@ import org.thingsboard.server.service.edge.rpc.processor.relation.RelationEdgePr
import org.thingsboard.server.service.edge.rpc.processor.rule.RuleChainEdgeProcessor; import org.thingsboard.server.service.edge.rpc.processor.rule.RuleChainEdgeProcessor;
import org.thingsboard.server.service.edge.rpc.processor.settings.AdminSettingsEdgeProcessor; import org.thingsboard.server.service.edge.rpc.processor.settings.AdminSettingsEdgeProcessor;
import org.thingsboard.server.service.edge.rpc.processor.telemetry.TelemetryEdgeProcessor; import org.thingsboard.server.service.edge.rpc.processor.telemetry.TelemetryEdgeProcessor;
import org.thingsboard.server.service.edge.rpc.processor.tenant.TenantEdgeProcessor;
import org.thingsboard.server.service.edge.rpc.processor.tenant.TenantProfileEdgeProcessor;
import org.thingsboard.server.service.edge.rpc.processor.user.UserEdgeProcessor; import org.thingsboard.server.service.edge.rpc.processor.user.UserEdgeProcessor;
import org.thingsboard.server.service.edge.rpc.processor.widget.WidgetBundleEdgeProcessor; import org.thingsboard.server.service.edge.rpc.processor.widget.WidgetBundleEdgeProcessor;
import org.thingsboard.server.service.edge.rpc.processor.widget.WidgetTypeEdgeProcessor; import org.thingsboard.server.service.edge.rpc.processor.widget.WidgetTypeEdgeProcessor;
@ -122,6 +126,12 @@ public class EdgeContextComponent {
@Autowired @Autowired
private OtaPackageService otaPackageService; private OtaPackageService otaPackageService;
@Autowired
private TenantService tenantService;
@Autowired
private TenantProfileService tenantProfileService;
@Autowired @Autowired
private QueueService queueService; private QueueService queueService;
@ -179,6 +189,12 @@ public class EdgeContextComponent {
@Autowired @Autowired
private QueueEdgeProcessor queueEdgeProcessor; private QueueEdgeProcessor queueEdgeProcessor;
@Autowired
private TenantEdgeProcessor tenantEdgeProcessor;
@Autowired
private TenantProfileEdgeProcessor tenantProfileEdgeProcessor;
@Autowired @Autowired
private EdgeMsgConstructor edgeMsgConstructor; private EdgeMsgConstructor edgeMsgConstructor;

158
application/src/main/java/org/thingsboard/server/service/edge/EdgeEventSourcingListener.java

@ -0,0 +1,158 @@
/**
* Copyright © 2016-2023 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.edge;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.transaction.event.TransactionalEventListener;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.cluster.TbClusterService;
import org.thingsboard.server.common.data.OtaPackageInfo;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.alarm.AlarmApiCallResult;
import org.thingsboard.server.common.data.edge.EdgeEventActionType;
import org.thingsboard.server.common.data.edge.EdgeEventType;
import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.relation.RelationTypeGroup;
import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.data.rule.RuleChainType;
import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.dao.edge.EdgeSynchronizationManager;
import org.thingsboard.server.dao.eventsourcing.ActionEntityEvent;
import org.thingsboard.server.dao.eventsourcing.DeleteEntityEvent;
import org.thingsboard.server.dao.eventsourcing.RelationActionEvent;
import org.thingsboard.server.dao.eventsourcing.SaveEntityEvent;
import javax.annotation.PostConstruct;
import static org.thingsboard.server.service.entitiy.DefaultTbNotificationEntityService.edgeTypeByActionType;
/**
* This event listener does not support async event processing because relay on ThreadLocal
* Another possible approach is to implement a special annotation and a bunch of classes similar to TransactionalApplicationListener
* This class is the simplest approach to maintain edge synchronization within the single class.
* <p>
* For async event publishers, you have to decide whether publish event on creating async task in the same thread where dao method called
* @Autowired
* EdgeEventSynchronizationManager edgeSynchronizationManager
* ...
* //some async write action make future
* if (!edgeSynchronizationManager.isSync()) {
* future.addCallback(eventPublisher.publishEvent(...))
* }
* */
@Component
@RequiredArgsConstructor
@Slf4j
public class EdgeEventSourcingListener {
private final TbClusterService tbClusterService;
private final EdgeSynchronizationManager edgeSynchronizationManager;
@PostConstruct
public void init() {
log.info("EdgeEventSourcingListener initiated");
}
@TransactionalEventListener(fallbackExecution = true)
public void handleEvent(SaveEntityEvent<?> event) {
if (edgeSynchronizationManager.isSync()) {
return;
}
try {
if (!isValidEdgeEventEntity(event.getEntity())) {
return;
}
log.trace("[{}] SaveEntityEvent called: {}", event.getTenantId(), event);
EdgeEventActionType action = Boolean.TRUE.equals(event.getAdded()) ? EdgeEventActionType.ADDED : EdgeEventActionType.UPDATED;
tbClusterService.sendNotificationMsgToEdge(event.getTenantId(), null, event.getEntityId(),
null, null, action);
} catch (Exception e) {
log.error("[{}] failed to process SaveEntityEvent: {}", event.getTenantId(), event);
}
}
@TransactionalEventListener(fallbackExecution = true)
public void handleEvent(DeleteEntityEvent<?> event) {
if (edgeSynchronizationManager.isSync()) {
return;
}
try {
log.trace("[{}] DeleteEntityEvent called: {}", event.getTenantId(), event);
tbClusterService.sendNotificationMsgToEdge(event.getTenantId(), event.getEdgeId(), event.getEntityId(),
JacksonUtil.toString(event.getEntity()), null, EdgeEventActionType.DELETED);
} catch (Exception e) {
log.error("[{}] failed to process DeleteEntityEvent: {}", event.getTenantId(), event);
}
}
@TransactionalEventListener(fallbackExecution = true)
public void handleEvent(ActionEntityEvent event) {
if (edgeSynchronizationManager.isSync()) {
return;
}
try {
log.trace("[{}] ActionEntityEvent called: {}", event.getTenantId(), event);
tbClusterService.sendNotificationMsgToEdge(event.getTenantId(), event.getEdgeId(), event.getEntityId(),
event.getBody(), null, edgeTypeByActionType(event.getActionType()));
} catch (Exception e) {
log.error("[{}] failed to process ActionEntityEvent: {}", event.getTenantId(), event);
}
}
@TransactionalEventListener(fallbackExecution = true)
public void handleEvent(RelationActionEvent event) {
if (edgeSynchronizationManager.isSync()) {
return;
}
try {
EntityRelation relation = event.getRelation();
if (relation == null) {
log.trace("[{}] skipping RelationActionEvent event in case relation is null: {}", event.getTenantId(), event);
return;
}
if (!RelationTypeGroup.COMMON.equals(relation.getTypeGroup())) {
log.trace("[{}] skipping RelationActionEvent event in case NOT COMMON relation type group: {}", event.getTenantId(), event);
return;
}
log.trace("[{}] RelationActionEvent called: {}", event.getTenantId(), event);
tbClusterService.sendNotificationMsgToEdge(event.getTenantId(), null, null,
JacksonUtil.toString(relation), EdgeEventType.RELATION, edgeTypeByActionType(event.getActionType()));
} catch (Exception e) {
log.error("[{}] failed to process RelationActionEvent: {}", event.getTenantId(), event);
}
}
private boolean isValidEdgeEventEntity(Object entity) {
if (entity instanceof OtaPackageInfo) {
OtaPackageInfo otaPackageInfo = (OtaPackageInfo) entity;
return otaPackageInfo.hasUrl() || otaPackageInfo.isHasData();
} else if (entity instanceof RuleChain) {
RuleChain ruleChain = (RuleChain) entity;
return RuleChainType.EDGE.equals(ruleChain.getType());
} else if (entity instanceof User) {
User user = (User) entity;
return !Authority.SYS_ADMIN.equals(user.getAuthority());
} else if (entity instanceof AlarmApiCallResult) {
AlarmApiCallResult alarmApiCallResult = (AlarmApiCallResult) entity;
return alarmApiCallResult.isModified();
}
// Default: If the entity doesn't match any of the conditions, consider it as valid.
return true;
}
}

20
application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcService.java

@ -37,6 +37,7 @@ import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.kv.BasicTsKvEntry; import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
import org.thingsboard.server.common.data.kv.BooleanDataEntry; import org.thingsboard.server.common.data.kv.BooleanDataEntry;
import org.thingsboard.server.common.data.kv.LongDataEntry; import org.thingsboard.server.common.data.kv.LongDataEntry;
import org.thingsboard.server.common.data.msg.TbMsgType;
import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbMsgDataType; import org.thingsboard.server.common.msg.TbMsgDataType;
import org.thingsboard.server.common.msg.TbMsgMetaData; import org.thingsboard.server.common.msg.TbMsgMetaData;
@ -71,10 +72,6 @@ import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Consumer; import java.util.function.Consumer;
import static org.thingsboard.server.common.data.DataConstants.CONNECT_EVENT;
import static org.thingsboard.server.common.data.DataConstants.DISCONNECT_EVENT;
import static org.thingsboard.server.common.data.DataConstants.SERVER_SCOPE;
@Service @Service
@Slf4j @Slf4j
@ConditionalOnProperty(prefix = "edges", value = "enabled", havingValue = "true") @ConditionalOnProperty(prefix = "edges", value = "enabled", havingValue = "true")
@ -278,7 +275,7 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i
save(edgeId, DefaultDeviceStateService.ACTIVITY_STATE, true); save(edgeId, DefaultDeviceStateService.ACTIVITY_STATE, true);
long lastConnectTs = System.currentTimeMillis(); long lastConnectTs = System.currentTimeMillis();
save(edgeId, DefaultDeviceStateService.LAST_CONNECT_TIME, lastConnectTs); save(edgeId, DefaultDeviceStateService.LAST_CONNECT_TIME, lastConnectTs);
pushRuleEngineMessage(edgeGrpcSession.getEdge().getTenantId(), edgeId, lastConnectTs, CONNECT_EVENT); pushRuleEngineMessage(edgeGrpcSession.getEdge().getTenantId(), edgeId, lastConnectTs, TbMsgType.CONNECT_EVENT);
cancelScheduleEdgeEventsCheck(edgeId); cancelScheduleEdgeEventsCheck(edgeId);
scheduleEdgeEventsCheck(edgeGrpcSession); scheduleEdgeEventsCheck(edgeGrpcSession);
} }
@ -341,7 +338,10 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i
sessionNewEvents.put(edgeId, false); sessionNewEvents.put(edgeId, false);
Futures.addCallback(session.processEdgeEvents(), new FutureCallback<>() { Futures.addCallback(session.processEdgeEvents(), new FutureCallback<>() {
@Override @Override
public void onSuccess(Void result) { public void onSuccess(Boolean newEventsAdded) {
if (Boolean.TRUE.equals(newEventsAdded)) {
sessionNewEvents.put(edgeId, true);
}
scheduleEdgeEventsCheck(session); scheduleEdgeEventsCheck(session);
} }
@ -395,7 +395,7 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i
save(edgeId, DefaultDeviceStateService.ACTIVITY_STATE, false); save(edgeId, DefaultDeviceStateService.ACTIVITY_STATE, false);
long lastDisconnectTs = System.currentTimeMillis(); long lastDisconnectTs = System.currentTimeMillis();
save(edgeId, DefaultDeviceStateService.LAST_DISCONNECT_TIME, lastDisconnectTs); save(edgeId, DefaultDeviceStateService.LAST_DISCONNECT_TIME, lastDisconnectTs);
pushRuleEngineMessage(toRemove.getEdge().getTenantId(), edgeId, lastDisconnectTs, DISCONNECT_EVENT); pushRuleEngineMessage(toRemove.getEdge().getTenantId(), edgeId, lastDisconnectTs, TbMsgType.DISCONNECT_EVENT);
cancelScheduleEdgeEventsCheck(edgeId); cancelScheduleEdgeEventsCheck(edgeId);
} else { } else {
log.debug("[{}] edge session [{}] is not available anymore, nothing to remove. most probably this session is already outdated!", edgeId, sessionId); log.debug("[{}] edge session [{}] is not available anymore, nothing to remove. most probably this session is already outdated!", edgeId, sessionId);
@ -448,10 +448,10 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i
} }
} }
private void pushRuleEngineMessage(TenantId tenantId, EdgeId edgeId, long ts, String msgType) { private void pushRuleEngineMessage(TenantId tenantId, EdgeId edgeId, long ts, TbMsgType msgType) {
try { try {
ObjectNode edgeState = JacksonUtil.newObjectNode(); ObjectNode edgeState = JacksonUtil.newObjectNode();
if (msgType.equals(CONNECT_EVENT)) { if (msgType.equals(TbMsgType.CONNECT_EVENT)) {
edgeState.put(DefaultDeviceStateService.ACTIVITY_STATE, true); edgeState.put(DefaultDeviceStateService.ACTIVITY_STATE, true);
edgeState.put(DefaultDeviceStateService.LAST_CONNECT_TIME, ts); edgeState.put(DefaultDeviceStateService.LAST_CONNECT_TIME, ts);
} else { } else {
@ -461,7 +461,7 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i
String data = JacksonUtil.toString(edgeState); String data = JacksonUtil.toString(edgeState);
TbMsgMetaData md = new TbMsgMetaData(); TbMsgMetaData md = new TbMsgMetaData();
if (!persistToTelemetry) { if (!persistToTelemetry) {
md.putValue(DataConstants.SCOPE, SERVER_SCOPE); md.putValue(DataConstants.SCOPE, DataConstants.SERVER_SCOPE);
} }
TbMsg tbMsg = TbMsg.newMsg(msgType, edgeId, md, TbMsgDataType.JSON, data); TbMsg tbMsg = TbMsg.newMsg(msgType, edgeId, md, TbMsgDataType.JSON, data);
clusterService.pushMsgToRuleEngine(tenantId, edgeId, tbMsg, null); clusterService.pushMsgToRuleEngine(tenantId, edgeId, tbMsg, null);

275
application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java

@ -24,6 +24,7 @@ import io.grpc.stub.StreamObserver;
import lombok.Data; import lombok.Data;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
import org.springframework.data.util.Pair;
import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.EdgeUtils; import org.thingsboard.server.common.data.EdgeUtils;
import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.edge.Edge;
@ -35,13 +36,19 @@ import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry;
import org.thingsboard.server.common.data.kv.LongDataEntry; import org.thingsboard.server.common.data.kv.LongDataEntry;
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.SortOrder;
import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.gen.edge.v1.AlarmUpdateMsg; import org.thingsboard.server.gen.edge.v1.AlarmUpdateMsg;
import org.thingsboard.server.gen.edge.v1.AssetProfileUpdateMsg;
import org.thingsboard.server.gen.edge.v1.AssetUpdateMsg;
import org.thingsboard.server.gen.edge.v1.AttributesRequestMsg; import org.thingsboard.server.gen.edge.v1.AttributesRequestMsg;
import org.thingsboard.server.gen.edge.v1.ConnectRequestMsg; import org.thingsboard.server.gen.edge.v1.ConnectRequestMsg;
import org.thingsboard.server.gen.edge.v1.ConnectResponseCode; import org.thingsboard.server.gen.edge.v1.ConnectResponseCode;
import org.thingsboard.server.gen.edge.v1.ConnectResponseMsg; import org.thingsboard.server.gen.edge.v1.ConnectResponseMsg;
import org.thingsboard.server.gen.edge.v1.DashboardUpdateMsg;
import org.thingsboard.server.gen.edge.v1.DeviceCredentialsRequestMsg; import org.thingsboard.server.gen.edge.v1.DeviceCredentialsRequestMsg;
import org.thingsboard.server.gen.edge.v1.DeviceCredentialsUpdateMsg; import org.thingsboard.server.gen.edge.v1.DeviceCredentialsUpdateMsg;
import org.thingsboard.server.gen.edge.v1.DeviceProfileUpdateMsg;
import org.thingsboard.server.gen.edge.v1.DeviceRpcCallMsg; import org.thingsboard.server.gen.edge.v1.DeviceRpcCallMsg;
import org.thingsboard.server.gen.edge.v1.DeviceUpdateMsg; import org.thingsboard.server.gen.edge.v1.DeviceUpdateMsg;
import org.thingsboard.server.gen.edge.v1.DownlinkMsg; import org.thingsboard.server.gen.edge.v1.DownlinkMsg;
@ -50,6 +57,7 @@ import org.thingsboard.server.gen.edge.v1.EdgeConfiguration;
import org.thingsboard.server.gen.edge.v1.EdgeUpdateMsg; import org.thingsboard.server.gen.edge.v1.EdgeUpdateMsg;
import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.EdgeVersion;
import org.thingsboard.server.gen.edge.v1.EntityDataProto; import org.thingsboard.server.gen.edge.v1.EntityDataProto;
import org.thingsboard.server.gen.edge.v1.EntityViewUpdateMsg;
import org.thingsboard.server.gen.edge.v1.EntityViewsRequestMsg; import org.thingsboard.server.gen.edge.v1.EntityViewsRequestMsg;
import org.thingsboard.server.gen.edge.v1.RelationRequestMsg; import org.thingsboard.server.gen.edge.v1.RelationRequestMsg;
import org.thingsboard.server.gen.edge.v1.RelationUpdateMsg; import org.thingsboard.server.gen.edge.v1.RelationUpdateMsg;
@ -68,17 +76,15 @@ import org.thingsboard.server.service.edge.rpc.fetch.GeneralEdgeEventFetcher;
import java.io.Closeable; import java.io.Closeable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantLock;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.stream.Collectors;
@Slf4j @Slf4j
@Data @Data
@ -89,6 +95,7 @@ public final class EdgeGrpcSession implements Closeable {
private static final int MAX_DOWNLINK_ATTEMPTS = 10; // max number of attemps to send downlink message if edge connected private static final int MAX_DOWNLINK_ATTEMPTS = 10; // max number of attemps to send downlink message if edge connected
private static final String QUEUE_START_TS_ATTR_KEY = "queueStartTs"; private static final String QUEUE_START_TS_ATTR_KEY = "queueStartTs";
private static final String QUEUE_START_SEQ_ID_ATTR_KEY = "queueStartSeqId";
private final UUID sessionId; private final UUID sessionId;
private final BiConsumer<EdgeId, EdgeGrpcSession> sessionOpenListener; private final BiConsumer<EdgeId, EdgeGrpcSession> sessionOpenListener;
@ -103,6 +110,12 @@ public final class EdgeGrpcSession implements Closeable {
private boolean connected; private boolean connected;
private boolean syncCompleted; private boolean syncCompleted;
private Long newStartTs;
private Long previousStartTs;
private Long newStartSeqId;
private Long previousStartSeqId;
private Long seqIdEnd;
private EdgeVersion edgeVersion; private EdgeVersion edgeVersion;
private int maxInboundMessageSize; private int maxInboundMessageSize;
@ -143,8 +156,8 @@ public final class EdgeGrpcSession implements Closeable {
} }
if (connected) { if (connected) {
if (requestMsg.getMsgType().equals(RequestMsgType.SYNC_REQUEST_RPC_MESSAGE)) { if (requestMsg.getMsgType().equals(RequestMsgType.SYNC_REQUEST_RPC_MESSAGE)) {
if (requestMsg.hasSyncRequestMsg() && requestMsg.getSyncRequestMsg().getSyncRequired()) { if (requestMsg.hasSyncRequestMsg()) {
boolean fullSync = true; boolean fullSync = false;
if (requestMsg.getSyncRequestMsg().hasFullSync()) { if (requestMsg.getSyncRequestMsg().hasFullSync()) {
fullSync = requestMsg.getSyncRequestMsg().getFullSync(); fullSync = requestMsg.getSyncRequestMsg().getFullSync();
} }
@ -204,10 +217,10 @@ public final class EdgeGrpcSession implements Closeable {
EdgeEventFetcher next = cursor.getNext(); EdgeEventFetcher next = cursor.getNext();
log.info("[{}][{}] starting sync process, cursor current idx = {}, class = {}", log.info("[{}][{}] starting sync process, cursor current idx = {}, class = {}",
edge.getTenantId(), edge.getId(), cursor.getCurrentIdx(), next.getClass().getSimpleName()); edge.getTenantId(), edge.getId(), cursor.getCurrentIdx(), next.getClass().getSimpleName());
ListenableFuture<UUID> uuidListenableFuture = startProcessingEdgeEvents(next); ListenableFuture<Pair<Long, Long>> future = startProcessingEdgeEvents(next);
Futures.addCallback(uuidListenableFuture, new FutureCallback<>() { Futures.addCallback(future, new FutureCallback<>() {
@Override @Override
public void onSuccess(@Nullable UUID result) { public void onSuccess(@Nullable Pair<Long, Long> result) {
doSync(cursor); doSync(cursor);
} }
@ -307,36 +320,51 @@ public final class EdgeGrpcSession implements Closeable {
sendDownlinkMsg(edgeConfigMsg); sendDownlinkMsg(edgeConfigMsg);
} }
ListenableFuture<Void> processEdgeEvents() throws Exception { ListenableFuture<Boolean> processEdgeEvents() throws Exception {
SettableFuture<Void> result = SettableFuture.create(); SettableFuture<Boolean> result = SettableFuture.create();
log.trace("[{}] starting processing edge events", this.sessionId); log.trace("[{}] starting processing edge events", this.sessionId);
if (isConnected() && isSyncCompleted()) { if (isConnected() && isSyncCompleted()) {
Long queueStartTs = getQueueStartTs().get(); Pair<Long, Long> startTsAndSeqId = getQueueStartTsAndSeqId().get();
this.previousStartTs = startTsAndSeqId.getFirst();
this.previousStartSeqId = startTsAndSeqId.getSecond();
GeneralEdgeEventFetcher fetcher = new GeneralEdgeEventFetcher( GeneralEdgeEventFetcher fetcher = new GeneralEdgeEventFetcher(
queueStartTs, this.previousStartTs,
this.previousStartSeqId,
this.seqIdEnd,
false,
Integer.toUnsignedLong(ctx.getEdgeEventStorageSettings().getMaxReadRecordsCount()),
ctx.getEdgeEventService()); ctx.getEdgeEventService());
ListenableFuture<UUID> ifOffsetFuture = startProcessingEdgeEvents(fetcher); Futures.addCallback(startProcessingEdgeEvents(fetcher), new FutureCallback<>() {
Futures.addCallback(ifOffsetFuture, new FutureCallback<>() {
@Override @Override
public void onSuccess(@Nullable UUID ifOffset) { public void onSuccess(@Nullable Pair<Long, Long> newStartTsAndSeqId) {
if (ifOffset != null) { if (newStartTsAndSeqId != null) {
Long newStartTs = Uuids.unixTimestamp(ifOffset); ListenableFuture<List<String>> updateFuture = updateQueueStartTsAndSeqId(newStartTsAndSeqId);
ListenableFuture<List<String>> updateFuture = updateQueueStartTs(newStartTs);
Futures.addCallback(updateFuture, new FutureCallback<>() { Futures.addCallback(updateFuture, new FutureCallback<>() {
@Override @Override
public void onSuccess(@Nullable List<String> list) { public void onSuccess(@Nullable List<String> list) {
log.debug("[{}] queue offset was updated [{}][{}]", sessionId, ifOffset, newStartTs); log.debug("[{}] queue offset was updated [{}]", sessionId, newStartTsAndSeqId);
result.set(null); if (fetcher.isSeqIdNewCycleStarted()) {
seqIdEnd = fetcher.getSeqIdEnd();
boolean newEventsAvailable = isNewEdgeEventsAvailable();
result.set(newEventsAvailable);
} else {
seqIdEnd = null;
boolean newEventsAvailable = isSeqIdStartedNewCycle();
if (!newEventsAvailable) {
newEventsAvailable = isNewEdgeEventsAvailable();
}
result.set(newEventsAvailable);
}
} }
@Override @Override
public void onFailure(Throwable t) { public void onFailure(Throwable t) {
log.error("[{}] Failed to update queue offset [{}]", sessionId, ifOffset, t); log.error("[{}] Failed to update queue offset [{}]", sessionId, newStartTsAndSeqId, t);
result.setException(t); result.setException(t);
} }
}, ctx.getGrpcCallbackExecutorService()); }, ctx.getGrpcCallbackExecutorService());
} else { } else {
log.trace("[{}] ifOffset is null. Skipping iteration without db update", sessionId); log.trace("[{}] newStartTsAndSeqId is null. Skipping iteration without db update", sessionId);
result.set(null); result.set(null);
} }
} }
@ -354,14 +382,14 @@ public final class EdgeGrpcSession implements Closeable {
return result; return result;
} }
private ListenableFuture<UUID> startProcessingEdgeEvents(EdgeEventFetcher fetcher) { private ListenableFuture<Pair<Long, Long>> startProcessingEdgeEvents(EdgeEventFetcher fetcher) {
SettableFuture<UUID> result = SettableFuture.create(); SettableFuture<Pair<Long, Long>> result = SettableFuture.create();
PageLink pageLink = fetcher.getPageLink(ctx.getEdgeEventStorageSettings().getMaxReadRecordsCount()); PageLink pageLink = fetcher.getPageLink(ctx.getEdgeEventStorageSettings().getMaxReadRecordsCount());
processEdgeEvents(fetcher, pageLink, result); processEdgeEvents(fetcher, pageLink, result);
return result; return result;
} }
private void processEdgeEvents(EdgeEventFetcher fetcher, PageLink pageLink, SettableFuture<UUID> result) { private void processEdgeEvents(EdgeEventFetcher fetcher, PageLink pageLink, SettableFuture<Pair<Long, Long>> result) {
try { try {
PageData<EdgeEvent> pageData = fetcher.fetchEdgeEvents(edge.getTenantId(), edge, pageLink); PageData<EdgeEvent> pageData = fetcher.fetchEdgeEvents(edge.getTenantId(), edge, pageLink);
if (isConnected() && !pageData.getData().isEmpty()) { if (isConnected() && !pageData.getData().isEmpty()) {
@ -377,8 +405,15 @@ public final class EdgeGrpcSession implements Closeable {
if (isConnected() && pageData.hasNext()) { if (isConnected() && pageData.hasNext()) {
processEdgeEvents(fetcher, pageLink.nextPageLink(), result); processEdgeEvents(fetcher, pageLink.nextPageLink(), result);
} else { } else {
UUID ifOffset = pageData.getData().get(pageData.getData().size() - 1).getUuidId(); EdgeEvent latestEdgeEvent = pageData.getData().get(pageData.getData().size() - 1);
result.set(ifOffset); UUID idOffset = latestEdgeEvent.getUuidId();
if (idOffset != null) {
Long newStartTs = Uuids.unixTimestamp(idOffset);
long newStartSeqId = latestEdgeEvent.getSeqId();
result.set(Pair.of(newStartTs, newStartSeqId));
} else {
result.set(null);
}
} }
} }
} }
@ -461,69 +496,113 @@ public final class EdgeGrpcSession implements Closeable {
} }
} }
private DownlinkMsg convertToDownlinkMsg(EdgeEvent edgeEvent) { private List<DownlinkMsg> convertToDownlinkMsgsPack(List<EdgeEvent> edgeEvents) {
log.trace("[{}][{}] converting edge event to downlink msg [{}]", edge.getTenantId(), this.sessionId, edgeEvent); List<DownlinkMsg> result = new ArrayList<>();
DownlinkMsg downlinkMsg = null; for (EdgeEvent edgeEvent : edgeEvents) {
try { log.trace("[{}][{}] converting edge event to downlink msg [{}]", edge.getTenantId(), this.sessionId, edgeEvent);
switch (edgeEvent.getAction()) { DownlinkMsg downlinkMsg = null;
case UPDATED: try {
case ADDED: switch (edgeEvent.getAction()) {
case DELETED: case UPDATED:
case ASSIGNED_TO_EDGE: case ADDED:
case UNASSIGNED_FROM_EDGE: case DELETED:
case ALARM_ACK: case ASSIGNED_TO_EDGE:
case ALARM_CLEAR: case UNASSIGNED_FROM_EDGE:
case CREDENTIALS_UPDATED: case ALARM_ACK:
case RELATION_ADD_OR_UPDATE: case ALARM_CLEAR:
case RELATION_DELETED: case CREDENTIALS_UPDATED:
case ASSIGNED_TO_CUSTOMER: case RELATION_ADD_OR_UPDATE:
case UNASSIGNED_FROM_CUSTOMER: case RELATION_DELETED:
case CREDENTIALS_REQUEST: case CREDENTIALS_REQUEST:
case RPC_CALL: case RPC_CALL:
downlinkMsg = convertEntityEventToDownlink(edgeEvent); case ASSIGNED_TO_CUSTOMER:
log.trace("[{}][{}] entity message processed [{}]", edgeEvent.getTenantId(), this.sessionId, downlinkMsg); case UNASSIGNED_FROM_CUSTOMER:
break; downlinkMsg = convertEntityEventToDownlink(edgeEvent);
case ATTRIBUTES_UPDATED: log.trace("[{}][{}] entity message processed [{}]", edgeEvent.getTenantId(), this.sessionId, downlinkMsg);
case POST_ATTRIBUTES: break;
case ATTRIBUTES_DELETED: case ATTRIBUTES_UPDATED:
case TIMESERIES_UPDATED: case POST_ATTRIBUTES:
downlinkMsg = ctx.getTelemetryProcessor().convertTelemetryEventToDownlink(edgeEvent); case ATTRIBUTES_DELETED:
break; case TIMESERIES_UPDATED:
default: downlinkMsg = ctx.getTelemetryProcessor().convertTelemetryEventToDownlink(edgeEvent);
log.warn("[{}][{}] Unsupported action type [{}]", edge.getTenantId(), this.sessionId, edgeEvent.getAction()); break;
default:
log.warn("[{}][{}] Unsupported action type [{}]", edge.getTenantId(), this.sessionId, edgeEvent.getAction());
}
} catch (Exception e) {
log.error("[{}][{}] Exception during converting edge event to downlink msg", edge.getTenantId(), this.sessionId, e);
}
if (downlinkMsg != null) {
result.add(downlinkMsg);
}
}
return result;
}
private ListenableFuture<Pair<Long, Long>> getQueueStartTsAndSeqId() {
ListenableFuture<List<AttributeKvEntry>> future =
ctx.getAttributesService().find(edge.getTenantId(), edge.getId(), DataConstants.SERVER_SCOPE, Arrays.asList(QUEUE_START_TS_ATTR_KEY, QUEUE_START_SEQ_ID_ATTR_KEY));
return Futures.transform(future, attributeKvEntries -> {
long startTs = 0L;
long startSeqId = 0L;
for (AttributeKvEntry attributeKvEntry : attributeKvEntries) {
if (QUEUE_START_TS_ATTR_KEY.equals(attributeKvEntry.getKey())) {
startTs = attributeKvEntry.getLongValue().isPresent() ? attributeKvEntry.getLongValue().get() : 0L;
}
if (QUEUE_START_SEQ_ID_ATTR_KEY.equals(attributeKvEntry.getKey())) {
startSeqId = attributeKvEntry.getLongValue().isPresent() ? attributeKvEntry.getLongValue().get() : 0L;
}
}
if (startSeqId == 0L) {
startSeqId = findStartSeqIdFromOldestEventIfAny();
} }
return Pair.of(startTs, startSeqId);
}, ctx.getGrpcCallbackExecutorService());
}
private boolean isSeqIdStartedNewCycle() {
try {
TimePageLink pageLink = new TimePageLink(ctx.getEdgeEventStorageSettings().getMaxReadRecordsCount(), 0, null, null, this.newStartTs, System.currentTimeMillis());
PageData<EdgeEvent> edgeEvents = ctx.getEdgeEventService().findEdgeEvents(edge.getTenantId(), edge.getId(), 0L, this.previousStartSeqId == 0 ? null : this.previousStartSeqId - 1, pageLink);
return !edgeEvents.getData().isEmpty();
} catch (Exception e) { } catch (Exception e) {
log.error("[{}][{}] Exception during converting edge event to downlink msg", edge.getTenantId(), this.sessionId, e); log.error("[{}][{}][{}] Failed to execute isSeqIdStartedNewCycle", edge.getTenantId(), edge.getId(), sessionId, e);
} }
return downlinkMsg; return false;
} }
private List<DownlinkMsg> convertToDownlinkMsgsPack(List<EdgeEvent> edgeEvents) { private boolean isNewEdgeEventsAvailable() {
return edgeEvents try {
.stream() TimePageLink pageLink = new TimePageLink(ctx.getEdgeEventStorageSettings().getMaxReadRecordsCount(), 0, null, null, this.newStartTs, System.currentTimeMillis());
.map(this::convertToDownlinkMsg) PageData<EdgeEvent> edgeEvents = ctx.getEdgeEventService().findEdgeEvents(edge.getTenantId(), edge.getId(), this.newStartSeqId, null, pageLink);
.filter(Objects::nonNull) return !edgeEvents.getData().isEmpty();
.collect(Collectors.toList()); } catch (Exception e) {
log.error("[{}][{}][{}] Failed to execute isNewEdgeEventsAvailable", edge.getTenantId(), edge.getId(), sessionId, e);
}
return false;
} }
private ListenableFuture<Long> getQueueStartTs() { private long findStartSeqIdFromOldestEventIfAny() {
ListenableFuture<Optional<AttributeKvEntry>> future = long startSeqId = 0L;
ctx.getAttributesService().find(edge.getTenantId(), edge.getId(), DataConstants.SERVER_SCOPE, QUEUE_START_TS_ATTR_KEY); try {
return Futures.transform(future, attributeKvEntryOpt -> { TimePageLink pageLink = new TimePageLink(1, 0, null, new SortOrder("createdTime"), null, null);
if (attributeKvEntryOpt != null && attributeKvEntryOpt.isPresent()) { PageData<EdgeEvent> edgeEvents = ctx.getEdgeEventService().findEdgeEvents(edge.getTenantId(), edge.getId(), null, null, pageLink);
AttributeKvEntry attributeKvEntry = attributeKvEntryOpt.get(); if (!edgeEvents.getData().isEmpty()) {
return attributeKvEntry.getLongValue().isPresent() ? attributeKvEntry.getLongValue().get() : 0L; startSeqId = edgeEvents.getData().get(0).getSeqId() - 1;
} else {
return 0L;
} }
}, ctx.getGrpcCallbackExecutorService()); } catch (Exception e) {
log.error("[{}][{}][{}] Failed to execute findStartSeqIdFromOldestEventIfAny", edge.getTenantId(), edge.getId(), sessionId, e);
}
return startSeqId;
} }
private ListenableFuture<List<String>> updateQueueStartTs(Long newStartTs) { private ListenableFuture<List<String>> updateQueueStartTsAndSeqId(Pair<Long, Long> pair) {
log.trace("[{}] updating QueueStartTs [{}][{}]", this.sessionId, edge.getId(), newStartTs); this.newStartTs = pair.getFirst();
List<AttributeKvEntry> attributes = Collections.singletonList( this.newStartSeqId = pair.getSecond();
new BaseAttributeKvEntry( log.trace("[{}] updateQueueStartTsAndSeqId [{}][{}][{}]", this.sessionId, edge.getId(), this.newStartTs, this.newStartSeqId);
new LongDataEntry(QUEUE_START_TS_ATTR_KEY, newStartTs), System.currentTimeMillis())); List<AttributeKvEntry> attributes = Arrays.asList(
new BaseAttributeKvEntry(new LongDataEntry(QUEUE_START_TS_ATTR_KEY, this.newStartTs), System.currentTimeMillis()),
new BaseAttributeKvEntry(new LongDataEntry(QUEUE_START_SEQ_ID_ATTR_KEY, this.newStartSeqId), System.currentTimeMillis()));
return ctx.getAttributesService().save(edge.getTenantId(), edge.getId(), DataConstants.SERVER_SCOPE, attributes); return ctx.getAttributesService().save(edge.getTenantId(), edge.getId(), DataConstants.SERVER_SCOPE, attributes);
} }
@ -566,6 +645,10 @@ public final class EdgeGrpcSession implements Closeable {
return ctx.getOtaPackageEdgeProcessor().convertOtaPackageEventToDownlink(edgeEvent); return ctx.getOtaPackageEdgeProcessor().convertOtaPackageEventToDownlink(edgeEvent);
case QUEUE: case QUEUE:
return ctx.getQueueEdgeProcessor().convertQueueEventToDownlink(edgeEvent); return ctx.getQueueEdgeProcessor().convertQueueEventToDownlink(edgeEvent);
case TENANT:
return ctx.getTenantEdgeProcessor().convertTenantEventToDownlink(edgeEvent);
case TENANT_PROFILE:
return ctx.getTenantProfileEdgeProcessor().convertTenantProfileEventToDownlink(edgeEvent);
default: default:
log.warn("Unsupported edge event type [{}]", edgeEvent); log.warn("Unsupported edge event type [{}]", edgeEvent);
return null; return null;
@ -580,6 +663,11 @@ public final class EdgeGrpcSession implements Closeable {
result.addAll(ctx.getTelemetryProcessor().processTelemetryMsg(edge.getTenantId(), entityData)); result.addAll(ctx.getTelemetryProcessor().processTelemetryMsg(edge.getTenantId(), entityData));
} }
} }
if (uplinkMsg.getDeviceProfileUpdateMsgCount() > 0) {
for (DeviceProfileUpdateMsg deviceProfileUpdateMsg : uplinkMsg.getDeviceProfileUpdateMsgList()) {
result.add(ctx.getDeviceProfileProcessor().processDeviceProfileMsgFromEdge(edge.getTenantId(), edge, deviceProfileUpdateMsg));
}
}
if (uplinkMsg.getDeviceUpdateMsgCount() > 0) { if (uplinkMsg.getDeviceUpdateMsgCount() > 0) {
for (DeviceUpdateMsg deviceUpdateMsg : uplinkMsg.getDeviceUpdateMsgList()) { for (DeviceUpdateMsg deviceUpdateMsg : uplinkMsg.getDeviceUpdateMsgList()) {
result.add(ctx.getDeviceProcessor().processDeviceMsgFromEdge(edge.getTenantId(), edge, deviceUpdateMsg)); result.add(ctx.getDeviceProcessor().processDeviceMsgFromEdge(edge.getTenantId(), edge, deviceUpdateMsg));
@ -590,16 +678,36 @@ public final class EdgeGrpcSession implements Closeable {
result.add(ctx.getDeviceProcessor().processDeviceCredentialsMsg(edge.getTenantId(), deviceCredentialsUpdateMsg)); result.add(ctx.getDeviceProcessor().processDeviceCredentialsMsg(edge.getTenantId(), deviceCredentialsUpdateMsg));
} }
} }
if (uplinkMsg.getAssetProfileUpdateMsgCount() > 0) {
for (AssetProfileUpdateMsg assetProfileUpdateMsg : uplinkMsg.getAssetProfileUpdateMsgList()) {
result.add(ctx.getAssetProfileProcessor().processAssetProfileMsgFromEdge(edge.getTenantId(), edge, assetProfileUpdateMsg));
}
}
if (uplinkMsg.getAssetUpdateMsgCount() > 0) {
for (AssetUpdateMsg assetUpdateMsg : uplinkMsg.getAssetUpdateMsgList()) {
result.add(ctx.getAssetProcessor().processAssetMsgFromEdge(edge.getTenantId(), edge, assetUpdateMsg));
}
}
if (uplinkMsg.getAlarmUpdateMsgCount() > 0) { if (uplinkMsg.getAlarmUpdateMsgCount() > 0) {
for (AlarmUpdateMsg alarmUpdateMsg : uplinkMsg.getAlarmUpdateMsgList()) { for (AlarmUpdateMsg alarmUpdateMsg : uplinkMsg.getAlarmUpdateMsgList()) {
result.add(ctx.getAlarmProcessor().processAlarmMsg(edge.getTenantId(), alarmUpdateMsg)); result.add(ctx.getAlarmProcessor().processAlarmMsg(edge.getTenantId(), alarmUpdateMsg));
} }
} }
if (uplinkMsg.getEntityViewUpdateMsgCount() > 0) {
for (EntityViewUpdateMsg entityViewUpdateMsg : uplinkMsg.getEntityViewUpdateMsgList()) {
result.add(ctx.getEntityViewProcessor().processEntityViewMsgFromEdge(edge.getTenantId(), edge, entityViewUpdateMsg));
}
}
if (uplinkMsg.getRelationUpdateMsgCount() > 0) { if (uplinkMsg.getRelationUpdateMsgCount() > 0) {
for (RelationUpdateMsg relationUpdateMsg : uplinkMsg.getRelationUpdateMsgList()) { for (RelationUpdateMsg relationUpdateMsg : uplinkMsg.getRelationUpdateMsgList()) {
result.add(ctx.getRelationProcessor().processRelationMsg(edge.getTenantId(), relationUpdateMsg)); result.add(ctx.getRelationProcessor().processRelationMsg(edge.getTenantId(), relationUpdateMsg));
} }
} }
if (uplinkMsg.getDashboardUpdateMsgCount() > 0) {
for (DashboardUpdateMsg dashboardUpdateMsg : uplinkMsg.getDashboardUpdateMsgList()) {
result.add(ctx.getDashboardProcessor().processDashboardMsgFromEdge(edge.getTenantId(), edge, dashboardUpdateMsg));
}
}
if (uplinkMsg.getRuleChainMetadataRequestMsgCount() > 0) { if (uplinkMsg.getRuleChainMetadataRequestMsgCount() > 0) {
for (RuleChainMetadataRequestMsg ruleChainMetadataRequestMsg : uplinkMsg.getRuleChainMetadataRequestMsgList()) { for (RuleChainMetadataRequestMsg ruleChainMetadataRequestMsg : uplinkMsg.getRuleChainMetadataRequestMsgList()) {
result.add(ctx.getEdgeRequestsService().processRuleChainMetadataRequestMsg(edge.getTenantId(), edge, ruleChainMetadataRequestMsg)); result.add(ctx.getEdgeRequestsService().processRuleChainMetadataRequestMsg(edge.getTenantId(), edge, ruleChainMetadataRequestMsg));
@ -693,8 +801,11 @@ public final class EdgeGrpcSession implements Closeable {
} }
private void interruptPreviousSendDownlinkMsgsTask() { private void interruptPreviousSendDownlinkMsgsTask() {
log.debug("[{}][{}][{}] Previous send downlink future was not properly completed, stopping it now!", edge.getTenantId(), edge.getId(), this.sessionId); if (sessionState.getSendDownlinkMsgsFuture() != null && !sessionState.getSendDownlinkMsgsFuture().isDone()
stopCurrentSendDownlinkMsgsTask(true); || sessionState.getScheduledSendDownlinkTask() != null && !sessionState.getScheduledSendDownlinkTask().isCancelled()) {
log.debug("[{}][{}][{}] Previous send downlink future was not properly completed, stopping it now!", edge.getTenantId(), edge.getId(), this.sessionId);
stopCurrentSendDownlinkMsgsTask(true);
}
} }
private void interruptGeneralProcessingOnSync() { private void interruptGeneralProcessingOnSync() {

6
application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeSyncCursor.java

@ -34,6 +34,7 @@ import org.thingsboard.server.service.edge.rpc.fetch.QueuesEdgeEventFetcher;
import org.thingsboard.server.service.edge.rpc.fetch.RuleChainsEdgeEventFetcher; import org.thingsboard.server.service.edge.rpc.fetch.RuleChainsEdgeEventFetcher;
import org.thingsboard.server.service.edge.rpc.fetch.SystemWidgetsBundlesEdgeEventFetcher; import org.thingsboard.server.service.edge.rpc.fetch.SystemWidgetsBundlesEdgeEventFetcher;
import org.thingsboard.server.service.edge.rpc.fetch.TenantAdminUsersEdgeEventFetcher; import org.thingsboard.server.service.edge.rpc.fetch.TenantAdminUsersEdgeEventFetcher;
import org.thingsboard.server.service.edge.rpc.fetch.TenantEdgeEventFetcher;
import org.thingsboard.server.service.edge.rpc.fetch.TenantWidgetsBundlesEdgeEventFetcher; import org.thingsboard.server.service.edge.rpc.fetch.TenantWidgetsBundlesEdgeEventFetcher;
import java.util.LinkedList; import java.util.LinkedList;
@ -51,8 +52,7 @@ public class EdgeSyncCursor {
fetchers.add(new QueuesEdgeEventFetcher(ctx.getQueueService())); fetchers.add(new QueuesEdgeEventFetcher(ctx.getQueueService()));
fetchers.add(new RuleChainsEdgeEventFetcher(ctx.getRuleChainService())); fetchers.add(new RuleChainsEdgeEventFetcher(ctx.getRuleChainService()));
fetchers.add(new AdminSettingsEdgeEventFetcher(ctx.getAdminSettingsService(), ctx.getFreemarkerConfig())); fetchers.add(new AdminSettingsEdgeEventFetcher(ctx.getAdminSettingsService(), ctx.getFreemarkerConfig()));
fetchers.add(new DeviceProfilesEdgeEventFetcher(ctx.getDeviceProfileService())); fetchers.add(new TenantEdgeEventFetcher(ctx.getTenantService()));
fetchers.add(new AssetProfilesEdgeEventFetcher(ctx.getAssetProfileService()));
fetchers.add(new TenantAdminUsersEdgeEventFetcher(ctx.getUserService())); fetchers.add(new TenantAdminUsersEdgeEventFetcher(ctx.getUserService()));
Customer publicCustomer = ctx.getCustomerService().findOrCreatePublicCustomer(edge.getTenantId()); Customer publicCustomer = ctx.getCustomerService().findOrCreatePublicCustomer(edge.getTenantId());
fetchers.add(new CustomerEdgeEventFetcher(publicCustomer.getId())); fetchers.add(new CustomerEdgeEventFetcher(publicCustomer.getId()));
@ -61,6 +61,8 @@ public class EdgeSyncCursor {
fetchers.add(new CustomerUsersEdgeEventFetcher(ctx.getUserService(), edge.getCustomerId())); fetchers.add(new CustomerUsersEdgeEventFetcher(ctx.getUserService(), edge.getCustomerId()));
} }
} }
fetchers.add(new DeviceProfilesEdgeEventFetcher(ctx.getDeviceProfileService()));
fetchers.add(new AssetProfilesEdgeEventFetcher(ctx.getAssetProfileService()));
fetchers.add(new DevicesEdgeEventFetcher(ctx.getDeviceService())); fetchers.add(new DevicesEdgeEventFetcher(ctx.getDeviceService()));
fetchers.add(new AssetsEdgeEventFetcher(ctx.getAssetService())); fetchers.add(new AssetsEdgeEventFetcher(ctx.getAssetService()));
fetchers.add(new EntityViewsEdgeEventFetcher(ctx.getEntityViewService())); fetchers.add(new EntityViewsEdgeEventFetcher(ctx.getEntityViewService()));

18
application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AlarmMsgConstructor.java

@ -18,7 +18,10 @@ package org.thingsboard.server.service.edge.rpc.constructor;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.EntityView;
import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.Alarm;
import org.thingsboard.server.common.data.asset.Asset;
import org.thingsboard.server.common.data.id.AssetId; import org.thingsboard.server.common.data.id.AssetId;
import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.EntityViewId; import org.thingsboard.server.common.data.id.EntityViewId;
@ -47,13 +50,22 @@ public class AlarmMsgConstructor {
String entityName = null; String entityName = null;
switch (alarm.getOriginator().getEntityType()) { switch (alarm.getOriginator().getEntityType()) {
case DEVICE: case DEVICE:
entityName = deviceService.findDeviceById(tenantId, new DeviceId(alarm.getOriginator().getId())).getName(); Device deviceById = deviceService.findDeviceById(tenantId, new DeviceId(alarm.getOriginator().getId()));
if (deviceById != null) {
entityName = deviceById.getName();
}
break; break;
case ASSET: case ASSET:
entityName = assetService.findAssetById(tenantId, new AssetId(alarm.getOriginator().getId())).getName(); Asset assetById = assetService.findAssetById(tenantId, new AssetId(alarm.getOriginator().getId()));
if (assetById != null) {
entityName = assetById.getName();
}
break; break;
case ENTITY_VIEW: case ENTITY_VIEW:
entityName = entityViewService.findEntityViewById(tenantId, new EntityViewId(alarm.getOriginator().getId())).getName(); EntityView entityViewById = entityViewService.findEntityViewById(tenantId, new EntityViewId(alarm.getOriginator().getId()));
if (entityViewById != null) {
entityName = entityViewById.getName();
}
break; break;
} }
AlarmUpdateMsg.Builder builder = AlarmUpdateMsg.newBuilder() AlarmUpdateMsg.Builder builder = AlarmUpdateMsg.newBuilder()

67
application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/TenantMsgConstructor.java

@ -0,0 +1,67 @@
/**
* Copyright © 2016-2023 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.edge.rpc.constructor;
import org.springframework.stereotype.Component;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.gen.edge.v1.TenantUpdateMsg;
import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
import org.thingsboard.server.queue.util.TbCoreComponent;
@Component
@TbCoreComponent
public class TenantMsgConstructor {
public TenantUpdateMsg constructTenantUpdateMsg(UpdateMsgType msgType, Tenant tenant) {
TenantUpdateMsg.Builder builder = TenantUpdateMsg.newBuilder()
.setMsgType(msgType)
.setIdMSB(tenant.getId().getId().getMostSignificantBits())
.setIdLSB(tenant.getId().getId().getLeastSignificantBits())
.setTitle(tenant.getTitle())
.setProfileIdMSB(tenant.getTenantProfileId().getId().getMostSignificantBits())
.setProfileIdLSB(tenant.getTenantProfileId().getId().getLeastSignificantBits())
.setRegion(tenant.getRegion());
if (tenant.getCountry() != null) {
builder.setCountry(tenant.getCountry());
}
if (tenant.getState() != null) {
builder.setState(tenant.getState());
}
if (tenant.getCity() != null) {
builder.setCity(tenant.getCity());
}
if (tenant.getAddress() != null) {
builder.setAddress(tenant.getAddress());
}
if (tenant.getAddress2() != null) {
builder.setAddress2(tenant.getAddress2());
}
if (tenant.getZip() != null) {
builder.setZip(tenant.getZip());
}
if (tenant.getPhone() != null) {
builder.setPhone(tenant.getPhone());
}
if (tenant.getEmail() != null) {
builder.setEmail(tenant.getEmail());
}
if (tenant.getAdditionalInfo() != null) {
builder.setAdditionalInfo(JacksonUtil.toString(tenant.getAdditionalInfo()));
}
return builder.build();
}
}

48
application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/TenantProfileMsgConstructor.java

@ -0,0 +1,48 @@
/**
* Copyright © 2016-2023 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.edge.rpc.constructor;
import com.google.protobuf.ByteString;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.TenantProfile;
import org.thingsboard.server.gen.edge.v1.TenantProfileUpdateMsg;
import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
import org.thingsboard.server.queue.util.DataDecodingEncodingService;
import org.thingsboard.server.queue.util.TbCoreComponent;
@Component
@TbCoreComponent
public class TenantProfileMsgConstructor {
@Autowired
private DataDecodingEncodingService dataDecodingEncodingService;
public TenantProfileUpdateMsg constructTenantProfileUpdateMsg(UpdateMsgType msgType, TenantProfile tenantProfile) {
TenantProfileUpdateMsg.Builder builder = TenantProfileUpdateMsg.newBuilder()
.setMsgType(msgType)
.setIdMSB(tenantProfile.getId().getId().getMostSignificantBits())
.setIdLSB(tenantProfile.getId().getId().getLeastSignificantBits())
.setName(tenantProfile.getName())
.setDefault(tenantProfile.isDefault())
.setIsolatedRuleChain(tenantProfile.isIsolatedTbRuleEngine())
.setProfileDataBytes(ByteString.copyFrom(dataDecodingEncodingService.encode(tenantProfile.getProfileData())));
if (tenantProfile.getDescription() != null) {
builder.setDescription(tenantProfile.getDescription());
}
return builder.build();
}
}

4
application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/WidgetTypeMsgConstructor.java

@ -36,8 +36,8 @@ public class WidgetTypeMsgConstructor {
if (widgetTypeDetails.getBundleAlias() != null) { if (widgetTypeDetails.getBundleAlias() != null) {
builder.setBundleAlias(widgetTypeDetails.getBundleAlias()); builder.setBundleAlias(widgetTypeDetails.getBundleAlias());
} }
if (widgetTypeDetails.getAlias() != null) { if (widgetTypeDetails.getFqn() != null) {
builder.setAlias(widgetTypeDetails.getAlias()); builder.setFqn(widgetTypeDetails.getFqn());
} }
if (widgetTypeDetails.getName() != null) { if (widgetTypeDetails.getName() != null) {
builder.setName(widgetTypeDetails.getName()); builder.setName(widgetTypeDetails.getName());

35
application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/GeneralEdgeEventFetcher.java

@ -16,19 +16,27 @@
package org.thingsboard.server.service.edge.rpc.fetch; package org.thingsboard.server.service.edge.rpc.fetch;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.edge.EdgeEvent; import org.thingsboard.server.common.data.edge.EdgeEvent;
import org.thingsboard.server.common.data.id.TenantId; 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.SortOrder;
import org.thingsboard.server.common.data.page.TimePageLink; import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.dao.edge.EdgeEventService; import org.thingsboard.server.dao.edge.EdgeEventService;
@AllArgsConstructor @AllArgsConstructor
@Slf4j
public class GeneralEdgeEventFetcher implements EdgeEventFetcher { public class GeneralEdgeEventFetcher implements EdgeEventFetcher {
private final Long queueStartTs; private final Long queueStartTs;
private Long seqIdStart;
@Getter
private Long seqIdEnd;
@Getter
private boolean seqIdNewCycleStarted;
private Long maxReadRecordsCount;
private final EdgeEventService edgeEventService; private final EdgeEventService edgeEventService;
@Override @Override
@ -37,13 +45,32 @@ public class GeneralEdgeEventFetcher implements EdgeEventFetcher {
pageSize, pageSize,
0, 0,
null, null,
new SortOrder("createdTime", SortOrder.Direction.ASC), null,
queueStartTs, queueStartTs,
null); System.currentTimeMillis());
} }
@Override @Override
public PageData<EdgeEvent> fetchEdgeEvents(TenantId tenantId, Edge edge, PageLink pageLink) { public PageData<EdgeEvent> fetchEdgeEvents(TenantId tenantId, Edge edge, PageLink pageLink) {
return edgeEventService.findEdgeEvents(tenantId, edge.getId(), (TimePageLink) pageLink, true); try {
PageData<EdgeEvent> edgeEvents = edgeEventService.findEdgeEvents(tenantId, edge.getId(), seqIdStart, seqIdEnd, (TimePageLink) pageLink);
if (edgeEvents.getData().isEmpty()) {
this.seqIdEnd = Math.max(this.maxReadRecordsCount, seqIdStart - this.maxReadRecordsCount);
edgeEvents = edgeEventService.findEdgeEvents(tenantId, edge.getId(), 0L, seqIdEnd, (TimePageLink) pageLink);
if (edgeEvents.getData().stream().anyMatch(ee -> ee.getSeqId() < seqIdStart)) {
log.info("[{}] seqId column of edge_event table started new cycle [{}]", tenantId, edge.getId());
this.seqIdNewCycleStarted = true;
this.seqIdStart = 0L;
} else {
edgeEvents = new PageData<>();
log.warn("[{}] unexpected edge notification message received. " +
"no new events found and seqId column of edge_event table doesn't started new cycle [{}]", tenantId, edge.getId());
}
}
return edgeEvents;
} catch (Exception e) {
log.error("[{}] failed to find edge events [{}]", tenantId, edge.getId());
}
return new PageData<>();
} }
} }

51
application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/TenantEdgeEventFetcher.java

@ -0,0 +1,51 @@
/**
* Copyright © 2016-2023 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.edge.rpc.fetch;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.thingsboard.server.common.data.EdgeUtils;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.edge.EdgeEvent;
import org.thingsboard.server.common.data.edge.EdgeEventActionType;
import org.thingsboard.server.common.data.edge.EdgeEventType;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.dao.tenant.TenantService;
import java.util.List;
@AllArgsConstructor
@Slf4j
public class TenantEdgeEventFetcher extends BasePageableEdgeEventFetcher<Tenant> {
private final TenantService tenantService;
@Override
PageData<Tenant> fetchPageData(TenantId tenantId, Edge edge, PageLink pageLink) {
Tenant tenant = tenantService.findTenantById(tenantId);
// returns PageData object to be in sync with other fetchers
return new PageData<>(List.of(tenant), 1, 1, false);
}
@Override
EdgeEvent constructEdgeEvent(TenantId tenantId, Edge edge, Tenant entity) {
return EdgeUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.TENANT,
EdgeEventActionType.UPDATED, entity.getId(), null);
}
}

148
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessor.java

@ -21,10 +21,16 @@ import com.google.common.util.concurrent.ListenableFuture;
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.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.cluster.TbClusterService; import org.thingsboard.server.cluster.TbClusterService;
import org.thingsboard.server.common.data.Dashboard;
import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.EdgeUtils; import org.thingsboard.server.common.data.EdgeUtils;
import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.EntityView;
import org.thingsboard.server.common.data.asset.Asset;
import org.thingsboard.server.common.data.asset.AssetProfile;
import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.edge.EdgeEvent; import org.thingsboard.server.common.data.edge.EdgeEvent;
import org.thingsboard.server.common.data.edge.EdgeEventActionType; import org.thingsboard.server.common.data.edge.EdgeEventActionType;
@ -42,8 +48,11 @@ 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.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.relation.EntityRelation;
import org.thingsboard.server.common.data.relation.RelationTypeGroup;
import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.data.rule.RuleChainConnectionInfo; import org.thingsboard.server.common.data.rule.RuleChainConnectionInfo;
import org.thingsboard.server.common.msg.TbMsgMetaData;
import org.thingsboard.server.dao.alarm.AlarmService; import org.thingsboard.server.dao.alarm.AlarmService;
import org.thingsboard.server.dao.asset.AssetProfileService; import org.thingsboard.server.dao.asset.AssetProfileService;
import org.thingsboard.server.dao.asset.AssetService; import org.thingsboard.server.dao.asset.AssetService;
@ -55,12 +64,14 @@ import org.thingsboard.server.dao.device.DeviceProfileService;
import org.thingsboard.server.dao.device.DeviceService; import org.thingsboard.server.dao.device.DeviceService;
import org.thingsboard.server.dao.edge.EdgeEventService; import org.thingsboard.server.dao.edge.EdgeEventService;
import org.thingsboard.server.dao.edge.EdgeService; import org.thingsboard.server.dao.edge.EdgeService;
import org.thingsboard.server.dao.edge.EdgeSynchronizationManager;
import org.thingsboard.server.dao.entityview.EntityViewService; import org.thingsboard.server.dao.entityview.EntityViewService;
import org.thingsboard.server.dao.ota.OtaPackageService; import org.thingsboard.server.dao.ota.OtaPackageService;
import org.thingsboard.server.dao.queue.QueueService; import org.thingsboard.server.dao.queue.QueueService;
import org.thingsboard.server.dao.relation.RelationService; import org.thingsboard.server.dao.relation.RelationService;
import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.rule.RuleChainService;
import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.DataValidator;
import org.thingsboard.server.dao.tenant.TenantProfileService;
import org.thingsboard.server.dao.tenant.TenantService; import org.thingsboard.server.dao.tenant.TenantService;
import org.thingsboard.server.dao.user.UserService; import org.thingsboard.server.dao.user.UserService;
import org.thingsboard.server.dao.widget.WidgetTypeService; import org.thingsboard.server.dao.widget.WidgetTypeService;
@ -84,6 +95,8 @@ import org.thingsboard.server.service.edge.rpc.constructor.OtaPackageMsgConstruc
import org.thingsboard.server.service.edge.rpc.constructor.QueueMsgConstructor; import org.thingsboard.server.service.edge.rpc.constructor.QueueMsgConstructor;
import org.thingsboard.server.service.edge.rpc.constructor.RelationMsgConstructor; import org.thingsboard.server.service.edge.rpc.constructor.RelationMsgConstructor;
import org.thingsboard.server.service.edge.rpc.constructor.RuleChainMsgConstructor; import org.thingsboard.server.service.edge.rpc.constructor.RuleChainMsgConstructor;
import org.thingsboard.server.service.edge.rpc.constructor.TenantMsgConstructor;
import org.thingsboard.server.service.edge.rpc.constructor.TenantProfileMsgConstructor;
import org.thingsboard.server.service.edge.rpc.constructor.UserMsgConstructor; import org.thingsboard.server.service.edge.rpc.constructor.UserMsgConstructor;
import org.thingsboard.server.service.edge.rpc.constructor.WidgetTypeMsgConstructor; import org.thingsboard.server.service.edge.rpc.constructor.WidgetTypeMsgConstructor;
import org.thingsboard.server.service.edge.rpc.constructor.WidgetsBundleMsgConstructor; import org.thingsboard.server.service.edge.rpc.constructor.WidgetsBundleMsgConstructor;
@ -104,6 +117,7 @@ import java.util.concurrent.locks.ReentrantLock;
public abstract class BaseEdgeProcessor { public abstract class BaseEdgeProcessor {
protected static final Lock deviceCreationLock = new ReentrantLock(); protected static final Lock deviceCreationLock = new ReentrantLock();
protected static final Lock assetCreationLock = new ReentrantLock();
protected static final int DEFAULT_PAGE_SIZE = 100; protected static final int DEFAULT_PAGE_SIZE = 100;
@ -140,6 +154,9 @@ public abstract class BaseEdgeProcessor {
@Autowired @Autowired
protected TenantService tenantService; protected TenantService tenantService;
@Autowired
protected TenantProfileService tenantProfileService;
@Autowired @Autowired
protected EdgeService edgeService; protected EdgeService edgeService;
@ -195,6 +212,21 @@ public abstract class BaseEdgeProcessor {
@Autowired @Autowired
protected DataValidator<Device> deviceValidator; protected DataValidator<Device> deviceValidator;
@Autowired
protected DataValidator<DeviceProfile> deviceProfileValidator;
@Autowired
protected DataValidator<Asset> assetValidator;
@Autowired
protected DataValidator<AssetProfile> assetProfileValidator;
@Autowired
protected DataValidator<Dashboard> dashboardValidator;
@Autowired
protected DataValidator<EntityView> entityViewValidator;
@Autowired @Autowired
protected EdgeMsgConstructor edgeMsgConstructor; protected EdgeMsgConstructor edgeMsgConstructor;
@ -234,6 +266,12 @@ public abstract class BaseEdgeProcessor {
@Autowired @Autowired
protected AssetProfileMsgConstructor assetProfileMsgConstructor; protected AssetProfileMsgConstructor assetProfileMsgConstructor;
@Autowired
protected TenantMsgConstructor tenantMsgConstructor;
@Autowired
protected TenantProfileMsgConstructor tenantProfileMsgConstructor;
@Autowired @Autowired
protected WidgetsBundleMsgConstructor widgetsBundleMsgConstructor; protected WidgetsBundleMsgConstructor widgetsBundleMsgConstructor;
@ -249,15 +287,18 @@ public abstract class BaseEdgeProcessor {
@Autowired @Autowired
protected QueueMsgConstructor queueMsgConstructor; protected QueueMsgConstructor queueMsgConstructor;
@Autowired
protected EdgeSynchronizationManager edgeSynchronizationManager;
@Autowired @Autowired
protected DbCallbackExecutorService dbCallbackExecutorService; protected DbCallbackExecutorService dbCallbackExecutorService;
protected ListenableFuture<Void> saveEdgeEvent(TenantId tenantId, protected ListenableFuture<Void> saveEdgeEvent(TenantId tenantId,
EdgeId edgeId, EdgeId edgeId,
EdgeEventType type, EdgeEventType type,
EdgeEventActionType action, EdgeEventActionType action,
EntityId entityId, EntityId entityId,
JsonNode body) { JsonNode body) {
log.debug("Pushing event to edge queue. tenantId [{}], edgeId [{}], type[{}], " + log.debug("Pushing event to edge queue. tenantId [{}], edgeId [{}], type[{}], " +
"action [{}], entityId [{}], body [{}]", "action [{}], entityId [{}], body [{}]",
tenantId, edgeId, type, action, entityId, body); tenantId, edgeId, type, action, entityId, body);
@ -288,7 +329,7 @@ public abstract class BaseEdgeProcessor {
return Futures.transform(Futures.allAsList(futures), voids -> null, dbCallbackExecutorService); return Futures.transform(Futures.allAsList(futures), voids -> null, dbCallbackExecutorService);
} }
protected List<ListenableFuture<Void>> processActionForAllEdgesByTenantId(TenantId tenantId, private List<ListenableFuture<Void>> processActionForAllEdgesByTenantId(TenantId tenantId,
EdgeEventType type, EdgeEventType type,
EdgeEventActionType actionType, EdgeEventActionType actionType,
EntityId entityId, EntityId entityId,
@ -340,36 +381,46 @@ public abstract class BaseEdgeProcessor {
} }
} }
protected ListenableFuture<Void> processEntityNotification(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) { public ListenableFuture<Void> processEntityNotification(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) {
EdgeEventActionType actionType = EdgeEventActionType.valueOf(edgeNotificationMsg.getAction());
EdgeEventType type = EdgeEventType.valueOf(edgeNotificationMsg.getType()); EdgeEventType type = EdgeEventType.valueOf(edgeNotificationMsg.getType());
EntityId entityId = EntityIdFactory.getByEdgeEventTypeAndUuid(type, EdgeEventActionType actionType = EdgeEventActionType.valueOf(edgeNotificationMsg.getAction());
new UUID(edgeNotificationMsg.getEntityIdMSB(), edgeNotificationMsg.getEntityIdLSB())); EntityId entityId = EntityIdFactory.getByEdgeEventTypeAndUuid(type, new UUID(edgeNotificationMsg.getEntityIdMSB(), edgeNotificationMsg.getEntityIdLSB()));
EdgeId edgeId = safeGetEdgeId(edgeNotificationMsg); if (type.isAllEdgesRelated()) {
switch (actionType) { return processEntityNotificationForAllEdges(tenantId, type, actionType, entityId);
case ADDED: } else {
case UPDATED: JsonNode body = JacksonUtil.toJsonNode(edgeNotificationMsg.getBody());
case CREDENTIALS_UPDATED: EdgeId edgeId = safeGetEdgeId(edgeNotificationMsg);
case ASSIGNED_TO_CUSTOMER: switch (actionType) {
case UNASSIGNED_FROM_CUSTOMER: case UPDATED:
case DELETED: case CREDENTIALS_UPDATED:
if (edgeId != null) { case ASSIGNED_TO_CUSTOMER:
return saveEdgeEvent(tenantId, edgeId, type, actionType, entityId, null); case UNASSIGNED_FROM_CUSTOMER:
} else { if (edgeId != null) {
return pushNotificationToAllRelatedEdges(tenantId, entityId, type, actionType); return saveEdgeEvent(tenantId, edgeId, type, actionType, entityId, body);
}
case ASSIGNED_TO_EDGE:
case UNASSIGNED_FROM_EDGE:
ListenableFuture<Void> future = saveEdgeEvent(tenantId, edgeId, type, actionType, entityId, null);
return Futures.transformAsync(future, unused -> {
if (type.equals(EdgeEventType.RULE_CHAIN)) {
return updateDependentRuleChains(tenantId, new RuleChainId(entityId.getId()), edgeId);
} else { } else {
return Futures.immediateFuture(null); return processNotificationToRelatedEdges(tenantId, entityId, type, actionType);
} }
}, dbCallbackExecutorService); case DELETED:
default: EdgeEventActionType deleted = EdgeEventActionType.DELETED;
return Futures.immediateFuture(null); if (edgeId != null) {
return saveEdgeEvent(tenantId, edgeId, type, deleted, entityId, body);
} else {
return Futures.transform(Futures.allAsList(processActionForAllEdgesByTenantId(tenantId, type, deleted, entityId, body)),
voids -> null, dbCallbackExecutorService);
}
case ASSIGNED_TO_EDGE:
case UNASSIGNED_FROM_EDGE:
ListenableFuture<Void> future = saveEdgeEvent(tenantId, edgeId, type, actionType, entityId, body);
return Futures.transformAsync(future, unused -> {
if (type.equals(EdgeEventType.RULE_CHAIN)) {
return updateDependentRuleChains(tenantId, new RuleChainId(entityId.getId()), edgeId);
} else {
return Futures.immediateFuture(null);
}
}, dbCallbackExecutorService);
default:
return Futures.immediateFuture(null);
}
} }
} }
@ -381,7 +432,7 @@ public abstract class BaseEdgeProcessor {
} }
} }
private ListenableFuture<Void> pushNotificationToAllRelatedEdges(TenantId tenantId, EntityId entityId, EdgeEventType type, EdgeEventActionType actionType) { private ListenableFuture<Void> processNotificationToRelatedEdges(TenantId tenantId, EntityId entityId, EdgeEventType type, EdgeEventActionType actionType) {
PageLink pageLink = new PageLink(DEFAULT_PAGE_SIZE); PageLink pageLink = new PageLink(DEFAULT_PAGE_SIZE);
PageData<EdgeId> pageData; PageData<EdgeId> pageData;
List<ListenableFuture<Void>> futures = new ArrayList<>(); List<ListenableFuture<Void>> futures = new ArrayList<>();
@ -432,10 +483,7 @@ public abstract class BaseEdgeProcessor {
return Futures.transform(Futures.allAsList(futures), voids -> null, dbCallbackExecutorService); return Futures.transform(Futures.allAsList(futures), voids -> null, dbCallbackExecutorService);
} }
protected ListenableFuture<Void> processEntityNotificationForAllEdges(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) { private ListenableFuture<Void> processEntityNotificationForAllEdges(TenantId tenantId, EdgeEventType type, EdgeEventActionType actionType, EntityId entityId) {
EdgeEventActionType actionType = EdgeEventActionType.valueOf(edgeNotificationMsg.getAction());
EdgeEventType type = EdgeEventType.valueOf(edgeNotificationMsg.getType());
EntityId entityId = EntityIdFactory.getByEdgeEventTypeAndUuid(type, new UUID(edgeNotificationMsg.getEntityIdMSB(), edgeNotificationMsg.getEntityIdLSB()));
switch (actionType) { switch (actionType) {
case ADDED: case ADDED:
case UPDATED: case UPDATED:
@ -508,4 +556,28 @@ public abstract class BaseEdgeProcessor {
return false; return false;
} }
} }
protected void createRelationFromEdge(TenantId tenantId, EdgeId edgeId, EntityId entityId) {
EntityRelation relation = new EntityRelation();
relation.setFrom(edgeId);
relation.setTo(entityId);
relation.setTypeGroup(RelationTypeGroup.COMMON);
relation.setType(EntityRelation.EDGE_TYPE);
relationService.saveRelation(tenantId, relation);
}
protected TbMsgMetaData getActionTbMsgMetaData(Edge edge, CustomerId customerId) {
TbMsgMetaData metaData = getTbMsgMetaData(edge);
if (customerId != null && !customerId.isNullUid()) {
metaData.putValue("customerId", customerId.toString());
}
return metaData;
}
protected TbMsgMetaData getTbMsgMetaData(Edge edge) {
TbMsgMetaData metaData = new TbMsgMetaData();
metaData.putValue("edgeId", edge.getId().toString());
metaData.putValue("edgeName", edge.getName());
return metaData;
}
} }

49
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/AlarmEdgeProcessor.java

@ -16,6 +16,7 @@
package org.thingsboard.server.service.edge.rpc.processor.alarm; package org.thingsboard.server.service.edge.rpc.processor.alarm;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@ -28,6 +29,7 @@ import org.thingsboard.server.common.data.edge.EdgeEventActionType;
import org.thingsboard.server.common.data.edge.EdgeEventType; import org.thingsboard.server.common.data.edge.EdgeEventType;
import org.thingsboard.server.common.data.id.AlarmId; import org.thingsboard.server.common.data.id.AlarmId;
import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId; 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;
@ -62,9 +64,9 @@ public class AlarmEdgeProcessor extends BaseAlarmProcessor {
AlarmId alarmId = new AlarmId(new UUID(edgeNotificationMsg.getEntityIdMSB(), edgeNotificationMsg.getEntityIdLSB())); AlarmId alarmId = new AlarmId(new UUID(edgeNotificationMsg.getEntityIdMSB(), edgeNotificationMsg.getEntityIdLSB()));
switch (actionType) { switch (actionType) {
case DELETED: case DELETED:
EdgeId edgeId = new EdgeId(new UUID(edgeNotificationMsg.getEdgeIdMSB(), edgeNotificationMsg.getEdgeIdLSB()));
Alarm deletedAlarm = JacksonUtil.OBJECT_MAPPER.readValue(edgeNotificationMsg.getBody(), Alarm.class); Alarm deletedAlarm = JacksonUtil.OBJECT_MAPPER.readValue(edgeNotificationMsg.getBody(), Alarm.class);
return saveEdgeEvent(tenantId, edgeId, EdgeEventType.ALARM, actionType, alarmId, JacksonUtil.OBJECT_MAPPER.valueToTree(deletedAlarm)); List<ListenableFuture<Void>> delFutures = pushEventToAllRelatedEdges(tenantId, deletedAlarm.getOriginator(), alarmId, actionType, JacksonUtil.OBJECT_MAPPER.valueToTree(deletedAlarm));
return Futures.transform(Futures.allAsList(delFutures), voids -> null, dbCallbackExecutorService);
default: default:
ListenableFuture<Alarm> alarmFuture = alarmService.findAlarmByIdAsync(tenantId, alarmId); ListenableFuture<Alarm> alarmFuture = alarmService.findAlarmByIdAsync(tenantId, alarmId);
return Futures.transformAsync(alarmFuture, alarm -> { return Futures.transformAsync(alarmFuture, alarm -> {
@ -75,28 +77,33 @@ public class AlarmEdgeProcessor extends BaseAlarmProcessor {
if (type == null) { if (type == null) {
return Futures.immediateFuture(null); return Futures.immediateFuture(null);
} }
PageLink pageLink = new PageLink(DEFAULT_PAGE_SIZE); List<ListenableFuture<Void>> futures = pushEventToAllRelatedEdges(tenantId, alarm.getOriginator(), alarmId, actionType, null);
PageData<EdgeId> pageData;
List<ListenableFuture<Void>> futures = new ArrayList<>();
do {
pageData = edgeService.findRelatedEdgeIdsByEntityId(tenantId, alarm.getOriginator(), pageLink);
if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) {
for (EdgeId relatedEdgeId : pageData.getData()) {
futures.add(saveEdgeEvent(tenantId,
relatedEdgeId,
EdgeEventType.ALARM,
EdgeEventActionType.valueOf(edgeNotificationMsg.getAction()),
alarmId,
null));
}
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
}
}
} while (pageData != null && pageData.hasNext());
return Futures.transform(Futures.allAsList(futures), voids -> null, dbCallbackExecutorService); return Futures.transform(Futures.allAsList(futures), voids -> null, dbCallbackExecutorService);
}, dbCallbackExecutorService); }, dbCallbackExecutorService);
} }
} }
private List<ListenableFuture<Void>> pushEventToAllRelatedEdges(TenantId tenantId, EntityId originatorId, AlarmId alarmId, EdgeEventActionType actionType, JsonNode body) {
PageLink pageLink = new PageLink(DEFAULT_PAGE_SIZE);
PageData<EdgeId> pageData;
List<ListenableFuture<Void>> futures = new ArrayList<>();
do {
pageData = edgeService.findRelatedEdgeIdsByEntityId(tenantId, originatorId, pageLink);
if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) {
for (EdgeId relatedEdgeId : pageData.getData()) {
futures.add(saveEdgeEvent(tenantId,
relatedEdgeId,
EdgeEventType.ALARM,
actionType,
alarmId,
body));
}
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
}
}
} while (pageData != null && pageData.hasNext());
return futures;
}
} }

12
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/BaseAlarmProcessor.java

@ -49,6 +49,7 @@ public abstract class BaseAlarmProcessor extends BaseEdgeProcessor {
return Futures.immediateFuture(null); return Futures.immediateFuture(null);
} }
try { try {
edgeSynchronizationManager.getSync().set(true);
switch (alarmUpdateMsg.getMsgType()) { switch (alarmUpdateMsg.getMsgType()) {
case ENTITY_CREATED_RPC_MESSAGE: case ENTITY_CREATED_RPC_MESSAGE:
case ENTITY_UPDATED_RPC_MESSAGE: case ENTITY_UPDATED_RPC_MESSAGE:
@ -72,26 +73,26 @@ public abstract class BaseAlarmProcessor extends BaseEdgeProcessor {
} else { } else {
alarmService.updateAlarm(AlarmUpdateRequest.fromAlarm(alarm)); alarmService.updateAlarm(AlarmUpdateRequest.fromAlarm(alarm));
} }
return Futures.immediateFuture(null); break;
case ALARM_ACK_RPC_MESSAGE: case ALARM_ACK_RPC_MESSAGE:
Alarm alarmToAck = alarmService.findAlarmById(tenantId, alarmId); Alarm alarmToAck = alarmService.findAlarmById(tenantId, alarmId);
if (alarmToAck != null) { if (alarmToAck != null) {
alarmService.acknowledgeAlarm(tenantId, alarmId, alarmUpdateMsg.getAckTs()); alarmService.acknowledgeAlarm(tenantId, alarmId, alarmUpdateMsg.getAckTs());
} }
return Futures.immediateFuture(null); break;
case ALARM_CLEAR_RPC_MESSAGE: case ALARM_CLEAR_RPC_MESSAGE:
Alarm alarmToClear = alarmService.findAlarmById(tenantId, alarmId); Alarm alarmToClear = alarmService.findAlarmById(tenantId, alarmId);
if (alarmToClear != null) { if (alarmToClear != null) {
alarmService.clearAlarm(tenantId, alarmId, alarmUpdateMsg.getClearTs(), alarmService.clearAlarm(tenantId, alarmId, alarmUpdateMsg.getClearTs(),
JacksonUtil.OBJECT_MAPPER.readTree(alarmUpdateMsg.getDetails())); JacksonUtil.OBJECT_MAPPER.readTree(alarmUpdateMsg.getDetails()));
} }
return Futures.immediateFuture(null); break;
case ENTITY_DELETED_RPC_MESSAGE: case ENTITY_DELETED_RPC_MESSAGE:
Alarm alarmToDelete = alarmService.findAlarmById(tenantId, alarmId); Alarm alarmToDelete = alarmService.findAlarmById(tenantId, alarmId);
if (alarmToDelete != null) { if (alarmToDelete != null) {
alarmService.delAlarm(tenantId, alarmId); alarmService.delAlarm(tenantId, alarmId);
} }
return Futures.immediateFuture(null); break;
case UNRECOGNIZED: case UNRECOGNIZED:
default: default:
return handleUnsupportedMsgType(alarmUpdateMsg.getMsgType()); return handleUnsupportedMsgType(alarmUpdateMsg.getMsgType());
@ -99,7 +100,10 @@ public abstract class BaseAlarmProcessor extends BaseEdgeProcessor {
} catch (Exception e) { } catch (Exception e) {
log.error("[{}] Failed to process alarm update msg [{}]", tenantId, alarmUpdateMsg, e); log.error("[{}] Failed to process alarm update msg [{}]", tenantId, alarmUpdateMsg, e);
return Futures.immediateFailedFuture(e); return Futures.immediateFailedFuture(e);
} finally {
edgeSynchronizationManager.getSync().remove();
} }
return Futures.immediateFuture(null);
} }
private EntityId getAlarmOriginator(TenantId tenantId, String entityName, EntityType entityType) { private EntityId getAlarmOriginator(TenantId tenantId, String entityName, EntityType entityType) {

99
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetEdgeProcessor.java

@ -15,26 +15,113 @@
*/ */
package org.thingsboard.server.service.edge.rpc.processor.asset; package org.thingsboard.server.service.edge.rpc.processor.asset;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.util.concurrent.Futures;
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.springframework.data.util.Pair;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.EdgeUtils; import org.thingsboard.server.common.data.EdgeUtils;
import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.asset.Asset;
import org.thingsboard.server.common.data.asset.AssetProfile; import org.thingsboard.server.common.data.asset.AssetProfile;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.edge.EdgeEvent; import org.thingsboard.server.common.data.edge.EdgeEvent;
import org.thingsboard.server.common.data.edge.EdgeEventActionType;
import org.thingsboard.server.common.data.edge.EdgeEventType;
import org.thingsboard.server.common.data.id.AssetId; import org.thingsboard.server.common.data.id.AssetId;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.msg.TbMsgType;
import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbMsgDataType;
import org.thingsboard.server.dao.asset.BaseAssetService;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.gen.edge.v1.AssetUpdateMsg; import org.thingsboard.server.gen.edge.v1.AssetUpdateMsg;
import org.thingsboard.server.gen.edge.v1.DownlinkMsg; import org.thingsboard.server.gen.edge.v1.DownlinkMsg;
import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.queue.TbQueueCallback;
import org.thingsboard.server.queue.TbQueueMsgMetadata;
import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor;
import java.util.UUID;
@Component @Component
@Slf4j @Slf4j
@TbCoreComponent @TbCoreComponent
public class AssetEdgeProcessor extends BaseEdgeProcessor { public class AssetEdgeProcessor extends BaseAssetProcessor {
public ListenableFuture<Void> processAssetMsgFromEdge(TenantId tenantId, Edge edge, AssetUpdateMsg assetUpdateMsg) {
log.trace("[{}] executing processAssetMsgFromEdge [{}] from edge [{}]", tenantId, assetUpdateMsg, edge.getName());
AssetId assetId = new AssetId(new UUID(assetUpdateMsg.getIdMSB(), assetUpdateMsg.getIdLSB()));
try {
edgeSynchronizationManager.getSync().set(true);
switch (assetUpdateMsg.getMsgType()) {
case ENTITY_CREATED_RPC_MESSAGE:
case ENTITY_UPDATED_RPC_MESSAGE:
saveOrUpdateAsset(tenantId, assetId, assetUpdateMsg, edge);
return Futures.immediateFuture(null);
case ENTITY_DELETED_RPC_MESSAGE:
Asset assetToDelete = assetService.findAssetById(tenantId, assetId);
if (assetToDelete != null) {
assetService.unassignAssetFromEdge(tenantId, assetId, edge.getId());
}
return Futures.immediateFuture(null);
case UNRECOGNIZED:
default:
return handleUnsupportedMsgType(assetUpdateMsg.getMsgType());
}
} catch (DataValidationException e) {
if (e.getMessage().contains("limit reached")) {
log.warn("[{}] Number of allowed asset violated {}", tenantId, assetUpdateMsg, e);
return Futures.immediateFuture(null);
} else {
return Futures.immediateFailedFuture(e);
}
} finally {
edgeSynchronizationManager.getSync().remove();
}
}
private void saveOrUpdateAsset(TenantId tenantId, AssetId assetId, AssetUpdateMsg assetUpdateMsg, Edge edge) {
CustomerId customerId = safeGetCustomerId(assetUpdateMsg.getCustomerIdMSB(), assetUpdateMsg.getCustomerIdLSB());
Pair<Boolean, Boolean> resultPair = super.saveOrUpdateAsset(tenantId, assetId, assetUpdateMsg, customerId);
Boolean created = resultPair.getFirst();
if (created) {
createRelationFromEdge(tenantId, edge.getId(), assetId);
pushAssetCreatedEventToRuleEngine(tenantId, edge, assetId);
assetService.assignAssetToEdge(tenantId, assetId, edge.getId());
}
Boolean assetNameUpdated = resultPair.getSecond();
if (assetNameUpdated) {
saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.ASSET, EdgeEventActionType.UPDATED, assetId, null);
}
}
private void pushAssetCreatedEventToRuleEngine(TenantId tenantId, Edge edge, AssetId assetId) {
try {
Asset asset = assetService.findAssetById(tenantId, assetId);
ObjectNode entityNode = JacksonUtil.OBJECT_MAPPER.valueToTree(asset);
TbMsg tbMsg = TbMsg.newMsg(TbMsgType.ENTITY_CREATED, assetId, asset.getCustomerId(),
getActionTbMsgMetaData(edge, asset.getCustomerId()), TbMsgDataType.JSON, JacksonUtil.OBJECT_MAPPER.writeValueAsString(entityNode));
tbClusterService.pushMsgToRuleEngine(tenantId, assetId, tbMsg, new TbQueueCallback() {
@Override
public void onSuccess(TbQueueMsgMetadata metadata) {
log.debug("Successfully send ENTITY_CREATED EVENT to rule engine [{}]", asset);
}
@Override
public void onFailure(Throwable t) {
log.warn("Failed to send ENTITY_CREATED EVENT to rule engine [{}]", asset, t);
}
});
} catch (JsonProcessingException | IllegalArgumentException e) {
log.warn("[{}] Failed to push asset action to rule engine: {}", assetId, DataConstants.ENTITY_CREATED, e);
}
}
public DownlinkMsg convertAssetEventToDownlink(EdgeEvent edgeEvent) { public DownlinkMsg convertAssetEventToDownlink(EdgeEvent edgeEvent) {
AssetId assetId = new AssetId(edgeEvent.getEntityId()); AssetId assetId = new AssetId(edgeEvent.getEntityId());
@ -46,7 +133,7 @@ public class AssetEdgeProcessor extends BaseEdgeProcessor {
case ASSIGNED_TO_CUSTOMER: case ASSIGNED_TO_CUSTOMER:
case UNASSIGNED_FROM_CUSTOMER: case UNASSIGNED_FROM_CUSTOMER:
Asset asset = assetService.findAssetById(edgeEvent.getTenantId(), assetId); Asset asset = assetService.findAssetById(edgeEvent.getTenantId(), assetId);
if (asset != null) { if (asset != null && !BaseAssetService.TB_SERVICE_QUEUE.equals(asset.getType())) {
UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction());
AssetUpdateMsg assetUpdateMsg = AssetUpdateMsg assetUpdateMsg =
assetMsgConstructor.constructAssetUpdatedMsg(msgType, asset); assetMsgConstructor.constructAssetUpdatedMsg(msgType, asset);
@ -72,8 +159,4 @@ public class AssetEdgeProcessor extends BaseEdgeProcessor {
} }
return downlinkMsg; return downlinkMsg;
} }
public ListenableFuture<Void> processAssetNotification(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) {
return processEntityNotification(tenantId, edgeNotificationMsg);
}
} }

77
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetProfileEdgeProcessor.java

@ -15,25 +15,91 @@
*/ */
package org.thingsboard.server.service.edge.rpc.processor.asset; package org.thingsboard.server.service.edge.rpc.processor.asset;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.util.concurrent.Futures;
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.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.EdgeUtils; import org.thingsboard.server.common.data.EdgeUtils;
import org.thingsboard.server.common.data.asset.AssetProfile; import org.thingsboard.server.common.data.asset.AssetProfile;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.edge.EdgeEvent; import org.thingsboard.server.common.data.edge.EdgeEvent;
import org.thingsboard.server.common.data.id.AssetProfileId; import org.thingsboard.server.common.data.id.AssetProfileId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.msg.TbMsgType;
import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbMsgDataType;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.gen.edge.v1.AssetProfileUpdateMsg; import org.thingsboard.server.gen.edge.v1.AssetProfileUpdateMsg;
import org.thingsboard.server.gen.edge.v1.DownlinkMsg; import org.thingsboard.server.gen.edge.v1.DownlinkMsg;
import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.queue.TbQueueCallback;
import org.thingsboard.server.queue.TbQueueMsgMetadata;
import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor;
import java.util.UUID;
@Component @Component
@Slf4j @Slf4j
@TbCoreComponent @TbCoreComponent
public class AssetProfileEdgeProcessor extends BaseEdgeProcessor { public class AssetProfileEdgeProcessor extends BaseAssetProfileProcessor {
public ListenableFuture<Void> processAssetProfileMsgFromEdge(TenantId tenantId, Edge edge, AssetProfileUpdateMsg assetProfileUpdateMsg) {
log.trace("[{}] executing processAssetProfileMsgFromEdge [{}] from edge [{}]", tenantId, assetProfileUpdateMsg, edge.getName());
AssetProfileId assetProfileId = new AssetProfileId(new UUID(assetProfileUpdateMsg.getIdMSB(), assetProfileUpdateMsg.getIdLSB()));
try {
edgeSynchronizationManager.getSync().set(true);
switch (assetProfileUpdateMsg.getMsgType()) {
case ENTITY_CREATED_RPC_MESSAGE:
case ENTITY_UPDATED_RPC_MESSAGE:
saveOrUpdateAssetProfile(tenantId, assetProfileId, assetProfileUpdateMsg, edge);
return Futures.immediateFuture(null);
case ENTITY_DELETED_RPC_MESSAGE:
case UNRECOGNIZED:
default:
return handleUnsupportedMsgType(assetProfileUpdateMsg.getMsgType());
}
} catch (DataValidationException e) {
log.warn("Failed to process AssetProfileUpdateMsg from Edge [{}]", assetProfileUpdateMsg, e);
return Futures.immediateFailedFuture(e);
} finally {
edgeSynchronizationManager.getSync().remove();
}
}
private void saveOrUpdateAssetProfile(TenantId tenantId, AssetProfileId assetProfileId, AssetProfileUpdateMsg assetProfileUpdateMsg, Edge edge) {
boolean created = super.saveOrUpdateAssetProfile(tenantId, assetProfileId, assetProfileUpdateMsg);
if (created) {
createRelationFromEdge(tenantId, edge.getId(), assetProfileId);
pushAssetProfileCreatedEventToRuleEngine(tenantId, edge, assetProfileId);
}
}
private void pushAssetProfileCreatedEventToRuleEngine(TenantId tenantId, Edge edge, AssetProfileId assetProfileId) {
try {
AssetProfile assetProfile = assetProfileService.findAssetProfileById(tenantId, assetProfileId);
ObjectNode entityNode = JacksonUtil.OBJECT_MAPPER.valueToTree(assetProfile);
TbMsg tbMsg = TbMsg.newMsg(TbMsgType.ENTITY_CREATED, assetProfileId, getTbMsgMetaData(edge),
TbMsgDataType.JSON, JacksonUtil.OBJECT_MAPPER.writeValueAsString(entityNode));
tbClusterService.pushMsgToRuleEngine(tenantId, assetProfileId, tbMsg, new TbQueueCallback() {
@Override
public void onSuccess(TbQueueMsgMetadata metadata) {
log.debug("Successfully send ENTITY_CREATED EVENT to rule engine [{}]", assetProfile);
}
@Override
public void onFailure(Throwable t) {
log.warn("Failed to send ENTITY_CREATED EVENT to rule engine [{}]", assetProfile, t);
}
});
} catch (JsonProcessingException | IllegalArgumentException e) {
log.warn("[{}] Failed to push asset profile action to rule engine: {}", assetProfileId, DataConstants.ENTITY_CREATED, e);
}
}
public DownlinkMsg convertAssetProfileEventToDownlink(EdgeEvent edgeEvent) { public DownlinkMsg convertAssetProfileEventToDownlink(EdgeEvent edgeEvent) {
AssetProfileId assetProfileId = new AssetProfileId(edgeEvent.getEntityId()); AssetProfileId assetProfileId = new AssetProfileId(edgeEvent.getEntityId());
@ -63,9 +129,4 @@ public class AssetProfileEdgeProcessor extends BaseEdgeProcessor {
} }
return downlinkMsg; return downlinkMsg;
} }
public ListenableFuture<Void> processAssetProfileNotification(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) {
return processEntityNotificationForAllEdges(tenantId, edgeNotificationMsg);
}
} }

77
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/BaseAssetProcessor.java

@ -0,0 +1,77 @@
/**
* Copyright © 2016-2023 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.edge.rpc.processor.asset;
import com.datastax.oss.driver.api.core.uuid.Uuids;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.util.Pair;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.asset.Asset;
import org.thingsboard.server.common.data.id.AssetId;
import org.thingsboard.server.common.data.id.AssetProfileId;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.gen.edge.v1.AssetUpdateMsg;
import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor;
import java.util.UUID;
@Slf4j
public abstract class BaseAssetProcessor extends BaseEdgeProcessor {
protected Pair<Boolean, Boolean> saveOrUpdateAsset(TenantId tenantId, AssetId assetId, AssetUpdateMsg assetUpdateMsg, CustomerId customerId) {
boolean created = false;
boolean assetNameUpdated = false;
assetCreationLock.lock();
try {
Asset asset = assetService.findAssetById(tenantId, assetId);
String assetName = assetUpdateMsg.getName();
if (asset == null) {
created = true;
asset = new Asset();
asset.setTenantId(tenantId);
asset.setCreatedTime(Uuids.unixTimestamp(assetId.getId()));
}
Asset assetByName = assetService.findAssetByTenantIdAndName(tenantId, assetName);
if (assetByName != null && !assetByName.getId().equals(assetId)) {
assetName = assetName + "_" + StringUtils.randomAlphanumeric(15);
log.warn("Asset with name {} already exists. Renaming asset name to {}",
assetUpdateMsg.getName(), assetName);
assetNameUpdated = true;
}
asset.setName(assetName);
asset.setType(assetUpdateMsg.getType());
asset.setLabel(assetUpdateMsg.hasLabel() ? assetUpdateMsg.getLabel() : null);
asset.setAdditionalInfo(assetUpdateMsg.hasAdditionalInfo()
? JacksonUtil.toJsonNode(assetUpdateMsg.getAdditionalInfo()) : null);
UUID assetProfileUUID = safeGetUUID(assetUpdateMsg.getAssetProfileIdMSB(), assetUpdateMsg.getAssetProfileIdLSB());
asset.setAssetProfileId(assetProfileUUID != null ? new AssetProfileId(assetProfileUUID) : null);
asset.setCustomerId(customerId);
assetValidator.validate(asset, Asset::getTenantId);
if (created) {
asset.setId(assetId);
}
assetService.saveAsset(asset, false);
} finally {
assetCreationLock.unlock();
}
return Pair.of(created, assetNameUpdated);
}
}

69
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/BaseAssetProfileProcessor.java

@ -0,0 +1,69 @@
/**
* Copyright © 2016-2023 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.edge.rpc.processor.asset;
import com.datastax.oss.driver.api.core.uuid.Uuids;
import lombok.extern.slf4j.Slf4j;
import org.thingsboard.server.common.data.asset.AssetProfile;
import org.thingsboard.server.common.data.id.AssetProfileId;
import org.thingsboard.server.common.data.id.DashboardId;
import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.gen.edge.v1.AssetProfileUpdateMsg;
import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor;
import java.nio.charset.StandardCharsets;
import java.util.UUID;
@Slf4j
public class BaseAssetProfileProcessor extends BaseEdgeProcessor {
protected boolean saveOrUpdateAssetProfile(TenantId tenantId, AssetProfileId assetProfileId, AssetProfileUpdateMsg assetProfileUpdateMsg) {
boolean created = false;
assetCreationLock.lock();
try {
AssetProfile assetProfile = assetProfileService.findAssetProfileById(tenantId, assetProfileId);
String assetProfileName = assetProfileUpdateMsg.getName();
if (assetProfile == null) {
created = true;
assetProfile = new AssetProfile();
assetProfile.setTenantId(tenantId);
assetProfile.setCreatedTime(Uuids.unixTimestamp(assetProfileId.getId()));
}
assetProfile.setName(assetProfileName);
assetProfile.setDefault(assetProfileUpdateMsg.getDefault());
assetProfile.setDefaultQueueName(assetProfileUpdateMsg.hasDefaultQueueName() ? assetProfileUpdateMsg.getDefaultQueueName() : null);
assetProfile.setDescription(assetProfileUpdateMsg.hasDescription() ? assetProfileUpdateMsg.getDescription() : null);
assetProfile.setImage(assetProfileUpdateMsg.hasImage()
? new String(assetProfileUpdateMsg.getImage().toByteArray(), StandardCharsets.UTF_8) : null);
UUID defaultRuleChainUUID = safeGetUUID(assetProfileUpdateMsg.getDefaultRuleChainIdMSB(), assetProfileUpdateMsg.getDefaultRuleChainIdLSB());
assetProfile.setDefaultRuleChainId(defaultRuleChainUUID != null ? new RuleChainId(defaultRuleChainUUID) : null);
UUID defaultDashboardUUID = safeGetUUID(assetProfileUpdateMsg.getDefaultDashboardIdMSB(), assetProfileUpdateMsg.getDefaultDashboardIdLSB());
assetProfile.setDefaultDashboardId(defaultDashboardUUID != null ? new DashboardId(defaultDashboardUUID) : null);
assetProfileValidator.validate(assetProfile, AssetProfile::getTenantId);
if (created) {
assetProfile.setId(assetProfileId);
}
assetProfileService.saveAssetProfile(assetProfile, false);
} finally {
assetCreationLock.unlock();
}
return created;
}
}

77
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/dashboard/BaseDashboardProcessor.java

@ -0,0 +1,77 @@
/**
* Copyright © 2016-2023 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.edge.rpc.processor.dashboard;
import com.datastax.oss.driver.api.core.uuid.Uuids;
import com.fasterxml.jackson.core.type.TypeReference;
import lombok.extern.slf4j.Slf4j;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.Dashboard;
import org.thingsboard.server.common.data.ShortCustomerInfo;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DashboardId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.gen.edge.v1.DashboardUpdateMsg;
import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor;
import java.util.Set;
@Slf4j
public abstract class BaseDashboardProcessor extends BaseEdgeProcessor {
protected boolean saveOrUpdateDashboard(TenantId tenantId, DashboardId dashboardId, DashboardUpdateMsg dashboardUpdateMsg, CustomerId customerId) {
boolean created = false;
Dashboard dashboard = dashboardService.findDashboardById(tenantId, dashboardId);
if (dashboard == null) {
created = true;
dashboard = new Dashboard();
dashboard.setTenantId(tenantId);
dashboard.setCreatedTime(Uuids.unixTimestamp(dashboardId.getId()));
}
dashboard.setTitle(dashboardUpdateMsg.getTitle());
dashboard.setConfiguration(JacksonUtil.toJsonNode(dashboardUpdateMsg.getConfiguration()));
Set<ShortCustomerInfo> assignedCustomers = null;
if (dashboardUpdateMsg.hasAssignedCustomers()) {
assignedCustomers = JacksonUtil.fromString(dashboardUpdateMsg.getAssignedCustomers(), new TypeReference<>() {
});
dashboard.setAssignedCustomers(assignedCustomers);
}
dashboardValidator.validate(dashboard, Dashboard::getTenantId);
if (created) {
dashboard.setId(dashboardId);
}
Dashboard savedDashboard = dashboardService.saveDashboard(dashboard, false);
if (assignedCustomers != null && !assignedCustomers.isEmpty()) {
for (ShortCustomerInfo assignedCustomer : assignedCustomers) {
if (assignedCustomer.getCustomerId().equals(customerId)) {
dashboardService.assignDashboardToCustomer(tenantId, dashboardId, assignedCustomer.getCustomerId());
}
}
} else {
unassignCustomersFromDashboard(tenantId, savedDashboard);
}
return created;
}
private void unassignCustomersFromDashboard(TenantId tenantId, Dashboard dashboard) {
if (dashboard.getAssignedCustomers() != null && !dashboard.getAssignedCustomers().isEmpty()) {
for (ShortCustomerInfo assignedCustomer : dashboard.getAssignedCustomers()) {
dashboardService.unassignDashboardFromCustomer(tenantId, dashboard.getId(), assignedCustomer.getCustomerId());
}
}
}
}

88
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/dashboard/DashboardEdgeProcessor.java

@ -15,25 +15,103 @@
*/ */
package org.thingsboard.server.service.edge.rpc.processor.dashboard; package org.thingsboard.server.service.edge.rpc.processor.dashboard;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.util.concurrent.Futures;
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.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.Dashboard; import org.thingsboard.server.common.data.Dashboard;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.EdgeUtils; import org.thingsboard.server.common.data.EdgeUtils;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.edge.EdgeEvent; import org.thingsboard.server.common.data.edge.EdgeEvent;
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.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.msg.TbMsgType;
import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbMsgDataType;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.gen.edge.v1.DashboardUpdateMsg; import org.thingsboard.server.gen.edge.v1.DashboardUpdateMsg;
import org.thingsboard.server.gen.edge.v1.DownlinkMsg; import org.thingsboard.server.gen.edge.v1.DownlinkMsg;
import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.queue.TbQueueCallback;
import org.thingsboard.server.queue.TbQueueMsgMetadata;
import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor;
import java.util.UUID;
@Component @Component
@Slf4j @Slf4j
@TbCoreComponent @TbCoreComponent
public class DashboardEdgeProcessor extends BaseEdgeProcessor { public class DashboardEdgeProcessor extends BaseDashboardProcessor {
public ListenableFuture<Void> processDashboardMsgFromEdge(TenantId tenantId, Edge edge, DashboardUpdateMsg dashboardUpdateMsg) {
log.trace("[{}] executing processDashboardMsgFromEdge [{}] from edge [{}]", tenantId, dashboardUpdateMsg, edge.getName());
DashboardId dashboardId = new DashboardId(new UUID(dashboardUpdateMsg.getIdMSB(), dashboardUpdateMsg.getIdLSB()));
try {
edgeSynchronizationManager.getSync().set(true);
switch (dashboardUpdateMsg.getMsgType()) {
case ENTITY_CREATED_RPC_MESSAGE:
case ENTITY_UPDATED_RPC_MESSAGE:
saveOrUpdateDashboard(tenantId, dashboardId, dashboardUpdateMsg, edge);
return Futures.immediateFuture(null);
case ENTITY_DELETED_RPC_MESSAGE:
Dashboard dashboardToDelete = dashboardService.findDashboardById(tenantId, dashboardId);
if (dashboardToDelete != null) {
dashboardService.unassignDashboardFromEdge(tenantId, dashboardId, edge.getId());
}
return Futures.immediateFuture(null);
case UNRECOGNIZED:
default:
return handleUnsupportedMsgType(dashboardUpdateMsg.getMsgType());
}
} catch (DataValidationException e) {
if (e.getMessage().contains("limit reached")) {
log.warn("[{}] Number of allowed dashboard violated {}", tenantId, dashboardUpdateMsg, e);
return Futures.immediateFuture(null);
} else {
return Futures.immediateFailedFuture(e);
}
} finally {
edgeSynchronizationManager.getSync().remove();
}
}
private void saveOrUpdateDashboard(TenantId tenantId, DashboardId dashboardId, DashboardUpdateMsg dashboardUpdateMsg, Edge edge) {
CustomerId customerId = safeGetCustomerId(dashboardUpdateMsg.getCustomerIdMSB(), dashboardUpdateMsg.getCustomerIdLSB());
boolean created = super.saveOrUpdateDashboard(tenantId, dashboardId, dashboardUpdateMsg, customerId);
if (created) {
createRelationFromEdge(tenantId, edge.getId(), dashboardId);
pushDashboardCreatedEventToRuleEngine(tenantId, edge, dashboardId);
dashboardService.assignDashboardToEdge(tenantId, dashboardId, edge.getId());
}
}
private void pushDashboardCreatedEventToRuleEngine(TenantId tenantId, Edge edge, DashboardId dashboardId) {
try {
Dashboard dashboard = dashboardService.findDashboardById(tenantId, dashboardId);
ObjectNode entityNode = JacksonUtil.OBJECT_MAPPER.valueToTree(dashboard);
TbMsg tbMsg = TbMsg.newMsg(TbMsgType.ENTITY_CREATED, dashboardId, null,
getActionTbMsgMetaData(edge, null), TbMsgDataType.JSON, JacksonUtil.OBJECT_MAPPER.writeValueAsString(entityNode));
tbClusterService.pushMsgToRuleEngine(tenantId, dashboardId, tbMsg, new TbQueueCallback() {
@Override
public void onSuccess(TbQueueMsgMetadata metadata) {
log.debug("Successfully send ENTITY_CREATED EVENT to rule engine [{}]", dashboard);
}
@Override
public void onFailure(Throwable t) {
log.warn("Failed to send ENTITY_CREATED EVENT to rule engine [{}]", dashboard, t);
}
});
} catch (JsonProcessingException | IllegalArgumentException e) {
log.warn("[{}] Failed to push dashboard action to rule engine: {}", dashboardId, DataConstants.ENTITY_CREATED, e);
}
}
public DownlinkMsg convertDashboardEventToDownlink(EdgeEvent edgeEvent) { public DownlinkMsg convertDashboardEventToDownlink(EdgeEvent edgeEvent) {
DashboardId dashboardId = new DashboardId(edgeEvent.getEntityId()); DashboardId dashboardId = new DashboardId(edgeEvent.getEntityId());
@ -67,8 +145,4 @@ public class DashboardEdgeProcessor extends BaseEdgeProcessor {
} }
return downlinkMsg; return downlinkMsg;
} }
public ListenableFuture<Void> processDashboardNotification(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) {
return processEntityNotification(tenantId, edgeNotificationMsg);
}
} }

20
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/BaseDeviceProcessor.java

@ -57,13 +57,13 @@ public abstract class BaseDeviceProcessor extends BaseEdgeProcessor {
device = new Device(); device = new Device();
device.setTenantId(tenantId); device.setTenantId(tenantId);
device.setCreatedTime(Uuids.unixTimestamp(deviceId.getId())); device.setCreatedTime(Uuids.unixTimestamp(deviceId.getId()));
Device deviceByName = deviceService.findDeviceByTenantIdAndName(tenantId, deviceName); }
if (deviceByName != null) { Device deviceByName = deviceService.findDeviceByTenantIdAndName(tenantId, deviceName);
deviceName = deviceName + "_" + StringUtils.randomAlphabetic(15); if (deviceByName != null && !deviceByName.getId().equals(deviceId)) {
log.warn("Device with name {} already exists. Renaming device name to {}", deviceName = deviceName + "_" + StringUtils.randomAlphabetic(15);
deviceUpdateMsg.getName(), deviceName); log.warn("Device with name {} already exists. Renaming device name to {}",
deviceNameUpdated = true; deviceUpdateMsg.getName(), deviceName);
} deviceNameUpdated = true;
} }
device.setName(deviceName); device.setName(deviceName);
device.setType(deviceUpdateMsg.getType()); device.setType(deviceUpdateMsg.getType());
@ -97,7 +97,7 @@ public abstract class BaseDeviceProcessor extends BaseEdgeProcessor {
deviceCredentials.setCredentialsId(StringUtils.randomAlphanumeric(20)); deviceCredentials.setCredentialsId(StringUtils.randomAlphanumeric(20));
deviceCredentialsService.createDeviceCredentials(device.getTenantId(), deviceCredentials); deviceCredentialsService.createDeviceCredentials(device.getTenantId(), deviceCredentials);
} }
tbClusterService.onDeviceUpdated(savedDevice, created ? null : device, false); tbClusterService.onDeviceUpdated(savedDevice, created ? null : device);
} finally { } finally {
deviceCreationLock.unlock(); deviceCreationLock.unlock();
} }
@ -113,6 +113,8 @@ public abstract class BaseDeviceProcessor extends BaseEdgeProcessor {
log.debug("Updating device credentials for device [{}]. New device credentials Id [{}], value [{}]", log.debug("Updating device credentials for device [{}]. New device credentials Id [{}], value [{}]",
device.getName(), deviceCredentialsUpdateMsg.getCredentialsId(), deviceCredentialsUpdateMsg.getCredentialsValue()); device.getName(), deviceCredentialsUpdateMsg.getCredentialsId(), deviceCredentialsUpdateMsg.getCredentialsValue());
try { try {
edgeSynchronizationManager.getSync().set(true);
DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(tenantId, device.getId()); DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(tenantId, device.getId());
deviceCredentials.setCredentialsType(DeviceCredentialsType.valueOf(deviceCredentialsUpdateMsg.getCredentialsType())); deviceCredentials.setCredentialsType(DeviceCredentialsType.valueOf(deviceCredentialsUpdateMsg.getCredentialsType()));
deviceCredentials.setCredentialsId(deviceCredentialsUpdateMsg.getCredentialsId()); deviceCredentials.setCredentialsId(deviceCredentialsUpdateMsg.getCredentialsId());
@ -123,6 +125,8 @@ public abstract class BaseDeviceProcessor extends BaseEdgeProcessor {
log.error("Can't update device credentials for device [{}], deviceCredentialsUpdateMsg [{}]", log.error("Can't update device credentials for device [{}], deviceCredentialsUpdateMsg [{}]",
device.getName(), deviceCredentialsUpdateMsg, e); device.getName(), deviceCredentialsUpdateMsg, e);
throw new RuntimeException(e); throw new RuntimeException(e);
} finally {
edgeSynchronizationManager.getSync().remove();
} }
} else { } else {
log.warn("Can't find device by id [{}], deviceCredentialsUpdateMsg [{}]", deviceId, deviceCredentialsUpdateMsg); log.warn("Can't find device by id [{}], deviceCredentialsUpdateMsg [{}]", deviceId, deviceCredentialsUpdateMsg);

102
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/BaseDeviceProfileProcessor.java

@ -0,0 +1,102 @@
/**
* Copyright © 2016-2023 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.edge.rpc.processor.device;
import com.datastax.oss.driver.api.core.uuid.Uuids;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.DeviceProfileProvisionType;
import org.thingsboard.server.common.data.DeviceProfileType;
import org.thingsboard.server.common.data.DeviceTransportType;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.device.profile.DeviceProfileData;
import org.thingsboard.server.common.data.id.DashboardId;
import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.data.id.OtaPackageId;
import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.gen.edge.v1.DeviceProfileUpdateMsg;
import org.thingsboard.server.queue.util.DataDecodingEncodingService;
import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor;
import java.nio.charset.StandardCharsets;
import java.util.Optional;
import java.util.UUID;
@Slf4j
public class BaseDeviceProfileProcessor extends BaseEdgeProcessor {
@Autowired
private DataDecodingEncodingService dataDecodingEncodingService;
protected boolean saveOrUpdateDeviceProfile(TenantId tenantId, DeviceProfileId deviceProfileId, DeviceProfileUpdateMsg deviceProfileUpdateMsg) {
boolean created = false;
deviceCreationLock.lock();
try {
DeviceProfile deviceProfile = deviceProfileService.findDeviceProfileById(tenantId, deviceProfileId);
if (deviceProfile == null) {
created = true;
deviceProfile = new DeviceProfile();
deviceProfile.setTenantId(tenantId);
deviceProfile.setCreatedTime(Uuids.unixTimestamp(deviceProfileId.getId()));
}
deviceProfile.setName(deviceProfileUpdateMsg.getName());
deviceProfile.setDescription(deviceProfileUpdateMsg.hasDescription() ? deviceProfileUpdateMsg.getDescription() : null);
deviceProfile.setDefault(deviceProfileUpdateMsg.getDefault());
deviceProfile.setType(DeviceProfileType.valueOf(deviceProfileUpdateMsg.getType()));
deviceProfile.setTransportType(deviceProfileUpdateMsg.hasTransportType()
? DeviceTransportType.valueOf(deviceProfileUpdateMsg.getTransportType()) : DeviceTransportType.DEFAULT);
deviceProfile.setImage(deviceProfileUpdateMsg.hasImage()
? new String(deviceProfileUpdateMsg.getImage().toByteArray(), StandardCharsets.UTF_8) : null);
deviceProfile.setProvisionType(deviceProfileUpdateMsg.hasProvisionType()
? DeviceProfileProvisionType.valueOf(deviceProfileUpdateMsg.getProvisionType()) : DeviceProfileProvisionType.DISABLED);
deviceProfile.setProvisionDeviceKey(deviceProfileUpdateMsg.hasProvisionDeviceKey()
? deviceProfileUpdateMsg.getProvisionDeviceKey() : null);
deviceProfile.setDefaultQueueName(deviceProfileUpdateMsg.getDefaultQueueName());
Optional<DeviceProfileData> profileDataOpt =
dataDecodingEncodingService.decode(deviceProfileUpdateMsg.getProfileDataBytes().toByteArray());
deviceProfile.setProfileData(profileDataOpt.orElse(null));
UUID defaultRuleChainUUID = safeGetUUID(deviceProfileUpdateMsg.getDefaultRuleChainIdMSB(), deviceProfileUpdateMsg.getDefaultRuleChainIdLSB());
deviceProfile.setDefaultRuleChainId(defaultRuleChainUUID != null ? new RuleChainId(defaultRuleChainUUID) : null);
UUID defaultDashboardUUID = safeGetUUID(deviceProfileUpdateMsg.getDefaultDashboardIdMSB(), deviceProfileUpdateMsg.getDefaultDashboardIdLSB());
deviceProfile.setDefaultDashboardId(defaultDashboardUUID != null ? new DashboardId(defaultDashboardUUID) : null);
String defaultQueueName = StringUtils.isNotBlank(deviceProfileUpdateMsg.getDefaultQueueName())
? deviceProfileUpdateMsg.getDefaultQueueName() : null;
deviceProfile.setDefaultQueueName(defaultQueueName);
UUID firmwareUUID = safeGetUUID(deviceProfileUpdateMsg.getFirmwareIdMSB(), deviceProfileUpdateMsg.getFirmwareIdLSB());
deviceProfile.setFirmwareId(firmwareUUID != null ? new OtaPackageId(firmwareUUID) : null);
UUID softwareUUID = safeGetUUID(deviceProfileUpdateMsg.getSoftwareIdMSB(), deviceProfileUpdateMsg.getSoftwareIdLSB());
deviceProfile.setSoftwareId(softwareUUID != null ? new OtaPackageId(softwareUUID) : null);
deviceProfileValidator.validate(deviceProfile, DeviceProfile::getTenantId);
if (created) {
deviceProfile.setId(deviceProfileId);
}
deviceProfileService.saveDeviceProfile(deviceProfile, false);
} finally {
deviceCreationLock.unlock();
}
return created;
}
}

47
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceEdgeProcessor.java

@ -35,18 +35,14 @@ import org.thingsboard.server.common.data.edge.EdgeEventActionType;
import org.thingsboard.server.common.data.edge.EdgeEventType; import org.thingsboard.server.common.data.edge.EdgeEventType;
import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.msg.TbMsgType;
import org.thingsboard.server.common.data.relation.RelationTypeGroup;
import org.thingsboard.server.common.data.rpc.RpcError; import org.thingsboard.server.common.data.rpc.RpcError;
import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbMsgDataType; import org.thingsboard.server.common.msg.TbMsgDataType;
import org.thingsboard.server.common.msg.TbMsgMetaData; import org.thingsboard.server.common.msg.TbMsgMetaData;
import org.thingsboard.server.common.msg.rpc.FromDeviceRpcResponse; import org.thingsboard.server.common.msg.rpc.FromDeviceRpcResponse;
import org.thingsboard.server.common.msg.session.SessionMsgType;
import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.gen.edge.v1.DeviceCredentialsRequestMsg; import org.thingsboard.server.gen.edge.v1.DeviceCredentialsRequestMsg;
import org.thingsboard.server.gen.edge.v1.DeviceCredentialsUpdateMsg; import org.thingsboard.server.gen.edge.v1.DeviceCredentialsUpdateMsg;
@ -54,7 +50,6 @@ import org.thingsboard.server.gen.edge.v1.DeviceRpcCallMsg;
import org.thingsboard.server.gen.edge.v1.DeviceUpdateMsg; import org.thingsboard.server.gen.edge.v1.DeviceUpdateMsg;
import org.thingsboard.server.gen.edge.v1.DownlinkMsg; import org.thingsboard.server.gen.edge.v1.DownlinkMsg;
import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.queue.TbQueueCallback; import org.thingsboard.server.queue.TbQueueCallback;
import org.thingsboard.server.queue.TbQueueMsgMetadata; import org.thingsboard.server.queue.TbQueueMsgMetadata;
import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.queue.util.TbCoreComponent;
@ -71,6 +66,8 @@ public class DeviceEdgeProcessor extends BaseDeviceProcessor {
log.trace("[{}] executing processDeviceMsgFromEdge [{}] from edge [{}]", tenantId, deviceUpdateMsg, edge.getName()); log.trace("[{}] executing processDeviceMsgFromEdge [{}] from edge [{}]", tenantId, deviceUpdateMsg, edge.getName());
DeviceId deviceId = new DeviceId(new UUID(deviceUpdateMsg.getIdMSB(), deviceUpdateMsg.getIdLSB())); DeviceId deviceId = new DeviceId(new UUID(deviceUpdateMsg.getIdMSB(), deviceUpdateMsg.getIdLSB()));
try { try {
edgeSynchronizationManager.getSync().set(true);
switch (deviceUpdateMsg.getMsgType()) { switch (deviceUpdateMsg.getMsgType()) {
case ENTITY_CREATED_RPC_MESSAGE: case ENTITY_CREATED_RPC_MESSAGE:
case ENTITY_UPDATED_RPC_MESSAGE: case ENTITY_UPDATED_RPC_MESSAGE:
@ -93,6 +90,8 @@ public class DeviceEdgeProcessor extends BaseDeviceProcessor {
} else { } else {
return Futures.immediateFailedFuture(e); return Futures.immediateFailedFuture(e);
} }
} finally {
edgeSynchronizationManager.getSync().remove();
} }
} }
@ -111,20 +110,11 @@ public class DeviceEdgeProcessor extends BaseDeviceProcessor {
} }
} }
private void createRelationFromEdge(TenantId tenantId, EdgeId edgeId, EntityId entityId) {
EntityRelation relation = new EntityRelation();
relation.setFrom(edgeId);
relation.setTo(entityId);
relation.setTypeGroup(RelationTypeGroup.COMMON);
relation.setType(EntityRelation.EDGE_TYPE);
relationService.saveRelation(tenantId, relation);
}
private void pushDeviceCreatedEventToRuleEngine(TenantId tenantId, Edge edge, DeviceId deviceId) { private void pushDeviceCreatedEventToRuleEngine(TenantId tenantId, Edge edge, DeviceId deviceId) {
try { try {
Device device = deviceService.findDeviceById(tenantId, deviceId); Device device = deviceService.findDeviceById(tenantId, deviceId);
ObjectNode entityNode = JacksonUtil.OBJECT_MAPPER.valueToTree(device); ObjectNode entityNode = JacksonUtil.OBJECT_MAPPER.valueToTree(device);
TbMsg tbMsg = TbMsg.newMsg(DataConstants.ENTITY_CREATED, deviceId, device.getCustomerId(), TbMsg tbMsg = TbMsg.newMsg(TbMsgType.ENTITY_CREATED, deviceId, device.getCustomerId(),
getActionTbMsgMetaData(edge, device.getCustomerId()), TbMsgDataType.JSON, JacksonUtil.OBJECT_MAPPER.writeValueAsString(entityNode)); getActionTbMsgMetaData(edge, device.getCustomerId()), TbMsgDataType.JSON, JacksonUtil.OBJECT_MAPPER.writeValueAsString(entityNode));
tbClusterService.pushMsgToRuleEngine(tenantId, deviceId, tbMsg, new TbQueueCallback() { tbClusterService.pushMsgToRuleEngine(tenantId, deviceId, tbMsg, new TbQueueCallback() {
@Override @Override
@ -134,27 +124,12 @@ public class DeviceEdgeProcessor extends BaseDeviceProcessor {
@Override @Override
public void onFailure(Throwable t) { public void onFailure(Throwable t) {
log.debug("Failed to send ENTITY_CREATED EVENT to rule engine [{}]", device, t); log.warn("Failed to send ENTITY_CREATED EVENT to rule engine [{}]", device, t);
} }
}); });
} catch (JsonProcessingException | IllegalArgumentException e) { } catch (JsonProcessingException | IllegalArgumentException e) {
log.warn("[{}] Failed to push device action to rule engine: {}", deviceId, DataConstants.ENTITY_CREATED, e); log.warn("[{}] Failed to push device action to rule engine: {}", deviceId, TbMsgType.ENTITY_CREATED.name(), e);
}
}
private TbMsgMetaData getActionTbMsgMetaData(Edge edge, CustomerId customerId) {
TbMsgMetaData metaData = getTbMsgMetaData(edge);
if (customerId != null && !customerId.isNullUid()) {
metaData.putValue("customerId", customerId.toString());
} }
return metaData;
}
private TbMsgMetaData getTbMsgMetaData(Edge edge) {
TbMsgMetaData metaData = new TbMsgMetaData();
metaData.putValue("edgeId", edge.getId().toString());
metaData.putValue("edgeName", edge.getName());
return metaData;
} }
public ListenableFuture<Void> processDeviceRpcCallFromEdge(TenantId tenantId, Edge edge, DeviceRpcCallMsg deviceRpcCallMsg) { public ListenableFuture<Void> processDeviceRpcCallFromEdge(TenantId tenantId, Edge edge, DeviceRpcCallMsg deviceRpcCallMsg) {
@ -216,7 +191,7 @@ public class DeviceEdgeProcessor extends BaseDeviceProcessor {
ObjectNode data = JacksonUtil.newObjectNode(); ObjectNode data = JacksonUtil.newObjectNode();
data.put("method", deviceRpcCallMsg.getRequestMsg().getMethod()); data.put("method", deviceRpcCallMsg.getRequestMsg().getMethod());
data.put("params", deviceRpcCallMsg.getRequestMsg().getParams()); data.put("params", deviceRpcCallMsg.getRequestMsg().getParams());
TbMsg tbMsg = TbMsg.newMsg(SessionMsgType.TO_SERVER_RPC_REQUEST.name(), deviceId, null, metaData, TbMsg tbMsg = TbMsg.newMsg(TbMsgType.TO_SERVER_RPC_REQUEST, deviceId, null, metaData,
TbMsgDataType.JSON, JacksonUtil.OBJECT_MAPPER.writeValueAsString(data)); TbMsgDataType.JSON, JacksonUtil.OBJECT_MAPPER.writeValueAsString(data));
tbClusterService.pushMsgToRuleEngine(tenantId, deviceId, tbMsg, new TbQueueCallback() { tbClusterService.pushMsgToRuleEngine(tenantId, deviceId, tbMsg, new TbQueueCallback() {
@Override @Override
@ -308,8 +283,4 @@ public class DeviceEdgeProcessor extends BaseDeviceProcessor {
.addDeviceCredentialsRequestMsg(deviceCredentialsRequestMsg); .addDeviceCredentialsRequestMsg(deviceCredentialsRequestMsg);
return builder.build(); return builder.build();
} }
public ListenableFuture<Void> processDeviceNotification(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) {
return processEntityNotification(tenantId, edgeNotificationMsg);
}
} }

77
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceProfileEdgeProcessor.java

@ -15,25 +15,92 @@
*/ */
package org.thingsboard.server.service.edge.rpc.processor.device; package org.thingsboard.server.service.edge.rpc.processor.device;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.util.concurrent.Futures;
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.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.EdgeUtils; import org.thingsboard.server.common.data.EdgeUtils;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.edge.EdgeEvent; import org.thingsboard.server.common.data.edge.EdgeEvent;
import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.msg.TbMsgType;
import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbMsgDataType;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.gen.edge.v1.DeviceProfileUpdateMsg; import org.thingsboard.server.gen.edge.v1.DeviceProfileUpdateMsg;
import org.thingsboard.server.gen.edge.v1.DownlinkMsg; import org.thingsboard.server.gen.edge.v1.DownlinkMsg;
import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.queue.TbQueueCallback;
import org.thingsboard.server.queue.TbQueueMsgMetadata;
import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor;
import java.util.UUID;
@Component @Component
@Slf4j @Slf4j
@TbCoreComponent @TbCoreComponent
public class DeviceProfileEdgeProcessor extends BaseEdgeProcessor { public class DeviceProfileEdgeProcessor extends BaseDeviceProfileProcessor {
public ListenableFuture<Void> processDeviceProfileMsgFromEdge(TenantId tenantId, Edge edge, DeviceProfileUpdateMsg deviceProfileUpdateMsg) {
log.trace("[{}] executing processDeviceProfileMsgFromEdge [{}] from edge [{}]", tenantId, deviceProfileUpdateMsg, edge.getName());
DeviceProfileId deviceProfileId = new DeviceProfileId(new UUID(deviceProfileUpdateMsg.getIdMSB(), deviceProfileUpdateMsg.getIdLSB()));
try {
edgeSynchronizationManager.getSync().set(true);
switch (deviceProfileUpdateMsg.getMsgType()) {
case ENTITY_CREATED_RPC_MESSAGE:
case ENTITY_UPDATED_RPC_MESSAGE:
saveOrUpdateDeviceProfile(tenantId, deviceProfileId, deviceProfileUpdateMsg, edge);
return Futures.immediateFuture(null);
case ENTITY_DELETED_RPC_MESSAGE:
case UNRECOGNIZED:
default:
return handleUnsupportedMsgType(deviceProfileUpdateMsg.getMsgType());
}
} catch (DataValidationException e) {
log.warn("Failed to process DeviceProfileUpdateMsg from Edge [{}]", deviceProfileUpdateMsg, e);
return Futures.immediateFailedFuture(e);
} finally {
edgeSynchronizationManager.getSync().remove();
}
}
private void saveOrUpdateDeviceProfile(TenantId tenantId, DeviceProfileId deviceProfileId, DeviceProfileUpdateMsg deviceProfileUpdateMsg, Edge edge) {
boolean created = super.saveOrUpdateDeviceProfile(tenantId, deviceProfileId, deviceProfileUpdateMsg);
if (created) {
createRelationFromEdge(tenantId, edge.getId(), deviceProfileId);
pushDeviceProfileCreatedEventToRuleEngine(tenantId, edge, deviceProfileId);
}
}
private void pushDeviceProfileCreatedEventToRuleEngine(TenantId tenantId, Edge edge, DeviceProfileId deviceProfileId) {
try {
DeviceProfile deviceProfile = deviceProfileService.findDeviceProfileById(tenantId, deviceProfileId);
ObjectNode entityNode = JacksonUtil.OBJECT_MAPPER.valueToTree(deviceProfile);
TbMsg tbMsg = TbMsg.newMsg(TbMsgType.ENTITY_CREATED, deviceProfileId, getTbMsgMetaData(edge),
TbMsgDataType.JSON, JacksonUtil.OBJECT_MAPPER.writeValueAsString(entityNode));
tbClusterService.pushMsgToRuleEngine(tenantId, deviceProfileId, tbMsg, new TbQueueCallback() {
@Override
public void onSuccess(TbQueueMsgMetadata metadata) {
log.debug("Successfully send ENTITY_CREATED EVENT to rule engine [{}]", deviceProfile);
}
@Override
public void onFailure(Throwable t) {
log.warn("Failed to send ENTITY_CREATED EVENT to rule engine [{}]", deviceProfile, t);
}
});
} catch (JsonProcessingException | IllegalArgumentException e) {
log.warn("[{}] Failed to push device profile action to rule engine: {}", deviceProfileId, DataConstants.ENTITY_CREATED, e);
}
}
public DownlinkMsg convertDeviceProfileEventToDownlink(EdgeEvent edgeEvent) { public DownlinkMsg convertDeviceProfileEventToDownlink(EdgeEvent edgeEvent) {
DeviceProfileId deviceProfileId = new DeviceProfileId(edgeEvent.getEntityId()); DeviceProfileId deviceProfileId = new DeviceProfileId(edgeEvent.getEntityId());
@ -63,8 +130,4 @@ public class DeviceProfileEdgeProcessor extends BaseEdgeProcessor {
} }
return downlinkMsg; return downlinkMsg;
} }
public ListenableFuture<Void> processDeviceProfileNotification(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) {
return processEntityNotificationForAllEdges(tenantId, edgeNotificationMsg);
}
} }

76
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/entityview/BaseEntityViewProcessor.java

@ -0,0 +1,76 @@
/**
* Copyright © 2016-2023 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.edge.rpc.processor.entityview;
import com.datastax.oss.driver.api.core.uuid.Uuids;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.util.Pair;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.EntityView;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.id.AssetId;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.EntityViewId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.gen.edge.v1.EdgeEntityType;
import org.thingsboard.server.gen.edge.v1.EntityViewUpdateMsg;
import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor;
import java.util.UUID;
@Slf4j
public abstract class BaseEntityViewProcessor extends BaseEdgeProcessor {
protected Pair<Boolean, Boolean> saveOrUpdateEntityView(TenantId tenantId, EntityViewId entityViewId, EntityViewUpdateMsg entityViewUpdateMsg, CustomerId customerId) {
boolean created = false;
boolean entityViewNameUpdated = false;
EntityView entityView = entityViewService.findEntityViewById(tenantId, entityViewId);
String entityViewName = entityViewUpdateMsg.getName();
if (entityView == null) {
created = true;
entityView = new EntityView();
entityView.setTenantId(tenantId);
entityView.setCreatedTime(Uuids.unixTimestamp(entityViewId.getId()));
}
EntityView entityViewByName = entityViewService.findEntityViewByTenantIdAndName(tenantId, entityViewName);
if (entityViewByName != null && !entityViewByName.getId().equals(entityViewId)) {
entityViewName = entityViewName + "_" + StringUtils.randomAlphanumeric(15);
log.warn("Entity view with name {} already exists. Renaming entity view name to {}",
entityViewUpdateMsg.getName(), entityViewName);
entityViewNameUpdated = true;
}
entityView.setName(entityViewName);
entityView.setType(entityViewUpdateMsg.getType());
entityView.setCustomerId(customerId);
entityView.setAdditionalInfo(entityViewUpdateMsg.hasAdditionalInfo() ?
JacksonUtil.toJsonNode(entityViewUpdateMsg.getAdditionalInfo()) : null);
UUID entityIdUUID = safeGetUUID(entityViewUpdateMsg.getEntityIdMSB(), entityViewUpdateMsg.getEntityIdLSB());
if (EdgeEntityType.DEVICE.equals(entityViewUpdateMsg.getEntityType())) {
entityView.setEntityId(entityIdUUID != null ? new DeviceId(entityIdUUID) : null);
} else if (EdgeEntityType.ASSET.equals(entityViewUpdateMsg.getEntityType())) {
entityView.setEntityId(entityIdUUID != null ? new AssetId(entityIdUUID) : null);
}
entityViewValidator.validate(entityView, EntityView::getTenantId);
if (created) {
entityView.setId(entityViewId);
}
entityViewService.saveEntityView(entityView, false);
return Pair.of(created, entityViewNameUpdated);
}
}

96
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/entityview/EntityViewEdgeProcessor.java

@ -15,25 +15,111 @@
*/ */
package org.thingsboard.server.service.edge.rpc.processor.entityview; package org.thingsboard.server.service.edge.rpc.processor.entityview;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.util.concurrent.Futures;
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.springframework.data.util.Pair;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.EdgeUtils; import org.thingsboard.server.common.data.EdgeUtils;
import org.thingsboard.server.common.data.EntityView; import org.thingsboard.server.common.data.EntityView;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.edge.EdgeEvent; import org.thingsboard.server.common.data.edge.EdgeEvent;
import org.thingsboard.server.common.data.edge.EdgeEventActionType;
import org.thingsboard.server.common.data.edge.EdgeEventType;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EntityViewId; import org.thingsboard.server.common.data.id.EntityViewId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.msg.TbMsgType;
import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbMsgDataType;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.gen.edge.v1.DownlinkMsg; import org.thingsboard.server.gen.edge.v1.DownlinkMsg;
import org.thingsboard.server.gen.edge.v1.EntityViewUpdateMsg; import org.thingsboard.server.gen.edge.v1.EntityViewUpdateMsg;
import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.queue.TbQueueCallback;
import org.thingsboard.server.queue.TbQueueMsgMetadata;
import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor;
import java.util.UUID;
@Component @Component
@Slf4j @Slf4j
@TbCoreComponent @TbCoreComponent
public class EntityViewEdgeProcessor extends BaseEdgeProcessor { public class EntityViewEdgeProcessor extends BaseEntityViewProcessor {
public ListenableFuture<Void> processEntityViewMsgFromEdge(TenantId tenantId, Edge edge, EntityViewUpdateMsg entityViewUpdateMsg) {
log.trace("[{}] executing processEntityViewMsgFromEdge [{}] from edge [{}]", tenantId, entityViewUpdateMsg, edge.getName());
EntityViewId entityViewId = new EntityViewId(new UUID(entityViewUpdateMsg.getIdMSB(), entityViewUpdateMsg.getIdLSB()));
try {
edgeSynchronizationManager.getSync().set(true);
switch (entityViewUpdateMsg.getMsgType()) {
case ENTITY_CREATED_RPC_MESSAGE:
case ENTITY_UPDATED_RPC_MESSAGE:
saveOrUpdateEntityView(tenantId, entityViewId, entityViewUpdateMsg, edge);
return Futures.immediateFuture(null);
case ENTITY_DELETED_RPC_MESSAGE:
EntityView entityViewToDelete = entityViewService.findEntityViewById(tenantId, entityViewId);
if (entityViewToDelete != null) {
entityViewService.unassignEntityViewFromEdge(tenantId, entityViewId, edge.getId());
}
return Futures.immediateFuture(null);
case UNRECOGNIZED:
default:
return handleUnsupportedMsgType(entityViewUpdateMsg.getMsgType());
}
} catch (DataValidationException e) {
if (e.getMessage().contains("limit reached")) {
log.warn("[{}] Number of allowed entity views violated {}", tenantId, entityViewUpdateMsg, e);
return Futures.immediateFuture(null);
} else {
return Futures.immediateFailedFuture(e);
}
} finally {
edgeSynchronizationManager.getSync().remove();
}
}
private void saveOrUpdateEntityView(TenantId tenantId, EntityViewId entityViewId, EntityViewUpdateMsg entityViewUpdateMsg, Edge edge) {
CustomerId customerId = safeGetCustomerId(entityViewUpdateMsg.getCustomerIdMSB(), entityViewUpdateMsg.getCustomerIdLSB());
Pair<Boolean, Boolean> resultPair = super.saveOrUpdateEntityView(tenantId, entityViewId, entityViewUpdateMsg, customerId);
Boolean created = resultPair.getFirst();
if (created) {
createRelationFromEdge(tenantId, edge.getId(), entityViewId);
pushAssetCreatedEventToRuleEngine(tenantId, edge, entityViewId);
entityViewService.assignEntityViewToEdge(tenantId, entityViewId, edge.getId());
}
Boolean assetNameUpdated = resultPair.getSecond();
if (assetNameUpdated) {
saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.ENTITY_VIEW, EdgeEventActionType.UPDATED, entityViewId, null);
}
}
private void pushAssetCreatedEventToRuleEngine(TenantId tenantId, Edge edge, EntityViewId entityViewId) {
try {
EntityView entityView = entityViewService.findEntityViewById(tenantId, entityViewId);
ObjectNode entityNode = JacksonUtil.OBJECT_MAPPER.valueToTree(entityView);
TbMsg tbMsg = TbMsg.newMsg(TbMsgType.ENTITY_CREATED, entityViewId, entityView.getCustomerId(),
getActionTbMsgMetaData(edge, entityView.getCustomerId()), TbMsgDataType.JSON, JacksonUtil.OBJECT_MAPPER.writeValueAsString(entityNode));
tbClusterService.pushMsgToRuleEngine(tenantId, entityViewId, tbMsg, new TbQueueCallback() {
@Override
public void onSuccess(TbQueueMsgMetadata metadata) {
log.debug("Successfully send ENTITY_CREATED EVENT to rule engine [{}]", entityView);
}
@Override
public void onFailure(Throwable t) {
log.warn("Failed to send ENTITY_CREATED EVENT to rule engine [{}]", entityView, t);
}
});
} catch (JsonProcessingException | IllegalArgumentException e) {
log.warn("[{}] Failed to push entity view action to rule engine: {}", entityViewId, DataConstants.ENTITY_CREATED, e);
}
}
public DownlinkMsg convertEntityViewEventToDownlink(EdgeEvent edgeEvent) { public DownlinkMsg convertEntityViewEventToDownlink(EdgeEvent edgeEvent) {
EntityViewId entityViewId = new EntityViewId(edgeEvent.getEntityId()); EntityViewId entityViewId = new EntityViewId(edgeEvent.getEntityId());
@ -67,8 +153,4 @@ public class EntityViewEdgeProcessor extends BaseEdgeProcessor {
} }
return downlinkMsg; return downlinkMsg;
} }
public ListenableFuture<Void> processEntityViewNotification(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) {
return processEntityNotification(tenantId, edgeNotificationMsg);
}
} }

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

@ -15,18 +15,15 @@
*/ */
package org.thingsboard.server.service.edge.rpc.processor.ota; package org.thingsboard.server.service.edge.rpc.processor.ota;
import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.EdgeUtils; import org.thingsboard.server.common.data.EdgeUtils;
import org.thingsboard.server.common.data.OtaPackage; import org.thingsboard.server.common.data.OtaPackage;
import org.thingsboard.server.common.data.edge.EdgeEvent; import org.thingsboard.server.common.data.edge.EdgeEvent;
import org.thingsboard.server.common.data.id.OtaPackageId; import org.thingsboard.server.common.data.id.OtaPackageId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.gen.edge.v1.DownlinkMsg; import org.thingsboard.server.gen.edge.v1.DownlinkMsg;
import org.thingsboard.server.gen.edge.v1.OtaPackageUpdateMsg; import org.thingsboard.server.gen.edge.v1.OtaPackageUpdateMsg;
import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor; import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor;
@ -63,8 +60,4 @@ public class OtaPackageEdgeProcessor extends BaseEdgeProcessor {
} }
return downlinkMsg; return downlinkMsg;
} }
public ListenableFuture<Void> processOtaPackageNotification(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) {
return processEntityNotificationForAllEdges(tenantId, edgeNotificationMsg);
}
} }

6
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/queue/QueueEdgeProcessor.java

@ -15,18 +15,15 @@
*/ */
package org.thingsboard.server.service.edge.rpc.processor.queue; package org.thingsboard.server.service.edge.rpc.processor.queue;
import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.EdgeUtils; import org.thingsboard.server.common.data.EdgeUtils;
import org.thingsboard.server.common.data.edge.EdgeEvent; import org.thingsboard.server.common.data.edge.EdgeEvent;
import org.thingsboard.server.common.data.id.QueueId; import org.thingsboard.server.common.data.id.QueueId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.queue.Queue; import org.thingsboard.server.common.data.queue.Queue;
import org.thingsboard.server.gen.edge.v1.DownlinkMsg; import org.thingsboard.server.gen.edge.v1.DownlinkMsg;
import org.thingsboard.server.gen.edge.v1.QueueUpdateMsg; import org.thingsboard.server.gen.edge.v1.QueueUpdateMsg;
import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor; import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor;
@ -64,7 +61,4 @@ public class QueueEdgeProcessor extends BaseEdgeProcessor {
return downlinkMsg; return downlinkMsg;
} }
public ListenableFuture<Void> processQueueNotification(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) {
return processEntityNotificationForAllEdges(tenantId, edgeNotificationMsg);
}
} }

16
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/relation/BaseRelationProcessor.java

@ -34,8 +34,9 @@ import java.util.UUID;
public abstract class BaseRelationProcessor extends BaseEdgeProcessor { public abstract class BaseRelationProcessor extends BaseEdgeProcessor {
public ListenableFuture<Void> processRelationMsg(TenantId tenantId, RelationUpdateMsg relationUpdateMsg) { public ListenableFuture<Void> processRelationMsg(TenantId tenantId, RelationUpdateMsg relationUpdateMsg) {
log.trace("[{}] processRelationFromEdge [{}]", tenantId, relationUpdateMsg); log.trace("[{}] processRelationMsg [{}]", tenantId, relationUpdateMsg);
try { try {
edgeSynchronizationManager.getSync().set(true);
EntityRelation entityRelation = new EntityRelation(); EntityRelation entityRelation = new EntityRelation();
UUID fromUUID = new UUID(relationUpdateMsg.getFromIdMSB(), relationUpdateMsg.getFromIdLSB()); UUID fromUUID = new UUID(relationUpdateMsg.getFromIdMSB(), relationUpdateMsg.getFromIdLSB());
@ -55,15 +56,15 @@ public abstract class BaseRelationProcessor extends BaseEdgeProcessor {
case ENTITY_UPDATED_RPC_MESSAGE: case ENTITY_UPDATED_RPC_MESSAGE:
if (isEntityExists(tenantId, entityRelation.getTo()) if (isEntityExists(tenantId, entityRelation.getTo())
&& isEntityExists(tenantId, entityRelation.getFrom())) { && isEntityExists(tenantId, entityRelation.getFrom())) {
return Futures.transform(relationService.saveRelationAsync(tenantId, entityRelation), relationService.saveRelation(tenantId, entityRelation);
(result) -> null, dbCallbackExecutorService); break;
} else { } else {
log.warn("Skipping relating update msg because from/to entity doesn't exists on edge, {}", relationUpdateMsg); log.warn("Skipping relating update msg because from/to entity doesn't exists on edge, {}", relationUpdateMsg);
return Futures.immediateFuture(null); break;
} }
case ENTITY_DELETED_RPC_MESSAGE: case ENTITY_DELETED_RPC_MESSAGE:
return Futures.transform(relationService.deleteRelationAsync(tenantId, entityRelation), relationService.deleteRelation(tenantId, entityRelation);
(result) -> null, dbCallbackExecutorService); break;
case UNRECOGNIZED: case UNRECOGNIZED:
default: default:
return handleUnsupportedMsgType(relationUpdateMsg.getMsgType()); return handleUnsupportedMsgType(relationUpdateMsg.getMsgType());
@ -71,6 +72,9 @@ public abstract class BaseRelationProcessor extends BaseEdgeProcessor {
} catch (Exception e) { } catch (Exception e) {
log.error("[{}] Failed to process relation update msg [{}]", tenantId, relationUpdateMsg, e); log.error("[{}] Failed to process relation update msg [{}]", tenantId, relationUpdateMsg, e);
return Futures.immediateFailedFuture(e); return Futures.immediateFailedFuture(e);
} finally {
edgeSynchronizationManager.getSync().remove();
} }
return Futures.immediateFuture(null);
} }
} }

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

@ -15,13 +15,11 @@
*/ */
package org.thingsboard.server.service.edge.rpc.processor.rule; package org.thingsboard.server.service.edge.rpc.processor.rule;
import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.EdgeUtils; import org.thingsboard.server.common.data.EdgeUtils;
import org.thingsboard.server.common.data.edge.EdgeEvent; import org.thingsboard.server.common.data.edge.EdgeEvent;
import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.data.rule.RuleChainMetaData; import org.thingsboard.server.common.data.rule.RuleChainMetaData;
import org.thingsboard.server.gen.edge.v1.DownlinkMsg; import org.thingsboard.server.gen.edge.v1.DownlinkMsg;
@ -29,7 +27,6 @@ import org.thingsboard.server.gen.edge.v1.EdgeVersion;
import org.thingsboard.server.gen.edge.v1.RuleChainMetadataUpdateMsg; import org.thingsboard.server.gen.edge.v1.RuleChainMetadataUpdateMsg;
import org.thingsboard.server.gen.edge.v1.RuleChainUpdateMsg; import org.thingsboard.server.gen.edge.v1.RuleChainUpdateMsg;
import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor; import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor;
@ -93,8 +90,4 @@ public class RuleChainEdgeProcessor extends BaseEdgeProcessor {
} }
return downlinkMsg; return downlinkMsg;
} }
public ListenableFuture<Void> processRuleChainNotification(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) {
return processEntityNotification(tenantId, edgeNotificationMsg);
}
} }

8
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/BaseTelemetryProcessor.java

@ -50,11 +50,11 @@ import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.TenantId; 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.AttributeKvEntry; import org.thingsboard.server.common.data.kv.AttributeKvEntry;
import org.thingsboard.server.common.data.msg.TbMsgType;
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.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.session.SessionMsgType;
import org.thingsboard.server.common.transport.adaptor.JsonConverter; import org.thingsboard.server.common.transport.adaptor.JsonConverter;
import org.thingsboard.server.common.transport.util.JsonUtils; import org.thingsboard.server.common.transport.util.JsonUtils;
import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.model.ModelConstants;
@ -184,7 +184,7 @@ public abstract class BaseTelemetryProcessor extends BaseEdgeProcessor {
JsonObject json = JsonUtils.getJsonObject(tsKv.getKvList()); JsonObject json = JsonUtils.getJsonObject(tsKv.getKvList());
metaData.putValue("ts", tsKv.getTs() + ""); metaData.putValue("ts", tsKv.getTs() + "");
var defaultQueueAndRuleChain = getDefaultQueueNameAndRuleChainId(tenantId, entityId); var defaultQueueAndRuleChain = getDefaultQueueNameAndRuleChainId(tenantId, entityId);
TbMsg tbMsg = TbMsg.newMsg(defaultQueueAndRuleChain.getKey(), SessionMsgType.POST_TELEMETRY_REQUEST.name(), entityId, customerId, metaData, gson.toJson(json), defaultQueueAndRuleChain.getValue(), null); TbMsg tbMsg = TbMsg.newMsg(defaultQueueAndRuleChain.getKey(), TbMsgType.POST_TELEMETRY_REQUEST, entityId, customerId, metaData, gson.toJson(json), defaultQueueAndRuleChain.getValue(), null);
tbClusterService.pushMsgToRuleEngine(tenantId, tbMsg.getOriginator(), tbMsg, new TbQueueCallback() { tbClusterService.pushMsgToRuleEngine(tenantId, tbMsg.getOriginator(), tbMsg, new TbQueueCallback() {
@Override @Override
public void onSuccess(TbQueueMsgMetadata metadata) { public void onSuccess(TbQueueMsgMetadata metadata) {
@ -228,7 +228,7 @@ public abstract class BaseTelemetryProcessor extends BaseEdgeProcessor {
SettableFuture<Void> futureToSet = SettableFuture.create(); SettableFuture<Void> futureToSet = SettableFuture.create();
JsonObject json = JsonUtils.getJsonObject(msg.getKvList()); JsonObject json = JsonUtils.getJsonObject(msg.getKvList());
var defaultQueueAndRuleChain = getDefaultQueueNameAndRuleChainId(tenantId, entityId); var defaultQueueAndRuleChain = getDefaultQueueNameAndRuleChainId(tenantId, entityId);
TbMsg tbMsg = TbMsg.newMsg(defaultQueueAndRuleChain.getKey(), SessionMsgType.POST_ATTRIBUTES_REQUEST.name(), entityId, customerId, metaData, gson.toJson(json), defaultQueueAndRuleChain.getValue(), null); TbMsg tbMsg = TbMsg.newMsg(defaultQueueAndRuleChain.getKey(), TbMsgType.POST_ATTRIBUTES_REQUEST, entityId, customerId, metaData, gson.toJson(json), defaultQueueAndRuleChain.getValue(), null);
tbClusterService.pushMsgToRuleEngine(tenantId, tbMsg.getOriginator(), tbMsg, new TbQueueCallback() { tbClusterService.pushMsgToRuleEngine(tenantId, tbMsg.getOriginator(), tbMsg, new TbQueueCallback() {
@Override @Override
public void onSuccess(TbQueueMsgMetadata metadata) { public void onSuccess(TbQueueMsgMetadata metadata) {
@ -257,7 +257,7 @@ public abstract class BaseTelemetryProcessor extends BaseEdgeProcessor {
@Override @Override
public void onSuccess(@Nullable Void tmp) { public void onSuccess(@Nullable Void tmp) {
var defaultQueueAndRuleChain = getDefaultQueueNameAndRuleChainId(tenantId, entityId); var defaultQueueAndRuleChain = getDefaultQueueNameAndRuleChainId(tenantId, entityId);
TbMsg tbMsg = TbMsg.newMsg(defaultQueueAndRuleChain.getKey(), DataConstants.ATTRIBUTES_UPDATED, entityId, TbMsg tbMsg = TbMsg.newMsg(defaultQueueAndRuleChain.getKey(), TbMsgType.ATTRIBUTES_UPDATED, entityId,
customerId, metaData, gson.toJson(json), defaultQueueAndRuleChain.getValue(), null); customerId, metaData, gson.toJson(json), defaultQueueAndRuleChain.getValue(), null);
tbClusterService.pushMsgToRuleEngine(tenantId, tbMsg.getOriginator(), tbMsg, new TbQueueCallback() { tbClusterService.pushMsgToRuleEngine(tenantId, tbMsg.getOriginator(), tbMsg, new TbQueueCallback() {
@Override @Override

57
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/tenant/TenantEdgeProcessor.java

@ -0,0 +1,57 @@
/**
* Copyright © 2016-2023 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.edge.rpc.processor.tenant;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.EdgeUtils;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.TenantProfile;
import org.thingsboard.server.common.data.edge.EdgeEvent;
import org.thingsboard.server.common.data.edge.EdgeEventActionType;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.gen.edge.v1.DownlinkMsg;
import org.thingsboard.server.gen.edge.v1.TenantProfileUpdateMsg;
import org.thingsboard.server.gen.edge.v1.TenantUpdateMsg;
import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor;
@Component
@Slf4j
@TbCoreComponent
public class TenantEdgeProcessor extends BaseEdgeProcessor {
public DownlinkMsg convertTenantEventToDownlink(EdgeEvent edgeEvent) {
TenantId tenantId = new TenantId(edgeEvent.getEntityId());
DownlinkMsg downlinkMsg = null;
if (EdgeEventActionType.UPDATED.equals(edgeEvent.getAction())) {
Tenant tenant = tenantService.findTenantById(tenantId);
if (tenant != null) {
UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction());
TenantUpdateMsg tenantUpdateMsg = tenantMsgConstructor.constructTenantUpdateMsg(msgType, tenant);
TenantProfile tenantProfile = tenantProfileService.findTenantProfileById(tenantId, tenant.getTenantProfileId());
TenantProfileUpdateMsg tenantProfileUpdateMsg = tenantProfileMsgConstructor.constructTenantProfileUpdateMsg(msgType, tenantProfile);
downlinkMsg = DownlinkMsg.newBuilder()
.setDownlinkMsgId(EdgeUtils.nextPositiveInt())
.addTenantUpdateMsg(tenantUpdateMsg)
.addTenantProfileUpdateMsg(tenantProfileUpdateMsg)
.build();
}
}
return downlinkMsg;
}
}

53
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/tenant/TenantProfileEdgeProcessor.java

@ -0,0 +1,53 @@
/**
* Copyright © 2016-2023 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.edge.rpc.processor.tenant;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.EdgeUtils;
import org.thingsboard.server.common.data.TenantProfile;
import org.thingsboard.server.common.data.edge.EdgeEvent;
import org.thingsboard.server.common.data.edge.EdgeEventActionType;
import org.thingsboard.server.common.data.id.TenantProfileId;
import org.thingsboard.server.gen.edge.v1.DownlinkMsg;
import org.thingsboard.server.gen.edge.v1.TenantProfileUpdateMsg;
import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor;
@Component
@Slf4j
@TbCoreComponent
public class TenantProfileEdgeProcessor extends BaseEdgeProcessor {
public DownlinkMsg convertTenantProfileEventToDownlink(EdgeEvent edgeEvent) {
TenantProfileId tenantProfileId = new TenantProfileId(edgeEvent.getEntityId());
DownlinkMsg downlinkMsg = null;
if (EdgeEventActionType.UPDATED.equals(edgeEvent.getAction())) {
TenantProfile tenantProfile = tenantProfileService.findTenantProfileById(edgeEvent.getTenantId(), tenantProfileId);
if (tenantProfile != null) {
UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction());
TenantProfileUpdateMsg tenantProfileUpdateMsg =
tenantProfileMsgConstructor.constructTenantProfileUpdateMsg(msgType, tenantProfile);
downlinkMsg = DownlinkMsg.newBuilder()
.setDownlinkMsgId(EdgeUtils.nextPositiveInt())
.addTenantProfileUpdateMsg(tenantProfileUpdateMsg)
.build();
}
}
return downlinkMsg;
}
}

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

@ -15,19 +15,16 @@
*/ */
package org.thingsboard.server.service.edge.rpc.processor.user; package org.thingsboard.server.service.edge.rpc.processor.user;
import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.EdgeUtils; import org.thingsboard.server.common.data.EdgeUtils;
import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.edge.EdgeEvent; import org.thingsboard.server.common.data.edge.EdgeEvent;
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.security.UserCredentials; import org.thingsboard.server.common.data.security.UserCredentials;
import org.thingsboard.server.gen.edge.v1.DownlinkMsg; import org.thingsboard.server.gen.edge.v1.DownlinkMsg;
import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
import org.thingsboard.server.gen.edge.v1.UserCredentialsUpdateMsg; import org.thingsboard.server.gen.edge.v1.UserCredentialsUpdateMsg;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor; import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor;
@ -67,11 +64,9 @@ public class UserEdgeProcessor extends BaseEdgeProcessor {
.addUserCredentialsUpdateMsg(userCredentialsUpdateMsg) .addUserCredentialsUpdateMsg(userCredentialsUpdateMsg)
.build(); .build();
} }
break;
} }
return downlinkMsg; return downlinkMsg;
} }
public ListenableFuture<Void> processUserNotification(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) {
return processEntityNotificationForAllEdges(tenantId, edgeNotificationMsg);
}
} }

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

@ -15,18 +15,15 @@
*/ */
package org.thingsboard.server.service.edge.rpc.processor.widget; package org.thingsboard.server.service.edge.rpc.processor.widget;
import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.EdgeUtils; import org.thingsboard.server.common.data.EdgeUtils;
import org.thingsboard.server.common.data.edge.EdgeEvent; import org.thingsboard.server.common.data.edge.EdgeEvent;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.WidgetsBundleId; import org.thingsboard.server.common.data.id.WidgetsBundleId;
import org.thingsboard.server.common.data.widget.WidgetsBundle; import org.thingsboard.server.common.data.widget.WidgetsBundle;
import org.thingsboard.server.gen.edge.v1.DownlinkMsg; import org.thingsboard.server.gen.edge.v1.DownlinkMsg;
import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
import org.thingsboard.server.gen.edge.v1.WidgetsBundleUpdateMsg; import org.thingsboard.server.gen.edge.v1.WidgetsBundleUpdateMsg;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor; import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor;
@ -63,8 +60,4 @@ public class WidgetBundleEdgeProcessor extends BaseEdgeProcessor {
} }
return downlinkMsg; return downlinkMsg;
} }
public ListenableFuture<Void> processWidgetsBundleNotification(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) {
return processEntityNotificationForAllEdges(tenantId, edgeNotificationMsg);
}
} }

6
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/widget/WidgetTypeEdgeProcessor.java

@ -15,18 +15,15 @@
*/ */
package org.thingsboard.server.service.edge.rpc.processor.widget; package org.thingsboard.server.service.edge.rpc.processor.widget;
import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.EdgeUtils; import org.thingsboard.server.common.data.EdgeUtils;
import org.thingsboard.server.common.data.edge.EdgeEvent; import org.thingsboard.server.common.data.edge.EdgeEvent;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.WidgetTypeId; import org.thingsboard.server.common.data.id.WidgetTypeId;
import org.thingsboard.server.common.data.widget.WidgetTypeDetails; import org.thingsboard.server.common.data.widget.WidgetTypeDetails;
import org.thingsboard.server.gen.edge.v1.DownlinkMsg; import org.thingsboard.server.gen.edge.v1.DownlinkMsg;
import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
import org.thingsboard.server.gen.edge.v1.WidgetTypeUpdateMsg; import org.thingsboard.server.gen.edge.v1.WidgetTypeUpdateMsg;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor; import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor;
@ -64,7 +61,4 @@ public class WidgetTypeEdgeProcessor extends BaseEdgeProcessor {
return downlinkMsg; return downlinkMsg;
} }
public ListenableFuture<Void> processWidgetTypeNotification(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) {
return processEntityNotificationForAllEdges(tenantId, edgeNotificationMsg);
}
} }

2
application/src/main/java/org/thingsboard/server/service/edge/rpc/sync/DefaultEdgeRequestsService.java

@ -167,6 +167,8 @@ public class DefaultEdgeRequestsService implements EdgeRequestsService {
attributes.put(attr.getKey(), attr.getDoubleValue().get()); attributes.put(attr.getKey(), attr.getDoubleValue().get());
} else if (attr.getDataType() == DataType.LONG && attr.getLongValue().isPresent()) { } else if (attr.getDataType() == DataType.LONG && attr.getLongValue().isPresent()) {
attributes.put(attr.getKey(), attr.getLongValue().get()); attributes.put(attr.getKey(), attr.getLongValue().get());
} else if (attr.getDataType() == DataType.JSON && attr.getJsonValue().isPresent()) {
attributes.set(attr.getKey(), JacksonUtil.toJsonNode(attr.getJsonValue().get()));
} else { } else {
attributes.put(attr.getKey(), attr.getValueAsString()); attributes.put(attr.getKey(), attr.getValueAsString());
} }

143
application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbNotificationEntityService.java

@ -21,29 +21,21 @@ import org.springframework.stereotype.Service;
import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.rule.engine.api.msg.DeviceCredentialsUpdateNotificationMsg; import org.thingsboard.rule.engine.api.msg.DeviceCredentialsUpdateNotificationMsg;
import org.thingsboard.server.cluster.TbClusterService; import org.thingsboard.server.cluster.TbClusterService;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.HasName; import org.thingsboard.server.common.data.HasName;
import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.alarm.Alarm;
import org.thingsboard.server.common.data.alarm.AlarmComment;
import org.thingsboard.server.common.data.alarm.AlarmInfo;
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;
import org.thingsboard.server.common.data.edge.EdgeEventType;
import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.EdgeId;
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.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.msg.TbMsgType;
import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.data.rule.RuleChainType;
import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbMsgDataType; import org.thingsboard.server.common.msg.TbMsgDataType;
@ -51,8 +43,6 @@ import org.thingsboard.server.common.msg.TbMsgMetaData;
import org.thingsboard.server.service.action.EntityActionService; import org.thingsboard.server.service.action.EntityActionService;
import org.thingsboard.server.service.gateway_device.GatewayNotificationsService; import org.thingsboard.server.service.gateway_device.GatewayNotificationsService;
import java.util.List;
@Slf4j @Slf4j
@Service @Service
@RequiredArgsConstructor @RequiredArgsConstructor
@ -98,54 +88,6 @@ public class DefaultTbNotificationEntityService implements TbNotificationEntityS
} }
} }
@Override
public <E extends HasName, I extends EntityId> void notifyDeleteEntity(TenantId tenantId, I entityId, E entity,
CustomerId customerId, ActionType actionType,
List<EdgeId> relatedEdgeIds,
User user, Object... additionalInfo) {
logEntityAction(tenantId, entityId, entity, customerId, actionType, user, additionalInfo);
sendDeleteNotificationMsg(tenantId, entityId, relatedEdgeIds, null);
}
@Override
public void notifyDeleteAlarm(TenantId tenantId, Alarm alarm, EntityId originatorId, CustomerId customerId,
List<EdgeId> relatedEdgeIds, User user, String body, Object... additionalInfo) {
logEntityAction(tenantId, originatorId, alarm, customerId, ActionType.DELETED, user, additionalInfo);
sendAlarmDeleteNotificationMsg(tenantId, alarm, relatedEdgeIds, body);
}
@Override
public void notifyDeleteRuleChain(TenantId tenantId, RuleChain ruleChain, List<EdgeId> relatedEdgeIds, User user) {
RuleChainId ruleChainId = ruleChain.getId();
logEntityAction(tenantId, ruleChainId, ruleChain, null, ActionType.DELETED, user, null, ruleChainId.toString());
if (RuleChainType.EDGE.equals(ruleChain.getType())) {
sendDeleteNotificationMsg(tenantId, ruleChainId, relatedEdgeIds, null);
}
}
@Override
public <I extends EntityId> void notifySendMsgToEdgeService(TenantId tenantId, I entityId, EdgeEventActionType edgeEventActionType) {
sendEntityNotificationMsg(tenantId, entityId, edgeEventActionType);
}
@Override
public <E extends HasName, I extends EntityId> void notifyAssignOrUnassignEntityToCustomer(TenantId tenantId, I entityId,
CustomerId customerId, E entity,
ActionType actionType,
User user, Object... additionalInfo) {
logEntityAction(tenantId, entityId, entity, customerId, actionType, user, additionalInfo);
sendEntityNotificationMsg(tenantId, entityId, edgeTypeByActionType(actionType), JacksonUtil.toString(customerId));
}
@Override
public <E extends HasName, I extends EntityId> void notifyAssignOrUnassignEntityToEdge(TenantId tenantId, I entityId,
CustomerId customerId, EdgeId edgeId,
E entity, ActionType actionType,
User user, Object... additionalInfo) {
logEntityAction(tenantId, entityId, entity, customerId, actionType, user, additionalInfo);
sendEntityAssignToEdgeNotificationMsg(tenantId, edgeId, entityId, edgeTypeByActionType(actionType));
}
@Override @Override
public void notifyCreateOrUpdateTenant(Tenant tenant, ComponentLifecycleEvent event) { public void notifyCreateOrUpdateTenant(Tenant tenant, ComponentLifecycleEvent event) {
tbClusterService.onTenantChange(tenant, null); tbClusterService.onTenantChange(tenant, null);
@ -168,18 +110,16 @@ public class DefaultTbNotificationEntityService implements TbNotificationEntityS
@Override @Override
public void notifyDeleteDevice(TenantId tenantId, DeviceId deviceId, CustomerId customerId, Device device, public void notifyDeleteDevice(TenantId tenantId, DeviceId deviceId, CustomerId customerId, Device device,
List<EdgeId> relatedEdgeIds, User user, Object... additionalInfo) { User user, Object... additionalInfo) {
gatewayNotificationsService.onDeviceDeleted(device); gatewayNotificationsService.onDeviceDeleted(device);
tbClusterService.onDeviceDeleted(device, null); tbClusterService.onDeviceDeleted(device, null);
logEntityAction(tenantId, deviceId, device, customerId, ActionType.DELETED, user, additionalInfo);
notifyDeleteEntity(tenantId, deviceId, device, customerId, ActionType.DELETED, relatedEdgeIds, user, additionalInfo);
} }
@Override @Override
public void notifyUpdateDeviceCredentials(TenantId tenantId, DeviceId deviceId, CustomerId customerId, Device device, public void notifyUpdateDeviceCredentials(TenantId tenantId, DeviceId deviceId, CustomerId customerId, Device device,
DeviceCredentials deviceCredentials, User user) { DeviceCredentials deviceCredentials, User user) {
tbClusterService.pushMsgToCore(new DeviceCredentialsUpdateNotificationMsg(tenantId, deviceCredentials.getDeviceId(), deviceCredentials), null); tbClusterService.pushMsgToCore(new DeviceCredentialsUpdateNotificationMsg(tenantId, deviceCredentials.getDeviceId(), deviceCredentials), null);
sendEntityNotificationMsg(tenantId, deviceId, EdgeEventActionType.CREDENTIALS_UPDATED);
logEntityAction(tenantId, deviceId, device, customerId, ActionType.CREDENTIALS_UPDATED, user, deviceCredentials); logEntityAction(tenantId, deviceId, device, customerId, ActionType.CREDENTIALS_UPDATED, user, deviceCredentials);
} }
@ -190,16 +130,6 @@ public class DefaultTbNotificationEntityService implements TbNotificationEntityS
pushAssignedFromNotification(tenant, newTenantId, device); pushAssignedFromNotification(tenant, newTenantId, device);
} }
@Override
public <E extends HasName, I extends EntityId> void notifyCreateOrUpdateEntity(TenantId tenantId, I entityId, E entity,
CustomerId customerId, ActionType actionType,
User user, Object... additionalInfo) {
logEntityAction(tenantId, entityId, entity, customerId, actionType, user, additionalInfo);
if (actionType == ActionType.UPDATED) {
sendEntityNotificationMsg(tenantId, entityId, EdgeEventActionType.UPDATED);
}
}
@Override @Override
public void notifyCreateOrUpdateOrDeleteEdge(TenantId tenantId, EdgeId edgeId, CustomerId customerId, Edge edge, public void notifyCreateOrUpdateOrDeleteEdge(TenantId tenantId, EdgeId edgeId, CustomerId customerId, Edge edge,
ActionType actionType, User user, Object... additionalInfo) { ActionType actionType, User user, Object... additionalInfo) {
@ -222,71 +152,16 @@ public class DefaultTbNotificationEntityService implements TbNotificationEntityS
} }
@Override @Override
public void notifyCreateOrUpdateAlarm(AlarmInfo alarm, ActionType actionType, User user, Object... additionalInfo) { public void logEntityRelationAction(TenantId tenantId, CustomerId customerId, EntityRelation relation, User user,
logEntityAction(alarm.getTenantId(), alarm.getOriginator(), alarm, alarm.getCustomerId(), actionType, user, additionalInfo); ActionType actionType, Exception e, Object... additionalInfo) {
sendEntityNotificationMsg(alarm.getTenantId(), alarm.getId(), edgeTypeByActionType(actionType)); logEntityAction(tenantId, relation.getFrom(), null, customerId, actionType, user, e, additionalInfo);
} logEntityAction(tenantId, relation.getTo(), null, customerId, actionType, user, e, additionalInfo);
@Override
public void notifyAlarmComment(Alarm alarm, AlarmComment alarmComment, ActionType actionType, User user) {
logEntityAction(alarm.getTenantId(), alarm.getId(), alarm, alarm.getCustomerId(), actionType, user, alarmComment);
}
@Override
public <E extends HasName, I extends EntityId> void notifyCreateOrUpdateOrDelete(TenantId tenantId, CustomerId customerId,
I entityId, E entity, User user,
ActionType actionType, boolean sendNotifyMsgToEdge, Exception e,
Object... additionalInfo) {
logEntityAction(tenantId, entityId, entity, customerId, actionType, user, e, additionalInfo);
if (sendNotifyMsgToEdge) {
sendEntityNotificationMsg(tenantId, entityId, edgeTypeByActionType(actionType));
}
}
@Override
public void notifyRelation(TenantId tenantId, CustomerId customerId, EntityRelation relation, User user,
ActionType actionType, Object... additionalInfo) {
logEntityAction(tenantId, relation.getFrom(), null, customerId, actionType, user, additionalInfo);
logEntityAction(tenantId, relation.getTo(), null, customerId, actionType, user, additionalInfo);
if (!EntityType.EDGE.equals(relation.getFrom().getEntityType()) && !EntityType.EDGE.equals(relation.getTo().getEntityType())) {
sendNotificationMsgToEdge(tenantId, null, null, JacksonUtil.toString(relation),
EdgeEventType.RELATION, edgeTypeByActionType(actionType));
}
}
private void sendEntityNotificationMsg(TenantId tenantId, EntityId entityId, EdgeEventActionType action) {
sendEntityNotificationMsg(tenantId, entityId, action, null);
}
private void sendEntityNotificationMsg(TenantId tenantId, EntityId entityId, EdgeEventActionType action, String body) {
sendNotificationMsgToEdge(tenantId, null, entityId, body, null, action);
}
private void sendAlarmDeleteNotificationMsg(TenantId tenantId, Alarm alarm, List<EdgeId> edgeIds, String body) {
sendDeleteNotificationMsg(tenantId, alarm.getId(), edgeIds, body);
}
private void sendDeleteNotificationMsg(TenantId tenantId, EntityId entityId, List<EdgeId> edgeIds, String body) {
if (edgeIds != null && !edgeIds.isEmpty()) {
for (EdgeId edgeId : edgeIds) {
sendNotificationMsgToEdge(tenantId, edgeId, entityId, body, null, EdgeEventActionType.DELETED);
}
}
}
private void sendEntityAssignToEdgeNotificationMsg(TenantId tenantId, EdgeId edgeId, EntityId entityId, EdgeEventActionType action) {
sendNotificationMsgToEdge(tenantId, edgeId, entityId, null, null, action);
}
private void sendNotificationMsgToEdge(TenantId tenantId, EdgeId edgeId, EntityId entityId, String body,
EdgeEventType type, EdgeEventActionType action) {
tbClusterService.sendNotificationMsgToEdge(tenantId, edgeId, entityId, body, type, action);
} }
private void pushAssignedFromNotification(Tenant currentTenant, TenantId newTenantId, Device assignedDevice) { private void pushAssignedFromNotification(Tenant currentTenant, TenantId newTenantId, Device assignedDevice) {
String data = JacksonUtil.toString(JacksonUtil.valueToTree(assignedDevice)); String data = JacksonUtil.toString(JacksonUtil.valueToTree(assignedDevice));
if (data != null) { if (data != null) {
TbMsg tbMsg = TbMsg.newMsg(DataConstants.ENTITY_ASSIGNED_FROM_TENANT, assignedDevice.getId(), TbMsg tbMsg = TbMsg.newMsg(TbMsgType.ENTITY_ASSIGNED_FROM_TENANT, assignedDevice.getId(),
assignedDevice.getCustomerId(), getMetaDataForAssignedFrom(currentTenant), TbMsgDataType.JSON, data); assignedDevice.getCustomerId(), getMetaDataForAssignedFrom(currentTenant), TbMsgDataType.JSON, data);
tbClusterService.pushMsgToRuleEngine(newTenantId, assignedDevice.getId(), tbMsg, null); tbClusterService.pushMsgToRuleEngine(newTenantId, assignedDevice.getId(), tbMsg, null);
} }
@ -327,6 +202,8 @@ public class DefaultTbNotificationEntityService implements TbNotificationEntityS
return EdgeEventActionType.ASSIGNED_TO_EDGE; return EdgeEventActionType.ASSIGNED_TO_EDGE;
case UNASSIGNED_FROM_EDGE: case UNASSIGNED_FROM_EDGE:
return EdgeEventActionType.UNASSIGNED_FROM_EDGE; return EdgeEventActionType.UNASSIGNED_FROM_EDGE;
case CREDENTIALS_UPDATED:
return EdgeEventActionType.CREDENTIALS_UPDATED;
default: default:
return null; return null;
} }

50
application/src/main/java/org/thingsboard/server/service/entitiy/TbNotificationEntityService.java

@ -19,12 +19,8 @@ import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.HasName; import org.thingsboard.server.common.data.HasName;
import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.alarm.Alarm;
import org.thingsboard.server.common.data.alarm.AlarmComment;
import org.thingsboard.server.common.data.alarm.AlarmInfo;
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.id.CustomerId; import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.EdgeId;
@ -32,11 +28,8 @@ import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.common.data.security.DeviceCredentials;
import java.util.List;
public interface TbNotificationEntityService { public interface TbNotificationEntityService {
<I extends EntityId> void logEntityAction(TenantId tenantId, I entityId, ActionType actionType, User user, <I extends EntityId> void logEntityAction(TenantId tenantId, I entityId, ActionType actionType, User user,
@ -55,33 +48,6 @@ public interface TbNotificationEntityService {
ActionType actionType, User user, Exception e, ActionType actionType, User user, Exception e,
Object... additionalInfo); Object... additionalInfo);
<E extends HasName, I extends EntityId> void notifyCreateOrUpdateEntity(TenantId tenantId, I entityId, E entity,
CustomerId customerId, ActionType actionType,
User user, Object... additionalInfo);
<E extends HasName, I extends EntityId> void notifyDeleteEntity(TenantId tenantId, I entityId, E entity,
CustomerId customerId, ActionType actionType,
List<EdgeId> relatedEdgeIds,
User user, Object... additionalInfo);
void notifyDeleteAlarm(TenantId tenantId, Alarm alarm, EntityId originatorId, CustomerId customerId,
List<EdgeId> relatedEdgeIds, User user, String body, Object... additionalInfo);
void notifyDeleteRuleChain(TenantId tenantId, RuleChain ruleChain,
List<EdgeId> relatedEdgeIds, User user);
<I extends EntityId> void notifySendMsgToEdgeService(TenantId tenantId, I entityId, EdgeEventActionType edgeEventActionType);
<E extends HasName, I extends EntityId> void notifyAssignOrUnassignEntityToCustomer(TenantId tenantId, I entityId,
CustomerId customerId, E entity,
ActionType actionType,
User user, Object... additionalInfo);
<E extends HasName, I extends EntityId> void notifyAssignOrUnassignEntityToEdge(TenantId tenantId, I entityId,
CustomerId customerId, EdgeId edgeId,
E entity, ActionType actionType,
User user, Object... additionalInfo);
void notifyCreateOrUpdateTenant(Tenant tenant, ComponentLifecycleEvent event); void notifyCreateOrUpdateTenant(Tenant tenant, ComponentLifecycleEvent event);
void notifyDeleteTenant(Tenant tenant); void notifyDeleteTenant(Tenant tenant);
@ -90,7 +56,7 @@ public interface TbNotificationEntityService {
Device oldDevice, ActionType actionType, User user, Object... additionalInfo); Device oldDevice, ActionType actionType, User user, Object... additionalInfo);
void notifyDeleteDevice(TenantId tenantId, DeviceId deviceId, CustomerId customerId, Device device, void notifyDeleteDevice(TenantId tenantId, DeviceId deviceId, CustomerId customerId, Device device,
List<EdgeId> relatedEdgeIds, User user, Object... additionalInfo); User user, Object... additionalInfo);
void notifyUpdateDeviceCredentials(TenantId tenantId, DeviceId deviceId, CustomerId customerId, Device device, void notifyUpdateDeviceCredentials(TenantId tenantId, DeviceId deviceId, CustomerId customerId, Device device,
DeviceCredentials deviceCredentials, User user); DeviceCredentials deviceCredentials, User user);
@ -101,16 +67,6 @@ public interface TbNotificationEntityService {
void notifyCreateOrUpdateOrDeleteEdge(TenantId tenantId, EdgeId edgeId, CustomerId customerId, Edge edge, ActionType actionType, void notifyCreateOrUpdateOrDeleteEdge(TenantId tenantId, EdgeId edgeId, CustomerId customerId, Edge edge, ActionType actionType,
User user, Object... additionalInfo); User user, Object... additionalInfo);
void notifyCreateOrUpdateAlarm(AlarmInfo alarm, ActionType actionType, User user, Object... additionalInfo); void logEntityRelationAction(TenantId tenantId, CustomerId customerId, EntityRelation relation, User user,
ActionType actionType, Exception e, Object... additionalInfo);
void notifyAlarmComment(Alarm alarm, AlarmComment alarmComment, ActionType actionType, User user);
<E extends HasName, I extends EntityId> void notifyCreateOrUpdateOrDelete(TenantId tenantId, CustomerId customerId,
I entityId, E entity, User user,
ActionType actionType, boolean sendNotifyMsgToEdge,
Exception e, Object... additionalInfo);
void notifyRelation(TenantId tenantId, CustomerId customerId, EntityRelation relation, User user,
ActionType actionType, Object... additionalInfo);
} }

5
application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmCommentService.java

@ -45,7 +45,8 @@ public class DefaultTbAlarmCommentService extends AbstractTbEntityService implem
} }
try { try {
AlarmComment savedAlarmComment = checkNotNull(alarmCommentService.createOrUpdateAlarmComment(alarm.getTenantId(), alarmComment)); AlarmComment savedAlarmComment = checkNotNull(alarmCommentService.createOrUpdateAlarmComment(alarm.getTenantId(), alarmComment));
notificationEntityService.notifyAlarmComment(alarm, savedAlarmComment, actionType, user); notificationEntityService.logEntityAction(alarm.getTenantId(), alarm.getId(), alarm, alarm.getCustomerId(), actionType, user, savedAlarmComment);
return savedAlarmComment; return savedAlarmComment;
} catch (Exception e) { } catch (Exception e) {
notificationEntityService.logEntityAction(alarm.getTenantId(), emptyId(EntityType.ALARM), alarm, actionType, user, e, alarmComment); notificationEntityService.logEntityAction(alarm.getTenantId(), emptyId(EntityType.ALARM), alarm, actionType, user, e, alarmComment);
@ -62,7 +63,7 @@ public class DefaultTbAlarmCommentService extends AbstractTbEntityService implem
String.format("User %s deleted his comment", String.format("User %s deleted his comment",
(user.getFirstName() == null || user.getLastName() == null) ? user.getName() : user.getFirstName() + " " + user.getLastName()))); (user.getFirstName() == null || user.getLastName() == null) ? user.getName() : user.getFirstName() + " " + user.getLastName())));
AlarmComment savedAlarmComment = checkNotNull(alarmCommentService.saveAlarmComment(alarm.getTenantId(), alarmComment)); AlarmComment savedAlarmComment = checkNotNull(alarmCommentService.saveAlarmComment(alarm.getTenantId(), alarmComment));
notificationEntityService.notifyAlarmComment(alarm, savedAlarmComment, ActionType.DELETED_COMMENT, user); notificationEntityService.logEntityAction(alarm.getTenantId(), alarm.getId(), alarm, alarm.getCustomerId(), ActionType.DELETED_COMMENT, user, savedAlarmComment);
} else { } else {
throw new ThingsboardException("System comment could not be deleted", ThingsboardErrorCode.BAD_REQUEST_PARAMS); throw new ThingsboardException("System comment could not be deleted", ThingsboardErrorCode.BAD_REQUEST_PARAMS);
} }

71
application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmService.java

@ -29,16 +29,20 @@ import org.thingsboard.server.common.data.alarm.AlarmComment;
import org.thingsboard.server.common.data.alarm.AlarmCommentType; import org.thingsboard.server.common.data.alarm.AlarmCommentType;
import org.thingsboard.server.common.data.alarm.AlarmCreateOrUpdateActiveRequest; import org.thingsboard.server.common.data.alarm.AlarmCreateOrUpdateActiveRequest;
import org.thingsboard.server.common.data.alarm.AlarmInfo; import org.thingsboard.server.common.data.alarm.AlarmInfo;
import org.thingsboard.server.common.data.alarm.AlarmQueryV2;
import org.thingsboard.server.common.data.alarm.AlarmUpdateRequest; import org.thingsboard.server.common.data.alarm.AlarmUpdateRequest;
import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.TenantId; 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.page.TimePageLink;
import org.thingsboard.server.service.entitiy.AbstractTbEntityService; import org.thingsboard.server.service.entitiy.AbstractTbEntityService;
import java.util.List; import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
@Service @Service
@AllArgsConstructor @AllArgsConstructor
@ -62,10 +66,6 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb
if (!result.isSuccessful()) { if (!result.isSuccessful()) {
throw new ThingsboardException(ThingsboardErrorCode.ITEM_NOT_FOUND); throw new ThingsboardException(ThingsboardErrorCode.ITEM_NOT_FOUND);
} }
actionType = result.isCreated() ? ActionType.ADDED : ActionType.UPDATED;
if (result.isModified()) {
notificationEntityService.notifyCreateOrUpdateAlarm(result.getAlarm(), actionType, user);
}
AlarmInfo resultAlarm = result.getAlarm(); AlarmInfo resultAlarm = result.getAlarm();
if (alarm.isAcknowledged() && !resultAlarm.isAcknowledged()) { if (alarm.isAcknowledged() && !resultAlarm.isAcknowledged()) {
resultAlarm = ack(resultAlarm, alarm.getAckTs(), user); resultAlarm = ack(resultAlarm, alarm.getAckTs(), user);
@ -80,6 +80,10 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb
} else if (newAssignee == null && curAssignee != null) { } else if (newAssignee == null && curAssignee != null) {
resultAlarm = unassign(alarm, alarm.getAssignTs(), user); resultAlarm = unassign(alarm, alarm.getAssignTs(), user);
} }
if (result.isModified()) {
notificationEntityService.logEntityAction(tenantId, alarm.getOriginator(), resultAlarm,
resultAlarm.getCustomerId(), actionType, user);
}
return new Alarm(resultAlarm); return new Alarm(resultAlarm);
} catch (Exception e) { } catch (Exception e) {
notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.ALARM), alarm, actionType, user, e); notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.ALARM), alarm, actionType, user, e);
@ -98,6 +102,7 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb
if (!result.isSuccessful()) { if (!result.isSuccessful()) {
throw new ThingsboardException(ThingsboardErrorCode.ITEM_NOT_FOUND); throw new ThingsboardException(ThingsboardErrorCode.ITEM_NOT_FOUND);
} }
AlarmInfo alarmInfo = result.getAlarm();
if (result.isModified()) { if (result.isModified()) {
AlarmComment alarmComment = AlarmComment.builder() AlarmComment alarmComment = AlarmComment.builder()
.alarmId(alarm.getId()) .alarmId(alarm.getId())
@ -112,11 +117,12 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb
} catch (ThingsboardException e) { } catch (ThingsboardException e) {
log.error("Failed to save alarm comment", e); log.error("Failed to save alarm comment", e);
} }
notificationEntityService.notifyCreateOrUpdateAlarm(result.getAlarm(), ActionType.ALARM_ACK, user); notificationEntityService.logEntityAction(alarm.getTenantId(), alarm.getOriginator(), alarmInfo,
alarmInfo.getCustomerId(), ActionType.ALARM_ACK, user);
} else { } else {
throw new ThingsboardException("Alarm was already acknowledged!", ThingsboardErrorCode.BAD_REQUEST_PARAMS); throw new ThingsboardException("Alarm was already acknowledged!", ThingsboardErrorCode.BAD_REQUEST_PARAMS);
} }
return result.getAlarm(); return alarmInfo;
} }
@Override @Override
@ -130,6 +136,7 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb
if (!result.isSuccessful()) { if (!result.isSuccessful()) {
throw new ThingsboardException(ThingsboardErrorCode.ITEM_NOT_FOUND); throw new ThingsboardException(ThingsboardErrorCode.ITEM_NOT_FOUND);
} }
AlarmInfo alarmInfo = result.getAlarm();
if (result.isCleared()) { if (result.isCleared()) {
AlarmComment alarmComment = AlarmComment.builder() AlarmComment alarmComment = AlarmComment.builder()
.alarmId(alarm.getId()) .alarmId(alarm.getId())
@ -144,11 +151,12 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb
} catch (ThingsboardException e) { } catch (ThingsboardException e) {
log.error("Failed to save alarm comment", e); log.error("Failed to save alarm comment", e);
} }
notificationEntityService.notifyCreateOrUpdateAlarm(result.getAlarm(), ActionType.ALARM_CLEAR, user); notificationEntityService.logEntityAction(alarm.getTenantId(), alarm.getOriginator(), alarmInfo,
alarmInfo.getCustomerId(), ActionType.ALARM_CLEAR, user);
} else { } else {
throw new ThingsboardException("Alarm was already cleared!", ThingsboardErrorCode.BAD_REQUEST_PARAMS); throw new ThingsboardException("Alarm was already cleared!", ThingsboardErrorCode.BAD_REQUEST_PARAMS);
} }
return result.getAlarm(); return alarmInfo;
} }
@Override @Override
@ -175,7 +183,8 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb
} catch (ThingsboardException e) { } catch (ThingsboardException e) {
log.error("Failed to save alarm comment", e); log.error("Failed to save alarm comment", e);
} }
notificationEntityService.notifyCreateOrUpdateAlarm(result.getAlarm(), ActionType.ALARM_ASSIGNED, user); notificationEntityService.logEntityAction(alarm.getTenantId(), alarm.getOriginator(), alarmInfo,
alarmInfo.getCustomerId(), ActionType.ALARM_ASSIGNED, user);
} else { } else {
throw new ThingsboardException("Alarm was already assigned to this user!", ThingsboardErrorCode.BAD_REQUEST_PARAMS); throw new ThingsboardException("Alarm was already assigned to this user!", ThingsboardErrorCode.BAD_REQUEST_PARAMS);
} }
@ -203,19 +212,53 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb
} catch (ThingsboardException e) { } catch (ThingsboardException e) {
log.error("Failed to save alarm comment", e); log.error("Failed to save alarm comment", e);
} }
notificationEntityService.notifyCreateOrUpdateAlarm(result.getAlarm(), ActionType.ALARM_UNASSIGNED, user); notificationEntityService.logEntityAction(alarm.getTenantId(), alarm.getOriginator(), alarmInfo,
alarmInfo.getCustomerId(), ActionType.ALARM_UNASSIGNED, user);
} else { } else {
throw new ThingsboardException("Alarm was already unassigned!", ThingsboardErrorCode.BAD_REQUEST_PARAMS); throw new ThingsboardException("Alarm was already unassigned!", ThingsboardErrorCode.BAD_REQUEST_PARAMS);
} }
return alarmInfo; return alarmInfo;
} }
@Override
public void unassignUserAlarms(TenantId tenantId, User user, long unassignTs) {
AlarmQueryV2 alarmQuery = AlarmQueryV2.builder().assigneeId(user.getId()).pageLink(new TimePageLink(Integer.MAX_VALUE)).build();
try {
List<AlarmInfo> alarms = alarmService.findAlarmsV2(tenantId, alarmQuery).get(30, TimeUnit.SECONDS).getData();
for (AlarmInfo alarm : alarms) {
AlarmApiCallResult result = alarmSubscriptionService.unassignAlarm(tenantId, alarm.getId(), getOrDefault(unassignTs));
if (!result.isSuccessful()) {
continue;
}
if (result.isModified()) {
AlarmComment alarmComment = AlarmComment.builder()
.alarmId(alarm.getId())
.type(AlarmCommentType.SYSTEM)
.comment(JacksonUtil.newObjectNode().put("text", String.format("Alarm was unassigned because user %s - was deleted",
(user.getFirstName() == null || user.getLastName() == null) ? user.getName() : user.getFirstName() + " " + user.getLastName()))
.put("userId", user.getId().toString())
.put("subtype", "ASSIGN"))
.build();
try {
alarmCommentService.saveAlarmComment(alarm, alarmComment, user);
} catch (ThingsboardException e) {
log.error("Failed to save alarm comment", e);
}
notificationEntityService.logEntityAction(alarm.getTenantId(), alarm.getOriginator(), result.getAlarm(),
alarm.getCustomerId(), ActionType.ALARM_UNASSIGNED, user);
}
}
} catch (InterruptedException | ExecutionException | TimeoutException e) {
throw new RuntimeException(e);
}
}
@Override @Override
public Boolean delete(Alarm alarm, User user) { public Boolean delete(Alarm alarm, User user) {
TenantId tenantId = alarm.getTenantId(); TenantId tenantId = alarm.getTenantId();
List<EdgeId> relatedEdgeIds = edgeService.findAllRelatedEdgeIds(tenantId, alarm.getOriginator()); notificationEntityService.logEntityAction(tenantId, alarm.getOriginator(), alarm, alarm.getCustomerId(),
notificationEntityService.notifyDeleteAlarm(tenantId, alarm, alarm.getOriginator(), alarm.getCustomerId(), ActionType.DELETED, user);
relatedEdgeIds, user, JacksonUtil.toString(alarm));
return alarmSubscriptionService.deleteAlarm(tenantId, alarm.getId()); return alarmSubscriptionService.deleteAlarm(tenantId, alarm.getId());
} }

3
application/src/main/java/org/thingsboard/server/service/entitiy/alarm/TbAlarmService.java

@ -19,6 +19,7 @@ import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.Alarm;
import org.thingsboard.server.common.data.alarm.AlarmInfo; import org.thingsboard.server.common.data.alarm.AlarmInfo;
import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.id.UserId;
public interface TbAlarmService { public interface TbAlarmService {
@ -37,5 +38,7 @@ public interface TbAlarmService {
AlarmInfo unassign(Alarm alarm, long unassignTs, User user) throws ThingsboardException; AlarmInfo unassign(Alarm alarm, long unassignTs, User user) throws ThingsboardException;
void unassignUserAlarms(TenantId tenantId, User user, long unassignTs);
Boolean delete(Alarm alarm, User user); Boolean delete(Alarm alarm, User user);
} }

36
application/src/main/java/org/thingsboard/server/service/entitiy/asset/DefaultTbAssetService.java

@ -36,8 +36,6 @@ import org.thingsboard.server.dao.asset.AssetService;
import org.thingsboard.server.service.entitiy.AbstractTbEntityService; import org.thingsboard.server.service.entitiy.AbstractTbEntityService;
import org.thingsboard.server.service.profile.TbAssetProfileCache; import org.thingsboard.server.service.profile.TbAssetProfileCache;
import java.util.List;
import static org.thingsboard.server.dao.asset.BaseAssetService.TB_SERVICE_QUEUE; import static org.thingsboard.server.dao.asset.BaseAssetService.TB_SERVICE_QUEUE;
@Service @Service
@ -62,8 +60,8 @@ public class DefaultTbAssetService extends AbstractTbEntityService implements Tb
} }
Asset savedAsset = checkNotNull(assetService.saveAsset(asset)); Asset savedAsset = checkNotNull(assetService.saveAsset(asset));
autoCommit(user, savedAsset.getId()); autoCommit(user, savedAsset.getId());
notificationEntityService.notifyCreateOrUpdateEntity(tenantId, savedAsset.getId(), savedAsset, notificationEntityService.logEntityAction(tenantId, savedAsset.getId(), savedAsset, asset.getCustomerId(),
asset.getCustomerId(), actionType, user); actionType, user);
tbClusterService.broadcastEntityStateChangeEvent(tenantId, savedAsset.getId(), tbClusterService.broadcastEntityStateChangeEvent(tenantId, savedAsset.getId(),
asset.getId() == null ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED); asset.getId() == null ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED);
return savedAsset; return savedAsset;
@ -75,17 +73,16 @@ public class DefaultTbAssetService extends AbstractTbEntityService implements Tb
@Override @Override
public ListenableFuture<Void> delete(Asset asset, User user) { public ListenableFuture<Void> delete(Asset asset, User user) {
ActionType actionType = ActionType.DELETED;
TenantId tenantId = asset.getTenantId(); TenantId tenantId = asset.getTenantId();
AssetId assetId = asset.getId(); AssetId assetId = asset.getId();
try { try {
List<EdgeId> relatedEdgeIds = edgeService.findAllRelatedEdgeIds(tenantId, assetId);
assetService.deleteAsset(tenantId, assetId); assetService.deleteAsset(tenantId, assetId);
notificationEntityService.notifyDeleteEntity(tenantId, assetId, asset, asset.getCustomerId(), notificationEntityService.logEntityAction(tenantId, assetId, asset, asset.getCustomerId(), actionType, user, assetId.toString());
ActionType.DELETED, relatedEdgeIds, user, assetId.toString());
tbClusterService.broadcastEntityStateChangeEvent(tenantId, assetId, ComponentLifecycleEvent.DELETED); tbClusterService.broadcastEntityStateChangeEvent(tenantId, assetId, ComponentLifecycleEvent.DELETED);
return removeAlarmsByEntityId(tenantId, assetId); return removeAlarmsByEntityId(tenantId, assetId);
} catch (Exception e) { } catch (Exception e) {
notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.ASSET), ActionType.DELETED, user, e, notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.ASSET), actionType, user, e,
assetId.toString()); assetId.toString());
throw e; throw e;
} }
@ -97,8 +94,8 @@ public class DefaultTbAssetService extends AbstractTbEntityService implements Tb
CustomerId customerId = customer.getId(); CustomerId customerId = customer.getId();
try { try {
Asset savedAsset = checkNotNull(assetService.assignAssetToCustomer(tenantId, assetId, customerId)); Asset savedAsset = checkNotNull(assetService.assignAssetToCustomer(tenantId, assetId, customerId));
notificationEntityService.notifyAssignOrUnassignEntityToCustomer(tenantId, assetId, customerId, savedAsset, notificationEntityService.logEntityAction(tenantId, assetId, savedAsset, customerId, actionType, user,
actionType, user, assetId.toString(), customerId.toString(), customer.getName()); assetId.toString(), customerId.toString(), customer.getName());
return savedAsset; return savedAsset;
} catch (Exception e) { } catch (Exception e) {
@ -114,8 +111,8 @@ public class DefaultTbAssetService extends AbstractTbEntityService implements Tb
try { try {
Asset savedAsset = checkNotNull(assetService.unassignAssetFromCustomer(tenantId, assetId)); Asset savedAsset = checkNotNull(assetService.unassignAssetFromCustomer(tenantId, assetId));
CustomerId customerId = customer.getId(); CustomerId customerId = customer.getId();
notificationEntityService.notifyAssignOrUnassignEntityToCustomer(tenantId, assetId, customerId, savedAsset, notificationEntityService.logEntityAction(tenantId, assetId, savedAsset, customerId, actionType, user,
actionType, user, assetId.toString(), customerId.toString(), customer.getName()); assetId.toString(), customerId.toString(), customer.getName());
return savedAsset; return savedAsset;
} catch (Exception e) { } catch (Exception e) {
@ -130,8 +127,9 @@ public class DefaultTbAssetService extends AbstractTbEntityService implements Tb
try { try {
Customer publicCustomer = customerService.findOrCreatePublicCustomer(tenantId); Customer publicCustomer = customerService.findOrCreatePublicCustomer(tenantId);
Asset savedAsset = checkNotNull(assetService.assignAssetToCustomer(tenantId, assetId, publicCustomer.getId())); Asset savedAsset = checkNotNull(assetService.assignAssetToCustomer(tenantId, assetId, publicCustomer.getId()));
notificationEntityService.notifyAssignOrUnassignEntityToCustomer(tenantId, assetId, savedAsset.getCustomerId(), savedAsset, CustomerId customerId = publicCustomer.getId();
actionType, user, assetId.toString(), publicCustomer.getId().toString(), publicCustomer.getName()); notificationEntityService.logEntityAction(tenantId, assetId, savedAsset, customerId, actionType, user,
assetId.toString(), customerId.toString(), publicCustomer.getName());
return savedAsset; return savedAsset;
} catch (Exception e) { } catch (Exception e) {
@ -146,9 +144,8 @@ public class DefaultTbAssetService extends AbstractTbEntityService implements Tb
EdgeId edgeId = edge.getId(); EdgeId edgeId = edge.getId();
try { try {
Asset savedAsset = checkNotNull(assetService.assignAssetToEdge(tenantId, assetId, edgeId)); Asset savedAsset = checkNotNull(assetService.assignAssetToEdge(tenantId, assetId, edgeId));
notificationEntityService.notifyAssignOrUnassignEntityToEdge(tenantId, assetId, savedAsset.getCustomerId(), notificationEntityService.logEntityAction(tenantId, assetId, savedAsset, savedAsset.getCustomerId(),
edgeId, savedAsset, actionType, user, assetId.toString(), edgeId.toString(), edge.getName()); actionType, user, assetId.toString(), edgeId.toString(), edge.getName());
return savedAsset; return savedAsset;
} catch (Exception e) { } catch (Exception e) {
notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.ASSET), actionType, notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.ASSET), actionType,
@ -164,9 +161,8 @@ public class DefaultTbAssetService extends AbstractTbEntityService implements Tb
EdgeId edgeId = edge.getId(); EdgeId edgeId = edge.getId();
try { try {
Asset savedAsset = checkNotNull(assetService.unassignAssetFromEdge(tenantId, assetId, edgeId)); Asset savedAsset = checkNotNull(assetService.unassignAssetFromEdge(tenantId, assetId, edgeId));
notificationEntityService.logEntityAction(tenantId, assetId, asset, asset.getCustomerId(),
notificationEntityService.notifyAssignOrUnassignEntityToEdge(tenantId, assetId, asset.getCustomerId(), actionType, user, assetId.toString(), edgeId.toString(), edge.getName());
edgeId, asset, actionType, user, assetId.toString(), edgeId.toString(), edge.getName());
return savedAsset; return savedAsset;
} catch (Exception e) { } catch (Exception e) {

11
application/src/main/java/org/thingsboard/server/service/entitiy/asset/profile/DefaultTbAssetProfileService.java

@ -59,8 +59,8 @@ public class DefaultTbAssetProfileService extends AbstractTbEntityService implem
tbClusterService.broadcastEntityStateChangeEvent(tenantId, savedAssetProfile.getId(), tbClusterService.broadcastEntityStateChangeEvent(tenantId, savedAssetProfile.getId(),
actionType.equals(ActionType.ADDED) ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED); actionType.equals(ActionType.ADDED) ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED);
notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, savedAssetProfile.getId(), notificationEntityService.logEntityAction(tenantId, savedAssetProfile.getId(), savedAssetProfile,
savedAssetProfile, user, actionType, true, null); null, actionType, user);
return savedAssetProfile; return savedAssetProfile;
} catch (Exception e) { } catch (Exception e) {
notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.ASSET_PROFILE), assetProfile, actionType, user, e); notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.ASSET_PROFILE), assetProfile, actionType, user, e);
@ -70,16 +70,17 @@ public class DefaultTbAssetProfileService extends AbstractTbEntityService implem
@Override @Override
public void delete(AssetProfile assetProfile, User user) { public void delete(AssetProfile assetProfile, User user) {
ActionType actionType = ActionType.DELETED;
AssetProfileId assetProfileId = assetProfile.getId(); AssetProfileId assetProfileId = assetProfile.getId();
TenantId tenantId = assetProfile.getTenantId(); TenantId tenantId = assetProfile.getTenantId();
try { try {
assetProfileService.deleteAssetProfile(tenantId, assetProfileId); assetProfileService.deleteAssetProfile(tenantId, assetProfileId);
tbClusterService.broadcastEntityStateChangeEvent(tenantId, assetProfileId, ComponentLifecycleEvent.DELETED); tbClusterService.broadcastEntityStateChangeEvent(tenantId, assetProfileId, ComponentLifecycleEvent.DELETED);
notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, assetProfileId, assetProfile, notificationEntityService.logEntityAction(tenantId, assetProfileId, assetProfile, null,
user, ActionType.DELETED, true, null, assetProfileId.toString()); actionType, user, assetProfileId.toString());
} catch (Exception e) { } catch (Exception e) {
notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.ASSET_PROFILE), ActionType.DELETED, notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.ASSET_PROFILE), actionType,
user, e, assetProfileId.toString()); user, e, assetProfileId.toString());
throw e; throw e;
} }

15
application/src/main/java/org/thingsboard/server/service/entitiy/customer/DefaultTbCustomerService.java

@ -22,13 +22,10 @@ import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.User; 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.id.CustomerId; import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
import org.thingsboard.server.service.entitiy.AbstractTbEntityService; import org.thingsboard.server.service.entitiy.AbstractTbEntityService;
import java.util.List;
@Service @Service
@AllArgsConstructor @AllArgsConstructor
public class DefaultTbCustomerService extends AbstractTbEntityService implements TbCustomerService { public class DefaultTbCustomerService extends AbstractTbEntityService implements TbCustomerService {
@ -40,7 +37,7 @@ public class DefaultTbCustomerService extends AbstractTbEntityService implements
try { try {
Customer savedCustomer = checkNotNull(customerService.saveCustomer(customer)); Customer savedCustomer = checkNotNull(customerService.saveCustomer(customer));
autoCommit(user, savedCustomer.getId()); autoCommit(user, savedCustomer.getId());
notificationEntityService.notifyCreateOrUpdateEntity(tenantId, savedCustomer.getId(), savedCustomer, null, actionType, user); notificationEntityService.logEntityAction(tenantId, savedCustomer.getId(), savedCustomer, null, actionType, user);
return savedCustomer; return savedCustomer;
} catch (Exception e) { } catch (Exception e) {
notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.CUSTOMER), customer, actionType, user, e); notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.CUSTOMER), customer, actionType, user, e);
@ -50,17 +47,17 @@ public class DefaultTbCustomerService extends AbstractTbEntityService implements
@Override @Override
public void delete(Customer customer, User user) { public void delete(Customer customer, User user) {
ActionType actionType = ActionType.DELETED;
TenantId tenantId = customer.getTenantId(); TenantId tenantId = customer.getTenantId();
CustomerId customerId = customer.getId(); CustomerId customerId = customer.getId();
try { try {
List<EdgeId> relatedEdgeIds = edgeService.findAllRelatedEdgeIds(tenantId, customer.getId());
customerService.deleteCustomer(tenantId, customerId); customerService.deleteCustomer(tenantId, customerId);
notificationEntityService.notifyDeleteEntity(tenantId, customer.getId(), customer, customerId, notificationEntityService.logEntityAction(tenantId, customer.getId(), customer, customerId, actionType,
ActionType.DELETED, relatedEdgeIds, user, customerId.toString()); user, customerId.toString());
tbClusterService.broadcastEntityStateChangeEvent(tenantId, customer.getId(), ComponentLifecycleEvent.DELETED); tbClusterService.broadcastEntityStateChangeEvent(tenantId, customer.getId(), ComponentLifecycleEvent.DELETED);
} catch (Exception e) { } catch (Exception e) {
notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.CUSTOMER), ActionType.DELETED, notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.CUSTOMER), actionType, user,
user, e, customerId.toString()); e, customerId.toString());
throw e; throw e;
} }
} }

49
application/src/main/java/org/thingsboard/server/service/entitiy/dashboard/DefaultTbDashboardService.java

@ -34,7 +34,6 @@ import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.entitiy.AbstractTbEntityService; import org.thingsboard.server.service.entitiy.AbstractTbEntityService;
import java.util.HashSet; import java.util.HashSet;
import java.util.List;
import java.util.Set; import java.util.Set;
@Service @Service
@ -51,8 +50,8 @@ public class DefaultTbDashboardService extends AbstractTbEntityService implement
try { try {
Dashboard savedDashboard = checkNotNull(dashboardService.saveDashboard(dashboard)); Dashboard savedDashboard = checkNotNull(dashboardService.saveDashboard(dashboard));
autoCommit(user, savedDashboard.getId()); autoCommit(user, savedDashboard.getId());
notificationEntityService.notifyCreateOrUpdateEntity(tenantId, savedDashboard.getId(), savedDashboard, notificationEntityService.logEntityAction(tenantId, savedDashboard.getId(), savedDashboard, null,
null, actionType, user); actionType, user);
return savedDashboard; return savedDashboard;
} catch (Exception e) { } catch (Exception e) {
notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.DASHBOARD), dashboard, actionType, user, e); notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.DASHBOARD), dashboard, actionType, user, e);
@ -62,15 +61,14 @@ public class DefaultTbDashboardService extends AbstractTbEntityService implement
@Override @Override
public void delete(Dashboard dashboard, User user) { public void delete(Dashboard dashboard, User user) {
ActionType actionType = ActionType.DELETED;
DashboardId dashboardId = dashboard.getId(); DashboardId dashboardId = dashboard.getId();
TenantId tenantId = dashboard.getTenantId(); TenantId tenantId = dashboard.getTenantId();
try { try {
List<EdgeId> relatedEdgeIds = edgeService.findAllRelatedEdgeIds(tenantId, dashboardId);
dashboardService.deleteDashboard(tenantId, dashboardId); dashboardService.deleteDashboard(tenantId, dashboardId);
notificationEntityService.notifyDeleteEntity(tenantId, dashboardId, dashboard, null, notificationEntityService.logEntityAction(tenantId, dashboardId, dashboard, null, actionType, user, dashboardId.toString());
ActionType.DELETED, relatedEdgeIds, user, dashboardId.toString());
} catch (Exception e) { } catch (Exception e) {
notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.DASHBOARD), ActionType.DELETED, user, e, dashboardId.toString()); notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.DASHBOARD), actionType, user, e, dashboardId.toString());
throw e; throw e;
} }
} }
@ -83,8 +81,8 @@ public class DefaultTbDashboardService extends AbstractTbEntityService implement
DashboardId dashboardId = dashboard.getId(); DashboardId dashboardId = dashboard.getId();
try { try {
Dashboard savedDashboard = checkNotNull(dashboardService.assignDashboardToCustomer(tenantId, dashboardId, customerId)); Dashboard savedDashboard = checkNotNull(dashboardService.assignDashboardToCustomer(tenantId, dashboardId, customerId));
notificationEntityService.notifyAssignOrUnassignEntityToCustomer(tenantId, dashboardId, customerId, savedDashboard, notificationEntityService.logEntityAction(tenantId, dashboardId, savedDashboard, customerId, actionType,
actionType, user, dashboardId.toString(), customerId.toString(), customer.getName()); user, dashboardId.toString(), customerId.toString(), customer.getName());
return savedDashboard; return savedDashboard;
} catch (Exception e) { } catch (Exception e) {
notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.DASHBOARD), actionType, notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.DASHBOARD), actionType,
@ -101,9 +99,8 @@ public class DefaultTbDashboardService extends AbstractTbEntityService implement
try { try {
Customer publicCustomer = customerService.findOrCreatePublicCustomer(tenantId); Customer publicCustomer = customerService.findOrCreatePublicCustomer(tenantId);
Dashboard savedDashboard = checkNotNull(dashboardService.assignDashboardToCustomer(tenantId, dashboardId, publicCustomer.getId())); Dashboard savedDashboard = checkNotNull(dashboardService.assignDashboardToCustomer(tenantId, dashboardId, publicCustomer.getId()));
notificationEntityService.notifyAssignOrUnassignEntityToCustomer(tenantId, dashboardId, publicCustomer.getId(), savedDashboard, notificationEntityService.logEntityAction(tenantId, dashboardId, savedDashboard, publicCustomer.getId(),
actionType, user, dashboardId.toString(), actionType, user, dashboardId.toString(), publicCustomer.getId().toString(), publicCustomer.getName());
publicCustomer.getId().toString(), publicCustomer.getName());
return savedDashboard; return savedDashboard;
} catch (Exception e) { } catch (Exception e) {
notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.DASHBOARD), actionType, user, e, dashboardId.toString()); notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.DASHBOARD), actionType, user, e, dashboardId.toString());
@ -119,9 +116,8 @@ public class DefaultTbDashboardService extends AbstractTbEntityService implement
try { try {
Customer publicCustomer = customerService.findOrCreatePublicCustomer(tenantId); Customer publicCustomer = customerService.findOrCreatePublicCustomer(tenantId);
Dashboard savedDashboard = checkNotNull(dashboardService.unassignDashboardFromCustomer(tenantId, dashboardId, publicCustomer.getId())); Dashboard savedDashboard = checkNotNull(dashboardService.unassignDashboardFromCustomer(tenantId, dashboardId, publicCustomer.getId()));
notificationEntityService.notifyAssignOrUnassignEntityToCustomer(tenantId, dashboardId, publicCustomer.getId(), dashboard, notificationEntityService.logEntityAction(tenantId, dashboardId, dashboard, publicCustomer.getId(), actionType,
actionType, user, dashboardId.toString(), user, dashboardId.toString(), publicCustomer.getId().toString(), publicCustomer.getName());
publicCustomer.getId().toString(), publicCustomer.getName());
return savedDashboard; return savedDashboard;
} catch (Exception e) { } catch (Exception e) {
notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.DASHBOARD), actionType, user, e, dashboardId.toString()); notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.DASHBOARD), actionType, user, e, dashboardId.toString());
@ -159,15 +155,15 @@ public class DefaultTbDashboardService extends AbstractTbEntityService implement
for (CustomerId customerId : addedCustomerIds) { for (CustomerId customerId : addedCustomerIds) {
savedDashboard = checkNotNull(dashboardService.assignDashboardToCustomer(tenantId, dashboardId, customerId)); savedDashboard = checkNotNull(dashboardService.assignDashboardToCustomer(tenantId, dashboardId, customerId));
ShortCustomerInfo customerInfo = savedDashboard.getAssignedCustomerInfo(customerId); ShortCustomerInfo customerInfo = savedDashboard.getAssignedCustomerInfo(customerId);
notificationEntityService.notifyAssignOrUnassignEntityToCustomer(tenantId, savedDashboard.getId(), customerId, savedDashboard, notificationEntityService.logEntityAction(tenantId, savedDashboard.getId(), savedDashboard, customerId,
actionType, user, dashboardId.toString(), customerId.toString(), customerInfo.getTitle()); actionType, user, dashboardId.toString(), customerId.toString(), customerInfo.getTitle());
} }
actionType = ActionType.UNASSIGNED_FROM_CUSTOMER; actionType = ActionType.UNASSIGNED_FROM_CUSTOMER;
for (CustomerId customerId : removedCustomerIds) { for (CustomerId customerId : removedCustomerIds) {
ShortCustomerInfo customerInfo = dashboard.getAssignedCustomerInfo(customerId); ShortCustomerInfo customerInfo = dashboard.getAssignedCustomerInfo(customerId);
savedDashboard = checkNotNull(dashboardService.unassignDashboardFromCustomer(tenantId, dashboardId, customerId)); savedDashboard = checkNotNull(dashboardService.unassignDashboardFromCustomer(tenantId, dashboardId, customerId));
notificationEntityService.notifyAssignOrUnassignEntityToCustomer(tenantId, savedDashboard.getId(), customerId, savedDashboard, notificationEntityService.logEntityAction(tenantId, savedDashboard.getId(), savedDashboard, customerId,
ActionType.UNASSIGNED_FROM_CUSTOMER, user, dashboardId.toString(), customerId.toString(), customerInfo.getTitle()); actionType, user, dashboardId.toString(), customerId.toString(), customerInfo.getTitle());
} }
return savedDashboard; return savedDashboard;
} }
@ -196,7 +192,7 @@ public class DefaultTbDashboardService extends AbstractTbEntityService implement
for (CustomerId customerId : addedCustomerIds) { for (CustomerId customerId : addedCustomerIds) {
savedDashboard = checkNotNull(dashboardService.assignDashboardToCustomer(tenantId, dashboardId, customerId)); savedDashboard = checkNotNull(dashboardService.assignDashboardToCustomer(tenantId, dashboardId, customerId));
ShortCustomerInfo customerInfo = savedDashboard.getAssignedCustomerInfo(customerId); ShortCustomerInfo customerInfo = savedDashboard.getAssignedCustomerInfo(customerId);
notificationEntityService.notifyAssignOrUnassignEntityToCustomer(tenantId, dashboardId, customerId, savedDashboard, notificationEntityService.logEntityAction(tenantId, dashboardId, savedDashboard, customerId,
actionType, user, dashboardId.toString(), customerId.toString(), customerInfo.getTitle()); actionType, user, dashboardId.toString(), customerId.toString(), customerInfo.getTitle());
} }
return savedDashboard; return savedDashboard;
@ -226,7 +222,7 @@ public class DefaultTbDashboardService extends AbstractTbEntityService implement
for (CustomerId customerId : removedCustomerIds) { for (CustomerId customerId : removedCustomerIds) {
ShortCustomerInfo customerInfo = dashboard.getAssignedCustomerInfo(customerId); ShortCustomerInfo customerInfo = dashboard.getAssignedCustomerInfo(customerId);
savedDashboard = checkNotNull(dashboardService.unassignDashboardFromCustomer(tenantId, dashboardId, customerId)); savedDashboard = checkNotNull(dashboardService.unassignDashboardFromCustomer(tenantId, dashboardId, customerId));
notificationEntityService.notifyAssignOrUnassignEntityToCustomer(tenantId, dashboardId, customerId, savedDashboard, notificationEntityService.logEntityAction(tenantId, dashboardId, savedDashboard, customerId,
actionType, user, dashboardId.toString(), customerId.toString(), customerInfo.getTitle()); actionType, user, dashboardId.toString(), customerId.toString(), customerInfo.getTitle());
} }
return savedDashboard; return savedDashboard;
@ -243,9 +239,8 @@ public class DefaultTbDashboardService extends AbstractTbEntityService implement
EdgeId edgeId = edge.getId(); EdgeId edgeId = edge.getId();
try { try {
Dashboard savedDashboard = checkNotNull(dashboardService.assignDashboardToEdge(tenantId, dashboardId, edgeId)); Dashboard savedDashboard = checkNotNull(dashboardService.assignDashboardToEdge(tenantId, dashboardId, edgeId));
notificationEntityService.notifyAssignOrUnassignEntityToEdge(tenantId, dashboardId, null, notificationEntityService.logEntityAction(tenantId, dashboardId, savedDashboard, null, actionType,
edgeId, savedDashboard, actionType, user, dashboardId.toString(), user, dashboardId.toString(), edgeId.toString(), edge.getName());
edgeId.toString(), edge.getName());
return savedDashboard; return savedDashboard;
} catch (Exception e) { } catch (Exception e) {
notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.DEVICE), notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.DEVICE),
@ -262,10 +257,8 @@ public class DefaultTbDashboardService extends AbstractTbEntityService implement
EdgeId edgeId = edge.getId(); EdgeId edgeId = edge.getId();
try { try {
Dashboard savedDevice = checkNotNull(dashboardService.unassignDashboardFromEdge(tenantId, dashboardId, edgeId)); Dashboard savedDevice = checkNotNull(dashboardService.unassignDashboardFromEdge(tenantId, dashboardId, edgeId));
notificationEntityService.logEntityAction(tenantId, dashboardId, dashboard, null, actionType,
notificationEntityService.notifyAssignOrUnassignEntityToEdge(tenantId, dashboardId, null, user, dashboardId.toString(), edgeId.toString(), edge.getName());
edgeId, dashboard, actionType, user, dashboardId.toString(),
edgeId.toString(), edge.getName());
return savedDevice; return savedDevice;
} catch (Exception e) { } catch (Exception e) {
notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.DASHBOARD), actionType, user, e, notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.DASHBOARD), actionType, user, e,
@ -281,7 +274,7 @@ public class DefaultTbDashboardService extends AbstractTbEntityService implement
DashboardId dashboardId = dashboard.getId(); DashboardId dashboardId = dashboard.getId();
try { try {
Dashboard savedDashboard = checkNotNull(dashboardService.unassignDashboardFromCustomer(tenantId, dashboardId, customer.getId())); Dashboard savedDashboard = checkNotNull(dashboardService.unassignDashboardFromCustomer(tenantId, dashboardId, customer.getId()));
notificationEntityService.notifyAssignOrUnassignEntityToCustomer(tenantId, dashboardId, customer.getId(), savedDashboard, notificationEntityService.logEntityAction(tenantId, dashboardId, savedDashboard, customer.getId(),
actionType, user, dashboardId.toString(), customer.getId().toString(), customer.getName()); actionType, user, dashboardId.toString(), customer.getId().toString(), customer.getName());
return savedDashboard; return savedDashboard;
} catch (Exception e) { } catch (Exception e) {

32
application/src/main/java/org/thingsboard/server/service/entitiy/device/DefaultTbDeviceService.java

@ -44,8 +44,6 @@ import org.thingsboard.server.dao.tenant.TenantService;
import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.entitiy.AbstractTbEntityService; import org.thingsboard.server.service.entitiy.AbstractTbEntityService;
import java.util.List;
@AllArgsConstructor @AllArgsConstructor
@TbCoreComponent @TbCoreComponent
@Service @Service
@ -97,10 +95,9 @@ public class DefaultTbDeviceService extends AbstractTbEntityService implements T
TenantId tenantId = device.getTenantId(); TenantId tenantId = device.getTenantId();
DeviceId deviceId = device.getId(); DeviceId deviceId = device.getId();
try { try {
List<EdgeId> relatedEdgeIds = edgeService.findAllRelatedEdgeIds(tenantId, deviceId);
deviceService.deleteDevice(tenantId, deviceId); deviceService.deleteDevice(tenantId, deviceId);
notificationEntityService.notifyDeleteDevice(tenantId, deviceId, device.getCustomerId(), device, notificationEntityService.notifyDeleteDevice(tenantId, deviceId, device.getCustomerId(), device,
relatedEdgeIds, user, deviceId.toString()); user, deviceId.toString());
return removeAlarmsByEntityId(tenantId, deviceId); return removeAlarmsByEntityId(tenantId, deviceId);
} catch (Exception e) { } catch (Exception e) {
@ -116,8 +113,8 @@ public class DefaultTbDeviceService extends AbstractTbEntityService implements T
CustomerId customerId = customer.getId(); CustomerId customerId = customer.getId();
try { try {
Device savedDevice = checkNotNull(deviceService.assignDeviceToCustomer(tenantId, deviceId, customerId)); Device savedDevice = checkNotNull(deviceService.assignDeviceToCustomer(tenantId, deviceId, customerId));
notificationEntityService.notifyAssignOrUnassignEntityToCustomer(tenantId, deviceId, customerId, savedDevice, notificationEntityService.logEntityAction(tenantId, deviceId, savedDevice, customerId, actionType, user,
actionType, user, deviceId.toString(), customerId.toString(), customer.getName()); deviceId.toString(), customerId.toString(), customer.getName());
return savedDevice; return savedDevice;
} catch (Exception e) { } catch (Exception e) {
@ -136,8 +133,8 @@ public class DefaultTbDeviceService extends AbstractTbEntityService implements T
Device savedDevice = checkNotNull(deviceService.unassignDeviceFromCustomer(tenantId, deviceId)); Device savedDevice = checkNotNull(deviceService.unassignDeviceFromCustomer(tenantId, deviceId));
CustomerId customerId = customer.getId(); CustomerId customerId = customer.getId();
notificationEntityService.notifyAssignOrUnassignEntityToCustomer(tenantId, deviceId, customerId, savedDevice, notificationEntityService.logEntityAction(tenantId, deviceId, savedDevice, customerId, actionType, user,
actionType, user, deviceId.toString(), customerId.toString(), customer.getName()); deviceId.toString(), customerId.toString(), customer.getName());
return savedDevice; return savedDevice;
} catch (Exception e) { } catch (Exception e) {
@ -154,9 +151,8 @@ public class DefaultTbDeviceService extends AbstractTbEntityService implements T
try { try {
Device savedDevice = checkNotNull(deviceService.assignDeviceToCustomer(tenantId, deviceId, publicCustomer.getId())); Device savedDevice = checkNotNull(deviceService.assignDeviceToCustomer(tenantId, deviceId, publicCustomer.getId()));
notificationEntityService.notifyAssignOrUnassignEntityToCustomer(tenantId, deviceId, savedDevice.getCustomerId(), savedDevice, notificationEntityService.logEntityAction(tenantId, deviceId, savedDevice, savedDevice.getCustomerId(),
actionType, user, deviceId.toString(), actionType, user, deviceId.toString(), publicCustomer.getId().toString(), publicCustomer.getName());
publicCustomer.getId().toString(), publicCustomer.getName());
return savedDevice; return savedDevice;
} catch (Exception e) { } catch (Exception e) {
@ -252,30 +248,32 @@ public class DefaultTbDeviceService extends AbstractTbEntityService implements T
EdgeId edgeId = edge.getId(); EdgeId edgeId = edge.getId();
try { try {
Device savedDevice = checkNotNull(deviceService.assignDeviceToEdge(tenantId, deviceId, edgeId)); Device savedDevice = checkNotNull(deviceService.assignDeviceToEdge(tenantId, deviceId, edgeId));
notificationEntityService.notifyAssignOrUnassignEntityToEdge(tenantId, deviceId, savedDevice.getCustomerId(), notificationEntityService.logEntityAction(tenantId, deviceId, savedDevice, savedDevice.getCustomerId(),
edgeId, savedDevice, actionType, user, deviceId.toString(), edgeId.toString(), edge.getName()); actionType, user, deviceId.toString(), edgeId.toString(), edge.getName());
return savedDevice; return savedDevice;
} catch (Exception e) { } catch (Exception e) {
notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.DEVICE), notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.DEVICE),
ActionType.ASSIGNED_TO_EDGE, user, e, deviceId.toString(), edgeId.toString()); actionType, user, e, deviceId.toString(), edgeId.toString());
throw e; throw e;
} }
} }
@Override @Override
public Device unassignDeviceFromEdge(Device device, Edge edge, User user) throws ThingsboardException { public Device unassignDeviceFromEdge(Device device, Edge edge, User user) throws ThingsboardException {
ActionType actionType = ActionType.UNASSIGNED_FROM_EDGE;
TenantId tenantId = device.getTenantId(); TenantId tenantId = device.getTenantId();
DeviceId deviceId = device.getId(); DeviceId deviceId = device.getId();
EdgeId edgeId = edge.getId(); EdgeId edgeId = edge.getId();
try { try {
Device savedDevice = checkNotNull(deviceService.unassignDeviceFromEdge(tenantId, deviceId, edgeId)); Device savedDevice = checkNotNull(deviceService.unassignDeviceFromEdge(tenantId, deviceId, edgeId));
notificationEntityService.logEntityAction(tenantId, deviceId, savedDevice, savedDevice.getCustomerId(),
actionType, user, deviceId.toString(), edgeId.toString(), edge.getName());
notificationEntityService.notifyAssignOrUnassignEntityToEdge(tenantId, deviceId, device.getCustomerId(),
edgeId, device, ActionType.UNASSIGNED_FROM_EDGE, user, deviceId.toString(), edgeId.toString(), edge.getName());
return savedDevice; return savedDevice;
} catch (Exception e) { } catch (Exception e) {
notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.DEVICE), notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.DEVICE),
ActionType.UNASSIGNED_FROM_EDGE, user, e, deviceId.toString(), edgeId.toString()); actionType, user, e, deviceId.toString(), edgeId.toString());
throw e; throw e;
} }
} }

11
application/src/main/java/org/thingsboard/server/service/entitiy/device/profile/DefaultTbDeviceProfileService.java

@ -67,8 +67,8 @@ public class DefaultTbDeviceProfileService extends AbstractTbEntityService imple
otaPackageStateService.update(savedDeviceProfile, isFirmwareChanged, isSoftwareChanged); otaPackageStateService.update(savedDeviceProfile, isFirmwareChanged, isSoftwareChanged);
notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, savedDeviceProfile.getId(), notificationEntityService.logEntityAction(tenantId, savedDeviceProfile.getId(), savedDeviceProfile,
savedDeviceProfile, user, actionType, true, null); null, actionType, user);
return savedDeviceProfile; return savedDeviceProfile;
} catch (Exception e) { } catch (Exception e) {
notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.DEVICE_PROFILE), deviceProfile, actionType, user, e); notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.DEVICE_PROFILE), deviceProfile, actionType, user, e);
@ -78,6 +78,7 @@ public class DefaultTbDeviceProfileService extends AbstractTbEntityService imple
@Override @Override
public void delete(DeviceProfile deviceProfile, User user) { public void delete(DeviceProfile deviceProfile, User user) {
ActionType actionType = ActionType.DELETED;
DeviceProfileId deviceProfileId = deviceProfile.getId(); DeviceProfileId deviceProfileId = deviceProfile.getId();
TenantId tenantId = deviceProfile.getTenantId(); TenantId tenantId = deviceProfile.getTenantId();
try { try {
@ -85,10 +86,10 @@ public class DefaultTbDeviceProfileService extends AbstractTbEntityService imple
tbClusterService.onDeviceProfileDelete(deviceProfile, null); tbClusterService.onDeviceProfileDelete(deviceProfile, null);
tbClusterService.broadcastEntityStateChangeEvent(tenantId, deviceProfileId, ComponentLifecycleEvent.DELETED); tbClusterService.broadcastEntityStateChangeEvent(tenantId, deviceProfileId, ComponentLifecycleEvent.DELETED);
notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, deviceProfileId, deviceProfile, notificationEntityService.logEntityAction(tenantId, deviceProfileId, deviceProfile, null,
user, ActionType.DELETED, true, null, deviceProfileId.toString()); actionType, user, deviceProfileId.toString());
} catch (Exception e) { } catch (Exception e) {
notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.DEVICE_PROFILE), ActionType.DELETED, notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.DEVICE_PROFILE), actionType,
user, e, deviceProfileId.toString()); user, e, deviceProfileId.toString());
throw e; throw e;
} }

8
application/src/main/java/org/thingsboard/server/service/entitiy/edge/DefaultTbEdgeService.java

@ -89,8 +89,8 @@ public class DefaultTbEdgeService extends AbstractTbEntityService implements TbE
CustomerId customerId = customer.getId(); CustomerId customerId = customer.getId();
try { try {
Edge savedEdge = checkNotNull(edgeService.assignEdgeToCustomer(tenantId, edgeId, customerId)); Edge savedEdge = checkNotNull(edgeService.assignEdgeToCustomer(tenantId, edgeId, customerId));
notificationEntityService.notifyAssignOrUnassignEntityToCustomer(tenantId, edgeId, customerId, savedEdge, notificationEntityService.logEntityAction(tenantId, edgeId, savedEdge, customerId, actionType,
actionType, user, edgeId.toString(), customerId.toString(), customer.getName()); user, edgeId.toString(), customerId.toString(), customer.getName());
return savedEdge; return savedEdge;
} catch (Exception e) { } catch (Exception e) {
@ -108,8 +108,8 @@ public class DefaultTbEdgeService extends AbstractTbEntityService implements TbE
CustomerId customerId = customer.getId(); CustomerId customerId = customer.getId();
try { try {
Edge savedEdge = checkNotNull(edgeService.unassignEdgeFromCustomer(tenantId, edgeId)); Edge savedEdge = checkNotNull(edgeService.unassignEdgeFromCustomer(tenantId, edgeId));
notificationEntityService.notifyAssignOrUnassignEntityToCustomer(tenantId, edgeId, customerId, savedEdge, notificationEntityService.logEntityAction(tenantId, edgeId, savedEdge, customerId, actionType,
actionType, user, edgeId.toString(), customerId.toString(), customer.getName()); user, edgeId.toString(), customerId.toString(), customer.getName());
return savedEdge; return savedEdge;
} catch (Exception e) { } catch (Exception e) {
notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.EDGE), notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.EDGE),

25
application/src/main/java/org/thingsboard/server/service/entitiy/entity/relation/DefaultTbEntityRelationService.java

@ -40,41 +40,38 @@ public class DefaultTbEntityRelationService extends AbstractTbEntityService impl
@Override @Override
public void save(TenantId tenantId, CustomerId customerId, EntityRelation relation, User user) throws ThingsboardException { public void save(TenantId tenantId, CustomerId customerId, EntityRelation relation, User user) throws ThingsboardException {
ActionType actionType = ActionType.RELATION_ADD_OR_UPDATE;
try { try {
relationService.saveRelation(tenantId, relation); relationService.saveRelation(tenantId, relation);
notificationEntityService.notifyRelation(tenantId, customerId, notificationEntityService.logEntityRelationAction(tenantId, customerId,
relation, user, ActionType.RELATION_ADD_OR_UPDATE, relation); relation, user, actionType, null, relation);
} catch (Exception e) { } catch (Exception e) {
notificationEntityService.logEntityAction(tenantId, relation.getFrom(), null, customerId, notificationEntityService.logEntityRelationAction(tenantId, customerId,
ActionType.RELATION_ADD_OR_UPDATE, user, e, relation); relation, user, actionType, e, relation);
notificationEntityService.logEntityAction(tenantId, relation.getTo(), null, customerId,
ActionType.RELATION_ADD_OR_UPDATE, user, e, relation);
throw e; throw e;
} }
} }
@Override @Override
public void delete(TenantId tenantId, CustomerId customerId, EntityRelation relation, User user) throws ThingsboardException { public void delete(TenantId tenantId, CustomerId customerId, EntityRelation relation, User user) throws ThingsboardException {
ActionType actionType = ActionType.RELATION_DELETED;
try { try {
boolean found = relationService.deleteRelation(tenantId, relation.getFrom(), relation.getTo(), relation.getType(), relation.getTypeGroup()); boolean found = relationService.deleteRelation(tenantId, relation.getFrom(), relation.getTo(), relation.getType(), relation.getTypeGroup());
if (!found) { if (!found) {
throw new ThingsboardException("Requested item wasn't found!", ThingsboardErrorCode.ITEM_NOT_FOUND); throw new ThingsboardException("Requested item wasn't found!", ThingsboardErrorCode.ITEM_NOT_FOUND);
} }
notificationEntityService.notifyRelation(tenantId, customerId, notificationEntityService.logEntityRelationAction(tenantId, customerId, relation, user, actionType, null, relation);
relation, user, ActionType.RELATION_DELETED, relation);
} catch (Exception e) { } catch (Exception e) {
notificationEntityService.logEntityAction(tenantId, relation.getFrom(), null, customerId, notificationEntityService.logEntityRelationAction(tenantId, customerId,
ActionType.RELATION_DELETED, user, e, relation); relation, user, actionType, e, relation);
notificationEntityService.logEntityAction(tenantId, relation.getTo(), null, customerId,
ActionType.RELATION_DELETED, user, e, relation);
throw e; throw e;
} }
} }
@Override @Override
public void deleteRelations(TenantId tenantId, CustomerId customerId, EntityId entityId, User user) throws ThingsboardException { public void deleteCommonRelations(TenantId tenantId, CustomerId customerId, EntityId entityId, User user) throws ThingsboardException {
try { try {
relationService.deleteEntityRelations(tenantId, entityId); relationService.deleteEntityCommonRelations(tenantId, entityId);
notificationEntityService.logEntityAction(tenantId, entityId, null, customerId, ActionType.RELATIONS_DELETED, user); notificationEntityService.logEntityAction(tenantId, entityId, null, customerId, ActionType.RELATIONS_DELETED, user);
} catch (Exception e) { } catch (Exception e) {
notificationEntityService.logEntityAction(tenantId, entityId, null, customerId, notificationEntityService.logEntityAction(tenantId, entityId, null, customerId,

2
application/src/main/java/org/thingsboard/server/service/entitiy/entity/relation/TbEntityRelationService.java

@ -28,6 +28,6 @@ public interface TbEntityRelationService {
void delete(TenantId tenantId, CustomerId customerId, EntityRelation entity, User user) throws ThingsboardException; void delete(TenantId tenantId, CustomerId customerId, EntityRelation entity, User user) throws ThingsboardException;
void deleteRelations(TenantId tenantId, CustomerId customerId, EntityId entityId, User user) throws ThingsboardException; void deleteCommonRelations(TenantId tenantId, CustomerId customerId, EntityId entityId, User user) throws ThingsboardException;
} }

67
application/src/main/java/org/thingsboard/server/service/entitiy/entityview/DefaultTbEntityViewService.java

@ -81,7 +81,7 @@ public class DefaultTbEntityViewService extends AbstractTbEntityService implemen
EntityView savedEntityView = checkNotNull(entityViewService.saveEntityView(entityView)); EntityView savedEntityView = checkNotNull(entityViewService.saveEntityView(entityView));
this.updateEntityViewAttributes(tenantId, savedEntityView, existingEntityView, user); this.updateEntityViewAttributes(tenantId, savedEntityView, existingEntityView, user);
autoCommit(user, savedEntityView.getId()); autoCommit(user, savedEntityView.getId());
notificationEntityService.notifyCreateOrUpdateEntity(savedEntityView.getTenantId(), savedEntityView.getId(), savedEntityView, notificationEntityService.logEntityAction(savedEntityView.getTenantId(), savedEntityView.getId(), savedEntityView,
null, actionType, user); null, actionType, user);
localCache.computeIfAbsent(savedEntityView.getTenantId(), (k) -> new ConcurrentReferenceHashMap<>()).clear(); localCache.computeIfAbsent(savedEntityView.getTenantId(), (k) -> new ConcurrentReferenceHashMap<>()).clear();
tbClusterService.broadcastEntityStateChangeEvent(savedEntityView.getTenantId(), savedEntityView.getId(), tbClusterService.broadcastEntityStateChangeEvent(savedEntityView.getTenantId(), savedEntityView.getId(),
@ -129,10 +129,9 @@ public class DefaultTbEntityViewService extends AbstractTbEntityService implemen
TenantId tenantId = entityView.getTenantId(); TenantId tenantId = entityView.getTenantId();
EntityViewId entityViewId = entityView.getId(); EntityViewId entityViewId = entityView.getId();
try { try {
List<EdgeId> relatedEdgeIds = edgeService.findAllRelatedEdgeIds(tenantId, entityViewId);
entityViewService.deleteEntityView(tenantId, entityViewId); entityViewService.deleteEntityView(tenantId, entityViewId);
notificationEntityService.notifyDeleteEntity(tenantId, entityViewId, entityView, entityView.getCustomerId(), ActionType.DELETED, notificationEntityService.logEntityAction(tenantId, entityViewId, entityView, entityView.getCustomerId(),
relatedEdgeIds, user, entityViewId.toString()); ActionType.DELETED, user, entityViewId.toString());
localCache.computeIfAbsent(tenantId, (k) -> new ConcurrentReferenceHashMap<>()).clear(); localCache.computeIfAbsent(tenantId, (k) -> new ConcurrentReferenceHashMap<>()).clear();
tbClusterService.broadcastEntityStateChangeEvent(tenantId, entityViewId, ComponentLifecycleEvent.DELETED); tbClusterService.broadcastEntityStateChangeEvent(tenantId, entityViewId, ComponentLifecycleEvent.DELETED);
@ -145,15 +144,31 @@ public class DefaultTbEntityViewService extends AbstractTbEntityService implemen
@Override @Override
public EntityView assignEntityViewToCustomer(TenantId tenantId, EntityViewId entityViewId, Customer customer, User user) throws ThingsboardException { public EntityView assignEntityViewToCustomer(TenantId tenantId, EntityViewId entityViewId, Customer customer, User user) throws ThingsboardException {
ActionType actionType = ActionType.ASSIGNED_TO_CUSTOMER;
CustomerId customerId = customer.getId(); CustomerId customerId = customer.getId();
try { try {
EntityView savedEntityView = checkNotNull(entityViewService.assignEntityViewToCustomer(tenantId, entityViewId, customerId)); EntityView savedEntityView = checkNotNull(entityViewService.assignEntityViewToCustomer(tenantId, entityViewId, customerId));
notificationEntityService.notifyAssignOrUnassignEntityToCustomer(tenantId, entityViewId, customerId, savedEntityView, notificationEntityService.logEntityAction(tenantId, entityViewId, savedEntityView, savedEntityView.getCustomerId(),
ActionType.ASSIGNED_TO_CUSTOMER, user, entityViewId.toString(), customerId.toString(), customer.getName()); actionType, user, entityViewId.toString(), customerId.toString(), customer.getName());
return savedEntityView;
} catch (Exception e) {
notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.ENTITY_VIEW),
actionType, user, e, entityViewId.toString(), customerId.toString());
throw e;
}
}
@Override
public EntityView unassignEntityViewFromCustomer(TenantId tenantId, EntityViewId entityViewId, Customer customer, User user) throws ThingsboardException {
ActionType actionType = ActionType.UNASSIGNED_FROM_CUSTOMER;
try {
EntityView savedEntityView = checkNotNull(entityViewService.unassignEntityViewFromCustomer(tenantId, entityViewId));
notificationEntityService.logEntityAction(tenantId, entityViewId, savedEntityView, customer.getId(),
actionType, user, savedEntityView.getId().toString(), customer.getId().toString(), customer.getName());
return savedEntityView; return savedEntityView;
} catch (Exception e) { } catch (Exception e) {
notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.ENTITY_VIEW), notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.ENTITY_VIEW),
ActionType.ASSIGNED_TO_CUSTOMER, user, e, entityViewId.toString(), customerId.toString()); actionType, user, e, entityViewId.toString());
throw e; throw e;
} }
} }
@ -165,9 +180,8 @@ public class DefaultTbEntityViewService extends AbstractTbEntityService implemen
try { try {
EntityView savedEntityView = checkNotNull(entityViewService.assignEntityViewToCustomer(tenantId, EntityView savedEntityView = checkNotNull(entityViewService.assignEntityViewToCustomer(tenantId,
entityViewId, publicCustomer.getId())); entityViewId, publicCustomer.getId()));
notificationEntityService.notifyAssignOrUnassignEntityToCustomer(tenantId, entityViewId, savedEntityView.getCustomerId(), savedEntityView, notificationEntityService.logEntityAction(tenantId, entityViewId, savedEntityView, savedEntityView.getCustomerId(),
actionType, user, savedEntityView.getId().toString(), actionType, user, savedEntityView.getId().toString(), publicCustomer.getId().toString(), publicCustomer.getName());
publicCustomer.getId().toString(), publicCustomer.getName());
return savedEntityView; return savedEntityView;
} catch (Exception e) { } catch (Exception e) {
notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.ENTITY_VIEW), notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.ENTITY_VIEW),
@ -178,16 +192,16 @@ public class DefaultTbEntityViewService extends AbstractTbEntityService implemen
@Override @Override
public EntityView assignEntityViewToEdge(TenantId tenantId, CustomerId customerId, EntityViewId entityViewId, Edge edge, User user) throws ThingsboardException { public EntityView assignEntityViewToEdge(TenantId tenantId, CustomerId customerId, EntityViewId entityViewId, Edge edge, User user) throws ThingsboardException {
ActionType actionType = ActionType.ASSIGNED_TO_EDGE;
EdgeId edgeId = edge.getId(); EdgeId edgeId = edge.getId();
try { try {
EntityView savedEntityView = checkNotNull(entityViewService.assignEntityViewToEdge(tenantId, entityViewId, edgeId)); EntityView savedEntityView = checkNotNull(entityViewService.assignEntityViewToEdge(tenantId, entityViewId, edgeId));
notificationEntityService.notifyAssignOrUnassignEntityToEdge(tenantId, entityViewId, customerId, notificationEntityService.logEntityAction(tenantId, entityViewId, savedEntityView, customerId, actionType,
edgeId, savedEntityView, ActionType.ASSIGNED_TO_EDGE, user, savedEntityView.getEntityId().toString(), user, savedEntityView.getEntityId().toString(), edgeId.toString(), edge.getName());
edgeId.toString(), edge.getName());
return savedEntityView; return savedEntityView;
} catch (Exception e) { } catch (Exception e) {
notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.ENTITY_VIEW), notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.ENTITY_VIEW),
ActionType.ASSIGNED_TO_EDGE, user, e, entityViewId.toString(), edgeId.toString()); actionType, user, e, entityViewId.toString(), edgeId.toString());
throw e; throw e;
} }
} }
@ -195,34 +209,17 @@ public class DefaultTbEntityViewService extends AbstractTbEntityService implemen
@Override @Override
public EntityView unassignEntityViewFromEdge(TenantId tenantId, CustomerId customerId, EntityView entityView, public EntityView unassignEntityViewFromEdge(TenantId tenantId, CustomerId customerId, EntityView entityView,
Edge edge, User user) throws ThingsboardException { Edge edge, User user) throws ThingsboardException {
ActionType actionType = ActionType.UNASSIGNED_FROM_EDGE;
EntityViewId entityViewId = entityView.getId(); EntityViewId entityViewId = entityView.getId();
EdgeId edgeId = edge.getId(); EdgeId edgeId = edge.getId();
try { try {
EntityView savedEntityView = checkNotNull(entityViewService.unassignEntityViewFromEdge(tenantId, entityViewId, edgeId)); EntityView savedEntityView = checkNotNull(entityViewService.unassignEntityViewFromEdge(tenantId, entityViewId, edgeId));
notificationEntityService.notifyAssignOrUnassignEntityToEdge(tenantId, entityViewId, customerId, notificationEntityService.logEntityAction(tenantId, entityViewId, savedEntityView, customerId, actionType,
edgeId, entityView, ActionType.UNASSIGNED_FROM_EDGE, user, entityViewId.toString(), user, entityViewId.toString(), edgeId.toString(), edge.getName());
edgeId.toString(), edge.getName());
return savedEntityView; return savedEntityView;
} catch (Exception e) { } catch (Exception e) {
notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.ENTITY_VIEW), notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.ENTITY_VIEW),
ActionType.UNASSIGNED_FROM_EDGE, user, e, entityViewId.toString(), edgeId.toString()); actionType, user, e, entityViewId.toString(), edgeId.toString());
throw e;
}
}
@Override
public EntityView unassignEntityViewFromCustomer(TenantId tenantId, EntityViewId entityViewId, Customer customer, User user) throws ThingsboardException {
ActionType actionType = ActionType.UNASSIGNED_FROM_CUSTOMER;
try {
EntityView savedEntityView = checkNotNull(entityViewService.unassignEntityViewFromCustomer(tenantId, entityViewId));
notificationEntityService.notifyAssignOrUnassignEntityToCustomer(tenantId, entityViewId, customer.getId(), savedEntityView,
actionType, user, savedEntityView.getId().toString(), customer.getId().toString(), customer.getName());
return savedEntityView;
} catch (Exception e) {
notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.ENTITY_VIEW),
actionType, user, e, entityViewId.toString());
throw e; throw e;
} }
} }

19
application/src/main/java/org/thingsboard/server/service/entitiy/ota/DefaultTbOtaPackageService.java

@ -50,9 +50,8 @@ public class DefaultTbOtaPackageService extends AbstractTbEntityService implemen
try { try {
OtaPackageInfo savedOtaPackageInfo = otaPackageService.saveOtaPackageInfo(new OtaPackageInfo(saveOtaPackageInfoRequest), saveOtaPackageInfoRequest.isUsesUrl()); OtaPackageInfo savedOtaPackageInfo = otaPackageService.saveOtaPackageInfo(new OtaPackageInfo(saveOtaPackageInfoRequest), saveOtaPackageInfoRequest.isUsesUrl());
boolean sendMsgToEdge = savedOtaPackageInfo.hasUrl() || savedOtaPackageInfo.isHasData(); notificationEntityService.logEntityAction(tenantId, savedOtaPackageInfo.getId(), savedOtaPackageInfo,
notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, savedOtaPackageInfo.getId(), null, actionType, user);
savedOtaPackageInfo, user, actionType, sendMsgToEdge, null);
return savedOtaPackageInfo; return savedOtaPackageInfo;
} catch (Exception e) { } catch (Exception e) {
@ -65,6 +64,7 @@ public class DefaultTbOtaPackageService extends AbstractTbEntityService implemen
@Override @Override
public OtaPackageInfo saveOtaPackageData(OtaPackageInfo otaPackageInfo, String checksum, ChecksumAlgorithm checksumAlgorithm, public OtaPackageInfo saveOtaPackageData(OtaPackageInfo otaPackageInfo, String checksum, ChecksumAlgorithm checksumAlgorithm,
byte[] data, String filename, String contentType, User user) throws ThingsboardException { byte[] data, String filename, String contentType, User user) throws ThingsboardException {
ActionType actionType = ActionType.UPDATED;
TenantId tenantId = otaPackageInfo.getTenantId(); TenantId tenantId = otaPackageInfo.getTenantId();
OtaPackageId otaPackageId = otaPackageInfo.getId(); OtaPackageId otaPackageId = otaPackageInfo.getId();
try { try {
@ -87,27 +87,26 @@ public class DefaultTbOtaPackageService extends AbstractTbEntityService implemen
otaPackage.setData(ByteBuffer.wrap(data)); otaPackage.setData(ByteBuffer.wrap(data));
otaPackage.setDataSize((long) data.length); otaPackage.setDataSize((long) data.length);
OtaPackageInfo savedOtaPackage = otaPackageService.saveOtaPackage(otaPackage); OtaPackageInfo savedOtaPackage = otaPackageService.saveOtaPackage(otaPackage);
notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, savedOtaPackage.getId(), notificationEntityService.logEntityAction(tenantId, savedOtaPackage.getId(), savedOtaPackage, null, actionType, user);
savedOtaPackage, user, ActionType.UPDATED, true, null);
return savedOtaPackage; return savedOtaPackage;
} catch (Exception e) { } catch (Exception e) {
notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.OTA_PACKAGE), ActionType.UPDATED, notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.OTA_PACKAGE), actionType, user, e, otaPackageId.toString());
user, e, otaPackageId.toString());
throw e; throw e;
} }
} }
@Override @Override
public void delete(OtaPackageInfo otaPackageInfo, User user) throws ThingsboardException { public void delete(OtaPackageInfo otaPackageInfo, User user) throws ThingsboardException {
ActionType actionType = ActionType.DELETED;
TenantId tenantId = otaPackageInfo.getTenantId(); TenantId tenantId = otaPackageInfo.getTenantId();
OtaPackageId otaPackageId = otaPackageInfo.getId(); OtaPackageId otaPackageId = otaPackageInfo.getId();
try { try {
otaPackageService.deleteOtaPackage(tenantId, otaPackageId); otaPackageService.deleteOtaPackage(tenantId, otaPackageId);
notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, otaPackageId, otaPackageInfo, notificationEntityService.logEntityAction(tenantId, otaPackageId, otaPackageInfo, null,
user, ActionType.DELETED, true, null, otaPackageInfo.getId().toString()); actionType, user, otaPackageInfo.getId().toString());
} catch (Exception e) { } catch (Exception e) {
notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.OTA_PACKAGE), notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.OTA_PACKAGE),
ActionType.DELETED, user, e, otaPackageId.toString()); actionType, user, e, otaPackageId.toString());
throw e; throw e;
} }
} }

48
application/src/main/java/org/thingsboard/server/service/entitiy/queue/DefaultTbQueueService.java

@ -15,12 +15,11 @@
*/ */
package org.thingsboard.server.service.entitiy.queue; package org.thingsboard.server.service.entitiy.queue;
import lombok.AllArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.thingsboard.server.cluster.TbClusterService; import org.thingsboard.server.cluster.TbClusterService;
import org.thingsboard.server.common.data.TenantProfile; import org.thingsboard.server.common.data.TenantProfile;
import org.thingsboard.server.common.data.edge.EdgeEventActionType;
import org.thingsboard.server.common.data.id.QueueId; import org.thingsboard.server.common.data.id.QueueId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.queue.Queue; import org.thingsboard.server.common.data.queue.Queue;
@ -28,7 +27,6 @@ import org.thingsboard.server.common.data.tenant.profile.TenantProfileQueueConfi
import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
import org.thingsboard.server.dao.queue.QueueService; import org.thingsboard.server.dao.queue.QueueService;
import org.thingsboard.server.queue.TbQueueAdmin; import org.thingsboard.server.queue.TbQueueAdmin;
import org.thingsboard.server.queue.scheduler.SchedulerComponent;
import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.entitiy.AbstractTbEntityService; import org.thingsboard.server.service.entitiy.AbstractTbEntityService;
@ -36,20 +34,17 @@ import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@Slf4j @Slf4j
@Service @Service
@TbCoreComponent @TbCoreComponent
@AllArgsConstructor @RequiredArgsConstructor
public class DefaultTbQueueService extends AbstractTbEntityService implements TbQueueService { public class DefaultTbQueueService extends AbstractTbEntityService implements TbQueueService {
private static final long DELETE_DELAY = 30;
private final QueueService queueService; private final QueueService queueService;
private final TbClusterService tbClusterService; private final TbClusterService tbClusterService;
private final TbQueueAdmin tbQueueAdmin; private final TbQueueAdmin tbQueueAdmin;
private final SchedulerComponent scheduler;
@Override @Override
public Queue saveQueue(Queue queue) { public Queue saveQueue(Queue queue) {
@ -71,8 +66,6 @@ public class DefaultTbQueueService extends AbstractTbEntityService implements Tb
onQueueUpdated(savedQueue, oldQueue); onQueueUpdated(savedQueue, oldQueue);
} }
notificationEntityService.notifySendMsgToEdgeService(queue.getTenantId(), savedQueue.getId(), create ? EdgeEventActionType.ADDED : EdgeEventActionType.UPDATED);
return savedQueue; return savedQueue;
} }
@ -93,7 +86,9 @@ public class DefaultTbQueueService extends AbstractTbEntityService implements Tb
private void onQueueCreated(Queue queue) { private void onQueueCreated(Queue queue) {
for (int i = 0; i < queue.getPartitions(); i++) { for (int i = 0; i < queue.getPartitions(); i++) {
tbQueueAdmin.createTopicIfNotExists( tbQueueAdmin.createTopicIfNotExists(
new TopicPartitionInfo(queue.getTopic(), queue.getTenantId(), i, false).getFullTopicName()); new TopicPartitionInfo(queue.getTopic(), queue.getTenantId(), i, false).getFullTopicName(),
queue.getCustomProperties()
);
} }
tbClusterService.onQueueChange(queue); tbClusterService.onQueueChange(queue);
@ -108,21 +103,15 @@ public class DefaultTbQueueService extends AbstractTbEntityService implements Tb
log.info("Added [{}] new partitions to [{}] queue", currentPartitions - oldPartitions, queue.getName()); log.info("Added [{}] new partitions to [{}] queue", currentPartitions - oldPartitions, queue.getName());
for (int i = oldPartitions; i < currentPartitions; i++) { for (int i = oldPartitions; i < currentPartitions; i++) {
tbQueueAdmin.createTopicIfNotExists( tbQueueAdmin.createTopicIfNotExists(
new TopicPartitionInfo(queue.getTopic(), queue.getTenantId(), i, false).getFullTopicName()); new TopicPartitionInfo(queue.getTopic(), queue.getTenantId(), i, false).getFullTopicName(),
queue.getCustomProperties()
);
} }
tbClusterService.onQueueChange(queue); tbClusterService.onQueueChange(queue);
} else { } else {
log.info("Removed [{}] partitions from [{}] queue", oldPartitions - currentPartitions, queue.getName()); log.info("Removed [{}] partitions from [{}] queue", oldPartitions - currentPartitions, queue.getName());
tbClusterService.onQueueChange(queue); tbClusterService.onQueueChange(queue);
// TODO: move all the messages left in old partitions and delete topics
scheduler.schedule(() -> {
for (int i = currentPartitions; i < oldPartitions; i++) {
String fullTopicName = new TopicPartitionInfo(queue.getTopic(), queue.getTenantId(), i, false).getFullTopicName();
log.info("Removed partition [{}]", fullTopicName);
tbQueueAdmin.deleteTopic(
fullTopicName);
}
}, DELETE_DELAY, TimeUnit.SECONDS);
} }
} else if (!oldQueue.equals(queue)) { } else if (!oldQueue.equals(queue)) {
tbClusterService.onQueueChange(queue); tbClusterService.onQueueChange(queue);
@ -131,22 +120,7 @@ public class DefaultTbQueueService extends AbstractTbEntityService implements Tb
private void onQueueDeleted(Queue queue) { private void onQueueDeleted(Queue queue) {
tbClusterService.onQueueDelete(queue); tbClusterService.onQueueDelete(queue);
// queueStatsService.deleteQueueStatsByQueueId(tenantId, queueId); // queueStatsService.deleteQueueStatsByQueueId(tenantId, queueId);
scheduler.schedule(() -> {
for (int i = 0; i < queue.getPartitions(); i++) {
String fullTopicName = new TopicPartitionInfo(queue.getTopic(), queue.getTenantId(), i, false).getFullTopicName();
log.info("Deleting queue [{}]", fullTopicName);
try {
tbQueueAdmin.deleteTopic(fullTopicName);
} catch (Exception e) {
log.error("Failed to delete queue [{}]", fullTopicName);
}
}
}, DELETE_DELAY, TimeUnit.SECONDS);
notificationEntityService.notifySendMsgToEdgeService(queue.getTenantId(), queue.getId(), EdgeEventActionType.DELETED);
} }
@Override @Override
@ -198,6 +172,10 @@ public class DefaultTbQueueService extends AbstractTbEntityService implements Tb
} }
} }
if (log.isDebugEnabled()) {
log.debug("[{}] Handling profile queue config update: creating queues {}, updating {}, deleting {}. Affected tenants: {}",
newTenantProfile.getUuidId(), toCreate, toUpdate, toRemove, tenantIds);
}
tenantIds.forEach(tenantId -> { tenantIds.forEach(tenantId -> {
toCreate.forEach(key -> saveQueue(new Queue(tenantId, newQueues.get(key)))); toCreate.forEach(key -> saveQueue(new Queue(tenantId, newQueues.get(key))));

8
application/src/main/java/org/thingsboard/server/service/entitiy/tenant/profile/DefaultTbTenantProfileService.java

@ -44,16 +44,14 @@ public class DefaultTbTenantProfileService extends AbstractTbEntityService imple
@Override @Override
public TenantProfile save(TenantId tenantId, TenantProfile tenantProfile, TenantProfile oldTenantProfile) throws ThingsboardException { public TenantProfile save(TenantId tenantId, TenantProfile tenantProfile, TenantProfile oldTenantProfile) throws ThingsboardException {
TenantProfile savedTenantProfile = checkNotNull(tenantProfileService.saveTenantProfile(tenantId, tenantProfile)); TenantProfile savedTenantProfile = checkNotNull(tenantProfileService.saveTenantProfile(tenantId, tenantProfile));
if (oldTenantProfile != null && savedTenantProfile.isIsolatedTbRuleEngine()) {
List<TenantId> tenantIds = tenantService.findTenantIdsByTenantProfileId(savedTenantProfile.getId());
tbQueueService.updateQueuesByTenants(tenantIds, savedTenantProfile, oldTenantProfile);
}
tenantProfileCache.put(savedTenantProfile); tenantProfileCache.put(savedTenantProfile);
tbClusterService.onTenantProfileChange(savedTenantProfile, null); tbClusterService.onTenantProfileChange(savedTenantProfile, null);
tbClusterService.broadcastEntityStateChangeEvent(TenantId.SYS_TENANT_ID, savedTenantProfile.getId(), tbClusterService.broadcastEntityStateChangeEvent(TenantId.SYS_TENANT_ID, savedTenantProfile.getId(),
tenantProfile.getId() == null ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED); tenantProfile.getId() == null ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED);
List<TenantId> tenantIds = tenantService.findTenantIdsByTenantProfileId(savedTenantProfile.getId());
tbQueueService.updateQueuesByTenants(tenantIds, savedTenantProfile, oldTenantProfile);
return savedTenantProfile; return savedTenantProfile;
} }

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

Loading…
Cancel
Save