Browse Source

Merge pull request #789 from thingsboard/develop/cluster-refactoring

Improvements to Cluster mode
pull/804/head
Andrew Shvayka 8 years ago
committed by GitHub
parent
commit
0c7478b8d1
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 9
      application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java
  2. 16
      application/src/main/java/org/thingsboard/server/actors/app/AppActor.java
  3. 35
      application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java
  4. 3
      application/src/main/java/org/thingsboard/server/actors/plugin/PluginProcessingContext.java
  5. 7
      application/src/main/java/org/thingsboard/server/actors/plugin/SharedPluginProcessingContext.java
  6. 2
      application/src/main/java/org/thingsboard/server/actors/plugin/ValidationCallback.java
  7. 105
      application/src/main/java/org/thingsboard/server/actors/rpc/BasicRpcSessionListener.java
  8. 2
      application/src/main/java/org/thingsboard/server/actors/rpc/RpcBroadcastMsg.java
  9. 45
      application/src/main/java/org/thingsboard/server/actors/rpc/RpcManagerActor.java
  10. 23
      application/src/main/java/org/thingsboard/server/actors/rpc/RpcSessionActor.java
  11. 2
      application/src/main/java/org/thingsboard/server/actors/rpc/RpcSessionCreateRequestMsg.java
  12. 3
      application/src/main/java/org/thingsboard/server/actors/rpc/RpcSessionTellMsg.java
  13. 25
      application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java
  14. 2
      application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActor.java
  15. 21
      application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java
  16. 4
      application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeToRuleChainTellNextMsg.java
  17. 4
      application/src/main/java/org/thingsboard/server/actors/service/ActorService.java
  18. 8
      application/src/main/java/org/thingsboard/server/actors/service/ContextAwareActor.java
  19. 144
      application/src/main/java/org/thingsboard/server/actors/service/DefaultActorService.java
  20. 2
      application/src/main/java/org/thingsboard/server/actors/session/ASyncMsgProcessor.java
  21. 22
      application/src/main/java/org/thingsboard/server/actors/session/AbstractSessionActorMsgProcessor.java
  22. 50
      application/src/main/java/org/thingsboard/server/actors/session/SessionActor.java
  23. 6
      application/src/main/java/org/thingsboard/server/actors/session/SessionManagerActor.java
  24. 6
      application/src/main/java/org/thingsboard/server/actors/session/SyncMsgProcessor.java
  25. 2
      application/src/main/java/org/thingsboard/server/actors/shared/ComponentMsgProcessor.java
  26. 9
      application/src/main/java/org/thingsboard/server/actors/shared/SessionTimeoutMsg.java
  27. 13
      application/src/main/java/org/thingsboard/server/service/cluster/discovery/ServerInstance.java
  28. 26
      application/src/main/java/org/thingsboard/server/service/cluster/discovery/ZkDiscoveryService.java
  29. 4
      application/src/main/java/org/thingsboard/server/service/cluster/routing/ConsistentClusterRoutingService.java
  30. 182
      application/src/main/java/org/thingsboard/server/service/cluster/rpc/ClusterGrpcService.java
  31. 26
      application/src/main/java/org/thingsboard/server/service/cluster/rpc/ClusterRpcService.java
  32. 40
      application/src/main/java/org/thingsboard/server/service/cluster/rpc/GrpcSession.java
  33. 15
      application/src/main/java/org/thingsboard/server/service/cluster/rpc/GrpcSessionListener.java
  34. 30
      application/src/main/java/org/thingsboard/server/service/cluster/rpc/RpcMsgListener.java
  35. 63
      application/src/main/java/org/thingsboard/server/service/cluster/rpc/RpcSessionCreationFuture.java
  36. 34
      application/src/main/java/org/thingsboard/server/service/encoding/DataDecodingEncodingService.java
  37. 67
      application/src/main/java/org/thingsboard/server/service/encoding/ProtoWithJavaSerializationDecodingEncodingService.java
  38. 18
      application/src/main/java/org/thingsboard/server/service/rpc/DefaultDeviceRpcService.java
  39. 350
      application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java
  40. 2
      application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetryWebSocketService.java
  41. 16
      application/src/main/java/org/thingsboard/server/service/telemetry/TelemetrySubscriptionService.java
  42. 121
      application/src/main/proto/cluster.proto
  43. 2
      application/src/main/resources/logback.xml
  44. 5
      application/src/main/resources/thingsboard.yml
  45. 31
      application/src/test/java/org/thingsboard/server/mqtt/DbConfigurationTestRule.java
  46. 45
      base-docker-compose.yml
  47. 3
      common/data/src/main/java/org/thingsboard/server/common/data/id/IdBased.java
  48. 7
      common/message/src/main/java/org/thingsboard/server/common/msg/MsgType.java
  49. 40
      common/message/src/main/java/org/thingsboard/server/common/msg/cluster/SendToClusterMsg.java
  50. 4
      common/message/src/main/java/org/thingsboard/server/common/msg/cluster/ToAllNodesMsg.java
  51. 3
      common/message/src/main/java/org/thingsboard/server/common/msg/core/ActorSystemToDeviceSessionActorMsg.java
  52. 9
      common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicActorSystemToDeviceSessionActorMsg.java
  53. 4
      common/message/src/main/java/org/thingsboard/server/common/msg/device/BasicDeviceToDeviceActorMsg.java
  54. 1
      common/message/src/main/java/org/thingsboard/server/common/msg/plugin/ComponentLifecycleMsg.java
  55. 18
      common/message/src/main/java/org/thingsboard/server/common/msg/session/BasicTransportToDeviceSessionActorMsg.java
  56. 3
      common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionCtrlMsg.java
  57. 3
      common/message/src/main/java/org/thingsboard/server/common/msg/session/TransportToDeviceSessionActorMsg.java
  58. 5
      common/message/src/main/java/org/thingsboard/server/common/msg/session/ctrl/SessionCloseMsg.java
  59. 12
      dao/pom.xml
  60. 32
      dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java
  61. 4
      dao/src/main/java/org/thingsboard/server/dao/alarm/CassandraAlarmDao.java
  62. 4
      dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java
  63. 17
      dao/src/main/java/org/thingsboard/server/dao/cassandra/AbstractCassandraCluster.java
  64. 2
      dao/src/main/java/org/thingsboard/server/dao/dashboard/CassandraDashboardInfoDao.java
  65. 4
      dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java
  66. 4
      dao/src/main/java/org/thingsboard/server/dao/nosql/RateLimitedResultSetFuture.java
  67. 76
      dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationService.java
  68. 4
      dao/src/main/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDao.java
  69. 2
      dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/JpaDashboardInfoDao.java
  70. 2
      dao/src/main/java/org/thingsboard/server/dao/timeseries/CassandraBaseTimeseriesDao.java
  71. 2
      dao/src/main/resources/cassandra/schema.cql
  72. 2
      dao/src/test/java/org/thingsboard/server/dao/CustomCassandraCQLUnit.java
  73. 2
      dao/src/test/resources/cassandra-test.yaml
  74. 27
      pom.xml
  75. 6
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbClearAlarmNode.java
  76. 10
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCreateAlarmNode.java
  77. 5
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/EntitiesCustomerIdAsyncLoader.java
  78. 5
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/EntitiesRelatedDeviceIdAsyncLoader.java
  79. 10
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/EntitiesRelatedEntityIdAsyncLoader.java
  80. 2
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/EntitiesTenantIdAsyncLoader.java
  81. 4
      transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java
  82. 4
      transport/coap/src/test/java/org/thingsboard/server/transport/coap/CoapServerTest.java
  83. 4
      transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java
  84. 12
      transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java
  85. 14
      transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionCtx.java

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

