Browse Source

Merge remote-tracking branch 'upstream/master'

pull/1183/head
Viacheslav Kukhtyn 8 years ago
parent
commit
527b27013e
  1. 19
      application/pom.xml
  2. 12
      application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java
  3. 21
      application/src/main/java/org/thingsboard/server/actors/app/AppActor.java
  4. 13
      application/src/main/java/org/thingsboard/server/actors/device/DeviceActor.java
  5. 530
      application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java
  6. 40
      application/src/main/java/org/thingsboard/server/actors/device/PendingSessionMsgData.java
  7. 7
      application/src/main/java/org/thingsboard/server/actors/device/SessionInfo.java
  8. 12
      application/src/main/java/org/thingsboard/server/actors/device/ToServerRpcRequestMetadata.java
  9. 40
      application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java
  10. 15
      application/src/main/java/org/thingsboard/server/actors/service/DefaultActorService.java
  11. 156
      application/src/main/java/org/thingsboard/server/actors/session/ASyncMsgProcessor.java
  12. 122
      application/src/main/java/org/thingsboard/server/actors/session/AbstractSessionActorMsgProcessor.java
  13. 143
      application/src/main/java/org/thingsboard/server/actors/session/SessionActor.java
  14. 180
      application/src/main/java/org/thingsboard/server/actors/session/SessionManagerActor.java
  15. 26
      application/src/main/java/org/thingsboard/server/actors/session/SessionTerminationMsg.java
  16. 95
      application/src/main/java/org/thingsboard/server/actors/session/SyncMsgProcessor.java
  17. 20
      application/src/main/java/org/thingsboard/server/actors/shared/ComponentMsgProcessor.java
  18. 36
      application/src/main/java/org/thingsboard/server/actors/shared/SessionTimeoutMsg.java
  19. 2
      application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java
  20. 50
      application/src/main/java/org/thingsboard/server/controller/BaseController.java
  21. 79
      application/src/main/java/org/thingsboard/server/controller/EntityViewController.java
  22. 13
      application/src/main/java/org/thingsboard/server/controller/TelemetryController.java
  23. 2
      application/src/main/java/org/thingsboard/server/service/cluster/discovery/DiscoveryService.java
  24. 8
      application/src/main/java/org/thingsboard/server/service/cluster/discovery/DummyDiscoveryService.java
  25. 8
      application/src/main/java/org/thingsboard/server/service/cluster/discovery/ZkDiscoveryService.java
  26. 111
      application/src/main/java/org/thingsboard/server/service/queue/DefaultMsgQueueService.java
  27. 32
      application/src/main/java/org/thingsboard/server/service/queue/MsgQueueService.java
  28. 3
      application/src/main/java/org/thingsboard/server/service/rpc/ToServerRpcResponseActorMsg.java
  29. 11
      application/src/main/java/org/thingsboard/server/service/script/RemoteJsInvokeService.java
  30. 47
      application/src/main/java/org/thingsboard/server/service/security/ValidationCallback.java
  31. 35
      application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java
  32. 14
      application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetryWebSocketService.java
  33. 173
      application/src/main/java/org/thingsboard/server/service/transport/LocalTransportApiService.java
  34. 225
      application/src/main/java/org/thingsboard/server/service/transport/LocalTransportService.java
  35. 200
      application/src/main/java/org/thingsboard/server/service/transport/RemoteRuleEngineTransportService.java
  36. 109
      application/src/main/java/org/thingsboard/server/service/transport/RemoteTransportApiService.java
  37. 21
      application/src/main/java/org/thingsboard/server/service/transport/RuleEngineTransportService.java
  38. 17
      application/src/main/java/org/thingsboard/server/service/transport/ToRuleEngineMsgDecoder.java
  39. 14
      application/src/main/java/org/thingsboard/server/service/transport/ToTransportMsgEncoder.java
  40. 31
      application/src/main/java/org/thingsboard/server/service/transport/TransportApiRequestDecoder.java
  41. 16
      application/src/main/java/org/thingsboard/server/service/transport/TransportApiResponseEncoder.java
  42. 13
      application/src/main/java/org/thingsboard/server/service/transport/TransportApiService.java
  43. 50
      application/src/main/java/org/thingsboard/server/service/transport/msg/TransportToDeviceActorMsgWrapper.java
  44. 123
      application/src/main/resources/thingsboard.yml
  45. 5
      application/src/test/java/org/thingsboard/server/controller/AbstractRuleEngineControllerTest.java
  46. 9
      application/src/test/java/org/thingsboard/server/rules/flow/AbstractRuleEngineFlowIntegrationTest.java
  47. 69
      application/src/test/java/org/thingsboard/server/rules/lifecycle/AbstractRuleEngineLifecycleIntegrationTest.java
  48. 2
      application/src/test/java/org/thingsboard/server/system/BaseHttpDeviceApiTest.java
  49. 2
      common/data/src/main/java/org/thingsboard/server/common/data/DataConstants.java
  50. 4
      common/message/pom.xml
  51. 2
      common/message/src/main/java/org/thingsboard/server/common/msg/EncryptionUtil.java
  52. 16
      common/message/src/main/java/org/thingsboard/server/common/msg/MsgType.java
  53. 30
      common/message/src/main/java/org/thingsboard/server/common/msg/core/ActorSystemToDeviceSessionActorMsg.java
  54. 47
      common/message/src/main/java/org/thingsboard/server/common/msg/core/AttributesUpdateNotification.java
  55. 28
      common/message/src/main/java/org/thingsboard/server/common/msg/core/AttributesUpdateRequest.java
  56. 52
      common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicActorSystemToDeviceSessionActorMsg.java
  57. 63
      common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicAttributesUpdateRequest.java
  58. 45
      common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicCommandAckResponse.java
  59. 59
      common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicGetAttributesRequest.java
  60. 40
      common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicGetAttributesResponse.java
  61. 79
      common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicResponseMsg.java
  62. 42
      common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicStatusCodeResponse.java
  63. 66
      common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicTelemetryUploadRequest.java
  64. 29
      common/message/src/main/java/org/thingsboard/server/common/msg/core/GetAttributesRequest.java
  65. 22
      common/message/src/main/java/org/thingsboard/server/common/msg/core/GetAttributesResponse.java
  66. 33
      common/message/src/main/java/org/thingsboard/server/common/msg/core/ResponseMsg.java
  67. 30
      common/message/src/main/java/org/thingsboard/server/common/msg/core/RpcUnsubscribeMsg.java
  68. 53
      common/message/src/main/java/org/thingsboard/server/common/msg/core/RuleEngineErrorMsg.java
  69. 38
      common/message/src/main/java/org/thingsboard/server/common/msg/core/SessionCloseNotification.java
  70. 35
      common/message/src/main/java/org/thingsboard/server/common/msg/core/SessionOpenMsg.java
  71. 20
      common/message/src/main/java/org/thingsboard/server/common/msg/core/StatusCodeResponse.java
  72. 29
      common/message/src/main/java/org/thingsboard/server/common/msg/core/TelemetryUploadRequest.java
  73. 41
      common/message/src/main/java/org/thingsboard/server/common/msg/core/ToDeviceRpcRequestMsg.java
  74. 36
      common/message/src/main/java/org/thingsboard/server/common/msg/core/ToDeviceRpcResponseMsg.java
  75. 36
      common/message/src/main/java/org/thingsboard/server/common/msg/core/ToServerRpcRequestMsg.java
  76. 11
      common/message/src/main/java/org/thingsboard/server/common/msg/core/ToServerRpcResponseMsg.java
  77. 107
      common/message/src/main/java/org/thingsboard/server/common/msg/device/BasicDeviceToDeviceActorMsg.java
  78. 41
      common/message/src/main/java/org/thingsboard/server/common/msg/device/DeviceToDeviceActorMsg.java
  79. 22
      common/message/src/main/java/org/thingsboard/server/common/msg/session/AdaptorToSessionActorMsg.java
  80. 32
      common/message/src/main/java/org/thingsboard/server/common/msg/session/BasicAdaptorToSessionActorMsg.java
  81. 32
      common/message/src/main/java/org/thingsboard/server/common/msg/session/BasicSessionActorToAdaptorMsg.java
  82. 44
      common/message/src/main/java/org/thingsboard/server/common/msg/session/BasicSessionMsg.java
  83. 74
      common/message/src/main/java/org/thingsboard/server/common/msg/session/BasicTransportToDeviceSessionActorMsg.java
  84. 24
      common/message/src/main/java/org/thingsboard/server/common/msg/session/FromDeviceMsg.java
  85. 22
      common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionActorToAdaptorMsg.java
  86. 16
      common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionContext.java
  87. 23
      common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionCtrlMsg.java
  88. 24
      common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionMsg.java
  89. 22
      common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionType.java
  90. 26
      common/message/src/main/java/org/thingsboard/server/common/msg/session/ToDeviceMsg.java
  91. 28
      common/message/src/main/java/org/thingsboard/server/common/msg/session/TransportToDeviceSessionActorMsg.java
  92. 68
      common/message/src/main/java/org/thingsboard/server/common/msg/session/ctrl/SessionCloseMsg.java
  93. 36
      common/message/src/main/java/org/thingsboard/server/common/msg/timeout/DeviceActorQueueTimeoutMsg.java
  94. 3
      common/pom.xml
  95. 6
      common/queue/pom.xml
  96. 50
      common/queue/src/main/java/org/thingsboard/server/kafka/AbstractTbKafkaTemplate.java
  97. 66
      common/queue/src/main/java/org/thingsboard/server/kafka/AsyncCallbackTemplate.java
  98. 7
      common/queue/src/main/java/org/thingsboard/server/kafka/TBKafkaConsumerTemplate.java
  99. 47
      common/queue/src/main/java/org/thingsboard/server/kafka/TBKafkaProducerTemplate.java
  100. 16
      common/queue/src/main/java/org/thingsboard/server/kafka/TbKafkaHandler.java

19
application/pom.xml

@ -57,20 +57,20 @@
<artifactId>rule-engine-components</artifactId>
</dependency>
<dependency>
<groupId>org.thingsboard.common</groupId>
<artifactId>transport</artifactId>
<groupId>org.thingsboard.common.transport</groupId>
<artifactId>transport-api</artifactId>
</dependency>
<dependency>
<groupId>org.thingsboard.transport</groupId>
<artifactId>http</artifactId>
<groupId>org.thingsboard.common.transport</groupId>
<artifactId>mqtt</artifactId>
</dependency>
<dependency>
<groupId>org.thingsboard.transport</groupId>
<artifactId>coap</artifactId>
<groupId>org.thingsboard.common.transport</groupId>
<artifactId>http</artifactId>
</dependency>
<dependency>
<groupId>org.thingsboard.transport</groupId>
<artifactId>mqtt</artifactId>
<groupId>org.thingsboard.common.transport</groupId>
<artifactId>coap</artifactId>
</dependency>
<dependency>
<groupId>org.thingsboard</groupId>
@ -542,7 +542,8 @@
<args>
<arg>-PprojectBuildDir=${project.build.directory}</arg>
<arg>-PprojectVersion=${project.version}</arg>
<arg>-PmainJar=${project.build.directory}/${project.build.finalName}-boot.${project.packaging}</arg>
<arg>-PmainJar=${project.build.directory}/${project.build.finalName}-boot.${project.packaging}
</arg>
<arg>-PpkgName=${pkg.name}</arg>
<arg>-PpkgInstallFolder=${pkg.installFolder}</arg>
<arg>-PpkgLogFolder=${pkg.unixLogFolder}</arg>

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

