Browse Source

Merge pull request #1166 from thingsboard/develop/2.2-transports

Develop/2.2 transports
pull/1168/head
Igor Kulikov 8 years ago
committed by GitHub
parent
commit
87a40a6ef2
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  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. 95
      application/src/main/java/org/thingsboard/server/actors/session/SyncMsgProcessor.java
  16. 16
      application/src/main/java/org/thingsboard/server/actors/shared/ComponentMsgProcessor.java
  17. 36
      application/src/main/java/org/thingsboard/server/actors/shared/SessionTimeoutMsg.java
  18. 2
      application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java
  19. 7
      application/src/main/java/org/thingsboard/server/controller/TelemetryController.java
  20. 2
      application/src/main/java/org/thingsboard/server/service/cluster/discovery/DiscoveryService.java
  21. 8
      application/src/main/java/org/thingsboard/server/service/cluster/discovery/DummyDiscoveryService.java
  22. 8
      application/src/main/java/org/thingsboard/server/service/cluster/discovery/ZkDiscoveryService.java
  23. 4
      application/src/main/java/org/thingsboard/server/service/queue/DefaultMsgQueueService.java
  24. 3
      application/src/main/java/org/thingsboard/server/service/rpc/ToServerRpcResponseActorMsg.java
  25. 11
      application/src/main/java/org/thingsboard/server/service/script/RemoteJsInvokeService.java
  26. 173
      application/src/main/java/org/thingsboard/server/service/transport/LocalTransportApiService.java
  27. 209
      application/src/main/java/org/thingsboard/server/service/transport/LocalTransportService.java
  28. 200
      application/src/main/java/org/thingsboard/server/service/transport/RemoteRuleEngineTransportService.java
  29. 109
      application/src/main/java/org/thingsboard/server/service/transport/RemoteTransportApiService.java
  30. 21
      application/src/main/java/org/thingsboard/server/service/transport/RuleEngineTransportService.java
  31. 17
      application/src/main/java/org/thingsboard/server/service/transport/ToRuleEngineMsgDecoder.java
  32. 14
      application/src/main/java/org/thingsboard/server/service/transport/ToTransportMsgEncoder.java
  33. 31
      application/src/main/java/org/thingsboard/server/service/transport/TransportApiRequestDecoder.java
  34. 16
      application/src/main/java/org/thingsboard/server/service/transport/TransportApiResponseEncoder.java
  35. 15
      application/src/main/java/org/thingsboard/server/service/transport/TransportApiService.java
  36. 50
      application/src/main/java/org/thingsboard/server/service/transport/msg/TransportToDeviceActorMsgWrapper.java
  37. 110
      application/src/main/resources/thingsboard.yml
  38. 4
      common/message/pom.xml
  39. 2
      common/message/src/main/java/org/thingsboard/server/common/msg/EncryptionUtil.java
  40. 16
      common/message/src/main/java/org/thingsboard/server/common/msg/MsgType.java
  41. 24
      common/message/src/main/java/org/thingsboard/server/common/msg/aware/SessionAwareMsg.java
  42. 30
      common/message/src/main/java/org/thingsboard/server/common/msg/core/ActorSystemToDeviceSessionActorMsg.java
  43. 47
      common/message/src/main/java/org/thingsboard/server/common/msg/core/AttributesUpdateNotification.java
  44. 28
      common/message/src/main/java/org/thingsboard/server/common/msg/core/AttributesUpdateRequest.java
  45. 52
      common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicActorSystemToDeviceSessionActorMsg.java
  46. 63
      common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicAttributesUpdateRequest.java
  47. 45
      common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicCommandAckResponse.java
  48. 59
      common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicGetAttributesRequest.java
  49. 40
      common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicGetAttributesResponse.java
  50. 79
      common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicResponseMsg.java
  51. 42
      common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicStatusCodeResponse.java
  52. 66
      common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicTelemetryUploadRequest.java
  53. 29
      common/message/src/main/java/org/thingsboard/server/common/msg/core/GetAttributesRequest.java
  54. 22
      common/message/src/main/java/org/thingsboard/server/common/msg/core/GetAttributesResponse.java
  55. 33
      common/message/src/main/java/org/thingsboard/server/common/msg/core/ResponseMsg.java
  56. 30
      common/message/src/main/java/org/thingsboard/server/common/msg/core/RpcUnsubscribeMsg.java
  57. 43
      common/message/src/main/java/org/thingsboard/server/common/msg/core/RuleEngineError.java
  58. 53
      common/message/src/main/java/org/thingsboard/server/common/msg/core/RuleEngineErrorMsg.java
  59. 38
      common/message/src/main/java/org/thingsboard/server/common/msg/core/SessionCloseNotification.java
  60. 35
      common/message/src/main/java/org/thingsboard/server/common/msg/core/SessionOpenMsg.java
  61. 20
      common/message/src/main/java/org/thingsboard/server/common/msg/core/StatusCodeResponse.java
  62. 29
      common/message/src/main/java/org/thingsboard/server/common/msg/core/TelemetryUploadRequest.java
  63. 41
      common/message/src/main/java/org/thingsboard/server/common/msg/core/ToDeviceRpcRequestMsg.java
  64. 36
      common/message/src/main/java/org/thingsboard/server/common/msg/core/ToDeviceRpcResponseMsg.java
  65. 36
      common/message/src/main/java/org/thingsboard/server/common/msg/core/ToServerRpcRequestMsg.java
  66. 11
      common/message/src/main/java/org/thingsboard/server/common/msg/core/ToServerRpcResponseMsg.java
  67. 107
      common/message/src/main/java/org/thingsboard/server/common/msg/device/BasicDeviceToDeviceActorMsg.java
  68. 41
      common/message/src/main/java/org/thingsboard/server/common/msg/device/DeviceToDeviceActorMsg.java
  69. 22
      common/message/src/main/java/org/thingsboard/server/common/msg/session/AdaptorToSessionActorMsg.java
  70. 32
      common/message/src/main/java/org/thingsboard/server/common/msg/session/BasicAdaptorToSessionActorMsg.java
  71. 32
      common/message/src/main/java/org/thingsboard/server/common/msg/session/BasicSessionActorToAdaptorMsg.java
  72. 44
      common/message/src/main/java/org/thingsboard/server/common/msg/session/BasicSessionMsg.java
  73. 74
      common/message/src/main/java/org/thingsboard/server/common/msg/session/BasicTransportToDeviceSessionActorMsg.java
  74. 22
      common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionActorToAdaptorMsg.java
  75. 16
      common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionContext.java
  76. 23
      common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionCtrlMsg.java
  77. 24
      common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionMsg.java
  78. 22
      common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionType.java
  79. 26
      common/message/src/main/java/org/thingsboard/server/common/msg/session/ToDeviceMsg.java
  80. 28
      common/message/src/main/java/org/thingsboard/server/common/msg/session/TransportToDeviceSessionActorMsg.java
  81. 68
      common/message/src/main/java/org/thingsboard/server/common/msg/session/ctrl/SessionCloseMsg.java
  82. 36
      common/message/src/main/java/org/thingsboard/server/common/msg/timeout/DeviceActorQueueTimeoutMsg.java
  83. 3
      common/pom.xml
  84. 6
      common/queue/pom.xml
  85. 50
      common/queue/src/main/java/org/thingsboard/server/kafka/AbstractTbKafkaTemplate.java
  86. 66
      common/queue/src/main/java/org/thingsboard/server/kafka/AsyncCallbackTemplate.java
  87. 7
      common/queue/src/main/java/org/thingsboard/server/kafka/TBKafkaConsumerTemplate.java
  88. 47
      common/queue/src/main/java/org/thingsboard/server/kafka/TBKafkaProducerTemplate.java
  89. 11
      common/queue/src/main/java/org/thingsboard/server/kafka/TbKafkaHandler.java
  90. 46
      common/queue/src/main/java/org/thingsboard/server/kafka/TbKafkaRequestTemplate.java
  91. 156
      common/queue/src/main/java/org/thingsboard/server/kafka/TbKafkaResponseTemplate.java
  92. 51
      common/queue/src/main/java/org/thingsboard/server/kafka/TbNodeIdProvider.java
  93. 88
      common/transport/coap/pom.xml
  94. 51
      common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportContext.java
  95. 445
      common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java
  96. 38
      common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportService.java
  97. 47
      common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/CoapTransportAdaptor.java
  98. 155
      common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/JsonCoapAdaptor.java
  99. 0
      common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/client/DeviceEmulator.java
  100. 81
      common/transport/http/pom.xml

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();
}
});
}

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();
}
}

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