@ -60,6 +60,7 @@ import org.thingsboard.server.service.cluster.discovery.DiscoveryService;
import org.thingsboard.server.service.cluster.routing.ClusterRoutingService; import org.thingsboard.server.service.cluster.routing.ClusterRoutingService;
import org.thingsboard.server.service.cluster.rpc.ClusterRpcService; import org.thingsboard.server.service.cluster.rpc.ClusterRpcService;
import org.thingsboard.server.service.component.ComponentDiscoveryService; import org.thingsboard.server.service.component.ComponentDiscoveryService;
import org.thingsboard.server.service.encoding.DataDecodingEncodingService;
import org.thingsboard.server.service.executors.DbCallbackExecutorService; import org.thingsboard.server.service.executors.DbCallbackExecutorService;
import org.thingsboard.server.service.executors.ExternalCallExecutorService; import org.thingsboard.server.service.executors.ExternalCallExecutorService;
import org.thingsboard.server.service.mail.MailExecutorService; import org.thingsboard.server.service.mail.MailExecutorService;
@ -102,6 +103,10 @@ public class ActorSystemContext {
@Getter @Getter
private ClusterRpcService rpcService; private ClusterRpcService rpcService;
@Autowired
@Getter
private DataDecodingEncodingService encodingService;
@Autowired @Autowired
@Getter @Getter
private DeviceAuthService deviceAuthService; private DeviceAuthService deviceAuthService;
@ -203,6 +208,10 @@ public class ActorSystemContext {
@Getter @Getter
private DeviceStateService deviceStateService; private DeviceStateService deviceStateService;
@Value("${cluster.partition_id}")
@Getter
private long queuePartitionId;
@Value("${actors.session.sync.timeout}") @Value("${actors.session.sync.timeout}")
@Getter @Getter
private long syncSessionTimeout; private long syncSessionTimeout;

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

@ -34,6 +34,8 @@ import org.thingsboard.server.common.data.page.PageDataIterable;
import org.thingsboard.server.common.msg.TbActorMsg; import org.thingsboard.server.common.msg.TbActorMsg;
import org.thingsboard.server.common.msg.aware.DeviceAwareMsg; import org.thingsboard.server.common.msg.aware.DeviceAwareMsg;
import org.thingsboard.server.common.msg.aware.TenantAwareMsg; import org.thingsboard.server.common.msg.aware.TenantAwareMsg;
import org.thingsboard.server.common.msg.cluster.SendToClusterMsg;
import org.thingsboard.server.common.msg.cluster.ServerAddress;
import org.thingsboard.server.common.msg.device.DeviceToDeviceActorMsg; import org.thingsboard.server.common.msg.device.DeviceToDeviceActorMsg;
import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg; import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg;
@ -45,6 +47,7 @@ import scala.concurrent.duration.Duration;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Optional;
public class AppActor extends RuleChainManagerActor { public class AppActor extends RuleChainManagerActor {
@ -89,6 +92,9 @@ public class AppActor extends RuleChainManagerActor {
@Override @Override
protected boolean process(TbActorMsg msg) { protected boolean process(TbActorMsg msg) {
switch (msg.getMsgType()) { switch (msg.getMsgType()) {
case SEND_TO_CLUSTER_MSG:
onPossibleClusterMsg((SendToClusterMsg) msg);
break;
case CLUSTER_EVENT_MSG: case CLUSTER_EVENT_MSG:
broadcast(msg); broadcast(msg);
break; break;
@ -112,6 +118,16 @@ public class AppActor extends RuleChainManagerActor {
return true; return true;
} }
private void onPossibleClusterMsg(SendToClusterMsg msg) {
Optional<ServerAddress> address = systemContext.getRoutingService().resolveById(msg.getEntityId());
if (address.isPresent()) {
systemContext.getRpcService().tell(
systemContext.getEncodingService().convertToProtoDataMessage(address.get(), msg.getMsg()));
} else {
self().tell(msg.getMsg(), ActorRef.noSender());
}
}
private void onServiceToRuleEngineMsg(ServiceToRuleEngineMsg msg) { private void onServiceToRuleEngineMsg(ServiceToRuleEngineMsg msg) {
if (SYSTEM_TENANT.equals(msg.getTenantId())) { if (SYSTEM_TENANT.equals(msg.getTenantId())) {
//TODO: ashvayka handle this. //TODO: ashvayka handle this.

35
application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java

@ -23,7 +23,6 @@ import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import com.google.gson.JsonParser; import com.google.gson.JsonParser;
import org.thingsboard.server.actors.ActorSystemContext; import org.thingsboard.server.actors.ActorSystemContext;
@ -47,7 +46,7 @@ import org.thingsboard.server.common.msg.core.AttributesUpdateRequest;
import org.thingsboard.server.common.msg.core.BasicCommandAckResponse; import org.thingsboard.server.common.msg.core.BasicCommandAckResponse;
import org.thingsboard.server.common.msg.core.BasicGetAttributesResponse; import org.thingsboard.server.common.msg.core.BasicGetAttributesResponse;
import org.thingsboard.server.common.msg.core.BasicStatusCodeResponse; import org.thingsboard.server.common.msg.core.BasicStatusCodeResponse;
import org.thingsboard.server.common.msg.core.BasicToDeviceSessionActorMsg; import org.thingsboard.server.common.msg.core.BasicActorSystemToDeviceSessionActorMsg;
import org.thingsboard.server.common.msg.core.GetAttributesRequest; import org.thingsboard.server.common.msg.core.GetAttributesRequest;
import org.thingsboard.server.common.msg.core.RuleEngineError; import org.thingsboard.server.common.msg.core.RuleEngineError;
import org.thingsboard.server.common.msg.core.RuleEngineErrorMsg; import org.thingsboard.server.common.msg.core.RuleEngineErrorMsg;
@ -57,13 +56,12 @@ import org.thingsboard.server.common.msg.core.SessionOpenMsg;
import org.thingsboard.server.common.msg.core.TelemetryUploadRequest; import org.thingsboard.server.common.msg.core.TelemetryUploadRequest;
import org.thingsboard.server.common.msg.core.ToDeviceRpcRequestMsg; import org.thingsboard.server.common.msg.core.ToDeviceRpcRequestMsg;
import org.thingsboard.server.common.msg.core.ToDeviceRpcResponseMsg; import org.thingsboard.server.common.msg.core.ToDeviceRpcResponseMsg;
import org.thingsboard.server.common.msg.core.ToDeviceSessionActorMsg; import org.thingsboard.server.common.msg.core.ActorSystemToDeviceSessionActorMsg;
import org.thingsboard.server.common.msg.core.ToServerRpcRequestMsg; import org.thingsboard.server.common.msg.core.ToServerRpcRequestMsg;
import org.thingsboard.server.common.msg.device.DeviceToDeviceActorMsg; import org.thingsboard.server.common.msg.device.DeviceToDeviceActorMsg;
import org.thingsboard.server.common.msg.kv.BasicAttributeKVMsg; import org.thingsboard.server.common.msg.kv.BasicAttributeKVMsg;
import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest; import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest;
import org.thingsboard.server.common.msg.session.FromDeviceMsg; import org.thingsboard.server.common.msg.session.FromDeviceMsg;
import org.thingsboard.server.common.msg.session.FromDeviceRequestMsg;
import org.thingsboard.server.common.msg.session.SessionMsgType; import org.thingsboard.server.common.msg.session.SessionMsgType;
import org.thingsboard.server.common.msg.session.SessionType; import org.thingsboard.server.common.msg.session.SessionType;
import org.thingsboard.server.common.msg.session.ToDeviceMsg; import org.thingsboard.server.common.msg.session.ToDeviceMsg;
@ -155,7 +153,7 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
boolean sent = rpcSubscriptions.size() > 0; boolean sent = rpcSubscriptions.size() > 0;
Set<SessionId> syncSessionSet = new HashSet<>(); Set<SessionId> syncSessionSet = new HashSet<>();
rpcSubscriptions.entrySet().forEach(sub -> { rpcSubscriptions.entrySet().forEach(sub -> {
ToDeviceSessionActorMsg response = new BasicToDeviceSessionActorMsg(rpcRequest, sub.getKey()); ActorSystemToDeviceSessionActorMsg response = new BasicActorSystemToDeviceSessionActorMsg(rpcRequest, sub.getKey());
sendMsgToSessionActor(response, sub.getValue().getServer()); sendMsgToSessionActor(response, sub.getValue().getServer());
if (SessionType.SYNC == sub.getValue().getType()) { if (SessionType.SYNC == sub.getValue().getType()) {
syncSessionSet.add(sub.getKey()); syncSessionSet.add(sub.getKey());
@ -197,7 +195,7 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
if (data != null) { if (data != null) {
logger.debug("[{}] Queue put [{}] timeout detected!", deviceId, msg.getId()); logger.debug("[{}] Queue put [{}] timeout detected!", deviceId, msg.getId());
ToDeviceMsg toDeviceMsg = new RuleEngineErrorMsg(data.getSessionMsgType(), RuleEngineError.QUEUE_PUT_TIMEOUT); ToDeviceMsg toDeviceMsg = new RuleEngineErrorMsg(data.getSessionMsgType(), RuleEngineError.QUEUE_PUT_TIMEOUT);
sendMsgToSessionActor(new BasicToDeviceSessionActorMsg(toDeviceMsg, data.getSessionId()), data.getServerAddress()); sendMsgToSessionActor(new BasicActorSystemToDeviceSessionActorMsg(toDeviceMsg, data.getSessionId()), data.getServerAddress());
} }
} }
@ -209,7 +207,7 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
logger.debug("[{}] Queue put [{}] ack detected. Remaining acks: {}!", deviceId, msg.getId(), remainingAcks); logger.debug("[{}] Queue put [{}] ack detected. Remaining acks: {}!", deviceId, msg.getId(), remainingAcks);
if (remainingAcks == 0) { if (remainingAcks == 0) {
ToDeviceMsg toDeviceMsg = BasicStatusCodeResponse.onSuccess(data.getSessionMsgType(), data.getRequestId()); ToDeviceMsg toDeviceMsg = BasicStatusCodeResponse.onSuccess(data.getSessionMsgType(), data.getRequestId());
sendMsgToSessionActor(new BasicToDeviceSessionActorMsg(toDeviceMsg, data.getSessionId()), data.getServerAddress()); sendMsgToSessionActor(new BasicActorSystemToDeviceSessionActorMsg(toDeviceMsg, data.getSessionId()), data.getServerAddress());
} }
} }
} }
@ -247,7 +245,7 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
body.getMethod(), body.getMethod(),
body.getParams() body.getParams()
); );
ToDeviceSessionActorMsg response = new BasicToDeviceSessionActorMsg(rpcRequest, sessionId); ActorSystemToDeviceSessionActorMsg response = new BasicActorSystemToDeviceSessionActorMsg(rpcRequest, sessionId);
sendMsgToSessionActor(response, server); sendMsgToSessionActor(response, server);
}; };
} }
@ -301,14 +299,14 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
public void onSuccess(@Nullable List<List<AttributeKvEntry>> result) { public void onSuccess(@Nullable List<List<AttributeKvEntry>> result) {
BasicGetAttributesResponse response = BasicGetAttributesResponse.onSuccess(request.getMsgType(), BasicGetAttributesResponse response = BasicGetAttributesResponse.onSuccess(request.getMsgType(),
request.getRequestId(), BasicAttributeKVMsg.from(result.get(0), result.get(1))); request.getRequestId(), BasicAttributeKVMsg.from(result.get(0), result.get(1)));
sendMsgToSessionActor(new BasicToDeviceSessionActorMsg(response, src.getSessionId()), src.getServerAddress()); sendMsgToSessionActor(new BasicActorSystemToDeviceSessionActorMsg(response, src.getSessionId()), src.getServerAddress());
} }
@Override @Override
public void onFailure(Throwable t) { public void onFailure(Throwable t) {
if (t instanceof Exception) { if (t instanceof Exception) {
ToDeviceMsg toDeviceMsg = BasicStatusCodeResponse.onError(SessionMsgType.GET_ATTRIBUTES_REQUEST, request.getRequestId(), (Exception) t); ToDeviceMsg toDeviceMsg = BasicStatusCodeResponse.onError(SessionMsgType.GET_ATTRIBUTES_REQUEST, request.getRequestId(), (Exception) t);
sendMsgToSessionActor(new BasicToDeviceSessionActorMsg(toDeviceMsg, src.getSessionId()), src.getServerAddress()); sendMsgToSessionActor(new BasicActorSystemToDeviceSessionActorMsg(toDeviceMsg, src.getSessionId()), src.getServerAddress());
} else { } else {
logger.error("[{}] Failed to process attributes request", deviceId, t); logger.error("[{}] Failed to process attributes request", deviceId, t);
} }
@ -390,14 +388,14 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
if (data != null) { if (data != null) {
logger.debug("[{}] Client side RPC request [{}] timeout detected!", deviceId, msg.getId()); logger.debug("[{}] Client side RPC request [{}] timeout detected!", deviceId, msg.getId());
ToDeviceMsg toDeviceMsg = new RuleEngineErrorMsg(SessionMsgType.TO_SERVER_RPC_REQUEST, RuleEngineError.TIMEOUT); ToDeviceMsg toDeviceMsg = new RuleEngineErrorMsg(SessionMsgType.TO_SERVER_RPC_REQUEST, RuleEngineError.TIMEOUT);
sendMsgToSessionActor(new BasicToDeviceSessionActorMsg(toDeviceMsg, data.getSessionId()), data.getServer()); sendMsgToSessionActor(new BasicActorSystemToDeviceSessionActorMsg(toDeviceMsg, data.getSessionId()), data.getServer());
} }
} }
void processToServerRPCResponse(ActorContext context, ToServerRpcResponseActorMsg msg) { void processToServerRPCResponse(ActorContext context, ToServerRpcResponseActorMsg msg) {
ToServerRpcRequestMetadata data = toServerRpcPendingMap.remove(msg.getMsg().getRequestId()); ToServerRpcRequestMetadata data = toServerRpcPendingMap.remove(msg.getMsg().getRequestId());
if (data != null) { if (data != null) {
sendMsgToSessionActor(new BasicToDeviceSessionActorMsg(msg.getMsg(), data.getSessionId()), data.getServer()); sendMsgToSessionActor(new BasicActorSystemToDeviceSessionActorMsg(msg.getMsg(), data.getSessionId()), data.getServer());
} }
} }
@ -408,7 +406,7 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
pendingMsgs.put(tbMsg.getId(), pendingMsgData); pendingMsgs.put(tbMsg.getId(), pendingMsgData);
scheduleMsgWithDelay(context, new DeviceActorQueueTimeoutMsg(tbMsg.getId(), systemContext.getQueuePersistenceTimeout()), systemContext.getQueuePersistenceTimeout()); scheduleMsgWithDelay(context, new DeviceActorQueueTimeoutMsg(tbMsg.getId(), systemContext.getQueuePersistenceTimeout()), systemContext.getQueuePersistenceTimeout());
} else { } else {
ToDeviceSessionActorMsg response = new BasicToDeviceSessionActorMsg(BasicStatusCodeResponse.onSuccess(sessionMsgType, requestId), pendingMsgData.getSessionId()); ActorSystemToDeviceSessionActorMsg response = new BasicActorSystemToDeviceSessionActorMsg(BasicStatusCodeResponse.onSuccess(sessionMsgType, requestId), pendingMsgData.getSessionId());
sendMsgToSessionActor(response, pendingMsgData.getServerAddress()); sendMsgToSessionActor(response, pendingMsgData.getServerAddress());
} }
context.parent().tell(new DeviceActorToRuleEngineMsg(context.self(), tbMsg), context.self()); context.parent().tell(new DeviceActorToRuleEngineMsg(context.self(), tbMsg), context.self());
@ -435,7 +433,7 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
if (notification != null) { if (notification != null) {
ToDeviceMsg finalNotification = notification; ToDeviceMsg finalNotification = notification;
attributeSubscriptions.entrySet().forEach(sub -> { attributeSubscriptions.entrySet().forEach(sub -> {
ToDeviceSessionActorMsg response = new BasicToDeviceSessionActorMsg(finalNotification, sub.getKey()); ActorSystemToDeviceSessionActorMsg response = new BasicActorSystemToDeviceSessionActorMsg(finalNotification, sub.getKey());
sendMsgToSessionActor(response, sub.getValue().getServer()); sendMsgToSessionActor(response, sub.getValue().getServer());
}); });
} }
@ -461,7 +459,7 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
BasicCommandAckResponse response = success BasicCommandAckResponse response = success
? BasicCommandAckResponse.onSuccess(SessionMsgType.TO_DEVICE_RPC_REQUEST, responseMsg.getRequestId()) ? BasicCommandAckResponse.onSuccess(SessionMsgType.TO_DEVICE_RPC_REQUEST, responseMsg.getRequestId())
: BasicCommandAckResponse.onError(SessionMsgType.TO_DEVICE_RPC_REQUEST, responseMsg.getRequestId(), new TimeoutException()); : BasicCommandAckResponse.onError(SessionMsgType.TO_DEVICE_RPC_REQUEST, responseMsg.getRequestId(), new TimeoutException());
sendMsgToSessionActor(new BasicToDeviceSessionActorMsg(response, msg.getSessionId()), msg.getServerAddress()); sendMsgToSessionActor(new BasicActorSystemToDeviceSessionActorMsg(response, msg.getSessionId()), msg.getServerAddress());
} }
} }
} }
@ -516,11 +514,12 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
} }
} }
private void sendMsgToSessionActor(ToDeviceSessionActorMsg response, Optional<ServerAddress> sessionAddress) { private void sendMsgToSessionActor(ActorSystemToDeviceSessionActorMsg response, Optional<ServerAddress> sessionAddress) {
if (sessionAddress.isPresent()) { if (sessionAddress.isPresent()) {
ServerAddress address = sessionAddress.get(); ServerAddress address = sessionAddress.get();
logger.debug("{} Forwarding msg: {}", address, response); logger.debug("{} Forwarding msg: {}", address, response);
systemContext.getRpcService().tell(sessionAddress.get(), response); systemContext.getRpcService().tell(systemContext.getEncodingService()
.convertToProtoDataMessage(sessionAddress.get(), response));
} else { } else {
systemContext.getSessionManagerActor().tell(response, ActorRef.noSender()); systemContext.getSessionManagerActor().tell(response, ActorRef.noSender());
} }
@ -528,7 +527,7 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
void processCredentialsUpdate() { void processCredentialsUpdate() {
sessions.forEach((k, v) -> { sessions.forEach((k, v) -> {
sendMsgToSessionActor(new BasicToDeviceSessionActorMsg(new SessionCloseNotification(), k), v.getServer()); sendMsgToSessionActor(new BasicActorSystemToDeviceSessionActorMsg(new SessionCloseNotification(), k), v.getServer());
}); });
attributeSubscriptions.clear(); attributeSubscriptions.clear();
rpcSubscriptions.clear(); rpcSubscriptions.clear();

3
application/src/main/java/org/thingsboard/server/actors/plugin/PluginProcessingContext.java

@ -82,7 +82,8 @@ public final class PluginProcessingContext implements PluginContext {
@Override @Override
public void sendPluginRpcMsg(RpcMsg msg) { public void sendPluginRpcMsg(RpcMsg msg) {
this.pluginCtx.rpcService.tell(new PluginRpcMsg(pluginCtx.tenantId, pluginCtx.pluginId, msg)); //ToDO is this a cluster messsage?
// this.pluginCtx.rpcService.tell(new PluginRpcMsg(pluginCtx.tenantId, pluginCtx.pluginId, msg));
} }
@Override @Override

7
application/src/main/java/org/thingsboard/server/actors/plugin/SharedPluginProcessingContext.java

@ -21,6 +21,7 @@ import org.thingsboard.server.actors.ActorSystemContext;
import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.PluginId; import org.thingsboard.server.common.data.id.PluginId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.msg.TbActorMsg;
import org.thingsboard.server.common.msg.cluster.ServerAddress; import org.thingsboard.server.common.msg.cluster.ServerAddress;
import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest; import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest;
import org.thingsboard.server.common.msg.timeout.TimeoutMsg; import org.thingsboard.server.common.msg.timeout.TimeoutMsg;
@ -100,7 +101,7 @@ public final class SharedPluginProcessingContext {
} }
public void toDeviceActor(DeviceAttributesEventNotificationMsg msg) { public void toDeviceActor(DeviceAttributesEventNotificationMsg msg) {
forward(msg.getDeviceId(), msg, rpcService::tell); forward(msg.getDeviceId(), msg);
} }
public void sendRpcRequest(ToDeviceRpcRequest msg) { public void sendRpcRequest(ToDeviceRpcRequest msg) {
@ -109,11 +110,11 @@ public final class SharedPluginProcessingContext {
// forward(msg.getDeviceId(), rpcMsg, rpcService::tell); // forward(msg.getDeviceId(), rpcMsg, rpcService::tell);
} }
private <T> void forward(DeviceId deviceId, T msg, BiConsumer<ServerAddress, T> rpcFunction) { private <T extends TbActorMsg> void forward(DeviceId deviceId, T msg) {
Optional<ServerAddress> instance = routingService.resolveById(deviceId); Optional<ServerAddress> instance = routingService.resolveById(deviceId);
if (instance.isPresent()) { if (instance.isPresent()) {
log.trace("[{}] Forwarding msg {} to remote device actor!", pluginId, msg); log.trace("[{}] Forwarding msg {} to remote device actor!", pluginId, msg);
rpcFunction.accept(instance.get(), msg); rpcService.tell(systemContext.getEncodingService().convertToProtoDataMessage(instance.get(), msg));
} else { } else {
log.trace("[{}] Forwarding msg {} to local device actor!", pluginId, msg); log.trace("[{}] Forwarding msg {} to local device actor!", pluginId, msg);
parentActor.tell(msg, ActorRef.noSender()); parentActor.tell(msg, ActorRef.noSender());

2
application/src/main/java/org/thingsboard/server/actors/plugin/ValidationCallback.java

@ -15,7 +15,7 @@
*/ */
package org.thingsboard.server.actors.plugin; package org.thingsboard.server.actors.plugin;
import com.hazelcast.util.function.Consumer; import java.util.function.Consumer;
import org.thingsboard.server.extensions.api.exception.AccessDeniedException; import org.thingsboard.server.extensions.api.exception.AccessDeniedException;
import org.thingsboard.server.extensions.api.exception.EntityNotFoundException; import org.thingsboard.server.extensions.api.exception.EntityNotFoundException;
import org.thingsboard.server.extensions.api.exception.InternalErrorException; import org.thingsboard.server.extensions.api.exception.InternalErrorException;

105
application/src/main/java/org/thingsboard/server/actors/rpc/BasicRpcSessionListener.java

@ -17,30 +17,10 @@ package org.thingsboard.server.actors.rpc;
import akka.actor.ActorRef; import akka.actor.ActorRef;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.util.SerializationUtils;
import org.springframework.util.StringUtils;
import org.thingsboard.server.actors.ActorSystemContext;
import org.thingsboard.server.actors.service.ActorService; import org.thingsboard.server.actors.service.ActorService;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.PluginId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.rpc.ToDeviceRpcRequestBody;
import org.thingsboard.server.common.msg.cluster.ServerAddress;
import org.thingsboard.server.common.msg.cluster.ToAllNodesMsg;
import org.thingsboard.server.common.msg.core.ToDeviceSessionActorMsg;
import org.thingsboard.server.common.msg.device.DeviceToDeviceActorMsg;
import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest;
import org.thingsboard.server.extensions.api.device.ToDeviceActorNotificationMsg;
import org.thingsboard.server.extensions.api.plugins.msg.*;
import org.thingsboard.server.extensions.api.plugins.rpc.PluginRpcMsg;
import org.thingsboard.server.extensions.api.plugins.rpc.RpcMsg;
import org.thingsboard.server.gen.cluster.ClusterAPIProtos; import org.thingsboard.server.gen.cluster.ClusterAPIProtos;
import org.thingsboard.server.service.cluster.rpc.GrpcSession; import org.thingsboard.server.service.cluster.rpc.GrpcSession;
import org.thingsboard.server.service.cluster.rpc.GrpcSessionListener; import org.thingsboard.server.service.cluster.rpc.GrpcSessionListener;
import org.thingsboard.server.service.rpc.ToDeviceRpcRequestActorMsg;
import java.io.Serializable;
import java.util.UUID;
/** /**
* @author Andrew Shvayka * @author Andrew Shvayka
@ -48,15 +28,12 @@ import java.util.UUID;
@Slf4j @Slf4j
public class BasicRpcSessionListener implements GrpcSessionListener { public class BasicRpcSessionListener implements GrpcSessionListener {
public static final String SESSION_RECEIVED_SESSION_ACTOR_MSG = "{} session [{}] received session actor msg {}";
private final ActorSystemContext context;
private final ActorService service; private final ActorService service;
private final ActorRef manager; private final ActorRef manager;
private final ActorRef self; private final ActorRef self;
public BasicRpcSessionListener(ActorSystemContext context, ActorRef manager, ActorRef self) { public BasicRpcSessionListener(ActorService service, ActorRef manager, ActorRef self) {
this.context = context; this.service = service;
this.service = context.getActorService();
this.manager = manager; this.manager = manager;
this.self = self; this.self = self;
} }
@ -76,47 +53,11 @@ public class BasicRpcSessionListener implements GrpcSessionListener {
} }
@Override @Override
public void onToPluginRpcMsg(GrpcSession session, ClusterAPIProtos.ToPluginRpcMessage msg) { public void onReceiveClusterGrpcMsg(GrpcSession session, ClusterAPIProtos.ClusterMessage clusterMessage) {
if (log.isTraceEnabled()) { log.trace("{} Service [{}] received session actor msg {}", getType(session),
log.trace("{} session [{}] received plugin msg {}", getType(session), session.getRemoteServer(), msg); session.getRemoteServer(),
} clusterMessage);
service.onMsg(convert(session.getRemoteServer(), msg)); service.onReceivedMsg(session.getRemoteServer(), clusterMessage);
}
@Override
public void onToDeviceActorRpcMsg(GrpcSession session, ClusterAPIProtos.ToDeviceActorRpcMessage msg) {
log.trace("{} session [{}] received device actor msg {}", getType(session), session.getRemoteServer(), msg);
service.onMsg((DeviceToDeviceActorMsg) deserialize(msg.getData().toByteArray()));
}
@Override
public void onToDeviceActorNotificationRpcMsg(GrpcSession session, ClusterAPIProtos.ToDeviceActorNotificationRpcMessage msg) {
log.trace("{} session [{}] received device actor notification msg {}", getType(session), session.getRemoteServer(), msg);
service.onMsg((ToDeviceActorNotificationMsg) deserialize(msg.getData().toByteArray()));
}
@Override
public void onToDeviceSessionActorRpcMsg(GrpcSession session, ClusterAPIProtos.ToDeviceSessionActorRpcMessage msg) {
log.trace(SESSION_RECEIVED_SESSION_ACTOR_MSG, getType(session), session.getRemoteServer(), msg);
service.onMsg((ToDeviceSessionActorMsg) deserialize(msg.getData().toByteArray()));
}
@Override
public void onToDeviceRpcRequestRpcMsg(GrpcSession session, ClusterAPIProtos.ToDeviceRpcRequestRpcMessage msg) {
log.trace(SESSION_RECEIVED_SESSION_ACTOR_MSG, getType(session), session.getRemoteServer(), msg);
service.onMsg(deserialize(session.getRemoteServer(), msg));
}
@Override
public void onFromDeviceRpcResponseRpcMsg(GrpcSession session, ClusterAPIProtos.ToPluginRpcResponseRpcMessage msg) {
log.trace(SESSION_RECEIVED_SESSION_ACTOR_MSG, getType(session), session.getRemoteServer(), msg);
service.onMsg(deserialize(session.getRemoteServer(), msg));
}
@Override
public void onToAllNodesRpcMessage(GrpcSession session, ClusterAPIProtos.ToAllNodesRpcMessage msg) {
log.trace(SESSION_RECEIVED_SESSION_ACTOR_MSG, getType(session), session.getRemoteServer(), msg);
service.onMsg((ToAllNodesMsg) deserialize(msg.getData().toByteArray()));
} }
@Override @Override
@ -130,37 +71,5 @@ public class BasicRpcSessionListener implements GrpcSessionListener {
return session.isClient() ? "Client" : "Server"; return session.isClient() ? "Client" : "Server";
} }
private static PluginRpcMsg convert(ServerAddress serverAddress, ClusterAPIProtos.ToPluginRpcMessage msg) {
ClusterAPIProtos.PluginAddress address = msg.getAddress();
TenantId tenantId = new TenantId(toUUID(address.getTenantId()));
PluginId pluginId = new PluginId(toUUID(address.getPluginId()));
RpcMsg rpcMsg = new RpcMsg(serverAddress, msg.getClazz(), msg.getData().toByteArray());
return new PluginRpcMsg(tenantId, pluginId, rpcMsg);
}
private static UUID toUUID(ClusterAPIProtos.Uid uid) {
return new UUID(uid.getPluginUuidMsb(), uid.getPluginUuidLsb());
}
private static ToDeviceRpcRequestActorMsg deserialize(ServerAddress serverAddress, ClusterAPIProtos.ToDeviceRpcRequestRpcMessage msg) {
TenantId deviceTenantId = new TenantId(toUUID(msg.getDeviceTenantId()));
DeviceId deviceId = new DeviceId(toUUID(msg.getDeviceId()));
ToDeviceRpcRequestBody requestBody = new ToDeviceRpcRequestBody(msg.getMethod(), msg.getParams());
ToDeviceRpcRequest request = new ToDeviceRpcRequest(toUUID(msg.getMsgId()), deviceTenantId, deviceId, msg.getOneway(), msg.getExpTime(), requestBody);
return new ToDeviceRpcRequestActorMsg(serverAddress, request);
}
private static ToPluginRpcResponseDeviceMsg deserialize(ServerAddress serverAddress, ClusterAPIProtos.ToPluginRpcResponseRpcMessage msg) {
RpcError error = !StringUtils.isEmpty(msg.getError()) ? RpcError.valueOf(msg.getError()) : null;
FromDeviceRpcResponse response = new FromDeviceRpcResponse(toUUID(msg.getMsgId()), msg.getResponse(), error);
return new ToPluginRpcResponseDeviceMsg(null, null, response);
}
@SuppressWarnings("unchecked")
private static <T extends Serializable> T deserialize(byte[] data) {
return (T) SerializationUtils.deserialize(data);
}
} }

2
application/src/main/java/org/thingsboard/server/actors/rpc/RpcBroadcastMsg.java

@ -23,5 +23,5 @@ import org.thingsboard.server.gen.cluster.ClusterAPIProtos;
*/ */
@Data @Data
public final class RpcBroadcastMsg { public final class RpcBroadcastMsg {
private final ClusterAPIProtos.ToRpcServerMessage msg; private final ClusterAPIProtos.ClusterMessage msg;
} }

45
application/src/main/java/org/thingsboard/server/actors/rpc/RpcManagerActor.java

@ -40,7 +40,7 @@ public class RpcManagerActor extends ContextAwareActor {
private final Map<ServerAddress, SessionActorInfo> sessionActors; private final Map<ServerAddress, SessionActorInfo> sessionActors;
private final Map<ServerAddress, Queue<ClusterAPIProtos.ToRpcServerMessage>> pendingMsgs; private final Map<ServerAddress, Queue<ClusterAPIProtos.ClusterMessage>> pendingMsgs;
private final ServerAddress instance; private final ServerAddress instance;
@ -65,8 +65,8 @@ public class RpcManagerActor extends ContextAwareActor {
@Override @Override
public void onReceive(Object msg) throws Exception { public void onReceive(Object msg) throws Exception {
if (msg instanceof RpcSessionTellMsg) { if (msg instanceof ClusterAPIProtos.ClusterMessage) {
onMsg((RpcSessionTellMsg) msg); onMsg((ClusterAPIProtos.ClusterMessage) msg);
} else if (msg instanceof RpcBroadcastMsg) { } else if (msg instanceof RpcBroadcastMsg) {
onMsg((RpcBroadcastMsg) msg); onMsg((RpcBroadcastMsg) msg);
} else if (msg instanceof RpcSessionCreateRequestMsg) { } else if (msg instanceof RpcSessionCreateRequestMsg) {
@ -84,27 +84,32 @@ public class RpcManagerActor extends ContextAwareActor {
private void onMsg(RpcBroadcastMsg msg) { private void onMsg(RpcBroadcastMsg msg) {
log.debug("Forwarding msg to session actors {}", msg); log.debug("Forwarding msg to session actors {}", msg);
sessionActors.keySet().forEach(address -> onMsg(new RpcSessionTellMsg(address, msg.getMsg()))); sessionActors.keySet().forEach(address -> onMsg(msg.getMsg()));
pendingMsgs.values().forEach(queue -> queue.add(msg.getMsg())); pendingMsgs.values().forEach(queue -> queue.add(msg.getMsg()));
} }
private void onMsg(RpcSessionTellMsg msg) { private void onMsg(ClusterAPIProtos.ClusterMessage msg) {
ServerAddress address = msg.getServerAddress(); if (msg.hasServerAddress()) {
SessionActorInfo session = sessionActors.get(address); ServerAddress address = new ServerAddress(msg.getServerAddress().getHost(),
if (session != null) { msg.getServerAddress().getPort());
log.debug("{} Forwarding msg to session actor", address); SessionActorInfo session = sessionActors.get(address);
session.actor.tell(msg, ActorRef.noSender()); if (session != null) {
} else { log.debug("{} Forwarding msg to session actor", address);
log.debug("{} Storing msg to pending queue", address); session.getActor().tell(msg, ActorRef.noSender());
Queue<ClusterAPIProtos.ToRpcServerMessage> queue = pendingMsgs.get(address); } else {
if (queue == null) { log.debug("{} Storing msg to pending queue", address);
queue = new LinkedList<>(); Queue<ClusterAPIProtos.ClusterMessage> queue = pendingMsgs.get(address);
pendingMsgs.put(address, queue); if (queue == null) {
queue = new LinkedList<>();
pendingMsgs.put(new ServerAddress(
msg.getServerAddress().getHost(), msg.getServerAddress().getPort()), queue);
}
queue.add(msg);
} }
queue.add(msg.getMsg()); } else {
logger.warning("Cluster msg doesn't have set Server Address [{}]", msg);
} }
} }
@Override @Override
public void postStop() { public void postStop() {
sessionActors.clear(); sessionActors.clear();
@ -167,10 +172,10 @@ public class RpcManagerActor extends ContextAwareActor {
private void register(ServerAddress remoteAddress, UUID uuid, ActorRef sender) { private void register(ServerAddress remoteAddress, UUID uuid, ActorRef sender) {
sessionActors.put(remoteAddress, new SessionActorInfo(uuid, sender)); sessionActors.put(remoteAddress, new SessionActorInfo(uuid, sender));
log.debug("[{}][{}] Registering session actor.", remoteAddress, uuid); log.debug("[{}][{}] Registering session actor.", remoteAddress, uuid);
Queue<ClusterAPIProtos.ToRpcServerMessage> data = pendingMsgs.remove(remoteAddress); Queue<ClusterAPIProtos.ClusterMessage> data = pendingMsgs.remove(remoteAddress);
if (data != null) { if (data != null) {
log.debug("[{}][{}] Forwarding {} pending messages.", remoteAddress, uuid, data.size()); log.debug("[{}][{}] Forwarding {} pending messages.", remoteAddress, uuid, data.size());
data.forEach(msg -> sender.tell(new RpcSessionTellMsg(remoteAddress, msg), ActorRef.noSender())); data.forEach(msg -> sender.tell(new RpcSessionTellMsg(msg), ActorRef.noSender()));
} else { } else {
log.debug("[{}][{}] No pending messages to forward.", remoteAddress, uuid); log.debug("[{}][{}] No pending messages to forward.", remoteAddress, uuid);
} }

23
application/src/main/java/org/thingsboard/server/actors/rpc/RpcSessionActor.java

@ -32,6 +32,8 @@ import org.thingsboard.server.service.cluster.rpc.GrpcSessionListener;
import java.util.UUID; import java.util.UUID;
import static org.thingsboard.server.gen.cluster.ClusterAPIProtos.MessageType.CONNECT_RPC_MESSAGE;
/** /**
* @author Andrew Shvayka * @author Andrew Shvayka
*/ */
@ -56,15 +58,15 @@ public class RpcSessionActor extends ContextAwareActor {
@Override @Override
public void onReceive(Object msg) throws Exception { public void onReceive(Object msg) throws Exception {
if (msg instanceof RpcSessionTellMsg) { if (msg instanceof ClusterAPIProtos.ClusterMessage) {
tell((RpcSessionTellMsg) msg); tell((ClusterAPIProtos.ClusterMessage) msg);
} else if (msg instanceof RpcSessionCreateRequestMsg) { } else if (msg instanceof RpcSessionCreateRequestMsg) {
initSession((RpcSessionCreateRequestMsg) msg); initSession((RpcSessionCreateRequestMsg) msg);
} }
} }
private void tell(RpcSessionTellMsg msg) { private void tell(ClusterAPIProtos.ClusterMessage msg) {
session.sendMsg(msg.getMsg()); session.sendMsg(msg);
} }
@Override @Override
@ -76,7 +78,7 @@ public class RpcSessionActor extends ContextAwareActor {
private void initSession(RpcSessionCreateRequestMsg msg) { private void initSession(RpcSessionCreateRequestMsg msg) {
log.info("[{}] Initializing session", context().self()); log.info("[{}] Initializing session", context().self());
ServerAddress remoteServer = msg.getRemoteAddress(); ServerAddress remoteServer = msg.getRemoteAddress();
listener = new BasicRpcSessionListener(systemContext, context().parent(), context().self()); listener = new BasicRpcSessionListener(systemContext.getActorService(), context().parent(), context().self());
if (msg.getRemoteAddress() == null) { if (msg.getRemoteAddress() == null) {
// Server session // Server session
session = new GrpcSession(listener); session = new GrpcSession(listener);
@ -91,7 +93,7 @@ public class RpcSessionActor extends ContextAwareActor {
session.initInputStream(); session.initInputStream();
ClusterRpcServiceGrpc.ClusterRpcServiceStub stub = ClusterRpcServiceGrpc.newStub(channel); ClusterRpcServiceGrpc.ClusterRpcServiceStub stub = ClusterRpcServiceGrpc.newStub(channel);
StreamObserver<ClusterAPIProtos.ToRpcServerMessage> outputStream = stub.handlePluginMsgs(session.getInputStream()); StreamObserver<ClusterAPIProtos.ClusterMessage> outputStream = stub.handleMsgs(session.getInputStream());
session.setOutputStream(outputStream); session.setOutputStream(outputStream);
session.initOutputStream(); session.initOutputStream();
@ -115,11 +117,10 @@ public class RpcSessionActor extends ContextAwareActor {
} }
} }
private ClusterAPIProtos.ToRpcServerMessage toConnectMsg() { private ClusterAPIProtos.ClusterMessage toConnectMsg() {
ServerAddress instance = systemContext.getDiscoveryService().getCurrentServer().getServerAddress(); ServerAddress instance = systemContext.getDiscoveryService().getCurrentServer().getServerAddress();
return ClusterAPIProtos.ToRpcServerMessage.newBuilder().setConnectMsg( return ClusterAPIProtos.ClusterMessage.newBuilder().setMessageType(CONNECT_RPC_MESSAGE).setServerAddress(
ClusterAPIProtos.ConnectRpcMessage.newBuilder().setServerAddress( ClusterAPIProtos.ServerAddress.newBuilder().setHost(instance.getHost())
ClusterAPIProtos.ServerAddress.newBuilder().setHost(instance.getHost()).setPort(instance.getPort()).build()).build()).build(); .setPort(instance.getPort()).build()).build();
} }
} }

2
application/src/main/java/org/thingsboard/server/actors/rpc/RpcSessionCreateRequestMsg.java

@ -30,6 +30,6 @@ public final class RpcSessionCreateRequestMsg {
private final UUID msgUid; private final UUID msgUid;
private final ServerAddress remoteAddress; private final ServerAddress remoteAddress;
private final StreamObserver<ClusterAPIProtos.ToRpcServerMessage> responseObserver; private final StreamObserver<ClusterAPIProtos.ClusterMessage> responseObserver;
} }

3
application/src/main/java/org/thingsboard/server/actors/rpc/RpcSessionTellMsg.java

@ -24,6 +24,5 @@ import org.thingsboard.server.gen.cluster.ClusterAPIProtos;
*/ */
@Data @Data
public final class RpcSessionTellMsg { public final class RpcSessionTellMsg {
private final ServerAddress serverAddress; private final ClusterAPIProtos.ClusterMessage msg;
private final ClusterAPIProtos.ToRpcServerMessage msg;
} }

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

@ -43,6 +43,7 @@ import org.thingsboard.server.dao.user.UserService;
import org.thingsboard.server.service.script.NashornJsEngine; import org.thingsboard.server.service.script.NashornJsEngine;
import scala.concurrent.duration.Duration; import scala.concurrent.duration.Duration;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -63,15 +64,24 @@ class DefaultTbContext implements TbContext {
@Override @Override
public void tellNext(TbMsg msg, String relationType) { public void tellNext(TbMsg msg, String relationType) {
tellNext(msg, relationType, null); tellNext(msg, Collections.singleton(relationType), null);
}
@Override
public void tellNext(TbMsg msg, Set<String> relationTypes) {
tellNext(msg, relationTypes, null);
} }
@Override @Override
public void tellNext(TbMsg msg, String relationType, Throwable th) { public void tellNext(TbMsg msg, String relationType, Throwable th) {
tellNext(msg, Collections.singleton(relationType), th);
}
private void tellNext(TbMsg msg, Set<String> relationTypes, Throwable th) {
if (nodeCtx.getSelf().isDebugMode()) { if (nodeCtx.getSelf().isDebugMode()) {
mainCtx.persistDebugOutput(nodeCtx.getTenantId(), nodeCtx.getSelf().getId(), msg, relationType, th); relationTypes.forEach(relationType -> mainCtx.persistDebugOutput(nodeCtx.getTenantId(), nodeCtx.getSelf().getId(), msg, relationType, th));
} }
nodeCtx.getChainActor().tell(new RuleNodeToRuleChainTellNextMsg(nodeCtx.getSelf().getId(), relationType, msg), nodeCtx.getSelfActor()); nodeCtx.getChainActor().tell(new RuleNodeToRuleChainTellNextMsg(nodeCtx.getSelf().getId(), relationTypes, msg), nodeCtx.getSelfActor());
} }
@Override @Override
@ -99,12 +109,12 @@ class DefaultTbContext implements TbContext {
@Override @Override
public TbMsg newMsg(String type, EntityId originator, TbMsgMetaData metaData, String data) { public TbMsg newMsg(String type, EntityId originator, TbMsgMetaData metaData, String data) {
return new TbMsg(UUIDs.timeBased(), type, originator, metaData.copy(), data, nodeCtx.getSelf().getRuleChainId(), nodeCtx.getSelf().getId(), 0L); return new TbMsg(UUIDs.timeBased(), type, originator, metaData.copy(), data, nodeCtx.getSelf().getRuleChainId(), nodeCtx.getSelf().getId(), mainCtx.getQueuePartitionId());
} }
@Override @Override
public TbMsg transformMsg(TbMsg origMsg, String type, EntityId originator, TbMsgMetaData metaData, String data) { public TbMsg transformMsg(TbMsg origMsg, String type, EntityId originator, TbMsgMetaData metaData, String data) {
return new TbMsg(origMsg.getId(), type, originator, metaData.copy(), data, origMsg.getRuleChainId(), origMsg.getRuleNodeId(), 0L); return new TbMsg(origMsg.getId(), type, originator, metaData.copy(), data, origMsg.getRuleChainId(), origMsg.getRuleNodeId(), mainCtx.getQueuePartitionId());
} }
@Override @Override
@ -117,11 +127,6 @@ class DefaultTbContext implements TbContext {
return nodeCtx.getTenantId(); return nodeCtx.getTenantId();
} }
@Override
public void tellNext(TbMsg msg, Set<String> relationTypes) {
relationTypes.forEach(type -> tellNext(msg, type));
}
@Override @Override
public ListeningExecutor getJsExecutor() { public ListeningExecutor getJsExecutor() {
return mainCtx.getJsExecutor(); return mainCtx.getJsExecutor();

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

@ -54,6 +54,8 @@ public class RuleChainActor extends ComponentActor<RuleChainId, RuleChainActorMe
case RULE_CHAIN_TO_RULE_CHAIN_MSG: case RULE_CHAIN_TO_RULE_CHAIN_MSG:
processor.onRuleChainToRuleChainMsg((RuleChainToRuleChainMsg) msg); processor.onRuleChainToRuleChainMsg((RuleChainToRuleChainMsg) msg);
break; break;
case CLUSTER_EVENT_MSG:
break;
default: default:
return false; return false;
} }

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

@ -95,12 +95,12 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
private void reprocess(List<RuleNode> ruleNodeList) { private void reprocess(List<RuleNode> ruleNodeList) {
for (RuleNode ruleNode : ruleNodeList) { for (RuleNode ruleNode : ruleNodeList) {
for (TbMsg tbMsg : queue.findUnprocessed(tenantId, ruleNode.getId().getId(), 0L)) { for (TbMsg tbMsg : queue.findUnprocessed(tenantId, ruleNode.getId().getId(), systemContext.getQueuePartitionId())) {
pushMsgToNode(nodeActors.get(ruleNode.getId()), tbMsg, ""); pushMsgToNode(nodeActors.get(ruleNode.getId()), tbMsg, "");
} }
} }
if (firstNode != null) { if (firstNode != null) {
for (TbMsg tbMsg : queue.findUnprocessed(tenantId, entityId.getId(), 0L)) { for (TbMsg tbMsg : queue.findUnprocessed(tenantId, entityId.getId(), systemContext.getQueuePartitionId())) {
pushMsgToNode(firstNode, tbMsg, ""); pushMsgToNode(firstNode, tbMsg, "");
} }
} }
@ -206,9 +206,8 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
void onTellNext(RuleNodeToRuleChainTellNextMsg envelope) { void onTellNext(RuleNodeToRuleChainTellNextMsg envelope) {
checkActive(); checkActive();
RuleNodeId originator = envelope.getOriginator(); RuleNodeId originator = envelope.getOriginator();
String targetRelationType = envelope.getRelationType();
List<RuleNodeRelation> relations = nodeRoutes.get(originator).stream() List<RuleNodeRelation> relations = nodeRoutes.get(originator).stream()
.filter(r -> targetRelationType == null || targetRelationType.equalsIgnoreCase(r.getType())) .filter(r -> contains(envelope.getRelationTypes(), r.getType()))
.collect(Collectors.toList()); .collect(Collectors.toList());
TbMsg msg = envelope.getMsg(); TbMsg msg = envelope.getMsg();
@ -237,6 +236,18 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
} }
} }
private boolean contains(Set<String> relationTypes, String type) {
if (relationTypes == null) {
return true;
}
for (String relationType : relationTypes) {
if (relationType.equalsIgnoreCase(type)) {
return true;
}
}
return false;
}
private void enqueueAndForwardMsgCopyToChain(TbMsg msg, EntityId target, String fromRelationType) { private void enqueueAndForwardMsgCopyToChain(TbMsg msg, EntityId target, String fromRelationType) {
RuleChainId targetRCId = new RuleChainId(target.getId()); RuleChainId targetRCId = new RuleChainId(target.getId());
TbMsg copyMsg = msg.copy(UUIDs.timeBased(), targetRCId, null, DEFAULT_CLUSTER_PARTITION); TbMsg copyMsg = msg.copy(UUIDs.timeBased(), targetRCId, null, DEFAULT_CLUSTER_PARTITION);
@ -269,6 +280,6 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
private TbMsg enrichWithRuleChainId(TbMsg tbMsg) { private TbMsg enrichWithRuleChainId(TbMsg tbMsg) {
// We don't put firstNodeId because it may change over time; // We don't put firstNodeId because it may change over time;
return new TbMsg(tbMsg.getId(), tbMsg.getType(), tbMsg.getOriginator(), tbMsg.getMetaData().copy(), tbMsg.getData(), entityId, null, 0L); return new TbMsg(tbMsg.getId(), tbMsg.getType(), tbMsg.getOriginator(), tbMsg.getMetaData().copy(), tbMsg.getData(), entityId, null, systemContext.getQueuePartitionId());
} }
} }

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

@ -21,6 +21,8 @@ import org.thingsboard.server.common.msg.MsgType;
import org.thingsboard.server.common.msg.TbActorMsg; import org.thingsboard.server.common.msg.TbActorMsg;
import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsg;
import java.util.Set;
/** /**
* Created by ashvayka on 19.03.18. * Created by ashvayka on 19.03.18.
*/ */
@ -28,7 +30,7 @@ import org.thingsboard.server.common.msg.TbMsg;
final class RuleNodeToRuleChainTellNextMsg implements TbActorMsg { final class RuleNodeToRuleChainTellNextMsg implements TbActorMsg {
private final RuleNodeId originator; private final RuleNodeId originator;
private final String relationType; private final Set<String> relationTypes;
private final TbMsg msg; private final TbMsg msg;
@Override @Override

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

@ -18,6 +18,7 @@ package org.thingsboard.server.actors.service;
import org.thingsboard.server.common.data.id.*; import org.thingsboard.server.common.data.id.*;
import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.cluster.SendToClusterMsg;
import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg; import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg;
import org.thingsboard.server.common.transport.SessionMsgProcessor; import org.thingsboard.server.common.transport.SessionMsgProcessor;
import org.thingsboard.server.service.cluster.discovery.DiscoveryServiceListener; import org.thingsboard.server.service.cluster.discovery.DiscoveryServiceListener;
@ -27,10 +28,11 @@ public interface ActorService extends SessionMsgProcessor, WebSocketMsgProcessor
void onEntityStateChange(TenantId tenantId, EntityId entityId, ComponentLifecycleEvent state); void onEntityStateChange(TenantId tenantId, EntityId entityId, ComponentLifecycleEvent state);
void onMsg(ServiceToRuleEngineMsg msg); void onMsg(SendToClusterMsg msg);
void onCredentialsUpdate(TenantId tenantId, DeviceId deviceId); void onCredentialsUpdate(TenantId tenantId, DeviceId deviceId);
void onDeviceNameOrTypeUpdate(TenantId tenantId, DeviceId deviceId, String deviceName, String deviceType); void onDeviceNameOrTypeUpdate(TenantId tenantId, DeviceId deviceId, String deviceName, String deviceType);
void onMsg(ServiceToRuleEngineMsg serviceToRuleEngineMsg);
} }

8
application/src/main/java/org/thingsboard/server/actors/service/ContextAwareActor.java

@ -39,8 +39,12 @@ public abstract class ContextAwareActor extends UntypedActor {
logger.debug("Processing msg: {}", msg); logger.debug("Processing msg: {}", msg);
} }
if (msg instanceof TbActorMsg) { if (msg instanceof TbActorMsg) {
if(!process((TbActorMsg) msg)){ try {
logger.warning("Unknown message: {}!", msg); if (!process((TbActorMsg) msg)) {
logger.warning("Unknown message: {}!", msg);
}
} catch (Exception e) {
throw e;
} }
} else { } else {
logger.warning("Unknown message: {}!", msg); logger.warning("Unknown message: {}!", msg);

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

@ -19,6 +19,7 @@ import akka.actor.ActorRef;
import akka.actor.ActorSystem; import akka.actor.ActorSystem;
import akka.actor.Props; import akka.actor.Props;
import akka.actor.Terminated; import akka.actor.Terminated;
import com.google.protobuf.ByteString;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -27,25 +28,23 @@ import org.thingsboard.server.actors.app.AppActor;
import org.thingsboard.server.actors.rpc.RpcBroadcastMsg; import org.thingsboard.server.actors.rpc.RpcBroadcastMsg;
import org.thingsboard.server.actors.rpc.RpcManagerActor; import org.thingsboard.server.actors.rpc.RpcManagerActor;
import org.thingsboard.server.actors.rpc.RpcSessionCreateRequestMsg; import org.thingsboard.server.actors.rpc.RpcSessionCreateRequestMsg;
import org.thingsboard.server.actors.rpc.RpcSessionTellMsg;
import org.thingsboard.server.actors.session.SessionManagerActor; import org.thingsboard.server.actors.session.SessionManagerActor;
import org.thingsboard.server.actors.stats.StatsActor; import org.thingsboard.server.actors.stats.StatsActor;
import org.thingsboard.server.common.data.id.*; import org.thingsboard.server.common.data.id.*;
import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
import org.thingsboard.server.common.msg.TbActorMsg;
import org.thingsboard.server.common.msg.aware.SessionAwareMsg; import org.thingsboard.server.common.msg.aware.SessionAwareMsg;
import org.thingsboard.server.common.msg.cluster.ClusterEventMsg; import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;
import org.thingsboard.server.common.msg.cluster.SendToClusterMsg;
import org.thingsboard.server.common.msg.cluster.ServerAddress; import org.thingsboard.server.common.msg.cluster.ServerAddress;
import org.thingsboard.server.common.msg.cluster.ToAllNodesMsg; import org.thingsboard.server.common.msg.cluster.ToAllNodesMsg;
import org.thingsboard.server.common.msg.core.ToDeviceSessionActorMsg;
import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg; import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg;
import org.thingsboard.server.extensions.api.device.DeviceNameOrTypeUpdateMsg; import org.thingsboard.server.extensions.api.device.DeviceNameOrTypeUpdateMsg;
import org.thingsboard.server.common.msg.device.DeviceToDeviceActorMsg;
import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
import org.thingsboard.server.extensions.api.device.DeviceCredentialsUpdateNotificationMsg; import org.thingsboard.server.extensions.api.device.DeviceCredentialsUpdateNotificationMsg;
import org.thingsboard.server.extensions.api.device.ToDeviceActorNotificationMsg;
import org.thingsboard.server.extensions.api.plugins.msg.ToPluginActorMsg;
import org.thingsboard.server.extensions.api.plugins.rest.PluginRestMsg; import org.thingsboard.server.extensions.api.plugins.rest.PluginRestMsg;
import org.thingsboard.server.extensions.api.plugins.ws.msg.PluginWebsocketMsg; import org.thingsboard.server.extensions.api.plugins.ws.msg.PluginWebsocketMsg;
import org.thingsboard.server.gen.cluster.ClusterAPIProtos;
import org.thingsboard.server.service.cluster.discovery.DiscoveryService; import org.thingsboard.server.service.cluster.discovery.DiscoveryService;
import org.thingsboard.server.service.cluster.discovery.ServerInstance; import org.thingsboard.server.service.cluster.discovery.ServerInstance;
import org.thingsboard.server.service.cluster.rpc.ClusterRpcService; import org.thingsboard.server.service.cluster.rpc.ClusterRpcService;
@ -55,7 +54,8 @@ import scala.concurrent.duration.Duration;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy; import javax.annotation.PreDestroy;
import java.util.Optional;
import static org.thingsboard.server.gen.cluster.ClusterAPIProtos.MessageType.CLUSTER_ACTOR_MESSAGE;
@Service @Service
@Slf4j @Slf4j
@ -127,7 +127,7 @@ public class DefaultActorService implements ActorService {
} }
@Override @Override
public void onMsg(ServiceToRuleEngineMsg msg) { public void onMsg(SendToClusterMsg msg) {
appActor.tell(msg, ActorRef.noSender()); appActor.tell(msg, ActorRef.noSender());
} }
@ -149,53 +149,6 @@ public class DefaultActorService implements ActorService {
appActor.tell(msg, ActorRef.noSender()); appActor.tell(msg, ActorRef.noSender());
} }
@Override
public void onMsg(ToPluginActorMsg msg) {
log.trace("Processing plugin rpc msg: {}", msg);
appActor.tell(msg, ActorRef.noSender());
}
@Override
public void onMsg(DeviceToDeviceActorMsg msg) {
log.trace("Processing device rpc msg: {}", msg);
appActor.tell(msg, ActorRef.noSender());
}
@Override
public void onMsg(ToDeviceActorNotificationMsg msg) {
log.trace("Processing notification rpc msg: {}", msg);
appActor.tell(msg, ActorRef.noSender());
}
@Override
public void onMsg(ToDeviceSessionActorMsg msg) {
log.trace("Processing session rpc msg: {}", msg);
sessionManagerActor.tell(msg, ActorRef.noSender());
}
@Override
public void onMsg(ToAllNodesMsg msg) {
log.trace("Processing broadcast rpc msg: {}", msg);
appActor.tell(msg, ActorRef.noSender());
}
@Override
public void onMsg(RpcSessionCreateRequestMsg msg) {
log.trace("Processing session create msg: {}", msg);
rpcManagerActor.tell(msg, ActorRef.noSender());
}
@Override
public void onMsg(RpcSessionTellMsg msg) {
log.trace("Processing session rpc msg: {}", msg);
rpcManagerActor.tell(msg, ActorRef.noSender());
}
@Override
public void onMsg(RpcBroadcastMsg msg) {
log.trace("Processing broadcast rpc msg: {}", msg);
rpcManagerActor.tell(msg, ActorRef.noSender());
}
@Override @Override
public void onServerAdded(ServerInstance server) { public void onServerAdded(ServerInstance server) {
@ -223,28 +176,29 @@ public class DefaultActorService implements ActorService {
@Override @Override
public void onCredentialsUpdate(TenantId tenantId, DeviceId deviceId) { public void onCredentialsUpdate(TenantId tenantId, DeviceId deviceId) {
DeviceCredentialsUpdateNotificationMsg msg = new DeviceCredentialsUpdateNotificationMsg(tenantId, deviceId); DeviceCredentialsUpdateNotificationMsg msg = new DeviceCredentialsUpdateNotificationMsg(tenantId, deviceId);
Optional<ServerAddress> address = actorContext.getRoutingService().resolveById(deviceId); appActor.tell(new SendToClusterMsg(deviceId, msg), ActorRef.noSender());
if (address.isPresent()) {
rpcService.tell(address.get(), msg);
} else {
onMsg(msg);
}
} }
@Override @Override
public void onDeviceNameOrTypeUpdate(TenantId tenantId, DeviceId deviceId, String deviceName, String deviceType) { public void onDeviceNameOrTypeUpdate(TenantId tenantId, DeviceId deviceId, String deviceName, String deviceType) {
log.trace("[{}] Processing onDeviceNameOrTypeUpdate event, deviceName: {}, deviceType: {}", deviceId, deviceName, deviceType); log.trace("[{}] Processing onDeviceNameOrTypeUpdate event, deviceName: {}, deviceType: {}", deviceId, deviceName, deviceType);
DeviceNameOrTypeUpdateMsg msg = new DeviceNameOrTypeUpdateMsg(tenantId, deviceId, deviceName, deviceType); DeviceNameOrTypeUpdateMsg msg = new DeviceNameOrTypeUpdateMsg(tenantId, deviceId, deviceName, deviceType);
Optional<ServerAddress> address = actorContext.getRoutingService().resolveById(deviceId); appActor.tell(new SendToClusterMsg(deviceId, msg), ActorRef.noSender());
if (address.isPresent()) { }
rpcService.tell(address.get(), msg);
} else { @Override
onMsg(msg); public void onMsg(ServiceToRuleEngineMsg msg) {
} appActor.tell(msg, ActorRef.noSender());
} }
public void broadcast(ToAllNodesMsg msg) { public void broadcast(ToAllNodesMsg msg) {
rpcService.broadcast(msg); actorContext.getEncodingService().encode(msg);
rpcService.broadcast(new RpcBroadcastMsg(ClusterAPIProtos.ClusterMessage
.newBuilder()
.setPayload(ByteString
.copyFrom(actorContext.getEncodingService().encode(msg)))
.setMessageType(CLUSTER_ACTOR_MESSAGE)
.build()));
appActor.tell(msg, ActorRef.noSender()); appActor.tell(msg, ActorRef.noSender());
} }
@ -253,4 +207,60 @@ public class DefaultActorService implements ActorService {
this.sessionManagerActor.tell(msg, ActorRef.noSender()); this.sessionManagerActor.tell(msg, ActorRef.noSender());
this.rpcManagerActor.tell(msg, ActorRef.noSender()); this.rpcManagerActor.tell(msg, ActorRef.noSender());
} }
@Override
public void onReceivedMsg(ServerAddress source, ClusterAPIProtos.ClusterMessage msg) {
ServerAddress serverAddress = new ServerAddress(source.getHost(), source.getPort());
log.info("Received msg [{}] from [{}]", msg.getMessageType().name(), serverAddress);
if(log.isDebugEnabled()){
log.info("MSG: ", msg);
}
switch (msg.getMessageType()) {
case CLUSTER_ACTOR_MESSAGE:
java.util.Optional<TbActorMsg> decodedMsg = actorContext.getEncodingService()
.decode(msg.getPayload().toByteArray());
if (decodedMsg.isPresent()) {
appActor.tell(decodedMsg.get(), ActorRef.noSender());
} else {
log.error("Error during decoding cluster proto message");
}
break;
case TO_ALL_NODES_MSG:
//TODO
break;
case CLUSTER_TELEMETRY_SUBSCRIPTION_CREATE_MESSAGE:
actorContext.getTsSubService().onNewRemoteSubscription(serverAddress, msg.getPayload().toByteArray());
break;
case CLUSTER_TELEMETRY_SUBSCRIPTION_UPDATE_MESSAGE:
actorContext.getTsSubService().onRemoteSubscriptionUpdate(serverAddress, msg.getPayload().toByteArray());
break;
case CLUSTER_TELEMETRY_SUBSCRIPTION_CLOSE_MESSAGE:
actorContext.getTsSubService().onRemoteSubscriptionClose(serverAddress, msg.getPayload().toByteArray());
break;
case CLUSTER_TELEMETRY_SESSION_CLOSE_MESSAGE:
actorContext.getTsSubService().onRemoteSessionClose(serverAddress, msg.getPayload().toByteArray());
break;
case CLUSTER_TELEMETRY_ATTR_UPDATE_MESSAGE:
actorContext.getTsSubService().onRemoteAttributesUpdate(serverAddress, msg.getPayload().toByteArray());
break;
case CLUSTER_TELEMETRY_TS_UPDATE_MESSAGE:
actorContext.getTsSubService().onRemoteTsUpdate(serverAddress, msg.getPayload().toByteArray());
break;
}
}
@Override
public void onSendMsg(ClusterAPIProtos.ClusterMessage msg) {
rpcManagerActor.tell(msg, ActorRef.noSender());
}
@Override
public void onRpcSessionCreateRequestMsg(RpcSessionCreateRequestMsg msg) {
rpcManagerActor.tell(msg, ActorRef.noSender());
}
@Override
public void onBroadcastMsg(RpcBroadcastMsg msg) {
rpcManagerActor.tell(msg, ActorRef.noSender());
}
} }

2
application/src/main/java/org/thingsboard/server/actors/session/ASyncMsgProcessor.java

@ -46,7 +46,7 @@ class ASyncMsgProcessor extends AbstractSessionActorMsgProcessor {
} }
@Override @Override
protected void processToDeviceActorMsg(ActorContext ctx, ToDeviceActorSessionMsg msg) { protected void processToDeviceActorMsg(ActorContext ctx, TransportToDeviceSessionActorMsg msg) {
updateSessionCtx(msg, SessionType.ASYNC); updateSessionCtx(msg, SessionType.ASYNC);
if (firstMsg) { if (firstMsg) {
toDeviceMsg(new SessionOpenMsg()).ifPresent(m -> forwardToAppActor(ctx, m)); toDeviceMsg(new SessionOpenMsg()).ifPresent(m -> forwardToAppActor(ctx, m));

22
application/src/main/java/org/thingsboard/server/actors/session/AbstractSessionActorMsgProcessor.java

@ -21,6 +21,7 @@ import org.thingsboard.server.actors.shared.SessionTimeoutMsg;
import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.SessionId; import org.thingsboard.server.common.data.id.SessionId;
import org.thingsboard.server.common.msg.cluster.ClusterEventMsg; import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;
import org.thingsboard.server.common.msg.cluster.SendToClusterMsg;
import org.thingsboard.server.common.msg.cluster.ServerAddress; import org.thingsboard.server.common.msg.cluster.ServerAddress;
import org.thingsboard.server.common.msg.device.BasicDeviceToDeviceActorMsg; import org.thingsboard.server.common.msg.device.BasicDeviceToDeviceActorMsg;
import org.thingsboard.server.common.msg.device.DeviceToDeviceActorMsg; import org.thingsboard.server.common.msg.device.DeviceToDeviceActorMsg;
@ -44,7 +45,7 @@ abstract class AbstractSessionActorMsgProcessor extends AbstractContextAwareMsgP
this.sessionId = sessionId; this.sessionId = sessionId;
} }
protected abstract void processToDeviceActorMsg(ActorContext ctx, ToDeviceActorSessionMsg msg); protected abstract void processToDeviceActorMsg(ActorContext ctx, TransportToDeviceSessionActorMsg msg);
protected abstract void processTimeoutMsg(ActorContext context, SessionTimeoutMsg msg); protected abstract void processTimeoutMsg(ActorContext context, SessionTimeoutMsg msg);
@ -62,12 +63,12 @@ abstract class AbstractSessionActorMsgProcessor extends AbstractContextAwareMsgP
protected void cleanupSession(ActorContext ctx) { protected void cleanupSession(ActorContext ctx) {
} }
protected void updateSessionCtx(ToDeviceActorSessionMsg msg, SessionType type) { protected void updateSessionCtx(TransportToDeviceSessionActorMsg msg, SessionType type) {
sessionCtx = msg.getSessionMsg().getSessionContext(); sessionCtx = msg.getSessionMsg().getSessionContext();
deviceToDeviceActorMsgPrototype = new BasicDeviceToDeviceActorMsg(msg, type); deviceToDeviceActorMsgPrototype = new BasicDeviceToDeviceActorMsg(msg, type);
} }
protected DeviceToDeviceActorMsg toDeviceMsg(ToDeviceActorSessionMsg msg) { protected DeviceToDeviceActorMsg toDeviceMsg(TransportToDeviceSessionActorMsg msg) {
AdaptorToSessionActorMsg adaptorMsg = msg.getSessionMsg(); AdaptorToSessionActorMsg adaptorMsg = msg.getSessionMsg();
return new BasicDeviceToDeviceActorMsg(deviceToDeviceActorMsgPrototype, adaptorMsg.getMsg()); return new BasicDeviceToDeviceActorMsg(deviceToDeviceActorMsgPrototype, adaptorMsg.getMsg());
} }
@ -86,23 +87,20 @@ abstract class AbstractSessionActorMsgProcessor extends AbstractContextAwareMsgP
return address; return address;
} }
protected Optional<ServerAddress> forwardToAppActorIfAdressChanged(ActorContext ctx, DeviceToDeviceActorMsg toForward, Optional<ServerAddress> oldAddress) { protected Optional<ServerAddress> forwardToAppActorIfAddressChanged(ActorContext ctx, DeviceToDeviceActorMsg toForward, Optional<ServerAddress> oldAddress) {
Optional<ServerAddress> newAddress = systemContext.getRoutingService().resolveById(toForward.getDeviceId()); Optional<ServerAddress> newAddress = systemContext.getRoutingService().resolveById(toForward.getDeviceId());
if (!newAddress.equals(oldAddress)) { if (!newAddress.equals(oldAddress)) {
if (newAddress.isPresent()) { getAppActor().tell(new SendToClusterMsg(toForward.getDeviceId(), toForward
systemContext.getRpcService().tell(newAddress.get(), .toOtherAddress(systemContext.getRoutingService().getCurrentServer())), ctx.self());
toForward.toOtherAddress(systemContext.getRoutingService().getCurrentServer()));
} else {
getAppActor().tell(toForward, ctx.self());
}
} }
return newAddress; return newAddress;
} }
protected void forwardToAppActor(ActorContext ctx, DeviceToDeviceActorMsg toForward, Optional<ServerAddress> address) { protected void forwardToAppActor(ActorContext ctx, DeviceToDeviceActorMsg toForward, Optional<ServerAddress> address) {
if (address.isPresent()) { if (address.isPresent()) {
systemContext.getRpcService().tell(address.get(), systemContext.getRpcService().tell(systemContext.getEncodingService().convertToProtoDataMessage(address.get(),
toForward.toOtherAddress(systemContext.getRoutingService().getCurrentServer())); toForward.toOtherAddress(systemContext.getRoutingService().getCurrentServer())));
} else { } else {
getAppActor().tell(toForward, ctx.self()); getAppActor().tell(toForward, ctx.self());
} }

50
application/src/main/java/org/thingsboard/server/actors/session/SessionActor.java

@ -17,7 +17,6 @@ package org.thingsboard.server.actors.session;
import akka.actor.OneForOneStrategy; import akka.actor.OneForOneStrategy;
import akka.actor.SupervisorStrategy; import akka.actor.SupervisorStrategy;
import akka.japi.Function;
import org.thingsboard.server.actors.ActorSystemContext; import org.thingsboard.server.actors.ActorSystemContext;
import org.thingsboard.server.actors.service.ContextAwareActor; import org.thingsboard.server.actors.service.ContextAwareActor;
import org.thingsboard.server.actors.service.ContextBasedCreator; import org.thingsboard.server.actors.service.ContextBasedCreator;
@ -25,8 +24,8 @@ import org.thingsboard.server.actors.shared.SessionTimeoutMsg;
import org.thingsboard.server.common.data.id.SessionId; import org.thingsboard.server.common.data.id.SessionId;
import org.thingsboard.server.common.msg.TbActorMsg; import org.thingsboard.server.common.msg.TbActorMsg;
import org.thingsboard.server.common.msg.cluster.ClusterEventMsg; import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;
import org.thingsboard.server.common.msg.core.ToDeviceSessionActorMsg; import org.thingsboard.server.common.msg.core.ActorSystemToDeviceSessionActorMsg;
import org.thingsboard.server.common.msg.session.ToDeviceActorSessionMsg; import org.thingsboard.server.common.msg.session.TransportToDeviceSessionActorMsg;
import org.thingsboard.server.common.msg.session.SessionCtrlMsg; import org.thingsboard.server.common.msg.session.SessionCtrlMsg;
import org.thingsboard.server.common.msg.session.SessionMsg; import org.thingsboard.server.common.msg.session.SessionMsg;
import org.thingsboard.server.common.msg.session.SessionType; import org.thingsboard.server.common.msg.session.SessionType;
@ -63,38 +62,37 @@ public class SessionActor extends ContextAwareActor {
@Override @Override
protected boolean process(TbActorMsg msg) { protected boolean process(TbActorMsg msg) {
//TODO Move everything here, to work with TbActorMsg switch (msg.getMsgType()) {
return false; case TRANSPORT_TO_DEVICE_SESSION_ACTOR_MSG:
} processTransportToSessionMsg((TransportToDeviceSessionActorMsg) msg);
break;
@Override case ACTOR_SYSTEM_TO_DEVICE_SESSION_ACTOR_MSG:
public void onReceive(Object msg) throws Exception { processActorsToSessionMsg((ActorSystemToDeviceSessionActorMsg) msg);
logger.debug("[{}] Processing: {}.", sessionId, msg); break;
if (msg instanceof ToDeviceActorSessionMsg) { case SESSION_TIMEOUT_MSG:
processDeviceMsg((ToDeviceActorSessionMsg) msg); processTimeoutMsg((SessionTimeoutMsg) msg);
} else if (msg instanceof ToDeviceSessionActorMsg) { break;
processToDeviceMsg((ToDeviceSessionActorMsg) msg); case SESSION_CTRL_MSG:
} else if (msg instanceof SessionTimeoutMsg) { processSessionCloseMsg((SessionCtrlMsg) msg);
processTimeoutMsg((SessionTimeoutMsg) msg); break;
} else if (msg instanceof SessionCtrlMsg) { case CLUSTER_EVENT_MSG:
processSessionCtrlMsg((SessionCtrlMsg) msg); processClusterEvent((ClusterEventMsg) msg);
} else if (msg instanceof ClusterEventMsg) { break;
processClusterEvent((ClusterEventMsg) msg); default: return false;
} else {
logger.warning("[{}] Unknown msg: {}", sessionId, msg);
} }
return true;
} }
private void processClusterEvent(ClusterEventMsg msg) { private void processClusterEvent(ClusterEventMsg msg) {
processor.processClusterEvent(context(), msg); processor.processClusterEvent(context(), msg);
} }
private void processDeviceMsg(ToDeviceActorSessionMsg msg) { private void processTransportToSessionMsg(TransportToDeviceSessionActorMsg msg) {
initProcessor(msg); initProcessor(msg);
processor.processToDeviceActorMsg(context(), msg); processor.processToDeviceActorMsg(context(), msg);
} }
private void processToDeviceMsg(ToDeviceSessionActorMsg msg) { private void processActorsToSessionMsg(ActorSystemToDeviceSessionActorMsg msg) {
processor.processToDeviceMsg(context(), msg.getMsg()); processor.processToDeviceMsg(context(), msg.getMsg());
} }
@ -106,7 +104,7 @@ public class SessionActor extends ContextAwareActor {
} }
} }
private void processSessionCtrlMsg(SessionCtrlMsg msg) { private void processSessionCloseMsg(SessionCtrlMsg msg) {
if (processor != null) { if (processor != null) {
processor.processSessionCtrlMsg(context(), msg); processor.processSessionCtrlMsg(context(), msg);
} else if (msg instanceof SessionCloseMsg) { } else if (msg instanceof SessionCloseMsg) {
@ -116,7 +114,7 @@ public class SessionActor extends ContextAwareActor {
} }
} }
private void initProcessor(ToDeviceActorSessionMsg msg) { private void initProcessor(TransportToDeviceSessionActorMsg msg) {
if (processor == null) { if (processor == null) {
SessionMsg sessionMsg = (SessionMsg) msg.getSessionMsg(); SessionMsg sessionMsg = (SessionMsg) msg.getSessionMsg();
if (sessionMsg.getSessionContext().getSessionType() == SessionType.SYNC) { if (sessionMsg.getSessionContext().getSessionType() == SessionType.SYNC) {

6
application/src/main/java/org/thingsboard/server/actors/session/SessionManagerActor.java

@ -17,7 +17,6 @@ package org.thingsboard.server.actors.session;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.UUID;
import akka.actor.*; import akka.actor.*;
import org.thingsboard.server.actors.ActorSystemContext; import org.thingsboard.server.actors.ActorSystemContext;
@ -33,8 +32,9 @@ import akka.event.Logging;
import akka.event.LoggingAdapter; import akka.event.LoggingAdapter;
import org.thingsboard.server.common.msg.cluster.ClusterEventMsg; import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;
import org.thingsboard.server.common.msg.core.SessionCloseMsg; import org.thingsboard.server.common.msg.core.SessionCloseMsg;
import org.thingsboard.server.common.msg.core.ToDeviceSessionActorMsg; import org.thingsboard.server.common.msg.core.ActorSystemToDeviceSessionActorMsg;
import org.thingsboard.server.common.msg.session.SessionCtrlMsg; import org.thingsboard.server.common.msg.session.SessionCtrlMsg;
import org.thingsboard.server.common.msg.session.TransportToDeviceSessionActorMsg;
public class SessionManagerActor extends ContextAwareActor { public class SessionManagerActor extends ContextAwareActor {
@ -104,7 +104,7 @@ public class SessionManagerActor extends ContextAwareActor {
} }
private void forwardToSessionActor(SessionAwareMsg msg) { private void forwardToSessionActor(SessionAwareMsg msg) {
if (msg instanceof ToDeviceSessionActorMsg || msg instanceof SessionCloseMsg) { if (msg instanceof ActorSystemToDeviceSessionActorMsg || msg instanceof SessionCloseMsg) {
String sessionIdStr = msg.getSessionId().toUidStr(); String sessionIdStr = msg.getSessionId().toUidStr();
ActorRef sessionActor = sessionActors.get(sessionIdStr); ActorRef sessionActor = sessionActors.get(sessionIdStr);
if (sessionActor != null) { if (sessionActor != null) {

6
application/src/main/java/org/thingsboard/server/actors/session/SyncMsgProcessor.java

@ -22,7 +22,7 @@ import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;
import org.thingsboard.server.common.msg.cluster.ServerAddress; import org.thingsboard.server.common.msg.cluster.ServerAddress;
import org.thingsboard.server.common.msg.device.DeviceToDeviceActorMsg; import org.thingsboard.server.common.msg.device.DeviceToDeviceActorMsg;
import org.thingsboard.server.common.msg.session.*; import org.thingsboard.server.common.msg.session.*;
import org.thingsboard.server.common.msg.session.ToDeviceActorSessionMsg; import org.thingsboard.server.common.msg.session.TransportToDeviceSessionActorMsg;
import org.thingsboard.server.common.msg.session.ctrl.SessionCloseMsg; import org.thingsboard.server.common.msg.session.ctrl.SessionCloseMsg;
import org.thingsboard.server.common.msg.session.ex.SessionException; import org.thingsboard.server.common.msg.session.ex.SessionException;
@ -41,7 +41,7 @@ class SyncMsgProcessor extends AbstractSessionActorMsgProcessor {
} }
@Override @Override
protected void processToDeviceActorMsg(ActorContext ctx, ToDeviceActorSessionMsg msg) { protected void processToDeviceActorMsg(ActorContext ctx, TransportToDeviceSessionActorMsg msg) {
updateSessionCtx(msg, SessionType.SYNC); updateSessionCtx(msg, SessionType.SYNC);
pendingMsg = toDeviceMsg(msg); pendingMsg = toDeviceMsg(msg);
pendingResponse = true; pendingResponse = true;
@ -73,7 +73,7 @@ class SyncMsgProcessor extends AbstractSessionActorMsgProcessor {
@Override @Override
public void processClusterEvent(ActorContext context, ClusterEventMsg msg) { public void processClusterEvent(ActorContext context, ClusterEventMsg msg) {
if (pendingResponse) { if (pendingResponse) {
Optional<ServerAddress> newTargetServer = forwardToAppActorIfAdressChanged(context, pendingMsg, currentTargetServer); Optional<ServerAddress> newTargetServer = forwardToAppActorIfAddressChanged(context, pendingMsg, currentTargetServer);
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
if (!newTargetServer.equals(currentTargetServer)) { if (!newTargetServer.equals(currentTargetServer)) {
if (newTargetServer.isPresent()) { if (newTargetServer.isPresent()) {

2
application/src/main/java/org/thingsboard/server/actors/shared/ComponentMsgProcessor.java

@ -89,7 +89,7 @@ public abstract class ComponentMsgProcessor<T extends EntityId> extends Abstract
protected void putToQueue(final TbMsg tbMsg, final Consumer<TbMsg> onSuccess) { protected void putToQueue(final TbMsg tbMsg, final Consumer<TbMsg> onSuccess) {
EntityId entityId = tbMsg.getRuleNodeId() != null ? tbMsg.getRuleNodeId() : tbMsg.getRuleChainId(); EntityId entityId = tbMsg.getRuleNodeId() != null ? tbMsg.getRuleNodeId() : tbMsg.getRuleChainId();
Futures.addCallback(queue.put(this.tenantId, tbMsg, entityId.getId(), 0), new FutureCallback<Void>() { Futures.addCallback(queue.put(this.tenantId, tbMsg, entityId.getId(), tbMsg.getClusterPartition()), new FutureCallback<Void>() {
@Override @Override
public void onSuccess(@Nullable Void result) { public void onSuccess(@Nullable Void result) {
onSuccess.accept(tbMsg); onSuccess.accept(tbMsg);

9
application/src/main/java/org/thingsboard/server/actors/shared/SessionTimeoutMsg.java

@ -17,13 +17,20 @@ package org.thingsboard.server.actors.shared;
import lombok.Data; import lombok.Data;
import org.thingsboard.server.common.data.id.SessionId; import org.thingsboard.server.common.data.id.SessionId;
import org.thingsboard.server.common.msg.MsgType;
import org.thingsboard.server.common.msg.TbActorMsg;
import java.io.Serializable; import java.io.Serializable;
@Data @Data
public class SessionTimeoutMsg implements Serializable { public class SessionTimeoutMsg implements Serializable, TbActorMsg {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private final SessionId sessionId; private final SessionId sessionId;
@Override
public MsgType getMsgType() {
return MsgType.SESSION_TIMEOUT_MSG;
}
} }

13
application/src/main/java/org/thingsboard/server/service/cluster/discovery/ServerInstance.java

@ -20,7 +20,7 @@ import lombok.EqualsAndHashCode;
import lombok.Getter; import lombok.Getter;
import lombok.ToString; import lombok.ToString;
import org.thingsboard.server.common.msg.cluster.ServerAddress; import org.thingsboard.server.common.msg.cluster.ServerAddress;
import org.thingsboard.server.gen.discovery.ServerInstanceProtos.ServerInfo; import org.thingsboard.server.gen.discovery.ServerInstanceProtos;
/** /**
* @author Andrew Shvayka * @author Andrew Shvayka
@ -29,8 +29,6 @@ import org.thingsboard.server.gen.discovery.ServerInstanceProtos.ServerInfo;
@EqualsAndHashCode(exclude = {"serverInfo", "serverAddress"}) @EqualsAndHashCode(exclude = {"serverInfo", "serverAddress"})
public final class ServerInstance implements Comparable<ServerInstance> { public final class ServerInstance implements Comparable<ServerInstance> {
@Getter(AccessLevel.PACKAGE)
private final ServerInfo serverInfo;
@Getter @Getter
private final String host; private final String host;
@Getter @Getter
@ -38,8 +36,13 @@ public final class ServerInstance implements Comparable<ServerInstance> {
@Getter @Getter
private final ServerAddress serverAddress; private final ServerAddress serverAddress;
public ServerInstance(ServerInfo serverInfo) { public ServerInstance(ServerAddress serverAddress) {
this.serverInfo = serverInfo; this.serverAddress = serverAddress;
this.host = serverAddress.getHost();
this.port = serverAddress.getPort();
}
public ServerInstance(ServerInstanceProtos.ServerInfo serverInfo) {
this.host = serverInfo.getHost(); this.host = serverInfo.getHost();
this.port = serverInfo.getPort(); this.port = serverInfo.getPort();
this.serverAddress = new ServerAddress(host, port); this.serverAddress = new ServerAddress(host, port);

26
application/src/main/java/org/thingsboard/server/service/cluster/discovery/ZkDiscoveryService.java

@ -15,8 +15,9 @@
*/ */
package org.thingsboard.server.service.cluster.discovery; package org.thingsboard.server.service.cluster.discovery;
import com.google.protobuf.InvalidProtocolBufferException;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.SerializationException;
import org.apache.commons.lang3.SerializationUtils;
import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.cache.ChildData; import org.apache.curator.framework.recipes.cache.ChildData;
@ -31,15 +32,17 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationListener; import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.thingsboard.server.gen.discovery.ServerInstanceProtos.ServerInfo; import org.thingsboard.server.common.msg.cluster.ServerAddress;
import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService;
import org.thingsboard.server.utils.MiscUtils; import org.thingsboard.server.utils.MiscUtils;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy; import javax.annotation.PreDestroy;
import java.io.IOException;
import java.util.List; import java.util.List;
import java.util.NoSuchElementException;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -67,6 +70,10 @@ public class ZkDiscoveryService implements DiscoveryService, PathChildrenCacheLi
@Autowired @Autowired
private ServerInstanceService serverInstance; private ServerInstanceService serverInstance;
@Autowired
@Lazy
private TelemetrySubscriptionService tsSubService;
private final List<DiscoveryServiceListener> listeners = new CopyOnWriteArrayList<>(); private final List<DiscoveryServiceListener> listeners = new CopyOnWriteArrayList<>();
private CuratorFramework client; private CuratorFramework client;
@ -113,7 +120,7 @@ public class ZkDiscoveryService implements DiscoveryService, PathChildrenCacheLi
log.info("[{}:{}] Creating ZK node for current instance", self.getHost(), self.getPort()); log.info("[{}:{}] Creating ZK node for current instance", self.getHost(), self.getPort());
nodePath = client.create() nodePath = client.create()
.creatingParentsIfNeeded() .creatingParentsIfNeeded()
.withMode(CreateMode.EPHEMERAL_SEQUENTIAL).forPath(zkNodesDir + "/", self.getServerInfo().toByteArray()); .withMode(CreateMode.EPHEMERAL_SEQUENTIAL).forPath(zkNodesDir + "/", SerializationUtils.serialize(self.getServerAddress()));
log.info("[{}:{}] Created ZK node for current instance: {}", self.getHost(), self.getPort(), nodePath); log.info("[{}:{}] Created ZK node for current instance: {}", self.getHost(), self.getPort(), nodePath);
} catch (Exception e) { } catch (Exception e) {
log.error("Failed to create ZK node", e); log.error("Failed to create ZK node", e);
@ -144,8 +151,8 @@ public class ZkDiscoveryService implements DiscoveryService, PathChildrenCacheLi
.filter(cd -> !cd.getPath().equals(nodePath)) .filter(cd -> !cd.getPath().equals(nodePath))
.map(cd -> { .map(cd -> {
try { try {
return new ServerInstance(ServerInfo.parseFrom(cd.getData())); return new ServerInstance( (ServerAddress) SerializationUtils.deserialize(cd.getData()));
} catch (InvalidProtocolBufferException e) { } catch (NoSuchElementException e) {
log.error("Failed to decode ZK node", e); log.error("Failed to decode ZK node", e);
throw new RuntimeException(e); throw new RuntimeException(e);
} }
@ -186,20 +193,23 @@ public class ZkDiscoveryService implements DiscoveryService, PathChildrenCacheLi
} }
ServerInstance instance; ServerInstance instance;
try { try {
instance = new ServerInstance(ServerInfo.parseFrom(data.getData())); ServerAddress serverAddress = SerializationUtils.deserialize(data.getData());
} catch (IOException e) { instance = new ServerInstance(serverAddress);
} catch (SerializationException e) {
log.error("Failed to decode server instance for node {}", data.getPath(), e); log.error("Failed to decode server instance for node {}", data.getPath(), e);
throw e; throw e;
} }
log.info("Processing [{}] event for [{}:{}]", pathChildrenCacheEvent.getType(), instance.getHost(), instance.getPort()); log.info("Processing [{}] event for [{}:{}]", pathChildrenCacheEvent.getType(), instance.getHost(), instance.getPort());
switch (pathChildrenCacheEvent.getType()) { switch (pathChildrenCacheEvent.getType()) {
case CHILD_ADDED: case CHILD_ADDED:
tsSubService.onClusterUpdate();
listeners.forEach(listener -> listener.onServerAdded(instance)); listeners.forEach(listener -> listener.onServerAdded(instance));
break; break;
case CHILD_UPDATED: case CHILD_UPDATED:
listeners.forEach(listener -> listener.onServerUpdated(instance)); listeners.forEach(listener -> listener.onServerUpdated(instance));
break; break;
case CHILD_REMOVED: case CHILD_REMOVED:
tsSubService.onClusterUpdate();
listeners.forEach(listener -> listener.onServerRemoved(instance)); listeners.forEach(listener -> listener.onServerRemoved(instance));
break; break;
default: default:

4
application/src/main/java/org/thingsboard/server/service/cluster/routing/ConsistentClusterRoutingService.java

@ -107,7 +107,7 @@ public class ConsistentClusterRoutingService implements ClusterRoutingService, D
@Override @Override
public void onServerAdded(ServerInstance server) { public void onServerAdded(ServerInstance server) {
log.debug("On server added event: {}", server); log.info("On server added event: {}", server);
addNode(server); addNode(server);
logCircle(); logCircle();
} }
@ -119,7 +119,7 @@ public class ConsistentClusterRoutingService implements ClusterRoutingService, D
@Override @Override
public void onServerRemoved(ServerInstance server) { public void onServerRemoved(ServerInstance server) {
log.debug("On server removed event: {}", server); log.info("On server removed event: {}", server);
removeNode(server); removeNode(server);
logCircle(); logCircle();
} }

182
application/src/main/java/org/thingsboard/server/service/cluster/rpc/ClusterGrpcService.java

@ -22,29 +22,23 @@ import io.grpc.stub.StreamObserver;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.util.SerializationUtils;
import org.thingsboard.server.actors.rpc.RpcBroadcastMsg; import org.thingsboard.server.actors.rpc.RpcBroadcastMsg;
import org.thingsboard.server.actors.rpc.RpcSessionCreateRequestMsg; import org.thingsboard.server.actors.rpc.RpcSessionCreateRequestMsg;
import org.thingsboard.server.actors.rpc.RpcSessionTellMsg; import org.thingsboard.server.common.msg.TbActorMsg;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.msg.cluster.ServerAddress; import org.thingsboard.server.common.msg.cluster.ServerAddress;
import org.thingsboard.server.common.msg.cluster.ToAllNodesMsg;
import org.thingsboard.server.common.msg.core.ToDeviceSessionActorMsg;
import org.thingsboard.server.common.msg.device.DeviceToDeviceActorMsg;
import org.thingsboard.server.extensions.api.device.ToDeviceActorNotificationMsg;
import org.thingsboard.server.extensions.api.plugins.msg.FromDeviceRpcResponse;
import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest;
import org.thingsboard.server.extensions.api.plugins.msg.ToPluginRpcResponseDeviceMsg;
import org.thingsboard.server.extensions.api.plugins.rpc.PluginRpcMsg;
import org.thingsboard.server.gen.cluster.ClusterAPIProtos; import org.thingsboard.server.gen.cluster.ClusterAPIProtos;
import org.thingsboard.server.gen.cluster.ClusterRpcServiceGrpc; import org.thingsboard.server.gen.cluster.ClusterRpcServiceGrpc;
import org.thingsboard.server.service.cluster.discovery.ServerInstance; import org.thingsboard.server.service.cluster.discovery.ServerInstance;
import org.thingsboard.server.service.cluster.discovery.ServerInstanceService; import org.thingsboard.server.service.cluster.discovery.ServerInstanceService;
import org.thingsboard.server.service.rpc.ToDeviceRpcRequestActorMsg; import org.thingsboard.server.service.encoding.DataDecodingEncodingService;
import javax.annotation.PreDestroy; import javax.annotation.PreDestroy;
import java.io.IOException; import java.io.IOException;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
@ -58,13 +52,17 @@ public class ClusterGrpcService extends ClusterRpcServiceGrpc.ClusterRpcServiceI
@Autowired @Autowired
private ServerInstanceService instanceService; private ServerInstanceService instanceService;
@Autowired
private DataDecodingEncodingService encodingService;
private RpcMsgListener listener; private RpcMsgListener listener;
private Server server; private Server server;
private ServerInstance instance; private ServerInstance instance;
private ConcurrentMap<UUID, RpcSessionCreationFuture> pendingSessionMap = new ConcurrentHashMap<>(); private ConcurrentMap<UUID, BlockingQueue<StreamObserver<ClusterAPIProtos.ClusterMessage>>> pendingSessionMap =
new ConcurrentHashMap<>();
public void init(RpcMsgListener listener) { public void init(RpcMsgListener listener) {
this.listener = listener; this.listener = listener;
@ -82,11 +80,11 @@ public class ClusterGrpcService extends ClusterRpcServiceGrpc.ClusterRpcServiceI
} }
@Override @Override
public void onSessionCreated(UUID msgUid, StreamObserver<ClusterAPIProtos.ToRpcServerMessage> msg) { public void onSessionCreated(UUID msgUid, StreamObserver<ClusterAPIProtos.ClusterMessage> inputStream) {
RpcSessionCreationFuture future = pendingSessionMap.remove(msgUid); BlockingQueue<StreamObserver<ClusterAPIProtos.ClusterMessage>> queue = pendingSessionMap.remove(msgUid);
if (future != null) { if (queue != null) {
try { try {
future.onMsg(msg); queue.put(inputStream);
} catch (InterruptedException e) { } catch (InterruptedException e) {
log.warn("Failed to report created session!"); log.warn("Failed to report created session!");
Thread.currentThread().interrupt(); Thread.currentThread().interrupt();
@ -97,11 +95,13 @@ public class ClusterGrpcService extends ClusterRpcServiceGrpc.ClusterRpcServiceI
} }
@Override @Override
public StreamObserver<ClusterAPIProtos.ToRpcServerMessage> handlePluginMsgs(StreamObserver<ClusterAPIProtos.ToRpcServerMessage> responseObserver) { public StreamObserver<ClusterAPIProtos.ClusterMessage> handleMsgs(
StreamObserver<ClusterAPIProtos.ClusterMessage> responseObserver) {
log.info("Processing new session."); log.info("Processing new session.");
return createSession(new RpcSessionCreateRequestMsg(UUID.randomUUID(), null, responseObserver)); return createSession(new RpcSessionCreateRequestMsg(UUID.randomUUID(), null, responseObserver));
} }
@PreDestroy @PreDestroy
public void stop() { public void stop() {
if (server != null) { if (server != null) {
@ -117,65 +117,18 @@ public class ClusterGrpcService extends ClusterRpcServiceGrpc.ClusterRpcServiceI
} }
} }
@Override
public void tell(ServerAddress serverAddress, DeviceToDeviceActorMsg toForward) {
ClusterAPIProtos.ToRpcServerMessage msg = ClusterAPIProtos.ToRpcServerMessage.newBuilder()
.setToDeviceActorRpcMsg(toProtoMsg(toForward)).build();
tell(serverAddress, msg);
}
@Override
public void tell(ServerAddress serverAddress, ToDeviceActorNotificationMsg toForward) {
ClusterAPIProtos.ToRpcServerMessage msg = ClusterAPIProtos.ToRpcServerMessage.newBuilder()
.setToDeviceActorNotificationRpcMsg(toProtoMsg(toForward)).build();
tell(serverAddress, msg);
}
@Override @Override
public void tell(ServerAddress serverAddress, ToDeviceRpcRequestActorMsg toForward) { public void broadcast(RpcBroadcastMsg msg) {
ClusterAPIProtos.ToRpcServerMessage msg = ClusterAPIProtos.ToRpcServerMessage.newBuilder() listener.onBroadcastMsg(msg);
.setToDeviceRpcRequestRpcMsg(toProtoMsg(toForward)).build();
tell(serverAddress, msg);
}
@Override
public void tell(ServerAddress serverAddress, ToPluginRpcResponseDeviceMsg toForward) {
ClusterAPIProtos.ToRpcServerMessage msg = ClusterAPIProtos.ToRpcServerMessage.newBuilder()
.setToPluginRpcResponseRpcMsg(toProtoMsg(toForward)).build();
tell(serverAddress, msg);
}
@Override
public void tell(ServerAddress serverAddress, ToDeviceSessionActorMsg toForward) {
ClusterAPIProtos.ToRpcServerMessage msg = ClusterAPIProtos.ToRpcServerMessage.newBuilder()
.setToDeviceSessionActorRpcMsg(toProtoMsg(toForward)).build();
tell(serverAddress, msg);
}
@Override
public void tell(PluginRpcMsg toForward) {
ClusterAPIProtos.ToRpcServerMessage msg = ClusterAPIProtos.ToRpcServerMessage.newBuilder()
.setToPluginRpcMsg(toProtoMsg(toForward)).build();
tell(toForward.getRpcMsg().getServerAddress(), msg);
}
@Override
public void broadcast(ToAllNodesMsg toForward) {
ClusterAPIProtos.ToRpcServerMessage msg = ClusterAPIProtos.ToRpcServerMessage.newBuilder()
.setToAllNodesRpcMsg(toProtoMsg(toForward)).build();
listener.onMsg(new RpcBroadcastMsg(msg));
}
private void tell(ServerAddress serverAddress, ClusterAPIProtos.ToRpcServerMessage msg) {
listener.onMsg(new RpcSessionTellMsg(serverAddress, msg));
} }
private StreamObserver<ClusterAPIProtos.ToRpcServerMessage> createSession(RpcSessionCreateRequestMsg msg) { private StreamObserver<ClusterAPIProtos.ClusterMessage> createSession(RpcSessionCreateRequestMsg msg) {
RpcSessionCreationFuture future = new RpcSessionCreationFuture(); BlockingQueue<StreamObserver<ClusterAPIProtos.ClusterMessage>> queue = new ArrayBlockingQueue<>(1);
pendingSessionMap.put(msg.getMsgUid(), future); pendingSessionMap.put(msg.getMsgUid(), queue);
listener.onMsg(msg); listener.onRpcSessionCreateRequestMsg(msg);
try { try {
StreamObserver<ClusterAPIProtos.ToRpcServerMessage> observer = future.get(); StreamObserver<ClusterAPIProtos.ClusterMessage> observer = queue.take();
log.info("Processed new session."); log.info("Processed new session.");
return observer; return observer;
} catch (Exception e) { } catch (Exception e) {
@ -184,76 +137,27 @@ public class ClusterGrpcService extends ClusterRpcServiceGrpc.ClusterRpcServiceI
} }
} }
private static ClusterAPIProtos.ToDeviceActorRpcMessage toProtoMsg(DeviceToDeviceActorMsg msg) { @Override
return ClusterAPIProtos.ToDeviceActorRpcMessage.newBuilder().setData( public void tell(ClusterAPIProtos.ClusterMessage message) {
ByteString.copyFrom(SerializationUtils.serialize(msg)) listener.onSendMsg(message);
).build();
}
private static ClusterAPIProtos.ToDeviceActorNotificationRpcMessage toProtoMsg(ToDeviceActorNotificationMsg msg) {
return ClusterAPIProtos.ToDeviceActorNotificationRpcMessage.newBuilder().setData(
ByteString.copyFrom(SerializationUtils.serialize(msg))
).build();
}
private static ClusterAPIProtos.ToDeviceRpcRequestRpcMessage toProtoMsg(ToDeviceRpcRequestActorMsg msg) {
ClusterAPIProtos.ToDeviceRpcRequestRpcMessage.Builder builder = ClusterAPIProtos.ToDeviceRpcRequestRpcMessage.newBuilder();
ToDeviceRpcRequest request = msg.getMsg();
builder.setDeviceTenantId(toUid(msg.getTenantId()));
builder.setDeviceId(toUid(msg.getDeviceId()));
builder.setMsgId(toUid(request.getId()));
builder.setOneway(request.isOneway());
builder.setExpTime(request.getExpirationTime());
builder.setMethod(request.getBody().getMethod());
builder.setParams(request.getBody().getParams());
return builder.build();
}
private static ClusterAPIProtos.ToPluginRpcResponseRpcMessage toProtoMsg(ToPluginRpcResponseDeviceMsg msg) {
ClusterAPIProtos.ToPluginRpcResponseRpcMessage.Builder builder = ClusterAPIProtos.ToPluginRpcResponseRpcMessage.newBuilder();
FromDeviceRpcResponse request = msg.getResponse();
builder.setMsgId(toUid(request.getId()));
request.getResponse().ifPresent(builder::setResponse);
request.getError().ifPresent(e -> builder.setError(e.name()));
return builder.build();
}
private ClusterAPIProtos.ToAllNodesRpcMessage toProtoMsg(ToAllNodesMsg msg) {
return ClusterAPIProtos.ToAllNodesRpcMessage.newBuilder().setData(
ByteString.copyFrom(SerializationUtils.serialize(msg))
).build();
}
private ClusterAPIProtos.ToPluginRpcMessage toProtoMsg(PluginRpcMsg msg) {
return ClusterAPIProtos.ToPluginRpcMessage.newBuilder()
.setClazz(msg.getRpcMsg().getMsgClazz())
.setData(ByteString.copyFrom(msg.getRpcMsg().getMsgData()))
.setAddress(ClusterAPIProtos.PluginAddress.newBuilder()
.setTenantId(toUid(msg.getPluginTenantId().getId()))
.setPluginId(toUid(msg.getPluginId().getId()))
.build()
).build();
}
private static ClusterAPIProtos.Uid toUid(EntityId uuid) {
return toUid(uuid.getId());
} }
private static ClusterAPIProtos.Uid toUid(UUID uuid) { @Override
return ClusterAPIProtos.Uid.newBuilder().setPluginUuidMsb(uuid.getMostSignificantBits()).setPluginUuidLsb( public void tell(ServerAddress serverAddress, TbActorMsg actorMsg) {
uuid.getLeastSignificantBits()).build(); listener.onSendMsg(encodingService.convertToProtoDataMessage(serverAddress, actorMsg));
} }
private static ClusterAPIProtos.ToDeviceSessionActorRpcMessage toProtoMsg(ToDeviceSessionActorMsg msg) { @Override
return ClusterAPIProtos.ToDeviceSessionActorRpcMessage.newBuilder().setData( public void tell(ServerAddress serverAddress, ClusterAPIProtos.MessageType msgType, byte[] data) {
ByteString.copyFrom(SerializationUtils.serialize(msg)) ClusterAPIProtos.ClusterMessage msg = ClusterAPIProtos.ClusterMessage
).build(); .newBuilder()
.setServerAddress(ClusterAPIProtos.ServerAddress
.newBuilder()
.setHost(serverAddress.getHost())
.setPort(serverAddress.getPort())
.build())
.setMessageType(msgType)
.setPayload(ByteString.copyFrom(data)).build();
listener.onSendMsg(msg);
} }
} }

26
application/src/main/java/org/thingsboard/server/service/cluster/rpc/ClusterRpcService.java

@ -16,15 +16,10 @@
package org.thingsboard.server.service.cluster.rpc; package org.thingsboard.server.service.cluster.rpc;
import io.grpc.stub.StreamObserver; import io.grpc.stub.StreamObserver;
import org.thingsboard.server.actors.rpc.RpcBroadcastMsg;
import org.thingsboard.server.common.msg.TbActorMsg;
import org.thingsboard.server.common.msg.cluster.ServerAddress; import org.thingsboard.server.common.msg.cluster.ServerAddress;
import org.thingsboard.server.common.msg.cluster.ToAllNodesMsg;
import org.thingsboard.server.common.msg.core.ToDeviceSessionActorMsg;
import org.thingsboard.server.common.msg.device.DeviceToDeviceActorMsg;
import org.thingsboard.server.extensions.api.device.ToDeviceActorNotificationMsg;
import org.thingsboard.server.extensions.api.plugins.msg.ToPluginRpcResponseDeviceMsg;
import org.thingsboard.server.extensions.api.plugins.rpc.PluginRpcMsg;
import org.thingsboard.server.gen.cluster.ClusterAPIProtos; import org.thingsboard.server.gen.cluster.ClusterAPIProtos;
import org.thingsboard.server.service.rpc.ToDeviceRpcRequestActorMsg;
import java.util.UUID; import java.util.UUID;
@ -35,20 +30,13 @@ public interface ClusterRpcService {
void init(RpcMsgListener listener); void init(RpcMsgListener listener);
void tell(ServerAddress serverAddress, DeviceToDeviceActorMsg toForward); void broadcast(RpcBroadcastMsg msg);
void tell(ServerAddress serverAddress, ToDeviceSessionActorMsg toForward); void onSessionCreated(UUID msgUid, StreamObserver<ClusterAPIProtos.ClusterMessage> inputStream);
void tell(ServerAddress serverAddress, ToDeviceActorNotificationMsg toForward); void tell(ClusterAPIProtos.ClusterMessage message);
void tell(ServerAddress serverAddress, ToDeviceRpcRequestActorMsg toForward); void tell(ServerAddress serverAddress, TbActorMsg actorMsg);
void tell(ServerAddress serverAddress, ToPluginRpcResponseDeviceMsg toForward);
void tell(PluginRpcMsg toForward);
void broadcast(ToAllNodesMsg msg);
void onSessionCreated(UUID msgUid, StreamObserver<ClusterAPIProtos.ToRpcServerMessage> inputStream);
void tell(ServerAddress serverAddress, ClusterAPIProtos.MessageType msgType, byte[] data);
} }

40
application/src/main/java/org/thingsboard/server/service/cluster/rpc/GrpcSession.java

@ -33,8 +33,8 @@ public final class GrpcSession implements Closeable {
private final UUID sessionId; private final UUID sessionId;
private final boolean client; private final boolean client;
private final GrpcSessionListener listener; private final GrpcSessionListener listener;
private StreamObserver<ClusterAPIProtos.ToRpcServerMessage> inputStream; private StreamObserver<ClusterAPIProtos.ClusterMessage> inputStream;
private StreamObserver<ClusterAPIProtos.ToRpcServerMessage> outputStream; private StreamObserver<ClusterAPIProtos.ClusterMessage> outputStream;
private boolean connected; private boolean connected;
private ServerAddress remoteServer; private ServerAddress remoteServer;
@ -56,17 +56,17 @@ public final class GrpcSession implements Closeable {
} }
public void initInputStream() { public void initInputStream() {
this.inputStream = new StreamObserver<ClusterAPIProtos.ToRpcServerMessage>() { this.inputStream = new StreamObserver<ClusterAPIProtos.ClusterMessage>() {
@Override @Override
public void onNext(ClusterAPIProtos.ToRpcServerMessage msg) { public void onNext(ClusterAPIProtos.ClusterMessage clusterMessage) {
if (!connected && msg.hasConnectMsg()) { if (!connected && clusterMessage.getMessageType() == ClusterAPIProtos.MessageType.CONNECT_RPC_MESSAGE) {
connected = true; connected = true;
ClusterAPIProtos.ServerAddress rpcAddress = msg.getConnectMsg().getServerAddress(); ServerAddress rpcAddress = new ServerAddress(clusterMessage.getServerAddress().getHost(), clusterMessage.getServerAddress().getPort());
remoteServer = new ServerAddress(rpcAddress.getHost(), rpcAddress.getPort()); remoteServer = new ServerAddress(rpcAddress.getHost(), rpcAddress.getPort());
listener.onConnected(GrpcSession.this); listener.onConnected(GrpcSession.this);
} }
if (connected) { if (connected) {
handleToRpcServerMessage(msg); listener.onReceiveClusterGrpcMsg(GrpcSession.this, clusterMessage);
} }
} }
@ -83,37 +83,13 @@ public final class GrpcSession implements Closeable {
}; };
} }
private void handleToRpcServerMessage(ClusterAPIProtos.ToRpcServerMessage msg) {
if (msg.hasToPluginRpcMsg()) {
listener.onToPluginRpcMsg(GrpcSession.this, msg.getToPluginRpcMsg());
}
if (msg.hasToDeviceActorRpcMsg()) {
listener.onToDeviceActorRpcMsg(GrpcSession.this, msg.getToDeviceActorRpcMsg());
}
if (msg.hasToDeviceSessionActorRpcMsg()) {
listener.onToDeviceSessionActorRpcMsg(GrpcSession.this, msg.getToDeviceSessionActorRpcMsg());
}
if (msg.hasToDeviceActorNotificationRpcMsg()) {
listener.onToDeviceActorNotificationRpcMsg(GrpcSession.this, msg.getToDeviceActorNotificationRpcMsg());
}
if (msg.hasToDeviceRpcRequestRpcMsg()) {
listener.onToDeviceRpcRequestRpcMsg(GrpcSession.this, msg.getToDeviceRpcRequestRpcMsg());
}
if (msg.hasToPluginRpcResponseRpcMsg()) {
listener.onFromDeviceRpcResponseRpcMsg(GrpcSession.this, msg.getToPluginRpcResponseRpcMsg());
}
if (msg.hasToAllNodesRpcMsg()) {
listener.onToAllNodesRpcMessage(GrpcSession.this, msg.getToAllNodesRpcMsg());
}
}
public void initOutputStream() { public void initOutputStream() {
if (client) { if (client) {
listener.onConnected(GrpcSession.this); listener.onConnected(GrpcSession.this);
} }
} }
public void sendMsg(ClusterAPIProtos.ToRpcServerMessage msg) { public void sendMsg(ClusterAPIProtos.ClusterMessage msg) {
outputStream.onNext(msg); outputStream.onNext(msg);
} }

15
application/src/main/java/org/thingsboard/server/service/cluster/rpc/GrpcSessionListener.java

@ -26,20 +26,7 @@ public interface GrpcSessionListener {
void onDisconnected(GrpcSession session); void onDisconnected(GrpcSession session);
void onToPluginRpcMsg(GrpcSession session, ClusterAPIProtos.ToPluginRpcMessage msg); void onReceiveClusterGrpcMsg(GrpcSession session, ClusterAPIProtos.ClusterMessage clusterMessage);
void onToDeviceActorRpcMsg(GrpcSession session, ClusterAPIProtos.ToDeviceActorRpcMessage msg);
void onToDeviceActorNotificationRpcMsg(GrpcSession grpcSession, ClusterAPIProtos.ToDeviceActorNotificationRpcMessage msg);
void onToDeviceSessionActorRpcMsg(GrpcSession session, ClusterAPIProtos.ToDeviceSessionActorRpcMessage msg);
void onToAllNodesRpcMessage(GrpcSession grpcSession, ClusterAPIProtos.ToAllNodesRpcMessage toAllNodesRpcMessage);
void onToDeviceRpcRequestRpcMsg(GrpcSession grpcSession, ClusterAPIProtos.ToDeviceRpcRequestRpcMessage toDeviceRpcRequestRpcMsg);
void onFromDeviceRpcResponseRpcMsg(GrpcSession grpcSession, ClusterAPIProtos.ToPluginRpcResponseRpcMessage toPluginRpcResponseRpcMsg);
void onError(GrpcSession session, Throwable t); void onError(GrpcSession session, Throwable t);
} }

30
application/src/main/java/org/thingsboard/server/service/cluster/rpc/RpcMsgListener.java

@ -17,32 +17,16 @@ package org.thingsboard.server.service.cluster.rpc;
import org.thingsboard.server.actors.rpc.RpcBroadcastMsg; import org.thingsboard.server.actors.rpc.RpcBroadcastMsg;
import org.thingsboard.server.actors.rpc.RpcSessionCreateRequestMsg; import org.thingsboard.server.actors.rpc.RpcSessionCreateRequestMsg;
import org.thingsboard.server.actors.rpc.RpcSessionTellMsg; import org.thingsboard.server.common.msg.cluster.ServerAddress;
import org.thingsboard.server.common.msg.cluster.ToAllNodesMsg; import org.thingsboard.server.gen.cluster.ClusterAPIProtos;
import org.thingsboard.server.common.msg.core.ToDeviceSessionActorMsg;
import org.thingsboard.server.common.msg.device.DeviceToDeviceActorMsg;
import org.thingsboard.server.extensions.api.device.ToDeviceActorNotificationMsg;
import org.thingsboard.server.extensions.api.plugins.msg.ToPluginActorMsg;
/** /**
* @author Andrew Shvayka * @author Andrew Shvayka
*/ */
public interface RpcMsgListener {
void onMsg(DeviceToDeviceActorMsg msg);
void onMsg(ToDeviceActorNotificationMsg msg);
void onMsg(ToDeviceSessionActorMsg msg);
void onMsg(ToAllNodesMsg nodeMsg);
void onMsg(ToPluginActorMsg msg);
void onMsg(RpcSessionCreateRequestMsg msg);
void onMsg(RpcSessionTellMsg rpcSessionTellMsg);
void onMsg(RpcBroadcastMsg rpcBroadcastMsg);
public interface RpcMsgListener {
void onReceivedMsg(ServerAddress remoteServer, ClusterAPIProtos.ClusterMessage msg);
void onSendMsg(ClusterAPIProtos.ClusterMessage msg);
void onRpcSessionCreateRequestMsg(RpcSessionCreateRequestMsg msg);
void onBroadcastMsg(RpcBroadcastMsg msg);
} }

63
application/src/main/java/org/thingsboard/server/service/cluster/rpc/RpcSessionCreationFuture.java

@ -1,63 +0,0 @@
/**
* Copyright © 2016-2018 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.cluster.rpc;
import io.grpc.stub.StreamObserver;
import org.thingsboard.server.gen.cluster.ClusterAPIProtos;
import java.util.concurrent.*;
/**
* @author Andrew Shvayka
*/
public class RpcSessionCreationFuture implements Future<StreamObserver<ClusterAPIProtos.ToRpcServerMessage>> {
private final BlockingQueue<StreamObserver<ClusterAPIProtos.ToRpcServerMessage>> queue = new ArrayBlockingQueue<>(1);
public void onMsg(StreamObserver<ClusterAPIProtos.ToRpcServerMessage> result) throws InterruptedException {
queue.put(result);
}
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
return false;
}
@Override
public boolean isCancelled() {
return false;
}
@Override
public boolean isDone() {
return false;
}
@Override
public StreamObserver<ClusterAPIProtos.ToRpcServerMessage> get() throws InterruptedException, ExecutionException {
return this.queue.take();
}
@Override
public StreamObserver<ClusterAPIProtos.ToRpcServerMessage> get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
StreamObserver<ClusterAPIProtos.ToRpcServerMessage> result = this.queue.poll(timeout, unit);
if (result == null) {
throw new TimeoutException();
} else {
return result;
}
}
}

34
application/src/main/java/org/thingsboard/server/service/encoding/DataDecodingEncodingService.java

@ -0,0 +1,34 @@
/**
* Copyright © 2016-2018 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.encoding;
import org.thingsboard.server.common.msg.TbActorMsg;
import org.thingsboard.server.common.msg.cluster.ServerAddress;
import org.thingsboard.server.gen.cluster.ClusterAPIProtos;
import java.util.Optional;
public interface DataDecodingEncodingService {
Optional<TbActorMsg> decode(byte[] byteArray);
byte[] encode(TbActorMsg msq);
ClusterAPIProtos.ClusterMessage convertToProtoDataMessage(ServerAddress serverAddress,
TbActorMsg msg);
}

67
application/src/main/java/org/thingsboard/server/service/encoding/ProtoWithJavaSerializationDecodingEncodingService.java

@ -0,0 +1,67 @@
/**
* Copyright © 2016-2018 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.encoding;
import com.google.protobuf.ByteString;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.SerializationUtils;
import org.thingsboard.server.common.msg.TbActorMsg;
import org.thingsboard.server.common.msg.cluster.ServerAddress;
import org.thingsboard.server.gen.cluster.ClusterAPIProtos;
import java.util.Optional;
import static org.thingsboard.server.gen.cluster.ClusterAPIProtos.MessageType.CLUSTER_ACTOR_MESSAGE;
@Slf4j
@Service
public class ProtoWithJavaSerializationDecodingEncodingService implements DataDecodingEncodingService {
@Override
public Optional<TbActorMsg> decode(byte[] byteArray) {
try {
TbActorMsg msg = (TbActorMsg) SerializationUtils.deserialize(byteArray);
return Optional.of(msg);
} catch (IllegalArgumentException e) {
log.error("Error during deserialization message, [{}]", e.getMessage());
return Optional.empty();
}
}
@Override
public byte[] encode(TbActorMsg msq) {
return SerializationUtils.serialize(msq);
}
@Override
public ClusterAPIProtos.ClusterMessage convertToProtoDataMessage(ServerAddress serverAddress,
TbActorMsg msg) {
return ClusterAPIProtos.ClusterMessage
.newBuilder()
.setServerAddress(ClusterAPIProtos.ServerAddress
.newBuilder()
.setHost(serverAddress.getHost())
.setPort(serverAddress.getPort())
.build())
.setMessageType(CLUSTER_ACTOR_MESSAGE)
.setPayload(ByteString.copyFrom(encode(msg))).build();
}
}

18
application/src/main/java/org/thingsboard/server/service/rpc/DefaultDeviceRpcService.java

@ -30,6 +30,8 @@ import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.UUIDBased; import org.thingsboard.server.common.data.id.UUIDBased;
import org.thingsboard.server.common.data.rpc.ToDeviceRpcRequestBody; import org.thingsboard.server.common.data.rpc.ToDeviceRpcRequestBody;
import org.thingsboard.server.common.msg.TbActorMsg;
import org.thingsboard.server.common.msg.cluster.SendToClusterMsg;
import org.thingsboard.server.common.msg.cluster.ServerAddress; import org.thingsboard.server.common.msg.cluster.ServerAddress;
import org.thingsboard.server.common.msg.core.ToServerRpcResponseMsg; import org.thingsboard.server.common.msg.core.ToServerRpcResponseMsg;
import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest; import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest;
@ -38,6 +40,7 @@ import org.thingsboard.server.dao.audit.AuditLogService;
import org.thingsboard.server.extensions.api.device.ToDeviceActorNotificationMsg; import org.thingsboard.server.extensions.api.device.ToDeviceActorNotificationMsg;
import org.thingsboard.server.extensions.api.plugins.msg.FromDeviceRpcResponse; import org.thingsboard.server.extensions.api.plugins.msg.FromDeviceRpcResponse;
import org.thingsboard.server.extensions.api.plugins.msg.RpcError; import org.thingsboard.server.extensions.api.plugins.msg.RpcError;
import org.thingsboard.server.gen.cluster.ClusterAPIProtos;
import org.thingsboard.server.service.cluster.routing.ClusterRoutingService; import org.thingsboard.server.service.cluster.routing.ClusterRoutingService;
import org.thingsboard.server.service.cluster.rpc.ClusterRpcService; import org.thingsboard.server.service.cluster.rpc.ClusterRpcService;
import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.model.SecurityUser;
@ -135,23 +138,16 @@ public class DefaultDeviceRpcService implements DeviceRpcService {
@Override @Override
public void sendRpcReplyToDevice(TenantId tenantId, DeviceId deviceId, int requestId, String body) { public void sendRpcReplyToDevice(TenantId tenantId, DeviceId deviceId, int requestId, String body) {
ToServerRpcResponseActorMsg rpcMsg = new ToServerRpcResponseActorMsg(tenantId, deviceId, new ToServerRpcResponseMsg(requestId, body)); ToServerRpcResponseActorMsg rpcMsg = new ToServerRpcResponseActorMsg(tenantId, deviceId, new ToServerRpcResponseMsg(requestId, body));
forward(deviceId, rpcMsg, rpcService::tell); forward(deviceId, rpcMsg);
} }
private void sendRpcRequest(ToDeviceRpcRequest msg) { private void sendRpcRequest(ToDeviceRpcRequest msg) {
log.trace("[{}] Forwarding msg {} to device actor!", msg.getDeviceId(), msg); log.trace("[{}] Forwarding msg {} to device actor!", msg.getDeviceId(), msg);
ToDeviceRpcRequestActorMsg rpcMsg = new ToDeviceRpcRequestActorMsg(msg); ToDeviceRpcRequestActorMsg rpcMsg = new ToDeviceRpcRequestActorMsg(msg);
forward(msg.getDeviceId(), rpcMsg, rpcService::tell); forward(msg.getDeviceId(), rpcMsg);
} }
private <T extends ToDeviceActorNotificationMsg> void forward(DeviceId deviceId, T msg, BiConsumer<ServerAddress, T> rpcFunction) { private <T extends ToDeviceActorNotificationMsg> void forward(DeviceId deviceId, T msg) {
Optional<ServerAddress> instance = routingService.resolveById(deviceId); actorService.onMsg(new SendToClusterMsg(deviceId, msg));
if (instance.isPresent()) {
log.trace("[{}] Forwarding msg {} to remote device actor!", msg.getTenantId(), msg);
rpcFunction.accept(instance.get(), msg);
} else {
log.trace("[{}] Forwarding msg {} to local device actor!", msg.getTenantId(), msg);
actorService.onMsg(msg);
}
} }
} }

350
application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java

@ -18,31 +18,42 @@ package org.thingsboard.server.service.telemetry;
import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import com.google.protobuf.InvalidProtocolBufferException;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import org.thingsboard.rule.engine.DonAsynchron;
import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.EntityIdFactory;
import org.thingsboard.server.common.data.kv.AttributeKvEntry; import org.thingsboard.server.common.data.kv.AttributeKvEntry;
import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry;
import org.thingsboard.server.common.data.kv.BaseTsKvQuery;
import org.thingsboard.server.common.data.kv.BasicTsKvEntry; import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
import org.thingsboard.server.common.data.kv.BooleanDataEntry; import org.thingsboard.server.common.data.kv.BooleanDataEntry;
import org.thingsboard.server.common.data.kv.DataType;
import org.thingsboard.server.common.data.kv.DoubleDataEntry; import org.thingsboard.server.common.data.kv.DoubleDataEntry;
import org.thingsboard.server.common.data.kv.KvEntry;
import org.thingsboard.server.common.data.kv.LongDataEntry; import org.thingsboard.server.common.data.kv.LongDataEntry;
import org.thingsboard.server.common.data.kv.StringDataEntry; import org.thingsboard.server.common.data.kv.StringDataEntry;
import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.common.data.kv.TsKvEntry;
import org.thingsboard.server.common.data.kv.TsKvQuery;
import org.thingsboard.server.common.msg.cluster.ServerAddress; import org.thingsboard.server.common.msg.cluster.ServerAddress;
import org.thingsboard.server.dao.attributes.AttributesService; import org.thingsboard.server.dao.attributes.AttributesService;
import org.thingsboard.server.dao.timeseries.TimeseriesService; import org.thingsboard.server.dao.timeseries.TimeseriesService;
import org.thingsboard.server.extensions.api.plugins.PluginContext;
import org.thingsboard.server.extensions.core.plugin.telemetry.handlers.TelemetryFeature; import org.thingsboard.server.extensions.core.plugin.telemetry.handlers.TelemetryFeature;
import org.thingsboard.server.extensions.core.plugin.telemetry.sub.Subscription; import org.thingsboard.server.extensions.core.plugin.telemetry.sub.Subscription;
import org.thingsboard.server.extensions.core.plugin.telemetry.sub.SubscriptionErrorCode;
import org.thingsboard.server.extensions.core.plugin.telemetry.sub.SubscriptionState; import org.thingsboard.server.extensions.core.plugin.telemetry.sub.SubscriptionState;
import org.thingsboard.server.extensions.core.plugin.telemetry.sub.SubscriptionUpdate; import org.thingsboard.server.extensions.core.plugin.telemetry.sub.SubscriptionUpdate;
import org.thingsboard.server.gen.cluster.ClusterAPIProtos;
import org.thingsboard.server.service.cluster.routing.ClusterRoutingService; import org.thingsboard.server.service.cluster.routing.ClusterRoutingService;
import org.thingsboard.server.service.cluster.rpc.ClusterRpcService;
import org.thingsboard.server.service.state.DefaultDeviceStateService; import org.thingsboard.server.service.state.DefaultDeviceStateService;
import org.thingsboard.server.service.state.DeviceStateService; import org.thingsboard.server.service.state.DeviceStateService;
@ -53,15 +64,18 @@ import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.TreeMap;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.stream.Collectors;
/** /**
* Created by ashvayka on 27.03.18. * Created by ashvayka on 27.03.18.
@ -82,6 +96,9 @@ public class DefaultTelemetrySubscriptionService implements TelemetrySubscriptio
@Autowired @Autowired
private ClusterRoutingService routingService; private ClusterRoutingService routingService;
@Autowired
private ClusterRpcService rpcService;
@Autowired @Autowired
@Lazy @Lazy
private DeviceStateService stateService; private DeviceStateService stateService;
@ -106,7 +123,6 @@ public class DefaultTelemetrySubscriptionService implements TelemetrySubscriptio
} }
private final Map<EntityId, Set<Subscription>> subscriptionsByEntityId = new HashMap<>(); private final Map<EntityId, Set<Subscription>> subscriptionsByEntityId = new HashMap<>();
private final Map<String, Map<Integer, Subscription>> subscriptionsByWsSessionId = new HashMap<>(); private final Map<String, Map<Integer, Subscription>> subscriptionsByWsSessionId = new HashMap<>();
@Override @Override
@ -117,7 +133,7 @@ public class DefaultTelemetrySubscriptionService implements TelemetrySubscriptio
ServerAddress address = server.get(); ServerAddress address = server.get();
log.trace("[{}] Forwarding subscription [{}] for device [{}] to [{}]", sessionId, sub.getSubscriptionId(), entityId, address); log.trace("[{}] Forwarding subscription [{}] for device [{}] to [{}]", sessionId, sub.getSubscriptionId(), entityId, address);
subscription = new Subscription(sub, true, address); subscription = new Subscription(sub, true, address);
// rpcHandler.onNewSubscription(ctx, address, sessionId, subscription); tellNewSubscription(address, sessionId, subscription);
} else { } else {
log.trace("[{}] Registering local subscription [{}] for device [{}]", sessionId, sub.getSubscriptionId(), entityId); log.trace("[{}] Registering local subscription [{}] for device [{}]", sessionId, sub.getSubscriptionId(), entityId);
subscription = new Subscription(sub, true); subscription = new Subscription(sub, true);
@ -189,6 +205,174 @@ public class DefaultTelemetrySubscriptionService implements TelemetrySubscriptio
, System.currentTimeMillis())), callback); , System.currentTimeMillis())), callback);
} }
@Override
public void onNewRemoteSubscription(ServerAddress serverAddress, byte[] data) {
ClusterAPIProtos.SubscriptionProto proto;
try {
proto = ClusterAPIProtos.SubscriptionProto.parseFrom(data);
} catch (InvalidProtocolBufferException e) {
throw new RuntimeException(e);
}
Map<String, Long> statesMap = proto.getKeyStatesList().stream().collect(
Collectors.toMap(ClusterAPIProtos.SubscriptionKetStateProto::getKey, ClusterAPIProtos.SubscriptionKetStateProto::getTs));
Subscription subscription = new Subscription(
new SubscriptionState(proto.getSessionId(), proto.getSubscriptionId(),
EntityIdFactory.getByTypeAndId(proto.getEntityType(), proto.getEntityId()),
TelemetryFeature.valueOf(proto.getType()), proto.getAllKeys(), statesMap, proto.getScope()),
false, new ServerAddress(serverAddress.getHost(), serverAddress.getPort()));
addRemoteWsSubscription(serverAddress, proto.getSessionId(), subscription);
}
@Override
public void onRemoteSubscriptionUpdate(ServerAddress serverAddress, byte[] data) {
ClusterAPIProtos.SubscriptionUpdateProto proto;
try {
proto = ClusterAPIProtos.SubscriptionUpdateProto.parseFrom(data);
} catch (InvalidProtocolBufferException e) {
throw new RuntimeException(e);
}
SubscriptionUpdate update = convert(proto);
String sessionId = proto.getSessionId();
log.trace("[{}] Processing remote subscription onUpdate [{}]", sessionId, update);
Optional<Subscription> subOpt = getSubscription(sessionId, update.getSubscriptionId());
if (subOpt.isPresent()) {
updateSubscriptionState(sessionId, subOpt.get(), update);
wsService.sendWsMsg(sessionId, update);
}
}
@Override
public void onRemoteSubscriptionClose(ServerAddress serverAddress, byte[] data) {
ClusterAPIProtos.SubscriptionCloseProto proto;
try {
proto = ClusterAPIProtos.SubscriptionCloseProto.parseFrom(data);
} catch (InvalidProtocolBufferException e) {
throw new RuntimeException(e);
}
removeSubscription(proto.getSessionId(), proto.getSubscriptionId());
}
@Override
public void onRemoteSessionClose(ServerAddress serverAddress, byte[] data) {
ClusterAPIProtos.SessionCloseProto proto;
try {
proto = ClusterAPIProtos.SessionCloseProto.parseFrom(data);
} catch (InvalidProtocolBufferException e) {
throw new RuntimeException(e);
}
cleanupRemoteWsSessionSubscriptions(proto.getSessionId());
}
@Override
public void onRemoteAttributesUpdate(ServerAddress serverAddress, byte[] data) {
ClusterAPIProtos.AttributeUpdateProto proto;
try {
proto = ClusterAPIProtos.AttributeUpdateProto.parseFrom(data);
} catch (InvalidProtocolBufferException e) {
throw new RuntimeException(e);
}
onAttributesUpdate(EntityIdFactory.getByTypeAndId(proto.getEntityType(), proto.getEntityId()), proto.getScope(),
proto.getDataList().stream().map(this::toAttribute).collect(Collectors.toList()));
}
@Override
public void onRemoteTsUpdate(ServerAddress serverAddress, byte[] data) {
ClusterAPIProtos.TimeseriesUpdateProto proto;
try {
proto = ClusterAPIProtos.TimeseriesUpdateProto.parseFrom(data);
} catch (InvalidProtocolBufferException e) {
throw new RuntimeException(e);
}
onTimeseriesUpdate(EntityIdFactory.getByTypeAndId(proto.getEntityType(), proto.getEntityId()),
proto.getDataList().stream().map(this::toTimeseries).collect(Collectors.toList()));
}
@Override
public void onClusterUpdate() {
log.trace("Processing cluster onUpdate msg!");
Iterator<Map.Entry<EntityId, Set<Subscription>>> deviceIterator = subscriptionsByEntityId.entrySet().iterator();
while (deviceIterator.hasNext()) {
Map.Entry<EntityId, Set<Subscription>> e = deviceIterator.next();
Set<Subscription> subscriptions = e.getValue();
Optional<ServerAddress> newAddressOptional = routingService.resolveById(e.getKey());
if (newAddressOptional.isPresent()) {
newAddressOptional.ifPresent(serverAddress -> checkSubsciptionsNewAddress(serverAddress, subscriptions));
} else {
checkSubsciptionsPrevAddress(subscriptions);
}
if (subscriptions.size() == 0) {
log.trace("[{}] No more subscriptions for this device on current server.", e.getKey());
deviceIterator.remove();
}
}
}
private void checkSubsciptionsNewAddress(ServerAddress newAddress, Set<Subscription> subscriptions) {
Iterator<Subscription> subscriptionIterator = subscriptions.iterator();
while (subscriptionIterator.hasNext()) {
Subscription s = subscriptionIterator.next();
if (s.isLocal()) {
if (!newAddress.equals(s.getServer())) {
log.trace("[{}] Local subscription is now handled on new server [{}]", s.getWsSessionId(), newAddress);
s.setServer(newAddress);
tellNewSubscription(newAddress, s.getWsSessionId(), s);
}
} else {
log.trace("[{}] Remote subscription is now handled on new server address: [{}]", s.getWsSessionId(), newAddress);
subscriptionIterator.remove();
//TODO: onUpdate state of subscription by WsSessionId and other maps.
}
}
}
private void checkSubsciptionsPrevAddress(Set<Subscription> subscriptions) {
for (Subscription s : subscriptions) {
if (s.isLocal() && s.getServer() != null) {
log.trace("[{}] Local subscription is no longer handled on remote server address [{}]", s.getWsSessionId(), s.getServer());
s.setServer(null);
} else {
log.trace("[{}] Remote subscription is on up to date server address.", s.getWsSessionId());
}
}
}
private void addRemoteWsSubscription(ServerAddress address, String sessionId, Subscription subscription) {
EntityId entityId = subscription.getEntityId();
log.trace("[{}] Registering remote subscription [{}] for device [{}] to [{}]", sessionId, subscription.getSubscriptionId(), entityId, address);
registerSubscription(sessionId, entityId, subscription);
if (subscription.getType() == TelemetryFeature.ATTRIBUTES) {
final Map<String, Long> keyStates = subscription.getKeyStates();
DonAsynchron.withCallback(attrService.find(entityId, DataConstants.CLIENT_SCOPE, keyStates.keySet()), values -> {
List<TsKvEntry> missedUpdates = new ArrayList<>();
values.forEach(latestEntry -> {
if (latestEntry.getLastUpdateTs() > keyStates.get(latestEntry.getKey())) {
missedUpdates.add(new BasicTsKvEntry(latestEntry.getLastUpdateTs(), latestEntry));
}
});
if (!missedUpdates.isEmpty()) {
tellRemoteSubUpdate(address, sessionId, new SubscriptionUpdate(subscription.getSubscriptionId(), missedUpdates));
}
},
e -> log.error("Failed to fetch missed updates.", e), tsCallBackExecutor);
} else if (subscription.getType() == TelemetryFeature.TIMESERIES) {
long curTs = System.currentTimeMillis();
List<TsKvQuery> queries = new ArrayList<>();
subscription.getKeyStates().entrySet().forEach(e -> {
queries.add(new BaseTsKvQuery(e.getKey(), e.getValue() + 1L, curTs));
});
DonAsynchron.withCallback(tsService.findAll(entityId, queries),
missedUpdates -> {
if (!missedUpdates.isEmpty()) {
tellRemoteSubUpdate(address, sessionId, new SubscriptionUpdate(subscription.getSubscriptionId(), missedUpdates));
}
},
e -> log.error("Failed to fetch missed updates.", e),
tsCallBackExecutor);
}
}
private void onAttributesUpdate(EntityId entityId, String scope, List<AttributeKvEntry> attributes) { private void onAttributesUpdate(EntityId entityId, String scope, List<AttributeKvEntry> attributes) {
Optional<ServerAddress> serverAddress = routingService.resolveById(entityId); Optional<ServerAddress> serverAddress = routingService.resolveById(entityId);
if (!serverAddress.isPresent()) { if (!serverAddress.isPresent()) {
@ -201,7 +385,7 @@ public class DefaultTelemetrySubscriptionService implements TelemetrySubscriptio
} }
} }
} else { } else {
// rpcHandler.onAttributesUpdate(ctx, serverAddress.get(), entityId, entries); tellRemoteAttributesUpdate(serverAddress.get(), entityId, scope, attributes);
} }
} }
@ -210,7 +394,7 @@ public class DefaultTelemetrySubscriptionService implements TelemetrySubscriptio
if (!serverAddress.isPresent()) { if (!serverAddress.isPresent()) {
onLocalTimeseriesUpdate(entityId, ts); onLocalTimeseriesUpdate(entityId, ts);
} else { } else {
// rpcHandler.onTimeseriesUpdate(ctx, serverAddress.get(), entityId, entries); tellRemoteTimeseriesUpdate(serverAddress.get(), entityId, ts);
} }
} }
@ -256,8 +440,7 @@ public class DefaultTelemetrySubscriptionService implements TelemetrySubscriptio
updateSubscriptionState(sessionId, s, update); updateSubscriptionState(sessionId, s, update);
wsService.sendWsMsg(sessionId, update); wsService.sendWsMsg(sessionId, update);
} else { } else {
//TODO: ashvayka tellRemoteSubUpdate(s.getServer(), sessionId, update);
// rpcHandler.onSubscriptionUpdate(ctx, s.getServer(), sessionId, update);
} }
} }
}); });
@ -278,11 +461,11 @@ public class DefaultTelemetrySubscriptionService implements TelemetrySubscriptio
sessionSubscriptions.put(subscription.getSubscriptionId(), subscription); sessionSubscriptions.put(subscription.getSubscriptionId(), subscription);
} }
public void cleanupLocalWsSessionSubscriptions(String sessionId) { private void cleanupLocalWsSessionSubscriptions(String sessionId) {
cleanupWsSessionSubscriptions(sessionId, true); cleanupWsSessionSubscriptions(sessionId, true);
} }
public void cleanupRemoteWsSessionSubscriptions(String sessionId) { private void cleanupRemoteWsSessionSubscriptions(String sessionId) {
cleanupWsSessionSubscriptions(sessionId, false); cleanupWsSessionSubscriptions(sessionId, false);
} }
@ -320,14 +503,14 @@ public class DefaultTelemetrySubscriptionService implements TelemetrySubscriptio
} }
for (ServerAddress address : affectedServers) { for (ServerAddress address : affectedServers) {
log.debug("[{}] Going to onSubscriptionUpdate [{}] server about session close event", sessionId, address); log.debug("[{}] Going to onSubscriptionUpdate [{}] server about session close event", sessionId, address);
// rpcHandler.onSessionClose(ctx, address, sessionId); tellRemoteSessionClose(address, sessionId);
} }
} }
private void processSubscriptionRemoval(String sessionId, Map<Integer, Subscription> sessionSubscriptions, Subscription subscription) { private void processSubscriptionRemoval(String sessionId, Map<Integer, Subscription> sessionSubscriptions, Subscription subscription) {
EntityId entityId = subscription.getEntityId(); EntityId entityId = subscription.getEntityId();
if (subscription.isLocal() && subscription.getServer() != null) { if (subscription.isLocal() && subscription.getServer() != null) {
// rpcHandler.onSubscriptionClose(ctx, subscription.getServer(), sessionId, subscription.getSubscriptionId()); tellRemoteSubClose(subscription.getServer(), sessionId, subscription.getSubscriptionId());
} }
if (sessionSubscriptions.isEmpty()) { if (sessionSubscriptions.isEmpty()) {
log.debug("[{}] Removed last subscription for particular session.", sessionId); log.debug("[{}] Removed last subscription for particular session.", sessionId);
@ -379,4 +562,151 @@ public class DefaultTelemetrySubscriptionService implements TelemetrySubscriptio
} }
}, wsCallBackExecutor); }, wsCallBackExecutor);
} }
private void tellNewSubscription(ServerAddress address, String sessionId, Subscription sub) {
ClusterAPIProtos.SubscriptionProto.Builder builder = ClusterAPIProtos.SubscriptionProto.newBuilder();
builder.setSessionId(sessionId);
builder.setSubscriptionId(sub.getSubscriptionId());
builder.setEntityType(sub.getEntityId().getEntityType().name());
builder.setEntityId(sub.getEntityId().getId().toString());
builder.setType(sub.getType().name());
builder.setAllKeys(sub.isAllKeys());
builder.setScope(sub.getScope());
sub.getKeyStates().entrySet().forEach(e -> builder.addKeyStates(
ClusterAPIProtos.SubscriptionKetStateProto.newBuilder().setKey(e.getKey()).setTs(e.getValue()).build()));
rpcService.tell(address, ClusterAPIProtos.MessageType.CLUSTER_TELEMETRY_SUBSCRIPTION_CREATE_MESSAGE, builder.build().toByteArray());
}
private void tellRemoteSubUpdate(ServerAddress address, String sessionId, SubscriptionUpdate update) {
ClusterAPIProtos.SubscriptionUpdateProto.Builder builder = ClusterAPIProtos.SubscriptionUpdateProto.newBuilder();
builder.setSessionId(sessionId);
builder.setSubscriptionId(update.getSubscriptionId());
builder.setErrorCode(update.getErrorCode());
if (update.getErrorMsg() != null) {
builder.setErrorMsg(update.getErrorMsg());
}
update.getData().entrySet().forEach(
e -> {
ClusterAPIProtos.SubscriptionUpdateValueListProto.Builder dataBuilder = ClusterAPIProtos.SubscriptionUpdateValueListProto.newBuilder();
dataBuilder.setKey(e.getKey());
e.getValue().forEach(v -> {
Object[] array = (Object[]) v;
dataBuilder.addTs((long) array[0]);
dataBuilder.addValue((String) array[1]);
});
builder.addData(dataBuilder.build());
}
);
rpcService.tell(address, ClusterAPIProtos.MessageType.CLUSTER_TELEMETRY_SUBSCRIPTION_UPDATE_MESSAGE, builder.build().toByteArray());
}
private void tellRemoteAttributesUpdate(ServerAddress address, EntityId entityId, String scope, List<AttributeKvEntry> attributes) {
ClusterAPIProtos.AttributeUpdateProto.Builder builder = ClusterAPIProtos.AttributeUpdateProto.newBuilder();
builder.setEntityId(entityId.getId().toString());
builder.setEntityType(entityId.getEntityType().name());
builder.setScope(scope);
attributes.forEach(v -> builder.addData(toKeyValueProto(v.getLastUpdateTs(), v).build()));
rpcService.tell(address, ClusterAPIProtos.MessageType.CLUSTER_TELEMETRY_ATTR_UPDATE_MESSAGE, builder.build().toByteArray());
}
private void tellRemoteTimeseriesUpdate(ServerAddress address, EntityId entityId, List<TsKvEntry> ts) {
ClusterAPIProtos.TimeseriesUpdateProto.Builder builder = ClusterAPIProtos.TimeseriesUpdateProto.newBuilder();
builder.setEntityId(entityId.getId().toString());
builder.setEntityType(entityId.getEntityType().name());
ts.forEach(v -> builder.addData(toKeyValueProto(v.getTs(), v).build()));
rpcService.tell(address, ClusterAPIProtos.MessageType.CLUSTER_TELEMETRY_TS_UPDATE_MESSAGE, builder.build().toByteArray());
}
private void tellRemoteSessionClose(ServerAddress address, String sessionId) {
ClusterAPIProtos.SessionCloseProto proto = ClusterAPIProtos.SessionCloseProto.newBuilder().setSessionId(sessionId).build();
rpcService.tell(address, ClusterAPIProtos.MessageType.CLUSTER_TELEMETRY_SESSION_CLOSE_MESSAGE, proto.toByteArray());
}
private void tellRemoteSubClose(ServerAddress address, String sessionId, int subscriptionId) {
ClusterAPIProtos.SubscriptionCloseProto proto = ClusterAPIProtos.SubscriptionCloseProto.newBuilder().setSessionId(sessionId).setSubscriptionId(subscriptionId).build();
rpcService.tell(address, ClusterAPIProtos.MessageType.CLUSTER_TELEMETRY_SUBSCRIPTION_CLOSE_MESSAGE, proto.toByteArray());
}
private ClusterAPIProtos.KeyValueProto.Builder toKeyValueProto(long ts, KvEntry attr) {
ClusterAPIProtos.KeyValueProto.Builder dataBuilder = ClusterAPIProtos.KeyValueProto.newBuilder();
dataBuilder.setKey(attr.getKey());
dataBuilder.setTs(ts);
dataBuilder.setValueType(attr.getDataType().ordinal());
switch (attr.getDataType()) {
case BOOLEAN:
Optional<Boolean> booleanValue = attr.getBooleanValue();
booleanValue.ifPresent(dataBuilder::setBoolValue);
break;
case LONG:
Optional<Long> longValue = attr.getLongValue();
longValue.ifPresent(dataBuilder::setLongValue);
break;
case DOUBLE:
Optional<Double> doubleValue = attr.getDoubleValue();
doubleValue.ifPresent(dataBuilder::setDoubleValue);
break;
case STRING:
Optional<String> stringValue = attr.getStrValue();
stringValue.ifPresent(dataBuilder::setStrValue);
break;
}
return dataBuilder;
}
private AttributeKvEntry toAttribute(ClusterAPIProtos.KeyValueProto proto) {
return new BaseAttributeKvEntry(getKvEntry(proto), proto.getTs());
}
private TsKvEntry toTimeseries(ClusterAPIProtos.KeyValueProto proto) {
return new BasicTsKvEntry(proto.getTs(), getKvEntry(proto));
}
private KvEntry getKvEntry(ClusterAPIProtos.KeyValueProto proto) {
KvEntry entry = null;
DataType type = DataType.values()[proto.getValueType()];
switch (type) {
case BOOLEAN:
entry = new BooleanDataEntry(proto.getKey(), proto.getBoolValue());
break;
case LONG:
entry = new LongDataEntry(proto.getKey(), proto.getLongValue());
break;
case DOUBLE:
entry = new DoubleDataEntry(proto.getKey(), proto.getDoubleValue());
break;
case STRING:
entry = new StringDataEntry(proto.getKey(), proto.getStrValue());
break;
}
return entry;
}
private SubscriptionUpdate convert(ClusterAPIProtos.SubscriptionUpdateProto proto) {
if (proto.getErrorCode() > 0) {
return new SubscriptionUpdate(proto.getSubscriptionId(), SubscriptionErrorCode.forCode(proto.getErrorCode()), proto.getErrorMsg());
} else {
Map<String, List<Object>> data = new TreeMap<>();
proto.getDataList().forEach(v -> {
List<Object> values = data.computeIfAbsent(v.getKey(), k -> new ArrayList<>());
for (int i = 0; i < v.getTsCount(); i++) {
Object[] value = new Object[2];
value[0] = v.getTs(i);
value[1] = v.getValue(i);
values.add(value);
}
});
return new SubscriptionUpdate(proto.getSubscriptionId(), data);
}
}
private Optional<Subscription> getSubscription(String sessionId, int subscriptionId) {
Subscription state = null;
Map<Integer, Subscription> subMap = subscriptionsByWsSessionId.get(sessionId);
if (subMap != null) {
state = subMap.get(subscriptionId);
}
return Optional.ofNullable(state);
}
} }

2
application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetryWebSocketService.java

@ -21,7 +21,7 @@ import com.google.common.base.Function;
import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import com.hazelcast.util.function.Consumer; import java.util.function.Consumer;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;

16
application/src/main/java/org/thingsboard/server/service/telemetry/TelemetrySubscriptionService.java

@ -17,7 +17,10 @@ package org.thingsboard.server.service.telemetry;
import org.thingsboard.rule.engine.api.RuleEngineTelemetryService; import org.thingsboard.rule.engine.api.RuleEngineTelemetryService;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.msg.cluster.ServerAddress;
import org.thingsboard.server.extensions.api.plugins.PluginContext;
import org.thingsboard.server.extensions.core.plugin.telemetry.sub.SubscriptionState; import org.thingsboard.server.extensions.core.plugin.telemetry.sub.SubscriptionState;
import org.thingsboard.server.gen.cluster.ClusterAPIProtos;
/** /**
* Created by ashvayka on 27.03.18. * Created by ashvayka on 27.03.18.
@ -30,4 +33,17 @@ public interface TelemetrySubscriptionService extends RuleEngineTelemetryService
void removeSubscription(String sessionId, int cmdId); void removeSubscription(String sessionId, int cmdId);
void onNewRemoteSubscription(ServerAddress serverAddress, byte[] data);
void onRemoteSubscriptionUpdate(ServerAddress serverAddress, byte[] bytes);
void onRemoteSubscriptionClose(ServerAddress serverAddress, byte[] bytes);
void onRemoteSessionClose(ServerAddress serverAddress, byte[] bytes);
void onRemoteAttributesUpdate(ServerAddress serverAddress, byte[] bytes);
void onRemoteTsUpdate(ServerAddress serverAddress, byte[] bytes);
void onClusterUpdate();
} }

121
application/src/main/proto/cluster.proto

@ -19,76 +19,105 @@ package cluster;
option java_package = "org.thingsboard.server.gen.cluster"; option java_package = "org.thingsboard.server.gen.cluster";
option java_outer_classname = "ClusterAPIProtos"; option java_outer_classname = "ClusterAPIProtos";
service ClusterRpcService {
rpc handleMsgs(stream ClusterMessage) returns (stream ClusterMessage) {}
}
message ClusterMessage {
MessageType messageType = 1;
MessageMataInfo messageMetaInfo = 2;
ServerAddress serverAddress = 3;
bytes payload = 4;
}
message ServerAddress { message ServerAddress {
string host = 1; string host = 1;
int32 port = 2; int32 port = 2;
} }
message Uid { message MessageMataInfo {
sint64 pluginUuidMsb = 1; string payloadMetaInfo = 1;
sint64 pluginUuidLsb = 2; repeated string tags = 2;
} }
message PluginAddress { enum MessageType {
Uid pluginId = 1;
Uid tenantId = 2; //Cluster control messages
RPC_SESSION_CREATE_REQUEST_MSG = 0;
TO_ALL_NODES_MSG = 1;
RPC_SESSION_TELL_MSG = 2;
RPC_BROADCAST_MSG = 3;
CONNECT_RPC_MESSAGE =4;
CLUSTER_ACTOR_MESSAGE = 5;
// Messages related to TelemetrySubscriptionService
CLUSTER_TELEMETRY_SUBSCRIPTION_CREATE_MESSAGE = 6;
CLUSTER_TELEMETRY_SUBSCRIPTION_UPDATE_MESSAGE = 7;
CLUSTER_TELEMETRY_SUBSCRIPTION_CLOSE_MESSAGE = 8;
CLUSTER_TELEMETRY_SESSION_CLOSE_MESSAGE = 9;
CLUSTER_TELEMETRY_ATTR_UPDATE_MESSAGE = 10;
CLUSTER_TELEMETRY_TS_UPDATE_MESSAGE = 11;
} }
message ToPluginRpcMessage { // Messages related to CLUSTER_TELEMETRY_MESSAGE
PluginAddress address = 1; message SubscriptionProto {
int32 clazz = 2; string sessionId = 1;
bytes data = 3; int32 subscriptionId = 2;
string entityType = 3;
string entityId = 4;
string type = 5;
bool allKeys = 6;
repeated SubscriptionKetStateProto keyStates = 7;
string scope = 8;
} }
message ToDeviceActorRpcMessage { message SubscriptionUpdateProto {
bytes data = 1; string sessionId = 1;
int32 subscriptionId = 2;
int32 errorCode = 3;
string errorMsg = 4;
repeated SubscriptionUpdateValueListProto data = 5;
} }
message ToDeviceSessionActorRpcMessage { message AttributeUpdateProto {
bytes data = 1; string entityType = 1;
string entityId = 2;
string scope = 3;
repeated KeyValueProto data = 4;
} }
message ToDeviceActorNotificationRpcMessage { message TimeseriesUpdateProto {
bytes data = 1; string entityType = 1;
string entityId = 2;
repeated KeyValueProto data = 4;
} }
message ToAllNodesRpcMessage { message SessionCloseProto {
bytes data = 1; string sessionId = 1;
} }
message ConnectRpcMessage { message SubscriptionCloseProto {
ServerAddress serverAddress = 1; string sessionId = 1;
int32 subscriptionId = 2;
} }
message ToDeviceRpcRequestRpcMessage { message SubscriptionKetStateProto {
Uid deviceTenantId = 2; string key = 1;
Uid deviceId = 3; int64 ts = 2;
Uid msgId = 4;
bool oneway = 5;
int64 expTime = 6;
string method = 7;
string params = 8;
}
message ToPluginRpcResponseRpcMessage {
Uid msgId = 2;
string response = 3;
string error = 4;
} }
message ToRpcServerMessage { message SubscriptionUpdateValueListProto {
ConnectRpcMessage connectMsg = 1; string key = 1;
ToPluginRpcMessage toPluginRpcMsg = 2; repeated int64 ts = 2;
ToDeviceActorRpcMessage toDeviceActorRpcMsg = 3; repeated string value = 3;
ToDeviceSessionActorRpcMessage toDeviceSessionActorRpcMsg = 4;
ToDeviceActorNotificationRpcMessage toDeviceActorNotificationRpcMsg = 5;
ToAllNodesRpcMessage toAllNodesRpcMsg = 6;
ToDeviceRpcRequestRpcMessage toDeviceRpcRequestRpcMsg = 7;
ToPluginRpcResponseRpcMessage toPluginRpcResponseRpcMsg = 8;
} }
service ClusterRpcService { message KeyValueProto {
rpc handlePluginMsgs(stream ToRpcServerMessage) returns (stream ToRpcServerMessage) {} string key = 1;
int64 ts = 2;
int32 valueType = 3;
string strValue = 4;
int64 longValue = 5;
double doubleValue = 6;
bool boolValue = 7;
} }

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

@ -25,7 +25,7 @@
</encoder> </encoder>
</appender> </appender>
<logger name="org.thingsboard.server" level="TRACE" /> <logger name="org.thingsboard.server" level="INFO" />
<logger name="akka" level="INFO" /> <logger name="akka" level="INFO" />
<root level="INFO"> <root level="INFO">

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

@ -58,6 +58,8 @@ cluster:
hash_function_name: "${CLUSTER_HASH_FUNCTION_NAME:murmur3_128}" hash_function_name: "${CLUSTER_HASH_FUNCTION_NAME:murmur3_128}"
# Amount of virtual nodes in consistent hash ring. # Amount of virtual nodes in consistent hash ring.
vitrual_nodes_size: "${CLUSTER_VIRTUAL_NODES_SIZE:16}" vitrual_nodes_size: "${CLUSTER_VIRTUAL_NODES_SIZE:16}"
# Queue partition id for current node
partition_id: "${QUEUE_PARTITION_ID:0}"
# Plugins configuration parameters # Plugins configuration parameters
plugins: plugins:
@ -106,7 +108,7 @@ mqtt:
# CoAP server parameters # CoAP server parameters
coap: coap:
# Enable/disable coap transport protocol. # Enable/disable coap transport protocol.
enabled: "${COAP_ENABLED:true}" enabled: "${COAP_ENABLED:false}"
bind_address: "${COAP_BIND_ADDRESS:0.0.0.0}" bind_address: "${COAP_BIND_ADDRESS:0.0.0.0}"
bind_port: "${COAP_BIND_PORT:5683}" bind_port: "${COAP_BIND_PORT:5683}"
adaptor: "${COAP_ADAPTOR_NAME:JsonCoapAdaptor}" adaptor: "${COAP_ADAPTOR_NAME:JsonCoapAdaptor}"
@ -312,6 +314,7 @@ rule:
#Message queue cleanup period in seconds #Message queue cleanup period in seconds
cleanup_period: "${RULE_QUEUE_CLEANUP_PERIOD:3600}" cleanup_period: "${RULE_QUEUE_CLEANUP_PERIOD:3600}"
# PostgreSQL DAO Configuration # PostgreSQL DAO Configuration
#spring: #spring:
# data: # data:

31
application/src/test/java/org/thingsboard/server/mqtt/DbConfigurationTestRule.java

@ -0,0 +1,31 @@
/**
* Copyright © 2016-2018 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.mqtt;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
/**
* Created by ashvayka on 11.05.18.
*/
public class DbConfigurationTestRule implements TestRule {
@Override
public Statement apply(Statement base, Description description) {
return null;
}
}

45
base-docker-compose.yml

@ -0,0 +1,45 @@
#
# Copyright © 2016-2018 The Thingsboard Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
version: '3.3'
services:
zookeeper:
image: wurstmeister/zookeeper
networks:
- core
ports:
- "2181:2181"
cassandra:
image: cassandra:3.11.2
networks:
- core
ports:
- "7199:7199"
- "9160:9160"
- "9042:9042"
redis:
image: redis:4.0
networks:
- core
command: redis-server --maxclients 2000
ports:
- "6379:6379"
networks:
core:

3
common/data/src/main/java/org/thingsboard/server/common/data/id/IdBased.java

@ -17,9 +17,10 @@ package org.thingsboard.server.common.data.id;
import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnore;
import java.io.Serializable;
import java.util.UUID; import java.util.UUID;
public abstract class IdBased<I extends UUIDBased> { public abstract class IdBased<I extends UUIDBased> implements Serializable {
protected I id; protected I id;

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

@ -28,6 +28,11 @@ public enum MsgType {
*/ */
CLUSTER_EVENT_MSG, CLUSTER_EVENT_MSG,
/**
* All messages, could be send to cluster
*/
SEND_TO_CLUSTER_MSG,
/** /**
* ADDED/UPDATED/DELETED events for main entities. * ADDED/UPDATED/DELETED events for main entities.
* *
@ -96,6 +101,6 @@ public enum MsgType {
/** /**
* Message that is sent from Rule Engine to the Device Actor when message is successfully pushed to queue. * Message that is sent from Rule Engine to the Device Actor when message is successfully pushed to queue.
*/ */
RULE_ENGINE_QUEUE_PUT_ACK_MSG; RULE_ENGINE_QUEUE_PUT_ACK_MSG, ACTOR_SYSTEM_TO_DEVICE_SESSION_ACTOR_MSG, TRANSPORT_TO_DEVICE_SESSION_ACTOR_MSG, SESSION_TIMEOUT_MSG, SESSION_CTRL_MSG;
} }

40
common/message/src/main/java/org/thingsboard/server/common/msg/cluster/SendToClusterMsg.java

@ -0,0 +1,40 @@
/**
* Copyright © 2016-2018 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.msg.cluster;
import lombok.Data;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.msg.MsgType;
import org.thingsboard.server.common.msg.TbActorMsg;
@Data
public class SendToClusterMsg implements TbActorMsg {
private TbActorMsg msg;
private EntityId entityId;
public SendToClusterMsg(EntityId entityId, TbActorMsg msg) {
this.entityId = entityId;
this.msg = msg;
}
@Override
public MsgType getMsgType() {
return MsgType.SEND_TO_CLUSTER_MSG;
}
}

4
common/message/src/main/java/org/thingsboard/server/common/msg/cluster/ToAllNodesMsg.java

@ -15,10 +15,12 @@
*/ */
package org.thingsboard.server.common.msg.cluster; package org.thingsboard.server.common.msg.cluster;
import org.thingsboard.server.common.msg.TbActorMsg;
import java.io.Serializable; import java.io.Serializable;
/** /**
* @author Andrew Shvayka * @author Andrew Shvayka
*/ */
public interface ToAllNodesMsg extends Serializable { public interface ToAllNodesMsg extends Serializable, TbActorMsg {
} }

3
common/message/src/main/java/org/thingsboard/server/common/msg/core/ToDeviceSessionActorMsg.java → common/message/src/main/java/org/thingsboard/server/common/msg/core/ActorSystemToDeviceSessionActorMsg.java

@ -15,6 +15,7 @@
*/ */
package org.thingsboard.server.common.msg.core; package org.thingsboard.server.common.msg.core;
import org.thingsboard.server.common.msg.TbActorMsg;
import org.thingsboard.server.common.msg.aware.SessionAwareMsg; import org.thingsboard.server.common.msg.aware.SessionAwareMsg;
import org.thingsboard.server.common.msg.session.ToDeviceMsg; import org.thingsboard.server.common.msg.session.ToDeviceMsg;
@ -23,7 +24,7 @@ import java.io.Serializable;
/** /**
* @author Andrew Shvayka * @author Andrew Shvayka
*/ */
public interface ToDeviceSessionActorMsg extends SessionAwareMsg, Serializable { public interface ActorSystemToDeviceSessionActorMsg extends SessionAwareMsg, Serializable, TbActorMsg {
ToDeviceMsg getMsg(); ToDeviceMsg getMsg();
} }

9
common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicToDeviceSessionActorMsg.java → common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicActorSystemToDeviceSessionActorMsg.java

@ -16,14 +16,15 @@
package org.thingsboard.server.common.msg.core; package org.thingsboard.server.common.msg.core;
import org.thingsboard.server.common.data.id.SessionId; import org.thingsboard.server.common.data.id.SessionId;
import org.thingsboard.server.common.msg.MsgType;
import org.thingsboard.server.common.msg.session.ToDeviceMsg; import org.thingsboard.server.common.msg.session.ToDeviceMsg;
public class BasicToDeviceSessionActorMsg implements ToDeviceSessionActorMsg { public class BasicActorSystemToDeviceSessionActorMsg implements ActorSystemToDeviceSessionActorMsg {
private final ToDeviceMsg msg; private final ToDeviceMsg msg;
private final SessionId sessionId; private final SessionId sessionId;
public BasicToDeviceSessionActorMsg(ToDeviceMsg msg, SessionId sessionId) { public BasicActorSystemToDeviceSessionActorMsg(ToDeviceMsg msg, SessionId sessionId) {
super(); super();
this.msg = msg; this.msg = msg;
this.sessionId = sessionId; this.sessionId = sessionId;
@ -44,4 +45,8 @@ public class BasicToDeviceSessionActorMsg implements ToDeviceSessionActorMsg {
return "BasicToSessionResponseMsg [msg=" + msg + ", sessionId=" + sessionId + "]"; return "BasicToSessionResponseMsg [msg=" + msg + ", sessionId=" + sessionId + "]";
} }
@Override
public MsgType getMsgType() {
return MsgType.ACTOR_SYSTEM_TO_DEVICE_SESSION_ACTOR_MSG;
}
} }

4
common/message/src/main/java/org/thingsboard/server/common/msg/device/BasicDeviceToDeviceActorMsg.java

@ -24,7 +24,7 @@ import org.thingsboard.server.common.msg.MsgType;
import org.thingsboard.server.common.msg.cluster.ServerAddress; import org.thingsboard.server.common.msg.cluster.ServerAddress;
import org.thingsboard.server.common.msg.session.FromDeviceMsg; import org.thingsboard.server.common.msg.session.FromDeviceMsg;
import org.thingsboard.server.common.msg.session.SessionType; import org.thingsboard.server.common.msg.session.SessionType;
import org.thingsboard.server.common.msg.session.ToDeviceActorSessionMsg; import org.thingsboard.server.common.msg.session.TransportToDeviceSessionActorMsg;
import java.util.Optional; import java.util.Optional;
@ -45,7 +45,7 @@ public class BasicDeviceToDeviceActorMsg implements DeviceToDeviceActorMsg {
this(null, other.getTenantId(), other.getCustomerId(), other.getDeviceId(), other.getSessionId(), other.getSessionType(), msg); this(null, other.getTenantId(), other.getCustomerId(), other.getDeviceId(), other.getSessionId(), other.getSessionType(), msg);
} }
public BasicDeviceToDeviceActorMsg(ToDeviceActorSessionMsg msg, SessionType sessionType) { public BasicDeviceToDeviceActorMsg(TransportToDeviceSessionActorMsg msg, SessionType sessionType) {
this(null, msg.getTenantId(), msg.getCustomerId(), msg.getDeviceId(), msg.getSessionId(), sessionType, msg.getSessionMsg().getMsg()); this(null, msg.getTenantId(), msg.getCustomerId(), msg.getDeviceId(), msg.getSessionId(), sessionType, msg.getSessionMsg().getMsg());
} }

1
common/message/src/main/java/org/thingsboard/server/common/msg/plugin/ComponentLifecycleMsg.java

@ -20,7 +20,6 @@ import lombok.ToString;
import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.id.*; import org.thingsboard.server.common.data.id.*;
import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.msg.MsgType; import org.thingsboard.server.common.msg.MsgType;
import org.thingsboard.server.common.msg.TbActorMsg; import org.thingsboard.server.common.msg.TbActorMsg;
import org.thingsboard.server.common.msg.aware.TenantAwareMsg; import org.thingsboard.server.common.msg.aware.TenantAwareMsg;

18
common/message/src/main/java/org/thingsboard/server/common/msg/session/BasicToDeviceActorSessionMsg.java → common/message/src/main/java/org/thingsboard/server/common/msg/session/BasicTransportToDeviceSessionActorMsg.java

@ -20,15 +20,16 @@ import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.SessionId; import org.thingsboard.server.common.data.id.SessionId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.msg.MsgType;
public class BasicToDeviceActorSessionMsg implements ToDeviceActorSessionMsg { public class BasicTransportToDeviceSessionActorMsg implements TransportToDeviceSessionActorMsg {
private final TenantId tenantId; private final TenantId tenantId;
private final CustomerId customerId; private final CustomerId customerId;
private final DeviceId deviceId; private final DeviceId deviceId;
private final AdaptorToSessionActorMsg msg; private final AdaptorToSessionActorMsg msg;
public BasicToDeviceActorSessionMsg(Device device, AdaptorToSessionActorMsg msg) { public BasicTransportToDeviceSessionActorMsg(Device device, AdaptorToSessionActorMsg msg) {
super(); super();
this.tenantId = device.getTenantId(); this.tenantId = device.getTenantId();
this.customerId = device.getCustomerId(); this.customerId = device.getCustomerId();
@ -36,13 +37,6 @@ public class BasicToDeviceActorSessionMsg implements ToDeviceActorSessionMsg {
this.msg = msg; this.msg = msg;
} }
public BasicToDeviceActorSessionMsg(ToDeviceActorSessionMsg deviceMsg) {
this.tenantId = deviceMsg.getTenantId();
this.customerId = deviceMsg.getCustomerId();
this.deviceId = deviceMsg.getDeviceId();
this.msg = deviceMsg.getSessionMsg();
}
@Override @Override
public DeviceId getDeviceId() { public DeviceId getDeviceId() {
return deviceId; return deviceId;
@ -69,8 +63,12 @@ public class BasicToDeviceActorSessionMsg implements ToDeviceActorSessionMsg {
@Override @Override
public String toString() { public String toString() {
return "BasicToDeviceActorSessionMsg [tenantId=" + tenantId + ", customerId=" + customerId + ", deviceId=" + deviceId + ", msg=" + msg return "BasicTransportToDeviceSessionActorMsg [tenantId=" + tenantId + ", customerId=" + customerId + ", deviceId=" + deviceId + ", msg=" + msg
+ "]"; + "]";
} }
@Override
public MsgType getMsgType() {
return MsgType.TRANSPORT_TO_DEVICE_SESSION_ACTOR_MSG;
}
} }

3
common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionCtrlMsg.java

@ -15,8 +15,9 @@
*/ */
package org.thingsboard.server.common.msg.session; package org.thingsboard.server.common.msg.session;
import org.thingsboard.server.common.msg.TbActorMsg;
import org.thingsboard.server.common.msg.aware.SessionAwareMsg; import org.thingsboard.server.common.msg.aware.SessionAwareMsg;
public interface SessionCtrlMsg extends SessionAwareMsg { public interface SessionCtrlMsg extends SessionAwareMsg, TbActorMsg {
} }

3
common/message/src/main/java/org/thingsboard/server/common/msg/session/ToDeviceActorSessionMsg.java → common/message/src/main/java/org/thingsboard/server/common/msg/session/TransportToDeviceSessionActorMsg.java

@ -15,12 +15,13 @@
*/ */
package org.thingsboard.server.common.msg.session; package org.thingsboard.server.common.msg.session;
import org.thingsboard.server.common.msg.TbActorMsg;
import org.thingsboard.server.common.msg.aware.CustomerAwareMsg; import org.thingsboard.server.common.msg.aware.CustomerAwareMsg;
import org.thingsboard.server.common.msg.aware.DeviceAwareMsg; import org.thingsboard.server.common.msg.aware.DeviceAwareMsg;
import org.thingsboard.server.common.msg.aware.SessionAwareMsg; import org.thingsboard.server.common.msg.aware.SessionAwareMsg;
import org.thingsboard.server.common.msg.aware.TenantAwareMsg; import org.thingsboard.server.common.msg.aware.TenantAwareMsg;
public interface ToDeviceActorSessionMsg extends DeviceAwareMsg, CustomerAwareMsg, TenantAwareMsg, SessionAwareMsg { public interface TransportToDeviceSessionActorMsg extends DeviceAwareMsg, CustomerAwareMsg, TenantAwareMsg, SessionAwareMsg, TbActorMsg {
AdaptorToSessionActorMsg getSessionMsg(); AdaptorToSessionActorMsg getSessionMsg();

5
common/message/src/main/java/org/thingsboard/server/common/msg/session/ctrl/SessionCloseMsg.java

@ -16,6 +16,7 @@
package org.thingsboard.server.common.msg.session.ctrl; package org.thingsboard.server.common.msg.session.ctrl;
import org.thingsboard.server.common.data.id.SessionId; import org.thingsboard.server.common.data.id.SessionId;
import org.thingsboard.server.common.msg.MsgType;
import org.thingsboard.server.common.msg.session.SessionCtrlMsg; import org.thingsboard.server.common.msg.session.SessionCtrlMsg;
public class SessionCloseMsg implements SessionCtrlMsg { public class SessionCloseMsg implements SessionCtrlMsg {
@ -60,4 +61,8 @@ public class SessionCloseMsg implements SessionCtrlMsg {
return timeout; return timeout;
} }
@Override
public MsgType getMsgType() {
return MsgType.SESSION_CTRL_MSG;
}
} }

12
dao/pom.xml

@ -152,22 +152,10 @@
<groupId>org.apache.curator</groupId> <groupId>org.apache.curator</groupId>
<artifactId>curator-x-discovery</artifactId> <artifactId>curator-x-discovery</artifactId>
</dependency> </dependency>
<dependency>
<groupId>com.hazelcast</groupId>
<artifactId>hazelcast-zookeeper</artifactId>
</dependency>
<dependency>
<groupId>com.hazelcast</groupId>
<artifactId>hazelcast</artifactId>
</dependency>
<dependency> <dependency>
<groupId>com.github.ben-manes.caffeine</groupId> <groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId> <artifactId>caffeine</artifactId>
</dependency> </dependency>
<dependency>
<groupId>com.hazelcast</groupId>
<artifactId>hazelcast-spring</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId> <artifactId>spring-boot-autoconfigure</artifactId>

32
dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java

@ -222,15 +222,14 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
public ListenableFuture<AlarmInfo> findAlarmInfoByIdAsync(AlarmId alarmId) { public ListenableFuture<AlarmInfo> findAlarmInfoByIdAsync(AlarmId alarmId) {
log.trace("Executing findAlarmInfoByIdAsync [{}]", alarmId); log.trace("Executing findAlarmInfoByIdAsync [{}]", alarmId);
validateId(alarmId, "Incorrect alarmId " + alarmId); validateId(alarmId, "Incorrect alarmId " + alarmId);
return Futures.transform(alarmDao.findAlarmByIdAsync(alarmId.getId()), return Futures.transformAsync(alarmDao.findAlarmByIdAsync(alarmId.getId()),
(AsyncFunction<Alarm, AlarmInfo>) alarm1 -> { a -> {
AlarmInfo alarmInfo = new AlarmInfo(alarm1); AlarmInfo alarmInfo = new AlarmInfo(a);
return Futures.transform( return Futures.transform(
entityService.fetchEntityNameAsync(alarmInfo.getOriginator()), (Function<String, AlarmInfo>) entityService.fetchEntityNameAsync(alarmInfo.getOriginator()), originatorName -> {
originatorName -> { alarmInfo.setOriginatorName(originatorName);
alarmInfo.setOriginatorName(originatorName); return alarmInfo;
return alarmInfo; }
}
); );
}); });
} }
@ -239,18 +238,17 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
public ListenableFuture<TimePageData<AlarmInfo>> findAlarms(AlarmQuery query) { public ListenableFuture<TimePageData<AlarmInfo>> findAlarms(AlarmQuery query) {
ListenableFuture<List<AlarmInfo>> alarms = alarmDao.findAlarms(query); ListenableFuture<List<AlarmInfo>> alarms = alarmDao.findAlarms(query);
if (query.getFetchOriginator() != null && query.getFetchOriginator().booleanValue()) { if (query.getFetchOriginator() != null && query.getFetchOriginator().booleanValue()) {
alarms = Futures.transform(alarms, (AsyncFunction<List<AlarmInfo>, List<AlarmInfo>>) input -> { alarms = Futures.transformAsync(alarms, input -> {
List<ListenableFuture<AlarmInfo>> alarmFutures = new ArrayList<>(input.size()); List<ListenableFuture<AlarmInfo>> alarmFutures = new ArrayList<>(input.size());
for (AlarmInfo alarmInfo : input) { for (AlarmInfo alarmInfo : input) {
alarmFutures.add(Futures.transform( alarmFutures.add(Futures.transform(
entityService.fetchEntityNameAsync(alarmInfo.getOriginator()), (Function<String, AlarmInfo>) entityService.fetchEntityNameAsync(alarmInfo.getOriginator()), originatorName -> {
originatorName -> { if (originatorName == null) {
if (originatorName == null) { originatorName = "Deleted";
originatorName = "Deleted"; }
} alarmInfo.setOriginatorName(originatorName);
alarmInfo.setOriginatorName(originatorName); return alarmInfo;
return alarmInfo; }
}
)); ));
} }
return Futures.successfulAsList(alarmFutures); return Futures.successfulAsList(alarmFutures);

4
dao/src/main/java/org/thingsboard/server/dao/alarm/CassandraAlarmDao.java

@ -102,12 +102,12 @@ public class CassandraAlarmDao extends CassandraAbstractModelDao<AlarmEntity, Al
} }
String relationType = BaseAlarmService.ALARM_RELATION_PREFIX + searchStatusName; String relationType = BaseAlarmService.ALARM_RELATION_PREFIX + searchStatusName;
ListenableFuture<List<EntityRelation>> relations = relationDao.findRelations(affectedEntity, relationType, RelationTypeGroup.ALARM, EntityType.ALARM, query.getPageLink()); ListenableFuture<List<EntityRelation>> relations = relationDao.findRelations(affectedEntity, relationType, RelationTypeGroup.ALARM, EntityType.ALARM, query.getPageLink());
return Futures.transform(relations, (AsyncFunction<List<EntityRelation>, List<AlarmInfo>>) input -> { return Futures.transformAsync(relations, input -> {
List<ListenableFuture<AlarmInfo>> alarmFutures = new ArrayList<>(input.size()); List<ListenableFuture<AlarmInfo>> alarmFutures = new ArrayList<>(input.size());
for (EntityRelation relation : input) { for (EntityRelation relation : input) {
alarmFutures.add(Futures.transform( alarmFutures.add(Futures.transform(
findAlarmByIdAsync(relation.getTo().getId()), findAlarmByIdAsync(relation.getTo().getId()),
(Function<Alarm, AlarmInfo>) AlarmInfo::new)); AlarmInfo::new));
} }
return Futures.successfulAsList(alarmFutures); return Futures.successfulAsList(alarmFutures);
}); });

4
dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java

@ -194,10 +194,10 @@ public class BaseAssetService extends AbstractEntityService implements AssetServ
@Override @Override
public ListenableFuture<List<Asset>> findAssetsByQuery(AssetSearchQuery query) { public ListenableFuture<List<Asset>> findAssetsByQuery(AssetSearchQuery query) {
ListenableFuture<List<EntityRelation>> relations = relationService.findByQuery(query.toEntitySearchQuery()); ListenableFuture<List<EntityRelation>> relations = relationService.findByQuery(query.toEntitySearchQuery());
ListenableFuture<List<Asset>> assets = Futures.transform(relations, (AsyncFunction<List<EntityRelation>, List<Asset>>) relations1 -> { ListenableFuture<List<Asset>> assets = Futures.transformAsync(relations, r -> {
EntitySearchDirection direction = query.toEntitySearchQuery().getParameters().getDirection(); EntitySearchDirection direction = query.toEntitySearchQuery().getParameters().getDirection();
List<ListenableFuture<Asset>> futures = new ArrayList<>(); List<ListenableFuture<Asset>> futures = new ArrayList<>();
for (EntityRelation relation : relations1) { for (EntityRelation relation : r) {
EntityId entityId = direction == EntitySearchDirection.FROM ? relation.getTo() : relation.getFrom(); EntityId entityId = direction == EntitySearchDirection.FROM ? relation.getTo() : relation.getFrom();
if (entityId.getEntityType() == EntityType.ASSET) { if (entityId.getEntityType() == EntityType.ASSET) {
futures.add(findAssetByIdAsync(new AssetId(entityId.getId()))); futures.add(findAssetByIdAsync(new AssetId(entityId.getId())));

17
dao/src/main/java/org/thingsboard/server/dao/cassandra/AbstractCassandraCluster.java

@ -16,10 +16,17 @@
package org.thingsboard.server.dao.cassandra; package org.thingsboard.server.dao.cassandra;
import com.datastax.driver.core.*; import com.datastax.driver.core.Cluster;
import com.datastax.driver.core.ConsistencyLevel;
import com.datastax.driver.core.HostDistance;
import com.datastax.driver.core.PoolingOptions;
import com.datastax.driver.core.ProtocolOptions.Compression; import com.datastax.driver.core.ProtocolOptions.Compression;
import com.datastax.driver.core.Session;
import com.datastax.driver.mapping.DefaultPropertyMapper;
import com.datastax.driver.mapping.Mapper; import com.datastax.driver.mapping.Mapper;
import com.datastax.driver.mapping.MappingConfiguration;
import com.datastax.driver.mapping.MappingManager; import com.datastax.driver.mapping.MappingManager;
import com.datastax.driver.mapping.PropertyAccessStrategy;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -149,7 +156,13 @@ public abstract class AbstractCassandraCluster {
} else { } else {
session = cluster.connect(); session = cluster.connect();
} }
mappingManager = new MappingManager(session); // For Cassandra Driver version 3.5.0
DefaultPropertyMapper propertyMapper = new DefaultPropertyMapper();
propertyMapper.setPropertyAccessStrategy(PropertyAccessStrategy.FIELDS);
MappingConfiguration configuration = MappingConfiguration.builder().withPropertyMapper(propertyMapper).build();
mappingManager = new MappingManager(session, configuration);
// For Cassandra Driver version 3.0.0
// mappingManager = new MappingManager(session);
break; break;
} catch (Exception e) { } catch (Exception e) {
log.warn("Failed to initialize cassandra cluster due to {}. Will retry in {} ms", e.getMessage(), initRetryInterval); log.warn("Failed to initialize cassandra cluster due to {}. Will retry in {} ms", e.getMessage(), initRetryInterval);

2
dao/src/main/java/org/thingsboard/server/dao/dashboard/CassandraDashboardInfoDao.java

@ -77,7 +77,7 @@ public class CassandraDashboardInfoDao extends CassandraAbstractSearchTextDao<Da
ListenableFuture<List<EntityRelation>> relations = relationDao.findRelations(new CustomerId(customerId), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.DASHBOARD, EntityType.DASHBOARD, pageLink); ListenableFuture<List<EntityRelation>> relations = relationDao.findRelations(new CustomerId(customerId), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.DASHBOARD, EntityType.DASHBOARD, pageLink);
return Futures.transform(relations, (AsyncFunction<List<EntityRelation>, List<DashboardInfo>>) input -> { return Futures.transformAsync(relations, input -> {
List<ListenableFuture<DashboardInfo>> dashboardFutures = new ArrayList<>(input.size()); List<ListenableFuture<DashboardInfo>> dashboardFutures = new ArrayList<>(input.size());
for (EntityRelation relation : input) { for (EntityRelation relation : input) {
dashboardFutures.add(findByIdAsync(relation.getTo().getId())); dashboardFutures.add(findByIdAsync(relation.getTo().getId()));

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

@ -227,10 +227,10 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
@Override @Override
public ListenableFuture<List<Device>> findDevicesByQuery(DeviceSearchQuery query) { public ListenableFuture<List<Device>> findDevicesByQuery(DeviceSearchQuery query) {
ListenableFuture<List<EntityRelation>> relations = relationService.findByQuery(query.toEntitySearchQuery()); ListenableFuture<List<EntityRelation>> relations = relationService.findByQuery(query.toEntitySearchQuery());
ListenableFuture<List<Device>> devices = Futures.transform(relations, (AsyncFunction<List<EntityRelation>, List<Device>>) relations1 -> { ListenableFuture<List<Device>> devices = Futures.transformAsync(relations, r -> {
EntitySearchDirection direction = query.toEntitySearchQuery().getParameters().getDirection(); EntitySearchDirection direction = query.toEntitySearchQuery().getParameters().getDirection();
List<ListenableFuture<Device>> futures = new ArrayList<>(); List<ListenableFuture<Device>> futures = new ArrayList<>();
for (EntityRelation relation : relations1) { for (EntityRelation relation : r) {
EntityId entityId = direction == EntitySearchDirection.FROM ? relation.getTo() : relation.getFrom(); EntityId entityId = direction == EntitySearchDirection.FROM ? relation.getTo() : relation.getFrom();
if (entityId.getEntityType() == EntityType.DEVICE) { if (entityId.getEntityType() == EntityType.DEVICE) {
futures.add(findDeviceByIdAsync(new DeviceId(entityId.getId()))); futures.add(findDeviceByIdAsync(new DeviceId(entityId.getId())));

4
dao/src/main/java/org/thingsboard/server/dao/nosql/RateLimitedResultSetFuture.java

@ -36,14 +36,14 @@ public class RateLimitedResultSetFuture implements ResultSetFuture {
private final ListenableFuture<Void> rateLimitFuture; private final ListenableFuture<Void> rateLimitFuture;
public RateLimitedResultSetFuture(Session session, AsyncRateLimiter rateLimiter, Statement statement) { public RateLimitedResultSetFuture(Session session, AsyncRateLimiter rateLimiter, Statement statement) {
this.rateLimitFuture = Futures.withFallback(rateLimiter.acquireAsync(), t -> { this.rateLimitFuture = Futures.catchingAsync(rateLimiter.acquireAsync(), Throwable.class, t -> {
if (!(t instanceof BufferLimitException)) { if (!(t instanceof BufferLimitException)) {
rateLimiter.release(); rateLimiter.release();
} }
return Futures.immediateFailedFuture(t); return Futures.immediateFailedFuture(t);
}); });
this.originalFuture = Futures.transform(rateLimitFuture, this.originalFuture = Futures.transform(rateLimitFuture,
(Function<Void, ResultSetFuture>) i -> executeAsyncWithRelease(rateLimiter, session, statement)); i -> executeAsyncWithRelease(rateLimiter, session, statement));
} }

76
dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationService.java

@ -16,7 +16,6 @@
package org.thingsboard.server.dao.relation; package org.thingsboard.server.dao.relation;
import com.google.common.base.Function; import com.google.common.base.Function;
import com.google.common.util.concurrent.AsyncFunction;
import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@ -29,12 +28,23 @@ import org.springframework.cache.annotation.Caching;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.relation.*; import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.relation.EntityRelationInfo;
import org.thingsboard.server.common.data.relation.EntityRelationsQuery;
import org.thingsboard.server.common.data.relation.EntitySearchDirection;
import org.thingsboard.server.common.data.relation.EntityTypeFilter;
import org.thingsboard.server.common.data.relation.RelationTypeGroup;
import org.thingsboard.server.common.data.relation.RelationsSearchParameters;
import org.thingsboard.server.dao.entity.EntityService; import org.thingsboard.server.dao.entity.EntityService;
import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.exception.DataValidationException;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.util.*; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
@ -175,7 +185,7 @@ public class BaseRelationService implements RelationService {
inboundRelationsList.add(relationDao.findAllByTo(entity, typeGroup)); inboundRelationsList.add(relationDao.findAllByTo(entity, typeGroup));
} }
ListenableFuture<List<List<EntityRelation>>> inboundRelations = Futures.allAsList(inboundRelationsList); ListenableFuture<List<List<EntityRelation>>> inboundRelations = Futures.allAsList(inboundRelationsList);
ListenableFuture<List<Boolean>> inboundDeletions = Futures.transform(inboundRelations, (List<List<EntityRelation>> relations) -> ListenableFuture<List<Boolean>> inboundDeletions = Futures.transform(inboundRelations, relations ->
getBooleans(relations, cache, true)); getBooleans(relations, cache, true));
ListenableFuture<Boolean> inboundFuture = Futures.transform(inboundDeletions, getListToBooleanFunction()); ListenableFuture<Boolean> inboundFuture = Futures.transform(inboundDeletions, getListToBooleanFunction());
@ -191,8 +201,7 @@ public class BaseRelationService implements RelationService {
outboundRelationsList.add(relationDao.findAllByFrom(entity, typeGroup)); outboundRelationsList.add(relationDao.findAllByFrom(entity, typeGroup));
} }
ListenableFuture<List<List<EntityRelation>>> outboundRelations = Futures.allAsList(outboundRelationsList); ListenableFuture<List<List<EntityRelation>>> outboundRelations = Futures.allAsList(outboundRelationsList);
Futures.transform(outboundRelations, (Function<List<List<EntityRelation>>, List<Boolean>>) relations -> Futures.transform(outboundRelations, relations -> getBooleans(relations, cache, false));
getBooleans(relations, cache, false));
boolean outboundDeleteResult = relationDao.deleteOutboundRelations(entity); boolean outboundDeleteResult = relationDao.deleteOutboundRelations(entity);
return inboundDeleteResult && outboundDeleteResult; return inboundDeleteResult && outboundDeleteResult;
@ -201,7 +210,7 @@ public class BaseRelationService implements RelationService {
private List<Boolean> getBooleans(List<List<EntityRelation>> relations, Cache cache, boolean isRemove) { private List<Boolean> getBooleans(List<List<EntityRelation>> relations, Cache cache, boolean isRemove) {
List<Boolean> results = new ArrayList<>(); List<Boolean> results = new ArrayList<>();
for (List<EntityRelation> relationList : relations) { for (List<EntityRelation> relationList : relations) {
relationList.stream().forEach(relation -> checkFromDeleteSync(cache, results, relation, isRemove)); relationList.forEach(relation -> checkFromDeleteSync(cache, results, relation, isRemove));
} }
return results; return results;
} }
@ -223,8 +232,8 @@ public class BaseRelationService implements RelationService {
inboundRelationsList.add(relationDao.findAllByTo(entity, typeGroup)); inboundRelationsList.add(relationDao.findAllByTo(entity, typeGroup));
} }
ListenableFuture<List<List<EntityRelation>>> inboundRelations = Futures.allAsList(inboundRelationsList); ListenableFuture<List<List<EntityRelation>>> inboundRelations = Futures.allAsList(inboundRelationsList);
ListenableFuture<List<Boolean>> inboundDeletions = Futures.transform(inboundRelations, ListenableFuture<List<Boolean>> inboundDeletions = Futures.transformAsync(inboundRelations,
(AsyncFunction<List<List<EntityRelation>>, List<Boolean>>) relations -> { relations -> {
List<ListenableFuture<Boolean>> results = getListenableFutures(relations, cache, true); List<ListenableFuture<Boolean>> results = getListenableFutures(relations, cache, true);
return Futures.allAsList(results); return Futures.allAsList(results);
}); });
@ -236,7 +245,7 @@ public class BaseRelationService implements RelationService {
outboundRelationsList.add(relationDao.findAllByFrom(entity, typeGroup)); outboundRelationsList.add(relationDao.findAllByFrom(entity, typeGroup));
} }
ListenableFuture<List<List<EntityRelation>>> outboundRelations = Futures.allAsList(outboundRelationsList); ListenableFuture<List<List<EntityRelation>>> outboundRelations = Futures.allAsList(outboundRelationsList);
Futures.transform(outboundRelations, (AsyncFunction<List<List<EntityRelation>>, List<Boolean>>) relations -> { Futures.transformAsync(outboundRelations, relations -> {
List<ListenableFuture<Boolean>> results = getListenableFutures(relations, cache, false); List<ListenableFuture<Boolean>> results = getListenableFutures(relations, cache, false);
return Futures.allAsList(results); return Futures.allAsList(results);
}); });
@ -248,7 +257,7 @@ public class BaseRelationService implements RelationService {
private List<ListenableFuture<Boolean>> getListenableFutures(List<List<EntityRelation>> relations, Cache cache, boolean isRemove) { private List<ListenableFuture<Boolean>> getListenableFutures(List<List<EntityRelation>> relations, Cache cache, boolean isRemove) {
List<ListenableFuture<Boolean>> results = new ArrayList<>(); List<ListenableFuture<Boolean>> results = new ArrayList<>();
for (List<EntityRelation> relationList : relations) { for (List<EntityRelation> relationList : relations) {
relationList.stream().forEach(relation -> checkFromDeleteAsync(cache, results, relation, isRemove)); relationList.forEach(relation -> checkFromDeleteAsync(cache, results, relation, isRemove));
} }
return results; return results;
} }
@ -315,17 +324,16 @@ public class BaseRelationService implements RelationService {
validate(from); validate(from);
validateTypeGroup(typeGroup); validateTypeGroup(typeGroup);
ListenableFuture<List<EntityRelation>> relations = relationDao.findAllByFrom(from, typeGroup); ListenableFuture<List<EntityRelation>> relations = relationDao.findAllByFrom(from, typeGroup);
ListenableFuture<List<EntityRelationInfo>> relationsInfo = Futures.transform(relations, return Futures.transformAsync(relations,
(AsyncFunction<List<EntityRelation>, List<EntityRelationInfo>>) relations1 -> { relations1 -> {
List<ListenableFuture<EntityRelationInfo>> futures = new ArrayList<>(); List<ListenableFuture<EntityRelationInfo>> futures = new ArrayList<>();
relations1.stream().forEach(relation -> relations1.forEach(relation ->
futures.add(fetchRelationInfoAsync(relation, futures.add(fetchRelationInfoAsync(relation,
relation2 -> relation2.getTo(), EntityRelation::getTo,
(EntityRelationInfo relationInfo, String entityName) -> relationInfo.setToName(entityName))) EntityRelationInfo::setToName))
); );
return Futures.successfulAsList(futures); return Futures.successfulAsList(futures);
}); });
return relationsInfo;
} }
@Cacheable(cacheNames = RELATIONS_CACHE, key = "{#from, #relationType, #typeGroup}") @Cacheable(cacheNames = RELATIONS_CACHE, key = "{#from, #relationType, #typeGroup}")
@ -371,30 +379,27 @@ public class BaseRelationService implements RelationService {
validate(to); validate(to);
validateTypeGroup(typeGroup); validateTypeGroup(typeGroup);
ListenableFuture<List<EntityRelation>> relations = relationDao.findAllByTo(to, typeGroup); ListenableFuture<List<EntityRelation>> relations = relationDao.findAllByTo(to, typeGroup);
ListenableFuture<List<EntityRelationInfo>> relationsInfo = Futures.transform(relations, return Futures.transformAsync(relations,
(AsyncFunction<List<EntityRelation>, List<EntityRelationInfo>>) relations1 -> { relations1 -> {
List<ListenableFuture<EntityRelationInfo>> futures = new ArrayList<>(); List<ListenableFuture<EntityRelationInfo>> futures = new ArrayList<>();
relations1.stream().forEach(relation -> relations1.forEach(relation ->
futures.add(fetchRelationInfoAsync(relation, futures.add(fetchRelationInfoAsync(relation,
relation2 -> relation2.getFrom(), EntityRelation::getFrom,
(EntityRelationInfo relationInfo, String entityName) -> relationInfo.setFromName(entityName))) EntityRelationInfo::setFromName))
); );
return Futures.successfulAsList(futures); return Futures.successfulAsList(futures);
}); });
return relationsInfo;
} }
private ListenableFuture<EntityRelationInfo> fetchRelationInfoAsync(EntityRelation relation, private ListenableFuture<EntityRelationInfo> fetchRelationInfoAsync(EntityRelation relation,
Function<EntityRelation, EntityId> entityIdGetter, Function<EntityRelation, EntityId> entityIdGetter,
BiConsumer<EntityRelationInfo, String> entityNameSetter) { BiConsumer<EntityRelationInfo, String> entityNameSetter) {
ListenableFuture<String> entityName = entityService.fetchEntityNameAsync(entityIdGetter.apply(relation)); ListenableFuture<String> entityName = entityService.fetchEntityNameAsync(entityIdGetter.apply(relation));
ListenableFuture<EntityRelationInfo> entityRelationInfo = return Futures.transform(entityName, entityName1 -> {
Futures.transform(entityName, (Function<String, EntityRelationInfo>) entityName1 -> { EntityRelationInfo entityRelationInfo1 = new EntityRelationInfo(relation);
EntityRelationInfo entityRelationInfo1 = new EntityRelationInfo(relation); entityNameSetter.accept(entityRelationInfo1, entityName1);
entityNameSetter.accept(entityRelationInfo1, entityName1); return entityRelationInfo1;
return entityRelationInfo1; });
});
return entityRelationInfo;
} }
@Cacheable(cacheNames = RELATIONS_CACHE, key = "{#to, #relationType, #typeGroup}") @Cacheable(cacheNames = RELATIONS_CACHE, key = "{#to, #relationType, #typeGroup}")
@ -429,7 +434,7 @@ public class BaseRelationService implements RelationService {
try { try {
ListenableFuture<Set<EntityRelation>> relationSet = findRelationsRecursively(params.getEntityId(), params.getDirection(), maxLvl, new ConcurrentHashMap<>()); ListenableFuture<Set<EntityRelation>> relationSet = findRelationsRecursively(params.getEntityId(), params.getDirection(), maxLvl, new ConcurrentHashMap<>());
return Futures.transform(relationSet, (Function<Set<EntityRelation>, List<EntityRelation>>) input -> { return Futures.transform(relationSet, input -> {
List<EntityRelation> relations = new ArrayList<>(); List<EntityRelation> relations = new ArrayList<>();
if (filters == null || filters.isEmpty()) { if (filters == null || filters.isEmpty()) {
relations.addAll(input); relations.addAll(input);
@ -453,10 +458,10 @@ public class BaseRelationService implements RelationService {
log.trace("Executing findInfoByQuery [{}]", query); log.trace("Executing findInfoByQuery [{}]", query);
ListenableFuture<List<EntityRelation>> relations = findByQuery(query); ListenableFuture<List<EntityRelation>> relations = findByQuery(query);
EntitySearchDirection direction = query.getParameters().getDirection(); EntitySearchDirection direction = query.getParameters().getDirection();
ListenableFuture<List<EntityRelationInfo>> relationsInfo = Futures.transform(relations, return Futures.transformAsync(relations,
(AsyncFunction<List<EntityRelation>, List<EntityRelationInfo>>) relations1 -> { relations1 -> {
List<ListenableFuture<EntityRelationInfo>> futures = new ArrayList<>(); List<ListenableFuture<EntityRelationInfo>> futures = new ArrayList<>();
relations1.stream().forEach(relation -> relations1.forEach(relation ->
futures.add(fetchRelationInfoAsync(relation, futures.add(fetchRelationInfoAsync(relation,
relation2 -> direction == EntitySearchDirection.FROM ? relation2.getTo() : relation2.getFrom(), relation2 -> direction == EntitySearchDirection.FROM ? relation2.getTo() : relation2.getFrom(),
(EntityRelationInfo relationInfo, String entityName) -> { (EntityRelationInfo relationInfo, String entityName) -> {
@ -469,7 +474,6 @@ public class BaseRelationService implements RelationService {
); );
return Futures.successfulAsList(futures); return Futures.successfulAsList(futures);
}); });
return relationsInfo;
} }
protected void validate(EntityRelation relation) { protected void validate(EntityRelation relation) {
@ -575,7 +579,7 @@ public class BaseRelationService implements RelationService {
} }
//TODO: try to remove this blocking operation //TODO: try to remove this blocking operation
List<Set<EntityRelation>> relations = Futures.successfulAsList(futures).get(); List<Set<EntityRelation>> relations = Futures.successfulAsList(futures).get();
relations.forEach(r -> r.forEach(d -> children.add(d))); relations.forEach(r -> r.forEach(children::add));
return Futures.immediateFuture(children); return Futures.immediateFuture(children);
} }

4
dao/src/main/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDao.java

@ -102,12 +102,12 @@ public class JpaAlarmDao extends JpaAbstractDao<AlarmEntity, Alarm> implements A
} }
String relationType = BaseAlarmService.ALARM_RELATION_PREFIX + searchStatusName; String relationType = BaseAlarmService.ALARM_RELATION_PREFIX + searchStatusName;
ListenableFuture<List<EntityRelation>> relations = relationDao.findRelations(affectedEntity, relationType, RelationTypeGroup.ALARM, EntityType.ALARM, query.getPageLink()); ListenableFuture<List<EntityRelation>> relations = relationDao.findRelations(affectedEntity, relationType, RelationTypeGroup.ALARM, EntityType.ALARM, query.getPageLink());
return Futures.transform(relations, (AsyncFunction<List<EntityRelation>, List<AlarmInfo>>) input -> { return Futures.transformAsync(relations, input -> {
List<ListenableFuture<AlarmInfo>> alarmFutures = new ArrayList<>(input.size()); List<ListenableFuture<AlarmInfo>> alarmFutures = new ArrayList<>(input.size());
for (EntityRelation relation : input) { for (EntityRelation relation : input) {
alarmFutures.add(Futures.transform( alarmFutures.add(Futures.transform(
findAlarmByIdAsync(relation.getTo().getId()), findAlarmByIdAsync(relation.getTo().getId()),
(Function<Alarm, AlarmInfo>) AlarmInfo::new)); AlarmInfo::new));
} }
return Futures.successfulAsList(alarmFutures); return Futures.successfulAsList(alarmFutures);
}); });

2
dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/JpaDashboardInfoDao.java

@ -86,7 +86,7 @@ public class JpaDashboardInfoDao extends JpaAbstractSearchTextDao<DashboardInfoE
ListenableFuture<List<EntityRelation>> relations = relationDao.findRelations(new CustomerId(customerId), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.DASHBOARD, EntityType.DASHBOARD, pageLink); ListenableFuture<List<EntityRelation>> relations = relationDao.findRelations(new CustomerId(customerId), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.DASHBOARD, EntityType.DASHBOARD, pageLink);
return Futures.transform(relations, (AsyncFunction<List<EntityRelation>, List<DashboardInfo>>) input -> { return Futures.transformAsync(relations, input -> {
List<ListenableFuture<DashboardInfo>> dashboardFutures = new ArrayList<>(input.size()); List<ListenableFuture<DashboardInfo>> dashboardFutures = new ArrayList<>(input.size());
for (EntityRelation relation : input) { for (EntityRelation relation : input) {
dashboardFutures.add(findByIdAsync(relation.getTo().getId())); dashboardFutures.add(findByIdAsync(relation.getTo().getId()));

2
dao/src/main/java/org/thingsboard/server/dao/timeseries/CassandraBaseTimeseriesDao.java

@ -217,7 +217,7 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem
ListenableFuture<List<Long>> partitionsListFuture = Futures.transform(partitionsFuture, getPartitionsArrayFunction(), readResultsProcessingExecutor); ListenableFuture<List<Long>> partitionsListFuture = Futures.transform(partitionsFuture, getPartitionsArrayFunction(), readResultsProcessingExecutor);
ListenableFuture<List<ResultSet>> aggregationChunks = Futures.transform(partitionsListFuture, ListenableFuture<List<ResultSet>> aggregationChunks = Futures.transformAsync(partitionsListFuture,
getFetchChunksAsyncFunction(entityId, key, aggregation, startTs, endTs), readResultsProcessingExecutor); getFetchChunksAsyncFunction(entityId, key, aggregation, startTs, endTs), readResultsProcessingExecutor);
return Futures.transform(aggregationChunks, new AggregatePartitionsFunction(aggregation, key, ts), readResultsProcessingExecutor); return Futures.transform(aggregationChunks, new AggregatePartitionsFunction(aggregation, key, ts), readResultsProcessingExecutor);

2
dao/src/main/resources/cassandra/schema.cql

@ -692,4 +692,4 @@ CREATE TABLE IF NOT EXISTS thingsboard.rule_node (
configuration text, configuration text,
additional_info text, additional_info text,
PRIMARY KEY (id) PRIMARY KEY (id)
); );

2
dao/src/test/java/org/thingsboard/server/dao/CustomCassandraCQLUnit.java

@ -26,7 +26,7 @@ import com.datastax.driver.core.Session;
import java.util.List; import java.util.List;
public class CustomCassandraCQLUnit extends BaseCassandraUnit { public class CustomCassandraCQLUnit extends BaseCassandraUnit {
private List<CQLDataSet> dataSets; protected List<CQLDataSet> dataSets;
public Session session; public Session session;
public Cluster cluster; public Cluster cluster;

2
dao/src/test/resources/cassandra-test.yaml

@ -103,6 +103,8 @@ commitlog_directory: target/embeddedCassandra/commitlog
hints_directory: target/embeddedCassandra/hints hints_directory: target/embeddedCassandra/hints
cdc_raw_directory: target/embeddedCassandra/cdc
# policy for data disk failures: # policy for data disk failures:
# stop: shut down gossip and Thrift, leaving the node effectively dead, but # stop: shut down gossip and Thrift, leaving the node effectively dead, but
# can still be inspected via JMX. # can still be inspected via JMX.

27
pom.xml

@ -41,10 +41,10 @@
<logback.version>1.2.3</logback.version> <logback.version>1.2.3</logback.version>
<mockito.version>1.9.5</mockito.version> <mockito.version>1.9.5</mockito.version>
<rat.version>0.10</rat.version> <rat.version>0.10</rat.version>
<cassandra.version>3.0.7</cassandra.version> <cassandra.version>3.5.0</cassandra.version>
<cassandra-unit.version>3.0.0.1</cassandra-unit.version> <cassandra-unit.version>3.3.0.2</cassandra-unit.version>
<takari-cpsuite.version>1.2.7</takari-cpsuite.version> <takari-cpsuite.version>1.2.7</takari-cpsuite.version>
<guava.version>18.0</guava.version> <guava.version>21.0</guava.version>
<caffeine.version>2.6.1</caffeine.version> <caffeine.version>2.6.1</caffeine.version>
<commons-lang3.version>3.4</commons-lang3.version> <commons-lang3.version>3.4</commons-lang3.version>
<commons-validator.version>1.5.0</commons-validator.version> <commons-validator.version>1.5.0</commons-validator.version>
@ -59,17 +59,15 @@
<velocity.version>1.7</velocity.version> <velocity.version>1.7</velocity.version>
<velocity-tools.version>2.0</velocity-tools.version> <velocity-tools.version>2.0</velocity-tools.version>
<mail.version>1.4.3</mail.version> <mail.version>1.4.3</mail.version>
<curator.version>2.11.0</curator.version> <curator.version>4.0.1</curator.version>
<protobuf.version>3.0.2</protobuf.version> <protobuf.version>3.0.2</protobuf.version>
<grpc.version>1.0.0</grpc.version> <grpc.version>1.12.0</grpc.version>
<lombok.version>1.16.18</lombok.version> <lombok.version>1.16.18</lombok.version>
<paho.client.version>1.1.0</paho.client.version> <paho.client.version>1.1.0</paho.client.version>
<netty.version>4.1.22.Final</netty.version> <netty.version>4.1.22.Final</netty.version>
<os-maven-plugin.version>1.5.0</os-maven-plugin.version> <os-maven-plugin.version>1.5.0</os-maven-plugin.version>
<rabbitmq.version>3.6.5</rabbitmq.version> <rabbitmq.version>3.6.5</rabbitmq.version>
<kafka.version>0.9.0.0</kafka.version> <kafka.version>0.9.0.0</kafka.version>
<hazelcast.version>3.6.6</hazelcast.version>
<hazelcast-zookeeper.version>3.6.1</hazelcast-zookeeper.version>
<surfire.version>2.19.1</surfire.version> <surfire.version>2.19.1</surfire.version>
<jar-plugin.version>3.0.2</jar-plugin.version> <jar-plugin.version>3.0.2</jar-plugin.version>
<springfox-swagger.version>2.6.1</springfox-swagger.version> <springfox-swagger.version>2.6.1</springfox-swagger.version>
@ -760,26 +758,11 @@
<artifactId>org.eclipse.paho.client.mqttv3</artifactId> <artifactId>org.eclipse.paho.client.mqttv3</artifactId>
<version>${paho.client.version}</version> <version>${paho.client.version}</version>
</dependency> </dependency>
<dependency>
<groupId>com.hazelcast</groupId>
<artifactId>hazelcast-spring</artifactId>
<version>${hazelcast.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.apache.curator</groupId> <groupId>org.apache.curator</groupId>
<artifactId>curator-x-discovery</artifactId> <artifactId>curator-x-discovery</artifactId>
<version>${curator.version}</version> <version>${curator.version}</version>
</dependency> </dependency>
<dependency>
<groupId>com.hazelcast</groupId>
<artifactId>hazelcast-zookeeper</artifactId>
<version>${hazelcast-zookeeper.version}</version>
</dependency>
<dependency>
<groupId>com.hazelcast</groupId>
<artifactId>hazelcast</artifactId>
<version>${hazelcast.version}</version>
</dependency>
<dependency> <dependency>
<groupId>io.springfox</groupId> <groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId> <artifactId>springfox-swagger-ui</artifactId>

6
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbClearAlarmNode.java

@ -56,7 +56,7 @@ public class TbClearAlarmNode extends TbAbstractAlarmNode<TbClearAlarmNodeConfig
@Override @Override
protected ListenableFuture<AlarmResult> processAlarm(TbContext ctx, TbMsg msg) { protected ListenableFuture<AlarmResult> processAlarm(TbContext ctx, TbMsg msg) {
ListenableFuture<Alarm> latest = ctx.getAlarmService().findLatestByOriginatorAndType(ctx.getTenantId(), msg.getOriginator(), config.getAlarmType()); ListenableFuture<Alarm> latest = ctx.getAlarmService().findLatestByOriginatorAndType(ctx.getTenantId(), msg.getOriginator(), config.getAlarmType());
return Futures.transform(latest, (AsyncFunction<Alarm, AlarmResult>) a -> { return Futures.transformAsync(latest, a -> {
if (a != null && !a.getStatus().isCleared()) { if (a != null && !a.getStatus().isCleared()) {
return clearAlarm(ctx, msg, a); return clearAlarm(ctx, msg, a);
} }
@ -66,9 +66,9 @@ public class TbClearAlarmNode extends TbAbstractAlarmNode<TbClearAlarmNodeConfig
private ListenableFuture<AlarmResult> clearAlarm(TbContext ctx, TbMsg msg, Alarm alarm) { private ListenableFuture<AlarmResult> clearAlarm(TbContext ctx, TbMsg msg, Alarm alarm) {
ListenableFuture<JsonNode> asyncDetails = buildAlarmDetails(ctx, msg, alarm.getDetails()); ListenableFuture<JsonNode> asyncDetails = buildAlarmDetails(ctx, msg, alarm.getDetails());
return Futures.transform(asyncDetails, (AsyncFunction<JsonNode, AlarmResult>) details -> { return Futures.transformAsync(asyncDetails, details -> {
ListenableFuture<Boolean> clearFuture = ctx.getAlarmService().clearAlarm(alarm.getId(), details, System.currentTimeMillis()); ListenableFuture<Boolean> clearFuture = ctx.getAlarmService().clearAlarm(alarm.getId(), details, System.currentTimeMillis());
return Futures.transform(clearFuture, (AsyncFunction<Boolean, AlarmResult>) cleared -> { return Futures.transformAsync(clearFuture, cleared -> {
if (cleared && details != null) { if (cleared && details != null) {
alarm.setDetails(details); alarm.setDetails(details);
} }

10
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCreateAlarmNode.java

@ -58,7 +58,7 @@ public class TbCreateAlarmNode extends TbAbstractAlarmNode<TbCreateAlarmNodeConf
@Override @Override
protected ListenableFuture<AlarmResult> processAlarm(TbContext ctx, TbMsg msg) { protected ListenableFuture<AlarmResult> processAlarm(TbContext ctx, TbMsg msg) {
ListenableFuture<Alarm> latest = ctx.getAlarmService().findLatestByOriginatorAndType(ctx.getTenantId(), msg.getOriginator(), config.getAlarmType()); ListenableFuture<Alarm> latest = ctx.getAlarmService().findLatestByOriginatorAndType(ctx.getTenantId(), msg.getOriginator(), config.getAlarmType());
return Futures.transform(latest, (AsyncFunction<Alarm, AlarmResult>) a -> { return Futures.transformAsync(latest, a -> {
if (a == null || a.getStatus().isCleared()) { if (a == null || a.getStatus().isCleared()) {
return createNewAlarm(ctx, msg); return createNewAlarm(ctx, msg);
} else { } else {
@ -70,10 +70,10 @@ public class TbCreateAlarmNode extends TbAbstractAlarmNode<TbCreateAlarmNodeConf
private ListenableFuture<AlarmResult> createNewAlarm(TbContext ctx, TbMsg msg) { private ListenableFuture<AlarmResult> createNewAlarm(TbContext ctx, TbMsg msg) {
ListenableFuture<Alarm> asyncAlarm = Futures.transform(buildAlarmDetails(ctx, msg, null), ListenableFuture<Alarm> asyncAlarm = Futures.transform(buildAlarmDetails(ctx, msg, null),
(Function<JsonNode, Alarm>) details -> buildAlarm(msg, details, ctx.getTenantId())); details -> buildAlarm(msg, details, ctx.getTenantId()));
ListenableFuture<Alarm> asyncCreated = Futures.transform(asyncAlarm, ListenableFuture<Alarm> asyncCreated = Futures.transform(asyncAlarm,
(Function<Alarm, Alarm>) alarm -> ctx.getAlarmService().createOrUpdateAlarm(alarm), ctx.getDbCallbackExecutor()); alarm -> ctx.getAlarmService().createOrUpdateAlarm(alarm), ctx.getDbCallbackExecutor());
return Futures.transform(asyncCreated, (Function<Alarm, AlarmResult>) alarm -> new AlarmResult(true, false, false, alarm)); return Futures.transform(asyncCreated, alarm -> new AlarmResult(true, false, false, alarm));
} }
private ListenableFuture<AlarmResult> updateAlarm(TbContext ctx, TbMsg msg, Alarm alarm) { private ListenableFuture<AlarmResult> updateAlarm(TbContext ctx, TbMsg msg, Alarm alarm) {
@ -85,7 +85,7 @@ public class TbCreateAlarmNode extends TbAbstractAlarmNode<TbCreateAlarmNodeConf
return ctx.getAlarmService().createOrUpdateAlarm(alarm); return ctx.getAlarmService().createOrUpdateAlarm(alarm);
}, ctx.getDbCallbackExecutor()); }, ctx.getDbCallbackExecutor());
return Futures.transform(asyncUpdated, (Function<Alarm, AlarmResult>) a -> new AlarmResult(false, true, false, a)); return Futures.transform(asyncUpdated, a -> new AlarmResult(false, true, false, a));
} }
private Alarm buildAlarm(TbMsg msg, JsonNode details, TenantId tenantId) { private Alarm buildAlarm(TbMsg msg, JsonNode details, TenantId tenantId) {

5
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/EntitiesCustomerIdAsyncLoader.java

@ -43,8 +43,7 @@ public class EntitiesCustomerIdAsyncLoader {
} }
private static <T extends HasCustomerId> ListenableFuture<CustomerId> getCustomerAsync(ListenableFuture<T> future) { private static <T extends HasCustomerId> ListenableFuture<CustomerId> getCustomerAsync(ListenableFuture<T> future) {
return Futures.transform(future, (AsyncFunction<HasCustomerId, CustomerId>) in -> { return Futures.transformAsync(future, in -> in != null ? Futures.immediateFuture(in.getCustomerId())
return in != null ? Futures.immediateFuture(in.getCustomerId()) : Futures.immediateFuture(null));
: Futures.immediateFuture(null);});
} }
} }

5
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/EntitiesRelatedDeviceIdAsyncLoader.java

@ -39,9 +39,8 @@ public class EntitiesRelatedDeviceIdAsyncLoader {
ListenableFuture<List<Device>> asyncDevices = deviceService.findDevicesByQuery(query); ListenableFuture<List<Device>> asyncDevices = deviceService.findDevicesByQuery(query);
return Futures.transform(asyncDevices, (AsyncFunction<List<Device>, DeviceId>) return Futures.transformAsync(asyncDevices, d -> CollectionUtils.isNotEmpty(d) ? Futures.immediateFuture(d.get(0).getId())
d -> CollectionUtils.isNotEmpty(d) ? Futures.immediateFuture(d.get(0).getId()) : Futures.immediateFuture(null));
: Futures.immediateFuture(null));
} }
private static DeviceSearchQuery buildQuery(EntityId originator, DeviceRelationsQuery deviceRelationsQuery) { private static DeviceSearchQuery buildQuery(EntityId originator, DeviceRelationsQuery deviceRelationsQuery) {

10
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/EntitiesRelatedEntityIdAsyncLoader.java

@ -38,13 +38,11 @@ public class EntitiesRelatedEntityIdAsyncLoader {
EntityRelationsQuery query = buildQuery(originator, relationsQuery); EntityRelationsQuery query = buildQuery(originator, relationsQuery);
ListenableFuture<List<EntityRelation>> asyncRelation = relationService.findByQuery(query); ListenableFuture<List<EntityRelation>> asyncRelation = relationService.findByQuery(query);
if (relationsQuery.getDirection() == EntitySearchDirection.FROM) { if (relationsQuery.getDirection() == EntitySearchDirection.FROM) {
return Futures.transform(asyncRelation, (AsyncFunction<? super List<EntityRelation>, EntityId>) return Futures.transformAsync(asyncRelation, r -> CollectionUtils.isNotEmpty(r) ? Futures.immediateFuture(r.get(0).getTo())
r -> CollectionUtils.isNotEmpty(r) ? Futures.immediateFuture(r.get(0).getTo()) : Futures.immediateFuture(null));
: Futures.immediateFuture(null));
} else if (relationsQuery.getDirection() == EntitySearchDirection.TO) { } else if (relationsQuery.getDirection() == EntitySearchDirection.TO) {
return Futures.transform(asyncRelation, (AsyncFunction<? super List<EntityRelation>, EntityId>) return Futures.transformAsync(asyncRelation, r -> CollectionUtils.isNotEmpty(r) ? Futures.immediateFuture(r.get(0).getFrom())
r -> CollectionUtils.isNotEmpty(r) ? Futures.immediateFuture(r.get(0).getFrom()) : Futures.immediateFuture(null));
: Futures.immediateFuture(null));
} }
return Futures.immediateFailedFuture(new IllegalStateException("Unknown direction")); return Futures.immediateFailedFuture(new IllegalStateException("Unknown direction"));
} }

2
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/EntitiesTenantIdAsyncLoader.java

@ -51,7 +51,7 @@ public class EntitiesTenantIdAsyncLoader {
} }
private static <T extends HasTenantId> ListenableFuture<TenantId> getTenantAsync(ListenableFuture<T> future) { private static <T extends HasTenantId> ListenableFuture<TenantId> getTenantAsync(ListenableFuture<T> future) {
return Futures.transform(future, (AsyncFunction<HasTenantId, TenantId>) in -> { return Futures.transformAsync(future, in -> {
return in != null ? Futures.immediateFuture(in.getTenantId()) return in != null ? Futures.immediateFuture(in.getTenantId())
: Futures.immediateFuture(null);}); : Futures.immediateFuture(null);});
} }

4
transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java

@ -38,8 +38,6 @@ import org.thingsboard.server.common.transport.quota.QuotaService;
import org.thingsboard.server.transport.coap.adaptors.CoapTransportAdaptor; import org.thingsboard.server.transport.coap.adaptors.CoapTransportAdaptor;
import org.thingsboard.server.transport.coap.session.CoapExchangeObserverProxy; import org.thingsboard.server.transport.coap.session.CoapExchangeObserverProxy;
import org.thingsboard.server.transport.coap.session.CoapSessionCtx; import org.thingsboard.server.transport.coap.session.CoapSessionCtx;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.ReflectionUtils; import org.springframework.util.ReflectionUtils;
@Slf4j @Slf4j
@ -186,7 +184,7 @@ public class CoapTransportResource extends CoapResource {
throw new IllegalArgumentException("Unsupported msg type: " + type); throw new IllegalArgumentException("Unsupported msg type: " + type);
} }
log.trace("Processing msg: {}", msg); log.trace("Processing msg: {}", msg);
processor.process(new BasicToDeviceActorSessionMsg(ctx.getDevice(), msg)); processor.process(new BasicTransportToDeviceSessionActorMsg(ctx.getDevice(), msg));
} catch (AdaptorException e) { } catch (AdaptorException e) {
log.debug("Failed to decode payload {}", e); log.debug("Failed to decode payload {}", e);
exchange.respond(ResponseCode.BAD_REQUEST, e.getMessage()); exchange.respond(ResponseCode.BAD_REQUEST, e.getMessage());

4
transport/coap/src/test/java/org/thingsboard/server/transport/coap/CoapServerTest.java

@ -108,8 +108,8 @@ public class CoapServerTest {
@Override @Override
public void process(SessionAwareMsg toActorMsg) { public void process(SessionAwareMsg toActorMsg) {
if (toActorMsg instanceof ToDeviceActorSessionMsg) { if (toActorMsg instanceof TransportToDeviceSessionActorMsg) {
AdaptorToSessionActorMsg sessionMsg = ((ToDeviceActorSessionMsg) toActorMsg).getSessionMsg(); AdaptorToSessionActorMsg sessionMsg = ((TransportToDeviceSessionActorMsg) toActorMsg).getSessionMsg();
try { try {
FromDeviceMsg deviceMsg = sessionMsg.getMsg(); FromDeviceMsg deviceMsg = sessionMsg.getMsg();
ToDeviceMsg toDeviceMsg = null; ToDeviceMsg toDeviceMsg = null;

4
transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java

@ -30,7 +30,7 @@ import org.thingsboard.server.common.data.security.DeviceTokenCredentials;
import org.thingsboard.server.common.msg.core.*; import org.thingsboard.server.common.msg.core.*;
import org.thingsboard.server.common.msg.session.AdaptorToSessionActorMsg; import org.thingsboard.server.common.msg.session.AdaptorToSessionActorMsg;
import org.thingsboard.server.common.msg.session.BasicAdaptorToSessionActorMsg; import org.thingsboard.server.common.msg.session.BasicAdaptorToSessionActorMsg;
import org.thingsboard.server.common.msg.session.BasicToDeviceActorSessionMsg; import org.thingsboard.server.common.msg.session.BasicTransportToDeviceSessionActorMsg;
import org.thingsboard.server.common.msg.session.FromDeviceMsg; import org.thingsboard.server.common.msg.session.FromDeviceMsg;
import org.thingsboard.server.common.transport.SessionMsgProcessor; import org.thingsboard.server.common.transport.SessionMsgProcessor;
import org.thingsboard.server.common.transport.adaptor.JsonConverter; import org.thingsboard.server.common.transport.adaptor.JsonConverter;
@ -219,7 +219,7 @@ public class DeviceApiController {
private void process(HttpSessionCtx ctx, FromDeviceMsg request) { private void process(HttpSessionCtx ctx, FromDeviceMsg request) {
AdaptorToSessionActorMsg msg = new BasicAdaptorToSessionActorMsg(ctx, request); AdaptorToSessionActorMsg msg = new BasicAdaptorToSessionActorMsg(ctx, request);
processor.process(new BasicToDeviceActorSessionMsg(ctx.getDevice(), msg)); processor.process(new BasicTransportToDeviceSessionActorMsg(ctx.getDevice(), msg));
} }
private boolean quotaExceeded(HttpServletRequest request, DeferredResult<ResponseEntity> responseWriter) { private boolean quotaExceeded(HttpServletRequest request, DeferredResult<ResponseEntity> responseWriter) {

12
transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java

@ -30,7 +30,7 @@ import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.security.DeviceTokenCredentials; import org.thingsboard.server.common.data.security.DeviceTokenCredentials;
import org.thingsboard.server.common.data.security.DeviceX509Credentials; import org.thingsboard.server.common.data.security.DeviceX509Credentials;
import org.thingsboard.server.common.msg.session.AdaptorToSessionActorMsg; import org.thingsboard.server.common.msg.session.AdaptorToSessionActorMsg;
import org.thingsboard.server.common.msg.session.BasicToDeviceActorSessionMsg; import org.thingsboard.server.common.msg.session.BasicTransportToDeviceSessionActorMsg;
import org.thingsboard.server.common.msg.session.ctrl.SessionCloseMsg; import org.thingsboard.server.common.msg.session.ctrl.SessionCloseMsg;
import org.thingsboard.server.common.transport.SessionMsgProcessor; import org.thingsboard.server.common.transport.SessionMsgProcessor;
import org.thingsboard.server.common.transport.adaptor.AdaptorException; import org.thingsboard.server.common.transport.adaptor.AdaptorException;
@ -207,7 +207,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
log.warn("[{}] Failed to process publish msg [{}][{}]", sessionId, topicName, msgId, e); log.warn("[{}] Failed to process publish msg [{}][{}]", sessionId, topicName, msgId, e);
} }
if (msg != null) { if (msg != null) {
processor.process(new BasicToDeviceActorSessionMsg(deviceSessionCtx.getDevice(), msg)); processor.process(new BasicTransportToDeviceSessionActorMsg(deviceSessionCtx.getDevice(), msg));
} else { } else {
log.info("[{}] Closing current session due to invalid publish msg [{}][{}]", sessionId, topicName, msgId); log.info("[{}] Closing current session due to invalid publish msg [{}][{}]", sessionId, topicName, msgId);
ctx.close(); ctx.close();
@ -227,11 +227,11 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
try { try {
if (topicName.equals(DEVICE_ATTRIBUTES_TOPIC)) { if (topicName.equals(DEVICE_ATTRIBUTES_TOPIC)) {
AdaptorToSessionActorMsg msg = adaptor.convertToActorMsg(deviceSessionCtx, SUBSCRIBE_ATTRIBUTES_REQUEST, mqttMsg); AdaptorToSessionActorMsg msg = adaptor.convertToActorMsg(deviceSessionCtx, SUBSCRIBE_ATTRIBUTES_REQUEST, mqttMsg);
processor.process(new BasicToDeviceActorSessionMsg(deviceSessionCtx.getDevice(), msg)); processor.process(new BasicTransportToDeviceSessionActorMsg(deviceSessionCtx.getDevice(), msg));
grantedQoSList.add(getMinSupportedQos(reqQoS)); grantedQoSList.add(getMinSupportedQos(reqQoS));
} else if (topicName.equals(DEVICE_RPC_REQUESTS_SUB_TOPIC)) { } else if (topicName.equals(DEVICE_RPC_REQUESTS_SUB_TOPIC)) {
AdaptorToSessionActorMsg msg = adaptor.convertToActorMsg(deviceSessionCtx, SUBSCRIBE_RPC_COMMANDS_REQUEST, mqttMsg); AdaptorToSessionActorMsg msg = adaptor.convertToActorMsg(deviceSessionCtx, SUBSCRIBE_RPC_COMMANDS_REQUEST, mqttMsg);
processor.process(new BasicToDeviceActorSessionMsg(deviceSessionCtx.getDevice(), msg)); processor.process(new BasicTransportToDeviceSessionActorMsg(deviceSessionCtx.getDevice(), msg));
grantedQoSList.add(getMinSupportedQos(reqQoS)); grantedQoSList.add(getMinSupportedQos(reqQoS));
} else if (topicName.equals(DEVICE_RPC_RESPONSE_SUB_TOPIC)) { } else if (topicName.equals(DEVICE_RPC_RESPONSE_SUB_TOPIC)) {
grantedQoSList.add(getMinSupportedQos(reqQoS)); grantedQoSList.add(getMinSupportedQos(reqQoS));
@ -261,10 +261,10 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
try { try {
if (topicName.equals(DEVICE_ATTRIBUTES_TOPIC)) { if (topicName.equals(DEVICE_ATTRIBUTES_TOPIC)) {
AdaptorToSessionActorMsg msg = adaptor.convertToActorMsg(deviceSessionCtx, UNSUBSCRIBE_ATTRIBUTES_REQUEST, mqttMsg); AdaptorToSessionActorMsg msg = adaptor.convertToActorMsg(deviceSessionCtx, UNSUBSCRIBE_ATTRIBUTES_REQUEST, mqttMsg);
processor.process(new BasicToDeviceActorSessionMsg(deviceSessionCtx.getDevice(), msg)); processor.process(new BasicTransportToDeviceSessionActorMsg(deviceSessionCtx.getDevice(), msg));
} else if (topicName.equals(DEVICE_RPC_REQUESTS_SUB_TOPIC)) { } else if (topicName.equals(DEVICE_RPC_REQUESTS_SUB_TOPIC)) {
AdaptorToSessionActorMsg msg = adaptor.convertToActorMsg(deviceSessionCtx, UNSUBSCRIBE_RPC_COMMANDS_REQUEST, mqttMsg); AdaptorToSessionActorMsg msg = adaptor.convertToActorMsg(deviceSessionCtx, UNSUBSCRIBE_RPC_COMMANDS_REQUEST, mqttMsg);
processor.process(new BasicToDeviceActorSessionMsg(deviceSessionCtx.getDevice(), msg)); processor.process(new BasicTransportToDeviceSessionActorMsg(deviceSessionCtx.getDevice(), msg));
} else if (topicName.equals(DEVICE_ATTRIBUTES_RESPONSES_TOPIC)) { } else if (topicName.equals(DEVICE_ATTRIBUTES_RESPONSES_TOPIC)) {
deviceSessionCtx.setDisallowAttributeResponses(); deviceSessionCtx.setDisallowAttributeResponses();
} }

14
transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionCtx.java

@ -30,7 +30,7 @@ import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry;
import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.msg.core.*; import org.thingsboard.server.common.msg.core.*;
import org.thingsboard.server.common.msg.session.BasicAdaptorToSessionActorMsg; import org.thingsboard.server.common.msg.session.BasicAdaptorToSessionActorMsg;
import org.thingsboard.server.common.msg.session.BasicToDeviceActorSessionMsg; import org.thingsboard.server.common.msg.session.BasicTransportToDeviceSessionActorMsg;
import org.thingsboard.server.common.msg.session.ctrl.SessionCloseMsg; import org.thingsboard.server.common.msg.session.ctrl.SessionCloseMsg;
import org.thingsboard.server.common.transport.SessionMsgProcessor; import org.thingsboard.server.common.transport.SessionMsgProcessor;
import org.thingsboard.server.common.transport.adaptor.AdaptorException; import org.thingsboard.server.common.transport.adaptor.AdaptorException;
@ -96,8 +96,8 @@ public class GatewaySessionCtx {
GatewayDeviceSessionCtx ctx = new GatewayDeviceSessionCtx(this, device); GatewayDeviceSessionCtx ctx = new GatewayDeviceSessionCtx(this, device);
devices.put(deviceName, ctx); devices.put(deviceName, ctx);
log.debug("[{}] Added device [{}] to the gateway session", gatewaySessionId, deviceName); log.debug("[{}] Added device [{}] to the gateway session", gatewaySessionId, deviceName);
processor.process(new BasicToDeviceActorSessionMsg(device, new BasicAdaptorToSessionActorMsg(ctx, new AttributesSubscribeMsg()))); processor.process(new BasicTransportToDeviceSessionActorMsg(device, new BasicAdaptorToSessionActorMsg(ctx, new AttributesSubscribeMsg())));
processor.process(new BasicToDeviceActorSessionMsg(device, new BasicAdaptorToSessionActorMsg(ctx, new RpcSubscribeMsg()))); processor.process(new BasicTransportToDeviceSessionActorMsg(device, new BasicAdaptorToSessionActorMsg(ctx, new RpcSubscribeMsg())));
} }
} }
@ -136,7 +136,7 @@ public class GatewaySessionCtx {
JsonConverter.parseWithTs(request, element.getAsJsonObject()); JsonConverter.parseWithTs(request, element.getAsJsonObject());
} }
GatewayDeviceSessionCtx deviceSessionCtx = devices.get(deviceName); GatewayDeviceSessionCtx deviceSessionCtx = devices.get(deviceName);
processor.process(new BasicToDeviceActorSessionMsg(deviceSessionCtx.getDevice(), processor.process(new BasicTransportToDeviceSessionActorMsg(deviceSessionCtx.getDevice(),
new BasicAdaptorToSessionActorMsg(deviceSessionCtx, request))); new BasicAdaptorToSessionActorMsg(deviceSessionCtx, request)));
} }
} else { } else {
@ -152,7 +152,7 @@ public class GatewaySessionCtx {
Integer requestId = jsonObj.get("id").getAsInt(); Integer requestId = jsonObj.get("id").getAsInt();
String data = jsonObj.get("data").toString(); String data = jsonObj.get("data").toString();
GatewayDeviceSessionCtx deviceSessionCtx = devices.get(deviceName); GatewayDeviceSessionCtx deviceSessionCtx = devices.get(deviceName);
processor.process(new BasicToDeviceActorSessionMsg(deviceSessionCtx.getDevice(), processor.process(new BasicTransportToDeviceSessionActorMsg(deviceSessionCtx.getDevice(),
new BasicAdaptorToSessionActorMsg(deviceSessionCtx, new ToDeviceRpcResponseMsg(requestId, data)))); new BasicAdaptorToSessionActorMsg(deviceSessionCtx, new ToDeviceRpcResponseMsg(requestId, data))));
} else { } else {
throw new JsonSyntaxException(CAN_T_PARSE_VALUE + json); throw new JsonSyntaxException(CAN_T_PARSE_VALUE + json);
@ -174,7 +174,7 @@ public class GatewaySessionCtx {
JsonObject deviceData = deviceEntry.getValue().getAsJsonObject(); JsonObject deviceData = deviceEntry.getValue().getAsJsonObject();
request.add(JsonConverter.parseValues(deviceData).stream().map(kv -> new BaseAttributeKvEntry(kv, ts)).collect(Collectors.toList())); request.add(JsonConverter.parseValues(deviceData).stream().map(kv -> new BaseAttributeKvEntry(kv, ts)).collect(Collectors.toList()));
GatewayDeviceSessionCtx deviceSessionCtx = devices.get(deviceName); GatewayDeviceSessionCtx deviceSessionCtx = devices.get(deviceName);
processor.process(new BasicToDeviceActorSessionMsg(deviceSessionCtx.getDevice(), processor.process(new BasicTransportToDeviceSessionActorMsg(deviceSessionCtx.getDevice(),
new BasicAdaptorToSessionActorMsg(deviceSessionCtx, request))); new BasicAdaptorToSessionActorMsg(deviceSessionCtx, request)));
} }
} else { } else {
@ -207,7 +207,7 @@ public class GatewaySessionCtx {
request = new BasicGetAttributesRequest(requestId, null, keys); request = new BasicGetAttributesRequest(requestId, null, keys);
} }
GatewayDeviceSessionCtx deviceSessionCtx = devices.get(deviceName); GatewayDeviceSessionCtx deviceSessionCtx = devices.get(deviceName);
processor.process(new BasicToDeviceActorSessionMsg(deviceSessionCtx.getDevice(), processor.process(new BasicTransportToDeviceSessionActorMsg(deviceSessionCtx.getDevice(),
new BasicAdaptorToSessionActorMsg(deviceSessionCtx, request))); new BasicAdaptorToSessionActorMsg(deviceSessionCtx, request)));
ack(msg); ack(msg);
} else { } else {

Loading…
Cancel
Save