diff --git a/application/pom.xml b/application/pom.xml
index c4766f2ba8..d99f60773e 100644
--- a/application/pom.xml
+++ b/application/pom.xml
@@ -57,20 +57,20 @@
rule-engine-components
- org.thingsboard.common
- transport
+ org.thingsboard.common.transport
+ transport-api
- org.thingsboard.transport
- http
+ org.thingsboard.common.transport
+ mqtt
- org.thingsboard.transport
- coap
+ org.thingsboard.common.transport
+ http
- org.thingsboard.transport
- mqtt
+ org.thingsboard.common.transport
+ coap
org.thingsboard
@@ -542,7 +542,8 @@
-PprojectBuildDir=${project.build.directory}
-PprojectVersion=${project.version}
- -PmainJar=${project.build.directory}/${project.build.finalName}-boot.${project.packaging}
+ -PmainJar=${project.build.directory}/${project.build.finalName}-boot.${project.packaging}
+
-PpkgName=${pkg.name}
-PpkgInstallFolder=${pkg.installFolder}
-PpkgLogFolder=${pkg.unixLogFolder}
diff --git a/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java b/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java
index 8930f8476e..5e19d690c4 100644
--- a/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java
+++ b/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;
diff --git a/application/src/main/java/org/thingsboard/server/actors/app/AppActor.java b/application/src/main/java/org/thingsboard/server/actors/app/AppActor.java
index 6a78f78d1b..f4373db9be 100644
--- a/application/src/main/java/org/thingsboard/server/actors/app/AppActor.java
+++ b/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 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()));
diff --git a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActor.java b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActor.java
index 99d004574e..bd2a0f4486 100644
--- a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActor.java
+++ b/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;
}
diff --git a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java
index a2ea0487cf..05902a6146 100644
--- a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java
+++ b/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 sessions;
- private final Map attributeSubscriptions;
- private final Map rpcSubscriptions;
+ private final Map sessions;
+ private final Map attributeSubscriptions;
+ private final Map rpcSubscriptions;
private final Map toDeviceRpcPendingMap;
private final Map toServerRpcPendingMap;
- private final Map 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 syncSessionSet = new HashSet<>();
+ Set 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 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 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> processPendingRpc(ActorContext context, SessionId sessionId, Optional server, Set sentOneWayIds) {
+ private Consumer> processPendingRpc(ActorContext context, UUID sessionId, String nodeId, Set 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> clientAttributesFuture = getAttributeKvEntries(deviceId, DataConstants.CLIENT_SCOPE, request.getClientAttributeNames());
- ListenableFuture> sharedAttributesFuture = getAttributeKvEntries(deviceId, DataConstants.SHARED_SCOPE, request.getSharedAttributeNames());
-
+ private void handleGetAttributesRequest(ActorContext context, SessionInfoProto sessionInfo, GetAttributeRequestMsg request) {
+ ListenableFuture> clientAttributesFuture = getAttributeKvEntries(deviceId, DataConstants.CLIENT_SCOPE, toOptionalSet(request.getClientAttributeNamesList()));
+ ListenableFuture> sharedAttributesFuture = getAttributeKvEntries(deviceId, DataConstants.SHARED_SCOPE, toOptionalSet(request.getSharedAttributeNamesList()));
+ int requestId = request.getRequestId();
Futures.addCallback(Futures.allAsList(Arrays.asList(clientAttributesFuture, sharedAttributesFuture)), new FutureCallback>>() {
@Override
public void onSuccess(@Nullable List> 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> tsData = request.getData();
-
- PendingSessionMsgData msgData = new PendingSessionMsgData(src.getSessionId(), src.getServerAddress(),
- SessionMsgType.POST_TELEMETRY_REQUEST, request.getRequestId(), true, tsData.size());
-
- for (Map.Entry> 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 sharedKeys = msg.getDeletedKeys().stream()
+ List 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 attributes = new ArrayList<>(msg.getValues());
if (attributes.size() > 0) {
- notification = new AttributesUpdateNotification(BasicAttributeKVMsg.fromShared(attributes));
+ List 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> 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> 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 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 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> toOptionalSet(List 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 toTsKvProtos(@Nullable List result) {
+ List 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();
+ }
}
diff --git a/application/src/main/java/org/thingsboard/server/actors/device/PendingSessionMsgData.java b/application/src/main/java/org/thingsboard/server/actors/device/PendingSessionMsgData.java
deleted file mode 100644
index 23ad966049..0000000000
--- a/application/src/main/java/org/thingsboard/server/actors/device/PendingSessionMsgData.java
+++ /dev/null
@@ -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;
- private final SessionMsgType sessionMsgType;
- private final int requestId;
- private final boolean replyOnQueueAck;
- private int ackMsgCount;
-
-}
diff --git a/application/src/main/java/org/thingsboard/server/actors/device/SessionInfo.java b/application/src/main/java/org/thingsboard/server/actors/device/SessionInfo.java
index 04c457c92e..43ae592cee 100644
--- a/application/src/main/java/org/thingsboard/server/actors/device/SessionInfo.java
+++ b/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 server;
+ private final String nodeId;
}
diff --git a/application/src/main/java/org/thingsboard/server/actors/device/ToServerRpcRequestMetadata.java b/application/src/main/java/org/thingsboard/server/actors/device/ToServerRpcRequestMetadata.java
index f82a8c2481..669d94b17d 100644
--- a/application/src/main/java/org/thingsboard/server/actors/device/ToServerRpcRequestMetadata.java
+++ b/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 server;
+ private final UUID sessionId;
+ private final TransportProtos.SessionType type;
+ private final String nodeId;
}
diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java
index fe02335223..3da90d1e74 100644
--- a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java
+++ b/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 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 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 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 pushMsgToNode(targetNodeCtx, queuedMsg, fromRelationType));
+ pushMsgToNode(targetNodeCtx, copy, fromRelationType);
}
private void pushToTarget(TbMsg msg, EntityId target, String fromRelationType) {
diff --git a/application/src/main/java/org/thingsboard/server/actors/service/DefaultActorService.java b/application/src/main/java/org/thingsboard/server/actors/service/DefaultActorService.java
index 1e9e23bae3..c2e8fd048a 100644
--- a/application/src/main/java/org/thingsboard/server/actors/service/DefaultActorService.java
+++ b/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());
}
diff --git a/application/src/main/java/org/thingsboard/server/actors/session/ASyncMsgProcessor.java b/application/src/main/java/org/thingsboard/server/actors/session/ASyncMsgProcessor.java
deleted file mode 100644
index a8f14fedf5..0000000000
--- a/application/src/main/java/org/thingsboard/server/actors/session/ASyncMsgProcessor.java
+++ /dev/null
@@ -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 pendingMap = new HashMap<>();
- private Optional 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 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);
- }
- }
- }
- }
-}
diff --git a/application/src/main/java/org/thingsboard/server/actors/session/AbstractSessionActorMsgProcessor.java b/application/src/main/java/org/thingsboard/server/actors/session/AbstractSessionActorMsgProcessor.java
deleted file mode 100644
index 469cda9c5f..0000000000
--- a/application/src/main/java/org/thingsboard/server/actors/session/AbstractSessionActorMsgProcessor.java
+++ /dev/null
@@ -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 toDeviceMsg(FromDeviceMsg msg) {
- if (deviceToDeviceActorMsgPrototype != null) {
- return Optional.of(new BasicDeviceToDeviceActorMsg(deviceToDeviceActorMsgPrototype, msg));
- } else {
- return Optional.empty();
- }
- }
-
- protected Optional forwardToAppActor(ActorContext ctx, DeviceToDeviceActorMsg toForward) {
- Optional address = systemContext.getRoutingService().resolveById(toForward.getDeviceId());
- forwardToAppActor(ctx, toForward, address);
- return address;
- }
-
- protected Optional forwardToAppActorIfAddressChanged(ActorContext ctx, DeviceToDeviceActorMsg toForward, Optional oldAddress) {
-
- Optional 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 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();
- }
-}
diff --git a/application/src/main/java/org/thingsboard/server/actors/session/SessionActor.java b/application/src/main/java/org/thingsboard/server/actors/session/SessionActor.java
deleted file mode 100644
index 05926c1825..0000000000
--- a/application/src/main/java/org/thingsboard/server/actors/session/SessionActor.java
+++ /dev/null
@@ -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 {
- 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);
- }
- }
-
-}
diff --git a/application/src/main/java/org/thingsboard/server/actors/session/SessionManagerActor.java b/application/src/main/java/org/thingsboard/server/actors/session/SessionManagerActor.java
deleted file mode 100644
index 9039d302e3..0000000000
--- a/application/src/main/java/org/thingsboard/server/actors/session/SessionManagerActor.java
+++ /dev/null
@@ -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 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 {
- 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() {
- @Override
- public SupervisorStrategy.Directive apply(Throwable t) {
- logger.error(t, "Unknown failure");
- return SupervisorStrategy.stop();
- }
- });
-}
diff --git a/application/src/main/java/org/thingsboard/server/actors/session/SyncMsgProcessor.java b/application/src/main/java/org/thingsboard/server/actors/session/SyncMsgProcessor.java
deleted file mode 100644
index cf8df1385f..0000000000
--- a/application/src/main/java/org/thingsboard/server/actors/session/SyncMsgProcessor.java
+++ /dev/null
@@ -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 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 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();
- }
-}
diff --git a/application/src/main/java/org/thingsboard/server/actors/shared/ComponentMsgProcessor.java b/application/src/main/java/org/thingsboard/server/actors/shared/ComponentMsgProcessor.java
index c35b255af6..2c40be6fba 100644
--- a/application/src/main/java/org/thingsboard/server/actors/shared/ComponentMsgProcessor.java
+++ b/application/src/main/java/org/thingsboard/server/actors/shared/ComponentMsgProcessor.java
@@ -35,14 +35,12 @@ public abstract class ComponentMsgProcessor 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 extends Abstract
}
}
- protected void putToQueue(final TbMsg tbMsg, final Consumer onSuccess) {
- EntityId entityId = tbMsg.getRuleNodeId() != null ? tbMsg.getRuleNodeId() : tbMsg.getRuleChainId();
- Futures.addCallback(queue.put(this.tenantId, tbMsg, entityId.getId(), tbMsg.getClusterPartition()), new FutureCallback() {
- @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);
- }
- });
- }
}
diff --git a/application/src/main/java/org/thingsboard/server/actors/shared/SessionTimeoutMsg.java b/application/src/main/java/org/thingsboard/server/actors/shared/SessionTimeoutMsg.java
deleted file mode 100644
index d015fe01de..0000000000
--- a/application/src/main/java/org/thingsboard/server/actors/shared/SessionTimeoutMsg.java
+++ /dev/null
@@ -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;
- }
-}
diff --git a/application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java b/application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java
index 460b64c772..347483a185 100644
--- a/application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java
+++ b/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:
diff --git a/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java b/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java
index 9f92019550..6c37614147 100644
--- a/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java
+++ b/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 saveTelemetry(EntityId entityIdSrc, String requestBody, long ttl) throws ThingsboardException {
- TelemetryUploadRequest telemetryRequest;
+ Map> 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 entries = new ArrayList<>();
- for (Map.Entry> entry : telemetryRequest.getData().entrySet()) {
+ for (Map.Entry> entry : telemetryRequest.entrySet()) {
for (KvEntry kv : entry.getValue()) {
entries.add(new BasicTsKvEntry(entry.getKey(), kv));
}
diff --git a/application/src/main/java/org/thingsboard/server/service/cluster/discovery/DiscoveryService.java b/application/src/main/java/org/thingsboard/server/service/cluster/discovery/DiscoveryService.java
index 04c4cedabd..f9caafa412 100644
--- a/application/src/main/java/org/thingsboard/server/service/cluster/discovery/DiscoveryService.java
+++ b/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 getOtherServers();
diff --git a/application/src/main/java/org/thingsboard/server/service/cluster/discovery/DummyDiscoveryService.java b/application/src/main/java/org/thingsboard/server/service/cluster/discovery/DummyDiscoveryService.java
index 859a7af4fc..358c8477d8 100644
--- a/application/src/main/java/org/thingsboard/server/service/cluster/discovery/DummyDiscoveryService.java
+++ b/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
diff --git a/application/src/main/java/org/thingsboard/server/service/cluster/discovery/ZkDiscoveryService.java b/application/src/main/java/org/thingsboard/server/service/cluster/discovery/ZkDiscoveryService.java
index b4829f27aa..bb92642970 100644
--- a/application/src/main/java/org/thingsboard/server/service/cluster/discovery/ZkDiscoveryService.java
+++ b/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();
diff --git a/application/src/main/java/org/thingsboard/server/service/queue/DefaultMsgQueueService.java b/application/src/main/java/org/thingsboard/server/service/queue/DefaultMsgQueueService.java
index 927584789d..416a4d6b0f 100644
--- a/application/src/main/java/org/thingsboard/server/service/queue/DefaultMsgQueueService.java
+++ b/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 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"));
}
diff --git a/application/src/main/java/org/thingsboard/server/service/rpc/ToServerRpcResponseActorMsg.java b/application/src/main/java/org/thingsboard/server/service/rpc/ToServerRpcResponseActorMsg.java
index 201f65613a..d33a3380f4 100644
--- a/application/src/main/java/org/thingsboard/server/service/rpc/ToServerRpcResponseActorMsg.java
+++ b/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.
*/
diff --git a/application/src/main/java/org/thingsboard/server/service/script/RemoteJsInvokeService.java b/application/src/main/java/org/thingsboard/server/service/script/RemoteJsInvokeService.java
index 37a62117be..db31bda942 100644
--- a/application/src/main/java/org/thingsboard/server/service/script/RemoteJsInvokeService.java
+++ b/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 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
builder = TbKafkaRequestTemplate.builder();
diff --git a/application/src/main/java/org/thingsboard/server/service/transport/LocalTransportApiService.java b/application/src/main/java/org/thingsboard/server/service/transport/LocalTransportApiService.java
new file mode 100644
index 0000000000..5c39f8af4d
--- /dev/null
+++ b/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 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 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 handle(GetOrCreateDeviceFromGatewayRequestMsg requestMsg) {
+ DeviceId gatewayId = new DeviceId(new UUID(requestMsg.getGatewayIdMSB(), requestMsg.getGatewayIdLSB()));
+ ListenableFuture 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 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 getEmptyTransportApiResponseFuture() {
+ return Futures.immediateFuture(getEmptyTransportApiResponse());
+ }
+
+ private TransportApiResponseMsg getEmptyTransportApiResponse() {
+ return TransportApiResponseMsg.newBuilder()
+ .setValidateTokenResponseMsg(ValidateDeviceCredentialsResponseMsg.getDefaultInstance()).build();
+ }
+}
diff --git a/application/src/main/java/org/thingsboard/server/service/transport/LocalTransportService.java b/application/src/main/java/org/thingsboard/server/service/transport/LocalTransportService.java
new file mode 100644
index 0000000000..d670db3c19
--- /dev/null
+++ b/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 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 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 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 callback) {
+ forwardToDeviceActor(TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setSessionEvent(msg).build(), callback);
+ }
+
+ @Override
+ public void process(SessionInfoProto sessionInfo, PostTelemetryMsg msg, TransportServiceCallback callback) {
+ forwardToDeviceActor(TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setPostTelemetry(msg).build(), callback);
+ }
+
+ @Override
+ public void process(SessionInfoProto sessionInfo, PostAttributeMsg msg, TransportServiceCallback callback) {
+ forwardToDeviceActor(TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setPostAttributes(msg).build(), callback);
+ }
+
+ @Override
+ public void process(SessionInfoProto sessionInfo, GetAttributeRequestMsg msg, TransportServiceCallback callback) {
+ forwardToDeviceActor(TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setGetAttributes(msg).build(), callback);
+ }
+
+ @Override
+ public void process(SessionInfoProto sessionInfo, SubscribeToAttributeUpdatesMsg msg, TransportServiceCallback callback) {
+ forwardToDeviceActor(TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setSubscribeToAttributes(msg).build(), callback);
+ }
+
+ @Override
+ public void process(SessionInfoProto sessionInfo, SubscribeToRPCMsg msg, TransportServiceCallback callback) {
+ forwardToDeviceActor(TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setSubscribeToRPC(msg).build(), callback);
+ }
+
+ @Override
+ public void process(SessionInfoProto sessionInfo, ToDeviceRpcResponseMsg msg, TransportServiceCallback callback) {
+ forwardToDeviceActor(TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setToDeviceRPCCallResponse(msg).build(), callback);
+ }
+
+ @Override
+ public void process(SessionInfoProto sessionInfo, ToServerRpcRequestMsg msg, TransportServiceCallback 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 onFailure) {
+ processToTransportMsg(msg);
+ if (onSuccess != null) {
+ onSuccess.run();
+ }
+ }
+
+ private void forwardToDeviceActor(TransportToDeviceActorMsg toDeviceActorMsg, TransportServiceCallback callback) {
+ TransportToDeviceActorMsgWrapper wrapper = new TransportToDeviceActorMsgWrapper(toDeviceActorMsg);
+ Optional 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 Consumer getThrowableConsumer(TransportServiceCallback callback) {
+ return e -> {
+ if (callback != null) {
+ callback.onError(e);
+ }
+ };
+ }
+
+}
diff --git a/application/src/main/java/org/thingsboard/server/service/transport/RemoteRuleEngineTransportService.java b/application/src/main/java/org/thingsboard/server/service/transport/RemoteRuleEngineTransportService.java
new file mode 100644
index 0000000000..38db1a415b
--- /dev/null
+++ b/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 ruleEngineConsumer;
+ private TBKafkaProducerTemplate notificationsProducer;
+
+ private ExecutorService mainConsumerExecutor = Executors.newSingleThreadExecutor();
+
+ private volatile boolean stopped = false;
+
+ @PostConstruct
+ public void init() {
+ TBKafkaProducerTemplate.TBKafkaProducerTemplateBuilder notificationsProducerBuilder = TBKafkaProducerTemplate.builder();
+ notificationsProducerBuilder.settings(kafkaSettings);
+ notificationsProducerBuilder.defaultTopic(notificationsTopic);
+ notificationsProducerBuilder.encoder(new ToTransportMsgEncoder());
+
+ notificationsProducer = notificationsProducerBuilder.build();
+ notificationsProducer.init();
+
+ TBKafkaConsumerTemplate.TBKafkaConsumerTemplateBuilder 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 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 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 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 onFailure;
+
+ QueueCallbackAdaptor(Runnable onSuccess, Consumer 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);
+ }
+ }
+ }
+ }
+
+}
diff --git a/application/src/main/java/org/thingsboard/server/service/transport/RemoteTransportApiService.java b/application/src/main/java/org/thingsboard/server/service/transport/RemoteTransportApiService.java
new file mode 100644
index 0000000000..6d422f4dd6
--- /dev/null
+++ b/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 transportApiTemplate;
+
+ @PostConstruct
+ public void init() {
+ this.transportCallbackExecutor = new ThreadPoolExecutor(0, 100, 60L, TimeUnit.SECONDS, new SynchronousQueue<>());
+
+ TBKafkaProducerTemplate.TBKafkaProducerTemplateBuilder responseBuilder = TBKafkaProducerTemplate.builder();
+ responseBuilder.settings(kafkaSettings);
+ responseBuilder.defaultTopic(transportApiResponsesTopic);
+ responseBuilder.encoder(new TransportApiResponseEncoder());
+
+ TBKafkaConsumerTemplate.TBKafkaConsumerTemplateBuilder 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
+ 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();
+ }
+ }
+
+}
diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicRequest.java b/application/src/main/java/org/thingsboard/server/service/transport/RuleEngineTransportService.java
similarity index 59%
rename from common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicRequest.java
rename to application/src/main/java/org/thingsboard/server/service/transport/RuleEngineTransportService.java
index 4fcde4456b..8ca484ffcf 100644
--- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicRequest.java
+++ b/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 onFailure);
- public Integer getRequestId() {
- return requestId;
- }
}
diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/RpcSubscribeMsg.java b/application/src/main/java/org/thingsboard/server/service/transport/ToRuleEngineMsgDecoder.java
similarity index 59%
rename from common/message/src/main/java/org/thingsboard/server/common/msg/core/RpcSubscribeMsg.java
rename to application/src/main/java/org/thingsboard/server/service/transport/ToRuleEngineMsgDecoder.java
index f8f24e8d20..16954eea10 100644
--- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/RpcSubscribeMsg.java
+++ b/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 {
@Override
- public SessionMsgType getMsgType() {
- return SessionMsgType.SUBSCRIBE_RPC_COMMANDS_REQUEST;
+ public ToRuleEngineMsg decode(byte[] data) throws IOException {
+ return ToRuleEngineMsg.parseFrom(data);
}
}
diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/AttributesSubscribeMsg.java b/application/src/main/java/org/thingsboard/server/service/transport/ToTransportMsgEncoder.java
similarity index 63%
rename from common/message/src/main/java/org/thingsboard/server/common/msg/core/AttributesSubscribeMsg.java
rename to application/src/main/java/org/thingsboard/server/service/transport/ToTransportMsgEncoder.java
index cadaf3ce56..bb0f5b620e 100644
--- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/AttributesSubscribeMsg.java
+++ b/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 {
@Override
- public SessionMsgType getMsgType() {
- return SessionMsgType.SUBSCRIBE_ATTRIBUTES_REQUEST;
+ public byte[] encode(ToTransportMsg value) {
+ return value.toByteArray();
}
}
diff --git a/application/src/main/java/org/thingsboard/server/service/transport/TransportApiRequestDecoder.java b/application/src/main/java/org/thingsboard/server/service/transport/TransportApiRequestDecoder.java
new file mode 100644
index 0000000000..c54f1a0303
--- /dev/null
+++ b/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 {
+ @Override
+ public TransportApiRequestMsg decode(byte[] data) throws IOException {
+ return TransportApiRequestMsg.parseFrom(data);
+ }
+}
diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/SessionCloseMsg.java b/application/src/main/java/org/thingsboard/server/service/transport/TransportApiResponseEncoder.java
similarity index 61%
rename from common/message/src/main/java/org/thingsboard/server/common/msg/core/SessionCloseMsg.java
rename to application/src/main/java/org/thingsboard/server/service/transport/TransportApiResponseEncoder.java
index 6d3ad5e8be..175a92df96 100644
--- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/SessionCloseMsg.java
+++ b/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 {
@Override
- public SessionMsgType getMsgType() {
- return SessionMsgType.SESSION_CLOSE;
+ public byte[] encode(TransportApiResponseMsg value) {
+ return value.toByteArray();
}
}
diff --git a/application/src/main/java/org/thingsboard/server/actors/session/SessionTerminationMsg.java b/application/src/main/java/org/thingsboard/server/service/transport/TransportApiService.java
similarity index 63%
rename from application/src/main/java/org/thingsboard/server/actors/session/SessionTerminationMsg.java
rename to application/src/main/java/org/thingsboard/server/service/transport/TransportApiService.java
index 38cf878fa7..cc6f5b9f15 100644
--- a/application/src/main/java/org/thingsboard/server/actors/session/SessionTerminationMsg.java
+++ b/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 {
-
- public SessionTerminationMsg(SessionId id) {
- super(id);
- }
+/**
+ * Created by ashvayka on 05.10.18.
+ */
+public interface TransportApiService extends TbKafkaHandler {
}
diff --git a/application/src/main/java/org/thingsboard/server/service/transport/msg/TransportToDeviceActorMsgWrapper.java b/application/src/main/java/org/thingsboard/server/service/transport/msg/TransportToDeviceActorMsgWrapper.java
new file mode 100644
index 0000000000..570082d542
--- /dev/null
+++ b/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;
+ }
+}
diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml
index 8d75dab4a0..90d15003bc 100644
--- a/application/src/main/resources/thingsboard.yml
+++ b/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}"
diff --git a/common/message/pom.xml b/common/message/pom.xml
index f4f20b08c6..d914d4f21d 100644
--- a/common/message/pom.xml
+++ b/common/message/pom.xml
@@ -40,6 +40,10 @@
org.thingsboard.common
data
+
+ org.bouncycastle
+ bcprov-jdk15on
+
org.slf4j
slf4j-api
diff --git a/dao/src/main/java/org/thingsboard/server/dao/EncryptionUtil.java b/common/message/src/main/java/org/thingsboard/server/common/msg/EncryptionUtil.java
similarity index 97%
rename from dao/src/main/java/org/thingsboard/server/dao/EncryptionUtil.java
rename to common/message/src/main/java/org/thingsboard/server/common/msg/EncryptionUtil.java
index a44ad57e19..41a608763a 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/EncryptionUtil.java
+++ b/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;
diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/MsgType.java b/common/message/src/main/java/org/thingsboard/server/common/msg/MsgType.java
index 60e5469fa5..24758b5b36 100644
--- a/common/message/src/main/java/org/thingsboard/server/common/msg/MsgType.java
+++ b/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;
}
diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/aware/SessionAwareMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/aware/SessionAwareMsg.java
deleted file mode 100644
index db6ff6f632..0000000000
--- a/common/message/src/main/java/org/thingsboard/server/common/msg/aware/SessionAwareMsg.java
+++ /dev/null
@@ -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();
-
-}
diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/ActorSystemToDeviceSessionActorMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/core/ActorSystemToDeviceSessionActorMsg.java
deleted file mode 100644
index 6bb5c9911d..0000000000
--- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/ActorSystemToDeviceSessionActorMsg.java
+++ /dev/null
@@ -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();
-}
diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/AttributesUpdateNotification.java b/common/message/src/main/java/org/thingsboard/server/common/msg/core/AttributesUpdateNotification.java
deleted file mode 100644
index 1f0f9ddc99..0000000000
--- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/AttributesUpdateNotification.java
+++ /dev/null
@@ -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;
- }
-}
diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/AttributesUpdateRequest.java b/common/message/src/main/java/org/thingsboard/server/common/msg/core/AttributesUpdateRequest.java
deleted file mode 100644
index d4cb4b1ae6..0000000000
--- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/AttributesUpdateRequest.java
+++ /dev/null
@@ -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 getAttributes();
-
-}
diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicActorSystemToDeviceSessionActorMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicActorSystemToDeviceSessionActorMsg.java
deleted file mode 100644
index cc298915c8..0000000000
--- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicActorSystemToDeviceSessionActorMsg.java
+++ /dev/null
@@ -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;
- }
-}
diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicAttributesUpdateRequest.java b/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicAttributesUpdateRequest.java
deleted file mode 100644
index 76efca0f07..0000000000
--- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicAttributesUpdateRequest.java
+++ /dev/null
@@ -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 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 entries) {
- this.data.addAll(entries);
- }
-
- @Override
- public SessionMsgType getMsgType() {
- return SessionMsgType.POST_ATTRIBUTES_REQUEST;
- }
-
- @Override
- public Set getAttributes() {
- return data;
- }
-
- @Override
- public String toString() {
- return "BasicAttributesUpdateRequest [data=" + data + "]";
- }
-
-}
diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicCommandAckResponse.java b/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicCommandAckResponse.java
deleted file mode 100644
index 3ee04ba216..0000000000
--- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicCommandAckResponse.java
+++ /dev/null
@@ -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 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 []";
- }
-}
diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicGetAttributesRequest.java b/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicGetAttributesRequest.java
deleted file mode 100644
index e0f6d7ecc4..0000000000
--- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicGetAttributesRequest.java
+++ /dev/null
@@ -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 clientKeys;
- private final Set sharedKeys;
-
- public BasicGetAttributesRequest(Integer requestId) {
- this(requestId, Collections.emptySet(), Collections.emptySet());
- }
-
- public BasicGetAttributesRequest(Integer requestId, Set clientKeys, Set sharedKeys) {
- super(requestId);
- this.clientKeys = clientKeys;
- this.sharedKeys = sharedKeys;
- }
-
- @Override
- public SessionMsgType getMsgType() {
- return SessionMsgType.GET_ATTRIBUTES_REQUEST;
- }
-
- @Override
- public Optional> getClientAttributeNames() {
- return Optional.ofNullable(clientKeys);
- }
-
- @Override
- public Optional> getSharedAttributeNames() {
- return Optional.ofNullable(sharedKeys);
- }
-
-}
diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicGetAttributesResponse.java b/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicGetAttributesResponse.java
deleted file mode 100644
index e3eb15d807..0000000000
--- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicGetAttributesResponse.java
+++ /dev/null
@@ -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 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);
- }
-
-}
diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicResponseMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicResponseMsg.java
deleted file mode 100644
index c61d8e5e03..0000000000
--- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicResponseMsg.java
+++ /dev/null
@@ -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 implements ResponseMsg {
-
- 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 getError() {
- return Optional.ofNullable(error);
- }
-
- @Override
- public Optional getData() {
- return Optional.ofNullable(data);
- }
-
- @Override
- public String toString() {
- return "BasicResponseMsg [success=" + success + ", data=" + data + ", error=" + error + "]";
- }
-
- public SessionMsgType getSessionMsgType() {
- return sessionMsgType;
- }
-}
diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicStatusCodeResponse.java b/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicStatusCodeResponse.java
deleted file mode 100644
index f21aa857bf..0000000000
--- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicStatusCodeResponse.java
+++ /dev/null
@@ -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 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);
- }
-}
diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicTelemetryUploadRequest.java b/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicTelemetryUploadRequest.java
deleted file mode 100644
index 60faeb1829..0000000000
--- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicTelemetryUploadRequest.java
+++ /dev/null
@@ -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> 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 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> getData() {
- return data;
- }
-
- @Override
- public String toString() {
- return "BasicTelemetryUploadRequest [data=" + data + "]";
- }
-
-}
diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/GetAttributesRequest.java b/common/message/src/main/java/org/thingsboard/server/common/msg/core/GetAttributesRequest.java
deleted file mode 100644
index 241bb6ba1e..0000000000
--- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/GetAttributesRequest.java
+++ /dev/null
@@ -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> getClientAttributeNames();
- Optional> getSharedAttributeNames();
-
-}
diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/GetAttributesResponse.java b/common/message/src/main/java/org/thingsboard/server/common/msg/core/GetAttributesResponse.java
deleted file mode 100644
index 6242d46e8f..0000000000
--- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/GetAttributesResponse.java
+++ /dev/null
@@ -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 {
-
-}
diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/ResponseMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/core/ResponseMsg.java
deleted file mode 100644
index 3e70460dcb..0000000000
--- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/ResponseMsg.java
+++ /dev/null
@@ -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 extends ToDeviceMsg {
-
- SessionMsgType getRequestMsgType();
-
- Integer getRequestId();
-
- Optional getError();
-
- Optional getData();
-}
diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/RpcUnsubscribeMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/core/RpcUnsubscribeMsg.java
deleted file mode 100644
index 23eb238d79..0000000000
--- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/RpcUnsubscribeMsg.java
+++ /dev/null
@@ -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;
- }
-}
diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/RuleEngineError.java b/common/message/src/main/java/org/thingsboard/server/common/msg/core/RuleEngineError.java
deleted file mode 100644
index dcfde0fb52..0000000000
--- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/RuleEngineError.java
+++ /dev/null
@@ -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();
- }
-}
diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/RuleEngineErrorMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/core/RuleEngineErrorMsg.java
deleted file mode 100644
index e0ff23b379..0000000000
--- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/RuleEngineErrorMsg.java
+++ /dev/null
@@ -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!");
- }
- }
-}
diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/SessionCloseNotification.java b/common/message/src/main/java/org/thingsboard/server/common/msg/core/SessionCloseNotification.java
deleted file mode 100644
index bf36a9b3c3..0000000000
--- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/SessionCloseNotification.java
+++ /dev/null
@@ -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;
- }
-
-}
diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/SessionOpenMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/core/SessionOpenMsg.java
deleted file mode 100644
index 28e319e525..0000000000
--- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/SessionOpenMsg.java
+++ /dev/null
@@ -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;
- }
-}
diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/StatusCodeResponse.java b/common/message/src/main/java/org/thingsboard/server/common/msg/core/StatusCodeResponse.java
deleted file mode 100644
index b2d8611d1f..0000000000
--- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/StatusCodeResponse.java
+++ /dev/null
@@ -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{
-
-}
diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/TelemetryUploadRequest.java b/common/message/src/main/java/org/thingsboard/server/common/msg/core/TelemetryUploadRequest.java
deleted file mode 100644
index 52c2f9e161..0000000000
--- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/TelemetryUploadRequest.java
+++ /dev/null
@@ -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> getData();
-
-}
diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/ToDeviceRpcRequestMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/core/ToDeviceRpcRequestMsg.java
deleted file mode 100644
index d1a1f96451..0000000000
--- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/ToDeviceRpcRequestMsg.java
+++ /dev/null
@@ -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;
- }
-}
diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/ToDeviceRpcResponseMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/core/ToDeviceRpcResponseMsg.java
deleted file mode 100644
index 4fa3024947..0000000000
--- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/ToDeviceRpcResponseMsg.java
+++ /dev/null
@@ -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;
- }
-}
diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/ToServerRpcRequestMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/core/ToServerRpcRequestMsg.java
deleted file mode 100644
index 3823acac70..0000000000
--- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/ToServerRpcRequestMsg.java
+++ /dev/null
@@ -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;
- }
-}
diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/ToServerRpcResponseMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/core/ToServerRpcResponseMsg.java
index 82f44e9113..fa9ef05fe8 100644
--- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/ToServerRpcResponseMsg.java
+++ b/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;
}
diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/device/BasicDeviceToDeviceActorMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/device/BasicDeviceToDeviceActorMsg.java
deleted file mode 100644
index 92c5105673..0000000000
--- a/common/message/src/main/java/org/thingsboard/server/common/msg/device/BasicDeviceToDeviceActorMsg.java
+++ /dev/null
@@ -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 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;
- }
-}
diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/device/DeviceToDeviceActorMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/device/DeviceToDeviceActorMsg.java
deleted file mode 100644
index fe8d9f2c21..0000000000
--- a/common/message/src/main/java/org/thingsboard/server/common/msg/device/DeviceToDeviceActorMsg.java
+++ /dev/null
@@ -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 getServerAddress();
-
- FromDeviceMsg getPayload();
-
- DeviceToDeviceActorMsg toOtherAddress(ServerAddress otherAddress);
-}
diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/session/AdaptorToSessionActorMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/session/AdaptorToSessionActorMsg.java
deleted file mode 100644
index 5383a5253e..0000000000
--- a/common/message/src/main/java/org/thingsboard/server/common/msg/session/AdaptorToSessionActorMsg.java
+++ /dev/null
@@ -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();
-
-}
diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/session/BasicAdaptorToSessionActorMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/session/BasicAdaptorToSessionActorMsg.java
deleted file mode 100644
index 21c28d25be..0000000000
--- a/common/message/src/main/java/org/thingsboard/server/common/msg/session/BasicAdaptorToSessionActorMsg.java
+++ /dev/null
@@ -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;
- }
-
-}
diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/session/BasicSessionActorToAdaptorMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/session/BasicSessionActorToAdaptorMsg.java
deleted file mode 100644
index 0e1a087d6e..0000000000
--- a/common/message/src/main/java/org/thingsboard/server/common/msg/session/BasicSessionActorToAdaptorMsg.java
+++ /dev/null
@@ -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;
- }
-
-}
diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/session/BasicSessionMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/session/BasicSessionMsg.java
deleted file mode 100644
index 855cce53d7..0000000000
--- a/common/message/src/main/java/org/thingsboard/server/common/msg/session/BasicSessionMsg.java
+++ /dev/null
@@ -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 + "]";
- }
-
-}
diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/session/BasicTransportToDeviceSessionActorMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/session/BasicTransportToDeviceSessionActorMsg.java
deleted file mode 100644
index 0704e6e1bf..0000000000
--- a/common/message/src/main/java/org/thingsboard/server/common/msg/session/BasicTransportToDeviceSessionActorMsg.java
+++ /dev/null
@@ -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;
- }
-}
diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionActorToAdaptorMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionActorToAdaptorMsg.java
deleted file mode 100644
index fa1f6d5e8f..0000000000
--- a/common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionActorToAdaptorMsg.java
+++ /dev/null
@@ -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();
-
-}
diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionContext.java b/common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionContext.java
index 2f2d88047e..7f7a669100 100644
--- a/common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionContext.java
+++ b/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();
}
diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionCtrlMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionCtrlMsg.java
deleted file mode 100644
index 8082f72184..0000000000
--- a/common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionCtrlMsg.java
+++ /dev/null
@@ -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 {
-
-}
diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionMsg.java
deleted file mode 100644
index 6b0b0af15d..0000000000
--- a/common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionMsg.java
+++ /dev/null
@@ -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();
-
-}
diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionType.java b/common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionType.java
deleted file mode 100644
index 831682d165..0000000000
--- a/common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionType.java
+++ /dev/null
@@ -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;
-
-}
diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/session/ToDeviceMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/session/ToDeviceMsg.java
deleted file mode 100644
index 705e86420b..0000000000
--- a/common/message/src/main/java/org/thingsboard/server/common/msg/session/ToDeviceMsg.java
+++ /dev/null
@@ -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();
-
-}
diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/session/TransportToDeviceSessionActorMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/session/TransportToDeviceSessionActorMsg.java
deleted file mode 100644
index deaef3d3e5..0000000000
--- a/common/message/src/main/java/org/thingsboard/server/common/msg/session/TransportToDeviceSessionActorMsg.java
+++ /dev/null
@@ -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();
-
-}
diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/session/ctrl/SessionCloseMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/session/ctrl/SessionCloseMsg.java
deleted file mode 100644
index 49fad13236..0000000000
--- a/common/message/src/main/java/org/thingsboard/server/common/msg/session/ctrl/SessionCloseMsg.java
+++ /dev/null
@@ -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;
- }
-}
diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/timeout/DeviceActorQueueTimeoutMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/timeout/DeviceActorQueueTimeoutMsg.java
deleted file mode 100644
index 0277983d8b..0000000000
--- a/common/message/src/main/java/org/thingsboard/server/common/msg/timeout/DeviceActorQueueTimeoutMsg.java
+++ /dev/null
@@ -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 {
-
- public DeviceActorQueueTimeoutMsg(UUID id, long timeout) {
- super(id, timeout);
- }
-
- @Override
- public MsgType getMsgType() {
- return MsgType.DEVICE_ACTOR_QUEUE_TIMEOUT_MSG;
- }
-}
diff --git a/common/pom.xml b/common/pom.xml
index e3cdabff98..e55d71fcd3 100644
--- a/common/pom.xml
+++ b/common/pom.xml
@@ -23,7 +23,6 @@
2.2.0-SNAPSHOT
thingsboard
- org.thingsboard
common
pom
@@ -37,8 +36,8 @@
data
message
- transport
queue
+ transport
diff --git a/common/queue/pom.xml b/common/queue/pom.xml
index 11bb13981f..765960e6a7 100644
--- a/common/queue/pom.xml
+++ b/common/queue/pom.xml
@@ -16,7 +16,7 @@
-->
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0
org.thingsboard
@@ -64,6 +64,10 @@
com.google.code.gson
gson
+
+ org.apache.commons
+ commons-lang3
+
org.slf4j
slf4j-api
diff --git a/common/queue/src/main/java/org/thingsboard/server/kafka/AbstractTbKafkaTemplate.java b/common/queue/src/main/java/org/thingsboard/server/kafka/AbstractTbKafkaTemplate.java
new file mode 100644
index 0000000000..386bfc574e
--- /dev/null
+++ b/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);
+ }
+}
diff --git a/common/queue/src/main/java/org/thingsboard/server/kafka/AsyncCallbackTemplate.java b/common/queue/src/main/java/org/thingsboard/server/kafka/AsyncCallbackTemplate.java
new file mode 100644
index 0000000000..b8ad758ef9
--- /dev/null
+++ b/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 void withCallbackAndTimeout(ListenableFuture future,
+ Consumer onSuccess,
+ Consumer onFailure,
+ long timeoutInMs,
+ ScheduledExecutorService timeoutExecutor,
+ Executor callbackExecutor) {
+ future = Futures.withTimeout(future, timeoutInMs, TimeUnit.MILLISECONDS, timeoutExecutor);
+ withCallback(future, onSuccess, onFailure, callbackExecutor);
+ }
+
+ public static void withCallback(ListenableFuture future, Consumer onSuccess,
+ Consumer onFailure, Executor executor) {
+ FutureCallback callback = new FutureCallback() {
+ @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);
+ }
+ }
+
+}
diff --git a/common/queue/src/main/java/org/thingsboard/server/kafka/TBKafkaConsumerTemplate.java b/common/queue/src/main/java/org/thingsboard/server/kafka/TBKafkaConsumerTemplate.java
index 397226479e..86be3a3c38 100644
--- a/common/queue/src/main/java/org/thingsboard/server/kafka/TBKafkaConsumerTemplate.java
+++ b/common/queue/src/main/java/org/thingsboard/server/kafka/TBKafkaConsumerTemplate.java
@@ -43,12 +43,15 @@ public class TBKafkaConsumerTemplate {
private final String topic;
@Builder
- private TBKafkaConsumerTemplate(TbKafkaSettings settings, TbKafkaDecoder decoder, TbKafkaRequestIdExtractor requestIdExtractor,
+ private TBKafkaConsumerTemplate(TbKafkaSettings settings, TbKafkaDecoder decoder,
+ TbKafkaRequestIdExtractor 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");
diff --git a/common/queue/src/main/java/org/thingsboard/server/kafka/TBKafkaProducerTemplate.java b/common/queue/src/main/java/org/thingsboard/server/kafka/TBKafkaProducerTemplate.java
index 7e24ad00a6..610a49007d 100644
--- a/common/queue/src/main/java/org/thingsboard/server/kafka/TBKafkaProducerTemplate.java
+++ b/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 {
private TbKafkaEnricher enricher = ((value, responseTopic, requestId) -> value);
private final TbKafkaPartitioner partitioner;
- private List partitionInfoList;
+ private ConcurrentMap> partitionInfoMap;
@Getter
private final String defaultTopic;
@@ -78,38 +78,51 @@ public class TBKafkaProducerTemplate {
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 send(String key, T value, Callback callback) {
+ return send(key, value, null, callback);
+ }
+
+ public Future send(String key, T value, Iterable headers, Callback callback) {
+ return send(key, value, null, headers, callback);
}
- public Future send(String key, T value) {
- return send(key, value, null, null);
+ public Future send(String key, T value, Long timestamp, Iterable headers, Callback callback) {
+ return send(this.defaultTopic, key, value, timestamp, headers, callback);
}
- public Future send(String key, T value, Iterable headers) {
- return send(key, value, null, headers);
+ public Future send(String topic, String key, T value, Iterable headers, Callback callback) {
+ return send(topic, key, value, null, headers, callback);
}
- public Future send(String key, T value, Long timestamp, Iterable headers) {
- return send(this.defaultTopic, key, value, timestamp, headers);
+ public Future send(String topic, String key, T value, Callback callback) {
+ return send(topic, key, value, null, null, callback);
}
- public Future send(String topic, String key, T value, Long timestamp, Iterable headers) {
+ public Future send(String topic, String key, T value, Long timestamp, Iterable headers, Callback callback) {
byte[] data = encoder.encode(value);
ProducerRecord 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));
}
}
}
diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/session/FromDeviceMsg.java b/common/queue/src/main/java/org/thingsboard/server/kafka/TbKafkaHandler.java
similarity index 71%
rename from common/message/src/main/java/org/thingsboard/server/common/msg/session/FromDeviceMsg.java
rename to common/queue/src/main/java/org/thingsboard/server/kafka/TbKafkaHandler.java
index 71b605712f..9f86155611 100644
--- a/common/message/src/main/java/org/thingsboard/server/common/msg/session/FromDeviceMsg.java
+++ b/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 {
- SessionMsgType getMsgType();
+ ListenableFuture handle(Request request);
}
diff --git a/common/queue/src/main/java/org/thingsboard/server/kafka/TbKafkaRequestTemplate.java b/common/queue/src/main/java/org/thingsboard/server/kafka/TbKafkaRequestTemplate.java
index 8a0f5293d9..77ad0331ab 100644
--- a/common/queue/src/main/java/org/thingsboard/server/kafka/TbKafkaRequestTemplate.java
+++ b/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 {
+public class TbKafkaRequestTemplate extends AbstractTbKafkaTemplate {
private final TBKafkaProducerTemplate requestTemplate;
private final TBKafkaConsumerTemplate responseTemplate;
@@ -93,13 +94,12 @@ public class TbKafkaRequestTemplate {
ConsumerRecords 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 {
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 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 {
SettableFuture 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 {
private final long expTime;
private final SettableFuture future;
diff --git a/common/queue/src/main/java/org/thingsboard/server/kafka/TbKafkaResponseTemplate.java b/common/queue/src/main/java/org/thingsboard/server/kafka/TbKafkaResponseTemplate.java
new file mode 100644
index 0000000000..4c23ac2211
--- /dev/null
+++ b/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 extends AbstractTbKafkaTemplate {
+
+ private final TBKafkaConsumerTemplate requestTemplate;
+ private final TBKafkaProducerTemplate responseTemplate;
+ private final TbKafkaHandler handler;
+ private final ConcurrentMap 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 requestTemplate,
+ TBKafkaProducerTemplate responseTemplate,
+ TbKafkaHandler 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 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);
+ }
+
+}
diff --git a/common/queue/src/main/java/org/thingsboard/server/kafka/TbNodeIdProvider.java b/common/queue/src/main/java/org/thingsboard/server/kafka/TbNodeIdProvider.java
new file mode 100644
index 0000000000..47dd725682
--- /dev/null
+++ b/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);
+ }
+
+}
diff --git a/common/transport/coap/pom.xml b/common/transport/coap/pom.xml
new file mode 100644
index 0000000000..d68a6db39f
--- /dev/null
+++ b/common/transport/coap/pom.xml
@@ -0,0 +1,88 @@
+
+
+ 4.0.0
+
+ org.thingsboard.common
+ 2.2.0-SNAPSHOT
+ transport
+
+ org.thingsboard.common.transport
+ coap
+ jar
+
+ Thingsboard CoAP Transport Common
+ https://thingsboard.io
+
+
+ UTF-8
+ ${basedir}/../../..
+
+
+
+
+ org.thingsboard.common.transport
+ transport-api
+
+
+ org.eclipse.californium
+ californium-core
+
+
+ org.springframework
+ spring-context-support
+
+
+ org.springframework
+ spring-context
+
+
+ org.slf4j
+ slf4j-api
+
+
+ org.slf4j
+ log4j-over-slf4j
+
+
+ ch.qos.logback
+ logback-core
+
+
+ ch.qos.logback
+ logback-classic
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+ junit
+ junit
+ test
+
+
+ org.mockito
+ mockito-all
+ test
+
+
+
+
diff --git a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportContext.java b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportContext.java
new file mode 100644
index 0000000000..13662cf1be
--- /dev/null
+++ b/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;
+
+}
diff --git a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java
new file mode 100644
index 0000000000..0e2a794a45
--- /dev/null
+++ b/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 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 = 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 = 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 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 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 decodeCredentials(Request request) {
+ List 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 getFeatureType(Request request) {
+ List 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 getRequestId(Request request) {
+ List 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 {
+ private final TransportContext transportContext;
+ private final CoapExchange exchange;
+ private final Consumer onSuccess;
+
+ DeviceAuthCallback(TransportContext transportContext, CoapExchange exchange, Consumer 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 {
+ 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 {
+ 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);
+ }
+
+}
diff --git a/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportService.java b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportService.java
similarity index 72%
rename from transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportService.java
rename to common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportService.java
index 6c8437cb03..278ca6d8c5 100644
--- a/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportService.java
+++ b/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);
}
diff --git a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/CoapTransportAdaptor.java b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/CoapTransportAdaptor.java
new file mode 100644
index 0000000000..9e48cd3030
--- /dev/null
+++ b/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;
+
+}
diff --git a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/JsonCoapAdaptor.java b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/JsonCoapAdaptor.java
new file mode 100644
index 0000000000..f5bfb49c08
--- /dev/null
+++ b/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 queryElements = inbound.getOptions().getUriQuery();
+ TransportProtos.GetAttributeRequestMsg.Builder result = TransportProtos.GetAttributeRequestMsg.newBuilder();
+ if (queryElements != null && queryElements.size() > 0) {
+ Set clientKeys = toKeys(queryElements, "clientKeys");
+ Set 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 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 toKeys(List 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;
+ }
+ }
+
+}
diff --git a/transport/coap/src/main/java/org/thingsboard/server/transport/coap/client/DeviceEmulator.java b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/client/DeviceEmulator.java
similarity index 100%
rename from transport/coap/src/main/java/org/thingsboard/server/transport/coap/client/DeviceEmulator.java
rename to common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/client/DeviceEmulator.java
diff --git a/common/transport/http/pom.xml b/common/transport/http/pom.xml
new file mode 100644
index 0000000000..5d735c9def
--- /dev/null
+++ b/common/transport/http/pom.xml
@@ -0,0 +1,81 @@
+
+
+ 4.0.0
+
+ org.thingsboard.common
+ 2.2.0-SNAPSHOT
+ transport
+
+ org.thingsboard.common.transport
+ http
+ jar
+
+ Thingsboard HTTP Transport Common
+ https://thingsboard.io
+
+
+ UTF-8
+ ${basedir}/../../..
+
+
+
+
+ org.thingsboard.common.transport
+ transport-api
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+ provided
+
+
+ org.slf4j
+ slf4j-api
+
+
+ org.slf4j
+ log4j-over-slf4j
+
+
+ ch.qos.logback
+ logback-core
+
+
+ ch.qos.logback
+ logback-classic
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+ junit
+ junit
+ test
+
+
+ org.mockito
+ mockito-all
+ test
+
+
+
+
diff --git a/common/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java b/common/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java
new file mode 100644
index 0000000000..b40fbc194f
--- /dev/null
+++ b/common/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java
@@ -0,0 +1,326 @@
+/**
+ * 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.http;
+
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.util.StringUtils;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.context.request.async.DeferredResult;
+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.JsonConverter;
+import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.DeviceInfoProto;
+import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeRequestMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeResponseMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.SessionCloseNotificationProto;
+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.ToServerRpcRequestMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.ToServerRpcResponseMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceCredentialsResponseMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceTokenRequestMsg;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.Arrays;
+import java.util.List;
+import java.util.UUID;
+import java.util.function.Consumer;
+
+/**
+ * @author Andrew Shvayka
+ */
+@RestController
+@ConditionalOnExpression("'${transport.type:null}'=='null' || ('${transport.type}'=='local' && '${transport.http.enabled}'=='true')")
+@RequestMapping("/api/v1")
+@Slf4j
+public class DeviceApiController {
+
+ @Autowired
+ private HttpTransportContext transportContext;
+
+ @RequestMapping(value = "/{deviceToken}/attributes", method = RequestMethod.GET, produces = "application/json")
+ public DeferredResult getDeviceAttributes(@PathVariable("deviceToken") String deviceToken,
+ @RequestParam(value = "clientKeys", required = false, defaultValue = "") String clientKeys,
+ @RequestParam(value = "sharedKeys", required = false, defaultValue = "") String sharedKeys,
+ HttpServletRequest httpRequest) {
+ DeferredResult responseWriter = new DeferredResult<>();
+ if (quotaExceeded(httpRequest, responseWriter)) {
+ return responseWriter;
+ }
+ transportContext.getTransportService().process(ValidateDeviceTokenRequestMsg.newBuilder().setToken(deviceToken).build(),
+ new DeviceAuthCallback(transportContext, responseWriter, sessionInfo -> {
+ GetAttributeRequestMsg.Builder request = GetAttributeRequestMsg.newBuilder().setRequestId(0);
+ List clientKeySet = !StringUtils.isEmpty(clientKeys) ? Arrays.asList(clientKeys.split(",")) : null;
+ List sharedKeySet = !StringUtils.isEmpty(sharedKeys) ? Arrays.asList(sharedKeys.split(",")) : null;
+ if (clientKeySet != null) {
+ request.addAllClientAttributeNames(clientKeySet);
+ }
+ if (sharedKeySet != null) {
+ request.addAllSharedAttributeNames(sharedKeySet);
+ }
+ TransportService transportService = transportContext.getTransportService();
+ transportService.registerSyncSession(sessionInfo, new HttpSessionListener(responseWriter), transportContext.getDefaultTimeout());
+ transportService.process(sessionInfo, request.build(), new SessionCloseOnErrorCallback(transportService, sessionInfo));
+ }));
+ return responseWriter;
+ }
+
+ @RequestMapping(value = "/{deviceToken}/attributes", method = RequestMethod.POST)
+ public DeferredResult postDeviceAttributes(@PathVariable("deviceToken") String deviceToken,
+ @RequestBody String json, HttpServletRequest request) {
+ DeferredResult responseWriter = new DeferredResult<>();
+ if (quotaExceeded(request, responseWriter)) {
+ return responseWriter;
+ }
+ transportContext.getTransportService().process(ValidateDeviceTokenRequestMsg.newBuilder().setToken(deviceToken).build(),
+ new DeviceAuthCallback(transportContext, responseWriter, sessionInfo -> {
+ TransportService transportService = transportContext.getTransportService();
+ transportService.process(sessionInfo, JsonConverter.convertToAttributesProto(new JsonParser().parse(json)),
+ new HttpOkCallback(responseWriter));
+ }));
+ return responseWriter;
+ }
+
+ @RequestMapping(value = "/{deviceToken}/telemetry", method = RequestMethod.POST)
+ public DeferredResult postTelemetry(@PathVariable("deviceToken") String deviceToken,
+ @RequestBody String json, HttpServletRequest request) {
+ DeferredResult responseWriter = new DeferredResult();
+ if (quotaExceeded(request, responseWriter)) {
+ return responseWriter;
+ }
+ transportContext.getTransportService().process(ValidateDeviceTokenRequestMsg.newBuilder().setToken(deviceToken).build(),
+ new DeviceAuthCallback(transportContext, responseWriter, sessionInfo -> {
+ TransportService transportService = transportContext.getTransportService();
+ transportService.process(sessionInfo, JsonConverter.convertToTelemetryProto(new JsonParser().parse(json)),
+ new HttpOkCallback(responseWriter));
+ }));
+ return responseWriter;
+ }
+
+ @RequestMapping(value = "/{deviceToken}/rpc", method = RequestMethod.GET, produces = "application/json")
+ public DeferredResult subscribeToCommands(@PathVariable("deviceToken") String deviceToken,
+ @RequestParam(value = "timeout", required = false, defaultValue = "0") long timeout,
+ HttpServletRequest httpRequest) {
+ DeferredResult responseWriter = new DeferredResult<>();
+ if (quotaExceeded(httpRequest, responseWriter)) {
+ return responseWriter;
+ }
+ transportContext.getTransportService().process(ValidateDeviceTokenRequestMsg.newBuilder().setToken(deviceToken).build(),
+ new DeviceAuthCallback(transportContext, responseWriter, sessionInfo -> {
+ TransportService transportService = transportContext.getTransportService();
+ transportService.registerSyncSession(sessionInfo, new HttpSessionListener(responseWriter),
+ timeout == 0 ? transportContext.getDefaultTimeout() : timeout);
+ transportService.process(sessionInfo, SubscribeToRPCMsg.getDefaultInstance(),
+ new SessionCloseOnErrorCallback(transportService, sessionInfo));
+
+ }));
+ return responseWriter;
+ }
+
+ @RequestMapping(value = "/{deviceToken}/rpc/{requestId}", method = RequestMethod.POST)
+ public DeferredResult replyToCommand(@PathVariable("deviceToken") String deviceToken,
+ @PathVariable("requestId") Integer requestId,
+ @RequestBody String json, HttpServletRequest request) {
+ DeferredResult responseWriter = new DeferredResult();
+ if (quotaExceeded(request, responseWriter)) {
+ return responseWriter;
+ }
+ transportContext.getTransportService().process(ValidateDeviceTokenRequestMsg.newBuilder().setToken(deviceToken).build(),
+ new DeviceAuthCallback(transportContext, responseWriter, sessionInfo -> {
+ TransportService transportService = transportContext.getTransportService();
+ transportService.process(sessionInfo, ToDeviceRpcResponseMsg.newBuilder().setRequestId(requestId).setPayload(json).build(), new HttpOkCallback(responseWriter));
+ }));
+ return responseWriter;
+ }
+
+ @RequestMapping(value = "/{deviceToken}/rpc", method = RequestMethod.POST)
+ public DeferredResult postRpcRequest(@PathVariable("deviceToken") String deviceToken,
+ @RequestBody String json, HttpServletRequest httpRequest) {
+ DeferredResult responseWriter = new DeferredResult();
+ if (quotaExceeded(httpRequest, responseWriter)) {
+ return responseWriter;
+ }
+ transportContext.getTransportService().process(ValidateDeviceTokenRequestMsg.newBuilder().setToken(deviceToken).build(),
+ new DeviceAuthCallback(transportContext, responseWriter, sessionInfo -> {
+ JsonObject request = new JsonParser().parse(json).getAsJsonObject();
+ TransportService transportService = transportContext.getTransportService();
+ transportService.registerSyncSession(sessionInfo, new HttpSessionListener(responseWriter), transportContext.getDefaultTimeout());
+ transportService.process(sessionInfo, ToServerRpcRequestMsg.newBuilder().setRequestId(0)
+ .setMethodName(request.get("method").getAsString())
+ .setParams(request.get("params").toString()).build(),
+ new SessionCloseOnErrorCallback(transportService, sessionInfo));
+ }));
+ return responseWriter;
+ }
+
+ @RequestMapping(value = "/{deviceToken}/attributes/updates", method = RequestMethod.GET, produces = "application/json")
+ public DeferredResult subscribeToAttributes(@PathVariable("deviceToken") String deviceToken,
+ @RequestParam(value = "timeout", required = false, defaultValue = "0") long timeout,
+ HttpServletRequest httpRequest) {
+ DeferredResult responseWriter = new DeferredResult<>();
+ if (quotaExceeded(httpRequest, responseWriter)) {
+ return responseWriter;
+ }
+ transportContext.getTransportService().process(ValidateDeviceTokenRequestMsg.newBuilder().setToken(deviceToken).build(),
+ new DeviceAuthCallback(transportContext, responseWriter, sessionInfo -> {
+ TransportService transportService = transportContext.getTransportService();
+ transportService.registerSyncSession(sessionInfo, new HttpSessionListener(responseWriter),
+ timeout == 0 ? transportContext.getDefaultTimeout() : timeout);
+ transportService.process(sessionInfo, SubscribeToAttributeUpdatesMsg.getDefaultInstance(),
+ new SessionCloseOnErrorCallback(transportService, sessionInfo));
+
+ }));
+ return responseWriter;
+ }
+
+ private boolean quotaExceeded(HttpServletRequest request, DeferredResult responseWriter) {
+ if (transportContext.getQuotaService().isQuotaExceeded(request.getRemoteAddr())) {
+ log.warn("REST Quota exceeded for [{}] . Disconnect", request.getRemoteAddr());
+ responseWriter.setResult(new ResponseEntity<>(HttpStatus.BANDWIDTH_LIMIT_EXCEEDED));
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ private static class DeviceAuthCallback implements TransportServiceCallback {
+ private final TransportContext transportContext;
+ private final DeferredResult responseWriter;
+ private final Consumer onSuccess;
+
+ DeviceAuthCallback(TransportContext transportContext, DeferredResult responseWriter, Consumer onSuccess) {
+ this.transportContext = transportContext;
+ this.responseWriter = responseWriter;
+ this.onSuccess = onSuccess;
+ }
+
+ @Override
+ public void onSuccess(ValidateDeviceCredentialsResponseMsg msg) {
+ if (msg.hasDeviceInfo()) {
+ UUID sessionId = UUID.randomUUID();
+ DeviceInfoProto deviceInfoProto = msg.getDeviceInfo();
+ SessionInfoProto sessionInfo = 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 {
+ responseWriter.setResult(new ResponseEntity<>(HttpStatus.UNAUTHORIZED));
+ }
+ }
+
+ @Override
+ public void onError(Throwable e) {
+ log.warn("Failed to process request", e);
+ responseWriter.setResult(new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR));
+ }
+ }
+
+ private static class SessionCloseOnErrorCallback implements TransportServiceCallback {
+ private final TransportService transportService;
+ private final SessionInfoProto sessionInfo;
+
+ SessionCloseOnErrorCallback(TransportService transportService, SessionInfoProto sessionInfo) {
+ this.transportService = transportService;
+ this.sessionInfo = sessionInfo;
+ }
+
+ @Override
+ public void onSuccess(Void msg) {
+ }
+
+ @Override
+ public void onError(Throwable e) {
+ transportService.deregisterSession(sessionInfo);
+ }
+ }
+
+ private static class HttpOkCallback implements TransportServiceCallback {
+ private final DeferredResult responseWriter;
+
+ public HttpOkCallback(DeferredResult responseWriter) {
+ this.responseWriter = responseWriter;
+ }
+
+ @Override
+ public void onSuccess(Void msg) {
+ responseWriter.setResult(new ResponseEntity<>(HttpStatus.OK));
+ }
+
+ @Override
+ public void onError(Throwable e) {
+ responseWriter.setResult(new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR));
+ }
+ }
+
+
+ private static class HttpSessionListener implements SessionMsgListener {
+
+ private final DeferredResult responseWriter;
+
+ HttpSessionListener(DeferredResult responseWriter) {
+ this.responseWriter = responseWriter;
+ }
+
+ @Override
+ public void onGetAttributesResponse(GetAttributeResponseMsg msg) {
+ responseWriter.setResult(new ResponseEntity<>(JsonConverter.toJson(msg).toString(), HttpStatus.OK));
+ }
+
+ @Override
+ public void onAttributeUpdate(AttributeUpdateNotificationMsg msg) {
+ responseWriter.setResult(new ResponseEntity<>(JsonConverter.toJson(msg).toString(), HttpStatus.OK));
+ }
+
+ @Override
+ public void onRemoteSessionCloseCommand(SessionCloseNotificationProto sessionCloseNotification) {
+ responseWriter.setResult(new ResponseEntity<>(HttpStatus.REQUEST_TIMEOUT));
+ }
+
+ @Override
+ public void onToDeviceRpcRequest(ToDeviceRpcRequestMsg msg) {
+ responseWriter.setResult(new ResponseEntity<>(JsonConverter.toJson(msg, true).toString(), HttpStatus.OK));
+ }
+
+ @Override
+ public void onToServerRpcResponse(ToServerRpcResponseMsg msg) {
+ responseWriter.setResult(new ResponseEntity<>(JsonConverter.toJson(msg).toString(), HttpStatus.OK));
+ }
+ }
+}
diff --git a/common/transport/http/src/main/java/org/thingsboard/server/transport/http/HttpTransportContext.java b/common/transport/http/src/main/java/org/thingsboard/server/transport/http/HttpTransportContext.java
new file mode 100644
index 0000000000..601c9ee8e8
--- /dev/null
+++ b/common/transport/http/src/main/java/org/thingsboard/server/transport/http/HttpTransportContext.java
@@ -0,0 +1,40 @@
+/**
+ * Copyright © 2016-2018 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.transport.http;
+
+import lombok.Getter;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.stereotype.Component;
+import org.thingsboard.server.common.transport.TransportContext;
+
+import javax.annotation.PostConstruct;
+
+/**
+ * Created by ashvayka on 04.10.18.
+ */
+@Slf4j
+@ConditionalOnExpression("'${transport.type:null}'=='null' || ('${transport.type}'=='local' && '${transport.http.enabled}'=='true')")
+@Component
+public class HttpTransportContext extends TransportContext {
+
+ @Getter
+ @Value("${transport.http.request_timeout}")
+ private long defaultTimeout;
+
+}
diff --git a/common/transport/mqtt/pom.xml b/common/transport/mqtt/pom.xml
new file mode 100644
index 0000000000..91f30a6f3d
--- /dev/null
+++ b/common/transport/mqtt/pom.xml
@@ -0,0 +1,98 @@
+
+
+ 4.0.0
+
+ org.thingsboard.common
+ 2.2.0-SNAPSHOT
+ transport
+
+ org.thingsboard.common.transport
+ mqtt
+ jar
+
+ Thingsboard MQTT Transport Common
+ https://thingsboard.io
+
+
+ UTF-8
+ ${basedir}/../../..
+
+
+
+
+ org.thingsboard.common.transport
+ transport-api
+
+
+ io.netty
+ netty-all
+
+
+ org.springframework
+ spring-context-support
+
+
+ org.springframework
+ spring-context
+
+
+ org.slf4j
+ slf4j-api
+
+
+ org.slf4j
+ log4j-over-slf4j
+
+
+ ch.qos.logback
+ logback-core
+
+
+ ch.qos.logback
+ logback-classic
+
+
+ com.google.guava
+ guava
+
+
+ com.google.code.findbugs
+ jsr305
+ 3.0.1
+ true
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+ junit
+ junit
+ test
+
+
+ org.mockito
+ mockito-all
+ test
+
+
+
+
diff --git a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttSslHandlerProvider.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttSslHandlerProvider.java
similarity index 69%
rename from transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttSslHandlerProvider.java
rename to common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttSslHandlerProvider.java
index 9c458fd39f..aa643ebc9b 100644
--- a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttSslHandlerProvider.java
+++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttSslHandlerProvider.java
@@ -20,15 +20,23 @@ import io.netty.handler.ssl.SslHandler;
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.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
-import org.thingsboard.server.common.data.security.DeviceCredentials;
-import org.thingsboard.server.dao.EncryptionUtil;
-import org.thingsboard.server.dao.device.DeviceCredentialsService;
+import org.thingsboard.server.common.msg.EncryptionUtil;
+import org.thingsboard.server.common.transport.TransportService;
+import org.thingsboard.server.common.transport.TransportServiceCallback;
+import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.transport.mqtt.util.SslUtil;
-import javax.net.ssl.*;
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.X509TrustManager;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
@@ -37,12 +45,15 @@ import java.net.URL;
import java.security.KeyStore;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
/**
* Created by valerii.sosliuk on 11/6/16.
*/
@Slf4j
@Component("MqttSslHandlerProvider")
+@ConditionalOnExpression("'${transport.type:null}'=='null' || ('${transport.type}'=='local' && '${transport.http.enabled}'=='true')")
@ConditionalOnProperty(prefix = "mqtt.ssl", value = "enabled", havingValue = "true", matchIfMissing = false)
public class MqttSslHandlerProvider {
@@ -58,8 +69,7 @@ public class MqttSslHandlerProvider {
private String keyStoreType;
@Autowired
- private DeviceCredentialsService deviceCredentialsService;
-
+ private TransportService transportService;
public SslHandler getSslHandler() {
try {
@@ -112,17 +122,17 @@ public class MqttSslHandlerProvider {
break;
}
}
- return new ThingsboardMqttX509TrustManager(x509Tm, deviceCredentialsService);
+ return new ThingsboardMqttX509TrustManager(x509Tm, transportService);
}
static class ThingsboardMqttX509TrustManager implements X509TrustManager {
private final X509TrustManager trustManager;
- private DeviceCredentialsService deviceCredentialsService;
+ private TransportService transportService;
- ThingsboardMqttX509TrustManager(X509TrustManager trustManager, DeviceCredentialsService deviceCredentialsService) {
+ ThingsboardMqttX509TrustManager(X509TrustManager trustManager, TransportService transportService) {
this.trustManager = trustManager;
- this.deviceCredentialsService = deviceCredentialsService;
+ this.transportService = transportService;
}
@Override
@@ -139,20 +149,39 @@ public class MqttSslHandlerProvider {
@Override
public void checkClientTrusted(X509Certificate[] chain,
String authType) throws CertificateException {
- DeviceCredentials deviceCredentials = null;
+ String credentialsBody = null;
for (X509Certificate cert : chain) {
try {
String strCert = SslUtil.getX509CertificateString(cert);
String sha3Hash = EncryptionUtil.getSha3Hash(strCert);
- deviceCredentials = deviceCredentialsService.findDeviceCredentialsByCredentialsId(sha3Hash);
- if (deviceCredentials != null && strCert.equals(deviceCredentials.getCredentialsValue())) {
+ final String[] credentialsBodyHolder = new String[1];
+ CountDownLatch latch = new CountDownLatch(1);
+ transportService.process(TransportProtos.ValidateDeviceX509CertRequestMsg.newBuilder().setHash(sha3Hash).build(),
+ new TransportServiceCallback() {
+ @Override
+ public void onSuccess(TransportProtos.ValidateDeviceCredentialsResponseMsg msg) {
+ if (!StringUtils.isEmpty(msg.getCredentialsBody())) {
+ credentialsBodyHolder[0] = msg.getCredentialsBody();
+ }
+ latch.countDown();
+ }
+
+ @Override
+ public void onError(Throwable e) {
+ log.error(e.getMessage(), e);
+ latch.countDown();
+ }
+ });
+ latch.await(10, TimeUnit.SECONDS);
+ if (strCert.equals(credentialsBodyHolder[0])) {
+ credentialsBody = credentialsBodyHolder[0];
break;
}
- } catch (IOException e) {
+ } catch (InterruptedException | IOException e) {
log.error(e.getMessage(), e);
}
}
- if (deviceCredentials == null) {
+ if (credentialsBody == null) {
throw new CertificateException("Invalid Device Certificate");
}
}
diff --git a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTopics.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTopics.java
similarity index 100%
rename from transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTopics.java
rename to common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTopics.java
diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportContext.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportContext.java
new file mode 100644
index 0000000000..538daa1f9c
--- /dev/null
+++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportContext.java
@@ -0,0 +1,64 @@
+/**
+ * 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.mqtt;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import io.netty.handler.ssl.SslHandler;
+import lombok.Data;
+import lombok.Getter;
+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.boot.autoconfigure.condition.ConditionalOnExpression;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.stereotype.Component;
+import org.thingsboard.server.common.transport.TransportContext;
+import org.thingsboard.server.common.transport.TransportService;
+import org.thingsboard.server.common.transport.quota.host.HostRequestsQuotaService;
+import org.thingsboard.server.kafka.TbNodeIdProvider;
+import org.thingsboard.server.transport.mqtt.adaptors.MqttTransportAdaptor;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+/**
+ * Created by ashvayka on 04.10.18.
+ */
+@Slf4j
+@ConditionalOnExpression("'${transport.type:null}'=='null' || ('${transport.type}'=='local' && '${transport.mqtt.enabled}'=='true')")
+@Component
+public class MqttTransportContext extends TransportContext {
+
+ @Getter
+ @Autowired(required = false)
+ private MqttSslHandlerProvider sslHandlerProvider;
+
+ @Getter
+ @Autowired
+ private MqttTransportAdaptor adaptor;
+
+ @Getter
+ @Value("${transport.mqtt.netty.max_payload_size}")
+ private Integer maxPayloadSize;
+
+ @Getter
+ @Setter
+ private SslHandler sslHandler;
+
+}
diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java
new file mode 100644
index 0000000000..89ecdd583e
--- /dev/null
+++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java
@@ -0,0 +1,558 @@
+/**
+ * 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.mqtt;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInboundHandlerAdapter;
+import io.netty.handler.codec.mqtt.MqttConnAckMessage;
+import io.netty.handler.codec.mqtt.MqttConnAckVariableHeader;
+import io.netty.handler.codec.mqtt.MqttConnectMessage;
+import io.netty.handler.codec.mqtt.MqttConnectReturnCode;
+import io.netty.handler.codec.mqtt.MqttFixedHeader;
+import io.netty.handler.codec.mqtt.MqttMessage;
+import io.netty.handler.codec.mqtt.MqttMessageIdVariableHeader;
+import io.netty.handler.codec.mqtt.MqttPubAckMessage;
+import io.netty.handler.codec.mqtt.MqttPublishMessage;
+import io.netty.handler.codec.mqtt.MqttQoS;
+import io.netty.handler.codec.mqtt.MqttSubAckMessage;
+import io.netty.handler.codec.mqtt.MqttSubAckPayload;
+import io.netty.handler.codec.mqtt.MqttSubscribeMessage;
+import io.netty.handler.codec.mqtt.MqttTopicSubscription;
+import io.netty.handler.codec.mqtt.MqttUnsubscribeMessage;
+import io.netty.handler.ssl.SslHandler;
+import io.netty.util.concurrent.Future;
+import io.netty.util.concurrent.GenericFutureListener;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.util.StringUtils;
+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.adaptor.AdaptorException;
+import org.thingsboard.server.common.transport.quota.QuotaService;
+import org.thingsboard.server.common.msg.EncryptionUtil;
+import org.thingsboard.server.gen.transport.TransportProtos;
+import org.thingsboard.server.gen.transport.TransportProtos.DeviceInfoProto;
+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.ValidateDeviceCredentialsResponseMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceTokenRequestMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceX509CertRequestMsg;
+import org.thingsboard.server.transport.mqtt.adaptors.MqttTransportAdaptor;
+import org.thingsboard.server.transport.mqtt.session.DeviceSessionCtx;
+import org.thingsboard.server.transport.mqtt.session.GatewaySessionHandler;
+import org.thingsboard.server.transport.mqtt.session.MqttTopicMatcher;
+import org.thingsboard.server.transport.mqtt.util.SslUtil;
+
+import javax.net.ssl.SSLPeerUnverifiedException;
+import javax.security.cert.X509Certificate;
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import static io.netty.handler.codec.mqtt.MqttConnectReturnCode.CONNECTION_ACCEPTED;
+import static io.netty.handler.codec.mqtt.MqttConnectReturnCode.CONNECTION_REFUSED_BAD_USER_NAME_OR_PASSWORD;
+import static io.netty.handler.codec.mqtt.MqttConnectReturnCode.CONNECTION_REFUSED_NOT_AUTHORIZED;
+import static io.netty.handler.codec.mqtt.MqttMessageType.CONNACK;
+import static io.netty.handler.codec.mqtt.MqttMessageType.PINGRESP;
+import static io.netty.handler.codec.mqtt.MqttMessageType.PUBACK;
+import static io.netty.handler.codec.mqtt.MqttMessageType.SUBACK;
+import static io.netty.handler.codec.mqtt.MqttMessageType.UNSUBACK;
+import static io.netty.handler.codec.mqtt.MqttQoS.AT_LEAST_ONCE;
+import static io.netty.handler.codec.mqtt.MqttQoS.AT_MOST_ONCE;
+import static io.netty.handler.codec.mqtt.MqttQoS.FAILURE;
+
+/**
+ * @author Andrew Shvayka
+ */
+@Slf4j
+public class MqttTransportHandler extends ChannelInboundHandlerAdapter implements GenericFutureListener>, SessionMsgListener {
+
+ private static final MqttQoS MAX_SUPPORTED_QOS_LVL = AT_LEAST_ONCE;
+
+ private final UUID sessionId;
+ private final MqttTransportContext context;
+ private final MqttTransportAdaptor adaptor;
+ private final TransportService transportService;
+ private final QuotaService quotaService;
+ private final SslHandler sslHandler;
+ private final ConcurrentMap mqttQoSMap;
+
+ private volatile SessionInfoProto sessionInfo;
+ private volatile InetSocketAddress address;
+ private volatile DeviceSessionCtx deviceSessionCtx;
+ private volatile GatewaySessionHandler gatewaySessionHandler;
+
+ MqttTransportHandler(MqttTransportContext context) {
+ this.sessionId = UUID.randomUUID();
+ this.context = context;
+ this.transportService = context.getTransportService();
+ this.adaptor = context.getAdaptor();
+ this.quotaService = context.getQuotaService();
+ this.sslHandler = context.getSslHandler();
+ this.mqttQoSMap = new ConcurrentHashMap<>();
+ this.deviceSessionCtx = new DeviceSessionCtx(sessionId, mqttQoSMap);
+ }
+
+ @Override
+ public void channelRead(ChannelHandlerContext ctx, Object msg) {
+ log.trace("[{}] Processing msg: {}", sessionId, msg);
+ if (msg instanceof MqttMessage) {
+ processMqttMsg(ctx, (MqttMessage) msg);
+ } else {
+ ctx.close();
+ }
+ }
+
+ private void processMqttMsg(ChannelHandlerContext ctx, MqttMessage msg) {
+ address = (InetSocketAddress) ctx.channel().remoteAddress();
+ if (msg.fixedHeader() == null) {
+ log.info("[{}:{}] Invalid message received", address.getHostName(), address.getPort());
+ processDisconnect(ctx);
+ return;
+ }
+
+ if (quotaService.isQuotaExceeded(address.getHostName())) {
+ log.warn("MQTT Quota exceeded for [{}:{}] . Disconnect", address.getHostName(), address.getPort());
+ processDisconnect(ctx);
+ return;
+ }
+
+ deviceSessionCtx.setChannel(ctx);
+ switch (msg.fixedHeader().messageType()) {
+ case CONNECT:
+ processConnect(ctx, (MqttConnectMessage) msg);
+ break;
+ case PUBLISH:
+ processPublish(ctx, (MqttPublishMessage) msg);
+ break;
+ case SUBSCRIBE:
+ processSubscribe(ctx, (MqttSubscribeMessage) msg);
+ break;
+ case UNSUBSCRIBE:
+ processUnsubscribe(ctx, (MqttUnsubscribeMessage) msg);
+ break;
+ case PINGREQ:
+ //TODO: should we push the notification to the rule engine?
+ if (checkConnected(ctx)) {
+ ctx.writeAndFlush(new MqttMessage(new MqttFixedHeader(PINGRESP, false, AT_MOST_ONCE, false, 0)));
+ }
+ break;
+ case DISCONNECT:
+ if (checkConnected(ctx)) {
+ processDisconnect(ctx);
+ }
+ break;
+ default:
+ break;
+ }
+
+ }
+
+ private void processPublish(ChannelHandlerContext ctx, MqttPublishMessage mqttMsg) {
+ if (!checkConnected(ctx)) {
+ return;
+ }
+ String topicName = mqttMsg.variableHeader().topicName();
+ int msgId = mqttMsg.variableHeader().packetId();
+ log.trace("[{}] Processing publish msg [{}][{}]!", sessionId, topicName, msgId);
+
+ if (topicName.startsWith(MqttTopics.BASE_GATEWAY_API_TOPIC)) {
+ if (gatewaySessionHandler != null) {
+ handleGatewayPublishMsg(topicName, msgId, mqttMsg);
+ }
+ } else {
+ processDevicePublish(ctx, mqttMsg, topicName, msgId);
+ }
+ }
+
+ private void handleGatewayPublishMsg(String topicName, int msgId, MqttPublishMessage mqttMsg) {
+ try {
+ switch (topicName) {
+ case MqttTopics.GATEWAY_TELEMETRY_TOPIC:
+ gatewaySessionHandler.onDeviceTelemetry(mqttMsg);
+ break;
+ case MqttTopics.GATEWAY_ATTRIBUTES_TOPIC:
+ gatewaySessionHandler.onDeviceAttributes(mqttMsg);
+ break;
+ case MqttTopics.GATEWAY_ATTRIBUTES_REQUEST_TOPIC:
+ gatewaySessionHandler.onDeviceAttributesRequest(mqttMsg);
+ break;
+ case MqttTopics.GATEWAY_RPC_TOPIC:
+ gatewaySessionHandler.onDeviceRpcResponse(mqttMsg);
+ break;
+ case MqttTopics.GATEWAY_CONNECT_TOPIC:
+ gatewaySessionHandler.onDeviceConnect(mqttMsg);
+ break;
+ case MqttTopics.GATEWAY_DISCONNECT_TOPIC:
+ gatewaySessionHandler.onDeviceDisconnect(mqttMsg);
+ break;
+ }
+ } catch (RuntimeException | AdaptorException e) {
+ log.warn("[{}] Failed to process publish msg [{}][{}]", sessionId, topicName, msgId, e);
+ }
+ }
+
+ private void processDevicePublish(ChannelHandlerContext ctx, MqttPublishMessage mqttMsg, String topicName, int msgId) {
+ try {
+ if (topicName.equals(MqttTopics.DEVICE_TELEMETRY_TOPIC)) {
+ TransportProtos.PostTelemetryMsg postTelemetryMsg = adaptor.convertToPostTelemetry(deviceSessionCtx, mqttMsg);
+ transportService.process(sessionInfo, postTelemetryMsg, getPubAckCallback(ctx, msgId, postTelemetryMsg));
+ } else if (topicName.equals(MqttTopics.DEVICE_ATTRIBUTES_TOPIC)) {
+ TransportProtos.PostAttributeMsg postAttributeMsg = adaptor.convertToPostAttributes(deviceSessionCtx, mqttMsg);
+ transportService.process(sessionInfo, postAttributeMsg, getPubAckCallback(ctx, msgId, postAttributeMsg));
+ } else if (topicName.startsWith(MqttTopics.DEVICE_ATTRIBUTES_REQUEST_TOPIC_PREFIX)) {
+ TransportProtos.GetAttributeRequestMsg getAttributeMsg = adaptor.convertToGetAttributes(deviceSessionCtx, mqttMsg);
+ transportService.process(sessionInfo, getAttributeMsg, getPubAckCallback(ctx, msgId, getAttributeMsg));
+ } else if (topicName.startsWith(MqttTopics.DEVICE_RPC_RESPONSE_TOPIC)) {
+ TransportProtos.ToDeviceRpcResponseMsg rpcResponseMsg = adaptor.convertToDeviceRpcResponse(deviceSessionCtx, mqttMsg);
+ transportService.process(sessionInfo, rpcResponseMsg, getPubAckCallback(ctx, msgId, rpcResponseMsg));
+ } else if (topicName.startsWith(MqttTopics.DEVICE_RPC_REQUESTS_TOPIC)) {
+ TransportProtos.ToServerRpcRequestMsg rpcRequestMsg = adaptor.convertToServerRpcRequest(deviceSessionCtx, mqttMsg);
+ transportService.process(sessionInfo, rpcRequestMsg, getPubAckCallback(ctx, msgId, rpcRequestMsg));
+ }
+ } catch (AdaptorException e) {
+ log.warn("[{}] Failed to process publish msg [{}][{}]", sessionId, topicName, msgId, e);
+ log.info("[{}] Closing current session due to invalid publish msg [{}][{}]", sessionId, topicName, msgId);
+ ctx.close();
+ }
+ }
+
+ private TransportServiceCallback getPubAckCallback(final ChannelHandlerContext ctx, final int msgId, final T msg) {
+ return new TransportServiceCallback() {
+ @Override
+ public void onSuccess(Void dummy) {
+ log.trace("[{}] Published msg: {}", sessionId, msg);
+ if (msgId > 0) {
+ ctx.writeAndFlush(createMqttPubAckMsg(msgId));
+ }
+ }
+
+ @Override
+ public void onError(Throwable e) {
+ log.trace("[{}] Failed to publish msg: {}", sessionId, msg, e);
+ ctx.close();
+ }
+ };
+ }
+
+ private void processSubscribe(ChannelHandlerContext ctx, MqttSubscribeMessage mqttMsg) {
+ if (!checkConnected(ctx)) {
+ return;
+ }
+ log.trace("[{}] Processing subscription [{}]!", sessionId, mqttMsg.variableHeader().messageId());
+ List grantedQoSList = new ArrayList<>();
+ for (MqttTopicSubscription subscription : mqttMsg.payload().topicSubscriptions()) {
+ String topic = subscription.topicName();
+ MqttQoS reqQoS = subscription.qualityOfService();
+ try {
+ switch (topic) {
+ case MqttTopics.DEVICE_ATTRIBUTES_TOPIC: {
+ transportService.process(sessionInfo, TransportProtos.SubscribeToAttributeUpdatesMsg.newBuilder().build(), null);
+ registerSubQoS(topic, grantedQoSList, reqQoS);
+ break;
+ }
+ case MqttTopics.DEVICE_RPC_REQUESTS_SUB_TOPIC: {
+ transportService.process(sessionInfo, TransportProtos.SubscribeToRPCMsg.newBuilder().build(), null);
+ registerSubQoS(topic, grantedQoSList, reqQoS);
+ break;
+ }
+ case MqttTopics.DEVICE_RPC_RESPONSE_SUB_TOPIC:
+ case MqttTopics.GATEWAY_ATTRIBUTES_TOPIC:
+ case MqttTopics.GATEWAY_RPC_TOPIC:
+ case MqttTopics.DEVICE_ATTRIBUTES_RESPONSES_TOPIC:
+ registerSubQoS(topic, grantedQoSList, reqQoS);
+ break;
+ default:
+ log.warn("[{}] Failed to subscribe to [{}][{}]", sessionId, topic, reqQoS);
+ grantedQoSList.add(FAILURE.value());
+ break;
+ }
+ } catch (Exception e) {
+ log.warn("[{}] Failed to subscribe to [{}][{}]", sessionId, topic, reqQoS);
+ grantedQoSList.add(FAILURE.value());
+ }
+ }
+ ctx.writeAndFlush(createSubAckMessage(mqttMsg.variableHeader().messageId(), grantedQoSList));
+ }
+
+ private void registerSubQoS(String topic, List grantedQoSList, MqttQoS reqQoS) {
+ grantedQoSList.add(getMinSupportedQos(reqQoS));
+ mqttQoSMap.put(new MqttTopicMatcher(topic), getMinSupportedQos(reqQoS));
+ }
+
+ private void processUnsubscribe(ChannelHandlerContext ctx, MqttUnsubscribeMessage mqttMsg) {
+ if (!checkConnected(ctx)) {
+ return;
+ }
+ log.trace("[{}] Processing subscription [{}]!", sessionId, mqttMsg.variableHeader().messageId());
+ for (String topicName : mqttMsg.payload().topics()) {
+ mqttQoSMap.remove(new MqttTopicMatcher(topicName));
+ try {
+ switch (topicName) {
+ case MqttTopics.DEVICE_ATTRIBUTES_TOPIC: {
+ transportService.process(sessionInfo, TransportProtos.SubscribeToAttributeUpdatesMsg.newBuilder().setUnsubscribe(true).build(), null);
+ break;
+ }
+ case MqttTopics.DEVICE_RPC_REQUESTS_SUB_TOPIC: {
+ transportService.process(sessionInfo, TransportProtos.SubscribeToRPCMsg.newBuilder().setUnsubscribe(true).build(), null);
+ break;
+ }
+ }
+ } catch (Exception e) {
+ log.warn("[{}] Failed to process unsubscription [{}] to [{}]", sessionId, mqttMsg.variableHeader().messageId(), topicName);
+ }
+ }
+ ctx.writeAndFlush(createUnSubAckMessage(mqttMsg.variableHeader().messageId()));
+ }
+
+ private MqttMessage createUnSubAckMessage(int msgId) {
+ MqttFixedHeader mqttFixedHeader =
+ new MqttFixedHeader(UNSUBACK, false, AT_LEAST_ONCE, false, 0);
+ MqttMessageIdVariableHeader mqttMessageIdVariableHeader = MqttMessageIdVariableHeader.from(msgId);
+ return new MqttMessage(mqttFixedHeader, mqttMessageIdVariableHeader);
+ }
+
+ private void processConnect(ChannelHandlerContext ctx, MqttConnectMessage msg) {
+ log.info("[{}] Processing connect msg for client: {}!", sessionId, msg.payload().clientIdentifier());
+ X509Certificate cert;
+ if (sslHandler != null && (cert = getX509Certificate()) != null) {
+ processX509CertConnect(ctx, cert);
+ } else {
+ processAuthTokenConnect(ctx, msg);
+ }
+ }
+
+ private void processAuthTokenConnect(ChannelHandlerContext ctx, MqttConnectMessage msg) {
+ String userName = msg.payload().userName();
+ if (StringUtils.isEmpty(userName)) {
+ ctx.writeAndFlush(createMqttConnAckMsg(CONNECTION_REFUSED_BAD_USER_NAME_OR_PASSWORD));
+ ctx.close();
+ } else {
+ transportService.process(ValidateDeviceTokenRequestMsg.newBuilder().setToken(userName).build(),
+ new TransportServiceCallback() {
+ @Override
+ public void onSuccess(ValidateDeviceCredentialsResponseMsg msg) {
+ onValidateDeviceResponse(msg, ctx);
+ }
+
+ @Override
+ public void onError(Throwable e) {
+ log.trace("[{}] Failed to process credentials: {}", address, userName, e);
+ ctx.writeAndFlush(createMqttConnAckMsg(MqttConnectReturnCode.CONNECTION_REFUSED_SERVER_UNAVAILABLE));
+ ctx.close();
+ }
+ });
+ }
+ }
+
+ private void processX509CertConnect(ChannelHandlerContext ctx, X509Certificate cert) {
+ try {
+ String strCert = SslUtil.getX509CertificateString(cert);
+ String sha3Hash = EncryptionUtil.getSha3Hash(strCert);
+ transportService.process(ValidateDeviceX509CertRequestMsg.newBuilder().setHash(sha3Hash).build(),
+ new TransportServiceCallback() {
+ @Override
+ public void onSuccess(ValidateDeviceCredentialsResponseMsg msg) {
+ onValidateDeviceResponse(msg, ctx);
+ }
+
+ @Override
+ public void onError(Throwable e) {
+ log.trace("[{}] Failed to process credentials: {}", address, sha3Hash, e);
+ ctx.writeAndFlush(createMqttConnAckMsg(MqttConnectReturnCode.CONNECTION_REFUSED_SERVER_UNAVAILABLE));
+ ctx.close();
+ }
+ });
+ } catch (Exception e) {
+ ctx.writeAndFlush(createMqttConnAckMsg(CONNECTION_REFUSED_NOT_AUTHORIZED));
+ ctx.close();
+ }
+ }
+
+ private X509Certificate getX509Certificate() {
+ try {
+ X509Certificate[] certChain = sslHandler.engine().getSession().getPeerCertificateChain();
+ if (certChain.length > 0) {
+ return certChain[0];
+ }
+ } catch (SSLPeerUnverifiedException e) {
+ log.warn(e.getMessage());
+ return null;
+ }
+ return null;
+ }
+
+ private void processDisconnect(ChannelHandlerContext ctx) {
+ ctx.close();
+ if (deviceSessionCtx.isConnected()) {
+ transportService.process(sessionInfo, getSessionEventMsg(SessionEvent.CLOSED), null);
+ transportService.deregisterSession(sessionInfo);
+ if (gatewaySessionHandler != null) {
+ gatewaySessionHandler.onGatewayDisconnect();
+ }
+ }
+ }
+
+ private MqttConnAckMessage createMqttConnAckMsg(MqttConnectReturnCode returnCode) {
+ MqttFixedHeader mqttFixedHeader =
+ new MqttFixedHeader(CONNACK, false, AT_MOST_ONCE, false, 0);
+ MqttConnAckVariableHeader mqttConnAckVariableHeader =
+ new MqttConnAckVariableHeader(returnCode, true);
+ return new MqttConnAckMessage(mqttFixedHeader, mqttConnAckVariableHeader);
+ }
+
+ @Override
+ public void channelReadComplete(ChannelHandlerContext ctx) {
+ ctx.flush();
+ }
+
+ @Override
+ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
+ log.error("[{}] Unexpected Exception", sessionId, cause);
+ ctx.close();
+ }
+
+ private static MqttSubAckMessage createSubAckMessage(Integer msgId, List grantedQoSList) {
+ MqttFixedHeader mqttFixedHeader =
+ new MqttFixedHeader(SUBACK, false, AT_LEAST_ONCE, false, 0);
+ MqttMessageIdVariableHeader mqttMessageIdVariableHeader = MqttMessageIdVariableHeader.from(msgId);
+ MqttSubAckPayload mqttSubAckPayload = new MqttSubAckPayload(grantedQoSList);
+ return new MqttSubAckMessage(mqttFixedHeader, mqttMessageIdVariableHeader, mqttSubAckPayload);
+ }
+
+ private static int getMinSupportedQos(MqttQoS reqQoS) {
+ return Math.min(reqQoS.value(), MAX_SUPPORTED_QOS_LVL.value());
+ }
+
+ public static MqttPubAckMessage createMqttPubAckMsg(int requestId) {
+ MqttFixedHeader mqttFixedHeader =
+ new MqttFixedHeader(PUBACK, false, AT_LEAST_ONCE, false, 0);
+ MqttMessageIdVariableHeader mqttMsgIdVariableHeader =
+ MqttMessageIdVariableHeader.from(requestId);
+ return new MqttPubAckMessage(mqttFixedHeader, mqttMsgIdVariableHeader);
+ }
+
+ private boolean checkConnected(ChannelHandlerContext ctx) {
+ if (deviceSessionCtx.isConnected()) {
+ return true;
+ } else {
+ log.info("[{}] Closing current session due to invalid msg order [{}][{}]", sessionId);
+ ctx.close();
+ return false;
+ }
+ }
+
+ private void checkGatewaySession() {
+ DeviceInfoProto device = deviceSessionCtx.getDeviceInfo();
+ try {
+ JsonNode infoNode = context.getMapper().readTree(device.getAdditionalInfo());
+ if (infoNode != null) {
+ JsonNode gatewayNode = infoNode.get("gateway");
+ if (gatewayNode != null && gatewayNode.asBoolean()) {
+ gatewaySessionHandler = new GatewaySessionHandler(context, deviceSessionCtx, sessionId);
+ }
+ }
+ } catch (IOException e) {
+ log.trace("[{}][{}] Failed to fetch device additional info", sessionId, device.getDeviceName(), e);
+ }
+ }
+
+ public static SessionEventMsg getSessionEventMsg(SessionEvent event) {
+ return SessionEventMsg.newBuilder()
+ .setSessionType(TransportProtos.SessionType.ASYNC)
+ .setEvent(event).build();
+ }
+
+ @Override
+ public void operationComplete(Future super Void> future) throws Exception {
+ if (deviceSessionCtx.isConnected()) {
+ transportService.process(sessionInfo, getSessionEventMsg(SessionEvent.CLOSED), null);
+ transportService.deregisterSession(sessionInfo);
+ }
+ }
+
+ private void onValidateDeviceResponse(ValidateDeviceCredentialsResponseMsg msg, ChannelHandlerContext ctx) {
+ if (!msg.hasDeviceInfo()) {
+ ctx.writeAndFlush(createMqttConnAckMsg(CONNECTION_REFUSED_NOT_AUTHORIZED));
+ ctx.close();
+ } else {
+ deviceSessionCtx.setDeviceInfo(msg.getDeviceInfo());
+ sessionInfo = SessionInfoProto.newBuilder()
+ .setNodeId(context.getNodeId())
+ .setSessionIdMSB(sessionId.getMostSignificantBits())
+ .setSessionIdLSB(sessionId.getLeastSignificantBits())
+ .setDeviceIdMSB(msg.getDeviceInfo().getDeviceIdMSB())
+ .setDeviceIdLSB(msg.getDeviceInfo().getDeviceIdLSB())
+ .setTenantIdMSB(msg.getDeviceInfo().getTenantIdMSB())
+ .setTenantIdLSB(msg.getDeviceInfo().getTenantIdLSB())
+ .build();
+ transportService.process(sessionInfo, getSessionEventMsg(SessionEvent.OPEN), null);
+ transportService.registerAsyncSession(sessionInfo, this);
+ checkGatewaySession();
+ ctx.writeAndFlush(createMqttConnAckMsg(CONNECTION_ACCEPTED));
+ }
+ }
+
+ @Override
+ public void onGetAttributesResponse(TransportProtos.GetAttributeResponseMsg response) {
+ try {
+ adaptor.convertToPublish(deviceSessionCtx, response).ifPresent(deviceSessionCtx.getChannel()::writeAndFlush);
+ } catch (Exception e) {
+ log.trace("[{}] Failed to convert device attributes response to MQTT msg", sessionId, e);
+ }
+ }
+
+ @Override
+ public void onAttributeUpdate(TransportProtos.AttributeUpdateNotificationMsg notification) {
+ try {
+ adaptor.convertToPublish(deviceSessionCtx, notification).ifPresent(deviceSessionCtx.getChannel()::writeAndFlush);
+ } catch (Exception e) {
+ log.trace("[{}] Failed to convert device attributes update to MQTT msg", sessionId, e);
+ }
+ }
+
+ @Override
+ public void onRemoteSessionCloseCommand(TransportProtos.SessionCloseNotificationProto sessionCloseNotification) {
+ log.trace("[{}] Received the remote command to close the session", sessionId);
+ processDisconnect(deviceSessionCtx.getChannel());
+ }
+
+ @Override
+ public void onToDeviceRpcRequest(TransportProtos.ToDeviceRpcRequestMsg rpcRequest) {
+ log.trace("[{}] Received RPC command to device", sessionId);
+ try {
+ adaptor.convertToPublish(deviceSessionCtx, rpcRequest).ifPresent(deviceSessionCtx.getChannel()::writeAndFlush);
+ } catch (Exception e) {
+ log.trace("[{}] Failed to convert device RPC commandto MQTT msg", sessionId, e);
+ }
+ }
+
+ @Override
+ public void onToServerRpcResponse(TransportProtos.ToServerRpcResponseMsg rpcResponse) {
+ log.trace("[{}] Received RPC command to device", sessionId);
+ try {
+ adaptor.convertToPublish(deviceSessionCtx, rpcResponse).ifPresent(deviceSessionCtx.getChannel()::writeAndFlush);
+ } catch (Exception e) {
+ log.trace("[{}] Failed to convert device RPC commandto MQTT msg", sessionId, e);
+ }
+ }
+}
diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportServerInitializer.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportServerInitializer.java
new file mode 100644
index 0000000000..550837842f
--- /dev/null
+++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportServerInitializer.java
@@ -0,0 +1,54 @@
+/**
+ * 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.mqtt;
+
+import io.netty.channel.ChannelInitializer;
+import io.netty.channel.ChannelPipeline;
+import io.netty.channel.socket.SocketChannel;
+import io.netty.handler.codec.mqtt.MqttDecoder;
+import io.netty.handler.codec.mqtt.MqttEncoder;
+import io.netty.handler.ssl.SslHandler;
+
+/**
+ * @author Andrew Shvayka
+ */
+public class MqttTransportServerInitializer extends ChannelInitializer {
+
+ private final MqttTransportContext context;
+
+ public MqttTransportServerInitializer(MqttTransportContext context) {
+ this.context = context;
+ }
+
+ @Override
+ public void initChannel(SocketChannel ch) {
+ ChannelPipeline pipeline = ch.pipeline();
+ SslHandler sslHandler = null;
+ if (context.getSslHandlerProvider() != null) {
+ sslHandler = context.getSslHandlerProvider().getSslHandler();
+ pipeline.addLast(sslHandler);
+ context.setSslHandler(sslHandler);
+ }
+ pipeline.addLast("decoder", new MqttDecoder(context.getMaxPayloadSize()));
+ pipeline.addLast("encoder", MqttEncoder.INSTANCE);
+
+ MqttTransportHandler handler = new MqttTransportHandler(context);
+
+ pipeline.addLast(handler);
+ ch.closeFuture().addListener(handler);
+ }
+
+}
diff --git a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportService.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportService.java
similarity index 58%
rename from transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportService.java
rename to common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportService.java
index bb8d4ad54c..55df31a735 100644
--- a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportService.java
+++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportService.java
@@ -24,15 +24,9 @@ import io.netty.util.ResourceLeakDetector;
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.boot.autoconfigure.condition.ConditionalOnProperty;
-import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
-import org.thingsboard.server.common.transport.SessionMsgProcessor;
-import org.thingsboard.server.common.transport.auth.DeviceAuthService;
-import org.thingsboard.server.common.transport.quota.host.HostRequestsQuotaService;
-import org.thingsboard.server.dao.device.DeviceService;
-import org.thingsboard.server.dao.relation.RelationService;
-import org.thingsboard.server.transport.mqtt.adaptors.MqttTransportAdaptor;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
@@ -41,51 +35,24 @@ import javax.annotation.PreDestroy;
* @author Andrew Shvayka
*/
@Service("MqttTransportService")
-@ConditionalOnProperty(prefix = "mqtt", value = "enabled", havingValue = "true", matchIfMissing = false)
+@ConditionalOnExpression("'${transport.type:null}'=='null' || ('${transport.type}'=='local' && '${transport.mqtt.enabled}'=='true')")
@Slf4j
public class MqttTransportService {
- private static final String V1 = "v1";
- private static final String DEVICE = "device";
-
- @Autowired(required = false)
- private ApplicationContext appContext;
-
- @Autowired(required = false)
- private SessionMsgProcessor processor;
-
- @Autowired(required = false)
- private DeviceService deviceService;
-
- @Autowired(required = false)
- private DeviceAuthService authService;
-
- @Autowired(required = false)
- private RelationService relationService;
-
- @Autowired(required = false)
- private MqttSslHandlerProvider sslHandlerProvider;
-
- @Autowired(required = false)
- private HostRequestsQuotaService quotaService;
-
- @Value("${mqtt.bind_address}")
+ @Value("${transport.mqtt.bind_address}")
private String host;
- @Value("${mqtt.bind_port}")
+ @Value("${transport.mqtt.bind_port}")
private Integer port;
- @Value("${mqtt.adaptor}")
- private String adaptorName;
- @Value("${mqtt.netty.leak_detector_level}")
+ @Value("${transport.mqtt.netty.leak_detector_level}")
private String leakDetectorLevel;
- @Value("${mqtt.netty.boss_group_thread_count}")
+ @Value("${transport.mqtt.netty.boss_group_thread_count}")
private Integer bossGroupThreadCount;
- @Value("${mqtt.netty.worker_group_thread_count}")
+ @Value("${transport.mqtt.netty.worker_group_thread_count}")
private Integer workerGroupThreadCount;
- @Value("${mqtt.netty.max_payload_size}")
- private Integer maxPayloadSize;
- private MqttTransportAdaptor adaptor;
+ @Autowired
+ private MqttTransportContext context;
private Channel serverChannel;
private EventLoopGroup bossGroup;
@@ -97,17 +64,12 @@ public class MqttTransportService {
ResourceLeakDetector.setLevel(ResourceLeakDetector.Level.valueOf(leakDetectorLevel.toUpperCase()));
log.info("Starting MQTT transport...");
- log.info("Lookup MQTT transport adaptor {}", adaptorName);
- this.adaptor = (MqttTransportAdaptor) appContext.getBean(adaptorName);
-
- log.info("Starting MQTT transport server");
bossGroup = new NioEventLoopGroup(bossGroupThreadCount);
workerGroup = new NioEventLoopGroup(workerGroupThreadCount);
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
- .childHandler(new MqttTransportServerInitializer(processor, deviceService, authService, relationService,
- adaptor, sslHandlerProvider, quotaService, maxPayloadSize));
+ .childHandler(new MqttTransportServerInitializer(context));
serverChannel = b.bind(host, port).sync().channel();
log.info("Mqtt transport started!");
diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java
new file mode 100644
index 0000000000..8393ca9e0c
--- /dev/null
+++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java
@@ -0,0 +1,217 @@
+/**
+ * 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.mqtt.adaptors;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import com.google.gson.JsonSyntaxException;
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.ByteBufAllocator;
+import io.netty.buffer.UnpooledByteBufAllocator;
+import io.netty.handler.codec.mqtt.MqttFixedHeader;
+import io.netty.handler.codec.mqtt.MqttMessage;
+import io.netty.handler.codec.mqtt.MqttMessageType;
+import io.netty.handler.codec.mqtt.MqttPublishMessage;
+import io.netty.handler.codec.mqtt.MqttPublishVariableHeader;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+import org.springframework.util.StringUtils;
+import org.thingsboard.server.common.transport.adaptor.AdaptorException;
+import org.thingsboard.server.common.transport.adaptor.JsonConverter;
+import org.thingsboard.server.gen.transport.TransportProtos;
+import org.thingsboard.server.transport.mqtt.MqttTopics;
+import org.thingsboard.server.transport.mqtt.session.MqttDeviceAwareSessionContext;
+
+import java.nio.charset.Charset;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Optional;
+import java.util.Set;
+import java.util.UUID;
+
+/**
+ * @author Andrew Shvayka
+ */
+@Component("JsonMqttAdaptor")
+@Slf4j
+public class JsonMqttAdaptor implements MqttTransportAdaptor {
+
+ private static final Gson GSON = new Gson();
+ private static final Charset UTF8 = Charset.forName("UTF-8");
+ private static final ByteBufAllocator ALLOCATOR = new UnpooledByteBufAllocator(false);
+
+ @Override
+ public TransportProtos.PostTelemetryMsg convertToPostTelemetry(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException {
+ String payload = validatePayload(ctx.getSessionId(), inbound.payload());
+ try {
+ return JsonConverter.convertToTelemetryProto(new JsonParser().parse(payload));
+ } catch (IllegalStateException | JsonSyntaxException ex) {
+ throw new AdaptorException(ex);
+ }
+ }
+
+ @Override
+ public TransportProtos.PostAttributeMsg convertToPostAttributes(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException {
+ String payload = validatePayload(ctx.getSessionId(), inbound.payload());
+ try {
+ return JsonConverter.convertToAttributesProto(new JsonParser().parse(payload));
+ } catch (IllegalStateException | JsonSyntaxException ex) {
+ throw new AdaptorException(ex);
+ }
+ }
+
+ @Override
+ public TransportProtos.GetAttributeRequestMsg convertToGetAttributes(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException {
+ String topicName = inbound.variableHeader().topicName();
+ try {
+ TransportProtos.GetAttributeRequestMsg.Builder result = TransportProtos.GetAttributeRequestMsg.newBuilder();
+ result.setRequestId(Integer.valueOf(topicName.substring(MqttTopics.DEVICE_ATTRIBUTES_REQUEST_TOPIC_PREFIX.length())));
+ String payload = inbound.payload().toString(UTF8);
+ JsonElement requestBody = new JsonParser().parse(payload);
+ Set clientKeys = toStringSet(requestBody, "clientKeys");
+ Set sharedKeys = toStringSet(requestBody, "sharedKeys");
+ if (clientKeys != null) {
+ result.addAllClientAttributeNames(clientKeys);
+ }
+ if (sharedKeys != null) {
+ result.addAllSharedAttributeNames(sharedKeys);
+ }
+ return result.build();
+ } catch (RuntimeException e) {
+ log.warn("Failed to decode get attributes request", e);
+ throw new AdaptorException(e);
+ }
+ }
+
+ @Override
+ public TransportProtos.ToDeviceRpcResponseMsg convertToDeviceRpcResponse(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException {
+ String topicName = inbound.variableHeader().topicName();
+ try {
+ Integer requestId = Integer.valueOf(topicName.substring(MqttTopics.DEVICE_RPC_RESPONSE_TOPIC.length()));
+ String payload = inbound.payload().toString(UTF8);
+ return TransportProtos.ToDeviceRpcResponseMsg.newBuilder().setRequestId(requestId).setPayload(payload).build();
+ } catch (RuntimeException e) {
+ log.warn("Failed to decode get attributes request", e);
+ throw new AdaptorException(e);
+ }
+ }
+
+ @Override
+ public TransportProtos.ToServerRpcRequestMsg convertToServerRpcRequest(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException {
+ String topicName = inbound.variableHeader().topicName();
+ String payload = validatePayload(ctx.getSessionId(), inbound.payload());
+ try {
+ Integer requestId = Integer.valueOf(topicName.substring(MqttTopics.DEVICE_RPC_REQUESTS_TOPIC.length()));
+ return JsonConverter.convertToServerRpcRequest(new JsonParser().parse(payload), requestId);
+ } catch (IllegalStateException | JsonSyntaxException ex) {
+ throw new AdaptorException(ex);
+ }
+ }
+
+ @Override
+ public Optional convertToPublish(MqttDeviceAwareSessionContext ctx, TransportProtos.GetAttributeResponseMsg responseMsg) throws AdaptorException {
+ if (!StringUtils.isEmpty(responseMsg.getError())) {
+ throw new AdaptorException(responseMsg.getError());
+ } else {
+ Integer requestId = responseMsg.getRequestId();
+ if (requestId >= 0) {
+ return Optional.of(createMqttPublishMsg(ctx,
+ MqttTopics.DEVICE_ATTRIBUTES_RESPONSE_TOPIC_PREFIX + requestId,
+ JsonConverter.toJson(responseMsg)));
+ }
+ return Optional.empty();
+ }
+ }
+
+ @Override
+ public Optional convertToGatewayPublish(MqttDeviceAwareSessionContext ctx, TransportProtos.GetAttributeResponseMsg responseMsg) throws AdaptorException {
+ if (!StringUtils.isEmpty(responseMsg.getError())) {
+ throw new AdaptorException(responseMsg.getError());
+ } else {
+ JsonObject result = JsonConverter.getJsonObjectForGateway(responseMsg);
+ return Optional.of(createMqttPublishMsg(ctx, MqttTopics.GATEWAY_ATTRIBUTES_RESPONSE_TOPIC, result));
+ }
+ }
+
+ @Override
+ public Optional convertToPublish(MqttDeviceAwareSessionContext ctx, TransportProtos.AttributeUpdateNotificationMsg notificationMsg) throws AdaptorException {
+ return Optional.of(createMqttPublishMsg(ctx, MqttTopics.DEVICE_ATTRIBUTES_TOPIC, JsonConverter.toJson(notificationMsg)));
+ }
+
+ @Override
+ public Optional convertToGatewayPublish(MqttDeviceAwareSessionContext ctx, String deviceName, TransportProtos.AttributeUpdateNotificationMsg notificationMsg) throws AdaptorException {
+ JsonObject result = JsonConverter.getJsonObjectForGateway(deviceName, notificationMsg);
+ return Optional.of(createMqttPublishMsg(ctx, MqttTopics.GATEWAY_ATTRIBUTES_TOPIC, result));
+ }
+
+ @Override
+ public Optional convertToPublish(MqttDeviceAwareSessionContext ctx, TransportProtos.ToDeviceRpcRequestMsg rpcRequest) throws AdaptorException {
+ return Optional.of(createMqttPublishMsg(ctx, MqttTopics.DEVICE_RPC_REQUESTS_TOPIC + rpcRequest.getRequestId(), JsonConverter.toJson(rpcRequest, false)));
+ }
+
+ @Override
+ public Optional convertToGatewayPublish(MqttDeviceAwareSessionContext ctx, String deviceName, TransportProtos.ToDeviceRpcRequestMsg rpcRequest) throws AdaptorException {
+ return Optional.of(createMqttPublishMsg(ctx, MqttTopics.GATEWAY_RPC_TOPIC, JsonConverter.toGatewayJson(deviceName, rpcRequest)));
+ }
+
+ @Override
+ public Optional convertToPublish(MqttDeviceAwareSessionContext ctx, TransportProtos.ToServerRpcResponseMsg rpcResponse) {
+ return Optional.of(createMqttPublishMsg(ctx, MqttTopics.DEVICE_RPC_RESPONSE_TOPIC + rpcResponse.getRequestId(), JsonConverter.toJson(rpcResponse)));
+ }
+
+ private MqttPublishMessage createMqttPublishMsg(MqttDeviceAwareSessionContext ctx, String topic, JsonElement json) {
+ MqttFixedHeader mqttFixedHeader =
+ new MqttFixedHeader(MqttMessageType.PUBLISH, false, ctx.getQoSForTopic(topic), false, 0);
+ MqttPublishVariableHeader header = new MqttPublishVariableHeader(topic, ctx.nextMsgId());
+ ByteBuf payload = ALLOCATOR.buffer();
+ payload.writeBytes(GSON.toJson(json).getBytes(UTF8));
+ return new MqttPublishMessage(mqttFixedHeader, header, payload);
+ }
+
+ private Set toStringSet(JsonElement requestBody, String name) {
+ JsonElement element = requestBody.getAsJsonObject().get(name);
+ if (element != null) {
+ return new HashSet<>(Arrays.asList(element.getAsString().split(",")));
+ } else {
+ return null;
+ }
+ }
+
+ public static JsonElement validateJsonPayload(UUID sessionId, ByteBuf payloadData) throws AdaptorException {
+ String payload = validatePayload(sessionId, payloadData);
+ try {
+ return new JsonParser().parse(payload);
+ } catch (JsonSyntaxException ex) {
+ throw new AdaptorException(ex);
+ }
+ }
+
+ private static String validatePayload(UUID sessionId, ByteBuf payloadData) throws AdaptorException {
+ try {
+ String payload = payloadData.toString(UTF8);
+ if (payload == null) {
+ log.warn("[{}] Payload is empty!", sessionId);
+ throw new AdaptorException(new IllegalArgumentException("Payload is empty!"));
+ }
+ return payload;
+ } finally {
+ payloadData.release();
+ }
+ }
+
+}
diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/MqttTransportAdaptor.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/MqttTransportAdaptor.java
new file mode 100644
index 0000000000..7af0e66e34
--- /dev/null
+++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/MqttTransportAdaptor.java
@@ -0,0 +1,62 @@
+/**
+ * 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.mqtt.adaptors;
+
+import io.netty.handler.codec.mqtt.MqttMessage;
+import io.netty.handler.codec.mqtt.MqttPublishMessage;
+import org.thingsboard.server.common.transport.adaptor.AdaptorException;
+import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeRequestMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeResponseMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.PostAttributeMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.PostTelemetryMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcRequestMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcResponseMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.ToServerRpcRequestMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.ToServerRpcResponseMsg;
+import org.thingsboard.server.transport.mqtt.session.MqttDeviceAwareSessionContext;
+
+import java.util.Optional;
+
+/**
+ * @author Andrew Shvayka
+ */
+public interface MqttTransportAdaptor {
+
+ PostTelemetryMsg convertToPostTelemetry(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException;
+
+ PostAttributeMsg convertToPostAttributes(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException;
+
+ GetAttributeRequestMsg convertToGetAttributes(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException;
+
+ ToDeviceRpcResponseMsg convertToDeviceRpcResponse(MqttDeviceAwareSessionContext ctx, MqttPublishMessage mqttMsg) throws AdaptorException;
+
+ ToServerRpcRequestMsg convertToServerRpcRequest(MqttDeviceAwareSessionContext ctx, MqttPublishMessage mqttMsg) throws AdaptorException;
+
+ Optional convertToPublish(MqttDeviceAwareSessionContext ctx, GetAttributeResponseMsg responseMsg) throws AdaptorException;
+
+ Optional convertToGatewayPublish(MqttDeviceAwareSessionContext ctx, GetAttributeResponseMsg responseMsg) throws AdaptorException;
+
+ Optional convertToPublish(MqttDeviceAwareSessionContext ctx, AttributeUpdateNotificationMsg notificationMsg) throws AdaptorException;
+
+ Optional convertToGatewayPublish(MqttDeviceAwareSessionContext ctx, String deviceName, AttributeUpdateNotificationMsg notificationMsg) throws AdaptorException;
+
+ Optional convertToPublish(MqttDeviceAwareSessionContext ctx, ToDeviceRpcRequestMsg rpcRequest) throws AdaptorException;
+
+ Optional convertToGatewayPublish(MqttDeviceAwareSessionContext ctx, String deviceName, ToDeviceRpcRequestMsg rpcRequest) throws AdaptorException;
+
+ Optional convertToPublish(MqttDeviceAwareSessionContext ctx, ToServerRpcResponseMsg rpcResponse) throws AdaptorException;
+}
diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/DeviceSessionCtx.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/DeviceSessionCtx.java
new file mode 100644
index 0000000000..fb05e0a624
--- /dev/null
+++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/DeviceSessionCtx.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.mqtt.session;
+
+import io.netty.channel.ChannelHandlerContext;
+import lombok.Getter;
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.UUID;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * @author Andrew Shvayka
+ */
+@Slf4j
+public class DeviceSessionCtx extends MqttDeviceAwareSessionContext {
+
+ @Getter
+ private ChannelHandlerContext channel;
+ private AtomicInteger msgIdSeq = new AtomicInteger(0);
+
+ public DeviceSessionCtx(UUID sessionId, ConcurrentMap mqttQoSMap) {
+ super(sessionId, mqttQoSMap);
+ }
+
+ public void setChannel(ChannelHandlerContext channel) {
+ this.channel = channel;
+ }
+
+ public int nextMsgId() {
+ return msgIdSeq.incrementAndGet();
+ }
+}
diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewayDeviceSessionCtx.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewayDeviceSessionCtx.java
new file mode 100644
index 0000000000..38c1ffff3a
--- /dev/null
+++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewayDeviceSessionCtx.java
@@ -0,0 +1,101 @@
+/**
+ * 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.mqtt.session;
+
+import lombok.extern.slf4j.Slf4j;
+import org.thingsboard.server.common.transport.SessionMsgListener;
+import org.thingsboard.server.gen.transport.TransportProtos;
+import org.thingsboard.server.gen.transport.TransportProtos.DeviceInfoProto;
+import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto;
+
+import java.util.UUID;
+import java.util.concurrent.ConcurrentMap;
+
+/**
+ * Created by ashvayka on 19.01.17.
+ */
+@Slf4j
+public class GatewayDeviceSessionCtx extends MqttDeviceAwareSessionContext implements SessionMsgListener {
+
+ private final GatewaySessionHandler parent;
+ private final SessionInfoProto sessionInfo;
+
+ public GatewayDeviceSessionCtx(GatewaySessionHandler parent, DeviceInfoProto deviceInfo, ConcurrentMap mqttQoSMap) {
+ super(UUID.randomUUID(), mqttQoSMap);
+ this.parent = parent;
+ this.sessionInfo = SessionInfoProto.newBuilder()
+ .setNodeId(parent.getNodeId())
+ .setSessionIdMSB(sessionId.getMostSignificantBits())
+ .setSessionIdLSB(sessionId.getLeastSignificantBits())
+ .setDeviceIdMSB(deviceInfo.getDeviceIdMSB())
+ .setDeviceIdLSB(deviceInfo.getDeviceIdLSB())
+ .setTenantIdMSB(deviceInfo.getTenantIdMSB())
+ .setTenantIdLSB(deviceInfo.getTenantIdLSB())
+ .build();
+ setDeviceInfo(deviceInfo);
+ }
+
+ @Override
+ public UUID getSessionId() {
+ return sessionId;
+ }
+
+ @Override
+ public int nextMsgId() {
+ return parent.nextMsgId();
+ }
+
+ SessionInfoProto getSessionInfo() {
+ return sessionInfo;
+ }
+
+ @Override
+ public void onGetAttributesResponse(TransportProtos.GetAttributeResponseMsg response) {
+ try {
+ parent.getAdaptor().convertToGatewayPublish(this, response).ifPresent(parent::writeAndFlush);
+ } catch (Exception e) {
+ log.trace("[{}] Failed to convert device attributes response to MQTT msg", sessionId, e);
+ }
+ }
+
+ @Override
+ public void onAttributeUpdate(TransportProtos.AttributeUpdateNotificationMsg notification) {
+ try {
+ parent.getAdaptor().convertToGatewayPublish(this, getDeviceInfo().getDeviceName(), notification).ifPresent(parent::writeAndFlush);
+ } catch (Exception e) {
+ log.trace("[{}] Failed to convert device attributes response to MQTT msg", sessionId, e);
+ }
+ }
+
+ @Override
+ public void onRemoteSessionCloseCommand(TransportProtos.SessionCloseNotificationProto sessionCloseNotification) {
+ parent.deregisterSession(getDeviceInfo().getDeviceName());
+ }
+
+ @Override
+ public void onToDeviceRpcRequest(TransportProtos.ToDeviceRpcRequestMsg request) {
+ try {
+ parent.getAdaptor().convertToGatewayPublish(this, getDeviceInfo().getDeviceName(), request).ifPresent(parent::writeAndFlush);
+ } catch (Exception e) {
+ log.trace("[{}] Failed to convert device attributes response to MQTT msg", sessionId, e);
+ }
+ }
+
+ @Override
+ public void onToServerRpcResponse(TransportProtos.ToServerRpcResponseMsg toServerResponse) {
+ // This feature is not supported in the TB IoT Gateway yet.
+ }
+}
diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionHandler.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionHandler.java
new file mode 100644
index 0000000000..d6000591a8
--- /dev/null
+++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionHandler.java
@@ -0,0 +1,370 @@
+/**
+ * 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.mqtt.session;
+
+
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.SettableFuture;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonNull;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonSyntaxException;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.handler.codec.mqtt.MqttMessage;
+import io.netty.handler.codec.mqtt.MqttPublishMessage;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.util.StringUtils;
+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.common.transport.adaptor.JsonConverter;
+import org.thingsboard.server.gen.transport.TransportProtos;
+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.SessionInfoProto;
+import org.thingsboard.server.transport.mqtt.MqttTransportContext;
+import org.thingsboard.server.transport.mqtt.MqttTransportHandler;
+import org.thingsboard.server.transport.mqtt.adaptors.JsonMqttAdaptor;
+import org.thingsboard.server.transport.mqtt.adaptors.MqttTransportAdaptor;
+
+import javax.annotation.Nullable;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+/**
+ * Created by ashvayka on 19.01.17.
+ */
+@Slf4j
+public class GatewaySessionHandler {
+
+ private static final String DEFAULT_DEVICE_TYPE = "default";
+ private static final String CAN_T_PARSE_VALUE = "Can't parse value: ";
+ private static final String DEVICE_PROPERTY = "device";
+
+ private final MqttTransportContext context;
+ private final TransportService transportService;
+ private final DeviceInfoProto gateway;
+ private final UUID sessionId;
+ private final Map devices;
+ private final ConcurrentMap mqttQoSMap;
+ private final ChannelHandlerContext channel;
+ private final DeviceSessionCtx deviceSessionCtx;
+
+ public GatewaySessionHandler(MqttTransportContext context, DeviceSessionCtx deviceSessionCtx, UUID sessionId) {
+ this.context = context;
+ this.transportService = context.getTransportService();
+ this.deviceSessionCtx = deviceSessionCtx;
+ this.gateway = deviceSessionCtx.getDeviceInfo();
+ this.sessionId = sessionId;
+ this.devices = new ConcurrentHashMap<>();
+ this.mqttQoSMap = deviceSessionCtx.getMqttQoSMap();
+ this.channel = deviceSessionCtx.getChannel();
+ }
+
+ public void onDeviceConnect(MqttPublishMessage msg) throws AdaptorException {
+ JsonElement json = getJson(msg);
+ String deviceName = checkDeviceName(getDeviceName(json));
+ String deviceType = getDeviceType(json);
+ log.trace("[{}] onDeviceConnect: {}", sessionId, deviceName);
+ Futures.addCallback(onDeviceConnect(deviceName, deviceType), new FutureCallback() {
+ @Override
+ public void onSuccess(@Nullable GatewayDeviceSessionCtx result) {
+ ack(msg);
+ log.trace("[{}] onDeviceConnectOk: {}", sessionId, deviceName);
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ log.warn("[{}] Failed to process device connect command: {}", sessionId, deviceName, t);
+
+ }
+ }, context.getExecutor());
+ }
+
+ private ListenableFuture onDeviceConnect(String deviceName, String deviceType) {
+ SettableFuture future = SettableFuture.create();
+ GatewayDeviceSessionCtx result = devices.get(deviceName);
+ if (result == null) {
+ transportService.process(GetOrCreateDeviceFromGatewayRequestMsg.newBuilder()
+ .setDeviceName(deviceName)
+ .setDeviceType(deviceType)
+ .setGatewayIdMSB(gateway.getDeviceIdMSB())
+ .setGatewayIdLSB(gateway.getDeviceIdLSB()).build(),
+ new TransportServiceCallback() {
+ @Override
+ public void onSuccess(GetOrCreateDeviceFromGatewayResponseMsg msg) {
+ GatewayDeviceSessionCtx deviceSessionCtx = new GatewayDeviceSessionCtx(GatewaySessionHandler.this, msg.getDeviceInfo(), mqttQoSMap);
+ if (devices.putIfAbsent(deviceName, deviceSessionCtx) == null) {
+ SessionInfoProto deviceSessionInfo = deviceSessionCtx.getSessionInfo();
+ transportService.process(deviceSessionInfo, MqttTransportHandler.getSessionEventMsg(TransportProtos.SessionEvent.OPEN), null);
+ transportService.process(deviceSessionInfo, TransportProtos.SubscribeToRPCMsg.getDefaultInstance(), null);
+ transportService.process(deviceSessionInfo, TransportProtos.SubscribeToAttributeUpdatesMsg.getDefaultInstance(), null);
+ transportService.registerAsyncSession(deviceSessionInfo, deviceSessionCtx);
+ }
+ future.set(devices.get(deviceName));
+ }
+
+ @Override
+ public void onError(Throwable e) {
+ log.warn("[{}] Failed to process device connect command: {}", sessionId, deviceName, e);
+ future.setException(e);
+ }
+ });
+ } else {
+ future.set(result);
+ }
+ return future;
+ }
+
+ public void onDeviceDisconnect(MqttPublishMessage msg) throws AdaptorException {
+ String deviceName = checkDeviceName(getDeviceName(getJson(msg)));
+ deregisterSession(deviceName);
+ ack(msg);
+ }
+
+ void deregisterSession(String deviceName) {
+ GatewayDeviceSessionCtx deviceSessionCtx = devices.remove(deviceName);
+ if (deviceSessionCtx != null) {
+ deregisterSession(deviceName, deviceSessionCtx);
+ } else {
+ log.debug("[{}] Device [{}] was already removed from the gateway session", sessionId, deviceName);
+ }
+ }
+
+ public void onGatewayDisconnect() {
+ devices.forEach(this::deregisterSession);
+ }
+
+ public void onDeviceTelemetry(MqttPublishMessage mqttMsg) throws AdaptorException {
+ JsonElement json = JsonMqttAdaptor.validateJsonPayload(sessionId, mqttMsg.payload());
+ int msgId = mqttMsg.variableHeader().packetId();
+ if (json.isJsonObject()) {
+ JsonObject jsonObj = json.getAsJsonObject();
+ for (Map.Entry deviceEntry : jsonObj.entrySet()) {
+ String deviceName = deviceEntry.getKey();
+ Futures.addCallback(checkDeviceConnected(deviceName),
+ new FutureCallback() {
+ @Override
+ public void onSuccess(@Nullable GatewayDeviceSessionCtx deviceCtx) {
+ if (!deviceEntry.getValue().isJsonArray()) {
+ throw new JsonSyntaxException(CAN_T_PARSE_VALUE + json);
+ }
+ TransportProtos.PostTelemetryMsg postTelemetryMsg = JsonConverter.convertToTelemetryProto(deviceEntry.getValue().getAsJsonArray());
+ transportService.process(deviceCtx.getSessionInfo(), postTelemetryMsg, getPubAckCallback(channel, deviceName, msgId, postTelemetryMsg));
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ log.debug("[{}] Failed to process device teleemtry command: {}", sessionId, deviceName, t);
+ }
+ }, context.getExecutor());
+ }
+ } else {
+ throw new JsonSyntaxException(CAN_T_PARSE_VALUE + json);
+ }
+ }
+
+ public void onDeviceAttributes(MqttPublishMessage mqttMsg) throws AdaptorException {
+ JsonElement json = JsonMqttAdaptor.validateJsonPayload(sessionId, mqttMsg.payload());
+ int msgId = mqttMsg.variableHeader().packetId();
+ if (json.isJsonObject()) {
+ JsonObject jsonObj = json.getAsJsonObject();
+ for (Map.Entry deviceEntry : jsonObj.entrySet()) {
+ String deviceName = deviceEntry.getKey();
+ Futures.addCallback(checkDeviceConnected(deviceName),
+ new FutureCallback() {
+ @Override
+ public void onSuccess(@Nullable GatewayDeviceSessionCtx deviceCtx) {
+ if (!deviceEntry.getValue().isJsonObject()) {
+ throw new JsonSyntaxException(CAN_T_PARSE_VALUE + json);
+ }
+ TransportProtos.PostAttributeMsg postAttributeMsg = JsonConverter.convertToAttributesProto(deviceEntry.getValue().getAsJsonObject());
+ transportService.process(deviceCtx.getSessionInfo(), postAttributeMsg, getPubAckCallback(channel, deviceName, msgId, postAttributeMsg));
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ log.debug("[{}] Failed to process device attributes command: {}", sessionId, deviceName, t);
+ }
+ }, context.getExecutor());
+ }
+ } else {
+ throw new JsonSyntaxException(CAN_T_PARSE_VALUE + json);
+ }
+ }
+
+ public void onDeviceRpcResponse(MqttPublishMessage mqttMsg) throws AdaptorException {
+ JsonElement json = JsonMqttAdaptor.validateJsonPayload(sessionId, mqttMsg.payload());
+ int msgId = mqttMsg.variableHeader().packetId();
+ if (json.isJsonObject()) {
+ JsonObject jsonObj = json.getAsJsonObject();
+ String deviceName = jsonObj.get(DEVICE_PROPERTY).getAsString();
+ Futures.addCallback(checkDeviceConnected(deviceName),
+ new FutureCallback() {
+ @Override
+ public void onSuccess(@Nullable GatewayDeviceSessionCtx deviceCtx) {
+ Integer requestId = jsonObj.get("id").getAsInt();
+ String data = jsonObj.get("data").toString();
+ TransportProtos.ToDeviceRpcResponseMsg rpcResponseMsg = TransportProtos.ToDeviceRpcResponseMsg.newBuilder()
+ .setRequestId(requestId).setPayload(data).build();
+ transportService.process(deviceCtx.getSessionInfo(), rpcResponseMsg, getPubAckCallback(channel, deviceName, msgId, rpcResponseMsg));
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ log.debug("[{}] Failed to process device teleemtry command: {}", sessionId, deviceName, t);
+ }
+ }, context.getExecutor());
+ } else {
+ throw new JsonSyntaxException(CAN_T_PARSE_VALUE + json);
+ }
+ }
+
+ public void onDeviceAttributesRequest(MqttPublishMessage msg) throws AdaptorException {
+ JsonElement json = JsonMqttAdaptor.validateJsonPayload(sessionId, msg.payload());
+ if (json.isJsonObject()) {
+ JsonObject jsonObj = json.getAsJsonObject();
+ int requestId = jsonObj.get("id").getAsInt();
+ String deviceName = jsonObj.get(DEVICE_PROPERTY).getAsString();
+ boolean clientScope = jsonObj.get("client").getAsBoolean();
+ Set keys;
+ if (jsonObj.has("key")) {
+ keys = Collections.singleton(jsonObj.get("key").getAsString());
+ } else {
+ JsonArray keysArray = jsonObj.get("keys").getAsJsonArray();
+ keys = new HashSet<>();
+ for (JsonElement keyObj : keysArray) {
+ keys.add(keyObj.getAsString());
+ }
+ }
+ TransportProtos.GetAttributeRequestMsg.Builder result = TransportProtos.GetAttributeRequestMsg.newBuilder();
+ result.setRequestId(requestId);
+
+ if (clientScope) {
+ result.addAllClientAttributeNames(keys);
+ } else {
+ result.addAllSharedAttributeNames(keys);
+ }
+ TransportProtos.GetAttributeRequestMsg requestMsg = result.build();
+ int msgId = msg.variableHeader().packetId();
+ Futures.addCallback(checkDeviceConnected(deviceName),
+ new FutureCallback() {
+ @Override
+ public void onSuccess(@Nullable GatewayDeviceSessionCtx deviceCtx) {
+ transportService.process(deviceCtx.getSessionInfo(), requestMsg, getPubAckCallback(channel, deviceName, msgId, requestMsg));
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ log.debug("[{}] Failed to process device attributes request command: {}", sessionId, deviceName, t);
+ }
+ }, context.getExecutor());
+ ack(msg);
+ } else {
+ throw new JsonSyntaxException(CAN_T_PARSE_VALUE + json);
+ }
+ }
+
+ private ListenableFuture checkDeviceConnected(String deviceName) {
+ GatewayDeviceSessionCtx ctx = devices.get(deviceName);
+ if (ctx == null) {
+ log.debug("[{}] Missing device [{}] for the gateway session", sessionId, deviceName);
+ return onDeviceConnect(deviceName, DEFAULT_DEVICE_TYPE);
+ } else {
+ return Futures.immediateFuture(ctx);
+ }
+ }
+
+ private String checkDeviceName(String deviceName) {
+ if (StringUtils.isEmpty(deviceName)) {
+ throw new RuntimeException("Device name is empty!");
+ } else {
+ return deviceName;
+ }
+ }
+
+ private String getDeviceName(JsonElement json) throws AdaptorException {
+ return json.getAsJsonObject().get(DEVICE_PROPERTY).getAsString();
+ }
+
+ private String getDeviceType(JsonElement json) throws AdaptorException {
+ JsonElement type = json.getAsJsonObject().get("type");
+ return type == null || type instanceof JsonNull ? DEFAULT_DEVICE_TYPE : type.getAsString();
+ }
+
+ private JsonElement getJson(MqttPublishMessage mqttMsg) throws AdaptorException {
+ return JsonMqttAdaptor.validateJsonPayload(sessionId, mqttMsg.payload());
+ }
+
+ private void ack(MqttPublishMessage msg) {
+ if (msg.variableHeader().packetId() > 0) {
+ writeAndFlush(MqttTransportHandler.createMqttPubAckMsg(msg.variableHeader().packetId()));
+ }
+ }
+
+ void writeAndFlush(MqttMessage mqttMessage) {
+ channel.writeAndFlush(mqttMessage);
+ }
+
+ public String getNodeId() {
+ return context.getNodeId();
+ }
+
+ private void deregisterSession(String deviceName, GatewayDeviceSessionCtx deviceSessionCtx) {
+ transportService.deregisterSession(deviceSessionCtx.getSessionInfo());
+ transportService.process(deviceSessionCtx.getSessionInfo(), MqttTransportHandler.getSessionEventMsg(TransportProtos.SessionEvent.CLOSED), null);
+ log.debug("[{}] Removed device [{}] from the gateway session", sessionId, deviceName);
+ }
+
+ private TransportServiceCallback getPubAckCallback(final ChannelHandlerContext ctx, final String deviceName, final int msgId, final T msg) {
+ return new TransportServiceCallback() {
+ @Override
+ public void onSuccess(Void dummy) {
+ log.trace("[{}][{}] Published msg: {}", sessionId, deviceName, msg);
+ if (msgId > 0) {
+ ctx.writeAndFlush(MqttTransportHandler.createMqttPubAckMsg(msgId));
+ }
+ }
+
+ @Override
+ public void onError(Throwable e) {
+ log.trace("[{}] Failed to publish msg: {}", sessionId, deviceName, msg, e);
+ ctx.close();
+ }
+ };
+ }
+
+ public MqttTransportContext getContext() {
+ return context;
+ }
+
+ public MqttTransportAdaptor getAdaptor() {
+ return context.getAdaptor();
+ }
+
+ public int nextMsgId() {
+ return deviceSessionCtx.nextMsgId();
+ }
+}
diff --git a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/MqttDeviceAwareSessionContext.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/MqttDeviceAwareSessionContext.java
similarity index 70%
rename from transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/MqttDeviceAwareSessionContext.java
rename to common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/MqttDeviceAwareSessionContext.java
index 0752331f5a..745b0ed444 100644
--- a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/MqttDeviceAwareSessionContext.java
+++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/MqttDeviceAwareSessionContext.java
@@ -16,14 +16,11 @@
package org.thingsboard.server.transport.mqtt.session;
import io.netty.handler.codec.mqtt.MqttQoS;
-import org.thingsboard.server.common.data.Device;
-import org.thingsboard.server.common.transport.SessionMsgProcessor;
-import org.thingsboard.server.common.transport.auth.DeviceAuthService;
import org.thingsboard.server.common.transport.session.DeviceAwareSessionContext;
-import org.thingsboard.server.transport.mqtt.MqttTopicMatcher;
import java.util.List;
import java.util.Map;
+import java.util.UUID;
import java.util.concurrent.ConcurrentMap;
import java.util.stream.Collectors;
@@ -34,13 +31,8 @@ public abstract class MqttDeviceAwareSessionContext extends DeviceAwareSessionCo
private final ConcurrentMap mqttQoSMap;
- public MqttDeviceAwareSessionContext(SessionMsgProcessor processor, DeviceAuthService authService, ConcurrentMap mqttQoSMap) {
- super(processor, authService);
- this.mqttQoSMap = mqttQoSMap;
- }
-
- public MqttDeviceAwareSessionContext(SessionMsgProcessor processor, DeviceAuthService authService, Device device, ConcurrentMap mqttQoSMap) {
- super(processor, authService, device);
+ public MqttDeviceAwareSessionContext(UUID sessionId, ConcurrentMap mqttQoSMap) {
+ super(sessionId);
this.mqttQoSMap = mqttQoSMap;
}
diff --git a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTopicMatcher.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/MqttTopicMatcher.java
similarity index 93%
rename from transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTopicMatcher.java
rename to common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/MqttTopicMatcher.java
index b4a20283f7..4d48223fcd 100644
--- a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTopicMatcher.java
+++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/MqttTopicMatcher.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.thingsboard.server.transport.mqtt;
+package org.thingsboard.server.transport.mqtt.session;
import java.util.regex.Pattern;
@@ -22,7 +22,7 @@ public class MqttTopicMatcher {
private final String topic;
private final Pattern topicRegex;
- MqttTopicMatcher(String topic) {
+ public MqttTopicMatcher(String topic) {
if(topic == null){
throw new NullPointerException("topic");
}
diff --git a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/util/SslUtil.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/util/SslUtil.java
similarity index 96%
rename from transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/util/SslUtil.java
rename to common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/util/SslUtil.java
index 96cdf142a6..d98d1b91bc 100644
--- a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/util/SslUtil.java
+++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/util/SslUtil.java
@@ -17,7 +17,7 @@ package org.thingsboard.server.transport.mqtt.util;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.Base64Utils;
-import org.thingsboard.server.dao.EncryptionUtil;
+import org.thingsboard.server.common.msg.EncryptionUtil;
import java.io.IOException;
import java.security.cert.CertificateEncodingException;
diff --git a/common/transport/pom.xml b/common/transport/pom.xml
index a349012fce..83e7ba806a 100644
--- a/common/transport/pom.xml
+++ b/common/transport/pom.xml
@@ -16,7 +16,7 @@
-->
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0
org.thingsboard
@@ -25,67 +25,20 @@
org.thingsboard.common
transport
- jar
+ pom
- Thingsboard Server Common Transport components
+ Thingsboard Server Commons
https://thingsboard.io
UTF-8
${basedir}/../..
-
-
-
- org.thingsboard.common
- data
-
-
- org.thingsboard.common
- message
-
-
- com.google.code.gson
- gson
-
-
- org.slf4j
- slf4j-api
-
-
- org.slf4j
- log4j-over-slf4j
-
-
- ch.qos.logback
- logback-core
-
-
- ch.qos.logback
- logback-classic
-
-
- junit
- junit
- test
-
-
- org.mockito
- mockito-all
- test
-
-
- org.springframework
- spring-context
-
-
- com.google.guava
- guava
-
-
- org.apache.commons
- commons-lang3
-
-
+
+ transport-api
+ mqtt
+ http
+ coap
+
diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/TransportAdaptor.java b/common/transport/src/main/java/org/thingsboard/server/common/transport/TransportAdaptor.java
deleted file mode 100644
index 080f874efb..0000000000
--- a/common/transport/src/main/java/org/thingsboard/server/common/transport/TransportAdaptor.java
+++ /dev/null
@@ -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.transport;
-
-import org.thingsboard.server.common.msg.session.AdaptorToSessionActorMsg;
-import org.thingsboard.server.common.msg.session.SessionMsgType;
-import org.thingsboard.server.common.msg.session.SessionActorToAdaptorMsg;
-import org.thingsboard.server.common.msg.session.SessionContext;
-import org.thingsboard.server.common.transport.adaptor.AdaptorException;
-
-import java.util.Optional;
-
-public interface TransportAdaptor {
-
- AdaptorToSessionActorMsg convertToActorMsg(C ctx, SessionMsgType type, T inbound) throws AdaptorException;
-
- Optional convertToAdaptorMsg(C ctx, SessionActorToAdaptorMsg msg) throws AdaptorException;
-
-}
diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java b/common/transport/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java
deleted file mode 100644
index e7c173485c..0000000000
--- a/common/transport/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java
+++ /dev/null
@@ -1,216 +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.transport.adaptor;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map.Entry;
-import java.util.function.Consumer;
-import java.util.stream.Collectors;
-
-import com.google.gson.*;
-import org.thingsboard.server.common.msg.core.*;
-
-import org.thingsboard.server.common.data.kv.*;
-import org.thingsboard.server.common.msg.kv.AttributesKVMsg;
-
-public class JsonConverter {
-
- private static final Gson GSON = new Gson();
- public static final String CAN_T_PARSE_VALUE = "Can't parse value: ";
-
- public static TelemetryUploadRequest convertToTelemetry(JsonElement jsonObject) throws JsonSyntaxException {
- return convertToTelemetry(jsonObject, BasicRequest.DEFAULT_REQUEST_ID);
- }
-
- public static TelemetryUploadRequest convertToTelemetry(JsonElement jsonObject, long ts) throws JsonSyntaxException {
- return convertToTelemetry(jsonObject, ts, BasicRequest.DEFAULT_REQUEST_ID);
- }
-
- public static TelemetryUploadRequest convertToTelemetry(JsonElement jsonObject, int requestId) throws JsonSyntaxException {
- return convertToTelemetry(jsonObject, System.currentTimeMillis(), requestId);
- }
-
- private static TelemetryUploadRequest convertToTelemetry(JsonElement jsonObject, long systemTs, int requestId) throws JsonSyntaxException {
- BasicTelemetryUploadRequest request = new BasicTelemetryUploadRequest(requestId);
- if (jsonObject.isJsonObject()) {
- parseObject(request, systemTs, jsonObject);
- } else if (jsonObject.isJsonArray()) {
- jsonObject.getAsJsonArray().forEach(je -> {
- if (je.isJsonObject()) {
- parseObject(request, systemTs, je.getAsJsonObject());
- } else {
- throw new JsonSyntaxException(CAN_T_PARSE_VALUE + je);
- }
- });
- } else {
- throw new JsonSyntaxException(CAN_T_PARSE_VALUE + jsonObject);
- }
- return request;
- }
-
- public static ToServerRpcRequestMsg convertToServerRpcRequest(JsonElement json, int requestId) throws JsonSyntaxException {
- JsonObject object = json.getAsJsonObject();
- return new ToServerRpcRequestMsg(requestId, object.get("method").getAsString(), GSON.toJson(object.get("params")));
- }
-
- private static void parseObject(BasicTelemetryUploadRequest request, long systemTs, JsonElement jsonObject) {
- JsonObject jo = jsonObject.getAsJsonObject();
- if (jo.has("ts") && jo.has("values")) {
- parseWithTs(request, jo);
- } else {
- parseWithoutTs(request, systemTs, jo);
- }
- }
-
- private static void parseWithoutTs(BasicTelemetryUploadRequest request, long systemTs, JsonObject jo) {
- for (KvEntry entry : parseValues(jo)) {
- request.add(systemTs, entry);
- }
- }
-
- public static void parseWithTs(BasicTelemetryUploadRequest request, JsonObject jo) {
- long ts = jo.get("ts").getAsLong();
- JsonObject valuesObject = jo.get("values").getAsJsonObject();
- for (KvEntry entry : parseValues(valuesObject)) {
- request.add(ts, entry);
- }
- }
-
- public static List parseValues(JsonObject valuesObject) {
- List result = new ArrayList<>();
- for (Entry valueEntry : valuesObject.entrySet()) {
- JsonElement element = valueEntry.getValue();
- if (element.isJsonPrimitive()) {
- JsonPrimitive value = element.getAsJsonPrimitive();
- if (value.isString()) {
- result.add(new StringDataEntry(valueEntry.getKey(), value.getAsString()));
- } else if (value.isBoolean()) {
- result.add(new BooleanDataEntry(valueEntry.getKey(), value.getAsBoolean()));
- } else if (value.isNumber()) {
- parseNumericValue(result, valueEntry, value);
- } else {
- throw new JsonSyntaxException(CAN_T_PARSE_VALUE + value);
- }
- } else {
- throw new JsonSyntaxException(CAN_T_PARSE_VALUE + element);
- }
- }
- return result;
- }
-
- private static void parseNumericValue(List result, Entry valueEntry, JsonPrimitive value) {
- if (value.getAsString().contains(".")) {
- result.add(new DoubleDataEntry(valueEntry.getKey(), value.getAsDouble()));
- } else {
- try {
- long longValue = Long.parseLong(value.getAsString());
- result.add(new LongDataEntry(valueEntry.getKey(), longValue));
- } catch (NumberFormatException e) {
- throw new JsonSyntaxException("Big integer values are not supported!");
- }
- }
- }
-
- public static AttributesUpdateRequest convertToAttributes(JsonElement element) {
- return convertToAttributes(element, BasicRequest.DEFAULT_REQUEST_ID);
- }
-
- public static AttributesUpdateRequest convertToAttributes(JsonElement element, int requestId) {
- if (element.isJsonObject()) {
- BasicAttributesUpdateRequest request = new BasicAttributesUpdateRequest(requestId);
- long ts = System.currentTimeMillis();
- request.add(parseValues(element.getAsJsonObject()).stream().map(kv -> new BaseAttributeKvEntry(kv, ts)).collect(Collectors.toList()));
- return request;
- } else {
- throw new JsonSyntaxException(CAN_T_PARSE_VALUE + element);
- }
- }
-
- public static JsonObject toJson(AttributesKVMsg payload, boolean asMap) {
- JsonObject result = new JsonObject();
- if (asMap) {
- if (!payload.getClientAttributes().isEmpty()) {
- JsonObject attrObject = new JsonObject();
- payload.getClientAttributes().forEach(addToObject(attrObject));
- result.add("client", attrObject);
- }
- if (!payload.getSharedAttributes().isEmpty()) {
- JsonObject attrObject = new JsonObject();
- payload.getSharedAttributes().forEach(addToObject(attrObject));
- result.add("shared", attrObject);
- }
- } else {
- payload.getClientAttributes().forEach(addToObject(result));
- payload.getSharedAttributes().forEach(addToObject(result));
- }
- if (!payload.getDeletedAttributes().isEmpty()) {
- JsonArray attrObject = new JsonArray();
- payload.getDeletedAttributes().forEach(addToObject(attrObject));
- result.add("deleted", attrObject);
- }
- return result;
- }
-
- private static Consumer addToObject(JsonArray result) {
- return key -> {
- result.add(key.getAttributeKey());
- };
- }
-
- private static Consumer addToObject(JsonObject result) {
- return de -> {
- JsonPrimitive value;
- switch (de.getDataType()) {
- case BOOLEAN:
- value = new JsonPrimitive(de.getBooleanValue().get());
- break;
- case DOUBLE:
- value = new JsonPrimitive(de.getDoubleValue().get());
- break;
- case LONG:
- value = new JsonPrimitive(de.getLongValue().get());
- break;
- case STRING:
- value = new JsonPrimitive(de.getStrValue().get());
- break;
- default:
- throw new IllegalArgumentException("Unsupported data type: " + de.getDataType());
- }
- result.add(de.getKey(), value);
- };
- }
-
- public static JsonObject toJson(ToDeviceRpcRequestMsg msg, boolean includeRequestId) {
- JsonObject result = new JsonObject();
- if (includeRequestId) {
- result.addProperty("id", msg.getRequestId());
- }
- result.addProperty("method", msg.getMethod());
- result.add("params", new JsonParser().parse(msg.getParams()));
- return result;
- }
-
- public static JsonElement toJson(ToServerRpcResponseMsg msg) {
- return new JsonParser().parse(msg.getData());
- }
-
- public static JsonElement toErrorJson(String errorMsg) {
- JsonObject error = new JsonObject();
- error.addProperty("error", errorMsg);
- return error;
- }
-}
diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/session/DeviceAwareSessionContext.java b/common/transport/src/main/java/org/thingsboard/server/common/transport/session/DeviceAwareSessionContext.java
deleted file mode 100644
index 2e7b412c31..0000000000
--- a/common/transport/src/main/java/org/thingsboard/server/common/transport/session/DeviceAwareSessionContext.java
+++ /dev/null
@@ -1,78 +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.transport.session;
-
-import lombok.extern.slf4j.Slf4j;
-import org.thingsboard.server.common.data.Device;
-import org.thingsboard.server.common.data.security.DeviceCredentialsFilter;
-import org.thingsboard.server.common.data.security.DeviceTokenCredentials;
-import org.thingsboard.server.common.msg.session.SessionContext;
-import org.thingsboard.server.common.transport.SessionMsgProcessor;
-import org.thingsboard.server.common.transport.auth.DeviceAuthResult;
-import org.thingsboard.server.common.transport.auth.DeviceAuthService;
-
-import java.util.Optional;
-
-/**
- * @author Andrew Shvayka
- */
-@Slf4j
-public abstract class DeviceAwareSessionContext implements SessionContext {
-
- protected final DeviceAuthService authService;
- protected final SessionMsgProcessor processor;
-
- protected volatile Device device;
-
- public DeviceAwareSessionContext(SessionMsgProcessor processor, DeviceAuthService authService) {
- this.processor = processor;
- this.authService = authService;
- }
-
- public DeviceAwareSessionContext(SessionMsgProcessor processor, DeviceAuthService authService, Device device) {
- this(processor, authService);
- this.device = device;
- }
-
-
- public boolean login(DeviceCredentialsFilter credentials) {
- DeviceAuthResult result = authService.process(credentials);
- if (result.isSuccess()) {
- Optional deviceOpt = authService.findDeviceById(result.getDeviceId());
- if (deviceOpt.isPresent()) {
- device = deviceOpt.get();
- }
- return true;
- } else {
- log.debug("Can't find device using credentials [{}] due to {}", credentials, result.getErrorMsg());
- return false;
- }
- }
-
- public DeviceAuthService getAuthService() {
- return authService;
- }
-
- public SessionMsgProcessor getProcessor() {
- return processor;
- }
-
- public Device getDevice() {
- return device;
- }
-
-
-}
diff --git a/common/transport/transport-api/pom.xml b/common/transport/transport-api/pom.xml
new file mode 100644
index 0000000000..3538e468d4
--- /dev/null
+++ b/common/transport/transport-api/pom.xml
@@ -0,0 +1,113 @@
+
+
+ 4.0.0
+
+ org.thingsboard.common
+ 2.2.0-SNAPSHOT
+ transport
+
+ org.thingsboard.common.transport
+ transport-api
+ jar
+
+ Thingsboard Server Common Transport components
+ https://thingsboard.io
+
+
+ UTF-8
+ ${basedir}/../../..
+
+
+
+
+ org.thingsboard.common
+ data
+
+
+ org.thingsboard.common
+ message
+
+
+ org.thingsboard.common
+ queue
+
+
+ com.google.code.gson
+ gson
+
+
+ org.slf4j
+ slf4j-api
+
+
+ org.slf4j
+ log4j-over-slf4j
+
+
+ ch.qos.logback
+ logback-core
+
+
+ ch.qos.logback
+ logback-classic
+
+
+ junit
+ junit
+ test
+
+
+ org.mockito
+ mockito-all
+ test
+
+
+ org.springframework
+ spring-context
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+ provided
+
+
+ com.google.guava
+ guava
+
+
+ org.apache.commons
+ commons-lang3
+
+
+ com.google.protobuf
+ protobuf-java
+
+
+
+
+
+
+ org.xolstice.maven.plugins
+ protobuf-maven-plugin
+
+
+
+
+
diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/SessionMsgListener.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/SessionMsgListener.java
new file mode 100644
index 0000000000..09d1bcb08f
--- /dev/null
+++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/SessionMsgListener.java
@@ -0,0 +1,38 @@
+/**
+ * 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.transport;
+
+import org.thingsboard.server.gen.transport.TransportProtos.ToServerRpcResponseMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeResponseMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.SessionCloseNotificationProto;
+import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcRequestMsg;
+
+/**
+ * Created by ashvayka on 04.10.18.
+ */
+public interface SessionMsgListener {
+
+ void onGetAttributesResponse(GetAttributeResponseMsg getAttributesResponse);
+
+ void onAttributeUpdate(AttributeUpdateNotificationMsg attributeUpdateNotification);
+
+ void onRemoteSessionCloseCommand(SessionCloseNotificationProto sessionCloseNotification);
+
+ void onToDeviceRpcRequest(ToDeviceRpcRequestMsg toDeviceRequest);
+
+ void onToServerRpcResponse(ToServerRpcResponseMsg toServerResponse);
+}
diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/SessionMsgProcessor.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/SessionMsgProcessor.java
similarity index 88%
rename from common/transport/src/main/java/org/thingsboard/server/common/transport/SessionMsgProcessor.java
rename to common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/SessionMsgProcessor.java
index 4cff643a5a..992492f7d6 100644
--- a/common/transport/src/main/java/org/thingsboard/server/common/transport/SessionMsgProcessor.java
+++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/SessionMsgProcessor.java
@@ -16,12 +16,9 @@
package org.thingsboard.server.common.transport;
import org.thingsboard.server.common.data.Device;
-import org.thingsboard.server.common.msg.aware.SessionAwareMsg;
public interface SessionMsgProcessor {
- void process(SessionAwareMsg msg);
-
void onDeviceAdded(Device device);
}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/SessionId.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportAdaptor.java
similarity index 77%
rename from common/data/src/main/java/org/thingsboard/server/common/data/id/SessionId.java
rename to common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportAdaptor.java
index 1b4772768f..b59d49362b 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/id/SessionId.java
+++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportAdaptor.java
@@ -13,12 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.thingsboard.server.common.data.id;
+package org.thingsboard.server.common.transport;
-import java.io.Serializable;
+import org.thingsboard.server.common.msg.session.SessionContext;
-public interface SessionId extends Serializable {
-
- String toUidStr();
+public interface TransportAdaptor {
}
diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportContext.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportContext.java
new file mode 100644
index 0000000000..ab429829e4
--- /dev/null
+++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportContext.java
@@ -0,0 +1,68 @@
+/**
+ * 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.transport;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import lombok.Data;
+import lombok.Getter;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.thingsboard.server.common.transport.quota.host.HostRequestsQuotaService;
+import org.thingsboard.server.kafka.TbNodeIdProvider;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+/**
+ * Created by ashvayka on 15.10.18.
+ */
+@Slf4j
+@Data
+public class TransportContext {
+
+ protected final ObjectMapper mapper = new ObjectMapper();
+
+ @Autowired
+ private TransportService transportService;
+
+ @Autowired
+ private TbNodeIdProvider nodeIdProvider;
+
+ @Autowired(required = false)
+ private HostRequestsQuotaService quotaService;
+
+ @Getter
+ private ExecutorService executor;
+
+ @PostConstruct
+ public void init() {
+ executor = Executors.newCachedThreadPool();
+ }
+
+ @PreDestroy
+ public void stop() {
+ if (executor != null) {
+ executor.shutdownNow();
+ }
+ }
+
+ public String getNodeId() {
+ return nodeIdProvider.getNodeId();
+ }
+
+}
diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportService.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportService.java
new file mode 100644
index 0000000000..caf178a641
--- /dev/null
+++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportService.java
@@ -0,0 +1,68 @@
+/**
+ * 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.transport;
+
+import org.thingsboard.server.gen.transport.TransportProtos;
+import org.thingsboard.server.gen.transport.TransportProtos.ToServerRpcRequestMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcResponseMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.SubscribeToAttributeUpdatesMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.SubscribeToRPCMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto;
+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.ValidateDeviceCredentialsResponseMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceTokenRequestMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceX509CertRequestMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeRequestMsg;
+
+/**
+ * Created by ashvayka on 04.10.18.
+ */
+public interface TransportService {
+
+ void process(ValidateDeviceTokenRequestMsg msg,
+ TransportServiceCallback callback);
+
+ void process(ValidateDeviceX509CertRequestMsg msg,
+ TransportServiceCallback callback);
+
+ void process(TransportProtos.GetOrCreateDeviceFromGatewayRequestMsg msg,
+ TransportServiceCallback callback);
+
+ void process(SessionInfoProto sessionInfo, SessionEventMsg msg, TransportServiceCallback callback);
+
+ void process(SessionInfoProto sessionInfo, PostTelemetryMsg msg, TransportServiceCallback callback);
+
+ void process(SessionInfoProto sessionInfo, PostAttributeMsg msg, TransportServiceCallback callback);
+
+ void process(SessionInfoProto sessionInfo, GetAttributeRequestMsg msg, TransportServiceCallback callback);
+
+ void process(SessionInfoProto sessionInfo, SubscribeToAttributeUpdatesMsg msg, TransportServiceCallback callback);
+
+ void process(SessionInfoProto sessionInfo, SubscribeToRPCMsg msg, TransportServiceCallback callback);
+
+ void process(SessionInfoProto sessionInfo, ToDeviceRpcResponseMsg msg, TransportServiceCallback callback);
+
+ void process(SessionInfoProto sessionInfo, ToServerRpcRequestMsg msg, TransportServiceCallback callback);
+
+ void registerAsyncSession(SessionInfoProto sessionInfo, SessionMsgListener listener);
+
+ void registerSyncSession(SessionInfoProto sessionInfo, SessionMsgListener listener, long timeout);
+
+ void deregisterSession(SessionInfoProto sessionInfo);
+
+}
diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/session/FromDeviceRequestMsg.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportServiceCallback.java
similarity index 76%
rename from common/message/src/main/java/org/thingsboard/server/common/msg/session/FromDeviceRequestMsg.java
rename to common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportServiceCallback.java
index 1ebeb671a6..5443f7ed29 100644
--- a/common/message/src/main/java/org/thingsboard/server/common/msg/session/FromDeviceRequestMsg.java
+++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportServiceCallback.java
@@ -13,13 +13,14 @@
* 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.common.transport;
/**
- * @author Andrew Shvayka
+ * Created by ashvayka on 04.10.18.
*/
-public interface FromDeviceRequestMsg extends FromDeviceMsg {
+public interface TransportServiceCallback {
- Integer getRequestId();
+ void onSuccess(T msg);
+ void onError(Throwable e);
}
diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/adaptor/AdaptorException.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/adaptor/AdaptorException.java
similarity index 100%
rename from common/transport/src/main/java/org/thingsboard/server/common/transport/adaptor/AdaptorException.java
rename to common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/adaptor/AdaptorException.java
diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java
new file mode 100644
index 0000000000..7498e8ee2a
--- /dev/null
+++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java
@@ -0,0 +1,430 @@
+/**
+ * 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.transport.adaptor;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import com.google.gson.JsonPrimitive;
+import com.google.gson.JsonSyntaxException;
+import org.springframework.util.StringUtils;
+import org.thingsboard.server.common.data.kv.AttributeKey;
+import org.thingsboard.server.common.data.kv.AttributeKvEntry;
+import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry;
+import org.thingsboard.server.common.data.kv.BooleanDataEntry;
+import org.thingsboard.server.common.data.kv.DoubleDataEntry;
+import org.thingsboard.server.common.data.kv.KvEntry;
+import org.thingsboard.server.common.data.kv.LongDataEntry;
+import org.thingsboard.server.common.data.kv.StringDataEntry;
+import org.thingsboard.server.common.msg.kv.AttributesKVMsg;
+import org.thingsboard.server.gen.transport.TransportProtos;
+import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg;
+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.TsKvListProto;
+import org.thingsboard.server.gen.transport.TransportProtos.TsKvProto;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+
+public class JsonConverter {
+
+ private static final Gson GSON = new Gson();
+ private static final String CAN_T_PARSE_VALUE = "Can't parse value: ";
+ private static final String DEVICE_PROPERTY = "device";
+
+ public static PostTelemetryMsg convertToTelemetryProto(JsonElement jsonObject) throws JsonSyntaxException {
+ long systemTs = System.currentTimeMillis();
+ PostTelemetryMsg.Builder builder = PostTelemetryMsg.newBuilder();
+ if (jsonObject.isJsonObject()) {
+ parseObject(builder, systemTs, jsonObject);
+ } else if (jsonObject.isJsonArray()) {
+ jsonObject.getAsJsonArray().forEach(je -> {
+ if (je.isJsonObject()) {
+ parseObject(builder, systemTs, je.getAsJsonObject());
+ } else {
+ throw new JsonSyntaxException(CAN_T_PARSE_VALUE + je);
+ }
+ });
+ } else {
+ throw new JsonSyntaxException(CAN_T_PARSE_VALUE + jsonObject);
+ }
+ return builder.build();
+ }
+
+ public static PostAttributeMsg convertToAttributesProto(JsonElement jsonObject) throws JsonSyntaxException {
+ if (jsonObject.isJsonObject()) {
+ PostAttributeMsg.Builder result = PostAttributeMsg.newBuilder();
+ List keyValueList = parseProtoValues(jsonObject.getAsJsonObject());
+ result.addAllKv(keyValueList);
+ return result.build();
+ } else {
+ throw new JsonSyntaxException(CAN_T_PARSE_VALUE + jsonObject);
+ }
+ }
+
+ public static JsonElement toJson(TransportProtos.ToDeviceRpcRequestMsg msg, boolean includeRequestId) {
+ JsonObject result = new JsonObject();
+ if (includeRequestId) {
+ result.addProperty("id", msg.getRequestId());
+ }
+ result.addProperty("method", msg.getMethodName());
+ result.add("params", new JsonParser().parse(msg.getParams()));
+ return result;
+ }
+
+ private static void parseObject(PostTelemetryMsg.Builder builder, long systemTs, JsonElement jsonObject) {
+ JsonObject jo = jsonObject.getAsJsonObject();
+ if (jo.has("ts") && jo.has("values")) {
+ parseWithTs(builder, jo);
+ } else {
+ parseWithoutTs(builder, systemTs, jo);
+ }
+ }
+
+ private static void parseWithoutTs(PostTelemetryMsg.Builder request, long systemTs, JsonObject jo) {
+ TsKvListProto.Builder builder = TsKvListProto.newBuilder();
+ builder.setTs(systemTs);
+ builder.addAllKv(parseProtoValues(jo));
+ request.addTsKvList(builder.build());
+ }
+
+ private static void parseWithTs(PostTelemetryMsg.Builder request, JsonObject jo) {
+ TsKvListProto.Builder builder = TsKvListProto.newBuilder();
+ builder.setTs(jo.get("ts").getAsLong());
+ builder.addAllKv(parseProtoValues(jo.get("values").getAsJsonObject()));
+ request.addTsKvList(builder.build());
+ }
+
+ private static List parseProtoValues(JsonObject valuesObject) {
+ List result = new ArrayList<>();
+ for (Entry valueEntry : valuesObject.entrySet()) {
+ JsonElement element = valueEntry.getValue();
+ if (element.isJsonPrimitive()) {
+ JsonPrimitive value = element.getAsJsonPrimitive();
+ if (value.isString()) {
+ result.add(KeyValueProto.newBuilder().setKey(valueEntry.getKey()).setType(KeyValueType.STRING_V)
+ .setStringV(value.getAsString()).build());
+ } else if (value.isBoolean()) {
+ result.add(KeyValueProto.newBuilder().setKey(valueEntry.getKey()).setType(KeyValueType.BOOLEAN_V)
+ .setBoolV(value.getAsBoolean()).build());
+ } else if (value.isNumber()) {
+ if (value.getAsString().contains(".")) {
+ result.add(KeyValueProto.newBuilder().setKey(valueEntry.getKey()).setType(KeyValueType.DOUBLE_V)
+ .setDoubleV(value.getAsDouble()).build());
+ } else {
+ try {
+ long longValue = Long.parseLong(value.getAsString());
+ result.add(KeyValueProto.newBuilder().setKey(valueEntry.getKey()).setType(KeyValueType.LONG_V)
+ .setLongV(longValue).build());
+ } catch (NumberFormatException e) {
+ throw new JsonSyntaxException("Big integer values are not supported!");
+ }
+ }
+ } else {
+ throw new JsonSyntaxException(CAN_T_PARSE_VALUE + value);
+ }
+ } else {
+ throw new JsonSyntaxException(CAN_T_PARSE_VALUE + element);
+ }
+ }
+ return result;
+ }
+
+ public static TransportProtos.ToServerRpcRequestMsg convertToServerRpcRequest(JsonElement json, int requestId) throws JsonSyntaxException {
+ JsonObject object = json.getAsJsonObject();
+ return TransportProtos.ToServerRpcRequestMsg.newBuilder().setRequestId(requestId).setMethodName(object.get("method").getAsString()).setParams(GSON.toJson(object.get("params"))).build();
+ }
+
+ private static void parseNumericValue(List result, Entry valueEntry, JsonPrimitive value) {
+ if (value.getAsString().contains(".")) {
+ result.add(new DoubleDataEntry(valueEntry.getKey(), value.getAsDouble()));
+ } else {
+ try {
+ long longValue = Long.parseLong(value.getAsString());
+ result.add(new LongDataEntry(valueEntry.getKey(), longValue));
+ } catch (NumberFormatException e) {
+ throw new JsonSyntaxException("Big integer values are not supported!");
+ }
+ }
+ }
+
+ public static JsonObject toJson(GetAttributeResponseMsg payload) {
+ JsonObject result = new JsonObject();
+ if (payload.getClientAttributeListCount() > 0) {
+ JsonObject attrObject = new JsonObject();
+ payload.getClientAttributeListList().forEach(addToObjectFromProto(attrObject));
+ result.add("client", attrObject);
+ }
+ if (payload.getSharedAttributeListCount() > 0) {
+ JsonObject attrObject = new JsonObject();
+ payload.getSharedAttributeListList().forEach(addToObjectFromProto(attrObject));
+ result.add("shared", attrObject);
+ }
+ if (payload.getDeletedAttributeKeysCount() > 0) {
+ JsonArray attrObject = new JsonArray();
+ payload.getDeletedAttributeKeysList().forEach(attrObject::add);
+ result.add("deleted", attrObject);
+ }
+ return result;
+ }
+
+ public static JsonElement toJson(AttributeUpdateNotificationMsg payload) {
+ JsonObject result = new JsonObject();
+ if (payload.getSharedUpdatedCount() > 0) {
+ payload.getSharedUpdatedList().forEach(addToObjectFromProto(result));
+ }
+ if (payload.getSharedDeletedCount() > 0) {
+ JsonArray attrObject = new JsonArray();
+ payload.getSharedDeletedList().forEach(attrObject::add);
+ result.add("deleted", attrObject);
+ }
+ return result;
+ }
+
+ public static JsonObject toJson(AttributesKVMsg payload, boolean asMap) {
+ JsonObject result = new JsonObject();
+ if (asMap) {
+ if (!payload.getClientAttributes().isEmpty()) {
+ JsonObject attrObject = new JsonObject();
+ payload.getClientAttributes().forEach(addToObject(attrObject));
+ result.add("client", attrObject);
+ }
+ if (!payload.getSharedAttributes().isEmpty()) {
+ JsonObject attrObject = new JsonObject();
+ payload.getSharedAttributes().forEach(addToObject(attrObject));
+ result.add("shared", attrObject);
+ }
+ } else {
+ payload.getClientAttributes().forEach(addToObject(result));
+ payload.getSharedAttributes().forEach(addToObject(result));
+ }
+ if (!payload.getDeletedAttributes().isEmpty()) {
+ JsonArray attrObject = new JsonArray();
+ payload.getDeletedAttributes().forEach(addToObject(attrObject));
+ result.add("deleted", attrObject);
+ }
+ return result;
+ }
+
+ public static JsonObject getJsonObjectForGateway(TransportProtos.GetAttributeResponseMsg responseMsg) {
+ JsonObject result = new JsonObject();
+ result.addProperty("id", responseMsg.getRequestId());
+ if (responseMsg.getClientAttributeListCount() > 0) {
+ addValues(result, responseMsg.getClientAttributeListList());
+ }
+ if (responseMsg.getSharedAttributeListCount() > 0) {
+ addValues(result, responseMsg.getSharedAttributeListList());
+ }
+ return result;
+ }
+
+ public static JsonObject getJsonObjectForGateway(String deviceName, AttributeUpdateNotificationMsg notificationMsg) {
+ JsonObject result = new JsonObject();
+ result.addProperty(DEVICE_PROPERTY, deviceName);
+ result.add("data", toJson(notificationMsg));
+ return result;
+ }
+
+ private static void addValues(JsonObject result, List kvList) {
+ if (kvList.size() == 1) {
+ addValueToJson(result, "value", kvList.get(0).getKv());
+ } else {
+ JsonObject values;
+ if (result.has("values")) {
+ values = result.get("values").getAsJsonObject();
+ } else {
+ values = new JsonObject();
+ result.add("values", values);
+ }
+ kvList.forEach(value -> addValueToJson(values, value.getKv().getKey(), value.getKv()));
+ }
+ }
+
+ private static void addValueToJson(JsonObject json, String name, TransportProtos.KeyValueProto entry) {
+ switch (entry.getType()) {
+ case BOOLEAN_V:
+ json.addProperty(name, entry.getBoolV());
+ break;
+ case STRING_V:
+ json.addProperty(name, entry.getStringV());
+ break;
+ case DOUBLE_V:
+ json.addProperty(name, entry.getDoubleV());
+ break;
+ case LONG_V:
+ json.addProperty(name, entry.getLongV());
+ break;
+ }
+ }
+
+ private static Consumer addToObject(JsonArray result) {
+ return key -> result.add(key.getAttributeKey());
+ }
+
+ private static Consumer addToObjectFromProto(JsonObject result) {
+ return de -> {
+ JsonPrimitive value;
+ switch (de.getKv().getType()) {
+ case BOOLEAN_V:
+ value = new JsonPrimitive(de.getKv().getBoolV());
+ break;
+ case DOUBLE_V:
+ value = new JsonPrimitive(de.getKv().getDoubleV());
+ break;
+ case LONG_V:
+ value = new JsonPrimitive(de.getKv().getLongV());
+ break;
+ case STRING_V:
+ value = new JsonPrimitive(de.getKv().getStringV());
+ break;
+ default:
+ throw new IllegalArgumentException("Unsupported data type: " + de.getKv().getType());
+ }
+ result.add(de.getKv().getKey(), value);
+ };
+ }
+
+ private static Consumer addToObject(JsonObject result) {
+ return de -> {
+ JsonPrimitive value;
+ switch (de.getDataType()) {
+ case BOOLEAN:
+ value = new JsonPrimitive(de.getBooleanValue().get());
+ break;
+ case DOUBLE:
+ value = new JsonPrimitive(de.getDoubleValue().get());
+ break;
+ case LONG:
+ value = new JsonPrimitive(de.getLongValue().get());
+ break;
+ case STRING:
+ value = new JsonPrimitive(de.getStrValue().get());
+ break;
+ default:
+ throw new IllegalArgumentException("Unsupported data type: " + de.getDataType());
+ }
+ result.add(de.getKey(), value);
+ };
+ }
+
+ public static JsonElement toJson(TransportProtos.ToServerRpcResponseMsg msg) {
+ if (StringUtils.isEmpty(msg.getError())) {
+ return new JsonParser().parse(msg.getPayload());
+ } else {
+ JsonObject errorMsg = new JsonObject();
+ errorMsg.addProperty("error", msg.getError());
+ return errorMsg;
+ }
+ }
+
+ public static JsonElement toErrorJson(String errorMsg) {
+ JsonObject error = new JsonObject();
+ error.addProperty("error", errorMsg);
+ return error;
+ }
+
+ public static JsonElement toGatewayJson(String deviceName, TransportProtos.ToDeviceRpcRequestMsg rpcRequest) {
+ JsonObject result = new JsonObject();
+ result.addProperty(DEVICE_PROPERTY, deviceName);
+ result.add("data", JsonConverter.toJson(rpcRequest, true));
+ return result;
+ }
+
+ public static Set convertToAttributes(JsonElement element) {
+ Set result = new HashSet<>();
+ long ts = System.currentTimeMillis();
+ result.addAll(parseValues(element.getAsJsonObject()).stream().map(kv -> new BaseAttributeKvEntry(kv, ts)).collect(Collectors.toList()));
+ return result;
+ }
+
+ private static List parseValues(JsonObject valuesObject) {
+ List result = new ArrayList<>();
+ for (Entry valueEntry : valuesObject.entrySet()) {
+ JsonElement element = valueEntry.getValue();
+ if (element.isJsonPrimitive()) {
+ JsonPrimitive value = element.getAsJsonPrimitive();
+ if (value.isString()) {
+ result.add(new StringDataEntry(valueEntry.getKey(), value.getAsString()));
+ } else if (value.isBoolean()) {
+ result.add(new BooleanDataEntry(valueEntry.getKey(), value.getAsBoolean()));
+ } else if (value.isNumber()) {
+ parseNumericValue(result, valueEntry, value);
+ } else {
+ throw new JsonSyntaxException(CAN_T_PARSE_VALUE + value);
+ }
+ } else {
+ throw new JsonSyntaxException(CAN_T_PARSE_VALUE + element);
+ }
+ }
+ return result;
+ }
+
+ public static Map> convertToTelemetry(JsonElement jsonObject, long systemTs) throws JsonSyntaxException {
+ Map> result = new HashMap<>();
+ if (jsonObject.isJsonObject()) {
+ parseObject(result, systemTs, jsonObject);
+ } else if (jsonObject.isJsonArray()) {
+ jsonObject.getAsJsonArray().forEach(je -> {
+ if (je.isJsonObject()) {
+ parseObject(result, systemTs, je.getAsJsonObject());
+ } else {
+ throw new JsonSyntaxException(CAN_T_PARSE_VALUE + je);
+ }
+ });
+ } else {
+ throw new JsonSyntaxException(CAN_T_PARSE_VALUE + jsonObject);
+ }
+ return result;
+ }
+
+ private static void parseObject(Map> result, long systemTs, JsonElement jsonObject) {
+ JsonObject jo = jsonObject.getAsJsonObject();
+ if (jo.has("ts") && jo.has("values")) {
+ parseWithTs(result, jo);
+ } else {
+ parseWithoutTs(result, systemTs, jo);
+ }
+ }
+
+ private static void parseWithoutTs(Map> result, long systemTs, JsonObject jo) {
+ for (KvEntry entry : parseValues(jo)) {
+ result.computeIfAbsent(systemTs, tmp -> new ArrayList<>()).add(entry);
+ }
+ }
+
+ public static void parseWithTs(Map> result, JsonObject jo) {
+ long ts = jo.get("ts").getAsLong();
+ JsonObject valuesObject = jo.get("values").getAsJsonObject();
+ for (KvEntry entry : parseValues(valuesObject)) {
+ result.computeIfAbsent(ts, tmp -> new ArrayList<>()).add(entry);
+ }
+ }
+
+
+}
diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/auth/DeviceAuthResult.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/auth/DeviceAuthResult.java
similarity index 100%
rename from common/transport/src/main/java/org/thingsboard/server/common/transport/auth/DeviceAuthResult.java
rename to common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/auth/DeviceAuthResult.java
diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/auth/DeviceAuthService.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/auth/DeviceAuthService.java
similarity index 100%
rename from common/transport/src/main/java/org/thingsboard/server/common/transport/auth/DeviceAuthService.java
rename to common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/auth/DeviceAuthService.java
diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/AbstractQuotaService.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/AbstractQuotaService.java
similarity index 100%
rename from common/transport/src/main/java/org/thingsboard/server/common/transport/quota/AbstractQuotaService.java
rename to common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/AbstractQuotaService.java
diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/Clock.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/Clock.java
similarity index 100%
rename from common/transport/src/main/java/org/thingsboard/server/common/transport/quota/Clock.java
rename to common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/Clock.java
diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/QuotaService.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/QuotaService.java
similarity index 100%
rename from common/transport/src/main/java/org/thingsboard/server/common/transport/quota/QuotaService.java
rename to common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/QuotaService.java
diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/RequestLimitPolicy.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/RequestLimitPolicy.java
similarity index 100%
rename from common/transport/src/main/java/org/thingsboard/server/common/transport/quota/RequestLimitPolicy.java
rename to common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/RequestLimitPolicy.java
diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/host/HostIntervalRegistryCleaner.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/host/HostIntervalRegistryCleaner.java
similarity index 100%
rename from common/transport/src/main/java/org/thingsboard/server/common/transport/quota/host/HostIntervalRegistryCleaner.java
rename to common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/host/HostIntervalRegistryCleaner.java
diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/host/HostIntervalRegistryLogger.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/host/HostIntervalRegistryLogger.java
similarity index 100%
rename from common/transport/src/main/java/org/thingsboard/server/common/transport/quota/host/HostIntervalRegistryLogger.java
rename to common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/host/HostIntervalRegistryLogger.java
diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/host/HostRequestIntervalRegistry.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/host/HostRequestIntervalRegistry.java
similarity index 100%
rename from common/transport/src/main/java/org/thingsboard/server/common/transport/quota/host/HostRequestIntervalRegistry.java
rename to common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/host/HostRequestIntervalRegistry.java
diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/host/HostRequestLimitPolicy.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/host/HostRequestLimitPolicy.java
similarity index 100%
rename from common/transport/src/main/java/org/thingsboard/server/common/transport/quota/host/HostRequestLimitPolicy.java
rename to common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/host/HostRequestLimitPolicy.java
diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/host/HostRequestsQuotaService.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/host/HostRequestsQuotaService.java
similarity index 100%
rename from common/transport/src/main/java/org/thingsboard/server/common/transport/quota/host/HostRequestsQuotaService.java
rename to common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/host/HostRequestsQuotaService.java
diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/inmemory/IntervalCount.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/inmemory/IntervalCount.java
similarity index 100%
rename from common/transport/src/main/java/org/thingsboard/server/common/transport/quota/inmemory/IntervalCount.java
rename to common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/inmemory/IntervalCount.java
diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/inmemory/IntervalRegistryCleaner.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/inmemory/IntervalRegistryCleaner.java
similarity index 100%
rename from common/transport/src/main/java/org/thingsboard/server/common/transport/quota/inmemory/IntervalRegistryCleaner.java
rename to common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/inmemory/IntervalRegistryCleaner.java
diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/inmemory/IntervalRegistryLogger.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/inmemory/IntervalRegistryLogger.java
similarity index 100%
rename from common/transport/src/main/java/org/thingsboard/server/common/transport/quota/inmemory/IntervalRegistryLogger.java
rename to common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/inmemory/IntervalRegistryLogger.java
diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/inmemory/KeyBasedIntervalRegistry.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/inmemory/KeyBasedIntervalRegistry.java
similarity index 100%
rename from common/transport/src/main/java/org/thingsboard/server/common/transport/quota/inmemory/KeyBasedIntervalRegistry.java
rename to common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/inmemory/KeyBasedIntervalRegistry.java
diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantIntervalRegistryCleaner.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantIntervalRegistryCleaner.java
similarity index 86%
rename from common/transport/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantIntervalRegistryCleaner.java
rename to common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantIntervalRegistryCleaner.java
index c48117069a..47b427840a 100644
--- a/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantIntervalRegistryCleaner.java
+++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantIntervalRegistryCleaner.java
@@ -16,10 +16,12 @@
package org.thingsboard.server.common.transport.quota.tenant;
import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.transport.quota.inmemory.IntervalRegistryCleaner;
@Component
+@ConditionalOnProperty(prefix = "quota.rule.tenant", value = "enabled", havingValue = "true", matchIfMissing = false)
public class TenantIntervalRegistryCleaner extends IntervalRegistryCleaner {
public TenantIntervalRegistryCleaner(TenantMsgsIntervalRegistry intervalRegistry,
diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantIntervalRegistryLogger.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantIntervalRegistryLogger.java
similarity index 91%
rename from common/transport/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantIntervalRegistryLogger.java
rename to common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantIntervalRegistryLogger.java
index c56f457423..3bdcf93d07 100644
--- a/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantIntervalRegistryLogger.java
+++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantIntervalRegistryLogger.java
@@ -17,6 +17,7 @@ package org.thingsboard.server.common.transport.quota.tenant;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.transport.quota.inmemory.IntervalRegistryLogger;
@@ -25,6 +26,7 @@ import java.util.concurrent.TimeUnit;
@Slf4j
@Component
+@ConditionalOnProperty(prefix = "quota.rule.tenant", value = "enabled", havingValue = "true", matchIfMissing = false)
public class TenantIntervalRegistryLogger extends IntervalRegistryLogger {
private final long logIntervalMin;
diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantMsgsIntervalRegistry.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantMsgsIntervalRegistry.java
similarity index 87%
rename from common/transport/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantMsgsIntervalRegistry.java
rename to common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantMsgsIntervalRegistry.java
index 6e8402c701..3402e62472 100644
--- a/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantMsgsIntervalRegistry.java
+++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantMsgsIntervalRegistry.java
@@ -16,10 +16,12 @@
package org.thingsboard.server.common.transport.quota.tenant;
import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.transport.quota.inmemory.KeyBasedIntervalRegistry;
@Component
+@ConditionalOnProperty(prefix = "quota.rule.tenant", value = "enabled", havingValue = "true", matchIfMissing = false)
public class TenantMsgsIntervalRegistry extends KeyBasedIntervalRegistry {
public TenantMsgsIntervalRegistry(@Value("${quota.rule.tenant.intervalMs}") long intervalDurationMs,
diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantQuotaService.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantQuotaService.java
similarity index 87%
rename from common/transport/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantQuotaService.java
rename to common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantQuotaService.java
index a68860a2e0..875a666a28 100644
--- a/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantQuotaService.java
+++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantQuotaService.java
@@ -16,10 +16,12 @@
package org.thingsboard.server.common.transport.quota.tenant;
import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.transport.quota.AbstractQuotaService;
@Component
+@ConditionalOnProperty(prefix = "quota.rule.tenant", value = "enabled", havingValue = "true", matchIfMissing = false)
public class TenantQuotaService extends AbstractQuotaService {
public TenantQuotaService(TenantMsgsIntervalRegistry requestRegistry, TenantRequestLimitPolicy requestsPolicy,
diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantRequestLimitPolicy.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantRequestLimitPolicy.java
similarity index 84%
rename from common/transport/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantRequestLimitPolicy.java
rename to common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantRequestLimitPolicy.java
index cc32c81ad7..c5dfc40480 100644
--- a/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantRequestLimitPolicy.java
+++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantRequestLimitPolicy.java
@@ -16,10 +16,12 @@
package org.thingsboard.server.common.transport.quota.tenant;
import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.transport.quota.RequestLimitPolicy;
@Component
+@ConditionalOnProperty(prefix = "quota.rule.tenant", value = "enabled", havingValue = "true", matchIfMissing = false)
public class TenantRequestLimitPolicy extends RequestLimitPolicy {
public TenantRequestLimitPolicy(@Value("${quota.rule.tenant.limit}") long limit) {
diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/AbstractTransportService.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/AbstractTransportService.java
new file mode 100644
index 0000000000..e299a027e8
--- /dev/null
+++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/AbstractTransportService.java
@@ -0,0 +1,116 @@
+/**
+ * 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.transport.service;
+
+import lombok.extern.slf4j.Slf4j;
+import org.thingsboard.server.common.transport.SessionMsgListener;
+import org.thingsboard.server.common.transport.TransportService;
+import org.thingsboard.server.gen.transport.TransportProtos;
+
+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;
+
+/**
+ * Created by ashvayka on 17.10.18.
+ */
+@Slf4j
+public abstract class AbstractTransportService implements TransportService {
+
+ protected ScheduledExecutorService schedulerExecutor;
+ protected ExecutorService transportCallbackExecutor;
+ protected ConcurrentMap sessions = new ConcurrentHashMap<>();
+
+ @Override
+ public void registerAsyncSession(TransportProtos.SessionInfoProto sessionInfo, SessionMsgListener listener) {
+ sessions.putIfAbsent(toId(sessionInfo), new SessionMetaData(sessionInfo, TransportProtos.SessionType.ASYNC, listener));
+ //TODO: monitor sessions periodically: PING REQ/RESP, etc.
+ }
+
+ @Override
+ public void registerSyncSession(TransportProtos.SessionInfoProto sessionInfo, SessionMsgListener listener, long timeout) {
+ sessions.putIfAbsent(toId(sessionInfo), new SessionMetaData(sessionInfo, TransportProtos.SessionType.SYNC, listener));
+ schedulerExecutor.schedule(() -> {
+ listener.onRemoteSessionCloseCommand(TransportProtos.SessionCloseNotificationProto.getDefaultInstance());
+ deregisterSession(sessionInfo);
+ }, timeout, TimeUnit.MILLISECONDS);
+
+ }
+
+ @Override
+ public void deregisterSession(TransportProtos.SessionInfoProto sessionInfo) {
+ sessions.remove(toId(sessionInfo));
+ }
+
+ protected void processToTransportMsg(TransportProtos.DeviceActorToTransportMsg toSessionMsg) {
+ UUID sessionId = new UUID(toSessionMsg.getSessionIdMSB(), toSessionMsg.getSessionIdLSB());
+ SessionMetaData md = sessions.get(sessionId);
+ if (md != null) {
+ SessionMsgListener listener = md.getListener();
+ transportCallbackExecutor.submit(() -> {
+ if (toSessionMsg.hasGetAttributesResponse()) {
+ listener.onGetAttributesResponse(toSessionMsg.getGetAttributesResponse());
+ }
+ if (toSessionMsg.hasAttributeUpdateNotification()) {
+ listener.onAttributeUpdate(toSessionMsg.getAttributeUpdateNotification());
+ }
+ if (toSessionMsg.hasSessionCloseNotification()) {
+ listener.onRemoteSessionCloseCommand(toSessionMsg.getSessionCloseNotification());
+ }
+ if (toSessionMsg.hasToDeviceRequest()) {
+ listener.onToDeviceRpcRequest(toSessionMsg.getToDeviceRequest());
+ }
+ if (toSessionMsg.hasToServerResponse()) {
+ listener.onToServerRpcResponse(toSessionMsg.getToServerResponse());
+ }
+ });
+ if (md.getSessionType() == TransportProtos.SessionType.SYNC) {
+ deregisterSession(md.getSessionInfo());
+ }
+ } else {
+ //TODO: should we notify the device actor about missed session?
+ log.debug("[{}] Missing session.", sessionId);
+ }
+ }
+
+ protected UUID toId(TransportProtos.SessionInfoProto sessionInfo) {
+ return new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB());
+ }
+
+ String getRoutingKey(TransportProtos.SessionInfoProto sessionInfo) {
+ return new UUID(sessionInfo.getDeviceIdMSB(), sessionInfo.getDeviceIdLSB()).toString();
+ }
+
+ public void init() {
+ this.schedulerExecutor = Executors.newSingleThreadScheduledExecutor();
+ this.transportCallbackExecutor = new ThreadPoolExecutor(0, 20, 60L, TimeUnit.SECONDS, new SynchronousQueue<>());
+ }
+
+ public void destroy() {
+ if (schedulerExecutor != null) {
+ schedulerExecutor.shutdownNow();
+ }
+ if (transportCallbackExecutor != null) {
+ transportCallbackExecutor.shutdownNow();
+ }
+ }
+}
diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/RemoteTransportService.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/RemoteTransportService.java
new file mode 100644
index 0000000000..aa3b42b58c
--- /dev/null
+++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/RemoteTransportService.java
@@ -0,0 +1,315 @@
+/**
+ * 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.transport.service;
+
+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.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.ConditionalOnExpression;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.stereotype.Service;
+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.gen.transport.TransportProtos;
+import org.thingsboard.server.gen.transport.TransportProtos.*;
+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.ToTransportMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.TransportApiRequestMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.TransportApiResponseMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg;
+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.*;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
+import java.time.Duration;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+/**
+ * Created by ashvayka on 05.10.18.
+ */
+@ConditionalOnExpression("'${transport.type:null}'=='null'")
+@Service
+@Slf4j
+public class RemoteTransportService extends AbstractTransportService {
+
+ @Value("${kafka.rule_engine.topic}")
+ private String ruleEngineTopic;
+ @Value("${kafka.notifications.topic}")
+ private String notificationsTopic;
+ @Value("${kafka.notifications.poll_interval}")
+ private int notificationsPollDuration;
+ @Value("${kafka.notifications.auto_commit_interval}")
+ private int notificationsAutoCommitInterval;
+ @Value("${kafka.transport_api.requests_topic}")
+ private String transportApiRequestsTopic;
+ @Value("${kafka.transport_api.responses_topic}")
+ private String transportApiResponsesTopic;
+ @Value("${kafka.transport_api.max_pending_requests}")
+ private long maxPendingRequests;
+ @Value("${kafka.transport_api.max_requests_timeout}")
+ private long maxRequestsTimeout;
+ @Value("${kafka.transport_api.response_poll_interval}")
+ private int responsePollDuration;
+ @Value("${kafka.transport_api.response_auto_commit_interval}")
+ private int autoCommitInterval;
+
+ @Autowired
+ private TbKafkaSettings kafkaSettings;
+ //We use this to get the node id. We should replace this with a component that provides the node id.
+ @Autowired
+ private TbNodeIdProvider nodeIdProvider;
+
+ private TbKafkaRequestTemplate transportApiTemplate;
+ private TBKafkaProducerTemplate ruleEngineProducer;
+ private TBKafkaConsumerTemplate mainConsumer;
+
+ private ExecutorService mainConsumerExecutor = Executors.newSingleThreadExecutor();
+
+ private volatile boolean stopped = false;
+
+ @PostConstruct
+ public void init() {
+ super.init();
+
+ TBKafkaProducerTemplate.TBKafkaProducerTemplateBuilder requestBuilder = TBKafkaProducerTemplate.builder();
+ requestBuilder.settings(kafkaSettings);
+ requestBuilder.defaultTopic(transportApiRequestsTopic);
+ requestBuilder.encoder(new TransportApiRequestEncoder());
+
+ TBKafkaConsumerTemplate.TBKafkaConsumerTemplateBuilder responseBuilder = TBKafkaConsumerTemplate.builder();
+ responseBuilder.settings(kafkaSettings);
+ responseBuilder.topic(transportApiResponsesTopic + "." + nodeIdProvider.getNodeId());
+ responseBuilder.clientId("transport-api-client-" + nodeIdProvider.getNodeId());
+ responseBuilder.groupId("transport-api-client");
+ responseBuilder.autoCommit(true);
+ responseBuilder.autoCommitIntervalMs(autoCommitInterval);
+ responseBuilder.decoder(new TransportApiResponseDecoder());
+
+ TbKafkaRequestTemplate.TbKafkaRequestTemplateBuilder
+ builder = TbKafkaRequestTemplate.builder();
+ builder.requestTemplate(requestBuilder.build());
+ builder.responseTemplate(responseBuilder.build());
+ builder.maxPendingRequests(maxPendingRequests);
+ builder.maxRequestTimeout(maxRequestsTimeout);
+ builder.pollInterval(responsePollDuration);
+ transportApiTemplate = builder.build();
+ transportApiTemplate.init();
+
+ TBKafkaProducerTemplate.TBKafkaProducerTemplateBuilder ruleEngineProducerBuilder = TBKafkaProducerTemplate.builder();
+ ruleEngineProducerBuilder.settings(kafkaSettings);
+ ruleEngineProducerBuilder.defaultTopic(ruleEngineTopic);
+ ruleEngineProducerBuilder.encoder(new ToRuleEngineMsgEncoder());
+ ruleEngineProducer = ruleEngineProducerBuilder.build();
+ ruleEngineProducer.init();
+
+ String notificationsTopicName = notificationsTopic + "." + nodeIdProvider.getNodeId();
+
+ try {
+ TBKafkaAdmin admin = new TBKafkaAdmin(kafkaSettings);
+ CreateTopicsResult result = admin.createTopic(new NewTopic(notificationsTopicName, 1, (short) 1));
+ result.all().get();
+ } catch (Exception e) {
+ log.trace("Failed to create topic: {}", e.getMessage(), e);
+ }
+
+ TBKafkaConsumerTemplate.TBKafkaConsumerTemplateBuilder mainConsumerBuilder = TBKafkaConsumerTemplate.builder();
+ mainConsumerBuilder.settings(kafkaSettings);
+ mainConsumerBuilder.topic(notificationsTopicName);
+ mainConsumerBuilder.clientId("transport-" + nodeIdProvider.getNodeId());
+ mainConsumerBuilder.groupId("transport");
+ mainConsumerBuilder.autoCommit(true);
+ mainConsumerBuilder.autoCommitIntervalMs(notificationsAutoCommitInterval);
+ mainConsumerBuilder.decoder(new ToTransportMsgResponseDecoder());
+ mainConsumer = mainConsumerBuilder.build();
+ mainConsumer.subscribe();
+
+ mainConsumerExecutor.execute(() -> {
+ while (!stopped) {
+ try {
+ ConsumerRecords records = mainConsumer.poll(Duration.ofMillis(notificationsPollDuration));
+ records.forEach(record -> {
+ try {
+ ToTransportMsg toTransportMsg = mainConsumer.decode(record);
+ if (toTransportMsg.hasToDeviceSessionMsg()) {
+ processToTransportMsg(toTransportMsg.getToDeviceSessionMsg());
+ }
+ } 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(notificationsPollDuration);
+ } catch (InterruptedException e2) {
+ log.trace("Failed to wait until the server has capacity to handle new requests", e2);
+ }
+ }
+ }
+ });
+ }
+
+ @PreDestroy
+ public void destroy() {
+ super.destroy();
+ stopped = true;
+ if (transportApiTemplate != null) {
+ transportApiTemplate.stop();
+ }
+ if (mainConsumer != null) {
+ mainConsumer.unsubscribe();
+ }
+ if (mainConsumerExecutor != null) {
+ mainConsumerExecutor.shutdownNow();
+ }
+ }
+
+ @Override
+ public void process(ValidateDeviceTokenRequestMsg msg, TransportServiceCallback callback) {
+ AsyncCallbackTemplate.withCallback(transportApiTemplate.post(msg.getToken(),
+ TransportApiRequestMsg.newBuilder().setValidateTokenRequestMsg(msg).build()),
+ response -> callback.onSuccess(response.getValidateTokenResponseMsg()), callback::onError, transportCallbackExecutor);
+ }
+
+ @Override
+ public void process(ValidateDeviceX509CertRequestMsg msg, TransportServiceCallback callback) {
+ AsyncCallbackTemplate.withCallback(transportApiTemplate.post(msg.getHash(),
+ TransportApiRequestMsg.newBuilder().setValidateX509CertRequestMsg(msg).build()),
+ response -> callback.onSuccess(response.getValidateTokenResponseMsg()), callback::onError, transportCallbackExecutor);
+ }
+
+ @Override
+ public void process(GetOrCreateDeviceFromGatewayRequestMsg msg, TransportServiceCallback callback) {
+ AsyncCallbackTemplate.withCallback(transportApiTemplate.post(msg.getDeviceName(),
+ TransportApiRequestMsg.newBuilder().setGetOrCreateDeviceRequestMsg(msg).build()),
+ response -> callback.onSuccess(response.getGetOrCreateDeviceResponseMsg()), callback::onError, transportCallbackExecutor);
+ }
+
+ @Override
+ public void process(SessionInfoProto sessionInfo, SessionEventMsg msg, TransportServiceCallback callback) {
+ ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg(
+ TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo)
+ .setSessionEvent(msg).build()
+ ).build();
+ send(sessionInfo, toRuleEngineMsg, callback);
+ }
+
+ @Override
+ public void process(SessionInfoProto sessionInfo, PostTelemetryMsg msg, TransportServiceCallback callback) {
+ ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg(
+ TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo)
+ .setPostTelemetry(msg).build()
+ ).build();
+ send(sessionInfo, toRuleEngineMsg, callback);
+ }
+
+ @Override
+ public void process(SessionInfoProto sessionInfo, PostAttributeMsg msg, TransportServiceCallback callback) {
+ ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg(
+ TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo)
+ .setPostAttributes(msg).build()
+ ).build();
+ send(sessionInfo, toRuleEngineMsg, callback);
+ }
+
+ @Override
+ public void process(SessionInfoProto sessionInfo, GetAttributeRequestMsg msg, TransportServiceCallback callback) {
+ ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg(
+ TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo)
+ .setGetAttributes(msg).build()
+ ).build();
+ send(sessionInfo, toRuleEngineMsg, callback);
+ }
+
+ @Override
+ public void process(SessionInfoProto sessionInfo, SubscribeToAttributeUpdatesMsg msg, TransportServiceCallback callback) {
+ ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg(
+ TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo)
+ .setSubscribeToAttributes(msg).build()
+ ).build();
+ send(sessionInfo, toRuleEngineMsg, callback);
+ }
+
+ @Override
+ public void process(SessionInfoProto sessionInfo, SubscribeToRPCMsg msg, TransportServiceCallback callback) {
+ ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg(
+ TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo)
+ .setSubscribeToRPC(msg).build()
+ ).build();
+ send(sessionInfo, toRuleEngineMsg, callback);
+ }
+
+ @Override
+ public void process(SessionInfoProto sessionInfo, ToDeviceRpcResponseMsg msg, TransportServiceCallback callback) {
+ ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg(
+ TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo)
+ .setToDeviceRPCCallResponse(msg).build()
+ ).build();
+ send(sessionInfo, toRuleEngineMsg, callback);
+ }
+
+ @Override
+ public void process(SessionInfoProto sessionInfo, ToServerRpcRequestMsg msg, TransportServiceCallback callback) {
+ ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg(
+ TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo)
+ .setToServerRPCCallRequest(msg).build()
+ ).build();
+ send(sessionInfo, toRuleEngineMsg, callback);
+ }
+
+ private static class TransportCallbackAdaptor implements Callback {
+ private final TransportServiceCallback callback;
+
+ TransportCallbackAdaptor(TransportServiceCallback callback) {
+ this.callback = callback;
+ }
+
+ @Override
+ public void onCompletion(RecordMetadata metadata, Exception exception) {
+ if (exception == null) {
+ if (callback != null) {
+ callback.onSuccess(null);
+ }
+ } else {
+ if (callback != null) {
+ callback.onError(exception);
+ }
+ }
+ }
+ }
+
+ private void send(SessionInfoProto sessionInfo, ToRuleEngineMsg toRuleEngineMsg, TransportServiceCallback callback) {
+ ruleEngineProducer.send(getRoutingKey(sessionInfo), toRuleEngineMsg, new TransportCallbackAdaptor(callback));
+ }
+}
diff --git a/application/src/main/java/org/thingsboard/server/actors/device/RuleEngineQueuePutAckMsg.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/SessionMetaData.java
similarity index 60%
rename from application/src/main/java/org/thingsboard/server/actors/device/RuleEngineQueuePutAckMsg.java
rename to common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/SessionMetaData.java
index 0b6fe54c4a..1de57114e4 100644
--- a/application/src/main/java/org/thingsboard/server/actors/device/RuleEngineQueuePutAckMsg.java
+++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/SessionMetaData.java
@@ -13,24 +13,20 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.thingsboard.server.actors.device;
+package org.thingsboard.server.common.transport.service;
import lombok.Data;
-import org.thingsboard.server.common.msg.MsgType;
-import org.thingsboard.server.common.msg.TbActorMsg;
-
-import java.util.UUID;
+import org.thingsboard.server.common.transport.SessionMsgListener;
+import org.thingsboard.server.gen.transport.TransportProtos;
/**
- * Created by ashvayka on 15.03.18.
+ * Created by ashvayka on 15.10.18.
*/
@Data
-public final class RuleEngineQueuePutAckMsg implements TbActorMsg {
+public class SessionMetaData {
- private final UUID id;
+ private final TransportProtos.SessionInfoProto sessionInfo;
+ private final TransportProtos.SessionType sessionType;
+ private final SessionMsgListener listener;
- @Override
- public MsgType getMsgType() {
- return MsgType.RULE_ENGINE_QUEUE_PUT_ACK_MSG;
- }
}
diff --git a/transport/http/src/main/java/org/thingsboard/server/transport/http/session/HttpSessionId.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/ToRuleEngineMsgEncoder.java
similarity index 62%
rename from transport/http/src/main/java/org/thingsboard/server/transport/http/session/HttpSessionId.java
rename to common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/ToRuleEngineMsgEncoder.java
index 3211765334..2842d7ef66 100644
--- a/transport/http/src/main/java/org/thingsboard/server/transport/http/session/HttpSessionId.java
+++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/ToRuleEngineMsgEncoder.java
@@ -13,25 +13,17 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.thingsboard.server.transport.http.session;
+package org.thingsboard.server.common.transport.service;
-import org.thingsboard.server.common.data.id.SessionId;
-
-import java.util.UUID;
+import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg;
+import org.thingsboard.server.kafka.TbKafkaEncoder;
/**
- * @author Andrew Shvayka
+ * Created by ashvayka on 05.10.18.
*/
-public class HttpSessionId implements SessionId {
-
- private final UUID id;
-
- public HttpSessionId() {
- this.id = UUID.randomUUID();
- }
-
+public class ToRuleEngineMsgEncoder implements TbKafkaEncoder {
@Override
- public String toUidStr() {
- return id.toString();
+ public byte[] encode(ToRuleEngineMsg value) {
+ return value.toByteArray();
}
}
diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/AttributesUnsubscribeMsg.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/ToTransportMsgResponseDecoder.java
similarity index 59%
rename from common/message/src/main/java/org/thingsboard/server/common/msg/core/AttributesUnsubscribeMsg.java
rename to common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/ToTransportMsgResponseDecoder.java
index c98ad2ae26..a905f76ace 100644
--- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/AttributesUnsubscribeMsg.java
+++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/ToTransportMsgResponseDecoder.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.common.transport.service;
-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.ToTransportMsg;
+import org.thingsboard.server.kafka.TbKafkaDecoder;
+
+import java.io.IOException;
/**
- * @author Andrew Shvayka
+ * Created by ashvayka on 05.10.18.
*/
-public class AttributesUnsubscribeMsg implements FromDeviceMsg {
+public class ToTransportMsgResponseDecoder implements TbKafkaDecoder {
@Override
- public SessionMsgType getMsgType() {
- return SessionMsgType.UNSUBSCRIBE_ATTRIBUTES_REQUEST;
+ public ToTransportMsg decode(byte[] data) throws IOException {
+ return ToTransportMsg.parseFrom(data);
}
}
diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/TransportApiRequestEncoder.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/TransportApiRequestEncoder.java
new file mode 100644
index 0000000000..a8f27aad8f
--- /dev/null
+++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/TransportApiRequestEncoder.java
@@ -0,0 +1,29 @@
+/**
+ * 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.transport.service;
+
+import org.thingsboard.server.gen.transport.TransportProtos.TransportApiRequestMsg;
+import org.thingsboard.server.kafka.TbKafkaEncoder;
+
+/**
+ * Created by ashvayka on 05.10.18.
+ */
+public class TransportApiRequestEncoder implements TbKafkaEncoder {
+ @Override
+ public byte[] encode(TransportApiRequestMsg value) {
+ return value.toByteArray();
+ }
+}
diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/TransportApiResponseDecoder.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/TransportApiResponseDecoder.java
new file mode 100644
index 0000000000..3b5c9ce579
--- /dev/null
+++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/TransportApiResponseDecoder.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.common.transport.service;
+
+import org.thingsboard.server.gen.transport.TransportProtos.TransportApiResponseMsg;
+import org.thingsboard.server.kafka.TbKafkaDecoder;
+
+import java.io.IOException;
+
+/**
+ * Created by ashvayka on 05.10.18.
+ */
+public class TransportApiResponseDecoder implements TbKafkaDecoder {
+ @Override
+ public TransportApiResponseMsg decode(byte[] data) throws IOException {
+ return TransportApiResponseMsg.parseFrom(data);
+ }
+}
diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/session/DeviceAwareSessionContext.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/session/DeviceAwareSessionContext.java
new file mode 100644
index 0000000000..93c0f41ddd
--- /dev/null
+++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/session/DeviceAwareSessionContext.java
@@ -0,0 +1,52 @@
+/**
+ * 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.transport.session;
+
+import lombok.Data;
+import lombok.Getter;
+import org.thingsboard.server.common.data.id.DeviceId;
+import org.thingsboard.server.common.msg.session.SessionContext;
+import org.thingsboard.server.gen.transport.TransportProtos.DeviceInfoProto;
+
+import java.util.UUID;
+
+/**
+ * @author Andrew Shvayka
+ */
+@Data
+public abstract class DeviceAwareSessionContext implements SessionContext {
+
+ @Getter
+ protected final UUID sessionId;
+ @Getter
+ private volatile DeviceId deviceId;
+ @Getter
+ private volatile DeviceInfoProto deviceInfo;
+
+ public DeviceId getDeviceId() {
+ return deviceId;
+ }
+
+ public void setDeviceInfo(DeviceInfoProto deviceInfo) {
+ this.deviceInfo = deviceInfo;
+ this.deviceId = new DeviceId(new UUID(deviceInfo.getDeviceIdMSB(), deviceInfo.getDeviceIdLSB()));
+ }
+
+
+ public boolean isConnected() {
+ return deviceInfo != null;
+ }
+}
diff --git a/common/transport/transport-api/src/main/proto/transport.proto b/common/transport/transport-api/src/main/proto/transport.proto
new file mode 100644
index 0000000000..0e36dabbf3
--- /dev/null
+++ b/common/transport/transport-api/src/main/proto/transport.proto
@@ -0,0 +1,217 @@
+/**
+ * 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.
+ */
+syntax = "proto3";
+package transport;
+
+option java_package = "org.thingsboard.server.gen.transport";
+option java_outer_classname = "TransportProtos";
+
+/**
+ * Data Structures;
+ */
+message SessionInfoProto {
+ string nodeId = 1;
+ int64 sessionIdMSB = 2;
+ int64 sessionIdLSB = 3;
+ int64 tenantIdMSB = 4;
+ int64 tenantIdLSB = 5;
+ int64 deviceIdMSB = 6;
+ int64 deviceIdLSB = 7;
+}
+
+enum SessionEvent {
+ OPEN = 0;
+ CLOSED = 1;
+}
+
+enum SessionType {
+ SYNC = 0;
+ ASYNC = 1;
+}
+
+enum KeyValueType {
+ BOOLEAN_V = 0;
+ LONG_V = 1;
+ DOUBLE_V = 2;
+ STRING_V = 3;
+}
+
+message KeyValueProto {
+ string key = 1;
+ KeyValueType type = 2;
+ bool bool_v = 3;
+ int64 long_v = 4;
+ double double_v = 5;
+ string string_v = 6;
+}
+
+message TsKvProto {
+ int64 ts = 1;
+ KeyValueProto kv = 2;
+}
+
+message TsKvListProto {
+ int64 ts = 1;
+ repeated KeyValueProto kv = 2;
+}
+
+message DeviceInfoProto {
+ int64 tenantIdMSB = 1;
+ int64 tenantIdLSB = 2;
+ int64 deviceIdMSB = 3;
+ int64 deviceIdLSB = 4;
+ string deviceName = 5;
+ string deviceType = 6;
+ string additionalInfo = 7;
+}
+
+/**
+ * Messages that use Data Structures;
+ */
+message SessionEventMsg {
+ SessionType sessionType = 1;
+ SessionEvent event = 2;
+}
+
+message PostTelemetryMsg {
+ repeated TsKvListProto tsKvList = 1;
+}
+
+message PostAttributeMsg {
+ repeated KeyValueProto kv = 1;
+}
+
+message GetAttributeRequestMsg {
+ int32 requestId = 1;
+ repeated string clientAttributeNames = 2;
+ repeated string sharedAttributeNames = 3;
+}
+
+message GetAttributeResponseMsg {
+ int32 requestId = 1;
+ repeated TsKvProto clientAttributeList = 2;
+ repeated TsKvProto sharedAttributeList = 3;
+ repeated string deletedAttributeKeys = 4;
+ string error = 5;
+}
+
+message AttributeUpdateNotificationMsg {
+ repeated TsKvProto sharedUpdated = 1;
+ repeated string sharedDeleted = 2;
+}
+
+message ValidateDeviceTokenRequestMsg {
+ string token = 1;
+}
+
+message ValidateDeviceX509CertRequestMsg {
+ string hash = 1;
+}
+
+message ValidateDeviceCredentialsResponseMsg {
+ DeviceInfoProto deviceInfo = 1;
+ string credentialsBody = 2;
+}
+
+message GetOrCreateDeviceFromGatewayRequestMsg {
+ int64 gatewayIdMSB = 1;
+ int64 gatewayIdLSB = 2;
+ string deviceName = 3;
+ string deviceType = 4;
+}
+
+message GetOrCreateDeviceFromGatewayResponseMsg {
+ DeviceInfoProto deviceInfo = 1;
+}
+
+message SessionCloseNotificationProto {
+ string message = 1;
+}
+
+message SubscribeToAttributeUpdatesMsg {
+ bool unsubscribe = 1;
+}
+
+message SubscribeToRPCMsg {
+ bool unsubscribe = 1;
+}
+
+message ToDeviceRpcRequestMsg {
+ int32 requestId = 1;
+ string methodName = 2;
+ string params = 3;
+}
+
+message ToDeviceRpcResponseMsg {
+ int32 requestId = 1;
+ string payload = 2;
+}
+
+message ToServerRpcRequestMsg {
+ int32 requestId = 1;
+ string methodName = 2;
+ string params = 3;
+}
+
+message ToServerRpcResponseMsg {
+ int32 requestId = 1;
+ string payload = 2;
+ string error = 3;
+}
+
+message TransportToDeviceActorMsg {
+ SessionInfoProto sessionInfo = 1;
+ SessionEventMsg sessionEvent = 2;
+ PostTelemetryMsg postTelemetry = 3;
+ PostAttributeMsg postAttributes = 4;
+ GetAttributeRequestMsg getAttributes = 5;
+ SubscribeToAttributeUpdatesMsg subscribeToAttributes = 6;
+ SubscribeToRPCMsg subscribeToRPC = 7;
+ ToDeviceRpcResponseMsg toDeviceRPCCallResponse = 8;
+ ToServerRpcRequestMsg toServerRPCCallRequest = 9;
+}
+
+message DeviceActorToTransportMsg {
+ int64 sessionIdMSB = 1;
+ int64 sessionIdLSB = 2;
+ SessionCloseNotificationProto sessionCloseNotification = 3;
+ GetAttributeResponseMsg getAttributesResponse = 4;
+ AttributeUpdateNotificationMsg attributeUpdateNotification = 5;
+ ToDeviceRpcRequestMsg toDeviceRequest = 6;
+ ToServerRpcResponseMsg toServerResponse = 7;
+}
+
+/**
+ * Main messages;
+ */
+message ToRuleEngineMsg {
+ TransportToDeviceActorMsg toDeviceActorMsg = 1;
+}
+
+message ToTransportMsg {
+ DeviceActorToTransportMsg toDeviceSessionMsg = 1;
+}
+
+message TransportApiRequestMsg {
+ ValidateDeviceTokenRequestMsg validateTokenRequestMsg = 1;
+ ValidateDeviceX509CertRequestMsg validateX509CertRequestMsg = 2;
+ GetOrCreateDeviceFromGatewayRequestMsg getOrCreateDeviceRequestMsg = 3;
+}
+
+message TransportApiResponseMsg {
+ ValidateDeviceCredentialsResponseMsg validateTokenResponseMsg = 1;
+ GetOrCreateDeviceFromGatewayResponseMsg getOrCreateDeviceResponseMsg = 2;
+}
\ No newline at end of file
diff --git a/common/transport/src/test/java/org/thingsboard/server/common/transport/quota/ClockTest.java b/common/transport/transport-api/src/test/java/org/thingsboard/server/common/transport/quota/ClockTest.java
similarity index 100%
rename from common/transport/src/test/java/org/thingsboard/server/common/transport/quota/ClockTest.java
rename to common/transport/transport-api/src/test/java/org/thingsboard/server/common/transport/quota/ClockTest.java
diff --git a/common/transport/src/test/java/org/thingsboard/server/common/transport/quota/HostRequestLimitPolicyTest.java b/common/transport/transport-api/src/test/java/org/thingsboard/server/common/transport/quota/HostRequestLimitPolicyTest.java
similarity index 100%
rename from common/transport/src/test/java/org/thingsboard/server/common/transport/quota/HostRequestLimitPolicyTest.java
rename to common/transport/transport-api/src/test/java/org/thingsboard/server/common/transport/quota/HostRequestLimitPolicyTest.java
diff --git a/common/transport/src/test/java/org/thingsboard/server/common/transport/quota/HostRequestsQuotaServiceTest.java b/common/transport/transport-api/src/test/java/org/thingsboard/server/common/transport/quota/HostRequestsQuotaServiceTest.java
similarity index 86%
rename from common/transport/src/test/java/org/thingsboard/server/common/transport/quota/HostRequestsQuotaServiceTest.java
rename to common/transport/transport-api/src/test/java/org/thingsboard/server/common/transport/quota/HostRequestsQuotaServiceTest.java
index 20f8a55d0d..37441e9d41 100644
--- a/common/transport/src/test/java/org/thingsboard/server/common/transport/quota/HostRequestsQuotaServiceTest.java
+++ b/common/transport/transport-api/src/test/java/org/thingsboard/server/common/transport/quota/HostRequestsQuotaServiceTest.java
@@ -17,7 +17,11 @@ package org.thingsboard.server.common.transport.quota;
import org.junit.Before;
import org.junit.Test;
-import org.thingsboard.server.common.transport.quota.host.*;
+import org.thingsboard.server.common.transport.quota.host.HostIntervalRegistryCleaner;
+import org.thingsboard.server.common.transport.quota.host.HostIntervalRegistryLogger;
+import org.thingsboard.server.common.transport.quota.host.HostRequestIntervalRegistry;
+import org.thingsboard.server.common.transport.quota.host.HostRequestLimitPolicy;
+import org.thingsboard.server.common.transport.quota.host.HostRequestsQuotaService;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
diff --git a/common/transport/src/test/java/org/thingsboard/server/common/transport/quota/inmemory/HostRequestIntervalRegistryTest.java b/common/transport/transport-api/src/test/java/org/thingsboard/server/common/transport/quota/inmemory/HostRequestIntervalRegistryTest.java
similarity index 100%
rename from common/transport/src/test/java/org/thingsboard/server/common/transport/quota/inmemory/HostRequestIntervalRegistryTest.java
rename to common/transport/transport-api/src/test/java/org/thingsboard/server/common/transport/quota/inmemory/HostRequestIntervalRegistryTest.java
diff --git a/common/transport/src/test/java/org/thingsboard/server/common/transport/quota/inmemory/IntervalCountTest.java b/common/transport/transport-api/src/test/java/org/thingsboard/server/common/transport/quota/inmemory/IntervalCountTest.java
similarity index 100%
rename from common/transport/src/test/java/org/thingsboard/server/common/transport/quota/inmemory/IntervalCountTest.java
rename to common/transport/transport-api/src/test/java/org/thingsboard/server/common/transport/quota/inmemory/IntervalCountTest.java
diff --git a/common/transport/src/test/java/org/thingsboard/server/common/transport/quota/inmemory/IntervalRegistryLoggerTest.java b/common/transport/transport-api/src/test/java/org/thingsboard/server/common/transport/quota/inmemory/IntervalRegistryLoggerTest.java
similarity index 100%
rename from common/transport/src/test/java/org/thingsboard/server/common/transport/quota/inmemory/IntervalRegistryLoggerTest.java
rename to common/transport/transport-api/src/test/java/org/thingsboard/server/common/transport/quota/inmemory/IntervalRegistryLoggerTest.java
diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsServiceImpl.java
index 4219b068f1..4b25f2b38a 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsServiceImpl.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsServiceImpl.java
@@ -26,7 +26,7 @@ import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.common.data.security.DeviceCredentialsType;
-import org.thingsboard.server.dao.EncryptionUtil;
+import org.thingsboard.server.common.msg.EncryptionUtil;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.service.DataValidator;
diff --git a/msa/docker/.env b/msa/docker/.env
index 018d4ac7b7..3d8faeb355 100644
--- a/msa/docker/.env
+++ b/msa/docker/.env
@@ -4,7 +4,10 @@ DOCKER_REPO=local-maven-build
JS_EXECUTOR_DOCKER_NAME=tb-js-executor
TB_NODE_DOCKER_NAME=tb-node
WEB_UI_DOCKER_NAME=tb-web-ui
+MQTT_TRANSPORT_DOCKER_NAME=tb-mqtt-transport
+HTTP_TRANSPORT_DOCKER_NAME=tb-http-transport
+COAP_TRANSPORT_DOCKER_NAME=tb-coap-transport
TB_VERSION=2.2.0-SNAPSHOT
-KAFKA_TOPICS=js.eval.requests:100:1:delete --config=retention.ms=60000 --config=retention.bytes=1073741824
+KAFKA_TOPICS=js.eval.requests:100:1:delete --config=retention.ms=60000 --config=retention.bytes=1073741824,tb.transport.api.requests:30:1:delete --config=retention.ms=60000 --config=retention.bytes=1073741824,tb.rule-engine:30:1
diff --git a/msa/docker/docker-compose.yml b/msa/docker/docker-compose.yml
index 91e6286d69..e3b56ae425 100644
--- a/msa/docker/docker-compose.yml
+++ b/msa/docker/docker-compose.yml
@@ -56,8 +56,6 @@ services:
image: "${DOCKER_REPO}/${TB_NODE_DOCKER_NAME}:${TB_VERSION}"
ports:
- "8080"
- - "1883:1883"
- - "5683:5683/udp"
logging:
driver: "json-file"
options:
@@ -75,6 +73,61 @@ services:
- ./tb-node/log:/var/log/thingsboard
depends_on:
- kafka
+ tb-mqtt-transport1:
+ restart: always
+ image: "${DOCKER_REPO}/${MQTT_TRANSPORT_DOCKER_NAME}:${TB_VERSION}"
+ ports:
+ - "1883"
+ environment:
+ TB_KAFKA_SERVERS: kafka:9092
+ env_file:
+ - tb-mqtt-transport.env
+ depends_on:
+ - kafka
+ tb-mqtt-transport2:
+ restart: always
+ image: "${DOCKER_REPO}/${MQTT_TRANSPORT_DOCKER_NAME}:${TB_VERSION}"
+ ports:
+ - "1883"
+ environment:
+ TB_KAFKA_SERVERS: kafka:9092
+ env_file:
+ - tb-mqtt-transport.env
+ depends_on:
+ - kafka
+ tb-http-transport1:
+ restart: always
+ image: "${DOCKER_REPO}/${HTTP_TRANSPORT_DOCKER_NAME}:${TB_VERSION}"
+ ports:
+ - "8081"
+ environment:
+ TB_KAFKA_SERVERS: kafka:9092
+ env_file:
+ - tb-http-transport.env
+ depends_on:
+ - kafka
+ tb-http-transport2:
+ restart: always
+ image: "${DOCKER_REPO}/${HTTP_TRANSPORT_DOCKER_NAME}:${TB_VERSION}"
+ ports:
+ - "8081"
+ environment:
+ TB_KAFKA_SERVERS: kafka:9092
+ env_file:
+ - tb-http-transport.env
+ depends_on:
+ - kafka
+ tb-coap-transport:
+ restart: always
+ image: "${DOCKER_REPO}/${COAP_TRANSPORT_DOCKER_NAME}:${TB_VERSION}"
+ ports:
+ - "5683:5683/udp"
+ environment:
+ TB_KAFKA_SERVERS: kafka:9092
+ env_file:
+ - tb-coap-transport.env
+ depends_on:
+ - kafka
tb-web-ui1:
restart: always
image: "${DOCKER_REPO}/${WEB_UI_DOCKER_NAME}:${TB_VERSION}"
@@ -106,12 +159,18 @@ services:
ports:
- "80:80"
- "443:443"
+ - "1883:1883"
- "9999:9999"
cap_add:
- NET_ADMIN
environment:
HTTP_PORT: 80
HTTPS_PORT: 443
+ MQTT_PORT: 1883
links:
- tb-web-ui1
- tb-web-ui2
+ - tb-mqtt-transport1
+ - tb-mqtt-transport2
+ - tb-http-transport1
+ - tb-http-transport2
diff --git a/msa/docker/haproxy/config/haproxy.cfg b/msa/docker/haproxy/config/haproxy.cfg
index 5f174653f9..cb70552a73 100644
--- a/msa/docker/haproxy/config/haproxy.cfg
+++ b/msa/docker/haproxy/config/haproxy.cfg
@@ -1,6 +1,9 @@
#HA Proxy Config
global
- maxconn 4096
+ ulimit-n 500000
+ maxconn 99999
+ maxpipes 99999
+ tune.maxaccept 500
log 127.0.0.1 local0
log 127.0.0.1 local1 notice
@@ -13,8 +16,6 @@ global
defaults
- option forwardfor
-
log global
mode http
@@ -30,23 +31,42 @@ listen stats
stats uri /stats
stats auth admin:admin@123
+listen mqtt-in
+ bind *:${MQTT_PORT}
+ mode tcp
+ option clitcpka # For TCP keep-alive
+ timeout client 3h
+ timeout server 3h
+ option tcplog
+ balance leastconn
+ server tbMqtt1 tb-mqtt-transport1:1883 check
+ server tbMqtt2 tb-mqtt-transport2:1883 check
+
frontend http-in
bind *:${HTTP_PORT}
+ option forwardfor
+
reqadd X-Forwarded-Proto:\ http
acl transport_http_acl path_beg /api/v1/
acl letsencrypt_http_acl path_beg /.well-known/acme-challenge/
redirect scheme https if !letsencrypt_http_acl !transport_http_acl
use_backend letsencrypt_http if letsencrypt_http_acl
+ use_backend tb-http-backend if transport_http_acl
default_backend tb-web-backend
frontend https_in
bind *:${HTTPS_PORT} ssl crt /usr/local/etc/haproxy/default.pem crt /usr/local/etc/haproxy/certs.d ciphers ECDHE-RSA-AES256-SHA:RC4-SHA:RC4:HIGH:!MD5:!aNULL:!EDH:!AESGCM
+ option forwardfor
+
reqadd X-Forwarded-Proto:\ https
+ acl transport_http_acl path_beg /api/v1/
+ use_backend tb-http-backend if transport_http_acl
+
default_backend tb-web-backend
backend letsencrypt_http
@@ -59,3 +79,10 @@ backend tb-web-backend
server tbWeb1 tb-web-ui1:8080 check
server tbWeb2 tb-web-ui2:8080 check
http-request set-header X-Forwarded-Port %[dst_port]
+
+backend tb-http-backend
+ balance leastconn
+ option tcp-check
+ option log-health-checks
+ server tbHttp1 tb-http-transport1:8081 check
+ server tbHttp2 tb-http-transport2:8081 check
diff --git a/msa/docker/tb-coap-transport.env b/msa/docker/tb-coap-transport.env
new file mode 100644
index 0000000000..b3331fe849
--- /dev/null
+++ b/msa/docker/tb-coap-transport.env
@@ -0,0 +1,6 @@
+
+COAP_BIND_ADDRESS=0.0.0.0
+COAP_BIND_PORT=5683
+COAP_TIMEOUT=10000
+
+TB_KAFKA_SERVERS=localhost:9092
\ No newline at end of file
diff --git a/msa/docker/tb-http-transport.env b/msa/docker/tb-http-transport.env
new file mode 100644
index 0000000000..33c479104e
--- /dev/null
+++ b/msa/docker/tb-http-transport.env
@@ -0,0 +1,6 @@
+
+HTTP_BIND_ADDRESS=0.0.0.0
+HTTP_BIND_PORT=8081
+HTTP_REQUEST_TIMEOUT=60000
+
+TB_KAFKA_SERVERS=localhost:9092
\ No newline at end of file
diff --git a/msa/docker/tb-mqtt-transport.env b/msa/docker/tb-mqtt-transport.env
new file mode 100644
index 0000000000..5ada3f786a
--- /dev/null
+++ b/msa/docker/tb-mqtt-transport.env
@@ -0,0 +1,6 @@
+
+MQTT_BIND_ADDRESS=0.0.0.0
+MQTT_BIND_PORT=1883
+MQTT_TIMEOUT=10000
+
+TB_KAFKA_SERVERS=localhost:9092
\ No newline at end of file
diff --git a/msa/docker/tb-node.env b/msa/docker/tb-node.env
index 874bf75d55..abeccb12eb 100644
--- a/msa/docker/tb-node.env
+++ b/msa/docker/tb-node.env
@@ -1,8 +1,6 @@
# ThingsBoard server configuration
-MQTT_BIND_ADDRESS=0.0.0.0
-MQTT_BIND_PORT=1883
-COAP_BIND_ADDRESS=0.0.0.0
-COAP_BIND_PORT=5683
+
+TRANSPORT_TYPE=remote
# type of database to use: sql[DEFAULT] or cassandra
DATABASE_TS_TYPE=sql
diff --git a/msa/pom.xml b/msa/pom.xml
index 21ddb4dafe..59f954a1ed 100644
--- a/msa/pom.xml
+++ b/msa/pom.xml
@@ -40,6 +40,7 @@
js-executor
web-ui
tb-node
+ transport
diff --git a/msa/transport/coap/docker/Dockerfile b/msa/transport/coap/docker/Dockerfile
new file mode 100644
index 0000000000..dcaef3a358
--- /dev/null
+++ b/msa/transport/coap/docker/Dockerfile
@@ -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.
+#
+
+FROM openjdk:8-jre
+
+COPY logback.xml ${pkg.name}.conf start-tb-coap-transport.sh ${pkg.name}.deb /tmp/
+
+RUN chmod a+x /tmp/*.sh \
+ && mv /tmp/start-tb-coap-transport.sh /usr/bin
+
+RUN dpkg -i /tmp/${pkg.name}.deb
+
+RUN update-rc.d ${pkg.name} disable
+
+RUN mv /tmp/logback.xml ${pkg.installFolder}/conf \
+ && mv /tmp/${pkg.name}.conf ${pkg.installFolder}/conf
+
+CMD ["start-tb-coap-transport.sh"]
diff --git a/msa/transport/coap/docker/logback.xml b/msa/transport/coap/docker/logback.xml
new file mode 100644
index 0000000000..e9d86928bd
--- /dev/null
+++ b/msa/transport/coap/docker/logback.xml
@@ -0,0 +1,50 @@
+
+
+
+
+
+
+ /var/log/${pkg.name}/${pkg.name}.log
+
+ /var/log/${pkg.name}/${pkg.name}.%d{yyyy-MM-dd}.%i.log
+ 100MB
+ 30
+ 3GB
+
+
+ %d{ISO8601} [%thread] %-5level %logger{36} - %msg%n
+
+
+
+
+
+ %d{ISO8601} [%thread] %-5level %logger{36} - %msg%n
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/msa/transport/coap/docker/start-tb-coap-transport.sh b/msa/transport/coap/docker/start-tb-coap-transport.sh
new file mode 100755
index 0000000000..43d4602230
--- /dev/null
+++ b/msa/transport/coap/docker/start-tb-coap-transport.sh
@@ -0,0 +1,29 @@
+#!/bin/bash
+#
+# 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.
+#
+
+CONF_FOLDER="${pkg.installFolder}/conf"
+jarfile=${pkg.installFolder}/bin/${pkg.name}.jar
+configfile=${pkg.name}.conf
+
+source "${CONF_FOLDER}/${configfile}"
+
+echo "Starting '${project.name}' ..."
+
+exec java -cp ${jarfile} $JAVA_OPTS -Dloader.main=org.thingsboard.server.coap.ThingsboardCoapTransportApplication \
+ -Dspring.jpa.hibernate.ddl-auto=none \
+ -Dlogging.config=${CONF_FOLDER}/logback.xml \
+ org.springframework.boot.loader.PropertiesLauncher
diff --git a/msa/transport/coap/docker/tb-coap-transport.conf b/msa/transport/coap/docker/tb-coap-transport.conf
new file mode 100644
index 0000000000..6d4c54aad1
--- /dev/null
+++ b/msa/transport/coap/docker/tb-coap-transport.conf
@@ -0,0 +1,23 @@
+#
+# 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.
+#
+
+export JAVA_OPTS="$JAVA_OPTS -Xloggc:@pkg.logFolder@/gc.log -XX:+IgnoreUnrecognizedVMOptions -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDetails -XX:+PrintGCDateStamps"
+export JAVA_OPTS="$JAVA_OPTS -XX:+PrintHeapAtGC -XX:+PrintTenuringDistribution -XX:+PrintGCApplicationStoppedTime -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10"
+export JAVA_OPTS="$JAVA_OPTS -XX:GCLogFileSize=10M -XX:-UseBiasedLocking -XX:+UseTLAB -XX:+ResizeTLAB -XX:+PerfDisableSharedMem -XX:+UseCondCardMark"
+export JAVA_OPTS="$JAVA_OPTS -XX:CMSWaitDuration=10000 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+CMSParallelInitialMarkEnabled"
+export JAVA_OPTS="$JAVA_OPTS -XX:+CMSEdenChunksRecordAlways -XX:CMSInitiatingOccupancyFraction=75 -XX:+UseCMSInitiatingOccupancyOnly -XX:+ExitOnOutOfMemoryError"
+export LOG_FILENAME=${pkg.name}.out
+export LOADER_PATH=${pkg.installFolder}/conf
diff --git a/msa/transport/coap/pom.xml b/msa/transport/coap/pom.xml
new file mode 100644
index 0000000000..07c4e63342
--- /dev/null
+++ b/msa/transport/coap/pom.xml
@@ -0,0 +1,137 @@
+
+
+ 4.0.0
+
+ org.thingsboard.msa
+ 2.2.0-SNAPSHOT
+ transport
+
+ org.thingsboard.msa.transport
+ coap
+ pom
+
+ ThingsBoard COAP Transport Microservice
+ https://thingsboard.io
+ ThingsBoard COAP Transport Microservice
+
+
+ UTF-8
+ ${basedir}/../../..
+ tb-coap-transport
+ tb-coap-transport
+ thingsboard
+ /var/log/${pkg.name}
+ /usr/share/${pkg.name}
+
+
+
+
+ org.thingsboard.transport
+ coap
+ ${project.version}
+ deb
+ deb
+ provided
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-dependency-plugin
+
+
+ copy-tb-coap-transport-deb
+ package
+
+ copy
+
+
+
+
+ org.thingsboard.transport
+ coap
+ deb
+ deb
+ ${pkg.name}.deb
+ ${project.build.directory}
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-resources-plugin
+
+
+ copy-docker-config
+ process-resources
+
+ copy-resources
+
+
+ ${project.build.directory}
+
+
+ docker
+ true
+
+
+
+
+
+
+
+ com.spotify
+ dockerfile-maven-plugin
+
+
+ build-docker-image
+ pre-integration-test
+
+ build
+
+
+
+
+ ${dockerfile.skip}
+ ${docker.repo}/${docker.name}
+ ${project.version}
+ true
+ false
+ ${project.build.directory}
+
+
+
+
+
+
+ jenkins
+ Jenkins Repository
+ http://repo.jenkins-ci.org/releases
+
+ false
+
+
+
+
diff --git a/msa/transport/http/docker/Dockerfile b/msa/transport/http/docker/Dockerfile
new file mode 100644
index 0000000000..212047fa42
--- /dev/null
+++ b/msa/transport/http/docker/Dockerfile
@@ -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.
+#
+
+FROM openjdk:8-jre
+
+COPY logback.xml ${pkg.name}.conf start-tb-http-transport.sh ${pkg.name}.deb /tmp/
+
+RUN chmod a+x /tmp/*.sh \
+ && mv /tmp/start-tb-http-transport.sh /usr/bin
+
+RUN dpkg -i /tmp/${pkg.name}.deb
+
+RUN update-rc.d ${pkg.name} disable
+
+RUN mv /tmp/logback.xml ${pkg.installFolder}/conf \
+ && mv /tmp/${pkg.name}.conf ${pkg.installFolder}/conf
+
+CMD ["start-tb-http-transport.sh"]
diff --git a/msa/transport/http/docker/logback.xml b/msa/transport/http/docker/logback.xml
new file mode 100644
index 0000000000..e9d86928bd
--- /dev/null
+++ b/msa/transport/http/docker/logback.xml
@@ -0,0 +1,50 @@
+
+
+
+
+
+
+ /var/log/${pkg.name}/${pkg.name}.log
+
+ /var/log/${pkg.name}/${pkg.name}.%d{yyyy-MM-dd}.%i.log
+ 100MB
+ 30
+ 3GB
+
+
+ %d{ISO8601} [%thread] %-5level %logger{36} - %msg%n
+
+
+
+
+
+ %d{ISO8601} [%thread] %-5level %logger{36} - %msg%n
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/msa/transport/http/docker/start-tb-http-transport.sh b/msa/transport/http/docker/start-tb-http-transport.sh
new file mode 100755
index 0000000000..667988fa42
--- /dev/null
+++ b/msa/transport/http/docker/start-tb-http-transport.sh
@@ -0,0 +1,29 @@
+#!/bin/bash
+#
+# 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.
+#
+
+CONF_FOLDER="${pkg.installFolder}/conf"
+jarfile=${pkg.installFolder}/bin/${pkg.name}.jar
+configfile=${pkg.name}.conf
+
+source "${CONF_FOLDER}/${configfile}"
+
+echo "Starting '${project.name}' ..."
+
+exec java -cp ${jarfile} $JAVA_OPTS -Dloader.main=org.thingsboard.server.http.ThingsboardHttpTransportApplication \
+ -Dspring.jpa.hibernate.ddl-auto=none \
+ -Dlogging.config=${CONF_FOLDER}/logback.xml \
+ org.springframework.boot.loader.PropertiesLauncher
diff --git a/msa/transport/http/docker/tb-http-transport.conf b/msa/transport/http/docker/tb-http-transport.conf
new file mode 100644
index 0000000000..6d4c54aad1
--- /dev/null
+++ b/msa/transport/http/docker/tb-http-transport.conf
@@ -0,0 +1,23 @@
+#
+# 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.
+#
+
+export JAVA_OPTS="$JAVA_OPTS -Xloggc:@pkg.logFolder@/gc.log -XX:+IgnoreUnrecognizedVMOptions -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDetails -XX:+PrintGCDateStamps"
+export JAVA_OPTS="$JAVA_OPTS -XX:+PrintHeapAtGC -XX:+PrintTenuringDistribution -XX:+PrintGCApplicationStoppedTime -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10"
+export JAVA_OPTS="$JAVA_OPTS -XX:GCLogFileSize=10M -XX:-UseBiasedLocking -XX:+UseTLAB -XX:+ResizeTLAB -XX:+PerfDisableSharedMem -XX:+UseCondCardMark"
+export JAVA_OPTS="$JAVA_OPTS -XX:CMSWaitDuration=10000 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+CMSParallelInitialMarkEnabled"
+export JAVA_OPTS="$JAVA_OPTS -XX:+CMSEdenChunksRecordAlways -XX:CMSInitiatingOccupancyFraction=75 -XX:+UseCMSInitiatingOccupancyOnly -XX:+ExitOnOutOfMemoryError"
+export LOG_FILENAME=${pkg.name}.out
+export LOADER_PATH=${pkg.installFolder}/conf
diff --git a/msa/transport/http/pom.xml b/msa/transport/http/pom.xml
new file mode 100644
index 0000000000..22eb299dd5
--- /dev/null
+++ b/msa/transport/http/pom.xml
@@ -0,0 +1,137 @@
+
+
+ 4.0.0
+
+ org.thingsboard.msa
+ 2.2.0-SNAPSHOT
+ transport
+
+ org.thingsboard.msa.transport
+ http
+ pom
+
+ ThingsBoard HTTP Transport Microservice
+ https://thingsboard.io
+ ThingsBoard HTTP Transport Microservice
+
+
+ UTF-8
+ ${basedir}/../../..
+ tb-http-transport
+ tb-http-transport
+ thingsboard
+ /var/log/${pkg.name}
+ /usr/share/${pkg.name}
+
+
+
+
+ org.thingsboard.transport
+ http
+ ${project.version}
+ deb
+ deb
+ provided
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-dependency-plugin
+
+
+ copy-tb-http-transport-deb
+ package
+
+ copy
+
+
+
+
+ org.thingsboard.transport
+ http
+ deb
+ deb
+ ${pkg.name}.deb
+ ${project.build.directory}
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-resources-plugin
+
+
+ copy-docker-config
+ process-resources
+
+ copy-resources
+
+
+ ${project.build.directory}
+
+
+ docker
+ true
+
+
+
+
+
+
+
+ com.spotify
+ dockerfile-maven-plugin
+
+
+ build-docker-image
+ pre-integration-test
+
+ build
+
+
+
+
+ ${dockerfile.skip}
+ ${docker.repo}/${docker.name}
+ ${project.version}
+ true
+ false
+ ${project.build.directory}
+
+
+
+
+
+
+ jenkins
+ Jenkins Repository
+ http://repo.jenkins-ci.org/releases
+
+ false
+
+
+
+
diff --git a/msa/transport/mqtt/docker/Dockerfile b/msa/transport/mqtt/docker/Dockerfile
new file mode 100644
index 0000000000..5827c65229
--- /dev/null
+++ b/msa/transport/mqtt/docker/Dockerfile
@@ -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.
+#
+
+FROM openjdk:8-jre
+
+COPY logback.xml ${pkg.name}.conf start-tb-mqtt-transport.sh ${pkg.name}.deb /tmp/
+
+RUN chmod a+x /tmp/*.sh \
+ && mv /tmp/start-tb-mqtt-transport.sh /usr/bin
+
+RUN dpkg -i /tmp/${pkg.name}.deb
+
+RUN update-rc.d ${pkg.name} disable
+
+RUN mv /tmp/logback.xml ${pkg.installFolder}/conf \
+ && mv /tmp/${pkg.name}.conf ${pkg.installFolder}/conf
+
+CMD ["start-tb-mqtt-transport.sh"]
diff --git a/msa/transport/mqtt/docker/logback.xml b/msa/transport/mqtt/docker/logback.xml
new file mode 100644
index 0000000000..e9d86928bd
--- /dev/null
+++ b/msa/transport/mqtt/docker/logback.xml
@@ -0,0 +1,50 @@
+
+
+
+
+
+
+ /var/log/${pkg.name}/${pkg.name}.log
+
+ /var/log/${pkg.name}/${pkg.name}.%d{yyyy-MM-dd}.%i.log
+ 100MB
+ 30
+ 3GB
+
+
+ %d{ISO8601} [%thread] %-5level %logger{36} - %msg%n
+
+
+
+
+
+ %d{ISO8601} [%thread] %-5level %logger{36} - %msg%n
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/msa/transport/mqtt/docker/start-tb-mqtt-transport.sh b/msa/transport/mqtt/docker/start-tb-mqtt-transport.sh
new file mode 100755
index 0000000000..8fe06d272e
--- /dev/null
+++ b/msa/transport/mqtt/docker/start-tb-mqtt-transport.sh
@@ -0,0 +1,29 @@
+#!/bin/bash
+#
+# 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.
+#
+
+CONF_FOLDER="${pkg.installFolder}/conf"
+jarfile=${pkg.installFolder}/bin/${pkg.name}.jar
+configfile=${pkg.name}.conf
+
+source "${CONF_FOLDER}/${configfile}"
+
+echo "Starting '${project.name}' ..."
+
+exec java -cp ${jarfile} $JAVA_OPTS -Dloader.main=org.thingsboard.server.mqtt.ThingsboardMqttTransportApplication \
+ -Dspring.jpa.hibernate.ddl-auto=none \
+ -Dlogging.config=${CONF_FOLDER}/logback.xml \
+ org.springframework.boot.loader.PropertiesLauncher
diff --git a/msa/transport/mqtt/docker/tb-mqtt-transport.conf b/msa/transport/mqtt/docker/tb-mqtt-transport.conf
new file mode 100644
index 0000000000..6d4c54aad1
--- /dev/null
+++ b/msa/transport/mqtt/docker/tb-mqtt-transport.conf
@@ -0,0 +1,23 @@
+#
+# 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.
+#
+
+export JAVA_OPTS="$JAVA_OPTS -Xloggc:@pkg.logFolder@/gc.log -XX:+IgnoreUnrecognizedVMOptions -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDetails -XX:+PrintGCDateStamps"
+export JAVA_OPTS="$JAVA_OPTS -XX:+PrintHeapAtGC -XX:+PrintTenuringDistribution -XX:+PrintGCApplicationStoppedTime -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10"
+export JAVA_OPTS="$JAVA_OPTS -XX:GCLogFileSize=10M -XX:-UseBiasedLocking -XX:+UseTLAB -XX:+ResizeTLAB -XX:+PerfDisableSharedMem -XX:+UseCondCardMark"
+export JAVA_OPTS="$JAVA_OPTS -XX:CMSWaitDuration=10000 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+CMSParallelInitialMarkEnabled"
+export JAVA_OPTS="$JAVA_OPTS -XX:+CMSEdenChunksRecordAlways -XX:CMSInitiatingOccupancyFraction=75 -XX:+UseCMSInitiatingOccupancyOnly -XX:+ExitOnOutOfMemoryError"
+export LOG_FILENAME=${pkg.name}.out
+export LOADER_PATH=${pkg.installFolder}/conf
diff --git a/msa/transport/mqtt/pom.xml b/msa/transport/mqtt/pom.xml
new file mode 100644
index 0000000000..975f9fcdd0
--- /dev/null
+++ b/msa/transport/mqtt/pom.xml
@@ -0,0 +1,137 @@
+
+
+ 4.0.0
+
+ org.thingsboard.msa
+ 2.2.0-SNAPSHOT
+ transport
+
+ org.thingsboard.msa.transport
+ mqtt
+ pom
+
+ ThingsBoard MQTT Transport Microservice
+ https://thingsboard.io
+ ThingsBoard MQTT Transport Microservice
+
+
+ UTF-8
+ ${basedir}/../../..
+ tb-mqtt-transport
+ tb-mqtt-transport
+ thingsboard
+ /var/log/${pkg.name}
+ /usr/share/${pkg.name}
+
+
+
+
+ org.thingsboard.transport
+ mqtt
+ ${project.version}
+ deb
+ deb
+ provided
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-dependency-plugin
+
+
+ copy-tb-mqtt-transport-deb
+ package
+
+ copy
+
+
+
+
+ org.thingsboard.transport
+ mqtt
+ deb
+ deb
+ ${pkg.name}.deb
+ ${project.build.directory}
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-resources-plugin
+
+
+ copy-docker-config
+ process-resources
+
+ copy-resources
+
+
+ ${project.build.directory}
+
+
+ docker
+ true
+
+
+
+
+
+
+
+ com.spotify
+ dockerfile-maven-plugin
+
+
+ build-docker-image
+ pre-integration-test
+
+ build
+
+
+
+
+ ${dockerfile.skip}
+ ${docker.repo}/${docker.name}
+ ${project.version}
+ true
+ false
+ ${project.build.directory}
+
+
+
+
+
+
+ jenkins
+ Jenkins Repository
+ http://repo.jenkins-ci.org/releases
+
+ false
+
+
+
+
diff --git a/msa/transport/pom.xml b/msa/transport/pom.xml
new file mode 100644
index 0000000000..9f091e3d20
--- /dev/null
+++ b/msa/transport/pom.xml
@@ -0,0 +1,55 @@
+
+
+ 4.0.0
+
+ org.thingsboard
+ 2.2.0-SNAPSHOT
+ msa
+
+ org.thingsboard.msa
+ transport
+ pom
+
+ ThingsBoard Transport Microservices
+ https://thingsboard.io
+
+
+ ${basedir}/../..
+
+
+
+ mqtt
+ http
+ coap
+
+
+
+
+
+
+ com.spotify
+ dockerfile-maven-plugin
+ 1.4.5
+
+
+
+
+
+
diff --git a/pom.xml b/pom.xml
index d6db123ebe..684c3d02a4 100755
--- a/pom.xml
+++ b/pom.xml
@@ -355,18 +355,23 @@
${project.version}
- org.thingsboard.transport
- http
+ org.thingsboard.common.transport
+ transport-api
${project.version}
- org.thingsboard.transport
- coap
+ org.thingsboard.common.transport
+ mqtt
${project.version}
- org.thingsboard.transport
- mqtt
+ org.thingsboard.common.transport
+ http
+ ${project.version}
+
+
+ org.thingsboard.common.transport
+ coap
${project.version}
diff --git a/rule-engine/rule-engine-components/pom.xml b/rule-engine/rule-engine-components/pom.xml
index 6390929911..c22ce784fa 100644
--- a/rule-engine/rule-engine-components/pom.xml
+++ b/rule-engine/rule-engine-components/pom.xml
@@ -45,8 +45,8 @@
provided
- org.thingsboard.common
- transport
+ org.thingsboard.common.transport
+ transport-api
provided
@@ -71,6 +71,10 @@
com.google.guava
guava
+
+ com.google.code.gson
+ gson
+
org.springframework
spring-web
diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCopyAttributesToEntityViewNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCopyAttributesToEntityViewNode.java
index fc0ff28749..459e35bd21 100644
--- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCopyAttributesToEntityViewNode.java
+++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCopyAttributesToEntityViewNode.java
@@ -88,8 +88,7 @@ public class TbCopyAttributesToEntityViewNode implements TbNode {
if ((endTime != 0 && endTime > now && startTime < now) || (endTime == 0 && startTime < now)) {
if (DataConstants.ATTRIBUTES_UPDATED.equals(msg.getType()) ||
SessionMsgType.POST_ATTRIBUTES_REQUEST.name().equals(msg.getType())) {
- Set attributes =
- JsonConverter.convertToAttributes(new JsonParser().parse(msg.getData())).getAttributes();
+ Set attributes = JsonConverter.convertToAttributes(new JsonParser().parse(msg.getData()));
List filteredAttributes =
attributes.stream().filter(attr -> attributeContainsInEntityView(scope, attr.getKey(), entityView)).collect(Collectors.toList());
ctx.getTelemetryService().saveAndNotify(entityView.getId(), scope, filteredAttributes,
diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNode.java
index 5cdb04ed5b..12dbc6dd89 100644
--- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNode.java
+++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNode.java
@@ -63,7 +63,7 @@ public class TbMsgAttributesNode implements TbNode {
}
String src = msg.getData();
- Set attributes = JsonConverter.convertToAttributes(new JsonParser().parse(src)).getAttributes();
+ Set attributes = JsonConverter.convertToAttributes(new JsonParser().parse(src));
ctx.getTelemetryService().saveAndNotify(msg.getOriginator(), config.getScope(), new ArrayList<>(attributes), new TelemetryNodeCallback(ctx, msg));
if (msg.getOriginator().getEntityType() == EntityType.DEVICE && DataConstants.SHARED_SCOPE.equals(config.getScope())) {
ctx.getTelemetryService().onSharedAttributesUpdate(ctx.getTenantId(), new DeviceId(msg.getOriginator().getId()), attributes);
diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java
index efdf4af3f2..4cb9999969 100644
--- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java
+++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java
@@ -29,7 +29,6 @@ import org.thingsboard.server.common.data.kv.KvEntry;
import org.thingsboard.server.common.data.kv.TsKvEntry;
import org.thingsboard.server.common.data.plugin.ComponentType;
import org.thingsboard.server.common.msg.TbMsg;
-import org.thingsboard.server.common.msg.core.TelemetryUploadRequest;
import org.thingsboard.server.common.msg.session.SessionMsgType;
import org.thingsboard.server.common.transport.adaptor.JsonConverter;
@@ -68,13 +67,13 @@ public class TbMsgTimeseriesNode implements TbNode {
if (!StringUtils.isEmpty(tsStr)) {
try {
ts = Long.parseLong(tsStr);
- } catch (NumberFormatException e) {}
+ } catch (NumberFormatException e) {
+ }
} else {
ts = System.currentTimeMillis();
}
String src = msg.getData();
- TelemetryUploadRequest telemetryUploadRequest = JsonConverter.convertToTelemetry(new JsonParser().parse(src), ts);
- Map> tsKvMap = telemetryUploadRequest.getData();
+ Map> tsKvMap = JsonConverter.convertToTelemetry(new JsonParser().parse(src), ts);
if (tsKvMap == null) {
ctx.tellFailure(msg, new IllegalArgumentException("Msg body is empty: " + src));
return;
diff --git a/transport/coap/build.gradle b/transport/coap/build.gradle
new file mode 100644
index 0000000000..6d54cb4789
--- /dev/null
+++ b/transport/coap/build.gradle
@@ -0,0 +1,140 @@
+/**
+ * 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.
+ */
+import org.apache.tools.ant.filters.ReplaceTokens
+
+buildscript {
+ ext {
+ osPackageVersion = "3.8.0"
+ }
+ repositories {
+ jcenter()
+ }
+ dependencies {
+ classpath("com.netflix.nebula:gradle-ospackage-plugin:${osPackageVersion}")
+ }
+}
+
+apply plugin: "nebula.ospackage"
+
+buildDir = projectBuildDir
+version = projectVersion
+distsDirName = "./"
+
+// OS Package plugin configuration
+ospackage {
+ packageName = pkgName
+ version = "${project.version}"
+ release = 1
+ os = LINUX
+ type = BINARY
+
+ into pkgInstallFolder
+
+ user pkgName
+ permissionGroup pkgName
+
+ // Copy the actual .jar file
+ from(mainJar) {
+ // Strip the version from the jar filename
+ rename { String fileName ->
+ "${pkgName}.jar"
+ }
+ fileMode 0500
+ into "bin"
+ }
+
+ // Copy the config files
+ from("target/conf") {
+ exclude "${pkgName}.conf"
+ fileType CONFIG | NOREPLACE
+ fileMode 0754
+ into "conf"
+ }
+
+}
+
+// Configure our RPM build task
+buildRpm {
+
+ arch = NOARCH
+
+ version = projectVersion.replace('-', '')
+ archiveName = "${pkgName}.rpm"
+
+ requires("java-1.8.0")
+
+ from("target/conf") {
+ include "${pkgName}.conf"
+ filter(ReplaceTokens, tokens: ['pkg.platform': 'rpm'])
+ fileType CONFIG | NOREPLACE
+ fileMode 0754
+ into "${pkgInstallFolder}/conf"
+ }
+
+ preInstall file("${buildDir}/control/rpm/preinst")
+ postInstall file("${buildDir}/control/rpm/postinst")
+ preUninstall file("${buildDir}/control/rpm/prerm")
+ postUninstall file("${buildDir}/control/rpm/postrm")
+
+ user pkgName
+ permissionGroup pkgName
+
+ // Copy the system unit files
+ from("${buildDir}/control/${pkgName}.service") {
+ addParentDirs = false
+ fileMode 0644
+ into "/usr/lib/systemd/system"
+ }
+
+ directory(pkgLogFolder, 0755)
+ link("${pkgInstallFolder}/bin/${pkgName}.yml", "${pkgInstallFolder}/conf/${pkgName}.yml")
+ link("/etc/${pkgName}/conf", "${pkgInstallFolder}/conf")
+}
+
+// Same as the buildRpm task
+buildDeb {
+
+ arch = "all"
+
+ archiveName = "${pkgName}.deb"
+
+ requires("openjdk-8-jre").or("java8-runtime").or("oracle-java8-installer").or("openjdk-8-jre-headless")
+
+ from("target/conf") {
+ include "${pkgName}.conf"
+ filter(ReplaceTokens, tokens: ['pkg.platform': 'deb'])
+ fileType CONFIG | NOREPLACE
+ fileMode 0754
+ into "${pkgInstallFolder}/conf"
+ }
+
+ configurationFile("${pkgInstallFolder}/conf/${pkgName}.conf")
+ configurationFile("${pkgInstallFolder}/conf/${pkgName}.yml")
+ configurationFile("${pkgInstallFolder}/conf/logback.xml")
+
+ preInstall file("${buildDir}/control/deb/preinst")
+ postInstall file("${buildDir}/control/deb/postinst")
+ preUninstall file("${buildDir}/control/deb/prerm")
+ postUninstall file("${buildDir}/control/deb/postrm")
+
+ user pkgName
+ permissionGroup pkgName
+
+ directory(pkgLogFolder, 0755)
+ link("/etc/init.d/${pkgName}", "${pkgInstallFolder}/bin/${pkgName}.jar")
+ link("${pkgInstallFolder}/bin/${pkgName}.yml", "${pkgInstallFolder}/conf/${pkgName}.yml")
+ link("/etc/${pkgName}/conf", "${pkgInstallFolder}/conf")
+}
diff --git a/transport/coap/pom.xml b/transport/coap/pom.xml
index a7015f0a35..a5e8107ab6 100644
--- a/transport/coap/pom.xml
+++ b/transport/coap/pom.xml
@@ -27,42 +27,37 @@
coap
jar
- Thingsboard COAP Transport
+ Thingsboard CoAP Transport Service
https://thingsboard.io
UTF-8
${basedir}/../..
+ tb-coap-transport
+ /var/log/${pkg.name}
+ /usr/share/${pkg.name}
+ ${project.build.directory}/windows
- org.thingsboard.common
- transport
-
-
- org.eclipse.californium
- californium-core
-
-
- org.springframework
- spring-context
+ org.thingsboard.common.transport
+ coap
- org.slf4j
- slf4j-api
-
-
- org.slf4j
- log4j-over-slf4j
+ org.thingsboard.common
+ queue
- ch.qos.logback
- logback-core
+ org.springframework.boot
+ spring-boot-starter-web
- ch.qos.logback
- logback-classic
+ com.sun.winsw
+ winsw
+ bin
+ exe
+ provided
org.springframework.boot
@@ -81,4 +76,266 @@
+
+ ${pkg.name}-${project.version}
+
+
+ ${project.basedir}/src/main/resources
+
+
+
+
+ org.apache.maven.plugins
+ maven-resources-plugin
+
+
+ copy-conf
+ process-resources
+
+ copy-resources
+
+
+ ${project.build.directory}/conf
+
+
+ src/main/resources
+
+ logback.xml
+
+ false
+
+
+
+
+
+ copy-service-conf
+ process-resources
+
+ copy-resources
+
+
+ ${project.build.directory}/conf
+
+
+ src/main/conf
+ true
+
+
+
+ src/main/filters/unix.properties
+
+
+
+
+ copy-win-conf
+ process-resources
+
+ copy-resources
+
+
+ ${pkg.win.dist}/conf
+
+
+ src/main/resources
+
+ logback.xml
+
+ false
+
+
+ src/main/conf
+
+ tb-coap-transport.conf
+
+ true
+
+
+
+ src/main/filters/windows.properties
+
+
+
+
+ copy-control
+ process-resources
+
+ copy-resources
+
+
+ ${project.build.directory}/control
+
+
+ src/main/scripts/control
+ true
+
+
+
+ src/main/filters/unix.properties
+
+
+
+
+ copy-windows-control
+ process-resources
+
+ copy-resources
+
+
+ ${pkg.win.dist}
+
+
+ src/main/scripts/windows
+ true
+
+
+
+ src/main/filters/windows.properties
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-dependency-plugin
+
+
+ copy-winsw-service
+ package
+
+ copy
+
+
+
+
+ com.sun.winsw
+ winsw
+ bin
+ exe
+ service.exe
+
+
+ ${pkg.win.dist}
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+
+
+ **/logback.xml
+
+
+
+ ThingsBoard CoAP Transport Service
+ ${project.version}
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+ org.thingsboard.server.coap.ThingsboardCoapTransportApplication
+ boot
+ ZIP
+ true
+ true
+
+ ${pkg.installFolder}/conf
+ ${pkg.unixLogFolder}
+ ${pkg.name}.out
+ ${pkg.name}
+
+
+
+
+
+ repackage
+
+
+
+
+
+ org.fortasoft
+ gradle-maven-plugin
+
+
+ build
+ buildDeb
+ buildRpm
+
+
+ -PprojectBuildDir=${project.build.directory}
+ -PprojectVersion=${project.version}
+ -PmainJar=${project.build.directory}/${project.build.finalName}-boot.${project.packaging}
+ -PpkgName=${pkg.name}
+ -PpkgInstallFolder=${pkg.installFolder}
+ -PpkgLogFolder=${pkg.unixLogFolder}
+
+
+
+
+ package
+
+ invoke
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+
+ ${pkg.name}
+
+ src/main/assembly/windows.xml
+
+
+
+
+ assembly
+ package
+
+ single
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-install-plugin
+
+ ${project.build.directory}/${pkg.name}.deb
+ ${project.artifactId}
+ ${project.groupId}
+ ${project.version}
+ deb
+ deb
+
+
+
+ install-deb
+ package
+
+ install-file
+
+
+
+
+
+
+
+
+ jenkins
+ Jenkins Repository
+ http://repo.jenkins-ci.org/releases
+
+ false
+
+
+
diff --git a/transport/coap/src/main/assembly/windows.xml b/transport/coap/src/main/assembly/windows.xml
new file mode 100644
index 0000000000..82da34ed13
--- /dev/null
+++ b/transport/coap/src/main/assembly/windows.xml
@@ -0,0 +1,71 @@
+
+
+ windows
+
+
+ zip
+
+
+
+
+
+ ${pkg.win.dist}
+ logs
+
+ */**
+
+
+
+ ${pkg.win.dist}/conf
+ conf
+ windows
+
+
+
+
+
+ ${project.build.directory}/${project.build.finalName}-boot.${project.packaging}
+ lib
+ ${pkg.name}.jar
+
+
+ ${pkg.win.dist}/service.exe
+
+ ${pkg.name}.exe
+
+
+ ${pkg.win.dist}/service.xml
+
+ ${pkg.name}.xml
+ windows
+
+
+ ${pkg.win.dist}/install.bat
+
+ windows
+
+
+ ${pkg.win.dist}/uninstall.bat
+
+ windows
+
+
+
diff --git a/transport/coap/src/main/conf/logback.xml b/transport/coap/src/main/conf/logback.xml
new file mode 100644
index 0000000000..f36469d5f2
--- /dev/null
+++ b/transport/coap/src/main/conf/logback.xml
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+ ${pkg.logFolder}/${pkg.name}.log
+
+ ${pkg.logFolder}/${pkg.name}.%d{yyyy-MM-dd}.%i.log
+ 100MB
+ 30
+ 3GB
+
+
+ %d{ISO8601} [%thread] %-5level %logger{36} - %msg%n
+
+
+
+
+
+
+
+
+
+
diff --git a/transport/coap/src/main/conf/tb-coap-transport.conf b/transport/coap/src/main/conf/tb-coap-transport.conf
new file mode 100644
index 0000000000..0afa91c18e
--- /dev/null
+++ b/transport/coap/src/main/conf/tb-coap-transport.conf
@@ -0,0 +1,23 @@
+#
+# 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.
+#
+
+export JAVA_OPTS="$JAVA_OPTS -Xloggc:@pkg.logFolder@/gc.log -XX:+IgnoreUnrecognizedVMOptions -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDetails -XX:+PrintGCDateStamps"
+export JAVA_OPTS="$JAVA_OPTS -XX:+PrintHeapAtGC -XX:+PrintTenuringDistribution -XX:+PrintGCApplicationStoppedTime -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10"
+export JAVA_OPTS="$JAVA_OPTS -XX:GCLogFileSize=10M -XX:-UseBiasedLocking -XX:+UseTLAB -XX:+ResizeTLAB -XX:+PerfDisableSharedMem -XX:+UseCondCardMark"
+export JAVA_OPTS="$JAVA_OPTS -XX:CMSWaitDuration=10000 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+CMSParallelInitialMarkEnabled"
+export JAVA_OPTS="$JAVA_OPTS -XX:+CMSEdenChunksRecordAlways -XX:CMSInitiatingOccupancyFraction=75 -XX:+UseCMSInitiatingOccupancyOnly"
+export LOG_FILENAME=${pkg.name}.out
+export LOADER_PATH=${pkg.installFolder}/conf
diff --git a/transport/coap/src/main/filters/unix.properties b/transport/coap/src/main/filters/unix.properties
new file mode 100644
index 0000000000..8967278673
--- /dev/null
+++ b/transport/coap/src/main/filters/unix.properties
@@ -0,0 +1 @@
+pkg.logFolder=${pkg.unixLogFolder}
\ No newline at end of file
diff --git a/transport/coap/src/main/filters/windows.properties b/transport/coap/src/main/filters/windows.properties
new file mode 100644
index 0000000000..a6e48d91ba
--- /dev/null
+++ b/transport/coap/src/main/filters/windows.properties
@@ -0,0 +1,2 @@
+pkg.logFolder=${BASE}\\logs
+pkg.winWrapperLogFolder=%BASE%\\logs
diff --git a/transport/coap/src/main/java/org/thingsboard/server/coap/ThingsboardCoapTransportApplication.java b/transport/coap/src/main/java/org/thingsboard/server/coap/ThingsboardCoapTransportApplication.java
new file mode 100644
index 0000000000..1ad86dd166
--- /dev/null
+++ b/transport/coap/src/main/java/org/thingsboard/server/coap/ThingsboardCoapTransportApplication.java
@@ -0,0 +1,48 @@
+/**
+ * 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.coap;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.SpringBootConfiguration;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.scheduling.annotation.EnableAsync;
+import org.springframework.scheduling.annotation.EnableScheduling;
+
+import java.util.Arrays;
+
+@SpringBootConfiguration
+@EnableAsync
+@EnableScheduling
+@ComponentScan({"org.thingsboard.server.coap", "org.thingsboard.server.common", "org.thingsboard.server.transport.coap", "org.thingsboard.server.kafka"})
+public class ThingsboardCoapTransportApplication {
+
+ private static final String SPRING_CONFIG_NAME_KEY = "--spring.config.name";
+ private static final String DEFAULT_SPRING_CONFIG_PARAM = SPRING_CONFIG_NAME_KEY + "=" + "tb-coap-transport";
+
+ public static void main(String[] args) {
+ SpringApplication.run(ThingsboardCoapTransportApplication.class, updateArguments(args));
+ }
+
+ private static String[] updateArguments(String[] args) {
+ if (Arrays.stream(args).noneMatch(arg -> arg.startsWith(SPRING_CONFIG_NAME_KEY))) {
+ String[] modifiedArgs = new String[args.length + 1];
+ System.arraycopy(args, 0, modifiedArgs, 0, args.length);
+ modifiedArgs[args.length] = DEFAULT_SPRING_CONFIG_PARAM;
+ return modifiedArgs;
+ }
+ return args;
+ }
+}
diff --git a/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java b/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java
deleted file mode 100644
index 8a596d7119..0000000000
--- a/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java
+++ /dev/null
@@ -1,243 +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.transport.coap;
-
-import java.lang.reflect.Field;
-import java.util.List;
-import java.util.Optional;
-
-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.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.thingsboard.server.common.data.id.SessionId;
-import org.thingsboard.server.common.data.security.DeviceCredentialsFilter;
-import org.thingsboard.server.common.data.security.DeviceTokenCredentials;
-import org.thingsboard.server.common.msg.session.*;
-import org.thingsboard.server.common.transport.SessionMsgProcessor;
-import org.thingsboard.server.common.transport.adaptor.AdaptorException;
-import org.thingsboard.server.common.transport.auth.DeviceAuthService;
-import org.thingsboard.server.common.transport.quota.QuotaService;
-import org.thingsboard.server.transport.coap.adaptors.CoapTransportAdaptor;
-import org.thingsboard.server.transport.coap.session.CoapExchangeObserverProxy;
-import org.thingsboard.server.transport.coap.session.CoapSessionCtx;
-import org.springframework.util.ReflectionUtils;
-
-@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 CoapTransportAdaptor adaptor;
- private final SessionMsgProcessor processor;
- private final DeviceAuthService authService;
- private final QuotaService quotaService;
- private final Field observerField;
- private final long timeout;
-
- public CoapTransportResource(SessionMsgProcessor processor, DeviceAuthService authService, CoapTransportAdaptor adaptor, String name,
- long timeout, QuotaService quotaService) {
- super(name);
- this.processor = processor;
- this.authService = authService;
- this.quotaService = quotaService;
- this.adaptor = adaptor;
- this.timeout = timeout;
- // 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(quotaService.isQuotaExceeded(exchange.getSourceAddress().getHostAddress())) {
- log.warn("COAP Quota exceeded for [{}:{}] . Disconnect", exchange.getSourceAddress().getHostAddress(), exchange.getSourcePort());
- exchange.respond(ResponseCode.BAD_REQUEST);
- return;
- }
-
- Optional 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;
- }
- Optional sessionId = processRequest(exchange, sessionMsgType);
- if (sessionId.isPresent()) {
- if (exchange.getRequestOptions().getObserve() == 1) {
- exchange.respond(ResponseCode.VALID);
- }
- }
- }
-
- @Override
- public void handlePOST(CoapExchange exchange) {
- if(quotaService.isQuotaExceeded(exchange.getSourceAddress().getHostAddress())) {
- log.warn("COAP Quota exceeded for [{}:{}] . Disconnect", exchange.getSourceAddress().getHostAddress(), exchange.getSourcePort());
- exchange.respond(ResponseCode.BAD_REQUEST);
- return;
- }
-
- Optional 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 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 Optional processRequest(CoapExchange exchange, SessionMsgType type) {
- log.trace("Processing {}", exchange.advanced().getRequest());
- exchange.accept();
- Exchange advanced = exchange.advanced();
- Request request = advanced.getRequest();
-
- Optional credentials = decodeCredentials(request);
- if (!credentials.isPresent()) {
- exchange.respond(ResponseCode.BAD_REQUEST);
- return Optional.empty();
- }
-
- CoapSessionCtx ctx = new CoapSessionCtx(exchange, adaptor, processor, authService, timeout);
-
- if (!ctx.login(credentials.get())) {
- exchange.respond(ResponseCode.UNAUTHORIZED);
- return Optional.empty();
- }
-
- AdaptorToSessionActorMsg msg;
- try {
- switch (type) {
- case GET_ATTRIBUTES_REQUEST:
- case POST_ATTRIBUTES_REQUEST:
- case POST_TELEMETRY_REQUEST:
- case TO_DEVICE_RPC_RESPONSE:
- case TO_SERVER_RPC_REQUEST:
- ctx.setSessionType(SessionType.SYNC);
- msg = adaptor.convertToActorMsg(ctx, type, request);
- break;
- case SUBSCRIBE_ATTRIBUTES_REQUEST:
- case SUBSCRIBE_RPC_COMMANDS_REQUEST:
- ExchangeObserver systemObserver = (ExchangeObserver) observerField.get(advanced);
- advanced.setObserver(new CoapExchangeObserverProxy(systemObserver, ctx));
- ctx.setSessionType(SessionType.ASYNC);
- msg = adaptor.convertToActorMsg(ctx, type, request);
- break;
- case UNSUBSCRIBE_ATTRIBUTES_REQUEST:
- case UNSUBSCRIBE_RPC_COMMANDS_REQUEST:
- ctx.setSessionType(SessionType.ASYNC);
- msg = adaptor.convertToActorMsg(ctx, type, request);
- break;
- default:
- log.trace("[{}] Unsupported msg type: {}", ctx.getSessionId(), type);
- throw new IllegalArgumentException("Unsupported msg type: " + type);
- }
- log.trace("Processing msg: {}", msg);
- processor.process(new BasicTransportToDeviceSessionActorMsg(ctx.getDevice(), msg));
- } catch (AdaptorException e) {
- log.debug("Failed to decode payload {}", e);
- exchange.respond(ResponseCode.BAD_REQUEST, e.getMessage());
- return Optional.empty();
- } catch (IllegalArgumentException | IllegalAccessException e) {
- log.debug("Failed to process payload {}", e);
- exchange.respond(ResponseCode.INTERNAL_SERVER_ERROR, e.getMessage());
- }
- return Optional.of(ctx.getSessionId());
- }
-
- private Optional decodeCredentials(Request request) {
- List uriPath = request.getOptions().getUriPath();
- DeviceCredentialsFilter credentials = null;
- if (uriPath.size() >= ACCESS_TOKEN_POSITION) {
- credentials = new DeviceTokenCredentials(uriPath.get(ACCESS_TOKEN_POSITION - 1));
- }
- return Optional.ofNullable(credentials);
- }
-
- private Optional getFeatureType(Request request) {
- List 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 getRequestId(Request request) {
- List 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;
- }
-
-}
diff --git a/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/CoapTransportAdaptor.java b/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/CoapTransportAdaptor.java
deleted file mode 100644
index d9a04b163a..0000000000
--- a/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/CoapTransportAdaptor.java
+++ /dev/null
@@ -1,25 +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.transport.coap.adaptors;
-
-import org.eclipse.californium.core.coap.Request;
-import org.eclipse.californium.core.coap.Response;
-import org.thingsboard.server.common.transport.TransportAdaptor;
-import org.thingsboard.server.transport.coap.session.CoapSessionCtx;
-
-public interface CoapTransportAdaptor extends TransportAdaptor {
-
-}
diff --git a/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/JsonCoapAdaptor.java b/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/JsonCoapAdaptor.java
deleted file mode 100644
index 1c9631129f..0000000000
--- a/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/JsonCoapAdaptor.java
+++ /dev/null
@@ -1,265 +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.transport.coap.adaptors;
-
-import java.util.*;
-
-import com.google.gson.JsonElement;
-import lombok.extern.slf4j.Slf4j;
-import org.eclipse.californium.core.coap.CoAP.ResponseCode;
-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.core.*;
-import org.thingsboard.server.common.msg.kv.AttributesKVMsg;
-import org.thingsboard.server.common.msg.session.AdaptorToSessionActorMsg;
-import org.thingsboard.server.common.msg.session.BasicAdaptorToSessionActorMsg;
-import org.thingsboard.server.common.msg.session.FromDeviceMsg;
-import org.thingsboard.server.common.msg.session.SessionMsgType;
-import org.thingsboard.server.common.msg.session.SessionActorToAdaptorMsg;
-import org.thingsboard.server.common.msg.session.SessionContext;
-import org.thingsboard.server.common.msg.session.ToDeviceMsg;
-import org.thingsboard.server.common.msg.session.ex.ProcessingTimeoutException;
-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.JsonObject;
-import com.google.gson.JsonParser;
-import com.google.gson.JsonSyntaxException;
-import org.thingsboard.server.transport.coap.CoapTransportResource;
-import org.thingsboard.server.transport.coap.session.CoapSessionCtx;
-
-@Component("JsonCoapAdaptor")
-@Slf4j
-public class JsonCoapAdaptor implements CoapTransportAdaptor {
-
- @Override
- public AdaptorToSessionActorMsg convertToActorMsg(CoapSessionCtx ctx, SessionMsgType type, Request inbound) throws AdaptorException {
- FromDeviceMsg msg = null;
- switch (type) {
- case POST_TELEMETRY_REQUEST:
- msg = convertToTelemetryUploadRequest(ctx, inbound);
- break;
- case POST_ATTRIBUTES_REQUEST:
- msg = convertToUpdateAttributesRequest(ctx, inbound);
- break;
- case GET_ATTRIBUTES_REQUEST:
- msg = convertToGetAttributesRequest(ctx, inbound);
- break;
- case SUBSCRIBE_RPC_COMMANDS_REQUEST:
- msg = new RpcSubscribeMsg();
- break;
- case UNSUBSCRIBE_RPC_COMMANDS_REQUEST:
- msg = new RpcUnsubscribeMsg();
- break;
- case TO_DEVICE_RPC_RESPONSE:
- msg = convertToDeviceRpcResponse(ctx, inbound);
- break;
- case TO_SERVER_RPC_REQUEST:
- msg = convertToServerRpcRequest(ctx, inbound);
- break;
- case SUBSCRIBE_ATTRIBUTES_REQUEST:
- msg = new AttributesSubscribeMsg();
- break;
- case UNSUBSCRIBE_ATTRIBUTES_REQUEST:
- msg = new AttributesUnsubscribeMsg();
- break;
- default:
- log.warn("[{}] Unsupported msg type: {}!", ctx.getSessionId(), type);
- throw new AdaptorException(new IllegalArgumentException("Unsupported msg type: " + type + "!"));
- }
- return new BasicAdaptorToSessionActorMsg(ctx, msg);
- }
-
- private FromDeviceMsg convertToDeviceRpcResponse(CoapSessionCtx ctx, Request inbound) throws AdaptorException {
- Optional requestId = CoapTransportResource.getRequestId(inbound);
- String payload = validatePayload(ctx, inbound);
- JsonObject response = new JsonParser().parse(payload).getAsJsonObject();
- return new ToDeviceRpcResponseMsg(
- requestId.orElseThrow(() -> new AdaptorException("Request id is missing!")),
- response.get("response").toString());
- }
-
- private FromDeviceMsg convertToServerRpcRequest(CoapSessionCtx ctx, Request inbound) throws AdaptorException {
-
- String payload = validatePayload(ctx, inbound);
-
- return JsonConverter.convertToServerRpcRequest(new JsonParser().parse(payload), 0);
- }
-
- @Override
- public Optional convertToAdaptorMsg(CoapSessionCtx ctx, SessionActorToAdaptorMsg source) throws AdaptorException {
- ToDeviceMsg msg = source.getMsg();
- switch (msg.getSessionMsgType()) {
- case STATUS_CODE_RESPONSE:
- case TO_DEVICE_RPC_RESPONSE_ACK:
- return Optional.of(convertStatusCodeResponse((StatusCodeResponse) msg));
- case GET_ATTRIBUTES_RESPONSE:
- return Optional.of(convertGetAttributesResponse((GetAttributesResponse) msg));
- case ATTRIBUTES_UPDATE_NOTIFICATION:
- return Optional.of(convertNotificationResponse(ctx, (AttributesUpdateNotification) msg));
- case TO_DEVICE_RPC_REQUEST:
- return Optional.of(convertToDeviceRpcRequest(ctx, (ToDeviceRpcRequestMsg) msg));
- case TO_SERVER_RPC_RESPONSE:
- return Optional.of(convertToServerRpcResponse(ctx, (ToServerRpcResponseMsg) msg));
- case RULE_ENGINE_ERROR:
- return Optional.of(convertToRuleEngineErrorResponse(ctx, (RuleEngineErrorMsg) msg));
- default:
- log.warn("[{}] Unsupported msg type: {}!", source.getSessionId(), msg.getSessionMsgType());
- throw new AdaptorException(new IllegalArgumentException("Unsupported msg type: " + msg.getSessionMsgType() + "!"));
- }
- }
-
- private Response convertToRuleEngineErrorResponse(CoapSessionCtx ctx, RuleEngineErrorMsg msg) {
- ResponseCode status = ResponseCode.INTERNAL_SERVER_ERROR;
- switch (msg.getError()) {
- case QUEUE_PUT_TIMEOUT:
- status = ResponseCode.GATEWAY_TIMEOUT;
- break;
- default:
- if (msg.getInSessionMsgType() == SessionMsgType.TO_SERVER_RPC_REQUEST) {
- status = ResponseCode.BAD_REQUEST;
- }
- break;
- }
- Response response = new Response(status);
- response.setPayload(JsonConverter.toErrorJson(msg.getErrorMsg()).toString());
- return response;
- }
-
- private Response convertNotificationResponse(CoapSessionCtx ctx, AttributesUpdateNotification msg) {
- return getObserveNotification(ctx, JsonConverter.toJson(msg.getData(), false));
- }
-
- private Response convertToDeviceRpcRequest(CoapSessionCtx ctx, ToDeviceRpcRequestMsg msg) {
- return getObserveNotification(ctx, JsonConverter.toJson(msg, true));
- }
-
- private Response getObserveNotification(CoapSessionCtx ctx, JsonObject json) {
- Response response = new Response(ResponseCode.CONTENT);
- response.getOptions().setObserve(ctx.nextSeqNumber());
- response.setPayload(json.toString());
- return response;
- }
-
- private AttributesUpdateRequest convertToUpdateAttributesRequest(SessionContext ctx, Request inbound) throws AdaptorException {
- String payload = validatePayload(ctx, inbound);
- try {
- return JsonConverter.convertToAttributes(new JsonParser().parse(payload));
- } catch (IllegalStateException | JsonSyntaxException ex) {
- throw new AdaptorException(ex);
- }
- }
-
- private FromDeviceMsg convertToGetAttributesRequest(SessionContext ctx, Request inbound) throws AdaptorException {
- List queryElements = inbound.getOptions().getUriQuery();
- if (queryElements != null && queryElements.size() > 0) {
- Set clientKeys = toKeys(ctx, queryElements, "clientKeys");
- Set sharedKeys = toKeys(ctx, queryElements, "sharedKeys");
- return new BasicGetAttributesRequest(0, clientKeys, sharedKeys);
- } else {
- return new BasicGetAttributesRequest(0);
- }
- }
-
- private Set toKeys(SessionContext ctx, List 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;
- }
- }
-
- private TelemetryUploadRequest convertToTelemetryUploadRequest(SessionContext ctx, Request inbound) throws AdaptorException {
- String payload = validatePayload(ctx, inbound);
- try {
- return JsonConverter.convertToTelemetry(new JsonParser().parse(payload));
- } catch (IllegalStateException | JsonSyntaxException ex) {
- throw new AdaptorException(ex);
- }
- }
-
- private Response convertStatusCodeResponse(StatusCodeResponse msg) {
- if (msg.isSuccess()) {
- Optional code = msg.getData();
- if (code.isPresent() && code.get() == 200) {
- return new Response(ResponseCode.VALID);
- } else {
- return new Response(ResponseCode.CREATED);
- }
- } else {
- return convertError(msg.getError());
- }
- }
-
- private String validatePayload(SessionContext ctx, Request inbound) throws AdaptorException {
- String payload = inbound.getPayloadString();
- if (payload == null) {
- log.warn("[{}] Payload is empty!", ctx.getSessionId());
- throw new AdaptorException(new IllegalArgumentException("Payload is empty!"));
- }
- return payload;
- }
-
- private Response convertToServerRpcResponse(SessionContext ctx, ToServerRpcResponseMsg msg) {
- if (msg.isSuccess()) {
- Response response = new Response(ResponseCode.CONTENT);
- JsonElement result = JsonConverter.toJson(msg);
- response.setPayload(result.toString());
- return response;
- } else {
- return convertError(Optional.of(new RuntimeException("Server RPC response is empty!")));
- }
- }
-
- private Response convertGetAttributesResponse(GetAttributesResponse msg) {
- if (msg.isSuccess()) {
- Optional payload = msg.getData();
- if (!payload.isPresent() || (payload.get().getClientAttributes().isEmpty() && payload.get().getSharedAttributes().isEmpty())) {
- return new Response(ResponseCode.NOT_FOUND);
- } else {
- Response response = new Response(ResponseCode.CONTENT);
- JsonObject result = JsonConverter.toJson(payload.get(), false);
- response.setPayload(result.toString());
- return response;
- }
- } else {
- return convertError(msg.getError());
- }
- }
-
- private Response convertError(Optional exception) {
- if (exception.isPresent()) {
- log.warn("Converting exception: {}", exception.get().getMessage(), exception.get());
- if (exception.get() instanceof ProcessingTimeoutException) {
- return new Response(ResponseCode.SERVICE_UNAVAILABLE);
- } else {
- return new Response(ResponseCode.INTERNAL_SERVER_ERROR);
- }
- } else {
- return new Response(ResponseCode.INTERNAL_SERVER_ERROR);
- }
- }
-
-}
diff --git a/transport/coap/src/main/java/org/thingsboard/server/transport/coap/session/CoapExchangeObserverProxy.java b/transport/coap/src/main/java/org/thingsboard/server/transport/coap/session/CoapExchangeObserverProxy.java
deleted file mode 100644
index 0c264817d5..0000000000
--- a/transport/coap/src/main/java/org/thingsboard/server/transport/coap/session/CoapExchangeObserverProxy.java
+++ /dev/null
@@ -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.transport.coap.session;
-
-import org.eclipse.californium.core.network.Exchange;
-import org.eclipse.californium.core.network.ExchangeObserver;
-
-public class CoapExchangeObserverProxy implements ExchangeObserver {
-
- private final ExchangeObserver proxy;
- private final CoapSessionCtx ctx;
-
- public CoapExchangeObserverProxy(ExchangeObserver proxy, CoapSessionCtx ctx) {
- super();
- this.proxy = proxy;
- this.ctx = ctx;
- }
-
- @Override
- public void completed(Exchange exchange) {
- proxy.completed(exchange);
- ctx.close();
- }
-
-}
diff --git a/transport/coap/src/main/java/org/thingsboard/server/transport/coap/session/CoapSessionCtx.java b/transport/coap/src/main/java/org/thingsboard/server/transport/coap/session/CoapSessionCtx.java
deleted file mode 100644
index 7a703b2be3..0000000000
--- a/transport/coap/src/main/java/org/thingsboard/server/transport/coap/session/CoapSessionCtx.java
+++ /dev/null
@@ -1,135 +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.transport.coap.session;
-
-import lombok.extern.slf4j.Slf4j;
-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.server.resources.CoapExchange;
-import org.thingsboard.server.common.data.id.SessionId;
-import org.thingsboard.server.common.msg.session.SessionActorToAdaptorMsg;
-import org.thingsboard.server.common.msg.session.SessionCtrlMsg;
-import org.thingsboard.server.common.msg.session.SessionType;
-import org.thingsboard.server.common.msg.session.ctrl.SessionCloseMsg;
-import org.thingsboard.server.common.msg.session.ex.SessionAuthException;
-import org.thingsboard.server.common.msg.session.ex.SessionException;
-import org.thingsboard.server.common.transport.SessionMsgProcessor;
-import org.thingsboard.server.common.transport.adaptor.AdaptorException;
-import org.thingsboard.server.common.transport.auth.DeviceAuthService;
-import org.thingsboard.server.common.transport.session.DeviceAwareSessionContext;
-import org.thingsboard.server.transport.coap.adaptors.CoapTransportAdaptor;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.concurrent.atomic.AtomicInteger;
-
-@Slf4j
-public class CoapSessionCtx extends DeviceAwareSessionContext {
-
- private final SessionId sessionId;
- private final CoapExchange exchange;
- private final CoapTransportAdaptor adaptor;
- private final String token;
- private final long timeout;
- private SessionType sessionType;
- private final AtomicInteger seqNumber = new AtomicInteger(2);
-
- public CoapSessionCtx(CoapExchange exchange, CoapTransportAdaptor adaptor, SessionMsgProcessor processor, DeviceAuthService authService, long timeout) {
- super(processor, authService);
- Request request = exchange.advanced().getRequest();
- this.token = request.getTokenString();
- this.sessionId = new CoapSessionId(request.getSource().getHostAddress(), request.getSourcePort(), this.token);
- this.exchange = exchange;
- this.adaptor = adaptor;
- this.timeout = timeout;
- }
-
-
- @Override
- public void onMsg(SessionActorToAdaptorMsg msg) throws SessionException {
- try {
- adaptor.convertToAdaptorMsg(this, msg).ifPresent(this::pushToNetwork);
- } catch (AdaptorException e) {
- logAndWrap(e);
- }
- }
-
- private void pushToNetwork(Response response) {
- exchange.respond(response);
- }
-
- private void logAndWrap(AdaptorException e) throws SessionException {
- log.warn("Failed to convert msg: {}", e.getMessage(), e);
- throw new SessionException(e);
- }
-
- @Override
- public void onMsg(SessionCtrlMsg msg) throws SessionException {
- log.debug("[{}] onCtrl: {}", sessionId, msg);
- if (msg instanceof SessionCloseMsg) {
- onSessionClose((SessionCloseMsg) msg);
- }
- }
-
- private void onSessionClose(SessionCloseMsg msg) {
- if (msg.isTimeout()) {
- exchange.respond(ResponseCode.SERVICE_UNAVAILABLE);
- } else if (msg.isCredentialsRevoked()) {
- exchange.respond(ResponseCode.UNAUTHORIZED);
- } else {
- exchange.respond(ResponseCode.INTERNAL_SERVER_ERROR);
- }
- }
-
- @Override
- public SessionId getSessionId() {
- return sessionId;
- }
-
- @Override
- public String toString() {
- return "CoapSessionCtx [sessionId=" + sessionId + "]";
- }
-
- @Override
- public boolean isClosed() {
- return exchange.advanced().isComplete() || exchange.advanced().isTimedOut();
- }
-
- public void close() {
- log.info("[{}] Closing processing context. Timeout: {}", sessionId, exchange.advanced().isTimedOut());
- processor.process(exchange.advanced().isTimedOut() ? SessionCloseMsg.onTimeout(sessionId) : SessionCloseMsg.onError(sessionId));
- }
-
- @Override
- public long getTimeout() {
- return timeout;
- }
-
- public void setSessionType(SessionType sessionType) {
- this.sessionType = sessionType;
- }
-
- @Override
- public SessionType getSessionType() {
- return sessionType;
- }
-
- public int nextSeqNumber() {
- return seqNumber.getAndIncrement();
- }
-}
diff --git a/transport/coap/src/main/java/org/thingsboard/server/transport/coap/session/CoapSessionId.java b/transport/coap/src/main/java/org/thingsboard/server/transport/coap/session/CoapSessionId.java
deleted file mode 100644
index 888fb2fc30..0000000000
--- a/transport/coap/src/main/java/org/thingsboard/server/transport/coap/session/CoapSessionId.java
+++ /dev/null
@@ -1,77 +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.transport.coap.session;
-
-import org.thingsboard.server.common.data.id.SessionId;
-
-public final class CoapSessionId implements SessionId {
-
- private final String clientAddress;
- private final int clientPort;
- private final String token;
-
- public CoapSessionId(String host, int port, String token) {
- super();
- this.clientAddress = host;
- this.clientPort = port;
- this.token = token;
- }
-
- @Override
- public int hashCode() {
- final int prime = 31;
- int result = 1;
- result = prime * result + ((clientAddress == null) ? 0 : clientAddress.hashCode());
- result = prime * result + clientPort;
- result = prime * result + ((token == null) ? 0 : token.hashCode());
- return result;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj)
- return true;
- if (obj == null)
- return false;
- if (getClass() != obj.getClass())
- return false;
- CoapSessionId other = (CoapSessionId) obj;
- if (clientAddress == null) {
- if (other.clientAddress != null)
- return false;
- } else if (!clientAddress.equals(other.clientAddress))
- return false;
- if (clientPort != other.clientPort)
- return false;
- if (token == null) {
- if (other.token != null)
- return false;
- } else if (!token.equals(other.token))
- return false;
- return true;
- }
-
- @Override
- public String toString() {
- return "CoapSessionId [clientAddress=" + clientAddress + ", clientPort=" + clientPort + ", token=" + token + "]";
- }
-
- @Override
- public String toUidStr() {
- return clientAddress + ":" + clientPort + ":" + token;
- }
-
-}
diff --git a/transport/coap/src/main/resources/logback.xml b/transport/coap/src/main/resources/logback.xml
new file mode 100644
index 0000000000..18864a9fab
--- /dev/null
+++ b/transport/coap/src/main/resources/logback.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+ %d{ISO8601} [%thread] %-5level %logger{36} - %msg%n
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/transport/coap/src/main/resources/tb-coap-transport.yml b/transport/coap/src/main/resources/tb-coap-transport.yml
new file mode 100644
index 0000000000..1790d822d2
--- /dev/null
+++ b/transport/coap/src/main/resources/tb-coap-transport.yml
@@ -0,0 +1,68 @@
+#
+# 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.
+#
+
+spring.main.web-environment: false
+spring.main.web-application-type: none
+
+# MQTT server parameters
+transport:
+ coap:
+ bind_address: "${COAP_BIND_ADDRESS:0.0.0.0}"
+ bind_port: "${COAP_BIND_PORT:5683}"
+ timeout: "${COAP_TIMEOUT:10000}"
+
+#Quota parameters
+quota:
+ host:
+ # Max allowed number of API requests in interval for single host
+ limit: "${QUOTA_HOST_LIMIT:10000}"
+ # Interval duration
+ intervalMs: "${QUOTA_HOST_INTERVAL_MS:60000}"
+ # Maximum silence duration for host after which Host removed from QuotaService. Must be bigger than intervalMs
+ ttlMs: "${QUOTA_HOST_TTL_MS:60000}"
+ # Interval for scheduled task that cleans expired records. TTL is used for expiring
+ cleanPeriodMs: "${QUOTA_HOST_CLEAN_PERIOD_MS:300000}"
+ # Enable Host API Limits
+ enabled: "${QUOTA_HOST_ENABLED:true}"
+ # Array of whitelist hosts
+ whitelist: "${QUOTA_HOST_WHITELIST:localhost,127.0.0.1}"
+ # Array of blacklist hosts
+ blacklist: "${QUOTA_HOST_BLACKLIST:}"
+ log:
+ topSize: 10
+ intervalMin: 2
+
+kafka:
+ enabled: true
+ bootstrap.servers: "${TB_KAFKA_SERVERS:localhost:9092}"
+ acks: "${TB_KAFKA_ACKS:all}"
+ retries: "${TB_KAFKA_RETRIES:1}"
+ 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}"
+ max_requests_timeout: "${TB_TRANSPORT_MAX_REQUEST_TIMEOUT:10000}"
+ response_poll_interval: "${TB_TRANSPORT_RESPONSE_POLL_INTERVAL_MS:25}"
+ response_auto_commit_interval: "${TB_TRANSPORT_RESPONSE_AUTO_COMMIT_INTERVAL_MS:100}"
+ rule_engine:
+ topic: "${TB_RULE_ENGINE_TOPIC:tb.rule-engine}"
+ notifications:
+ topic: "${TB_TRANSPORT_NOTIFICATIONS_TOPIC:tb.transport.notifications}"
+ poll_interval: "${TB_TRANSPORT_NOTIFICATIONS_POLL_INTERVAL_MS:25}"
+ auto_commit_interval: "${TB_TRANSPORT_NOTIFICATIONS_AUTO_COMMIT_INTERVAL_MS:100}"
diff --git a/transport/coap/src/main/scripts/control/deb/postinst b/transport/coap/src/main/scripts/control/deb/postinst
new file mode 100644
index 0000000000..d4066c027b
--- /dev/null
+++ b/transport/coap/src/main/scripts/control/deb/postinst
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+chown -R ${pkg.name}: ${pkg.logFolder}
+chown -R ${pkg.name}: ${pkg.installFolder}
+update-rc.d ${pkg.name} defaults
+
diff --git a/transport/coap/src/main/scripts/control/deb/postrm b/transport/coap/src/main/scripts/control/deb/postrm
new file mode 100644
index 0000000000..61865803c3
--- /dev/null
+++ b/transport/coap/src/main/scripts/control/deb/postrm
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+update-rc.d -f ${pkg.name} remove
diff --git a/transport/coap/src/main/scripts/control/deb/preinst b/transport/coap/src/main/scripts/control/deb/preinst
new file mode 100644
index 0000000000..6be5959285
--- /dev/null
+++ b/transport/coap/src/main/scripts/control/deb/preinst
@@ -0,0 +1,18 @@
+#!/bin/sh
+
+if ! getent group ${pkg.name} >/dev/null; then
+ addgroup --system ${pkg.name}
+fi
+
+if ! getent passwd ${pkg.name} >/dev/null; then
+ adduser --quiet \
+ --system \
+ --ingroup ${pkg.name} \
+ --quiet \
+ --disabled-login \
+ --disabled-password \
+ --home ${pkg.installFolder} \
+ --no-create-home \
+ -gecos "Thingsboard application" \
+ ${pkg.name}
+fi
diff --git a/transport/coap/src/main/scripts/control/deb/prerm b/transport/coap/src/main/scripts/control/deb/prerm
new file mode 100644
index 0000000000..898d3efd5c
--- /dev/null
+++ b/transport/coap/src/main/scripts/control/deb/prerm
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+if [ -e /var/run/${pkg.name}/${pkg.name}.pid ]; then
+ service ${pkg.name} stop
+fi
diff --git a/transport/coap/src/main/scripts/control/rpm/postinst b/transport/coap/src/main/scripts/control/rpm/postinst
new file mode 100644
index 0000000000..8a7a88f7e0
--- /dev/null
+++ b/transport/coap/src/main/scripts/control/rpm/postinst
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+chown -R ${pkg.name}: ${pkg.logFolder}
+chown -R ${pkg.name}: ${pkg.installFolder}
+
+if [ $1 -eq 1 ] ; then
+ # Initial installation
+ systemctl --no-reload enable ${pkg.name}.service >/dev/null 2>&1 || :
+fi
diff --git a/transport/coap/src/main/scripts/control/rpm/postrm b/transport/coap/src/main/scripts/control/rpm/postrm
new file mode 100644
index 0000000000..8e1f8a2048
--- /dev/null
+++ b/transport/coap/src/main/scripts/control/rpm/postrm
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+if [ $1 -ge 1 ] ; then
+ # Package upgrade, not uninstall
+ systemctl try-restart ${pkg.name}.service >/dev/null 2>&1 || :
+fi
diff --git a/transport/coap/src/main/scripts/control/rpm/preinst b/transport/coap/src/main/scripts/control/rpm/preinst
new file mode 100644
index 0000000000..e19fc884c8
--- /dev/null
+++ b/transport/coap/src/main/scripts/control/rpm/preinst
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+getent group ${pkg.name} >/dev/null || groupadd -r ${pkg.name}
+getent passwd ${pkg.name} >/dev/null || \
+useradd -d ${pkg.installFolder} -g ${pkg.name} -M -r ${pkg.name} -s /sbin/nologin \
+-c "Thingsboard application"
diff --git a/transport/coap/src/main/scripts/control/rpm/prerm b/transport/coap/src/main/scripts/control/rpm/prerm
new file mode 100644
index 0000000000..accb487b8e
--- /dev/null
+++ b/transport/coap/src/main/scripts/control/rpm/prerm
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+if [ $1 -eq 0 ] ; then
+ # Package removal, not upgrade
+ systemctl --no-reload disable --now ${pkg.name}.service > /dev/null 2>&1 || :
+fi
diff --git a/transport/coap/src/main/scripts/control/tb-coap-transport.service b/transport/coap/src/main/scripts/control/tb-coap-transport.service
new file mode 100644
index 0000000000..d456fc03c0
--- /dev/null
+++ b/transport/coap/src/main/scripts/control/tb-coap-transport.service
@@ -0,0 +1,11 @@
+[Unit]
+Description=${pkg.name}
+After=syslog.target
+
+[Service]
+User=${pkg.name}
+ExecStart=${pkg.installFolder}/bin/${pkg.name}.jar
+SuccessExitStatus=143
+
+[Install]
+WantedBy=multi-user.target
diff --git a/transport/coap/src/main/scripts/windows/install.bat b/transport/coap/src/main/scripts/windows/install.bat
new file mode 100644
index 0000000000..dba773663c
--- /dev/null
+++ b/transport/coap/src/main/scripts/windows/install.bat
@@ -0,0 +1,87 @@
+@ECHO OFF
+
+setlocal ENABLEEXTENSIONS
+
+@ECHO Detecting Java version installed.
+:CHECK_JAVA_64
+@ECHO Detecting if it is 64 bit machine
+set KEY_NAME="HKEY_LOCAL_MACHINE\Software\Wow6432Node\JavaSoft\Java Runtime Environment"
+set VALUE_NAME=CurrentVersion
+
+FOR /F "usebackq skip=2 tokens=1-3" %%A IN (`REG QUERY %KEY_NAME% /v %VALUE_NAME% 2^>nul`) DO (
+ set ValueName=%%A
+ set ValueType=%%B
+ set ValueValue=%%C
+)
+@ECHO CurrentVersion %ValueValue%
+
+SET KEY_NAME="%KEY_NAME:~1,-1%\%ValueValue%"
+SET VALUE_NAME=JavaHome
+
+if defined ValueName (
+ FOR /F "usebackq skip=2 tokens=1,2*" %%A IN (`REG QUERY %KEY_NAME% /v %VALUE_NAME% 2^>nul`) DO (
+ set ValueName2=%%A
+ set ValueType2=%%B
+ set JRE_PATH2=%%C
+
+ if defined ValueName2 (
+ set ValueName = %ValueName2%
+ set ValueType = %ValueType2%
+ set ValueValue = %JRE_PATH2%
+ )
+ )
+)
+
+IF NOT "%JRE_PATH2%" == "" GOTO JAVA_INSTALLED
+
+:CHECK_JAVA_32
+@ECHO Detecting if it is 32 bit machine
+set KEY_NAME="HKEY_LOCAL_MACHINE\Software\JavaSoft\Java Runtime Environment"
+set VALUE_NAME=CurrentVersion
+
+FOR /F "usebackq skip=2 tokens=1-3" %%A IN (`REG QUERY %KEY_NAME% /v %VALUE_NAME% 2^>nul`) DO (
+ set ValueName=%%A
+ set ValueType=%%B
+ set ValueValue=%%C
+)
+@ECHO CurrentVersion %ValueValue%
+
+SET KEY_NAME="%KEY_NAME:~1,-1%\%ValueValue%"
+SET VALUE_NAME=JavaHome
+
+if defined ValueName (
+ FOR /F "usebackq skip=2 tokens=1,2*" %%A IN (`REG QUERY %KEY_NAME% /v %VALUE_NAME% 2^>nul`) DO (
+ set ValueName2=%%A
+ set ValueType2=%%B
+ set JRE_PATH2=%%C
+
+ if defined ValueName2 (
+ set ValueName = %ValueName2%
+ set ValueType = %ValueType2%
+ set ValueValue = %JRE_PATH2%
+ )
+ )
+)
+
+IF "%JRE_PATH2%" == "" GOTO JAVA_NOT_INSTALLED
+
+:JAVA_INSTALLED
+
+@ECHO Java 1.8 found!
+@ECHO Installing ${pkg.name} ...
+
+%BASE%${pkg.name}.exe install
+
+@ECHO ${pkg.name} installed successfully!
+
+GOTO END
+
+:JAVA_NOT_INSTALLED
+@ECHO Java 1.8 or above is not installed
+@ECHO Please go to https://java.com/ and install Java. Then retry installation.
+PAUSE
+GOTO END
+
+:END
+
+
diff --git a/transport/coap/src/main/scripts/windows/service.xml b/transport/coap/src/main/scripts/windows/service.xml
new file mode 100644
index 0000000000..f7b9d307c8
--- /dev/null
+++ b/transport/coap/src/main/scripts/windows/service.xml
@@ -0,0 +1,36 @@
+
+ ${pkg.name}
+ ${project.name}
+ ${project.description}
+ %BASE%\conf
+ ${pkg.winWrapperLogFolder}
+ rotate
+
+ java
+ -Xloggc:%BASE%\logs\gc.log
+ -XX:+HeapDumpOnOutOfMemoryError
+ -XX:+PrintGCDetails
+ -XX:+PrintGCDateStamps
+ -XX:+PrintHeapAtGC
+ -XX:+PrintTenuringDistribution
+ -XX:+PrintGCApplicationStoppedTime
+ -XX:+UseGCLogFileRotation
+ -XX:NumberOfGCLogFiles=10
+ -XX:GCLogFileSize=10M
+ -XX:-UseBiasedLocking
+ -XX:+UseTLAB
+ -XX:+ResizeTLAB
+ -XX:+PerfDisableSharedMem
+ -XX:+UseCondCardMark
+ -XX:CMSWaitDuration=10000
+ -XX:+UseParNewGC
+ -XX:+UseConcMarkSweepGC
+ -XX:+CMSParallelRemarkEnabled
+ -XX:+CMSParallelInitialMarkEnabled
+ -XX:+CMSEdenChunksRecordAlways
+ -XX:CMSInitiatingOccupancyFraction=75
+ -XX:+UseCMSInitiatingOccupancyOnly
+ -jar
+ %BASE%\lib\${pkg.name}.jar
+
+
diff --git a/transport/coap/src/main/scripts/windows/uninstall.bat b/transport/coap/src/main/scripts/windows/uninstall.bat
new file mode 100644
index 0000000000..921e4c8395
--- /dev/null
+++ b/transport/coap/src/main/scripts/windows/uninstall.bat
@@ -0,0 +1,9 @@
+@ECHO OFF
+
+@ECHO Stopping ${pkg.name} ...
+net stop ${pkg.name}
+
+@ECHO Uninstalling ${pkg.name} ...
+%~dp0${pkg.name}.exe uninstall
+
+@ECHO DONE.
\ No newline at end of file
diff --git a/transport/coap/src/test/java/org/thingsboard/server/transport/coap/CoapServerTest.java b/transport/coap/src/test/java/org/thingsboard/server/transport/coap/CoapServerTest.java
deleted file mode 100644
index e2fd7cd6c4..0000000000
--- a/transport/coap/src/test/java/org/thingsboard/server/transport/coap/CoapServerTest.java
+++ /dev/null
@@ -1,208 +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.transport.coap;
-
-import lombok.extern.slf4j.Slf4j;
-import org.eclipse.californium.core.CoapClient;
-import org.eclipse.californium.core.CoapResponse;
-import org.eclipse.californium.core.coap.CoAP.ResponseCode;
-import org.eclipse.californium.core.coap.MediaTypeRegistry;
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.test.annotation.DirtiesContext;
-import org.springframework.test.annotation.DirtiesContext.ClassMode;
-import org.springframework.test.context.junit4.SpringRunner;
-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.TenantId;
-import org.thingsboard.server.common.data.kv.AttributeKvEntry;
-import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry;
-import org.thingsboard.server.common.data.kv.LongDataEntry;
-import org.thingsboard.server.common.data.kv.StringDataEntry;
-import org.thingsboard.server.common.data.security.DeviceCredentialsFilter;
-import org.thingsboard.server.common.data.security.DeviceCredentialsType;
-import org.thingsboard.server.common.data.security.DeviceTokenCredentials;
-import org.thingsboard.server.common.msg.aware.SessionAwareMsg;
-import org.thingsboard.server.common.msg.core.BasicGetAttributesResponse;
-import org.thingsboard.server.common.msg.core.BasicRequest;
-import org.thingsboard.server.common.msg.core.BasicStatusCodeResponse;
-import org.thingsboard.server.common.msg.kv.BasicAttributeKVMsg;
-import org.thingsboard.server.common.msg.session.*;
-import org.thingsboard.server.common.transport.SessionMsgProcessor;
-import org.thingsboard.server.common.transport.auth.DeviceAuthResult;
-import org.thingsboard.server.common.transport.auth.DeviceAuthService;
-import org.thingsboard.server.common.transport.quota.host.HostRequestsQuotaService;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Optional;
-import java.util.UUID;
-
-@RunWith(SpringRunner.class)
-@DirtiesContext(classMode = ClassMode.BEFORE_CLASS)
-@Slf4j
-public class CoapServerTest {
-
- private static final int TEST_PORT = 5555;
- private static final String TELEMETRY_POST_MESSAGE = "[{\"key1\":\"value1\"}]";
- private static final String TEST_ATTRIBUTES_RESPONSE = "{\"key1\":\"value1\",\"key2\":42}";
- private static final String DEVICE1_TOKEN = "Device1Token";
-
- @Configuration
- public static class EchoCoapServerITConfiguration extends CoapServerTestConfiguration {
-
- @Bean
- public static DeviceAuthService authService() {
- return new DeviceAuthService() {
-
- private final DeviceId devId = new DeviceId(UUID.randomUUID());
-
- @Override
- public DeviceAuthResult process(DeviceCredentialsFilter credentials) {
- if (credentials != null && credentials.getCredentialsType() == DeviceCredentialsType.ACCESS_TOKEN) {
- DeviceTokenCredentials tokenCredentials = (DeviceTokenCredentials) credentials;
- if (tokenCredentials.getCredentialsId().equals(DEVICE1_TOKEN)) {
- return DeviceAuthResult.of(devId);
- }
- }
- return DeviceAuthResult.of("Credentials are invalid!");
- }
-
- @Override
- public Optional findDeviceById(DeviceId deviceId) {
- if (deviceId.equals(devId)) {
- Device dev = new Device();
- dev.setId(devId);
- dev.setTenantId(new TenantId(UUID.randomUUID()));
- dev.setCustomerId(new CustomerId(UUID.randomUUID()));
- return Optional.of(dev);
- } else {
- return Optional.empty();
- }
- }
- };
- }
-
- @Bean
- public static SessionMsgProcessor sessionMsgProcessor() {
- return new SessionMsgProcessor() {
-
- @Override
- public void process(SessionAwareMsg toActorMsg) {
- if (toActorMsg instanceof TransportToDeviceSessionActorMsg) {
- AdaptorToSessionActorMsg sessionMsg = ((TransportToDeviceSessionActorMsg) toActorMsg).getSessionMsg();
- try {
- FromDeviceMsg deviceMsg = sessionMsg.getMsg();
- ToDeviceMsg toDeviceMsg = null;
- if (deviceMsg.getMsgType() == SessionMsgType.POST_TELEMETRY_REQUEST) {
- toDeviceMsg = BasicStatusCodeResponse.onSuccess(deviceMsg.getMsgType(), BasicRequest.DEFAULT_REQUEST_ID);
- } else if (deviceMsg.getMsgType() == SessionMsgType.GET_ATTRIBUTES_REQUEST) {
- List data = new ArrayList<>();
- data.add(new BaseAttributeKvEntry(new StringDataEntry("key1", "value1"), System.currentTimeMillis()));
- data.add(new BaseAttributeKvEntry(new LongDataEntry("key2", 42L), System.currentTimeMillis()));
- BasicAttributeKVMsg kv = BasicAttributeKVMsg.fromClient(data);
- toDeviceMsg = BasicGetAttributesResponse.onSuccess(deviceMsg.getMsgType(), BasicRequest.DEFAULT_REQUEST_ID, kv);
- }
- if (toDeviceMsg != null) {
- sessionMsg.getSessionContext().onMsg(new BasicSessionActorToAdaptorMsg(sessionMsg.getSessionContext(), toDeviceMsg));
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
-
- @Override
- public void onDeviceAdded(Device device) {
-
- }
-
-
- };
- }
-
- @Bean
- public static HostRequestsQuotaService quotaService() {
- return new HostRequestsQuotaService(null, null, null, null, false);
- }
- }
-
- @Autowired
- private CoapTransportService service;
-
- @Before
- public void beforeTest() {
- log.info("Service info: {}", service.toString());
- }
-
- @Test
- public void testBadJsonTelemetryPostRequest() {
- CoapClient client = new CoapClient(getBaseTestUrl() + DEVICE1_TOKEN + "/" + FeatureType.TELEMETRY.name().toLowerCase());
- CoapResponse response = client.setTimeout(6000).post("test", MediaTypeRegistry.APPLICATION_JSON);
- Assert.assertEquals(ResponseCode.BAD_REQUEST, response.getCode());
- log.info("Response: {}, {}", response.getCode(), response.getResponseText());
- }
-
- @Test
- public void testNoCredentialsPostRequest() {
- CoapClient client = new CoapClient(getBaseTestUrl());
- CoapResponse response = client.setTimeout(6000).post(TELEMETRY_POST_MESSAGE, MediaTypeRegistry.APPLICATION_JSON);
- Assert.assertEquals(ResponseCode.BAD_REQUEST, response.getCode());
- log.info("Response: {}, {}", response.getCode(), response.getResponseText());
- }
-
- @Test
- public void testValidJsonTelemetryPostRequest() {
- CoapClient client = new CoapClient(getBaseTestUrl() + DEVICE1_TOKEN + "/" + FeatureType.TELEMETRY.name().toLowerCase());
- CoapResponse response = client.setTimeout(6000).post(TELEMETRY_POST_MESSAGE, MediaTypeRegistry.APPLICATION_JSON);
- Assert.assertEquals(ResponseCode.CREATED, response.getCode());
- log.info("Response: {}, {}", response.getCode(), response.getResponseText());
- }
-
- @Test
- public void testNoCredentialsAttributesGetRequest() {
- CoapClient client = new CoapClient("coap://localhost:5555/api/v1?keys=key1,key2");
- CoapResponse response = client.setTimeout(6000).get();
- Assert.assertEquals(ResponseCode.BAD_REQUEST, response.getCode());
- }
-
- @Test
- public void testNoKeysAttributesGetRequest() {
- CoapClient client = new CoapClient(getBaseTestUrl() + DEVICE1_TOKEN + "/" + FeatureType.ATTRIBUTES.name().toLowerCase() + "?data=key1,key2");
- CoapResponse response = client.setTimeout(6000).get();
- Assert.assertEquals(ResponseCode.CONTENT, response.getCode());
- }
-
- @Test
- public void testValidAttributesGetRequest() {
- CoapClient client = new CoapClient(getBaseTestUrl() + DEVICE1_TOKEN + "/" + FeatureType.ATTRIBUTES.name().toLowerCase() + "?clientKeys=key1,key2");
- CoapResponse response = client.setTimeout(6000).get();
- Assert.assertEquals(ResponseCode.CONTENT, response.getCode());
- Assert.assertEquals(TEST_ATTRIBUTES_RESPONSE, response.getResponseText());
- log.info("Response: {}, {}", response.getCode(), response.getResponseText());
- }
-
- private String getBaseTestUrl() {
- return "coap://localhost:" + TEST_PORT + "/api/v1/";
- }
-
-}
diff --git a/transport/coap/src/test/java/org/thingsboard/server/transport/coap/CoapServerTestConfiguration.java b/transport/coap/src/test/java/org/thingsboard/server/transport/coap/CoapServerTestConfiguration.java
deleted file mode 100644
index f2cd080293..0000000000
--- a/transport/coap/src/test/java/org/thingsboard/server/transport/coap/CoapServerTestConfiguration.java
+++ /dev/null
@@ -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.transport.coap;
-
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.ComponentScan;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.context.annotation.PropertySource;
-import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
-import org.springframework.test.context.TestPropertySource;
-
-@Configuration
-@ComponentScan({ "org.thingsboard.server.transport.coap" })
-@PropertySource("classpath:coap-transport-test.properties")
-public class CoapServerTestConfiguration {
-
- @Bean
- public static PropertySourcesPlaceholderConfigurer propertyConfig() {
- return new PropertySourcesPlaceholderConfigurer();
- }
-
-}
diff --git a/transport/coap/src/test/resources/coap-transport-test.properties b/transport/coap/src/test/resources/coap-transport-test.properties
deleted file mode 100644
index 469b0348b1..0000000000
--- a/transport/coap/src/test/resources/coap-transport-test.properties
+++ /dev/null
@@ -1,4 +0,0 @@
-coap.bind_address=0.0.0.0
-coap.bind_port=5555
-coap.adaptor=JsonCoapAdaptor
-coap.timeout=10000
\ No newline at end of file
diff --git a/transport/http/build.gradle b/transport/http/build.gradle
new file mode 100644
index 0000000000..6d54cb4789
--- /dev/null
+++ b/transport/http/build.gradle
@@ -0,0 +1,140 @@
+/**
+ * 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.
+ */
+import org.apache.tools.ant.filters.ReplaceTokens
+
+buildscript {
+ ext {
+ osPackageVersion = "3.8.0"
+ }
+ repositories {
+ jcenter()
+ }
+ dependencies {
+ classpath("com.netflix.nebula:gradle-ospackage-plugin:${osPackageVersion}")
+ }
+}
+
+apply plugin: "nebula.ospackage"
+
+buildDir = projectBuildDir
+version = projectVersion
+distsDirName = "./"
+
+// OS Package plugin configuration
+ospackage {
+ packageName = pkgName
+ version = "${project.version}"
+ release = 1
+ os = LINUX
+ type = BINARY
+
+ into pkgInstallFolder
+
+ user pkgName
+ permissionGroup pkgName
+
+ // Copy the actual .jar file
+ from(mainJar) {
+ // Strip the version from the jar filename
+ rename { String fileName ->
+ "${pkgName}.jar"
+ }
+ fileMode 0500
+ into "bin"
+ }
+
+ // Copy the config files
+ from("target/conf") {
+ exclude "${pkgName}.conf"
+ fileType CONFIG | NOREPLACE
+ fileMode 0754
+ into "conf"
+ }
+
+}
+
+// Configure our RPM build task
+buildRpm {
+
+ arch = NOARCH
+
+ version = projectVersion.replace('-', '')
+ archiveName = "${pkgName}.rpm"
+
+ requires("java-1.8.0")
+
+ from("target/conf") {
+ include "${pkgName}.conf"
+ filter(ReplaceTokens, tokens: ['pkg.platform': 'rpm'])
+ fileType CONFIG | NOREPLACE
+ fileMode 0754
+ into "${pkgInstallFolder}/conf"
+ }
+
+ preInstall file("${buildDir}/control/rpm/preinst")
+ postInstall file("${buildDir}/control/rpm/postinst")
+ preUninstall file("${buildDir}/control/rpm/prerm")
+ postUninstall file("${buildDir}/control/rpm/postrm")
+
+ user pkgName
+ permissionGroup pkgName
+
+ // Copy the system unit files
+ from("${buildDir}/control/${pkgName}.service") {
+ addParentDirs = false
+ fileMode 0644
+ into "/usr/lib/systemd/system"
+ }
+
+ directory(pkgLogFolder, 0755)
+ link("${pkgInstallFolder}/bin/${pkgName}.yml", "${pkgInstallFolder}/conf/${pkgName}.yml")
+ link("/etc/${pkgName}/conf", "${pkgInstallFolder}/conf")
+}
+
+// Same as the buildRpm task
+buildDeb {
+
+ arch = "all"
+
+ archiveName = "${pkgName}.deb"
+
+ requires("openjdk-8-jre").or("java8-runtime").or("oracle-java8-installer").or("openjdk-8-jre-headless")
+
+ from("target/conf") {
+ include "${pkgName}.conf"
+ filter(ReplaceTokens, tokens: ['pkg.platform': 'deb'])
+ fileType CONFIG | NOREPLACE
+ fileMode 0754
+ into "${pkgInstallFolder}/conf"
+ }
+
+ configurationFile("${pkgInstallFolder}/conf/${pkgName}.conf")
+ configurationFile("${pkgInstallFolder}/conf/${pkgName}.yml")
+ configurationFile("${pkgInstallFolder}/conf/logback.xml")
+
+ preInstall file("${buildDir}/control/deb/preinst")
+ postInstall file("${buildDir}/control/deb/postinst")
+ preUninstall file("${buildDir}/control/deb/prerm")
+ postUninstall file("${buildDir}/control/deb/postrm")
+
+ user pkgName
+ permissionGroup pkgName
+
+ directory(pkgLogFolder, 0755)
+ link("/etc/init.d/${pkgName}", "${pkgInstallFolder}/bin/${pkgName}.jar")
+ link("${pkgInstallFolder}/bin/${pkgName}.yml", "${pkgInstallFolder}/conf/${pkgName}.yml")
+ link("/etc/${pkgName}/conf", "${pkgInstallFolder}/conf")
+}
diff --git a/transport/http/pom.xml b/transport/http/pom.xml
index 996470832c..407d995381 100644
--- a/transport/http/pom.xml
+++ b/transport/http/pom.xml
@@ -27,39 +27,37 @@
http
jar
- Thingsboard HTTP Transport
+ Thingsboard HTTP Transport Service
https://thingsboard.io
UTF-8
${basedir}/../..
+ tb-http-transport
+ /var/log/${pkg.name}
+ /usr/share/${pkg.name}
+ ${project.build.directory}/windows
- org.thingsboard.common
- transport
-
-
- org.springframework.boot
- spring-boot-starter-web
- provided
+ org.thingsboard.common.transport
+ http
- org.slf4j
- slf4j-api
-
-
- org.slf4j
- log4j-over-slf4j
+ org.thingsboard.common
+ queue
- ch.qos.logback
- logback-core
+ com.sun.winsw
+ winsw
+ bin
+ exe
+ provided
- ch.qos.logback
- logback-classic
+ org.springframework.boot
+ spring-boot-starter-web
org.springframework.boot
@@ -78,4 +76,266 @@
+
+ ${pkg.name}-${project.version}
+
+
+ ${project.basedir}/src/main/resources
+
+
+
+
+ org.apache.maven.plugins
+ maven-resources-plugin
+
+
+ copy-conf
+ process-resources
+
+ copy-resources
+
+
+ ${project.build.directory}/conf
+
+
+ src/main/resources
+
+ logback.xml
+
+ false
+
+
+
+
+
+ copy-service-conf
+ process-resources
+
+ copy-resources
+
+
+ ${project.build.directory}/conf
+
+
+ src/main/conf
+ true
+
+
+
+ src/main/filters/unix.properties
+
+
+
+
+ copy-win-conf
+ process-resources
+
+ copy-resources
+
+
+ ${pkg.win.dist}/conf
+
+
+ src/main/resources
+
+ logback.xml
+
+ false
+
+
+ src/main/conf
+
+ tb-http-transport.conf
+
+ true
+
+
+
+ src/main/filters/windows.properties
+
+
+
+
+ copy-control
+ process-resources
+
+ copy-resources
+
+
+ ${project.build.directory}/control
+
+
+ src/main/scripts/control
+ true
+
+
+
+ src/main/filters/unix.properties
+
+
+
+
+ copy-windows-control
+ process-resources
+
+ copy-resources
+
+
+ ${pkg.win.dist}
+
+
+ src/main/scripts/windows
+ true
+
+
+
+ src/main/filters/windows.properties
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-dependency-plugin
+
+
+ copy-winsw-service
+ package
+
+ copy
+
+
+
+
+ com.sun.winsw
+ winsw
+ bin
+ exe
+ service.exe
+
+
+ ${pkg.win.dist}
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+
+
+ **/logback.xml
+
+
+
+ ThingsBoard HTTP Transport Service
+ ${project.version}
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+ org.thingsboard.server.http.ThingsboardHttpTransportApplication
+ boot
+ ZIP
+ true
+ true
+
+ ${pkg.installFolder}/conf
+ ${pkg.unixLogFolder}
+ ${pkg.name}.out
+ ${pkg.name}
+
+
+
+
+
+ repackage
+
+
+
+
+
+ org.fortasoft
+ gradle-maven-plugin
+
+
+ build
+ buildDeb
+ buildRpm
+
+
+ -PprojectBuildDir=${project.build.directory}
+ -PprojectVersion=${project.version}
+ -PmainJar=${project.build.directory}/${project.build.finalName}-boot.${project.packaging}
+ -PpkgName=${pkg.name}
+ -PpkgInstallFolder=${pkg.installFolder}
+ -PpkgLogFolder=${pkg.unixLogFolder}
+
+
+
+
+ package
+
+ invoke
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+
+ ${pkg.name}
+
+ src/main/assembly/windows.xml
+
+
+
+
+ assembly
+ package
+
+ single
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-install-plugin
+
+ ${project.build.directory}/${pkg.name}.deb
+ ${project.artifactId}
+ ${project.groupId}
+ ${project.version}
+ deb
+ deb
+
+
+
+ install-deb
+ package
+
+ install-file
+
+
+
+
+
+
+
+
+ jenkins
+ Jenkins Repository
+ http://repo.jenkins-ci.org/releases
+
+ false
+
+
+
diff --git a/transport/http/src/main/assembly/windows.xml b/transport/http/src/main/assembly/windows.xml
new file mode 100644
index 0000000000..82da34ed13
--- /dev/null
+++ b/transport/http/src/main/assembly/windows.xml
@@ -0,0 +1,71 @@
+
+
+ windows
+
+
+ zip
+
+
+
+
+
+ ${pkg.win.dist}
+ logs
+
+ */**
+
+
+
+ ${pkg.win.dist}/conf
+ conf
+ windows
+
+
+
+
+
+ ${project.build.directory}/${project.build.finalName}-boot.${project.packaging}
+ lib
+ ${pkg.name}.jar
+
+
+ ${pkg.win.dist}/service.exe
+
+ ${pkg.name}.exe
+
+
+ ${pkg.win.dist}/service.xml
+
+ ${pkg.name}.xml
+ windows
+
+
+ ${pkg.win.dist}/install.bat
+
+ windows
+
+
+ ${pkg.win.dist}/uninstall.bat
+
+ windows
+
+
+
diff --git a/transport/http/src/main/conf/logback.xml b/transport/http/src/main/conf/logback.xml
new file mode 100644
index 0000000000..f36469d5f2
--- /dev/null
+++ b/transport/http/src/main/conf/logback.xml
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+ ${pkg.logFolder}/${pkg.name}.log
+
+ ${pkg.logFolder}/${pkg.name}.%d{yyyy-MM-dd}.%i.log
+ 100MB
+ 30
+ 3GB
+
+
+ %d{ISO8601} [%thread] %-5level %logger{36} - %msg%n
+
+
+
+
+
+
+
+
+
+
diff --git a/transport/http/src/main/conf/tb-http-transport.conf b/transport/http/src/main/conf/tb-http-transport.conf
new file mode 100644
index 0000000000..0afa91c18e
--- /dev/null
+++ b/transport/http/src/main/conf/tb-http-transport.conf
@@ -0,0 +1,23 @@
+#
+# 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.
+#
+
+export JAVA_OPTS="$JAVA_OPTS -Xloggc:@pkg.logFolder@/gc.log -XX:+IgnoreUnrecognizedVMOptions -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDetails -XX:+PrintGCDateStamps"
+export JAVA_OPTS="$JAVA_OPTS -XX:+PrintHeapAtGC -XX:+PrintTenuringDistribution -XX:+PrintGCApplicationStoppedTime -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10"
+export JAVA_OPTS="$JAVA_OPTS -XX:GCLogFileSize=10M -XX:-UseBiasedLocking -XX:+UseTLAB -XX:+ResizeTLAB -XX:+PerfDisableSharedMem -XX:+UseCondCardMark"
+export JAVA_OPTS="$JAVA_OPTS -XX:CMSWaitDuration=10000 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+CMSParallelInitialMarkEnabled"
+export JAVA_OPTS="$JAVA_OPTS -XX:+CMSEdenChunksRecordAlways -XX:CMSInitiatingOccupancyFraction=75 -XX:+UseCMSInitiatingOccupancyOnly"
+export LOG_FILENAME=${pkg.name}.out
+export LOADER_PATH=${pkg.installFolder}/conf
diff --git a/transport/http/src/main/filters/unix.properties b/transport/http/src/main/filters/unix.properties
new file mode 100644
index 0000000000..8967278673
--- /dev/null
+++ b/transport/http/src/main/filters/unix.properties
@@ -0,0 +1 @@
+pkg.logFolder=${pkg.unixLogFolder}
\ No newline at end of file
diff --git a/transport/http/src/main/filters/windows.properties b/transport/http/src/main/filters/windows.properties
new file mode 100644
index 0000000000..a6e48d91ba
--- /dev/null
+++ b/transport/http/src/main/filters/windows.properties
@@ -0,0 +1,2 @@
+pkg.logFolder=${BASE}\\logs
+pkg.winWrapperLogFolder=%BASE%\\logs
diff --git a/transport/http/src/main/java/org/thingsboard/server/http/ThingsboardHttpTransportApplication.java b/transport/http/src/main/java/org/thingsboard/server/http/ThingsboardHttpTransportApplication.java
new file mode 100644
index 0000000000..517794c346
--- /dev/null
+++ b/transport/http/src/main/java/org/thingsboard/server/http/ThingsboardHttpTransportApplication.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.http;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.scheduling.annotation.EnableAsync;
+
+import java.util.Arrays;
+
+@SpringBootApplication
+@EnableAsync
+@ComponentScan({"org.thingsboard.server.http", "org.thingsboard.server.common", "org.thingsboard.server.transport.http", "org.thingsboard.server.kafka"})
+public class ThingsboardHttpTransportApplication {
+
+ private static final String SPRING_CONFIG_NAME_KEY = "--spring.config.name";
+ private static final String DEFAULT_SPRING_CONFIG_PARAM = SPRING_CONFIG_NAME_KEY + "=" + "tb-http-transport";
+
+ public static void main(String[] args) {
+ SpringApplication.run(ThingsboardHttpTransportApplication.class, updateArguments(args));
+ }
+
+ private static String[] updateArguments(String[] args) {
+ if (Arrays.stream(args).noneMatch(arg -> arg.startsWith(SPRING_CONFIG_NAME_KEY))) {
+ String[] modifiedArgs = new String[args.length + 1];
+ System.arraycopy(args, 0, modifiedArgs, 0, args.length);
+ modifiedArgs[args.length] = DEFAULT_SPRING_CONFIG_PARAM;
+ return modifiedArgs;
+ }
+ return args;
+ }
+}
diff --git a/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java b/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java
deleted file mode 100644
index d26d076b21..0000000000
--- a/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java
+++ /dev/null
@@ -1,235 +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.transport.http;
-
-import com.google.gson.JsonObject;
-import com.google.gson.JsonParser;
-import com.google.gson.JsonSyntaxException;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.http.HttpStatus;
-import org.springframework.http.ResponseEntity;
-import org.springframework.util.StringUtils;
-import org.springframework.web.bind.annotation.*;
-import org.springframework.web.context.request.async.DeferredResult;
-import org.thingsboard.server.common.data.security.DeviceTokenCredentials;
-import org.thingsboard.server.common.msg.core.*;
-import org.thingsboard.server.common.msg.session.AdaptorToSessionActorMsg;
-import org.thingsboard.server.common.msg.session.BasicAdaptorToSessionActorMsg;
-import org.thingsboard.server.common.msg.session.BasicTransportToDeviceSessionActorMsg;
-import org.thingsboard.server.common.msg.session.FromDeviceMsg;
-import org.thingsboard.server.common.transport.SessionMsgProcessor;
-import org.thingsboard.server.common.transport.adaptor.JsonConverter;
-import org.thingsboard.server.common.transport.auth.DeviceAuthService;
-import org.thingsboard.server.common.transport.quota.QuotaService;
-import org.thingsboard.server.common.transport.quota.host.HostRequestsQuotaService;
-import org.thingsboard.server.transport.http.session.HttpSessionCtx;
-
-import javax.servlet.http.HttpServletRequest;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * @author Andrew Shvayka
- */
-@RestController
-@RequestMapping("/api/v1")
-@Slf4j
-public class DeviceApiController {
-
- @Value("${http.request_timeout}")
- private long defaultTimeout;
-
- @Autowired(required = false)
- private SessionMsgProcessor processor;
-
- @Autowired(required = false)
- private DeviceAuthService authService;
-
- @Autowired(required = false)
- private HostRequestsQuotaService quotaService;
-
- @RequestMapping(value = "/{deviceToken}/attributes", method = RequestMethod.GET, produces = "application/json")
- public DeferredResult getDeviceAttributes(@PathVariable("deviceToken") String deviceToken,
- @RequestParam(value = "clientKeys", required = false, defaultValue = "") String clientKeys,
- @RequestParam(value = "sharedKeys", required = false, defaultValue = "") String sharedKeys,
- HttpServletRequest httpRequest) {
- DeferredResult responseWriter = new DeferredResult();
- if (quotaExceeded(httpRequest, responseWriter)) {
- return responseWriter;
- }
- HttpSessionCtx ctx = getHttpSessionCtx(responseWriter);
- if (ctx.login(new DeviceTokenCredentials(deviceToken))) {
- GetAttributesRequest request;
- if (StringUtils.isEmpty(clientKeys) && StringUtils.isEmpty(sharedKeys)) {
- request = new BasicGetAttributesRequest(0);
- } else {
- Set clientKeySet = !StringUtils.isEmpty(clientKeys) ? new HashSet<>(Arrays.asList(clientKeys.split(","))) : null;
- Set sharedKeySet = !StringUtils.isEmpty(sharedKeys) ? new HashSet<>(Arrays.asList(sharedKeys.split(","))) : null;
- request = new BasicGetAttributesRequest(0, clientKeySet, sharedKeySet);
- }
- process(ctx, request);
- } else {
- responseWriter.setResult(new ResponseEntity<>(HttpStatus.UNAUTHORIZED));
- }
-
- return responseWriter;
- }
-
- @RequestMapping(value = "/{deviceToken}/attributes", method = RequestMethod.POST)
- public DeferredResult postDeviceAttributes(@PathVariable("deviceToken") String deviceToken,
- @RequestBody String json, HttpServletRequest request) {
- DeferredResult responseWriter = new DeferredResult();
- if (quotaExceeded(request, responseWriter)) {
- return responseWriter;
- }
- HttpSessionCtx ctx = getHttpSessionCtx(responseWriter);
- if (ctx.login(new DeviceTokenCredentials(deviceToken))) {
- try {
- process(ctx, JsonConverter.convertToAttributes(new JsonParser().parse(json)));
- } catch (IllegalStateException | JsonSyntaxException ex) {
- responseWriter.setResult(new ResponseEntity<>(HttpStatus.BAD_REQUEST));
- }
- } else {
- responseWriter.setResult(new ResponseEntity<>(HttpStatus.UNAUTHORIZED));
- }
- return responseWriter;
- }
-
- @RequestMapping(value = "/{deviceToken}/telemetry", method = RequestMethod.POST)
- public DeferredResult postTelemetry(@PathVariable("deviceToken") String deviceToken,
- @RequestBody String json, HttpServletRequest request) {
- DeferredResult responseWriter = new DeferredResult();
- if (quotaExceeded(request, responseWriter)) {
- return responseWriter;
- }
- HttpSessionCtx ctx = getHttpSessionCtx(responseWriter);
- if (ctx.login(new DeviceTokenCredentials(deviceToken))) {
- try {
- process(ctx, JsonConverter.convertToTelemetry(new JsonParser().parse(json)));
- } catch (IllegalStateException | JsonSyntaxException ex) {
- responseWriter.setResult(new ResponseEntity<>(HttpStatus.BAD_REQUEST));
- }
- } else {
- responseWriter.setResult(new ResponseEntity<>(HttpStatus.UNAUTHORIZED));
- }
- return responseWriter;
- }
-
- @RequestMapping(value = "/{deviceToken}/rpc", method = RequestMethod.GET, produces = "application/json")
- public DeferredResult subscribeToCommands(@PathVariable("deviceToken") String deviceToken,
- @RequestParam(value = "timeout", required = false, defaultValue = "0") long timeout,
- HttpServletRequest request) {
-
- return subscribe(deviceToken, timeout, new RpcSubscribeMsg(), request);
- }
-
- @RequestMapping(value = "/{deviceToken}/rpc/{requestId}", method = RequestMethod.POST)
- public DeferredResult replyToCommand(@PathVariable("deviceToken") String deviceToken,
- @PathVariable("requestId") Integer requestId,
- @RequestBody String json, HttpServletRequest request) {
- DeferredResult responseWriter = new DeferredResult();
- if (quotaExceeded(request, responseWriter)) {
- return responseWriter;
- }
- HttpSessionCtx ctx = getHttpSessionCtx(responseWriter);
- if (ctx.login(new DeviceTokenCredentials(deviceToken))) {
- try {
- JsonObject response = new JsonParser().parse(json).getAsJsonObject();
- process(ctx, new ToDeviceRpcResponseMsg(requestId, response.toString()));
- } catch (IllegalStateException | JsonSyntaxException ex) {
- responseWriter.setResult(new ResponseEntity<>(HttpStatus.BAD_REQUEST));
- }
- } else {
- responseWriter.setResult(new ResponseEntity<>(HttpStatus.UNAUTHORIZED));
- }
- return responseWriter;
- }
-
- @RequestMapping(value = "/{deviceToken}/rpc", method = RequestMethod.POST)
- public DeferredResult postRpcRequest(@PathVariable("deviceToken") String deviceToken,
- @RequestBody String json, HttpServletRequest httpRequest) {
- DeferredResult responseWriter = new DeferredResult();
- if (quotaExceeded(httpRequest, responseWriter)) {
- return responseWriter;
- }
- HttpSessionCtx ctx = getHttpSessionCtx(responseWriter);
- if (ctx.login(new DeviceTokenCredentials(deviceToken))) {
- try {
- JsonObject request = new JsonParser().parse(json).getAsJsonObject();
- process(ctx, new ToServerRpcRequestMsg(0,
- request.get("method").getAsString(),
- request.get("params").toString()));
- } catch (IllegalStateException | JsonSyntaxException ex) {
- responseWriter.setResult(new ResponseEntity<>(HttpStatus.BAD_REQUEST));
- }
- } else {
- responseWriter.setResult(new ResponseEntity<>(HttpStatus.UNAUTHORIZED));
- }
- return responseWriter;
- }
-
- @RequestMapping(value = "/{deviceToken}/attributes/updates", method = RequestMethod.GET, produces = "application/json")
- public DeferredResult subscribeToAttributes(@PathVariable("deviceToken") String deviceToken,
- @RequestParam(value = "timeout", required = false, defaultValue = "0") long timeout,
- HttpServletRequest httpRequest) {
-
- return subscribe(deviceToken, timeout, new AttributesSubscribeMsg(), httpRequest);
- }
-
- private DeferredResult subscribe(String deviceToken, long timeout, FromDeviceMsg msg, HttpServletRequest httpRequest) {
- DeferredResult responseWriter = new DeferredResult();
- if (quotaExceeded(httpRequest, responseWriter)) {
- return responseWriter;
- }
- HttpSessionCtx ctx = getHttpSessionCtx(responseWriter, timeout);
- if (ctx.login(new DeviceTokenCredentials(deviceToken))) {
- try {
- process(ctx, msg);
- } catch (IllegalStateException | JsonSyntaxException ex) {
- responseWriter.setResult(new ResponseEntity<>(HttpStatus.BAD_REQUEST));
- }
- } else {
- responseWriter.setResult(new ResponseEntity<>(HttpStatus.UNAUTHORIZED));
- }
- return responseWriter;
- }
-
- private HttpSessionCtx getHttpSessionCtx(DeferredResult responseWriter) {
- return getHttpSessionCtx(responseWriter, defaultTimeout);
- }
-
- private HttpSessionCtx getHttpSessionCtx(DeferredResult responseWriter, long timeout) {
- return new HttpSessionCtx(processor, authService, responseWriter, timeout != 0 ? timeout : defaultTimeout);
- }
-
- private void process(HttpSessionCtx ctx, FromDeviceMsg request) {
- AdaptorToSessionActorMsg msg = new BasicAdaptorToSessionActorMsg(ctx, request);
- processor.process(new BasicTransportToDeviceSessionActorMsg(ctx.getDevice(), msg));
- }
-
- private boolean quotaExceeded(HttpServletRequest request, DeferredResult responseWriter) {
- if (quotaService.isQuotaExceeded(request.getRemoteAddr())) {
- log.warn("REST Quota exceeded for [{}] . Disconnect", request.getRemoteAddr());
- responseWriter.setResult(new ResponseEntity<>(HttpStatus.BANDWIDTH_LIMIT_EXCEEDED));
- return true;
- }
- return false;
- }
-
-}
diff --git a/transport/http/src/main/java/org/thingsboard/server/transport/http/session/HttpSessionCtx.java b/transport/http/src/main/java/org/thingsboard/server/transport/http/session/HttpSessionCtx.java
deleted file mode 100644
index 4732785de2..0000000000
--- a/transport/http/src/main/java/org/thingsboard/server/transport/http/session/HttpSessionCtx.java
+++ /dev/null
@@ -1,164 +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.transport.http.session;
-
-import com.google.gson.JsonObject;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.http.HttpStatus;
-import org.springframework.http.ResponseEntity;
-import org.springframework.web.context.request.async.DeferredResult;
-import org.thingsboard.server.common.data.id.SessionId;
-import org.thingsboard.server.common.msg.core.*;
-import org.thingsboard.server.common.msg.session.*;
-import org.thingsboard.server.common.msg.session.ex.SessionException;
-import org.thingsboard.server.common.transport.SessionMsgProcessor;
-import org.thingsboard.server.common.transport.adaptor.JsonConverter;
-import org.thingsboard.server.common.transport.auth.DeviceAuthService;
-import org.thingsboard.server.common.transport.session.DeviceAwareSessionContext;
-
-import java.util.Optional;
-import java.util.function.Consumer;
-
-/**
- * @author Andrew Shvayka
- */
-@Slf4j
-public class HttpSessionCtx extends DeviceAwareSessionContext {
-
- private final SessionId sessionId;
- private final long timeout;
- private final DeferredResult responseWriter;
-
- public HttpSessionCtx(SessionMsgProcessor processor, DeviceAuthService authService, DeferredResult responseWriter, long timeout) {
- super(processor, authService);
- this.sessionId = new HttpSessionId();
- this.responseWriter = responseWriter;
- this.timeout = timeout;
- }
-
- @Override
- public SessionType getSessionType() {
- return SessionType.SYNC;
- }
-
- @Override
- public void onMsg(SessionActorToAdaptorMsg source) throws SessionException {
- ToDeviceMsg msg = source.getMsg();
- switch (msg.getSessionMsgType()) {
- case GET_ATTRIBUTES_RESPONSE:
- reply((GetAttributesResponse) msg);
- return;
- case STATUS_CODE_RESPONSE:
- reply((StatusCodeResponse) msg);
- return;
- case ATTRIBUTES_UPDATE_NOTIFICATION:
- reply((AttributesUpdateNotification) msg);
- return;
- case TO_DEVICE_RPC_REQUEST:
- reply((ToDeviceRpcRequestMsg) msg);
- return;
- case TO_SERVER_RPC_RESPONSE:
- reply((ToServerRpcResponseMsg) msg);
- return;
- case RULE_ENGINE_ERROR:
- reply((RuleEngineErrorMsg) msg);
- return;
- default:
- break;
- }
- }
-
- private void reply(RuleEngineErrorMsg msg) {
- HttpStatus status = HttpStatus.INTERNAL_SERVER_ERROR;
- switch (msg.getError()) {
- case QUEUE_PUT_TIMEOUT:
- status = HttpStatus.REQUEST_TIMEOUT;
- break;
- default:
- if (msg.getInSessionMsgType() == SessionMsgType.TO_SERVER_RPC_REQUEST) {
- status = HttpStatus.BAD_REQUEST;
- }
- break;
- }
- responseWriter.setResult(new ResponseEntity<>(JsonConverter.toErrorJson(msg.getErrorMsg()).toString(), status));
- }
-
- private void reply(ResponseMsg extends T> msg, Consumer f) {
- Optional msgError = msg.getError();
- if (!msgError.isPresent()) {
- Optional extends T> msgData = msg.getData();
- if (msgData.isPresent()) {
- f.accept(msgData.get());
- }
- } else {
- Exception e = msgError.get();
- responseWriter.setResult(new ResponseEntity<>(e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR));
- }
- }
-
- private void reply(ToDeviceRpcRequestMsg msg) {
- responseWriter.setResult(new ResponseEntity<>(JsonConverter.toJson(msg, true).toString(), HttpStatus.OK));
- }
-
- private void reply(ToServerRpcResponseMsg msg) {
- responseWriter.setResult(new ResponseEntity<>(JsonConverter.toJson(msg).toString(), HttpStatus.OK));
- }
-
- private void reply(AttributesUpdateNotification msg) {
- responseWriter.setResult(new ResponseEntity<>(JsonConverter.toJson(msg.getData(), false).toString(), HttpStatus.OK));
- }
-
- private void reply(GetAttributesResponse msg) {
- reply(msg, payload -> {
- if (payload.getClientAttributes().isEmpty() && payload.getSharedAttributes().isEmpty()) {
- responseWriter.setResult(new ResponseEntity<>(HttpStatus.NOT_FOUND));
- } else {
- JsonObject result = JsonConverter.toJson(payload, false);
- responseWriter.setResult(new ResponseEntity<>(result.toString(), HttpStatus.OK));
- }
- });
- }
-
- private void reply(StatusCodeResponse msg) {
- reply(msg, payload -> {
- if (payload == 0) {
- responseWriter.setResult(new ResponseEntity<>(HttpStatus.OK));
- } else {
- responseWriter.setResult(new ResponseEntity<>(HttpStatus.valueOf(payload)));
- }
- });
- }
-
- @Override
- public void onMsg(SessionCtrlMsg msg) throws SessionException {
- //Do nothing
- }
-
- @Override
- public boolean isClosed() {
- return false;
- }
-
- @Override
- public long getTimeout() {
- return timeout;
- }
-
- @Override
- public SessionId getSessionId() {
- return sessionId;
- }
-}
diff --git a/transport/http/src/main/resources/logback.xml b/transport/http/src/main/resources/logback.xml
new file mode 100644
index 0000000000..18864a9fab
--- /dev/null
+++ b/transport/http/src/main/resources/logback.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+ %d{ISO8601} [%thread] %-5level %logger{36} - %msg%n
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/transport/http/src/main/resources/tb-http-transport.yml b/transport/http/src/main/resources/tb-http-transport.yml
new file mode 100644
index 0000000000..65a1ad93a0
--- /dev/null
+++ b/transport/http/src/main/resources/tb-http-transport.yml
@@ -0,0 +1,69 @@
+#
+# 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.
+#
+
+server:
+ # Server bind address
+ address: "${HTTP_BIND_ADDRESS:0.0.0.0}"
+ # Server bind port
+ port: "${HTTP_BIND_PORT:8081}"
+
+# HTTP server parameters
+transport:
+ http:
+ request_timeout: "${HTTP_REQUEST_TIMEOUT:60000}"
+
+#Quota parameters
+quota:
+ host:
+ # Max allowed number of API requests in interval for single host
+ limit: "${QUOTA_HOST_LIMIT:10000}"
+ # Interval duration
+ intervalMs: "${QUOTA_HOST_INTERVAL_MS:60000}"
+ # Maximum silence duration for host after which Host removed from QuotaService. Must be bigger than intervalMs
+ ttlMs: "${QUOTA_HOST_TTL_MS:60000}"
+ # Interval for scheduled task that cleans expired records. TTL is used for expiring
+ cleanPeriodMs: "${QUOTA_HOST_CLEAN_PERIOD_MS:300000}"
+ # Enable Host API Limits
+ enabled: "${QUOTA_HOST_ENABLED:true}"
+ # Array of whitelist hosts
+ whitelist: "${QUOTA_HOST_WHITELIST:localhost,127.0.0.1}"
+ # Array of blacklist hosts
+ blacklist: "${QUOTA_HOST_BLACKLIST:}"
+ log:
+ topSize: 10
+ intervalMin: 2
+
+kafka:
+ enabled: true
+ bootstrap.servers: "${TB_KAFKA_SERVERS:localhost:9092}"
+ acks: "${TB_KAFKA_ACKS:all}"
+ retries: "${TB_KAFKA_RETRIES:1}"
+ 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}"
+ max_requests_timeout: "${TB_TRANSPORT_MAX_REQUEST_TIMEOUT:10000}"
+ response_poll_interval: "${TB_TRANSPORT_RESPONSE_POLL_INTERVAL_MS:25}"
+ response_auto_commit_interval: "${TB_TRANSPORT_RESPONSE_AUTO_COMMIT_INTERVAL_MS:100}"
+ rule_engine:
+ topic: "${TB_RULE_ENGINE_TOPIC:tb.rule-engine}"
+ notifications:
+ topic: "${TB_TRANSPORT_NOTIFICATIONS_TOPIC:tb.transport.notifications}"
+ poll_interval: "${TB_TRANSPORT_NOTIFICATIONS_POLL_INTERVAL_MS:25}"
+ auto_commit_interval: "${TB_TRANSPORT_NOTIFICATIONS_AUTO_COMMIT_INTERVAL_MS:100}"
diff --git a/transport/http/src/main/scripts/control/deb/postinst b/transport/http/src/main/scripts/control/deb/postinst
new file mode 100644
index 0000000000..d4066c027b
--- /dev/null
+++ b/transport/http/src/main/scripts/control/deb/postinst
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+chown -R ${pkg.name}: ${pkg.logFolder}
+chown -R ${pkg.name}: ${pkg.installFolder}
+update-rc.d ${pkg.name} defaults
+
diff --git a/transport/http/src/main/scripts/control/deb/postrm b/transport/http/src/main/scripts/control/deb/postrm
new file mode 100644
index 0000000000..61865803c3
--- /dev/null
+++ b/transport/http/src/main/scripts/control/deb/postrm
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+update-rc.d -f ${pkg.name} remove
diff --git a/transport/http/src/main/scripts/control/deb/preinst b/transport/http/src/main/scripts/control/deb/preinst
new file mode 100644
index 0000000000..6be5959285
--- /dev/null
+++ b/transport/http/src/main/scripts/control/deb/preinst
@@ -0,0 +1,18 @@
+#!/bin/sh
+
+if ! getent group ${pkg.name} >/dev/null; then
+ addgroup --system ${pkg.name}
+fi
+
+if ! getent passwd ${pkg.name} >/dev/null; then
+ adduser --quiet \
+ --system \
+ --ingroup ${pkg.name} \
+ --quiet \
+ --disabled-login \
+ --disabled-password \
+ --home ${pkg.installFolder} \
+ --no-create-home \
+ -gecos "Thingsboard application" \
+ ${pkg.name}
+fi
diff --git a/transport/http/src/main/scripts/control/deb/prerm b/transport/http/src/main/scripts/control/deb/prerm
new file mode 100644
index 0000000000..898d3efd5c
--- /dev/null
+++ b/transport/http/src/main/scripts/control/deb/prerm
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+if [ -e /var/run/${pkg.name}/${pkg.name}.pid ]; then
+ service ${pkg.name} stop
+fi
diff --git a/transport/http/src/main/scripts/control/rpm/postinst b/transport/http/src/main/scripts/control/rpm/postinst
new file mode 100644
index 0000000000..8a7a88f7e0
--- /dev/null
+++ b/transport/http/src/main/scripts/control/rpm/postinst
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+chown -R ${pkg.name}: ${pkg.logFolder}
+chown -R ${pkg.name}: ${pkg.installFolder}
+
+if [ $1 -eq 1 ] ; then
+ # Initial installation
+ systemctl --no-reload enable ${pkg.name}.service >/dev/null 2>&1 || :
+fi
diff --git a/transport/http/src/main/scripts/control/rpm/postrm b/transport/http/src/main/scripts/control/rpm/postrm
new file mode 100644
index 0000000000..8e1f8a2048
--- /dev/null
+++ b/transport/http/src/main/scripts/control/rpm/postrm
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+if [ $1 -ge 1 ] ; then
+ # Package upgrade, not uninstall
+ systemctl try-restart ${pkg.name}.service >/dev/null 2>&1 || :
+fi
diff --git a/transport/http/src/main/scripts/control/rpm/preinst b/transport/http/src/main/scripts/control/rpm/preinst
new file mode 100644
index 0000000000..e19fc884c8
--- /dev/null
+++ b/transport/http/src/main/scripts/control/rpm/preinst
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+getent group ${pkg.name} >/dev/null || groupadd -r ${pkg.name}
+getent passwd ${pkg.name} >/dev/null || \
+useradd -d ${pkg.installFolder} -g ${pkg.name} -M -r ${pkg.name} -s /sbin/nologin \
+-c "Thingsboard application"
diff --git a/transport/http/src/main/scripts/control/rpm/prerm b/transport/http/src/main/scripts/control/rpm/prerm
new file mode 100644
index 0000000000..accb487b8e
--- /dev/null
+++ b/transport/http/src/main/scripts/control/rpm/prerm
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+if [ $1 -eq 0 ] ; then
+ # Package removal, not upgrade
+ systemctl --no-reload disable --now ${pkg.name}.service > /dev/null 2>&1 || :
+fi
diff --git a/transport/http/src/main/scripts/control/tb-http-transport.service b/transport/http/src/main/scripts/control/tb-http-transport.service
new file mode 100644
index 0000000000..d456fc03c0
--- /dev/null
+++ b/transport/http/src/main/scripts/control/tb-http-transport.service
@@ -0,0 +1,11 @@
+[Unit]
+Description=${pkg.name}
+After=syslog.target
+
+[Service]
+User=${pkg.name}
+ExecStart=${pkg.installFolder}/bin/${pkg.name}.jar
+SuccessExitStatus=143
+
+[Install]
+WantedBy=multi-user.target
diff --git a/transport/http/src/main/scripts/windows/install.bat b/transport/http/src/main/scripts/windows/install.bat
new file mode 100644
index 0000000000..dba773663c
--- /dev/null
+++ b/transport/http/src/main/scripts/windows/install.bat
@@ -0,0 +1,87 @@
+@ECHO OFF
+
+setlocal ENABLEEXTENSIONS
+
+@ECHO Detecting Java version installed.
+:CHECK_JAVA_64
+@ECHO Detecting if it is 64 bit machine
+set KEY_NAME="HKEY_LOCAL_MACHINE\Software\Wow6432Node\JavaSoft\Java Runtime Environment"
+set VALUE_NAME=CurrentVersion
+
+FOR /F "usebackq skip=2 tokens=1-3" %%A IN (`REG QUERY %KEY_NAME% /v %VALUE_NAME% 2^>nul`) DO (
+ set ValueName=%%A
+ set ValueType=%%B
+ set ValueValue=%%C
+)
+@ECHO CurrentVersion %ValueValue%
+
+SET KEY_NAME="%KEY_NAME:~1,-1%\%ValueValue%"
+SET VALUE_NAME=JavaHome
+
+if defined ValueName (
+ FOR /F "usebackq skip=2 tokens=1,2*" %%A IN (`REG QUERY %KEY_NAME% /v %VALUE_NAME% 2^>nul`) DO (
+ set ValueName2=%%A
+ set ValueType2=%%B
+ set JRE_PATH2=%%C
+
+ if defined ValueName2 (
+ set ValueName = %ValueName2%
+ set ValueType = %ValueType2%
+ set ValueValue = %JRE_PATH2%
+ )
+ )
+)
+
+IF NOT "%JRE_PATH2%" == "" GOTO JAVA_INSTALLED
+
+:CHECK_JAVA_32
+@ECHO Detecting if it is 32 bit machine
+set KEY_NAME="HKEY_LOCAL_MACHINE\Software\JavaSoft\Java Runtime Environment"
+set VALUE_NAME=CurrentVersion
+
+FOR /F "usebackq skip=2 tokens=1-3" %%A IN (`REG QUERY %KEY_NAME% /v %VALUE_NAME% 2^>nul`) DO (
+ set ValueName=%%A
+ set ValueType=%%B
+ set ValueValue=%%C
+)
+@ECHO CurrentVersion %ValueValue%
+
+SET KEY_NAME="%KEY_NAME:~1,-1%\%ValueValue%"
+SET VALUE_NAME=JavaHome
+
+if defined ValueName (
+ FOR /F "usebackq skip=2 tokens=1,2*" %%A IN (`REG QUERY %KEY_NAME% /v %VALUE_NAME% 2^>nul`) DO (
+ set ValueName2=%%A
+ set ValueType2=%%B
+ set JRE_PATH2=%%C
+
+ if defined ValueName2 (
+ set ValueName = %ValueName2%
+ set ValueType = %ValueType2%
+ set ValueValue = %JRE_PATH2%
+ )
+ )
+)
+
+IF "%JRE_PATH2%" == "" GOTO JAVA_NOT_INSTALLED
+
+:JAVA_INSTALLED
+
+@ECHO Java 1.8 found!
+@ECHO Installing ${pkg.name} ...
+
+%BASE%${pkg.name}.exe install
+
+@ECHO ${pkg.name} installed successfully!
+
+GOTO END
+
+:JAVA_NOT_INSTALLED
+@ECHO Java 1.8 or above is not installed
+@ECHO Please go to https://java.com/ and install Java. Then retry installation.
+PAUSE
+GOTO END
+
+:END
+
+
diff --git a/transport/http/src/main/scripts/windows/service.xml b/transport/http/src/main/scripts/windows/service.xml
new file mode 100644
index 0000000000..f7b9d307c8
--- /dev/null
+++ b/transport/http/src/main/scripts/windows/service.xml
@@ -0,0 +1,36 @@
+
+ ${pkg.name}
+ ${project.name}
+ ${project.description}
+ %BASE%\conf
+ ${pkg.winWrapperLogFolder}
+ rotate
+
+ java
+ -Xloggc:%BASE%\logs\gc.log
+ -XX:+HeapDumpOnOutOfMemoryError
+ -XX:+PrintGCDetails
+ -XX:+PrintGCDateStamps
+ -XX:+PrintHeapAtGC
+ -XX:+PrintTenuringDistribution
+ -XX:+PrintGCApplicationStoppedTime
+ -XX:+UseGCLogFileRotation
+ -XX:NumberOfGCLogFiles=10
+ -XX:GCLogFileSize=10M
+ -XX:-UseBiasedLocking
+ -XX:+UseTLAB
+ -XX:+ResizeTLAB
+ -XX:+PerfDisableSharedMem
+ -XX:+UseCondCardMark
+ -XX:CMSWaitDuration=10000
+ -XX:+UseParNewGC
+ -XX:+UseConcMarkSweepGC
+ -XX:+CMSParallelRemarkEnabled
+ -XX:+CMSParallelInitialMarkEnabled
+ -XX:+CMSEdenChunksRecordAlways
+ -XX:CMSInitiatingOccupancyFraction=75
+ -XX:+UseCMSInitiatingOccupancyOnly
+ -jar
+ %BASE%\lib\${pkg.name}.jar
+
+
diff --git a/transport/http/src/main/scripts/windows/uninstall.bat b/transport/http/src/main/scripts/windows/uninstall.bat
new file mode 100644
index 0000000000..921e4c8395
--- /dev/null
+++ b/transport/http/src/main/scripts/windows/uninstall.bat
@@ -0,0 +1,9 @@
+@ECHO OFF
+
+@ECHO Stopping ${pkg.name} ...
+net stop ${pkg.name}
+
+@ECHO Uninstalling ${pkg.name} ...
+%~dp0${pkg.name}.exe uninstall
+
+@ECHO DONE.
\ No newline at end of file
diff --git a/transport/mqtt/build.gradle b/transport/mqtt/build.gradle
new file mode 100644
index 0000000000..6d54cb4789
--- /dev/null
+++ b/transport/mqtt/build.gradle
@@ -0,0 +1,140 @@
+/**
+ * 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.
+ */
+import org.apache.tools.ant.filters.ReplaceTokens
+
+buildscript {
+ ext {
+ osPackageVersion = "3.8.0"
+ }
+ repositories {
+ jcenter()
+ }
+ dependencies {
+ classpath("com.netflix.nebula:gradle-ospackage-plugin:${osPackageVersion}")
+ }
+}
+
+apply plugin: "nebula.ospackage"
+
+buildDir = projectBuildDir
+version = projectVersion
+distsDirName = "./"
+
+// OS Package plugin configuration
+ospackage {
+ packageName = pkgName
+ version = "${project.version}"
+ release = 1
+ os = LINUX
+ type = BINARY
+
+ into pkgInstallFolder
+
+ user pkgName
+ permissionGroup pkgName
+
+ // Copy the actual .jar file
+ from(mainJar) {
+ // Strip the version from the jar filename
+ rename { String fileName ->
+ "${pkgName}.jar"
+ }
+ fileMode 0500
+ into "bin"
+ }
+
+ // Copy the config files
+ from("target/conf") {
+ exclude "${pkgName}.conf"
+ fileType CONFIG | NOREPLACE
+ fileMode 0754
+ into "conf"
+ }
+
+}
+
+// Configure our RPM build task
+buildRpm {
+
+ arch = NOARCH
+
+ version = projectVersion.replace('-', '')
+ archiveName = "${pkgName}.rpm"
+
+ requires("java-1.8.0")
+
+ from("target/conf") {
+ include "${pkgName}.conf"
+ filter(ReplaceTokens, tokens: ['pkg.platform': 'rpm'])
+ fileType CONFIG | NOREPLACE
+ fileMode 0754
+ into "${pkgInstallFolder}/conf"
+ }
+
+ preInstall file("${buildDir}/control/rpm/preinst")
+ postInstall file("${buildDir}/control/rpm/postinst")
+ preUninstall file("${buildDir}/control/rpm/prerm")
+ postUninstall file("${buildDir}/control/rpm/postrm")
+
+ user pkgName
+ permissionGroup pkgName
+
+ // Copy the system unit files
+ from("${buildDir}/control/${pkgName}.service") {
+ addParentDirs = false
+ fileMode 0644
+ into "/usr/lib/systemd/system"
+ }
+
+ directory(pkgLogFolder, 0755)
+ link("${pkgInstallFolder}/bin/${pkgName}.yml", "${pkgInstallFolder}/conf/${pkgName}.yml")
+ link("/etc/${pkgName}/conf", "${pkgInstallFolder}/conf")
+}
+
+// Same as the buildRpm task
+buildDeb {
+
+ arch = "all"
+
+ archiveName = "${pkgName}.deb"
+
+ requires("openjdk-8-jre").or("java8-runtime").or("oracle-java8-installer").or("openjdk-8-jre-headless")
+
+ from("target/conf") {
+ include "${pkgName}.conf"
+ filter(ReplaceTokens, tokens: ['pkg.platform': 'deb'])
+ fileType CONFIG | NOREPLACE
+ fileMode 0754
+ into "${pkgInstallFolder}/conf"
+ }
+
+ configurationFile("${pkgInstallFolder}/conf/${pkgName}.conf")
+ configurationFile("${pkgInstallFolder}/conf/${pkgName}.yml")
+ configurationFile("${pkgInstallFolder}/conf/logback.xml")
+
+ preInstall file("${buildDir}/control/deb/preinst")
+ postInstall file("${buildDir}/control/deb/postinst")
+ preUninstall file("${buildDir}/control/deb/prerm")
+ postUninstall file("${buildDir}/control/deb/postrm")
+
+ user pkgName
+ permissionGroup pkgName
+
+ directory(pkgLogFolder, 0755)
+ link("/etc/init.d/${pkgName}", "${pkgInstallFolder}/bin/${pkgName}.jar")
+ link("${pkgInstallFolder}/bin/${pkgName}.yml", "${pkgInstallFolder}/conf/${pkgName}.yml")
+ link("/etc/${pkgName}/conf", "${pkgInstallFolder}/conf")
+}
diff --git a/transport/mqtt/pom.xml b/transport/mqtt/pom.xml
index 785e9a5306..6395b40365 100644
--- a/transport/mqtt/pom.xml
+++ b/transport/mqtt/pom.xml
@@ -27,46 +27,37 @@
mqtt
jar
- Thingsboard MQTT Transport
+ Thingsboard MQTT Transport Service
https://thingsboard.io
UTF-8
${basedir}/../..
+ tb-mqtt-transport
+ /var/log/${pkg.name}
+ /usr/share/${pkg.name}
+ ${project.build.directory}/windows
- org.thingsboard.common
- transport
-
-
- io.netty
- netty-all
+ org.thingsboard.common.transport
+ mqtt
- org.springframework
- spring-context
-
-
- org.slf4j
- slf4j-api
-
-
- org.slf4j
- log4j-over-slf4j
-
-
- ch.qos.logback
- logback-core
+ org.thingsboard.common
+ queue
- ch.qos.logback
- logback-classic
+ org.springframework.boot
+ spring-boot-starter-web
- com.google.guava
- guava
+ com.sun.winsw
+ winsw
+ bin
+ exe
+ provided
org.springframework.boot
@@ -85,4 +76,266 @@
+
+ ${pkg.name}-${project.version}
+
+
+ ${project.basedir}/src/main/resources
+
+
+
+
+ org.apache.maven.plugins
+ maven-resources-plugin
+
+
+ copy-conf
+ process-resources
+
+ copy-resources
+
+
+ ${project.build.directory}/conf
+
+
+ src/main/resources
+
+ logback.xml
+
+ false
+
+
+
+
+
+ copy-service-conf
+ process-resources
+
+ copy-resources
+
+
+ ${project.build.directory}/conf
+
+
+ src/main/conf
+ true
+
+
+
+ src/main/filters/unix.properties
+
+
+
+
+ copy-win-conf
+ process-resources
+
+ copy-resources
+
+
+ ${pkg.win.dist}/conf
+
+
+ src/main/resources
+
+ logback.xml
+
+ false
+
+
+ src/main/conf
+
+ tb-mqtt-transport.conf
+
+ true
+
+
+
+ src/main/filters/windows.properties
+
+
+
+
+ copy-control
+ process-resources
+
+ copy-resources
+
+
+ ${project.build.directory}/control
+
+
+ src/main/scripts/control
+ true
+
+
+
+ src/main/filters/unix.properties
+
+
+
+
+ copy-windows-control
+ process-resources
+
+ copy-resources
+
+
+ ${pkg.win.dist}
+
+
+ src/main/scripts/windows
+ true
+
+
+
+ src/main/filters/windows.properties
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-dependency-plugin
+
+
+ copy-winsw-service
+ package
+
+ copy
+
+
+
+
+ com.sun.winsw
+ winsw
+ bin
+ exe
+ service.exe
+
+
+ ${pkg.win.dist}
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+
+
+ **/logback.xml
+
+
+
+ ThingsBoard MQTT Transport Service
+ ${project.version}
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+ org.thingsboard.server.mqtt.ThingsboardMqttTransportApplication
+ boot
+ ZIP
+ true
+ true
+
+ ${pkg.installFolder}/conf
+ ${pkg.unixLogFolder}
+ ${pkg.name}.out
+ ${pkg.name}
+
+
+
+
+
+ repackage
+
+
+
+
+
+ org.fortasoft
+ gradle-maven-plugin
+
+
+ build
+ buildDeb
+ buildRpm
+
+
+ -PprojectBuildDir=${project.build.directory}
+ -PprojectVersion=${project.version}
+ -PmainJar=${project.build.directory}/${project.build.finalName}-boot.${project.packaging}
+ -PpkgName=${pkg.name}
+ -PpkgInstallFolder=${pkg.installFolder}
+ -PpkgLogFolder=${pkg.unixLogFolder}
+
+
+
+
+ package
+
+ invoke
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+
+ ${pkg.name}
+
+ src/main/assembly/windows.xml
+
+
+
+
+ assembly
+ package
+
+ single
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-install-plugin
+
+ ${project.build.directory}/${pkg.name}.deb
+ ${project.artifactId}
+ ${project.groupId}
+ ${project.version}
+ deb
+ deb
+
+
+
+ install-deb
+ package
+
+ install-file
+
+
+
+
+
+
+
+
+ jenkins
+ Jenkins Repository
+ http://repo.jenkins-ci.org/releases
+
+ false
+
+
+
diff --git a/transport/mqtt/src/main/assembly/windows.xml b/transport/mqtt/src/main/assembly/windows.xml
new file mode 100644
index 0000000000..82da34ed13
--- /dev/null
+++ b/transport/mqtt/src/main/assembly/windows.xml
@@ -0,0 +1,71 @@
+
+
+ windows
+
+
+ zip
+
+
+
+
+
+ ${pkg.win.dist}
+ logs
+
+ */**
+
+
+
+ ${pkg.win.dist}/conf
+ conf
+ windows
+
+
+
+
+
+ ${project.build.directory}/${project.build.finalName}-boot.${project.packaging}
+ lib
+ ${pkg.name}.jar
+
+
+ ${pkg.win.dist}/service.exe
+
+ ${pkg.name}.exe
+
+
+ ${pkg.win.dist}/service.xml
+
+ ${pkg.name}.xml
+ windows
+
+
+ ${pkg.win.dist}/install.bat
+
+ windows
+
+
+ ${pkg.win.dist}/uninstall.bat
+
+ windows
+
+
+
diff --git a/transport/mqtt/src/main/conf/logback.xml b/transport/mqtt/src/main/conf/logback.xml
new file mode 100644
index 0000000000..f36469d5f2
--- /dev/null
+++ b/transport/mqtt/src/main/conf/logback.xml
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+ ${pkg.logFolder}/${pkg.name}.log
+
+ ${pkg.logFolder}/${pkg.name}.%d{yyyy-MM-dd}.%i.log
+ 100MB
+ 30
+ 3GB
+
+
+ %d{ISO8601} [%thread] %-5level %logger{36} - %msg%n
+
+
+
+
+
+
+
+
+
+
diff --git a/transport/mqtt/src/main/conf/tb-mqtt-transport.conf b/transport/mqtt/src/main/conf/tb-mqtt-transport.conf
new file mode 100644
index 0000000000..0afa91c18e
--- /dev/null
+++ b/transport/mqtt/src/main/conf/tb-mqtt-transport.conf
@@ -0,0 +1,23 @@
+#
+# 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.
+#
+
+export JAVA_OPTS="$JAVA_OPTS -Xloggc:@pkg.logFolder@/gc.log -XX:+IgnoreUnrecognizedVMOptions -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDetails -XX:+PrintGCDateStamps"
+export JAVA_OPTS="$JAVA_OPTS -XX:+PrintHeapAtGC -XX:+PrintTenuringDistribution -XX:+PrintGCApplicationStoppedTime -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10"
+export JAVA_OPTS="$JAVA_OPTS -XX:GCLogFileSize=10M -XX:-UseBiasedLocking -XX:+UseTLAB -XX:+ResizeTLAB -XX:+PerfDisableSharedMem -XX:+UseCondCardMark"
+export JAVA_OPTS="$JAVA_OPTS -XX:CMSWaitDuration=10000 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+CMSParallelInitialMarkEnabled"
+export JAVA_OPTS="$JAVA_OPTS -XX:+CMSEdenChunksRecordAlways -XX:CMSInitiatingOccupancyFraction=75 -XX:+UseCMSInitiatingOccupancyOnly"
+export LOG_FILENAME=${pkg.name}.out
+export LOADER_PATH=${pkg.installFolder}/conf
diff --git a/transport/mqtt/src/main/filters/unix.properties b/transport/mqtt/src/main/filters/unix.properties
new file mode 100644
index 0000000000..8967278673
--- /dev/null
+++ b/transport/mqtt/src/main/filters/unix.properties
@@ -0,0 +1 @@
+pkg.logFolder=${pkg.unixLogFolder}
\ No newline at end of file
diff --git a/transport/mqtt/src/main/filters/windows.properties b/transport/mqtt/src/main/filters/windows.properties
new file mode 100644
index 0000000000..a6e48d91ba
--- /dev/null
+++ b/transport/mqtt/src/main/filters/windows.properties
@@ -0,0 +1,2 @@
+pkg.logFolder=${BASE}\\logs
+pkg.winWrapperLogFolder=%BASE%\\logs
diff --git a/transport/mqtt/src/main/java/org/thingsboard/server/mqtt/ThingsboardMqttTransportApplication.java b/transport/mqtt/src/main/java/org/thingsboard/server/mqtt/ThingsboardMqttTransportApplication.java
new file mode 100644
index 0000000000..2bae2ff855
--- /dev/null
+++ b/transport/mqtt/src/main/java/org/thingsboard/server/mqtt/ThingsboardMqttTransportApplication.java
@@ -0,0 +1,49 @@
+/**
+ * Copyright © 2016-2018 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.thingsboard.server.mqtt;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.SpringBootConfiguration;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.scheduling.annotation.EnableAsync;
+import org.springframework.scheduling.annotation.EnableScheduling;
+
+import java.util.Arrays;
+
+@SpringBootConfiguration
+@EnableAsync
+@EnableScheduling
+@ComponentScan({"org.thingsboard.server.mqtt", "org.thingsboard.server.common", "org.thingsboard.server.transport.mqtt", "org.thingsboard.server.kafka"})
+public class ThingsboardMqttTransportApplication {
+
+ private static final String SPRING_CONFIG_NAME_KEY = "--spring.config.name";
+ private static final String DEFAULT_SPRING_CONFIG_PARAM = SPRING_CONFIG_NAME_KEY + "=" + "tb-mqtt-transport";
+
+ public static void main(String[] args) {
+ SpringApplication.run(ThingsboardMqttTransportApplication.class, updateArguments(args));
+ }
+
+ private static String[] updateArguments(String[] args) {
+ if (Arrays.stream(args).noneMatch(arg -> arg.startsWith(SPRING_CONFIG_NAME_KEY))) {
+ String[] modifiedArgs = new String[args.length + 1];
+ System.arraycopy(args, 0, modifiedArgs, 0, args.length);
+ modifiedArgs[args.length] = DEFAULT_SPRING_CONFIG_PARAM;
+ return modifiedArgs;
+ }
+ return args;
+ }
+}
diff --git a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java b/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java
deleted file mode 100644
index 3c8365e27d..0000000000
--- a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java
+++ /dev/null
@@ -1,456 +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.transport.mqtt;
-
-import com.fasterxml.jackson.databind.JsonNode;
-import io.netty.channel.ChannelHandlerContext;
-import io.netty.channel.ChannelInboundHandlerAdapter;
-import io.netty.handler.codec.mqtt.*;
-import io.netty.handler.codec.mqtt.MqttConnectReturnCode;
-import io.netty.handler.codec.mqtt.MqttQoS;
-import io.netty.handler.ssl.SslHandler;
-import io.netty.util.concurrent.Future;
-import io.netty.util.concurrent.GenericFutureListener;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.util.StringUtils;
-import org.thingsboard.server.common.data.Device;
-import org.thingsboard.server.common.data.security.DeviceTokenCredentials;
-import org.thingsboard.server.common.data.security.DeviceX509Credentials;
-import org.thingsboard.server.common.msg.core.SessionOpenMsg;
-import org.thingsboard.server.common.msg.session.AdaptorToSessionActorMsg;
-import org.thingsboard.server.common.msg.session.BasicAdaptorToSessionActorMsg;
-import org.thingsboard.server.common.msg.session.BasicTransportToDeviceSessionActorMsg;
-import org.thingsboard.server.common.msg.session.ctrl.SessionCloseMsg;
-import org.thingsboard.server.common.transport.SessionMsgProcessor;
-import org.thingsboard.server.common.transport.adaptor.AdaptorException;
-import org.thingsboard.server.common.transport.auth.DeviceAuthService;
-import org.thingsboard.server.common.transport.quota.QuotaService;
-import org.thingsboard.server.dao.EncryptionUtil;
-import org.thingsboard.server.dao.device.DeviceService;
-import org.thingsboard.server.dao.relation.RelationService;
-import org.thingsboard.server.transport.mqtt.adaptors.MqttTransportAdaptor;
-import org.thingsboard.server.transport.mqtt.session.DeviceSessionCtx;
-import org.thingsboard.server.transport.mqtt.session.GatewaySessionCtx;
-import org.thingsboard.server.transport.mqtt.util.SslUtil;
-
-import javax.net.ssl.SSLPeerUnverifiedException;
-import javax.security.cert.X509Certificate;
-import java.net.InetSocketAddress;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-import java.util.regex.Pattern;
-
-import static io.netty.handler.codec.mqtt.MqttConnectReturnCode.*;
-import static io.netty.handler.codec.mqtt.MqttMessageType.*;
-import static io.netty.handler.codec.mqtt.MqttQoS.*;
-import static org.thingsboard.server.common.msg.session.SessionMsgType.*;
-import static org.thingsboard.server.transport.mqtt.MqttTopics.*;
-
-/**
- * @author Andrew Shvayka
- */
-@Slf4j
-public class MqttTransportHandler extends ChannelInboundHandlerAdapter implements GenericFutureListener> {
-
- public static final MqttQoS MAX_SUPPORTED_QOS_LVL = AT_LEAST_ONCE;
-
- private final DeviceSessionCtx deviceSessionCtx;
- private final String sessionId;
- private final MqttTransportAdaptor adaptor;
- private final SessionMsgProcessor processor;
- private final DeviceService deviceService;
- private final DeviceAuthService authService;
- private final RelationService relationService;
- private final QuotaService quotaService;
- private final SslHandler sslHandler;
- private final ConcurrentMap mqttQoSMap;
-
- private volatile boolean connected;
- private volatile InetSocketAddress address;
- private volatile GatewaySessionCtx gatewaySessionCtx;
-
- public MqttTransportHandler(SessionMsgProcessor processor, DeviceService deviceService, DeviceAuthService authService, RelationService relationService,
- MqttTransportAdaptor adaptor, SslHandler sslHandler, QuotaService quotaService) {
- this.processor = processor;
- this.deviceService = deviceService;
- this.relationService = relationService;
- this.authService = authService;
- this.adaptor = adaptor;
- this.mqttQoSMap = new ConcurrentHashMap<>();
- this.deviceSessionCtx = new DeviceSessionCtx(processor, authService, adaptor, mqttQoSMap);
- this.sessionId = deviceSessionCtx.getSessionId().toUidStr();
- this.sslHandler = sslHandler;
- this.quotaService = quotaService;
- }
-
- @Override
- public void channelRead(ChannelHandlerContext ctx, Object msg) {
- log.trace("[{}] Processing msg: {}", sessionId, msg);
- if (msg instanceof MqttMessage) {
- processMqttMsg(ctx, (MqttMessage) msg);
- } else {
- ctx.close();
- }
- }
-
- private void processMqttMsg(ChannelHandlerContext ctx, MqttMessage msg) {
- address = (InetSocketAddress) ctx.channel().remoteAddress();
- if (msg.fixedHeader() == null) {
- log.info("[{}:{}] Invalid message received", address.getHostName(), address.getPort());
- processDisconnect(ctx);
- return;
- }
-
- if (quotaService.isQuotaExceeded(address.getHostName())) {
- log.warn("MQTT Quota exceeded for [{}:{}] . Disconnect", address.getHostName(), address.getPort());
- processDisconnect(ctx);
- return;
- }
-
- deviceSessionCtx.setChannel(ctx);
- switch (msg.fixedHeader().messageType()) {
- case CONNECT:
- processConnect(ctx, (MqttConnectMessage) msg);
- break;
- case PUBLISH:
- processPublish(ctx, (MqttPublishMessage) msg);
- break;
- case SUBSCRIBE:
- processSubscribe(ctx, (MqttSubscribeMessage) msg);
- break;
- case UNSUBSCRIBE:
- processUnsubscribe(ctx, (MqttUnsubscribeMessage) msg);
- break;
- case PINGREQ:
- if (checkConnected(ctx)) {
- ctx.writeAndFlush(new MqttMessage(new MqttFixedHeader(PINGRESP, false, AT_MOST_ONCE, false, 0)));
- }
- break;
- case DISCONNECT:
- if (checkConnected(ctx)) {
- processDisconnect(ctx);
- }
- break;
- default:
- break;
- }
-
- }
-
- private void processPublish(ChannelHandlerContext ctx, MqttPublishMessage mqttMsg) {
- if (!checkConnected(ctx)) {
- return;
- }
- String topicName = mqttMsg.variableHeader().topicName();
- int msgId = mqttMsg.variableHeader().messageId();
- log.trace("[{}] Processing publish msg [{}][{}]!", sessionId, topicName, msgId);
-
- if (topicName.startsWith(BASE_GATEWAY_API_TOPIC)) {
- if (gatewaySessionCtx != null) {
- gatewaySessionCtx.setChannel(ctx);
- handleMqttPublishMsg(topicName, msgId, mqttMsg);
- }
- } else {
- processDevicePublish(ctx, mqttMsg, topicName, msgId);
- }
- }
-
- private void handleMqttPublishMsg(String topicName, int msgId, MqttPublishMessage mqttMsg) {
- try {
- switch (topicName) {
- case GATEWAY_TELEMETRY_TOPIC:
- gatewaySessionCtx.onDeviceTelemetry(mqttMsg);
- break;
- case GATEWAY_ATTRIBUTES_TOPIC:
- gatewaySessionCtx.onDeviceAttributes(mqttMsg);
- break;
- case GATEWAY_ATTRIBUTES_REQUEST_TOPIC:
- gatewaySessionCtx.onDeviceAttributesRequest(mqttMsg);
- break;
- case GATEWAY_RPC_TOPIC:
- gatewaySessionCtx.onDeviceRpcResponse(mqttMsg);
- break;
- case GATEWAY_CONNECT_TOPIC:
- gatewaySessionCtx.onDeviceConnect(mqttMsg);
- break;
- case GATEWAY_DISCONNECT_TOPIC:
- gatewaySessionCtx.onDeviceDisconnect(mqttMsg);
- break;
- }
- } catch (RuntimeException | AdaptorException e) {
- log.warn("[{}] Failed to process publish msg [{}][{}]", sessionId, topicName, msgId, e);
- }
- }
-
- private void processDevicePublish(ChannelHandlerContext ctx, MqttPublishMessage mqttMsg, String topicName, int msgId) {
- AdaptorToSessionActorMsg msg = null;
- try {
- if (topicName.equals(DEVICE_TELEMETRY_TOPIC)) {
- msg = adaptor.convertToActorMsg(deviceSessionCtx, POST_TELEMETRY_REQUEST, mqttMsg);
- } else if (topicName.equals(DEVICE_ATTRIBUTES_TOPIC)) {
- msg = adaptor.convertToActorMsg(deviceSessionCtx, POST_ATTRIBUTES_REQUEST, mqttMsg);
- } else if (topicName.startsWith(DEVICE_ATTRIBUTES_REQUEST_TOPIC_PREFIX)) {
- msg = adaptor.convertToActorMsg(deviceSessionCtx, GET_ATTRIBUTES_REQUEST, mqttMsg);
- if (msgId >= 0) {
- ctx.writeAndFlush(createMqttPubAckMsg(msgId));
- }
- } else if (topicName.startsWith(DEVICE_RPC_RESPONSE_TOPIC)) {
- msg = adaptor.convertToActorMsg(deviceSessionCtx, TO_DEVICE_RPC_RESPONSE, mqttMsg);
- if (msgId >= 0) {
- ctx.writeAndFlush(createMqttPubAckMsg(msgId));
- }
- } else if (topicName.startsWith(DEVICE_RPC_REQUESTS_TOPIC)) {
- msg = adaptor.convertToActorMsg(deviceSessionCtx, TO_SERVER_RPC_REQUEST, mqttMsg);
- if (msgId >= 0) {
- ctx.writeAndFlush(createMqttPubAckMsg(msgId));
- }
- }
- } catch (AdaptorException e) {
- log.warn("[{}] Failed to process publish msg [{}][{}]", sessionId, topicName, msgId, e);
- }
- if (msg != null) {
- processor.process(new BasicTransportToDeviceSessionActorMsg(deviceSessionCtx.getDevice(), msg));
- } else {
- log.info("[{}] Closing current session due to invalid publish msg [{}][{}]", sessionId, topicName, msgId);
- ctx.close();
- }
- }
-
- private void processSubscribe(ChannelHandlerContext ctx, MqttSubscribeMessage mqttMsg) {
- if (!checkConnected(ctx)) {
- return;
- }
- log.trace("[{}] Processing subscription [{}]!", sessionId, mqttMsg.variableHeader().messageId());
- List grantedQoSList = new ArrayList<>();
- for (MqttTopicSubscription subscription : mqttMsg.payload().topicSubscriptions()) {
- String topic = subscription.topicName();
- MqttQoS reqQoS = subscription.qualityOfService();
- try {
- switch (topic) {
- case DEVICE_ATTRIBUTES_TOPIC: {
- AdaptorToSessionActorMsg msg = adaptor.convertToActorMsg(deviceSessionCtx, SUBSCRIBE_ATTRIBUTES_REQUEST, mqttMsg);
- processor.process(new BasicTransportToDeviceSessionActorMsg(deviceSessionCtx.getDevice(), msg));
- registerSubQoS(topic, grantedQoSList, reqQoS);
- break;
- }
- case DEVICE_RPC_REQUESTS_SUB_TOPIC: {
- AdaptorToSessionActorMsg msg = adaptor.convertToActorMsg(deviceSessionCtx, SUBSCRIBE_RPC_COMMANDS_REQUEST, mqttMsg);
- processor.process(new BasicTransportToDeviceSessionActorMsg(deviceSessionCtx.getDevice(), msg));
- registerSubQoS(topic, grantedQoSList, reqQoS);
- break;
- }
- case DEVICE_RPC_RESPONSE_SUB_TOPIC:
- case GATEWAY_ATTRIBUTES_TOPIC:
- case GATEWAY_RPC_TOPIC:
- registerSubQoS(topic, grantedQoSList, reqQoS);
- break;
- case DEVICE_ATTRIBUTES_RESPONSES_TOPIC:
- deviceSessionCtx.setAllowAttributeResponses();
- registerSubQoS(topic, grantedQoSList, reqQoS);
- break;
- default:
- log.warn("[{}] Failed to subscribe to [{}][{}]", sessionId, topic, reqQoS);
- grantedQoSList.add(FAILURE.value());
- break;
- }
- } catch (AdaptorException e) {
- log.warn("[{}] Failed to subscribe to [{}][{}]", sessionId, topic, reqQoS);
- grantedQoSList.add(FAILURE.value());
- }
- }
- ctx.writeAndFlush(createSubAckMessage(mqttMsg.variableHeader().messageId(), grantedQoSList));
- }
-
- private void registerSubQoS(String topic, List grantedQoSList, MqttQoS reqQoS) {
- grantedQoSList.add(getMinSupportedQos(reqQoS));
- mqttQoSMap.put(new MqttTopicMatcher(topic), getMinSupportedQos(reqQoS));
- }
-
- private void processUnsubscribe(ChannelHandlerContext ctx, MqttUnsubscribeMessage mqttMsg) {
- if (!checkConnected(ctx)) {
- return;
- }
- log.trace("[{}] Processing subscription [{}]!", sessionId, mqttMsg.variableHeader().messageId());
- for (String topicName : mqttMsg.payload().topics()) {
- mqttQoSMap.remove(new MqttTopicMatcher(topicName));
- try {
- switch (topicName) {
- case DEVICE_ATTRIBUTES_TOPIC: {
- AdaptorToSessionActorMsg msg = adaptor.convertToActorMsg(deviceSessionCtx, UNSUBSCRIBE_ATTRIBUTES_REQUEST, mqttMsg);
- processor.process(new BasicTransportToDeviceSessionActorMsg(deviceSessionCtx.getDevice(), msg));
- break;
- }
- case DEVICE_RPC_REQUESTS_SUB_TOPIC: {
- AdaptorToSessionActorMsg msg = adaptor.convertToActorMsg(deviceSessionCtx, UNSUBSCRIBE_RPC_COMMANDS_REQUEST, mqttMsg);
- processor.process(new BasicTransportToDeviceSessionActorMsg(deviceSessionCtx.getDevice(), msg));
- break;
- }
- case DEVICE_ATTRIBUTES_RESPONSES_TOPIC:
- deviceSessionCtx.setDisallowAttributeResponses();
- break;
- }
- } catch (AdaptorException e) {
- log.warn("[{}] Failed to process unsubscription [{}] to [{}]", sessionId, mqttMsg.variableHeader().messageId(), topicName);
- }
- }
- ctx.writeAndFlush(createUnSubAckMessage(mqttMsg.variableHeader().messageId()));
- }
-
- private MqttMessage createUnSubAckMessage(int msgId) {
- MqttFixedHeader mqttFixedHeader =
- new MqttFixedHeader(UNSUBACK, false, AT_LEAST_ONCE, false, 0);
- MqttMessageIdVariableHeader mqttMessageIdVariableHeader = MqttMessageIdVariableHeader.from(msgId);
- return new MqttMessage(mqttFixedHeader, mqttMessageIdVariableHeader);
- }
-
- private void processConnect(ChannelHandlerContext ctx, MqttConnectMessage msg) {
- log.info("[{}] Processing connect msg for client: {}!", sessionId, msg.payload().clientIdentifier());
- X509Certificate cert;
- if (sslHandler != null && (cert = getX509Certificate()) != null) {
- processX509CertConnect(ctx, cert);
- } else {
- processAuthTokenConnect(ctx, msg);
- }
- }
-
- private void processAuthTokenConnect(ChannelHandlerContext ctx, MqttConnectMessage msg) {
- String userName = msg.payload().userName();
- if (StringUtils.isEmpty(userName)) {
- ctx.writeAndFlush(createMqttConnAckMsg(CONNECTION_REFUSED_BAD_USER_NAME_OR_PASSWORD));
- ctx.close();
- } else if (!deviceSessionCtx.login(new DeviceTokenCredentials(msg.payload().userName()))) {
- ctx.writeAndFlush(createMqttConnAckMsg(CONNECTION_REFUSED_NOT_AUTHORIZED));
- ctx.close();
- } else {
- ctx.writeAndFlush(createMqttConnAckMsg(CONNECTION_ACCEPTED));
- connected = true;
- processor.process(new BasicTransportToDeviceSessionActorMsg(deviceSessionCtx.getDevice(),
- new BasicAdaptorToSessionActorMsg(deviceSessionCtx, new SessionOpenMsg())));
- checkGatewaySession();
- }
- }
-
- private void processX509CertConnect(ChannelHandlerContext ctx, X509Certificate cert) {
- try {
- String strCert = SslUtil.getX509CertificateString(cert);
- String sha3Hash = EncryptionUtil.getSha3Hash(strCert);
- if (deviceSessionCtx.login(new DeviceX509Credentials(sha3Hash))) {
- ctx.writeAndFlush(createMqttConnAckMsg(CONNECTION_ACCEPTED));
- connected = true;
- processor.process(new BasicTransportToDeviceSessionActorMsg(deviceSessionCtx.getDevice(),
- new BasicAdaptorToSessionActorMsg(deviceSessionCtx, new SessionOpenMsg())));
- checkGatewaySession();
- } else {
- ctx.writeAndFlush(createMqttConnAckMsg(CONNECTION_REFUSED_NOT_AUTHORIZED));
- ctx.close();
- }
- } catch (Exception e) {
- ctx.writeAndFlush(createMqttConnAckMsg(CONNECTION_REFUSED_NOT_AUTHORIZED));
- ctx.close();
- }
- }
-
- private X509Certificate getX509Certificate() {
- try {
- X509Certificate[] certChain = sslHandler.engine().getSession().getPeerCertificateChain();
- if (certChain.length > 0) {
- return certChain[0];
- }
- } catch (SSLPeerUnverifiedException e) {
- log.warn(e.getMessage());
- return null;
- }
- return null;
- }
-
- private void processDisconnect(ChannelHandlerContext ctx) {
- ctx.close();
- if (connected) {
- processor.process(SessionCloseMsg.onDisconnect(deviceSessionCtx.getSessionId()));
- if (gatewaySessionCtx != null) {
- gatewaySessionCtx.onGatewayDisconnect();
- }
- }
- }
-
- private MqttConnAckMessage createMqttConnAckMsg(MqttConnectReturnCode returnCode) {
- MqttFixedHeader mqttFixedHeader =
- new MqttFixedHeader(CONNACK, false, AT_MOST_ONCE, false, 0);
- MqttConnAckVariableHeader mqttConnAckVariableHeader =
- new MqttConnAckVariableHeader(returnCode, true);
- return new MqttConnAckMessage(mqttFixedHeader, mqttConnAckVariableHeader);
- }
-
- @Override
- public void channelReadComplete(ChannelHandlerContext ctx) {
- ctx.flush();
- }
-
- @Override
- public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
- log.error("[{}] Unexpected Exception", sessionId, cause);
- ctx.close();
- }
-
- private static MqttSubAckMessage createSubAckMessage(Integer msgId, List grantedQoSList) {
- MqttFixedHeader mqttFixedHeader =
- new MqttFixedHeader(SUBACK, false, AT_LEAST_ONCE, false, 0);
- MqttMessageIdVariableHeader mqttMessageIdVariableHeader = MqttMessageIdVariableHeader.from(msgId);
- MqttSubAckPayload mqttSubAckPayload = new MqttSubAckPayload(grantedQoSList);
- return new MqttSubAckMessage(mqttFixedHeader, mqttMessageIdVariableHeader, mqttSubAckPayload);
- }
-
- private static int getMinSupportedQos(MqttQoS reqQoS) {
- return Math.min(reqQoS.value(), MAX_SUPPORTED_QOS_LVL.value());
- }
-
- public static MqttPubAckMessage createMqttPubAckMsg(int requestId) {
- MqttFixedHeader mqttFixedHeader =
- new MqttFixedHeader(PUBACK, false, AT_LEAST_ONCE, false, 0);
- MqttMessageIdVariableHeader mqttMsgIdVariableHeader =
- MqttMessageIdVariableHeader.from(requestId);
- return new MqttPubAckMessage(mqttFixedHeader, mqttMsgIdVariableHeader);
- }
-
- private boolean checkConnected(ChannelHandlerContext ctx) {
- if (connected) {
- return true;
- } else {
- log.info("[{}] Closing current session due to invalid msg order [{}][{}]", sessionId);
- ctx.close();
- return false;
- }
- }
-
- private void checkGatewaySession() {
- Device device = deviceSessionCtx.getDevice();
- JsonNode infoNode = device.getAdditionalInfo();
- if (infoNode != null) {
- JsonNode gatewayNode = infoNode.get("gateway");
- if (gatewayNode != null && gatewayNode.asBoolean()) {
- gatewaySessionCtx = new GatewaySessionCtx(processor, deviceService, authService, relationService, deviceSessionCtx);
- }
- }
- }
-
- @Override
- public void operationComplete(Future super Void> future) throws Exception {
- processor.process(SessionCloseMsg.onError(deviceSessionCtx.getSessionId()));
- }
-}
diff --git a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportServerInitializer.java b/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportServerInitializer.java
deleted file mode 100644
index 432966eeff..0000000000
--- a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportServerInitializer.java
+++ /dev/null
@@ -1,76 +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.transport.mqtt;
-
-import io.netty.channel.ChannelInitializer;
-import io.netty.channel.ChannelPipeline;
-import io.netty.channel.socket.SocketChannel;
-import io.netty.handler.codec.mqtt.MqttDecoder;
-import io.netty.handler.codec.mqtt.MqttEncoder;
-import io.netty.handler.ssl.SslHandler;
-import org.thingsboard.server.common.transport.SessionMsgProcessor;
-import org.thingsboard.server.common.transport.auth.DeviceAuthService;
-import org.thingsboard.server.common.transport.quota.QuotaService;
-import org.thingsboard.server.dao.device.DeviceService;
-import org.thingsboard.server.dao.relation.RelationService;
-import org.thingsboard.server.transport.mqtt.adaptors.MqttTransportAdaptor;
-
-/**
- * @author Andrew Shvayka
- */
-public class MqttTransportServerInitializer extends ChannelInitializer {
-
- private final SessionMsgProcessor processor;
- private final DeviceService deviceService;
- private final DeviceAuthService authService;
- private final RelationService relationService;
- private final MqttTransportAdaptor adaptor;
- private final MqttSslHandlerProvider sslHandlerProvider;
- private final QuotaService quotaService;
- private final int maxPayloadSize;
-
- public MqttTransportServerInitializer(SessionMsgProcessor processor, DeviceService deviceService, DeviceAuthService authService, RelationService relationService,
- MqttTransportAdaptor adaptor, MqttSslHandlerProvider sslHandlerProvider,
- QuotaService quotaService, int maxPayloadSize) {
- this.processor = processor;
- this.deviceService = deviceService;
- this.authService = authService;
- this.relationService = relationService;
- this.adaptor = adaptor;
- this.sslHandlerProvider = sslHandlerProvider;
- this.quotaService = quotaService;
- this.maxPayloadSize = maxPayloadSize;
- }
-
- @Override
- public void initChannel(SocketChannel ch) {
- ChannelPipeline pipeline = ch.pipeline();
- SslHandler sslHandler = null;
- if (sslHandlerProvider != null) {
- sslHandler = sslHandlerProvider.getSslHandler();
- pipeline.addLast(sslHandler);
- }
- pipeline.addLast("decoder", new MqttDecoder(maxPayloadSize));
- pipeline.addLast("encoder", MqttEncoder.INSTANCE);
-
- MqttTransportHandler handler = new MqttTransportHandler(processor, deviceService, authService, relationService,
- adaptor, sslHandler, quotaService);
-
- pipeline.addLast(handler);
- ch.closeFuture().addListener(handler);
- }
-
-}
diff --git a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java b/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java
deleted file mode 100644
index c8baaf7e53..0000000000
--- a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java
+++ /dev/null
@@ -1,273 +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.transport.mqtt.adaptors;
-
-import com.google.gson.Gson;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonParser;
-import com.google.gson.JsonSyntaxException;
-import io.netty.buffer.ByteBuf;
-import io.netty.buffer.ByteBufAllocator;
-import io.netty.buffer.UnpooledByteBufAllocator;
-import io.netty.handler.codec.mqtt.*;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.stereotype.Component;
-import org.thingsboard.server.common.data.id.SessionId;
-import org.thingsboard.server.common.msg.core.*;
-import org.thingsboard.server.common.msg.kv.AttributesKVMsg;
-import org.thingsboard.server.common.msg.session.*;
-import org.thingsboard.server.common.transport.adaptor.AdaptorException;
-import org.thingsboard.server.common.transport.adaptor.JsonConverter;
-import org.thingsboard.server.transport.mqtt.MqttTopics;
-import org.thingsboard.server.transport.mqtt.session.DeviceSessionCtx;
-import org.thingsboard.server.transport.mqtt.MqttTransportHandler;
-
-import java.nio.charset.Charset;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.Optional;
-import java.util.Set;
-
-/**
- * @author Andrew Shvayka
- */
-@Component("JsonMqttAdaptor")
-@Slf4j
-public class JsonMqttAdaptor implements MqttTransportAdaptor {
-
- private static final Gson GSON = new Gson();
- private static final Charset UTF8 = Charset.forName("UTF-8");
- private static final ByteBufAllocator ALLOCATOR = new UnpooledByteBufAllocator(false);
-
- @Override
- public AdaptorToSessionActorMsg convertToActorMsg(DeviceSessionCtx ctx, SessionMsgType type, MqttMessage inbound) throws AdaptorException {
- FromDeviceMsg msg;
- switch (type) {
- case POST_TELEMETRY_REQUEST:
- msg = convertToTelemetryUploadRequest(ctx, (MqttPublishMessage) inbound);
- break;
- case POST_ATTRIBUTES_REQUEST:
- msg = convertToUpdateAttributesRequest(ctx, (MqttPublishMessage) inbound);
- break;
- case SUBSCRIBE_ATTRIBUTES_REQUEST:
- msg = new AttributesSubscribeMsg();
- break;
- case UNSUBSCRIBE_ATTRIBUTES_REQUEST:
- msg = new AttributesUnsubscribeMsg();
- break;
- case SUBSCRIBE_RPC_COMMANDS_REQUEST:
- msg = new RpcSubscribeMsg();
- break;
- case UNSUBSCRIBE_RPC_COMMANDS_REQUEST:
- msg = new RpcUnsubscribeMsg();
- break;
- case GET_ATTRIBUTES_REQUEST:
- msg = convertToGetAttributesRequest(ctx, (MqttPublishMessage) inbound);
- break;
- case TO_DEVICE_RPC_RESPONSE:
- msg = convertToRpcCommandResponse(ctx, (MqttPublishMessage) inbound);
- break;
- case TO_SERVER_RPC_REQUEST:
- msg = convertToServerRpcRequest(ctx, (MqttPublishMessage) inbound);
- break;
- default:
- log.warn("[{}] Unsupported msg type: {}!", ctx.getSessionId(), type);
- throw new AdaptorException(new IllegalArgumentException("Unsupported msg type: " + type + "!"));
- }
- return new BasicAdaptorToSessionActorMsg(ctx, msg);
- }
-
- @Override
- public Optional convertToAdaptorMsg(DeviceSessionCtx ctx, SessionActorToAdaptorMsg sessionMsg) throws AdaptorException {
- MqttMessage result = null;
- ToDeviceMsg msg = sessionMsg.getMsg();
- switch (msg.getSessionMsgType()) {
- case STATUS_CODE_RESPONSE:
- case GET_ATTRIBUTES_RESPONSE:
- ResponseMsg> responseMsg = (ResponseMsg) msg;
- Optional responseError = responseMsg.getError();
- if (responseMsg.isSuccess()) {
- result = convertResponseMsg(ctx, msg, responseMsg, responseError);
- } else {
- if (responseError.isPresent()) {
- throw new AdaptorException(responseError.get());
- }
- }
- break;
- case ATTRIBUTES_UPDATE_NOTIFICATION:
- AttributesUpdateNotification notification = (AttributesUpdateNotification) msg;
- result = createMqttPublishMsg(ctx, MqttTopics.DEVICE_ATTRIBUTES_TOPIC, notification.getData(), false);
- break;
- case TO_DEVICE_RPC_REQUEST:
- ToDeviceRpcRequestMsg rpcRequest = (ToDeviceRpcRequestMsg) msg;
- result = createMqttPublishMsg(ctx, MqttTopics.DEVICE_RPC_REQUESTS_TOPIC + rpcRequest.getRequestId(),
- rpcRequest);
- break;
- case TO_SERVER_RPC_RESPONSE:
- ToServerRpcResponseMsg rpcResponse = (ToServerRpcResponseMsg) msg;
- result = createMqttPublishMsg(ctx, MqttTopics.DEVICE_RPC_RESPONSE_TOPIC + rpcResponse.getRequestId(),
- rpcResponse);
- break;
- case RULE_ENGINE_ERROR:
- RuleEngineErrorMsg errorMsg = (RuleEngineErrorMsg) msg;
- result = createMqttPublishMsg(ctx, "errors", JsonConverter.toErrorJson(errorMsg.getErrorMsg()));
- break;
- default:
- break;
- }
- return Optional.ofNullable(result);
- }
-
- private MqttMessage convertResponseMsg(DeviceSessionCtx ctx, ToDeviceMsg msg,
- ResponseMsg> responseMsg, Optional responseError) throws AdaptorException {
- MqttMessage result = null;
- SessionMsgType requestMsgType = responseMsg.getRequestMsgType();
- Integer requestId = responseMsg.getRequestId();
- if (requestId >= 0) {
- if (requestMsgType == SessionMsgType.POST_ATTRIBUTES_REQUEST || requestMsgType == SessionMsgType.POST_TELEMETRY_REQUEST) {
- result = MqttTransportHandler.createMqttPubAckMsg(requestId);
- } else if (requestMsgType == SessionMsgType.GET_ATTRIBUTES_REQUEST) {
- GetAttributesResponse response = (GetAttributesResponse) msg;
- Optional responseData = response.getData();
- if (response.isSuccess() && responseData.isPresent()) {
- result = createMqttPublishMsg(ctx,
- MqttTopics.DEVICE_ATTRIBUTES_RESPONSE_TOPIC_PREFIX + requestId,
- responseData.get(), true);
- } else {
- if (responseError.isPresent()) {
- throw new AdaptorException(responseError.get());
- }
- }
- }
- }
- return result;
- }
-
- private MqttPublishMessage createMqttPublishMsg(DeviceSessionCtx ctx, String topic, AttributesKVMsg msg, boolean asMap) {
- return createMqttPublishMsg(ctx, topic, JsonConverter.toJson(msg, asMap));
- }
-
- private MqttPublishMessage createMqttPublishMsg(DeviceSessionCtx ctx, String topic, ToDeviceRpcRequestMsg msg) {
- return createMqttPublishMsg(ctx, topic, JsonConverter.toJson(msg, false));
- }
-
- private MqttPublishMessage createMqttPublishMsg(DeviceSessionCtx ctx, String topic, ToServerRpcResponseMsg msg) {
- return createMqttPublishMsg(ctx, topic, JsonConverter.toJson(msg));
- }
-
- private MqttPublishMessage createMqttPublishMsg(DeviceSessionCtx ctx, String topic, JsonElement json) {
- MqttFixedHeader mqttFixedHeader =
- new MqttFixedHeader(MqttMessageType.PUBLISH, false, ctx.getQoSForTopic(topic), false, 0);
- MqttPublishVariableHeader header = new MqttPublishVariableHeader(topic, ctx.nextMsgId());
- ByteBuf payload = ALLOCATOR.buffer();
- payload.writeBytes(GSON.toJson(json).getBytes(UTF8));
- return new MqttPublishMessage(mqttFixedHeader, header, payload);
- }
-
- private FromDeviceMsg convertToGetAttributesRequest(DeviceSessionCtx ctx, MqttPublishMessage inbound) throws AdaptorException {
- String topicName = inbound.variableHeader().topicName();
- try {
- Integer requestId = Integer.valueOf(topicName.substring(MqttTopics.DEVICE_ATTRIBUTES_REQUEST_TOPIC_PREFIX.length()));
- String payload = inbound.payload().toString(UTF8);
- JsonElement requestBody = new JsonParser().parse(payload);
- Set clientKeys = toStringSet(requestBody, "clientKeys");
- Set sharedKeys = toStringSet(requestBody, "sharedKeys");
- if (clientKeys == null && sharedKeys == null) {
- return new BasicGetAttributesRequest(requestId);
- } else {
- return new BasicGetAttributesRequest(requestId, clientKeys, sharedKeys);
- }
- } catch (RuntimeException e) {
- log.warn("Failed to decode get attributes request", e);
- throw new AdaptorException(e);
- }
- }
-
- private FromDeviceMsg convertToRpcCommandResponse(DeviceSessionCtx ctx, MqttPublishMessage inbound) throws AdaptorException {
- String topicName = inbound.variableHeader().topicName();
- try {
- Integer requestId = Integer.valueOf(topicName.substring(MqttTopics.DEVICE_RPC_RESPONSE_TOPIC.length()));
- String payload = inbound.payload().toString(UTF8);
- return new ToDeviceRpcResponseMsg(
- requestId,
- payload);
- } catch (RuntimeException e) {
- log.warn("Failed to decode get attributes request", e);
- throw new AdaptorException(e);
- }
- }
-
- private Set toStringSet(JsonElement requestBody, String name) {
- JsonElement element = requestBody.getAsJsonObject().get(name);
- if (element != null) {
- return new HashSet<>(Arrays.asList(element.getAsString().split(",")));
- } else {
- return null;
- }
- }
-
- private AttributesUpdateRequest convertToUpdateAttributesRequest(SessionContext ctx, MqttPublishMessage inbound) throws AdaptorException {
- String payload = validatePayload(ctx.getSessionId(), inbound.payload());
- try {
- return JsonConverter.convertToAttributes(new JsonParser().parse(payload), inbound.variableHeader().messageId());
- } catch (IllegalStateException | JsonSyntaxException ex) {
- throw new AdaptorException(ex);
- }
- }
-
- private TelemetryUploadRequest convertToTelemetryUploadRequest(SessionContext ctx, MqttPublishMessage inbound) throws AdaptorException {
- String payload = validatePayload(ctx.getSessionId(), inbound.payload());
- try {
- return JsonConverter.convertToTelemetry(new JsonParser().parse(payload), inbound.variableHeader().messageId());
- } catch (IllegalStateException | JsonSyntaxException ex) {
- throw new AdaptorException(ex);
- }
- }
-
- private FromDeviceMsg convertToServerRpcRequest(DeviceSessionCtx ctx, MqttPublishMessage inbound) throws AdaptorException {
- String topicName = inbound.variableHeader().topicName();
- String payload = validatePayload(ctx.getSessionId(), inbound.payload());
- try {
- Integer requestId = Integer.valueOf(topicName.substring(MqttTopics.DEVICE_RPC_REQUESTS_TOPIC.length()));
- return JsonConverter.convertToServerRpcRequest(new JsonParser().parse(payload), requestId);
- } catch (IllegalStateException | JsonSyntaxException ex) {
- throw new AdaptorException(ex);
- }
- }
-
- public static JsonElement validateJsonPayload(SessionId sessionId, ByteBuf payloadData) throws AdaptorException {
- String payload = validatePayload(sessionId, payloadData);
- try {
- return new JsonParser().parse(payload);
- } catch (JsonSyntaxException ex) {
- throw new AdaptorException(ex);
- }
- }
-
- public static String validatePayload(SessionId sessionId, ByteBuf payloadData) throws AdaptorException {
- try {
- String payload = payloadData.toString(UTF8);
- if (payload == null) {
- log.warn("[{}] Payload is empty!", sessionId.toUidStr());
- throw new AdaptorException(new IllegalArgumentException("Payload is empty!"));
- }
- return payload;
- } finally {
- payloadData.release();
- }
- }
-
-}
diff --git a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/MqttTransportAdaptor.java b/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/MqttTransportAdaptor.java
deleted file mode 100644
index bf83a1fd98..0000000000
--- a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/MqttTransportAdaptor.java
+++ /dev/null
@@ -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.transport.mqtt.adaptors;
-
-import io.netty.handler.codec.mqtt.MqttMessage;
-import org.thingsboard.server.common.transport.TransportAdaptor;
-import org.thingsboard.server.transport.mqtt.session.DeviceSessionCtx;
-
-/**
- * @author Andrew Shvayka
- */
-public interface MqttTransportAdaptor extends TransportAdaptor {
-}
diff --git a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/DeviceSessionCtx.java b/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/DeviceSessionCtx.java
deleted file mode 100644
index 256ed119da..0000000000
--- a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/DeviceSessionCtx.java
+++ /dev/null
@@ -1,119 +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.transport.mqtt.session;
-
-import io.netty.channel.ChannelHandlerContext;
-import io.netty.handler.codec.mqtt.MqttFixedHeader;
-import io.netty.handler.codec.mqtt.MqttMessage;
-import io.netty.handler.codec.mqtt.MqttMessageType;
-import io.netty.handler.codec.mqtt.MqttQoS;
-import lombok.extern.slf4j.Slf4j;
-import org.thingsboard.server.common.data.id.SessionId;
-import org.thingsboard.server.common.msg.session.SessionActorToAdaptorMsg;
-import org.thingsboard.server.common.msg.session.SessionCtrlMsg;
-import org.thingsboard.server.common.msg.session.SessionType;
-import org.thingsboard.server.common.msg.session.ctrl.SessionCloseMsg;
-import org.thingsboard.server.common.msg.session.ex.SessionException;
-import org.thingsboard.server.common.transport.SessionMsgProcessor;
-import org.thingsboard.server.common.transport.adaptor.AdaptorException;
-import org.thingsboard.server.common.transport.auth.DeviceAuthService;
-import org.thingsboard.server.transport.mqtt.MqttTopicMatcher;
-import org.thingsboard.server.transport.mqtt.adaptors.MqttTransportAdaptor;
-
-import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.atomic.AtomicInteger;
-
-/**
- * @author Andrew Shvayka
- */
-@Slf4j
-public class DeviceSessionCtx extends MqttDeviceAwareSessionContext {
-
- private final MqttTransportAdaptor adaptor;
- private final MqttSessionId sessionId;
- private ChannelHandlerContext channel;
- private volatile boolean allowAttributeResponses;
- private AtomicInteger msgIdSeq = new AtomicInteger(0);
-
- public DeviceSessionCtx(SessionMsgProcessor processor, DeviceAuthService authService, MqttTransportAdaptor adaptor, ConcurrentMap mqttQoSMap) {
- super(processor, authService, mqttQoSMap);
- this.adaptor = adaptor;
- this.sessionId = new MqttSessionId();
- }
-
- @Override
- public SessionType getSessionType() {
- return SessionType.ASYNC;
- }
-
- @Override
- public void onMsg(SessionActorToAdaptorMsg msg) throws SessionException {
- try {
- adaptor.convertToAdaptorMsg(this, msg).ifPresent(this::pushToNetwork);
- } catch (AdaptorException e) {
- //TODO: close channel with disconnect;
- logAndWrap(e);
- }
- }
-
- private void logAndWrap(AdaptorException e) throws SessionException {
- log.warn("Failed to convert msg: {}", e.getMessage(), e);
- throw new SessionException(e);
- }
-
- private void pushToNetwork(MqttMessage msg) {
- channel.writeAndFlush(msg);
- }
-
- @Override
- public void onMsg(SessionCtrlMsg msg) throws SessionException {
- if (msg instanceof SessionCloseMsg) {
- pushToNetwork(new MqttMessage(new MqttFixedHeader(MqttMessageType.DISCONNECT, false, MqttQoS.AT_MOST_ONCE, false, 0)));
- channel.close();
- }
- }
-
- @Override
- public boolean isClosed() {
- return false;
- }
-
- @Override
- public long getTimeout() {
- return 0;
- }
-
- @Override
- public SessionId getSessionId() {
- return sessionId;
- }
-
- public void setChannel(ChannelHandlerContext channel) {
- this.channel = channel;
- }
-
- public void setAllowAttributeResponses() {
- allowAttributeResponses = true;
- }
-
- public void setDisallowAttributeResponses() {
- allowAttributeResponses = false;
- }
-
- public int nextMsgId() {
- return msgIdSeq.incrementAndGet();
- }
-}
diff --git a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewayDeviceSessionCtx.java b/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewayDeviceSessionCtx.java
deleted file mode 100644
index bbc9c086a8..0000000000
--- a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewayDeviceSessionCtx.java
+++ /dev/null
@@ -1,208 +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.transport.mqtt.session;
-
-import com.google.gson.Gson;
-import com.google.gson.JsonArray;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonObject;
-import io.netty.buffer.ByteBuf;
-import io.netty.buffer.ByteBufAllocator;
-import io.netty.buffer.UnpooledByteBufAllocator;
-import io.netty.handler.codec.mqtt.*;
-import org.thingsboard.server.common.data.Device;
-import org.thingsboard.server.common.data.id.SessionId;
-import org.thingsboard.server.common.data.kv.AttributeKvEntry;
-import org.thingsboard.server.common.data.kv.KvEntry;
-import org.thingsboard.server.common.msg.core.*;
-import org.thingsboard.server.common.msg.kv.AttributesKVMsg;
-import org.thingsboard.server.common.msg.session.*;
-import org.thingsboard.server.common.msg.session.ex.SessionException;
-import org.thingsboard.server.common.transport.adaptor.JsonConverter;
-import org.thingsboard.server.common.transport.session.DeviceAwareSessionContext;
-import org.thingsboard.server.transport.mqtt.MqttTopicMatcher;
-import org.thingsboard.server.transport.mqtt.MqttTopics;
-import org.thingsboard.server.transport.mqtt.MqttTransportHandler;
-
-import java.nio.charset.Charset;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.atomic.AtomicInteger;
-
-/**
- * Created by ashvayka on 19.01.17.
- */
-public class GatewayDeviceSessionCtx extends MqttDeviceAwareSessionContext {
-
- private static final Gson GSON = new Gson();
- private static final Charset UTF8 = Charset.forName("UTF-8");
- private static final ByteBufAllocator ALLOCATOR = new UnpooledByteBufAllocator(false);
- public static final String DEVICE_PROPERTY = "device";
-
- private GatewaySessionCtx parent;
- private final MqttSessionId sessionId;
- private volatile boolean closed;
- private AtomicInteger msgIdSeq = new AtomicInteger(0);
-
- public GatewayDeviceSessionCtx(GatewaySessionCtx parent, Device device, ConcurrentMap mqttQoSMap) {
- super(parent.getProcessor(), parent.getAuthService(), device, mqttQoSMap);
- this.parent = parent;
- this.sessionId = new MqttSessionId();
- }
-
- @Override
- public SessionId getSessionId() {
- return sessionId;
- }
-
- @Override
- public SessionType getSessionType() {
- return SessionType.ASYNC;
- }
-
- @Override
- public void onMsg(SessionActorToAdaptorMsg sessionMsg) throws SessionException {
- Optional message = getToDeviceMsg(sessionMsg);
- message.ifPresent(parent::writeAndFlush);
- }
-
- private Optional getToDeviceMsg(SessionActorToAdaptorMsg sessionMsg) {
- ToDeviceMsg msg = sessionMsg.getMsg();
- switch (msg.getSessionMsgType()) {
- case STATUS_CODE_RESPONSE:
- ResponseMsg> responseMsg = (ResponseMsg) msg;
- if (responseMsg.isSuccess()) {
- SessionMsgType requestMsgType = responseMsg.getRequestMsgType();
- Integer requestId = responseMsg.getRequestId();
- if (requestId >= 0 && (requestMsgType == SessionMsgType.POST_ATTRIBUTES_REQUEST || requestMsgType == SessionMsgType.POST_TELEMETRY_REQUEST)) {
- return Optional.of(MqttTransportHandler.createMqttPubAckMsg(requestId));
- }
- }
- break;
- case GET_ATTRIBUTES_RESPONSE:
- GetAttributesResponse response = (GetAttributesResponse) msg;
- if (response.isSuccess()) {
- return Optional.of(createMqttPublishMsg(MqttTopics.GATEWAY_ATTRIBUTES_RESPONSE_TOPIC, response));
- } else {
- //TODO: push error handling to the gateway
- }
- break;
- case ATTRIBUTES_UPDATE_NOTIFICATION:
- AttributesUpdateNotification notification = (AttributesUpdateNotification) msg;
- return Optional.of(createMqttPublishMsg(MqttTopics.GATEWAY_ATTRIBUTES_TOPIC, notification.getData()));
- case TO_DEVICE_RPC_REQUEST:
- ToDeviceRpcRequestMsg rpcRequest = (ToDeviceRpcRequestMsg) msg;
- return Optional.of(createMqttPublishMsg(MqttTopics.GATEWAY_RPC_TOPIC, rpcRequest));
- default:
- break;
- }
- return Optional.empty();
- }
-
- @Override
- public void onMsg(SessionCtrlMsg msg) throws SessionException {
- //Do nothing
- }
-
- @Override
- public boolean isClosed() {
- return closed;
- }
-
- public void setClosed(boolean closed) {
- this.closed = closed;
- }
-
- @Override
- public long getTimeout() {
- return 0;
- }
-
- private MqttMessage createMqttPublishMsg(String topic, GetAttributesResponse response) {
- JsonObject result = new JsonObject();
- result.addProperty("id", response.getRequestId());
- result.addProperty(DEVICE_PROPERTY, device.getName());
- Optional responseData = response.getData();
- if (responseData.isPresent()) {
- AttributesKVMsg msg = responseData.get();
- if (msg.getClientAttributes() != null) {
- addValues(result, msg.getClientAttributes());
- }
- if (msg.getSharedAttributes() != null) {
- addValues(result, msg.getSharedAttributes());
- }
- }
- return createMqttPublishMsg(topic, result);
- }
-
- private void addValues(JsonObject result, List kvList) {
- if (kvList.size() == 1) {
- addValueToJson(result, "value", kvList.get(0));
- } else {
- JsonObject values;
- if (result.has("values")) {
- values = result.get("values").getAsJsonObject();
- } else {
- values = new JsonObject();
- result.add("values", values);
- }
- kvList.forEach(value -> addValueToJson(values, value.getKey(), value));
- }
- }
-
- private void addValueToJson(JsonObject json, String name, KvEntry entry) {
- switch (entry.getDataType()) {
- case BOOLEAN:
- entry.getBooleanValue().ifPresent(aBoolean -> json.addProperty(name, aBoolean));
- break;
- case STRING:
- entry.getStrValue().ifPresent(aString -> json.addProperty(name, aString));
- break;
- case DOUBLE:
- entry.getDoubleValue().ifPresent(aDouble -> json.addProperty(name, aDouble));
- break;
- case LONG:
- entry.getLongValue().ifPresent(aLong -> json.addProperty(name, aLong));
- break;
- }
- }
-
- private MqttMessage createMqttPublishMsg(String topic, AttributesKVMsg data) {
- JsonObject result = new JsonObject();
- result.addProperty(DEVICE_PROPERTY, device.getName());
- result.add("data", JsonConverter.toJson(data, false));
- return createMqttPublishMsg(topic, result);
- }
-
- private MqttMessage createMqttPublishMsg(String topic, ToDeviceRpcRequestMsg data) {
- JsonObject result = new JsonObject();
- result.addProperty(DEVICE_PROPERTY, device.getName());
- result.add("data", JsonConverter.toJson(data, true));
- return createMqttPublishMsg(topic, result);
- }
-
- private MqttPublishMessage createMqttPublishMsg(String topic, JsonElement json) {
- MqttFixedHeader mqttFixedHeader =
- new MqttFixedHeader(MqttMessageType.PUBLISH, false, getQoSForTopic(topic), false, 0);
- MqttPublishVariableHeader header = new MqttPublishVariableHeader(topic, msgIdSeq.incrementAndGet());
- ByteBuf payload = ALLOCATOR.buffer();
- payload.writeBytes(GSON.toJson(json).getBytes(UTF8));
- return new MqttPublishMessage(mqttFixedHeader, header, payload);
- }
-
-}
diff --git a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionCtx.java b/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionCtx.java
deleted file mode 100644
index b9291b794a..0000000000
--- a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionCtx.java
+++ /dev/null
@@ -1,277 +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.transport.mqtt.session;
-
-import com.google.gson.JsonArray;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonNull;
-import com.google.gson.JsonObject;
-import com.google.gson.JsonSyntaxException;
-import io.netty.channel.ChannelHandlerContext;
-import io.netty.handler.codec.mqtt.MqttMessage;
-import io.netty.handler.codec.mqtt.MqttPublishMessage;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.util.StringUtils;
-import org.thingsboard.server.common.data.Device;
-import org.thingsboard.server.common.data.id.SessionId;
-import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry;
-import org.thingsboard.server.common.data.relation.EntityRelation;
-import org.thingsboard.server.common.msg.core.*;
-import org.thingsboard.server.common.msg.session.BasicAdaptorToSessionActorMsg;
-import org.thingsboard.server.common.msg.session.BasicTransportToDeviceSessionActorMsg;
-import org.thingsboard.server.common.msg.session.ctrl.SessionCloseMsg;
-import org.thingsboard.server.common.transport.SessionMsgProcessor;
-import org.thingsboard.server.common.transport.adaptor.AdaptorException;
-import org.thingsboard.server.common.transport.adaptor.JsonConverter;
-import org.thingsboard.server.common.transport.auth.DeviceAuthService;
-import org.thingsboard.server.dao.device.DeviceService;
-import org.thingsboard.server.dao.relation.RelationService;
-import org.thingsboard.server.transport.mqtt.MqttTopicMatcher;
-import org.thingsboard.server.transport.mqtt.MqttTransportHandler;
-import org.thingsboard.server.transport.mqtt.adaptors.JsonMqttAdaptor;
-
-import java.util.*;
-import java.util.concurrent.ConcurrentMap;
-import java.util.stream.Collectors;
-
-import static org.thingsboard.server.transport.mqtt.adaptors.JsonMqttAdaptor.validateJsonPayload;
-
-/**
- * Created by ashvayka on 19.01.17.
- */
-@Slf4j
-public class GatewaySessionCtx {
-
- private static final String DEFAULT_DEVICE_TYPE = "default";
- public static final String CAN_T_PARSE_VALUE = "Can't parse value: ";
- public static final String DEVICE_PROPERTY = "device";
- private final Device gateway;
- private final SessionId gatewaySessionId;
- private final SessionMsgProcessor processor;
- private final DeviceService deviceService;
- private final DeviceAuthService authService;
- private final RelationService relationService;
- private final Map devices;
- private final ConcurrentMap mqttQoSMap;
- private ChannelHandlerContext channel;
-
- public GatewaySessionCtx(SessionMsgProcessor processor, DeviceService deviceService, DeviceAuthService authService, RelationService relationService, DeviceSessionCtx gatewaySessionCtx) {
- this.processor = processor;
- this.deviceService = deviceService;
- this.authService = authService;
- this.relationService = relationService;
- this.gateway = gatewaySessionCtx.getDevice();
- this.gatewaySessionId = gatewaySessionCtx.getSessionId();
- this.devices = new HashMap<>();
- this.mqttQoSMap = gatewaySessionCtx.getMqttQoSMap();
- }
-
- public void onDeviceConnect(MqttPublishMessage msg) throws AdaptorException {
- JsonElement json = getJson(msg);
- String deviceName = checkDeviceName(getDeviceName(json));
- String deviceType = getDeviceType(json);
- onDeviceConnect(deviceName, deviceType);
- ack(msg);
- }
-
- private void onDeviceConnect(String deviceName, String deviceType) {
- if (!devices.containsKey(deviceName)) {
- Device device = deviceService.findDeviceByTenantIdAndName(gateway.getTenantId(), deviceName);
- if (device == null) {
- device = new Device();
- device.setTenantId(gateway.getTenantId());
- device.setName(deviceName);
- device.setType(deviceType);
- device.setCustomerId(gateway.getCustomerId());
- device = deviceService.saveDevice(device);
- relationService.saveRelationAsync(new EntityRelation(gateway.getId(), device.getId(), "Created"));
- processor.onDeviceAdded(device);
- }
- GatewayDeviceSessionCtx ctx = new GatewayDeviceSessionCtx(this, device, mqttQoSMap);
- devices.put(deviceName, ctx);
- log.debug("[{}] Added device [{}] to the gateway session", gatewaySessionId, deviceName);
- processor.process(new BasicTransportToDeviceSessionActorMsg(device, new BasicAdaptorToSessionActorMsg(ctx, new AttributesSubscribeMsg())));
- processor.process(new BasicTransportToDeviceSessionActorMsg(device, new BasicAdaptorToSessionActorMsg(ctx, new RpcSubscribeMsg())));
- }
- }
-
- public void onDeviceDisconnect(MqttPublishMessage msg) throws AdaptorException {
- String deviceName = checkDeviceName(getDeviceName(getJson(msg)));
- GatewayDeviceSessionCtx deviceSessionCtx = devices.remove(deviceName);
- if (deviceSessionCtx != null) {
- processor.process(SessionCloseMsg.onDisconnect(deviceSessionCtx.getSessionId()));
- deviceSessionCtx.setClosed(true);
- log.debug("[{}] Removed device [{}] from the gateway session", gatewaySessionId, deviceName);
- } else {
- log.debug("[{}] Device [{}] was already removed from the gateway session", gatewaySessionId, deviceName);
- }
- ack(msg);
- }
-
- public void onGatewayDisconnect() {
- devices.forEach((k, v) -> {
- processor.process(SessionCloseMsg.onDisconnect(v.getSessionId()));
- });
- }
-
- public void onDeviceTelemetry(MqttPublishMessage mqttMsg) throws AdaptorException {
- JsonElement json = validateJsonPayload(gatewaySessionId, mqttMsg.payload());
- int requestId = mqttMsg.variableHeader().messageId();
- if (json.isJsonObject()) {
- JsonObject jsonObj = json.getAsJsonObject();
- for (Map.Entry deviceEntry : jsonObj.entrySet()) {
- String deviceName = checkDeviceConnected(deviceEntry.getKey());
- if (!deviceEntry.getValue().isJsonArray()) {
- throw new JsonSyntaxException(CAN_T_PARSE_VALUE + json);
- }
- BasicTelemetryUploadRequest request = new BasicTelemetryUploadRequest(requestId);
- JsonArray deviceData = deviceEntry.getValue().getAsJsonArray();
- for (JsonElement element : deviceData) {
- JsonConverter.parseWithTs(request, element.getAsJsonObject());
- }
- GatewayDeviceSessionCtx deviceSessionCtx = devices.get(deviceName);
- processor.process(new BasicTransportToDeviceSessionActorMsg(deviceSessionCtx.getDevice(),
- new BasicAdaptorToSessionActorMsg(deviceSessionCtx, request)));
- }
- } else {
- throw new JsonSyntaxException(CAN_T_PARSE_VALUE + json);
- }
- }
-
- public void onDeviceRpcResponse(MqttPublishMessage mqttMsg) throws AdaptorException {
- JsonElement json = validateJsonPayload(gatewaySessionId, mqttMsg.payload());
- if (json.isJsonObject()) {
- JsonObject jsonObj = json.getAsJsonObject();
- String deviceName = checkDeviceConnected(jsonObj.get(DEVICE_PROPERTY).getAsString());
- Integer requestId = jsonObj.get("id").getAsInt();
- String data = jsonObj.get("data").toString();
- GatewayDeviceSessionCtx deviceSessionCtx = devices.get(deviceName);
- processor.process(new BasicTransportToDeviceSessionActorMsg(deviceSessionCtx.getDevice(),
- new BasicAdaptorToSessionActorMsg(deviceSessionCtx, new ToDeviceRpcResponseMsg(requestId, data))));
- ack(mqttMsg);
- } else {
- throw new JsonSyntaxException(CAN_T_PARSE_VALUE + json);
- }
- }
-
- public void onDeviceAttributes(MqttPublishMessage mqttMsg) throws AdaptorException {
- JsonElement json = validateJsonPayload(gatewaySessionId, mqttMsg.payload());
- int requestId = mqttMsg.variableHeader().messageId();
- if (json.isJsonObject()) {
- JsonObject jsonObj = json.getAsJsonObject();
- for (Map.Entry deviceEntry : jsonObj.entrySet()) {
- String deviceName = checkDeviceConnected(deviceEntry.getKey());
- if (!deviceEntry.getValue().isJsonObject()) {
- throw new JsonSyntaxException(CAN_T_PARSE_VALUE + json);
- }
- long ts = System.currentTimeMillis();
- BasicAttributesUpdateRequest request = new BasicAttributesUpdateRequest(requestId);
- JsonObject deviceData = deviceEntry.getValue().getAsJsonObject();
- request.add(JsonConverter.parseValues(deviceData).stream().map(kv -> new BaseAttributeKvEntry(kv, ts)).collect(Collectors.toList()));
- GatewayDeviceSessionCtx deviceSessionCtx = devices.get(deviceName);
- processor.process(new BasicTransportToDeviceSessionActorMsg(deviceSessionCtx.getDevice(),
- new BasicAdaptorToSessionActorMsg(deviceSessionCtx, request)));
- }
- } else {
- throw new JsonSyntaxException(CAN_T_PARSE_VALUE + json);
- }
- }
-
- public void onDeviceAttributesRequest(MqttPublishMessage msg) throws AdaptorException {
- JsonElement json = validateJsonPayload(gatewaySessionId, msg.payload());
- if (json.isJsonObject()) {
- JsonObject jsonObj = json.getAsJsonObject();
- int requestId = jsonObj.get("id").getAsInt();
- String deviceName = jsonObj.get(DEVICE_PROPERTY).getAsString();
- boolean clientScope = jsonObj.get("client").getAsBoolean();
- Set keys;
- if (jsonObj.has("key")) {
- keys = Collections.singleton(jsonObj.get("key").getAsString());
- } else {
- JsonArray keysArray = jsonObj.get("keys").getAsJsonArray();
- keys = new HashSet<>();
- for (JsonElement keyObj : keysArray) {
- keys.add(keyObj.getAsString());
- }
- }
-
- BasicGetAttributesRequest request;
- if (clientScope) {
- request = new BasicGetAttributesRequest(requestId, keys, null);
- } else {
- request = new BasicGetAttributesRequest(requestId, null, keys);
- }
- GatewayDeviceSessionCtx deviceSessionCtx = devices.get(deviceName);
- processor.process(new BasicTransportToDeviceSessionActorMsg(deviceSessionCtx.getDevice(),
- new BasicAdaptorToSessionActorMsg(deviceSessionCtx, request)));
- ack(msg);
- } else {
- throw new JsonSyntaxException(CAN_T_PARSE_VALUE + json);
- }
- }
-
- private String checkDeviceConnected(String deviceName) {
- if (!devices.containsKey(deviceName)) {
- log.debug("[{}] Missing device [{}] for the gateway session", gatewaySessionId, deviceName);
- onDeviceConnect(deviceName, DEFAULT_DEVICE_TYPE);
- }
- return deviceName;
- }
-
- private String checkDeviceName(String deviceName) {
- if (StringUtils.isEmpty(deviceName)) {
- throw new RuntimeException("Device name is empty!");
- } else {
- return deviceName;
- }
- }
-
- private String getDeviceName(JsonElement json) throws AdaptorException {
- return json.getAsJsonObject().get(DEVICE_PROPERTY).getAsString();
- }
-
- private String getDeviceType(JsonElement json) throws AdaptorException {
- JsonElement type = json.getAsJsonObject().get("type");
- return type == null || type instanceof JsonNull ? DEFAULT_DEVICE_TYPE : type.getAsString();
- }
-
- private JsonElement getJson(MqttPublishMessage mqttMsg) throws AdaptorException {
- return JsonMqttAdaptor.validateJsonPayload(gatewaySessionId, mqttMsg.payload());
- }
-
- protected SessionMsgProcessor getProcessor() {
- return processor;
- }
-
- DeviceAuthService getAuthService() {
- return authService;
- }
-
- public void setChannel(ChannelHandlerContext channel) {
- this.channel = channel;
- }
-
- private void ack(MqttPublishMessage msg) {
- if (msg.variableHeader().messageId() > 0) {
- writeAndFlush(MqttTransportHandler.createMqttPubAckMsg(msg.variableHeader().messageId()));
- }
- }
-
- void writeAndFlush(MqttMessage mqttMessage) {
- channel.writeAndFlush(mqttMessage);
- }
-
-}
diff --git a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/MqttSessionId.java b/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/MqttSessionId.java
deleted file mode 100644
index 03006128bf..0000000000
--- a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/MqttSessionId.java
+++ /dev/null
@@ -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.transport.mqtt.session;
-
-import org.thingsboard.server.common.data.id.SessionId;
-
-import java.util.UUID;
-import java.util.concurrent.atomic.AtomicLong;
-
-/**
- * @author Andrew Shvayka
- */
-public class MqttSessionId implements SessionId {
-
- private static final AtomicLong idSeq = new AtomicLong();
-
- private final long id;
-
- public MqttSessionId() {
- this.id = idSeq.incrementAndGet();
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
-
- MqttSessionId that = (MqttSessionId) o;
-
- return id == that.id;
-
- }
-
- @Override
- public String toString() {
- return "MqttSessionId{" +
- "id=" + id +
- '}';
- }
-
- @Override
- public int hashCode() {
- return (int) (id ^ (id >>> 32));
- }
-
- @Override
- public String toUidStr() {
- return "mqtt" + id;
- }
-}
diff --git a/transport/mqtt/src/main/resources/logback.xml b/transport/mqtt/src/main/resources/logback.xml
new file mode 100644
index 0000000000..18864a9fab
--- /dev/null
+++ b/transport/mqtt/src/main/resources/logback.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+ %d{ISO8601} [%thread] %-5level %logger{36} - %msg%n
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/transport/mqtt/src/main/resources/tb-mqtt-transport.yml b/transport/mqtt/src/main/resources/tb-mqtt-transport.yml
new file mode 100644
index 0000000000..1d425e4761
--- /dev/null
+++ b/transport/mqtt/src/main/resources/tb-mqtt-transport.yml
@@ -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.
+#
+
+spring.main.web-environment: false
+spring.main.web-application-type: none
+
+# MQTT server parameters
+transport:
+ mqtt:
+ 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}"
+
+#Quota parameters
+quota:
+ host:
+ # Max allowed number of API requests in interval for single host
+ limit: "${QUOTA_HOST_LIMIT:10000}"
+ # Interval duration
+ intervalMs: "${QUOTA_HOST_INTERVAL_MS:60000}"
+ # Maximum silence duration for host after which Host removed from QuotaService. Must be bigger than intervalMs
+ ttlMs: "${QUOTA_HOST_TTL_MS:60000}"
+ # Interval for scheduled task that cleans expired records. TTL is used for expiring
+ cleanPeriodMs: "${QUOTA_HOST_CLEAN_PERIOD_MS:300000}"
+ # Enable Host API Limits
+ enabled: "${QUOTA_HOST_ENABLED:true}"
+ # Array of whitelist hosts
+ whitelist: "${QUOTA_HOST_WHITELIST:localhost,127.0.0.1}"
+ # Array of blacklist hosts
+ blacklist: "${QUOTA_HOST_BLACKLIST:}"
+ log:
+ topSize: 10
+ intervalMin: 2
+
+kafka:
+ enabled: true
+ bootstrap.servers: "${TB_KAFKA_SERVERS:localhost:9092}"
+ acks: "${TB_KAFKA_ACKS:all}"
+ retries: "${TB_KAFKA_RETRIES:1}"
+ 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}"
+ max_requests_timeout: "${TB_TRANSPORT_MAX_REQUEST_TIMEOUT:10000}"
+ response_poll_interval: "${TB_TRANSPORT_RESPONSE_POLL_INTERVAL_MS:25}"
+ response_auto_commit_interval: "${TB_TRANSPORT_RESPONSE_AUTO_COMMIT_INTERVAL_MS:100}"
+ rule_engine:
+ topic: "${TB_RULE_ENGINE_TOPIC:tb.rule-engine}"
+ notifications:
+ topic: "${TB_TRANSPORT_NOTIFICATIONS_TOPIC:tb.transport.notifications}"
+ poll_interval: "${TB_TRANSPORT_NOTIFICATIONS_POLL_INTERVAL_MS:25}"
+ auto_commit_interval: "${TB_TRANSPORT_NOTIFICATIONS_AUTO_COMMIT_INTERVAL_MS:100}"
diff --git a/transport/mqtt/src/main/scripts/control/deb/postinst b/transport/mqtt/src/main/scripts/control/deb/postinst
new file mode 100644
index 0000000000..d4066c027b
--- /dev/null
+++ b/transport/mqtt/src/main/scripts/control/deb/postinst
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+chown -R ${pkg.name}: ${pkg.logFolder}
+chown -R ${pkg.name}: ${pkg.installFolder}
+update-rc.d ${pkg.name} defaults
+
diff --git a/transport/mqtt/src/main/scripts/control/deb/postrm b/transport/mqtt/src/main/scripts/control/deb/postrm
new file mode 100644
index 0000000000..61865803c3
--- /dev/null
+++ b/transport/mqtt/src/main/scripts/control/deb/postrm
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+update-rc.d -f ${pkg.name} remove
diff --git a/transport/mqtt/src/main/scripts/control/deb/preinst b/transport/mqtt/src/main/scripts/control/deb/preinst
new file mode 100644
index 0000000000..6be5959285
--- /dev/null
+++ b/transport/mqtt/src/main/scripts/control/deb/preinst
@@ -0,0 +1,18 @@
+#!/bin/sh
+
+if ! getent group ${pkg.name} >/dev/null; then
+ addgroup --system ${pkg.name}
+fi
+
+if ! getent passwd ${pkg.name} >/dev/null; then
+ adduser --quiet \
+ --system \
+ --ingroup ${pkg.name} \
+ --quiet \
+ --disabled-login \
+ --disabled-password \
+ --home ${pkg.installFolder} \
+ --no-create-home \
+ -gecos "Thingsboard application" \
+ ${pkg.name}
+fi
diff --git a/transport/mqtt/src/main/scripts/control/deb/prerm b/transport/mqtt/src/main/scripts/control/deb/prerm
new file mode 100644
index 0000000000..898d3efd5c
--- /dev/null
+++ b/transport/mqtt/src/main/scripts/control/deb/prerm
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+if [ -e /var/run/${pkg.name}/${pkg.name}.pid ]; then
+ service ${pkg.name} stop
+fi
diff --git a/transport/mqtt/src/main/scripts/control/rpm/postinst b/transport/mqtt/src/main/scripts/control/rpm/postinst
new file mode 100644
index 0000000000..8a7a88f7e0
--- /dev/null
+++ b/transport/mqtt/src/main/scripts/control/rpm/postinst
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+chown -R ${pkg.name}: ${pkg.logFolder}
+chown -R ${pkg.name}: ${pkg.installFolder}
+
+if [ $1 -eq 1 ] ; then
+ # Initial installation
+ systemctl --no-reload enable ${pkg.name}.service >/dev/null 2>&1 || :
+fi
diff --git a/transport/mqtt/src/main/scripts/control/rpm/postrm b/transport/mqtt/src/main/scripts/control/rpm/postrm
new file mode 100644
index 0000000000..8e1f8a2048
--- /dev/null
+++ b/transport/mqtt/src/main/scripts/control/rpm/postrm
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+if [ $1 -ge 1 ] ; then
+ # Package upgrade, not uninstall
+ systemctl try-restart ${pkg.name}.service >/dev/null 2>&1 || :
+fi
diff --git a/transport/mqtt/src/main/scripts/control/rpm/preinst b/transport/mqtt/src/main/scripts/control/rpm/preinst
new file mode 100644
index 0000000000..e19fc884c8
--- /dev/null
+++ b/transport/mqtt/src/main/scripts/control/rpm/preinst
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+getent group ${pkg.name} >/dev/null || groupadd -r ${pkg.name}
+getent passwd ${pkg.name} >/dev/null || \
+useradd -d ${pkg.installFolder} -g ${pkg.name} -M -r ${pkg.name} -s /sbin/nologin \
+-c "Thingsboard application"
diff --git a/transport/mqtt/src/main/scripts/control/rpm/prerm b/transport/mqtt/src/main/scripts/control/rpm/prerm
new file mode 100644
index 0000000000..accb487b8e
--- /dev/null
+++ b/transport/mqtt/src/main/scripts/control/rpm/prerm
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+if [ $1 -eq 0 ] ; then
+ # Package removal, not upgrade
+ systemctl --no-reload disable --now ${pkg.name}.service > /dev/null 2>&1 || :
+fi
diff --git a/transport/mqtt/src/main/scripts/control/tb-mqtt-transport.service b/transport/mqtt/src/main/scripts/control/tb-mqtt-transport.service
new file mode 100644
index 0000000000..d456fc03c0
--- /dev/null
+++ b/transport/mqtt/src/main/scripts/control/tb-mqtt-transport.service
@@ -0,0 +1,11 @@
+[Unit]
+Description=${pkg.name}
+After=syslog.target
+
+[Service]
+User=${pkg.name}
+ExecStart=${pkg.installFolder}/bin/${pkg.name}.jar
+SuccessExitStatus=143
+
+[Install]
+WantedBy=multi-user.target
diff --git a/transport/mqtt/src/main/scripts/windows/install.bat b/transport/mqtt/src/main/scripts/windows/install.bat
new file mode 100644
index 0000000000..dba773663c
--- /dev/null
+++ b/transport/mqtt/src/main/scripts/windows/install.bat
@@ -0,0 +1,87 @@
+@ECHO OFF
+
+setlocal ENABLEEXTENSIONS
+
+@ECHO Detecting Java version installed.
+:CHECK_JAVA_64
+@ECHO Detecting if it is 64 bit machine
+set KEY_NAME="HKEY_LOCAL_MACHINE\Software\Wow6432Node\JavaSoft\Java Runtime Environment"
+set VALUE_NAME=CurrentVersion
+
+FOR /F "usebackq skip=2 tokens=1-3" %%A IN (`REG QUERY %KEY_NAME% /v %VALUE_NAME% 2^>nul`) DO (
+ set ValueName=%%A
+ set ValueType=%%B
+ set ValueValue=%%C
+)
+@ECHO CurrentVersion %ValueValue%
+
+SET KEY_NAME="%KEY_NAME:~1,-1%\%ValueValue%"
+SET VALUE_NAME=JavaHome
+
+if defined ValueName (
+ FOR /F "usebackq skip=2 tokens=1,2*" %%A IN (`REG QUERY %KEY_NAME% /v %VALUE_NAME% 2^>nul`) DO (
+ set ValueName2=%%A
+ set ValueType2=%%B
+ set JRE_PATH2=%%C
+
+ if defined ValueName2 (
+ set ValueName = %ValueName2%
+ set ValueType = %ValueType2%
+ set ValueValue = %JRE_PATH2%
+ )
+ )
+)
+
+IF NOT "%JRE_PATH2%" == "" GOTO JAVA_INSTALLED
+
+:CHECK_JAVA_32
+@ECHO Detecting if it is 32 bit machine
+set KEY_NAME="HKEY_LOCAL_MACHINE\Software\JavaSoft\Java Runtime Environment"
+set VALUE_NAME=CurrentVersion
+
+FOR /F "usebackq skip=2 tokens=1-3" %%A IN (`REG QUERY %KEY_NAME% /v %VALUE_NAME% 2^>nul`) DO (
+ set ValueName=%%A
+ set ValueType=%%B
+ set ValueValue=%%C
+)
+@ECHO CurrentVersion %ValueValue%
+
+SET KEY_NAME="%KEY_NAME:~1,-1%\%ValueValue%"
+SET VALUE_NAME=JavaHome
+
+if defined ValueName (
+ FOR /F "usebackq skip=2 tokens=1,2*" %%A IN (`REG QUERY %KEY_NAME% /v %VALUE_NAME% 2^>nul`) DO (
+ set ValueName2=%%A
+ set ValueType2=%%B
+ set JRE_PATH2=%%C
+
+ if defined ValueName2 (
+ set ValueName = %ValueName2%
+ set ValueType = %ValueType2%
+ set ValueValue = %JRE_PATH2%
+ )
+ )
+)
+
+IF "%JRE_PATH2%" == "" GOTO JAVA_NOT_INSTALLED
+
+:JAVA_INSTALLED
+
+@ECHO Java 1.8 found!
+@ECHO Installing ${pkg.name} ...
+
+%BASE%${pkg.name}.exe install
+
+@ECHO ${pkg.name} installed successfully!
+
+GOTO END
+
+:JAVA_NOT_INSTALLED
+@ECHO Java 1.8 or above is not installed
+@ECHO Please go to https://java.com/ and install Java. Then retry installation.
+PAUSE
+GOTO END
+
+:END
+
+
diff --git a/transport/mqtt/src/main/scripts/windows/service.xml b/transport/mqtt/src/main/scripts/windows/service.xml
new file mode 100644
index 0000000000..f7b9d307c8
--- /dev/null
+++ b/transport/mqtt/src/main/scripts/windows/service.xml
@@ -0,0 +1,36 @@
+
+ ${pkg.name}
+ ${project.name}
+ ${project.description}
+ %BASE%\conf
+ ${pkg.winWrapperLogFolder}
+ rotate
+
+ java
+ -Xloggc:%BASE%\logs\gc.log
+ -XX:+HeapDumpOnOutOfMemoryError
+ -XX:+PrintGCDetails
+ -XX:+PrintGCDateStamps
+ -XX:+PrintHeapAtGC
+ -XX:+PrintTenuringDistribution
+ -XX:+PrintGCApplicationStoppedTime
+ -XX:+UseGCLogFileRotation
+ -XX:NumberOfGCLogFiles=10
+ -XX:GCLogFileSize=10M
+ -XX:-UseBiasedLocking
+ -XX:+UseTLAB
+ -XX:+ResizeTLAB
+ -XX:+PerfDisableSharedMem
+ -XX:+UseCondCardMark
+ -XX:CMSWaitDuration=10000
+ -XX:+UseParNewGC
+ -XX:+UseConcMarkSweepGC
+ -XX:+CMSParallelRemarkEnabled
+ -XX:+CMSParallelInitialMarkEnabled
+ -XX:+CMSEdenChunksRecordAlways
+ -XX:CMSInitiatingOccupancyFraction=75
+ -XX:+UseCMSInitiatingOccupancyOnly
+ -jar
+ %BASE%\lib\${pkg.name}.jar
+
+
diff --git a/transport/mqtt/src/main/scripts/windows/uninstall.bat b/transport/mqtt/src/main/scripts/windows/uninstall.bat
new file mode 100644
index 0000000000..921e4c8395
--- /dev/null
+++ b/transport/mqtt/src/main/scripts/windows/uninstall.bat
@@ -0,0 +1,9 @@
+@ECHO OFF
+
+@ECHO Stopping ${pkg.name} ...
+net stop ${pkg.name}
+
+@ECHO Uninstalling ${pkg.name} ...
+%~dp0${pkg.name}.exe uninstall
+
+@ECHO DONE.
\ No newline at end of file
diff --git a/transport/pom.xml b/transport/pom.xml
index 95b0112a2a..0bb9300b27 100644
--- a/transport/pom.xml
+++ b/transport/pom.xml
@@ -23,7 +23,6 @@
2.2.0-SNAPSHOT
thingsboard
- org.thingsboard
transport
pom
@@ -36,23 +35,8 @@
http
- coap
mqtt
+ coap
-
-
- org.thingsboard
- dao
-
-
- org.springframework.boot
- spring-boot-autoconfigure
-
-
- org.bouncycastle
- bcprov-jdk15on
-
-
-