@ -31,6 +31,7 @@ import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import org.thingsboard.rule.engine.api.MailService;
import org.thingsboard.server.actors.service.ActorService;
@ -63,12 +64,12 @@ import org.thingsboard.server.service.encoding.DataDecodingEncodingService;
import org.thingsboard.server.service.executors.DbCallbackExecutorService;
import org.thingsboard.server.service.executors.ExternalCallExecutorService;
import org.thingsboard.server.service.mail.MailExecutorService;
import org.thingsboard.server.service.queue.MsgQueueService;
import org.thingsboard.server.service.rpc.DeviceRpcService;
import org.thingsboard.server.service.script.JsExecutorService;
import org.thingsboard.server.service.script.JsInvokeService;
import org.thingsboard.server.service.state.DeviceStateService;
import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService;
import org.thingsboard.server.service.transport.RuleEngineTransportService;
import javax.annotation.Nullable;
import java.io.IOException;
@ -198,11 +199,12 @@ public class ActorSystemContext {
@Autowired
@Getter
private MsgQueueService msgQueueService;
private DeviceStateService deviceStateService;
@Lazy
@Autowired
@Getter
private DeviceStateService deviceStateService;
private RuleEngineTransportService ruleEngineTransportService;
@Value("${cluster.partition_id}")
@Getter
@ -260,10 +262,6 @@ public class ActorSystemContext {
@Setter
private ActorRef appActor;
@Getter
@Setter
private ActorRef sessionManagerActor;
@Getter
@Setter
private ActorRef statsActor;

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

@ -38,8 +38,6 @@ import org.thingsboard.server.common.msg.TbActorMsg;
import org.thingsboard.server.common.msg.aware.TenantAwareMsg;
import org.thingsboard.server.common.msg.cluster.SendToClusterMsg;
import org.thingsboard.server.common.msg.cluster.ServerAddress;
import org.thingsboard.server.common.msg.core.BasicActorSystemToDeviceSessionActorMsg;
import org.thingsboard.server.common.msg.device.DeviceToDeviceActorMsg;
import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg;
import org.thingsboard.server.dao.model.ModelConstants;
@ -105,7 +103,7 @@ public class AppActor extends RuleChainManagerActor {
case SERVICE_TO_RULE_ENGINE_MSG:
onServiceToRuleEngineMsg((ServiceToRuleEngineMsg) msg);
break;
case DEVICE_SESSION_TO_DEVICE_ACTOR_MSG:
case TRANSPORT_TO_DEVICE_ACTOR_MSG:
case DEVICE_ATTRIBUTES_UPDATE_TO_DEVICE_ACTOR_MSG:
case DEVICE_CREDENTIALS_UPDATE_TO_DEVICE_ACTOR_MSG:
case DEVICE_NAME_OR_TYPE_UPDATE_TO_DEVICE_ACTOR_MSG:
@ -114,19 +112,12 @@ public class AppActor extends RuleChainManagerActor {
case REMOTE_TO_RULE_CHAIN_TELL_NEXT_MSG:
onToDeviceActorMsg((TenantAwareMsg) msg);
break;
case ACTOR_SYSTEM_TO_DEVICE_SESSION_ACTOR_MSG:
onToDeviceSessionMsg((BasicActorSystemToDeviceSessionActorMsg) msg);
break;
default:
return false;
}
return true;
}
private void onToDeviceSessionMsg(BasicActorSystemToDeviceSessionActorMsg msg) {
systemContext.getSessionManagerActor().tell(msg, self());
}
private void onPossibleClusterMsg(SendToClusterMsg msg) {
Optional<ServerAddress> address = systemContext.getRoutingService().resolveById(msg.getEntityId());
if (address.isPresent()) {
@ -169,16 +160,6 @@ public class AppActor extends RuleChainManagerActor {
getOrCreateTenantActor(msg.getTenantId()).tell(msg, ActorRef.noSender());
}
private void processDeviceMsg(DeviceToDeviceActorMsg deviceToDeviceActorMsg) {
TenantId tenantId = deviceToDeviceActorMsg.getTenantId();
ActorRef tenantActor = getOrCreateTenantActor(tenantId);
if (deviceToDeviceActorMsg.getPayload().getMsgType().requiresRulesProcessing()) {
// tenantActor.tell(new RuleChainDeviceMsg(deviceToDeviceActorMsg, ruleManager.getRuleChain(this.context())), context().self());
} else {
tenantActor.tell(deviceToDeviceActorMsg, context().self());
}
}
private ActorRef getOrCreateTenantActor(TenantId tenantId) {
return tenantActors.computeIfAbsent(tenantId, k -> context().actorOf(Props.create(new TenantActor.ActorCreator(systemContext, tenantId))
.withDispatcher(DefaultActorService.CORE_DISPATCHER_NAME), tenantId.toString()));

13
application/src/main/java/org/thingsboard/server/actors/device/DeviceActor.java

@ -26,12 +26,11 @@ import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.msg.TbActorMsg;
import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;
import org.thingsboard.server.common.msg.device.DeviceToDeviceActorMsg;
import org.thingsboard.server.common.msg.timeout.DeviceActorClientSideRpcTimeoutMsg;
import org.thingsboard.server.common.msg.timeout.DeviceActorQueueTimeoutMsg;
import org.thingsboard.server.common.msg.timeout.DeviceActorServerSideRpcTimeoutMsg;
import org.thingsboard.server.service.rpc.ToDeviceRpcRequestActorMsg;
import org.thingsboard.server.service.rpc.ToServerRpcResponseActorMsg;
import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper;
public class DeviceActor extends ContextAwareActor {
@ -50,8 +49,8 @@ public class DeviceActor extends ContextAwareActor {
case CLUSTER_EVENT_MSG:
processor.processClusterEventMsg((ClusterEventMsg) msg);
break;
case DEVICE_SESSION_TO_DEVICE_ACTOR_MSG:
processor.process(context(), (DeviceToDeviceActorMsg) msg);
case TRANSPORT_TO_DEVICE_ACTOR_MSG:
processor.process(context(), (TransportToDeviceActorMsgWrapper) msg);
break;
case DEVICE_ATTRIBUTES_UPDATE_TO_DEVICE_ACTOR_MSG:
processor.processAttributesUpdate(context(), (DeviceAttributesEventNotificationMsg) msg);
@ -74,12 +73,6 @@ public class DeviceActor extends ContextAwareActor {
case DEVICE_ACTOR_CLIENT_SIDE_RPC_TIMEOUT_MSG:
processor.processClientSideRpcTimeout(context(), (DeviceActorClientSideRpcTimeoutMsg) msg);
break;
case DEVICE_ACTOR_QUEUE_TIMEOUT_MSG:
processor.processQueueTimeout(context(), (DeviceActorQueueTimeoutMsg) msg);
break;
case RULE_ENGINE_QUEUE_PUT_ACK_MSG:
processor.processQueueAck(context(), (RuleEngineQueuePutAckMsg) msg);
break;
default:
return false;
}

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

@ -16,7 +16,6 @@
package org.thingsboard.server.actors.device;
import akka.actor.ActorContext;
import akka.actor.ActorRef;
import akka.event.LoggingAdapter;
import com.datastax.driver.core.utils.UUIDs;
import com.google.common.util.concurrent.FutureCallback;
@ -33,7 +32,6 @@ import org.thingsboard.server.actors.shared.AbstractContextAwareMsgProcessor;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.SessionId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.kv.AttributeKey;
import org.thingsboard.server.common.data.kv.AttributeKvEntry;
@ -44,36 +42,34 @@ import org.thingsboard.server.common.msg.TbMsgDataType;
import org.thingsboard.server.common.msg.TbMsgMetaData;
import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;
import org.thingsboard.server.common.msg.cluster.ServerAddress;
import org.thingsboard.server.common.msg.core.ActorSystemToDeviceSessionActorMsg;
import org.thingsboard.server.common.msg.core.AttributesUpdateNotification;
import org.thingsboard.server.common.msg.core.AttributesUpdateRequest;
import org.thingsboard.server.common.msg.core.BasicActorSystemToDeviceSessionActorMsg;
import org.thingsboard.server.common.msg.core.BasicCommandAckResponse;
import org.thingsboard.server.common.msg.core.BasicGetAttributesResponse;
import org.thingsboard.server.common.msg.core.BasicStatusCodeResponse;
import org.thingsboard.server.common.msg.core.GetAttributesRequest;
import org.thingsboard.server.common.msg.core.RuleEngineError;
import org.thingsboard.server.common.msg.core.RuleEngineErrorMsg;
import org.thingsboard.server.common.msg.core.SessionCloseMsg;
import org.thingsboard.server.common.msg.core.SessionCloseNotification;
import org.thingsboard.server.common.msg.core.SessionOpenMsg;
import org.thingsboard.server.common.msg.core.TelemetryUploadRequest;
import org.thingsboard.server.common.msg.core.ToDeviceRpcRequestMsg;
import org.thingsboard.server.common.msg.core.ToDeviceRpcResponseMsg;
import org.thingsboard.server.common.msg.core.ToServerRpcRequestMsg;
import org.thingsboard.server.common.msg.device.DeviceToDeviceActorMsg;
import org.thingsboard.server.common.msg.kv.BasicAttributeKVMsg;
import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest;
import org.thingsboard.server.common.msg.session.FromDeviceMsg;
import org.thingsboard.server.common.msg.session.SessionMsgType;
import org.thingsboard.server.common.msg.session.SessionType;
import org.thingsboard.server.common.msg.session.ToDeviceMsg;
import org.thingsboard.server.common.msg.timeout.DeviceActorClientSideRpcTimeoutMsg;
import org.thingsboard.server.common.msg.timeout.DeviceActorQueueTimeoutMsg;
import org.thingsboard.server.common.msg.timeout.DeviceActorServerSideRpcTimeoutMsg;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg;
import org.thingsboard.server.gen.transport.TransportProtos.DeviceActorToTransportMsg;
import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeRequestMsg;
import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeResponseMsg;
import org.thingsboard.server.gen.transport.TransportProtos.KeyValueProto;
import org.thingsboard.server.gen.transport.TransportProtos.KeyValueType;
import org.thingsboard.server.gen.transport.TransportProtos.PostAttributeMsg;
import org.thingsboard.server.gen.transport.TransportProtos.PostTelemetryMsg;
import org.thingsboard.server.gen.transport.TransportProtos.SessionCloseNotificationProto;
import org.thingsboard.server.gen.transport.TransportProtos.SessionEvent;
import org.thingsboard.server.gen.transport.TransportProtos.SessionEventMsg;
import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto;
import org.thingsboard.server.gen.transport.TransportProtos.SubscribeToAttributeUpdatesMsg;
import org.thingsboard.server.gen.transport.TransportProtos.SubscribeToRPCMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcRequestMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcResponseMsg;
import org.thingsboard.server.gen.transport.TransportProtos.TransportToDeviceActorMsg;
import org.thingsboard.server.gen.transport.TransportProtos.TsKvListProto;
import org.thingsboard.server.gen.transport.TransportProtos.TsKvProto;
import org.thingsboard.server.service.rpc.FromDeviceRpcResponse;
import org.thingsboard.server.service.rpc.ToDeviceRpcRequestActorMsg;
import org.thingsboard.server.service.rpc.ToServerRpcResponseActorMsg;
import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper;
import javax.annotation.Nullable;
import java.util.ArrayList;
@ -87,9 +83,7 @@ import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.TimeoutException;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Collectors;
/**
@ -99,12 +93,11 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
private final TenantId tenantId;
private final DeviceId deviceId;
private final Map<SessionId, SessionInfo> sessions;
private final Map<SessionId, SessionInfo> attributeSubscriptions;
private final Map<SessionId, SessionInfo> rpcSubscriptions;
private final Map<UUID, SessionInfo> sessions;
private final Map<UUID, SessionInfo> attributeSubscriptions;
private final Map<UUID, SessionInfo> rpcSubscriptions;
private final Map<Integer, ToDeviceRpcRequestMetadata> toDeviceRpcPendingMap;
private final Map<Integer, ToServerRpcRequestMetadata> toServerRpcPendingMap;
private final Map<UUID, PendingSessionMsgData> pendingMsgs;
private final Gson gson = new Gson();
private final JsonParser jsonParser = new JsonParser();
@ -123,7 +116,6 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
this.rpcSubscriptions = new HashMap<>();
this.toDeviceRpcPendingMap = new HashMap<>();
this.toServerRpcPendingMap = new HashMap<>();
this.pendingMsgs = new HashMap<>();
initAttributes();
}
@ -139,11 +131,8 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
void processRpcRequest(ActorContext context, ToDeviceRpcRequestActorMsg msg) {
ToDeviceRpcRequest request = msg.getMsg();
ToDeviceRpcRequestBody body = request.getBody();
ToDeviceRpcRequestMsg rpcRequest = new ToDeviceRpcRequestMsg(
rpcSeq++,
body.getMethod(),
body.getParams()
);
ToDeviceRpcRequestMsg rpcRequest = ToDeviceRpcRequestMsg.newBuilder().setRequestId(
rpcSeq++).setMethodName(body.getMethod()).setParams(body.getParams()).build();
long timeout = request.getExpirationTime() - System.currentTimeMillis();
if (timeout <= 0) {
@ -152,11 +141,10 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
}
boolean sent = rpcSubscriptions.size() > 0;
Set<SessionId> syncSessionSet = new HashSet<>();
Set<UUID> syncSessionSet = new HashSet<>();
rpcSubscriptions.entrySet().forEach(sub -> {
ActorSystemToDeviceSessionActorMsg response = new BasicActorSystemToDeviceSessionActorMsg(rpcRequest, sub.getKey());
sendMsgToSessionActor(response, sub.getValue().getServer());
if (SessionType.SYNC == sub.getValue().getType()) {
sendToTransport(rpcRequest, sub.getKey(), sub.getValue().getNodeId());
if (TransportProtos.SessionType.SYNC == sub.getValue().getType()) {
syncSessionSet.add(sub.getKey());
}
});
@ -191,32 +179,11 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
}
}
void processQueueTimeout(ActorContext context, DeviceActorQueueTimeoutMsg msg) {
PendingSessionMsgData data = pendingMsgs.remove(msg.getId());
if (data != null) {
logger.debug("[{}] Queue put [{}] timeout detected!", deviceId, msg.getId());
ToDeviceMsg toDeviceMsg = new RuleEngineErrorMsg(data.getSessionMsgType(), RuleEngineError.QUEUE_PUT_TIMEOUT);
sendMsgToSessionActor(new BasicActorSystemToDeviceSessionActorMsg(toDeviceMsg, data.getSessionId()), data.getServerAddress());
}
}
void processQueueAck(ActorContext context, RuleEngineQueuePutAckMsg msg) {
PendingSessionMsgData data = pendingMsgs.remove(msg.getId());
if (data != null && data.isReplyOnQueueAck()) {
int remainingAcks = data.getAckMsgCount() - 1;
data.setAckMsgCount(remainingAcks);
logger.debug("[{}] Queue put [{}] ack detected. Remaining acks: {}!", deviceId, msg.getId(), remainingAcks);
if (remainingAcks == 0) {
ToDeviceMsg toDeviceMsg = BasicStatusCodeResponse.onSuccess(data.getSessionMsgType(), data.getRequestId());
sendMsgToSessionActor(new BasicActorSystemToDeviceSessionActorMsg(toDeviceMsg, data.getSessionId()), data.getServerAddress());
}
}
}
private void sendPendingRequests(ActorContext context, SessionId sessionId, SessionType type, Optional<ServerAddress> server) {
private void sendPendingRequests(ActorContext context, UUID sessionId, SessionInfoProto sessionInfo) {
TransportProtos.SessionType sessionType = getSessionType(sessionId);
if (!toDeviceRpcPendingMap.isEmpty()) {
logger.debug("[{}] Pushing {} pending RPC messages to new async session [{}]", deviceId, toDeviceRpcPendingMap.size(), sessionId);
if (type == SessionType.SYNC) {
if (sessionType == TransportProtos.SessionType.SYNC) {
logger.debug("[{}] Cleanup sync rpc session [{}]", deviceId, sessionId);
rpcSubscriptions.remove(sessionId);
}
@ -224,16 +191,16 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
logger.debug("[{}] No pending RPC messages for new async session [{}]", deviceId, sessionId);
}
Set<Integer> sentOneWayIds = new HashSet<>();
if (type == SessionType.ASYNC) {
toDeviceRpcPendingMap.entrySet().forEach(processPendingRpc(context, sessionId, server, sentOneWayIds));
if (sessionType == TransportProtos.SessionType.ASYNC) {
toDeviceRpcPendingMap.entrySet().forEach(processPendingRpc(context, sessionId, sessionInfo.getNodeId(), sentOneWayIds));
} else {
toDeviceRpcPendingMap.entrySet().stream().findFirst().ifPresent(processPendingRpc(context, sessionId, server, sentOneWayIds));
toDeviceRpcPendingMap.entrySet().stream().findFirst().ifPresent(processPendingRpc(context, sessionId, sessionInfo.getNodeId(), sentOneWayIds));
}
sentOneWayIds.forEach(toDeviceRpcPendingMap::remove);
}
private Consumer<Map.Entry<Integer, ToDeviceRpcRequestMetadata>> processPendingRpc(ActorContext context, SessionId sessionId, Optional<ServerAddress> server, Set<Integer> sentOneWayIds) {
private Consumer<Map.Entry<Integer, ToDeviceRpcRequestMetadata>> processPendingRpc(ActorContext context, UUID sessionId, String nodeId, Set<Integer> sentOneWayIds) {
return entry -> {
ToDeviceRpcRequestActorMsg requestActorMsg = entry.getValue().getMsg();
ToDeviceRpcRequest request = entry.getValue().getMsg().getMsg();
@ -242,40 +209,40 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
sentOneWayIds.add(entry.getKey());
systemContext.getDeviceRpcService().processRpcResponseFromDevice(new FromDeviceRpcResponse(request.getId(), requestActorMsg.getServerAddress(), null, null));
}
ToDeviceRpcRequestMsg rpcRequest = new ToDeviceRpcRequestMsg(
entry.getKey(),
body.getMethod(),
body.getParams()
);
ActorSystemToDeviceSessionActorMsg response = new BasicActorSystemToDeviceSessionActorMsg(rpcRequest, sessionId);
sendMsgToSessionActor(response, server);
ToDeviceRpcRequestMsg rpcRequest = ToDeviceRpcRequestMsg.newBuilder().setRequestId(
entry.getKey()).setMethodName(body.getMethod()).setParams(body.getParams()).build();
sendToTransport(rpcRequest, sessionId, nodeId);
};
}
void process(ActorContext context, DeviceToDeviceActorMsg msg) {
processSubscriptionCommands(context, msg);
processRpcResponses(context, msg);
processSessionStateMsgs(msg);
SessionMsgType sessionMsgType = msg.getPayload().getMsgType();
if (sessionMsgType.requiresRulesProcessing()) {
switch (sessionMsgType) {
case GET_ATTRIBUTES_REQUEST:
handleGetAttributesRequest(msg);
break;
case POST_ATTRIBUTES_REQUEST:
handlePostAttributesRequest(context, msg);
reportActivity();
break;
case POST_TELEMETRY_REQUEST:
handlePostTelemetryRequest(context, msg);
reportActivity();
break;
case TO_SERVER_RPC_REQUEST:
handleClientSideRPCRequest(context, msg);
reportActivity();
break;
}
void process(ActorContext context, TransportToDeviceActorMsgWrapper wrapper) {
TransportToDeviceActorMsg msg = wrapper.getMsg();
if (msg.hasSessionEvent()) {
processSessionStateMsgs(msg.getSessionInfo(), msg.getSessionEvent());
}
if (msg.hasSubscribeToAttributes()) {
processSubscriptionCommands(context, msg.getSessionInfo(), msg.getSubscribeToAttributes());
}
if (msg.hasSubscribeToRPC()) {
processSubscriptionCommands(context, msg.getSessionInfo(), msg.getSubscribeToRPC());
}
if (msg.hasPostAttributes()) {
handlePostAttributesRequest(context, msg.getSessionInfo(), msg.getPostAttributes());
reportActivity();
}
if (msg.hasPostTelemetry()) {
handlePostTelemetryRequest(context, msg.getSessionInfo(), msg.getPostTelemetry());
reportActivity();
}
if (msg.hasGetAttributes()) {
handleGetAttributesRequest(context, msg.getSessionInfo(), msg.getGetAttributes());
}
if (msg.hasToDeviceRPCCallResponse()) {
processRpcResponses(context, msg.getSessionInfo(), msg.getToDeviceRPCCallResponse());
}
if (msg.hasToServerRPCCallRequest()) {
handleClientSideRPCRequest(context, msg.getSessionInfo(), msg.getToServerRPCCallRequest());
reportActivity();
}
}
@ -291,27 +258,27 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
systemContext.getDeviceStateService().onDeviceDisconnect(deviceId);
}
private void handleGetAttributesRequest(DeviceToDeviceActorMsg src) {
GetAttributesRequest request = (GetAttributesRequest) src.getPayload();
ListenableFuture<List<AttributeKvEntry>> clientAttributesFuture = getAttributeKvEntries(deviceId, DataConstants.CLIENT_SCOPE, request.getClientAttributeNames());
ListenableFuture<List<AttributeKvEntry>> sharedAttributesFuture = getAttributeKvEntries(deviceId, DataConstants.SHARED_SCOPE, request.getSharedAttributeNames());
private void handleGetAttributesRequest(ActorContext context, SessionInfoProto sessionInfo, GetAttributeRequestMsg request) {
ListenableFuture<List<AttributeKvEntry>> clientAttributesFuture = getAttributeKvEntries(deviceId, DataConstants.CLIENT_SCOPE, toOptionalSet(request.getClientAttributeNamesList()));
ListenableFuture<List<AttributeKvEntry>> sharedAttributesFuture = getAttributeKvEntries(deviceId, DataConstants.SHARED_SCOPE, toOptionalSet(request.getSharedAttributeNamesList()));
int requestId = request.getRequestId();
Futures.addCallback(Futures.allAsList(Arrays.asList(clientAttributesFuture, sharedAttributesFuture)), new FutureCallback<List<List<AttributeKvEntry>>>() {
@Override
public void onSuccess(@Nullable List<List<AttributeKvEntry>> result) {
BasicGetAttributesResponse response = BasicGetAttributesResponse.onSuccess(request.getMsgType(),
request.getRequestId(), BasicAttributeKVMsg.from(result.get(0), result.get(1)));
sendMsgToSessionActor(new BasicActorSystemToDeviceSessionActorMsg(response, src.getSessionId()), src.getServerAddress());
GetAttributeResponseMsg responseMsg = GetAttributeResponseMsg.newBuilder()
.setRequestId(requestId)
.addAllClientAttributeList(toTsKvProtos(result.get(0)))
.addAllSharedAttributeList(toTsKvProtos(result.get(1)))
.build();
sendToTransport(responseMsg, sessionInfo);
}
@Override
public void onFailure(Throwable t) {
if (t instanceof Exception) {
ToDeviceMsg toDeviceMsg = BasicStatusCodeResponse.onError(SessionMsgType.GET_ATTRIBUTES_REQUEST, request.getRequestId(), (Exception) t);
sendMsgToSessionActor(new BasicActorSystemToDeviceSessionActorMsg(toDeviceMsg, src.getSessionId()), src.getServerAddress());
} else {
logger.error("[{}] Failed to process attributes request", deviceId, t);
}
GetAttributeResponseMsg responseMsg = GetAttributeResponseMsg.newBuilder()
.setError(t.getMessage())
.build();
sendToTransport(responseMsg, sessionInfo);
}
});
}
@ -328,115 +295,98 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
}
}
private void handlePostAttributesRequest(ActorContext context, DeviceToDeviceActorMsg src) {
AttributesUpdateRequest request = (AttributesUpdateRequest) src.getPayload();
JsonObject json = new JsonObject();
for (AttributeKvEntry kv : request.getAttributes()) {
kv.getBooleanValue().ifPresent(v -> json.addProperty(kv.getKey(), v));
kv.getLongValue().ifPresent(v -> json.addProperty(kv.getKey(), v));
kv.getDoubleValue().ifPresent(v -> json.addProperty(kv.getKey(), v));
kv.getStrValue().ifPresent(v -> json.addProperty(kv.getKey(), v));
}
TbMsg tbMsg = new TbMsg(UUIDs.timeBased(), SessionMsgType.POST_ATTRIBUTES_REQUEST.name(), deviceId, defaultMetaData.copy(), TbMsgDataType.JSON, gson.toJson(json), null, null, 0L);
PendingSessionMsgData msgData = new PendingSessionMsgData(src.getSessionId(), src.getServerAddress(),
SessionMsgType.POST_ATTRIBUTES_REQUEST, request.getRequestId(), true, 1);
pushToRuleEngineWithTimeout(context, tbMsg, msgData);
private void handlePostAttributesRequest(ActorContext context, SessionInfoProto sessionInfo, PostAttributeMsg postAttributes) {
JsonObject json = getJsonObject(postAttributes.getKvList());
TbMsg tbMsg = new TbMsg(UUIDs.timeBased(), SessionMsgType.POST_ATTRIBUTES_REQUEST.name(), deviceId, defaultMetaData.copy(),
TbMsgDataType.JSON, gson.toJson(json), null, null, 0L);
pushToRuleEngine(context, tbMsg);
}
private void handlePostTelemetryRequest(ActorContext context, DeviceToDeviceActorMsg src) {
TelemetryUploadRequest request = (TelemetryUploadRequest) src.getPayload();
Map<Long, List<KvEntry>> tsData = request.getData();
PendingSessionMsgData msgData = new PendingSessionMsgData(src.getSessionId(), src.getServerAddress(),
SessionMsgType.POST_TELEMETRY_REQUEST, request.getRequestId(), true, tsData.size());
for (Map.Entry<Long, List<KvEntry>> entry : tsData.entrySet()) {
JsonObject json = new JsonObject();
for (KvEntry kv : entry.getValue()) {
kv.getBooleanValue().ifPresent(v -> json.addProperty(kv.getKey(), v));
kv.getLongValue().ifPresent(v -> json.addProperty(kv.getKey(), v));
kv.getDoubleValue().ifPresent(v -> json.addProperty(kv.getKey(), v));
kv.getStrValue().ifPresent(v -> json.addProperty(kv.getKey(), v));
}
private void handlePostTelemetryRequest(ActorContext context, SessionInfoProto sessionInfo, PostTelemetryMsg postTelemetry) {
for (TsKvListProto tsKv : postTelemetry.getTsKvListList()) {
JsonObject json = getJsonObject(tsKv.getKvList());
TbMsgMetaData metaData = defaultMetaData.copy();
metaData.putValue("ts", entry.getKey() + "");
metaData.putValue("ts", tsKv.getTs() + "");
TbMsg tbMsg = new TbMsg(UUIDs.timeBased(), SessionMsgType.POST_TELEMETRY_REQUEST.name(), deviceId, metaData, TbMsgDataType.JSON, gson.toJson(json), null, null, 0L);
pushToRuleEngineWithTimeout(context, tbMsg, msgData);
pushToRuleEngine(context, tbMsg);
}
}
private void handleClientSideRPCRequest(ActorContext context, DeviceToDeviceActorMsg src) {
ToServerRpcRequestMsg request = (ToServerRpcRequestMsg) src.getPayload();
private void handleClientSideRPCRequest(ActorContext context, SessionInfoProto sessionInfo, TransportProtos.ToServerRpcRequestMsg request) {
UUID sessionId = getSessionId(sessionInfo);
JsonObject json = new JsonObject();
json.addProperty("method", request.getMethod());
json.addProperty("method", request.getMethodName());
json.add("params", jsonParser.parse(request.getParams()));
TbMsgMetaData requestMetaData = defaultMetaData.copy();
requestMetaData.putValue("requestId", Integer.toString(request.getRequestId()));
TbMsg tbMsg = new TbMsg(UUIDs.timeBased(), SessionMsgType.TO_SERVER_RPC_REQUEST.name(), deviceId, requestMetaData, TbMsgDataType.JSON, gson.toJson(json), null, null, 0L);
PendingSessionMsgData msgData = new PendingSessionMsgData(src.getSessionId(), src.getServerAddress(), SessionMsgType.TO_SERVER_RPC_REQUEST, request.getRequestId(), false, 1);
pushToRuleEngineWithTimeout(context, tbMsg, msgData);
context.parent().tell(new DeviceActorToRuleEngineMsg(context.self(), tbMsg), context.self());
scheduleMsgWithDelay(context, new DeviceActorClientSideRpcTimeoutMsg(request.getRequestId(), systemContext.getClientSideRpcTimeout()), systemContext.getClientSideRpcTimeout());
toServerRpcPendingMap.put(request.getRequestId(), new ToServerRpcRequestMetadata(src.getSessionId(), src.getSessionType(), src.getServerAddress()));
toServerRpcPendingMap.put(request.getRequestId(), new ToServerRpcRequestMetadata(sessionId, getSessionType(sessionId), sessionInfo.getNodeId()));
}
private TransportProtos.SessionType getSessionType(UUID sessionId) {
return sessions.containsKey(sessionId) ? TransportProtos.SessionType.ASYNC : TransportProtos.SessionType.SYNC;
}
public void processClientSideRpcTimeout(ActorContext context, DeviceActorClientSideRpcTimeoutMsg msg) {
void processClientSideRpcTimeout(ActorContext context, DeviceActorClientSideRpcTimeoutMsg msg) {
ToServerRpcRequestMetadata data = toServerRpcPendingMap.remove(msg.getId());
if (data != null) {
logger.debug("[{}] Client side RPC request [{}] timeout detected!", deviceId, msg.getId());
ToDeviceMsg toDeviceMsg = new RuleEngineErrorMsg(SessionMsgType.TO_SERVER_RPC_REQUEST, RuleEngineError.TIMEOUT);
sendMsgToSessionActor(new BasicActorSystemToDeviceSessionActorMsg(toDeviceMsg, data.getSessionId()), data.getServer());
sendToTransport(TransportProtos.ToServerRpcResponseMsg.newBuilder()
.setRequestId(msg.getId()).setError("timeout").build()
, data.getSessionId(), data.getNodeId());
}
}
void processToServerRPCResponse(ActorContext context, ToServerRpcResponseActorMsg msg) {
ToServerRpcRequestMetadata data = toServerRpcPendingMap.remove(msg.getMsg().getRequestId());
int requestId = msg.getMsg().getRequestId();
ToServerRpcRequestMetadata data = toServerRpcPendingMap.remove(requestId);
if (data != null) {
sendMsgToSessionActor(new BasicActorSystemToDeviceSessionActorMsg(msg.getMsg(), data.getSessionId()), data.getServer());
sendToTransport(TransportProtos.ToServerRpcResponseMsg.newBuilder()
.setRequestId(requestId).setPayload(msg.getMsg().getData()).build()
, data.getSessionId(), data.getNodeId());
}
}
private void pushToRuleEngineWithTimeout(ActorContext context, TbMsg tbMsg, PendingSessionMsgData pendingMsgData) {
SessionMsgType sessionMsgType = pendingMsgData.getSessionMsgType();
int requestId = pendingMsgData.getRequestId();
if (systemContext.isQueuePersistenceEnabled()) {
pendingMsgs.put(tbMsg.getId(), pendingMsgData);
scheduleMsgWithDelay(context, new DeviceActorQueueTimeoutMsg(tbMsg.getId(), systemContext.getQueuePersistenceTimeout()), systemContext.getQueuePersistenceTimeout());
} else {
ActorSystemToDeviceSessionActorMsg response = new BasicActorSystemToDeviceSessionActorMsg(BasicStatusCodeResponse.onSuccess(sessionMsgType, requestId), pendingMsgData.getSessionId());
sendMsgToSessionActor(response, pendingMsgData.getServerAddress());
}
private void pushToRuleEngine(ActorContext context, TbMsg tbMsg) {
context.parent().tell(new DeviceActorToRuleEngineMsg(context.self(), tbMsg), context.self());
}
void processAttributesUpdate(ActorContext context, DeviceAttributesEventNotificationMsg msg) {
if (attributeSubscriptions.size() > 0) {
ToDeviceMsg notification = null;
boolean hasNotificationData = false;
AttributeUpdateNotificationMsg.Builder notification = AttributeUpdateNotificationMsg.newBuilder();
if (msg.isDeleted()) {
List<AttributeKey> sharedKeys = msg.getDeletedKeys().stream()
List<String> sharedKeys = msg.getDeletedKeys().stream()
.filter(key -> DataConstants.SHARED_SCOPE.equals(key.getScope()))
.map(AttributeKey::getAttributeKey)
.collect(Collectors.toList());
notification = new AttributesUpdateNotification(BasicAttributeKVMsg.fromDeleted(sharedKeys));
if (!sharedKeys.isEmpty()) {
notification.addAllSharedDeleted(sharedKeys);
hasNotificationData = true;
}
} else {
if (DataConstants.SHARED_SCOPE.equals(msg.getScope())) {
List<AttributeKvEntry> attributes = new ArrayList<>(msg.getValues());
if (attributes.size() > 0) {
notification = new AttributesUpdateNotification(BasicAttributeKVMsg.fromShared(attributes));
List<TsKvProto> sharedUpdated = msg.getValues().stream().map(this::toTsKvProto)
.collect(Collectors.toList());
if (!sharedUpdated.isEmpty()) {
notification.addAllSharedUpdated(sharedUpdated);
hasNotificationData = true;
}
} else {
logger.debug("[{}] No public server side attributes changed!", deviceId);
}
}
}
if (notification != null) {
ToDeviceMsg finalNotification = notification;
if (hasNotificationData) {
AttributeUpdateNotificationMsg finalNotification = notification.build();
attributeSubscriptions.entrySet().forEach(sub -> {
ActorSystemToDeviceSessionActorMsg response = new BasicActorSystemToDeviceSessionActorMsg(finalNotification, sub.getKey());
sendMsgToSessionActor(response, sub.getValue().getServer());
sendToTransport(finalNotification, sub.getKey(), sub.getValue().getNodeId());
});
}
} else {
@ -444,75 +394,83 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
}
}
private void processRpcResponses(ActorContext context, DeviceToDeviceActorMsg msg) {
SessionId sessionId = msg.getSessionId();
FromDeviceMsg inMsg = msg.getPayload();
if (inMsg.getMsgType() == SessionMsgType.TO_DEVICE_RPC_RESPONSE) {
logger.debug("[{}] Processing rpc command response [{}]", deviceId, sessionId);
ToDeviceRpcResponseMsg responseMsg = (ToDeviceRpcResponseMsg) inMsg;
ToDeviceRpcRequestMetadata requestMd = toDeviceRpcPendingMap.remove(responseMsg.getRequestId());
boolean success = requestMd != null;
if (success) {
systemContext.getDeviceRpcService().processRpcResponseFromDevice(new FromDeviceRpcResponse(requestMd.getMsg().getMsg().getId(),
requestMd.getMsg().getServerAddress(), responseMsg.getData(), null));
} else {
logger.debug("[{}] Rpc command response [{}] is stale!", deviceId, responseMsg.getRequestId());
}
if (msg.getSessionType() == SessionType.SYNC) {
BasicCommandAckResponse response = success
? BasicCommandAckResponse.onSuccess(SessionMsgType.TO_DEVICE_RPC_REQUEST, responseMsg.getRequestId())
: BasicCommandAckResponse.onError(SessionMsgType.TO_DEVICE_RPC_REQUEST, responseMsg.getRequestId(), new TimeoutException());
sendMsgToSessionActor(new BasicActorSystemToDeviceSessionActorMsg(response, msg.getSessionId()), msg.getServerAddress());
}
private void processRpcResponses(ActorContext context, SessionInfoProto sessionInfo, ToDeviceRpcResponseMsg responseMsg) {
UUID sessionId = getSessionId(sessionInfo);
logger.debug("[{}] Processing rpc command response [{}]", deviceId, sessionId);
ToDeviceRpcRequestMetadata requestMd = toDeviceRpcPendingMap.remove(responseMsg.getRequestId());
boolean success = requestMd != null;
if (success) {
systemContext.getDeviceRpcService().processRpcResponseFromDevice(new FromDeviceRpcResponse(requestMd.getMsg().getMsg().getId(),
requestMd.getMsg().getServerAddress(), responseMsg.getPayload(), null));
} else {
logger.debug("[{}] Rpc command response [{}] is stale!", deviceId, responseMsg.getRequestId());
}
}
void processClusterEventMsg(ClusterEventMsg msg) {
if (!msg.isAdded()) {
logger.debug("[{}] Clearing attributes/rpc subscription for server [{}]", deviceId, msg.getServerAddress());
Predicate<Map.Entry<SessionId, SessionInfo>> filter = e -> e.getValue().getServer()
.map(serverAddress -> serverAddress.equals(msg.getServerAddress())).orElse(false);
attributeSubscriptions.entrySet().removeIf(filter);
rpcSubscriptions.entrySet().removeIf(filter);
}
// if (!msg.isAdded()) {
// logger.debug("[{}] Clearing attributes/rpc subscription for server [{}]", deviceId, msg.getServerAddress());
// Predicate<Map.Entry<SessionId, SessionInfo>> filter = e -> e.getValue().getServer()
// .map(serverAddress -> serverAddress.equals(msg.getServerAddress())).orElse(false);
// attributeSubscriptions.entrySet().removeIf(filter);
// rpcSubscriptions.entrySet().removeIf(filter);
// }
}
private void processSubscriptionCommands(ActorContext context, DeviceToDeviceActorMsg msg) {
SessionId sessionId = msg.getSessionId();
SessionType sessionType = msg.getSessionType();
FromDeviceMsg inMsg = msg.getPayload();
if (inMsg.getMsgType() == SessionMsgType.SUBSCRIBE_ATTRIBUTES_REQUEST) {
logger.debug("[{}] Registering attributes subscription for session [{}]", deviceId, sessionId);
attributeSubscriptions.put(sessionId, new SessionInfo(sessionType, msg.getServerAddress()));
} else if (inMsg.getMsgType() == SessionMsgType.UNSUBSCRIBE_ATTRIBUTES_REQUEST) {
private void processSubscriptionCommands(ActorContext context, SessionInfoProto sessionInfo, SubscribeToAttributeUpdatesMsg subscribeCmd) {
UUID sessionId = getSessionId(sessionInfo);
if (subscribeCmd.getUnsubscribe()) {
logger.debug("[{}] Canceling attributes subscription for session [{}]", deviceId, sessionId);
attributeSubscriptions.remove(sessionId);
} else if (inMsg.getMsgType() == SessionMsgType.SUBSCRIBE_RPC_COMMANDS_REQUEST) {
logger.debug("[{}] Registering rpc subscription for session [{}][{}]", deviceId, sessionId, sessionType);
rpcSubscriptions.put(sessionId, new SessionInfo(sessionType, msg.getServerAddress()));
sendPendingRequests(context, sessionId, sessionType, msg.getServerAddress());
} else if (inMsg.getMsgType() == SessionMsgType.UNSUBSCRIBE_RPC_COMMANDS_REQUEST) {
logger.debug("[{}] Canceling rpc subscription for session [{}][{}]", deviceId, sessionId, sessionType);
} else {
SessionInfo session = sessions.get(sessionId);
if (session == null) {
session = new SessionInfo(TransportProtos.SessionType.SYNC, sessionInfo.getNodeId());
}
logger.debug("[{}] Registering attributes subscription for session [{}]", deviceId, sessionId);
attributeSubscriptions.put(sessionId, session);
}
}
private UUID getSessionId(SessionInfoProto sessionInfo) {
return new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB());
}
private void processSubscriptionCommands(ActorContext context, SessionInfoProto sessionInfo, SubscribeToRPCMsg subscribeCmd) {
UUID sessionId = getSessionId(sessionInfo);
if (subscribeCmd.getUnsubscribe()) {
logger.debug("[{}] Canceling rpc subscription for session [{}]", deviceId, sessionId);
rpcSubscriptions.remove(sessionId);
} else {
SessionInfo session = sessions.get(sessionId);
if (session == null) {
session = new SessionInfo(TransportProtos.SessionType.SYNC, sessionInfo.getNodeId());
}
logger.debug("[{}] Registering rpc subscription for session [{}]", deviceId, sessionId);
rpcSubscriptions.put(sessionId, session);
sendPendingRequests(context, sessionId, sessionInfo);
}
}
private void processSessionStateMsgs(DeviceToDeviceActorMsg msg) {
SessionId sessionId = msg.getSessionId();
FromDeviceMsg inMsg = msg.getPayload();
if (inMsg instanceof SessionOpenMsg) {
private void processSessionStateMsgs(SessionInfoProto sessionInfo, SessionEventMsg msg) {
UUID sessionId = getSessionId(sessionInfo);
if (msg.getEvent() == SessionEvent.OPEN) {
if(sessions.containsKey(sessionId)){
logger.debug("[{}] Received duplicate session open event [{}]", deviceId, sessionId);
return;
}
logger.debug("[{}] Processing new session [{}]", deviceId, sessionId);
if (sessions.size() >= systemContext.getMaxConcurrentSessionsPerDevice()) {
SessionId sessionIdToRemove = sessions.keySet().stream().findFirst().orElse(null);
UUID sessionIdToRemove = sessions.keySet().stream().findFirst().orElse(null);
if (sessionIdToRemove != null) {
closeSession(sessionIdToRemove, sessions.remove(sessionIdToRemove));
}
}
sessions.put(sessionId, new SessionInfo(SessionType.ASYNC, msg.getServerAddress()));
sessions.put(sessionId, new SessionInfo(TransportProtos.SessionType.ASYNC, sessionInfo.getNodeId()));
if (sessions.size() == 1) {
reportSessionOpen();
}
} else if (inMsg instanceof SessionCloseMsg) {
} else if (msg.getEvent() == SessionEvent.CLOSED) {
logger.debug("[{}] Canceling subscriptions for closed session [{}]", deviceId, sessionId);
sessions.remove(sessionId);
attributeSubscriptions.remove(sessionId);
@ -523,25 +481,18 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
}
}
private void sendMsgToSessionActor(ActorSystemToDeviceSessionActorMsg response, Optional<ServerAddress> sessionAddress) {
if (sessionAddress.isPresent()) {
ServerAddress address = sessionAddress.get();
logger.debug("{} Forwarding msg: {}", address, response);
systemContext.getRpcService().tell(systemContext.getEncodingService()
.convertToProtoDataMessage(sessionAddress.get(), response));
} else {
systemContext.getSessionManagerActor().tell(response, ActorRef.noSender());
}
}
void processCredentialsUpdate() {
sessions.forEach(this::closeSession);
attributeSubscriptions.clear();
rpcSubscriptions.clear();
}
private void closeSession(SessionId sessionId, SessionInfo sessionInfo) {
sendMsgToSessionActor(new BasicActorSystemToDeviceSessionActorMsg(new SessionCloseNotification(), sessionId), sessionInfo.getServer());
private void closeSession(UUID sessionId, SessionInfo sessionInfo) {
DeviceActorToTransportMsg msg = DeviceActorToTransportMsg.newBuilder()
.setSessionIdMSB(sessionId.getMostSignificantBits())
.setSessionIdLSB(sessionId.getLeastSignificantBits())
.setSessionCloseNotification(SessionCloseNotificationProto.getDefaultInstance()).build();
systemContext.getRuleEngineTransportService().process(sessionInfo.getNodeId(), msg);
}
void processNameOrTypeUpdate(DeviceNameOrTypeUpdateMsg msg) {
@ -552,4 +503,107 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
this.defaultMetaData.putValue("deviceType", deviceType);
}
private JsonObject getJsonObject(List<KeyValueProto> tsKv) {
JsonObject json = new JsonObject();
for (KeyValueProto kv : tsKv) {
switch (kv.getType()) {
case BOOLEAN_V:
json.addProperty(kv.getKey(), kv.getBoolV());
break;
case LONG_V:
json.addProperty(kv.getKey(), kv.getLongV());
break;
case DOUBLE_V:
json.addProperty(kv.getKey(), kv.getDoubleV());
break;
case STRING_V:
json.addProperty(kv.getKey(), kv.getStringV());
break;
}
}
return json;
}
private Optional<Set<String>> toOptionalSet(List<String> strings) {
if (strings == null || strings.isEmpty()) {
return Optional.empty();
} else {
return Optional.of(new HashSet<>(strings));
}
}
private void sendToTransport(GetAttributeResponseMsg responseMsg, SessionInfoProto sessionInfo) {
DeviceActorToTransportMsg msg = DeviceActorToTransportMsg.newBuilder()
.setSessionIdMSB(sessionInfo.getSessionIdMSB())
.setSessionIdLSB(sessionInfo.getSessionIdLSB())
.setGetAttributesResponse(responseMsg).build();
systemContext.getRuleEngineTransportService().process(sessionInfo.getNodeId(), msg);
}
private void sendToTransport(AttributeUpdateNotificationMsg notificationMsg, UUID sessionId, String nodeId) {
DeviceActorToTransportMsg msg = DeviceActorToTransportMsg.newBuilder()
.setSessionIdMSB(sessionId.getMostSignificantBits())
.setSessionIdLSB(sessionId.getLeastSignificantBits())
.setAttributeUpdateNotification(notificationMsg).build();
systemContext.getRuleEngineTransportService().process(nodeId, msg);
}
private void sendToTransport(ToDeviceRpcRequestMsg rpcMsg, UUID sessionId, String nodeId) {
DeviceActorToTransportMsg msg = DeviceActorToTransportMsg.newBuilder()
.setSessionIdMSB(sessionId.getMostSignificantBits())
.setSessionIdLSB(sessionId.getLeastSignificantBits())
.setToDeviceRequest(rpcMsg).build();
systemContext.getRuleEngineTransportService().process(nodeId, msg);
}
private void sendToTransport(TransportProtos.ToServerRpcResponseMsg rpcMsg, UUID sessionId, String nodeId) {
DeviceActorToTransportMsg msg = DeviceActorToTransportMsg.newBuilder()
.setSessionIdMSB(sessionId.getMostSignificantBits())
.setSessionIdLSB(sessionId.getLeastSignificantBits())
.setToServerResponse(rpcMsg).build();
systemContext.getRuleEngineTransportService().process(nodeId, msg);
}
private List<TsKvProto> toTsKvProtos(@Nullable List<AttributeKvEntry> result) {
List<TsKvProto> clientAttributes;
if (result == null || result.isEmpty()) {
clientAttributes = Collections.emptyList();
} else {
clientAttributes = new ArrayList<>(result.size());
for (AttributeKvEntry attrEntry : result) {
clientAttributes.add(toTsKvProto(attrEntry));
}
}
return clientAttributes;
}
private TsKvProto toTsKvProto(AttributeKvEntry attrEntry) {
return TsKvProto.newBuilder().setTs(attrEntry.getLastUpdateTs())
.setKv(toKeyValueProto(attrEntry)).build();
}
private KeyValueProto toKeyValueProto(KvEntry kvEntry) {
KeyValueProto.Builder builder = KeyValueProto.newBuilder();
builder.setKey(kvEntry.getKey());
switch (kvEntry.getDataType()) {
case BOOLEAN:
builder.setType(KeyValueType.BOOLEAN_V);
builder.setBoolV(kvEntry.getBooleanValue().get());
break;
case DOUBLE:
builder.setType(KeyValueType.DOUBLE_V);
builder.setDoubleV(kvEntry.getDoubleValue().get());
break;
case LONG:
builder.setType(KeyValueType.LONG_V);
builder.setLongV(kvEntry.getLongValue().get());
break;
case STRING:
builder.setType(KeyValueType.STRING_V);
builder.setStringV(kvEntry.getStrValue().get());
break;
}
return builder.build();
}
}

40
application/src/main/java/org/thingsboard/server/actors/device/PendingSessionMsgData.java

@ -1,40 +0,0 @@
/**
* Copyright © 2016-2018 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.actors.device;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.thingsboard.server.common.data.id.SessionId;
import org.thingsboard.server.common.msg.cluster.ServerAddress;
import org.thingsboard.server.common.msg.session.SessionMsgType;
import java.util.Optional;
/**
* Created by ashvayka on 17.04.18.
*/
@Data
@AllArgsConstructor
public final class PendingSessionMsgData {
private final SessionId sessionId;
private final Optional<ServerAddress> serverAddress;
private final SessionMsgType sessionMsgType;
private final int requestId;
private final boolean replyOnQueueAck;
private int ackMsgCount;
}

7
application/src/main/java/org/thingsboard/server/actors/device/SessionInfo.java

@ -16,10 +16,7 @@
package org.thingsboard.server.actors.device;
import lombok.Data;
import org.thingsboard.server.common.msg.cluster.ServerAddress;
import org.thingsboard.server.common.msg.session.SessionType;
import java.util.Optional;
import org.thingsboard.server.gen.transport.TransportProtos.SessionType;
/**
* @author Andrew Shvayka
@ -27,5 +24,5 @@ import java.util.Optional;
@Data
public class SessionInfo {
private final SessionType type;
private final Optional<ServerAddress> server;
private final String nodeId;
}

12
application/src/main/java/org/thingsboard/server/actors/device/ToServerRpcRequestMetadata.java

@ -16,18 +16,16 @@
package org.thingsboard.server.actors.device;
import lombok.Data;
import org.thingsboard.server.common.data.id.SessionId;
import org.thingsboard.server.common.msg.cluster.ServerAddress;
import org.thingsboard.server.common.msg.session.SessionType;
import org.thingsboard.server.gen.transport.TransportProtos;
import java.util.Optional;
import java.util.UUID;
/**
* @author Andrew Shvayka
*/
@Data
public class ToServerRpcRequestMetadata {
private final SessionId sessionId;
private final SessionType type;
private final Optional<ServerAddress> server;
private final UUID sessionId;
private final TransportProtos.SessionType type;
private final String nodeId;
}

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

@ -25,7 +25,6 @@ import java.util.Optional;
import org.thingsboard.server.actors.ActorSystemContext;
import org.thingsboard.server.actors.device.DeviceActorToRuleEngineMsg;
import org.thingsboard.server.actors.device.RuleEngineQueuePutAckMsg;
import org.thingsboard.server.actors.service.DefaultActorService;
import org.thingsboard.server.actors.shared.ComponentMsgProcessor;
import org.thingsboard.server.common.data.EntityType;
@ -90,26 +89,12 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
nodeActors.put(ruleNode.getId(), new RuleNodeCtx(tenantId, self, ruleNodeActor, ruleNode));
}
initRoutes(ruleChain, ruleNodeList);
reprocess(ruleNodeList);
started = true;
} else {
onUpdate(context);
}
}
private void reprocess(List<RuleNode> ruleNodeList) {
for (RuleNode ruleNode : ruleNodeList) {
for (TbMsg tbMsg : queue.findUnprocessed(tenantId, ruleNode.getId().getId(), systemContext.getQueuePartitionId())) {
pushMsgToNode(nodeActors.get(ruleNode.getId()), tbMsg, "");
}
}
if (firstNode != null) {
for (TbMsg tbMsg : queue.findUnprocessed(tenantId, entityId.getId(), systemContext.getQueuePartitionId())) {
pushMsgToNode(firstNode, tbMsg, "");
}
}
}
@Override
public void onUpdate(ActorContext context) throws Exception {
RuleChain ruleChain = service.findRuleChainById(entityId);
@ -134,7 +119,6 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
});
initRoutes(ruleChain, ruleNodeList);
reprocess(ruleNodeList);
}
@Override
@ -188,17 +172,14 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
void onServiceToRuleEngineMsg(ServiceToRuleEngineMsg envelope) {
checkActive();
if (firstNode != null) {
putToQueue(enrichWithRuleChainId(envelope.getTbMsg()), msg -> pushMsgToNode(firstNode, msg, ""));
pushMsgToNode(firstNode, enrichWithRuleChainId(envelope.getTbMsg()), "");
}
}
void onDeviceActorToRuleEngineMsg(DeviceActorToRuleEngineMsg envelope) {
checkActive();
if (firstNode != null) {
putToQueue(enrichWithRuleChainId(envelope.getTbMsg()), msg -> {
pushMsgToNode(firstNode, msg, "");
envelope.getCallbackRef().tell(new RuleEngineQueuePutAckMsg(msg.getId()), self);
});
pushMsgToNode(firstNode, enrichWithRuleChainId(envelope.getTbMsg()), "");
}
}
@ -206,15 +187,16 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
checkActive();
if (envelope.isEnqueue()) {
if (firstNode != null) {
putToQueue(enrichWithRuleChainId(envelope.getMsg()), msg -> pushMsgToNode(firstNode, msg, envelope.getFromRelationType()));
pushMsgToNode(firstNode, enrichWithRuleChainId(envelope.getMsg()), envelope.getFromRelationType());
}
} else {
if (firstNode != null) {
pushMsgToNode(firstNode, envelope.getMsg(), envelope.getFromRelationType());
} else {
TbMsg msg = envelope.getMsg();
EntityId ackId = msg.getRuleNodeId() != null ? msg.getRuleNodeId() : msg.getRuleChainId();
queue.ack(tenantId, envelope.getMsg(), ackId.getId(), msg.getClusterPartition());
// TODO: Ack this message in Kafka
// TbMsg msg = envelope.getMsg();
// EntityId ackId = msg.getRuleNodeId() != null ? msg.getRuleNodeId() : msg.getRuleChainId();
// queue.ack(tenantId, envelope.getMsg(), ackId.getId(), msg.getClusterPartition());
}
}
}
@ -249,7 +231,8 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
EntityId ackId = msg.getRuleNodeId() != null ? msg.getRuleNodeId() : msg.getRuleChainId();
if (relationsCount == 0) {
if (ackId != null) {
queue.ack(tenantId, msg, ackId.getId(), msg.getClusterPartition());
// TODO: Ack this message in Kafka
// queue.ack(tenantId, msg, ackId.getId(), msg.getClusterPartition());
}
} else if (relationsCount == 1) {
for (RuleNodeRelation relation : relations) {
@ -269,7 +252,8 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
}
//TODO: Ideally this should happen in async way when all targets confirm that the copied messages are successfully written to corresponding target queues.
if (ackId != null) {
queue.ack(tenantId, msg, ackId.getId(), msg.getClusterPartition());
// TODO: Ack this message in Kafka
// queue.ack(tenantId, msg, ackId.getId(), msg.getClusterPartition());
}
}
}
@ -296,7 +280,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
RuleNodeId targetId = new RuleNodeId(target.getId());
RuleNodeCtx targetNodeCtx = nodeActors.get(targetId);
TbMsg copy = msg.copy(UUIDs.timeBased(), entityId, targetId, DEFAULT_CLUSTER_PARTITION);
putToQueue(copy, queuedMsg -> pushMsgToNode(targetNodeCtx, queuedMsg, fromRelationType));
pushMsgToNode(targetNodeCtx, copy, fromRelationType);
}
private void pushToTarget(TbMsg msg, EntityId target, String fromRelationType) {

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

@ -30,7 +30,6 @@ import org.thingsboard.server.actors.app.AppActor;
import org.thingsboard.server.actors.rpc.RpcBroadcastMsg;
import org.thingsboard.server.actors.rpc.RpcManagerActor;
import org.thingsboard.server.actors.rpc.RpcSessionCreateRequestMsg;
import org.thingsboard.server.actors.session.SessionManagerActor;
import org.thingsboard.server.actors.stats.StatsActor;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.id.DeviceId;
@ -38,7 +37,6 @@ import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
import org.thingsboard.server.common.msg.TbActorMsg;
import org.thingsboard.server.common.msg.aware.SessionAwareMsg;
import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;
import org.thingsboard.server.common.msg.cluster.SendToClusterMsg;
import org.thingsboard.server.common.msg.cluster.ServerAddress;
@ -90,8 +88,6 @@ public class DefaultActorService implements ActorService {
private ActorRef appActor;
private ActorRef sessionManagerActor;
private ActorRef rpcManagerActor;
@PostConstruct
@ -104,10 +100,6 @@ public class DefaultActorService implements ActorService {
appActor = system.actorOf(Props.create(new AppActor.ActorCreator(actorContext)).withDispatcher(APP_DISPATCHER_NAME), "appActor");
actorContext.setAppActor(appActor);
sessionManagerActor = system.actorOf(Props.create(new SessionManagerActor.ActorCreator(actorContext)).withDispatcher(CORE_DISPATCHER_NAME),
"sessionManagerActor");
actorContext.setSessionManagerActor(sessionManagerActor);
rpcManagerActor = system.actorOf(Props.create(new RpcManagerActor.ActorCreator(actorContext)).withDispatcher(CORE_DISPATCHER_NAME),
"rpcManagerActor");
@ -134,12 +126,6 @@ public class DefaultActorService implements ActorService {
appActor.tell(msg, ActorRef.noSender());
}
@Override
public void process(SessionAwareMsg msg) {
log.debug("Processing session aware msg: {}", msg);
sessionManagerActor.tell(msg, ActorRef.noSender());
}
@Override
public void onServerAdded(ServerInstance server) {
log.trace("Processing onServerAdded msg: {}", server);
@ -194,7 +180,6 @@ public class DefaultActorService implements ActorService {
private void broadcast(ClusterEventMsg msg) {
this.appActor.tell(msg, ActorRef.noSender());
this.sessionManagerActor.tell(msg, ActorRef.noSender());
this.rpcManagerActor.tell(msg, ActorRef.noSender());
}

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

@ -1,156 +0,0 @@
/**
* Copyright © 2016-2018 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.actors.session;
import akka.actor.ActorContext;
import akka.event.LoggingAdapter;
import org.thingsboard.server.actors.ActorSystemContext;
import org.thingsboard.server.actors.shared.SessionTimeoutMsg;
import org.thingsboard.server.common.data.id.SessionId;
import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;
import org.thingsboard.server.common.msg.cluster.ServerAddress;
import org.thingsboard.server.common.msg.core.AttributesSubscribeMsg;
import org.thingsboard.server.common.msg.core.ResponseMsg;
import org.thingsboard.server.common.msg.core.RpcSubscribeMsg;
import org.thingsboard.server.common.msg.core.SessionCloseMsg;
import org.thingsboard.server.common.msg.core.SessionOpenMsg;
import org.thingsboard.server.common.msg.device.DeviceToDeviceActorMsg;
import org.thingsboard.server.common.msg.session.BasicSessionActorToAdaptorMsg;
import org.thingsboard.server.common.msg.session.FromDeviceMsg;
import org.thingsboard.server.common.msg.session.FromDeviceRequestMsg;
import org.thingsboard.server.common.msg.session.SessionMsgType;
import org.thingsboard.server.common.msg.session.SessionType;
import org.thingsboard.server.common.msg.session.ToDeviceMsg;
import org.thingsboard.server.common.msg.session.TransportToDeviceSessionActorMsg;
import org.thingsboard.server.common.msg.session.ex.SessionException;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
class ASyncMsgProcessor extends AbstractSessionActorMsgProcessor {
private boolean firstMsg = true;
private Map<Integer, DeviceToDeviceActorMsg> pendingMap = new HashMap<>();
private Optional<ServerAddress> currentTargetServer;
private boolean subscribedToAttributeUpdates;
private boolean subscribedToRpcCommands;
public ASyncMsgProcessor(ActorSystemContext ctx, LoggingAdapter logger, SessionId sessionId) {
super(ctx, logger, sessionId);
}
@Override
protected void processToDeviceActorMsg(ActorContext ctx, TransportToDeviceSessionActorMsg msg) {
updateSessionCtx(msg, SessionType.ASYNC);
DeviceToDeviceActorMsg pendingMsg = toDeviceMsg(msg);
FromDeviceMsg fromDeviceMsg = pendingMsg.getPayload();
if (firstMsg) {
if (fromDeviceMsg.getMsgType() != SessionMsgType.SESSION_OPEN) {
toDeviceMsg(new SessionOpenMsg()).ifPresent(m -> forwardToAppActor(ctx, m));
}
firstMsg = false;
}
switch (fromDeviceMsg.getMsgType()) {
case POST_TELEMETRY_REQUEST:
case POST_ATTRIBUTES_REQUEST:
FromDeviceRequestMsg requestMsg = (FromDeviceRequestMsg) fromDeviceMsg;
if (requestMsg.getRequestId() >= 0) {
logger.debug("[{}] Pending request {} registered", requestMsg.getRequestId(), requestMsg.getMsgType());
//TODO: handle duplicates.
pendingMap.put(requestMsg.getRequestId(), pendingMsg);
}
break;
case SUBSCRIBE_ATTRIBUTES_REQUEST:
subscribedToAttributeUpdates = true;
break;
case UNSUBSCRIBE_ATTRIBUTES_REQUEST:
subscribedToAttributeUpdates = false;
break;
case SUBSCRIBE_RPC_COMMANDS_REQUEST:
subscribedToRpcCommands = true;
break;
case UNSUBSCRIBE_RPC_COMMANDS_REQUEST:
subscribedToRpcCommands = false;
break;
default:
break;
}
currentTargetServer = forwardToAppActor(ctx, pendingMsg);
}
@Override
public void processToDeviceMsg(ActorContext context, ToDeviceMsg msg) {
try {
if (msg.getSessionMsgType() != SessionMsgType.SESSION_CLOSE) {
switch (msg.getSessionMsgType()) {
case STATUS_CODE_RESPONSE:
case GET_ATTRIBUTES_RESPONSE:
ResponseMsg responseMsg = (ResponseMsg) msg;
if (responseMsg.getRequestId() >= 0) {
logger.debug("[{}] Pending request processed: {}", responseMsg.getRequestId(), responseMsg);
pendingMap.remove(responseMsg.getRequestId());
}
break;
default:
break;
}
sessionCtx.onMsg(new BasicSessionActorToAdaptorMsg(this.sessionCtx, msg));
} else {
sessionCtx.onMsg(org.thingsboard.server.common.msg.session.ctrl.SessionCloseMsg.onCredentialsRevoked(sessionCtx.getSessionId()));
}
} catch (SessionException e) {
logger.warning("Failed to push session response msg", e);
}
}
@Override
public void processTimeoutMsg(ActorContext context, SessionTimeoutMsg msg) {
// TODO Auto-generated method stub
}
@Override
protected void cleanupSession(ActorContext ctx) {
toDeviceMsg(new SessionCloseMsg()).ifPresent(m -> forwardToAppActor(ctx, m));
}
@Override
public void processClusterEvent(ActorContext context, ClusterEventMsg msg) {
if (pendingMap.size() > 0 || subscribedToAttributeUpdates || subscribedToRpcCommands) {
Optional<ServerAddress> newTargetServer = systemContext.getRoutingService().resolveById(getDeviceId());
if (!newTargetServer.equals(currentTargetServer)) {
firstMsg = true;
currentTargetServer = newTargetServer;
pendingMap.values().forEach(v -> {
forwardToAppActor(context, v, currentTargetServer);
if (currentTargetServer.isPresent()) {
logger.debug("[{}] Forwarded msg to new server: {}", sessionId, currentTargetServer.get());
} else {
logger.debug("[{}] Forwarded msg to local server.", sessionId);
}
});
if (subscribedToAttributeUpdates) {
toDeviceMsg(new AttributesSubscribeMsg()).ifPresent(m -> forwardToAppActor(context, m, currentTargetServer));
logger.debug("[{}] Forwarded attributes subscription.", sessionId);
}
if (subscribedToRpcCommands) {
toDeviceMsg(new RpcSubscribeMsg()).ifPresent(m -> forwardToAppActor(context, m, currentTargetServer));
logger.debug("[{}] Forwarded rpc commands subscription.", sessionId);
}
}
}
}
}

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

@ -1,122 +0,0 @@
/**
* Copyright © 2016-2018 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.actors.session;
import akka.actor.ActorContext;
import akka.actor.ActorRef;
import akka.event.LoggingAdapter;
import org.thingsboard.server.actors.ActorSystemContext;
import org.thingsboard.server.actors.shared.AbstractContextAwareMsgProcessor;
import org.thingsboard.server.actors.shared.SessionTimeoutMsg;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.SessionId;
import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;
import org.thingsboard.server.common.msg.cluster.SendToClusterMsg;
import org.thingsboard.server.common.msg.cluster.ServerAddress;
import org.thingsboard.server.common.msg.device.BasicDeviceToDeviceActorMsg;
import org.thingsboard.server.common.msg.device.DeviceToDeviceActorMsg;
import org.thingsboard.server.common.msg.session.AdaptorToSessionActorMsg;
import org.thingsboard.server.common.msg.session.FromDeviceMsg;
import org.thingsboard.server.common.msg.session.SessionContext;
import org.thingsboard.server.common.msg.session.SessionCtrlMsg;
import org.thingsboard.server.common.msg.session.SessionType;
import org.thingsboard.server.common.msg.session.ToDeviceMsg;
import org.thingsboard.server.common.msg.session.TransportToDeviceSessionActorMsg;
import org.thingsboard.server.common.msg.session.ctrl.SessionCloseMsg;
import java.util.Optional;
abstract class AbstractSessionActorMsgProcessor extends AbstractContextAwareMsgProcessor {
protected final SessionId sessionId;
protected SessionContext sessionCtx;
protected DeviceToDeviceActorMsg deviceToDeviceActorMsgPrototype;
protected AbstractSessionActorMsgProcessor(ActorSystemContext ctx, LoggingAdapter logger, SessionId sessionId) {
super(ctx, logger);
this.sessionId = sessionId;
}
protected abstract void processToDeviceActorMsg(ActorContext ctx, TransportToDeviceSessionActorMsg msg);
protected abstract void processTimeoutMsg(ActorContext context, SessionTimeoutMsg msg);
protected abstract void processToDeviceMsg(ActorContext context, ToDeviceMsg msg);
public abstract void processClusterEvent(ActorContext context, ClusterEventMsg msg);
protected void processSessionCtrlMsg(ActorContext ctx, SessionCtrlMsg msg) {
if (msg instanceof SessionCloseMsg) {
cleanupSession(ctx);
terminateSession(ctx, sessionId);
}
}
protected void cleanupSession(ActorContext ctx) {
}
protected void updateSessionCtx(TransportToDeviceSessionActorMsg msg, SessionType type) {
sessionCtx = msg.getSessionMsg().getSessionContext();
deviceToDeviceActorMsgPrototype = new BasicDeviceToDeviceActorMsg(msg, type);
}
protected DeviceToDeviceActorMsg toDeviceMsg(TransportToDeviceSessionActorMsg msg) {
AdaptorToSessionActorMsg adaptorMsg = msg.getSessionMsg();
return new BasicDeviceToDeviceActorMsg(deviceToDeviceActorMsgPrototype, adaptorMsg.getMsg());
}
protected Optional<DeviceToDeviceActorMsg> toDeviceMsg(FromDeviceMsg msg) {
if (deviceToDeviceActorMsgPrototype != null) {
return Optional.of(new BasicDeviceToDeviceActorMsg(deviceToDeviceActorMsgPrototype, msg));
} else {
return Optional.empty();
}
}
protected Optional<ServerAddress> forwardToAppActor(ActorContext ctx, DeviceToDeviceActorMsg toForward) {
Optional<ServerAddress> address = systemContext.getRoutingService().resolveById(toForward.getDeviceId());
forwardToAppActor(ctx, toForward, address);
return address;
}
protected Optional<ServerAddress> forwardToAppActorIfAddressChanged(ActorContext ctx, DeviceToDeviceActorMsg toForward, Optional<ServerAddress> oldAddress) {
Optional<ServerAddress> newAddress = systemContext.getRoutingService().resolveById(toForward.getDeviceId());
if (!newAddress.equals(oldAddress)) {
getAppActor().tell(new SendToClusterMsg(toForward.getDeviceId(), toForward
.toOtherAddress(systemContext.getRoutingService().getCurrentServer())), ctx.self());
}
return newAddress;
}
protected void forwardToAppActor(ActorContext ctx, DeviceToDeviceActorMsg toForward, Optional<ServerAddress> address) {
if (address.isPresent()) {
systemContext.getRpcService().tell(systemContext.getEncodingService().convertToProtoDataMessage(address.get(),
toForward.toOtherAddress(systemContext.getRoutingService().getCurrentServer())));
} else {
getAppActor().tell(toForward, ctx.self());
}
}
public static void terminateSession(ActorContext ctx, SessionId sessionId) {
ctx.parent().tell(new SessionTerminationMsg(sessionId), ActorRef.noSender());
ctx.stop(ctx.self());
}
public DeviceId getDeviceId() {
return deviceToDeviceActorMsgPrototype.getDeviceId();
}
}

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

@ -1,143 +0,0 @@
/**
* Copyright © 2016-2018 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.actors.session;
import akka.actor.OneForOneStrategy;
import akka.actor.SupervisorStrategy;
import akka.event.Logging;
import akka.event.LoggingAdapter;
import org.thingsboard.server.actors.ActorSystemContext;
import org.thingsboard.server.actors.service.ContextAwareActor;
import org.thingsboard.server.actors.service.ContextBasedCreator;
import org.thingsboard.server.actors.shared.SessionTimeoutMsg;
import org.thingsboard.server.common.data.id.SessionId;
import org.thingsboard.server.common.msg.TbActorMsg;
import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;
import org.thingsboard.server.common.msg.core.ActorSystemToDeviceSessionActorMsg;
import org.thingsboard.server.common.msg.session.SessionCtrlMsg;
import org.thingsboard.server.common.msg.session.SessionMsg;
import org.thingsboard.server.common.msg.session.SessionType;
import org.thingsboard.server.common.msg.session.TransportToDeviceSessionActorMsg;
import org.thingsboard.server.common.msg.session.ctrl.SessionCloseMsg;
import scala.concurrent.duration.Duration;
public class SessionActor extends ContextAwareActor {
private final LoggingAdapter logger = Logging.getLogger(getContext().system(), this);
private final SessionId sessionId;
private AbstractSessionActorMsgProcessor processor;
private SessionActor(ActorSystemContext systemContext, SessionId sessionId) {
super(systemContext);
this.sessionId = sessionId;
}
@Override
public SupervisorStrategy supervisorStrategy() {
return new OneForOneStrategy(-1, Duration.Inf(),
throwable -> {
logger.error(throwable, "Unknown session error");
if (throwable instanceof Error) {
return OneForOneStrategy.escalate();
} else {
return OneForOneStrategy.resume();
}
});
}
@Override
protected boolean process(TbActorMsg msg) {
switch (msg.getMsgType()) {
case TRANSPORT_TO_DEVICE_SESSION_ACTOR_MSG:
processTransportToSessionMsg((TransportToDeviceSessionActorMsg) msg);
break;
case ACTOR_SYSTEM_TO_DEVICE_SESSION_ACTOR_MSG:
processActorsToSessionMsg((ActorSystemToDeviceSessionActorMsg) msg);
break;
case SESSION_TIMEOUT_MSG:
processTimeoutMsg((SessionTimeoutMsg) msg);
break;
case SESSION_CTRL_MSG:
processSessionCloseMsg((SessionCtrlMsg) msg);
break;
case CLUSTER_EVENT_MSG:
processClusterEvent((ClusterEventMsg) msg);
break;
default: return false;
}
return true;
}
private void processClusterEvent(ClusterEventMsg msg) {
processor.processClusterEvent(context(), msg);
}
private void processTransportToSessionMsg(TransportToDeviceSessionActorMsg msg) {
initProcessor(msg);
processor.processToDeviceActorMsg(context(), msg);
}
private void processActorsToSessionMsg(ActorSystemToDeviceSessionActorMsg msg) {
processor.processToDeviceMsg(context(), msg.getMsg());
}
private void processTimeoutMsg(SessionTimeoutMsg msg) {
if (processor != null) {
processor.processTimeoutMsg(context(), msg);
} else {
logger.warning("[{}] Can't process timeout msg: {} without processor", sessionId, msg);
}
}
private void processSessionCloseMsg(SessionCtrlMsg msg) {
if (processor != null) {
processor.processSessionCtrlMsg(context(), msg);
} else if (msg instanceof SessionCloseMsg) {
AbstractSessionActorMsgProcessor.terminateSession(context(), sessionId);
} else {
logger.warning("[{}] Can't process session ctrl msg: {} without processor", sessionId, msg);
}
}
private void initProcessor(TransportToDeviceSessionActorMsg msg) {
if (processor == null) {
SessionMsg sessionMsg = (SessionMsg) msg.getSessionMsg();
if (sessionMsg.getSessionContext().getSessionType() == SessionType.SYNC) {
processor = new SyncMsgProcessor(systemContext, logger, sessionId);
} else {
processor = new ASyncMsgProcessor(systemContext, logger, sessionId);
}
}
}
public static class ActorCreator extends ContextBasedCreator<SessionActor> {
private static final long serialVersionUID = 1L;
private final SessionId sessionId;
public ActorCreator(ActorSystemContext context, SessionId sessionId) {
super(context);
this.sessionId = sessionId;
}
@Override
public SessionActor create() throws Exception {
return new SessionActor(context, sessionId);
}
}
}

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

@ -1,180 +0,0 @@
/**
* Copyright © 2016-2018 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.actors.session;
import akka.actor.ActorInitializationException;
import akka.actor.ActorRef;
import akka.actor.InvalidActorNameException;
import akka.actor.LocalActorRef;
import akka.actor.OneForOneStrategy;
import akka.actor.Props;
import akka.actor.SupervisorStrategy;
import akka.actor.Terminated;
import akka.event.Logging;
import akka.event.LoggingAdapter;
import akka.japi.Function;
import org.thingsboard.server.actors.ActorSystemContext;
import org.thingsboard.server.actors.service.ContextAwareActor;
import org.thingsboard.server.actors.service.ContextBasedCreator;
import org.thingsboard.server.actors.service.DefaultActorService;
import org.thingsboard.server.actors.shared.SessionTimeoutMsg;
import org.thingsboard.server.common.data.id.SessionId;
import org.thingsboard.server.common.msg.TbActorMsg;
import org.thingsboard.server.common.msg.aware.SessionAwareMsg;
import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;
import org.thingsboard.server.common.msg.core.ActorSystemToDeviceSessionActorMsg;
import org.thingsboard.server.common.msg.core.SessionCloseMsg;
import org.thingsboard.server.common.msg.session.SessionCtrlMsg;
import scala.concurrent.duration.Duration;
import java.util.HashMap;
import java.util.Map;
public class SessionManagerActor extends ContextAwareActor {
private static final int INITIAL_SESSION_MAP_SIZE = 1024;
private final LoggingAdapter log = Logging.getLogger(getContext().system(), this);
private final Map<String, ActorRef> sessionActors;
SessionManagerActor(ActorSystemContext systemContext) {
super(systemContext);
this.sessionActors = new HashMap<>(INITIAL_SESSION_MAP_SIZE);
}
@Override
public SupervisorStrategy supervisorStrategy() {
return strategy;
}
@Override
protected boolean process(TbActorMsg msg) {
//TODO Move everything here, to work with TbActorMsg
return false;
}
@Override
public void onReceive(Object msg) throws Exception {
if (msg instanceof SessionCtrlMsg) {
onSessionCtrlMsg((SessionCtrlMsg) msg);
} else if (msg instanceof SessionAwareMsg) {
forwardToSessionActor((SessionAwareMsg) msg);
} else if (msg instanceof SessionTerminationMsg) {
onSessionTermination((SessionTerminationMsg) msg);
} else if (msg instanceof Terminated) {
onTermination((Terminated) msg);
} else if (msg instanceof SessionTimeoutMsg) {
onSessionTimeout((SessionTimeoutMsg) msg);
} else if (msg instanceof ClusterEventMsg) {
broadcast(msg);
}
}
private void broadcast(Object msg) {
sessionActors.values().forEach(actorRef -> actorRef.tell(msg, ActorRef.noSender()));
}
private void onSessionTimeout(SessionTimeoutMsg msg) {
String sessionIdStr = msg.getSessionId().toUidStr();
ActorRef sessionActor = sessionActors.get(sessionIdStr);
if (sessionActor != null) {
sessionActor.tell(msg, ActorRef.noSender());
}
}
private void onSessionCtrlMsg(SessionCtrlMsg msg) {
String sessionIdStr = msg.getSessionId().toUidStr();
ActorRef sessionActor = sessionActors.get(sessionIdStr);
if (sessionActor != null) {
sessionActor.tell(msg, ActorRef.noSender());
}
}
private void onSessionTermination(SessionTerminationMsg msg) {
String sessionIdStr = msg.getId().toUidStr();
ActorRef sessionActor = sessionActors.remove(sessionIdStr);
if (sessionActor != null) {
log.debug("[{}] Removed session actor.", sessionIdStr);
//TODO: onSubscriptionUpdate device actor about session close;
} else {
log.debug("[{}] Session actor was already removed.", sessionIdStr);
}
}
private void forwardToSessionActor(SessionAwareMsg msg) {
if (msg instanceof ActorSystemToDeviceSessionActorMsg || msg instanceof SessionCloseMsg) {
String sessionIdStr = msg.getSessionId().toUidStr();
ActorRef sessionActor = sessionActors.get(sessionIdStr);
if (sessionActor != null) {
sessionActor.tell(msg, ActorRef.noSender());
} else {
log.debug("[{}] Session actor was already removed.", sessionIdStr);
}
} else {
try {
getOrCreateSessionActor(msg.getSessionId()).tell(msg, self());
} catch (InvalidActorNameException e) {
log.info("Invalid msg : {}", msg);
}
}
}
private ActorRef getOrCreateSessionActor(SessionId sessionId) {
String sessionIdStr = sessionId.toUidStr();
ActorRef sessionActor = sessionActors.get(sessionIdStr);
if (sessionActor == null) {
log.debug("[{}] Creating session actor.", sessionIdStr);
sessionActor = context().actorOf(
Props.create(new SessionActor.ActorCreator(systemContext, sessionId)).withDispatcher(DefaultActorService.SESSION_DISPATCHER_NAME),
sessionIdStr);
sessionActors.put(sessionIdStr, sessionActor);
log.debug("[{}] Created session actor.", sessionIdStr);
}
return sessionActor;
}
private void onTermination(Terminated message) {
ActorRef terminated = message.actor();
if (terminated instanceof LocalActorRef) {
log.info("Removed actor: {}.", terminated);
//TODO: cleanup session actors map
} else {
throw new IllegalStateException("Remote actors are not supported!");
}
}
public static class ActorCreator extends ContextBasedCreator<SessionManagerActor> {
private static final long serialVersionUID = 1L;
public ActorCreator(ActorSystemContext context) {
super(context);
}
@Override
public SessionManagerActor create() throws Exception {
return new SessionManagerActor(context);
}
}
private final SupervisorStrategy strategy = new OneForOneStrategy(3, Duration.create("1 minute"), new Function<Throwable, SupervisorStrategy.Directive>() {
@Override
public SupervisorStrategy.Directive apply(Throwable t) {
logger.error(t, "Unknown failure");
return SupervisorStrategy.stop();
}
});
}

26
application/src/main/java/org/thingsboard/server/actors/session/SessionTerminationMsg.java

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

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

@ -1,95 +0,0 @@
/**
* Copyright © 2016-2018 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.actors.session;
import akka.actor.ActorContext;
import akka.event.LoggingAdapter;
import org.thingsboard.server.actors.ActorSystemContext;
import org.thingsboard.server.actors.shared.SessionTimeoutMsg;
import org.thingsboard.server.common.data.id.SessionId;
import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;
import org.thingsboard.server.common.msg.cluster.ServerAddress;
import org.thingsboard.server.common.msg.device.DeviceToDeviceActorMsg;
import org.thingsboard.server.common.msg.session.BasicSessionActorToAdaptorMsg;
import org.thingsboard.server.common.msg.session.SessionContext;
import org.thingsboard.server.common.msg.session.SessionType;
import org.thingsboard.server.common.msg.session.ToDeviceMsg;
import org.thingsboard.server.common.msg.session.TransportToDeviceSessionActorMsg;
import org.thingsboard.server.common.msg.session.ctrl.SessionCloseMsg;
import org.thingsboard.server.common.msg.session.ex.SessionException;
import java.util.Optional;
class SyncMsgProcessor extends AbstractSessionActorMsgProcessor {
private DeviceToDeviceActorMsg pendingMsg;
private Optional<ServerAddress> currentTargetServer;
private boolean pendingResponse;
public SyncMsgProcessor(ActorSystemContext ctx, LoggingAdapter logger, SessionId sessionId) {
super(ctx, logger, sessionId);
}
@Override
protected void processToDeviceActorMsg(ActorContext ctx, TransportToDeviceSessionActorMsg msg) {
updateSessionCtx(msg, SessionType.SYNC);
pendingMsg = toDeviceMsg(msg);
pendingResponse = true;
currentTargetServer = forwardToAppActor(ctx, pendingMsg);
scheduleMsgWithDelay(ctx, new SessionTimeoutMsg(sessionId), getTimeout(systemContext, msg.getSessionMsg().getSessionContext()), ctx.parent());
}
public void processTimeoutMsg(ActorContext context, SessionTimeoutMsg msg) {
if (pendingResponse) {
try {
sessionCtx.onMsg(SessionCloseMsg.onTimeout(sessionId));
} catch (SessionException e) {
logger.warning("Failed to push session close msg", e);
}
terminateSession(context, this.sessionId);
}
}
public void processToDeviceMsg(ActorContext context, ToDeviceMsg msg) {
try {
sessionCtx.onMsg(new BasicSessionActorToAdaptorMsg(this.sessionCtx, msg));
pendingResponse = false;
} catch (SessionException e) {
logger.warning("Failed to push session response msg", e);
}
terminateSession(context, this.sessionId);
}
@Override
public void processClusterEvent(ActorContext context, ClusterEventMsg msg) {
if (pendingResponse) {
Optional<ServerAddress> newTargetServer = forwardToAppActorIfAddressChanged(context, pendingMsg, currentTargetServer);
if (logger.isDebugEnabled()) {
if (!newTargetServer.equals(currentTargetServer)) {
if (newTargetServer.isPresent()) {
logger.debug("[{}] Forwarded msg to new server: {}", sessionId, newTargetServer.get());
} else {
logger.debug("[{}] Forwarded msg to local server.", sessionId);
}
}
}
currentTargetServer = newTargetServer;
}
}
private long getTimeout(ActorSystemContext ctx, SessionContext sessionCtx) {
return sessionCtx.getTimeout() > 0 ? sessionCtx.getTimeout() : ctx.getSyncSessionTimeout();
}
}

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

@ -26,7 +26,6 @@ import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.plugin.ComponentLifecycleState;
import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;
import org.thingsboard.server.service.queue.MsgQueueService;
import javax.annotation.Nullable;
import java.util.function.Consumer;
@ -35,14 +34,12 @@ public abstract class ComponentMsgProcessor<T extends EntityId> extends Abstract
protected final TenantId tenantId;
protected final T entityId;
protected final MsgQueueService queue;
protected ComponentLifecycleState state;
protected ComponentMsgProcessor(ActorSystemContext systemContext, LoggingAdapter logger, TenantId tenantId, T id) {
super(systemContext, logger);
this.tenantId = tenantId;
this.entityId = id;
this.queue = systemContext.getMsgQueueService();
}
public abstract void start(ActorContext context) throws Exception;
@ -82,22 +79,9 @@ public abstract class ComponentMsgProcessor<T extends EntityId> extends Abstract
protected void checkActive() {
if (state != ComponentLifecycleState.ACTIVE) {
throw new IllegalStateException("Rule chain is not active!");
logger.warning("Rule chain is not active. Current state [{}] for processor [{}] tenant [{}]", state, tenantId, entityId);
throw new IllegalStateException("Rule chain is not active! " + entityId + " - " + tenantId);
}
}
protected void putToQueue(final TbMsg tbMsg, final Consumer<TbMsg> onSuccess) {
EntityId entityId = tbMsg.getRuleNodeId() != null ? tbMsg.getRuleNodeId() : tbMsg.getRuleChainId();
Futures.addCallback(queue.put(this.tenantId, tbMsg, entityId.getId(), tbMsg.getClusterPartition()), new FutureCallback<Void>() {
@Override
public void onSuccess(@Nullable Void result) {
onSuccess.accept(tbMsg);
}
@Override
public void onFailure(Throwable t) {
logger.debug("Failed to push message [{}] to queue due to [{}]", tbMsg, t);
}
});
}
}

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

@ -1,36 +0,0 @@
/**
* Copyright © 2016-2018 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.actors.shared;
import lombok.Data;
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;
@Data
public class SessionTimeoutMsg implements Serializable, TbActorMsg {
private static final long serialVersionUID = 1L;
private final SessionId sessionId;
@Override
public MsgType getMsgType() {
return MsgType.SESSION_TIMEOUT_MSG;
}
}

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

@ -87,7 +87,7 @@ public class TenantActor extends RuleChainManagerActor {
case DEVICE_ACTOR_TO_RULE_ENGINE_MSG:
onDeviceActorToRuleEngineMsg((DeviceActorToRuleEngineMsg) msg);
break;
case DEVICE_SESSION_TO_DEVICE_ACTOR_MSG:
case TRANSPORT_TO_DEVICE_ACTOR_MSG:
case DEVICE_ATTRIBUTES_UPDATE_TO_DEVICE_ACTOR_MSG:
case DEVICE_CREDENTIALS_UPDATE_TO_DEVICE_ACTOR_MSG:
case DEVICE_NAME_OR_TYPE_UPDATE_TO_DEVICE_ACTOR_MSG:

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

@ -51,6 +51,7 @@ import org.thingsboard.server.common.msg.TbMsgMetaData;
import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg;
import org.thingsboard.server.dao.alarm.AlarmService;
import org.thingsboard.server.dao.asset.AssetService;
import org.thingsboard.server.dao.attributes.AttributesService;
import org.thingsboard.server.dao.audit.AuditLogService;
import org.thingsboard.server.dao.customer.CustomerService;
import org.thingsboard.server.dao.dashboard.DashboardService;
@ -70,6 +71,7 @@ import org.thingsboard.server.exception.ThingsboardErrorResponseHandler;
import org.thingsboard.server.service.component.ComponentDiscoveryService;
import org.thingsboard.server.service.security.model.SecurityUser;
import org.thingsboard.server.service.state.DeviceStateService;
import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService;
import javax.mail.MessagingException;
import javax.servlet.http.HttpServletRequest;
@ -143,6 +145,12 @@ public abstract class BaseController {
@Autowired
protected EntityViewService entityViewService;
@Autowired
protected TelemetrySubscriptionService tsSubService;
@Autowired
protected AttributesService attributesService;
@ExceptionHandler(ThingsboardException.class)
public void handleThingsboardException(ThingsboardException ex, HttpServletResponse response) {
errorResponseHandler.handle(ex, response);
@ -251,7 +259,6 @@ public abstract class BaseController {
Customer checkCustomerId(CustomerId customerId) throws ThingsboardException {
try {
validateId(customerId, "Incorrect customerId " + customerId);
SecurityUser authUser = getCurrentUser();
if (authUser.getAuthority() == Authority.SYS_ADMIN ||
(authUser.getAuthority() != Authority.TENANT_ADMIN &&
@ -259,9 +266,13 @@ public abstract class BaseController {
throw new ThingsboardException(YOU_DON_T_HAVE_PERMISSION_TO_PERFORM_THIS_OPERATION,
ThingsboardErrorCode.PERMISSION_DENIED);
}
Customer customer = customerService.findCustomerById(customerId);
checkCustomer(customer);
return customer;
if (customerId != null && !customerId.isNullUid()) {
Customer customer = customerService.findCustomerById(customerId);
checkCustomer(customer);
return customer;
} else {
return null;
}
} catch (Exception e) {
throw handleException(e, false);
}
@ -342,9 +353,7 @@ public abstract class BaseController {
protected void checkDevice(Device device) throws ThingsboardException {
checkNotNull(device);
checkTenantId(device.getTenantId());
if (device.getCustomerId() != null && !device.getCustomerId().getId().equals(ModelConstants.NULL_UUID)) {
checkCustomerId(device.getCustomerId());
}
checkCustomerId(device.getCustomerId());
}
protected EntityView checkEntityViewId(EntityViewId entityViewId) throws ThingsboardException {
@ -361,9 +370,7 @@ public abstract class BaseController {
protected void checkEntityView(EntityView entityView) throws ThingsboardException {
checkNotNull(entityView);
checkTenantId(entityView.getTenantId());
if (entityView.getCustomerId() != null && !entityView.getCustomerId().getId().equals(ModelConstants.NULL_UUID)) {
checkCustomerId(entityView.getCustomerId());
}
checkCustomerId(entityView.getCustomerId());
}
Asset checkAssetId(AssetId assetId) throws ThingsboardException {
@ -380,9 +387,7 @@ public abstract class BaseController {
protected void checkAsset(Asset asset) throws ThingsboardException {
checkNotNull(asset);
checkTenantId(asset.getTenantId());
if (asset.getCustomerId() != null && !asset.getCustomerId().getId().equals(ModelConstants.NULL_UUID)) {
checkCustomerId(asset.getCustomerId());
}
checkCustomerId(asset.getCustomerId());
}
Alarm checkAlarmId(AlarmId alarmId) throws ThingsboardException {
@ -491,8 +496,7 @@ public abstract class BaseController {
ComponentDescriptor checkComponentDescriptorByClazz(String clazz) throws ThingsboardException {
try {
log.debug("[{}] Lookup component descriptor", clazz);
ComponentDescriptor componentDescriptor = checkNotNull(componentDescriptorService.getComponent(clazz));
return componentDescriptor;
return checkNotNull(componentDescriptorService.getComponent(clazz));
} catch (Exception e) {
throw handleException(e, false);
}
@ -556,16 +560,16 @@ public abstract class BaseController {
}
protected <I extends EntityId> I emptyId(EntityType entityType) {
return (I)EntityIdFactory.getByTypeAndUuid(entityType, ModelConstants.NULL_UUID);
return (I) EntityIdFactory.getByTypeAndUuid(entityType, ModelConstants.NULL_UUID);
}
protected <E extends HasName, I extends EntityId> void logEntityAction(I entityId, E entity, CustomerId customerId,
ActionType actionType, Exception e, Object... additionalInfo) throws ThingsboardException {
ActionType actionType, Exception e, Object... additionalInfo) throws ThingsboardException {
logEntityAction(getCurrentUser(), entityId, entity, customerId, actionType, e, additionalInfo);
}
protected <E extends HasName, I extends EntityId> void logEntityAction(User user, I entityId, E entity, CustomerId customerId,
ActionType actionType, Exception e, Object... additionalInfo) throws ThingsboardException {
ActionType actionType, Exception e, Object... additionalInfo) throws ThingsboardException {
if (customerId == null || customerId.isNullUid()) {
customerId = user.getCustomerId();
}
@ -581,7 +585,7 @@ public abstract class BaseController {
}
private <E extends HasName, I extends EntityId> void pushEntityActionToRuleEngine(I entityId, E entity, User user, CustomerId customerId,
ActionType actionType, Object... additionalInfo) {
ActionType actionType, Object... additionalInfo) {
String msgType = null;
switch (actionType) {
case ADDED:
@ -605,6 +609,12 @@ public abstract class BaseController {
case ATTRIBUTES_DELETED:
msgType = DataConstants.ATTRIBUTES_DELETED;
break;
case ALARM_ACK:
msgType = DataConstants.ALARM_ACK;
break;
case ALARM_CLEAR:
msgType = DataConstants.ALARM_CLEAR;
break;
}
if (!StringUtils.isEmpty(msgType)) {
try {
@ -654,7 +664,7 @@ public abstract class BaseController {
String scope = extractParameter(String.class, 0, additionalInfo);
List<String> keys = extractParameter(List.class, 1, additionalInfo);
metaData.putValue("scope", scope);
ArrayNode attrsArrayNode = entityNode.putArray("attributes");
ArrayNode attrsArrayNode = entityNode.putArray("attributes");
if (keys != null) {
keys.forEach(attrsArrayNode::add);
}

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

@ -15,7 +15,10 @@
*/
package org.thingsboard.server.controller;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PathVariable;
@ -26,7 +29,9 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.EntitySubtype;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.EntityView;
@ -34,15 +39,24 @@ import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.entityview.EntityViewSearchQuery;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.EntityViewId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.UUIDBased;
import org.thingsboard.server.common.data.kv.AttributeKvEntry;
import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.common.msg.cluster.SendToClusterMsg;
import org.thingsboard.server.dao.exception.IncorrectParameterException;
import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.service.security.model.SecurityUser;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;
import static org.thingsboard.server.controller.CustomerController.CUSTOMER_ID;
@ -52,6 +66,7 @@ import static org.thingsboard.server.controller.CustomerController.CUSTOMER_ID;
*/
@RestController
@RequestMapping("/api")
@Slf4j
public class EntityViewController extends BaseController {
public static final String ENTITY_VIEW_ID = "entityViewId";
@ -75,6 +90,20 @@ public class EntityViewController extends BaseController {
try {
entityView.setTenantId(getCurrentUser().getTenantId());
EntityView savedEntityView = checkNotNull(entityViewService.saveEntityView(entityView));
List<ListenableFuture<List<Void>>> futures = new ArrayList<>();
if (savedEntityView.getKeys() != null && savedEntityView.getKeys().getAttributes() != null) {
futures.add(copyAttributesFromEntityToEntityView(savedEntityView, DataConstants.CLIENT_SCOPE, savedEntityView.getKeys().getAttributes().getCs(), getCurrentUser()));
futures.add(copyAttributesFromEntityToEntityView(savedEntityView, DataConstants.SERVER_SCOPE, savedEntityView.getKeys().getAttributes().getSs(), getCurrentUser()));
futures.add(copyAttributesFromEntityToEntityView(savedEntityView, DataConstants.SHARED_SCOPE, savedEntityView.getKeys().getAttributes().getSh(), getCurrentUser()));
}
for (ListenableFuture<List<Void>> future : futures) {
try {
future.get();
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException("Failed to copy attributes to entity view", e);
}
}
logEntityAction(savedEntityView.getId(), savedEntityView, null,
entityView.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null);
return savedEntityView;
@ -85,6 +114,56 @@ public class EntityViewController extends BaseController {
}
}
private ListenableFuture<List<Void>> copyAttributesFromEntityToEntityView(EntityView entityView, String scope, Collection<String> keys, SecurityUser user) throws ThingsboardException {
EntityViewId entityId = entityView.getId();
if (keys != null && !keys.isEmpty()) {
ListenableFuture<List<AttributeKvEntry>> getAttrFuture = attributesService.find(entityView.getEntityId(), scope, keys);
return Futures.transform(getAttrFuture, attributeKvEntries -> {
List<AttributeKvEntry> attributes;
if (attributeKvEntries != null && !attributeKvEntries.isEmpty()) {
attributes =
attributeKvEntries.stream()
.filter(attributeKvEntry -> {
long startTime = entityView.getStartTimeMs();
long endTime = entityView.getEndTimeMs();
long lastUpdateTs = attributeKvEntry.getLastUpdateTs();
return startTime == 0 && endTime == 0 ||
(endTime == 0 && startTime < lastUpdateTs) ||
(startTime == 0 && endTime > lastUpdateTs)
? true : startTime < lastUpdateTs && endTime > lastUpdateTs;
}).collect(Collectors.toList());
tsSubService.saveAndNotify(entityId, scope, attributes, new FutureCallback<Void>() {
@Override
public void onSuccess(@Nullable Void tmp) {
try {
logAttributesUpdated(user, entityId, scope, attributes, null);
} catch (ThingsboardException e) {
log.error("Failed to log attribute updates", e);
}
}
@Override
public void onFailure(Throwable t) {
try {
logAttributesUpdated(user, entityId, scope, attributes, t);
} catch (ThingsboardException e) {
log.error("Failed to log attribute updates", e);
}
}
});
}
return null;
});
} else {
return Futures.immediateFuture(null);
}
}
private void logAttributesUpdated(SecurityUser user, EntityId entityId, String scope, List<AttributeKvEntry> attributes, Throwable e) throws ThingsboardException {
logEntityAction(user, (UUIDBased & EntityId) entityId, null, null, ActionType.ATTRIBUTES_UPDATED, toException(e),
scope, attributes);
}
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/entityView/{entityViewId}", method = RequestMethod.DELETE)
@ResponseStatus(value = HttpStatus.OK)

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

@ -59,7 +59,6 @@ import org.thingsboard.server.common.data.kv.ReadTsKvQuery;
import org.thingsboard.server.common.data.kv.StringDataEntry;
import org.thingsboard.server.common.data.kv.TsKvEntry;
import org.thingsboard.server.common.msg.cluster.SendToClusterMsg;
import org.thingsboard.server.common.msg.core.TelemetryUploadRequest;
import org.thingsboard.server.common.transport.adaptor.JsonConverter;
import org.thingsboard.server.dao.attributes.AttributesService;
import org.thingsboard.server.dao.timeseries.TimeseriesService;
@ -93,15 +92,9 @@ import java.util.stream.Collectors;
@Slf4j
public class TelemetryController extends BaseController {
@Autowired
private AttributesService attributesService;
@Autowired
private TimeseriesService tsService;
@Autowired
private TelemetrySubscriptionService tsSubService;
@Autowired
private AccessValidator accessValidator;
@ -352,7 +345,7 @@ public class TelemetryController extends BaseController {
}
private DeferredResult<ResponseEntity> saveTelemetry(EntityId entityIdSrc, String requestBody, long ttl) throws ThingsboardException {
TelemetryUploadRequest telemetryRequest;
Map<Long, List<KvEntry>> telemetryRequest;
JsonElement telemetryJson;
try {
telemetryJson = new JsonParser().parse(requestBody);
@ -360,12 +353,12 @@ public class TelemetryController extends BaseController {
return getImmediateDeferredResult("Unable to parse timeseries payload: Invalid JSON body!", HttpStatus.BAD_REQUEST);
}
try {
telemetryRequest = JsonConverter.convertToTelemetry(telemetryJson);
telemetryRequest = JsonConverter.convertToTelemetry(telemetryJson, System.currentTimeMillis());
} catch (Exception e) {
return getImmediateDeferredResult("Unable to parse timeseries payload. Invalid JSON body: " + e.getMessage(), HttpStatus.BAD_REQUEST);
}
List<TsKvEntry> entries = new ArrayList<>();
for (Map.Entry<Long, List<KvEntry>> entry : telemetryRequest.getData().entrySet()) {
for (Map.Entry<Long, List<KvEntry>> entry : telemetryRequest.entrySet()) {
for (KvEntry kv : entry.getValue()) {
entries.add(new BasicTsKvEntry(entry.getKey(), kv));
}

2
application/src/main/java/org/thingsboard/server/service/cluster/discovery/DiscoveryService.java

@ -26,8 +26,6 @@ public interface DiscoveryService {
void unpublishCurrentServer();
String getNodeId();
ServerInstance getCurrentServer();
List<ServerInstance> getOtherServers();

8
application/src/main/java/org/thingsboard/server/service/cluster/discovery/DummyDiscoveryService.java

@ -38,17 +38,9 @@ public class DummyDiscoveryService implements DiscoveryService {
@Autowired
private ServerInstanceService serverInstance;
private String nodeId;
@PostConstruct
public void init() {
log.info("Initializing...");
this.nodeId = RandomStringUtils.randomAlphabetic(10);
}
@Override
public String getNodeId() {
return nodeId;
}
@Override

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

@ -94,13 +94,10 @@ public class ZkDiscoveryService implements DiscoveryService, PathChildrenCacheLi
private CuratorFramework client;
private PathChildrenCache cache;
private String nodePath;
//TODO: make persistent?
private String nodeId;
@PostConstruct
public void init() {
log.info("Initializing...");
this.nodeId = RandomStringUtils.randomAlphabetic(10);
Assert.hasLength(zkUrl, MiscUtils.missingProperty("zk.url"));
Assert.notNull(zkRetryInterval, MiscUtils.missingProperty("zk.retry_interval_ms"));
Assert.notNull(zkConnectionTimeout, MiscUtils.missingProperty("zk.connection_timeout_ms"));
@ -183,11 +180,6 @@ public class ZkDiscoveryService implements DiscoveryService, PathChildrenCacheLi
}
}
@Override
public String getNodeId() {
return nodeId;
}
@Override
public ServerInstance getCurrentServer() {
return serverInstance.getSelf();

111
application/src/main/java/org/thingsboard/server/service/queue/DefaultMsgQueueService.java

@ -1,111 +0,0 @@
/**
* Copyright © 2016-2018 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.queue;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.transport.quota.tenant.TenantQuotaService;
import org.thingsboard.server.dao.queue.MsgQueue;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
@Service
@Slf4j
public class DefaultMsgQueueService implements MsgQueueService {
@Value("${actors.rule.queue.max_size}")
private long queueMaxSize;
@Value("${actors.rule.queue.cleanup_period}")
private long queueCleanUpPeriod;
@Autowired
private MsgQueue msgQueue;
@Autowired
private TenantQuotaService quotaService;
private ScheduledExecutorService cleanupExecutor;
private Map<TenantId, AtomicLong> pendingCountPerTenant = new ConcurrentHashMap<>();
@PostConstruct
public void init() {
if (queueCleanUpPeriod > 0) {
cleanupExecutor = Executors.newSingleThreadScheduledExecutor();
cleanupExecutor.scheduleAtFixedRate(() -> cleanup(),
queueCleanUpPeriod, queueCleanUpPeriod, TimeUnit.SECONDS);
}
}
@PreDestroy
public void stop() {
if (cleanupExecutor != null) {
cleanupExecutor.shutdownNow();
}
}
@Override
public ListenableFuture<Void> put(TenantId tenantId, TbMsg msg, UUID nodeId, long clusterPartition) {
if(quotaService.isQuotaExceeded(tenantId.getId().toString())) {
log.warn("Tenant TbMsg Quota exceeded for [{}:{}] . Reject", tenantId.getId());
return Futures.immediateFailedFuture(new RuntimeException("Tenant TbMsg Quota exceeded"));
}
AtomicLong pendingMsgCount = pendingCountPerTenant.computeIfAbsent(tenantId, key -> new AtomicLong());
if (pendingMsgCount.incrementAndGet() < queueMaxSize) {
return msgQueue.put(tenantId, msg, nodeId, clusterPartition);
} else {
pendingMsgCount.decrementAndGet();
return Futures.immediateFailedFuture(new RuntimeException("Message queue is full!"));
}
}
@Override
public ListenableFuture<Void> ack(TenantId tenantId, TbMsg msg, UUID nodeId, long clusterPartition) {
ListenableFuture<Void> result = msgQueue.ack(tenantId, msg, nodeId, clusterPartition);
AtomicLong pendingMsgCount = pendingCountPerTenant.computeIfAbsent(tenantId, key -> new AtomicLong());
pendingMsgCount.decrementAndGet();
return result;
}
@Override
public Iterable<TbMsg> findUnprocessed(TenantId tenantId, UUID nodeId, long clusterPartition) {
return msgQueue.findUnprocessed(tenantId, nodeId, clusterPartition);
}
private void cleanup() {
pendingCountPerTenant.forEach((tenantId, pendingMsgCount) -> {
pendingMsgCount.set(0);
msgQueue.cleanUp(tenantId);
});
}
}

32
application/src/main/java/org/thingsboard/server/service/queue/MsgQueueService.java

@ -1,32 +0,0 @@
/**
* Copyright © 2016-2018 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.queue;
import com.google.common.util.concurrent.ListenableFuture;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.msg.TbMsg;
import java.util.UUID;
public interface MsgQueueService {
ListenableFuture<Void> put(TenantId tenantId, TbMsg msg, UUID nodeId, long clusterPartition);
ListenableFuture<Void> ack(TenantId tenantId, TbMsg msg, UUID nodeId, long clusterPartition);
Iterable<TbMsg> findUnprocessed(TenantId tenantId, UUID nodeId, long clusterPartition);
}

3
application/src/main/java/org/thingsboard/server/service/rpc/ToServerRpcResponseActorMsg.java

@ -22,11 +22,8 @@ import org.thingsboard.rule.engine.api.msg.ToDeviceActorNotificationMsg;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.msg.MsgType;
import org.thingsboard.server.common.msg.cluster.ServerAddress;
import org.thingsboard.server.common.msg.core.ToServerRpcResponseMsg;
import java.util.Optional;
/**
* Created by ashvayka on 16.04.18.
*/

11
application/src/main/java/org/thingsboard/server/service/script/RemoteJsInvokeService.java

@ -28,6 +28,7 @@ import org.thingsboard.server.kafka.TBKafkaConsumerTemplate;
import org.thingsboard.server.kafka.TBKafkaProducerTemplate;
import org.thingsboard.server.kafka.TbKafkaRequestTemplate;
import org.thingsboard.server.kafka.TbKafkaSettings;
import org.thingsboard.server.kafka.TbNodeIdProvider;
import org.thingsboard.server.service.cluster.discovery.DiscoveryService;
import javax.annotation.PostConstruct;
@ -42,7 +43,7 @@ import java.util.concurrent.ConcurrentHashMap;
public class RemoteJsInvokeService extends AbstractJsInvokeService {
@Autowired
private DiscoveryService discoveryService;
private TbNodeIdProvider nodeIdProvider;
@Autowired
private TbKafkaSettings kafkaSettings;
@ -97,15 +98,13 @@ public class RemoteJsInvokeService extends AbstractJsInvokeService {
TBKafkaConsumerTemplate.TBKafkaConsumerTemplateBuilder<JsInvokeProtos.RemoteJsResponse> responseBuilder = TBKafkaConsumerTemplate.builder();
responseBuilder.settings(kafkaSettings);
responseBuilder.topic(responseTopicPrefix + "." + discoveryService.getNodeId());
responseBuilder.clientId(discoveryService.getNodeId());
responseBuilder.topic(responseTopicPrefix + "." + nodeIdProvider.getNodeId());
responseBuilder.clientId("js-" + nodeIdProvider.getNodeId());
responseBuilder.groupId("rule-engine-node");
responseBuilder.autoCommit(true);
responseBuilder.autoCommitIntervalMs(autoCommitInterval);
responseBuilder.decoder(new RemoteJsResponseDecoder());
responseBuilder.requestIdExtractor((response) -> {
return new UUID(response.getRequestIdMSB(), response.getRequestIdLSB());
});
responseBuilder.requestIdExtractor((response) -> new UUID(response.getRequestIdMSB(), response.getRequestIdLSB()));
TbKafkaRequestTemplate.TbKafkaRequestTemplateBuilder
<JsInvokeProtos.RemoteJsRequest, JsInvokeProtos.RemoteJsResponse> builder = TbKafkaRequestTemplate.builder();

47
application/src/main/java/org/thingsboard/server/service/security/ValidationCallback.java

@ -36,29 +36,10 @@ public class ValidationCallback<T> implements FutureCallback<ValidationResult> {
@Override
public void onSuccess(ValidationResult result) {
ValidationResultCode resultCode = result.getResultCode();
if (resultCode == ValidationResultCode.OK) {
if (result.getResultCode() == ValidationResultCode.OK) {
action.onSuccess(response);
} else {
Exception e;
switch (resultCode) {
case ENTITY_NOT_FOUND:
e = new EntityNotFoundException(result.getMessage());
break;
case UNAUTHORIZED:
e = new UnauthorizedException(result.getMessage());
break;
case ACCESS_DENIED:
e = new AccessDeniedException(result.getMessage());
break;
case INTERNAL_ERROR:
e = new InternalErrorException(result.getMessage());
break;
default:
e = new UnauthorizedException("Permission denied.");
break;
}
onFailure(e);
onFailure(getException(result));
}
}
@ -66,4 +47,28 @@ public class ValidationCallback<T> implements FutureCallback<ValidationResult> {
public void onFailure(Throwable e) {
action.onFailure(e);
}
public static Exception getException(ValidationResult result) {
ValidationResultCode resultCode = result.getResultCode();
Exception e;
switch (resultCode) {
case ENTITY_NOT_FOUND:
e = new EntityNotFoundException(result.getMessage());
break;
case UNAUTHORIZED:
e = new UnauthorizedException(result.getMessage());
break;
case ACCESS_DENIED:
e = new AccessDeniedException(result.getMessage());
break;
case INTERNAL_ERROR:
e = new InternalErrorException(result.getMessage());
break;
default:
e = new UnauthorizedException("Permission denied.");
break;
}
return e;
}
}

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

@ -143,7 +143,7 @@ public class DefaultTelemetrySubscriptionService implements TelemetrySubscriptio
public void addLocalWsSubscription(String sessionId, EntityId entityId, SubscriptionState sub) {
long startTime = 0L;
long endTime = 0L;
if (entityId.getEntityType().equals(EntityType.ENTITY_VIEW)) {
if (entityId.getEntityType().equals(EntityType.ENTITY_VIEW) && TelemetryFeature.TIMESERIES.equals(sub.getType())) {
EntityView entityView = entityViewService.findEntityViewById(new EntityViewId(entityId.getId()));
entityId = entityView.getEntityId();
startTime = entityView.getStartTimeMs();
@ -165,38 +165,15 @@ public class DefaultTelemetrySubscriptionService implements TelemetrySubscriptio
}
private SubscriptionState getUpdatedSubscriptionState(EntityId entityId, SubscriptionState sub, EntityView entityView) {
boolean allKeys;
Map<String, Long> keyStates;
if (sub.getType().equals(TelemetryFeature.TIMESERIES) && !entityView.getKeys().getTimeseries().isEmpty()) {
allKeys = false;
if(sub.isAllKeys()) {
keyStates = entityView.getKeys().getTimeseries().stream().collect(Collectors.toMap(k -> k, k -> 0L));
} else {
keyStates = sub.getKeyStates().entrySet()
.stream().filter(entry -> entityView.getKeys().getTimeseries().contains(entry.getKey()))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
} else if (sub.getType().equals(TelemetryFeature.ATTRIBUTES)) {
if (sub.getScope().equals(DataConstants.CLIENT_SCOPE) && !entityView.getKeys().getAttributes().getCs().isEmpty()) {
allKeys = false;
keyStates = filterMap(sub, entityView.getKeys().getAttributes().getCs());
} else if (sub.getScope().equals(DataConstants.SERVER_SCOPE) && !entityView.getKeys().getAttributes().getSs().isEmpty()) {
allKeys = false;
keyStates = filterMap(sub, entityView.getKeys().getAttributes().getSs());
} else if (sub.getScope().equals(DataConstants.SERVER_SCOPE) && !entityView.getKeys().getAttributes().getSh().isEmpty()) {
allKeys = false;
keyStates = filterMap(sub, entityView.getKeys().getAttributes().getSh());
} else {
allKeys = sub.isAllKeys();
keyStates = sub.getKeyStates();
}
} else {
allKeys = sub.isAllKeys();
keyStates = sub.getKeyStates();
}
return new SubscriptionState(sub.getWsSessionId(), sub.getSubscriptionId(), entityId, sub.getType(), allKeys, keyStates, sub.getScope());
}
private Map<String, Long> filterMap(SubscriptionState sub, List<String> allowedKeys) {
return sub.getKeyStates().entrySet()
.stream().filter(entry -> allowedKeys.contains(entry.getKey()))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
return new SubscriptionState(sub.getWsSessionId(), sub.getSubscriptionId(), entityId, sub.getType(), false, keyStates, sub.getScope());
}
@Override
@ -467,7 +444,7 @@ public class DefaultTelemetrySubscriptionService implements TelemetrySubscriptio
onLocalSubUpdate(entityId, s -> TelemetryFeature.ATTRIBUTES == s.getType() && (StringUtils.isEmpty(s.getScope()) || scope.equals(s.getScope())), s -> {
List<TsKvEntry> subscriptionUpdate = null;
for (AttributeKvEntry kv : attributes) {
if (isInTimeRange(s, kv.getLastUpdateTs()) && (s.isAllKeys() || s.getKeyStates().containsKey(kv.getKey()))) {
if (s.isAllKeys() || s.getKeyStates().containsKey(kv.getKey())) {
if (subscriptionUpdate == null) {
subscriptionUpdate = new ArrayList<>();
}

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

@ -37,13 +37,18 @@ import org.thingsboard.server.common.data.kv.TsKvEntry;
import org.thingsboard.server.dao.attributes.AttributesService;
import org.thingsboard.server.dao.timeseries.TimeseriesService;
import org.thingsboard.server.service.security.AccessValidator;
import org.thingsboard.server.service.security.ValidationCallback;
import org.thingsboard.server.service.security.ValidationResult;
import org.thingsboard.server.service.security.ValidationResultCode;
import org.thingsboard.server.service.telemetry.cmd.AttributesSubscriptionCmd;
import org.thingsboard.server.service.telemetry.cmd.GetHistoryCmd;
import org.thingsboard.server.service.telemetry.cmd.SubscriptionCmd;
import org.thingsboard.server.service.telemetry.cmd.TelemetryPluginCmd;
import org.thingsboard.server.service.telemetry.cmd.TelemetryPluginCmdsWrapper;
import org.thingsboard.server.service.telemetry.cmd.TimeseriesSubscriptionCmd;
import org.thingsboard.server.service.telemetry.exception.AccessDeniedException;
import org.thingsboard.server.service.telemetry.exception.EntityNotFoundException;
import org.thingsboard.server.service.telemetry.exception.InternalErrorException;
import org.thingsboard.server.service.telemetry.exception.UnauthorizedException;
import org.thingsboard.server.service.telemetry.sub.SubscriptionErrorCode;
import org.thingsboard.server.service.telemetry.sub.SubscriptionState;
@ -535,11 +540,16 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
};
}
private FutureCallback<ValidationResult> on(Consumer<ValidationResult> success, Consumer<Throwable> failure) {
private FutureCallback<ValidationResult> on(Consumer<Void> success, Consumer<Throwable> failure) {
return new FutureCallback<ValidationResult>() {
@Override
public void onSuccess(@Nullable ValidationResult result) {
success.accept(result);
ValidationResultCode resultCode = result.getResultCode();
if (resultCode == ValidationResultCode.OK) {
success.accept(null);
} else {
onFailure(ValidationCallback.getException(result));
}
}
@Override

173
application/src/main/java/org/thingsboard/server/service/transport/LocalTransportApiService.java

@ -0,0 +1,173 @@
/**
* 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.transport;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.common.data.security.DeviceCredentialsType;
import org.thingsboard.server.dao.device.DeviceCredentialsService;
import org.thingsboard.server.dao.device.DeviceService;
import org.thingsboard.server.dao.relation.RelationService;
import org.thingsboard.server.gen.transport.TransportProtos.DeviceInfoProto;
import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFromGatewayRequestMsg;
import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFromGatewayResponseMsg;
import org.thingsboard.server.gen.transport.TransportProtos.TransportApiRequestMsg;
import org.thingsboard.server.gen.transport.TransportProtos.TransportApiResponseMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceCredentialsResponseMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceTokenRequestMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceX509CertRequestMsg;
import org.thingsboard.server.kafka.TBKafkaConsumerTemplate;
import org.thingsboard.server.kafka.TBKafkaProducerTemplate;
import org.thingsboard.server.kafka.TbKafkaResponseTemplate;
import org.thingsboard.server.kafka.TbKafkaSettings;
import org.thingsboard.server.service.cluster.discovery.DiscoveryService;
import org.thingsboard.server.service.executors.DbCallbackExecutorService;
import org.thingsboard.server.service.state.DeviceStateService;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.ReentrantLock;
/**
* Created by ashvayka on 05.10.18.
*/
@Slf4j
@Service
public class LocalTransportApiService implements TransportApiService {
private static final ObjectMapper mapper = new ObjectMapper();
@Autowired
private DeviceService deviceService;
@Autowired
private RelationService relationService;
@Autowired
private DeviceCredentialsService deviceCredentialsService;
@Autowired
private DeviceStateService deviceStateService;
@Autowired
private DbCallbackExecutorService dbCallbackExecutorService;
private ReentrantLock deviceCreationLock = new ReentrantLock();
@Override
public ListenableFuture<TransportApiResponseMsg> handle(TransportApiRequestMsg transportApiRequestMsg) {
if (transportApiRequestMsg.hasValidateTokenRequestMsg()) {
ValidateDeviceTokenRequestMsg msg = transportApiRequestMsg.getValidateTokenRequestMsg();
return validateCredentials(msg.getToken(), DeviceCredentialsType.ACCESS_TOKEN);
} else if (transportApiRequestMsg.hasValidateX509CertRequestMsg()) {
ValidateDeviceX509CertRequestMsg msg = transportApiRequestMsg.getValidateX509CertRequestMsg();
return validateCredentials(msg.getHash(), DeviceCredentialsType.X509_CERTIFICATE);
} else if (transportApiRequestMsg.hasGetOrCreateDeviceRequestMsg()) {
return handle(transportApiRequestMsg.getGetOrCreateDeviceRequestMsg());
}
return getEmptyTransportApiResponseFuture();
}
private ListenableFuture<TransportApiResponseMsg> validateCredentials(String credentialsId, DeviceCredentialsType credentialsType) {
//TODO: Make async and enable caching
DeviceCredentials credentials = deviceCredentialsService.findDeviceCredentialsByCredentialsId(credentialsId);
if (credentials != null && credentials.getCredentialsType() == credentialsType) {
return getDeviceInfo(credentials.getDeviceId());
} else {
return getEmptyTransportApiResponseFuture();
}
}
private ListenableFuture<TransportApiResponseMsg> handle(GetOrCreateDeviceFromGatewayRequestMsg requestMsg) {
DeviceId gatewayId = new DeviceId(new UUID(requestMsg.getGatewayIdMSB(), requestMsg.getGatewayIdLSB()));
ListenableFuture<Device> gatewayFuture = deviceService.findDeviceByIdAsync(gatewayId);
return Futures.transform(gatewayFuture, gateway -> {
deviceCreationLock.lock();
try {
Device device = deviceService.findDeviceByTenantIdAndName(gateway.getTenantId(), requestMsg.getDeviceName());
if (device == null) {
device = new Device();
device.setTenantId(gateway.getTenantId());
device.setName(requestMsg.getDeviceName());
device.setType(requestMsg.getDeviceType());
device.setCustomerId(gateway.getCustomerId());
device = deviceService.saveDevice(device);
relationService.saveRelationAsync(new EntityRelation(gateway.getId(), device.getId(), "Created"));
deviceStateService.onDeviceAdded(device);
}
return TransportApiResponseMsg.newBuilder()
.setGetOrCreateDeviceResponseMsg(GetOrCreateDeviceFromGatewayResponseMsg.newBuilder().setDeviceInfo(getDeviceInfoProto(device)).build()).build();
} catch (JsonProcessingException e) {
log.warn("[{}] Failed to lookup device by gateway id and name", gatewayId, requestMsg.getDeviceName(), e);
throw new RuntimeException(e);
} finally {
deviceCreationLock.unlock();
}
}, dbCallbackExecutorService);
}
private ListenableFuture<TransportApiResponseMsg> getDeviceInfo(DeviceId deviceId) {
return Futures.transform(deviceService.findDeviceByIdAsync(deviceId), device -> {
if (device == null) {
log.trace("[{}] Failed to lookup device by id", deviceId);
return getEmptyTransportApiResponse();
}
try {
return TransportApiResponseMsg.newBuilder()
.setValidateTokenResponseMsg(ValidateDeviceCredentialsResponseMsg.newBuilder().setDeviceInfo(getDeviceInfoProto(device)).build()).build();
} catch (JsonProcessingException e) {
log.warn("[{}] Failed to lookup device by id", deviceId, e);
return getEmptyTransportApiResponse();
}
});
}
private DeviceInfoProto getDeviceInfoProto(Device device) throws JsonProcessingException {
return DeviceInfoProto.newBuilder()
.setTenantIdMSB(device.getTenantId().getId().getMostSignificantBits())
.setTenantIdLSB(device.getTenantId().getId().getLeastSignificantBits())
.setDeviceIdMSB(device.getId().getId().getMostSignificantBits())
.setDeviceIdLSB(device.getId().getId().getLeastSignificantBits())
.setDeviceName(device.getName())
.setDeviceType(device.getType())
.setAdditionalInfo(mapper.writeValueAsString(device.getAdditionalInfo()))
.build();
}
private ListenableFuture<TransportApiResponseMsg> getEmptyTransportApiResponseFuture() {
return Futures.immediateFuture(getEmptyTransportApiResponse());
}
private TransportApiResponseMsg getEmptyTransportApiResponse() {
return TransportApiResponseMsg.newBuilder()
.setValidateTokenResponseMsg(ValidateDeviceCredentialsResponseMsg.getDefaultInstance()).build();
}
}

225
application/src/main/java/org/thingsboard/server/service/transport/LocalTransportService.java

@ -0,0 +1,225 @@
/**
* 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.transport;
import akka.actor.ActorRef;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Service;
import org.thingsboard.rule.engine.api.util.DonAsynchron;
import org.thingsboard.server.actors.ActorSystemContext;
import org.thingsboard.server.common.msg.cluster.ServerAddress;
import org.thingsboard.server.common.transport.SessionMsgListener;
import org.thingsboard.server.common.transport.TransportService;
import org.thingsboard.server.common.transport.TransportServiceCallback;
import org.thingsboard.server.common.transport.service.AbstractTransportService;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.gen.transport.TransportProtos.DeviceActorToTransportMsg;
import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeRequestMsg;
import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFromGatewayRequestMsg;
import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFromGatewayResponseMsg;
import org.thingsboard.server.gen.transport.TransportProtos.PostAttributeMsg;
import org.thingsboard.server.gen.transport.TransportProtos.PostTelemetryMsg;
import org.thingsboard.server.gen.transport.TransportProtos.SessionEventMsg;
import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto;
import org.thingsboard.server.gen.transport.TransportProtos.SubscribeToAttributeUpdatesMsg;
import org.thingsboard.server.gen.transport.TransportProtos.SubscribeToRPCMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcResponseMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToServerRpcRequestMsg;
import org.thingsboard.server.gen.transport.TransportProtos.TransportApiRequestMsg;
import org.thingsboard.server.gen.transport.TransportProtos.TransportToDeviceActorMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceCredentialsResponseMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceTokenRequestMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceX509CertRequestMsg;
import org.thingsboard.server.service.cluster.routing.ClusterRoutingService;
import org.thingsboard.server.service.cluster.rpc.ClusterRpcService;
import org.thingsboard.server.service.encoding.DataDecodingEncodingService;
import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
/**
* Created by ashvayka on 12.10.18.
*/
@Slf4j
@Service
@ConditionalOnProperty(prefix = "transport", value = "type", havingValue = "local")
public class LocalTransportService extends AbstractTransportService implements RuleEngineTransportService {
@Autowired
private TransportApiService transportApiService;
@Autowired
private ActorSystemContext actorContext;
//TODO: completely replace this routing with the Kafka routing by partition ids.
@Autowired
private ClusterRoutingService routingService;
@Autowired
private ClusterRpcService rpcService;
@Autowired
private DataDecodingEncodingService encodingService;
@PostConstruct
public void init() {
super.init();
}
@PreDestroy
public void destroy() {
super.destroy();
}
@Override
public void process(ValidateDeviceTokenRequestMsg msg, TransportServiceCallback<ValidateDeviceCredentialsResponseMsg> callback) {
DonAsynchron.withCallback(
transportApiService.handle(TransportApiRequestMsg.newBuilder().setValidateTokenRequestMsg(msg).build()),
transportApiResponseMsg -> {
if (callback != null) {
callback.onSuccess(transportApiResponseMsg.getValidateTokenResponseMsg());
}
},
getThrowableConsumer(callback), transportCallbackExecutor);
}
@Override
public void process(ValidateDeviceX509CertRequestMsg msg, TransportServiceCallback<ValidateDeviceCredentialsResponseMsg> callback) {
DonAsynchron.withCallback(
transportApiService.handle(TransportApiRequestMsg.newBuilder().setValidateX509CertRequestMsg(msg).build()),
transportApiResponseMsg -> {
if (callback != null) {
callback.onSuccess(transportApiResponseMsg.getValidateTokenResponseMsg());
}
},
getThrowableConsumer(callback), transportCallbackExecutor);
}
@Override
public void process(GetOrCreateDeviceFromGatewayRequestMsg msg, TransportServiceCallback<GetOrCreateDeviceFromGatewayResponseMsg> callback) {
DonAsynchron.withCallback(
transportApiService.handle(TransportApiRequestMsg.newBuilder().setGetOrCreateDeviceRequestMsg(msg).build()),
transportApiResponseMsg -> {
if (callback != null) {
callback.onSuccess(transportApiResponseMsg.getGetOrCreateDeviceResponseMsg());
}
},
getThrowableConsumer(callback), transportCallbackExecutor);
}
@Override
public void process(SessionInfoProto sessionInfo, SessionEventMsg msg, TransportServiceCallback<Void> callback) {
if (checkLimits(sessionInfo, callback)) {
forwardToDeviceActor(TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setSessionEvent(msg).build(), callback);
}
}
@Override
public void process(SessionInfoProto sessionInfo, PostTelemetryMsg msg, TransportServiceCallback<Void> callback) {
if (checkLimits(sessionInfo, callback)) {
forwardToDeviceActor(TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setPostTelemetry(msg).build(), callback);
}
}
@Override
public void process(SessionInfoProto sessionInfo, PostAttributeMsg msg, TransportServiceCallback<Void> callback) {
if (checkLimits(sessionInfo, callback)) {
forwardToDeviceActor(TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setPostAttributes(msg).build(), callback);
}
}
@Override
public void process(SessionInfoProto sessionInfo, GetAttributeRequestMsg msg, TransportServiceCallback<Void> callback) {
if (checkLimits(sessionInfo, callback)) {
forwardToDeviceActor(TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setGetAttributes(msg).build(), callback);
}
}
@Override
public void process(SessionInfoProto sessionInfo, SubscribeToAttributeUpdatesMsg msg, TransportServiceCallback<Void> callback) {
if (checkLimits(sessionInfo, callback)) {
forwardToDeviceActor(TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setSubscribeToAttributes(msg).build(), callback);
}
}
@Override
public void process(SessionInfoProto sessionInfo, SubscribeToRPCMsg msg, TransportServiceCallback<Void> callback) {
if (checkLimits(sessionInfo, callback)) {
forwardToDeviceActor(TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setSubscribeToRPC(msg).build(), callback);
}
}
@Override
public void process(SessionInfoProto sessionInfo, ToDeviceRpcResponseMsg msg, TransportServiceCallback<Void> callback) {
if (checkLimits(sessionInfo, callback)) {
forwardToDeviceActor(TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setToDeviceRPCCallResponse(msg).build(), callback);
}
}
@Override
public void process(SessionInfoProto sessionInfo, ToServerRpcRequestMsg msg, TransportServiceCallback<Void> callback) {
if (checkLimits(sessionInfo, callback)) {
forwardToDeviceActor(TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setToServerRPCCallRequest(msg).build(), callback);
}
}
@Override
public void process(String nodeId, DeviceActorToTransportMsg msg) {
process(nodeId, msg, null, null);
}
@Override
public void process(String nodeId, DeviceActorToTransportMsg msg, Runnable onSuccess, Consumer<Throwable> onFailure) {
processToTransportMsg(msg);
if (onSuccess != null) {
onSuccess.run();
}
}
private void forwardToDeviceActor(TransportToDeviceActorMsg toDeviceActorMsg, TransportServiceCallback<Void> callback) {
TransportToDeviceActorMsgWrapper wrapper = new TransportToDeviceActorMsgWrapper(toDeviceActorMsg);
Optional<ServerAddress> address = routingService.resolveById(wrapper.getDeviceId());
if (address.isPresent()) {
rpcService.tell(encodingService.convertToProtoDataMessage(address.get(), wrapper));
} else {
actorContext.getAppActor().tell(wrapper, ActorRef.noSender());
}
if (callback != null) {
callback.onSuccess(null);
}
}
private <T> Consumer<Throwable> getThrowableConsumer(TransportServiceCallback<T> callback) {
return e -> {
if (callback != null) {
callback.onError(e);
}
};
}
}

200
application/src/main/java/org/thingsboard/server/service/transport/RemoteRuleEngineTransportService.java

@ -0,0 +1,200 @@
/**
* 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.transport;
import akka.actor.ActorRef;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.kafka.clients.consumer.ConsumerRecords;
import org.apache.kafka.clients.producer.Callback;
import org.apache.kafka.clients.producer.RecordMetadata;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Service;
import org.thingsboard.server.actors.ActorSystemContext;
import org.thingsboard.server.actors.service.ActorService;
import org.thingsboard.server.common.msg.cluster.ServerAddress;
import org.thingsboard.server.gen.transport.TransportProtos.DeviceActorToTransportMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg;
import org.thingsboard.server.gen.transport.TransportProtos.TransportToDeviceActorMsg;
import org.thingsboard.server.kafka.TBKafkaConsumerTemplate;
import org.thingsboard.server.kafka.TBKafkaProducerTemplate;
import org.thingsboard.server.kafka.TbKafkaSettings;
import org.thingsboard.server.kafka.TbNodeIdProvider;
import org.thingsboard.server.service.cluster.discovery.DiscoveryService;
import org.thingsboard.server.service.cluster.routing.ClusterRoutingService;
import org.thingsboard.server.service.cluster.rpc.ClusterRpcService;
import org.thingsboard.server.service.encoding.DataDecodingEncodingService;
import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.time.Duration;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.function.Consumer;
/**
* Created by ashvayka on 09.10.18.
*/
@Slf4j
@Service
@ConditionalOnProperty(prefix = "transport", value = "type", havingValue = "remote")
public class RemoteRuleEngineTransportService implements RuleEngineTransportService {
@Value("${transport.remote.rule_engine.topic}")
private String ruleEngineTopic;
@Value("${transport.remote.notifications.topic}")
private String notificationsTopic;
@Value("${transport.remote.rule_engine.poll_interval}")
private int pollDuration;
@Value("${transport.remote.rule_engine.auto_commit_interval}")
private int autoCommitInterval;
@Autowired
private TbKafkaSettings kafkaSettings;
@Autowired
private TbNodeIdProvider nodeIdProvider;
@Autowired
private ActorSystemContext actorContext;
//TODO: completely replace this routing with the Kafka routing by partition ids.
@Autowired
private ClusterRoutingService routingService;
@Autowired
private ClusterRpcService rpcService;
@Autowired
private DataDecodingEncodingService encodingService;
private TBKafkaConsumerTemplate<ToRuleEngineMsg> ruleEngineConsumer;
private TBKafkaProducerTemplate<ToTransportMsg> notificationsProducer;
private ExecutorService mainConsumerExecutor = Executors.newSingleThreadExecutor();
private volatile boolean stopped = false;
@PostConstruct
public void init() {
TBKafkaProducerTemplate.TBKafkaProducerTemplateBuilder<ToTransportMsg> notificationsProducerBuilder = TBKafkaProducerTemplate.builder();
notificationsProducerBuilder.settings(kafkaSettings);
notificationsProducerBuilder.defaultTopic(notificationsTopic);
notificationsProducerBuilder.encoder(new ToTransportMsgEncoder());
notificationsProducer = notificationsProducerBuilder.build();
notificationsProducer.init();
TBKafkaConsumerTemplate.TBKafkaConsumerTemplateBuilder<ToRuleEngineMsg> ruleEngineConsumerBuilder = TBKafkaConsumerTemplate.builder();
ruleEngineConsumerBuilder.settings(kafkaSettings);
ruleEngineConsumerBuilder.topic(ruleEngineTopic);
ruleEngineConsumerBuilder.clientId("transport-" + nodeIdProvider.getNodeId());
ruleEngineConsumerBuilder.groupId("tb-node");
ruleEngineConsumerBuilder.autoCommit(true);
ruleEngineConsumerBuilder.autoCommitIntervalMs(autoCommitInterval);
ruleEngineConsumerBuilder.decoder(new ToRuleEngineMsgDecoder());
ruleEngineConsumer = ruleEngineConsumerBuilder.build();
ruleEngineConsumer.subscribe();
mainConsumerExecutor.execute(() -> {
while (!stopped) {
try {
ConsumerRecords<String, byte[]> records = ruleEngineConsumer.poll(Duration.ofMillis(pollDuration));
records.forEach(record -> {
try {
ToRuleEngineMsg toRuleEngineMsg = ruleEngineConsumer.decode(record);
if (toRuleEngineMsg.hasToDeviceActorMsg()) {
forwardToDeviceActor(toRuleEngineMsg.getToDeviceActorMsg());
}
} catch (Throwable e) {
log.warn("Failed to process the notification.", e);
}
});
} catch (Exception e) {
log.warn("Failed to obtain messages from queue.", e);
try {
Thread.sleep(pollDuration);
} catch (InterruptedException e2) {
log.trace("Failed to wait until the server has capacity to handle new requests", e2);
}
}
}
});
}
@Override
public void process(String nodeId, DeviceActorToTransportMsg msg) {
process(nodeId, msg, null, null);
}
@Override
public void process(String nodeId, DeviceActorToTransportMsg msg, Runnable onSuccess, Consumer<Throwable> onFailure) {
notificationsProducer.send(notificationsTopic + "." + nodeId,
new UUID(msg.getSessionIdMSB(), msg.getSessionIdLSB()).toString(),
ToTransportMsg.newBuilder().setToDeviceSessionMsg(msg).build()
, new QueueCallbackAdaptor(onSuccess, onFailure));
}
private void forwardToDeviceActor(TransportToDeviceActorMsg toDeviceActorMsg) {
TransportToDeviceActorMsgWrapper wrapper = new TransportToDeviceActorMsgWrapper(toDeviceActorMsg);
Optional<ServerAddress> address = routingService.resolveById(wrapper.getDeviceId());
if (address.isPresent()) {
rpcService.tell(encodingService.convertToProtoDataMessage(address.get(), wrapper));
} else {
actorContext.getAppActor().tell(wrapper, ActorRef.noSender());
}
}
@PreDestroy
public void destroy() {
stopped = true;
if (ruleEngineConsumer != null) {
ruleEngineConsumer.unsubscribe();
}
if (mainConsumerExecutor != null) {
mainConsumerExecutor.shutdownNow();
}
}
private static class QueueCallbackAdaptor implements Callback {
private final Runnable onSuccess;
private final Consumer<Throwable> onFailure;
QueueCallbackAdaptor(Runnable onSuccess, Consumer<Throwable> onFailure) {
this.onSuccess = onSuccess;
this.onFailure = onFailure;
}
@Override
public void onCompletion(RecordMetadata metadata, Exception exception) {
if (exception == null) {
if (onSuccess != null) {
onSuccess.run();
}
} else {
if (onFailure != null) {
onFailure.accept(exception);
}
}
}
}
}

109
application/src/main/java/org/thingsboard/server/service/transport/RemoteTransportApiService.java

@ -0,0 +1,109 @@
/**
* 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.transport;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;
import org.thingsboard.server.gen.transport.TransportProtos.TransportApiRequestMsg;
import org.thingsboard.server.gen.transport.TransportProtos.TransportApiResponseMsg;
import org.thingsboard.server.kafka.*;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* Created by ashvayka on 05.10.18.
*/
@Slf4j
@Component
@ConditionalOnProperty(prefix = "transport", value = "type", havingValue = "remote")
public class RemoteTransportApiService {
@Value("${transport.remote.transport_api.requests_topic}")
private String transportApiRequestsTopic;
@Value("${transport.remote.transport_api.responses_topic}")
private String transportApiResponsesTopic;
@Value("${transport.remote.transport_api.max_pending_requests}")
private int maxPendingRequests;
@Value("${transport.remote.transport_api.request_timeout}")
private long requestTimeout;
@Value("${transport.remote.transport_api.request_poll_interval}")
private int responsePollDuration;
@Value("${transport.remote.transport_api.request_auto_commit_interval}")
private int autoCommitInterval;
@Autowired
private TbKafkaSettings kafkaSettings;
@Autowired
private TbNodeIdProvider nodeIdProvider;
@Autowired
private TransportApiService transportApiService;
private ExecutorService transportCallbackExecutor;
private TbKafkaResponseTemplate<TransportApiRequestMsg, TransportApiResponseMsg> transportApiTemplate;
@PostConstruct
public void init() {
this.transportCallbackExecutor = new ThreadPoolExecutor(0, 100, 60L, TimeUnit.SECONDS, new SynchronousQueue<>());
TBKafkaProducerTemplate.TBKafkaProducerTemplateBuilder<TransportApiResponseMsg> responseBuilder = TBKafkaProducerTemplate.builder();
responseBuilder.settings(kafkaSettings);
responseBuilder.defaultTopic(transportApiResponsesTopic);
responseBuilder.encoder(new TransportApiResponseEncoder());
TBKafkaConsumerTemplate.TBKafkaConsumerTemplateBuilder<TransportApiRequestMsg> requestBuilder = TBKafkaConsumerTemplate.builder();
requestBuilder.settings(kafkaSettings);
requestBuilder.topic(transportApiRequestsTopic);
requestBuilder.clientId(nodeIdProvider.getNodeId());
requestBuilder.groupId("tb-node");
requestBuilder.autoCommit(true);
requestBuilder.autoCommitIntervalMs(autoCommitInterval);
requestBuilder.decoder(new TransportApiRequestDecoder());
TbKafkaResponseTemplate.TbKafkaResponseTemplateBuilder
<TransportApiRequestMsg, TransportApiResponseMsg> builder = TbKafkaResponseTemplate.builder();
builder.requestTemplate(requestBuilder.build());
builder.responseTemplate(responseBuilder.build());
builder.maxPendingRequests(maxPendingRequests);
builder.requestTimeout(requestTimeout);
builder.pollInterval(responsePollDuration);
builder.executor(transportCallbackExecutor);
builder.handler(transportApiService);
transportApiTemplate = builder.build();
transportApiTemplate.init();
}
@PreDestroy
public void destroy() {
if (transportApiTemplate != null) {
transportApiTemplate.stop();
}
if (transportCallbackExecutor != null) {
transportCallbackExecutor.shutdownNow();
}
}
}

21
common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicRequest.java → application/src/main/java/org/thingsboard/server/service/transport/RuleEngineTransportService.java

@ -13,24 +13,19 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.msg.core;
package org.thingsboard.server.service.transport;
import java.io.Serializable;
import org.thingsboard.server.gen.transport.TransportProtos.DeviceActorToTransportMsg;
import java.util.function.Consumer;
/**
* @author Andrew Shvayka
* Created by ashvayka on 05.10.18.
*/
public class BasicRequest implements Serializable {
public static final Integer DEFAULT_REQUEST_ID = 0;
public interface RuleEngineTransportService {
private final Integer requestId;
void process(String nodeId, DeviceActorToTransportMsg msg);
public BasicRequest(Integer requestId) {
this.requestId = requestId;
}
void process(String nodeId, DeviceActorToTransportMsg msg, Runnable onSuccess, Consumer<Throwable> onFailure);
public Integer getRequestId() {
return requestId;
}
}

17
common/message/src/main/java/org/thingsboard/server/common/msg/core/RpcSubscribeMsg.java → application/src/main/java/org/thingsboard/server/service/transport/ToRuleEngineMsgDecoder.java

@ -13,18 +13,19 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.msg.core;
package org.thingsboard.server.service.transport;
import org.thingsboard.server.common.msg.session.FromDeviceMsg;
import org.thingsboard.server.common.msg.session.SessionMsgType;
import org.thingsboard.server.common.msg.session.SessionMsgType;
import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg;
import org.thingsboard.server.kafka.TbKafkaDecoder;
import java.io.IOException;
/**
* @author Andrew Shvayka
* Created by ashvayka on 05.10.18.
*/
public class RpcSubscribeMsg implements FromDeviceMsg {
public class ToRuleEngineMsgDecoder implements TbKafkaDecoder<ToRuleEngineMsg> {
@Override
public SessionMsgType getMsgType() {
return SessionMsgType.SUBSCRIBE_RPC_COMMANDS_REQUEST;
public ToRuleEngineMsg decode(byte[] data) throws IOException {
return ToRuleEngineMsg.parseFrom(data);
}
}

14
common/message/src/main/java/org/thingsboard/server/common/msg/core/AttributesSubscribeMsg.java → application/src/main/java/org/thingsboard/server/service/transport/ToTransportMsgEncoder.java

@ -13,17 +13,17 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.msg.core;
package org.thingsboard.server.service.transport;
import org.thingsboard.server.common.msg.session.FromDeviceMsg;
import org.thingsboard.server.common.msg.session.SessionMsgType;
import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg;
import org.thingsboard.server.kafka.TbKafkaEncoder;
/**
* @author Andrew Shvayka
* Created by ashvayka on 05.10.18.
*/
public class AttributesSubscribeMsg implements FromDeviceMsg {
public class ToTransportMsgEncoder implements TbKafkaEncoder<ToTransportMsg> {
@Override
public SessionMsgType getMsgType() {
return SessionMsgType.SUBSCRIBE_ATTRIBUTES_REQUEST;
public byte[] encode(ToTransportMsg value) {
return value.toByteArray();
}
}

31
application/src/main/java/org/thingsboard/server/service/transport/TransportApiRequestDecoder.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.service.transport;
import org.thingsboard.server.gen.transport.TransportProtos.TransportApiRequestMsg;
import org.thingsboard.server.kafka.TbKafkaDecoder;
import java.io.IOException;
/**
* Created by ashvayka on 05.10.18.
*/
public class TransportApiRequestDecoder implements TbKafkaDecoder<TransportApiRequestMsg> {
@Override
public TransportApiRequestMsg decode(byte[] data) throws IOException {
return TransportApiRequestMsg.parseFrom(data);
}
}

16
common/message/src/main/java/org/thingsboard/server/common/msg/core/SessionCloseMsg.java → application/src/main/java/org/thingsboard/server/service/transport/TransportApiResponseEncoder.java

@ -13,18 +13,18 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.msg.core;
package org.thingsboard.server.service.transport;
import org.thingsboard.server.common.msg.session.FromDeviceMsg;
import org.thingsboard.server.common.msg.session.SessionMsgType;
import org.thingsboard.server.common.msg.session.SessionMsgType;
import org.thingsboard.server.kafka.TbKafkaEncoder;
import org.thingsboard.server.gen.transport.TransportProtos.TransportApiResponseMsg;
/**
* @author Andrew Shvayka
* Created by ashvayka on 05.10.18.
*/
public class SessionCloseMsg implements FromDeviceMsg {
public class TransportApiResponseEncoder implements TbKafkaEncoder<TransportApiResponseMsg> {
@Override
public SessionMsgType getMsgType() {
return SessionMsgType.SESSION_CLOSE;
public byte[] encode(TransportApiResponseMsg value) {
return value.toByteArray();
}
}

13
common/message/src/main/java/org/thingsboard/server/common/msg/aware/SessionAwareMsg.java → application/src/main/java/org/thingsboard/server/service/transport/TransportApiService.java

@ -13,12 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.msg.aware;
package org.thingsboard.server.service.transport;
import org.thingsboard.server.common.data.id.SessionId;
public interface SessionAwareMsg {
SessionId getSessionId();
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.kafka.TbKafkaHandler;
/**
* Created by ashvayka on 05.10.18.
*/
public interface TransportApiService extends TbKafkaHandler<TransportProtos.TransportApiRequestMsg, TransportProtos.TransportApiResponseMsg> {
}

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

@ -0,0 +1,50 @@
/**
* 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.transport.msg;
import lombok.Data;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.msg.MsgType;
import org.thingsboard.server.common.msg.TbActorMsg;
import org.thingsboard.server.common.msg.aware.DeviceAwareMsg;
import org.thingsboard.server.common.msg.aware.TenantAwareMsg;
import org.thingsboard.server.gen.transport.TransportProtos.TransportToDeviceActorMsg;
import java.io.Serializable;
import java.util.UUID;
/**
* Created by ashvayka on 09.10.18.
*/
@Data
public class TransportToDeviceActorMsgWrapper implements TbActorMsg, DeviceAwareMsg, TenantAwareMsg, Serializable {
private final TenantId tenantId;
private final DeviceId deviceId;
private final TransportToDeviceActorMsg msg;
public TransportToDeviceActorMsgWrapper(TransportToDeviceActorMsg msg) {
this.msg = msg;
this.tenantId = new TenantId(new UUID(msg.getSessionInfo().getTenantIdMSB(), msg.getSessionInfo().getTenantIdLSB()));
this.deviceId = new DeviceId(new UUID(msg.getSessionInfo().getDeviceIdMSB(), msg.getSessionInfo().getDeviceIdLSB()));
}
@Override
public MsgType getMsgType() {
return MsgType.TRANSPORT_TO_DEVICE_ACTOR_MSG;
}
}

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

@ -82,47 +82,6 @@ dashboard:
# Maximum allowed datapoints fetched by widgets
max_datapoints_limit: "${DASHBOARD_MAX_DATAPOINTS_LIMIT:50000}"
# Device communication protocol parameters
http:
request_timeout: "${HTTP_REQUEST_TIMEOUT:60000}"
# MQTT server parameters
mqtt:
# Enable/disable mqtt transport protocol.
enabled: "${MQTT_ENABLED:true}"
bind_address: "${MQTT_BIND_ADDRESS:0.0.0.0}"
bind_port: "${MQTT_BIND_PORT:1883}"
adaptor: "${MQTT_ADAPTOR_NAME:JsonMqttAdaptor}"
timeout: "${MQTT_TIMEOUT:10000}"
netty:
leak_detector_level: "${NETTY_LEASK_DETECTOR_LVL:DISABLED}"
boss_group_thread_count: "${NETTY_BOSS_GROUP_THREADS:1}"
worker_group_thread_count: "${NETTY_WORKER_GROUP_THREADS:12}"
max_payload_size: "${NETTY_MAX_PAYLOAD_SIZE:65536}"
# MQTT SSL configuration
ssl:
# Enable/disable SSL support
enabled: "${MQTT_SSL_ENABLED:false}"
# SSL protocol: See http://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#SSLContext
protocol: "${MQTT_SSL_PROTOCOL:TLSv1.2}"
# Path to the key store that holds the SSL certificate
key_store: "${MQTT_SSL_KEY_STORE:mqttserver.jks}"
# Password used to access the key store
key_store_password: "${MQTT_SSL_KEY_STORE_PASSWORD:server_ks_password}"
# Password used to access the key
key_password: "${MQTT_SSL_KEY_PASSWORD:server_key_password}"
# Type of the key store
key_store_type: "${MQTT_SSL_KEY_STORE_TYPE:JKS}"
# CoAP server parameters
coap:
# Enable/disable coap transport protocol.
enabled: "${COAP_ENABLED:false}"
bind_address: "${COAP_BIND_ADDRESS:0.0.0.0}"
bind_port: "${COAP_BIND_PORT:5683}"
adaptor: "${COAP_ADAPTOR_NAME:JsonCoapAdaptor}"
timeout: "${COAP_TIMEOUT:10000}"
#Quota parameters
quota:
host:
@ -154,7 +113,7 @@ quota:
# Interval for scheduled task that cleans expired records. TTL is used for expiring
cleanPeriodMs: "${QUOTA_TENANT_CLEAN_PERIOD_MS:300000}"
# Enable Host API Limits
enabled: "${QUOTA_TENANT_ENABLED:false}"
enabled: "${QUOTA_TENANT_ENABLED:true}"
# Array of whitelist tenants
whitelist: "${QUOTA_TENANT_WHITELIST:}"
# Array of blacklist tenants
@ -223,12 +182,6 @@ cassandra:
permit_max_wait_time: "${PERMIT_MAX_WAIT_TIME:120000}"
rate_limit_print_interval_ms: "${CASSANDRA_QUERY_RATE_LIMIT_PRINT_MS:30000}"
queue:
msg.ttl: 604800 # 7 days
ack.ttl: 604800 # 7 days
partitions.ttl: 604800 # 7 days
partitioning: "HOURS"
# SQL configuration parameters
sql:
# Specify executor service type used to perform timeseries insert tasks: SINGLE FIXED CACHED
@ -262,13 +215,6 @@ actors:
node:
# Errors for particular actor are persisted once per specified amount of milliseconds
error_persist_frequency: "${ACTORS_RULE_NODE_ERROR_FREQUENCY:3000}"
queue:
# Message queue type
type: "${ACTORS_RULE_QUEUE_TYPE:memory}"
# Message queue maximum size (per tenant)
max_size: "${ACTORS_RULE_QUEUE_MAX_SIZE:100}"
# Message queue cleanup period in seconds
cleanup_period: "${ACTORS_RULE_QUEUE_CLEANUP_PERIOD:3600}"
statistics:
# Enable/disable actor statistics
enabled: "${ACTORS_STATISTICS_ENABLED:true}"
@ -427,6 +373,19 @@ kafka:
batch.size: "${TB_KAFKA_BATCH_SIZE:16384}"
linger.ms: "${TB_KAFKA_LINGER_MS:1}"
buffer.memory: "${TB_BUFFER_MEMORY:33554432}"
transport_api:
requests_topic: "${TB_TRANSPORT_API_REQUEST_TOPIC:tb.transport.api.requests}"
responses_topic: "${TB_TRANSPORT_API_RESPONSE_TOPIC:tb.transport.api.responses}"
max_pending_requests: "${TB_TRANSPORT_MAX_PENDING_REQUESTS:10000}"
request_timeout: "${TB_TRANSPORT_MAX_REQUEST_TIMEOUT:10000}"
request_poll_interval: "${TB_TRANSPORT_REQUEST_POLL_INTERVAL_MS:25}"
request_auto_commit_interval: "${TB_TRANSPORT_REQUEST_AUTO_COMMIT_INTERVAL_MS:100}"
rule_engine:
topic: "${TB_RULE_ENGINE_TOPIC:tb.rule-engine}"
poll_interval: "${TB_RULE_ENGINE_POLL_INTERVAL_MS:25}"
auto_commit_interval: "${TB_RULE_ENGINE_AUTO_COMMIT_INTERVAL_MS:100}"
notifications:
topic: "${TB_TRANSPORT_NOTIFICATIONS_TOPIC:tb.transport.notifications}"
js:
evaluator: "${JS_EVALUATOR:local}" # local/remote
@ -456,3 +415,57 @@ js:
response_auto_commit_interval: "${REMOTE_JS_RESPONSE_AUTO_COMMIT_INTERVAL_MS:100}"
# Maximum allowed JavaScript execution errors before JavaScript will be blacklisted
max_errors: "${REMOTE_JS_SANDBOX_MAX_ERRORS:3}"
transport:
type: "${TRANSPORT_TYPE:local}" # local or remote
remote:
transport_api:
requests_topic: "${TB_TRANSPORT_API_REQUEST_TOPIC:tb.transport.api.requests}"
responses_topic: "${TB_TRANSPORT_API_RESPONSE_TOPIC:tb.transport.api.responses}"
max_pending_requests: "${TB_TRANSPORT_MAX_PENDING_REQUESTS:10000}"
request_timeout: "${TB_TRANSPORT_MAX_REQUEST_TIMEOUT:10000}"
request_poll_interval: "${TB_TRANSPORT_RESPONSE_POLL_INTERVAL_MS:25}"
request_auto_commit_interval: "${TB_TRANSPORT_RESPONSE_AUTO_COMMIT_INTERVAL_MS:1000}"
rule_engine:
topic: "${TB_RULE_ENGINE_TOPIC:tb.rule-engine}"
poll_interval: "${TB_RULE_ENGINE_POLL_INTERVAL_MS:25}"
auto_commit_interval: "${TB_RULE_ENGINE_AUTO_COMMIT_INTERVAL_MS:100}"
notifications:
topic: "${TB_TRANSPORT_NOTIFICATIONS_TOPIC:tb.transport.notifications}"
# Local HTTP transport parameters
http:
enabled: "${HTTP_ENABLED:true}"
request_timeout: "${HTTP_REQUEST_TIMEOUT:60000}"
# Local MQTT transport parameters
mqtt:
# Enable/disable mqtt transport protocol.
enabled: "${MQTT_ENABLED:true}"
bind_address: "${MQTT_BIND_ADDRESS:0.0.0.0}"
bind_port: "${MQTT_BIND_PORT:1883}"
timeout: "${MQTT_TIMEOUT:10000}"
netty:
leak_detector_level: "${NETTY_LEAK_DETECTOR_LVL:DISABLED}"
boss_group_thread_count: "${NETTY_BOSS_GROUP_THREADS:1}"
worker_group_thread_count: "${NETTY_WORKER_GROUP_THREADS:12}"
max_payload_size: "${NETTY_MAX_PAYLOAD_SIZE:65536}"
# MQTT SSL configuration
ssl:
# Enable/disable SSL support
enabled: "${MQTT_SSL_ENABLED:false}"
# SSL protocol: See http://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#SSLContext
protocol: "${MQTT_SSL_PROTOCOL:TLSv1.2}"
# Path to the key store that holds the SSL certificate
key_store: "${MQTT_SSL_KEY_STORE:mqttserver.jks}"
# Password used to access the key store
key_store_password: "${MQTT_SSL_KEY_STORE_PASSWORD:server_ks_password}"
# Password used to access the key
key_password: "${MQTT_SSL_KEY_PASSWORD:server_key_password}"
# Type of the key store
key_store_type: "${MQTT_SSL_KEY_STORE_TYPE:JKS}"
# Local CoAP transport parameters
coap:
# Enable/disable coap transport protocol.
enabled: "${COAP_ENABLED:true}"
bind_address: "${COAP_BIND_ADDRESS:0.0.0.0}"
bind_port: "${COAP_BIND_PORT:5683}"
timeout: "${COAP_TIMEOUT:10000}"

5
application/src/test/java/org/thingsboard/server/controller/AbstractRuleEngineControllerTest.java

@ -27,9 +27,7 @@ import org.thingsboard.server.common.data.page.TimePageData;
import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.data.rule.RuleChainMetaData;
import org.thingsboard.server.dao.queue.MsgQueue;
import org.thingsboard.server.dao.rule.RuleChainService;
import org.thingsboard.server.service.queue.MsgQueueService;
import java.io.IOException;
import java.util.function.Predicate;
@ -42,9 +40,6 @@ public class AbstractRuleEngineControllerTest extends AbstractControllerTest {
@Autowired
protected RuleChainService ruleChainService;
@Autowired
protected MsgQueueService msgQueueService;
protected RuleChain saveRuleChain(RuleChain ruleChain) throws Exception {
return doPost("/api/ruleChain", ruleChain, RuleChain.class);
}

9
application/src/test/java/org/thingsboard/server/rules/flow/AbstractRuleEngineFlowIntegrationTest.java

@ -191,9 +191,6 @@ public abstract class AbstractRuleEngineFlowIntegrationTest extends AbstractRule
Assert.assertEquals("serverAttributeValue1", getMetadata(outEvent).get("ss_serverAttributeKey1").asText());
Assert.assertEquals("serverAttributeValue2", getMetadata(outEvent).get("ss_serverAttributeKey2").asText());
List<TbMsg> unAckMsgList = Lists.newArrayList(msgQueueService.findUnprocessed(savedTenant.getId(), ruleChain.getId().getId(), 0L));
Assert.assertEquals(0, unAckMsgList.size());
}
@Test
@ -311,12 +308,6 @@ public abstract class AbstractRuleEngineFlowIntegrationTest extends AbstractRule
Assert.assertEquals("serverAttributeValue1", getMetadata(outEvent).get("ss_serverAttributeKey1").asText());
Assert.assertEquals("serverAttributeValue2", getMetadata(outEvent).get("ss_serverAttributeKey2").asText());
List<TbMsg> unAckMsgList = Lists.newArrayList(msgQueueService.findUnprocessed(savedTenant.getId(), rootRuleChain.getId().getId(), 0L));
Assert.assertEquals(0, unAckMsgList.size());
unAckMsgList = Lists.newArrayList(msgQueueService.findUnprocessed(savedTenant.getId(), secondaryRuleChain.getId().getId(), 0L));
Assert.assertEquals(0, unAckMsgList.size());
}
}

69
application/src/test/java/org/thingsboard/server/rules/lifecycle/AbstractRuleEngineLifecycleIntegrationTest.java

@ -162,73 +162,4 @@ public abstract class AbstractRuleEngineLifecycleIntegrationTest extends Abstrac
Assert.assertEquals("serverAttributeValue", getMetadata(outEvent).get("ss_serverAttributeKey").asText());
}
@Test
public void testRuleChainWithOneRuleAndMsgFromQueue() throws Exception {
// Creating Rule Chain
RuleChain ruleChain = new RuleChain();
ruleChain.setName("Simple Rule Chain");
ruleChain.setTenantId(savedTenant.getId());
ruleChain.setRoot(true);
ruleChain.setDebugMode(true);
ruleChain = saveRuleChain(ruleChain);
Assert.assertNull(ruleChain.getFirstRuleNodeId());
// Saving the device
Device device = new Device();
device.setName("My device");
device.setType("default");
device = doPost("/api/device", device, Device.class);
attributesService.save(device.getId(), DataConstants.SERVER_SCOPE,
Collections.singletonList(new BaseAttributeKvEntry(new StringDataEntry("serverAttributeKey", "serverAttributeValue"), System.currentTimeMillis())));
// Pushing Message to the system
TbMsg tbMsg = new TbMsg(UUIDs.timeBased(),
"CUSTOM",
device.getId(),
new TbMsgMetaData(),
"{}",
ruleChain.getId(), null, 0L);
msgQueueService.put(device.getTenantId(), tbMsg, ruleChain.getId().getId(), 0L);
Thread.sleep(1000);
RuleChainMetaData metaData = new RuleChainMetaData();
metaData.setRuleChainId(ruleChain.getId());
RuleNode ruleNode = new RuleNode();
ruleNode.setName("Simple Rule Node");
ruleNode.setType(org.thingsboard.rule.engine.metadata.TbGetAttributesNode.class.getName());
ruleNode.setDebugMode(true);
TbGetAttributesNodeConfiguration configuration = new TbGetAttributesNodeConfiguration();
configuration.setServerAttributeNames(Collections.singletonList("serverAttributeKey"));
ruleNode.setConfiguration(mapper.valueToTree(configuration));
metaData.setNodes(Collections.singletonList(ruleNode));
metaData.setFirstNodeIndex(0);
metaData = saveRuleChainMetaData(metaData);
Assert.assertNotNull(metaData);
ruleChain = getRuleChain(ruleChain.getId());
Assert.assertNotNull(ruleChain.getFirstRuleNodeId());
Thread.sleep(3000);
TimePageData<Event> eventsPage = getDebugEvents(savedTenant.getId(), ruleChain.getFirstRuleNodeId(), 1000);
List<Event> events = eventsPage.getData().stream().filter(filterByCustomEvent()).collect(Collectors.toList());
Assert.assertEquals(2, events.size());
Event inEvent = events.stream().filter(e -> e.getBody().get("type").asText().equals(DataConstants.IN)).findFirst().get();
Assert.assertEquals(ruleChain.getFirstRuleNodeId(), inEvent.getEntityId());
Assert.assertEquals(device.getId().getId().toString(), inEvent.getBody().get("entityId").asText());
Event outEvent = events.stream().filter(e -> e.getBody().get("type").asText().equals(DataConstants.OUT)).findFirst().get();
Assert.assertEquals(ruleChain.getFirstRuleNodeId(), outEvent.getEntityId());
Assert.assertEquals(device.getId().getId().toString(), outEvent.getBody().get("entityId").asText());
Assert.assertEquals("serverAttributeValue", getMetadata(outEvent).get("ss_serverAttributeKey").asText());
}
}

2
application/src/test/java/org/thingsboard/server/system/BaseHttpDeviceApiTest.java

@ -57,7 +57,7 @@ public abstract class BaseHttpDeviceApiTest extends AbstractControllerTest {
@Test
public void testGetAttributes() throws Exception {
doGetAsync("/api/v1/" + "WRONG_TOKEN" + "/attributes?clientKeys=keyA,keyB,keyC").andExpect(status().isUnauthorized());
doGetAsync("/api/v1/" + deviceCredentials.getCredentialsId() + "/attributes?clientKeys=keyA,keyB,keyC").andExpect(status().isNotFound());
doGetAsync("/api/v1/" + deviceCredentials.getCredentialsId() + "/attributes?clientKeys=keyA,keyB,keyC").andExpect(status().isOk());
Map<String, String> attrMap = new HashMap<>();
attrMap.put("keyA", "valueA");

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

@ -57,6 +57,8 @@ public class DataConstants {
public static final String ENTITY_UNASSIGNED = "ENTITY_UNASSIGNED";
public static final String ATTRIBUTES_UPDATED = "ATTRIBUTES_UPDATED";
public static final String ATTRIBUTES_DELETED = "ATTRIBUTES_DELETED";
public static final String ALARM_ACK = "ALARM_ACK";
public static final String ALARM_CLEAR = "ALARM_CLEAR";
public static final String RPC_CALL_FROM_SERVER_TO_DEVICE = "RPC_CALL_FROM_SERVER_TO_DEVICE";

4
common/message/pom.xml

@ -40,6 +40,10 @@
<groupId>org.thingsboard.common</groupId>
<artifactId>data</artifactId>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>

2
dao/src/main/java/org/thingsboard/server/dao/EncryptionUtil.java → common/message/src/main/java/org/thingsboard/server/common/msg/EncryptionUtil.java

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.dao;
package org.thingsboard.server.common.msg;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.crypto.digests.SHA3Digest;

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

@ -77,11 +77,6 @@ public enum MsgType {
*/
RULE_TO_SELF_MSG,
/**
* Message that is sent by Session Actor to Device Actor. Represents messages from the device itself.
*/
DEVICE_SESSION_TO_DEVICE_ACTOR_MSG,
DEVICE_ATTRIBUTES_UPDATE_TO_DEVICE_ACTOR_MSG,
DEVICE_CREDENTIALS_UPDATE_TO_DEVICE_ACTOR_MSG,
@ -96,8 +91,6 @@ public enum MsgType {
DEVICE_ACTOR_CLIENT_SIDE_RPC_TIMEOUT_MSG,
DEVICE_ACTOR_QUEUE_TIMEOUT_MSG,
/**
* Message that is sent from the Device Actor to Rule Engine. Requires acknowledgement
*/
@ -106,11 +99,16 @@ public enum MsgType {
/**
* Message that is sent from Rule Engine to the Device Actor when message is successfully pushed to queue.
*/
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,
STATS_PERSIST_TICK_MSG;
STATS_PERSIST_TICK_MSG,
/**
* Message that is sent by TransportRuleEngineService to Device Actor. Represents messages from the device itself.
*/
TRANSPORT_TO_DEVICE_ACTOR_MSG;
}

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

@ -1,30 +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.common.msg.core;
import org.thingsboard.server.common.msg.TbActorMsg;
import org.thingsboard.server.common.msg.aware.SessionAwareMsg;
import org.thingsboard.server.common.msg.session.ToDeviceMsg;
import java.io.Serializable;
/**
* @author Andrew Shvayka
*/
public interface ActorSystemToDeviceSessionActorMsg extends SessionAwareMsg, Serializable, TbActorMsg {
ToDeviceMsg getMsg();
}

47
common/message/src/main/java/org/thingsboard/server/common/msg/core/AttributesUpdateNotification.java

@ -1,47 +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.common.msg.core;
import lombok.ToString;
import org.thingsboard.server.common.msg.kv.AttributesKVMsg;
import org.thingsboard.server.common.msg.session.SessionMsgType;
import org.thingsboard.server.common.msg.session.SessionMsgType;
import org.thingsboard.server.common.msg.session.ToDeviceMsg;
@ToString
public class AttributesUpdateNotification implements ToDeviceMsg {
private static final long serialVersionUID = 1L;
private AttributesKVMsg data;
public AttributesUpdateNotification(AttributesKVMsg data) {
this.data = data;
}
@Override
public boolean isSuccess() {
return true;
}
public SessionMsgType getSessionMsgType() {
return SessionMsgType.ATTRIBUTES_UPDATE_NOTIFICATION;
}
public AttributesKVMsg getData() {
return data;
}
}

28
common/message/src/main/java/org/thingsboard/server/common/msg/core/AttributesUpdateRequest.java

@ -1,28 +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.common.msg.core;
import java.util.Set;
import org.thingsboard.server.common.data.kv.AttributeKvEntry;
import org.thingsboard.server.common.msg.session.FromDeviceMsg;
import org.thingsboard.server.common.msg.session.FromDeviceRequestMsg;
public interface AttributesUpdateRequest extends FromDeviceRequestMsg {
Set<AttributeKvEntry> getAttributes();
}

52
common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicActorSystemToDeviceSessionActorMsg.java

@ -1,52 +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.common.msg.core;
import org.thingsboard.server.common.data.id.SessionId;
import org.thingsboard.server.common.msg.MsgType;
import org.thingsboard.server.common.msg.session.ToDeviceMsg;
public class BasicActorSystemToDeviceSessionActorMsg implements ActorSystemToDeviceSessionActorMsg {
private final ToDeviceMsg msg;
private final SessionId sessionId;
public BasicActorSystemToDeviceSessionActorMsg(ToDeviceMsg msg, SessionId sessionId) {
super();
this.msg = msg;
this.sessionId = sessionId;
}
@Override
public SessionId getSessionId() {
return sessionId;
}
@Override
public ToDeviceMsg getMsg() {
return msg;
}
@Override
public String toString() {
return "BasicActorSystemToDeviceSessionActorMsg [msg=" + msg + ", sessionId=" + sessionId + "]";
}
@Override
public MsgType getMsgType() {
return MsgType.ACTOR_SYSTEM_TO_DEVICE_SESSION_ACTOR_MSG;
}
}

63
common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicAttributesUpdateRequest.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.common.msg.core;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.Set;
import org.thingsboard.server.common.data.kv.AttributeKvEntry;
import org.thingsboard.server.common.msg.session.SessionMsgType;
public class BasicAttributesUpdateRequest extends BasicRequest implements AttributesUpdateRequest {
private static final long serialVersionUID = 1L;
private final Set<AttributeKvEntry> data;
public BasicAttributesUpdateRequest() {
this(DEFAULT_REQUEST_ID);
}
public BasicAttributesUpdateRequest(Integer requestId) {
super(requestId);
this.data = new LinkedHashSet<>();
}
public void add(AttributeKvEntry entry) {
this.data.add(entry);
}
public void add(Collection<AttributeKvEntry> entries) {
this.data.addAll(entries);
}
@Override
public SessionMsgType getMsgType() {
return SessionMsgType.POST_ATTRIBUTES_REQUEST;
}
@Override
public Set<AttributeKvEntry> getAttributes() {
return data;
}
@Override
public String toString() {
return "BasicAttributesUpdateRequest [data=" + data + "]";
}
}

45
common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicCommandAckResponse.java

@ -1,45 +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.common.msg.core;
import org.thingsboard.server.common.msg.session.SessionMsgType;
import org.thingsboard.server.common.msg.session.SessionMsgType;
public class BasicCommandAckResponse extends BasicResponseMsg<Integer> implements StatusCodeResponse {
private static final long serialVersionUID = 1L;
public static BasicCommandAckResponse onSuccess(SessionMsgType requestMsgType, Integer requestId) {
return BasicCommandAckResponse.onSuccess(requestMsgType, requestId, 200);
}
public static BasicCommandAckResponse onSuccess(SessionMsgType requestMsgType, Integer requestId, Integer code) {
return new BasicCommandAckResponse(requestMsgType, requestId, true, null, code);
}
public static BasicCommandAckResponse onError(SessionMsgType requestMsgType, Integer requestId, Exception error) {
return new BasicCommandAckResponse(requestMsgType, requestId, false, error, null);
}
private BasicCommandAckResponse(SessionMsgType requestMsgType, Integer requestId, boolean success, Exception error, Integer code) {
super(requestMsgType, requestId, SessionMsgType.TO_DEVICE_RPC_RESPONSE_ACK, success, error, code);
}
@Override
public String toString() {
return "BasicStatusCodeResponse []";
}
}

59
common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicGetAttributesRequest.java

@ -1,59 +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.common.msg.core;
import lombok.ToString;
import org.thingsboard.server.common.msg.session.SessionMsgType;
import org.thingsboard.server.common.msg.session.SessionMsgType;
import java.util.Collections;
import java.util.Optional;
import java.util.Set;
@ToString
public class BasicGetAttributesRequest extends BasicRequest implements GetAttributesRequest {
private static final long serialVersionUID = 1L;
private final Set<String> clientKeys;
private final Set<String> sharedKeys;
public BasicGetAttributesRequest(Integer requestId) {
this(requestId, Collections.emptySet(), Collections.emptySet());
}
public BasicGetAttributesRequest(Integer requestId, Set<String> clientKeys, Set<String> sharedKeys) {
super(requestId);
this.clientKeys = clientKeys;
this.sharedKeys = sharedKeys;
}
@Override
public SessionMsgType getMsgType() {
return SessionMsgType.GET_ATTRIBUTES_REQUEST;
}
@Override
public Optional<Set<String>> getClientAttributeNames() {
return Optional.ofNullable(clientKeys);
}
@Override
public Optional<Set<String>> getSharedAttributeNames() {
return Optional.ofNullable(sharedKeys);
}
}

40
common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicGetAttributesResponse.java

@ -1,40 +0,0 @@
/**
* Copyright © 2016-2018 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.msg.core;
import lombok.ToString;
import org.thingsboard.server.common.msg.kv.AttributesKVMsg;
import org.thingsboard.server.common.msg.session.SessionMsgType;
import org.thingsboard.server.common.msg.session.SessionMsgType;
@ToString
public class BasicGetAttributesResponse extends BasicResponseMsg<AttributesKVMsg> implements GetAttributesResponse {
private static final long serialVersionUID = 1L;
public static BasicGetAttributesResponse onSuccess(SessionMsgType requestMsgType, int requestId, AttributesKVMsg code) {
return new BasicGetAttributesResponse(requestMsgType, requestId, true, null, code);
}
public static BasicGetAttributesResponse onError(SessionMsgType requestMsgType, int requestId, Exception error) {
return new BasicGetAttributesResponse(requestMsgType, requestId, false, error, null);
}
private BasicGetAttributesResponse(SessionMsgType requestMsgType, int requestId, boolean success, Exception error, AttributesKVMsg code) {
super(requestMsgType, requestId, SessionMsgType.GET_ATTRIBUTES_RESPONSE, success, error, code);
}
}

79
common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicResponseMsg.java

@ -1,79 +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.common.msg.core;
import java.io.Serializable;
import java.util.Optional;
import org.thingsboard.server.common.msg.session.SessionMsgType;
import org.thingsboard.server.common.msg.session.SessionMsgType;
public class BasicResponseMsg<T extends Serializable> implements ResponseMsg<T> {
private static final long serialVersionUID = 1L;
private final SessionMsgType requestMsgType;
private final Integer requestId;
private final SessionMsgType sessionMsgType;
private final boolean success;
private final T data;
private final Exception error;
protected BasicResponseMsg(SessionMsgType requestMsgType, Integer requestId, SessionMsgType sessionMsgType, boolean success, Exception error, T data) {
super();
this.requestMsgType = requestMsgType;
this.requestId = requestId;
this.sessionMsgType = sessionMsgType;
this.success = success;
this.error = error;
this.data = data;
}
@Override
public SessionMsgType getRequestMsgType() {
return requestMsgType;
}
@Override
public Integer getRequestId() {
return requestId;
}
@Override
public boolean isSuccess() {
return success;
}
@Override
public Optional<Exception> getError() {
return Optional.ofNullable(error);
}
@Override
public Optional<T> getData() {
return Optional.ofNullable(data);
}
@Override
public String toString() {
return "BasicResponseMsg [success=" + success + ", data=" + data + ", error=" + error + "]";
}
public SessionMsgType getSessionMsgType() {
return sessionMsgType;
}
}

42
common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicStatusCodeResponse.java

@ -1,42 +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.common.msg.core;
import lombok.ToString;
import org.thingsboard.server.common.msg.session.SessionMsgType;
import org.thingsboard.server.common.msg.session.SessionMsgType;
@ToString
public class BasicStatusCodeResponse extends BasicResponseMsg<Integer> implements StatusCodeResponse {
private static final long serialVersionUID = 1L;
public static BasicStatusCodeResponse onSuccess(SessionMsgType requestMsgType, Integer requestId) {
return BasicStatusCodeResponse.onSuccess(requestMsgType, requestId, 0);
}
public static BasicStatusCodeResponse onSuccess(SessionMsgType requestMsgType, Integer requestId, Integer code) {
return new BasicStatusCodeResponse(requestMsgType, requestId, true, null, code);
}
public static BasicStatusCodeResponse onError(SessionMsgType requestMsgType, Integer requestId, Exception error) {
return new BasicStatusCodeResponse(requestMsgType, requestId, false, error, null);
}
private BasicStatusCodeResponse(SessionMsgType requestMsgType, Integer requestId, boolean success, Exception error, Integer code) {
super(requestMsgType, requestId, SessionMsgType.STATUS_CODE_RESPONSE, success, error, code);
}
}

66
common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicTelemetryUploadRequest.java

@ -1,66 +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.common.msg.core;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.thingsboard.server.common.data.kv.KvEntry;
import org.thingsboard.server.common.msg.session.SessionMsgType;
import org.thingsboard.server.common.msg.session.SessionMsgType;
public class BasicTelemetryUploadRequest extends BasicRequest implements TelemetryUploadRequest {
private static final long serialVersionUID = 1L;
private final Map<Long, List<KvEntry>> data;
public BasicTelemetryUploadRequest() {
this(DEFAULT_REQUEST_ID);
}
public BasicTelemetryUploadRequest(Integer requestId) {
super(requestId);
this.data = new HashMap<>();
}
public void add(long ts, KvEntry entry) {
List<KvEntry> tsEntries = data.get(ts);
if (tsEntries == null) {
tsEntries = new ArrayList<>();
data.put(ts, tsEntries);
}
tsEntries.add(entry);
}
@Override
public SessionMsgType getMsgType() {
return SessionMsgType.POST_TELEMETRY_REQUEST;
}
@Override
public Map<Long, List<KvEntry>> getData() {
return data;
}
@Override
public String toString() {
return "BasicTelemetryUploadRequest [data=" + data + "]";
}
}

29
common/message/src/main/java/org/thingsboard/server/common/msg/core/GetAttributesRequest.java

@ -1,29 +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.common.msg.core;
import java.util.Optional;
import java.util.Set;
import org.thingsboard.server.common.msg.session.FromDeviceMsg;
import org.thingsboard.server.common.msg.session.FromDeviceRequestMsg;
public interface GetAttributesRequest extends FromDeviceRequestMsg {
Optional<Set<String>> getClientAttributeNames();
Optional<Set<String>> getSharedAttributeNames();
}

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

@ -1,22 +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.common.msg.core;
import org.thingsboard.server.common.msg.kv.AttributesKVMsg;
public interface GetAttributesResponse extends ResponseMsg<AttributesKVMsg> {
}

33
common/message/src/main/java/org/thingsboard/server/common/msg/core/ResponseMsg.java

@ -1,33 +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.common.msg.core;
import java.io.Serializable;
import java.util.Optional;
import org.thingsboard.server.common.msg.session.SessionMsgType;
import org.thingsboard.server.common.msg.session.ToDeviceMsg;
public interface ResponseMsg<T extends Serializable> extends ToDeviceMsg {
SessionMsgType getRequestMsgType();
Integer getRequestId();
Optional<Exception> getError();
Optional<T> getData();
}

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

@ -1,30 +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.common.msg.core;
import org.thingsboard.server.common.msg.session.FromDeviceMsg;
import org.thingsboard.server.common.msg.session.SessionMsgType;
import org.thingsboard.server.common.msg.session.SessionMsgType;
/**
* @author Andrew Shvayka
*/
public class RpcUnsubscribeMsg implements FromDeviceMsg {
@Override
public SessionMsgType getMsgType() {
return SessionMsgType.UNSUBSCRIBE_RPC_COMMANDS_REQUEST;
}
}

53
common/message/src/main/java/org/thingsboard/server/common/msg/core/RuleEngineErrorMsg.java

@ -1,53 +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.common.msg.core;
import lombok.Data;
import org.thingsboard.server.common.msg.session.SessionMsgType;
import org.thingsboard.server.common.msg.session.SessionMsgType;
import org.thingsboard.server.common.msg.session.ToDeviceMsg;
/**
* @author Andrew Shvayka
*/
@Data
public class RuleEngineErrorMsg implements ToDeviceMsg {
private final SessionMsgType inSessionMsgType;
private final RuleEngineError error;
@Override
public boolean isSuccess() {
return false;
}
public SessionMsgType getSessionMsgType() {
return SessionMsgType.RULE_ENGINE_ERROR;
}
public String getErrorMsg() {
switch (error) {
case QUEUE_PUT_TIMEOUT:
return "Timeout during persistence of the message to the queue!";
case SERVER_ERROR:
return "Error during processing of message by the server!";
case TIMEOUT:
return "Timeout during processing of message by the server!";
default:
throw new RuntimeException("Error " + error + " is not supported!");
}
}
}

38
common/message/src/main/java/org/thingsboard/server/common/msg/core/SessionCloseNotification.java

@ -1,38 +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.common.msg.core;
import lombok.ToString;
import org.thingsboard.server.common.msg.kv.AttributesKVMsg;
import org.thingsboard.server.common.msg.session.SessionMsgType;
import org.thingsboard.server.common.msg.session.SessionMsgType;
import org.thingsboard.server.common.msg.session.ToDeviceMsg;
@ToString
public class SessionCloseNotification implements ToDeviceMsg {
private static final long serialVersionUID = 1L;
@Override
public boolean isSuccess() {
return true;
}
public SessionMsgType getSessionMsgType() {
return SessionMsgType.SESSION_CLOSE;
}
}

35
common/message/src/main/java/org/thingsboard/server/common/msg/core/SessionOpenMsg.java

@ -1,35 +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.common.msg.core;
import lombok.Data;
import org.thingsboard.server.common.data.id.SessionId;
import org.thingsboard.server.common.msg.aware.SessionAwareMsg;
import org.thingsboard.server.common.msg.session.FromDeviceMsg;
import org.thingsboard.server.common.msg.session.SessionMsgType;
import org.thingsboard.server.common.msg.session.SessionMsgType;
/**
* @author Andrew Shvayka
*/
@Data
public class SessionOpenMsg implements FromDeviceMsg {
@Override
public SessionMsgType getMsgType() {
return SessionMsgType.SESSION_OPEN;
}
}

20
common/message/src/main/java/org/thingsboard/server/common/msg/core/StatusCodeResponse.java

@ -1,20 +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.common.msg.core;
public interface StatusCodeResponse extends ResponseMsg<Integer>{
}

29
common/message/src/main/java/org/thingsboard/server/common/msg/core/TelemetryUploadRequest.java

@ -1,29 +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.common.msg.core;
import java.util.List;
import java.util.Map;
import org.thingsboard.server.common.data.kv.KvEntry;
import org.thingsboard.server.common.msg.session.FromDeviceMsg;
import org.thingsboard.server.common.msg.session.FromDeviceRequestMsg;
public interface TelemetryUploadRequest extends FromDeviceRequestMsg {
Map<Long, List<KvEntry>> getData();
}

41
common/message/src/main/java/org/thingsboard/server/common/msg/core/ToDeviceRpcRequestMsg.java

@ -1,41 +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.common.msg.core;
import lombok.Data;
import org.thingsboard.server.common.msg.session.SessionMsgType;
import org.thingsboard.server.common.msg.session.SessionMsgType;
import org.thingsboard.server.common.msg.session.ToDeviceMsg;
/**
* @author Andrew Shvayka
*/
@Data
public class ToDeviceRpcRequestMsg implements ToDeviceMsg {
private final int requestId;
private final String method;
private final String params;
public SessionMsgType getSessionMsgType() {
return SessionMsgType.TO_DEVICE_RPC_REQUEST;
}
@Override
public boolean isSuccess() {
return true;
}
}

36
common/message/src/main/java/org/thingsboard/server/common/msg/core/ToDeviceRpcResponseMsg.java

@ -1,36 +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.common.msg.core;
import lombok.Data;
import org.thingsboard.server.common.msg.session.FromDeviceMsg;
import org.thingsboard.server.common.msg.session.SessionMsgType;
import org.thingsboard.server.common.msg.session.SessionMsgType;
/**
* @author Andrew Shvayka
*/
@Data
public class ToDeviceRpcResponseMsg implements FromDeviceMsg {
private final int requestId;
private final String data;
@Override
public SessionMsgType getMsgType() {
return SessionMsgType.TO_DEVICE_RPC_RESPONSE;
}
}

36
common/message/src/main/java/org/thingsboard/server/common/msg/core/ToServerRpcRequestMsg.java

@ -1,36 +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.common.msg.core;
import lombok.Data;
import org.thingsboard.server.common.msg.session.FromDeviceRequestMsg;
import org.thingsboard.server.common.msg.session.SessionMsgType;
/**
* @author Andrew Shvayka
*/
@Data
public class ToServerRpcRequestMsg implements FromDeviceRequestMsg {
private final Integer requestId;
private final String method;
private final String params;
@Override
public SessionMsgType getMsgType() {
return SessionMsgType.TO_SERVER_RPC_REQUEST;
}
}

11
common/message/src/main/java/org/thingsboard/server/common/msg/core/ToServerRpcResponseMsg.java

@ -16,25 +16,16 @@
package org.thingsboard.server.common.msg.core;
import lombok.Data;
import org.thingsboard.server.common.msg.session.FromDeviceMsg;
import org.thingsboard.server.common.msg.session.SessionMsgType;
import org.thingsboard.server.common.msg.session.SessionMsgType;
import org.thingsboard.server.common.msg.session.ToDeviceMsg;
/**
* @author Andrew Shvayka
*/
@Data
public class ToServerRpcResponseMsg implements ToDeviceMsg {
public class ToServerRpcResponseMsg {
private final int requestId;
private final String data;
public SessionMsgType getSessionMsgType() {
return SessionMsgType.TO_SERVER_RPC_RESPONSE;
}
@Override
public boolean isSuccess() {
return true;
}

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

@ -1,107 +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.common.msg.device;
import lombok.ToString;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.SessionId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.msg.MsgType;
import org.thingsboard.server.common.msg.cluster.ServerAddress;
import org.thingsboard.server.common.msg.session.FromDeviceMsg;
import org.thingsboard.server.common.msg.session.SessionType;
import org.thingsboard.server.common.msg.session.TransportToDeviceSessionActorMsg;
import java.util.Optional;
@ToString
public class BasicDeviceToDeviceActorMsg implements DeviceToDeviceActorMsg {
private static final long serialVersionUID = -1866795134993115408L;
private final TenantId tenantId;
private final CustomerId customerId;
private final DeviceId deviceId;
private final SessionId sessionId;
private final SessionType sessionType;
private final ServerAddress serverAddress;
private final FromDeviceMsg msg;
public BasicDeviceToDeviceActorMsg(DeviceToDeviceActorMsg other, FromDeviceMsg msg) {
this(null, other.getTenantId(), other.getCustomerId(), other.getDeviceId(), other.getSessionId(), other.getSessionType(), msg);
}
public BasicDeviceToDeviceActorMsg(TransportToDeviceSessionActorMsg msg, SessionType sessionType) {
this(null, msg.getTenantId(), msg.getCustomerId(), msg.getDeviceId(), msg.getSessionId(), sessionType, msg.getSessionMsg().getMsg());
}
private BasicDeviceToDeviceActorMsg(ServerAddress serverAddress, TenantId tenantId, CustomerId customerId, DeviceId deviceId, SessionId sessionId, SessionType sessionType,
FromDeviceMsg msg) {
super();
this.serverAddress = serverAddress;
this.tenantId = tenantId;
this.customerId = customerId;
this.deviceId = deviceId;
this.sessionId = sessionId;
this.sessionType = sessionType;
this.msg = msg;
}
@Override
public DeviceId getDeviceId() {
return deviceId;
}
@Override
public CustomerId getCustomerId() {
return customerId;
}
public TenantId getTenantId() {
return tenantId;
}
@Override
public SessionId getSessionId() {
return sessionId;
}
@Override
public SessionType getSessionType() {
return sessionType;
}
@Override
public Optional<ServerAddress> getServerAddress() {
return Optional.ofNullable(serverAddress);
}
@Override
public FromDeviceMsg getPayload() {
return msg;
}
@Override
public DeviceToDeviceActorMsg toOtherAddress(ServerAddress otherAddress) {
return new BasicDeviceToDeviceActorMsg(otherAddress, tenantId, customerId, deviceId, sessionId, sessionType, msg);
}
@Override
public MsgType getMsgType() {
return MsgType.DEVICE_SESSION_TO_DEVICE_ACTOR_MSG;
}
}

41
common/message/src/main/java/org/thingsboard/server/common/msg/device/DeviceToDeviceActorMsg.java

@ -1,41 +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.common.msg.device;
import java.io.Serializable;
import java.util.Optional;
import org.thingsboard.server.common.data.id.SessionId;
import org.thingsboard.server.common.msg.TbActorMsg;
import org.thingsboard.server.common.msg.aware.CustomerAwareMsg;
import org.thingsboard.server.common.msg.aware.DeviceAwareMsg;
import org.thingsboard.server.common.msg.aware.TenantAwareMsg;
import org.thingsboard.server.common.msg.cluster.ServerAddress;
import org.thingsboard.server.common.msg.session.FromDeviceMsg;
import org.thingsboard.server.common.msg.session.SessionType;
public interface DeviceToDeviceActorMsg extends TbActorMsg, DeviceAwareMsg, CustomerAwareMsg, TenantAwareMsg, Serializable {
SessionId getSessionId();
SessionType getSessionType();
Optional<ServerAddress> getServerAddress();
FromDeviceMsg getPayload();
DeviceToDeviceActorMsg toOtherAddress(ServerAddress otherAddress);
}

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

@ -1,22 +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.common.msg.session;
public interface AdaptorToSessionActorMsg extends SessionMsg {
FromDeviceMsg getMsg();
}

32
common/message/src/main/java/org/thingsboard/server/common/msg/session/BasicAdaptorToSessionActorMsg.java

@ -1,32 +0,0 @@
/**
* Copyright © 2016-2018 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.msg.session;
public class BasicAdaptorToSessionActorMsg extends BasicSessionMsg implements AdaptorToSessionActorMsg {
private final FromDeviceMsg msg;
public BasicAdaptorToSessionActorMsg(SessionContext ctx, FromDeviceMsg msg) {
super(ctx);
this.msg = msg;
}
@Override
public FromDeviceMsg getMsg() {
return msg;
}
}

32
common/message/src/main/java/org/thingsboard/server/common/msg/session/BasicSessionActorToAdaptorMsg.java

@ -1,32 +0,0 @@
/**
* Copyright © 2016-2018 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.msg.session;
public class BasicSessionActorToAdaptorMsg extends BasicSessionMsg implements SessionActorToAdaptorMsg {
private final ToDeviceMsg msg;
public BasicSessionActorToAdaptorMsg(SessionContext ctx, ToDeviceMsg msg) {
super(ctx);
this.msg = msg;
}
@Override
public ToDeviceMsg getMsg() {
return msg;
}
}

44
common/message/src/main/java/org/thingsboard/server/common/msg/session/BasicSessionMsg.java

@ -1,44 +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.common.msg.session;
import org.thingsboard.server.common.data.id.SessionId;
public class BasicSessionMsg implements SessionMsg {
private final SessionContext ctx;
public BasicSessionMsg(SessionContext ctx) {
super();
this.ctx = ctx;
}
@Override
public SessionId getSessionId() {
return ctx.getSessionId();
}
@Override
public SessionContext getSessionContext() {
return ctx;
}
@Override
public String toString() {
return "BasicSessionMsg [ctx=" + ctx + "]";
}
}

74
common/message/src/main/java/org/thingsboard/server/common/msg/session/BasicTransportToDeviceSessionActorMsg.java

@ -1,74 +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.common.msg.session;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.SessionId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.msg.MsgType;
public class BasicTransportToDeviceSessionActorMsg implements TransportToDeviceSessionActorMsg {
private final TenantId tenantId;
private final CustomerId customerId;
private final DeviceId deviceId;
private final AdaptorToSessionActorMsg msg;
public BasicTransportToDeviceSessionActorMsg(Device device, AdaptorToSessionActorMsg msg) {
super();
this.tenantId = device.getTenantId();
this.customerId = device.getCustomerId();
this.deviceId = device.getId();
this.msg = msg;
}
@Override
public DeviceId getDeviceId() {
return deviceId;
}
@Override
public CustomerId getCustomerId() {
return customerId;
}
public TenantId getTenantId() {
return tenantId;
}
@Override
public SessionId getSessionId() {
return msg.getSessionId();
}
@Override
public AdaptorToSessionActorMsg getSessionMsg() {
return msg;
}
@Override
public String toString() {
return "BasicTransportToDeviceSessionActorMsg [tenantId=" + tenantId + ", customerId=" + customerId + ", deviceId=" + deviceId + ", msg=" + msg
+ "]";
}
@Override
public MsgType getMsgType() {
return MsgType.TRANSPORT_TO_DEVICE_SESSION_ACTOR_MSG;
}
}

24
common/message/src/main/java/org/thingsboard/server/common/msg/session/FromDeviceMsg.java

@ -1,24 +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.common.msg.session;
import java.io.Serializable;
public interface FromDeviceMsg extends Serializable {
SessionMsgType getMsgType();
}

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

@ -1,22 +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.common.msg.session;
public interface SessionActorToAdaptorMsg extends SessionMsg {
ToDeviceMsg getMsg();
}

16
common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionContext.java

@ -15,21 +15,11 @@
*/
package org.thingsboard.server.common.msg.session;
import org.thingsboard.server.common.data.id.SessionId;
import org.thingsboard.server.common.msg.session.ex.SessionException;
import java.util.UUID;
public interface SessionContext {
SessionId getSessionId();
SessionType getSessionType();
void onMsg(SessionActorToAdaptorMsg msg) throws SessionException;
void onMsg(SessionCtrlMsg msg) throws SessionException;
boolean isClosed();
long getTimeout();
UUID getSessionId();
int nextMsgId();
}

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

@ -1,23 +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.common.msg.session;
import org.thingsboard.server.common.msg.TbActorMsg;
import org.thingsboard.server.common.msg.aware.SessionAwareMsg;
public interface SessionCtrlMsg extends SessionAwareMsg, TbActorMsg {
}

24
common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionMsg.java

@ -1,24 +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.common.msg.session;
import org.thingsboard.server.common.msg.aware.SessionAwareMsg;
public interface SessionMsg extends SessionAwareMsg {
SessionContext getSessionContext();
}

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

@ -1,22 +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.common.msg.session;
public enum SessionType {
SYNC, ASYNC;
}

26
common/message/src/main/java/org/thingsboard/server/common/msg/session/ToDeviceMsg.java

@ -1,26 +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.common.msg.session;
import java.io.Serializable;
public interface ToDeviceMsg extends Serializable {
boolean isSuccess();
SessionMsgType getSessionMsgType();
}

28
common/message/src/main/java/org/thingsboard/server/common/msg/session/TransportToDeviceSessionActorMsg.java

@ -1,28 +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.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.DeviceAwareMsg;
import org.thingsboard.server.common.msg.aware.SessionAwareMsg;
import org.thingsboard.server.common.msg.aware.TenantAwareMsg;
public interface TransportToDeviceSessionActorMsg extends DeviceAwareMsg, CustomerAwareMsg, TenantAwareMsg, SessionAwareMsg, TbActorMsg {
AdaptorToSessionActorMsg getSessionMsg();
}

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

@ -1,68 +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.common.msg.session.ctrl;
import org.thingsboard.server.common.data.id.SessionId;
import org.thingsboard.server.common.msg.MsgType;
import org.thingsboard.server.common.msg.session.SessionCtrlMsg;
public class SessionCloseMsg implements SessionCtrlMsg {
private final SessionId sessionId;
private final boolean revoked;
private final boolean timeout;
public static SessionCloseMsg onDisconnect(SessionId sessionId) {
return new SessionCloseMsg(sessionId, false, false);
}
public static SessionCloseMsg onError(SessionId sessionId) {
return new SessionCloseMsg(sessionId, false, false);
}
public static SessionCloseMsg onTimeout(SessionId sessionId) {
return new SessionCloseMsg(sessionId, false, true);
}
public static SessionCloseMsg onCredentialsRevoked(SessionId sessionId) {
return new SessionCloseMsg(sessionId, true, false);
}
private SessionCloseMsg(SessionId sessionId, boolean unauthorized, boolean timeout) {
super();
this.sessionId = sessionId;
this.revoked = unauthorized;
this.timeout = timeout;
}
@Override
public SessionId getSessionId() {
return sessionId;
}
public boolean isCredentialsRevoked() {
return revoked;
}
public boolean isTimeout() {
return timeout;
}
@Override
public MsgType getMsgType() {
return MsgType.SESSION_CTRL_MSG;
}
}

36
common/message/src/main/java/org/thingsboard/server/common/msg/timeout/DeviceActorQueueTimeoutMsg.java

@ -1,36 +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.common.msg.timeout;
import org.thingsboard.server.common.msg.MsgType;
import org.thingsboard.server.common.msg.timeout.TimeoutMsg;
import java.util.UUID;
/**
* @author Andrew Shvayka
*/
public final class DeviceActorQueueTimeoutMsg extends TimeoutMsg<UUID> {
public DeviceActorQueueTimeoutMsg(UUID id, long timeout) {
super(id, timeout);
}
@Override
public MsgType getMsgType() {
return MsgType.DEVICE_ACTOR_QUEUE_TIMEOUT_MSG;
}
}

3
common/pom.xml

@ -23,7 +23,6 @@
<version>2.2.0-SNAPSHOT</version>
<artifactId>thingsboard</artifactId>
</parent>
<groupId>org.thingsboard</groupId>
<artifactId>common</artifactId>
<packaging>pom</packaging>
@ -37,8 +36,8 @@
<modules>
<module>data</module>
<module>message</module>
<module>transport</module>
<module>queue</module>
<module>transport</module>
</modules>
</project>

6
common/queue/pom.xml

@ -16,7 +16,7 @@
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
@ -64,6 +64,10 @@
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>

50
common/queue/src/main/java/org/thingsboard/server/kafka/AbstractTbKafkaTemplate.java

@ -0,0 +1,50 @@
/**
* 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.kafka;
import lombok.extern.slf4j.Slf4j;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.UUID;
/**
* Created by ashvayka on 25.09.18.
*/
@Slf4j
public abstract class AbstractTbKafkaTemplate {
protected byte[] uuidToBytes(UUID uuid) {
ByteBuffer buf = ByteBuffer.allocate(16);
buf.putLong(uuid.getMostSignificantBits());
buf.putLong(uuid.getLeastSignificantBits());
return buf.array();
}
protected static UUID bytesToUuid(byte[] bytes) {
ByteBuffer bb = ByteBuffer.wrap(bytes);
long firstLong = bb.getLong();
long secondLong = bb.getLong();
return new UUID(firstLong, secondLong);
}
protected byte[] stringToBytes(String string) {
return string.getBytes(StandardCharsets.UTF_8);
}
protected String bytesToString(byte[] data) {
return new String(data, StandardCharsets.UTF_8);
}
}

66
common/queue/src/main/java/org/thingsboard/server/kafka/AsyncCallbackTemplate.java

@ -0,0 +1,66 @@
/**
* 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.kafka;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
/**
* Created by ashvayka on 05.10.18.
*/
public class AsyncCallbackTemplate {
public static <T> void withCallbackAndTimeout(ListenableFuture<T> future,
Consumer<T> onSuccess,
Consumer<Throwable> onFailure,
long timeoutInMs,
ScheduledExecutorService timeoutExecutor,
Executor callbackExecutor) {
future = Futures.withTimeout(future, timeoutInMs, TimeUnit.MILLISECONDS, timeoutExecutor);
withCallback(future, onSuccess, onFailure, callbackExecutor);
}
public static <T> void withCallback(ListenableFuture<T> future, Consumer<T> onSuccess,
Consumer<Throwable> onFailure, Executor executor) {
FutureCallback<T> callback = new FutureCallback<T>() {
@Override
public void onSuccess(T result) {
try {
onSuccess.accept(result);
} catch (Throwable th) {
onFailure(th);
}
}
@Override
public void onFailure(Throwable t) {
onFailure.accept(t);
}
};
if (executor != null) {
Futures.addCallback(future, callback, executor);
} else {
Futures.addCallback(future, callback);
}
}
}

7
common/queue/src/main/java/org/thingsboard/server/kafka/TBKafkaConsumerTemplate.java

@ -43,12 +43,15 @@ public class TBKafkaConsumerTemplate<T> {
private final String topic;
@Builder
private TBKafkaConsumerTemplate(TbKafkaSettings settings, TbKafkaDecoder<T> decoder, TbKafkaRequestIdExtractor<T> requestIdExtractor,
private TBKafkaConsumerTemplate(TbKafkaSettings settings, TbKafkaDecoder<T> decoder,
TbKafkaRequestIdExtractor<T> requestIdExtractor,
String clientId, String groupId, String topic,
boolean autoCommit, int autoCommitIntervalMs) {
Properties props = settings.toProps();
props.put(ConsumerConfig.CLIENT_ID_CONFIG, clientId);
props.put(ConsumerConfig.GROUP_ID_CONFIG, groupId);
if (groupId != null) {
props.put(ConsumerConfig.GROUP_ID_CONFIG, groupId);
}
props.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, autoCommit);
props.put(ConsumerConfig.AUTO_COMMIT_INTERVAL_MS_CONFIG, autoCommitIntervalMs);
props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringDeserializer");

47
common/queue/src/main/java/org/thingsboard/server/kafka/TBKafkaProducerTemplate.java

@ -20,6 +20,7 @@ import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.apache.kafka.clients.admin.CreateTopicsResult;
import org.apache.kafka.clients.admin.NewTopic;
import org.apache.kafka.clients.producer.Callback;
import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.ProducerConfig;
import org.apache.kafka.clients.producer.ProducerRecord;
@ -27,13 +28,12 @@ import org.apache.kafka.clients.producer.RecordMetadata;
import org.apache.kafka.common.PartitionInfo;
import org.apache.kafka.common.header.Header;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.List;
import java.util.Properties;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.Future;
import java.util.function.BiConsumer;
/**
* Created by ashvayka on 24.09.18.
@ -48,7 +48,7 @@ public class TBKafkaProducerTemplate<T> {
private TbKafkaEnricher<T> enricher = ((value, responseTopic, requestId) -> value);
private final TbKafkaPartitioner<T> partitioner;
private List<PartitionInfo> partitionInfoList;
private ConcurrentMap<String, List<PartitionInfo>> partitionInfoMap;
@Getter
private final String defaultTopic;
@ -78,38 +78,51 @@ public class TBKafkaProducerTemplate<T> {
log.trace("Failed to create topic: {}", e.getMessage(), e);
}
//Maybe this should not be cached, but we don't plan to change size of partitions
this.partitionInfoList = producer.partitionsFor(defaultTopic);
this.partitionInfoMap = new ConcurrentHashMap<>();
this.partitionInfoMap.putIfAbsent(defaultTopic, producer.partitionsFor(defaultTopic));
}
public T enrich(T value, String responseTopic, UUID requestId) {
return enricher.enrich(value, responseTopic, requestId);
T enrich(T value, String responseTopic, UUID requestId) {
if (enricher != null) {
return enricher.enrich(value, responseTopic, requestId);
} else {
return value;
}
}
public Future<RecordMetadata> send(String key, T value, Callback callback) {
return send(key, value, null, callback);
}
public Future<RecordMetadata> send(String key, T value, Iterable<Header> headers, Callback callback) {
return send(key, value, null, headers, callback);
}
public Future<RecordMetadata> send(String key, T value) {
return send(key, value, null, null);
public Future<RecordMetadata> send(String key, T value, Long timestamp, Iterable<Header> headers, Callback callback) {
return send(this.defaultTopic, key, value, timestamp, headers, callback);
}
public Future<RecordMetadata> send(String key, T value, Iterable<Header> headers) {
return send(key, value, null, headers);
public Future<RecordMetadata> send(String topic, String key, T value, Iterable<Header> headers, Callback callback) {
return send(topic, key, value, null, headers, callback);
}
public Future<RecordMetadata> send(String key, T value, Long timestamp, Iterable<Header> headers) {
return send(this.defaultTopic, key, value, timestamp, headers);
public Future<RecordMetadata> send(String topic, String key, T value, Callback callback) {
return send(topic, key, value, null, null, callback);
}
public Future<RecordMetadata> send(String topic, String key, T value, Long timestamp, Iterable<Header> headers) {
public Future<RecordMetadata> send(String topic, String key, T value, Long timestamp, Iterable<Header> headers, Callback callback) {
byte[] data = encoder.encode(value);
ProducerRecord<String, byte[]> record;
Integer partition = getPartition(topic, key, value, data);
record = new ProducerRecord<>(this.defaultTopic, partition, timestamp, key, data, headers);
return producer.send(record);
record = new ProducerRecord<>(topic, partition, timestamp, key, data, headers);
return producer.send(record, callback);
}
private Integer getPartition(String topic, String key, T value, byte[] data) {
if (partitioner == null) {
return null;
} else {
return partitioner.partition(this.defaultTopic, key, value, data, partitionInfoList);
return partitioner.partition(topic, key, value, data, partitionInfoMap.computeIfAbsent(topic, producer::partitionsFor));
}
}
}

16
dao/src/main/java/org/thingsboard/server/dao/queue/memory/InMemoryMsgKey.java → common/queue/src/main/java/org/thingsboard/server/kafka/TbKafkaHandler.java

@ -13,17 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.dao.queue.memory;
package org.thingsboard.server.kafka;
import lombok.Data;
import java.util.UUID;
import com.google.common.util.concurrent.ListenableFuture;
/**
* Created by ashvayka on 30.04.18.
* Created by ashvayka on 05.10.18.
*/
@Data
public final class InMemoryMsgKey {
final UUID nodeId;
final long clusterPartition;
public interface TbKafkaHandler<Request, Response> {
ListenableFuture<Response> handle(Request request);
}

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

Loading…
Cancel
Save