Browse Source

Merge pull request #4903 from thingsboard/edge-3.3

Edge 3.3
pull/4910/head
Andrew Shvayka 5 years ago
committed by GitHub
parent
commit
9d4e10e8ad
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 162
      application/src/main/data/json/demo/edge_management/rule_chains/edge_root_rule_chain.json
  2. 15
      application/src/main/data/json/tenant/edge_management/rule_chains/edge_root_rule_chain.json
  3. 6
      application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java
  4. 38
      application/src/main/java/org/thingsboard/server/controller/BaseController.java
  5. 7
      application/src/main/java/org/thingsboard/server/controller/EdgeController.java
  6. 15
      application/src/main/java/org/thingsboard/server/controller/RuleChainController.java
  7. 4
      application/src/main/java/org/thingsboard/server/controller/TenantController.java
  8. 366
      application/src/main/java/org/thingsboard/server/service/edge/DefaultEdgeNotificationService.java
  9. 168
      application/src/main/java/org/thingsboard/server/service/edge/EdgeContextComponent.java
  10. 48
      application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeEventUtils.java
  11. 125
      application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcService.java
  12. 986
      application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java
  13. 9
      application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeRpcService.java
  14. 74
      application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeSyncCursor.java
  15. 2
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AdminSettingsMsgConstructor.java
  16. 4
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AlarmMsgConstructor.java
  17. 4
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AssetMsgConstructor.java
  18. 4
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/CustomerMsgConstructor.java
  19. 4
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DashboardMsgConstructor.java
  20. 10
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DeviceMsgConstructor.java
  21. 6
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DeviceProfileMsgConstructor.java
  22. 4
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/EntityDataMsgConstructor.java
  23. 6
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/EntityViewMsgConstructor.java
  24. 4
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RelationMsgConstructor.java
  25. 12
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RuleChainMsgConstructor.java
  26. 6
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/UserMsgConstructor.java
  27. 4
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/WidgetTypeMsgConstructor.java
  28. 4
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/WidgetsBundleMsgConstructor.java
  29. 137
      application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/AdminSettingsEdgeEventFetcher.java
  30. 47
      application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/AssetsEdgeEventFetcher.java
  31. 65
      application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/BasePageableEdgeEventFetcher.java
  32. 49
      application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/BaseUsersEdgeEventFetcher.java
  33. 49
      application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/BaseWidgetsBundlesEdgeEventFetcher.java
  34. 49
      application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/CustomerEdgeEventFetcher.java
  35. 41
      application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/CustomerUsersEdgeEventFetcher.java
  36. 47
      application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/DashboardsEdgeEventFetcher.java
  37. 47
      application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/DeviceProfilesEdgeEventFetcher.java
  38. 29
      application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/EdgeEventFetcher.java
  39. 49
      application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/GeneralEdgeEventFetcher.java
  40. 47
      application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/RuleChainsEdgeEventFetcher.java
  41. 36
      application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/SystemWidgetsBundlesEdgeEventFetcher.java
  42. 34
      application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/TenantAdminUsersEdgeEventFetcher.java
  43. 37
      application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/TenantWidgetsBundlesEdgeEventFetcher.java
  44. 41
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/AdminSettingsEdgeProcessor.java
  45. 78
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/AlarmEdgeProcessor.java
  46. 69
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/AssetEdgeProcessor.java
  47. 106
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessor.java
  48. 101
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/CustomerEdgeProcessor.java
  49. 74
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/DashboardEdgeProcessor.java
  50. 172
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/DeviceEdgeProcessor.java
  51. 63
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/DeviceProfileEdgeProcessor.java
  52. 102
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/EdgeProcessor.java
  53. 206
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/EntityEdgeProcessor.java
  54. 69
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/EntityViewEdgeProcessor.java
  55. 64
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/RelationEdgeProcessor.java
  56. 83
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/RuleChainEdgeProcessor.java
  57. 57
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/TelemetryEdgeProcessor.java
  58. 73
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/UserEdgeProcessor.java
  59. 62
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/WidgetBundleEdgeProcessor.java
  60. 63
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/WidgetTypeEdgeProcessor.java
  61. 515
      application/src/main/java/org/thingsboard/server/service/edge/rpc/sync/DefaultEdgeRequestsService.java
  62. 25
      application/src/main/java/org/thingsboard/server/service/edge/rpc/sync/EdgeRequestsService.java
  63. 33
      application/src/main/java/org/thingsboard/server/service/executors/GrpcCallbackExecutorService.java
  64. 7
      application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java
  65. 4
      application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/AbstractOAuth2ClientMapper.java
  66. 11
      application/src/main/java/org/thingsboard/server/service/ttl/EdgeEventsCleanUpService.java
  67. 5
      application/src/main/resources/thingsboard.yml
  68. 40
      application/src/test/java/org/thingsboard/server/controller/BaseEdgeControllerTest.java
  69. 163
      application/src/test/java/org/thingsboard/server/controller/BaseRuleChainControllerTest.java
  70. 23
      application/src/test/java/org/thingsboard/server/controller/sql/RuleChainControllerSqlTest.java
  71. 687
      application/src/test/java/org/thingsboard/server/edge/BaseEdgeTest.java
  72. 156
      application/src/test/java/org/thingsboard/server/edge/imitator/EdgeImitator.java
  73. 2
      application/src/test/resources/application-test.properties
  74. 2
      common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceService.java
  75. 1
      common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeEventService.java
  76. 10
      common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeService.java
  77. 2
      common/dao-api/src/main/java/org/thingsboard/server/dao/rule/RuleChainService.java
  78. 6
      common/data/src/main/java/org/thingsboard/server/common/data/EdgeUtils.java
  79. 73
      common/data/src/main/java/org/thingsboard/server/common/data/page/BasePageDataIterable.java
  80. 59
      common/data/src/main/java/org/thingsboard/server/common/data/page/PageDataIterable.java
  81. 39
      common/data/src/main/java/org/thingsboard/server/common/data/page/PageDataIterableByTenant.java
  82. 43
      common/data/src/main/java/org/thingsboard/server/common/data/page/PageDataIterableByTenantIdEntityId.java
  83. 37
      common/edge-api/src/main/java/org/thingsboard/edge/rpc/EdgeGrpcClient.java
  84. 12
      common/edge-api/src/main/java/org/thingsboard/edge/rpc/EdgeRpcClient.java
  85. 70
      common/edge-api/src/main/proto/edge.proto
  86. 2
      dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java
  87. 24
      dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java
  88. 5
      dao/src/main/java/org/thingsboard/server/dao/edge/BaseEdgeEventService.java
  89. 24
      dao/src/main/java/org/thingsboard/server/dao/edge/EdgeDao.java
  90. 6
      dao/src/main/java/org/thingsboard/server/dao/edge/EdgeEventDao.java
  91. 157
      dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java
  92. 4
      dao/src/main/java/org/thingsboard/server/dao/entity/AbstractEntityService.java
  93. 29
      dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java
  94. 4
      dao/src/main/java/org/thingsboard/server/dao/rule/RuleChainDao.java
  95. 11
      dao/src/main/java/org/thingsboard/server/dao/sql/edge/EdgeRepository.java
  96. 21
      dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaBaseEdgeEventDao.java
  97. 62
      dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaEdgeDao.java
  98. 33
      dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleChainDao.java
  99. 9
      dao/src/main/java/org/thingsboard/server/dao/sql/rule/RuleChainRepository.java
  100. 13
      dao/src/test/java/org/thingsboard/server/dao/service/AbstractServiceTest.java

162
application/src/main/data/json/demo/edge_management/rule_chains/edge_root_rule_chain.json

@ -1,162 +0,0 @@
{
"ruleChain": {
"additionalInfo": null,
"name": "Edge Root Rule Chain",
"type": "EDGE",
"firstRuleNodeId": null,
"root": true,
"debugMode": false,
"configuration": null
},
"metadata": {
"firstNodeIndex": 0,
"nodes": [
{
"additionalInfo": {
"description": "Process incoming messages from devices with the alarm rules defined in the device profile. Dispatch all incoming messages with \"Success\" relation type.",
"layoutX": 203,
"layoutY": 259
},
"type": "org.thingsboard.rule.engine.profile.TbDeviceProfileNode",
"name": "Device Profile Node",
"debugMode": false,
"configuration": {
"persistAlarmRulesState": false,
"fetchAlarmRulesStateOnStart": false
}
},
{
"additionalInfo": {
"layoutX": 823,
"layoutY": 157
},
"type": "org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode",
"name": "Save Timeseries",
"debugMode": false,
"configuration": {
"defaultTTL": 0
}
},
{
"additionalInfo": {
"layoutX": 824,
"layoutY": 52
},
"type": "org.thingsboard.rule.engine.telemetry.TbMsgAttributesNode",
"name": "Save Client Attributes",
"debugMode": false,
"configuration": {
"scope": "CLIENT_SCOPE"
}
},
{
"additionalInfo": {
"layoutX": 347,
"layoutY": 149
},
"type": "org.thingsboard.rule.engine.filter.TbMsgTypeSwitchNode",
"name": "Message Type Switch",
"debugMode": false,
"configuration": {
"version": 0
}
},
{
"additionalInfo": {
"layoutX": 825,
"layoutY": 266
},
"type": "org.thingsboard.rule.engine.action.TbLogNode",
"name": "Log RPC from Device",
"debugMode": false,
"configuration": {
"jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);"
}
},
{
"additionalInfo": {
"layoutX": 824,
"layoutY": 378
},
"type": "org.thingsboard.rule.engine.action.TbLogNode",
"name": "Log Other",
"debugMode": false,
"configuration": {
"jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);"
}
},
{
"additionalInfo": {
"layoutX": 824,
"layoutY": 466
},
"type": "org.thingsboard.rule.engine.rpc.TbSendRPCRequestNode",
"name": "RPC Call Request",
"debugMode": false,
"configuration": {
"timeoutInSeconds": 60
}
},
{
"additionalInfo": {
"layoutX": 1134,
"layoutY": 132
},
"type": "org.thingsboard.rule.engine.edge.TbMsgPushToCloudNode",
"name": "Push to cloud",
"debugMode": false,
"configuration": {
"version": 0
}
}
],
"connections": [
{
"fromIndex": 0,
"toIndex": 3,
"type": "Success"
},
{
"fromIndex": 1,
"toIndex": 7,
"type": "Success"
},
{
"fromIndex": 2,
"toIndex": 7,
"type": "Success"
},
{
"fromIndex": 3,
"toIndex": 6,
"type": "RPC Request to Device"
},
{
"fromIndex": 3,
"toIndex": 5,
"type": "Other"
},
{
"fromIndex": 3,
"toIndex": 2,
"type": "Post attributes"
},
{
"fromIndex": 3,
"toIndex": 1,
"type": "Post telemetry"
},
{
"fromIndex": 3,
"toIndex": 4,
"type": "RPC Request from Device"
},
{
"fromIndex": 4,
"toIndex": 7,
"type": "Success"
}
],
"ruleChainConnections": null
}
}

15
application/src/main/data/json/tenant/edge_management/rule_chains/edge_root_rule_chain.json

@ -14,8 +14,8 @@
{
"additionalInfo": {
"description": "Process incoming messages from devices with the alarm rules defined in the device profile. Dispatch all incoming messages with \"Success\" relation type.",
"layoutX": 203,
"layoutY": 259
"layoutX": 187,
"layoutY": 468
},
"type": "org.thingsboard.rule.engine.profile.TbDeviceProfileNode",
"name": "Device Profile Node",
@ -99,14 +99,14 @@
},
{
"additionalInfo": {
"layoutX": 1134,
"layoutY": 132
"layoutX": 1129,
"layoutY": 52
},
"type": "org.thingsboard.rule.engine.edge.TbMsgPushToCloudNode",
"name": "Push to cloud",
"debugMode": false,
"configuration": {
"version": 0
"scope": "SERVER_SCOPE"
}
}
],
@ -151,6 +151,11 @@
"toIndex": 4,
"type": "RPC Request from Device"
},
{
"fromIndex": 3,
"toIndex": 7,
"type": "Attributes Updated"
},
{
"fromIndex": 4,
"toIndex": 7,

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

@ -245,11 +245,11 @@ public class TenantActor extends RuleChainManagerActor {
EdgeId edgeId = new EdgeId(msg.getEntityId().getId());
EdgeRpcService edgeRpcService = systemContext.getEdgeRpcService();
if (msg.getEvent() == ComponentLifecycleEvent.DELETED) {
edgeRpcService.deleteEdge(edgeId);
edgeRpcService.deleteEdge(tenantId, edgeId);
} else {
Edge edge = systemContext.getEdgeService().findEdgeById(tenantId, edgeId);
if (msg.getEvent() == ComponentLifecycleEvent.UPDATED) {
edgeRpcService.updateEdge(edge);
edgeRpcService.updateEdge(tenantId, edge);
}
}
} else if (isRuleEngineForCurrentTenant) {
@ -277,7 +277,7 @@ public class TenantActor extends RuleChainManagerActor {
private void onToEdgeSessionMsg(EdgeEventUpdateMsg msg) {
log.trace("[{}] onToEdgeSessionMsg [{}]", msg.getTenantId(), msg);
systemContext.getEdgeRpcService().onEdgeEvent(msg.getEdgeId());
systemContext.getEdgeRpcService().onEdgeEvent(tenantId, msg.getEdgeId());
}
public static class ActorCreator extends ContextBasedCreator {

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

@ -37,12 +37,12 @@ import org.thingsboard.server.common.data.EdgeUtils;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.EntityView;
import org.thingsboard.server.common.data.EntityViewInfo;
import org.thingsboard.server.common.data.OtaPackage;
import org.thingsboard.server.common.data.OtaPackageInfo;
import org.thingsboard.server.common.data.HasName;
import org.thingsboard.server.common.data.HasTenantId;
import org.thingsboard.server.common.data.TbResourceInfo;
import org.thingsboard.server.common.data.OtaPackage;
import org.thingsboard.server.common.data.OtaPackageInfo;
import org.thingsboard.server.common.data.TbResource;
import org.thingsboard.server.common.data.TbResourceInfo;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.TenantInfo;
import org.thingsboard.server.common.data.TenantProfile;
@ -70,14 +70,15 @@ import org.thingsboard.server.common.data.id.EntityIdFactory;
import org.thingsboard.server.common.data.id.EntityViewId;
import org.thingsboard.server.common.data.id.OtaPackageId;
import org.thingsboard.server.common.data.id.RpcId;
import org.thingsboard.server.common.data.id.TbResourceId;
import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.RuleNodeId;
import org.thingsboard.server.common.data.id.TbResourceId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.TenantProfileId;
import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.id.WidgetTypeId;
import org.thingsboard.server.common.data.id.WidgetsBundleId;
import org.thingsboard.server.common.data.page.PageDataIterableByTenantIdEntityId;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.page.SortOrder;
import org.thingsboard.server.common.data.page.TimePageLink;
@ -103,10 +104,10 @@ import org.thingsboard.server.dao.edge.EdgeService;
import org.thingsboard.server.dao.entityview.EntityViewService;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.exception.IncorrectParameterException;
import org.thingsboard.server.dao.ota.OtaPackageService;
import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.dao.oauth2.OAuth2ConfigTemplateService;
import org.thingsboard.server.dao.oauth2.OAuth2Service;
import org.thingsboard.server.dao.ota.OtaPackageService;
import org.thingsboard.server.dao.relation.RelationService;
import org.thingsboard.server.dao.rpc.RpcService;
import org.thingsboard.server.dao.rule.RuleChainService;
@ -123,11 +124,10 @@ import org.thingsboard.server.queue.provider.TbQueueProducerProvider;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.action.RuleEngineEntityActionService;
import org.thingsboard.server.service.component.ComponentDiscoveryService;
import org.thingsboard.server.service.ota.OtaPackageStateService;
import org.thingsboard.server.service.edge.EdgeNotificationService;
import org.thingsboard.server.service.edge.rpc.EdgeGrpcService;
import org.thingsboard.server.service.edge.rpc.init.SyncEdgeService;
import org.thingsboard.server.service.edge.rpc.EdgeRpcService;
import org.thingsboard.server.service.lwm2m.LwM2MServerSecurityInfoRepository;
import org.thingsboard.server.service.ota.OtaPackageStateService;
import org.thingsboard.server.service.profile.TbDeviceProfileCache;
import org.thingsboard.server.service.queue.TbClusterService;
import org.thingsboard.server.service.resource.TbResourceService;
@ -141,6 +141,8 @@ import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService;
import javax.mail.MessagingException;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.Set;
@ -156,6 +158,8 @@ public abstract class BaseController {
protected static final String DEFAULT_DASHBOARD = "defaultDashboardId";
protected static final String HOME_DASHBOARD = "homeDashboardId";
private static final int DEFAULT_PAGE_SIZE = 1000;
private static final ObjectMapper json = new ObjectMapper();
@Autowired
@ -270,10 +274,7 @@ public abstract class BaseController {
protected EdgeNotificationService edgeNotificationService;
@Autowired(required = false)
protected SyncEdgeService syncEdgeService;
@Autowired(required = false)
protected EdgeGrpcService edgeGrpcService;
protected EdgeRpcService edgeGrpcService;
@Autowired
protected RuleEngineEntityActionService ruleEngineEntityActionService;
@ -917,11 +918,14 @@ public abstract class BaseController {
if (!edgesEnabled) {
return null;
}
List<EdgeId> result = null;
try {
result = edgeService.findRelatedEdgeIdsByEntityId(tenantId, entityId).get();
} catch (Exception e) {
log.error("[{}] can't find related edge ids for entity [{}]", tenantId, entityId, e);
if (EntityType.EDGE.equals(entityId.getEntityType())) {
return Collections.singletonList(new EdgeId(entityId.getId()));
}
PageDataIterableByTenantIdEntityId<EdgeId> relatedEdgeIdsIterator =
new PageDataIterableByTenantIdEntityId<>(edgeService::findRelatedEdgeIdsByEntityId, tenantId, entityId, DEFAULT_PAGE_SIZE);
List<EdgeId> result = new ArrayList<>();
for(EdgeId edgeId : relatedEdgeIdsIterator) {
result.add(edgeId);
}
return result;
}

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

@ -49,7 +49,6 @@ import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.exception.IncorrectParameterException;
import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.edge.rpc.EdgeGrpcSession;
import org.thingsboard.server.service.security.model.SecurityUser;
import org.thingsboard.server.service.security.permission.Operation;
import org.thingsboard.server.service.security.permission.Resource;
@ -128,7 +127,7 @@ public class EdgeController extends BaseController {
accessControlService.checkPermission(getCurrentUser(), Resource.EDGE, operation,
edge.getId(), edge);
Edge savedEdge = checkNotNull(edgeService.saveEdge(edge));
Edge savedEdge = checkNotNull(edgeService.saveEdge(edge, true));
if (created) {
ruleChainService.assignRuleChainToEdge(tenantId, edgeTemplateRootRuleChain.getId(), savedEdge.getId());
@ -536,9 +535,7 @@ public class EdgeController extends BaseController {
edgeId = checkNotNull(edgeId);
SecurityUser user = getCurrentUser();
TenantId tenantId = user.getTenantId();
EdgeGrpcSession session = edgeGrpcService.getEdgeGrpcSessionById(tenantId, edgeId);
Edge edge = session.getEdge();
syncEdgeService.sync(tenantId, edge);
edgeGrpcService.startSyncProcess(tenantId, edgeId);
} else {
throw new ThingsboardException("Edges support disabled", ThingsboardErrorCode.GENERAL);
}

15
application/src/main/java/org/thingsboard/server/controller/RuleChainController.java

@ -50,6 +50,7 @@ import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.RuleNodeId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageDataIterableByTenant;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
import org.thingsboard.server.common.data.rule.DefaultRuleChainCreateRequest;
@ -70,6 +71,7 @@ import org.thingsboard.server.service.script.RuleNodeJsScriptEngine;
import org.thingsboard.server.service.security.permission.Operation;
import org.thingsboard.server.service.security.permission.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
@ -86,6 +88,8 @@ public class RuleChainController extends BaseController {
public static final String RULE_CHAIN_ID = "ruleChainId";
public static final String RULE_NODE_ID = "ruleNodeId";
private static final int DEFAULT_PAGE_SIZE = 1000;
private static final ObjectMapper objectMapper = new ObjectMapper();
public static final int TIMEOUT = 20;
@ -479,7 +483,7 @@ public class RuleChainController extends BaseController {
return objectMapper.writeValueAsString(resultNode);
}
private JsonNode convertMsgToOut(TbMsg msg) throws Exception{
private JsonNode convertMsgToOut(TbMsg msg) throws Exception {
ObjectNode msgData = objectMapper.createObjectNode();
if (!StringUtils.isEmpty(msg.getData())) {
msgData.set("msg", objectMapper.readTree(msg.getData()));
@ -634,13 +638,20 @@ public class RuleChainController extends BaseController {
}
}
// TODO: @voba refactor this - add new config to edge rule chain to set it as auto-assign
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/ruleChain/autoAssignToEdgeRuleChains", method = RequestMethod.GET)
@ResponseBody
public List<RuleChain> getAutoAssignToEdgeRuleChains() throws ThingsboardException {
try {
TenantId tenantId = getCurrentUser().getTenantId();
return checkNotNull(ruleChainService.findAutoAssignToEdgeRuleChainsByTenantId(tenantId)).get();
List<RuleChain> result = new ArrayList<>();
PageDataIterableByTenant<RuleChain> autoAssignRuleChainsIterator =
new PageDataIterableByTenant<>(ruleChainService::findAutoAssignToEdgeRuleChainsByTenantId, tenantId, DEFAULT_PAGE_SIZE);
for (RuleChain ruleChain : autoAssignRuleChainsIterator) {
result.add(ruleChain);
}
return checkNotNull(result);
} catch (Exception e) {
throw handleException(e);
}

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

@ -95,9 +95,7 @@ public class TenantController extends BaseController {
tenant = checkNotNull(tenantService.saveTenant(tenant));
if (newTenant) {
installScripts.createDefaultRuleChains(tenant.getId());
if (edgesEnabled) {
installScripts.createDefaultEdgeRuleChains(tenant.getId());
}
installScripts.createDefaultEdgeRuleChains(tenant.getId());
}
tenantProfileCache.evict(tenant.getId());
tbClusterService.onTenantChange(tenant, null);

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

@ -15,9 +15,7 @@
*/
package org.thingsboard.server.service.edge;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
@ -25,45 +23,30 @@ import lombok.extern.slf4j.Slf4j;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.EdgeUtils;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.alarm.Alarm;
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.AlarmId;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.EntityIdFactory;
import org.thingsboard.server.common.data.id.RuleChainId;
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.common.data.page.TimePageLink;
import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.data.rule.RuleChainConnectionInfo;
import org.thingsboard.server.common.msg.queue.TbCallback;
import org.thingsboard.server.dao.alarm.AlarmService;
import org.thingsboard.server.dao.edge.EdgeEventService;
import org.thingsboard.server.dao.edge.EdgeService;
import org.thingsboard.server.dao.rule.RuleChainService;
import org.thingsboard.server.dao.user.UserService;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.edge.rpc.processor.AlarmEdgeProcessor;
import org.thingsboard.server.service.edge.rpc.processor.CustomerEdgeProcessor;
import org.thingsboard.server.service.edge.rpc.processor.EdgeProcessor;
import org.thingsboard.server.service.edge.rpc.processor.EntityEdgeProcessor;
import org.thingsboard.server.service.edge.rpc.processor.RelationEdgeProcessor;
import org.thingsboard.server.service.executors.DbCallbackExecutorService;
import org.thingsboard.server.service.queue.TbClusterService;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@ -73,30 +56,32 @@ import java.util.concurrent.Executors;
@Slf4j
public class DefaultEdgeNotificationService implements EdgeNotificationService {
private static final ObjectMapper mapper = new ObjectMapper();
@Autowired
private EdgeService edgeService;
private static final int DEFAULT_LIMIT = 100;
@Autowired
private EdgeEventService edgeEventService;
@Autowired
private EdgeService edgeService;
private TbClusterService clusterService;
@Autowired
private AlarmService alarmService;
private DbCallbackExecutorService dbCallbackExecutorService;
@Autowired
private UserService userService;
private EdgeProcessor edgeProcessor;
@Autowired
private RuleChainService ruleChainService;
private EntityEdgeProcessor entityProcessor;
@Autowired
private EdgeEventService edgeEventService;
private AlarmEdgeProcessor alarmProcessor;
@Autowired
private TbClusterService clusterService;
private RelationEdgeProcessor relationProcessor;
@Autowired
private DbCallbackExecutorService dbCallbackExecutorService;
private CustomerEdgeProcessor customerProcessor;
private ExecutorService tsCallBackExecutor;
@ -115,7 +100,7 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService {
@Override
public Edge setEdgeRootRuleChain(TenantId tenantId, Edge edge, RuleChainId ruleChainId) throws IOException {
edge.setRootRuleChainId(ruleChainId);
Edge savedEdge = edgeService.saveEdge(edge);
Edge savedEdge = edgeService.saveEdge(edge, true);
saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.RULE_CHAIN, EdgeEventActionType.UPDATED, ruleChainId, null);
return savedEdge;
}
@ -160,7 +145,7 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService {
EdgeEventType type = EdgeEventType.valueOf(edgeNotificationMsg.getType());
switch (type) {
case EDGE:
processEdge(tenantId, edgeNotificationMsg);
edgeProcessor.processEdgeNotification(tenantId, edgeNotificationMsg);
break;
case USER:
case ASSET:
@ -169,20 +154,20 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService {
case ENTITY_VIEW:
case DASHBOARD:
case RULE_CHAIN:
processEntity(tenantId, edgeNotificationMsg);
entityProcessor.processEntityNotification(tenantId, edgeNotificationMsg);
break;
case CUSTOMER:
processCustomer(tenantId, edgeNotificationMsg);
customerProcessor.processCustomerNotification(tenantId, edgeNotificationMsg);
break;
case WIDGETS_BUNDLE:
case WIDGET_TYPE:
processWidgetBundleOrWidgetType(tenantId, edgeNotificationMsg);
entityProcessor.processEntityNotificationForAllEdges(tenantId, edgeNotificationMsg);
break;
case ALARM:
processAlarm(tenantId, edgeNotificationMsg);
alarmProcessor.processAlarmNotification(tenantId, edgeNotificationMsg);
break;
case RELATION:
processRelation(tenantId, edgeNotificationMsg);
relationProcessor.processRelationNotification(tenantId, edgeNotificationMsg);
break;
default:
log.debug("Edge event type [{}] is not designed to be pushed to edge", type);
@ -195,311 +180,6 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService {
}
}
private void processEdge(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) {
try {
EdgeEventActionType actionType = EdgeEventActionType.valueOf(edgeNotificationMsg.getAction());
EdgeId edgeId = new EdgeId(new UUID(edgeNotificationMsg.getEntityIdMSB(), edgeNotificationMsg.getEntityIdLSB()));
ListenableFuture<Edge> edgeFuture;
switch (actionType) {
case ASSIGNED_TO_CUSTOMER:
CustomerId customerId = mapper.readValue(edgeNotificationMsg.getBody(), CustomerId.class);
edgeFuture = edgeService.findEdgeByIdAsync(tenantId, edgeId);
Futures.addCallback(edgeFuture, new FutureCallback<Edge>() {
@Override
public void onSuccess(@Nullable Edge edge) {
if (edge != null && !customerId.isNullUid()) {
saveEdgeEvent(edge.getTenantId(), edge.getId(), EdgeEventType.CUSTOMER, EdgeEventActionType.ADDED, customerId, null);
PageLink pageLink = new PageLink(DEFAULT_LIMIT);
PageData<User> pageData;
do {
pageData = userService.findCustomerUsers(tenantId, customerId, pageLink);
if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) {
log.trace("[{}] [{}] user(s) are going to be added to edge.", edge.getId(), pageData.getData().size());
for (User user : pageData.getData()) {
saveEdgeEvent(edge.getTenantId(), edge.getId(), EdgeEventType.USER, EdgeEventActionType.ADDED, user.getId(), null);
}
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
}
}
} while (pageData != null && pageData.hasNext());
}
}
@Override
public void onFailure(Throwable t) {
log.error("Can't find edge by id [{}]", edgeNotificationMsg, t);
}
}, dbCallbackExecutorService);
break;
case UNASSIGNED_FROM_CUSTOMER:
CustomerId customerIdToDelete = mapper.readValue(edgeNotificationMsg.getBody(), CustomerId.class);
edgeFuture = edgeService.findEdgeByIdAsync(tenantId, edgeId);
Futures.addCallback(edgeFuture, new FutureCallback<Edge>() {
@Override
public void onSuccess(@Nullable Edge edge) {
if (edge != null && !customerIdToDelete.isNullUid()) {
saveEdgeEvent(edge.getTenantId(), edge.getId(), EdgeEventType.CUSTOMER, EdgeEventActionType.DELETED, customerIdToDelete, null);
}
}
@Override
public void onFailure(Throwable t) {
log.error("Can't find edge by id [{}]", edgeNotificationMsg, t);
}
}, dbCallbackExecutorService);
break;
}
} catch (Exception e) {
log.error("Exception during processing edge event", e);
}
}
private void processWidgetBundleOrWidgetType(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) {
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) {
case ADDED:
case UPDATED:
case DELETED:
processActionForAllEdges(tenantId, type, actionType, entityId);
break;
}
}
private void processCustomer(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) {
EdgeEventActionType actionType = EdgeEventActionType.valueOf(edgeNotificationMsg.getAction());
EdgeEventType type = EdgeEventType.valueOf(edgeNotificationMsg.getType());
UUID uuid = new UUID(edgeNotificationMsg.getEntityIdMSB(), edgeNotificationMsg.getEntityIdLSB());
CustomerId customerId = new CustomerId(EntityIdFactory.getByEdgeEventTypeAndUuid(type, uuid).getId());
switch (actionType) {
case UPDATED:
PageLink pageLink = new PageLink(DEFAULT_LIMIT);
PageData<Edge> pageData;
do {
pageData = edgeService.findEdgesByTenantIdAndCustomerId(tenantId, customerId, pageLink);
if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) {
for (Edge edge : pageData.getData()) {
saveEdgeEvent(tenantId, edge.getId(), type, actionType, customerId, null);
}
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
}
}
} while (pageData != null && pageData.hasNext());
break;
case DELETED:
EdgeId edgeId = new EdgeId(new UUID(edgeNotificationMsg.getEdgeIdMSB(), edgeNotificationMsg.getEdgeIdLSB()));
saveEdgeEvent(tenantId, edgeId, type, actionType, customerId, null);
break;
}
}
private void processEntity(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) {
EdgeEventActionType actionType = EdgeEventActionType.valueOf(edgeNotificationMsg.getAction());
EdgeEventType type = EdgeEventType.valueOf(edgeNotificationMsg.getType());
EntityId entityId = EntityIdFactory.getByEdgeEventTypeAndUuid(type,
new UUID(edgeNotificationMsg.getEntityIdMSB(), edgeNotificationMsg.getEntityIdLSB()));
EdgeId edgeId = new EdgeId(new UUID(edgeNotificationMsg.getEdgeIdMSB(), edgeNotificationMsg.getEdgeIdLSB()));
ListenableFuture<List<EdgeId>> edgeIdsFuture;
switch (actionType) {
case ADDED: // used only for USER entity
case UPDATED:
case CREDENTIALS_UPDATED:
edgeIdsFuture = edgeService.findRelatedEdgeIdsByEntityId(tenantId, entityId);
Futures.addCallback(edgeIdsFuture, new FutureCallback<List<EdgeId>>() {
@Override
public void onSuccess(@Nullable List<EdgeId> edgeIds) {
if (edgeIds != null && !edgeIds.isEmpty()) {
for (EdgeId edgeId : edgeIds) {
saveEdgeEvent(tenantId, edgeId, type, actionType, entityId, null);
}
}
}
@Override
public void onFailure(Throwable throwable) {
log.error("Failed to find related edge ids [{}]", edgeNotificationMsg, throwable);
}
}, dbCallbackExecutorService);
break;
case ASSIGNED_TO_CUSTOMER:
case UNASSIGNED_FROM_CUSTOMER:
edgeIdsFuture = edgeService.findRelatedEdgeIdsByEntityId(tenantId, entityId);
Futures.addCallback(edgeIdsFuture, new FutureCallback<List<EdgeId>>() {
@Override
public void onSuccess(@Nullable List<EdgeId> edgeIds) {
if (edgeIds != null && !edgeIds.isEmpty()) {
for (EdgeId edgeId : edgeIds) {
try {
CustomerId customerId = mapper.readValue(edgeNotificationMsg.getBody(), CustomerId.class);
ListenableFuture<Edge> future = edgeService.findEdgeByIdAsync(tenantId, edgeId);
Futures.addCallback(future, new FutureCallback<Edge>() {
@Override
public void onSuccess(@Nullable Edge edge) {
if (edge != null && edge.getCustomerId() != null &&
!edge.getCustomerId().isNullUid() && edge.getCustomerId().equals(customerId)) {
saveEdgeEvent(tenantId, edgeId, type, actionType, entityId, null);
}
}
@Override
public void onFailure(Throwable throwable) {
log.error("Failed to find edge by id [{}]", edgeNotificationMsg, throwable);
}
}, dbCallbackExecutorService);
} catch (Exception e) {
log.error("Can't parse customer id from entity body [{}]", edgeNotificationMsg, e);
}
}
}
}
@Override
public void onFailure(Throwable throwable) {
log.error("Failed to find related edge ids [{}]", edgeNotificationMsg, throwable);
}
}, dbCallbackExecutorService);
break;
case DELETED:
saveEdgeEvent(tenantId, edgeId, type, actionType, entityId, null);
break;
case ASSIGNED_TO_EDGE:
case UNASSIGNED_FROM_EDGE:
saveEdgeEvent(tenantId, edgeId, type, actionType, entityId, null);
if (type.equals(EdgeEventType.RULE_CHAIN)) {
updateDependentRuleChains(tenantId, new RuleChainId(entityId.getId()), edgeId);
}
break;
}
}
private void updateDependentRuleChains(TenantId tenantId, RuleChainId processingRuleChainId, EdgeId edgeId) {
PageLink pageLink = new PageLink(DEFAULT_LIMIT);
PageData<RuleChain> pageData;
do {
pageData = ruleChainService.findRuleChainsByTenantIdAndEdgeId(tenantId, edgeId, pageLink);
if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) {
for (RuleChain ruleChain : pageData.getData()) {
if (!ruleChain.getId().equals(processingRuleChainId)) {
List<RuleChainConnectionInfo> connectionInfos =
ruleChainService.loadRuleChainMetaData(ruleChain.getTenantId(), ruleChain.getId()).getRuleChainConnections();
if (connectionInfos != null && !connectionInfos.isEmpty()) {
for (RuleChainConnectionInfo connectionInfo : connectionInfos) {
if (connectionInfo.getTargetRuleChainId().equals(processingRuleChainId)) {
saveEdgeEvent(tenantId,
edgeId,
EdgeEventType.RULE_CHAIN_METADATA,
EdgeEventActionType.UPDATED,
ruleChain.getId(),
null);
}
}
}
}
}
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
}
}
} while (pageData != null && pageData.hasNext());
}
private void processAlarm(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) {
AlarmId alarmId = new AlarmId(new UUID(edgeNotificationMsg.getEntityIdMSB(), edgeNotificationMsg.getEntityIdLSB()));
ListenableFuture<Alarm> alarmFuture = alarmService.findAlarmByIdAsync(tenantId, alarmId);
Futures.addCallback(alarmFuture, new FutureCallback<Alarm>() {
@Override
public void onSuccess(@Nullable Alarm alarm) {
if (alarm != null) {
EdgeEventType type = EdgeUtils.getEdgeEventTypeByEntityType(alarm.getOriginator().getEntityType());
if (type != null) {
ListenableFuture<List<EdgeId>> relatedEdgeIdsByEntityIdFuture = edgeService.findRelatedEdgeIdsByEntityId(tenantId, alarm.getOriginator());
Futures.addCallback(relatedEdgeIdsByEntityIdFuture, new FutureCallback<List<EdgeId>>() {
@Override
public void onSuccess(@Nullable List<EdgeId> relatedEdgeIdsByEntityId) {
if (relatedEdgeIdsByEntityId != null) {
for (EdgeId edgeId : relatedEdgeIdsByEntityId) {
saveEdgeEvent(tenantId,
edgeId,
EdgeEventType.ALARM,
EdgeEventActionType.valueOf(edgeNotificationMsg.getAction()),
alarmId,
null);
}
}
}
@Override
public void onFailure(Throwable t) {
log.warn("[{}] can't find related edge ids by entity id [{}]", tenantId.getId(), alarm.getOriginator(), t);
}
}, dbCallbackExecutorService);
}
}
}
@Override
public void onFailure(Throwable t) {
log.warn("[{}] can't find alarm by id [{}]", tenantId.getId(), alarmId.getId(), t);
}
}, dbCallbackExecutorService);
}
private void processRelation(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) throws JsonProcessingException {
EntityRelation relation = mapper.readValue(edgeNotificationMsg.getBody(), EntityRelation.class);
if (!relation.getFrom().getEntityType().equals(EntityType.EDGE) &&
!relation.getTo().getEntityType().equals(EntityType.EDGE)) {
List<ListenableFuture<List<EdgeId>>> futures = new ArrayList<>();
futures.add(edgeService.findRelatedEdgeIdsByEntityId(tenantId, relation.getTo()));
futures.add(edgeService.findRelatedEdgeIdsByEntityId(tenantId, relation.getFrom()));
ListenableFuture<List<List<EdgeId>>> combinedFuture = Futures.allAsList(futures);
Futures.addCallback(combinedFuture, new FutureCallback<List<List<EdgeId>>>() {
@Override
public void onSuccess(@Nullable List<List<EdgeId>> listOfListsEdgeIds) {
Set<EdgeId> uniqueEdgeIds = new HashSet<>();
if (listOfListsEdgeIds != null && !listOfListsEdgeIds.isEmpty()) {
for (List<EdgeId> listOfListsEdgeId : listOfListsEdgeIds) {
if (listOfListsEdgeId != null) {
uniqueEdgeIds.addAll(listOfListsEdgeId);
}
}
}
if (!uniqueEdgeIds.isEmpty()) {
for (EdgeId edgeId : uniqueEdgeIds) {
saveEdgeEvent(tenantId,
edgeId,
EdgeEventType.RELATION,
EdgeEventActionType.valueOf(edgeNotificationMsg.getAction()),
null,
mapper.valueToTree(relation));
}
}
}
@Override
public void onFailure(Throwable t) {
log.warn("[{}] can't find related edge ids by relation to id [{}] and relation from id [{}]" ,
tenantId.getId(), relation.getTo().getId(), relation.getFrom().getId(), t);
}
}, dbCallbackExecutorService);
}
}
private void processActionForAllEdges(TenantId tenantId, EdgeEventType type, EdgeEventActionType actionType, EntityId entityId) {
PageLink pageLink = new PageLink(DEFAULT_LIMIT);
PageData<Edge> pageData;
do {
pageData = edgeService.findEdgesByTenantId(tenantId, pageLink);
if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) {
for (Edge edge : pageData.getData()) {
saveEdgeEvent(tenantId, edge.getId(), type, actionType, entityId, null);
}
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
}
}
} while (pageData != null && pageData.hasNext());
}
}

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

@ -16,218 +16,130 @@
package org.thingsboard.server.service.edge;
import lombok.Data;
import lombok.Getter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import org.thingsboard.server.actors.service.ActorService;
import org.thingsboard.server.dao.alarm.AlarmService;
import org.thingsboard.server.dao.asset.AssetService;
import org.thingsboard.server.dao.attributes.AttributesService;
import org.thingsboard.server.dao.customer.CustomerService;
import org.thingsboard.server.dao.dashboard.DashboardService;
import org.thingsboard.server.dao.device.DeviceCredentialsService;
import org.thingsboard.server.dao.device.DeviceProfileService;
import org.thingsboard.server.dao.device.DeviceService;
import org.thingsboard.server.dao.edge.EdgeEventService;
import org.thingsboard.server.dao.edge.EdgeService;
import org.thingsboard.server.dao.entityview.EntityViewService;
import org.thingsboard.server.dao.relation.RelationService;
import org.thingsboard.server.dao.rule.RuleChainService;
import org.thingsboard.server.dao.settings.AdminSettingsService;
import org.thingsboard.server.dao.user.UserService;
import org.thingsboard.server.dao.widget.WidgetTypeService;
import org.thingsboard.server.dao.widget.WidgetsBundleService;
import org.thingsboard.server.queue.discovery.PartitionService;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.edge.rpc.EdgeEventStorageSettings;
import org.thingsboard.server.service.edge.rpc.constructor.AdminSettingsMsgConstructor;
import org.thingsboard.server.service.edge.rpc.constructor.AlarmMsgConstructor;
import org.thingsboard.server.service.edge.rpc.constructor.AssetMsgConstructor;
import org.thingsboard.server.service.edge.rpc.constructor.CustomerMsgConstructor;
import org.thingsboard.server.service.edge.rpc.constructor.DashboardMsgConstructor;
import org.thingsboard.server.service.edge.rpc.constructor.DeviceMsgConstructor;
import org.thingsboard.server.service.edge.rpc.constructor.DeviceProfileMsgConstructor;
import org.thingsboard.server.service.edge.rpc.constructor.EntityDataMsgConstructor;
import org.thingsboard.server.service.edge.rpc.constructor.EntityViewMsgConstructor;
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.UserMsgConstructor;
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.init.SyncEdgeService;
import org.thingsboard.server.service.edge.rpc.processor.AlarmProcessor;
import org.thingsboard.server.service.edge.rpc.processor.DeviceProcessor;
import org.thingsboard.server.service.edge.rpc.processor.RelationProcessor;
import org.thingsboard.server.service.edge.rpc.processor.TelemetryProcessor;
import org.thingsboard.server.service.edge.rpc.processor.AdminSettingsEdgeProcessor;
import org.thingsboard.server.service.edge.rpc.processor.AlarmEdgeProcessor;
import org.thingsboard.server.service.edge.rpc.processor.AssetEdgeProcessor;
import org.thingsboard.server.service.edge.rpc.processor.CustomerEdgeProcessor;
import org.thingsboard.server.service.edge.rpc.processor.DashboardEdgeProcessor;
import org.thingsboard.server.service.edge.rpc.processor.DeviceEdgeProcessor;
import org.thingsboard.server.service.edge.rpc.processor.DeviceProfileEdgeProcessor;
import org.thingsboard.server.service.edge.rpc.processor.EntityEdgeProcessor;
import org.thingsboard.server.service.edge.rpc.processor.EntityViewEdgeProcessor;
import org.thingsboard.server.service.edge.rpc.processor.RelationEdgeProcessor;
import org.thingsboard.server.service.edge.rpc.processor.RuleChainEdgeProcessor;
import org.thingsboard.server.service.edge.rpc.processor.TelemetryEdgeProcessor;
import org.thingsboard.server.service.edge.rpc.processor.UserEdgeProcessor;
import org.thingsboard.server.service.edge.rpc.processor.WidgetBundleEdgeProcessor;
import org.thingsboard.server.service.edge.rpc.processor.WidgetTypeEdgeProcessor;
import org.thingsboard.server.service.edge.rpc.sync.EdgeRequestsService;
import org.thingsboard.server.service.executors.DbCallbackExecutorService;
import org.thingsboard.server.service.queue.TbClusterService;
import org.thingsboard.server.service.state.DeviceStateService;
import org.thingsboard.server.service.executors.GrpcCallbackExecutorService;
@Component
@TbCoreComponent
@Data
@Lazy
public class EdgeContextComponent {
@Lazy
@Autowired
private EdgeService edgeService;
@Autowired
private PartitionService partitionService;
@Lazy
@Autowired
private EdgeEventService edgeEventService;
@Lazy
@Autowired
private AssetService assetService;
private AdminSettingsService adminSettingsService;
@Lazy
@Autowired
private DeviceService deviceService;
private AssetService assetService;
@Lazy
@Autowired
private DeviceProfileService deviceProfileService;
@Lazy
@Autowired
private DeviceCredentialsService deviceCredentialsService;
@Lazy
@Autowired
private EntityViewService entityViewService;
@Lazy
@Autowired
private AttributesService attributesService;
@Lazy
@Autowired
private CustomerService customerService;
@Lazy
@Autowired
private RelationService relationService;
@Lazy
@Autowired
private AlarmService alarmService;
@Lazy
@Autowired
private DashboardService dashboardService;
@Lazy
@Autowired
private RuleChainService ruleChainService;
@Lazy
@Autowired
private UserService userService;
@Lazy
@Autowired
private ActorService actorService;
@Lazy
@Autowired
private WidgetsBundleService widgetsBundleService;
@Lazy
@Autowired
private WidgetTypeService widgetTypeService;
@Lazy
@Autowired
private DeviceStateService deviceStateService;
@Lazy
@Autowired
private TbClusterService tbClusterService;
@Lazy
@Autowired
private SyncEdgeService syncEdgeService;
private EdgeRequestsService edgeRequestsService;
@Lazy
@Autowired
private RuleChainMsgConstructor ruleChainMsgConstructor;
private AlarmEdgeProcessor alarmProcessor;
@Lazy
@Autowired
private AlarmMsgConstructor alarmMsgConstructor;
private DeviceProfileEdgeProcessor deviceProfileProcessor;
@Lazy
@Autowired
private DeviceMsgConstructor deviceMsgConstructor;
private DeviceEdgeProcessor deviceProcessor;
@Lazy
@Autowired
private DeviceProfileMsgConstructor deviceProfileMsgConstructor;
private EntityEdgeProcessor entityProcessor;
@Lazy
@Autowired
private AssetMsgConstructor assetMsgConstructor;
private AssetEdgeProcessor assetProcessor;
@Lazy
@Autowired
private EntityViewMsgConstructor entityViewMsgConstructor;
private EntityViewEdgeProcessor entityViewProcessor;
@Lazy
@Autowired
private DashboardMsgConstructor dashboardMsgConstructor;
private UserEdgeProcessor userProcessor;
@Lazy
@Autowired
private CustomerMsgConstructor customerMsgConstructor;
private RelationEdgeProcessor relationProcessor;
@Lazy
@Autowired
private UserMsgConstructor userMsgConstructor;
private TelemetryEdgeProcessor telemetryProcessor;
@Lazy
@Autowired
private RelationMsgConstructor relationMsgConstructor;
private DashboardEdgeProcessor dashboardProcessor;
@Lazy
@Autowired
private WidgetsBundleMsgConstructor widgetsBundleMsgConstructor;
private RuleChainEdgeProcessor ruleChainProcessor;
@Lazy
@Autowired
private WidgetTypeMsgConstructor widgetTypeMsgConstructor;
private CustomerEdgeProcessor customerProcessor;
@Lazy
@Autowired
private AdminSettingsMsgConstructor adminSettingsMsgConstructor;
private WidgetBundleEdgeProcessor widgetBundleProcessor;
@Lazy
@Autowired
private EntityDataMsgConstructor entityDataMsgConstructor;
private WidgetTypeEdgeProcessor widgetTypeProcessor;
@Lazy
@Autowired
private AlarmProcessor alarmProcessor;
private AdminSettingsEdgeProcessor adminSettingsProcessor;
@Lazy
@Autowired
private DeviceProcessor deviceProcessor;
@Lazy
@Autowired
private RelationProcessor relationProcessor;
@Lazy
@Autowired
private TelemetryProcessor telemetryProcessor;
@Lazy
@Autowired
private EdgeEventStorageSettings edgeEventStorageSettings;
@Autowired
@Getter
private DbCallbackExecutorService dbCallbackExecutor;
@Autowired
private GrpcCallbackExecutorService grpcCallbackExecutorService;
}

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

@ -0,0 +1,48 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.edge.rpc;
import com.fasterxml.jackson.databind.JsonNode;
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.EdgeId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
public final class EdgeEventUtils {
private EdgeEventUtils() {
}
public static EdgeEvent constructEdgeEvent(TenantId tenantId,
EdgeId edgeId,
EdgeEventType type,
EdgeEventActionType action,
EntityId entityId,
JsonNode body) {
EdgeEvent edgeEvent = new EdgeEvent();
edgeEvent.setTenantId(tenantId);
edgeEvent.setEdgeId(edgeId);
edgeEvent.setType(type);
edgeEvent.setAction(action);
if (entityId != null) {
edgeEvent.setEntityId(entityId.getId());
}
edgeEvent.setBody(body);
return edgeEvent;
}
}

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

@ -16,8 +16,8 @@
package org.thingsboard.server.service.edge.rpc;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.io.Resources;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import io.grpc.Server;
import io.grpc.netty.NettyServerBuilder;
import io.grpc.stub.StreamObserver;
@ -29,16 +29,15 @@ import org.springframework.stereotype.Service;
import org.thingsboard.common.util.ThingsBoardThreadFactory;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.ResourceUtils;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
import org.thingsboard.server.common.data.kv.BooleanDataEntry;
import org.thingsboard.server.common.data.kv.LongDataEntry;
import org.thingsboard.server.gen.edge.EdgeRpcServiceGrpc;
import org.thingsboard.server.gen.edge.RequestMsg;
import org.thingsboard.server.gen.edge.ResponseMsg;
import org.thingsboard.server.gen.edge.v1.EdgeRpcServiceGrpc;
import org.thingsboard.server.gen.edge.v1.RequestMsg;
import org.thingsboard.server.gen.edge.v1.ResponseMsg;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.edge.EdgeContextComponent;
import org.thingsboard.server.service.state.DefaultDeviceStateService;
@ -47,10 +46,10 @@ import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService;
import javax.annotation.Nullable;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
@ -59,6 +58,8 @@ import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
@Service
@Slf4j
@ -67,7 +68,8 @@ import java.util.concurrent.TimeUnit;
public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase implements EdgeRpcService {
private final ConcurrentMap<EdgeId, EdgeGrpcSession> sessions = new ConcurrentHashMap<>();
private final ConcurrentMap<EdgeId, Boolean> sessionNewEvents = new ConcurrentHashMap<>();
private final ConcurrentMap<EdgeId, Lock> sessionNewEventsLocks = new ConcurrentHashMap<>();
private final Map<EdgeId, Boolean> sessionNewEvents = new HashMap<>();
private final ConcurrentMap<EdgeId, ScheduledFuture<?>> sessionEdgeEventChecks = new ConcurrentHashMap<>();
private static final ObjectMapper mapper = new ObjectMapper();
@ -83,10 +85,15 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i
private boolean persistToTelemetry;
@Value("${edges.rpc.client_max_keep_alive_time_sec}")
private int clientMaxKeepAliveTimeSec;
@Value("${edges.rpc.max_inbound_message_size:4194304}")
private int maxInboundMessageSize;
@Value("${edges.scheduler_pool_size}")
private int schedulerPoolSize;
@Value("${edges.send_scheduler_pool_size}")
private int sendSchedulerPoolSize;
@Autowired
private EdgeContextComponent ctx;
@ -95,13 +102,16 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i
private Server server;
private ScheduledExecutorService scheduler;
private ScheduledExecutorService edgeEventProcessingExecutorService;
private ScheduledExecutorService sendDownlinkExecutorService;
@PostConstruct
public void init() {
log.info("Initializing Edge RPC service!");
NettyServerBuilder builder = NettyServerBuilder.forPort(rpcPort)
.permitKeepAliveTime(clientMaxKeepAliveTimeSec, TimeUnit.SECONDS)
.maxInboundMessageSize(maxInboundMessageSize)
.addService(this);
if (sslEnabled) {
try {
@ -121,7 +131,8 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i
log.error("Failed to start Edge RPC server!", e);
throw new RuntimeException("Failed to start Edge RPC server!");
}
this.scheduler = Executors.newScheduledThreadPool(schedulerPoolSize, ThingsBoardThreadFactory.forName("edge-scheduler"));
this.edgeEventProcessingExecutorService = Executors.newScheduledThreadPool(schedulerPoolSize, ThingsBoardThreadFactory.forName("edge-scheduler"));
this.sendDownlinkExecutorService = Executors.newScheduledThreadPool(sendSchedulerPoolSize, ThingsBoardThreadFactory.forName("edge-send-scheduler"));
log.info("Edge RPC service initialized!");
}
@ -138,62 +149,84 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i
sessionEdgeEventChecks.remove(edgeId);
}
}
if (scheduler != null) {
scheduler.shutdownNow();
if (edgeEventProcessingExecutorService != null) {
edgeEventProcessingExecutorService.shutdownNow();
}
if (sendDownlinkExecutorService != null) {
sendDownlinkExecutorService.shutdownNow();
}
}
@Override
public StreamObserver<RequestMsg> handleMsgs(StreamObserver<ResponseMsg> outputStream) {
return new EdgeGrpcSession(ctx, outputStream, this::onEdgeConnect, this::onEdgeDisconnect, mapper).getInputStream();
return new EdgeGrpcSession(ctx, outputStream, this::onEdgeConnect, this::onEdgeDisconnect, mapper, sendDownlinkExecutorService).getInputStream();
}
@Override
public void updateEdge(Edge edge) {
public void updateEdge(TenantId tenantId, Edge edge) {
EdgeGrpcSession session = sessions.get(edge.getId());
if (session != null && session.isConnected()) {
log.debug("[{}] Updating configuration for edge [{}] [{}]", edge.getTenantId(), edge.getName(), edge.getId());
log.debug("[{}] Updating configuration for edge [{}] [{}]", tenantId, edge.getName(), edge.getId());
session.onConfigurationUpdate(edge);
} else {
log.debug("[{}] Session doesn't exist for edge [{}] [{}]", edge.getTenantId(), edge.getName(), edge.getId());
log.debug("[{}] Session doesn't exist for edge [{}] [{}]", tenantId, edge.getName(), edge.getId());
}
}
@Override
public void deleteEdge(EdgeId edgeId) {
public void deleteEdge(TenantId tenantId, EdgeId edgeId) {
EdgeGrpcSession session = sessions.get(edgeId);
if (session != null && session.isConnected()) {
log.info("Closing and removing session for edge [{}]", edgeId);
log.info("[{}] Closing and removing session for edge [{}]", tenantId, edgeId);
session.close();
sessions.remove(edgeId);
sessionNewEvents.remove(edgeId);
final Lock newEventLock = sessionNewEventsLocks.computeIfAbsent(edgeId, id -> new ReentrantLock());
newEventLock.lock();
try {
sessionNewEvents.remove(edgeId);
} finally {
newEventLock.unlock();
}
cancelScheduleEdgeEventsCheck(edgeId);
}
}
@Override
public void onEdgeEvent(EdgeId edgeId) {
log.trace("[{}] onEdgeEvent", edgeId.getId());
if (Boolean.FALSE.equals(sessionNewEvents.get(edgeId))) {
log.trace("[{}] set session new events flag to true", edgeId.getId());
sessionNewEvents.put(edgeId, true);
public void onEdgeEvent(TenantId tenantId, EdgeId edgeId) {
log.trace("[{}] onEdgeEvent [{}]", tenantId, edgeId.getId());
final Lock newEventLock = sessionNewEventsLocks.computeIfAbsent(edgeId, id -> new ReentrantLock());
newEventLock.lock();
try {
if (Boolean.FALSE.equals(sessionNewEvents.get(edgeId))) {
log.trace("[{}] set session new events flag to true [{}]", tenantId, edgeId.getId());
sessionNewEvents.put(edgeId, true);
}
} finally {
newEventLock.unlock();
}
}
private void onEdgeConnect(EdgeId edgeId, EdgeGrpcSession edgeGrpcSession) {
log.info("[{}] edge [{}] connected successfully.", edgeGrpcSession.getSessionId(), edgeId);
sessions.put(edgeId, edgeGrpcSession);
sessionNewEvents.put(edgeId, false);
final Lock newEventLock = sessionNewEventsLocks.computeIfAbsent(edgeId, id -> new ReentrantLock());
newEventLock.lock();
try {
sessionNewEvents.put(edgeId, true);
} finally {
newEventLock.unlock();
}
save(edgeId, DefaultDeviceStateService.ACTIVITY_STATE, true);
save(edgeId, DefaultDeviceStateService.LAST_CONNECT_TIME, System.currentTimeMillis());
cancelScheduleEdgeEventsCheck(edgeId);
scheduleEdgeEventsCheck(edgeGrpcSession);
}
public EdgeGrpcSession getEdgeGrpcSessionById(TenantId tenantId, EdgeId edgeId) {
@Override
public void startSyncProcess(TenantId tenantId, EdgeId edgeId) {
EdgeGrpcSession session = sessions.get(edgeId);
if (session != null && session.isConnected()) {
return session;
session.startSyncProcess(tenantId, edgeId);
} else {
log.error("[{}] Edge is not connected [{}]", tenantId, edgeId);
throw new RuntimeException("Edge is not connected");
@ -204,19 +237,37 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i
EdgeId edgeId = session.getEdge().getId();
UUID tenantId = session.getEdge().getTenantId().getId();
if (sessions.containsKey(edgeId)) {
ScheduledFuture<?> schedule = scheduler.schedule(() -> {
ScheduledFuture<?> edgeEventCheckTask = edgeEventProcessingExecutorService.schedule(() -> {
try {
if (Boolean.TRUE.equals(sessionNewEvents.get(edgeId))) {
log.trace("[{}] Set session new events flag to false", edgeId.getId());
sessionNewEvents.put(edgeId, false);
session.processEdgeEvents();
final Lock newEventLock = sessionNewEventsLocks.computeIfAbsent(edgeId, id -> new ReentrantLock());
newEventLock.lock();
try {
if (Boolean.TRUE.equals(sessionNewEvents.get(edgeId))) {
log.trace("[{}] Set session new events flag to false", edgeId.getId());
sessionNewEvents.put(edgeId, false);
Futures.addCallback(session.processEdgeEvents(), new FutureCallback<>() {
@Override
public void onSuccess(Void result) {
scheduleEdgeEventsCheck(session);
}
@Override
public void onFailure(Throwable t) {
log.warn("[{}] Failed to process edge events for edge [{}]!", tenantId, session.getEdge().getId().getId(), t);
scheduleEdgeEventsCheck(session);
}
}, ctx.getGrpcCallbackExecutorService());
} else {
scheduleEdgeEventsCheck(session);
}
} finally {
newEventLock.unlock();
}
} catch (Exception e) {
log.warn("[{}] Failed to process edge events for edge [{}]!", tenantId, session.getEdge().getId().getId(), e);
}
scheduleEdgeEventsCheck(session);
}, ctx.getEdgeEventStorageSettings().getNoRecordsSleepInterval(), TimeUnit.MILLISECONDS);
sessionEdgeEventChecks.put(edgeId, schedule);
sessionEdgeEventChecks.put(edgeId, edgeEventCheckTask);
log.trace("[{}] Check edge event scheduled for edge [{}]", tenantId, edgeId.getId());
} else {
log.debug("[{}] Session was removed and edge event check schedule must not be started [{}]",
@ -238,7 +289,13 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i
private void onEdgeDisconnect(EdgeId edgeId) {
log.info("[{}] edge disconnected!", edgeId);
sessions.remove(edgeId);
sessionNewEvents.remove(edgeId);
final Lock newEventLock = sessionNewEventsLocks.computeIfAbsent(edgeId, id -> new ReentrantLock());
newEventLock.lock();
try {
sessionNewEvents.remove(edgeId);
} finally {
newEventLock.unlock();
}
save(edgeId, DefaultDeviceStateService.ACTIVITY_STATE, false);
save(edgeId, DefaultDeviceStateService.LAST_DISCONNECT_TIME, System.currentTimeMillis());
cancelScheduleEdgeEventsCheck(edgeId);

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

File diff suppressed because it is too large

9
application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeRpcService.java

@ -17,12 +17,15 @@ package org.thingsboard.server.service.edge.rpc;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.TenantId;
public interface EdgeRpcService {
void updateEdge(Edge edge);
void updateEdge(TenantId tenantId, Edge edge);
void deleteEdge(EdgeId edgeId);
void deleteEdge(TenantId tenantId, EdgeId edgeId);
void onEdgeEvent(EdgeId edgeId);
void onEdgeEvent(TenantId tenantId, EdgeId edgeId);
void startSyncProcess(TenantId tenantId, EdgeId edgeId);
}

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

@ -0,0 +1,74 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.edge.rpc;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.service.edge.EdgeContextComponent;
import org.thingsboard.server.service.edge.rpc.fetch.AdminSettingsEdgeEventFetcher;
import org.thingsboard.server.service.edge.rpc.fetch.AssetsEdgeEventFetcher;
import org.thingsboard.server.service.edge.rpc.fetch.CustomerEdgeEventFetcher;
import org.thingsboard.server.service.edge.rpc.fetch.CustomerUsersEdgeEventFetcher;
import org.thingsboard.server.service.edge.rpc.fetch.DashboardsEdgeEventFetcher;
import org.thingsboard.server.service.edge.rpc.fetch.DeviceProfilesEdgeEventFetcher;
import org.thingsboard.server.service.edge.rpc.fetch.EdgeEventFetcher;
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.TenantAdminUsersEdgeEventFetcher;
import org.thingsboard.server.service.edge.rpc.fetch.TenantWidgetsBundlesEdgeEventFetcher;
import java.util.LinkedList;
import java.util.List;
import java.util.NoSuchElementException;
public class EdgeSyncCursor {
List<EdgeEventFetcher> fetchers = new LinkedList<>();
int currentIdx = 0;
public EdgeSyncCursor(EdgeContextComponent ctx, Edge edge) {
fetchers.add(new SystemWidgetsBundlesEdgeEventFetcher(ctx.getWidgetsBundleService()));
fetchers.add(new TenantWidgetsBundlesEdgeEventFetcher(ctx.getWidgetsBundleService()));
fetchers.add(new DeviceProfilesEdgeEventFetcher(ctx.getDeviceProfileService()));
fetchers.add(new RuleChainsEdgeEventFetcher(ctx.getRuleChainService()));
fetchers.add(new TenantAdminUsersEdgeEventFetcher(ctx.getUserService()));
if (edge.getCustomerId() != null && !EntityId.NULL_UUID.equals(edge.getCustomerId().getId())) {
fetchers.add(new CustomerEdgeEventFetcher());
fetchers.add(new CustomerUsersEdgeEventFetcher(ctx.getUserService(), edge.getCustomerId()));
}
fetchers.add(new AdminSettingsEdgeEventFetcher(ctx.getAdminSettingsService()));
fetchers.add(new AssetsEdgeEventFetcher(ctx.getAssetService()));
fetchers.add(new DashboardsEdgeEventFetcher(ctx.getDashboardService()));
}
public boolean hasNext() {
return fetchers.size() > currentIdx;
}
public EdgeEventFetcher getNext() {
if (!hasNext()) {
throw new NoSuchElementException();
}
EdgeEventFetcher edgeEventFetcher = fetchers.get(currentIdx);
currentIdx++;
return edgeEventFetcher;
}
public int getCurrentIdx() {
return currentIdx;
}
}

2
application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AdminSettingsMsgConstructor.java

@ -18,7 +18,7 @@ package org.thingsboard.server.service.edge.rpc.constructor;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.AdminSettings;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.gen.edge.AdminSettingsUpdateMsg;
import org.thingsboard.server.gen.edge.v1.AdminSettingsUpdateMsg;
import org.thingsboard.server.queue.util.TbCoreComponent;
@Component

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

@ -26,8 +26,8 @@ import org.thingsboard.server.dao.asset.AssetService;
import org.thingsboard.server.dao.device.DeviceService;
import org.thingsboard.server.dao.entityview.EntityViewService;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.gen.edge.AlarmUpdateMsg;
import org.thingsboard.server.gen.edge.UpdateMsgType;
import org.thingsboard.server.gen.edge.v1.AlarmUpdateMsg;
import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
import org.thingsboard.server.queue.util.TbCoreComponent;
@Component

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

@ -20,8 +20,8 @@ import org.thingsboard.server.common.data.asset.Asset;
import org.thingsboard.server.common.data.id.AssetId;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.gen.edge.AssetUpdateMsg;
import org.thingsboard.server.gen.edge.UpdateMsgType;
import org.thingsboard.server.gen.edge.v1.AssetUpdateMsg;
import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
import org.thingsboard.server.queue.util.TbCoreComponent;
@Component

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

@ -19,8 +19,8 @@ import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.gen.edge.CustomerUpdateMsg;
import org.thingsboard.server.gen.edge.UpdateMsgType;
import org.thingsboard.server.gen.edge.v1.CustomerUpdateMsg;
import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
import org.thingsboard.server.queue.util.TbCoreComponent;
@Component

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

@ -20,8 +20,8 @@ import org.thingsboard.server.common.data.Dashboard;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DashboardId;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.gen.edge.DashboardUpdateMsg;
import org.thingsboard.server.gen.edge.UpdateMsgType;
import org.thingsboard.server.gen.edge.v1.DashboardUpdateMsg;
import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
import org.thingsboard.server.queue.util.TbCoreComponent;
@Component

10
application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DeviceMsgConstructor.java

@ -23,11 +23,11 @@ import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.gen.edge.DeviceCredentialsUpdateMsg;
import org.thingsboard.server.gen.edge.DeviceRpcCallMsg;
import org.thingsboard.server.gen.edge.DeviceUpdateMsg;
import org.thingsboard.server.gen.edge.RpcRequestMsg;
import org.thingsboard.server.gen.edge.UpdateMsgType;
import org.thingsboard.server.gen.edge.v1.DeviceCredentialsUpdateMsg;
import org.thingsboard.server.gen.edge.v1.DeviceRpcCallMsg;
import org.thingsboard.server.gen.edge.v1.DeviceUpdateMsg;
import org.thingsboard.server.gen.edge.v1.RpcRequestMsg;
import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
import org.thingsboard.server.queue.util.TbCoreComponent;
import java.util.UUID;

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

@ -21,8 +21,8 @@ import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.transport.util.DataDecodingEncodingService;
import org.thingsboard.server.gen.edge.DeviceProfileUpdateMsg;
import org.thingsboard.server.gen.edge.UpdateMsgType;
import org.thingsboard.server.gen.edge.v1.DeviceProfileUpdateMsg;
import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
import org.thingsboard.server.queue.util.TbCoreComponent;
@Component
@ -41,7 +41,7 @@ public class DeviceProfileMsgConstructor {
.setDefault(deviceProfile.isDefault())
.setType(deviceProfile.getType().name())
.setProfileDataBytes(ByteString.copyFrom(dataDecodingEncodingService.encode(deviceProfile.getProfileData())));
// TODO: voba - should this be always null at the moment??
// TODO: @voba - add possibility to setup edge rule chain as device profile default
// if (deviceProfile.getDefaultRuleChainId() != null) {
// builder.setDefaultRuleChainIdMSB(deviceProfile.getDefaultRuleChainId().getId().getMostSignificantBits())
// .setDefaultRuleChainIdLSB(deviceProfile.getDefaultRuleChainId().getId().getLeastSignificantBits());

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

@ -25,8 +25,8 @@ import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.edge.EdgeEventActionType;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.transport.adaptor.JsonConverter;
import org.thingsboard.server.gen.edge.AttributeDeleteMsg;
import org.thingsboard.server.gen.edge.EntityDataProto;
import org.thingsboard.server.gen.edge.v1.AttributeDeleteMsg;
import org.thingsboard.server.gen.edge.v1.EntityDataProto;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.queue.util.TbCoreComponent;

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

@ -20,9 +20,9 @@ import org.thingsboard.server.common.data.EntityView;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EntityViewId;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.gen.edge.EdgeEntityType;
import org.thingsboard.server.gen.edge.EntityViewUpdateMsg;
import org.thingsboard.server.gen.edge.UpdateMsgType;
import org.thingsboard.server.gen.edge.v1.EdgeEntityType;
import org.thingsboard.server.gen.edge.v1.EntityViewUpdateMsg;
import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
import org.thingsboard.server.queue.util.TbCoreComponent;
@Component

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

@ -18,8 +18,8 @@ package org.thingsboard.server.service.edge.rpc.constructor;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.gen.edge.RelationUpdateMsg;
import org.thingsboard.server.gen.edge.UpdateMsgType;
import org.thingsboard.server.gen.edge.v1.RelationUpdateMsg;
import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
import org.thingsboard.server.queue.util.TbCoreComponent;
@Component

12
application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RuleChainMsgConstructor.java

@ -26,12 +26,12 @@ import org.thingsboard.server.common.data.rule.RuleChainConnectionInfo;
import org.thingsboard.server.common.data.rule.RuleChainMetaData;
import org.thingsboard.server.common.data.rule.RuleNode;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.gen.edge.NodeConnectionInfoProto;
import org.thingsboard.server.gen.edge.RuleChainConnectionInfoProto;
import org.thingsboard.server.gen.edge.RuleChainMetadataUpdateMsg;
import org.thingsboard.server.gen.edge.RuleChainUpdateMsg;
import org.thingsboard.server.gen.edge.RuleNodeProto;
import org.thingsboard.server.gen.edge.UpdateMsgType;
import org.thingsboard.server.gen.edge.v1.NodeConnectionInfoProto;
import org.thingsboard.server.gen.edge.v1.RuleChainConnectionInfoProto;
import org.thingsboard.server.gen.edge.v1.RuleChainMetadataUpdateMsg;
import org.thingsboard.server.gen.edge.v1.RuleChainUpdateMsg;
import org.thingsboard.server.gen.edge.v1.RuleNodeProto;
import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
import org.thingsboard.server.queue.util.TbCoreComponent;
import java.util.ArrayList;

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

@ -21,9 +21,9 @@ import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.security.UserCredentials;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.gen.edge.UpdateMsgType;
import org.thingsboard.server.gen.edge.UserCredentialsUpdateMsg;
import org.thingsboard.server.gen.edge.UserUpdateMsg;
import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
import org.thingsboard.server.gen.edge.v1.UserCredentialsUpdateMsg;
import org.thingsboard.server.gen.edge.v1.UserUpdateMsg;
import org.thingsboard.server.queue.util.TbCoreComponent;
@Component

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

@ -20,8 +20,8 @@ import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.WidgetTypeId;
import org.thingsboard.server.common.data.widget.WidgetType;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.gen.edge.UpdateMsgType;
import org.thingsboard.server.gen.edge.WidgetTypeUpdateMsg;
import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
import org.thingsboard.server.gen.edge.v1.WidgetTypeUpdateMsg;
import org.thingsboard.server.queue.util.TbCoreComponent;
@Component

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

@ -20,8 +20,8 @@ import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.WidgetsBundleId;
import org.thingsboard.server.common.data.widget.WidgetsBundle;
import org.thingsboard.server.gen.edge.UpdateMsgType;
import org.thingsboard.server.gen.edge.WidgetsBundleUpdateMsg;
import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
import org.thingsboard.server.gen.edge.v1.WidgetsBundleUpdateMsg;
import org.thingsboard.server.queue.util.TbCoreComponent;
import java.nio.charset.StandardCharsets;

137
application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/AdminSettingsEdgeEventFetcher.java

@ -0,0 +1,137 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.edge.rpc.fetch;
import com.datastax.oss.driver.api.core.uuid.Uuids;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.text.WordUtils;
import org.springframework.core.io.DefaultResourceLoader;
import org.thingsboard.server.common.data.AdminSettings;
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.AdminSettingsId;
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.settings.AdminSettingsService;
import org.thingsboard.server.service.edge.rpc.EdgeEventUtils;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@AllArgsConstructor
@Slf4j
public class AdminSettingsEdgeEventFetcher implements EdgeEventFetcher {
private static final ObjectMapper mapper = new ObjectMapper();
private final AdminSettingsService adminSettingsService;
@Override
public PageLink getPageLink(int pageSize) {
return null;
}
@Override
public PageData<EdgeEvent> fetchEdgeEvents(TenantId tenantId, Edge edge, PageLink pageLink) throws Exception {
List<EdgeEvent> result = new ArrayList<>();
AdminSettings systemMailSettings = adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, "mail");
result.add(EdgeEventUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.ADMIN_SETTINGS,
EdgeEventActionType.UPDATED, null, mapper.valueToTree(systemMailSettings)));
AdminSettings tenantMailSettings = convertToTenantAdminSettings(systemMailSettings.getKey(), (ObjectNode) systemMailSettings.getJsonValue());
result.add(EdgeEventUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.ADMIN_SETTINGS,
EdgeEventActionType.UPDATED, null, mapper.valueToTree(tenantMailSettings)));
AdminSettings systemMailTemplates = loadMailTemplates();
result.add(EdgeEventUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.ADMIN_SETTINGS,
EdgeEventActionType.UPDATED, null, mapper.valueToTree(systemMailTemplates)));
AdminSettings tenantMailTemplates = convertToTenantAdminSettings(systemMailTemplates.getKey(), (ObjectNode) systemMailTemplates.getJsonValue());
result.add(EdgeEventUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.ADMIN_SETTINGS,
EdgeEventActionType.UPDATED, null, mapper.valueToTree(tenantMailTemplates)));
// @voba - returns PageData object to be in sync with other fetchers
return new PageData<>(result, 1, result.size(), false);
}
private AdminSettings loadMailTemplates() throws Exception {
Map<String, Object> mailTemplates = new HashMap<>();
Pattern startPattern = Pattern.compile("<div class=\"content\".*?>");
Pattern endPattern = Pattern.compile("<div class=\"footer\".*?>");
File[] files = new DefaultResourceLoader().getResource("classpath:/templates/").getFile().listFiles();
for (File file : files) {
Map<String, String> mailTemplate = new HashMap<>();
String name = validateName(file.getName());
String stringTemplate = FileUtils.readFileToString(file, StandardCharsets.UTF_8);
Matcher start = startPattern.matcher(stringTemplate);
Matcher end = endPattern.matcher(stringTemplate);
if (start.find() && end.find()) {
String body = StringUtils.substringBetween(stringTemplate, start.group(), end.group()).replaceAll("\t", "");
String subject = StringUtils.substringBetween(body, "<h2>", "</h2>");
mailTemplate.put("subject", subject);
mailTemplate.put("body", body);
mailTemplates.put(name, mailTemplate);
} else {
log.error("Can't load mail template from file {}", file.getName());
}
}
AdminSettings adminSettings = new AdminSettings();
adminSettings.setId(new AdminSettingsId(Uuids.timeBased()));
adminSettings.setKey("mailTemplates");
adminSettings.setJsonValue(mapper.convertValue(mailTemplates, JsonNode.class));
return adminSettings;
}
private String validateName(String name) throws Exception {
StringBuilder nameBuilder = new StringBuilder();
name = name.replace(".vm", "");
String[] nameParts = name.split("\\.");
if (nameParts.length >= 1) {
nameBuilder.append(nameParts[0]);
for (int i = 1; i < nameParts.length; i++) {
String word = WordUtils.capitalize(nameParts[i]);
nameBuilder.append(word);
}
return nameBuilder.toString();
} else {
throw new Exception("Error during filename validation");
}
}
private AdminSettings convertToTenantAdminSettings(String key, ObjectNode jsonValue) {
AdminSettings tenantMailSettings = new AdminSettings();
jsonValue.put("useSystemMailSettings", true);
tenantMailSettings.setJsonValue(jsonValue);
tenantMailSettings.setKey(key);
return tenantMailSettings;
}
}

47
application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/AssetsEdgeEventFetcher.java

@ -0,0 +1,47 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.edge.rpc.fetch;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.thingsboard.server.common.data.asset.Asset;
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.asset.AssetService;
import org.thingsboard.server.service.edge.rpc.EdgeEventUtils;
@AllArgsConstructor
@Slf4j
public class AssetsEdgeEventFetcher extends BasePageableEdgeEventFetcher<Asset> {
private final AssetService assetService;
@Override
PageData<Asset> fetchPageData(TenantId tenantId, Edge edge, PageLink pageLink) {
return assetService.findAssetsByTenantIdAndEdgeId(tenantId, edge.getId(), pageLink);
}
@Override
EdgeEvent constructEdgeEvent(TenantId tenantId, Edge edge, Asset asset) {
return EdgeEventUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.ASSET,
EdgeEventActionType.ADDED, asset.getId(), null);
}
}

65
application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/BasePageableEdgeEventFetcher.java

@ -0,0 +1,65 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.edge.rpc.fetch;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.thingsboard.server.common.data.BaseData;
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.EntityId;
import org.thingsboard.server.common.data.id.EventId;
import org.thingsboard.server.common.data.id.HasId;
import org.thingsboard.server.common.data.id.HasUUID;
import org.thingsboard.server.common.data.id.IdBased;
import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.UUIDBased;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.service.edge.rpc.EdgeEventUtils;
import java.util.ArrayList;
import java.util.List;
@Slf4j
public abstract class BasePageableEdgeEventFetcher<T> implements EdgeEventFetcher {
@Override
public PageLink getPageLink(int pageSize) {
return new PageLink(pageSize);
}
@Override
public PageData<EdgeEvent> fetchEdgeEvents(TenantId tenantId, Edge edge, PageLink pageLink) {
log.trace("[{}] start fetching edge events [{}]", tenantId, edge.getId());
PageData<T> pageData = fetchPageData(tenantId, edge, pageLink);
List<EdgeEvent> result = new ArrayList<>();
if (!pageData.getData().isEmpty()) {
for (T entity : pageData.getData()) {
result.add(constructEdgeEvent(tenantId, edge, entity));
}
}
return new PageData<>(result, pageData.getTotalPages(), pageData.getTotalElements(), pageData.hasNext());
}
abstract PageData<T> fetchPageData(TenantId tenantId, Edge edge, PageLink pageLink);
abstract EdgeEvent constructEdgeEvent(TenantId tenantId, Edge edge, T entity);
}

49
application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/BaseUsersEdgeEventFetcher.java

@ -0,0 +1,49 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.edge.rpc.fetch;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.thingsboard.server.common.data.User;
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.user.UserService;
import org.thingsboard.server.service.edge.rpc.EdgeEventUtils;
@Slf4j
@AllArgsConstructor
public abstract class BaseUsersEdgeEventFetcher extends BasePageableEdgeEventFetcher<User> {
protected final UserService userService;
@Override
PageData<User> fetchPageData(TenantId tenantId, Edge edge, PageLink pageLink) {
return findUsers(tenantId, pageLink);
}
@Override
EdgeEvent constructEdgeEvent(TenantId tenantId, Edge edge, User user) {
return EdgeEventUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.USER,
EdgeEventActionType.ADDED, user.getId(), null);
}
protected abstract PageData<User> findUsers(TenantId tenantId, PageLink pageLink);
}

49
application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/BaseWidgetsBundlesEdgeEventFetcher.java

@ -0,0 +1,49 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.edge.rpc.fetch;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
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.common.data.widget.WidgetsBundle;
import org.thingsboard.server.dao.widget.WidgetsBundleService;
import org.thingsboard.server.service.edge.rpc.EdgeEventUtils;
@Slf4j
@AllArgsConstructor
public abstract class BaseWidgetsBundlesEdgeEventFetcher extends BasePageableEdgeEventFetcher<WidgetsBundle> {
protected final WidgetsBundleService widgetsBundleService;
@Override
PageData<WidgetsBundle> fetchPageData(TenantId tenantId, Edge edge, PageLink pageLink) {
return findWidgetsBundles(tenantId, pageLink);
}
@Override
EdgeEvent constructEdgeEvent(TenantId tenantId, Edge edge, WidgetsBundle widgetsBundle) {
return EdgeEventUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.WIDGETS_BUNDLE,
EdgeEventActionType.ADDED, widgetsBundle.getId(), null);
}
protected abstract PageData<WidgetsBundle> findWidgetsBundles(TenantId tenantId, PageLink pageLink);
}

49
application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/CustomerEdgeEventFetcher.java

@ -0,0 +1,49 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.edge.rpc.fetch;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
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.service.edge.rpc.EdgeEventUtils;
import java.util.ArrayList;
import java.util.List;
@AllArgsConstructor
@Slf4j
public class CustomerEdgeEventFetcher implements EdgeEventFetcher {
@Override
public PageLink getPageLink(int pageSize) {
return null;
}
@Override
public PageData<EdgeEvent> fetchEdgeEvents(TenantId tenantId, Edge edge, PageLink pageLink) {
List<EdgeEvent> result = new ArrayList<>();
result.add(EdgeEventUtils.constructEdgeEvent(edge.getTenantId(), edge.getId(),
EdgeEventType.CUSTOMER, EdgeEventActionType.ADDED, edge.getCustomerId(), null));
// @voba - returns PageData object to be in sync with other fetchers
return new PageData<>(result, 1, result.size(), false);
}
}

41
application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/CustomerUsersEdgeEventFetcher.java

@ -0,0 +1,41 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.edge.rpc.fetch;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.edge.EdgeEvent;
import org.thingsboard.server.common.data.id.CustomerId;
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.user.UserService;
public class CustomerUsersEdgeEventFetcher extends BaseUsersEdgeEventFetcher {
private final CustomerId customerId;
public CustomerUsersEdgeEventFetcher(UserService userService, CustomerId customerId) {
super(userService);
this.customerId = customerId;
}
@Override
protected PageData<User> findUsers(TenantId tenantId, PageLink pageLink) {
return userService.findCustomerUsers(tenantId, customerId, pageLink);
}
}

47
application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/DashboardsEdgeEventFetcher.java

@ -0,0 +1,47 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.edge.rpc.fetch;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.thingsboard.server.common.data.DashboardInfo;
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.dashboard.DashboardService;
import org.thingsboard.server.service.edge.rpc.EdgeEventUtils;
@AllArgsConstructor
@Slf4j
public class DashboardsEdgeEventFetcher extends BasePageableEdgeEventFetcher<DashboardInfo> {
private final DashboardService dashboardService;
@Override
PageData<DashboardInfo> fetchPageData(TenantId tenantId, Edge edge, PageLink pageLink) {
return dashboardService.findDashboardsByTenantIdAndEdgeId(tenantId, edge.getId(), pageLink);
}
@Override
EdgeEvent constructEdgeEvent(TenantId tenantId, Edge edge, DashboardInfo dashboardInfo) {
return EdgeEventUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.DASHBOARD,
EdgeEventActionType.ADDED, dashboardInfo.getId(), null);
}
}

47
application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/DeviceProfilesEdgeEventFetcher.java

@ -0,0 +1,47 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.edge.rpc.fetch;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.thingsboard.server.common.data.DeviceProfile;
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.device.DeviceProfileService;
import org.thingsboard.server.service.edge.rpc.EdgeEventUtils;
@AllArgsConstructor
@Slf4j
public class DeviceProfilesEdgeEventFetcher extends BasePageableEdgeEventFetcher<DeviceProfile> {
private final DeviceProfileService deviceProfileService;
@Override
PageData<DeviceProfile> fetchPageData(TenantId tenantId, Edge edge, PageLink pageLink) {
return deviceProfileService.findDeviceProfiles(tenantId, pageLink);
}
@Override
EdgeEvent constructEdgeEvent(TenantId tenantId, Edge edge, DeviceProfile deviceProfile) {
return EdgeEventUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE_PROFILE,
EdgeEventActionType.ADDED, deviceProfile.getId(), null);
}
}

29
application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/EdgeEventFetcher.java

@ -0,0 +1,29 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.edge.rpc.fetch;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.edge.EdgeEvent;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
public interface EdgeEventFetcher {
PageLink getPageLink(int pageSize);
PageData<EdgeEvent> fetchEdgeEvents(TenantId tenantId, Edge edge, PageLink pageLink) throws Exception;
}

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

@ -0,0 +1,49 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.edge.rpc.fetch;
import lombok.AllArgsConstructor;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.edge.EdgeEvent;
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.common.data.page.SortOrder;
import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.dao.edge.EdgeEventService;
@AllArgsConstructor
public class GeneralEdgeEventFetcher implements EdgeEventFetcher {
private final Long queueStartTs;
private final EdgeEventService edgeEventService;
@Override
public PageLink getPageLink(int pageSize) {
return new TimePageLink(
pageSize,
0,
null,
new SortOrder("createdTime", SortOrder.Direction.ASC),
queueStartTs,
null);
}
@Override
public PageData<EdgeEvent> fetchEdgeEvents(TenantId tenantId, Edge edge, PageLink pageLink) {
return edgeEventService.findEdgeEvents(tenantId, edge.getId(), (TimePageLink) pageLink, true);
}
}

47
application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/RuleChainsEdgeEventFetcher.java

@ -0,0 +1,47 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.edge.rpc.fetch;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
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.common.data.rule.RuleChain;
import org.thingsboard.server.dao.rule.RuleChainService;
import org.thingsboard.server.service.edge.rpc.EdgeEventUtils;
@Slf4j
@AllArgsConstructor
public class RuleChainsEdgeEventFetcher extends BasePageableEdgeEventFetcher<RuleChain> {
private final RuleChainService ruleChainService;
@Override
PageData<RuleChain> fetchPageData(TenantId tenantId, Edge edge, PageLink pageLink) {
return ruleChainService.findRuleChainsByTenantIdAndEdgeId(tenantId, edge.getId(), pageLink);
}
@Override
EdgeEvent constructEdgeEvent(TenantId tenantId, Edge edge, RuleChain ruleChain) {
return EdgeEventUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.RULE_CHAIN,
EdgeEventActionType.ADDED, ruleChain.getId(), null);
}
}

36
application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/SystemWidgetsBundlesEdgeEventFetcher.java

@ -0,0 +1,36 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.edge.rpc.fetch;
import lombok.extern.slf4j.Slf4j;
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.common.data.widget.WidgetsBundle;
import org.thingsboard.server.dao.widget.WidgetsBundleService;
@Slf4j
public class SystemWidgetsBundlesEdgeEventFetcher extends BaseWidgetsBundlesEdgeEventFetcher {
public SystemWidgetsBundlesEdgeEventFetcher(WidgetsBundleService widgetsBundleService) {
super(widgetsBundleService);
}
@Override
protected PageData<WidgetsBundle> findWidgetsBundles(TenantId tenantId, PageLink pageLink) {
return widgetsBundleService.findSystemWidgetsBundlesByPageLink(tenantId, pageLink);
}
}

34
application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/TenantAdminUsersEdgeEventFetcher.java

@ -0,0 +1,34 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.edge.rpc.fetch;
import org.thingsboard.server.common.data.User;
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.user.UserService;
public class TenantAdminUsersEdgeEventFetcher extends BaseUsersEdgeEventFetcher {
public TenantAdminUsersEdgeEventFetcher(UserService userService) {
super(userService);
}
@Override
protected PageData<User> findUsers(TenantId tenantId, PageLink pageLink) {
return userService.findTenantAdmins(tenantId, pageLink);
}
}

37
application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/TenantWidgetsBundlesEdgeEventFetcher.java

@ -0,0 +1,37 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.edge.rpc.fetch;
import lombok.extern.slf4j.Slf4j;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.edge.EdgeEvent;
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.common.data.widget.WidgetsBundle;
import org.thingsboard.server.dao.widget.WidgetsBundleService;
@Slf4j
public class TenantWidgetsBundlesEdgeEventFetcher extends BaseWidgetsBundlesEdgeEventFetcher {
public TenantWidgetsBundlesEdgeEventFetcher(WidgetsBundleService widgetsBundleService) {
super(widgetsBundleService);
}
@Override
protected PageData<WidgetsBundle> findWidgetsBundles(TenantId tenantId, PageLink pageLink) {
return widgetsBundleService.findAllTenantWidgetsBundlesByTenantIdAndPageLink(tenantId, pageLink);
}
}

41
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/AdminSettingsEdgeProcessor.java

@ -0,0 +1,41 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.edge.rpc.processor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.AdminSettings;
import org.thingsboard.server.common.data.EdgeUtils;
import org.thingsboard.server.common.data.edge.EdgeEvent;
import org.thingsboard.server.gen.edge.v1.AdminSettingsUpdateMsg;
import org.thingsboard.server.gen.edge.v1.DownlinkMsg;
import org.thingsboard.server.queue.util.TbCoreComponent;
@Component
@Slf4j
@TbCoreComponent
public class AdminSettingsEdgeProcessor extends BaseEdgeProcessor {
public DownlinkMsg processAdminSettingsToEdge(EdgeEvent edgeEvent) {
AdminSettings adminSettings = mapper.convertValue(edgeEvent.getBody(), AdminSettings.class);
AdminSettingsUpdateMsg adminSettingsUpdateMsg = adminSettingsMsgConstructor.constructAdminSettingsUpdateMsg(adminSettings);
return DownlinkMsg.newBuilder()
.setDownlinkMsgId(EdgeUtils.nextPositiveInt())
.addAdminSettingsUpdateMsg(adminSettingsUpdateMsg)
.build();
}
}

78
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/AlarmProcessor.java → application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/AlarmEdgeProcessor.java

@ -15,25 +15,41 @@
*/
package org.thingsboard.server.service.edge.rpc.processor;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.EdgeUtils;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.alarm.Alarm;
import org.thingsboard.server.common.data.alarm.AlarmSeverity;
import org.thingsboard.server.common.data.alarm.AlarmStatus;
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.AlarmId;
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.gen.edge.AlarmUpdateMsg;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.gen.edge.v1.AlarmUpdateMsg;
import org.thingsboard.server.gen.edge.v1.DownlinkMsg;
import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.queue.util.TbCoreComponent;
import java.util.UUID;
@Component
@Slf4j
@TbCoreComponent
public class AlarmProcessor extends BaseProcessor {
public class AlarmEdgeProcessor extends BaseEdgeProcessor {
public ListenableFuture<Void> onAlarmUpdate(TenantId tenantId, AlarmUpdateMsg alarmUpdateMsg) {
public ListenableFuture<Void> processAlarmFromEdge(TenantId tenantId, AlarmUpdateMsg alarmUpdateMsg) {
log.trace("[{}] onAlarmUpdate [{}]", tenantId, alarmUpdateMsg);
EntityId originatorId = getAlarmOriginator(tenantId, alarmUpdateMsg.getOriginatorName(),
EntityType.valueOf(alarmUpdateMsg.getOriginatorType()));
@ -96,4 +112,60 @@ public class AlarmProcessor extends BaseProcessor {
return null;
}
}
public DownlinkMsg processAlarmToEdge(Edge edge, EdgeEvent edgeEvent, UpdateMsgType msgType) {
DownlinkMsg downlinkMsg = null;
try {
AlarmId alarmId = new AlarmId(edgeEvent.getEntityId());
Alarm alarm = alarmService.findAlarmByIdAsync(edgeEvent.getTenantId(), alarmId).get();
if (alarm != null) {
downlinkMsg = DownlinkMsg.newBuilder()
.setDownlinkMsgId(EdgeUtils.nextPositiveInt())
.addAlarmUpdateMsg(alarmMsgConstructor.constructAlarmUpdatedMsg(edge.getTenantId(), msgType, alarm))
.build();
}
} catch (Exception e) {
log.error("Can't process alarm msg [{}] [{}]", edgeEvent, msgType, e);
}
return downlinkMsg;
}
public void processAlarmNotification(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) {
AlarmId alarmId = new AlarmId(new UUID(edgeNotificationMsg.getEntityIdMSB(), edgeNotificationMsg.getEntityIdLSB()));
ListenableFuture<Alarm> alarmFuture = alarmService.findAlarmByIdAsync(tenantId, alarmId);
Futures.addCallback(alarmFuture, new FutureCallback<Alarm>() {
@Override
public void onSuccess(@Nullable Alarm alarm) {
if (alarm != null) {
EdgeEventType type = EdgeUtils.getEdgeEventTypeByEntityType(alarm.getOriginator().getEntityType());
if (type != null) {
PageLink pageLink = new PageLink(DEFAULT_PAGE_SIZE);
PageData<EdgeId> pageData;
do {
pageData = edgeService.findRelatedEdgeIdsByEntityId(tenantId, alarm.getOriginator(), pageLink);
if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) {
for (EdgeId edgeId : pageData.getData()) {
saveEdgeEvent(tenantId,
edgeId,
EdgeEventType.ALARM,
EdgeEventActionType.valueOf(edgeNotificationMsg.getAction()),
alarmId,
null);
}
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
}
}
} while (pageData != null && pageData.hasNext());
}
}
}
@Override
public void onFailure(Throwable t) {
log.warn("[{}] can't find alarm by id [{}] {}", tenantId.getId(), alarmId.getId(), t);
}
}, dbCallbackExecutorService);
}
}

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

@ -0,0 +1,69 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.edge.rpc.processor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.EdgeUtils;
import org.thingsboard.server.common.data.asset.Asset;
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.id.AssetId;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.gen.edge.v1.AssetUpdateMsg;
import org.thingsboard.server.gen.edge.v1.DownlinkMsg;
import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
import org.thingsboard.server.queue.util.TbCoreComponent;
@Component
@Slf4j
@TbCoreComponent
public class AssetEdgeProcessor extends BaseEdgeProcessor {
public DownlinkMsg processAssetToEdge(Edge edge, EdgeEvent edgeEvent, UpdateMsgType msgType, EdgeEventActionType action) {
AssetId assetId = new AssetId(edgeEvent.getEntityId());
DownlinkMsg downlinkMsg = null;
switch (action) {
case ADDED:
case UPDATED:
case ASSIGNED_TO_EDGE:
case ASSIGNED_TO_CUSTOMER:
case UNASSIGNED_FROM_CUSTOMER:
Asset asset = assetService.findAssetById(edgeEvent.getTenantId(), assetId);
if (asset != null) {
CustomerId customerId = getCustomerIdIfEdgeAssignedToCustomer(asset, edge);
AssetUpdateMsg assetUpdateMsg =
assetMsgConstructor.constructAssetUpdatedMsg(msgType, asset, customerId);
downlinkMsg = DownlinkMsg.newBuilder()
.setDownlinkMsgId(EdgeUtils.nextPositiveInt())
.addAssetUpdateMsg(assetUpdateMsg)
.build();
}
break;
case DELETED:
case UNASSIGNED_FROM_EDGE:
AssetUpdateMsg assetUpdateMsg =
assetMsgConstructor.constructAssetDeleteMsg(assetId);
downlinkMsg = DownlinkMsg.newBuilder()
.setDownlinkMsgId(EdgeUtils.nextPositiveInt())
.addAssetUpdateMsg(assetUpdateMsg)
.build();
break;
}
return downlinkMsg;
}
}

106
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/BaseProcessor.java → application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessor.java

@ -23,35 +23,62 @@ import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.springframework.beans.factory.annotation.Autowired;
import org.thingsboard.server.common.data.HasCustomerId;
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.CustomerId;
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.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.dao.alarm.AlarmService;
import org.thingsboard.server.dao.asset.AssetService;
import org.thingsboard.server.dao.attributes.AttributesService;
import org.thingsboard.server.dao.customer.CustomerService;
import org.thingsboard.server.dao.dashboard.DashboardService;
import org.thingsboard.server.dao.device.DeviceCredentialsService;
import org.thingsboard.server.dao.device.DeviceProfileService;
import org.thingsboard.server.dao.device.DeviceService;
import org.thingsboard.server.dao.edge.EdgeEventService;
import org.thingsboard.server.dao.edge.EdgeService;
import org.thingsboard.server.dao.entityview.EntityViewService;
import org.thingsboard.server.dao.relation.RelationService;
import org.thingsboard.server.dao.rule.RuleChainService;
import org.thingsboard.server.dao.user.UserService;
import org.thingsboard.server.dao.widget.WidgetTypeService;
import org.thingsboard.server.dao.widget.WidgetsBundleService;
import org.thingsboard.server.service.edge.rpc.constructor.AdminSettingsMsgConstructor;
import org.thingsboard.server.service.edge.rpc.constructor.AlarmMsgConstructor;
import org.thingsboard.server.service.edge.rpc.constructor.AssetMsgConstructor;
import org.thingsboard.server.service.edge.rpc.constructor.CustomerMsgConstructor;
import org.thingsboard.server.service.edge.rpc.constructor.DashboardMsgConstructor;
import org.thingsboard.server.service.edge.rpc.constructor.DeviceMsgConstructor;
import org.thingsboard.server.service.edge.rpc.constructor.DeviceProfileMsgConstructor;
import org.thingsboard.server.service.edge.rpc.constructor.EntityDataMsgConstructor;
import org.thingsboard.server.service.edge.rpc.constructor.EntityViewMsgConstructor;
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.UserMsgConstructor;
import org.thingsboard.server.service.edge.rpc.constructor.WidgetTypeMsgConstructor;
import org.thingsboard.server.service.edge.rpc.constructor.WidgetsBundleMsgConstructor;
import org.thingsboard.server.service.executors.DbCallbackExecutorService;
import org.thingsboard.server.service.profile.DefaultTbDeviceProfileCache;
import org.thingsboard.server.service.profile.TbDeviceProfileCache;
import org.thingsboard.server.service.queue.TbClusterService;
import org.thingsboard.server.service.state.DeviceStateService;
@Slf4j
public abstract class BaseProcessor {
public abstract class BaseEdgeProcessor {
protected static final ObjectMapper mapper = new ObjectMapper();
protected static final int DEFAULT_PAGE_SIZE = 1000;
@Autowired
protected RuleChainService ruleChainService;
@Autowired
protected AlarmService alarmService;
@ -79,6 +106,9 @@ public abstract class BaseProcessor {
@Autowired
protected UserService userService;
@Autowired
protected DeviceProfileService deviceProfileService;
@Autowired
protected RelationService relationService;
@ -97,6 +127,54 @@ public abstract class BaseProcessor {
@Autowired
protected EdgeEventService edgeEventService;
@Autowired
protected WidgetsBundleService widgetsBundleService;
@Autowired
protected WidgetTypeService widgetTypeService;
@Autowired
protected EntityDataMsgConstructor entityDataMsgConstructor;
@Autowired
protected RuleChainMsgConstructor ruleChainMsgConstructor;
@Autowired
protected AlarmMsgConstructor alarmMsgConstructor;
@Autowired
protected DeviceMsgConstructor deviceMsgConstructor;
@Autowired
protected AssetMsgConstructor assetMsgConstructor;
@Autowired
protected EntityViewMsgConstructor entityViewMsgConstructor;
@Autowired
protected DashboardMsgConstructor dashboardMsgConstructor;
@Autowired
protected RelationMsgConstructor relationMsgConstructor;
@Autowired
protected UserMsgConstructor userMsgConstructor;
@Autowired
protected CustomerMsgConstructor customerMsgConstructor;
@Autowired
protected DeviceProfileMsgConstructor deviceProfileMsgConstructor;
@Autowired
protected WidgetsBundleMsgConstructor widgetsBundleMsgConstructor;
@Autowired
protected WidgetTypeMsgConstructor widgetTypeMsgConstructor;
@Autowired
protected AdminSettingsMsgConstructor adminSettingsMsgConstructor;
@Autowired
protected DbCallbackExecutorService dbCallbackExecutorService;
@ -133,4 +211,28 @@ public abstract class BaseProcessor {
}, dbCallbackExecutorService);
return future;
}
protected CustomerId getCustomerIdIfEdgeAssignedToCustomer(HasCustomerId hasCustomerIdEntity, Edge edge) {
if (!edge.getCustomerId().isNullUid() && edge.getCustomerId().equals(hasCustomerIdEntity.getCustomerId())) {
return edge.getCustomerId();
} else {
return null;
}
}
protected void processActionForAllEdges(TenantId tenantId, EdgeEventType type, EdgeEventActionType actionType, EntityId entityId) {
PageLink pageLink = new PageLink(DEFAULT_PAGE_SIZE);
PageData<Edge> pageData;
do {
pageData = edgeService.findEdgesByTenantId(tenantId, pageLink);
if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) {
for (Edge edge : pageData.getData()) {
saveEdgeEvent(tenantId, edge.getId(), type, actionType, entityId, null);
}
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
}
}
} while (pageData != null && pageData.hasNext());
}
}

101
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/CustomerEdgeProcessor.java

@ -0,0 +1,101 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.edge.rpc.processor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.Customer;
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.EdgeEventActionType;
import org.thingsboard.server.common.data.edge.EdgeEventType;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.EntityIdFactory;
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.gen.edge.v1.CustomerUpdateMsg;
import org.thingsboard.server.gen.edge.v1.DownlinkMsg;
import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.queue.util.TbCoreComponent;
import java.util.UUID;
@Component
@Slf4j
@TbCoreComponent
public class CustomerEdgeProcessor extends BaseEdgeProcessor {
public DownlinkMsg processCustomerToEdge(EdgeEvent edgeEvent, UpdateMsgType msgType, EdgeEventActionType action) {
CustomerId customerId = new CustomerId(edgeEvent.getEntityId());
DownlinkMsg downlinkMsg = null;
switch (action) {
case ADDED:
case UPDATED:
Customer customer = customerService.findCustomerById(edgeEvent.getTenantId(), customerId);
if (customer != null) {
CustomerUpdateMsg customerUpdateMsg =
customerMsgConstructor.constructCustomerUpdatedMsg(msgType, customer);
downlinkMsg = DownlinkMsg.newBuilder()
.setDownlinkMsgId(EdgeUtils.nextPositiveInt())
.addCustomerUpdateMsg(customerUpdateMsg)
.build();
}
break;
case DELETED:
CustomerUpdateMsg customerUpdateMsg =
customerMsgConstructor.constructCustomerDeleteMsg(customerId);
downlinkMsg = DownlinkMsg.newBuilder()
.setDownlinkMsgId(EdgeUtils.nextPositiveInt())
.addCustomerUpdateMsg(customerUpdateMsg)
.build();
break;
}
return downlinkMsg;
}
public void processCustomerNotification(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) {
EdgeEventActionType actionType = EdgeEventActionType.valueOf(edgeNotificationMsg.getAction());
EdgeEventType type = EdgeEventType.valueOf(edgeNotificationMsg.getType());
UUID uuid = new UUID(edgeNotificationMsg.getEntityIdMSB(), edgeNotificationMsg.getEntityIdLSB());
CustomerId customerId = new CustomerId(EntityIdFactory.getByEdgeEventTypeAndUuid(type, uuid).getId());
switch (actionType) {
case UPDATED:
PageLink pageLink = new PageLink(DEFAULT_PAGE_SIZE);
PageData<Edge> pageData;
do {
pageData = edgeService.findEdgesByTenantIdAndCustomerId(tenantId, customerId, pageLink);
if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) {
for (Edge edge : pageData.getData()) {
saveEdgeEvent(tenantId, edge.getId(), type, actionType, customerId, null);
}
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
}
}
} while (pageData != null && pageData.hasNext());
break;
case DELETED:
EdgeId edgeId = new EdgeId(new UUID(edgeNotificationMsg.getEdgeIdMSB(), edgeNotificationMsg.getEdgeIdLSB()));
saveEdgeEvent(tenantId, edgeId, type, actionType, customerId, null);
break;
}
}
}

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

@ -0,0 +1,74 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.edge.rpc.processor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.Dashboard;
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.EdgeEventActionType;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DashboardId;
import org.thingsboard.server.gen.edge.v1.DashboardUpdateMsg;
import org.thingsboard.server.gen.edge.v1.DownlinkMsg;
import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
import org.thingsboard.server.queue.util.TbCoreComponent;
import java.util.Collections;
@Component
@Slf4j
@TbCoreComponent
public class DashboardEdgeProcessor extends BaseEdgeProcessor {
public DownlinkMsg processDashboardToEdge(Edge edge, EdgeEvent edgeEvent, UpdateMsgType msgType, EdgeEventActionType action) {
DashboardId dashboardId = new DashboardId(edgeEvent.getEntityId());
DownlinkMsg downlinkMsg = null;
switch (action) {
case ADDED:
case UPDATED:
case ASSIGNED_TO_EDGE:
case ASSIGNED_TO_CUSTOMER:
case UNASSIGNED_FROM_CUSTOMER:
Dashboard dashboard = dashboardService.findDashboardById(edgeEvent.getTenantId(), dashboardId);
if (dashboard != null) {
CustomerId customerId = null;
if (!edge.getCustomerId().isNullUid() && dashboard.isAssignedToCustomer(edge.getCustomerId())) {
customerId = edge.getCustomerId();
}
DashboardUpdateMsg dashboardUpdateMsg =
dashboardMsgConstructor.constructDashboardUpdatedMsg(msgType, dashboard, customerId);
downlinkMsg = DownlinkMsg.newBuilder()
.setDownlinkMsgId(EdgeUtils.nextPositiveInt())
.addDashboardUpdateMsg(dashboardUpdateMsg)
.build();
}
break;
case DELETED:
case UNASSIGNED_FROM_EDGE:
DashboardUpdateMsg dashboardUpdateMsg =
dashboardMsgConstructor.constructDashboardDeleteMsg(dashboardId);
downlinkMsg = DownlinkMsg.newBuilder()
.setDownlinkMsgId(EdgeUtils.nextPositiveInt())
.addDashboardUpdateMsg(dashboardUpdateMsg)
.build();
break;
}
return downlinkMsg;
}
}

172
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/DeviceProcessor.java → application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/DeviceEdgeProcessor.java

@ -15,6 +15,7 @@
*/
package org.thingsboard.server.service.edge.rpc.processor;
import com.datastax.oss.driver.api.core.uuid.Uuids;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.util.concurrent.FutureCallback;
@ -24,13 +25,15 @@ import com.google.common.util.concurrent.SettableFuture;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.springframework.stereotype.Component;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.rule.engine.api.RpcError;
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.EdgeUtils;
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.CustomerId;
@ -39,6 +42,8 @@ import org.thingsboard.server.common.data.id.DeviceProfileId;
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.page.PageData;
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.security.DeviceCredentials;
@ -47,73 +52,78 @@ import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbMsgDataType;
import org.thingsboard.server.common.msg.TbMsgMetaData;
import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.gen.edge.DeviceCredentialsUpdateMsg;
import org.thingsboard.server.gen.edge.DeviceRpcCallMsg;
import org.thingsboard.server.gen.edge.DeviceUpdateMsg;
import org.thingsboard.server.gen.edge.v1.DeviceCredentialsUpdateMsg;
import org.thingsboard.server.gen.edge.v1.DeviceRpcCallMsg;
import org.thingsboard.server.gen.edge.v1.DeviceUpdateMsg;
import org.thingsboard.server.gen.edge.v1.DownlinkMsg;
import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
import org.thingsboard.server.queue.TbQueueCallback;
import org.thingsboard.server.queue.TbQueueMsgMetadata;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.rpc.FromDeviceRpcResponse;
import org.thingsboard.server.service.rpc.FromDeviceRpcResponseActorMsg;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.locks.ReentrantLock;
@Component
@Slf4j
@TbCoreComponent
public class DeviceProcessor extends BaseProcessor {
public class DeviceEdgeProcessor extends BaseEdgeProcessor {
private static final ReentrantLock deviceCreationLock = new ReentrantLock();
public ListenableFuture<Void> onDeviceUpdate(TenantId tenantId, Edge edge, DeviceUpdateMsg deviceUpdateMsg) {
public ListenableFuture<Void> processDeviceFromEdge(TenantId tenantId, Edge edge, DeviceUpdateMsg deviceUpdateMsg) {
log.trace("[{}] onDeviceUpdate [{}] from edge [{}]", tenantId, deviceUpdateMsg, edge.getName());
switch (deviceUpdateMsg.getMsgType()) {
case ENTITY_CREATED_RPC_MESSAGE:
String deviceName = deviceUpdateMsg.getName();
Device device = deviceService.findDeviceByTenantIdAndName(tenantId, deviceName);
if (device != null) {
ListenableFuture<List<EdgeId>> future = edgeService.findRelatedEdgeIdsByEntityId(tenantId, device.getId());
SettableFuture<Void> futureToSet = SettableFuture.create();
Futures.addCallback(future, new FutureCallback<List<EdgeId>>() {
@Override
public void onSuccess(@Nullable List<EdgeId> edgeIds) {
boolean update = false;
if (edgeIds != null && !edgeIds.isEmpty()) {
if (edgeIds.contains(edge.getId())) {
update = true;
}
PageLink pageLink = new PageLink(DEFAULT_PAGE_SIZE);
PageData<EdgeId> pageData;
do {
pageData = edgeService.findRelatedEdgeIdsByEntityId(tenantId, device.getId(), pageLink);
boolean update = false;
if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) {
if (pageData.getData().contains(edge.getId())) {
update = true;
}
Device device;
if (update) {
log.info("[{}] Device with name '{}' already exists on the cloud, and related to this edge [{}]. " +
"deviceUpdateMsg [{}], Updating device", tenantId, deviceName, edge.getId(), deviceUpdateMsg);
updateDevice(tenantId, edge, deviceUpdateMsg);
} else {
log.info("[{}] Device with name '{}' already exists on the cloud, but not related to this edge [{}]. deviceUpdateMsg [{}]." +
"Creating a new device with random prefix and relate to this edge", tenantId, deviceName, edge.getId(), deviceUpdateMsg);
String newDeviceName = deviceUpdateMsg.getName() + "_" + RandomStringUtils.randomAlphabetic(15);
device = createDevice(tenantId, edge, deviceUpdateMsg, newDeviceName);
ObjectNode body = mapper.createObjectNode();
body.put("conflictName", deviceName);
saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE, EdgeEventActionType.ENTITY_MERGE_REQUEST, device.getId(), body);
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
}
futureToSet.set(null);
}
@Override
public void onFailure(Throwable t) {
log.error("[{}] Failed to get related edge ids by device id [{}], edge [{}]", tenantId, deviceUpdateMsg, edge.getId(), t);
futureToSet.setException(t);
if (update) {
log.info("[{}] Device with name '{}' already exists on the cloud, and related to this edge [{}]. " +
"deviceUpdateMsg [{}], Updating device", tenantId, deviceName, edge.getId(), deviceUpdateMsg);
updateDevice(tenantId, edge, deviceUpdateMsg);
} else {
log.info("[{}] Device with name '{}' already exists on the cloud, but not related to this edge [{}]. deviceUpdateMsg [{}]." +
"Creating a new device with random prefix and relate to this edge", tenantId, deviceName, edge.getId(), deviceUpdateMsg);
String newDeviceName = deviceUpdateMsg.getName() + "_" + RandomStringUtils.randomAlphabetic(15);
Device newDevice = createDevice(tenantId, edge, deviceUpdateMsg, newDeviceName);
ObjectNode body = mapper.createObjectNode();
body.put("conflictName", deviceName);
ListenableFuture<EdgeEvent> future =
saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE, EdgeEventActionType.ENTITY_MERGE_REQUEST, newDevice.getId(), body);
Futures.addCallback(future, new FutureCallback<>() {
@Override
public void onSuccess(EdgeEvent edgeEvent) {
saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE, EdgeEventActionType.CREDENTIALS_REQUEST, newDevice.getId(), null);
}
@Override
public void onFailure(Throwable t) {
log.error("[{}] Failed to save ENTITY_MERGE_REQUEST edge event [{}][{}]", tenantId, deviceUpdateMsg, edge.getId(), t);
}
}, dbCallbackExecutorService);
}
}, dbCallbackExecutorService);
return futureToSet;
} while (pageData != null && pageData.hasNext());
} else {
log.info("[{}] Creating new device and replacing device entity on the edge [{}]", tenantId, deviceUpdateMsg);
device = createDevice(tenantId, edge, deviceUpdateMsg, deviceUpdateMsg.getName());
saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE, EdgeEventActionType.ENTITY_MERGE_REQUEST, device.getId(), null);
saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE, EdgeEventActionType.CREDENTIALS_REQUEST, device.getId(), null);
}
break;
case ENTITY_UPDATED_RPC_MESSAGE:
@ -133,7 +143,7 @@ public class DeviceProcessor extends BaseProcessor {
return Futures.immediateFuture(null);
}
public ListenableFuture<Void> onDeviceCredentialsUpdate(TenantId tenantId, DeviceCredentialsUpdateMsg deviceCredentialsUpdateMsg) {
public ListenableFuture<Void> processDeviceCredentialsFromEdge(TenantId tenantId, DeviceCredentialsUpdateMsg deviceCredentialsUpdateMsg) {
log.debug("Executing onDeviceCredentialsUpdate, deviceCredentialsUpdateMsg [{}]", deviceCredentialsUpdateMsg);
DeviceId deviceId = new DeviceId(new UUID(deviceCredentialsUpdateMsg.getDeviceIdMSB(), deviceCredentialsUpdateMsg.getDeviceIdLSB()));
ListenableFuture<Device> deviceFuture = deviceService.findDeviceByIdAsync(tenantId, deviceId);
@ -179,11 +189,19 @@ public class DeviceProcessor extends BaseProcessor {
private Device createDevice(TenantId tenantId, Edge edge, DeviceUpdateMsg deviceUpdateMsg, String deviceName) {
Device device;
deviceCreationLock.lock();
try {
deviceCreationLock.lock();
log.debug("[{}] Creating device entity [{}] from edge [{}]", tenantId, deviceUpdateMsg, edge.getName());
device = new Device();
device.setTenantId(edge.getTenantId());
DeviceId deviceId = new DeviceId(new UUID(deviceUpdateMsg.getIdMSB(), deviceUpdateMsg.getIdLSB()));
device = deviceService.findDeviceById(tenantId, deviceId);
boolean created = false;
if (device == null) {
device = new Device();
device.setTenantId(tenantId);
device.setId(deviceId);
device.setCreatedTime(Uuids.unixTimestamp(deviceId.getId()));
created = true;
}
// make device private, if edge is public
device.setCustomerId(getCustomerId(edge));
device.setName(deviceName);
@ -195,9 +213,17 @@ public class DeviceProcessor extends BaseProcessor {
new UUID(deviceUpdateMsg.getDeviceProfileIdMSB(), deviceUpdateMsg.getDeviceProfileIdLSB()));
device.setDeviceProfileId(deviceProfileId);
}
device = deviceService.saveDevice(device);
Device savedDevice = deviceService.saveDevice(device, false);
if (created) {
DeviceCredentials deviceCredentials = new DeviceCredentials();
deviceCredentials.setDeviceId(new DeviceId(savedDevice.getUuidId()));
deviceCredentials.setCredentialsType(DeviceCredentialsType.ACCESS_TOKEN);
deviceCredentials.setCredentialsId(org.apache.commons.lang3.RandomStringUtils.randomAlphanumeric(20));
deviceCredentialsService.createDeviceCredentials(device.getTenantId(), deviceCredentials);
deviceStateService.onDeviceAdded(savedDevice);
}
createRelationFromEdge(tenantId, edge.getId(), device.getId());
deviceStateService.onDeviceAdded(device);
pushDeviceCreatedEventToRuleEngine(tenantId, edge, device);
deviceService.assignDeviceToEdge(edge.getTenantId(), device.getId(), edge.getId());
} finally {
@ -264,7 +290,7 @@ public class DeviceProcessor extends BaseProcessor {
return metaData;
}
public ListenableFuture<Void> processDeviceRpcCallResponseMsg(TenantId tenantId, DeviceRpcCallMsg deviceRpcCallMsg) {
public ListenableFuture<Void> processDeviceRpcCallResponseFromEdge(TenantId tenantId, DeviceRpcCallMsg deviceRpcCallMsg) {
log.trace("[{}] processDeviceRpcCallResponseMsg [{}]", tenantId, deviceRpcCallMsg);
SettableFuture<Void> futureToSet = SettableFuture.create();
UUID requestUuid = new UUID(deviceRpcCallMsg.getRequestUuidMSB(), deviceRpcCallMsg.getRequestUuidLSB());
@ -296,4 +322,58 @@ public class DeviceProcessor extends BaseProcessor {
return futureToSet;
}
public DownlinkMsg processDeviceToEdge(Edge edge, EdgeEvent edgeEvent,
UpdateMsgType msgType, EdgeEventActionType edgeEdgeEventActionType) {
DeviceId deviceId = new DeviceId(edgeEvent.getEntityId());
DownlinkMsg downlinkMsg = null;
switch (edgeEdgeEventActionType) {
case ADDED:
case UPDATED:
case ASSIGNED_TO_EDGE:
case ASSIGNED_TO_CUSTOMER:
case UNASSIGNED_FROM_CUSTOMER:
Device device = deviceService.findDeviceById(edgeEvent.getTenantId(), deviceId);
if (device != null) {
CustomerId customerId = getCustomerIdIfEdgeAssignedToCustomer(device, edge);
DeviceUpdateMsg deviceUpdateMsg =
deviceMsgConstructor.constructDeviceUpdatedMsg(msgType, device, customerId, null);
downlinkMsg = DownlinkMsg.newBuilder()
.setDownlinkMsgId(EdgeUtils.nextPositiveInt())
.addDeviceUpdateMsg(deviceUpdateMsg)
.build();
}
break;
case DELETED:
case UNASSIGNED_FROM_EDGE:
DeviceUpdateMsg deviceUpdateMsg =
deviceMsgConstructor.constructDeviceDeleteMsg(deviceId);
downlinkMsg = DownlinkMsg.newBuilder()
.setDownlinkMsgId(EdgeUtils.nextPositiveInt())
.addDeviceUpdateMsg(deviceUpdateMsg)
.build();
break;
case CREDENTIALS_UPDATED:
DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(edge.getTenantId(), deviceId);
if (deviceCredentials != null) {
DeviceCredentialsUpdateMsg deviceCredentialsUpdateMsg =
deviceMsgConstructor.constructDeviceCredentialsUpdatedMsg(deviceCredentials);
downlinkMsg = DownlinkMsg.newBuilder()
.setDownlinkMsgId(EdgeUtils.nextPositiveInt())
.addDeviceCredentialsUpdateMsg(deviceCredentialsUpdateMsg)
.build();
}
break;
}
return downlinkMsg;
}
public DownlinkMsg processRpcCallMsgToEdge(EdgeEvent edgeEvent) {
log.trace("Executing processRpcCall, edgeEvent [{}]", edgeEvent);
DeviceRpcCallMsg deviceRpcCallMsg =
deviceMsgConstructor.constructDeviceRpcCallMsg(edgeEvent.getEntityId(), edgeEvent.getBody());
return DownlinkMsg.newBuilder()
.setDownlinkMsgId(EdgeUtils.nextPositiveInt())
.addDeviceRpcCallMsg(deviceRpcCallMsg)
.build();
}
}

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

@ -0,0 +1,63 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.edge.rpc.processor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.EdgeUtils;
import org.thingsboard.server.common.data.edge.EdgeEvent;
import org.thingsboard.server.common.data.edge.EdgeEventActionType;
import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.gen.edge.v1.DeviceProfileUpdateMsg;
import org.thingsboard.server.gen.edge.v1.DownlinkMsg;
import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
import org.thingsboard.server.queue.util.TbCoreComponent;
@Component
@Slf4j
@TbCoreComponent
public class DeviceProfileEdgeProcessor extends BaseEdgeProcessor {
public DownlinkMsg processDeviceProfileToEdge(EdgeEvent edgeEvent, UpdateMsgType msgType, EdgeEventActionType action) {
DeviceProfileId deviceProfileId = new DeviceProfileId(edgeEvent.getEntityId());
DownlinkMsg downlinkMsg = null;
switch (action) {
case ADDED:
case UPDATED:
DeviceProfile deviceProfile = deviceProfileService.findDeviceProfileById(edgeEvent.getTenantId(), deviceProfileId);
if (deviceProfile != null) {
DeviceProfileUpdateMsg deviceProfileUpdateMsg =
deviceProfileMsgConstructor.constructDeviceProfileUpdatedMsg(msgType, deviceProfile);
downlinkMsg = DownlinkMsg.newBuilder()
.setDownlinkMsgId(EdgeUtils.nextPositiveInt())
.addDeviceProfileUpdateMsg(deviceProfileUpdateMsg)
.build();
}
break;
case DELETED:
DeviceProfileUpdateMsg deviceProfileUpdateMsg =
deviceProfileMsgConstructor.constructDeviceProfileDeleteMsg(deviceProfileId);
downlinkMsg = DownlinkMsg.newBuilder()
.setDownlinkMsgId(EdgeUtils.nextPositiveInt())
.addDeviceProfileUpdateMsg(deviceProfileUpdateMsg)
.build();
break;
}
return downlinkMsg;
}
}

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

@ -0,0 +1,102 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.edge.rpc.processor;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.User;
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.id.CustomerId;
import org.thingsboard.server.common.data.id.EdgeId;
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.gen.transport.TransportProtos;
import org.thingsboard.server.queue.util.TbCoreComponent;
import java.util.UUID;
@Component
@Slf4j
@TbCoreComponent
public class EdgeProcessor extends BaseEdgeProcessor {
public void processEdgeNotification(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) {
try {
EdgeEventActionType actionType = EdgeEventActionType.valueOf(edgeNotificationMsg.getAction());
EdgeId edgeId = new EdgeId(new UUID(edgeNotificationMsg.getEntityIdMSB(), edgeNotificationMsg.getEntityIdLSB()));
ListenableFuture<Edge> edgeFuture;
switch (actionType) {
case ASSIGNED_TO_CUSTOMER:
CustomerId customerId = mapper.readValue(edgeNotificationMsg.getBody(), CustomerId.class);
edgeFuture = edgeService.findEdgeByIdAsync(tenantId, edgeId);
Futures.addCallback(edgeFuture, new FutureCallback<Edge>() {
@Override
public void onSuccess(@Nullable Edge edge) {
if (edge != null && !customerId.isNullUid()) {
saveEdgeEvent(edge.getTenantId(), edge.getId(), EdgeEventType.CUSTOMER, EdgeEventActionType.ADDED, customerId, null);
PageLink pageLink = new PageLink(DEFAULT_PAGE_SIZE);
PageData<User> pageData;
do {
pageData = userService.findCustomerUsers(tenantId, customerId, pageLink);
if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) {
log.trace("[{}] [{}] user(s) are going to be added to edge.", edge.getId(), pageData.getData().size());
for (User user : pageData.getData()) {
saveEdgeEvent(edge.getTenantId(), edge.getId(), EdgeEventType.USER, EdgeEventActionType.ADDED, user.getId(), null);
}
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
}
}
} while (pageData != null && pageData.hasNext());
}
}
@Override
public void onFailure(Throwable t) {
log.error("Can't find edge by id [{}]", edgeNotificationMsg, t);
}
}, dbCallbackExecutorService);
break;
case UNASSIGNED_FROM_CUSTOMER:
CustomerId customerIdToDelete = mapper.readValue(edgeNotificationMsg.getBody(), CustomerId.class);
edgeFuture = edgeService.findEdgeByIdAsync(tenantId, edgeId);
Futures.addCallback(edgeFuture, new FutureCallback<Edge>() {
@Override
public void onSuccess(@Nullable Edge edge) {
if (edge != null && !customerIdToDelete.isNullUid()) {
saveEdgeEvent(edge.getTenantId(), edge.getId(), EdgeEventType.CUSTOMER, EdgeEventActionType.DELETED, customerIdToDelete, null);
}
}
@Override
public void onFailure(Throwable t) {
log.error("Can't find edge by id [{}]", edgeNotificationMsg, t);
}
}, dbCallbackExecutorService);
break;
}
} catch (Exception e) {
log.error("Exception during processing edge event", e);
}
}
}

206
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/EntityEdgeProcessor.java

@ -0,0 +1,206 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.edge.rpc.processor;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.Device;
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.EdgeEventActionType;
import org.thingsboard.server.common.data.edge.EdgeEventType;
import org.thingsboard.server.common.data.id.CustomerId;
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.EntityIdFactory;
import org.thingsboard.server.common.data.id.RuleChainId;
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.common.data.rule.RuleChain;
import org.thingsboard.server.common.data.rule.RuleChainConnectionInfo;
import org.thingsboard.server.gen.edge.v1.DeviceCredentialsRequestMsg;
import org.thingsboard.server.gen.edge.v1.DeviceUpdateMsg;
import org.thingsboard.server.gen.edge.v1.DownlinkMsg;
import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.queue.util.TbCoreComponent;
import java.util.List;
import java.util.UUID;
@Component
@Slf4j
@TbCoreComponent
public class EntityEdgeProcessor extends BaseEdgeProcessor {
public DownlinkMsg processEntityMergeRequestMessageToEdge(Edge edge, EdgeEvent edgeEvent) {
DownlinkMsg downlinkMsg = null;
if (EdgeEventType.DEVICE.equals(edgeEvent.getType())) {
DeviceId deviceId = new DeviceId(edgeEvent.getEntityId());
Device device = deviceService.findDeviceById(edge.getTenantId(), deviceId);
CustomerId customerId = getCustomerIdIfEdgeAssignedToCustomer(device, edge);
String conflictName = null;
if(edgeEvent.getBody() != null) {
conflictName = edgeEvent.getBody().get("conflictName").asText();
}
DeviceUpdateMsg deviceUpdateMsg = deviceMsgConstructor
.constructDeviceUpdatedMsg(UpdateMsgType.ENTITY_MERGE_RPC_MESSAGE, device, customerId, conflictName);
downlinkMsg = DownlinkMsg.newBuilder()
.setDownlinkMsgId(EdgeUtils.nextPositiveInt())
.addDeviceUpdateMsg(deviceUpdateMsg)
.build();
}
return downlinkMsg;
}
public DownlinkMsg processCredentialsRequestMessageToEdge(EdgeEvent edgeEvent) {
DownlinkMsg downlinkMsg = null;
if (EdgeEventType.DEVICE.equals(edgeEvent.getType())) {
DeviceId deviceId = new DeviceId(edgeEvent.getEntityId());
DeviceCredentialsRequestMsg deviceCredentialsRequestMsg = DeviceCredentialsRequestMsg.newBuilder()
.setDeviceIdMSB(deviceId.getId().getMostSignificantBits())
.setDeviceIdLSB(deviceId.getId().getLeastSignificantBits())
.build();
DownlinkMsg.Builder builder = DownlinkMsg.newBuilder()
.setDownlinkMsgId(EdgeUtils.nextPositiveInt())
.addDeviceCredentialsRequestMsg(deviceCredentialsRequestMsg);
downlinkMsg = builder.build();
}
return downlinkMsg;
}
public void processEntityNotification(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) {
EdgeEventActionType actionType = EdgeEventActionType.valueOf(edgeNotificationMsg.getAction());
EdgeEventType type = EdgeEventType.valueOf(edgeNotificationMsg.getType());
EntityId entityId = EntityIdFactory.getByEdgeEventTypeAndUuid(type,
new UUID(edgeNotificationMsg.getEntityIdMSB(), edgeNotificationMsg.getEntityIdLSB()));
EdgeId edgeId = new EdgeId(new UUID(edgeNotificationMsg.getEdgeIdMSB(), edgeNotificationMsg.getEdgeIdLSB()));
PageLink pageLink = new PageLink(DEFAULT_PAGE_SIZE);
PageData<EdgeId> pageData;
switch (actionType) {
case ADDED: // used only for USER entity
case UPDATED:
case CREDENTIALS_UPDATED:
do {
pageData = edgeService.findRelatedEdgeIdsByEntityId(tenantId, entityId, pageLink);
if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) {
for (EdgeId relatedEdgeId : pageData.getData()) {
saveEdgeEvent(tenantId, relatedEdgeId, type, actionType, entityId, null);
}
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
}
}
} while (pageData != null && pageData.hasNext());
break;
case ASSIGNED_TO_CUSTOMER:
case UNASSIGNED_FROM_CUSTOMER:
do {
pageData = edgeService.findRelatedEdgeIdsByEntityId(tenantId, entityId, pageLink);
if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) {
for (EdgeId relatedEdgeId : pageData.getData()) {
try {
CustomerId customerId = mapper.readValue(edgeNotificationMsg.getBody(), CustomerId.class);
ListenableFuture<Edge> future = edgeService.findEdgeByIdAsync(tenantId, relatedEdgeId);
Futures.addCallback(future, new FutureCallback<>() {
@Override
public void onSuccess(@Nullable Edge edge) {
if (edge != null && edge.getCustomerId() != null &&
!edge.getCustomerId().isNullUid() && edge.getCustomerId().equals(customerId)) {
saveEdgeEvent(tenantId, relatedEdgeId, type, actionType, entityId, null);
}
}
@Override
public void onFailure(Throwable t) {
log.error("Failed to find edge by id [{}] {}", edgeNotificationMsg, t);
}
}, dbCallbackExecutorService);
} catch (Exception e) {
log.error("Can't parse customer id from entity body [{}]", edgeNotificationMsg, e);
}
}
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
}
}
} while (pageData != null && pageData.hasNext());
break;
case DELETED:
saveEdgeEvent(tenantId, edgeId, type, actionType, entityId, null);
break;
case ASSIGNED_TO_EDGE:
case UNASSIGNED_FROM_EDGE:
saveEdgeEvent(tenantId, edgeId, type, actionType, entityId, null);
if (type.equals(EdgeEventType.RULE_CHAIN)) {
updateDependentRuleChains(tenantId, new RuleChainId(entityId.getId()), edgeId);
}
break;
}
}
private void updateDependentRuleChains(TenantId tenantId, RuleChainId processingRuleChainId, EdgeId edgeId) {
PageLink pageLink = new PageLink(DEFAULT_PAGE_SIZE);
PageData<RuleChain> pageData;
do {
pageData = ruleChainService.findRuleChainsByTenantIdAndEdgeId(tenantId, edgeId, pageLink);
if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) {
for (RuleChain ruleChain : pageData.getData()) {
if (!ruleChain.getId().equals(processingRuleChainId)) {
List<RuleChainConnectionInfo> connectionInfos =
ruleChainService.loadRuleChainMetaData(ruleChain.getTenantId(), ruleChain.getId()).getRuleChainConnections();
if (connectionInfos != null && !connectionInfos.isEmpty()) {
for (RuleChainConnectionInfo connectionInfo : connectionInfos) {
if (connectionInfo.getTargetRuleChainId().equals(processingRuleChainId)) {
saveEdgeEvent(tenantId,
edgeId,
EdgeEventType.RULE_CHAIN_METADATA,
EdgeEventActionType.UPDATED,
ruleChain.getId(),
null);
}
}
}
}
}
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
}
}
} while (pageData != null && pageData.hasNext());
}
public void processEntityNotificationForAllEdges(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) {
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) {
case ADDED:
case UPDATED:
case DELETED:
processActionForAllEdges(tenantId, type, actionType, entityId);
break;
}
}
}

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

@ -0,0 +1,69 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.edge.rpc.processor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.EdgeUtils;
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.EdgeEventActionType;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EntityViewId;
import org.thingsboard.server.gen.edge.v1.DownlinkMsg;
import org.thingsboard.server.gen.edge.v1.EntityViewUpdateMsg;
import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
import org.thingsboard.server.queue.util.TbCoreComponent;
@Component
@Slf4j
@TbCoreComponent
public class EntityViewEdgeProcessor extends BaseEdgeProcessor {
public DownlinkMsg processEntityViewToEdge(Edge edge, EdgeEvent edgeEvent, UpdateMsgType msgType, EdgeEventActionType action) {
EntityViewId entityViewId = new EntityViewId(edgeEvent.getEntityId());
DownlinkMsg downlinkMsg = null;
switch (action) {
case ADDED:
case UPDATED:
case ASSIGNED_TO_EDGE:
case ASSIGNED_TO_CUSTOMER:
case UNASSIGNED_FROM_CUSTOMER:
EntityView entityView = entityViewService.findEntityViewById(edgeEvent.getTenantId(), entityViewId);
if (entityView != null) {
CustomerId customerId = getCustomerIdIfEdgeAssignedToCustomer(entityView, edge);
EntityViewUpdateMsg entityViewUpdateMsg =
entityViewMsgConstructor.constructEntityViewUpdatedMsg(msgType, entityView, customerId);
downlinkMsg = DownlinkMsg.newBuilder()
.setDownlinkMsgId(EdgeUtils.nextPositiveInt())
.addEntityViewUpdateMsg(entityViewUpdateMsg)
.build();
}
break;
case DELETED:
case UNASSIGNED_FROM_EDGE:
EntityViewUpdateMsg entityViewUpdateMsg =
entityViewMsgConstructor.constructEntityViewDeleteMsg(entityViewId);
downlinkMsg = DownlinkMsg.newBuilder()
.setDownlinkMsgId(EdgeUtils.nextPositiveInt())
.addEntityViewUpdateMsg(entityViewUpdateMsg)
.build();
break;
}
return downlinkMsg;
}
}

64
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/RelationProcessor.java → application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/RelationEdgeProcessor.java

@ -15,35 +15,50 @@
*/
package org.thingsboard.server.service.edge.rpc.processor;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.EdgeUtils;
import org.thingsboard.server.common.data.EntityType;
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.exception.ThingsboardErrorCode;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.AssetId;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DashboardId;
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.EntityIdFactory;
import org.thingsboard.server.common.data.id.EntityViewId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.page.PageData;
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.gen.edge.RelationUpdateMsg;
import org.thingsboard.server.gen.edge.v1.DownlinkMsg;
import org.thingsboard.server.gen.edge.v1.RelationUpdateMsg;
import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.queue.util.TbCoreComponent;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
@Component
@Slf4j
@TbCoreComponent
public class RelationProcessor extends BaseProcessor {
public class RelationEdgeProcessor extends BaseEdgeProcessor {
public ListenableFuture<Void> onRelationUpdate(TenantId tenantId, RelationUpdateMsg relationUpdateMsg) {
public ListenableFuture<Void> processRelationFromEdge(TenantId tenantId, RelationUpdateMsg relationUpdateMsg) {
log.trace("[{}] onRelationUpdate [{}]", tenantId, relationUpdateMsg);
try {
EntityRelation entityRelation = new EntityRelation();
@ -100,5 +115,48 @@ public class RelationProcessor extends BaseProcessor {
}
}
public DownlinkMsg processRelationToEdge(EdgeEvent edgeEvent, UpdateMsgType msgType) {
EntityRelation entityRelation = mapper.convertValue(edgeEvent.getBody(), EntityRelation.class);
RelationUpdateMsg relationUpdateMsg = relationMsgConstructor.constructRelationUpdatedMsg(msgType, entityRelation);
return DownlinkMsg.newBuilder()
.setDownlinkMsgId(EdgeUtils.nextPositiveInt())
.addRelationUpdateMsg(relationUpdateMsg)
.build();
}
public void processRelationNotification(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) throws JsonProcessingException {
EntityRelation relation = mapper.readValue(edgeNotificationMsg.getBody(), EntityRelation.class);
if (!relation.getFrom().getEntityType().equals(EntityType.EDGE) &&
!relation.getTo().getEntityType().equals(EntityType.EDGE)) {
Set<EdgeId> uniqueEdgeIds = new HashSet<>();
uniqueEdgeIds.addAll(findRelatedEdgeIds(tenantId, relation.getTo()));
uniqueEdgeIds.addAll(findRelatedEdgeIds(tenantId, relation.getFrom()));
if (!uniqueEdgeIds.isEmpty()) {
for (EdgeId edgeId : uniqueEdgeIds) {
saveEdgeEvent(tenantId,
edgeId,
EdgeEventType.RELATION,
EdgeEventActionType.valueOf(edgeNotificationMsg.getAction()),
null,
mapper.valueToTree(relation));
}
}
}
}
private List<EdgeId> findRelatedEdgeIds(TenantId tenantId, EntityId entityId) {
List<EdgeId> result = new ArrayList<>();
PageLink pageLink = new PageLink(DEFAULT_PAGE_SIZE);
PageData<EdgeId> pageData;
do {
pageData = edgeService.findRelatedEdgeIdsByEntityId(tenantId, entityId, pageLink);
if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) {
result.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
}
}
} while (pageData != null && pageData.hasNext());
return result;
}
}

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

@ -0,0 +1,83 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.edge.rpc.processor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
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.EdgeEventActionType;
import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.data.rule.RuleChainMetaData;
import org.thingsboard.server.gen.edge.v1.DownlinkMsg;
import org.thingsboard.server.gen.edge.v1.RuleChainMetadataUpdateMsg;
import org.thingsboard.server.gen.edge.v1.RuleChainUpdateMsg;
import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
import org.thingsboard.server.queue.util.TbCoreComponent;
@Component
@Slf4j
@TbCoreComponent
public class RuleChainEdgeProcessor extends BaseEdgeProcessor {
public DownlinkMsg processRuleChainToEdge(Edge edge, EdgeEvent edgeEvent, UpdateMsgType msgType, EdgeEventActionType action) {
RuleChainId ruleChainId = new RuleChainId(edgeEvent.getEntityId());
DownlinkMsg downlinkMsg = null;
switch (action) {
case ADDED:
case UPDATED:
case ASSIGNED_TO_EDGE:
RuleChain ruleChain = ruleChainService.findRuleChainById(edgeEvent.getTenantId(), ruleChainId);
if (ruleChain != null) {
RuleChainUpdateMsg ruleChainUpdateMsg =
ruleChainMsgConstructor.constructRuleChainUpdatedMsg(edge.getRootRuleChainId(), msgType, ruleChain);
downlinkMsg = DownlinkMsg.newBuilder()
.setDownlinkMsgId(EdgeUtils.nextPositiveInt())
.addRuleChainUpdateMsg(ruleChainUpdateMsg)
.build();
}
break;
case DELETED:
case UNASSIGNED_FROM_EDGE:
downlinkMsg = DownlinkMsg.newBuilder()
.setDownlinkMsgId(EdgeUtils.nextPositiveInt())
.addRuleChainUpdateMsg(ruleChainMsgConstructor.constructRuleChainDeleteMsg(ruleChainId))
.build();
break;
}
return downlinkMsg;
}
public DownlinkMsg processRuleChainMetadataToEdge(EdgeEvent edgeEvent, UpdateMsgType msgType) {
RuleChainId ruleChainId = new RuleChainId(edgeEvent.getEntityId());
RuleChain ruleChain = ruleChainService.findRuleChainById(edgeEvent.getTenantId(), ruleChainId);
DownlinkMsg downlinkMsg = null;
if (ruleChain != null) {
RuleChainMetaData ruleChainMetaData = ruleChainService.loadRuleChainMetaData(edgeEvent.getTenantId(), ruleChainId);
RuleChainMetadataUpdateMsg ruleChainMetadataUpdateMsg =
ruleChainMsgConstructor.constructRuleChainMetadataUpdatedMsg(msgType, ruleChainMetaData);
if (ruleChainMetadataUpdateMsg != null) {
downlinkMsg = DownlinkMsg.newBuilder()
.setDownlinkMsgId(EdgeUtils.nextPositiveInt())
.addRuleChainMetadataUpdateMsg(ruleChainMetadataUpdateMsg)
.build();
}
}
return downlinkMsg;
}
}

57
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/TelemetryProcessor.java → application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/TelemetryEdgeProcessor.java

@ -15,11 +15,13 @@
*/
package org.thingsboard.server.service.edge.rpc.processor;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.SettableFuture;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.tuple.ImmutablePair;
@ -29,13 +31,17 @@ import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg;
import org.thingsboard.server.common.data.DataConstants;
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.EntityType;
import org.thingsboard.server.common.data.EntityView;
import org.thingsboard.server.common.data.asset.Asset;
import org.thingsboard.server.common.data.edge.EdgeEvent;
import org.thingsboard.server.common.data.edge.EdgeEventActionType;
import org.thingsboard.server.common.data.id.AssetId;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DashboardId;
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.EntityViewId;
import org.thingsboard.server.common.data.id.RuleChainId;
@ -49,8 +55,9 @@ import org.thingsboard.server.common.msg.queue.ServiceQueue;
import org.thingsboard.server.common.msg.session.SessionMsgType;
import org.thingsboard.server.common.transport.adaptor.JsonConverter;
import org.thingsboard.server.common.transport.util.JsonUtils;
import org.thingsboard.server.gen.edge.AttributeDeleteMsg;
import org.thingsboard.server.gen.edge.EntityDataProto;
import org.thingsboard.server.gen.edge.v1.AttributeDeleteMsg;
import org.thingsboard.server.gen.edge.v1.DownlinkMsg;
import org.thingsboard.server.gen.edge.v1.EntityDataProto;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.queue.TbQueueCallback;
import org.thingsboard.server.queue.TbQueueMsgMetadata;
@ -66,16 +73,16 @@ import java.util.UUID;
@Component
@Slf4j
@TbCoreComponent
public class TelemetryProcessor extends BaseProcessor {
public class TelemetryEdgeProcessor extends BaseEdgeProcessor {
private final Gson gson = new Gson();
public List<ListenableFuture<Void>> onTelemetryUpdate(TenantId tenantId, CustomerId customerId, EntityDataProto entityData) {
public List<ListenableFuture<Void>> processTelemetryFromEdge(TenantId tenantId, CustomerId customerId, EntityDataProto entityData) {
log.trace("[{}] onTelemetryUpdate [{}]", tenantId, entityData);
List<ListenableFuture<Void>> result = new ArrayList<>();
EntityId entityId = constructEntityId(entityData);
if ((entityData.hasPostAttributesMsg() || entityData.hasPostTelemetryMsg() || entityData.hasAttributesUpdatedMsg()) && entityId != null) {
// TODO: voba - in terms of performance we should not fetch device from DB by id
// @voba - in terms of performance we should not fetch device from DB by id
// TbMsgMetaData metaData = constructBaseMsgMetadata(tenantId, entityId);
TbMsgMetaData metaData = new TbMsgMetaData();
metaData.putValue(DataConstants.MSG_SOURCE_KEY, DataConstants.EDGE_MSG_SOURCE);
@ -279,4 +286,44 @@ public class TelemetryProcessor extends BaseProcessor {
return null;
}
}
public DownlinkMsg processTelemetryMessageToEdge(EdgeEvent edgeEvent) throws JsonProcessingException {
EntityId entityId;
switch (edgeEvent.getType()) {
case DEVICE:
entityId = new DeviceId(edgeEvent.getEntityId());
break;
case ASSET:
entityId = new AssetId(edgeEvent.getEntityId());
break;
case ENTITY_VIEW:
entityId = new EntityViewId(edgeEvent.getEntityId());
break;
case DASHBOARD:
entityId = new DashboardId(edgeEvent.getEntityId());
break;
case TENANT:
entityId = new TenantId(edgeEvent.getEntityId());
break;
case CUSTOMER:
entityId = new CustomerId(edgeEvent.getEntityId());
break;
case EDGE:
entityId = new EdgeId(edgeEvent.getEntityId());
break;
default:
log.warn("Unsupported edge event type [{}]", edgeEvent);
return null;
}
return constructEntityDataProtoMsg(entityId, edgeEvent.getAction(), JsonUtils.parse(mapper.writeValueAsString(edgeEvent.getBody())));
}
private DownlinkMsg constructEntityDataProtoMsg(EntityId entityId, EdgeEventActionType actionType, JsonElement entityData) {
EntityDataProto entityDataProto = entityDataMsgConstructor.constructEntityDataMsg(entityId, actionType, entityData);
return DownlinkMsg.newBuilder()
.setDownlinkMsgId(EdgeUtils.nextPositiveInt())
.addEntityData(entityDataProto)
.build();
}
}

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

@ -0,0 +1,73 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.edge.rpc.processor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.EdgeUtils;
import org.thingsboard.server.common.data.User;
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.id.CustomerId;
import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.security.UserCredentials;
import org.thingsboard.server.gen.edge.v1.DownlinkMsg;
import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
import org.thingsboard.server.gen.edge.v1.UserCredentialsUpdateMsg;
import org.thingsboard.server.queue.util.TbCoreComponent;
@Component
@Slf4j
@TbCoreComponent
public class UserEdgeProcessor extends BaseEdgeProcessor {
public DownlinkMsg processUserToEdge(Edge edge, EdgeEvent edgeEvent, UpdateMsgType msgType, EdgeEventActionType edgeEdgeEventActionType) {
UserId userId = new UserId(edgeEvent.getEntityId());
DownlinkMsg downlinkMsg = null;
switch (edgeEdgeEventActionType) {
case ADDED:
case UPDATED:
User user = userService.findUserById(edgeEvent.getTenantId(), userId);
if (user != null) {
CustomerId customerId = getCustomerIdIfEdgeAssignedToCustomer(user, edge);
downlinkMsg = DownlinkMsg.newBuilder()
.setDownlinkMsgId(EdgeUtils.nextPositiveInt())
.addUserUpdateMsg(userMsgConstructor.constructUserUpdatedMsg(msgType, user, customerId))
.build();
}
break;
case DELETED:
downlinkMsg = DownlinkMsg.newBuilder()
.setDownlinkMsgId(EdgeUtils.nextPositiveInt())
.addUserUpdateMsg(userMsgConstructor.constructUserDeleteMsg(userId))
.build();
break;
case CREDENTIALS_UPDATED:
UserCredentials userCredentialsByUserId = userService.findUserCredentialsByUserId(edge.getTenantId(), userId);
if (userCredentialsByUserId != null && userCredentialsByUserId.isEnabled()) {
UserCredentialsUpdateMsg userCredentialsUpdateMsg =
userMsgConstructor.constructUserCredentialsUpdatedMsg(userCredentialsByUserId);
downlinkMsg = DownlinkMsg.newBuilder()
.setDownlinkMsgId(EdgeUtils.nextPositiveInt())
.addUserCredentialsUpdateMsg(userCredentialsUpdateMsg)
.build();
}
}
return downlinkMsg;
}
}

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

@ -0,0 +1,62 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.edge.rpc.processor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.EdgeUtils;
import org.thingsboard.server.common.data.edge.EdgeEvent;
import org.thingsboard.server.common.data.edge.EdgeEventActionType;
import org.thingsboard.server.common.data.id.WidgetsBundleId;
import org.thingsboard.server.common.data.widget.WidgetsBundle;
import org.thingsboard.server.gen.edge.v1.DownlinkMsg;
import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
import org.thingsboard.server.gen.edge.v1.WidgetsBundleUpdateMsg;
import org.thingsboard.server.queue.util.TbCoreComponent;
@Component
@Slf4j
@TbCoreComponent
public class WidgetBundleEdgeProcessor extends BaseEdgeProcessor {
public DownlinkMsg processWidgetsBundleToEdge(EdgeEvent edgeEvent, UpdateMsgType msgType, EdgeEventActionType edgeEdgeEventActionType) {
WidgetsBundleId widgetsBundleId = new WidgetsBundleId(edgeEvent.getEntityId());
DownlinkMsg downlinkMsg = null;
switch (edgeEdgeEventActionType) {
case ADDED:
case UPDATED:
WidgetsBundle widgetsBundle = widgetsBundleService.findWidgetsBundleById(edgeEvent.getTenantId(), widgetsBundleId);
if (widgetsBundle != null) {
WidgetsBundleUpdateMsg widgetsBundleUpdateMsg =
widgetsBundleMsgConstructor.constructWidgetsBundleUpdateMsg(msgType, widgetsBundle);
downlinkMsg = DownlinkMsg.newBuilder()
.setDownlinkMsgId(EdgeUtils.nextPositiveInt())
.addWidgetsBundleUpdateMsg(widgetsBundleUpdateMsg)
.build();
}
break;
case DELETED:
WidgetsBundleUpdateMsg widgetsBundleUpdateMsg =
widgetsBundleMsgConstructor.constructWidgetsBundleDeleteMsg(widgetsBundleId);
downlinkMsg = DownlinkMsg.newBuilder()
.setDownlinkMsgId(EdgeUtils.nextPositiveInt())
.addWidgetsBundleUpdateMsg(widgetsBundleUpdateMsg)
.build();
break;
}
return downlinkMsg;
}
}

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

@ -0,0 +1,63 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.edge.rpc.processor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.EdgeUtils;
import org.thingsboard.server.common.data.edge.EdgeEvent;
import org.thingsboard.server.common.data.edge.EdgeEventActionType;
import org.thingsboard.server.common.data.id.WidgetTypeId;
import org.thingsboard.server.common.data.widget.WidgetType;
import org.thingsboard.server.gen.edge.v1.DownlinkMsg;
import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
import org.thingsboard.server.gen.edge.v1.WidgetTypeUpdateMsg;
import org.thingsboard.server.queue.util.TbCoreComponent;
@Component
@Slf4j
@TbCoreComponent
public class WidgetTypeEdgeProcessor extends BaseEdgeProcessor {
public DownlinkMsg processWidgetTypeToEdge(EdgeEvent edgeEvent, UpdateMsgType msgType, EdgeEventActionType edgeEdgeEventActionType) {
WidgetTypeId widgetTypeId = new WidgetTypeId(edgeEvent.getEntityId());
DownlinkMsg downlinkMsg = null;
switch (edgeEdgeEventActionType) {
case ADDED:
case UPDATED:
WidgetType widgetType = widgetTypeService.findWidgetTypeById(edgeEvent.getTenantId(), widgetTypeId);
if (widgetType != null) {
WidgetTypeUpdateMsg widgetTypeUpdateMsg =
widgetTypeMsgConstructor.constructWidgetTypeUpdateMsg(msgType, widgetType);
downlinkMsg = DownlinkMsg.newBuilder()
.setDownlinkMsgId(EdgeUtils.nextPositiveInt())
.addWidgetTypeUpdateMsg(widgetTypeUpdateMsg)
.build();
}
break;
case DELETED:
WidgetTypeUpdateMsg widgetTypeUpdateMsg =
widgetTypeMsgConstructor.constructWidgetTypeDeleteMsg(widgetTypeId);
downlinkMsg = DownlinkMsg.newBuilder()
.setDownlinkMsgId(EdgeUtils.nextPositiveInt())
.addWidgetTypeUpdateMsg(widgetTypeUpdateMsg)
.build();
break;
}
return downlinkMsg;
}
}

515
application/src/main/java/org/thingsboard/server/service/edge/rpc/init/DefaultSyncEdgeService.java → application/src/main/java/org/thingsboard/server/service/edge/rpc/sync/DefaultEdgeRequestsService.java

@ -13,9 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.edge.rpc.init;
package org.thingsboard.server.service.edge.rpc.sync;
import com.datastax.oss.driver.api.core.uuid.Uuids;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
@ -24,34 +23,27 @@ import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.SettableFuture;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.text.WordUtils;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.AdminSettings;
import org.thingsboard.server.common.data.DashboardInfo;
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.EntityType;
import org.thingsboard.server.common.data.EntityView;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.asset.Asset;
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.AdminSettingsId;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.EntityIdFactory;
import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.id.WidgetsBundleId;
import org.thingsboard.server.common.data.kv.AttributeKvEntry;
import org.thingsboard.server.common.data.kv.DataType;
import org.thingsboard.server.common.data.page.PageData;
@ -59,48 +51,43 @@ import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.relation.EntityRelationsQuery;
import org.thingsboard.server.common.data.relation.EntitySearchDirection;
import org.thingsboard.server.common.data.relation.RelationTypeGroup;
import org.thingsboard.server.common.data.relation.RelationsSearchParameters;
import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.data.widget.WidgetType;
import org.thingsboard.server.common.data.widget.WidgetsBundle;
import org.thingsboard.server.dao.asset.AssetService;
import org.thingsboard.server.dao.attributes.AttributesService;
import org.thingsboard.server.dao.dashboard.DashboardService;
import org.thingsboard.server.dao.device.DeviceProfileService;
import org.thingsboard.server.dao.device.DeviceService;
import org.thingsboard.server.dao.edge.EdgeEventService;
import org.thingsboard.server.dao.entityview.EntityViewService;
import org.thingsboard.server.dao.relation.RelationService;
import org.thingsboard.server.dao.rule.RuleChainService;
import org.thingsboard.server.dao.settings.AdminSettingsService;
import org.thingsboard.server.dao.user.UserService;
import org.thingsboard.server.dao.widget.WidgetTypeService;
import org.thingsboard.server.dao.widget.WidgetsBundleService;
import org.thingsboard.server.gen.edge.AttributesRequestMsg;
import org.thingsboard.server.gen.edge.DeviceCredentialsRequestMsg;
import org.thingsboard.server.gen.edge.RelationRequestMsg;
import org.thingsboard.server.gen.edge.RuleChainMetadataRequestMsg;
import org.thingsboard.server.gen.edge.UserCredentialsRequestMsg;
import org.thingsboard.server.gen.edge.v1.AttributesRequestMsg;
import org.thingsboard.server.gen.edge.v1.DeviceCredentialsRequestMsg;
import org.thingsboard.server.gen.edge.v1.DeviceProfileDevicesRequestMsg;
import org.thingsboard.server.gen.edge.v1.EntityViewsRequestMsg;
import org.thingsboard.server.gen.edge.v1.RelationRequestMsg;
import org.thingsboard.server.gen.edge.v1.RuleChainMetadataRequestMsg;
import org.thingsboard.server.gen.edge.v1.UserCredentialsRequestMsg;
import org.thingsboard.server.gen.edge.v1.WidgetBundleTypesRequestMsg;
import org.thingsboard.server.service.edge.rpc.EdgeEventUtils;
import org.thingsboard.server.service.executors.DbCallbackExecutorService;
import org.thingsboard.server.service.queue.TbClusterService;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@Service
@Slf4j
public class DefaultSyncEdgeService implements SyncEdgeService {
public class DefaultEdgeRequestsService implements EdgeRequestsService {
private static final ObjectMapper mapper = new ObjectMapper();
private static final int DEFAULT_LIMIT = 100;
private static final int DEFAULT_PAGE_SIZE = 1000;
@Autowired
private EdgeEventService edgeEventService;
@ -108,29 +95,17 @@ public class DefaultSyncEdgeService implements SyncEdgeService {
@Autowired
private AttributesService attributesService;
@Autowired
private RuleChainService ruleChainService;
@Autowired
private RelationService relationService;
@Autowired
private DeviceService deviceService;
@Autowired
private DeviceProfileService deviceProfileService;
@Autowired
private AssetService assetService;
@Autowired
private EntityViewService entityViewService;
@Autowired
private DashboardService dashboardService;
@Autowired
private UserService userService;
private DeviceProfileService deviceProfileService;
@Autowired
private WidgetsBundleService widgetsBundleService;
@ -138,309 +113,12 @@ public class DefaultSyncEdgeService implements SyncEdgeService {
@Autowired
private WidgetTypeService widgetTypeService;
@Autowired
private AdminSettingsService adminSettingsService;
@Autowired
private DbCallbackExecutorService dbCallbackExecutorService;
@Autowired
private TbClusterService tbClusterService;
@Override
public void sync(TenantId tenantId, Edge edge) {
log.trace("[{}][{}] Staring edge sync process", tenantId, edge.getId());
try {
syncWidgetsBundles(tenantId, edge);
// TODO: voba - implement this functionality
// syncAdminSettings(edge);
syncDeviceProfiles(tenantId, edge);
syncRuleChains(tenantId, edge);
syncUsers(tenantId, edge);
syncAssets(tenantId, edge);
syncEntityViews(tenantId, edge);
syncDashboards(tenantId, edge);
syncWidgetsTypes(tenantId, edge);
syncDevices(tenantId, edge);
} catch (Exception e) {
log.error("[{}][{}] Exception during sync process", tenantId, edge.getId(), e);
}
}
private void syncRuleChains(TenantId tenantId, Edge edge) {
log.trace("[{}] syncRuleChains [{}]", tenantId, edge.getName());
try {
PageLink pageLink = new PageLink(DEFAULT_LIMIT);
PageData<RuleChain> pageData;
do {
pageData = ruleChainService.findRuleChainsByTenantIdAndEdgeId(tenantId, edge.getId(), pageLink);
if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) {
log.trace("[{}] [{}] rule chains(s) are going to be pushed to edge.", edge.getId(), pageData.getData().size());
for (RuleChain ruleChain : pageData.getData()) {
saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.RULE_CHAIN, EdgeEventActionType.ADDED, ruleChain.getId(), null);
}
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
}
}
} while (pageData != null && pageData.hasNext());
} catch (Exception e) {
log.error("Exception during loading edge rule chain(s) on sync!", e);
}
}
private void syncDevices(TenantId tenantId, Edge edge) {
log.trace("[{}] syncDevices [{}]", tenantId, edge.getName());
try {
PageLink pageLink = new PageLink(DEFAULT_LIMIT);
PageData<Device> pageData;
do {
pageData = deviceService.findDevicesByTenantIdAndEdgeId(tenantId, edge.getId(), pageLink);
if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) {
log.trace("[{}] [{}] device(s) are going to be pushed to edge.", edge.getId(), pageData.getData().size());
for (Device device : pageData.getData()) {
saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE, EdgeEventActionType.ADDED, device.getId(), null);
}
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
}
}
} while (pageData != null && pageData.hasNext());
} catch (Exception e) {
log.error("Exception during loading edge device(s) on sync!", e);
}
}
private void syncDeviceProfiles(TenantId tenantId, Edge edge) {
log.trace("[{}] syncDeviceProfiles [{}]", tenantId, edge.getName());
try {
PageLink pageLink = new PageLink(DEFAULT_LIMIT);
PageData<DeviceProfile> pageData;
do {
pageData = deviceProfileService.findDeviceProfiles(tenantId, pageLink);
if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) {
log.trace("[{}] [{}] user(s) are going to be pushed to edge.", edge.getId(), pageData.getData().size());
for (DeviceProfile deviceProfile : pageData.getData()) {
saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE_PROFILE, EdgeEventActionType.ADDED, deviceProfile.getId(), null);
}
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
}
}
} while (pageData != null && pageData.hasNext());
} catch (Exception e) {
log.error("Exception during loading device profile(s) on sync!", e);
}
}
private void syncAssets(TenantId tenantId, Edge edge) {
log.trace("[{}] syncAssets [{}]", tenantId, edge.getName());
try {
PageLink pageLink = new PageLink(DEFAULT_LIMIT);
PageData<Asset> pageData;
do {
pageData = assetService.findAssetsByTenantIdAndEdgeId(tenantId, edge.getId(), pageLink);
if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) {
log.trace("[{}] [{}] asset(s) are going to be pushed to edge.", edge.getId(), pageData.getData().size());
for (Asset asset : pageData.getData()) {
saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.ASSET, EdgeEventActionType.ADDED, asset.getId(), null);
}
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
}
}
} while (pageData != null && pageData.hasNext());
} catch (Exception e) {
log.error("Exception during loading edge asset(s) on sync!", e);
}
}
private void syncEntityViews(TenantId tenantId, Edge edge) {
log.trace("[{}] syncEntityViews [{}]", tenantId, edge.getName());
try {
PageLink pageLink = new PageLink(DEFAULT_LIMIT);
PageData<EntityView> pageData;
do {
pageData = entityViewService.findEntityViewsByTenantIdAndEdgeId(tenantId, edge.getId(), pageLink);
if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) {
log.trace("[{}] [{}] entity view(s) are going to be pushed to edge.", edge.getId(), pageData.getData().size());
for (EntityView entityView : pageData.getData()) {
saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.ENTITY_VIEW, EdgeEventActionType.ADDED, entityView.getId(), null);
}
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
}
}
} while (pageData != null && pageData.hasNext());
} catch (Exception e) {
log.error("Exception during loading edge entity view(s) on sync!", e);
}
}
private void syncDashboards(TenantId tenantId, Edge edge) {
log.trace("[{}] syncDashboards [{}]", tenantId, edge.getName());
try {
PageLink pageLink = new PageLink(DEFAULT_LIMIT);
PageData<DashboardInfo> pageData;
do {
pageData = dashboardService.findDashboardsByTenantIdAndEdgeId(tenantId, edge.getId(), pageLink);
if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) {
log.trace("[{}] [{}] dashboard(s) are going to be pushed to edge.", edge.getId(), pageData.getData().size());
for (DashboardInfo dashboardInfo : pageData.getData()) {
saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.DASHBOARD, EdgeEventActionType.ADDED, dashboardInfo.getId(), null);
}
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
}
}
} while (pageData != null && pageData.hasNext());
} catch (Exception e) {
log.error("Exception during loading edge dashboard(s) on sync!", e);
}
}
private void syncUsers(TenantId tenantId, Edge edge) {
log.trace("[{}] syncUsers [{}]", tenantId, edge.getName());
try {
PageLink pageLink = new PageLink(DEFAULT_LIMIT);
PageData<User> pageData;
do {
pageData = userService.findTenantAdmins(tenantId, pageLink);
pushUsersToEdge(tenantId, pageData, edge);
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
}
} while (pageData.hasNext());
syncCustomerUsers(tenantId, edge);
} catch (Exception e) {
log.error("Exception during loading edge user(s) on sync!", e);
}
}
private void syncCustomerUsers(TenantId tenantId, Edge edge) {
if (edge.getCustomerId() != null && !EntityId.NULL_UUID.equals(edge.getCustomerId().getId())) {
saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.CUSTOMER, EdgeEventActionType.ADDED, edge.getCustomerId(), null);
PageLink pageLink = new PageLink(DEFAULT_LIMIT);
PageData<User> pageData;
do {
pageData = userService.findCustomerUsers(tenantId, edge.getCustomerId(), pageLink);
pushUsersToEdge(tenantId, pageData, edge);
if (pageData != null && pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
}
} while (pageData != null && pageData.hasNext());
}
}
private void pushUsersToEdge(TenantId tenantId, PageData<User> pageData, Edge edge) {
if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) {
log.trace("[{}] [{}] user(s) are going to be pushed to edge.", edge.getId(), pageData.getData().size());
for (User user : pageData.getData()) {
saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.USER, EdgeEventActionType.ADDED, user.getId(), null);
}
}
}
private void syncWidgetsBundles(TenantId tenantId, Edge edge) {
log.trace("[{}] syncWidgetsBundles [{}]", tenantId, edge.getName());
List<WidgetsBundle> widgetsBundlesToPush = new ArrayList<>();
widgetsBundlesToPush.addAll(widgetsBundleService.findAllTenantWidgetsBundlesByTenantId(tenantId));
widgetsBundlesToPush.addAll(widgetsBundleService.findSystemWidgetsBundles(tenantId));
try {
for (WidgetsBundle widgetsBundle : widgetsBundlesToPush) {
saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.WIDGETS_BUNDLE, EdgeEventActionType.ADDED, widgetsBundle.getId(), null);
}
} catch (Exception e) {
log.error("Exception during loading widgets bundle(s) on sync!", e);
}
}
private void syncWidgetsTypes(TenantId tenantId, Edge edge) {
log.trace("[{}] syncWidgetsTypes [{}]", tenantId, edge.getName());
List<WidgetsBundle> widgetsBundlesToPush = new ArrayList<>();
widgetsBundlesToPush.addAll(widgetsBundleService.findAllTenantWidgetsBundlesByTenantId(tenantId));
widgetsBundlesToPush.addAll(widgetsBundleService.findSystemWidgetsBundles(tenantId));
try {
for (WidgetsBundle widgetsBundle : widgetsBundlesToPush) {
List<WidgetType> widgetTypesToPush =
widgetTypeService.findWidgetTypesByTenantIdAndBundleAlias(widgetsBundle.getTenantId(), widgetsBundle.getAlias());
for (WidgetType widgetType : widgetTypesToPush) {
saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.WIDGET_TYPE, EdgeEventActionType.ADDED, widgetType.getId(), null);
}
}
} catch (Exception e) {
log.error("Exception during loading widgets type(s) on sync!", e);
}
}
private void syncAdminSettings(TenantId tenantId, Edge edge) {
log.trace("[{}] syncAdminSettings [{}]", tenantId, edge.getName());
try {
AdminSettings systemMailSettings = adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, "mail");
saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.ADMIN_SETTINGS, EdgeEventActionType.UPDATED, null, mapper.valueToTree(systemMailSettings));
AdminSettings tenantMailSettings = convertToTenantAdminSettings(systemMailSettings.getKey(), (ObjectNode) systemMailSettings.getJsonValue());
saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.ADMIN_SETTINGS, EdgeEventActionType.UPDATED, null, mapper.valueToTree(tenantMailSettings));
AdminSettings systemMailTemplates = loadMailTemplates();
saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.ADMIN_SETTINGS, EdgeEventActionType.UPDATED, null, mapper.valueToTree(systemMailTemplates));
AdminSettings tenantMailTemplates = convertToTenantAdminSettings(systemMailTemplates.getKey(), (ObjectNode) systemMailTemplates.getJsonValue());
saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.ADMIN_SETTINGS, EdgeEventActionType.UPDATED, null, mapper.valueToTree(tenantMailTemplates));
} catch (Exception e) {
log.error("Can't load admin settings", e);
}
}
private AdminSettings loadMailTemplates() throws Exception {
Map<String, Object> mailTemplates = new HashMap<>();
Pattern startPattern = Pattern.compile("<div class=\"content\".*?>");
Pattern endPattern = Pattern.compile("<div class=\"footer\".*?>");
File[] files = new DefaultResourceLoader().getResource("classpath:/templates/").getFile().listFiles();
for (File file : files) {
Map<String, String> mailTemplate = new HashMap<>();
String name = validateName(file.getName());
String stringTemplate = FileUtils.readFileToString(file, StandardCharsets.UTF_8);
Matcher start = startPattern.matcher(stringTemplate);
Matcher end = endPattern.matcher(stringTemplate);
if (start.find() && end.find()) {
String body = StringUtils.substringBetween(stringTemplate, start.group(), end.group()).replaceAll("\t", "");
String subject = StringUtils.substringBetween(body, "<h2>", "</h2>");
mailTemplate.put("subject", subject);
mailTemplate.put("body", body);
mailTemplates.put(name, mailTemplate);
} else {
log.error("Can't load mail template from file {}", file.getName());
}
}
AdminSettings adminSettings = new AdminSettings();
adminSettings.setId(new AdminSettingsId(Uuids.timeBased()));
adminSettings.setKey("mailTemplates");
adminSettings.setJsonValue(mapper.convertValue(mailTemplates, JsonNode.class));
return adminSettings;
}
private String validateName(String name) throws Exception {
StringBuilder nameBuilder = new StringBuilder();
name = name.replace(".vm", "");
String[] nameParts = name.split("\\.");
if (nameParts.length >= 1) {
nameBuilder.append(nameParts[0]);
for (int i = 1; i < nameParts.length; i++) {
String word = WordUtils.capitalize(nameParts[i]);
nameBuilder.append(word);
}
return nameBuilder.toString();
} else {
throw new Exception("Error during filename validation");
}
}
private AdminSettings convertToTenantAdminSettings(String key, ObjectNode jsonValue) {
AdminSettings tenantMailSettings = new AdminSettings();
jsonValue.put("useSystemMailSettings", true);
tenantMailSettings.setJsonValue(jsonValue);
tenantMailSettings.setKey(key);
return tenantMailSettings;
}
@Override
public ListenableFuture<Void> processRuleChainMetadataRequestMsg(TenantId tenantId, Edge edge, RuleChainMetadataRequestMsg ruleChainMetadataRequestMsg) {
log.trace("[{}] processRuleChainMetadataRequestMsg [{}][{}]", tenantId, edge.getName(), ruleChainMetadataRequestMsg);
@ -448,7 +126,8 @@ public class DefaultSyncEdgeService implements SyncEdgeService {
if (ruleChainMetadataRequestMsg.getRuleChainIdMSB() != 0 && ruleChainMetadataRequestMsg.getRuleChainIdLSB() != 0) {
RuleChainId ruleChainId =
new RuleChainId(new UUID(ruleChainMetadataRequestMsg.getRuleChainIdMSB(), ruleChainMetadataRequestMsg.getRuleChainIdLSB()));
ListenableFuture<EdgeEvent> future = saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.RULE_CHAIN_METADATA, EdgeEventActionType.ADDED, ruleChainId, null);
ListenableFuture<EdgeEvent> future = saveEdgeEvent(tenantId, edge.getId(),
EdgeEventType.RULE_CHAIN_METADATA, EdgeEventActionType.ADDED, ruleChainId, null);
Futures.addCallback(future, new FutureCallback<EdgeEvent>() {
@Override
public void onSuccess(@Nullable EdgeEvent result) {
@ -584,7 +263,8 @@ public class DefaultSyncEdgeService implements SyncEdgeService {
return futureToSet;
}
private ListenableFuture<List<EntityRelation>> findRelationByQuery(TenantId tenantId, Edge edge, EntityId entityId, EntitySearchDirection direction) {
private ListenableFuture<List<EntityRelation>> findRelationByQuery(TenantId tenantId, Edge edge,
EntityId entityId, EntitySearchDirection direction) {
EntityRelationsQuery query = new EntityRelationsQuery();
query.setParameters(new RelationsSearchParameters(entityId, direction, -1, false));
return relationService.findByQuery(tenantId, query);
@ -596,7 +276,8 @@ public class DefaultSyncEdgeService implements SyncEdgeService {
SettableFuture<Void> futureToSet = SettableFuture.create();
if (deviceCredentialsRequestMsg.getDeviceIdMSB() != 0 && deviceCredentialsRequestMsg.getDeviceIdLSB() != 0) {
DeviceId deviceId = new DeviceId(new UUID(deviceCredentialsRequestMsg.getDeviceIdMSB(), deviceCredentialsRequestMsg.getDeviceIdLSB()));
ListenableFuture<EdgeEvent> future = saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE, EdgeEventActionType.CREDENTIALS_UPDATED, deviceId, null);
ListenableFuture<EdgeEvent> future = saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE,
EdgeEventActionType.CREDENTIALS_UPDATED, deviceId, null);
Futures.addCallback(future, new FutureCallback<EdgeEvent>() {
@Override
public void onSuccess(@Nullable EdgeEvent result) {
@ -619,8 +300,9 @@ public class DefaultSyncEdgeService implements SyncEdgeService {
SettableFuture<Void> futureToSet = SettableFuture.create();
if (userCredentialsRequestMsg.getUserIdMSB() != 0 && userCredentialsRequestMsg.getUserIdLSB() != 0) {
UserId userId = new UserId(new UUID(userCredentialsRequestMsg.getUserIdMSB(), userCredentialsRequestMsg.getUserIdLSB()));
ListenableFuture<EdgeEvent> future = saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.USER, EdgeEventActionType.CREDENTIALS_UPDATED, userId, null);
Futures.addCallback(future, new FutureCallback<EdgeEvent>() {
ListenableFuture<EdgeEvent> future = saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.USER,
EdgeEventActionType.CREDENTIALS_UPDATED, userId, null);
Futures.addCallback(future, new FutureCallback<>() {
@Override
public void onSuccess(@Nullable EdgeEvent result) {
futureToSet.set(null);
@ -636,6 +318,139 @@ public class DefaultSyncEdgeService implements SyncEdgeService {
return futureToSet;
}
@Override
public ListenableFuture<Void> processDeviceProfileDevicesRequestMsg(TenantId tenantId, Edge edge, DeviceProfileDevicesRequestMsg deviceProfileDevicesRequestMsg) {
log.trace("[{}] processDeviceProfileDevicesRequestMsg [{}][{}]", tenantId, edge.getName(), deviceProfileDevicesRequestMsg);
SettableFuture<Void> futureToSet = SettableFuture.create();
if (deviceProfileDevicesRequestMsg.getDeviceProfileIdMSB() != 0 && deviceProfileDevicesRequestMsg.getDeviceProfileIdLSB() != 0) {
DeviceProfileId deviceProfileId = new DeviceProfileId(new UUID(deviceProfileDevicesRequestMsg.getDeviceProfileIdMSB(), deviceProfileDevicesRequestMsg.getDeviceProfileIdLSB()));
DeviceProfile deviceProfileById = deviceProfileService.findDeviceProfileById(tenantId, deviceProfileId);
List<ListenableFuture<EdgeEvent>> futures;
if (deviceProfileById != null) {
futures = syncDevices(tenantId, edge, deviceProfileById.getName());
} else {
futures = new ArrayList<>();
}
Futures.addCallback(Futures.allAsList(futures), new FutureCallback<>() {
@Override
public void onSuccess(@Nullable List<EdgeEvent> result) {
futureToSet.set(null);
}
@Override
public void onFailure(Throwable t) {
log.error("Can't sync devices by device profile [{}]", deviceProfileDevicesRequestMsg, t);
futureToSet.setException(t);
}
}, dbCallbackExecutorService);
}
return futureToSet;
}
private List<ListenableFuture<EdgeEvent>> syncDevices(TenantId tenantId, Edge edge, String deviceType) {
List<ListenableFuture<EdgeEvent>> futures = new ArrayList<>();
log.trace("[{}] syncDevices [{}][{}]", tenantId, edge.getName(), deviceType);
try {
PageLink pageLink = new PageLink(DEFAULT_PAGE_SIZE);
PageData<Device> pageData;
do {
pageData = deviceService.findDevicesByTenantIdAndEdgeIdAndType(tenantId, edge.getId(), deviceType, pageLink);
if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) {
log.trace("[{}] [{}] device(s) are going to be pushed to edge.", edge.getId(), pageData.getData().size());
for (Device device : pageData.getData()) {
futures.add(saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE, EdgeEventActionType.ADDED, device.getId(), null));
}
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
}
}
} while (pageData != null && pageData.hasNext());
} catch (Exception e) {
log.error("Exception during loading edge device(s) on sync!", e);
}
return futures;
}
@Override
public ListenableFuture<Void> processWidgetBundleTypesRequestMsg(TenantId tenantId, Edge edge,
WidgetBundleTypesRequestMsg widgetBundleTypesRequestMsg) {
log.trace("[{}] processWidgetBundleTypesRequestMsg [{}][{}]", tenantId, edge.getName(), widgetBundleTypesRequestMsg);
SettableFuture<Void> futureToSet = SettableFuture.create();
if (widgetBundleTypesRequestMsg.getWidgetBundleIdMSB() != 0 && widgetBundleTypesRequestMsg.getWidgetBundleIdLSB() != 0) {
WidgetsBundleId widgetsBundleId = new WidgetsBundleId(new UUID(widgetBundleTypesRequestMsg.getWidgetBundleIdMSB(), widgetBundleTypesRequestMsg.getWidgetBundleIdLSB()));
WidgetsBundle widgetsBundleById = widgetsBundleService.findWidgetsBundleById(tenantId, widgetsBundleId);
List<ListenableFuture<EdgeEvent>> futures = new ArrayList<>();
if (widgetsBundleById != null) {
List<WidgetType> widgetTypesToPush =
widgetTypeService.findWidgetTypesByTenantIdAndBundleAlias(widgetsBundleById.getTenantId(), widgetsBundleById.getAlias());
for (WidgetType widgetType : widgetTypesToPush) {
futures.add(saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.WIDGET_TYPE, EdgeEventActionType.ADDED, widgetType.getId(), null));
}
}
Futures.addCallback(Futures.allAsList(futures), new FutureCallback<>() {
@Override
public void onSuccess(@Nullable List<EdgeEvent> result) {
futureToSet.set(null);
}
@Override
public void onFailure(Throwable t) {
log.error("Can't sync widget types by widget bundle [{}]", widgetBundleTypesRequestMsg, t);
futureToSet.setException(t);
}
}, dbCallbackExecutorService);
}
return futureToSet;
}
@Override
public ListenableFuture<Void> processEntityViewsRequestMsg(TenantId tenantId, Edge edge, EntityViewsRequestMsg entityViewsRequestMsg) {
log.trace("[{}] processEntityViewsRequestMsg [{}][{}]", tenantId, edge.getName(), entityViewsRequestMsg);
EntityId entityId = EntityIdFactory.getByTypeAndUuid(
EntityType.valueOf(entityViewsRequestMsg.getEntityType()),
new UUID(entityViewsRequestMsg.getEntityIdMSB(), entityViewsRequestMsg.getEntityIdLSB()));
SettableFuture<Void> futureToSet = SettableFuture.create();
Futures.addCallback(entityViewService.findEntityViewsByTenantIdAndEntityIdAsync(tenantId, entityId), new FutureCallback<>() {
@Override
public void onSuccess(@Nullable List<EntityView> entityViews) {
try {
if (entityViews != null && !entityViews.isEmpty()) {
for (EntityView entityView : entityViews) {
Futures.addCallback(relationService.checkRelation(tenantId, edge.getId(), entityView.getId(),
EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE), new FutureCallback<>() {
@Override
public void onSuccess(@Nullable Boolean result) {
if (Boolean.TRUE.equals(result)) {
saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.ENTITY_VIEW,
EdgeEventActionType.ADDED, entityView.getId(), null);
}
}
@Override
public void onFailure(Throwable t) {
log.error("Exception during loading relation [{}] to edge on sync!", t, t);
futureToSet.setException(t);
}
}, dbCallbackExecutorService);
}
}
futureToSet.set(null);
} catch (Exception e) {
log.error("Exception during loading relation(s) to edge on sync!", e);
futureToSet.setException(e);
}
}
@Override
public void onFailure(Throwable t) {
log.error("[{}] Can't find entity views by entity id [{}]", tenantId, entityId, t);
futureToSet.setException(t);
}
}, dbCallbackExecutorService);
return futureToSet;
}
private ListenableFuture<EdgeEvent> saveEdgeEvent(TenantId tenantId,
EdgeId edgeId,
EdgeEventType type,
@ -645,17 +460,10 @@ public class DefaultSyncEdgeService implements SyncEdgeService {
log.trace("Pushing edge event to edge queue. tenantId [{}], edgeId [{}], type [{}], action[{}], entityId [{}], body [{}]",
tenantId, edgeId, type, action, entityId, body);
EdgeEvent edgeEvent = new EdgeEvent();
edgeEvent.setTenantId(tenantId);
edgeEvent.setEdgeId(edgeId);
edgeEvent.setType(type);
edgeEvent.setAction(action);
if (entityId != null) {
edgeEvent.setEntityId(entityId.getId());
}
edgeEvent.setBody(body);
EdgeEvent edgeEvent = EdgeEventUtils.constructEdgeEvent(tenantId, edgeId, type, action, entityId, body);
ListenableFuture<EdgeEvent> future = edgeEventService.saveAsync(edgeEvent);
Futures.addCallback(future, new FutureCallback<EdgeEvent>() {
Futures.addCallback(future, new FutureCallback<>() {
@Override
public void onSuccess(@Nullable EdgeEvent result) {
tbClusterService.onEdgeEventUpdate(tenantId, edgeId);
@ -668,4 +476,5 @@ public class DefaultSyncEdgeService implements SyncEdgeService {
}, dbCallbackExecutorService);
return future;
}
}

25
application/src/main/java/org/thingsboard/server/service/edge/rpc/init/SyncEdgeService.java → application/src/main/java/org/thingsboard/server/service/edge/rpc/sync/EdgeRequestsService.java

@ -13,20 +13,21 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.edge.rpc.init;
package org.thingsboard.server.service.edge.rpc.sync;
import com.google.common.util.concurrent.ListenableFuture;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.gen.edge.AttributesRequestMsg;
import org.thingsboard.server.gen.edge.DeviceCredentialsRequestMsg;
import org.thingsboard.server.gen.edge.RelationRequestMsg;
import org.thingsboard.server.gen.edge.RuleChainMetadataRequestMsg;
import org.thingsboard.server.gen.edge.UserCredentialsRequestMsg;
import org.thingsboard.server.gen.edge.v1.AttributesRequestMsg;
import org.thingsboard.server.gen.edge.v1.DeviceCredentialsRequestMsg;
import org.thingsboard.server.gen.edge.v1.DeviceProfileDevicesRequestMsg;
import org.thingsboard.server.gen.edge.v1.EntityViewsRequestMsg;
import org.thingsboard.server.gen.edge.v1.RelationRequestMsg;
import org.thingsboard.server.gen.edge.v1.RuleChainMetadataRequestMsg;
import org.thingsboard.server.gen.edge.v1.UserCredentialsRequestMsg;
import org.thingsboard.server.gen.edge.v1.WidgetBundleTypesRequestMsg;
public interface SyncEdgeService {
void sync(TenantId tenantId, Edge edge);
public interface EdgeRequestsService {
ListenableFuture<Void> processRuleChainMetadataRequestMsg(TenantId tenantId, Edge edge, RuleChainMetadataRequestMsg ruleChainMetadataRequestMsg);
@ -37,4 +38,10 @@ public interface SyncEdgeService {
ListenableFuture<Void> processDeviceCredentialsRequestMsg(TenantId tenantId, Edge edge, DeviceCredentialsRequestMsg deviceCredentialsRequestMsg);
ListenableFuture<Void> processUserCredentialsRequestMsg(TenantId tenantId, Edge edge, UserCredentialsRequestMsg userCredentialsRequestMsg);
ListenableFuture<Void> processDeviceProfileDevicesRequestMsg(TenantId tenantId, Edge edge, DeviceProfileDevicesRequestMsg deviceProfileDevicesRequestMsg);
ListenableFuture<Void> processWidgetBundleTypesRequestMsg(TenantId tenantId, Edge edge, WidgetBundleTypesRequestMsg widgetBundleTypesRequestMsg);
ListenableFuture<Void> processEntityViewsRequestMsg(TenantId tenantId, Edge edge, EntityViewsRequestMsg entityViewsRequestMsg);
}

33
application/src/main/java/org/thingsboard/server/service/executors/GrpcCallbackExecutorService.java

@ -0,0 +1,33 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.executors;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.thingsboard.common.util.AbstractListeningExecutor;
@Component
public class GrpcCallbackExecutorService extends AbstractListeningExecutor {
@Value("${edges.grpc_callback_thread_pool_size}")
private int grpcCallbackExecutorThreadPoolSize;
@Override
protected int getThreadPollSize() {
return grpcCallbackExecutorThreadPoolSize;
}
}

7
application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java

@ -228,13 +228,14 @@ public class InstallScripts {
}
}
public void loadDemoRuleChains(TenantId tenantId) throws Exception {
public void loadDemoRuleChains(TenantId tenantId) {
try {
createDefaultRuleChains(tenantId);
createDefaultRuleChain(tenantId, "Thermostat");
createDefaultEdgeRuleChains(tenantId);
} catch (Exception e) {
log.error("Unable to load dashboard from json", e);
throw new RuntimeException("Unable to load dashboard from json", e);
log.error("Unable to load rule chain from json", e);
throw new RuntimeException("Unable to load rule chain from json", e);
}
}

4
application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/AbstractOAuth2ClientMapper.java

@ -177,9 +177,7 @@ public abstract class AbstractOAuth2ClientMapper {
tenant.setTitle(tenantName);
tenant = tenantService.saveTenant(tenant);
installScripts.createDefaultRuleChains(tenant.getId());
if (edgesEnabled) {
installScripts.createDefaultEdgeRuleChains(tenant.getId());
}
installScripts.createDefaultEdgeRuleChains(tenant.getId());
tenantProfileCache.evict(tenant.getId());
tbClusterService.onTenantChange(tenant, null);
tbClusterService.onEntityStateChange(tenant.getId(), tenant.getId(),

11
application/src/main/java/org/thingsboard/server/service/ttl/EdgeEventsCleanUpService.java

@ -19,10 +19,9 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.thingsboard.server.dao.edge.EdgeService;
import org.thingsboard.server.dao.edge.EdgeEventService;
import org.thingsboard.server.queue.discovery.PartitionService;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.ttl.AbstractCleanUpService;
@TbCoreComponent
@Slf4j
@ -35,17 +34,17 @@ public class EdgeEventsCleanUpService extends AbstractCleanUpService {
@Value("${sql.ttl.edge_events.enabled}")
private boolean ttlTaskExecutionEnabled;
private final EdgeService edgeService;
private final EdgeEventService edgeEventService;
public EdgeEventsCleanUpService(PartitionService partitionService, EdgeService edgeService) {
public EdgeEventsCleanUpService(PartitionService partitionService, EdgeEventService edgeEventService) {
super(partitionService);
this.edgeService = edgeService;
this.edgeEventService = edgeEventService;
}
@Scheduled(initialDelayString = "${sql.ttl.edge_events.execution_interval_ms}", fixedDelayString = "${sql.ttl.edge_events.execution_interval_ms}")
public void cleanUp() {
if (ttlTaskExecutionEnabled && isSystemTenantPartitionMine()) {
edgeService.cleanupEvents(ttl);
edgeEventService.cleanupEvents(ttl);
}
}

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

@ -702,11 +702,14 @@ edges:
enabled: "${EDGES_RPC_SSL_ENABLED:false}"
cert: "${EDGES_RPC_SSL_CERT:certChainFile.pem}"
private_key: "${EDGES_RPC_SSL_PRIVATE_KEY:privateKeyFile.pem}"
max_inbound_message_size: "${EDGES_RPC_MAX_INBOUND_MESSAGE_SIZE:4194304}"
storage:
max_read_records_count: "${EDGES_STORAGE_MAX_READ_RECORDS_COUNT:50}"
no_read_records_sleep: "${EDGES_NO_READ_RECORDS_SLEEP:1000}"
sleep_between_batches: "${EDGES_SLEEP_BETWEEN_BATCHES:1000}"
scheduler_pool_size: "${EDGES_SCHEDULER_POOL_SIZE:4}"
scheduler_pool_size: "${EDGES_SCHEDULER_POOL_SIZE:1}"
send_scheduler_pool_size: "${EDGES_SEND_SCHEDULER_POOL_SIZE:1}"
grpc_callback_thread_pool_size: "${EDGES_GRPC_CALLBACK_POOL_SIZE:1}"
edge_events_ttl: "${EDGES_EDGE_EVENTS_TTL:0}"
state:
persistToTelemetry: "${EDGES_PERSIST_STATE_TO_TELEMETRY:false}"

40
application/src/test/java/org/thingsboard/server/controller/BaseEdgeControllerTest.java

@ -37,12 +37,12 @@ import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.edge.imitator.EdgeImitator;
import org.thingsboard.server.gen.edge.AssetUpdateMsg;
import org.thingsboard.server.gen.edge.DeviceProfileUpdateMsg;
import org.thingsboard.server.gen.edge.DeviceUpdateMsg;
import org.thingsboard.server.gen.edge.RuleChainUpdateMsg;
import org.thingsboard.server.gen.edge.UserCredentialsUpdateMsg;
import org.thingsboard.server.gen.edge.UserUpdateMsg;
import org.thingsboard.server.gen.edge.v1.AssetUpdateMsg;
import org.thingsboard.server.gen.edge.v1.DeviceProfileUpdateMsg;
import org.thingsboard.server.gen.edge.v1.DeviceUpdateMsg;
import org.thingsboard.server.gen.edge.v1.RuleChainUpdateMsg;
import org.thingsboard.server.gen.edge.v1.UserCredentialsUpdateMsg;
import org.thingsboard.server.gen.edge.v1.UserUpdateMsg;
import java.util.ArrayList;
import java.util.Collections;
@ -657,7 +657,7 @@ public abstract class BaseEdgeControllerTest extends AbstractControllerTest {
Device device = new Device();
device.setName("Edge Device 1");
device.setType("test");
device.setType("default");
Device savedDevice = doPost("/api/device", device, Device.class);
doPost("/api/edge/" + edge.getId().getId().toString()
+ "/device/" + savedDevice.getId().getId().toString(), Device.class);
@ -671,29 +671,27 @@ public abstract class BaseEdgeControllerTest extends AbstractControllerTest {
EdgeImitator edgeImitator = new EdgeImitator("localhost", 7070, edge.getRoutingKey(), edge.getSecret());
edgeImitator.ignoreType(UserCredentialsUpdateMsg.class);
edgeImitator.expectMessageAmount(7);
edgeImitator.connect();
Assert.assertTrue(edgeImitator.waitForMessages());
Assert.assertEquals(7, edgeImitator.getDownlinkMsgs().size());
Assert.assertTrue(edgeImitator.findMessageByType(RuleChainUpdateMsg.class).isPresent());
Assert.assertTrue(edgeImitator.findMessageByType(DeviceProfileUpdateMsg.class).isPresent());
Assert.assertTrue(edgeImitator.findMessageByType(DeviceUpdateMsg.class).isPresent());
Assert.assertTrue(edgeImitator.findMessageByType(AssetUpdateMsg.class).isPresent());
Assert.assertTrue(edgeImitator.findMessageByType(UserUpdateMsg.class).isPresent());
edgeImitator.getDownlinkMsgs().clear();
Assert.assertEquals(2, edgeImitator.findAllMessagesByType(RuleChainUpdateMsg.class).size()); // one msg during sync process, another from edge creation
Assert.assertEquals(1, edgeImitator.findAllMessagesByType(DeviceProfileUpdateMsg.class).size()); // one msg during sync process for 'default' device profile
Assert.assertEquals(1, edgeImitator.findAllMessagesByType(DeviceUpdateMsg.class).size()); // one msg once device assigned to edge
Assert.assertEquals(2, edgeImitator.findAllMessagesByType(AssetUpdateMsg.class).size()); // two msgs - one during sync process, and one more once asset assigned to edge
Assert.assertEquals(1, edgeImitator.findAllMessagesByType(UserUpdateMsg.class).size()); // one msg during sync process for tenant admin user
edgeImitator.expectMessageAmount(7);
edgeImitator.expectMessageAmount(4);
doPost("/api/edge/sync/" + edge.getId());
Assert.assertTrue(edgeImitator.waitForMessages());
Assert.assertEquals(7, edgeImitator.getDownlinkMsgs().size());
Assert.assertTrue(edgeImitator.findMessageByType(RuleChainUpdateMsg.class).isPresent());
Assert.assertTrue(edgeImitator.findMessageByType(DeviceProfileUpdateMsg.class).isPresent());
Assert.assertTrue(edgeImitator.findMessageByType(DeviceUpdateMsg.class).isPresent());
Assert.assertTrue(edgeImitator.findMessageByType(AssetUpdateMsg.class).isPresent());
Assert.assertTrue(edgeImitator.findMessageByType(UserUpdateMsg.class).isPresent());
Assert.assertEquals(4, edgeImitator.getDownlinkMsgs().size());
Assert.assertEquals(1, edgeImitator.findAllMessagesByType(RuleChainUpdateMsg.class).size());
Assert.assertEquals(1, edgeImitator.findAllMessagesByType(DeviceProfileUpdateMsg.class).size());
Assert.assertEquals(1, edgeImitator.findAllMessagesByType(AssetUpdateMsg.class).size());
Assert.assertEquals(1, edgeImitator.findAllMessagesByType(UserUpdateMsg.class).size());
edgeImitator.allowIgnoredTypes();
try {

163
application/src/test/java/org/thingsboard/server/controller/BaseRuleChainControllerTest.java

@ -0,0 +1,163 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.controller;
import com.fasterxml.jackson.core.type.TypeReference;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
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 java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
public abstract class BaseRuleChainControllerTest extends AbstractControllerTest {
private IdComparator<RuleChain> idComparator = new IdComparator<>();
private Tenant savedTenant;
private User tenantAdmin;
@Before
public void beforeTest() throws Exception {
loginSysAdmin();
Tenant tenant = new Tenant();
tenant.setTitle("My tenant");
savedTenant = doPost("/api/tenant", tenant, Tenant.class);
Assert.assertNotNull(savedTenant);
tenantAdmin = new User();
tenantAdmin.setAuthority(Authority.TENANT_ADMIN);
tenantAdmin.setTenantId(savedTenant.getId());
tenantAdmin.setEmail("tenant2@thingsboard.org");
tenantAdmin.setFirstName("Joe");
tenantAdmin.setLastName("Downs");
tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1");
}
@After
public void afterTest() throws Exception {
loginSysAdmin();
doDelete("/api/tenant/" + savedTenant.getId().getId().toString())
.andExpect(status().isOk());
}
@Test
public void testSaveRuleChain() throws Exception {
RuleChain ruleChain = new RuleChain();
ruleChain.setName("RuleChain");
RuleChain savedRuleChain = doPost("/api/ruleChain", ruleChain, RuleChain.class);
Assert.assertNotNull(savedRuleChain);
Assert.assertNotNull(savedRuleChain.getId());
Assert.assertTrue(savedRuleChain.getCreatedTime() > 0);
Assert.assertEquals(ruleChain.getName(), savedRuleChain.getName());
savedRuleChain.setName("New RuleChain");
doPost("/api/ruleChain", savedRuleChain, RuleChain.class);
RuleChain foundRuleChain = doGet("/api/ruleChain/" + savedRuleChain.getId().getId().toString(), RuleChain.class);
Assert.assertEquals(savedRuleChain.getName(), foundRuleChain.getName());
}
@Test
public void testFindRuleChainById() throws Exception {
RuleChain ruleChain = new RuleChain();
ruleChain.setName("RuleChain");
RuleChain savedRuleChain = doPost("/api/ruleChain", ruleChain, RuleChain.class);
RuleChain foundRuleChain = doGet("/api/ruleChain/" + savedRuleChain.getId().getId().toString(), RuleChain.class);
Assert.assertNotNull(foundRuleChain);
Assert.assertEquals(savedRuleChain, foundRuleChain);
}
@Test
public void testDeleteRuleChain() throws Exception {
RuleChain ruleChain = new RuleChain();
ruleChain.setName("RuleChain");
RuleChain savedRuleChain = doPost("/api/ruleChain", ruleChain, RuleChain.class);
doDelete("/api/ruleChain/" + savedRuleChain.getId().getId().toString())
.andExpect(status().isOk());
doGet("/api/ruleChain/" + savedRuleChain.getId().getId().toString())
.andExpect(status().isNotFound());
}
@Test
public void testFindEdgeRuleChainsByTenantIdAndName() throws Exception {
Edge edge = constructEdge("My edge", "default");
Edge savedEdge = doPost("/api/edge", edge, Edge.class);
List<RuleChain> edgeRuleChains = new ArrayList<>();
PageLink pageLink = new PageLink(17);
PageData<RuleChain> pageData = doGetTypedWithPageLink("/api/edge/" + savedEdge.getId().getId() + "/ruleChains?",
new TypeReference<>() {}, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(1, pageData.getTotalElements());
edgeRuleChains.addAll(pageData.getData());
for (int i = 0; i < 28; i++) {
RuleChain ruleChain = new RuleChain();
ruleChain.setName("RuleChain " + i);
ruleChain.setType(RuleChainType.EDGE);
RuleChain savedRuleChain = doPost("/api/ruleChain", ruleChain, RuleChain.class);
doPost("/api/edge/" + savedEdge.getId().getId().toString()
+ "/ruleChain/" + savedRuleChain.getId().getId().toString(), RuleChain.class);
edgeRuleChains.add(savedRuleChain);
}
List<RuleChain> loadedEdgeRuleChains = new ArrayList<>();
pageLink = new PageLink(17);
do {
pageData = doGetTypedWithPageLink("/api/edge/" + savedEdge.getId().getId() + "/ruleChains?",
new TypeReference<>() {}, pageLink);
loadedEdgeRuleChains.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
}
} while (pageData.hasNext());
Collections.sort(edgeRuleChains, idComparator);
Collections.sort(loadedEdgeRuleChains, idComparator);
Assert.assertEquals(edgeRuleChains, loadedEdgeRuleChains);
for (RuleChain ruleChain : loadedEdgeRuleChains) {
if (!ruleChain.isRoot()) {
doDelete("/api/edge/" + savedEdge.getId().getId().toString()
+ "/ruleChain/" + ruleChain.getId().getId().toString(), RuleChain.class);
}
}
pageLink = new PageLink(17);
pageData = doGetTypedWithPageLink("/api/edge/" + savedEdge.getId().getId() + "/ruleChains?",
new TypeReference<>() {}, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(1, pageData.getTotalElements());
}
}

23
application/src/test/java/org/thingsboard/server/controller/sql/RuleChainControllerSqlTest.java

@ -0,0 +1,23 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.controller.sql;
import org.thingsboard.server.controller.BaseRuleChainControllerTest;
import org.thingsboard.server.dao.service.DaoSqlTest;
@DaoSqlTest
public class RuleChainControllerSqlTest extends BaseRuleChainControllerTest {
}

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

@ -65,7 +65,6 @@ import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.EntityIdFactory;
import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.query.EntityKeyValueType;
@ -86,32 +85,34 @@ import org.thingsboard.server.common.transport.adaptor.JsonConverter;
import org.thingsboard.server.controller.AbstractControllerTest;
import org.thingsboard.server.dao.edge.EdgeEventService;
import org.thingsboard.server.edge.imitator.EdgeImitator;
import org.thingsboard.server.gen.edge.AlarmUpdateMsg;
import org.thingsboard.server.gen.edge.AssetUpdateMsg;
import org.thingsboard.server.gen.edge.AttributeDeleteMsg;
import org.thingsboard.server.gen.edge.AttributesRequestMsg;
import org.thingsboard.server.gen.edge.CustomerUpdateMsg;
import org.thingsboard.server.gen.edge.DashboardUpdateMsg;
import org.thingsboard.server.gen.edge.DeviceCredentialsRequestMsg;
import org.thingsboard.server.gen.edge.DeviceCredentialsUpdateMsg;
import org.thingsboard.server.gen.edge.DeviceProfileUpdateMsg;
import org.thingsboard.server.gen.edge.DeviceRpcCallMsg;
import org.thingsboard.server.gen.edge.DeviceUpdateMsg;
import org.thingsboard.server.gen.edge.EdgeConfiguration;
import org.thingsboard.server.gen.edge.EntityDataProto;
import org.thingsboard.server.gen.edge.EntityViewUpdateMsg;
import org.thingsboard.server.gen.edge.RelationRequestMsg;
import org.thingsboard.server.gen.edge.RelationUpdateMsg;
import org.thingsboard.server.gen.edge.RpcResponseMsg;
import org.thingsboard.server.gen.edge.RuleChainMetadataRequestMsg;
import org.thingsboard.server.gen.edge.RuleChainMetadataUpdateMsg;
import org.thingsboard.server.gen.edge.RuleChainUpdateMsg;
import org.thingsboard.server.gen.edge.UpdateMsgType;
import org.thingsboard.server.gen.edge.UplinkMsg;
import org.thingsboard.server.gen.edge.UserCredentialsRequestMsg;
import org.thingsboard.server.gen.edge.UserCredentialsUpdateMsg;
import org.thingsboard.server.gen.edge.WidgetTypeUpdateMsg;
import org.thingsboard.server.gen.edge.WidgetsBundleUpdateMsg;
import org.thingsboard.server.gen.edge.v1.AlarmUpdateMsg;
import org.thingsboard.server.gen.edge.v1.AssetUpdateMsg;
import org.thingsboard.server.gen.edge.v1.AttributeDeleteMsg;
import org.thingsboard.server.gen.edge.v1.AttributesRequestMsg;
import org.thingsboard.server.gen.edge.v1.CustomerUpdateMsg;
import org.thingsboard.server.gen.edge.v1.DashboardUpdateMsg;
import org.thingsboard.server.gen.edge.v1.DeviceCredentialsRequestMsg;
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.DeviceUpdateMsg;
import org.thingsboard.server.gen.edge.v1.EdgeConfiguration;
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.RelationRequestMsg;
import org.thingsboard.server.gen.edge.v1.RelationUpdateMsg;
import org.thingsboard.server.gen.edge.v1.RpcResponseMsg;
import org.thingsboard.server.gen.edge.v1.RuleChainMetadataRequestMsg;
import org.thingsboard.server.gen.edge.v1.RuleChainMetadataUpdateMsg;
import org.thingsboard.server.gen.edge.v1.RuleChainUpdateMsg;
import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
import org.thingsboard.server.gen.edge.v1.UplinkMsg;
import org.thingsboard.server.gen.edge.v1.UserCredentialsRequestMsg;
import org.thingsboard.server.gen.edge.v1.UserCredentialsUpdateMsg;
import org.thingsboard.server.gen.edge.v1.UserUpdateMsg;
import org.thingsboard.server.gen.edge.v1.WidgetTypeUpdateMsg;
import org.thingsboard.server.gen.edge.v1.WidgetsBundleUpdateMsg;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.service.queue.TbClusterService;
@ -165,15 +166,15 @@ abstract public class BaseEdgeTest extends AbstractControllerTest {
// sleep 1 seconds to avoid CREDENTIALS updated message for the user
// user credentials is going to be stored and updated event pushed to edge notification service
// while service will be processing this event edge could be already added and additional message will be pushed
Thread.sleep(1000);
Thread.sleep(500);
installation();
edgeImitator = new EdgeImitator("localhost", 7070, edge.getRoutingKey(), edge.getSecret());
// TODO: voba - should be less, but events from SyncEdgeService stack with events from controller. will be fixed in next releases
// so ideally sync process should check current edge queue and add only missing entities to the edge queue
edgeImitator.expectMessageAmount(10);
edgeImitator.expectMessageAmount(9);
edgeImitator.connect();
testReceivedInitialData();
}
@After
@ -190,120 +191,78 @@ abstract public class BaseEdgeTest extends AbstractControllerTest {
@Test
public void test() throws Exception {
testReceivedInitialData();
int expectedDownlinkSize = 10;
Assert.assertEquals(expectedDownlinkSize, edgeImitator.getDownlinkMsgs().size());
testDevices();
expectedDownlinkSize = expectedDownlinkSize + 4;
Assert.assertEquals(expectedDownlinkSize, edgeImitator.getDownlinkMsgs().size());
testAssets();
expectedDownlinkSize = expectedDownlinkSize + 4;
Assert.assertEquals(expectedDownlinkSize, edgeImitator.getDownlinkMsgs().size());
testRuleChains();
expectedDownlinkSize = expectedDownlinkSize + 3;
Assert.assertEquals(expectedDownlinkSize, edgeImitator.getDownlinkMsgs().size());
testDashboards();
expectedDownlinkSize = expectedDownlinkSize + 3;
Assert.assertEquals(expectedDownlinkSize, edgeImitator.getDownlinkMsgs().size());
testRelations();
expectedDownlinkSize = expectedDownlinkSize + 2;
Assert.assertEquals(expectedDownlinkSize, edgeImitator.getDownlinkMsgs().size());
testAlarms();
expectedDownlinkSize = expectedDownlinkSize + 3;
Assert.assertEquals(expectedDownlinkSize, edgeImitator.getDownlinkMsgs().size());
testEntityView();
expectedDownlinkSize = expectedDownlinkSize + 2;
Assert.assertEquals(expectedDownlinkSize, edgeImitator.getDownlinkMsgs().size());
testCustomer();
expectedDownlinkSize = expectedDownlinkSize + 2;
Assert.assertEquals(expectedDownlinkSize, edgeImitator.getDownlinkMsgs().size());
testCustomerAndNewUser();
testWidgetsBundleAndWidgetType();
expectedDownlinkSize = expectedDownlinkSize + 4;
Assert.assertEquals(expectedDownlinkSize, edgeImitator.getDownlinkMsgs().size());
testTimeseries();
expectedDownlinkSize = expectedDownlinkSize + 1;
Assert.assertEquals(expectedDownlinkSize, edgeImitator.getDownlinkMsgs().size());
testAttributes();
expectedDownlinkSize = expectedDownlinkSize + 3;
Assert.assertEquals(expectedDownlinkSize, edgeImitator.getDownlinkMsgs().size());
testSendMessagesToCloud();
expectedDownlinkSize = expectedDownlinkSize + 9;
Assert.assertEquals(expectedDownlinkSize, edgeImitator.getDownlinkMsgs().size());
testRpcCall();
expectedDownlinkSize = expectedDownlinkSize + 1;
Assert.assertEquals(expectedDownlinkSize, edgeImitator.getDownlinkMsgs().size());
}
private Device findDeviceByName(String deviceName) throws Exception {
List<Device> edgeDevices = doGetTypedWithPageLink("/api/edge/" + edge.getId().getId().toString() + "/devices?",
new TypeReference<PageData<Device>>() {
}, new PageLink(100)).getData();
Optional<Device> foundDevice = edgeDevices.stream().filter(d -> d.getName().equals(deviceName)).findAny();
Assert.assertTrue(foundDevice.isPresent());
Device device = foundDevice.get();
Assert.assertEquals(deviceName, device.getName());
return device;
}
private Asset findAssetByName(String assetName) throws Exception {
List<Asset> edgeAssets = doGetTypedWithPageLink("/api/edge/" + edge.getId().getId().toString() + "/assets?",
new TypeReference<PageData<Asset>>() {
}, new PageLink(100)).getData();
testTimeseriesWithFailures();
Assert.assertEquals(1, edgeAssets.size());
Asset asset = edgeAssets.get(0);
Assert.assertEquals(assetName, asset.getName());
return asset;
}
private Device saveDevice(String deviceName, String type) throws Exception {
Device device = new Device();
device.setName(deviceName);
device.setType(type);
return doPost("/api/device", device, Device.class);
testSendMessagesToCloud();
}
private Asset saveAsset(String assetName) throws Exception {
Asset asset = new Asset();
asset.setName(assetName);
asset.setType("test");
return doPost("/api/asset", asset, Asset.class);
}
private void installation() throws Exception {
edge = doPost("/api/edge", constructEdge("Test Edge", "test"), Edge.class);
private void testRpcCall() throws Exception {
Device device = findDeviceByName("Edge Device 1");
DeviceProfile deviceProfile = this.createDeviceProfile(CUSTOM_DEVICE_PROFILE_NAME, null);
extendDeviceProfileData(deviceProfile);
doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class);
ObjectNode body = mapper.createObjectNode();
body.put("requestId", new Random().nextInt());
body.put("requestUUID", Uuids.timeBased().toString());
body.put("oneway", false);
body.put("expirationTime", System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(10));
body.put("method", "test_method");
body.put("params", "{\"param1\":\"value1\"}");
Device savedDevice = saveDevice("Edge Device 1", CUSTOM_DEVICE_PROFILE_NAME);
doPost("/api/edge/" + edge.getId().getId().toString()
+ "/device/" + savedDevice.getId().getId().toString(), Device.class);
EdgeEvent edgeEvent = constructEdgeEvent(tenantId, edge.getId(), EdgeEventActionType.RPC_CALL, device.getId().getId(), EdgeEventType.DEVICE, body);
edgeImitator.expectMessageAmount(1);
edgeEventService.saveAsync(edgeEvent);
clusterService.onEdgeEventUpdate(tenantId, edge.getId());
Assert.assertTrue(edgeImitator.waitForMessages());
Asset savedAsset = saveAsset("Edge Asset 1");
doPost("/api/edge/" + edge.getId().getId().toString()
+ "/asset/" + savedAsset.getId().getId().toString(), Asset.class);
}
AbstractMessage latestMessage = edgeImitator.getLatestMessage();
Assert.assertTrue(latestMessage instanceof DeviceRpcCallMsg);
DeviceRpcCallMsg latestDeviceRpcCallMsg = (DeviceRpcCallMsg) latestMessage;
Assert.assertEquals("test_method", latestDeviceRpcCallMsg.getRequestMsg().getMethod());
private void extendDeviceProfileData(DeviceProfile deviceProfile) {
DeviceProfileData profileData = deviceProfile.getProfileData();
List<DeviceProfileAlarm> alarms = new ArrayList<>();
DeviceProfileAlarm deviceProfileAlarm = new DeviceProfileAlarm();
deviceProfileAlarm.setAlarmType("High Temperature");
AlarmRule alarmRule = new AlarmRule();
alarmRule.setAlarmDetails("Alarm Details");
AlarmCondition alarmCondition = new AlarmCondition();
alarmCondition.setSpec(new SimpleAlarmConditionSpec());
List<AlarmConditionFilter> condition = new ArrayList<>();
AlarmConditionFilter alarmConditionFilter = new AlarmConditionFilter();
alarmConditionFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.ATTRIBUTE, "temperature"));
NumericFilterPredicate predicate = new NumericFilterPredicate();
predicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER);
predicate.setValue(new FilterPredicateValue<>(55.0));
alarmConditionFilter.setPredicate(predicate);
alarmConditionFilter.setValueType(EntityKeyValueType.NUMERIC);
condition.add(alarmConditionFilter);
alarmCondition.setCondition(condition);
alarmRule.setCondition(alarmCondition);
deviceProfileAlarm.setClearRule(alarmRule);
TreeMap<AlarmSeverity, AlarmRule> createRules = new TreeMap<>();
createRules.put(AlarmSeverity.CRITICAL, alarmRule);
deviceProfileAlarm.setCreateRules(createRules);
alarms.add(deviceProfileAlarm);
profileData.setAlarms(alarms);
profileData.setProvisionConfiguration(new AllowCreateNewDevicesDeviceProfileProvisionConfiguration("123"));
}
private void testReceivedInitialData() throws Exception {
@ -315,9 +274,6 @@ abstract public class BaseEdgeTest extends AbstractControllerTest {
testAutoGeneratedCodeByProtobuf(configuration);
UserId userId = edgeImitator.getUserId();
Assert.assertNotNull(userId);
Optional<DeviceUpdateMsg> deviceUpdateMsgOpt = edgeImitator.findMessageByType(DeviceUpdateMsg.class);
Assert.assertTrue(deviceUpdateMsgOpt.isPresent());
DeviceUpdateMsg deviceUpdateMsg = deviceUpdateMsgOpt.get();
@ -361,7 +317,7 @@ abstract public class BaseEdgeTest extends AbstractControllerTest {
Optional<RuleChainUpdateMsg> ruleChainUpdateMsgOpt = edgeImitator.findMessageByType(RuleChainUpdateMsg.class);
Assert.assertTrue(ruleChainUpdateMsgOpt.isPresent());
RuleChainUpdateMsg ruleChainUpdateMsg = ruleChainUpdateMsgOpt.get();
Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, ruleChainUpdateMsg.getMsgType());
Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, ruleChainUpdateMsg.getMsgType());
UUID ruleChainUUID = new UUID(ruleChainUpdateMsg.getIdMSB(), ruleChainUpdateMsg.getIdLSB());
RuleChain ruleChain = doGet("/api/ruleChain/" + ruleChainUUID.toString(), RuleChain.class);
Assert.assertNotNull(ruleChain);
@ -442,7 +398,6 @@ abstract public class BaseEdgeTest extends AbstractControllerTest {
log.info("Devices tested successfully");
}
private void testAssets() throws Exception {
log.info("Testing assets");
@ -513,23 +468,20 @@ abstract public class BaseEdgeTest extends AbstractControllerTest {
log.info("Testing RuleChains");
// 1
edgeImitator.expectMessageAmount(1);
edgeImitator.expectMessageAmount(2);
RuleChain ruleChain = new RuleChain();
ruleChain.setName("Edge Test Rule Chain");
ruleChain.setType(RuleChainType.EDGE);
RuleChain savedRuleChain = doPost("/api/ruleChain", ruleChain, RuleChain.class);
createRuleChainMetadata(savedRuleChain);
// sleep 1 seconds to avoid ENTITY_UPDATED_RPC_MESSAGE for the rule chain
// rule chain metadata is going to be stored and updated event pushed to edge notification service
// while service will be processing this event assignment rule chain to edge will be completed if bad timing
Thread.sleep(1000);
doPost("/api/edge/" + edge.getId().getId().toString()
+ "/ruleChain/" + savedRuleChain.getId().getId().toString(), RuleChain.class);
createRuleChainMetadata(savedRuleChain);
Assert.assertTrue(edgeImitator.waitForMessages());
AbstractMessage latestMessage = edgeImitator.getLatestMessage();
Assert.assertTrue(latestMessage instanceof RuleChainUpdateMsg);
RuleChainUpdateMsg ruleChainUpdateMsg = (RuleChainUpdateMsg) latestMessage;
Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, ruleChainUpdateMsg.getMsgType());
Optional<RuleChainUpdateMsg> ruleChainUpdateMsgOpt = edgeImitator.findMessageByType(RuleChainUpdateMsg.class);
Assert.assertTrue(ruleChainUpdateMsgOpt.isPresent());
RuleChainUpdateMsg ruleChainUpdateMsg = ruleChainUpdateMsgOpt.get();
Assert.assertTrue(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE.equals(ruleChainUpdateMsg.getMsgType()) ||
UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE.equals(ruleChainUpdateMsg.getMsgType()));
Assert.assertEquals(ruleChainUpdateMsg.getIdMSB(), savedRuleChain.getUuidId().getMostSignificantBits());
Assert.assertEquals(ruleChainUpdateMsg.getIdLSB(), savedRuleChain.getUuidId().getLeastSignificantBits());
Assert.assertEquals(ruleChainUpdateMsg.getName(), savedRuleChain.getName());
@ -542,9 +494,9 @@ abstract public class BaseEdgeTest extends AbstractControllerTest {
doDelete("/api/edge/" + edge.getId().getId().toString()
+ "/ruleChain/" + savedRuleChain.getId().getId().toString(), RuleChain.class);
Assert.assertTrue(edgeImitator.waitForMessages());
latestMessage = edgeImitator.getLatestMessage();
Assert.assertTrue(latestMessage instanceof RuleChainUpdateMsg);
ruleChainUpdateMsg = (RuleChainUpdateMsg) latestMessage;
ruleChainUpdateMsgOpt = edgeImitator.findMessageByType(RuleChainUpdateMsg.class);
Assert.assertTrue(ruleChainUpdateMsgOpt.isPresent());
ruleChainUpdateMsg = ruleChainUpdateMsgOpt.get();
Assert.assertEquals(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE, ruleChainUpdateMsg.getMsgType());
Assert.assertEquals(ruleChainUpdateMsg.getIdMSB(), savedRuleChain.getUuidId().getMostSignificantBits());
Assert.assertEquals(ruleChainUpdateMsg.getIdLSB(), savedRuleChain.getUuidId().getLeastSignificantBits());
@ -797,31 +749,41 @@ abstract public class BaseEdgeTest extends AbstractControllerTest {
doPost("/api/edge/" + edge.getId().getId().toString()
+ "/entityView/" + savedEntityView.getId().getId().toString(), EntityView.class);
Assert.assertTrue(edgeImitator.waitForMessages());
AbstractMessage latestMessage = edgeImitator.getLatestMessage();
Assert.assertTrue(latestMessage instanceof EntityViewUpdateMsg);
EntityViewUpdateMsg entityViewUpdateMsg = (EntityViewUpdateMsg) latestMessage;
Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, entityViewUpdateMsg.getMsgType());
Assert.assertEquals(entityViewUpdateMsg.getType(), savedEntityView.getType());
Assert.assertEquals(entityViewUpdateMsg.getName(), savedEntityView.getName());
Assert.assertEquals(entityViewUpdateMsg.getIdMSB(), savedEntityView.getUuidId().getMostSignificantBits());
Assert.assertEquals(entityViewUpdateMsg.getIdLSB(), savedEntityView.getUuidId().getLeastSignificantBits());
Assert.assertEquals(entityViewUpdateMsg.getEntityIdMSB(), device.getUuidId().getMostSignificantBits());
Assert.assertEquals(entityViewUpdateMsg.getEntityIdLSB(), device.getUuidId().getLeastSignificantBits());
Assert.assertEquals(entityViewUpdateMsg.getEntityType().name(), device.getId().getEntityType().name());
verifyEntityViewUpdateMsg(savedEntityView, device);
// 2
UplinkMsg.Builder uplinkMsgBuilder = UplinkMsg.newBuilder();
EntityViewsRequestMsg.Builder entityViewsRequestBuilder = EntityViewsRequestMsg.newBuilder();
entityViewsRequestBuilder.setEntityIdMSB(device.getUuidId().getMostSignificantBits());
entityViewsRequestBuilder.setEntityIdLSB(device.getUuidId().getLeastSignificantBits());
entityViewsRequestBuilder.setEntityType(device.getId().getEntityType().name());
testAutoGeneratedCodeByProtobuf(entityViewsRequestBuilder);
uplinkMsgBuilder.addEntityViewsRequestMsg(entityViewsRequestBuilder.build());
testAutoGeneratedCodeByProtobuf(uplinkMsgBuilder);
edgeImitator.expectResponsesAmount(1);
edgeImitator.expectMessageAmount(1);
edgeImitator.sendUplinkMsg(uplinkMsgBuilder.build());
Assert.assertTrue(edgeImitator.waitForResponses());
Assert.assertTrue(edgeImitator.waitForMessages());
verifyEntityViewUpdateMsg(savedEntityView, device);
// 3
edgeImitator.expectMessageAmount(1);
doDelete("/api/edge/" + edge.getId().getId().toString()
+ "/entityView/" + savedEntityView.getId().getId().toString(), EntityView.class);
Assert.assertTrue(edgeImitator.waitForMessages());
latestMessage = edgeImitator.getLatestMessage();
AbstractMessage latestMessage = edgeImitator.getLatestMessage();
Assert.assertTrue(latestMessage instanceof EntityViewUpdateMsg);
entityViewUpdateMsg = (EntityViewUpdateMsg) latestMessage;
EntityViewUpdateMsg entityViewUpdateMsg = (EntityViewUpdateMsg) latestMessage;
Assert.assertEquals(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE, entityViewUpdateMsg.getMsgType());
Assert.assertEquals(entityViewUpdateMsg.getIdMSB(), savedEntityView.getUuidId().getMostSignificantBits());
Assert.assertEquals(entityViewUpdateMsg.getIdLSB(), savedEntityView.getUuidId().getLeastSignificantBits());
// 3
edgeImitator.expectMessageAmount(1);
doDelete("/api/entityView/" + savedEntityView.getId().getId().toString())
.andExpect(status().isOk());
@ -830,7 +792,21 @@ abstract public class BaseEdgeTest extends AbstractControllerTest {
log.info("EntityView tested successfully");
}
private void testCustomer() throws Exception {
private void verifyEntityViewUpdateMsg(EntityView entityView, Device device) {
AbstractMessage latestMessage = edgeImitator.getLatestMessage();
Assert.assertTrue(latestMessage instanceof EntityViewUpdateMsg);
EntityViewUpdateMsg entityViewUpdateMsg = (EntityViewUpdateMsg) latestMessage;
Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, entityViewUpdateMsg.getMsgType());
Assert.assertEquals(entityViewUpdateMsg.getType(), entityView.getType());
Assert.assertEquals(entityViewUpdateMsg.getName(), entityView.getName());
Assert.assertEquals(entityViewUpdateMsg.getIdMSB(), entityView.getUuidId().getMostSignificantBits());
Assert.assertEquals(entityViewUpdateMsg.getIdLSB(), entityView.getUuidId().getLeastSignificantBits());
Assert.assertEquals(entityViewUpdateMsg.getEntityIdMSB(), device.getUuidId().getMostSignificantBits());
Assert.assertEquals(entityViewUpdateMsg.getEntityIdLSB(), device.getUuidId().getLeastSignificantBits());
Assert.assertEquals(entityViewUpdateMsg.getEntityType().name(), device.getId().getEntityType().name());
}
private void testCustomerAndNewUser() throws Exception {
log.info("Testing Customer");
// 1
@ -852,6 +828,26 @@ abstract public class BaseEdgeTest extends AbstractControllerTest {
// 2
edgeImitator.expectMessageAmount(1);
User customerUser = new User();
customerUser.setAuthority(Authority.CUSTOMER_USER);
customerUser.setTenantId(savedTenant.getId());
customerUser.setCustomerId(savedCustomer.getId());
customerUser.setEmail("customerUser@thingsboard.org");
customerUser.setFirstName("John");
customerUser.setLastName("Edwards");
User savedUser = doPost("/api/user", customerUser, User.class);
Assert.assertTrue(edgeImitator.waitForMessages());
latestMessage = edgeImitator.getLatestMessage();
Assert.assertTrue(latestMessage instanceof UserUpdateMsg);
UserUpdateMsg userUpdateMsg = (UserUpdateMsg) latestMessage;
Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, userUpdateMsg.getMsgType());
Assert.assertEquals(userUpdateMsg.getIdMSB(), savedUser.getUuidId().getMostSignificantBits());
Assert.assertEquals(userUpdateMsg.getIdLSB(), savedUser.getUuidId().getLeastSignificantBits());
Assert.assertEquals(userUpdateMsg.getEmail(), savedUser.getEmail());
testAutoGeneratedCodeByProtobuf(userUpdateMsg);
// 3
edgeImitator.expectMessageAmount(1);
doDelete("/api/customer/edge/" + edge.getId().getId().toString(), Edge.class);
Assert.assertTrue(edgeImitator.waitForMessages());
latestMessage = edgeImitator.getLatestMessage();
@ -861,7 +857,6 @@ abstract public class BaseEdgeTest extends AbstractControllerTest {
Assert.assertEquals(customerUpdateMsg.getIdMSB(), savedCustomer.getUuidId().getMostSignificantBits());
Assert.assertEquals(customerUpdateMsg.getIdLSB(), savedCustomer.getUuidId().getLeastSignificantBits());
// 3
edgeImitator.expectMessageAmount(1);
doDelete("/api/customer/" + savedCustomer.getId().getId().toString())
.andExpect(status().isOk());
@ -978,12 +973,12 @@ abstract public class BaseEdgeTest extends AbstractControllerTest {
log.info("Attributes tested successfully");
}
private void testAttributesDeleteMsg(Device device) throws JsonProcessingException, InterruptedException {
String deleteAttributesData = "{\"scope\":\"SERVER_SCOPE\",\"keys\":[\"key1\",\"key2\"]}";
JsonNode deleteAttributesEntityData = mapper.readTree(deleteAttributesData);
EdgeEvent edgeEvent = constructEdgeEvent(tenantId, edge.getId(), EdgeEventActionType.ATTRIBUTES_DELETED, device.getId().getId(), EdgeEventType.DEVICE, deleteAttributesEntityData);
private void testAttributesUpdatedMsg(Device device) throws JsonProcessingException, InterruptedException {
String attributesData = "{\"scope\":\"SERVER_SCOPE\",\"kv\":{\"key1\":\"value1\"}}";
JsonNode attributesEntityData = mapper.readTree(attributesData);
EdgeEvent edgeEvent1 = constructEdgeEvent(tenantId, edge.getId(), EdgeEventActionType.ATTRIBUTES_UPDATED, device.getId().getId(), EdgeEventType.DEVICE, attributesEntityData);
edgeImitator.expectMessageAmount(1);
edgeEventService.saveAsync(edgeEvent);
edgeEventService.saveAsync(edgeEvent1);
clusterService.onEdgeEventUpdate(tenantId, edge.getId());
Assert.assertTrue(edgeImitator.waitForMessages());
@ -993,15 +988,14 @@ abstract public class BaseEdgeTest extends AbstractControllerTest {
Assert.assertEquals(device.getUuidId().getMostSignificantBits(), latestEntityDataMsg.getEntityIdMSB());
Assert.assertEquals(device.getUuidId().getLeastSignificantBits(), latestEntityDataMsg.getEntityIdLSB());
Assert.assertEquals(device.getId().getEntityType().name(), latestEntityDataMsg.getEntityType());
Assert.assertEquals("SERVER_SCOPE", latestEntityDataMsg.getPostAttributeScope());
Assert.assertTrue(latestEntityDataMsg.hasAttributesUpdatedMsg());
Assert.assertTrue(latestEntityDataMsg.hasAttributeDeleteMsg());
AttributeDeleteMsg attributeDeleteMsg = latestEntityDataMsg.getAttributeDeleteMsg();
Assert.assertEquals(attributeDeleteMsg.getScope(), deleteAttributesEntityData.get("scope").asText());
Assert.assertEquals(2, attributeDeleteMsg.getAttributeNamesCount());
Assert.assertEquals("key1", attributeDeleteMsg.getAttributeNames(0));
Assert.assertEquals("key2", attributeDeleteMsg.getAttributeNames(1));
TransportProtos.PostAttributeMsg attributesUpdatedMsg = latestEntityDataMsg.getAttributesUpdatedMsg();
Assert.assertEquals(1, attributesUpdatedMsg.getKvCount());
TransportProtos.KeyValueProto keyValueProto = attributesUpdatedMsg.getKv(0);
Assert.assertEquals("key1", keyValueProto.getKey());
Assert.assertEquals("value1", keyValueProto.getStringV());
}
private void testPostAttributesMsg(Device device) throws JsonProcessingException, InterruptedException {
@ -1029,12 +1023,12 @@ abstract public class BaseEdgeTest extends AbstractControllerTest {
Assert.assertEquals("value2", keyValueProto.getStringV());
}
private void testAttributesUpdatedMsg(Device device) throws JsonProcessingException, InterruptedException {
String attributesData = "{\"scope\":\"SERVER_SCOPE\",\"kv\":{\"key1\":\"value1\"}}";
JsonNode attributesEntityData = mapper.readTree(attributesData);
EdgeEvent edgeEvent1 = constructEdgeEvent(tenantId, edge.getId(), EdgeEventActionType.ATTRIBUTES_UPDATED, device.getId().getId(), EdgeEventType.DEVICE, attributesEntityData);
private void testAttributesDeleteMsg(Device device) throws JsonProcessingException, InterruptedException {
String deleteAttributesData = "{\"scope\":\"SERVER_SCOPE\",\"keys\":[\"key1\",\"key2\"]}";
JsonNode deleteAttributesEntityData = mapper.readTree(deleteAttributesData);
EdgeEvent edgeEvent = constructEdgeEvent(tenantId, edge.getId(), EdgeEventActionType.ATTRIBUTES_DELETED, device.getId().getId(), EdgeEventType.DEVICE, deleteAttributesEntityData);
edgeImitator.expectMessageAmount(1);
edgeEventService.saveAsync(edgeEvent1);
edgeEventService.saveAsync(edgeEvent);
clusterService.onEdgeEventUpdate(tenantId, edge.getId());
Assert.assertTrue(edgeImitator.waitForMessages());
@ -1044,14 +1038,86 @@ abstract public class BaseEdgeTest extends AbstractControllerTest {
Assert.assertEquals(device.getUuidId().getMostSignificantBits(), latestEntityDataMsg.getEntityIdMSB());
Assert.assertEquals(device.getUuidId().getLeastSignificantBits(), latestEntityDataMsg.getEntityIdLSB());
Assert.assertEquals(device.getId().getEntityType().name(), latestEntityDataMsg.getEntityType());
Assert.assertEquals("SERVER_SCOPE", latestEntityDataMsg.getPostAttributeScope());
Assert.assertTrue(latestEntityDataMsg.hasAttributesUpdatedMsg());
TransportProtos.PostAttributeMsg attributesUpdatedMsg = latestEntityDataMsg.getAttributesUpdatedMsg();
Assert.assertEquals(1, attributesUpdatedMsg.getKvCount());
TransportProtos.KeyValueProto keyValueProto = attributesUpdatedMsg.getKv(0);
Assert.assertEquals("key1", keyValueProto.getKey());
Assert.assertEquals("value1", keyValueProto.getStringV());
Assert.assertTrue(latestEntityDataMsg.hasAttributeDeleteMsg());
AttributeDeleteMsg attributeDeleteMsg = latestEntityDataMsg.getAttributeDeleteMsg();
Assert.assertEquals(attributeDeleteMsg.getScope(), deleteAttributesEntityData.get("scope").asText());
Assert.assertEquals(2, attributeDeleteMsg.getAttributeNamesCount());
Assert.assertEquals("key1", attributeDeleteMsg.getAttributeNames(0));
Assert.assertEquals("key2", attributeDeleteMsg.getAttributeNames(1));
}
private void testRpcCall() throws Exception {
Device device = findDeviceByName("Edge Device 1");
ObjectNode body = mapper.createObjectNode();
body.put("requestId", new Random().nextInt());
body.put("requestUUID", Uuids.timeBased().toString());
body.put("oneway", false);
body.put("expirationTime", System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(10));
body.put("method", "test_method");
body.put("params", "{\"param1\":\"value1\"}");
EdgeEvent edgeEvent = constructEdgeEvent(tenantId, edge.getId(), EdgeEventActionType.RPC_CALL, device.getId().getId(), EdgeEventType.DEVICE, body);
edgeImitator.expectMessageAmount(1);
edgeEventService.saveAsync(edgeEvent);
clusterService.onEdgeEventUpdate(tenantId, edge.getId());
Assert.assertTrue(edgeImitator.waitForMessages());
AbstractMessage latestMessage = edgeImitator.getLatestMessage();
Assert.assertTrue(latestMessage instanceof DeviceRpcCallMsg);
DeviceRpcCallMsg latestDeviceRpcCallMsg = (DeviceRpcCallMsg) latestMessage;
Assert.assertEquals("test_method", latestDeviceRpcCallMsg.getRequestMsg().getMethod());
}
private void testTimeseriesWithFailures() throws Exception {
log.info("Testing timeseries with failures");
int numberOfTimeseriesToSend = 1000;
edgeImitator.setRandomFailuresOnTimeseriesDownlink(true);
// imitator will generate failure in 5% of cases
edgeImitator.setFailureProbability(5.0);
edgeImitator.expectMessageAmount(numberOfTimeseriesToSend);
Device device = findDeviceByName("Edge Device 1");
for (int idx = 1; idx <= numberOfTimeseriesToSend; idx++) {
String timeseriesData = "{\"data\":{\"idx\":" + idx + "},\"ts\":" + System.currentTimeMillis() + "}";
JsonNode timeseriesEntityData = mapper.readTree(timeseriesData);
EdgeEvent edgeEvent = constructEdgeEvent(tenantId, edge.getId(), EdgeEventActionType.TIMESERIES_UPDATED,
device.getId().getId(), EdgeEventType.DEVICE, timeseriesEntityData);
edgeEventService.saveAsync(edgeEvent);
clusterService.onEdgeEventUpdate(tenantId, edge.getId());
}
Assert.assertTrue(edgeImitator.waitForMessages(60));
List<EntityDataProto> allTelemetryMsgs = edgeImitator.findAllMessagesByType(EntityDataProto.class);
Assert.assertEquals(numberOfTimeseriesToSend, allTelemetryMsgs.size());
for (int idx = 1; idx <= numberOfTimeseriesToSend; idx++) {
Assert.assertTrue(isIdxExistsInTheDownlinkList(idx, allTelemetryMsgs));
}
edgeImitator.setRandomFailuresOnTimeseriesDownlink(false);
log.info("Timeseries with failures tested successfully");
}
private boolean isIdxExistsInTheDownlinkList(int idx, List<EntityDataProto> allTelemetryMsgs) {
for (EntityDataProto proto : allTelemetryMsgs) {
TransportProtos.PostTelemetryMsg postTelemetryMsg = proto.getPostTelemetryMsg();
Assert.assertEquals(1, postTelemetryMsg.getTsKvListCount());
TransportProtos.TsKvListProto tsKvListProto = postTelemetryMsg.getTsKvList(0);
Assert.assertEquals(1, tsKvListProto.getKvCount());
TransportProtos.KeyValueProto keyValueProto = tsKvListProto.getKv(0);
Assert.assertEquals("idx", keyValueProto.getKey());
if (keyValueProto.getLongV() == idx) {
return true;
}
}
return false;
}
private void testSendMessagesToCloud() throws Exception {
@ -1095,11 +1161,12 @@ abstract public class BaseEdgeTest extends AbstractControllerTest {
Assert.assertTrue(edgeImitator.waitForMessages());
AbstractMessage latestMessage = edgeImitator.getLatestMessage();
Assert.assertTrue(latestMessage instanceof DeviceUpdateMsg);
DeviceUpdateMsg latestDeviceUpdateMsg = (DeviceUpdateMsg) latestMessage;
Assert.assertEquals("Edge Device 2", latestDeviceUpdateMsg.getName());
Assert.assertTrue(latestMessage instanceof DeviceCredentialsRequestMsg);
DeviceCredentialsRequestMsg latestDeviceCredentialsRequestMsg = (DeviceCredentialsRequestMsg) latestMessage;
Assert.assertEquals(uuid.getMostSignificantBits(), latestDeviceCredentialsRequestMsg.getDeviceIdMSB());
Assert.assertEquals(uuid.getLeastSignificantBits(), latestDeviceCredentialsRequestMsg.getDeviceIdLSB());
UUID newDeviceId = new UUID(latestDeviceUpdateMsg.getIdMSB(), latestDeviceUpdateMsg.getIdLSB());
UUID newDeviceId = new UUID(latestDeviceCredentialsRequestMsg.getDeviceIdMSB(), latestDeviceCredentialsRequestMsg.getDeviceIdLSB());
Device device = doGet("/api/device/" + newDeviceId, Device.class);
Assert.assertNotNull(device);
@ -1123,7 +1190,7 @@ abstract public class BaseEdgeTest extends AbstractControllerTest {
uplinkMsgBuilder.addDeviceUpdateMsg(deviceUpdateMsgBuilder.build());
edgeImitator.expectResponsesAmount(1);
edgeImitator.expectMessageAmount(1);
edgeImitator.expectMessageAmount(2);
testAutoGeneratedCodeByProtobuf(uplinkMsgBuilder);
edgeImitator.sendUplinkMsg(uplinkMsgBuilder.build());
@ -1131,7 +1198,7 @@ abstract public class BaseEdgeTest extends AbstractControllerTest {
Assert.assertTrue(edgeImitator.waitForResponses());
Assert.assertTrue(edgeImitator.waitForMessages());
AbstractMessage latestMessage = edgeImitator.getLatestMessage();
AbstractMessage latestMessage = edgeImitator.getMessageFromTail(2);
Assert.assertTrue(latestMessage instanceof DeviceUpdateMsg);
DeviceUpdateMsg latestDeviceUpdateMsg = (DeviceUpdateMsg) latestMessage;
Assert.assertNotEquals(deviceOnCloudName, latestDeviceUpdateMsg.getName());
@ -1144,6 +1211,18 @@ abstract public class BaseEdgeTest extends AbstractControllerTest {
Device device = doGet("/api/device/" + newDeviceId, Device.class);
Assert.assertNotNull(device);
Assert.assertNotEquals(deviceOnCloudName, device.getName());
latestMessage = edgeImitator.getLatestMessage();
Assert.assertTrue(latestMessage instanceof DeviceCredentialsRequestMsg);
DeviceCredentialsRequestMsg latestDeviceCredentialsRequestMsg = (DeviceCredentialsRequestMsg) latestMessage;
Assert.assertEquals(uuid.getMostSignificantBits(), latestDeviceCredentialsRequestMsg.getDeviceIdMSB());
Assert.assertEquals(uuid.getLeastSignificantBits(), latestDeviceCredentialsRequestMsg.getDeviceIdLSB());
newDeviceId = new UUID(latestDeviceCredentialsRequestMsg.getDeviceIdMSB(), latestDeviceCredentialsRequestMsg.getDeviceIdLSB());
device = doGet("/api/device/" + newDeviceId, Device.class);
Assert.assertNotNull(device);
Assert.assertNotEquals(deviceOnCloudName, device.getName());
}
private void sendRelationRequest() throws Exception {
@ -1225,46 +1304,6 @@ abstract public class BaseEdgeTest extends AbstractControllerTest {
Assert.assertEquals(AlarmSeverity.CRITICAL, alarmInfo.getSeverity());
}
private void sendRelation() throws Exception {
List<Device> edgeDevices = doGetTypedWithPageLink("/api/edge/" + edge.getId().getId().toString() + "/devices?",
new TypeReference<PageData<Device>>() {}, new PageLink(100)).getData();
Optional<Device> foundDevice1 = edgeDevices.stream().filter(device1 -> device1.getName().equals("Edge Device 1")).findAny();
Assert.assertTrue(foundDevice1.isPresent());
Device device1 = foundDevice1.get();
Optional<Device> foundDevice2 = edgeDevices.stream().filter(device2 -> device2.getName().equals("Edge Device 2")).findAny();
Assert.assertTrue(foundDevice2.isPresent());
Device device2 = foundDevice2.get();
UplinkMsg.Builder uplinkMsgBuilder = UplinkMsg.newBuilder();
RelationUpdateMsg.Builder relationUpdateMsgBuilder = RelationUpdateMsg.newBuilder();
relationUpdateMsgBuilder.setType("test");
relationUpdateMsgBuilder.setTypeGroup(RelationTypeGroup.COMMON.name());
relationUpdateMsgBuilder.setToIdMSB(device1.getId().getId().getMostSignificantBits());
relationUpdateMsgBuilder.setToIdLSB(device1.getId().getId().getLeastSignificantBits());
relationUpdateMsgBuilder.setToEntityType(device1.getId().getEntityType().name());
relationUpdateMsgBuilder.setFromIdMSB(device2.getId().getId().getMostSignificantBits());
relationUpdateMsgBuilder.setFromIdLSB(device2.getId().getId().getLeastSignificantBits());
relationUpdateMsgBuilder.setFromEntityType(device2.getId().getEntityType().name());
relationUpdateMsgBuilder.setAdditionalInfo("{}");
testAutoGeneratedCodeByProtobuf(relationUpdateMsgBuilder);
uplinkMsgBuilder.addRelationUpdateMsg(relationUpdateMsgBuilder.build());
testAutoGeneratedCodeByProtobuf(uplinkMsgBuilder);
edgeImitator.expectResponsesAmount(1);
edgeImitator.sendUplinkMsg(uplinkMsgBuilder.build());
Assert.assertTrue(edgeImitator.waitForResponses());
EntityRelation relation = doGet("/api/relation?" +
"&fromId=" + device2.getId().getId().toString() +
"&fromType=" + device2.getId().getEntityType().name() +
"&relationType=" + "test" +
"&relationTypeGroup=" + RelationTypeGroup.COMMON.name() +
"&toId=" + device1.getId().getId().toString() +
"&toType=" + device1.getId().getEntityType().name(), EntityRelation.class);
Assert.assertNotNull(relation);
}
private void sendTelemetry() throws Exception {
List<Device> edgeDevices = doGetTypedWithPageLink("/api/edge/" + edge.getId().getId().toString() + "/devices?",
new TypeReference<PageData<Device>>() {}, new PageLink(100)).getData();
@ -1309,9 +1348,15 @@ abstract public class BaseEdgeTest extends AbstractControllerTest {
edgeImitator.sendUplinkMsg(uplinkMsgBuilder2.build());
Assert.assertTrue(edgeImitator.waitForResponses());
// Wait before device attributes saved to database before requesting them from controller
Thread.sleep(1000);
Map<String, List<Map<String, String>>> timeseries = doGetAsyncTyped("/api/plugins/telemetry/DEVICE/" + device.getUuidId() + "/values/timeseries?keys=" + timeseriesKey, new TypeReference<>() {});
int attempt = 0;
Map<String, List<Map<String, String>>> timeseries;
do {
timeseries = doGetAsyncTyped("/api/plugins/telemetry/DEVICE/" + device.getUuidId() + "/values/timeseries?keys=" + timeseriesKey,
new TypeReference<>() {});
// Wait before device attributes saved to database before requesting them from controller
Thread.sleep(100);
attempt++;
} while (!timeseries.containsKey(timeseriesKey) || attempt < 10);
Assert.assertTrue(timeseries.containsKey(timeseriesKey));
Assert.assertEquals(1, timeseries.get(timeseriesKey).size());
Assert.assertEquals(timeseriesValue, timeseries.get(timeseriesKey).get(0).get("value"));
@ -1323,6 +1368,69 @@ abstract public class BaseEdgeTest extends AbstractControllerTest {
}
private void sendRelation() throws Exception {
List<Device> edgeDevices = doGetTypedWithPageLink("/api/edge/" + edge.getId().getId().toString() + "/devices?",
new TypeReference<PageData<Device>>() {}, new PageLink(100)).getData();
Optional<Device> foundDevice1 = edgeDevices.stream().filter(device1 -> device1.getName().equals("Edge Device 1")).findAny();
Assert.assertTrue(foundDevice1.isPresent());
Device device1 = foundDevice1.get();
Optional<Device> foundDevice2 = edgeDevices.stream().filter(device2 -> device2.getName().equals("Edge Device 2")).findAny();
Assert.assertTrue(foundDevice2.isPresent());
Device device2 = foundDevice2.get();
UplinkMsg.Builder uplinkMsgBuilder = UplinkMsg.newBuilder();
RelationUpdateMsg.Builder relationUpdateMsgBuilder = RelationUpdateMsg.newBuilder();
relationUpdateMsgBuilder.setType("test");
relationUpdateMsgBuilder.setTypeGroup(RelationTypeGroup.COMMON.name());
relationUpdateMsgBuilder.setToIdMSB(device1.getId().getId().getMostSignificantBits());
relationUpdateMsgBuilder.setToIdLSB(device1.getId().getId().getLeastSignificantBits());
relationUpdateMsgBuilder.setToEntityType(device1.getId().getEntityType().name());
relationUpdateMsgBuilder.setFromIdMSB(device2.getId().getId().getMostSignificantBits());
relationUpdateMsgBuilder.setFromIdLSB(device2.getId().getId().getLeastSignificantBits());
relationUpdateMsgBuilder.setFromEntityType(device2.getId().getEntityType().name());
relationUpdateMsgBuilder.setAdditionalInfo("{}");
testAutoGeneratedCodeByProtobuf(relationUpdateMsgBuilder);
uplinkMsgBuilder.addRelationUpdateMsg(relationUpdateMsgBuilder.build());
testAutoGeneratedCodeByProtobuf(uplinkMsgBuilder);
edgeImitator.expectResponsesAmount(1);
edgeImitator.sendUplinkMsg(uplinkMsgBuilder.build());
Assert.assertTrue(edgeImitator.waitForResponses());
EntityRelation relation = doGet("/api/relation?" +
"&fromId=" + device2.getId().getId().toString() +
"&fromType=" + device2.getId().getEntityType().name() +
"&relationType=" + "test" +
"&relationTypeGroup=" + RelationTypeGroup.COMMON.name() +
"&toId=" + device1.getId().getId().toString() +
"&toType=" + device1.getId().getEntityType().name(), EntityRelation.class);
Assert.assertNotNull(relation);
}
private void sendDeleteDeviceOnEdge() throws Exception {
Device device = findDeviceByName("Edge Device 2");
UplinkMsg.Builder upLinkMsgBuilder = UplinkMsg.newBuilder();
DeviceUpdateMsg.Builder deviceDeleteMsgBuilder = DeviceUpdateMsg.newBuilder();
deviceDeleteMsgBuilder.setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE);
deviceDeleteMsgBuilder.setIdMSB(device.getId().getId().getMostSignificantBits());
deviceDeleteMsgBuilder.setIdLSB(device.getId().getId().getLeastSignificantBits());
testAutoGeneratedCodeByProtobuf(deviceDeleteMsgBuilder);
upLinkMsgBuilder.addDeviceUpdateMsg(deviceDeleteMsgBuilder.build());
testAutoGeneratedCodeByProtobuf(upLinkMsgBuilder);
edgeImitator.expectResponsesAmount(1);
edgeImitator.sendUplinkMsg(upLinkMsgBuilder.build());
Assert.assertTrue(edgeImitator.waitForResponses());
device = doGet("/api/device/" + device.getId().getId().toString(), Device.class);
Assert.assertNotNull(device);
List<Device> edgeDevices = doGetTypedWithPageLink("/api/edge/" + edge.getId().getId().toString() + "/devices?",
new TypeReference<PageData<Device>>() {
}, new PageLink(100)).getData();
Assert.assertFalse(edgeDevices.contains(device));
}
private void sendRuleChainMetadataRequest() throws Exception {
RuleChainId edgeRootRuleChainId = edge.getRootRuleChainId();
@ -1351,12 +1459,10 @@ abstract public class BaseEdgeTest extends AbstractControllerTest {
}
private void sendUserCredentialsRequest() throws Exception {
UserId userId = edgeImitator.getUserId();
UplinkMsg.Builder uplinkMsgBuilder = UplinkMsg.newBuilder();
UserCredentialsRequestMsg.Builder userCredentialsRequestMsgBuilder = UserCredentialsRequestMsg.newBuilder();
userCredentialsRequestMsgBuilder.setUserIdMSB(userId.getId().getMostSignificantBits());
userCredentialsRequestMsgBuilder.setUserIdLSB(userId.getId().getLeastSignificantBits());
userCredentialsRequestMsgBuilder.setUserIdMSB(tenantAdmin.getId().getId().getMostSignificantBits());
userCredentialsRequestMsgBuilder.setUserIdLSB(tenantAdmin.getId().getId().getLeastSignificantBits());
testAutoGeneratedCodeByProtobuf(userCredentialsRequestMsgBuilder);
uplinkMsgBuilder.addUserCredentialsRequestMsg(userCredentialsRequestMsgBuilder.build());
@ -1371,8 +1477,8 @@ abstract public class BaseEdgeTest extends AbstractControllerTest {
AbstractMessage latestMessage = edgeImitator.getLatestMessage();
Assert.assertTrue(latestMessage instanceof UserCredentialsUpdateMsg);
UserCredentialsUpdateMsg userCredentialsUpdateMsg = (UserCredentialsUpdateMsg) latestMessage;
Assert.assertEquals(userCredentialsUpdateMsg.getUserIdMSB(), userId.getId().getMostSignificantBits());
Assert.assertEquals(userCredentialsUpdateMsg.getUserIdLSB(), userId.getId().getLeastSignificantBits());
Assert.assertEquals(userCredentialsUpdateMsg.getUserIdMSB(), tenantAdmin.getId().getId().getMostSignificantBits());
Assert.assertEquals(userCredentialsUpdateMsg.getUserIdLSB(), tenantAdmin.getId().getId().getLeastSignificantBits());
testAutoGeneratedCodeByProtobuf(userCredentialsUpdateMsg);
}
@ -1406,25 +1512,6 @@ abstract public class BaseEdgeTest extends AbstractControllerTest {
Assert.assertEquals(deviceCredentialsUpdateMsg.getCredentialsId(), deviceCredentials.getCredentialsId());
}
private void sendDeviceCredentialsUpdate() throws Exception {
Device device = findDeviceByName("Edge Device 1");
UplinkMsg.Builder uplinkMsgBuilder = UplinkMsg.newBuilder();
DeviceCredentialsUpdateMsg.Builder deviceCredentialsUpdateMsgBuilder = DeviceCredentialsUpdateMsg.newBuilder();
deviceCredentialsUpdateMsgBuilder.setDeviceIdMSB(device.getUuidId().getMostSignificantBits());
deviceCredentialsUpdateMsgBuilder.setDeviceIdLSB(device.getUuidId().getLeastSignificantBits());
deviceCredentialsUpdateMsgBuilder.setCredentialsType(DeviceCredentialsType.ACCESS_TOKEN.name());
deviceCredentialsUpdateMsgBuilder.setCredentialsId("NEW_TOKEN");
testAutoGeneratedCodeByProtobuf(deviceCredentialsUpdateMsgBuilder);
uplinkMsgBuilder.addDeviceCredentialsUpdateMsg(deviceCredentialsUpdateMsgBuilder.build());
testAutoGeneratedCodeByProtobuf(uplinkMsgBuilder);
edgeImitator.expectResponsesAmount(1);
edgeImitator.sendUplinkMsg(uplinkMsgBuilder.build());
Assert.assertTrue(edgeImitator.waitForResponses());
}
private void sendDeviceRpcResponse() throws Exception {
Device device = findDeviceByName("Edge Device 1");
@ -1450,6 +1537,25 @@ abstract public class BaseEdgeTest extends AbstractControllerTest {
Assert.assertTrue(edgeImitator.waitForResponses());
}
private void sendDeviceCredentialsUpdate() throws Exception {
Device device = findDeviceByName("Edge Device 1");
UplinkMsg.Builder uplinkMsgBuilder = UplinkMsg.newBuilder();
DeviceCredentialsUpdateMsg.Builder deviceCredentialsUpdateMsgBuilder = DeviceCredentialsUpdateMsg.newBuilder();
deviceCredentialsUpdateMsgBuilder.setDeviceIdMSB(device.getUuidId().getMostSignificantBits());
deviceCredentialsUpdateMsgBuilder.setDeviceIdLSB(device.getUuidId().getLeastSignificantBits());
deviceCredentialsUpdateMsgBuilder.setCredentialsType(DeviceCredentialsType.ACCESS_TOKEN.name());
deviceCredentialsUpdateMsgBuilder.setCredentialsId("NEW_TOKEN");
testAutoGeneratedCodeByProtobuf(deviceCredentialsUpdateMsgBuilder);
uplinkMsgBuilder.addDeviceCredentialsUpdateMsg(deviceCredentialsUpdateMsgBuilder.build());
testAutoGeneratedCodeByProtobuf(uplinkMsgBuilder);
edgeImitator.expectResponsesAmount(1);
edgeImitator.sendUplinkMsg(uplinkMsgBuilder.build());
Assert.assertTrue(edgeImitator.waitForResponses());
}
private void sendAttributesRequest() throws Exception {
Device device = findDeviceByName("Edge Device 1");
sendAttributesRequest(device, DataConstants.SERVER_SCOPE, "{\"key1\":\"value1\"}", "key1", "value1");
@ -1463,7 +1569,8 @@ abstract public class BaseEdgeTest extends AbstractControllerTest {
attributesData);
// Wait before device attributes saved to database before requesting them from edge
Thread.sleep(1000);
// queue used to save attributes to database
Thread.sleep(500);
UplinkMsg.Builder uplinkMsgBuilder = UplinkMsg.newBuilder();
AttributesRequestMsg.Builder attributesRequestMsgBuilder = AttributesRequestMsg.newBuilder();
@ -1497,72 +1604,42 @@ abstract public class BaseEdgeTest extends AbstractControllerTest {
Assert.assertEquals(expectedValue, keyValueProto.getStringV());
}
private void sendDeleteDeviceOnEdge() throws Exception {
Device device = findDeviceByName("Edge Device 2");
UplinkMsg.Builder upLinkMsgBuilder = UplinkMsg.newBuilder();
DeviceUpdateMsg.Builder deviceDeleteMsgBuilder = DeviceUpdateMsg.newBuilder();
deviceDeleteMsgBuilder.setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE);
deviceDeleteMsgBuilder.setIdMSB(device.getId().getId().getMostSignificantBits());
deviceDeleteMsgBuilder.setIdLSB(device.getId().getId().getLeastSignificantBits());
testAutoGeneratedCodeByProtobuf(deviceDeleteMsgBuilder);
// Utility methods
upLinkMsgBuilder.addDeviceUpdateMsg(deviceDeleteMsgBuilder.build());
testAutoGeneratedCodeByProtobuf(upLinkMsgBuilder);
edgeImitator.expectResponsesAmount(1);
edgeImitator.sendUplinkMsg(upLinkMsgBuilder.build());
Assert.assertTrue(edgeImitator.waitForResponses());
device = doGet("/api/device/" + device.getId().getId().toString(), Device.class);
Assert.assertNotNull(device);
private Device findDeviceByName(String deviceName) throws Exception {
List<Device> edgeDevices = doGetTypedWithPageLink("/api/edge/" + edge.getId().getId().toString() + "/devices?",
new TypeReference<PageData<Device>>() {
}, new PageLink(100)).getData();
Assert.assertFalse(edgeDevices.contains(device));
Optional<Device> foundDevice = edgeDevices.stream().filter(d -> d.getName().equals(deviceName)).findAny();
Assert.assertTrue(foundDevice.isPresent());
Device device = foundDevice.get();
Assert.assertEquals(deviceName, device.getName());
return device;
}
private void installation() throws Exception {
edge = doPost("/api/edge", constructEdge("Test Edge", "test"), Edge.class);
DeviceProfile deviceProfile = this.createDeviceProfile(CUSTOM_DEVICE_PROFILE_NAME, null);
extendDeviceProfileData(deviceProfile);
doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class);
private Asset findAssetByName(String assetName) throws Exception {
List<Asset> edgeAssets = doGetTypedWithPageLink("/api/edge/" + edge.getId().getId().toString() + "/assets?",
new TypeReference<PageData<Asset>>() {
}, new PageLink(100)).getData();
Device savedDevice = saveDevice("Edge Device 1", CUSTOM_DEVICE_PROFILE_NAME);
doPost("/api/edge/" + edge.getId().getId().toString()
+ "/device/" + savedDevice.getId().getId().toString(), Device.class);
Assert.assertEquals(1, edgeAssets.size());
Asset asset = edgeAssets.get(0);
Assert.assertEquals(assetName, asset.getName());
return asset;
}
Asset savedAsset = saveAsset("Edge Asset 1");
doPost("/api/edge/" + edge.getId().getId().toString()
+ "/asset/" + savedAsset.getId().getId().toString(), Asset.class);
private Device saveDevice(String deviceName, String type) throws Exception {
Device device = new Device();
device.setName(deviceName);
device.setType(type);
return doPost("/api/device", device, Device.class);
}
private void extendDeviceProfileData(DeviceProfile deviceProfile) {
DeviceProfileData profileData = deviceProfile.getProfileData();
List<DeviceProfileAlarm> alarms = new ArrayList<>();
DeviceProfileAlarm deviceProfileAlarm = new DeviceProfileAlarm();
deviceProfileAlarm.setAlarmType("High Temperature");
AlarmRule alarmRule = new AlarmRule();
alarmRule.setAlarmDetails("Alarm Details");
AlarmCondition alarmCondition = new AlarmCondition();
alarmCondition.setSpec(new SimpleAlarmConditionSpec());
List<AlarmConditionFilter> condition = new ArrayList<>();
AlarmConditionFilter alarmConditionFilter = new AlarmConditionFilter();
alarmConditionFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.ATTRIBUTE, "temperature"));
NumericFilterPredicate predicate = new NumericFilterPredicate();
predicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER);
predicate.setValue(new FilterPredicateValue<>(55.0));
alarmConditionFilter.setPredicate(predicate);
alarmConditionFilter.setValueType(EntityKeyValueType.NUMERIC);
condition.add(alarmConditionFilter);
alarmCondition.setCondition(condition);
alarmRule.setCondition(alarmCondition);
deviceProfileAlarm.setClearRule(alarmRule);
TreeMap<AlarmSeverity, AlarmRule> createRules = new TreeMap<>();
createRules.put(AlarmSeverity.CRITICAL, alarmRule);
deviceProfileAlarm.setCreateRules(createRules);
alarms.add(deviceProfileAlarm);
profileData.setAlarms(alarms);
profileData.setProvisionConfiguration(new AllowCreateNewDevicesDeviceProfileProvisionConfiguration("123"));
private Asset saveAsset(String assetName) throws Exception {
Asset asset = new Asset();
asset.setName(assetName);
asset.setType("test");
return doPost("/api/asset", asset, Asset.class);
}
private EdgeEvent constructEdgeEvent(TenantId tenantId, EdgeId edgeId, EdgeEventActionType edgeEventAction, UUID entityId, EdgeEventType edgeEventType, JsonNode entityBody) {

156
application/src/test/java/org/thingsboard/server/edge/imitator/EdgeImitator.java

@ -21,41 +21,41 @@ import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.protobuf.AbstractMessage;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.thingsboard.edge.rpc.EdgeGrpcClient;
import org.thingsboard.edge.rpc.EdgeRpcClient;
import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.gen.edge.AlarmUpdateMsg;
import org.thingsboard.server.gen.edge.AssetUpdateMsg;
import org.thingsboard.server.gen.edge.CustomerUpdateMsg;
import org.thingsboard.server.gen.edge.DashboardUpdateMsg;
import org.thingsboard.server.gen.edge.DeviceCredentialsRequestMsg;
import org.thingsboard.server.gen.edge.DeviceCredentialsUpdateMsg;
import org.thingsboard.server.gen.edge.DeviceProfileUpdateMsg;
import org.thingsboard.server.gen.edge.DeviceRpcCallMsg;
import org.thingsboard.server.gen.edge.DeviceUpdateMsg;
import org.thingsboard.server.gen.edge.DownlinkMsg;
import org.thingsboard.server.gen.edge.DownlinkResponseMsg;
import org.thingsboard.server.gen.edge.EdgeConfiguration;
import org.thingsboard.server.gen.edge.EntityDataProto;
import org.thingsboard.server.gen.edge.EntityViewUpdateMsg;
import org.thingsboard.server.gen.edge.RelationUpdateMsg;
import org.thingsboard.server.gen.edge.RuleChainMetadataUpdateMsg;
import org.thingsboard.server.gen.edge.RuleChainUpdateMsg;
import org.thingsboard.server.gen.edge.UplinkMsg;
import org.thingsboard.server.gen.edge.UplinkResponseMsg;
import org.thingsboard.server.gen.edge.UserCredentialsUpdateMsg;
import org.thingsboard.server.gen.edge.UserUpdateMsg;
import org.thingsboard.server.gen.edge.WidgetTypeUpdateMsg;
import org.thingsboard.server.gen.edge.WidgetsBundleUpdateMsg;
import org.thingsboard.server.gen.edge.v1.AlarmUpdateMsg;
import org.thingsboard.server.gen.edge.v1.AssetUpdateMsg;
import org.thingsboard.server.gen.edge.v1.CustomerUpdateMsg;
import org.thingsboard.server.gen.edge.v1.DashboardUpdateMsg;
import org.thingsboard.server.gen.edge.v1.DeviceCredentialsRequestMsg;
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.DeviceUpdateMsg;
import org.thingsboard.server.gen.edge.v1.DownlinkMsg;
import org.thingsboard.server.gen.edge.v1.DownlinkResponseMsg;
import org.thingsboard.server.gen.edge.v1.EdgeConfiguration;
import org.thingsboard.server.gen.edge.v1.EntityDataProto;
import org.thingsboard.server.gen.edge.v1.EntityViewUpdateMsg;
import org.thingsboard.server.gen.edge.v1.RelationUpdateMsg;
import org.thingsboard.server.gen.edge.v1.RuleChainMetadataUpdateMsg;
import org.thingsboard.server.gen.edge.v1.RuleChainUpdateMsg;
import org.thingsboard.server.gen.edge.v1.UplinkMsg;
import org.thingsboard.server.gen.edge.v1.UplinkResponseMsg;
import org.thingsboard.server.gen.edge.v1.UserCredentialsUpdateMsg;
import org.thingsboard.server.gen.edge.v1.UserUpdateMsg;
import org.thingsboard.server.gen.edge.v1.WidgetTypeUpdateMsg;
import org.thingsboard.server.gen.edge.v1.WidgetsBundleUpdateMsg;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
@ -75,12 +75,17 @@ public class EdgeImitator {
private CountDownLatch responsesLatch;
private List<Class<? extends AbstractMessage>> ignoredTypes;
@Setter
private boolean randomFailuresOnTimeseriesDownlink = false;
@Setter
private double failureProbability = 0.0;
@Getter
private EdgeConfiguration configuration;
@Getter
private UserId userId;
@Getter
private final List<AbstractMessage> downlinkMsgs;
private List<AbstractMessage> downlinkMsgs;
private boolean connected = false;
public EdgeImitator(String host, int port, String routingKey, String routingSecret) throws NoSuchFieldException, IllegalAccessException {
edgeRpcClient = new EdgeGrpcClient();
@ -103,16 +108,18 @@ public class EdgeImitator {
}
public void connect() {
connected = true;
edgeRpcClient.connect(routingKey, routingSecret,
this::onUplinkResponse,
this::onEdgeUpdate,
this::onDownlink,
this::onClose);
edgeRpcClient.sendSyncRequestMsg();
edgeRpcClient.sendSyncRequestMsg(true);
}
public void disconnect() throws InterruptedException {
connected = false;
edgeRpcClient.disconnect(false);
}
@ -129,23 +136,27 @@ public class EdgeImitator {
this.configuration = edgeConfiguration;
}
private void onUserUpdate(UserUpdateMsg userUpdateMsg) {
this.userId = new UserId(new UUID(userUpdateMsg.getIdMSB(), userUpdateMsg.getIdLSB()));
}
private void onDownlink(DownlinkMsg downlinkMsg) {
ListenableFuture<List<Void>> future = processDownlinkMsg(downlinkMsg);
Futures.addCallback(future, new FutureCallback<List<Void>>() {
Futures.addCallback(future, new FutureCallback<>() {
@Override
public void onSuccess(@Nullable List<Void> result) {
DownlinkResponseMsg downlinkResponseMsg = DownlinkResponseMsg.newBuilder().setSuccess(true).build();
edgeRpcClient.sendDownlinkResponseMsg(downlinkResponseMsg);
if (connected) {
DownlinkResponseMsg downlinkResponseMsg = DownlinkResponseMsg.newBuilder()
.setDownlinkMsgId(downlinkMsg.getDownlinkMsgId())
.setSuccess(true).build();
edgeRpcClient.sendDownlinkResponseMsg(downlinkResponseMsg);
}
}
@Override
public void onFailure(Throwable t) {
DownlinkResponseMsg downlinkResponseMsg = DownlinkResponseMsg.newBuilder().setSuccess(false).setErrorMsg(t.getMessage()).build();
edgeRpcClient.sendDownlinkResponseMsg(downlinkResponseMsg);
if (connected) {
DownlinkResponseMsg downlinkResponseMsg = DownlinkResponseMsg.newBuilder()
.setDownlinkMsgId(downlinkMsg.getDownlinkMsgId())
.setSuccess(false).setErrorMsg(t.getMessage()).build();
edgeRpcClient.sendDownlinkResponseMsg(downlinkResponseMsg);
}
}
}, MoreExecutors.directExecutor());
}
@ -157,7 +168,7 @@ public class EdgeImitator {
private ListenableFuture<List<Void>> processDownlinkMsg(DownlinkMsg downlinkMsg) {
List<ListenableFuture<Void>> result = new ArrayList<>();
if (downlinkMsg.getDeviceUpdateMsgCount() > 0) {
for (DeviceUpdateMsg deviceUpdateMsg: downlinkMsg.getDeviceUpdateMsgList()) {
for (DeviceUpdateMsg deviceUpdateMsg : downlinkMsg.getDeviceUpdateMsgList()) {
result.add(saveDownlinkMsg(deviceUpdateMsg));
}
}
@ -167,93 +178,105 @@ public class EdgeImitator {
}
}
if (downlinkMsg.getDeviceCredentialsUpdateMsgCount() > 0) {
for (DeviceCredentialsUpdateMsg deviceCredentialsUpdateMsg: downlinkMsg.getDeviceCredentialsUpdateMsgList()) {
for (DeviceCredentialsUpdateMsg deviceCredentialsUpdateMsg : downlinkMsg.getDeviceCredentialsUpdateMsgList()) {
result.add(saveDownlinkMsg(deviceCredentialsUpdateMsg));
}
}
if (downlinkMsg.getAssetUpdateMsgCount() > 0) {
for (AssetUpdateMsg assetUpdateMsg: downlinkMsg.getAssetUpdateMsgList()) {
for (AssetUpdateMsg assetUpdateMsg : downlinkMsg.getAssetUpdateMsgList()) {
result.add(saveDownlinkMsg(assetUpdateMsg));
}
}
if (downlinkMsg.getRuleChainUpdateMsgCount() > 0) {
for (RuleChainUpdateMsg ruleChainUpdateMsg: downlinkMsg.getRuleChainUpdateMsgList()) {
for (RuleChainUpdateMsg ruleChainUpdateMsg : downlinkMsg.getRuleChainUpdateMsgList()) {
result.add(saveDownlinkMsg(ruleChainUpdateMsg));
}
}
if (downlinkMsg.getRuleChainMetadataUpdateMsgCount() > 0) {
for (RuleChainMetadataUpdateMsg ruleChainMetadataUpdateMsg: downlinkMsg.getRuleChainMetadataUpdateMsgList()) {
for (RuleChainMetadataUpdateMsg ruleChainMetadataUpdateMsg : downlinkMsg.getRuleChainMetadataUpdateMsgList()) {
result.add(saveDownlinkMsg(ruleChainMetadataUpdateMsg));
}
}
if (downlinkMsg.getDashboardUpdateMsgCount() > 0) {
for (DashboardUpdateMsg dashboardUpdateMsg: downlinkMsg.getDashboardUpdateMsgList()) {
for (DashboardUpdateMsg dashboardUpdateMsg : downlinkMsg.getDashboardUpdateMsgList()) {
result.add(saveDownlinkMsg(dashboardUpdateMsg));
}
}
if (downlinkMsg.getRelationUpdateMsgCount() > 0) {
for (RelationUpdateMsg relationUpdateMsg: downlinkMsg.getRelationUpdateMsgList()) {
for (RelationUpdateMsg relationUpdateMsg : downlinkMsg.getRelationUpdateMsgList()) {
result.add(saveDownlinkMsg(relationUpdateMsg));
}
}
if (downlinkMsg.getAlarmUpdateMsgCount() > 0) {
for (AlarmUpdateMsg alarmUpdateMsg: downlinkMsg.getAlarmUpdateMsgList()) {
for (AlarmUpdateMsg alarmUpdateMsg : downlinkMsg.getAlarmUpdateMsgList()) {
result.add(saveDownlinkMsg(alarmUpdateMsg));
}
}
if (downlinkMsg.getEntityDataCount() > 0) {
for (EntityDataProto entityData: downlinkMsg.getEntityDataList()) {
result.add(saveDownlinkMsg(entityData));
for (EntityDataProto entityData : downlinkMsg.getEntityDataList()) {
if (randomFailuresOnTimeseriesDownlink) {
if (getRandomBoolean()) {
result.add(Futures.immediateFailedFuture(new RuntimeException("Random failure. This is expected error for edge test")));
} else {
result.add(saveDownlinkMsg(entityData));
}
} else {
result.add(saveDownlinkMsg(entityData));
}
}
}
if (downlinkMsg.getEntityViewUpdateMsgCount() > 0) {
for (EntityViewUpdateMsg entityViewUpdateMsg: downlinkMsg.getEntityViewUpdateMsgList()) {
for (EntityViewUpdateMsg entityViewUpdateMsg : downlinkMsg.getEntityViewUpdateMsgList()) {
result.add(saveDownlinkMsg(entityViewUpdateMsg));
}
}
if (downlinkMsg.getCustomerUpdateMsgCount() > 0) {
for (CustomerUpdateMsg customerUpdateMsg: downlinkMsg.getCustomerUpdateMsgList()) {
for (CustomerUpdateMsg customerUpdateMsg : downlinkMsg.getCustomerUpdateMsgList()) {
result.add(saveDownlinkMsg(customerUpdateMsg));
}
}
if (downlinkMsg.getWidgetsBundleUpdateMsgCount() > 0) {
for (WidgetsBundleUpdateMsg widgetsBundleUpdateMsg: downlinkMsg.getWidgetsBundleUpdateMsgList()) {
for (WidgetsBundleUpdateMsg widgetsBundleUpdateMsg : downlinkMsg.getWidgetsBundleUpdateMsgList()) {
result.add(saveDownlinkMsg(widgetsBundleUpdateMsg));
}
}
if (downlinkMsg.getWidgetTypeUpdateMsgCount() > 0) {
for (WidgetTypeUpdateMsg widgetTypeUpdateMsg: downlinkMsg.getWidgetTypeUpdateMsgList()) {
for (WidgetTypeUpdateMsg widgetTypeUpdateMsg : downlinkMsg.getWidgetTypeUpdateMsgList()) {
result.add(saveDownlinkMsg(widgetTypeUpdateMsg));
}
}
if (downlinkMsg.getUserUpdateMsgCount() > 0) {
for (UserUpdateMsg userUpdateMsg: downlinkMsg.getUserUpdateMsgList()) {
onUserUpdate(userUpdateMsg);
for (UserUpdateMsg userUpdateMsg : downlinkMsg.getUserUpdateMsgList()) {
result.add(saveDownlinkMsg(userUpdateMsg));
}
}
if (downlinkMsg.getUserCredentialsUpdateMsgCount() > 0) {
for (UserCredentialsUpdateMsg userCredentialsUpdateMsg: downlinkMsg.getUserCredentialsUpdateMsgList()) {
for (UserCredentialsUpdateMsg userCredentialsUpdateMsg : downlinkMsg.getUserCredentialsUpdateMsgList()) {
result.add(saveDownlinkMsg(userCredentialsUpdateMsg));
}
}
if (downlinkMsg.getDeviceRpcCallMsgCount() > 0) {
for (DeviceRpcCallMsg deviceRpcCallMsg: downlinkMsg.getDeviceRpcCallMsgList()) {
for (DeviceRpcCallMsg deviceRpcCallMsg : downlinkMsg.getDeviceRpcCallMsgList()) {
result.add(saveDownlinkMsg(deviceRpcCallMsg));
}
}
if (downlinkMsg.getDeviceCredentialsRequestMsgCount() > 0) {
for (DeviceCredentialsRequestMsg deviceCredentialsRequestMsg: downlinkMsg.getDeviceCredentialsRequestMsgList()) {
for (DeviceCredentialsRequestMsg deviceCredentialsRequestMsg : downlinkMsg.getDeviceCredentialsRequestMsgList()) {
result.add(saveDownlinkMsg(deviceCredentialsRequestMsg));
}
}
return Futures.allAsList(result);
}
private boolean getRandomBoolean() {
double randomValue = ThreadLocalRandom.current().nextDouble() * 100;
return randomValue <= this.failureProbability;
}
private ListenableFuture<Void> saveDownlinkMsg(AbstractMessage message) {
if (!ignoredTypes.contains(message.getClass())) {
lock.lock();
try {
lock.lock();
downlinkMsgs.add(message);
} finally {
lock.unlock();
@ -267,11 +290,14 @@ public class EdgeImitator {
return waitForMessages(5);
}
public boolean waitForMessages(int timeout) throws InterruptedException {
return messagesLatch.await(timeout, TimeUnit.SECONDS);
public boolean waitForMessages(int timeoutInSeconds) throws InterruptedException {
return messagesLatch.await(timeoutInSeconds, TimeUnit.SECONDS);
}
public void expectMessageAmount(int messageAmount) {
// clear downlinks
downlinkMsgs.clear();
messagesLatch = new CountDownLatch(messageAmount);
}
@ -286,8 +312,8 @@ public class EdgeImitator {
@SuppressWarnings("unchecked")
public <T extends AbstractMessage> Optional<T> findMessageByType(Class<T> tClass) {
Optional<T> result;
lock.lock();
try {
lock.lock();
result = (Optional<T>) downlinkMsgs.stream().filter(downlinkMsg -> downlinkMsg.getClass().isAssignableFrom(tClass)).findAny();
} finally {
lock.unlock();
@ -298,8 +324,8 @@ public class EdgeImitator {
@SuppressWarnings("unchecked")
public <T extends AbstractMessage> List<T> findAllMessagesByType(Class<T> tClass) {
List<T> result;
lock.lock();
try {
lock.lock();
result = (List<T>) downlinkMsgs.stream().filter(downlinkMsg -> downlinkMsg.getClass().isAssignableFrom(tClass)).collect(Collectors.toList());
} finally {
lock.unlock();
@ -308,7 +334,11 @@ public class EdgeImitator {
}
public AbstractMessage getLatestMessage() {
return downlinkMsgs.get(downlinkMsgs.size() - 1);
return getMessageFromTail(1);
}
public AbstractMessage getMessageFromTail(int offset) {
return downlinkMsgs.get(downlinkMsgs.size() - offset);
}
public void ignoreType(Class<? extends AbstractMessage> type) {

2
application/src/test/resources/application-test.properties

@ -1,6 +1,8 @@
transport.lwm2m.security.key_store=lwm2m/credentials/serverKeyStore.jks
transport.lwm2m.security.key_store_password=server
edges.enabled=true
edges.storage.no_read_records_sleep=500
edges.storage.sleep_between_batches=500
transport.lwm2m.server.security.key_alias=server
transport.lwm2m.server.security.key_password=server
transport.lwm2m.bootstrap.security.key_alias=server

2
common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceService.java

@ -46,6 +46,8 @@ public interface DeviceService {
Device findDeviceByTenantIdAndName(TenantId tenantId, String name);
Device saveDevice(Device device, boolean doValidate);
Device saveDevice(Device device);
Device saveDeviceWithAccessToken(Device device, String accessToken);

1
common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeEventService.java

@ -28,4 +28,5 @@ public interface EdgeEventService {
PageData<EdgeEvent> findEdgeEvents(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink, boolean withTsUpdate);
void cleanupEvents(long ttl);
}

10
common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeService.java

@ -44,7 +44,7 @@ public interface EdgeService {
Optional<Edge> findEdgeByRoutingKey(TenantId tenantId, String routingKey);
Edge saveEdge(Edge edge);
Edge saveEdge(Edge edge, boolean doValidate);
Edge assignEdgeToCustomer(TenantId tenantId, EdgeId edgeId, CustomerId customerId);
@ -82,17 +82,13 @@ public interface EdgeService {
void assignDefaultRuleChainsToEdge(TenantId tenantId, EdgeId edgeId);
ListenableFuture<List<Edge>> findEdgesByTenantIdAndRuleChainId(TenantId tenantId, RuleChainId ruleChainId);
PageData<Edge> findEdgesByTenantIdAndEntityId(TenantId tenantId, EntityId ruleChainId, PageLink pageLink);
ListenableFuture<List<Edge>> findEdgesByTenantIdAndDashboardId(TenantId tenantId, DashboardId dashboardId);
ListenableFuture<List<EdgeId>> findRelatedEdgeIdsByEntityId(TenantId tenantId, EntityId entityId);
PageData<EdgeId> findRelatedEdgeIdsByEntityId(TenantId tenantId, EntityId entityId, PageLink pageLink);
Object checkInstance(Object request);
Object activateInstance(String licenseSecret, String releaseDate);
String findMissingToRelatedRuleChains(TenantId tenantId, EdgeId edgeId);
void cleanupEvents(long ttl);
}

2
common/dao-api/src/main/java/org/thingsboard/server/dao/rule/RuleChainService.java

@ -87,6 +87,6 @@ public interface RuleChainService {
boolean unsetAutoAssignToEdgeRuleChain(TenantId tenantId, RuleChainId ruleChainId);
ListenableFuture<List<RuleChain>> findAutoAssignToEdgeRuleChainsByTenantId(TenantId tenantId);
PageData<RuleChain> findAutoAssignToEdgeRuleChainsByTenantId(TenantId tenantId, PageLink pageLink);
}

6
common/data/src/main/java/org/thingsboard/server/common/data/EdgeUtils.java

@ -18,6 +18,8 @@ package org.thingsboard.server.common.data;
import lombok.extern.slf4j.Slf4j;
import org.thingsboard.server.common.data.edge.EdgeEventType;
import java.util.concurrent.ThreadLocalRandom;
@Slf4j
public final class EdgeUtils {
@ -57,4 +59,8 @@ public final class EdgeUtils {
return null;
}
}
public static int nextPositiveInt() {
return ThreadLocalRandom.current().nextInt(0, Integer.MAX_VALUE);
}
}

73
common/data/src/main/java/org/thingsboard/server/common/data/page/BasePageDataIterable.java

@ -0,0 +1,73 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.page;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
public abstract class BasePageDataIterable<T> implements Iterable<T>, Iterator<T> {
private final int fetchSize;
private List<T> currentItems;
private int currentIdx;
private boolean hasNextPack;
private PageLink nextPackLink;
private boolean initialized;
public BasePageDataIterable(int fetchSize) {
super();
this.fetchSize = fetchSize;
}
@Override
public Iterator<T> iterator() {
return this;
}
@Override
public boolean hasNext() {
if (!initialized) {
fetch(new PageLink(fetchSize));
initialized = true;
}
if (currentIdx == currentItems.size()) {
if (hasNextPack) {
fetch(nextPackLink);
}
}
return currentIdx < currentItems.size();
}
@Override
public T next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
return currentItems.get(currentIdx++);
}
private void fetch(PageLink link) {
PageData<T> pageData = fetchPageData(link);
currentIdx = 0;
currentItems = pageData.getData();
hasNextPack = pageData.hasNext();
nextPackLink = link.nextPageLink();
}
abstract PageData<T> fetchPageData(PageLink link);
}

59
common/data/src/main/java/org/thingsboard/server/common/data/page/PageDataIterable.java

@ -15,70 +15,21 @@
*/
package org.thingsboard.server.common.data.page;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import org.thingsboard.server.common.data.BaseData;
import org.thingsboard.server.common.data.SearchTextBased;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.UUIDBased;
public class PageDataIterable<T> implements Iterable<T>, Iterator<T> {
public class PageDataIterable<T> extends BasePageDataIterable<T> {
private final FetchFunction<T> function;
private final int fetchSize;
private List<T> currentItems;
private int currentIdx;
private boolean hasNextPack;
private PageLink nextPackLink;
private boolean initialized;
public PageDataIterable(FetchFunction<T> function, int fetchSize) {
super();
super(fetchSize);
this.function = function;
this.fetchSize = fetchSize;
}
@Override
public Iterator<T> iterator() {
return this;
PageData<T> fetchPageData(PageLink link) {
return function.fetch(link);
}
@Override
public boolean hasNext() {
if(!initialized){
fetch(new PageLink(fetchSize));
initialized = true;
}
if(currentIdx == currentItems.size()){
if(hasNextPack){
fetch(nextPackLink);
}
}
return currentIdx < currentItems.size();
}
private void fetch(PageLink link) {
PageData<T> pageData = function.fetch(link);
currentIdx = 0;
currentItems = pageData.getData();
hasNextPack = pageData.hasNext();
nextPackLink = link.nextPageLink();
}
@Override
public T next() {
if(!hasNext()){
throw new NoSuchElementException();
}
return currentItems.get(currentIdx++);
}
public static interface FetchFunction<T> {
public interface FetchFunction<T> {
PageData<T> fetch(PageLink link);
}
}

39
common/data/src/main/java/org/thingsboard/server/common/data/page/PageDataIterableByTenant.java

@ -0,0 +1,39 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.page;
import org.thingsboard.server.common.data.id.TenantId;
public class PageDataIterableByTenant<T> extends BasePageDataIterable<T> {
private final FetchFunction<T> function;
private final TenantId tenantId;
public PageDataIterableByTenant(FetchFunction<T> function, TenantId tenantId, int fetchSize) {
super(fetchSize);
this.function = function;
this.tenantId = tenantId;
}
@Override
PageData<T> fetchPageData(PageLink link) {
return function.fetch(tenantId, link);
}
public interface FetchFunction<T> {
PageData<T> fetch(TenantId tenantId, PageLink link);
}
}

43
common/data/src/main/java/org/thingsboard/server/common/data/page/PageDataIterableByTenantIdEntityId.java

@ -0,0 +1,43 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.page;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
public class PageDataIterableByTenantIdEntityId<T> extends BasePageDataIterable<T> {
private final FetchFunction<T> function;
private final TenantId tenantId;
private final EntityId entityId;
public PageDataIterableByTenantIdEntityId(FetchFunction<T> function, TenantId tenantId, EntityId entityId, int fetchSize) {
super(fetchSize);
this.function = function;
this.tenantId = tenantId;
this.entityId = entityId;
}
@Override
PageData<T> fetchPageData(PageLink link) {
return function.fetch(tenantId, entityId, link);
}
public interface FetchFunction<T> {
PageData<T> fetch(TenantId tenantId, EntityId entityId, PageLink link);
}
}

37
common/edge-api/src/main/java/org/thingsboard/edge/rpc/EdgeGrpcClient.java

@ -25,18 +25,19 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.thingsboard.edge.exception.EdgeConnectionException;
import org.thingsboard.server.common.data.ResourceUtils;
import org.thingsboard.server.gen.edge.ConnectRequestMsg;
import org.thingsboard.server.gen.edge.ConnectResponseCode;
import org.thingsboard.server.gen.edge.ConnectResponseMsg;
import org.thingsboard.server.gen.edge.DownlinkMsg;
import org.thingsboard.server.gen.edge.DownlinkResponseMsg;
import org.thingsboard.server.gen.edge.EdgeConfiguration;
import org.thingsboard.server.gen.edge.EdgeRpcServiceGrpc;
import org.thingsboard.server.gen.edge.RequestMsg;
import org.thingsboard.server.gen.edge.RequestMsgType;
import org.thingsboard.server.gen.edge.ResponseMsg;
import org.thingsboard.server.gen.edge.UplinkMsg;
import org.thingsboard.server.gen.edge.UplinkResponseMsg;
import org.thingsboard.server.gen.edge.v1.ConnectRequestMsg;
import org.thingsboard.server.gen.edge.v1.ConnectResponseCode;
import org.thingsboard.server.gen.edge.v1.ConnectResponseMsg;
import org.thingsboard.server.gen.edge.v1.DownlinkMsg;
import org.thingsboard.server.gen.edge.v1.DownlinkResponseMsg;
import org.thingsboard.server.gen.edge.v1.EdgeConfiguration;
import org.thingsboard.server.gen.edge.v1.EdgeRpcServiceGrpc;
import org.thingsboard.server.gen.edge.v1.RequestMsg;
import org.thingsboard.server.gen.edge.v1.RequestMsgType;
import org.thingsboard.server.gen.edge.v1.ResponseMsg;
import org.thingsboard.server.gen.edge.v1.SyncRequestMsg;
import org.thingsboard.server.gen.edge.v1.UplinkMsg;
import org.thingsboard.server.gen.edge.v1.UplinkResponseMsg;
import javax.net.ssl.SSLException;
import java.io.File;
@ -103,7 +104,7 @@ public class EdgeGrpcClient implements EdgeRpcClient {
Consumer<EdgeConfiguration> onEdgeUpdate,
Consumer<DownlinkMsg> onDownlink,
Consumer<Exception> onError) {
return new StreamObserver<ResponseMsg>() {
return new StreamObserver<>() {
@Override
public void onNext(ResponseMsg responseMsg) {
if (responseMsg.hasConnectResponseMsg()) {
@ -184,8 +185,8 @@ public class EdgeGrpcClient implements EdgeRpcClient {
@Override
public void sendUplinkMsg(UplinkMsg msg) {
uplinkMsgLock.lock();
try {
uplinkMsgLock.lock();
this.inputStream.onNext(RequestMsg.newBuilder()
.setMsgType(RequestMsgType.UPLINK_RPC_MESSAGE)
.setUplinkMsg(msg)
@ -196,11 +197,13 @@ public class EdgeGrpcClient implements EdgeRpcClient {
}
@Override
public void sendSyncRequestMsg() {
public void sendSyncRequestMsg(boolean syncRequired) {
uplinkMsgLock.lock();
try {
uplinkMsgLock.lock();
SyncRequestMsg syncRequestMsg = SyncRequestMsg.newBuilder().setSyncRequired(syncRequired).build();
this.inputStream.onNext(RequestMsg.newBuilder()
.setMsgType(RequestMsgType.SYNC_REQUEST_RPC_MESSAGE)
.setSyncRequestMsg(syncRequestMsg)
.build());
} finally {
uplinkMsgLock.unlock();
@ -209,8 +212,8 @@ public class EdgeGrpcClient implements EdgeRpcClient {
@Override
public void sendDownlinkResponseMsg(DownlinkResponseMsg downlinkResponseMsg) {
uplinkMsgLock.lock();
try {
uplinkMsgLock.lock();
this.inputStream.onNext(RequestMsg.newBuilder()
.setMsgType(RequestMsgType.UPLINK_RPC_MESSAGE)
.setDownlinkResponseMsg(downlinkResponseMsg)

12
common/edge-api/src/main/java/org/thingsboard/edge/rpc/EdgeRpcClient.java

@ -15,11 +15,11 @@
*/
package org.thingsboard.edge.rpc;
import org.thingsboard.server.gen.edge.DownlinkMsg;
import org.thingsboard.server.gen.edge.DownlinkResponseMsg;
import org.thingsboard.server.gen.edge.EdgeConfiguration;
import org.thingsboard.server.gen.edge.UplinkMsg;
import org.thingsboard.server.gen.edge.UplinkResponseMsg;
import org.thingsboard.server.gen.edge.v1.DownlinkMsg;
import org.thingsboard.server.gen.edge.v1.DownlinkResponseMsg;
import org.thingsboard.server.gen.edge.v1.EdgeConfiguration;
import org.thingsboard.server.gen.edge.v1.UplinkMsg;
import org.thingsboard.server.gen.edge.v1.UplinkResponseMsg;
import java.util.function.Consumer;
@ -34,7 +34,7 @@ public interface EdgeRpcClient {
void disconnect(boolean onError) throws InterruptedException;
void sendSyncRequestMsg();
void sendSyncRequestMsg(boolean syncRequired);
void sendUplinkMsg(UplinkMsg uplinkMsg);

70
common/edge-api/src/main/proto/edge.proto

@ -15,7 +15,7 @@
*/
syntax = "proto3";
option java_package = "org.thingsboard.server.gen.edge";
option java_package = "org.thingsboard.server.gen.edge.v1";
option java_multiple_files = true;
option java_outer_classname = "EdgeProtos";
@ -38,6 +38,7 @@ message RequestMsg {
ConnectRequestMsg connectRequestMsg = 2;
UplinkMsg uplinkMsg = 3;
DownlinkResponseMsg downlinkResponseMsg = 4;
SyncRequestMsg syncRequestMsg = 5;
}
message ResponseMsg {
@ -74,6 +75,13 @@ message ConnectResponseMsg {
EdgeConfiguration configuration = 3;
}
message SyncRequestMsg {
bool syncRequired = 1;
}
message SyncCompletedMsg {
}
message EdgeConfiguration {
int64 edgeIdMSB = 1;
int64 edgeIdLSB = 2;
@ -358,6 +366,22 @@ message DeviceCredentialsRequestMsg {
int64 deviceIdLSB = 2;
}
message DeviceProfileDevicesRequestMsg {
int64 deviceProfileIdMSB = 1;
int64 deviceProfileIdLSB = 2;
}
message WidgetBundleTypesRequestMsg {
int64 widgetBundleIdMSB = 1;
int64 widgetBundleIdLSB = 2;
}
message EntityViewsRequestMsg {
int64 entityIdMSB = 1;
int64 entityIdLSB = 2;
string entityType = 3;
}
message DeviceRpcCallMsg {
int64 deviceIdMSB = 1;
int64 deviceIdLSB = 2;
@ -402,38 +426,44 @@ message UplinkMsg {
repeated UserCredentialsRequestMsg userCredentialsRequestMsg = 10;
repeated DeviceCredentialsRequestMsg deviceCredentialsRequestMsg = 11;
repeated DeviceRpcCallMsg deviceRpcCallMsg = 12;
repeated DeviceProfileDevicesRequestMsg deviceProfileDevicesRequestMsg = 13;
repeated WidgetBundleTypesRequestMsg widgetBundleTypesRequestMsg = 14;
repeated EntityViewsRequestMsg entityViewsRequestMsg = 15;
}
message UplinkResponseMsg {
bool success = 1;
string errorMsg = 2;
int32 uplinkMsgId = 3;
}
message DownlinkResponseMsg {
bool success = 1;
string errorMsg = 2;
int32 downlinkMsgId = 3;
}
message DownlinkMsg {
int32 downlinkMsgId = 1;
repeated EntityDataProto entityData = 2;
repeated DeviceCredentialsRequestMsg deviceCredentialsRequestMsg = 3;
repeated DeviceUpdateMsg deviceUpdateMsg = 4;
repeated DeviceProfileUpdateMsg deviceProfileUpdateMsg = 5;
repeated DeviceCredentialsUpdateMsg deviceCredentialsUpdateMsg = 6;
repeated RuleChainUpdateMsg ruleChainUpdateMsg = 7;
repeated RuleChainMetadataUpdateMsg ruleChainMetadataUpdateMsg = 8;
repeated DashboardUpdateMsg dashboardUpdateMsg = 9;
repeated AssetUpdateMsg assetUpdateMsg = 10;
repeated EntityViewUpdateMsg entityViewUpdateMsg = 11;
repeated AlarmUpdateMsg alarmUpdateMsg = 12;
repeated UserUpdateMsg userUpdateMsg = 13;
repeated UserCredentialsUpdateMsg userCredentialsUpdateMsg = 14;
repeated CustomerUpdateMsg customerUpdateMsg = 15;
repeated RelationUpdateMsg relationUpdateMsg = 16;
repeated WidgetsBundleUpdateMsg widgetsBundleUpdateMsg = 17;
repeated WidgetTypeUpdateMsg widgetTypeUpdateMsg = 18;
repeated AdminSettingsUpdateMsg adminSettingsUpdateMsg = 19;
repeated DeviceRpcCallMsg deviceRpcCallMsg = 20;
SyncCompletedMsg syncCompletedMsg = 2;
repeated EntityDataProto entityData = 3;
repeated DeviceCredentialsRequestMsg deviceCredentialsRequestMsg = 4;
repeated DeviceUpdateMsg deviceUpdateMsg = 5;
repeated DeviceProfileUpdateMsg deviceProfileUpdateMsg = 6;
repeated DeviceCredentialsUpdateMsg deviceCredentialsUpdateMsg = 7;
repeated RuleChainUpdateMsg ruleChainUpdateMsg = 8;
repeated RuleChainMetadataUpdateMsg ruleChainMetadataUpdateMsg = 9;
repeated DashboardUpdateMsg dashboardUpdateMsg = 10;
repeated AssetUpdateMsg assetUpdateMsg = 11;
repeated EntityViewUpdateMsg entityViewUpdateMsg = 12;
repeated AlarmUpdateMsg alarmUpdateMsg = 13;
repeated UserUpdateMsg userUpdateMsg = 14;
repeated UserCredentialsUpdateMsg userCredentialsUpdateMsg = 15;
repeated CustomerUpdateMsg customerUpdateMsg = 16;
repeated RelationUpdateMsg relationUpdateMsg = 17;
repeated WidgetsBundleUpdateMsg widgetsBundleUpdateMsg = 18;
repeated WidgetTypeUpdateMsg widgetTypeUpdateMsg = 19;
repeated AdminSettingsUpdateMsg adminSettingsUpdateMsg = 20;
repeated DeviceRpcCallMsg deviceRpcCallMsg = 21;
}

2
dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java

@ -248,8 +248,8 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D
log.trace("Executing findOrCreateDefaultDeviceProfile");
DeviceProfile deviceProfile = findDeviceProfileByName(tenantId, name);
if (deviceProfile == null) {
findOrCreateLock.lock();
try {
findOrCreateLock.lock();
deviceProfile = findDeviceProfileByName(tenantId, name);
if (deviceProfile == null) {
deviceProfile = this.doCreateDefaultDeviceProfile(tenantId, name, name.equals("default"));

24
dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java

@ -186,7 +186,13 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
})
@Override
public Device saveDeviceWithAccessToken(Device device, String accessToken) {
return doSaveDevice(device, accessToken);
return doSaveDevice(device, accessToken, true);
}
@CacheEvict(cacheNames = DEVICE_CACHE, key = "{#device.tenantId, #device.name}")
@Override
public Device saveDevice(Device device, boolean doValidate) {
return doSaveDevice(device, null, doValidate);
}
@Caching(evict= {
@ -195,7 +201,7 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
})
@Override
public Device saveDevice(Device device) {
return doSaveDevice(device, null);
return doSaveDevice(device, null, true);
}
@CacheEvict(cacheNames = DEVICE_CACHE, key = "{#device.tenantId, #device.name}")
@ -205,7 +211,7 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
Device deviceWithName = this.findDeviceByTenantIdAndName(device.getTenantId(), device.getName());
device = deviceWithName == null ? device : deviceWithName.updateDevice(device);
}
Device savedDevice = this.saveDeviceWithoutCredentials(device);
Device savedDevice = this.saveDeviceWithoutCredentials(device, true);
deviceCredentials.setDeviceId(savedDevice.getId());
if (device.getId() == null) {
deviceCredentialsService.createDeviceCredentials(savedDevice.getTenantId(), deviceCredentials);
@ -220,8 +226,8 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
return savedDevice;
}
private Device doSaveDevice(Device device, String accessToken) {
Device savedDevice = this.saveDeviceWithoutCredentials(device);
private Device doSaveDevice(Device device, String accessToken, boolean doValidate) {
Device savedDevice = this.saveDeviceWithoutCredentials(device, doValidate);
if (device.getId() == null) {
DeviceCredentials deviceCredentials = new DeviceCredentials();
deviceCredentials.setDeviceId(new DeviceId(savedDevice.getUuidId()));
@ -232,9 +238,11 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
return savedDevice;
}
private Device saveDeviceWithoutCredentials(Device device) {
private Device saveDeviceWithoutCredentials(Device device, boolean doValidate) {
log.trace("Executing saveDevice [{}]", device);
deviceValidator.validate(device, Device::getTenantId);
if (doValidate) {
deviceValidator.validate(device, Device::getTenantId);
}
try {
DeviceProfile deviceProfile;
if (device.getDeviceProfileId() == null) {
@ -563,7 +571,7 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
device.setTenantId(tenantId);
device.setCustomerId(null);
return doSaveDevice(device, null);
return doSaveDevice(device, null, true);
}
@Override

5
dao/src/main/java/org/thingsboard/server/dao/edge/BaseEdgeEventService.java

@ -46,6 +46,11 @@ public class BaseEdgeEventService implements EdgeEventService {
return edgeEventDao.findEdgeEvents(tenantId.getId(), edgeId, pageLink, withTsUpdate);
}
@Override
public void cleanupEvents(long ttl) {
edgeEventDao.cleanupEvents(ttl);
}
private DataValidator<EdgeEvent> edgeEventValidator =
new DataValidator<EdgeEvent>() {
@Override

24
dao/src/main/java/org/thingsboard/server/dao/edge/EdgeDao.java

@ -17,6 +17,7 @@ package org.thingsboard.server.dao.edge;
import com.google.common.util.concurrent.ListenableFuture;
import org.thingsboard.server.common.data.EntitySubtype;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.edge.EdgeInfo;
import org.thingsboard.server.common.data.id.TenantId;
@ -160,26 +161,13 @@ public interface EdgeDao extends Dao<Edge> {
PageData<EdgeInfo> findEdgeInfosByTenantId(UUID tenantId, PageLink pageLink);
/**
* Find edges by tenantId and ruleChainId.
* Find edges by tenantId and entityId.
*
* @param tenantId the tenantId
* @param ruleChainId the ruleChainId
* @return the list of rule chain objects
*/
ListenableFuture<List<Edge>> findEdgesByTenantIdAndRuleChainId(UUID tenantId, UUID ruleChainId);
/**
* Find edges by tenantId and dashboardId.
*
* @param tenantId the tenantId
* @param dashboardId the dashboardId
* @return the list of rule chain objects
* @param entityId the entityId
* @param entityType the entityType
* @return the list of edge objects
*/
ListenableFuture<List<Edge>> findEdgesByTenantIdAndDashboardId(UUID tenantId, UUID dashboardId);
PageData<Edge> findEdgesByTenantIdAndEntityId(UUID tenantId, UUID entityId, EntityType entityType, PageLink pageLink);
/**
* Executes stored procedure to cleanup old edge events.
* @param ttl the ttl for edge events in seconds
*/
void cleanupEvents(long ttl);
}

6
dao/src/main/java/org/thingsboard/server/dao/edge/EdgeEventDao.java

@ -48,4 +48,10 @@ public interface EdgeEventDao extends Dao<EdgeEvent> {
*/
PageData<EdgeEvent> findEdgeEvents(UUID tenantId, EdgeId edgeId, TimePageLink pageLink, boolean withTsUpdate);
/**
* Executes stored procedure to cleanup old edge events.
* @param ttl the ttl for edge events in seconds
*/
void cleanupEvents(long ttl);
}

157
dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java

@ -19,7 +19,6 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.base.Function;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
@ -49,7 +48,6 @@ import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.edge.EdgeInfo;
import org.thingsboard.server.common.data.edge.EdgeSearchQuery;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DashboardId;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.IdBased;
@ -60,7 +58,6 @@ import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.relation.EntitySearchDirection;
import org.thingsboard.server.common.data.relation.RelationTypeGroup;
import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.data.rule.RuleChainConnectionInfo;
import org.thingsboard.server.dao.customer.CustomerDao;
@ -107,7 +104,7 @@ public class EdgeServiceImpl extends AbstractEntityService implements EdgeServic
private static final ObjectMapper mapper = new ObjectMapper();
private static final int DEFAULT_LIMIT = 100;
private static final int DEFAULT_PAGE_SIZE = 1000;
private RestTemplate restTemplate;
@ -183,9 +180,11 @@ public class EdgeServiceImpl extends AbstractEntityService implements EdgeServic
@CacheEvict(cacheNames = EDGE_CACHE, key = "{#edge.tenantId, #edge.name}")
@Override
public Edge saveEdge(Edge edge) {
public Edge saveEdge(Edge edge, boolean doValidate) {
log.trace("Executing saveEdge [{}]", edge);
edgeValidator.validate(edge, Edge::getTenantId);
if (doValidate) {
edgeValidator.validate(edge, Edge::getTenantId);
}
try {
return edgeDao.save(edge.getTenantId(), edge);
} catch (Exception t) {
@ -204,7 +203,7 @@ public class EdgeServiceImpl extends AbstractEntityService implements EdgeServic
log.trace("[{}] Executing assignEdgeToCustomer [{}][{}]", tenantId, edgeId, customerId);
Edge edge = findEdgeById(tenantId, edgeId);
edge.setCustomerId(customerId);
return saveEdge(edge);
return saveEdge(edge, true);
}
@Override
@ -212,7 +211,7 @@ public class EdgeServiceImpl extends AbstractEntityService implements EdgeServic
log.trace("[{}] Executing unassignEdgeFromCustomer [{}]", tenantId, edgeId);
Edge edge = findEdgeById(tenantId, edgeId);
edge.setCustomerId(null);
return saveEdge(edge);
return saveEdge(edge, true);
}
@Override
@ -381,38 +380,27 @@ public class EdgeServiceImpl extends AbstractEntityService implements EdgeServic
@Override
public void assignDefaultRuleChainsToEdge(TenantId tenantId, EdgeId edgeId) {
log.trace("Executing assignDefaultRuleChainsToEdge, tenantId [{}], edgeId [{}]", tenantId, edgeId);
ListenableFuture<List<RuleChain>> future = ruleChainService.findAutoAssignToEdgeRuleChainsByTenantId(tenantId);
Futures.addCallback(future, new FutureCallback<List<RuleChain>>() {
@Override
public void onSuccess(List<RuleChain> ruleChains) {
if (ruleChains != null && !ruleChains.isEmpty()) {
for (RuleChain ruleChain : ruleChains) {
ruleChainService.assignRuleChainToEdge(tenantId, ruleChain.getId(), edgeId);
}
PageLink pageLink = new PageLink(DEFAULT_PAGE_SIZE);
PageData<RuleChain> pageData;
do {
pageData = ruleChainService.findAutoAssignToEdgeRuleChainsByTenantId(tenantId, pageLink);
if (pageData.getData().size() > 0) {
for (RuleChain ruleChain : pageData.getData()) {
ruleChainService.assignRuleChainToEdge(tenantId, ruleChain.getId(), edgeId);
}
}
@Override
public void onFailure(Throwable t) {
log.warn("[{}] can't find default edge rule chains [{}]", tenantId.getId(), edgeId.getId(), t);
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
}
}, MoreExecutors.directExecutor());
}
@Override
public ListenableFuture<List<Edge>> findEdgesByTenantIdAndRuleChainId(TenantId tenantId, RuleChainId ruleChainId) {
log.trace("Executing findEdgesByTenantIdAndRuleChainId, tenantId [{}], ruleChainId [{}]", tenantId, ruleChainId);
Validator.validateId(tenantId, "Incorrect tenantId " + tenantId);
Validator.validateId(ruleChainId, "Incorrect ruleChainId " + ruleChainId);
return edgeDao.findEdgesByTenantIdAndRuleChainId(tenantId.getId(), ruleChainId.getId());
} while (pageData.hasNext());
}
@Override
public ListenableFuture<List<Edge>> findEdgesByTenantIdAndDashboardId(TenantId tenantId, DashboardId dashboardId) {
log.trace("Executing findEdgesByTenantIdAndDashboardId, tenantId [{}], dashboardId [{}]", tenantId, dashboardId);
public PageData<Edge> findEdgesByTenantIdAndEntityId(TenantId tenantId, EntityId entityId, PageLink pageLink) {
log.trace("Executing findEdgesByTenantIdAndEntityId, tenantId [{}], entityId [{}], pageLink [{}]", tenantId, entityId, pageLink);
Validator.validateId(tenantId, "Incorrect tenantId " + tenantId);
Validator.validateId(dashboardId, "Incorrect dashboardId " + dashboardId);
return edgeDao.findEdgesByTenantIdAndDashboardId(tenantId.getId(), dashboardId.getId());
validatePageLink(pageLink);
return edgeDao.findEdgesByTenantIdAndEntityId(tenantId.getId(), entityId.getId(), entityId.getEntityType(), pageLink);
}
private DataValidator<Edge> edgeValidator =
@ -500,88 +488,58 @@ public class EdgeServiceImpl extends AbstractEntityService implements EdgeServic
};
@Override
public ListenableFuture<List<EdgeId>> findRelatedEdgeIdsByEntityId(TenantId tenantId, EntityId entityId) {
// TODO: voba - rewrite 'find' to use native SQL queries instead of fetching relations
log.trace("[{}] Executing findRelatedEdgeIdsByEntityId [{}]", tenantId, entityId);
public PageData<EdgeId> findRelatedEdgeIdsByEntityId(TenantId tenantId, EntityId entityId, PageLink pageLink) {
log.trace("[{}] Executing findRelatedEdgeIdsByEntityId [{}] [{}]", tenantId, entityId, pageLink);
if (EntityType.TENANT.equals(entityId.getEntityType()) ||
EntityType.CUSTOMER.equals(entityId.getEntityType()) ||
EntityType.DEVICE_PROFILE.equals(entityId.getEntityType())) {
List<EdgeId> result = new ArrayList<>();
PageLink pageLink = new PageLink(DEFAULT_LIMIT);
PageData<Edge> pageData;
do {
if (EntityType.TENANT.equals(entityId.getEntityType()) ||
EntityType.DEVICE_PROFILE.equals(entityId.getEntityType())) {
pageData = findEdgesByTenantId(tenantId, pageLink);
} else {
pageData = findEdgesByTenantIdAndCustomerId(tenantId, new CustomerId(entityId.getId()), pageLink);
}
if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) {
for (Edge edge : pageData.getData()) {
result.add(edge.getId());
}
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
}
}
} while (pageData != null && pageData.hasNext());
return Futures.immediateFuture(result);
if (EntityType.TENANT.equals(entityId.getEntityType()) ||
EntityType.DEVICE_PROFILE.equals(entityId.getEntityType())) {
return convertToEdgeIds(findEdgesByTenantId(tenantId, pageLink));
} else {
return convertToEdgeIds(findEdgesByTenantIdAndCustomerId(tenantId, new CustomerId(entityId.getId()), pageLink));
}
} else {
switch (entityId.getEntityType()) {
case EDGE:
List<EdgeId> edgeIds = Collections.singletonList(new EdgeId(entityId.getId()));
return new PageData<>(edgeIds, 1, 1, false);
case DEVICE:
case ASSET:
case ENTITY_VIEW:
ListenableFuture<List<EntityRelation>> originatorEdgeRelationsFuture =
relationService.findByToAndTypeAsync(tenantId, entityId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE);
return Futures.transform(originatorEdgeRelationsFuture, originatorEdgeRelations -> {
if (originatorEdgeRelations != null && originatorEdgeRelations.size() > 0 &&
originatorEdgeRelations.get(0).getFrom() != null) {
return Collections.singletonList(new EdgeId(originatorEdgeRelations.get(0).getFrom().getId()));
} else {
return Collections.emptyList();
}
}, MoreExecutors.directExecutor());
case DASHBOARD:
return convertToEdgeIds(findEdgesByTenantIdAndDashboardId(tenantId, new DashboardId(entityId.getId())));
case RULE_CHAIN:
return convertToEdgeIds(findEdgesByTenantIdAndRuleChainId(tenantId, new RuleChainId(entityId.getId())));
return convertToEdgeIds(findEdgesByTenantIdAndEntityId(tenantId, entityId, pageLink));
case USER:
User userById = userService.findUserById(tenantId, new UserId(entityId.getId()));
if (userById == null) {
return Futures.immediateFuture(Collections.emptyList());
return createEmptyEdgeIdPageData();
}
if (userById.getCustomerId() == null || userById.getCustomerId().isNullUid()) {
return convertToEdgeIds(findEdgesByTenantId(tenantId, pageLink));
} else {
return convertToEdgeIds(findEdgesByTenantIdAndCustomerId(tenantId, userById.getCustomerId(), pageLink));
}
List<Edge> result = new ArrayList<>();
PageLink pageLink = new PageLink(DEFAULT_LIMIT);
PageData<Edge> pageData;
do {
if (userById.getCustomerId() == null || userById.getCustomerId().isNullUid()) {
pageData = findEdgesByTenantId(tenantId, pageLink);
} else {
pageData = findEdgesByTenantIdAndCustomerId(tenantId, new CustomerId(entityId.getId()), pageLink);
}
if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) {
result.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
}
}
} while (pageData != null && pageData.hasNext());
return convertToEdgeIds(Futures.immediateFuture(result));
default:
return Futures.immediateFuture(Collections.emptyList());
log.warn("[{}] Unsupported entity type {}", tenantId, entityId.getEntityType());
return createEmptyEdgeIdPageData();
}
}
}
private ListenableFuture<List<EdgeId>> convertToEdgeIds(ListenableFuture<List<Edge>> future) {
return Futures.transform(future, edges -> {
if (edges != null && !edges.isEmpty()) {
return edges.stream().map(IdBased::getId).collect(Collectors.toList());
} else {
return Collections.emptyList();
}
}, MoreExecutors.directExecutor());
private PageData<EdgeId> createEmptyEdgeIdPageData() {
return new PageData<>(new ArrayList<>(), 0, 0, false);
}
private PageData<EdgeId> convertToEdgeIds(PageData<Edge> pageData) {
if (pageData == null) {
return createEmptyEdgeIdPageData();
}
List<EdgeId> edgeIds = new ArrayList<>();
if (pageData.getData() != null && !pageData.getData().isEmpty()) {
edgeIds = pageData.getData().stream().map(IdBased::getId).collect(Collectors.toList());
}
return new PageData<>(edgeIds, pageData.getTotalPages(), pageData.getTotalElements(), pageData.hasNext());
}
@Override
@ -627,14 +585,9 @@ public class EdgeServiceImpl extends AbstractEntityService implements EdgeServic
return result.toString();
}
@Override
public void cleanupEvents(long ttl) {
edgeDao.cleanupEvents(ttl);
}
private List<RuleChain> findEdgeRuleChains(TenantId tenantId, EdgeId edgeId) {
List<RuleChain> result = new ArrayList<>();
PageLink pageLink = new PageLink(DEFAULT_LIMIT);
PageLink pageLink = new PageLink(DEFAULT_PAGE_SIZE);
PageData<RuleChain> pageData;
do {
pageData = ruleChainService.findRuleChainsByTenantIdAndEdgeId(tenantId, edgeId, pageLink);

4
dao/src/main/java/org/thingsboard/server/dao/entity/AbstractEntityService.java

@ -77,8 +77,8 @@ public abstract class AbstractEntityService {
List<EntityView> entityViews = entityViewService.findEntityViewsByTenantIdAndEntityIdAsync(tenantId, entityId).get();
if (entityViews != null && !entityViews.isEmpty()) {
EntityView entityView = entityViews.get(0);
// TODO: voba - refactor this blocking operation in 3.3+
Boolean relationExists = relationService.checkRelation(tenantId,edgeId, entityView.getId(),
// TODO: @voba - refactor this blocking operation
Boolean relationExists = relationService.checkRelation(tenantId, edgeId, entityView.getId(),
EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE).get();
if (relationExists) {
throw new DataValidationException("Can't unassign device/asset from edge that is related to entity view and entity view is assigned to edge!");

29
dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java

@ -66,7 +66,6 @@ import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;
import static org.thingsboard.server.common.data.DataConstants.TENANT;
@ -375,18 +374,21 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
throw new DataValidationException("Deletion of Root Tenant Rule Chain is prohibited!");
}
if (RuleChainType.EDGE.equals(ruleChain.getType())) {
try {
List<Edge> edges = edgeService.findEdgesByTenantIdAndRuleChainId(tenantId, ruleChainId).get();
if (edges != null && !edges.isEmpty()) {
for (Edge edge : edges) {
PageLink pageLink = new PageLink(DEFAULT_PAGE_SIZE);
PageData<Edge> pageData;
do {
pageData = edgeService.findEdgesByTenantIdAndEntityId(tenantId, ruleChainId, pageLink);
if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) {
for (Edge edge : pageData.getData()) {
if (edge.getRootRuleChainId() != null && edge.getRootRuleChainId().equals(ruleChainId)) {
throw new DataValidationException("Can't delete rule chain that is root for edge [" + edge.getName() + "]. Please assign another root rule chain first to the edge!");
}
}
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
}
}
} catch (InterruptedException | ExecutionException e) {
log.error("Can't get edges by tenant id [{}] and rule chain id [{}]", tenantId.getId(), ruleChainId.getId(), e);
}
} while (pageData != null && pageData.hasNext());
}
}
checkRuleNodesAndDelete(tenantId, ruleChainId);
@ -631,15 +633,14 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
}
@Override
public ListenableFuture<List<RuleChain>> findAutoAssignToEdgeRuleChainsByTenantId(TenantId tenantId) {
log.trace("Executing findAutoAssignToEdgeRuleChainsByTenantId, tenantId [{}]", tenantId);
public PageData<RuleChain> findAutoAssignToEdgeRuleChainsByTenantId(TenantId tenantId, PageLink pageLink) {
log.trace("Executing findAutoAssignToEdgeRuleChainsByTenantId, tenantId [{}], pageLink {}", tenantId, pageLink);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
return ruleChainDao.findAutoAssignToEdgeRuleChainsByTenantId(tenantId.getId());
return ruleChainDao.findAutoAssignToEdgeRuleChainsByTenantId(tenantId.getId(), pageLink);
}
private void checkRuleNodesAndDelete(TenantId tenantId, RuleChainId ruleChainId) {
try{
try {
ruleChainDao.removeById(tenantId, ruleChainId.getId());
} catch (Exception t) {
ConstraintViolationException e = extractConstraintViolationException(t).orElse(null);
@ -674,7 +675,7 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
@Override
protected void validateCreate(TenantId tenantId, RuleChain data) {
DefaultTenantProfileConfiguration profileConfiguration =
(DefaultTenantProfileConfiguration)tenantProfileCache.get(tenantId).getProfileData().getConfiguration();
(DefaultTenantProfileConfiguration) tenantProfileCache.get(tenantId).getProfileData().getConfiguration();
long maxRuleChains = profileConfiguration.getMaxRuleChains();
validateNumberOfEntitiesPerTenant(tenantId, ruleChainDao, maxRuleChains, EntityType.RULE_CHAIN);
}

4
dao/src/main/java/org/thingsboard/server/dao/rule/RuleChainDao.java

@ -15,7 +15,6 @@
*/
package org.thingsboard.server.dao.rule;
import com.google.common.util.concurrent.ListenableFuture;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.rule.RuleChain;
@ -23,7 +22,6 @@ import org.thingsboard.server.common.data.rule.RuleChainType;
import org.thingsboard.server.dao.Dao;
import org.thingsboard.server.dao.TenantEntityDao;
import java.util.List;
import java.util.UUID;
/**
@ -75,5 +73,5 @@ public interface RuleChainDao extends Dao<RuleChain>, TenantEntityDao {
* @param tenantId the tenantId
* @return the list of rule chain objects
*/
ListenableFuture<List<RuleChain>> findAutoAssignToEdgeRuleChainsByTenantId(UUID tenantId);
PageData<RuleChain> findAutoAssignToEdgeRuleChainsByTenantId(UUID tenantId, PageLink pageLink);
}

11
dao/src/main/java/org/thingsboard/server/dao/sql/edge/EdgeRepository.java

@ -22,6 +22,7 @@ import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.repository.query.Param;
import org.thingsboard.server.dao.model.sql.EdgeEntity;
import org.thingsboard.server.dao.model.sql.EdgeInfoEntity;
import org.thingsboard.server.dao.model.sql.RuleChainEntity;
import java.util.List;
import java.util.UUID;
@ -110,6 +111,16 @@ public interface EdgeRepository extends PagingAndSortingRepository<EdgeEntity, U
@Param("textSearch") String textSearch,
Pageable pageable);
@Query("SELECT ee FROM EdgeEntity ee, RelationEntity re WHERE ee.tenantId = :tenantId " +
"AND ee.id = re.fromId AND re.fromType = 'EDGE' AND re.relationTypeGroup = 'EDGE' " +
"AND re.relationType = 'Contains' AND re.toId = :entityId AND re.toType = :entityType " +
"AND LOWER(ee.searchText) LIKE LOWER(CONCAT(:searchText, '%'))")
Page<EdgeEntity> findByTenantIdAndEntityId(@Param("tenantId") UUID tenantId,
@Param("entityId") UUID entityId,
@Param("entityType") String entityType,
@Param("searchText") String searchText,
Pageable pageable);
@Query("SELECT DISTINCT d.type FROM EdgeEntity d WHERE d.tenantId = :tenantId")
List<String> findTenantEdgeTypes(@Param("tenantId") UUID tenantId);

21
dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaBaseEdgeEventDao.java

@ -35,6 +35,10 @@ import org.thingsboard.server.dao.model.sql.EdgeEventEntity;
import org.thingsboard.server.dao.model.sql.EventEntity;
import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Optional;
import java.util.UUID;
@ -116,4 +120,21 @@ public class JpaBaseEdgeEventDao extends JpaAbstractSearchTextDao<EdgeEventEntit
return Optional.of(DaoUtil.getData(edgeEventRepository.save(entity)));
}
@Override
public void cleanupEvents(long ttl) {
log.info("Going to cleanup old edge events using ttl: {}s", ttl);
try (Connection connection = dataSource.getConnection();
PreparedStatement stmt = connection.prepareStatement("call cleanup_edge_events_by_ttl(?,?)")) {
stmt.setLong(1, ttl);
stmt.setLong(2, 0);
stmt.execute();
printWarnings(stmt);
try (ResultSet resultSet = stmt.getResultSet()) {
resultSet.next();
log.info("Total edge events removed by TTL: [{}]", resultSet.getLong(1));
}
} catch (SQLException e) {
log.error("SQLException occurred during edge events TTL task execution ", e);
}
}
}

62
dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaEdgeDao.java

@ -15,9 +15,7 @@
*/
package org.thingsboard.server.dao.sql.edge;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.repository.CrudRepository;
@ -26,24 +24,15 @@ import org.thingsboard.server.common.data.EntitySubtype;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.edge.EdgeInfo;
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.common.data.page.PageData;
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.dao.DaoUtil;
import org.thingsboard.server.dao.edge.EdgeDao;
import org.thingsboard.server.dao.model.sql.EdgeEntity;
import org.thingsboard.server.dao.model.sql.EdgeInfoEntity;
import org.thingsboard.server.dao.relation.RelationDao;
import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@ -58,9 +47,6 @@ public class JpaEdgeDao extends JpaAbstractSearchTextDao<EdgeEntity, Edge> imple
@Autowired
private EdgeRepository edgeRepository;
@Autowired
private RelationDao relationDao;
@Override
protected Class<EdgeEntity> getEntityClass() {
return EdgeEntity.class;
@ -185,45 +171,15 @@ public class JpaEdgeDao extends JpaAbstractSearchTextDao<EdgeEntity, Edge> imple
}
@Override
public ListenableFuture<List<Edge>> findEdgesByTenantIdAndRuleChainId(UUID tenantId, UUID ruleChainId) {
log.debug("Try to find edges by tenantId [{}], ruleChainId [{}]", tenantId, ruleChainId);
ListenableFuture<List<EntityRelation>> relations = relationDao.findAllByToAndType(new TenantId(tenantId), new RuleChainId(ruleChainId), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE);
return transformFromRelationToEdge(tenantId, relations);
}
@Override
public ListenableFuture<List<Edge>> findEdgesByTenantIdAndDashboardId(UUID tenantId, UUID dashboardId) {
log.debug("Try to find edges by tenantId [{}], dashboardId [{}]", tenantId, dashboardId);
ListenableFuture<List<EntityRelation>> relations = relationDao.findAllByToAndType(new TenantId(tenantId), new DashboardId(dashboardId), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE);
return transformFromRelationToEdge(tenantId, relations);
}
@Override
public void cleanupEvents(long ttl) {
log.info("Going to cleanup old edge events using ttl: {}s", ttl);
try (Connection connection = dataSource.getConnection();
PreparedStatement stmt = connection.prepareStatement("call cleanup_edge_events_by_ttl(?,?)")) {
stmt.setLong(1, ttl);
stmt.setLong(2, 0);
stmt.execute();
printWarnings(stmt);
try (ResultSet resultSet = stmt.getResultSet()) {
resultSet.next();
log.info("Total edge events removed by TTL: [{}]", resultSet.getLong(1));
}
} catch (SQLException e) {
log.error("SQLException occurred during edge events TTL task execution ", e);
}
}
private ListenableFuture<List<Edge>> transformFromRelationToEdge(UUID tenantId, ListenableFuture<List<EntityRelation>> relations) {
return Futures.transformAsync(relations, input -> {
List<ListenableFuture<Edge>> edgeFutures = new ArrayList<>(input.size());
for (EntityRelation relation : input) {
edgeFutures.add(findByIdAsync(new TenantId(tenantId), relation.getFrom().getId()));
}
return Futures.successfulAsList(edgeFutures);
}, MoreExecutors.directExecutor());
public PageData<Edge> findEdgesByTenantIdAndEntityId(UUID tenantId, UUID entityId, EntityType entityType, PageLink pageLink) {
log.debug("Try to find edges by tenantId [{}], entityId [{}], entityType [{}], pageLink [{}]", tenantId, entityId, entityType, pageLink);
return DaoUtil.toPageData(
edgeRepository.findByTenantIdAndEntityId(
tenantId,
entityId,
entityType.name(),
Objects.toString(pageLink.getTextSearch(), ""),
DaoUtil.toPageable(pageLink)));
}
private List<EntitySubtype> convertTenantEdgeTypesToDto(UUID tenantId, List<String> types) {

33
dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleChainDao.java

@ -15,9 +15,6 @@
*/
package org.thingsboard.server.dao.sql.rule;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.repository.CrudRepository;
@ -25,19 +22,13 @@ import org.springframework.stereotype.Component;
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.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.dao.DaoUtil;
import org.thingsboard.server.dao.model.sql.RuleChainEntity;
import org.thingsboard.server.dao.relation.RelationDao;
import org.thingsboard.server.dao.rule.RuleChainDao;
import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
@ -48,9 +39,6 @@ public class JpaRuleChainDao extends JpaAbstractSearchTextDao<RuleChainEntity, R
@Autowired
private RuleChainRepository ruleChainRepository;
@Autowired
private RelationDao relationDao;
@Override
protected Class<RuleChainEntity> getEntityClass() {
return RuleChainEntity.class;
@ -91,7 +79,6 @@ public class JpaRuleChainDao extends JpaAbstractSearchTextDao<RuleChainEntity, R
@Override
public PageData<RuleChain> findRuleChainsByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, PageLink pageLink) {
log.debug("Try to find rule chains by tenantId [{}], edgeId [{}] and pageLink [{}]", tenantId, edgeId, pageLink);
return DaoUtil.toPageData(ruleChainRepository
.findByTenantIdAndEdgeId(
tenantId,
@ -101,21 +88,13 @@ public class JpaRuleChainDao extends JpaAbstractSearchTextDao<RuleChainEntity, R
}
@Override
public ListenableFuture<List<RuleChain>> findAutoAssignToEdgeRuleChainsByTenantId(UUID tenantId) {
public PageData<RuleChain> findAutoAssignToEdgeRuleChainsByTenantId(UUID tenantId, PageLink pageLink) {
log.debug("Try to find auto assign to edge rule chains by tenantId [{}]", tenantId);
ListenableFuture<List<EntityRelation>> relations =
relationDao.findAllByFromAndType(new TenantId(tenantId), new TenantId(tenantId), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE_AUTO_ASSIGN_RULE_CHAIN);
return Futures.transformAsync(relations, input -> {
if (input != null && !input.isEmpty()) {
List<ListenableFuture<RuleChain>> ruleChainsFutures = new ArrayList<>(input.size());
for (EntityRelation relation : input) {
ruleChainsFutures.add(findByIdAsync(new TenantId(tenantId), relation.getTo().getId()));
}
return Futures.successfulAsList(ruleChainsFutures);
} else {
return Futures.immediateFuture(Collections.emptyList());
}
}, MoreExecutors.directExecutor());
return DaoUtil.toPageData(ruleChainRepository
.findAutoAssignByTenantId(
tenantId,
Objects.toString(pageLink.getTextSearch(), ""),
DaoUtil.toPageable(pageLink)));
}
@Override

9
dao/src/main/java/org/thingsboard/server/dao/sql/rule/RuleChainRepository.java

@ -21,7 +21,6 @@ import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.repository.query.Param;
import org.thingsboard.server.common.data.rule.RuleChainType;
import org.thingsboard.server.dao.model.sql.DashboardInfoEntity;
import org.thingsboard.server.dao.model.sql.RuleChainEntity;
import java.util.UUID;
@ -51,6 +50,14 @@ public interface RuleChainRepository extends PagingAndSortingRepository<RuleChai
@Param("searchText") String searchText,
Pageable pageable);
@Query("SELECT rc FROM RuleChainEntity rc, RelationEntity re WHERE rc.tenantId = :tenantId " +
"AND rc.id = re.toId AND re.toType = 'RULE_CHAIN' AND re.relationTypeGroup = 'EDGE_AUTO_ASSIGN_RULE_CHAIN' " +
"AND re.relationType = 'Contains' AND re.fromId = :tenantId AND re.fromType = 'TENANT' " +
"AND LOWER(rc.searchText) LIKE LOWER(CONCAT(:searchText, '%'))")
Page<RuleChainEntity> findAutoAssignByTenantId(@Param("tenantId") UUID tenantId,
@Param("searchText") String searchText,
Pageable pageable);
RuleChainEntity findByTenantIdAndTypeAndRootIsTrue(UUID tenantId, RuleChainType ruleChainType);
Long countByTenantId(UUID tenantId);

13
dao/src/test/java/org/thingsboard/server/dao/service/AbstractServiceTest.java

@ -18,6 +18,7 @@ package org.thingsboard.server.dao.service;
import com.datastax.oss.driver.api.core.uuid.Uuids;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.lang3.RandomStringUtils;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
@ -36,6 +37,7 @@ import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration;
import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileTransportConfiguration;
import org.thingsboard.server.common.data.device.profile.DeviceProfileData;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.HasId;
import org.thingsboard.server.common.data.id.TenantId;
@ -240,4 +242,15 @@ public abstract class AbstractServiceTest {
return savedTenant.getId();
}
protected Edge constructEdge(TenantId tenantId, String name, String type) {
Edge edge = new Edge();
edge.setTenantId(tenantId);
edge.setName(name);
edge.setType(type);
edge.setSecret(RandomStringUtils.randomAlphanumeric(20));
edge.setRoutingKey(RandomStringUtils.randomAlphanumeric(20));
edge.setEdgeLicenseKey(RandomStringUtils.randomAlphanumeric(20));
edge.setCloudEndpoint("http://localhost:8080");
return edge;
}
}

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

Loading…
Cancel
Save