@ -35,14 +35,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;
@ -87,18 +85,4 @@ public abstract class ComponentMsgProcessor<T extends EntityId> extends Abstract
}
}
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:

7
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;
@ -346,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);
@ -354,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();

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

@ -49,7 +49,7 @@ public class DefaultMsgQueueService implements MsgQueueService {
@Autowired
private MsgQueue msgQueue;
@Autowired
@Autowired(required = false)
private TenantQuotaService quotaService;
private ScheduledExecutorService cleanupExecutor;
@ -74,7 +74,7 @@ public class DefaultMsgQueueService implements MsgQueueService {
@Override
public ListenableFuture<Void> put(TenantId tenantId, TbMsg msg, UUID nodeId, long clusterPartition) {
if(quotaService.isQuotaExceeded(tenantId.getId().toString())) {
if(quotaService != null && quotaService.isQuotaExceeded(tenantId.getId().toString())) {
log.warn("Tenant TbMsg Quota exceeded for [{}:{}] . Reject", tenantId.getId());
return Futures.immediateFailedFuture(new RuntimeException("Tenant TbMsg Quota exceeded"));
}

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();

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();
}
}

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

@ -0,0 +1,209 @@
/**
* 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) {
forwardToDeviceActor(TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setSessionEvent(msg).build(), callback);
}
@Override
public void process(SessionInfoProto sessionInfo, PostTelemetryMsg msg, TransportServiceCallback<Void> callback) {
forwardToDeviceActor(TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setPostTelemetry(msg).build(), callback);
}
@Override
public void process(SessionInfoProto sessionInfo, PostAttributeMsg msg, TransportServiceCallback<Void> callback) {
forwardToDeviceActor(TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setPostAttributes(msg).build(), callback);
}
@Override
public void process(SessionInfoProto sessionInfo, GetAttributeRequestMsg msg, TransportServiceCallback<Void> callback) {
forwardToDeviceActor(TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setGetAttributes(msg).build(), callback);
}
@Override
public void process(SessionInfoProto sessionInfo, SubscribeToAttributeUpdatesMsg msg, TransportServiceCallback<Void> callback) {
forwardToDeviceActor(TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setSubscribeToAttributes(msg).build(), callback);
}
@Override
public void process(SessionInfoProto sessionInfo, SubscribeToRPCMsg msg, TransportServiceCallback<Void> callback) {
forwardToDeviceActor(TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setSubscribeToRPC(msg).build(), callback);
}
@Override
public void process(SessionInfoProto sessionInfo, ToDeviceRpcResponseMsg msg, TransportServiceCallback<Void> callback) {
forwardToDeviceActor(TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setToDeviceRPCCallResponse(msg).build(), callback);
}
@Override
public void process(SessionInfoProto sessionInfo, ToServerRpcRequestMsg msg, TransportServiceCallback<Void> 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();
}
}

15
application/src/main/java/org/thingsboard/server/actors/session/SessionTerminationMsg.java → application/src/main/java/org/thingsboard/server/service/transport/TransportApiService.java

@ -13,14 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.actors.session;
package org.thingsboard.server.service.transport;
import org.thingsboard.server.actors.shared.ActorTerminationMsg;
import org.thingsboard.server.common.data.id.SessionId;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.kafka.TbKafkaHandler;
public class SessionTerminationMsg extends ActorTerminationMsg<SessionId> {
public SessionTerminationMsg(SessionId id) {
super(id);
}
/**
* 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;
}
}

110
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
@ -427,6 +386,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 +428,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_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}"
# 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}"

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;
}

24
common/message/src/main/java/org/thingsboard/server/common/msg/aware/SessionAwareMsg.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.aware;
import org.thingsboard.server.common.data.id.SessionId;
public interface SessionAwareMsg {
SessionId getSessionId();
}

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;
}
}

43
common/message/src/main/java/org/thingsboard/server/common/msg/core/RuleEngineError.java

@ -1,43 +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;
/**
* @author Andrew Shvayka
*/
public enum RuleEngineError {
QUEUE_PUT_TIMEOUT(true), SERVER_ERROR(true), TIMEOUT;
private final boolean critical;
RuleEngineError() {
this(false);
}
RuleEngineError(boolean critical) {
this.critical = critical;
}
public boolean isCritical() {
return critical;
}
public int getPriority() {
return ordinal();
}
}

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;
}
}

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));
}
}
}

11
common/message/src/main/java/org/thingsboard/server/common/msg/session/FromDeviceMsg.java → common/queue/src/main/java/org/thingsboard/server/kafka/TbKafkaHandler.java

@ -13,12 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.msg.session;
package org.thingsboard.server.kafka;
import java.io.Serializable;
import com.google.common.util.concurrent.ListenableFuture;
public interface FromDeviceMsg extends Serializable {
/**
* Created by ashvayka on 05.10.18.
*/
public interface TbKafkaHandler<Request, Response> {
SessionMsgType getMsgType();
ListenableFuture<Response> handle(Request request);
}

46
common/queue/src/main/java/org/thingsboard/server/kafka/TbKafkaRequestTemplate.java

@ -23,24 +23,25 @@ import lombok.extern.slf4j.Slf4j;
import org.apache.kafka.clients.admin.CreateTopicsResult;
import org.apache.kafka.clients.admin.NewTopic;
import org.apache.kafka.clients.consumer.ConsumerRecords;
import org.apache.kafka.clients.producer.RecordMetadata;
import org.apache.kafka.common.header.Header;
import org.apache.kafka.common.header.internals.RecordHeader;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeoutException;
/**
* Created by ashvayka on 25.09.18.
*/
@Slf4j
public class TbKafkaRequestTemplate<Request, Response> {
public class TbKafkaRequestTemplate<Request, Response> extends AbstractTbKafkaTemplate {
private final TBKafkaProducerTemplate<Request> requestTemplate;
private final TBKafkaConsumerTemplate<Response> responseTemplate;
@ -93,13 +94,12 @@ public class TbKafkaRequestTemplate<Request, Response> {
ConsumerRecords<String, byte[]> responses = responseTemplate.poll(Duration.ofMillis(pollInterval));
responses.forEach(response -> {
Header requestIdHeader = response.headers().lastHeader(TbKafkaSettings.REQUEST_ID_HEADER);
Response decocedResponse = null;
Response decodedResponse = null;
UUID requestId = null;
if (requestIdHeader == null) {
try {
decocedResponse = responseTemplate.decode(response);
requestId = responseTemplate.extractRequestId(decocedResponse);
decodedResponse = responseTemplate.decode(response);
requestId = responseTemplate.extractRequestId(decodedResponse);
} catch (IOException e) {
log.error("Failed to decode response", e);
}
@ -107,17 +107,17 @@ public class TbKafkaRequestTemplate<Request, Response> {
requestId = bytesToUuid(requestIdHeader.value());
}
if (requestId == null) {
log.error("[{}] Missing requestId in header and response", response);
log.error("[{}] Missing requestId in header and body", response);
} else {
ResponseMetaData<Response> expectedResponse = pendingRequests.remove(requestId);
if (expectedResponse == null) {
log.trace("[{}] Invalid or stale request", requestId);
} else {
try {
if (decocedResponse == null) {
decocedResponse = responseTemplate.decode(response);
if (decodedResponse == null) {
decodedResponse = responseTemplate.decode(response);
}
expectedResponse.future.set(decocedResponse);
expectedResponse.future.set(decodedResponse);
} catch (IOException e) {
expectedResponse.future.setException(e);
}
@ -160,28 +160,10 @@ public class TbKafkaRequestTemplate<Request, Response> {
SettableFuture<Response> future = SettableFuture.create();
pendingRequests.putIfAbsent(requestId, new ResponseMetaData<>(tickTs + maxRequestTimeout, future));
request = requestTemplate.enrich(request, responseTemplate.getTopic(), requestId);
requestTemplate.send(key, request, headers);
requestTemplate.send(key, request, headers, null);
return future;
}
private byte[] uuidToBytes(UUID uuid) {
ByteBuffer buf = ByteBuffer.allocate(16);
buf.putLong(uuid.getMostSignificantBits());
buf.putLong(uuid.getLeastSignificantBits());
return buf.array();
}
private static UUID bytesToUuid(byte[] bytes) {
ByteBuffer bb = ByteBuffer.wrap(bytes);
long firstLong = bb.getLong();
long secondLong = bb.getLong();
return new UUID(firstLong, secondLong);
}
private byte[] stringToBytes(String string) {
return string.getBytes(StandardCharsets.UTF_8);
}
private static class ResponseMetaData<T> {
private final long expTime;
private final SettableFuture<T> future;

156
common/queue/src/main/java/org/thingsboard/server/kafka/TbKafkaResponseTemplate.java

@ -0,0 +1,156 @@
/**
* 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.Builder;
import lombok.extern.slf4j.Slf4j;
import org.apache.kafka.clients.consumer.ConsumerRecords;
import org.apache.kafka.common.header.Header;
import org.apache.kafka.common.header.internals.RecordHeader;
import java.time.Duration;
import java.util.Collections;
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.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Created by ashvayka on 25.09.18.
*/
@Slf4j
public class TbKafkaResponseTemplate<Request, Response> extends AbstractTbKafkaTemplate {
private final TBKafkaConsumerTemplate<Request> requestTemplate;
private final TBKafkaProducerTemplate<Response> responseTemplate;
private final TbKafkaHandler<Request, Response> handler;
private final ConcurrentMap<UUID, String> pendingRequests;
private final ExecutorService loopExecutor;
private final ScheduledExecutorService timeoutExecutor;
private final ExecutorService callbackExecutor;
private final int maxPendingRequests;
private final long requestTimeout;
private final long pollInterval;
private volatile boolean stopped = false;
private final AtomicInteger pendingRequestCount = new AtomicInteger();
@Builder
public TbKafkaResponseTemplate(TBKafkaConsumerTemplate<Request> requestTemplate,
TBKafkaProducerTemplate<Response> responseTemplate,
TbKafkaHandler<Request, Response> handler,
long pollInterval,
long requestTimeout,
int maxPendingRequests,
ExecutorService executor) {
this.requestTemplate = requestTemplate;
this.responseTemplate = responseTemplate;
this.handler = handler;
this.pendingRequests = new ConcurrentHashMap<>();
this.maxPendingRequests = maxPendingRequests;
this.pollInterval = pollInterval;
this.requestTimeout = requestTimeout;
this.callbackExecutor = executor;
this.timeoutExecutor = Executors.newSingleThreadScheduledExecutor();
this.loopExecutor = Executors.newSingleThreadExecutor();
}
public void init() {
this.responseTemplate.init();
requestTemplate.subscribe();
loopExecutor.submit(() -> {
while (!stopped) {
try {
while (pendingRequestCount.get() >= maxPendingRequests) {
try {
Thread.sleep(pollInterval);
} catch (InterruptedException e) {
log.trace("Failed to wait until the server has capacity to handle new requests", e);
}
}
ConsumerRecords<String, byte[]> requests = requestTemplate.poll(Duration.ofMillis(pollInterval));
requests.forEach(request -> {
Header requestIdHeader = request.headers().lastHeader(TbKafkaSettings.REQUEST_ID_HEADER);
if (requestIdHeader == null) {
log.error("[{}] Missing requestId in header", request);
return;
}
UUID requestId = bytesToUuid(requestIdHeader.value());
if (requestId == null) {
log.error("[{}] Missing requestId in header and body", request);
return;
}
Header responseTopicHeader = request.headers().lastHeader(TbKafkaSettings.RESPONSE_TOPIC_HEADER);
if (responseTopicHeader == null) {
log.error("[{}] Missing response topic in header", request);
return;
}
String responseTopic = bytesToString(responseTopicHeader.value());
try {
pendingRequestCount.getAndIncrement();
Request decodedRequest = requestTemplate.decode(request);
AsyncCallbackTemplate.withCallbackAndTimeout(handler.handle(decodedRequest),
response -> {
pendingRequestCount.decrementAndGet();
reply(requestId, responseTopic, response);
},
e -> {
pendingRequestCount.decrementAndGet();
if (e.getCause() != null && e.getCause() instanceof TimeoutException) {
log.warn("[{}] Timedout to process the request: {}", requestId, request, e);
} else {
log.trace("[{}] Failed to process the request: {}", requestId, request, e);
}
},
requestTimeout,
timeoutExecutor,
callbackExecutor);
} catch (Throwable e) {
pendingRequestCount.decrementAndGet();
log.warn("[{}] Failed to process the request: {}", requestId, request, e);
}
});
} catch (Throwable e) {
log.warn("Failed to obtain messages from queue.", e);
try {
Thread.sleep(pollInterval);
} catch (InterruptedException e2) {
log.trace("Failed to wait until the server has capacity to handle new requests", e2);
}
}
}
});
}
public void stop() {
stopped = true;
if (timeoutExecutor != null) {
timeoutExecutor.shutdownNow();
}
if (loopExecutor != null) {
loopExecutor.shutdownNow();
}
}
private void reply(UUID requestId, String topic, Response response) {
responseTemplate.send(topic, requestId.toString(), response, Collections.singletonList(new RecordHeader(TbKafkaSettings.REQUEST_ID_HEADER, uuidToBytes(requestId))), null);
}
}

51
common/queue/src/main/java/org/thingsboard/server/kafka/TbNodeIdProvider.java

@ -0,0 +1,51 @@
/**
* 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.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import javax.annotation.PostConstruct;
import java.net.InetAddress;
import java.net.UnknownHostException;
/**
* Created by ashvayka on 12.10.18.
*/
@Slf4j
@Component
public class TbNodeIdProvider {
@Getter
@Value("${cluster.node_id:#{null}}")
private String nodeId;
@PostConstruct
public void init() {
if (StringUtils.isEmpty(nodeId)) {
try {
nodeId = InetAddress.getLocalHost().getHostName();
} catch (UnknownHostException e) {
nodeId = org.apache.commons.lang3.RandomStringUtils.randomAlphabetic(10);
}
}
log.info("Current NodeId: {}", nodeId);
}
}

88
common/transport/coap/pom.xml

@ -0,0 +1,88 @@
<!--
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.
-->
<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">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard.common</groupId>
<version>2.2.0-SNAPSHOT</version>
<artifactId>transport</artifactId>
</parent>
<groupId>org.thingsboard.common.transport</groupId>
<artifactId>coap</artifactId>
<packaging>jar</packaging>
<name>Thingsboard CoAP Transport Common</name>
<url>https://thingsboard.io</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<main.dir>${basedir}/../../..</main.dir>
</properties>
<dependencies>
<dependency>
<groupId>org.thingsboard.common.transport</groupId>
<artifactId>transport-api</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.californium</groupId>
<artifactId>californium-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

51
common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportContext.java

@ -0,0 +1,51 @@
/**
* 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.transport.coap;
import lombok.Getter;
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.ConditionalOnExpression;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.transport.TransportContext;
import org.thingsboard.server.transport.coap.adaptors.CoapTransportAdaptor;
/**
* Created by ashvayka on 18.10.18.
*/
@Slf4j
@ConditionalOnExpression("'${transport.type:null}'=='null' || ('${transport.type}'=='local' && '${transport.coap.enabled}'=='true')")
@Component
public class CoapTransportContext extends TransportContext {
@Getter
@Value("${transport.coap.bind_address}")
private String host;
@Getter
@Value("${transport.coap.bind_port}")
private Integer port;
@Getter
@Value("${transport.coap.timeout}")
private Long timeout;
@Getter
@Autowired
private CoapTransportAdaptor adaptor;
}

445
common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java

@ -0,0 +1,445 @@
/**
* 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.transport.coap;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.californium.core.CoapResource;
import org.eclipse.californium.core.coap.CoAP.ResponseCode;
import org.eclipse.californium.core.coap.Request;
import org.eclipse.californium.core.coap.Response;
import org.eclipse.californium.core.network.Exchange;
import org.eclipse.californium.core.network.ExchangeObserver;
import org.eclipse.californium.core.server.resources.CoapExchange;
import org.eclipse.californium.core.server.resources.Resource;
import org.springframework.util.ReflectionUtils;
import org.thingsboard.server.common.data.security.DeviceTokenCredentials;
import org.thingsboard.server.common.msg.session.FeatureType;
import org.thingsboard.server.common.msg.session.SessionMsgType;
import org.thingsboard.server.common.transport.SessionMsgListener;
import org.thingsboard.server.common.transport.TransportContext;
import org.thingsboard.server.common.transport.TransportService;
import org.thingsboard.server.common.transport.TransportServiceCallback;
import org.thingsboard.server.common.transport.adaptor.AdaptorException;
import org.thingsboard.server.gen.transport.TransportProtos;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
@Slf4j
public class CoapTransportResource extends CoapResource {
// coap://localhost:port/api/v1/DEVICE_TOKEN/[attributes|telemetry|rpc[/requestId]]
private static final int ACCESS_TOKEN_POSITION = 3;
private static final int FEATURE_TYPE_POSITION = 4;
private static final int REQUEST_ID_POSITION = 5;
private final CoapTransportContext transportContext;
private final TransportService transportService;
private final Field observerField;
private final long timeout;
private final ConcurrentMap<String, TransportProtos.SessionInfoProto> tokenToSessionIdMap = new ConcurrentHashMap<>();
public CoapTransportResource(CoapTransportContext context, String name) {
super(name);
this.transportContext = context;
this.transportService = context.getTransportService();
this.timeout = context.getTimeout();
// This is important to turn off existing observable logic in
// CoapResource. We will have our own observe monitoring due to 1:1
// observe relationship.
this.setObservable(false);
observerField = ReflectionUtils.findField(Exchange.class, "observer");
observerField.setAccessible(true);
}
@Override
public void handleGET(CoapExchange exchange) {
if (transportContext.getQuotaService().isQuotaExceeded(exchange.getSourceAddress().getHostAddress())) {
log.warn("CoAP Quota exceeded for [{}:{}] . Disconnect", exchange.getSourceAddress().getHostAddress(), exchange.getSourcePort());
exchange.respond(ResponseCode.BAD_REQUEST);
return;
}
Optional<FeatureType> featureType = getFeatureType(exchange.advanced().getRequest());
if (!featureType.isPresent()) {
log.trace("Missing feature type parameter");
exchange.respond(ResponseCode.BAD_REQUEST);
} else if (featureType.get() == FeatureType.TELEMETRY) {
log.trace("Can't fetch/subscribe to timeseries updates");
exchange.respond(ResponseCode.BAD_REQUEST);
} else if (exchange.getRequestOptions().hasObserve()) {
processExchangeGetRequest(exchange, featureType.get());
} else if (featureType.get() == FeatureType.ATTRIBUTES) {
processRequest(exchange, SessionMsgType.GET_ATTRIBUTES_REQUEST);
} else {
log.trace("Invalid feature type parameter");
exchange.respond(ResponseCode.BAD_REQUEST);
}
}
private void processExchangeGetRequest(CoapExchange exchange, FeatureType featureType) {
boolean unsubscribe = exchange.getRequestOptions().getObserve() == 1;
SessionMsgType sessionMsgType;
if (featureType == FeatureType.RPC) {
sessionMsgType = unsubscribe ? SessionMsgType.UNSUBSCRIBE_RPC_COMMANDS_REQUEST : SessionMsgType.SUBSCRIBE_RPC_COMMANDS_REQUEST;
} else {
sessionMsgType = unsubscribe ? SessionMsgType.UNSUBSCRIBE_ATTRIBUTES_REQUEST : SessionMsgType.SUBSCRIBE_ATTRIBUTES_REQUEST;
}
processRequest(exchange, sessionMsgType);
}
@Override
public void handlePOST(CoapExchange exchange) {
if (transportContext.getQuotaService().isQuotaExceeded(exchange.getSourceAddress().getHostAddress())) {
log.warn("CoAP Quota exceeded for [{}:{}] . Disconnect", exchange.getSourceAddress().getHostAddress(), exchange.getSourcePort());
exchange.respond(ResponseCode.BAD_REQUEST);
return;
}
Optional<FeatureType> featureType = getFeatureType(exchange.advanced().getRequest());
if (!featureType.isPresent()) {
log.trace("Missing feature type parameter");
exchange.respond(ResponseCode.BAD_REQUEST);
} else {
switch (featureType.get()) {
case ATTRIBUTES:
processRequest(exchange, SessionMsgType.POST_ATTRIBUTES_REQUEST);
break;
case TELEMETRY:
processRequest(exchange, SessionMsgType.POST_TELEMETRY_REQUEST);
break;
case RPC:
Optional<Integer> requestId = getRequestId(exchange.advanced().getRequest());
if (requestId.isPresent()) {
processRequest(exchange, SessionMsgType.TO_DEVICE_RPC_RESPONSE);
} else {
processRequest(exchange, SessionMsgType.TO_SERVER_RPC_REQUEST);
}
break;
}
}
}
private void processRequest(CoapExchange exchange, SessionMsgType type) {
log.trace("Processing {}", exchange.advanced().getRequest());
exchange.accept();
Exchange advanced = exchange.advanced();
Request request = advanced.getRequest();
Optional<DeviceTokenCredentials> credentials = decodeCredentials(request);
if (!credentials.isPresent()) {
exchange.respond(ResponseCode.BAD_REQUEST);
return;
}
transportService.process(TransportProtos.ValidateDeviceTokenRequestMsg.newBuilder().setToken(credentials.get().getCredentialsId()).build(),
new DeviceAuthCallback(transportContext, exchange, sessionInfo -> {
UUID sessionId = new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB());
try {
switch (type) {
case POST_ATTRIBUTES_REQUEST:
transportService.process(sessionInfo,
transportContext.getAdaptor().convertToPostAttributes(sessionId, request),
new CoapOkCallback(exchange));
break;
case POST_TELEMETRY_REQUEST:
transportService.process(sessionInfo,
transportContext.getAdaptor().convertToPostTelemetry(sessionId, request),
new CoapOkCallback(exchange));
break;
case SUBSCRIBE_ATTRIBUTES_REQUEST:
advanced.setObserver(new CoapExchangeObserverProxy((ExchangeObserver) observerField.get(advanced),
registerAsyncCoapSession(exchange, request, sessionInfo, sessionId)));
transportService.process(sessionInfo,
TransportProtos.SubscribeToAttributeUpdatesMsg.getDefaultInstance(),
new CoapNoOpCallback(exchange));
break;
case UNSUBSCRIBE_ATTRIBUTES_REQUEST:
TransportProtos.SessionInfoProto attrSession = lookupAsyncSessionInfo(request);
if (attrSession != null) {
transportService.process(attrSession,
TransportProtos.SubscribeToAttributeUpdatesMsg.newBuilder().setUnsubscribe(true).build(),
new CoapOkCallback(exchange));
closeAndDeregister(sessionInfo);
}
break;
case SUBSCRIBE_RPC_COMMANDS_REQUEST:
advanced.setObserver(new CoapExchangeObserverProxy((ExchangeObserver) observerField.get(advanced),
registerAsyncCoapSession(exchange, request, sessionInfo, sessionId)));
transportService.process(sessionInfo,
TransportProtos.SubscribeToRPCMsg.getDefaultInstance(),
new CoapNoOpCallback(exchange));
break;
case UNSUBSCRIBE_RPC_COMMANDS_REQUEST:
TransportProtos.SessionInfoProto rpcSession = lookupAsyncSessionInfo(request);
if (rpcSession != null) {
transportService.process(rpcSession,
TransportProtos.SubscribeToRPCMsg.newBuilder().setUnsubscribe(true).build(),
new CoapOkCallback(exchange));
transportService.process(sessionInfo, getSessionEventMsg(TransportProtos.SessionEvent.CLOSED), null);
transportService.deregisterSession(rpcSession);
}
break;
case TO_DEVICE_RPC_RESPONSE:
transportService.process(sessionInfo,
transportContext.getAdaptor().convertToDeviceRpcResponse(sessionId, request),
new CoapOkCallback(exchange));
break;
case TO_SERVER_RPC_REQUEST:
transportService.process(sessionInfo,
transportContext.getAdaptor().convertToServerRpcRequest(sessionId, request),
new CoapNoOpCallback(exchange));
break;
case GET_ATTRIBUTES_REQUEST:
transportService.registerSyncSession(sessionInfo, new CoapSessionListener(sessionId, exchange), transportContext.getTimeout());
transportService.process(sessionInfo,
transportContext.getAdaptor().convertToGetAttributes(sessionId, request),
new CoapNoOpCallback(exchange));
break;
}
} catch (AdaptorException e) {
log.trace("[{}] Failed to decode message: ", sessionId, e);
exchange.respond(ResponseCode.BAD_REQUEST);
} catch (IllegalAccessException e) {
log.trace("[{}] Failed to process message: ", sessionId, e);
exchange.respond(ResponseCode.INTERNAL_SERVER_ERROR);
}
}));
}
private TransportProtos.SessionInfoProto lookupAsyncSessionInfo(Request request) {
String token = request.getSource().getHostAddress() + ":" + request.getSourcePort() + ":" + request.getTokenString();
return tokenToSessionIdMap.remove(token);
}
private String registerAsyncCoapSession(CoapExchange exchange, Request request, TransportProtos.SessionInfoProto sessionInfo, UUID sessionId) {
String token = request.getSource().getHostAddress() + ":" + request.getSourcePort() + ":" + request.getTokenString();
tokenToSessionIdMap.putIfAbsent(token, sessionInfo);
CoapSessionListener attrListener = new CoapSessionListener(sessionId, exchange);
transportService.registerAsyncSession(sessionInfo, attrListener);
transportService.process(sessionInfo, getSessionEventMsg(TransportProtos.SessionEvent.OPEN), null);
return token;
}
private static TransportProtos.SessionEventMsg getSessionEventMsg(TransportProtos.SessionEvent event) {
return TransportProtos.SessionEventMsg.newBuilder()
.setSessionType(TransportProtos.SessionType.ASYNC)
.setEvent(event).build();
}
private Optional<DeviceTokenCredentials> decodeCredentials(Request request) {
List<String> uriPath = request.getOptions().getUriPath();
if (uriPath.size() >= ACCESS_TOKEN_POSITION) {
return Optional.of(new DeviceTokenCredentials(uriPath.get(ACCESS_TOKEN_POSITION - 1)));
} else {
return Optional.empty();
}
}
private Optional<FeatureType> getFeatureType(Request request) {
List<String> uriPath = request.getOptions().getUriPath();
try {
if (uriPath.size() >= FEATURE_TYPE_POSITION) {
return Optional.of(FeatureType.valueOf(uriPath.get(FEATURE_TYPE_POSITION - 1).toUpperCase()));
}
} catch (RuntimeException e) {
log.warn("Failed to decode feature type: {}", uriPath);
}
return Optional.empty();
}
public static Optional<Integer> getRequestId(Request request) {
List<String> uriPath = request.getOptions().getUriPath();
try {
if (uriPath.size() >= REQUEST_ID_POSITION) {
return Optional.of(Integer.valueOf(uriPath.get(REQUEST_ID_POSITION - 1)));
}
} catch (RuntimeException e) {
log.warn("Failed to decode feature type: {}", uriPath);
}
return Optional.empty();
}
@Override
public Resource getChild(String name) {
return this;
}
private static class DeviceAuthCallback implements TransportServiceCallback<TransportProtos.ValidateDeviceCredentialsResponseMsg> {
private final TransportContext transportContext;
private final CoapExchange exchange;
private final Consumer<TransportProtos.SessionInfoProto> onSuccess;
DeviceAuthCallback(TransportContext transportContext, CoapExchange exchange, Consumer<TransportProtos.SessionInfoProto> onSuccess) {
this.transportContext = transportContext;
this.exchange = exchange;
this.onSuccess = onSuccess;
}
@Override
public void onSuccess(TransportProtos.ValidateDeviceCredentialsResponseMsg msg) {
if (msg.hasDeviceInfo()) {
UUID sessionId = UUID.randomUUID();
TransportProtos.DeviceInfoProto deviceInfoProto = msg.getDeviceInfo();
TransportProtos.SessionInfoProto sessionInfo = TransportProtos.SessionInfoProto.newBuilder()
.setNodeId(transportContext.getNodeId())
.setTenantIdMSB(deviceInfoProto.getTenantIdMSB())
.setTenantIdLSB(deviceInfoProto.getTenantIdLSB())
.setDeviceIdMSB(deviceInfoProto.getDeviceIdMSB())
.setDeviceIdLSB(deviceInfoProto.getDeviceIdLSB())
.setSessionIdMSB(sessionId.getMostSignificantBits())
.setSessionIdLSB(sessionId.getLeastSignificantBits())
.build();
onSuccess.accept(sessionInfo);
} else {
exchange.respond(ResponseCode.UNAUTHORIZED);
}
}
@Override
public void onError(Throwable e) {
log.warn("Failed to process request", e);
exchange.respond(ResponseCode.INTERNAL_SERVER_ERROR);
}
}
private static class CoapOkCallback implements TransportServiceCallback<Void> {
private final CoapExchange exchange;
CoapOkCallback(CoapExchange exchange) {
this.exchange = exchange;
}
@Override
public void onSuccess(Void msg) {
exchange.respond(ResponseCode.VALID);
}
@Override
public void onError(Throwable e) {
exchange.respond(ResponseCode.INTERNAL_SERVER_ERROR);
}
}
private static class CoapNoOpCallback implements TransportServiceCallback<Void> {
private final CoapExchange exchange;
CoapNoOpCallback(CoapExchange exchange) {
this.exchange = exchange;
}
@Override
public void onSuccess(Void msg) {
}
@Override
public void onError(Throwable e) {
exchange.respond(ResponseCode.INTERNAL_SERVER_ERROR);
}
}
public class CoapSessionListener implements SessionMsgListener {
private final CoapExchange exchange;
private final AtomicInteger seqNumber = new AtomicInteger(2);
CoapSessionListener(UUID sessionId, CoapExchange exchange) {
this.exchange = exchange;
}
@Override
public void onGetAttributesResponse(TransportProtos.GetAttributeResponseMsg msg) {
try {
exchange.respond(transportContext.getAdaptor().convertToPublish(this, msg));
} catch (AdaptorException e) {
log.trace("Failed to reply due to error", e);
exchange.respond(ResponseCode.INTERNAL_SERVER_ERROR);
}
}
@Override
public void onAttributeUpdate(TransportProtos.AttributeUpdateNotificationMsg msg) {
try {
exchange.respond(transportContext.getAdaptor().convertToPublish(this, msg));
} catch (AdaptorException e) {
log.trace("Failed to reply due to error", e);
exchange.respond(ResponseCode.INTERNAL_SERVER_ERROR);
}
}
@Override
public void onRemoteSessionCloseCommand(TransportProtos.SessionCloseNotificationProto sessionCloseNotification) {
exchange.respond(ResponseCode.SERVICE_UNAVAILABLE);
}
@Override
public void onToDeviceRpcRequest(TransportProtos.ToDeviceRpcRequestMsg msg) {
try {
exchange.respond(transportContext.getAdaptor().convertToPublish(this, msg));
} catch (AdaptorException e) {
log.trace("Failed to reply due to error", e);
exchange.respond(ResponseCode.INTERNAL_SERVER_ERROR);
}
}
@Override
public void onToServerRpcResponse(TransportProtos.ToServerRpcResponseMsg msg) {
try {
exchange.respond(transportContext.getAdaptor().convertToPublish(this, msg));
} catch (AdaptorException e) {
log.trace("Failed to reply due to error", e);
exchange.respond(ResponseCode.INTERNAL_SERVER_ERROR);
}
}
public int getNextSeqNumber() {
return seqNumber.getAndIncrement();
}
}
public class CoapExchangeObserverProxy implements ExchangeObserver {
private final ExchangeObserver proxy;
private final String token;
CoapExchangeObserverProxy(ExchangeObserver proxy, String token) {
super();
this.proxy = proxy;
this.token = token;
}
@Override
public void completed(Exchange exchange) {
proxy.completed(exchange);
TransportProtos.SessionInfoProto session = tokenToSessionIdMap.remove(token);
if (session != null) {
closeAndDeregister(session);
}
}
}
private void closeAndDeregister(TransportProtos.SessionInfoProto session) {
transportService.process(session, getSessionEventMsg(TransportProtos.SessionEvent.CLOSED), null);
transportService.deregisterSession(session);
}
}

38
transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportService.java → common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportService.java

@ -21,6 +21,7 @@ import org.eclipse.californium.core.CoapServer;
import org.eclipse.californium.core.network.CoapEndpoint;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
@ -37,49 +38,26 @@ import java.net.InetSocketAddress;
import java.net.UnknownHostException;
@Service("CoapTransportService")
@ConditionalOnProperty(prefix = "coap", value = "enabled", havingValue = "true", matchIfMissing = true)
@ConditionalOnExpression("'${transport.type:null}'=='null' || ('${transport.type}'=='local' && '${transport.coap.enabled}'=='true')")
@Slf4j
public class CoapTransportService {
private static final String V1 = "v1";
private static final String API = "api";
private CoapServer server;
@Autowired(required = false)
private ApplicationContext appContext;
@Autowired(required = false)
private SessionMsgProcessor processor;
@Autowired(required = false)
private DeviceAuthService authService;
@Autowired
private CoapTransportContext coapTransportContext;
@Autowired(required = false)
private HostRequestsQuotaService quotaService;
@Value("${coap.bind_address}")
private String host;
@Value("${coap.bind_port}")
private Integer port;
@Value("${coap.adaptor}")
private String adaptorName;
@Value("${coap.timeout}")
private Long timeout;
private CoapTransportAdaptor adaptor;
private CoapServer server;
@PostConstruct
public void init() throws UnknownHostException {
log.info("Starting CoAP transport...");
log.info("Lookup CoAP transport adaptor {}", adaptorName);
this.adaptor = (CoapTransportAdaptor) appContext.getBean(adaptorName);
log.info("Starting CoAP transport server");
this.server = new CoapServer();
createResources();
InetAddress addr = InetAddress.getByName(host);
InetSocketAddress sockAddr = new InetSocketAddress(addr, port);
InetAddress addr = InetAddress.getByName(coapTransportContext.getHost());
InetSocketAddress sockAddr = new InetSocketAddress(addr, coapTransportContext.getPort());
server.addEndpoint(new CoapEndpoint(sockAddr));
server.start();
log.info("CoAP transport started!");
@ -87,7 +65,7 @@ public class CoapTransportService {
private void createResources() {
CoapResource api = new CoapResource(API);
api.add(new CoapTransportResource(processor, authService, adaptor, V1, timeout, quotaService));
api.add(new CoapTransportResource(coapTransportContext, V1));
server.add(api);
}

47
common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/CoapTransportAdaptor.java

@ -0,0 +1,47 @@
/**
* 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.transport.coap.adaptors;
import org.eclipse.californium.core.coap.Request;
import org.eclipse.californium.core.coap.Response;
import org.thingsboard.server.common.transport.adaptor.AdaptorException;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.transport.coap.CoapTransportResource;
import java.util.UUID;
import java.util.Optional;
public interface CoapTransportAdaptor {
TransportProtos.PostTelemetryMsg convertToPostTelemetry(UUID sessionId, Request inbound) throws AdaptorException;
TransportProtos.PostAttributeMsg convertToPostAttributes(UUID sessionId, Request inbound) throws AdaptorException;
TransportProtos.GetAttributeRequestMsg convertToGetAttributes(UUID sessionId, Request inbound) throws AdaptorException;
TransportProtos.ToDeviceRpcResponseMsg convertToDeviceRpcResponse(UUID sessionId, Request inbound) throws AdaptorException;
TransportProtos.ToServerRpcRequestMsg convertToServerRpcRequest(UUID sessionId, Request inbound) throws AdaptorException;
Response convertToPublish(CoapTransportResource.CoapSessionListener session, TransportProtos.GetAttributeResponseMsg responseMsg) throws AdaptorException;
Response convertToPublish(CoapTransportResource.CoapSessionListener session, TransportProtos.AttributeUpdateNotificationMsg notificationMsg) throws AdaptorException;
Response convertToPublish(CoapTransportResource.CoapSessionListener session, TransportProtos.ToDeviceRpcRequestMsg rpcRequest) throws AdaptorException;
Response convertToPublish(CoapTransportResource.CoapSessionListener coapSessionListener, TransportProtos.ToServerRpcResponseMsg msg) throws AdaptorException;
}

155
common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/JsonCoapAdaptor.java

@ -0,0 +1,155 @@
/**
* 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.transport.coap.adaptors;
import java.util.*;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.californium.core.coap.CoAP;
import org.eclipse.californium.core.coap.Request;
import org.eclipse.californium.core.coap.Response;
import org.springframework.util.StringUtils;
import org.thingsboard.server.common.msg.kv.AttributesKVMsg;
import org.thingsboard.server.common.msg.session.SessionContext;
import org.thingsboard.server.common.transport.adaptor.AdaptorException;
import org.thingsboard.server.common.transport.adaptor.JsonConverter;
import org.springframework.stereotype.Component;
import com.google.gson.JsonParser;
import com.google.gson.JsonSyntaxException;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.transport.coap.CoapTransportResource;
@Component("JsonCoapAdaptor")
@Slf4j
public class JsonCoapAdaptor implements CoapTransportAdaptor {
@Override
public TransportProtos.PostTelemetryMsg convertToPostTelemetry(UUID sessionId, Request inbound) throws AdaptorException {
String payload = validatePayload(sessionId, inbound);
try {
return JsonConverter.convertToTelemetryProto(new JsonParser().parse(payload));
} catch (IllegalStateException | JsonSyntaxException ex) {
throw new AdaptorException(ex);
}
}
@Override
public TransportProtos.PostAttributeMsg convertToPostAttributes(UUID sessionId, Request inbound) throws AdaptorException {
String payload = validatePayload(sessionId, inbound);
try {
return JsonConverter.convertToAttributesProto(new JsonParser().parse(payload));
} catch (IllegalStateException | JsonSyntaxException ex) {
throw new AdaptorException(ex);
}
}
@Override
public TransportProtos.GetAttributeRequestMsg convertToGetAttributes(UUID sessionId, Request inbound) throws AdaptorException {
List<String> queryElements = inbound.getOptions().getUriQuery();
TransportProtos.GetAttributeRequestMsg.Builder result = TransportProtos.GetAttributeRequestMsg.newBuilder();
if (queryElements != null && queryElements.size() > 0) {
Set<String> clientKeys = toKeys(queryElements, "clientKeys");
Set<String> sharedKeys = toKeys(queryElements, "sharedKeys");
if (clientKeys != null) {
result.addAllClientAttributeNames(clientKeys);
}
if (sharedKeys != null) {
result.addAllSharedAttributeNames(sharedKeys);
}
}
return result.build();
}
@Override
public TransportProtos.ToDeviceRpcResponseMsg convertToDeviceRpcResponse(UUID sessionId, Request inbound) throws AdaptorException {
Optional<Integer> requestId = CoapTransportResource.getRequestId(inbound);
String payload = validatePayload(sessionId, inbound);
JsonObject response = new JsonParser().parse(payload).getAsJsonObject();
return TransportProtos.ToDeviceRpcResponseMsg.newBuilder().setRequestId(requestId.orElseThrow(() -> new AdaptorException("Request id is missing!")))
.setPayload(response.toString()).build();
}
@Override
public TransportProtos.ToServerRpcRequestMsg convertToServerRpcRequest(UUID sessionId, Request inbound) throws AdaptorException {
String payload = validatePayload(sessionId, inbound);
return JsonConverter.convertToServerRpcRequest(new JsonParser().parse(payload), 0);
}
@Override
public Response convertToPublish(CoapTransportResource.CoapSessionListener session, TransportProtos.AttributeUpdateNotificationMsg msg) throws AdaptorException {
return getObserveNotification(session.getNextSeqNumber(), JsonConverter.toJson(msg));
}
@Override
public Response convertToPublish(CoapTransportResource.CoapSessionListener session, TransportProtos.ToDeviceRpcRequestMsg msg) throws AdaptorException {
return getObserveNotification(session.getNextSeqNumber(), JsonConverter.toJson(msg, true));
}
@Override
public Response convertToPublish(CoapTransportResource.CoapSessionListener coapSessionListener, TransportProtos.ToServerRpcResponseMsg msg) throws AdaptorException {
Response response = new Response(CoAP.ResponseCode.CONTENT);
JsonElement result = JsonConverter.toJson(msg);
response.setPayload(result.toString());
return response;
}
@Override
public Response convertToPublish(CoapTransportResource.CoapSessionListener session, TransportProtos.GetAttributeResponseMsg msg) throws AdaptorException {
if (msg.getClientAttributeListCount() == 0 && msg.getSharedAttributeListCount() == 0 && msg.getDeletedAttributeKeysCount() == 0) {
return new Response(CoAP.ResponseCode.NOT_FOUND);
} else {
Response response = new Response(CoAP.ResponseCode.CONTENT);
JsonObject result = JsonConverter.toJson(msg);
response.setPayload(result.toString());
return response;
}
}
private Response getObserveNotification(int seqNumber, JsonElement json) {
Response response = new Response(CoAP.ResponseCode.CONTENT);
response.getOptions().setObserve(seqNumber);
response.setPayload(json.toString());
return response;
}
private String validatePayload(UUID sessionId, Request inbound) throws AdaptorException {
String payload = inbound.getPayloadString();
if (payload == null) {
log.warn("[{}] Payload is empty!", sessionId);
throw new AdaptorException(new IllegalArgumentException("Payload is empty!"));
}
return payload;
}
private Set<String> toKeys(List<String> queryElements, String attributeName) throws AdaptorException {
String keys = null;
for (String queryElement : queryElements) {
String[] queryItem = queryElement.split("=");
if (queryItem.length == 2 && queryItem[0].equals(attributeName)) {
keys = queryItem[1];
}
}
if (keys != null && !StringUtils.isEmpty(keys)) {
return new HashSet<>(Arrays.asList(keys.split(",")));
} else {
return null;
}
}
}

0
transport/coap/src/main/java/org/thingsboard/server/transport/coap/client/DeviceEmulator.java → common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/client/DeviceEmulator.java

81
common/transport/http/pom.xml

@ -0,0 +1,81 @@
<!--
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.
-->
<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">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard.common</groupId>
<version>2.2.0-SNAPSHOT</version>
<artifactId>transport</artifactId>
</parent>
<groupId>org.thingsboard.common.transport</groupId>
<artifactId>http</artifactId>
<packaging>jar</packaging>
<name>Thingsboard HTTP Transport Common</name>
<url>https://thingsboard.io</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<main.dir>${basedir}/../../..</main.dir>
</properties>
<dependencies>
<dependency>
<groupId>org.thingsboard.common.transport</groupId>
<artifactId>transport-api</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

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

Loading…
Cancel
Save